[chore] Update go-sqlite3 to 0.18.3 (#3295)

* [chore] Update go-sqlite3 to 0.18.3

* [chore] Fix getting the sqlite3.Conn
This commit is contained in:
Daenney 2024-09-14 16:36:25 +02:00 committed by GitHub
parent 486852fb38
commit 4fa0d41202
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
23 changed files with 194 additions and 104 deletions

2
go.mod
View file

@ -44,7 +44,7 @@ require (
github.com/miekg/dns v1.1.62 github.com/miekg/dns v1.1.62
github.com/minio/minio-go/v7 v7.0.76 github.com/minio/minio-go/v7 v7.0.76
github.com/mitchellh/mapstructure v1.5.0 github.com/mitchellh/mapstructure v1.5.0
github.com/ncruces/go-sqlite3 v0.18.1 github.com/ncruces/go-sqlite3 v0.18.3
github.com/oklog/ulid v1.3.1 github.com/oklog/ulid v1.3.1
github.com/prometheus/client_golang v1.20.3 github.com/prometheus/client_golang v1.20.3
github.com/spf13/cobra v1.8.1 github.com/spf13/cobra v1.8.1

4
go.sum
View file

@ -440,8 +440,8 @@ github.com/moul/http2curl v1.0.0 h1:dRMWoAtb+ePxMlLkrCbAqh4TlPHXvoGUSQ323/9Zahs=
github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ= github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/ncruces/go-sqlite3 v0.18.1 h1:iN8IMZV5EMxpH88NUac9vId23eTKNFUhP7jgY0EBbNc= github.com/ncruces/go-sqlite3 v0.18.3 h1:tyMa75uh7LcINcfo0WrzOvcTkfz8Hqu0TEPX+KVyes4=
github.com/ncruces/go-sqlite3 v0.18.1/go.mod h1:eEOyZnW1dGTJ+zDpMuzfYamEUBtdFz5zeYhqLBtHxvM= github.com/ncruces/go-sqlite3 v0.18.3/go.mod h1:HAwOtA+cyEX3iN6YmkpQwfT4vMMgCB7rQRFUdOgEFik=
github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4= github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
github.com/ncruces/julianday v1.0.0 h1:fH0OKwa7NWvniGQtxdJRxAgkBMolni2BjDHaWTxqt7M= github.com/ncruces/julianday v1.0.0 h1:fH0OKwa7NWvniGQtxdJRxAgkBMolni2BjDHaWTxqt7M=

View file

@ -25,7 +25,6 @@ import (
"github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/db"
"github.com/ncruces/go-sqlite3"
sqlite3driver "github.com/ncruces/go-sqlite3/driver" sqlite3driver "github.com/ncruces/go-sqlite3/driver"
_ "github.com/ncruces/go-sqlite3/embed" // embed wasm binary _ "github.com/ncruces/go-sqlite3/embed" // embed wasm binary
_ "github.com/ncruces/go-sqlite3/vfs/memdb" // include memdb vfs _ "github.com/ncruces/go-sqlite3/vfs/memdb" // include memdb vfs
@ -110,7 +109,7 @@ func (c *sqliteConn) ExecContext(ctx context.Context, query string, args []drive
func (c *sqliteConn) Close() (err error) { func (c *sqliteConn) Close() (err error) {
// Get acces the underlying raw sqlite3 conn. // Get acces the underlying raw sqlite3 conn.
raw := c.connIface.(sqlite3.DriverConn).Raw() raw := c.connIface.(sqlite3driver.Conn).Raw()
// see: https://www.sqlite.org/pragma.html#pragma_optimize // see: https://www.sqlite.org/pragma.html#pragma_optimize
const onClose = "PRAGMA analysis_limit=1000; PRAGMA optimize;" const onClose = "PRAGMA analysis_limit=1000; PRAGMA optimize;"

View file

@ -21,6 +21,8 @@ type Blob struct {
bytes int64 bytes int64
offset int64 offset int64
handle uint32 handle uint32
bufptr uint32
buflen int64
} }
var _ io.ReadWriteSeeker = &Blob{} var _ io.ReadWriteSeeker = &Blob{}
@ -66,7 +68,7 @@ func (b *Blob) Close() error {
} }
r := b.c.call("sqlite3_blob_close", uint64(b.handle)) r := b.c.call("sqlite3_blob_close", uint64(b.handle))
b.c.free(b.bufptr)
b.handle = 0 b.handle = 0
return b.c.error(r) return b.c.error(r)
} }
@ -86,17 +88,18 @@ func (b *Blob) Read(p []byte) (n int, err error) {
return 0, io.EOF return 0, io.EOF
} }
avail := b.bytes - b.offset
want := int64(len(p)) want := int64(len(p))
avail := b.bytes - b.offset
if want > avail { if want > avail {
want = avail want = avail
} }
if want > b.buflen {
defer b.c.arena.mark()() b.bufptr = b.c.realloc(b.bufptr, uint64(want))
ptr := b.c.arena.new(uint64(want)) b.buflen = want
}
r := b.c.call("sqlite3_blob_read", uint64(b.handle), r := b.c.call("sqlite3_blob_read", uint64(b.handle),
uint64(ptr), uint64(want), uint64(b.offset)) uint64(b.bufptr), uint64(want), uint64(b.offset))
err = b.c.error(r) err = b.c.error(r)
if err != nil { if err != nil {
return 0, err return 0, err
@ -106,7 +109,7 @@ func (b *Blob) Read(p []byte) (n int, err error) {
err = io.EOF err = io.EOF
} }
copy(p, util.View(b.c.mod, ptr, uint64(want))) copy(p, util.View(b.c.mod, b.bufptr, uint64(want)))
return int(want), err return int(want), err
} }
@ -123,19 +126,20 @@ func (b *Blob) WriteTo(w io.Writer) (n int64, err error) {
if want > avail { if want > avail {
want = avail want = avail
} }
if want > b.buflen {
defer b.c.arena.mark()() b.bufptr = b.c.realloc(b.bufptr, uint64(want))
ptr := b.c.arena.new(uint64(want)) b.buflen = want
}
for want > 0 { for want > 0 {
r := b.c.call("sqlite3_blob_read", uint64(b.handle), r := b.c.call("sqlite3_blob_read", uint64(b.handle),
uint64(ptr), uint64(want), uint64(b.offset)) uint64(b.bufptr), uint64(want), uint64(b.offset))
err = b.c.error(r) err = b.c.error(r)
if err != nil { if err != nil {
return n, err return n, err
} }
mem := util.View(b.c.mod, ptr, uint64(want)) mem := util.View(b.c.mod, b.bufptr, uint64(want))
m, err := w.Write(mem[:want]) m, err := w.Write(mem[:want])
b.offset += int64(m) b.offset += int64(m)
n += int64(m) n += int64(m)
@ -159,11 +163,15 @@ func (b *Blob) WriteTo(w io.Writer) (n int64, err error) {
// //
// https://sqlite.org/c3ref/blob_write.html // https://sqlite.org/c3ref/blob_write.html
func (b *Blob) Write(p []byte) (n int, err error) { func (b *Blob) Write(p []byte) (n int, err error) {
defer b.c.arena.mark()() want := int64(len(p))
ptr := b.c.arena.bytes(p) if want > b.buflen {
b.bufptr = b.c.realloc(b.bufptr, uint64(want))
b.buflen = want
}
util.WriteBytes(b.c.mod, b.bufptr, p)
r := b.c.call("sqlite3_blob_write", uint64(b.handle), r := b.c.call("sqlite3_blob_write", uint64(b.handle),
uint64(ptr), uint64(len(p)), uint64(b.offset)) uint64(b.bufptr), uint64(want), uint64(b.offset))
err = b.c.error(r) err = b.c.error(r)
if err != nil { if err != nil {
return 0, err return 0, err
@ -187,16 +195,17 @@ func (b *Blob) ReadFrom(r io.Reader) (n int64, err error) {
if want < 1 { if want < 1 {
want = 1 want = 1
} }
if want > b.buflen {
defer b.c.arena.mark()() b.bufptr = b.c.realloc(b.bufptr, uint64(want))
ptr := b.c.arena.new(uint64(want)) b.buflen = want
}
for { for {
mem := util.View(b.c.mod, ptr, uint64(want)) mem := util.View(b.c.mod, b.bufptr, uint64(want))
m, err := r.Read(mem[:want]) m, err := r.Read(mem[:want])
if m > 0 { if m > 0 {
r := b.c.call("sqlite3_blob_write", uint64(b.handle), r := b.c.call("sqlite3_blob_write", uint64(b.handle),
uint64(ptr), uint64(m), uint64(b.offset)) uint64(b.bufptr), uint64(m), uint64(b.offset))
err := b.c.error(r) err := b.c.error(r)
if err != nil { if err != nil {
return n, err return n, err

View file

@ -294,3 +294,17 @@ func autoVacuumCallback(ctx context.Context, mod api.Module, pApp, zSchema, nDbP
schema := util.ReadString(mod, zSchema, _MAX_NAME) schema := util.ReadString(mod, zSchema, _MAX_NAME)
return uint32(fn(schema, uint(nDbPage), uint(nFreePage), uint(nBytePerPage))) return uint32(fn(schema, uint(nDbPage), uint(nFreePage), uint(nBytePerPage)))
} }
// SoftHeapLimit imposes a soft limit on heap size.
//
// https://sqlite.org/c3ref/hard_heap_limit64.html
func (c *Conn) SoftHeapLimit(n int64) int64 {
return int64(c.call("sqlite3_soft_heap_limit64", uint64(n)))
}
// SoftHeapLimit imposes a hard limit on heap size.
//
// https://sqlite.org/c3ref/hard_heap_limit64.html
func (c *Conn) HardHeapLimit(n int64) int64 {
return int64(c.call("sqlite3_hard_heap_limit64", uint64(n)))
}

View file

@ -23,6 +23,7 @@ type Conn struct {
interrupt context.Context interrupt context.Context
pending *Stmt pending *Stmt
stmts []*Stmt stmts []*Stmt
timer *time.Timer
busy func(int) bool busy func(int) bool
log func(xErrorCode, string) log func(xErrorCode, string)
collation func(*Conn, string) collation func(*Conn, string)
@ -389,11 +390,25 @@ func timeoutCallback(ctx context.Context, mod api.Module, pDB uint32, count, tmo
} }
if delay = min(delay, tmout-prior); delay > 0 { if delay = min(delay, tmout-prior); delay > 0 {
time.Sleep(time.Duration(delay) * time.Millisecond) delay := time.Duration(delay) * time.Millisecond
retry = 1 if c.interrupt == nil || c.interrupt.Done() == nil {
time.Sleep(delay)
return 1
}
if c.timer == nil {
c.timer = time.NewTimer(delay)
} else {
c.timer.Reset(delay)
}
select {
case <-c.interrupt.Done():
c.timer.Stop()
case <-c.timer.C:
return 1
} }
} }
return retry }
return 0
} }
// BusyHandler registers a callback to handle [BUSY] errors. // BusyHandler registers a callback to handle [BUSY] errors.
@ -492,9 +507,7 @@ func (c *Conn) stmtsIter(yield func(*Stmt) bool) {
// DriverConn is implemented by the SQLite [database/sql] driver connection. // DriverConn is implemented by the SQLite [database/sql] driver connection.
// //
// It can be used to access SQLite features like [online backup]. // Deprecated: use [github.com/ncruces/go-sqlite3/driver.Conn] instead.
//
// [online backup]: https://sqlite.org/backup.html
type DriverConn interface { type DriverConn interface {
Raw() *Conn Raw() *Conn
} }

View file

@ -7,12 +7,9 @@ const (
_ROW = 100 /* sqlite3_step() has another row ready */ _ROW = 100 /* sqlite3_step() has another row ready */
_DONE = 101 /* sqlite3_step() has finished executing */ _DONE = 101 /* sqlite3_step() has finished executing */
_UTF8 = 1
_MAX_NAME = 1e6 // Self-imposed limit for most NUL terminated strings. _MAX_NAME = 1e6 // Self-imposed limit for most NUL terminated strings.
_MAX_LENGTH = 1e9 _MAX_LENGTH = 1e9
_MAX_SQL_LENGTH = 1e9 _MAX_SQL_LENGTH = 1e9
_MAX_ALLOCATION_SIZE = 0x7ffffeff
_MAX_FUNCTION_ARG = 100 _MAX_FUNCTION_ARG = 100
ptrlen = 4 ptrlen = 4

View file

@ -84,9 +84,8 @@ func (ctx Context) ResultFloat(value float64) {
// https://sqlite.org/c3ref/result_blob.html // https://sqlite.org/c3ref/result_blob.html
func (ctx Context) ResultText(value string) { func (ctx Context) ResultText(value string) {
ptr := ctx.c.newString(value) ptr := ctx.c.newString(value)
ctx.c.call("sqlite3_result_text64", ctx.c.call("sqlite3_result_text_go",
uint64(ctx.handle), uint64(ptr), uint64(len(value)), uint64(ctx.handle), uint64(ptr), uint64(len(value)))
uint64(ctx.c.freer), _UTF8)
} }
// ResultRawText sets the text result of the function to a []byte. // ResultRawText sets the text result of the function to a []byte.
@ -94,9 +93,8 @@ func (ctx Context) ResultText(value string) {
// https://sqlite.org/c3ref/result_blob.html // https://sqlite.org/c3ref/result_blob.html
func (ctx Context) ResultRawText(value []byte) { func (ctx Context) ResultRawText(value []byte) {
ptr := ctx.c.newBytes(value) ptr := ctx.c.newBytes(value)
ctx.c.call("sqlite3_result_text64", ctx.c.call("sqlite3_result_text_go",
uint64(ctx.handle), uint64(ptr), uint64(len(value)), uint64(ctx.handle), uint64(ptr), uint64(len(value)))
uint64(ctx.c.freer), _UTF8)
} }
// ResultBlob sets the result of the function to a []byte. // ResultBlob sets the result of the function to a []byte.
@ -105,9 +103,8 @@ func (ctx Context) ResultRawText(value []byte) {
// https://sqlite.org/c3ref/result_blob.html // https://sqlite.org/c3ref/result_blob.html
func (ctx Context) ResultBlob(value []byte) { func (ctx Context) ResultBlob(value []byte) {
ptr := ctx.c.newBytes(value) ptr := ctx.c.newBytes(value)
ctx.c.call("sqlite3_result_blob64", ctx.c.call("sqlite3_result_blob_go",
uint64(ctx.handle), uint64(ptr), uint64(len(value)), uint64(ctx.handle), uint64(ptr), uint64(len(value)))
uint64(ctx.c.freer))
} }
// ResultZeroBlob sets the result of the function to a zero-filled, length n BLOB. // ResultZeroBlob sets the result of the function to a zero-filled, length n BLOB.
@ -154,9 +151,8 @@ func (ctx Context) resultRFC3339Nano(value time.Time) {
buf := util.View(ctx.c.mod, ptr, maxlen) buf := util.View(ctx.c.mod, ptr, maxlen)
buf = value.AppendFormat(buf[:0], time.RFC3339Nano) buf = value.AppendFormat(buf[:0], time.RFC3339Nano)
ctx.c.call("sqlite3_result_text64", ctx.c.call("sqlite3_result_text_go",
uint64(ctx.handle), uint64(ptr), uint64(len(buf)), uint64(ctx.handle), uint64(ptr), uint64(len(buf)))
uint64(ctx.c.freer), _UTF8)
} }
// ResultPointer sets the result of the function to NULL, just like [Context.ResultNull], // ResultPointer sets the result of the function to NULL, just like [Context.ResultNull],

View file

@ -130,6 +130,11 @@ type SQLite struct {
term func(*sqlite3.Conn) error term func(*sqlite3.Conn) error
} }
var (
// Ensure these interfaces are implemented:
_ driver.DriverContext = &SQLite{}
)
// Open implements [database/sql/driver.Driver]. // Open implements [database/sql/driver.Driver].
func (d *SQLite) Open(name string) (driver.Conn, error) { func (d *SQLite) Open(name string) (driver.Conn, error) {
c, err := d.newConnector(name) c, err := d.newConnector(name)
@ -255,6 +260,35 @@ func (n *connector) Connect(ctx context.Context) (_ driver.Conn, err error) {
return c, nil return c, nil
} }
// Conn is implemented by the SQLite [database/sql] driver connections.
//
// It can be used to access SQLite features like [online backup]:
//
// db, err := driver.Open("temp.db")
// if err != nil {
// log.Fatal(err)
// }
// defer db.Close()
//
// conn, err := db.Conn(context.TODO())
// if err != nil {
// log.Fatal(err)
// }
//
// err = conn.Raw(func(driverConn any) error {
// conn := driverConn.(driver.Conn)
// return conn.Raw().Backup("main", "backup.db")
// })
// if err != nil {
// log.Fatal(err)
// }
//
// [online backup]: https://sqlite.org/backup.html
type Conn interface {
Raw() *sqlite3.Conn
driver.Conn
}
type conn struct { type conn struct {
*sqlite3.Conn *sqlite3.Conn
txLock string txLock string
@ -266,10 +300,10 @@ type conn struct {
var ( var (
// Ensure these interfaces are implemented: // Ensure these interfaces are implemented:
_ Conn = &conn{}
_ driver.ConnBeginTx = &conn{}
_ driver.ConnPrepareContext = &conn{} _ driver.ConnPrepareContext = &conn{}
_ driver.ExecerContext = &conn{} _ driver.ExecerContext = &conn{}
_ driver.ConnBeginTx = &conn{}
_ sqlite3.DriverConn = &conn{}
) )
func (c *conn) Raw() *sqlite3.Conn { func (c *conn) Raw() *sqlite3.Conn {

View file

@ -1,7 +1,4 @@
aligned_alloc aligned_alloc
free
malloc
malloc_destructor
sqlite3_anycollseq_init sqlite3_anycollseq_init
sqlite3_autovacuum_pages_go sqlite3_autovacuum_pages_go
sqlite3_backup_finish sqlite3_backup_finish
@ -9,7 +6,7 @@ sqlite3_backup_init
sqlite3_backup_pagecount sqlite3_backup_pagecount
sqlite3_backup_remaining sqlite3_backup_remaining
sqlite3_backup_step sqlite3_backup_step
sqlite3_bind_blob64 sqlite3_bind_blob_go
sqlite3_bind_double sqlite3_bind_double
sqlite3_bind_int64 sqlite3_bind_int64
sqlite3_bind_null sqlite3_bind_null
@ -17,7 +14,7 @@ sqlite3_bind_parameter_count
sqlite3_bind_parameter_index sqlite3_bind_parameter_index
sqlite3_bind_parameter_name sqlite3_bind_parameter_name
sqlite3_bind_pointer_go sqlite3_bind_pointer_go
sqlite3_bind_text64 sqlite3_bind_text_go
sqlite3_bind_value sqlite3_bind_value
sqlite3_bind_zeroblob64 sqlite3_bind_zeroblob64
sqlite3_blob_bytes sqlite3_blob_bytes
@ -74,17 +71,21 @@ sqlite3_filename_database
sqlite3_filename_journal sqlite3_filename_journal
sqlite3_filename_wal sqlite3_filename_wal
sqlite3_finalize sqlite3_finalize
sqlite3_free
sqlite3_get_autocommit sqlite3_get_autocommit
sqlite3_get_auxdata sqlite3_get_auxdata
sqlite3_hard_heap_limit64
sqlite3_interrupt sqlite3_interrupt
sqlite3_last_insert_rowid sqlite3_last_insert_rowid
sqlite3_limit sqlite3_limit
sqlite3_malloc64
sqlite3_open_v2 sqlite3_open_v2
sqlite3_overload_function sqlite3_overload_function
sqlite3_prepare_v3 sqlite3_prepare_v3
sqlite3_progress_handler_go sqlite3_progress_handler_go
sqlite3_realloc64
sqlite3_reset sqlite3_reset
sqlite3_result_blob64 sqlite3_result_blob_go
sqlite3_result_double sqlite3_result_double
sqlite3_result_error sqlite3_result_error
sqlite3_result_error_code sqlite3_result_error_code
@ -93,13 +94,14 @@ sqlite3_result_error_toobig
sqlite3_result_int64 sqlite3_result_int64
sqlite3_result_null sqlite3_result_null
sqlite3_result_pointer_go sqlite3_result_pointer_go
sqlite3_result_text64 sqlite3_result_text_go
sqlite3_result_value sqlite3_result_value
sqlite3_result_zeroblob64 sqlite3_result_zeroblob64
sqlite3_rollback_hook_go sqlite3_rollback_hook_go
sqlite3_set_authorizer_go sqlite3_set_authorizer_go
sqlite3_set_auxdata_go sqlite3_set_auxdata_go
sqlite3_set_last_insert_rowid sqlite3_set_last_insert_rowid
sqlite3_soft_heap_limit64
sqlite3_step sqlite3_step
sqlite3_stmt_busy sqlite3_stmt_busy
sqlite3_stmt_readonly sqlite3_stmt_readonly

Binary file not shown.

View file

@ -5,5 +5,6 @@ golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0= golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0=
golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4= golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4=
golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk= golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk=
golang.org/x/term v0.24.0/go.mod h1:lOBK/LVxemqiMij05LGJ0tzNr8xlmwBRJ81PX6wVLH8=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=

View file

@ -39,7 +39,7 @@ type mmappedMemory struct {
func (m *mmappedMemory) Reallocate(size uint64) []byte { func (m *mmappedMemory) Reallocate(size uint64) []byte {
com := uint64(len(m.buf)) com := uint64(len(m.buf))
res := uint64(cap(m.buf)) res := uint64(cap(m.buf))
if com < size && size < res { if com < size && size <= res {
// Round up to the page size. // Round up to the page size.
rnd := uint64(unix.Getpagesize() - 1) rnd := uint64(unix.Getpagesize() - 1)
new := (size + rnd) &^ rnd new := (size + rnd) &^ rnd

View file

@ -48,7 +48,7 @@ type virtualMemory struct {
func (m *virtualMemory) Reallocate(size uint64) []byte { func (m *virtualMemory) Reallocate(size uint64) []byte {
com := uint64(len(m.buf)) com := uint64(len(m.buf))
res := uint64(cap(m.buf)) res := uint64(cap(m.buf))
if com < size && size < res { if com < size && size <= res {
// Round up to the page size. // Round up to the page size.
rnd := uint64(windows.Getpagesize() - 1) rnd := uint64(windows.Getpagesize() - 1)
new := (size + rnd) &^ rnd new := (size + rnd) &^ rnd

View file

@ -86,7 +86,6 @@ type sqlite struct {
mask uint32 mask uint32
} }
stack [9]uint64 stack [9]uint64
freer uint32
} }
func instantiateSQLite() (sqlt *sqlite, err error) { func instantiateSQLite() (sqlt *sqlite, err error) {
@ -102,14 +101,7 @@ func instantiateSQLite() (sqlt *sqlite, err error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
if sqlt.getfn("sqlite3_progress_handler_go") == nil {
global := sqlt.mod.ExportedGlobal("malloc_destructor")
if global == nil {
return nil, util.BadBinaryErr
}
sqlt.freer = util.ReadUint32(sqlt.mod, uint32(global.Get()))
if sqlt.freer == 0 {
return nil, util.BadBinaryErr return nil, util.BadBinaryErr
} }
return sqlt, nil return sqlt, nil
@ -196,14 +188,19 @@ func (sqlt *sqlite) free(ptr uint32) {
if ptr == 0 { if ptr == 0 {
return return
} }
sqlt.call("free", uint64(ptr)) sqlt.call("sqlite3_free", uint64(ptr))
} }
func (sqlt *sqlite) new(size uint64) uint32 { func (sqlt *sqlite) new(size uint64) uint32 {
if size > _MAX_ALLOCATION_SIZE { ptr := uint32(sqlt.call("sqlite3_malloc64", size))
if ptr == 0 && size != 0 {
panic(util.OOMErr) panic(util.OOMErr)
} }
ptr := uint32(sqlt.call("malloc", size)) return ptr
}
func (sqlt *sqlite) realloc(ptr uint32, size uint64) uint32 {
ptr = uint32(sqlt.call("sqlite3_realloc64", uint64(ptr), size))
if ptr == 0 && size != 0 { if ptr == 0 && size != 0 {
panic(util.OOMErr) panic(util.OOMErr)
} }
@ -214,7 +211,11 @@ func (sqlt *sqlite) newBytes(b []byte) uint32 {
if (*[0]byte)(b) == nil { if (*[0]byte)(b) == nil {
return 0 return 0
} }
ptr := sqlt.new(uint64(len(b))) size := len(b)
if size == 0 {
size = 1
}
ptr := sqlt.new(uint64(size))
util.WriteBytes(sqlt.mod, ptr, b) util.WriteBytes(sqlt.mod, ptr, b)
return ptr return ptr
} }

View file

@ -246,10 +246,9 @@ func (s *Stmt) BindText(param int, value string) error {
return TOOBIG return TOOBIG
} }
ptr := s.c.newString(value) ptr := s.c.newString(value)
r := s.c.call("sqlite3_bind_text64", r := s.c.call("sqlite3_bind_text_go",
uint64(s.handle), uint64(param), uint64(s.handle), uint64(param),
uint64(ptr), uint64(len(value)), uint64(ptr), uint64(len(value)))
uint64(s.c.freer), _UTF8)
return s.c.error(r) return s.c.error(r)
} }
@ -262,10 +261,9 @@ func (s *Stmt) BindRawText(param int, value []byte) error {
return TOOBIG return TOOBIG
} }
ptr := s.c.newBytes(value) ptr := s.c.newBytes(value)
r := s.c.call("sqlite3_bind_text64", r := s.c.call("sqlite3_bind_text_go",
uint64(s.handle), uint64(param), uint64(s.handle), uint64(param),
uint64(ptr), uint64(len(value)), uint64(ptr), uint64(len(value)))
uint64(s.c.freer), _UTF8)
return s.c.error(r) return s.c.error(r)
} }
@ -279,10 +277,9 @@ func (s *Stmt) BindBlob(param int, value []byte) error {
return TOOBIG return TOOBIG
} }
ptr := s.c.newBytes(value) ptr := s.c.newBytes(value)
r := s.c.call("sqlite3_bind_blob64", r := s.c.call("sqlite3_bind_blob_go",
uint64(s.handle), uint64(param), uint64(s.handle), uint64(param),
uint64(ptr), uint64(len(value)), uint64(ptr), uint64(len(value)))
uint64(s.c.freer))
return s.c.error(r) return s.c.error(r)
} }
@ -335,10 +332,9 @@ func (s *Stmt) bindRFC3339Nano(param int, value time.Time) error {
buf := util.View(s.c.mod, ptr, maxlen) buf := util.View(s.c.mod, ptr, maxlen)
buf = value.AppendFormat(buf[:0], time.RFC3339Nano) buf = value.AppendFormat(buf[:0], time.RFC3339Nano)
r := s.c.call("sqlite3_bind_text64", r := s.c.call("sqlite3_bind_text_go",
uint64(s.handle), uint64(param), uint64(s.handle), uint64(param),
uint64(ptr), uint64(len(buf)), uint64(ptr), uint64(len(buf)))
uint64(s.c.freer), _UTF8)
return s.c.error(r) return s.c.error(r)
} }

View file

@ -79,16 +79,15 @@ func (f *vfsFile) Lock(lock LockLevel) error {
// A PENDING lock is needed before acquiring an EXCLUSIVE lock. // A PENDING lock is needed before acquiring an EXCLUSIVE lock.
if f.lock < LOCK_PENDING { if f.lock < LOCK_PENDING {
// If we're already RESERVED, we can block indefinitely, // If we're already RESERVED, we can block indefinitely,
// since only new readers may briefly hold the PENDING lock. // since only incoming readers may briefly hold the PENDING lock.
if rc := osGetPendingLock(f.File, reserved /* block */); rc != _OK { if rc := osGetPendingLock(f.File, reserved /* block */); rc != _OK {
return rc return rc
} }
f.lock = LOCK_PENDING f.lock = LOCK_PENDING
} }
// We already have PENDING, so we're just waiting for readers to leave. // We are now PENDING, so we're just waiting for readers to leave.
// If we were RESERVED, we can wait for a little while, before invoking // If we were RESERVED, we can block for a bit before invoking the busy handler.
// the busy handler; we will only do this once. if rc := osGetExclusiveLock(f.File, reserved /* block */); rc != _OK {
if rc := osGetExclusiveLock(f.File, reserved /* wait */); rc != _OK {
return rc return rc
} }
f.lock = LOCK_EXCLUSIVE f.lock = LOCK_EXCLUSIVE

View file

@ -53,10 +53,11 @@ func osLock(file *os.File, typ int16, start, len int64, timeout time.Duration, d
if errno, _ := err.(unix.Errno); errno != unix.EAGAIN { if errno, _ := err.(unix.Errno); errno != unix.EAGAIN {
break break
} }
if timeout < time.Since(before) { if time.Since(before) > timeout {
break break
} }
time.Sleep(time.Duration(rand.Int63n(int64(time.Millisecond)))) const sleepIncrement = 1024*1024 - 1 // power of two, ~1ms
time.Sleep(time.Duration(rand.Int63() & sleepIncrement))
} }
} }
return osLockErrorCode(err, def) return osLockErrorCode(err, def)

View file

@ -32,9 +32,9 @@ func osGetPendingLock(file *os.File, block bool) _ErrorCode {
return osWriteLock(file, _PENDING_BYTE, 1, timeout) return osWriteLock(file, _PENDING_BYTE, 1, timeout)
} }
func osGetExclusiveLock(file *os.File, wait bool) _ErrorCode { func osGetExclusiveLock(file *os.File, block bool) _ErrorCode {
var timeout time.Duration var timeout time.Duration
if wait { if block {
timeout = time.Millisecond timeout = time.Millisecond
} }
// Acquire the EXCLUSIVE lock. // Acquire the EXCLUSIVE lock.

View file

@ -38,9 +38,9 @@ func osGetPendingLock(file *os.File, block bool) _ErrorCode {
return osWriteLock(file, _PENDING_BYTE, 1, timeout) return osWriteLock(file, _PENDING_BYTE, 1, timeout)
} }
func osGetExclusiveLock(file *os.File, wait bool) _ErrorCode { func osGetExclusiveLock(file *os.File, block bool) _ErrorCode {
var timeout time.Duration var timeout time.Duration
if wait { if block {
timeout = time.Millisecond timeout = time.Millisecond
} }
@ -134,10 +134,11 @@ func osLock(file *os.File, flags, start, len uint32, timeout time.Duration, def
if errno, _ := err.(windows.Errno); errno != windows.ERROR_LOCK_VIOLATION { if errno, _ := err.(windows.Errno); errno != windows.ERROR_LOCK_VIOLATION {
break break
} }
if timeout < time.Since(before) { if time.Since(before) > timeout {
break break
} }
time.Sleep(time.Duration(rand.Int63n(int64(time.Millisecond)))) const sleepIncrement = 1024*1024 - 1 // power of two, ~1ms
time.Sleep(time.Duration(rand.Int63() & sleepIncrement))
} }
} }
return osLockErrorCode(err, def) return osLockErrorCode(err, def)

View file

@ -6,6 +6,7 @@ import (
"context" "context"
"io" "io"
"os" "os"
"time"
"github.com/ncruces/go-sqlite3/internal/util" "github.com/ncruces/go-sqlite3/internal/util"
"github.com/tetratelabs/wazero/api" "github.com/tetratelabs/wazero/api"
@ -49,6 +50,7 @@ type vfsShm struct {
path string path string
regions []*util.MappedRegion regions []*util.MappedRegion
readOnly bool readOnly bool
blocking bool
} }
func (s *vfsShm) shmOpen() _ErrorCode { func (s *vfsShm) shmOpen() _ErrorCode {
@ -76,6 +78,13 @@ func (s *vfsShm) shmOpen() _ErrorCode {
if s.readOnly { if s.readOnly {
return _READONLY_CANTINIT return _READONLY_CANTINIT
} }
// Do not use a blocking lock here.
// If the lock cannot be obtained immediately,
// it means some other connection is truncating the file.
// And after it has done so, it will not release its lock,
// but only downgrade it to a shared lock.
// So no point in blocking here.
// The call below to obtain the shared DMS lock may use a blocking lock.
if rc := osWriteLock(s.File, _SHM_DMS, 1, 0); rc != _OK { if rc := osWriteLock(s.File, _SHM_DMS, 1, 0); rc != _OK {
return rc return rc
} }
@ -83,7 +92,7 @@ func (s *vfsShm) shmOpen() _ErrorCode {
return _IOERR_SHMOPEN return _IOERR_SHMOPEN
} }
} }
if rc := osReadLock(s.File, _SHM_DMS, 1, 0); rc != _OK { if rc := osReadLock(s.File, _SHM_DMS, 1, time.Millisecond); rc != _OK {
return rc return rc
} }
return _OK return _OK
@ -150,13 +159,18 @@ func (s *vfsShm) shmLock(offset, n int32, flags _ShmFlag) _ErrorCode {
panic(util.AssertErr()) panic(util.AssertErr())
} }
var timeout time.Duration
if s.blocking {
timeout = time.Millisecond
}
switch { switch {
case flags&_SHM_UNLOCK != 0: case flags&_SHM_UNLOCK != 0:
return osUnlock(s.File, _SHM_BASE+int64(offset), int64(n)) return osUnlock(s.File, _SHM_BASE+int64(offset), int64(n))
case flags&_SHM_SHARED != 0: case flags&_SHM_SHARED != 0:
return osReadLock(s.File, _SHM_BASE+int64(offset), int64(n), 0) return osReadLock(s.File, _SHM_BASE+int64(offset), int64(n), timeout)
case flags&_SHM_EXCLUSIVE != 0: case flags&_SHM_EXCLUSIVE != 0:
return osWriteLock(s.File, _SHM_BASE+int64(offset), int64(n), 0) return osWriteLock(s.File, _SHM_BASE+int64(offset), int64(n), timeout)
default: default:
panic(util.AssertErr()) panic(util.AssertErr())
} }
@ -181,3 +195,7 @@ func (s *vfsShm) shmUnmap(delete bool) {
s.Close() s.Close()
s.File = nil s.File = nil
} }
func (s *vfsShm) shmEnableBlocking(block bool) {
s.blocking = block
}

View file

@ -243,6 +243,15 @@ func vfsFileControl(ctx context.Context, mod api.Module, pFile uint32, op _Fcntl
return _OK return _OK
} }
case _FCNTL_LOCK_TIMEOUT:
if file, ok := file.(FileSharedMemory); ok {
if iface, ok := file.SharedMemory().(interface{ shmEnableBlocking(bool) }); ok {
if i := util.ReadUint32(mod, pArg); i == 0 || i == 1 {
iface.shmEnableBlocking(i != 0)
}
}
}
case _FCNTL_PERSIST_WAL: case _FCNTL_PERSIST_WAL:
if file, ok := file.(FilePersistentWAL); ok { if file, ok := file.(FilePersistentWAL); ok {
if i := util.ReadUint32(mod, pArg); int32(i) >= 0 { if i := util.ReadUint32(mod, pArg); int32(i) >= 0 {
@ -347,7 +356,7 @@ func vfsFileControl(ctx context.Context, mod api.Module, pFile uint32, op _Fcntl
out = err.Error() out = err.Error()
} }
if out != "" { if out != "" {
fn := mod.ExportedFunction("malloc") fn := mod.ExportedFunction("sqlite3_malloc64")
stack := [...]uint64{uint64(len(out) + 1)} stack := [...]uint64{uint64(len(out) + 1)}
if err := fn.CallWithStack(ctx, stack[:]); err != nil { if err := fn.CallWithStack(ctx, stack[:]); err != nil {
panic(err) panic(err)

2
vendor/modules.txt vendored
View file

@ -523,7 +523,7 @@ github.com/modern-go/reflect2
# github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 # github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822
## explicit ## explicit
github.com/munnerz/goautoneg github.com/munnerz/goautoneg
# github.com/ncruces/go-sqlite3 v0.18.1 # github.com/ncruces/go-sqlite3 v0.18.3
## explicit; go 1.21 ## explicit; go 1.21
github.com/ncruces/go-sqlite3 github.com/ncruces/go-sqlite3
github.com/ncruces/go-sqlite3/driver github.com/ncruces/go-sqlite3/driver