diff --git a/go.mod b/go.mod index 2a9545ff8..624486a41 100644 --- a/go.mod +++ b/go.mod @@ -44,7 +44,7 @@ require ( github.com/miekg/dns v1.1.62 github.com/minio/minio-go/v7 v7.0.76 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/prometheus/client_golang v1.20.3 github.com/spf13/cobra v1.8.1 diff --git a/go.sum b/go.sum index c8f54dd72..d0826e1b9 100644 --- a/go.sum +++ b/go.sum @@ -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/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/ncruces/go-sqlite3 v0.18.1 h1:iN8IMZV5EMxpH88NUac9vId23eTKNFUhP7jgY0EBbNc= -github.com/ncruces/go-sqlite3 v0.18.1/go.mod h1:eEOyZnW1dGTJ+zDpMuzfYamEUBtdFz5zeYhqLBtHxvM= +github.com/ncruces/go-sqlite3 v0.18.3 h1:tyMa75uh7LcINcfo0WrzOvcTkfz8Hqu0TEPX+KVyes4= +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/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= github.com/ncruces/julianday v1.0.0 h1:fH0OKwa7NWvniGQtxdJRxAgkBMolni2BjDHaWTxqt7M= diff --git a/internal/db/sqlite/driver.go b/internal/db/sqlite/driver.go index cea976d94..ca8ccf8ca 100644 --- a/internal/db/sqlite/driver.go +++ b/internal/db/sqlite/driver.go @@ -25,7 +25,6 @@ "github.com/superseriousbusiness/gotosocial/internal/db" - "github.com/ncruces/go-sqlite3" sqlite3driver "github.com/ncruces/go-sqlite3/driver" _ "github.com/ncruces/go-sqlite3/embed" // embed wasm binary _ "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) { // 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 const onClose = "PRAGMA analysis_limit=1000; PRAGMA optimize;" diff --git a/vendor/github.com/ncruces/go-sqlite3/blob.go b/vendor/github.com/ncruces/go-sqlite3/blob.go index 6e4f7d914..010948e88 100644 --- a/vendor/github.com/ncruces/go-sqlite3/blob.go +++ b/vendor/github.com/ncruces/go-sqlite3/blob.go @@ -21,6 +21,8 @@ type Blob struct { bytes int64 offset int64 handle uint32 + bufptr uint32 + buflen int64 } var _ io.ReadWriteSeeker = &Blob{} @@ -66,7 +68,7 @@ func (b *Blob) Close() error { } r := b.c.call("sqlite3_blob_close", uint64(b.handle)) - + b.c.free(b.bufptr) b.handle = 0 return b.c.error(r) } @@ -86,17 +88,18 @@ func (b *Blob) Read(p []byte) (n int, err error) { return 0, io.EOF } - avail := b.bytes - b.offset want := int64(len(p)) + avail := b.bytes - b.offset if want > avail { want = avail } - - defer b.c.arena.mark()() - ptr := b.c.arena.new(uint64(want)) + if want > b.buflen { + b.bufptr = b.c.realloc(b.bufptr, uint64(want)) + b.buflen = want + } 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) if err != nil { return 0, err @@ -106,7 +109,7 @@ func (b *Blob) Read(p []byte) (n int, err error) { 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 } @@ -123,19 +126,20 @@ func (b *Blob) WriteTo(w io.Writer) (n int64, err error) { if want > avail { want = avail } - - defer b.c.arena.mark()() - ptr := b.c.arena.new(uint64(want)) + if want > b.buflen { + b.bufptr = b.c.realloc(b.bufptr, uint64(want)) + b.buflen = want + } for want > 0 { 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) if err != nil { 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]) b.offset += 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 func (b *Blob) Write(p []byte) (n int, err error) { - defer b.c.arena.mark()() - ptr := b.c.arena.bytes(p) + want := int64(len(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), - uint64(ptr), uint64(len(p)), uint64(b.offset)) + uint64(b.bufptr), uint64(want), uint64(b.offset)) err = b.c.error(r) if err != nil { return 0, err @@ -187,16 +195,17 @@ func (b *Blob) ReadFrom(r io.Reader) (n int64, err error) { if want < 1 { want = 1 } - - defer b.c.arena.mark()() - ptr := b.c.arena.new(uint64(want)) + if want > b.buflen { + b.bufptr = b.c.realloc(b.bufptr, uint64(want)) + b.buflen = want + } 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]) if m > 0 { 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) if err != nil { return n, err diff --git a/vendor/github.com/ncruces/go-sqlite3/config.go b/vendor/github.com/ncruces/go-sqlite3/config.go index 3f60b8fe9..c1154b2f5 100644 --- a/vendor/github.com/ncruces/go-sqlite3/config.go +++ b/vendor/github.com/ncruces/go-sqlite3/config.go @@ -294,3 +294,17 @@ func autoVacuumCallback(ctx context.Context, mod api.Module, pApp, zSchema, nDbP schema := util.ReadString(mod, zSchema, _MAX_NAME) 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))) +} diff --git a/vendor/github.com/ncruces/go-sqlite3/conn.go b/vendor/github.com/ncruces/go-sqlite3/conn.go index b4335f4c4..79b1dba9c 100644 --- a/vendor/github.com/ncruces/go-sqlite3/conn.go +++ b/vendor/github.com/ncruces/go-sqlite3/conn.go @@ -23,6 +23,7 @@ type Conn struct { interrupt context.Context pending *Stmt stmts []*Stmt + timer *time.Timer busy func(int) bool log func(xErrorCode, 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 { - time.Sleep(time.Duration(delay) * time.Millisecond) - retry = 1 + delay := time.Duration(delay) * time.Millisecond + 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. @@ -492,9 +507,7 @@ func (c *Conn) stmtsIter(yield func(*Stmt) bool) { // DriverConn is implemented by the SQLite [database/sql] driver connection. // -// It can be used to access SQLite features like [online backup]. -// -// [online backup]: https://sqlite.org/backup.html +// Deprecated: use [github.com/ncruces/go-sqlite3/driver.Conn] instead. type DriverConn interface { Raw() *Conn } diff --git a/vendor/github.com/ncruces/go-sqlite3/const.go b/vendor/github.com/ncruces/go-sqlite3/const.go index 11eb33c88..e4c7d728e 100644 --- a/vendor/github.com/ncruces/go-sqlite3/const.go +++ b/vendor/github.com/ncruces/go-sqlite3/const.go @@ -7,13 +7,10 @@ _ROW = 100 /* sqlite3_step() has another row ready */ _DONE = 101 /* sqlite3_step() has finished executing */ - _UTF8 = 1 - - _MAX_NAME = 1e6 // Self-imposed limit for most NUL terminated strings. - _MAX_LENGTH = 1e9 - _MAX_SQL_LENGTH = 1e9 - _MAX_ALLOCATION_SIZE = 0x7ffffeff - _MAX_FUNCTION_ARG = 100 + _MAX_NAME = 1e6 // Self-imposed limit for most NUL terminated strings. + _MAX_LENGTH = 1e9 + _MAX_SQL_LENGTH = 1e9 + _MAX_FUNCTION_ARG = 100 ptrlen = 4 ) diff --git a/vendor/github.com/ncruces/go-sqlite3/context.go b/vendor/github.com/ncruces/go-sqlite3/context.go index 4fcda56d4..be5dd92c5 100644 --- a/vendor/github.com/ncruces/go-sqlite3/context.go +++ b/vendor/github.com/ncruces/go-sqlite3/context.go @@ -84,9 +84,8 @@ func (ctx Context) ResultFloat(value float64) { // https://sqlite.org/c3ref/result_blob.html func (ctx Context) ResultText(value string) { ptr := ctx.c.newString(value) - ctx.c.call("sqlite3_result_text64", - uint64(ctx.handle), uint64(ptr), uint64(len(value)), - uint64(ctx.c.freer), _UTF8) + ctx.c.call("sqlite3_result_text_go", + uint64(ctx.handle), uint64(ptr), uint64(len(value))) } // 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 func (ctx Context) ResultRawText(value []byte) { ptr := ctx.c.newBytes(value) - ctx.c.call("sqlite3_result_text64", - uint64(ctx.handle), uint64(ptr), uint64(len(value)), - uint64(ctx.c.freer), _UTF8) + ctx.c.call("sqlite3_result_text_go", + uint64(ctx.handle), uint64(ptr), uint64(len(value))) } // 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 func (ctx Context) ResultBlob(value []byte) { ptr := ctx.c.newBytes(value) - ctx.c.call("sqlite3_result_blob64", - uint64(ctx.handle), uint64(ptr), uint64(len(value)), - uint64(ctx.c.freer)) + ctx.c.call("sqlite3_result_blob_go", + uint64(ctx.handle), uint64(ptr), uint64(len(value))) } // 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 = value.AppendFormat(buf[:0], time.RFC3339Nano) - ctx.c.call("sqlite3_result_text64", - uint64(ctx.handle), uint64(ptr), uint64(len(buf)), - uint64(ctx.c.freer), _UTF8) + ctx.c.call("sqlite3_result_text_go", + uint64(ctx.handle), uint64(ptr), uint64(len(buf))) } // ResultPointer sets the result of the function to NULL, just like [Context.ResultNull], diff --git a/vendor/github.com/ncruces/go-sqlite3/driver/driver.go b/vendor/github.com/ncruces/go-sqlite3/driver/driver.go index c02ba4b4f..c6c758ac2 100644 --- a/vendor/github.com/ncruces/go-sqlite3/driver/driver.go +++ b/vendor/github.com/ncruces/go-sqlite3/driver/driver.go @@ -130,6 +130,11 @@ type SQLite struct { term func(*sqlite3.Conn) error } +var ( + // Ensure these interfaces are implemented: + _ driver.DriverContext = &SQLite{} +) + // Open implements [database/sql/driver.Driver]. func (d *SQLite) Open(name string) (driver.Conn, error) { c, err := d.newConnector(name) @@ -255,6 +260,35 @@ func (n *connector) Connect(ctx context.Context) (_ driver.Conn, err error) { 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 { *sqlite3.Conn txLock string @@ -266,10 +300,10 @@ type conn struct { var ( // Ensure these interfaces are implemented: + _ Conn = &conn{} + _ driver.ConnBeginTx = &conn{} _ driver.ConnPrepareContext = &conn{} _ driver.ExecerContext = &conn{} - _ driver.ConnBeginTx = &conn{} - _ sqlite3.DriverConn = &conn{} ) func (c *conn) Raw() *sqlite3.Conn { diff --git a/vendor/github.com/ncruces/go-sqlite3/embed/exports.txt b/vendor/github.com/ncruces/go-sqlite3/embed/exports.txt index e7882cb56..b624ee1f3 100644 --- a/vendor/github.com/ncruces/go-sqlite3/embed/exports.txt +++ b/vendor/github.com/ncruces/go-sqlite3/embed/exports.txt @@ -1,7 +1,4 @@ aligned_alloc -free -malloc -malloc_destructor sqlite3_anycollseq_init sqlite3_autovacuum_pages_go sqlite3_backup_finish @@ -9,7 +6,7 @@ sqlite3_backup_init sqlite3_backup_pagecount sqlite3_backup_remaining sqlite3_backup_step -sqlite3_bind_blob64 +sqlite3_bind_blob_go sqlite3_bind_double sqlite3_bind_int64 sqlite3_bind_null @@ -17,7 +14,7 @@ sqlite3_bind_parameter_count sqlite3_bind_parameter_index sqlite3_bind_parameter_name sqlite3_bind_pointer_go -sqlite3_bind_text64 +sqlite3_bind_text_go sqlite3_bind_value sqlite3_bind_zeroblob64 sqlite3_blob_bytes @@ -74,17 +71,21 @@ sqlite3_filename_database sqlite3_filename_journal sqlite3_filename_wal sqlite3_finalize +sqlite3_free sqlite3_get_autocommit sqlite3_get_auxdata +sqlite3_hard_heap_limit64 sqlite3_interrupt sqlite3_last_insert_rowid sqlite3_limit +sqlite3_malloc64 sqlite3_open_v2 sqlite3_overload_function sqlite3_prepare_v3 sqlite3_progress_handler_go +sqlite3_realloc64 sqlite3_reset -sqlite3_result_blob64 +sqlite3_result_blob_go sqlite3_result_double sqlite3_result_error sqlite3_result_error_code @@ -93,13 +94,14 @@ sqlite3_result_error_toobig sqlite3_result_int64 sqlite3_result_null sqlite3_result_pointer_go -sqlite3_result_text64 +sqlite3_result_text_go sqlite3_result_value sqlite3_result_zeroblob64 sqlite3_rollback_hook_go sqlite3_set_authorizer_go sqlite3_set_auxdata_go sqlite3_set_last_insert_rowid +sqlite3_soft_heap_limit64 sqlite3_step sqlite3_stmt_busy sqlite3_stmt_readonly diff --git a/vendor/github.com/ncruces/go-sqlite3/embed/sqlite3.wasm b/vendor/github.com/ncruces/go-sqlite3/embed/sqlite3.wasm index 5f4b64a9c..6b1805778 100644 Binary files a/vendor/github.com/ncruces/go-sqlite3/embed/sqlite3.wasm and b/vendor/github.com/ncruces/go-sqlite3/embed/sqlite3.wasm differ diff --git a/vendor/github.com/ncruces/go-sqlite3/go.work.sum b/vendor/github.com/ncruces/go-sqlite3/go.work.sum index 76ac94b0d..085f015b8 100644 --- a/vendor/github.com/ncruces/go-sqlite3/go.work.sum +++ b/vendor/github.com/ncruces/go-sqlite3/go.work.sum @@ -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.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4= 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.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= diff --git a/vendor/github.com/ncruces/go-sqlite3/internal/alloc/alloc_unix.go b/vendor/github.com/ncruces/go-sqlite3/internal/alloc/alloc_unix.go index 39a3a38cc..009cc1f0b 100644 --- a/vendor/github.com/ncruces/go-sqlite3/internal/alloc/alloc_unix.go +++ b/vendor/github.com/ncruces/go-sqlite3/internal/alloc/alloc_unix.go @@ -39,7 +39,7 @@ type mmappedMemory struct { func (m *mmappedMemory) Reallocate(size uint64) []byte { com := uint64(len(m.buf)) res := uint64(cap(m.buf)) - if com < size && size < res { + if com < size && size <= res { // Round up to the page size. rnd := uint64(unix.Getpagesize() - 1) new := (size + rnd) &^ rnd diff --git a/vendor/github.com/ncruces/go-sqlite3/internal/alloc/alloc_windows.go b/vendor/github.com/ncruces/go-sqlite3/internal/alloc/alloc_windows.go index 27d875f2e..62d499649 100644 --- a/vendor/github.com/ncruces/go-sqlite3/internal/alloc/alloc_windows.go +++ b/vendor/github.com/ncruces/go-sqlite3/internal/alloc/alloc_windows.go @@ -48,7 +48,7 @@ type virtualMemory struct { func (m *virtualMemory) Reallocate(size uint64) []byte { com := uint64(len(m.buf)) res := uint64(cap(m.buf)) - if com < size && size < res { + if com < size && size <= res { // Round up to the page size. rnd := uint64(windows.Getpagesize() - 1) new := (size + rnd) &^ rnd diff --git a/vendor/github.com/ncruces/go-sqlite3/sqlite.go b/vendor/github.com/ncruces/go-sqlite3/sqlite.go index 712ad5160..b8c06d618 100644 --- a/vendor/github.com/ncruces/go-sqlite3/sqlite.go +++ b/vendor/github.com/ncruces/go-sqlite3/sqlite.go @@ -86,7 +86,6 @@ funcs struct { mask uint32 } stack [9]uint64 - freer uint32 } func instantiateSQLite() (sqlt *sqlite, err error) { @@ -102,14 +101,7 @@ func instantiateSQLite() (sqlt *sqlite, err error) { if err != nil { return nil, err } - - 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 { + if sqlt.getfn("sqlite3_progress_handler_go") == nil { return nil, util.BadBinaryErr } return sqlt, nil @@ -196,14 +188,19 @@ func (sqlt *sqlite) free(ptr uint32) { if ptr == 0 { return } - sqlt.call("free", uint64(ptr)) + sqlt.call("sqlite3_free", uint64(ptr)) } 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) } - 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 { panic(util.OOMErr) } @@ -214,7 +211,11 @@ func (sqlt *sqlite) newBytes(b []byte) uint32 { if (*[0]byte)(b) == nil { 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) return ptr } diff --git a/vendor/github.com/ncruces/go-sqlite3/stmt.go b/vendor/github.com/ncruces/go-sqlite3/stmt.go index 8e6ad2c10..e5d72465a 100644 --- a/vendor/github.com/ncruces/go-sqlite3/stmt.go +++ b/vendor/github.com/ncruces/go-sqlite3/stmt.go @@ -246,10 +246,9 @@ func (s *Stmt) BindText(param int, value string) error { return TOOBIG } 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(ptr), uint64(len(value)), - uint64(s.c.freer), _UTF8) + uint64(ptr), uint64(len(value))) return s.c.error(r) } @@ -262,10 +261,9 @@ func (s *Stmt) BindRawText(param int, value []byte) error { return TOOBIG } 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(ptr), uint64(len(value)), - uint64(s.c.freer), _UTF8) + uint64(ptr), uint64(len(value))) return s.c.error(r) } @@ -279,10 +277,9 @@ func (s *Stmt) BindBlob(param int, value []byte) error { return TOOBIG } 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(ptr), uint64(len(value)), - uint64(s.c.freer)) + uint64(ptr), uint64(len(value))) 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 = 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(ptr), uint64(len(buf)), - uint64(s.c.freer), _UTF8) + uint64(ptr), uint64(len(buf))) return s.c.error(r) } diff --git a/vendor/github.com/ncruces/go-sqlite3/vfs/lock.go b/vendor/github.com/ncruces/go-sqlite3/vfs/lock.go index 86a988ae8..5366fdb71 100644 --- a/vendor/github.com/ncruces/go-sqlite3/vfs/lock.go +++ b/vendor/github.com/ncruces/go-sqlite3/vfs/lock.go @@ -79,16 +79,15 @@ func (f *vfsFile) Lock(lock LockLevel) error { // A PENDING lock is needed before acquiring an EXCLUSIVE lock. if f.lock < LOCK_PENDING { // 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 { return rc } f.lock = LOCK_PENDING } - // We already have PENDING, so we're just waiting for readers to leave. - // If we were RESERVED, we can wait for a little while, before invoking - // the busy handler; we will only do this once. - if rc := osGetExclusiveLock(f.File, reserved /* wait */); rc != _OK { + // We are now PENDING, so we're just waiting for readers to leave. + // If we were RESERVED, we can block for a bit before invoking the busy handler. + if rc := osGetExclusiveLock(f.File, reserved /* block */); rc != _OK { return rc } f.lock = LOCK_EXCLUSIVE diff --git a/vendor/github.com/ncruces/go-sqlite3/vfs/os_linux.go b/vendor/github.com/ncruces/go-sqlite3/vfs/os_linux.go index 7bb78c0af..e163e804d 100644 --- a/vendor/github.com/ncruces/go-sqlite3/vfs/os_linux.go +++ b/vendor/github.com/ncruces/go-sqlite3/vfs/os_linux.go @@ -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 { break } - if timeout < time.Since(before) { + if time.Since(before) > timeout { 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) diff --git a/vendor/github.com/ncruces/go-sqlite3/vfs/os_unix_lock.go b/vendor/github.com/ncruces/go-sqlite3/vfs/os_unix_lock.go index ffa1f5e19..2616a0f5e 100644 --- a/vendor/github.com/ncruces/go-sqlite3/vfs/os_unix_lock.go +++ b/vendor/github.com/ncruces/go-sqlite3/vfs/os_unix_lock.go @@ -32,9 +32,9 @@ func osGetPendingLock(file *os.File, block bool) _ErrorCode { 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 - if wait { + if block { timeout = time.Millisecond } // Acquire the EXCLUSIVE lock. diff --git a/vendor/github.com/ncruces/go-sqlite3/vfs/os_windows.go b/vendor/github.com/ncruces/go-sqlite3/vfs/os_windows.go index 7425b5581..4cad777d3 100644 --- a/vendor/github.com/ncruces/go-sqlite3/vfs/os_windows.go +++ b/vendor/github.com/ncruces/go-sqlite3/vfs/os_windows.go @@ -38,9 +38,9 @@ func osGetPendingLock(file *os.File, block bool) _ErrorCode { 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 - if wait { + if block { 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 { break } - if timeout < time.Since(before) { + if time.Since(before) > timeout { 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) diff --git a/vendor/github.com/ncruces/go-sqlite3/vfs/shm.go b/vendor/github.com/ncruces/go-sqlite3/vfs/shm.go index 7b0d4b677..36f55d0b6 100644 --- a/vendor/github.com/ncruces/go-sqlite3/vfs/shm.go +++ b/vendor/github.com/ncruces/go-sqlite3/vfs/shm.go @@ -6,6 +6,7 @@ "context" "io" "os" + "time" "github.com/ncruces/go-sqlite3/internal/util" "github.com/tetratelabs/wazero/api" @@ -49,6 +50,7 @@ type vfsShm struct { path string regions []*util.MappedRegion readOnly bool + blocking bool } func (s *vfsShm) shmOpen() _ErrorCode { @@ -76,6 +78,13 @@ func (s *vfsShm) shmOpen() _ErrorCode { if s.readOnly { 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 { return rc } @@ -83,7 +92,7 @@ func (s *vfsShm) shmOpen() _ErrorCode { 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 _OK @@ -150,13 +159,18 @@ func (s *vfsShm) shmLock(offset, n int32, flags _ShmFlag) _ErrorCode { panic(util.AssertErr()) } + var timeout time.Duration + if s.blocking { + timeout = time.Millisecond + } + switch { case flags&_SHM_UNLOCK != 0: return osUnlock(s.File, _SHM_BASE+int64(offset), int64(n)) 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: - return osWriteLock(s.File, _SHM_BASE+int64(offset), int64(n), 0) + return osWriteLock(s.File, _SHM_BASE+int64(offset), int64(n), timeout) default: panic(util.AssertErr()) } @@ -181,3 +195,7 @@ func (s *vfsShm) shmUnmap(delete bool) { s.Close() s.File = nil } + +func (s *vfsShm) shmEnableBlocking(block bool) { + s.blocking = block +} diff --git a/vendor/github.com/ncruces/go-sqlite3/vfs/vfs.go b/vendor/github.com/ncruces/go-sqlite3/vfs/vfs.go index 983f28560..eb606bf88 100644 --- a/vendor/github.com/ncruces/go-sqlite3/vfs/vfs.go +++ b/vendor/github.com/ncruces/go-sqlite3/vfs/vfs.go @@ -243,6 +243,15 @@ func vfsFileControl(ctx context.Context, mod api.Module, pFile uint32, op _Fcntl 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: if file, ok := file.(FilePersistentWAL); ok { 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() } if out != "" { - fn := mod.ExportedFunction("malloc") + fn := mod.ExportedFunction("sqlite3_malloc64") stack := [...]uint64{uint64(len(out) + 1)} if err := fn.CallWithStack(ctx, stack[:]); err != nil { panic(err) diff --git a/vendor/modules.txt b/vendor/modules.txt index 1549117be..7cd7827d4 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -523,7 +523,7 @@ github.com/modern-go/reflect2 # github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 ## explicit github.com/munnerz/goautoneg -# github.com/ncruces/go-sqlite3 v0.18.1 +# github.com/ncruces/go-sqlite3 v0.18.3 ## explicit; go 1.21 github.com/ncruces/go-sqlite3 github.com/ncruces/go-sqlite3/driver