mirror of
https://github.com/superseriousbusiness/gotosocial.git
synced 2025-04-16 22:34:05 +00:00
bump ncruces/go-sqlite3 to v0.25.0
This commit is contained in:
parent
6473886c8e
commit
9fe53dbf1c
36 changed files with 636 additions and 578 deletions
2
go.mod
2
go.mod
|
@ -54,7 +54,7 @@ require (
|
|||
github.com/miekg/dns v1.1.64
|
||||
github.com/minio/minio-go/v7 v7.0.85
|
||||
github.com/mitchellh/mapstructure v1.5.0
|
||||
github.com/ncruces/go-sqlite3 v0.24.0
|
||||
github.com/ncruces/go-sqlite3 v0.25.0
|
||||
github.com/oklog/ulid v1.3.1
|
||||
github.com/prometheus/client_golang v1.21.1
|
||||
github.com/rivo/uniseg v0.4.7
|
||||
|
|
4
go.sum
generated
4
go.sum
generated
|
@ -322,8 +322,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.24.0 h1:Z4jfmzu2NCd4SmyFwLT2OmF3EnTZbqwATvdiuNHNhLA=
|
||||
github.com/ncruces/go-sqlite3 v0.24.0/go.mod h1:/Vs8ACZHjJ1SA6E9RZUn3EyB1OP3nDQ4z/ar+0fplTQ=
|
||||
github.com/ncruces/go-sqlite3 v0.25.0 h1:trugKUs98Zwy9KwRr/EUxZHL92LYt7UqcKqAfpGpK+I=
|
||||
github.com/ncruces/go-sqlite3 v0.25.0/go.mod h1:n6Z7036yFilJx04yV0mi5JWaF66rUmXn1It9Ux8dx68=
|
||||
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=
|
||||
|
|
22
vendor/github.com/ncruces/go-sqlite3/README.md
generated
vendored
22
vendor/github.com/ncruces/go-sqlite3/README.md
generated
vendored
|
@ -65,17 +65,20 @@ db.QueryRow(`SELECT sqlite_version()`).Scan(&version)
|
|||
This module replaces the SQLite [OS Interface](https://sqlite.org/vfs.html)
|
||||
(aka VFS) with a [pure Go](vfs/) implementation,
|
||||
which has advantages and disadvantages.
|
||||
|
||||
Read more about the Go VFS design [here](vfs/README.md).
|
||||
|
||||
Because each database connection executes within a Wasm sandboxed environment,
|
||||
memory usage will be higher than alternatives.
|
||||
|
||||
### Testing
|
||||
|
||||
This project aims for [high test coverage](https://github.com/ncruces/go-sqlite3/wiki/Test-coverage-report).
|
||||
It also benefits greatly from [SQLite's](https://sqlite.org/testing.html) and
|
||||
[wazero's](https://tetrate.io/blog/introducing-wazero-from-tetrate/#:~:text=Rock%2Dsolid%20test%20approach) thorough testing.
|
||||
[wazero's](https://tetrate.io/blog/introducing-wazero-from-tetrate/#:~:text=Rock%2Dsolid%20test%20approach)
|
||||
thorough testing.
|
||||
|
||||
Every commit is [tested](https://github.com/ncruces/go-sqlite3/wiki/Support-matrix) on
|
||||
Linux (amd64/arm64/386/riscv64/ppc64le/s390x), macOS (amd64/arm64),
|
||||
Linux (amd64/arm64/386/riscv64/ppc64le/s390x), macOS (arm64/amd64),
|
||||
Windows (amd64), FreeBSD (amd64/arm64), OpenBSD (amd64), NetBSD (amd64/arm64),
|
||||
DragonFly BSD (amd64), illumos (amd64), and Solaris (amd64).
|
||||
|
||||
|
@ -84,12 +87,21 @@ The Go VFS is tested by running SQLite's
|
|||
|
||||
### Performance
|
||||
|
||||
Perfomance of the [`database/sql`](https://pkg.go.dev/database/sql) driver is
|
||||
Performance of the [`database/sql`](https://pkg.go.dev/database/sql) driver is
|
||||
[competitive](https://github.com/cvilsmeier/go-sqlite-bench) with alternatives.
|
||||
|
||||
The Wasm and VFS layers are also tested by running SQLite's
|
||||
The Wasm and VFS layers are also benchmarked by running SQLite's
|
||||
[speedtest1](https://github.com/sqlite/sqlite/blob/master/test/speedtest1.c).
|
||||
|
||||
### Concurrency
|
||||
|
||||
This module behaves similarly to SQLite in [multi-thread](https://sqlite.org/threadsafe.html) mode:
|
||||
it is goroutine-safe, provided that no single database connection, or object derived from it,
|
||||
is used concurrently by multiple goroutines.
|
||||
|
||||
The [`database/sql`](https://pkg.go.dev/database/sql) API is safe to use concurrently,
|
||||
according to its documentation.
|
||||
|
||||
### FAQ, issues, new features
|
||||
|
||||
For questions, please see [Discussions](https://github.com/ncruces/go-sqlite3/discussions/categories/q-a).
|
||||
|
|
9
vendor/github.com/ncruces/go-sqlite3/blob.go
generated
vendored
9
vendor/github.com/ncruces/go-sqlite3/blob.go
generated
vendored
|
@ -31,6 +31,10 @@ var _ io.ReadWriteSeeker = &Blob{}
|
|||
//
|
||||
// https://sqlite.org/c3ref/blob_open.html
|
||||
func (c *Conn) OpenBlob(db, table, column string, row int64, write bool) (*Blob, error) {
|
||||
if c.interrupt.Err() != nil {
|
||||
return nil, INTERRUPT
|
||||
}
|
||||
|
||||
defer c.arena.mark()()
|
||||
blobPtr := c.arena.new(ptrlen)
|
||||
dbPtr := c.arena.string(db)
|
||||
|
@ -42,7 +46,6 @@ func (c *Conn) OpenBlob(db, table, column string, row int64, write bool) (*Blob,
|
|||
flags = 1
|
||||
}
|
||||
|
||||
c.checkInterrupt(c.handle)
|
||||
rc := res_t(c.call("sqlite3_blob_open", stk_t(c.handle),
|
||||
stk_t(dbPtr), stk_t(tablePtr), stk_t(columnPtr),
|
||||
stk_t(row), stk_t(flags), stk_t(blobPtr)))
|
||||
|
@ -253,7 +256,9 @@ func (b *Blob) Seek(offset int64, whence int) (int64, error) {
|
|||
//
|
||||
// https://sqlite.org/c3ref/blob_reopen.html
|
||||
func (b *Blob) Reopen(row int64) error {
|
||||
b.c.checkInterrupt(b.c.handle)
|
||||
if b.c.interrupt.Err() != nil {
|
||||
return INTERRUPT
|
||||
}
|
||||
err := b.c.error(res_t(b.c.call("sqlite3_blob_reopen", stk_t(b.handle), stk_t(row))))
|
||||
b.bytes = int64(int32(b.c.call("sqlite3_blob_bytes", stk_t(b.handle))))
|
||||
b.offset = 0
|
||||
|
|
16
vendor/github.com/ncruces/go-sqlite3/config.go
generated
vendored
16
vendor/github.com/ncruces/go-sqlite3/config.go
generated
vendored
|
@ -4,6 +4,7 @@ import (
|
|||
"context"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/tetratelabs/wazero/api"
|
||||
|
||||
|
@ -48,6 +49,15 @@ func (c *Conn) Config(op DBConfig, arg ...bool) (bool, error) {
|
|||
return util.ReadBool(c.mod, argsPtr), c.error(rc)
|
||||
}
|
||||
|
||||
var defaultLogger atomic.Pointer[func(code ExtendedErrorCode, msg string)]
|
||||
|
||||
// ConfigLog sets up the default error logging callback for new connections.
|
||||
//
|
||||
// https://sqlite.org/errlog.html
|
||||
func ConfigLog(cb func(code ExtendedErrorCode, msg string)) {
|
||||
defaultLogger.Store(&cb)
|
||||
}
|
||||
|
||||
// ConfigLog sets up the error logging callback for the connection.
|
||||
//
|
||||
// https://sqlite.org/errlog.html
|
||||
|
@ -265,6 +275,10 @@ func traceCallback(ctx context.Context, mod api.Module, evt TraceEvent, pDB, pAr
|
|||
//
|
||||
// https://sqlite.org/c3ref/wal_checkpoint_v2.html
|
||||
func (c *Conn) WALCheckpoint(schema string, mode CheckpointMode) (nLog, nCkpt int, err error) {
|
||||
if c.interrupt.Err() != nil {
|
||||
return 0, 0, INTERRUPT
|
||||
}
|
||||
|
||||
defer c.arena.mark()()
|
||||
nLogPtr := c.arena.new(ptrlen)
|
||||
nCkptPtr := c.arena.new(ptrlen)
|
||||
|
@ -378,6 +392,6 @@ func (c *Conn) EnableChecksums(schema string) error {
|
|||
}
|
||||
|
||||
// Checkpoint the WAL.
|
||||
_, _, err = c.WALCheckpoint(schema, CHECKPOINT_RESTART)
|
||||
_, _, err = c.WALCheckpoint(schema, CHECKPOINT_FULL)
|
||||
return err
|
||||
}
|
||||
|
|
79
vendor/github.com/ncruces/go-sqlite3/conn.go
generated
vendored
79
vendor/github.com/ncruces/go-sqlite3/conn.go
generated
vendored
|
@ -25,7 +25,6 @@ type Conn struct {
|
|||
*sqlite
|
||||
|
||||
interrupt context.Context
|
||||
pending *Stmt
|
||||
stmts []*Stmt
|
||||
busy func(context.Context, int) bool
|
||||
log func(xErrorCode, string)
|
||||
|
@ -41,6 +40,7 @@ type Conn struct {
|
|||
busylst time.Time
|
||||
arena arena
|
||||
handle ptr_t
|
||||
gosched uint8
|
||||
}
|
||||
|
||||
// Open calls [OpenFlags] with [OPEN_READWRITE], [OPEN_CREATE] and [OPEN_URI].
|
||||
|
@ -49,7 +49,7 @@ func Open(filename string) (*Conn, error) {
|
|||
}
|
||||
|
||||
// OpenContext is like [Open] but includes a context,
|
||||
// which is used to interrupt the process of opening the connectiton.
|
||||
// which is used to interrupt the process of opening the connection.
|
||||
func OpenContext(ctx context.Context, filename string) (*Conn, error) {
|
||||
return newConn(ctx, filename, OPEN_READWRITE|OPEN_CREATE|OPEN_URI)
|
||||
}
|
||||
|
@ -92,6 +92,9 @@ func newConn(ctx context.Context, filename string, flags OpenFlag) (ret *Conn, _
|
|||
}()
|
||||
|
||||
c.ctx = context.WithValue(c.ctx, connKey{}, c)
|
||||
if logger := defaultLogger.Load(); logger != nil {
|
||||
c.ConfigLog(*logger)
|
||||
}
|
||||
c.arena = c.newArena()
|
||||
c.handle, err = c.openDB(filename, flags)
|
||||
if err == nil {
|
||||
|
@ -117,7 +120,7 @@ func (c *Conn) openDB(filename string, flags OpenFlag) (ptr_t, error) {
|
|||
return 0, err
|
||||
}
|
||||
|
||||
c.call("sqlite3_progress_handler_go", stk_t(handle), 100)
|
||||
c.call("sqlite3_progress_handler_go", stk_t(handle), 1000)
|
||||
if flags|OPEN_URI != 0 && strings.HasPrefix(filename, "file:") {
|
||||
var pragmas strings.Builder
|
||||
if _, after, ok := strings.Cut(filename, "?"); ok {
|
||||
|
@ -129,7 +132,6 @@ func (c *Conn) openDB(filename string, flags OpenFlag) (ptr_t, error) {
|
|||
}
|
||||
}
|
||||
if pragmas.Len() != 0 {
|
||||
c.checkInterrupt(handle)
|
||||
pragmaPtr := c.arena.string(pragmas.String())
|
||||
rc := res_t(c.call("sqlite3_exec", stk_t(handle), stk_t(pragmaPtr), 0, 0, 0))
|
||||
if err := c.sqlite.error(rc, handle, pragmas.String()); err != nil {
|
||||
|
@ -163,9 +165,6 @@ func (c *Conn) Close() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
c.pending.Close()
|
||||
c.pending = nil
|
||||
|
||||
rc := res_t(c.call("sqlite3_close", stk_t(c.handle)))
|
||||
if err := c.error(rc); err != nil {
|
||||
return err
|
||||
|
@ -180,11 +179,16 @@ func (c *Conn) Close() error {
|
|||
//
|
||||
// https://sqlite.org/c3ref/exec.html
|
||||
func (c *Conn) Exec(sql string) error {
|
||||
defer c.arena.mark()()
|
||||
sqlPtr := c.arena.string(sql)
|
||||
if c.interrupt.Err() != nil {
|
||||
return INTERRUPT
|
||||
}
|
||||
return c.exec(sql)
|
||||
}
|
||||
|
||||
c.checkInterrupt(c.handle)
|
||||
rc := res_t(c.call("sqlite3_exec", stk_t(c.handle), stk_t(sqlPtr), 0, 0, 0))
|
||||
func (c *Conn) exec(sql string) error {
|
||||
defer c.arena.mark()()
|
||||
textPtr := c.arena.string(sql)
|
||||
rc := res_t(c.call("sqlite3_exec", stk_t(c.handle), stk_t(textPtr), 0, 0, 0))
|
||||
return c.error(rc, sql)
|
||||
}
|
||||
|
||||
|
@ -203,20 +207,22 @@ func (c *Conn) PrepareFlags(sql string, flags PrepareFlag) (stmt *Stmt, tail str
|
|||
if len(sql) > _MAX_SQL_LENGTH {
|
||||
return nil, "", TOOBIG
|
||||
}
|
||||
if c.interrupt.Err() != nil {
|
||||
return nil, "", INTERRUPT
|
||||
}
|
||||
|
||||
defer c.arena.mark()()
|
||||
stmtPtr := c.arena.new(ptrlen)
|
||||
tailPtr := c.arena.new(ptrlen)
|
||||
sqlPtr := c.arena.string(sql)
|
||||
textPtr := c.arena.string(sql)
|
||||
|
||||
c.checkInterrupt(c.handle)
|
||||
rc := res_t(c.call("sqlite3_prepare_v3", stk_t(c.handle),
|
||||
stk_t(sqlPtr), stk_t(len(sql)+1), stk_t(flags),
|
||||
stk_t(textPtr), stk_t(len(sql)+1), stk_t(flags),
|
||||
stk_t(stmtPtr), stk_t(tailPtr)))
|
||||
|
||||
stmt = &Stmt{c: c}
|
||||
stmt = &Stmt{c: c, sql: sql}
|
||||
stmt.handle = util.Read32[ptr_t](c.mod, stmtPtr)
|
||||
if sql := sql[util.Read32[ptr_t](c.mod, tailPtr)-sqlPtr:]; sql != "" {
|
||||
if sql := sql[util.Read32[ptr_t](c.mod, tailPtr)-textPtr:]; sql != "" {
|
||||
tail = sql
|
||||
}
|
||||
|
||||
|
@ -337,43 +343,17 @@ func (c *Conn) GetInterrupt() context.Context {
|
|||
//
|
||||
// https://sqlite.org/c3ref/interrupt.html
|
||||
func (c *Conn) SetInterrupt(ctx context.Context) (old context.Context) {
|
||||
if ctx == nil {
|
||||
panic("nil Context")
|
||||
}
|
||||
old = c.interrupt
|
||||
c.interrupt = ctx
|
||||
|
||||
if ctx == old || ctx.Done() == old.Done() {
|
||||
return old
|
||||
}
|
||||
|
||||
// A busy SQL statement prevents SQLite from ignoring an interrupt
|
||||
// that comes before any other statements are started.
|
||||
if c.pending == nil {
|
||||
defer c.arena.mark()()
|
||||
stmtPtr := c.arena.new(ptrlen)
|
||||
loopPtr := c.arena.string(`WITH RECURSIVE c(x) AS (VALUES(0) UNION ALL SELECT x FROM c) SELECT x FROM c`)
|
||||
c.call("sqlite3_prepare_v3", stk_t(c.handle), stk_t(loopPtr), math.MaxUint64,
|
||||
stk_t(PREPARE_PERSISTENT), stk_t(stmtPtr), 0)
|
||||
c.pending = &Stmt{c: c}
|
||||
c.pending.handle = util.Read32[ptr_t](c.mod, stmtPtr)
|
||||
}
|
||||
|
||||
if old.Done() != nil && ctx.Err() == nil {
|
||||
c.pending.Reset()
|
||||
}
|
||||
if ctx.Done() != nil {
|
||||
c.pending.Step()
|
||||
}
|
||||
return old
|
||||
}
|
||||
|
||||
func (c *Conn) checkInterrupt(handle ptr_t) {
|
||||
if c.interrupt.Err() != nil {
|
||||
c.call("sqlite3_interrupt", stk_t(handle))
|
||||
}
|
||||
}
|
||||
|
||||
func progressCallback(ctx context.Context, mod api.Module, _ ptr_t) (interrupt int32) {
|
||||
if c, ok := ctx.Value(connKey{}).(*Conn); ok {
|
||||
if c.interrupt.Done() != nil {
|
||||
if c.gosched++; c.gosched%16 == 0 {
|
||||
runtime.Gosched()
|
||||
}
|
||||
if c.interrupt.Err() != nil {
|
||||
|
@ -429,11 +409,8 @@ func (c *Conn) BusyHandler(cb func(ctx context.Context, count int) (retry bool))
|
|||
|
||||
func busyCallback(ctx context.Context, mod api.Module, pDB ptr_t, count int32) (retry int32) {
|
||||
if c, ok := ctx.Value(connKey{}).(*Conn); ok && c.handle == pDB && c.busy != nil {
|
||||
interrupt := c.interrupt
|
||||
if interrupt == nil {
|
||||
interrupt = context.Background()
|
||||
}
|
||||
if interrupt.Err() == nil && c.busy(interrupt, int(count)) {
|
||||
if interrupt := c.interrupt; interrupt.Err() == nil &&
|
||||
c.busy(interrupt, int(count)) {
|
||||
retry = 1
|
||||
}
|
||||
}
|
||||
|
|
7
vendor/github.com/ncruces/go-sqlite3/const.go
generated
vendored
7
vendor/github.com/ncruces/go-sqlite3/const.go
generated
vendored
|
@ -11,10 +11,9 @@ const (
|
|||
_ROW = 100 /* sqlite3_step() has another row ready */
|
||||
_DONE = 101 /* sqlite3_step() has finished executing */
|
||||
|
||||
_MAX_NAME = 1e6 // Self-imposed limit for most NUL terminated strings.
|
||||
_MAX_LENGTH = 1e9
|
||||
_MAX_SQL_LENGTH = 1e9
|
||||
_MAX_FUNCTION_ARG = 100
|
||||
_MAX_NAME = 1e6 // Self-imposed limit for most NUL terminated strings.
|
||||
_MAX_LENGTH = 1e9
|
||||
_MAX_SQL_LENGTH = 1e9
|
||||
|
||||
ptrlen = util.PtrLen
|
||||
intlen = util.IntLen
|
||||
|
|
10
vendor/github.com/ncruces/go-sqlite3/context.go
generated
vendored
10
vendor/github.com/ncruces/go-sqlite3/context.go
generated
vendored
|
@ -89,20 +89,26 @@ func (ctx Context) ResultText(value string) {
|
|||
}
|
||||
|
||||
// ResultRawText sets the text result of the function to a []byte.
|
||||
// Returning a nil slice is the same as calling [Context.ResultNull].
|
||||
//
|
||||
// https://sqlite.org/c3ref/result_blob.html
|
||||
func (ctx Context) ResultRawText(value []byte) {
|
||||
if len(value) == 0 {
|
||||
ctx.ResultText("")
|
||||
return
|
||||
}
|
||||
ptr := ctx.c.newBytes(value)
|
||||
ctx.c.call("sqlite3_result_text_go",
|
||||
stk_t(ctx.handle), stk_t(ptr), stk_t(len(value)))
|
||||
}
|
||||
|
||||
// ResultBlob sets the result of the function to a []byte.
|
||||
// Returning a nil slice is the same as calling [Context.ResultNull].
|
||||
//
|
||||
// https://sqlite.org/c3ref/result_blob.html
|
||||
func (ctx Context) ResultBlob(value []byte) {
|
||||
if len(value) == 0 {
|
||||
ctx.ResultZeroBlob(0)
|
||||
return
|
||||
}
|
||||
ptr := ctx.c.newBytes(value)
|
||||
ctx.c.call("sqlite3_result_blob_go",
|
||||
stk_t(ctx.handle), stk_t(ptr), stk_t(len(value)))
|
||||
|
|
188
vendor/github.com/ncruces/go-sqlite3/driver/driver.go
generated
vendored
188
vendor/github.com/ncruces/go-sqlite3/driver/driver.go
generated
vendored
|
@ -20,22 +20,45 @@
|
|||
// - a [serializable] transaction is always "immediate";
|
||||
// - a [read-only] transaction is always "deferred".
|
||||
//
|
||||
// # Datatypes In SQLite
|
||||
//
|
||||
// SQLite is dynamically typed.
|
||||
// Columns can mostly hold any value regardless of their declared type.
|
||||
// SQLite supports most [driver.Value] types out of the box,
|
||||
// but bool and [time.Time] require special care.
|
||||
//
|
||||
// Booleans can be stored on any column type and scanned back to a *bool.
|
||||
// However, if scanned to a *any, booleans may either become an
|
||||
// int64, string or bool, depending on the declared type of the column.
|
||||
// If you use BOOLEAN for your column type,
|
||||
// 1 and 0 will always scan as true and false.
|
||||
//
|
||||
// # Working with time
|
||||
//
|
||||
// Time values can similarly be stored on any column type.
|
||||
// The time encoding/decoding format can be specified using "_timefmt":
|
||||
//
|
||||
// sql.Open("sqlite3", "file:demo.db?_timefmt=sqlite")
|
||||
//
|
||||
// Possible values are: "auto" (the default), "sqlite", "rfc3339";
|
||||
// Special values are: "auto" (the default), "sqlite", "rfc3339";
|
||||
// - "auto" encodes as RFC 3339 and decodes any [format] supported by SQLite;
|
||||
// - "sqlite" encodes as SQLite and decodes any [format] supported by SQLite;
|
||||
// - "rfc3339" encodes and decodes RFC 3339 only.
|
||||
//
|
||||
// If you encode as RFC 3339 (the default),
|
||||
// consider using the TIME [collating sequence] to produce a time-ordered sequence.
|
||||
// You can also set "_timefmt" to an arbitrary [sqlite3.TimeFormat] or [time.Layout].
|
||||
//
|
||||
// To scan values in other formats, [sqlite3.TimeFormat.Scanner] may be helpful.
|
||||
// To bind values in other formats, [sqlite3.TimeFormat.Encode] them before binding.
|
||||
// If you encode as RFC 3339 (the default),
|
||||
// consider using the TIME [collating sequence] to produce time-ordered sequences.
|
||||
//
|
||||
// If you encode as RFC 3339 (the default),
|
||||
// time values will scan back to a *time.Time unless your column type is TEXT.
|
||||
// Otherwise, if scanned to a *any, time values may either become an
|
||||
// int64, float64 or string, depending on the time format and declared type of the column.
|
||||
// If you use DATE, TIME, DATETIME, or TIMESTAMP for your column type,
|
||||
// "_timefmt" will be used to decode values.
|
||||
//
|
||||
// To scan values in custom formats, [sqlite3.TimeFormat.Scanner] may be helpful.
|
||||
// To bind values in custom formats, [sqlite3.TimeFormat.Encode] them before binding.
|
||||
//
|
||||
// When using a custom time struct, you'll have to implement
|
||||
// [database/sql/driver.Valuer] and [database/sql.Scanner].
|
||||
|
@ -48,7 +71,7 @@
|
|||
// The Scan method needs to take into account that the value it receives can be of differing types.
|
||||
// It can already be a [time.Time], if the driver decoded the value according to "_timefmt" rules.
|
||||
// Or it can be a: string, int64, float64, []byte, or nil,
|
||||
// depending on the column type and what whoever wrote the value.
|
||||
// depending on the column type and whoever wrote the value.
|
||||
// [sqlite3.TimeFormat.Decode] may help.
|
||||
//
|
||||
// # Setting PRAGMAs
|
||||
|
@ -358,13 +381,10 @@ func (c *conn) Commit() error {
|
|||
}
|
||||
|
||||
func (c *conn) Rollback() error {
|
||||
err := c.Conn.Exec(`ROLLBACK` + c.txReset)
|
||||
if errors.Is(err, sqlite3.INTERRUPT) {
|
||||
old := c.Conn.SetInterrupt(context.Background())
|
||||
defer c.Conn.SetInterrupt(old)
|
||||
err = c.Conn.Exec(`ROLLBACK` + c.txReset)
|
||||
}
|
||||
return err
|
||||
// ROLLBACK even if interrupted.
|
||||
old := c.Conn.SetInterrupt(context.Background())
|
||||
defer c.Conn.SetInterrupt(old)
|
||||
return c.Conn.Exec(`ROLLBACK` + c.txReset)
|
||||
}
|
||||
|
||||
func (c *conn) Prepare(query string) (driver.Stmt, error) {
|
||||
|
@ -598,6 +618,28 @@ const (
|
|||
_TIME
|
||||
)
|
||||
|
||||
func scanFromDecl(decl string) scantype {
|
||||
// These types are only used before we have rows,
|
||||
// and otherwise as type hints.
|
||||
// The first few ensure STRICT tables are strictly typed.
|
||||
// The other two are type hints for booleans and time.
|
||||
switch decl {
|
||||
case "INT", "INTEGER":
|
||||
return _INT
|
||||
case "REAL":
|
||||
return _REAL
|
||||
case "TEXT":
|
||||
return _TEXT
|
||||
case "BLOB":
|
||||
return _BLOB
|
||||
case "BOOLEAN":
|
||||
return _BOOL
|
||||
case "DATE", "TIME", "DATETIME", "TIMESTAMP":
|
||||
return _TIME
|
||||
}
|
||||
return _ANY
|
||||
}
|
||||
|
||||
var (
|
||||
// Ensure these interfaces are implemented:
|
||||
_ driver.RowsColumnTypeDatabaseTypeName = &rows{}
|
||||
|
@ -622,6 +664,18 @@ func (r *rows) Columns() []string {
|
|||
return r.names
|
||||
}
|
||||
|
||||
func (r *rows) scanType(index int) scantype {
|
||||
if r.scans == nil {
|
||||
count := r.Stmt.ColumnCount()
|
||||
scans := make([]scantype, count)
|
||||
for i := range scans {
|
||||
scans[i] = scanFromDecl(strings.ToUpper(r.Stmt.ColumnDeclType(i)))
|
||||
}
|
||||
r.scans = scans
|
||||
}
|
||||
return r.scans[index]
|
||||
}
|
||||
|
||||
func (r *rows) loadColumnMetadata() {
|
||||
if r.nulls == nil {
|
||||
count := r.Stmt.ColumnCount()
|
||||
|
@ -635,24 +689,7 @@ func (r *rows) loadColumnMetadata() {
|
|||
r.Stmt.ColumnTableName(i),
|
||||
col)
|
||||
types[i] = strings.ToUpper(types[i])
|
||||
// These types are only used before we have rows,
|
||||
// and otherwise as type hints.
|
||||
// The first few ensure STRICT tables are strictly typed.
|
||||
// The other two are type hints for booleans and time.
|
||||
switch types[i] {
|
||||
case "INT", "INTEGER":
|
||||
scans[i] = _INT
|
||||
case "REAL":
|
||||
scans[i] = _REAL
|
||||
case "TEXT":
|
||||
scans[i] = _TEXT
|
||||
case "BLOB":
|
||||
scans[i] = _BLOB
|
||||
case "BOOLEAN":
|
||||
scans[i] = _BOOL
|
||||
case "DATE", "TIME", "DATETIME", "TIMESTAMP":
|
||||
scans[i] = _TIME
|
||||
}
|
||||
scans[i] = scanFromDecl(types[i])
|
||||
}
|
||||
}
|
||||
r.nulls = nulls
|
||||
|
@ -661,27 +698,15 @@ func (r *rows) loadColumnMetadata() {
|
|||
}
|
||||
}
|
||||
|
||||
func (r *rows) declType(index int) string {
|
||||
if r.types == nil {
|
||||
count := r.Stmt.ColumnCount()
|
||||
types := make([]string, count)
|
||||
for i := range types {
|
||||
types[i] = strings.ToUpper(r.Stmt.ColumnDeclType(i))
|
||||
}
|
||||
r.types = types
|
||||
}
|
||||
return r.types[index]
|
||||
}
|
||||
|
||||
func (r *rows) ColumnTypeDatabaseTypeName(index int) string {
|
||||
r.loadColumnMetadata()
|
||||
decltype := r.types[index]
|
||||
if len := len(decltype); len > 0 && decltype[len-1] == ')' {
|
||||
if i := strings.LastIndexByte(decltype, '('); i >= 0 {
|
||||
decltype = decltype[:i]
|
||||
decl := r.types[index]
|
||||
if len := len(decl); len > 0 && decl[len-1] == ')' {
|
||||
if i := strings.LastIndexByte(decl, '('); i >= 0 {
|
||||
decl = decl[:i]
|
||||
}
|
||||
}
|
||||
return strings.TrimSpace(decltype)
|
||||
return strings.TrimSpace(decl)
|
||||
}
|
||||
|
||||
func (r *rows) ColumnTypeNullable(index int) (nullable, ok bool) {
|
||||
|
@ -748,36 +773,49 @@ func (r *rows) Next(dest []driver.Value) error {
|
|||
}
|
||||
|
||||
data := unsafe.Slice((*any)(unsafe.SliceData(dest)), len(dest))
|
||||
err := r.Stmt.Columns(data...)
|
||||
if err := r.Stmt.ColumnsRaw(data...); err != nil {
|
||||
return err
|
||||
}
|
||||
for i := range dest {
|
||||
if t, ok := r.decodeTime(i, dest[i]); ok {
|
||||
dest[i] = t
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *rows) decodeTime(i int, v any) (_ time.Time, ok bool) {
|
||||
switch v := v.(type) {
|
||||
case int64, float64:
|
||||
// could be a time value
|
||||
case string:
|
||||
if r.tmWrite != "" && r.tmWrite != time.RFC3339 && r.tmWrite != time.RFC3339Nano {
|
||||
scan := r.scanType(i)
|
||||
switch v := dest[i].(type) {
|
||||
case int64:
|
||||
if scan == _BOOL {
|
||||
switch v {
|
||||
case 1:
|
||||
dest[i] = true
|
||||
case 0:
|
||||
dest[i] = false
|
||||
}
|
||||
continue
|
||||
}
|
||||
case []byte:
|
||||
if len(v) == cap(v) { // a BLOB
|
||||
continue
|
||||
}
|
||||
if scan != _TEXT {
|
||||
switch r.tmWrite {
|
||||
case "", time.RFC3339, time.RFC3339Nano:
|
||||
t, ok := maybeTime(v)
|
||||
if ok {
|
||||
dest[i] = t
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
dest[i] = string(v)
|
||||
case float64:
|
||||
break
|
||||
default:
|
||||
continue
|
||||
}
|
||||
t, ok := maybeTime(v)
|
||||
if ok {
|
||||
return t, true
|
||||
if scan == _TIME {
|
||||
t, err := r.tmRead.Decode(dest[i])
|
||||
if err == nil {
|
||||
dest[i] = t
|
||||
continue
|
||||
}
|
||||
}
|
||||
default:
|
||||
return
|
||||
}
|
||||
switch r.declType(i) {
|
||||
case "DATE", "TIME", "DATETIME", "TIMESTAMP":
|
||||
// could be a time value
|
||||
default:
|
||||
return
|
||||
}
|
||||
t, err := r.tmRead.Decode(v)
|
||||
return t, err == nil
|
||||
return nil
|
||||
}
|
||||
|
|
11
vendor/github.com/ncruces/go-sqlite3/driver/time.go
generated
vendored
11
vendor/github.com/ncruces/go-sqlite3/driver/time.go
generated
vendored
|
@ -1,12 +1,15 @@
|
|||
package driver
|
||||
|
||||
import "time"
|
||||
import (
|
||||
"bytes"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Convert a string in [time.RFC3339Nano] format into a [time.Time]
|
||||
// if it roundtrips back to the same string.
|
||||
// This way times can be persisted to, and recovered from, the database,
|
||||
// but if a string is needed, [database/sql] will recover the same string.
|
||||
func maybeTime(text string) (_ time.Time, _ bool) {
|
||||
func maybeTime(text []byte) (_ time.Time, _ bool) {
|
||||
// Weed out (some) values that can't possibly be
|
||||
// [time.RFC3339Nano] timestamps.
|
||||
if len(text) < len("2006-01-02T15:04:05Z") {
|
||||
|
@ -21,8 +24,8 @@ func maybeTime(text string) (_ time.Time, _ bool) {
|
|||
|
||||
// Slow path.
|
||||
var buf [len(time.RFC3339Nano)]byte
|
||||
date, err := time.Parse(time.RFC3339Nano, text)
|
||||
if err == nil && text == string(date.AppendFormat(buf[:0], time.RFC3339Nano)) {
|
||||
date, err := time.Parse(time.RFC3339Nano, string(text))
|
||||
if err == nil && bytes.Equal(text, date.AppendFormat(buf[:0], time.RFC3339Nano)) {
|
||||
return date, true
|
||||
}
|
||||
return
|
||||
|
|
18
vendor/github.com/ncruces/go-sqlite3/error.go
generated
vendored
18
vendor/github.com/ncruces/go-sqlite3/error.go
generated
vendored
|
@ -2,7 +2,6 @@ package sqlite3
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/ncruces/go-sqlite3/internal/util"
|
||||
|
@ -12,7 +11,6 @@ import (
|
|||
//
|
||||
// https://sqlite.org/c3ref/errcode.html
|
||||
type Error struct {
|
||||
str string
|
||||
msg string
|
||||
sql string
|
||||
code res_t
|
||||
|
@ -29,19 +27,13 @@ func (e *Error) Code() ErrorCode {
|
|||
//
|
||||
// https://sqlite.org/rescode.html
|
||||
func (e *Error) ExtendedCode() ExtendedErrorCode {
|
||||
return ExtendedErrorCode(e.code)
|
||||
return xErrorCode(e.code)
|
||||
}
|
||||
|
||||
// Error implements the error interface.
|
||||
func (e *Error) Error() string {
|
||||
var b strings.Builder
|
||||
b.WriteString("sqlite3: ")
|
||||
|
||||
if e.str != "" {
|
||||
b.WriteString(e.str)
|
||||
} else {
|
||||
b.WriteString(strconv.Itoa(int(e.code)))
|
||||
}
|
||||
b.WriteString(util.ErrorCodeString(uint32(e.code)))
|
||||
|
||||
if e.msg != "" {
|
||||
b.WriteString(": ")
|
||||
|
@ -103,12 +95,12 @@ func (e ErrorCode) Error() string {
|
|||
|
||||
// Temporary returns true for [BUSY] errors.
|
||||
func (e ErrorCode) Temporary() bool {
|
||||
return e == BUSY
|
||||
return e == BUSY || e == INTERRUPT
|
||||
}
|
||||
|
||||
// ExtendedCode returns the extended error code for this error.
|
||||
func (e ErrorCode) ExtendedCode() ExtendedErrorCode {
|
||||
return ExtendedErrorCode(e)
|
||||
return xErrorCode(e)
|
||||
}
|
||||
|
||||
// Error implements the error interface.
|
||||
|
@ -133,7 +125,7 @@ func (e ExtendedErrorCode) As(err any) bool {
|
|||
|
||||
// Temporary returns true for [BUSY] errors.
|
||||
func (e ExtendedErrorCode) Temporary() bool {
|
||||
return ErrorCode(e) == BUSY
|
||||
return ErrorCode(e) == BUSY || ErrorCode(e) == INTERRUPT
|
||||
}
|
||||
|
||||
// Timeout returns true for [BUSY_TIMEOUT] errors.
|
||||
|
|
159
vendor/github.com/ncruces/go-sqlite3/func.go
generated
vendored
159
vendor/github.com/ncruces/go-sqlite3/func.go
generated
vendored
|
@ -3,7 +3,9 @@ package sqlite3
|
|||
import (
|
||||
"context"
|
||||
"io"
|
||||
"iter"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/tetratelabs/wazero/api"
|
||||
|
||||
|
@ -45,7 +47,7 @@ func (c Conn) AnyCollationNeeded() error {
|
|||
// CreateCollation defines a new collating sequence.
|
||||
//
|
||||
// https://sqlite.org/c3ref/create_collation.html
|
||||
func (c *Conn) CreateCollation(name string, fn func(a, b []byte) int) error {
|
||||
func (c *Conn) CreateCollation(name string, fn CollatingFunction) error {
|
||||
var funcPtr ptr_t
|
||||
defer c.arena.mark()()
|
||||
namePtr := c.arena.string(name)
|
||||
|
@ -57,6 +59,10 @@ func (c *Conn) CreateCollation(name string, fn func(a, b []byte) int) error {
|
|||
return c.error(rc)
|
||||
}
|
||||
|
||||
// Collating function is the type of a collation callback.
|
||||
// Implementations must not retain a or b.
|
||||
type CollatingFunction func(a, b []byte) int
|
||||
|
||||
// CreateFunction defines a new scalar SQL function.
|
||||
//
|
||||
// https://sqlite.org/c3ref/create_function.html
|
||||
|
@ -77,34 +83,67 @@ func (c *Conn) CreateFunction(name string, nArg int, flag FunctionFlag, fn Scala
|
|||
// Implementations must not retain arg.
|
||||
type ScalarFunction func(ctx Context, arg ...Value)
|
||||
|
||||
// CreateWindowFunction defines a new aggregate or aggregate window SQL function.
|
||||
// If fn returns a [WindowFunction], then an aggregate window function is created.
|
||||
// If fn returns an [io.Closer], it will be called to free resources.
|
||||
// CreateAggregateFunction defines a new aggregate SQL function.
|
||||
//
|
||||
// https://sqlite.org/c3ref/create_function.html
|
||||
func (c *Conn) CreateWindowFunction(name string, nArg int, flag FunctionFlag, fn func() AggregateFunction) error {
|
||||
func (c *Conn) CreateAggregateFunction(name string, nArg int, flag FunctionFlag, fn AggregateSeqFunction) error {
|
||||
var funcPtr ptr_t
|
||||
defer c.arena.mark()()
|
||||
namePtr := c.arena.string(name)
|
||||
call := "sqlite3_create_aggregate_function_go"
|
||||
if fn != nil {
|
||||
agg := fn()
|
||||
if c, ok := agg.(io.Closer); ok {
|
||||
if err := c.Close(); err != nil {
|
||||
return err
|
||||
funcPtr = util.AddHandle(c.ctx, AggregateConstructor(func() AggregateFunction {
|
||||
var a aggregateFunc
|
||||
coro := func(yieldCoro func(struct{}) bool) {
|
||||
seq := func(yieldSeq func([]Value) bool) {
|
||||
for yieldSeq(a.arg) {
|
||||
if !yieldCoro(struct{}{}) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
fn(&a.ctx, seq)
|
||||
}
|
||||
}
|
||||
if _, ok := agg.(WindowFunction); ok {
|
||||
call = "sqlite3_create_window_function_go"
|
||||
}
|
||||
funcPtr = util.AddHandle(c.ctx, fn)
|
||||
a.next, a.stop = iter.Pull(coro)
|
||||
return &a
|
||||
}))
|
||||
}
|
||||
rc := res_t(c.call(call,
|
||||
rc := res_t(c.call("sqlite3_create_aggregate_function_go",
|
||||
stk_t(c.handle), stk_t(namePtr), stk_t(nArg),
|
||||
stk_t(flag), stk_t(funcPtr)))
|
||||
return c.error(rc)
|
||||
}
|
||||
|
||||
// AggregateSeqFunction is the type of an aggregate SQL function.
|
||||
// Implementations must not retain the slices yielded by seq.
|
||||
type AggregateSeqFunction func(ctx *Context, seq iter.Seq[[]Value])
|
||||
|
||||
// CreateWindowFunction defines a new aggregate or aggregate window SQL function.
|
||||
// If fn returns a [WindowFunction], an aggregate window function is created.
|
||||
// If fn returns an [io.Closer], it will be called to free resources.
|
||||
//
|
||||
// https://sqlite.org/c3ref/create_function.html
|
||||
func (c *Conn) CreateWindowFunction(name string, nArg int, flag FunctionFlag, fn AggregateConstructor) error {
|
||||
var funcPtr ptr_t
|
||||
defer c.arena.mark()()
|
||||
namePtr := c.arena.string(name)
|
||||
if fn != nil {
|
||||
funcPtr = util.AddHandle(c.ctx, AggregateConstructor(func() AggregateFunction {
|
||||
agg := fn()
|
||||
if win, ok := agg.(WindowFunction); ok {
|
||||
return win
|
||||
}
|
||||
return windowFunc{agg, name}
|
||||
}))
|
||||
}
|
||||
rc := res_t(c.call("sqlite3_create_window_function_go",
|
||||
stk_t(c.handle), stk_t(namePtr), stk_t(nArg),
|
||||
stk_t(flag), stk_t(funcPtr)))
|
||||
return c.error(rc)
|
||||
}
|
||||
|
||||
// AggregateConstructor is a an [AggregateFunction] constructor.
|
||||
type AggregateConstructor func() AggregateFunction
|
||||
|
||||
// AggregateFunction is the interface an aggregate function should implement.
|
||||
//
|
||||
// https://sqlite.org/appfunc.html
|
||||
|
@ -153,26 +192,24 @@ func collationCallback(ctx context.Context, mod api.Module, pArg, pDB ptr_t, eTe
|
|||
}
|
||||
|
||||
func compareCallback(ctx context.Context, mod api.Module, pApp ptr_t, nKey1 int32, pKey1 ptr_t, nKey2 int32, pKey2 ptr_t) uint32 {
|
||||
fn := util.GetHandle(ctx, pApp).(func(a, b []byte) int)
|
||||
fn := util.GetHandle(ctx, pApp).(CollatingFunction)
|
||||
return uint32(fn(util.View(mod, pKey1, int64(nKey1)), util.View(mod, pKey2, int64(nKey2))))
|
||||
}
|
||||
|
||||
func funcCallback(ctx context.Context, mod api.Module, pCtx, pApp ptr_t, nArg int32, pArg ptr_t) {
|
||||
args := getFuncArgs()
|
||||
defer putFuncArgs(args)
|
||||
db := ctx.Value(connKey{}).(*Conn)
|
||||
args := callbackArgs(db, nArg, pArg)
|
||||
defer returnArgs(args)
|
||||
fn := util.GetHandle(db.ctx, pApp).(ScalarFunction)
|
||||
callbackArgs(db, args[:nArg], pArg)
|
||||
fn(Context{db, pCtx}, args[:nArg]...)
|
||||
fn(Context{db, pCtx}, *args...)
|
||||
}
|
||||
|
||||
func stepCallback(ctx context.Context, mod api.Module, pCtx, pAgg, pApp ptr_t, nArg int32, pArg ptr_t) {
|
||||
args := getFuncArgs()
|
||||
defer putFuncArgs(args)
|
||||
db := ctx.Value(connKey{}).(*Conn)
|
||||
callbackArgs(db, args[:nArg], pArg)
|
||||
args := callbackArgs(db, nArg, pArg)
|
||||
defer returnArgs(args)
|
||||
fn, _ := callbackAggregate(db, pAgg, pApp)
|
||||
fn.Step(Context{db, pCtx}, args[:nArg]...)
|
||||
fn.Step(Context{db, pCtx}, *args...)
|
||||
}
|
||||
|
||||
func valueCallback(ctx context.Context, mod api.Module, pCtx, pAgg, pApp ptr_t, final int32) {
|
||||
|
@ -196,12 +233,11 @@ func valueCallback(ctx context.Context, mod api.Module, pCtx, pAgg, pApp ptr_t,
|
|||
}
|
||||
|
||||
func inverseCallback(ctx context.Context, mod api.Module, pCtx, pAgg ptr_t, nArg int32, pArg ptr_t) {
|
||||
args := getFuncArgs()
|
||||
defer putFuncArgs(args)
|
||||
db := ctx.Value(connKey{}).(*Conn)
|
||||
callbackArgs(db, args[:nArg], pArg)
|
||||
args := callbackArgs(db, nArg, pArg)
|
||||
defer returnArgs(args)
|
||||
fn := util.GetHandle(db.ctx, pAgg).(WindowFunction)
|
||||
fn.Inverse(Context{db, pCtx}, args[:nArg]...)
|
||||
fn.Inverse(Context{db, pCtx}, *args...)
|
||||
}
|
||||
|
||||
func callbackAggregate(db *Conn, pAgg, pApp ptr_t) (AggregateFunction, ptr_t) {
|
||||
|
@ -211,7 +247,7 @@ func callbackAggregate(db *Conn, pAgg, pApp ptr_t) (AggregateFunction, ptr_t) {
|
|||
}
|
||||
|
||||
// We need to create the aggregate.
|
||||
fn := util.GetHandle(db.ctx, pApp).(func() AggregateFunction)()
|
||||
fn := util.GetHandle(db.ctx, pApp).(AggregateConstructor)()
|
||||
if pAgg != 0 {
|
||||
handle := util.AddHandle(db.ctx, fn)
|
||||
util.Write32(db.mod, pAgg, handle)
|
||||
|
@ -220,25 +256,64 @@ func callbackAggregate(db *Conn, pAgg, pApp ptr_t) (AggregateFunction, ptr_t) {
|
|||
return fn, 0
|
||||
}
|
||||
|
||||
func callbackArgs(db *Conn, arg []Value, pArg ptr_t) {
|
||||
for i := range arg {
|
||||
arg[i] = Value{
|
||||
var (
|
||||
valueArgsPool sync.Pool
|
||||
valueArgsLen atomic.Int32
|
||||
)
|
||||
|
||||
func callbackArgs(db *Conn, nArg int32, pArg ptr_t) *[]Value {
|
||||
arg, ok := valueArgsPool.Get().(*[]Value)
|
||||
if !ok || cap(*arg) < int(nArg) {
|
||||
max := valueArgsLen.Or(nArg) | nArg
|
||||
lst := make([]Value, max)
|
||||
arg = &lst
|
||||
}
|
||||
lst := (*arg)[:nArg]
|
||||
for i := range lst {
|
||||
lst[i] = Value{
|
||||
c: db,
|
||||
handle: util.Read32[ptr_t](db.mod, pArg+ptr_t(i)*ptrlen),
|
||||
}
|
||||
}
|
||||
*arg = lst
|
||||
return arg
|
||||
}
|
||||
|
||||
var funcArgsPool sync.Pool
|
||||
|
||||
func putFuncArgs(p *[_MAX_FUNCTION_ARG]Value) {
|
||||
funcArgsPool.Put(p)
|
||||
func returnArgs(p *[]Value) {
|
||||
valueArgsPool.Put(p)
|
||||
}
|
||||
|
||||
func getFuncArgs() *[_MAX_FUNCTION_ARG]Value {
|
||||
if p := funcArgsPool.Get(); p == nil {
|
||||
return new([_MAX_FUNCTION_ARG]Value)
|
||||
} else {
|
||||
return p.(*[_MAX_FUNCTION_ARG]Value)
|
||||
type aggregateFunc struct {
|
||||
next func() (struct{}, bool)
|
||||
stop func()
|
||||
ctx Context
|
||||
arg []Value
|
||||
}
|
||||
|
||||
func (a *aggregateFunc) Step(ctx Context, arg ...Value) {
|
||||
a.ctx = ctx
|
||||
a.arg = append(a.arg[:0], arg...)
|
||||
if _, more := a.next(); !more {
|
||||
a.stop()
|
||||
}
|
||||
}
|
||||
|
||||
func (a *aggregateFunc) Value(ctx Context) {
|
||||
a.ctx = ctx
|
||||
a.stop()
|
||||
}
|
||||
|
||||
func (a *aggregateFunc) Close() error {
|
||||
a.stop()
|
||||
return nil
|
||||
}
|
||||
|
||||
type windowFunc struct {
|
||||
AggregateFunction
|
||||
name string
|
||||
}
|
||||
|
||||
func (w windowFunc) Inverse(ctx Context, arg ...Value) {
|
||||
// Implementing inverse allows certain queries that don't really need it to succeed.
|
||||
ctx.ResultError(util.ErrorString(w.name + ": may not be used as a window function"))
|
||||
}
|
||||
|
|
4
vendor/github.com/ncruces/go-sqlite3/internal/util/error.go
generated
vendored
4
vendor/github.com/ncruces/go-sqlite3/internal/util/error.go
generated
vendored
|
@ -75,7 +75,7 @@ func ErrorCodeString(rc uint32) string {
|
|||
return "sqlite3: unable to open database file"
|
||||
case PROTOCOL:
|
||||
return "sqlite3: locking protocol"
|
||||
case FORMAT:
|
||||
case EMPTY:
|
||||
break
|
||||
case SCHEMA:
|
||||
return "sqlite3: database schema has changed"
|
||||
|
@ -91,7 +91,7 @@ func ErrorCodeString(rc uint32) string {
|
|||
break
|
||||
case AUTH:
|
||||
return "sqlite3: authorization denied"
|
||||
case EMPTY:
|
||||
case FORMAT:
|
||||
break
|
||||
case RANGE:
|
||||
return "sqlite3: column index out of range"
|
||||
|
|
5
vendor/github.com/ncruces/go-sqlite3/internal/util/mem.go
generated
vendored
5
vendor/github.com/ncruces/go-sqlite3/internal/util/mem.go
generated
vendored
|
@ -135,11 +135,10 @@ func ReadString(mod api.Module, ptr Ptr_t, maxlen int64) string {
|
|||
panic(RangeErr)
|
||||
}
|
||||
}
|
||||
if i := bytes.IndexByte(buf, 0); i < 0 {
|
||||
panic(NoNulErr)
|
||||
} else {
|
||||
if i := bytes.IndexByte(buf, 0); i >= 0 {
|
||||
return string(buf[:i])
|
||||
}
|
||||
panic(NoNulErr)
|
||||
}
|
||||
|
||||
func WriteBytes(mod api.Module, ptr Ptr_t, b []byte) {
|
||||
|
|
38
vendor/github.com/ncruces/go-sqlite3/sqlite.go
generated
vendored
38
vendor/github.com/ncruces/go-sqlite3/sqlite.go
generated
vendored
|
@ -120,33 +120,33 @@ func (sqlt *sqlite) error(rc res_t, handle ptr_t, sql ...string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
err := Error{code: rc}
|
||||
|
||||
if err.Code() == NOMEM || err.ExtendedCode() == IOERR_NOMEM {
|
||||
if ErrorCode(rc) == NOMEM || xErrorCode(rc) == IOERR_NOMEM {
|
||||
panic(util.OOMErr)
|
||||
}
|
||||
|
||||
if ptr := ptr_t(sqlt.call("sqlite3_errstr", stk_t(rc))); ptr != 0 {
|
||||
err.str = util.ReadString(sqlt.mod, ptr, _MAX_NAME)
|
||||
}
|
||||
|
||||
if handle != 0 {
|
||||
var msg, query string
|
||||
if ptr := ptr_t(sqlt.call("sqlite3_errmsg", stk_t(handle))); ptr != 0 {
|
||||
err.msg = util.ReadString(sqlt.mod, ptr, _MAX_LENGTH)
|
||||
msg = util.ReadString(sqlt.mod, ptr, _MAX_LENGTH)
|
||||
switch {
|
||||
case msg == "not an error":
|
||||
msg = ""
|
||||
case msg == util.ErrorCodeString(uint32(rc))[len("sqlite3: "):]:
|
||||
msg = ""
|
||||
}
|
||||
}
|
||||
|
||||
if len(sql) != 0 {
|
||||
if i := int32(sqlt.call("sqlite3_error_offset", stk_t(handle))); i != -1 {
|
||||
err.sql = sql[0][i:]
|
||||
query = sql[0][i:]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch err.msg {
|
||||
case err.str, "not an error":
|
||||
err.msg = ""
|
||||
if msg != "" || query != "" {
|
||||
return &Error{code: rc, msg: msg, sql: query}
|
||||
}
|
||||
}
|
||||
return &err
|
||||
return xErrorCode(rc)
|
||||
}
|
||||
|
||||
func (sqlt *sqlite) getfn(name string) api.Function {
|
||||
|
@ -212,14 +212,10 @@ func (sqlt *sqlite) realloc(ptr ptr_t, size int64) ptr_t {
|
|||
}
|
||||
|
||||
func (sqlt *sqlite) newBytes(b []byte) ptr_t {
|
||||
if (*[0]byte)(b) == nil {
|
||||
if len(b) == 0 {
|
||||
return 0
|
||||
}
|
||||
size := len(b)
|
||||
if size == 0 {
|
||||
size = 1
|
||||
}
|
||||
ptr := sqlt.new(int64(size))
|
||||
ptr := sqlt.new(int64(len(b)))
|
||||
util.WriteBytes(sqlt.mod, ptr, b)
|
||||
return ptr
|
||||
}
|
||||
|
@ -288,7 +284,7 @@ func (a *arena) new(size int64) ptr_t {
|
|||
}
|
||||
|
||||
func (a *arena) bytes(b []byte) ptr_t {
|
||||
if (*[0]byte)(b) == nil {
|
||||
if len(b) == 0 {
|
||||
return 0
|
||||
}
|
||||
ptr := a.new(int64(len(b)))
|
||||
|
|
140
vendor/github.com/ncruces/go-sqlite3/stmt.go
generated
vendored
140
vendor/github.com/ncruces/go-sqlite3/stmt.go
generated
vendored
|
@ -106,7 +106,14 @@ func (s *Stmt) Busy() bool {
|
|||
//
|
||||
// https://sqlite.org/c3ref/step.html
|
||||
func (s *Stmt) Step() bool {
|
||||
s.c.checkInterrupt(s.c.handle)
|
||||
if s.c.interrupt.Err() != nil {
|
||||
s.err = INTERRUPT
|
||||
return false
|
||||
}
|
||||
return s.step()
|
||||
}
|
||||
|
||||
func (s *Stmt) step() bool {
|
||||
rc := res_t(s.c.call("sqlite3_step", stk_t(s.handle)))
|
||||
switch rc {
|
||||
case _ROW:
|
||||
|
@ -131,7 +138,11 @@ func (s *Stmt) Err() error {
|
|||
// Exec is a convenience function that repeatedly calls [Stmt.Step] until it returns false,
|
||||
// then calls [Stmt.Reset] to reset the statement and get any error that occurred.
|
||||
func (s *Stmt) Exec() error {
|
||||
for s.Step() {
|
||||
if s.c.interrupt.Err() != nil {
|
||||
return INTERRUPT
|
||||
}
|
||||
// TODO: implement this in C.
|
||||
for s.step() {
|
||||
}
|
||||
return s.Reset()
|
||||
}
|
||||
|
@ -254,13 +265,15 @@ func (s *Stmt) BindText(param int, value string) error {
|
|||
|
||||
// BindRawText binds a []byte to the prepared statement as text.
|
||||
// The leftmost SQL parameter has an index of 1.
|
||||
// Binding a nil slice is the same as calling [Stmt.BindNull].
|
||||
//
|
||||
// https://sqlite.org/c3ref/bind_blob.html
|
||||
func (s *Stmt) BindRawText(param int, value []byte) error {
|
||||
if len(value) > _MAX_LENGTH {
|
||||
return TOOBIG
|
||||
}
|
||||
if len(value) == 0 {
|
||||
return s.BindText(param, "")
|
||||
}
|
||||
ptr := s.c.newBytes(value)
|
||||
rc := res_t(s.c.call("sqlite3_bind_text_go",
|
||||
stk_t(s.handle), stk_t(param),
|
||||
|
@ -270,13 +283,15 @@ func (s *Stmt) BindRawText(param int, value []byte) error {
|
|||
|
||||
// BindBlob binds a []byte to the prepared statement.
|
||||
// The leftmost SQL parameter has an index of 1.
|
||||
// Binding a nil slice is the same as calling [Stmt.BindNull].
|
||||
//
|
||||
// https://sqlite.org/c3ref/bind_blob.html
|
||||
func (s *Stmt) BindBlob(param int, value []byte) error {
|
||||
if len(value) > _MAX_LENGTH {
|
||||
return TOOBIG
|
||||
}
|
||||
if len(value) == 0 {
|
||||
return s.BindZeroBlob(param, 0)
|
||||
}
|
||||
ptr := s.c.newBytes(value)
|
||||
rc := res_t(s.c.call("sqlite3_bind_blob_go",
|
||||
stk_t(s.handle), stk_t(param),
|
||||
|
@ -560,7 +575,7 @@ func (s *Stmt) ColumnBlob(col int, buf []byte) []byte {
|
|||
func (s *Stmt) ColumnRawText(col int) []byte {
|
||||
ptr := ptr_t(s.c.call("sqlite3_column_text",
|
||||
stk_t(s.handle), stk_t(col)))
|
||||
return s.columnRawBytes(col, ptr)
|
||||
return s.columnRawBytes(col, ptr, 1)
|
||||
}
|
||||
|
||||
// ColumnRawBlob returns the value of the result column as a []byte.
|
||||
|
@ -572,10 +587,10 @@ func (s *Stmt) ColumnRawText(col int) []byte {
|
|||
func (s *Stmt) ColumnRawBlob(col int) []byte {
|
||||
ptr := ptr_t(s.c.call("sqlite3_column_blob",
|
||||
stk_t(s.handle), stk_t(col)))
|
||||
return s.columnRawBytes(col, ptr)
|
||||
return s.columnRawBytes(col, ptr, 0)
|
||||
}
|
||||
|
||||
func (s *Stmt) columnRawBytes(col int, ptr ptr_t) []byte {
|
||||
func (s *Stmt) columnRawBytes(col int, ptr ptr_t, nul int32) []byte {
|
||||
if ptr == 0 {
|
||||
rc := res_t(s.c.call("sqlite3_errcode", stk_t(s.c.handle)))
|
||||
if rc != _ROW && rc != _DONE {
|
||||
|
@ -586,7 +601,7 @@ func (s *Stmt) columnRawBytes(col int, ptr ptr_t) []byte {
|
|||
|
||||
n := int32(s.c.call("sqlite3_column_bytes",
|
||||
stk_t(s.handle), stk_t(col)))
|
||||
return util.View(s.c.mod, ptr, int64(n))
|
||||
return util.View(s.c.mod, ptr, int64(n+nul))[:n]
|
||||
}
|
||||
|
||||
// ColumnJSON parses the JSON-encoded value of the result column
|
||||
|
@ -633,22 +648,12 @@ func (s *Stmt) ColumnValue(col int) Value {
|
|||
// [INTEGER] columns will be retrieved as int64 values,
|
||||
// [FLOAT] as float64, [NULL] as nil,
|
||||
// [TEXT] as string, and [BLOB] as []byte.
|
||||
// Any []byte are owned by SQLite and may be invalidated by
|
||||
// subsequent calls to [Stmt] methods.
|
||||
func (s *Stmt) Columns(dest ...any) error {
|
||||
defer s.c.arena.mark()()
|
||||
count := int64(len(dest))
|
||||
typePtr := s.c.arena.new(count)
|
||||
dataPtr := s.c.arena.new(count * 8)
|
||||
|
||||
rc := res_t(s.c.call("sqlite3_columns_go",
|
||||
stk_t(s.handle), stk_t(count), stk_t(typePtr), stk_t(dataPtr)))
|
||||
if err := s.c.error(rc); err != nil {
|
||||
types, ptr, err := s.columns(int64(len(dest)))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
types := util.View(s.c.mod, typePtr, count)
|
||||
|
||||
// Avoid bounds checks on types below.
|
||||
if len(types) != len(dest) {
|
||||
panic(util.AssertErr())
|
||||
|
@ -657,26 +662,95 @@ func (s *Stmt) Columns(dest ...any) error {
|
|||
for i := range dest {
|
||||
switch types[i] {
|
||||
case byte(INTEGER):
|
||||
dest[i] = util.Read64[int64](s.c.mod, dataPtr)
|
||||
dest[i] = util.Read64[int64](s.c.mod, ptr)
|
||||
case byte(FLOAT):
|
||||
dest[i] = util.ReadFloat64(s.c.mod, dataPtr)
|
||||
dest[i] = util.ReadFloat64(s.c.mod, ptr)
|
||||
case byte(NULL):
|
||||
dest[i] = nil
|
||||
default:
|
||||
ptr := util.Read32[ptr_t](s.c.mod, dataPtr+0)
|
||||
if ptr == 0 {
|
||||
dest[i] = []byte{}
|
||||
continue
|
||||
}
|
||||
len := util.Read32[int32](s.c.mod, dataPtr+4)
|
||||
buf := util.View(s.c.mod, ptr, int64(len))
|
||||
if types[i] == byte(TEXT) {
|
||||
case byte(TEXT):
|
||||
len := util.Read32[int32](s.c.mod, ptr+4)
|
||||
if len != 0 {
|
||||
ptr := util.Read32[ptr_t](s.c.mod, ptr)
|
||||
buf := util.View(s.c.mod, ptr, int64(len))
|
||||
dest[i] = string(buf)
|
||||
} else {
|
||||
dest[i] = buf
|
||||
dest[i] = ""
|
||||
}
|
||||
case byte(BLOB):
|
||||
len := util.Read32[int32](s.c.mod, ptr+4)
|
||||
if len != 0 {
|
||||
ptr := util.Read32[ptr_t](s.c.mod, ptr)
|
||||
buf := util.View(s.c.mod, ptr, int64(len))
|
||||
tmp, _ := dest[i].([]byte)
|
||||
dest[i] = append(tmp[:0], buf...)
|
||||
} else {
|
||||
dest[i], _ = dest[i].([]byte)
|
||||
}
|
||||
}
|
||||
dataPtr += 8
|
||||
ptr += 8
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ColumnsRaw populates result columns into the provided slice.
|
||||
// The slice must have [Stmt.ColumnCount] length.
|
||||
//
|
||||
// [INTEGER] columns will be retrieved as int64 values,
|
||||
// [FLOAT] as float64, [NULL] as nil,
|
||||
// [TEXT] and [BLOB] as []byte.
|
||||
// Any []byte are owned by SQLite and may be invalidated by
|
||||
// subsequent calls to [Stmt] methods.
|
||||
func (s *Stmt) ColumnsRaw(dest ...any) error {
|
||||
types, ptr, err := s.columns(int64(len(dest)))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Avoid bounds checks on types below.
|
||||
if len(types) != len(dest) {
|
||||
panic(util.AssertErr())
|
||||
}
|
||||
|
||||
for i := range dest {
|
||||
switch types[i] {
|
||||
case byte(INTEGER):
|
||||
dest[i] = util.Read64[int64](s.c.mod, ptr)
|
||||
case byte(FLOAT):
|
||||
dest[i] = util.ReadFloat64(s.c.mod, ptr)
|
||||
case byte(NULL):
|
||||
dest[i] = nil
|
||||
default:
|
||||
len := util.Read32[int32](s.c.mod, ptr+4)
|
||||
if len == 0 && types[i] == byte(BLOB) {
|
||||
dest[i] = []byte{}
|
||||
} else {
|
||||
cap := len
|
||||
if types[i] == byte(TEXT) {
|
||||
cap++
|
||||
}
|
||||
ptr := util.Read32[ptr_t](s.c.mod, ptr)
|
||||
buf := util.View(s.c.mod, ptr, int64(cap))[:len]
|
||||
dest[i] = buf
|
||||
}
|
||||
}
|
||||
ptr += 8
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Stmt) columns(count int64) ([]byte, ptr_t, error) {
|
||||
defer s.c.arena.mark()()
|
||||
typePtr := s.c.arena.new(count)
|
||||
dataPtr := s.c.arena.new(count * 8)
|
||||
|
||||
rc := res_t(s.c.call("sqlite3_columns_go",
|
||||
stk_t(s.handle), stk_t(count), stk_t(typePtr), stk_t(dataPtr)))
|
||||
if rc == res_t(MISUSE) {
|
||||
return nil, 0, MISUSE
|
||||
}
|
||||
if err := s.c.error(rc); err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
return util.View(s.c.mod, typePtr, count), dataPtr, nil
|
||||
}
|
||||
|
|
24
vendor/github.com/ncruces/go-sqlite3/txn.go
generated
vendored
24
vendor/github.com/ncruces/go-sqlite3/txn.go
generated
vendored
|
@ -2,7 +2,6 @@ package sqlite3
|
|||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"math/rand"
|
||||
"runtime"
|
||||
"strconv"
|
||||
|
@ -21,11 +20,13 @@ type Txn struct {
|
|||
}
|
||||
|
||||
// Begin starts a deferred transaction.
|
||||
// It panics if a transaction is in-progress.
|
||||
// For nested transactions, use [Conn.Savepoint].
|
||||
//
|
||||
// https://sqlite.org/lang_transaction.html
|
||||
func (c *Conn) Begin() Txn {
|
||||
// BEGIN even if interrupted.
|
||||
err := c.txnExecInterrupted(`BEGIN DEFERRED`)
|
||||
err := c.exec(`BEGIN DEFERRED`)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
@ -120,7 +121,8 @@ func (tx Txn) Commit() error {
|
|||
//
|
||||
// https://sqlite.org/lang_transaction.html
|
||||
func (tx Txn) Rollback() error {
|
||||
return tx.c.txnExecInterrupted(`ROLLBACK`)
|
||||
// ROLLBACK even if interrupted.
|
||||
return tx.c.exec(`ROLLBACK`)
|
||||
}
|
||||
|
||||
// Savepoint is a marker within a transaction
|
||||
|
@ -143,7 +145,7 @@ func (c *Conn) Savepoint() Savepoint {
|
|||
// Names can be reused, but this makes catching bugs more likely.
|
||||
name = QuoteIdentifier(name + "_" + strconv.Itoa(int(rand.Int31())))
|
||||
|
||||
err := c.txnExecInterrupted(`SAVEPOINT ` + name)
|
||||
err := c.exec(`SAVEPOINT ` + name)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
@ -199,7 +201,7 @@ func (s Savepoint) Release(errp *error) {
|
|||
return
|
||||
}
|
||||
// ROLLBACK and RELEASE even if interrupted.
|
||||
err := s.c.txnExecInterrupted(`ROLLBACK TO ` + s.name + `; RELEASE ` + s.name)
|
||||
err := s.c.exec(`ROLLBACK TO ` + s.name + `; RELEASE ` + s.name)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
@ -212,17 +214,7 @@ func (s Savepoint) Release(errp *error) {
|
|||
// https://sqlite.org/lang_transaction.html
|
||||
func (s Savepoint) Rollback() error {
|
||||
// ROLLBACK even if interrupted.
|
||||
return s.c.txnExecInterrupted(`ROLLBACK TO ` + s.name)
|
||||
}
|
||||
|
||||
func (c *Conn) txnExecInterrupted(sql string) error {
|
||||
err := c.Exec(sql)
|
||||
if errors.Is(err, INTERRUPT) {
|
||||
old := c.SetInterrupt(context.Background())
|
||||
defer c.SetInterrupt(old)
|
||||
err = c.Exec(sql)
|
||||
}
|
||||
return err
|
||||
return s.c.exec(`ROLLBACK TO ` + s.name)
|
||||
}
|
||||
|
||||
// TxnState determines the transaction state of a database.
|
||||
|
|
16
vendor/github.com/ncruces/go-sqlite3/util/osutil/open.go
generated
vendored
16
vendor/github.com/ncruces/go-sqlite3/util/osutil/open.go
generated
vendored
|
@ -1,16 +0,0 @@
|
|||
//go:build !windows
|
||||
|
||||
package osutil
|
||||
|
||||
import (
|
||||
"io/fs"
|
||||
"os"
|
||||
)
|
||||
|
||||
// OpenFile behaves the same as [os.OpenFile],
|
||||
// except on Windows it sets [syscall.FILE_SHARE_DELETE].
|
||||
//
|
||||
// See: https://go.dev/issue/32088#issuecomment-502850674
|
||||
func OpenFile(name string, flag int, perm fs.FileMode) (*os.File, error) {
|
||||
return os.OpenFile(name, flag, perm)
|
||||
}
|
115
vendor/github.com/ncruces/go-sqlite3/util/osutil/open_windows.go
generated
vendored
115
vendor/github.com/ncruces/go-sqlite3/util/osutil/open_windows.go
generated
vendored
|
@ -1,115 +0,0 @@
|
|||
package osutil
|
||||
|
||||
import (
|
||||
"io/fs"
|
||||
"os"
|
||||
. "syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// OpenFile behaves the same as [os.OpenFile],
|
||||
// except on Windows it sets [syscall.FILE_SHARE_DELETE].
|
||||
//
|
||||
// See: https://go.dev/issue/32088#issuecomment-502850674
|
||||
func OpenFile(name string, flag int, perm fs.FileMode) (*os.File, error) {
|
||||
if name == "" {
|
||||
return nil, &os.PathError{Op: "open", Path: name, Err: ENOENT}
|
||||
}
|
||||
r, e := syscallOpen(name, flag|O_CLOEXEC, uint32(perm.Perm()))
|
||||
if e != nil {
|
||||
return nil, &os.PathError{Op: "open", Path: name, Err: e}
|
||||
}
|
||||
return os.NewFile(uintptr(r), name), nil
|
||||
}
|
||||
|
||||
// syscallOpen is a copy of [syscall.Open]
|
||||
// that uses [syscall.FILE_SHARE_DELETE].
|
||||
//
|
||||
// https://go.dev/src/syscall/syscall_windows.go
|
||||
func syscallOpen(path string, mode int, perm uint32) (fd Handle, err error) {
|
||||
if len(path) == 0 {
|
||||
return InvalidHandle, ERROR_FILE_NOT_FOUND
|
||||
}
|
||||
pathp, err := UTF16PtrFromString(path)
|
||||
if err != nil {
|
||||
return InvalidHandle, err
|
||||
}
|
||||
var access uint32
|
||||
switch mode & (O_RDONLY | O_WRONLY | O_RDWR) {
|
||||
case O_RDONLY:
|
||||
access = GENERIC_READ
|
||||
case O_WRONLY:
|
||||
access = GENERIC_WRITE
|
||||
case O_RDWR:
|
||||
access = GENERIC_READ | GENERIC_WRITE
|
||||
}
|
||||
if mode&O_CREAT != 0 {
|
||||
access |= GENERIC_WRITE
|
||||
}
|
||||
if mode&O_APPEND != 0 {
|
||||
access &^= GENERIC_WRITE
|
||||
access |= FILE_APPEND_DATA
|
||||
}
|
||||
sharemode := uint32(FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE)
|
||||
var sa *SecurityAttributes
|
||||
if mode&O_CLOEXEC == 0 {
|
||||
sa = makeInheritSa()
|
||||
}
|
||||
var createmode uint32
|
||||
switch {
|
||||
case mode&(O_CREAT|O_EXCL) == (O_CREAT | O_EXCL):
|
||||
createmode = CREATE_NEW
|
||||
case mode&(O_CREAT|O_TRUNC) == (O_CREAT | O_TRUNC):
|
||||
createmode = CREATE_ALWAYS
|
||||
case mode&O_CREAT == O_CREAT:
|
||||
createmode = OPEN_ALWAYS
|
||||
case mode&O_TRUNC == O_TRUNC:
|
||||
createmode = TRUNCATE_EXISTING
|
||||
default:
|
||||
createmode = OPEN_EXISTING
|
||||
}
|
||||
var attrs uint32 = FILE_ATTRIBUTE_NORMAL
|
||||
if perm&S_IWRITE == 0 {
|
||||
attrs = FILE_ATTRIBUTE_READONLY
|
||||
if createmode == CREATE_ALWAYS {
|
||||
const _ERROR_BAD_NETPATH = Errno(53)
|
||||
// We have been asked to create a read-only file.
|
||||
// If the file already exists, the semantics of
|
||||
// the Unix open system call is to preserve the
|
||||
// existing permissions. If we pass CREATE_ALWAYS
|
||||
// and FILE_ATTRIBUTE_READONLY to CreateFile,
|
||||
// and the file already exists, CreateFile will
|
||||
// change the file permissions.
|
||||
// Avoid that to preserve the Unix semantics.
|
||||
h, e := CreateFile(pathp, access, sharemode, sa, TRUNCATE_EXISTING, FILE_ATTRIBUTE_NORMAL, 0)
|
||||
switch e {
|
||||
case ERROR_FILE_NOT_FOUND, _ERROR_BAD_NETPATH, ERROR_PATH_NOT_FOUND:
|
||||
// File does not exist. These are the same
|
||||
// errors as Errno.Is checks for ErrNotExist.
|
||||
// Carry on to create the file.
|
||||
default:
|
||||
// Success or some different error.
|
||||
return h, e
|
||||
}
|
||||
}
|
||||
}
|
||||
if createmode == OPEN_EXISTING && access == GENERIC_READ {
|
||||
// Necessary for opening directory handles.
|
||||
attrs |= FILE_FLAG_BACKUP_SEMANTICS
|
||||
}
|
||||
if mode&O_SYNC != 0 {
|
||||
const _FILE_FLAG_WRITE_THROUGH = 0x80000000
|
||||
attrs |= _FILE_FLAG_WRITE_THROUGH
|
||||
}
|
||||
if mode&O_NONBLOCK != 0 {
|
||||
attrs |= FILE_FLAG_OVERLAPPED
|
||||
}
|
||||
return CreateFile(pathp, access, sharemode, sa, createmode, attrs, 0)
|
||||
}
|
||||
|
||||
func makeInheritSa() *SecurityAttributes {
|
||||
var sa SecurityAttributes
|
||||
sa.Length = uint32(unsafe.Sizeof(sa))
|
||||
sa.InheritHandle = 1
|
||||
return &sa
|
||||
}
|
33
vendor/github.com/ncruces/go-sqlite3/util/osutil/osfs.go
generated
vendored
33
vendor/github.com/ncruces/go-sqlite3/util/osutil/osfs.go
generated
vendored
|
@ -1,33 +0,0 @@
|
|||
package osutil
|
||||
|
||||
import (
|
||||
"io/fs"
|
||||
"os"
|
||||
)
|
||||
|
||||
// FS implements [fs.FS], [fs.StatFS], and [fs.ReadFileFS]
|
||||
// using package [os].
|
||||
//
|
||||
// This filesystem does not respect [fs.ValidPath] rules,
|
||||
// and fails [testing/fstest.TestFS]!
|
||||
//
|
||||
// Still, it can be a useful tool to unify implementations
|
||||
// that can access either the [os] filesystem or an [fs.FS].
|
||||
// It's OK to use this to open files, but you should avoid
|
||||
// opening directories, resolving paths, or walking the file system.
|
||||
type FS struct{}
|
||||
|
||||
// Open implements [fs.FS].
|
||||
func (FS) Open(name string) (fs.File, error) {
|
||||
return OpenFile(name, os.O_RDONLY, 0)
|
||||
}
|
||||
|
||||
// ReadFileFS implements [fs.StatFS].
|
||||
func (FS) Stat(name string) (fs.FileInfo, error) {
|
||||
return os.Stat(name)
|
||||
}
|
||||
|
||||
// ReadFile implements [fs.ReadFileFS].
|
||||
func (FS) ReadFile(name string) ([]byte, error) {
|
||||
return os.ReadFile(name)
|
||||
}
|
2
vendor/github.com/ncruces/go-sqlite3/util/osutil/osutil.go
generated
vendored
2
vendor/github.com/ncruces/go-sqlite3/util/osutil/osutil.go
generated
vendored
|
@ -1,2 +0,0 @@
|
|||
// Package osutil implements operating system utilities.
|
||||
package osutil
|
2
vendor/github.com/ncruces/go-sqlite3/util/sql3util/sql3util.go
generated
vendored
2
vendor/github.com/ncruces/go-sqlite3/util/sql3util/sql3util.go
generated
vendored
|
@ -5,5 +5,5 @@ package sql3util
|
|||
//
|
||||
// https://sqlite.org/fileformat.html#pages
|
||||
func ValidPageSize(s int) bool {
|
||||
return 512 <= s && s <= 65536 && s&(s-1) == 0
|
||||
return s&(s-1) == 0 && 512 <= s && s <= 65536
|
||||
}
|
||||
|
|
8
vendor/github.com/ncruces/go-sqlite3/value.go
generated
vendored
8
vendor/github.com/ncruces/go-sqlite3/value.go
generated
vendored
|
@ -139,7 +139,7 @@ func (v Value) Blob(buf []byte) []byte {
|
|||
// https://sqlite.org/c3ref/value_blob.html
|
||||
func (v Value) RawText() []byte {
|
||||
ptr := ptr_t(v.c.call("sqlite3_value_text", v.protected()))
|
||||
return v.rawBytes(ptr)
|
||||
return v.rawBytes(ptr, 1)
|
||||
}
|
||||
|
||||
// RawBlob returns the value as a []byte.
|
||||
|
@ -149,16 +149,16 @@ func (v Value) RawText() []byte {
|
|||
// https://sqlite.org/c3ref/value_blob.html
|
||||
func (v Value) RawBlob() []byte {
|
||||
ptr := ptr_t(v.c.call("sqlite3_value_blob", v.protected()))
|
||||
return v.rawBytes(ptr)
|
||||
return v.rawBytes(ptr, 0)
|
||||
}
|
||||
|
||||
func (v Value) rawBytes(ptr ptr_t) []byte {
|
||||
func (v Value) rawBytes(ptr ptr_t, nul int32) []byte {
|
||||
if ptr == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
n := int32(v.c.call("sqlite3_value_bytes", v.protected()))
|
||||
return util.View(v.c.mod, ptr, int64(n))
|
||||
return util.View(v.c.mod, ptr, int64(n+nul))[:n]
|
||||
}
|
||||
|
||||
// Pointer gets the pointer associated with this value,
|
||||
|
|
35
vendor/github.com/ncruces/go-sqlite3/vfs/README.md
generated
vendored
35
vendor/github.com/ncruces/go-sqlite3/vfs/README.md
generated
vendored
|
@ -6,22 +6,30 @@ It replaces the default SQLite VFS with a **pure Go** implementation,
|
|||
and exposes [interfaces](https://pkg.go.dev/github.com/ncruces/go-sqlite3/vfs#VFS)
|
||||
that should allow you to implement your own [custom VFSes](#custom-vfses).
|
||||
|
||||
Since it is a from scratch reimplementation,
|
||||
there are naturally some ways it deviates from the original.
|
||||
See the [support matrix](https://github.com/ncruces/go-sqlite3/wiki/Support-matrix)
|
||||
for the list of supported OS and CPU architectures.
|
||||
|
||||
The main differences are [file locking](#file-locking) and [WAL mode](#write-ahead-logging) support.
|
||||
Since this is a from scratch reimplementation,
|
||||
there are naturally some ways it deviates from the original.
|
||||
It's also not as battle tested as the original.
|
||||
|
||||
The main differences to be aware of are
|
||||
[file locking](#file-locking) and
|
||||
[WAL mode](#write-ahead-logging) support.
|
||||
|
||||
### File Locking
|
||||
|
||||
POSIX advisory locks, which SQLite uses on Unix, are
|
||||
[broken by design](https://github.com/sqlite/sqlite/blob/b74eb0/src/os_unix.c#L1073-L1161).
|
||||
POSIX advisory locks,
|
||||
which SQLite uses on [Unix](https://github.com/sqlite/sqlite/blob/5d60f4/src/os_unix.c#L13-L14),
|
||||
are [broken by design](https://github.com/sqlite/sqlite/blob/5d60f4/src/os_unix.c#L1074-L1162).
|
||||
Instead, on Linux and macOS, this package uses
|
||||
[OFD locks](https://www.gnu.org/software/libc/manual/html_node/Open-File-Description-Locks.html)
|
||||
to synchronize access to database files.
|
||||
|
||||
This package can also use
|
||||
[BSD locks](https://man.freebsd.org/cgi/man.cgi?query=flock&sektion=2),
|
||||
albeit with reduced concurrency (`BEGIN IMMEDIATE` behaves like `BEGIN EXCLUSIVE`).
|
||||
albeit with reduced concurrency (`BEGIN IMMEDIATE` behaves like `BEGIN EXCLUSIVE`,
|
||||
[docs](https://sqlite.org/lang_transaction.html#immediate)).
|
||||
BSD locks are the default on BSD and illumos,
|
||||
but you can opt into them with the `sqlite3_flock` build tag.
|
||||
|
||||
|
@ -44,11 +52,11 @@ to check if your build supports file locking.
|
|||
|
||||
### Write-Ahead Logging
|
||||
|
||||
On Unix, this package may use `mmap` to implement
|
||||
On Unix, this package uses `mmap` to implement
|
||||
[shared-memory for the WAL-index](https://sqlite.org/wal.html#implementation_of_shared_memory_for_the_wal_index),
|
||||
like SQLite.
|
||||
|
||||
On Windows, this package may use `MapViewOfFile`, like SQLite.
|
||||
On Windows, this package uses `MapViewOfFile`, like SQLite.
|
||||
|
||||
You can also opt into a cross-platform, in-process, memory sharing implementation
|
||||
with the `sqlite3_dotlk` build tag.
|
||||
|
@ -63,6 +71,11 @@ you must disable connection pooling by calling
|
|||
You can use [`vfs.SupportsSharedMemory`](https://pkg.go.dev/github.com/ncruces/go-sqlite3/vfs#SupportsSharedMemory)
|
||||
to check if your build supports shared memory.
|
||||
|
||||
### Blocking Locks
|
||||
|
||||
On Windows and macOS, this package implements
|
||||
[Wal-mode blocking locks](https://sqlite.org/src/doc/tip/doc/wal-lock.md).
|
||||
|
||||
### Batch-Atomic Write
|
||||
|
||||
On Linux, this package may support
|
||||
|
@ -94,8 +107,10 @@ The VFS can be customized with a few build tags:
|
|||
> [`unix-flock` VFS](https://sqlite.org/compile.html#enable_locking_style);
|
||||
> `sqlite3_dotlk` builds are compatible with the
|
||||
> [`unix-dotfile` VFS](https://sqlite.org/compile.html#enable_locking_style).
|
||||
> If incompatible file locking is used, accessing databases concurrently with
|
||||
> _other_ SQLite libraries will eventually corrupt data.
|
||||
|
||||
> [!CAUTION]
|
||||
> Concurrently accessing databases using incompatible VFSes
|
||||
> will eventually corrupt data.
|
||||
|
||||
### Custom VFSes
|
||||
|
||||
|
|
24
vendor/github.com/ncruces/go-sqlite3/vfs/cksm.go
generated
vendored
24
vendor/github.com/ncruces/go-sqlite3/vfs/cksm.go
generated
vendored
|
@ -49,9 +49,7 @@ func (c cksmFile) ReadAt(p []byte, off int64) (n int, err error) {
|
|||
n, err = c.File.ReadAt(p, off)
|
||||
p = p[:n]
|
||||
|
||||
// SQLite is reading the header of a database file.
|
||||
if c.isDB && off == 0 && len(p) >= 100 &&
|
||||
bytes.HasPrefix(p, []byte("SQLite format 3\000")) {
|
||||
if isHeader(c.isDB, p, off) {
|
||||
c.init((*[100]byte)(p))
|
||||
}
|
||||
|
||||
|
@ -67,9 +65,7 @@ func (c cksmFile) ReadAt(p []byte, off int64) (n int, err error) {
|
|||
}
|
||||
|
||||
func (c cksmFile) WriteAt(p []byte, off int64) (n int, err error) {
|
||||
// SQLite is writing the first page of a database file.
|
||||
if c.isDB && off == 0 && len(p) >= 100 &&
|
||||
bytes.HasPrefix(p, []byte("SQLite format 3\000")) {
|
||||
if isHeader(c.isDB, p, off) {
|
||||
c.init((*[100]byte)(p))
|
||||
}
|
||||
|
||||
|
@ -116,9 +112,11 @@ func (c cksmFile) fileControl(ctx context.Context, mod api.Module, op _FcntlOpco
|
|||
c.inCkpt = true
|
||||
case _FCNTL_CKPT_DONE:
|
||||
c.inCkpt = false
|
||||
}
|
||||
if rc := vfsFileControlImpl(ctx, mod, c, op, pArg); rc != _NOTFOUND {
|
||||
return rc
|
||||
case _FCNTL_PRAGMA:
|
||||
rc := vfsFileControlImpl(ctx, mod, c, op, pArg)
|
||||
if rc != _NOTFOUND {
|
||||
return rc
|
||||
}
|
||||
}
|
||||
return vfsFileControlImpl(ctx, mod, c.File, op, pArg)
|
||||
}
|
||||
|
@ -135,6 +133,14 @@ func (f *cksmFlags) init(header *[100]byte) {
|
|||
}
|
||||
}
|
||||
|
||||
func isHeader(isDB bool, p []byte, off int64) bool {
|
||||
check := sql3util.ValidPageSize(len(p))
|
||||
if isDB {
|
||||
check = off == 0 && len(p) >= 100
|
||||
}
|
||||
return check && bytes.HasPrefix(p, []byte("SQLite format 3\000"))
|
||||
}
|
||||
|
||||
func cksmCompute(a []byte) (cksm [8]byte) {
|
||||
var s1, s2 uint32
|
||||
for len(a) >= 8 {
|
||||
|
|
28
vendor/github.com/ncruces/go-sqlite3/vfs/file.go
generated
vendored
28
vendor/github.com/ncruces/go-sqlite3/vfs/file.go
generated
vendored
|
@ -6,9 +6,8 @@ import (
|
|||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"syscall"
|
||||
|
||||
"github.com/ncruces/go-sqlite3/util/osutil"
|
||||
)
|
||||
|
||||
type vfsOS struct{}
|
||||
|
@ -40,7 +39,7 @@ func (vfsOS) Delete(path string, syncDir bool) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if canSyncDirs && syncDir {
|
||||
if isUnix && syncDir {
|
||||
f, err := os.Open(filepath.Dir(path))
|
||||
if err != nil {
|
||||
return _OK
|
||||
|
@ -96,7 +95,7 @@ func (vfsOS) OpenFilename(name *Filename, flags OpenFlag) (File, OpenFlag, error
|
|||
if name == nil {
|
||||
f, err = os.CreateTemp(os.Getenv("SQLITE_TMPDIR"), "*.db")
|
||||
} else {
|
||||
f, err = osutil.OpenFile(name.String(), oflags, 0666)
|
||||
f, err = os.OpenFile(name.String(), oflags, 0666)
|
||||
}
|
||||
if err != nil {
|
||||
if name == nil {
|
||||
|
@ -118,15 +117,17 @@ func (vfsOS) OpenFilename(name *Filename, flags OpenFlag) (File, OpenFlag, error
|
|||
return nil, flags, _IOERR_FSTAT
|
||||
}
|
||||
}
|
||||
if flags&OPEN_DELETEONCLOSE != 0 {
|
||||
if isUnix && flags&OPEN_DELETEONCLOSE != 0 {
|
||||
os.Remove(f.Name())
|
||||
}
|
||||
|
||||
file := vfsFile{
|
||||
File: f,
|
||||
psow: true,
|
||||
atomic: osBatchAtomic(f),
|
||||
readOnly: flags&OPEN_READONLY != 0,
|
||||
syncDir: canSyncDirs && isCreate && isJournl,
|
||||
syncDir: isUnix && isCreate && isJournl,
|
||||
delete: !isUnix && flags&OPEN_DELETEONCLOSE != 0,
|
||||
shm: NewSharedMemory(name.String()+"-shm", flags),
|
||||
}
|
||||
return &file, flags, nil
|
||||
|
@ -139,6 +140,8 @@ type vfsFile struct {
|
|||
readOnly bool
|
||||
keepWAL bool
|
||||
syncDir bool
|
||||
atomic bool
|
||||
delete bool
|
||||
psow bool
|
||||
}
|
||||
|
||||
|
@ -152,6 +155,9 @@ var (
|
|||
)
|
||||
|
||||
func (f *vfsFile) Close() error {
|
||||
if f.delete {
|
||||
defer os.Remove(f.Name())
|
||||
}
|
||||
if f.shm != nil {
|
||||
f.shm.Close()
|
||||
}
|
||||
|
@ -175,7 +181,7 @@ func (f *vfsFile) Sync(flags SyncFlag) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if canSyncDirs && f.syncDir {
|
||||
if isUnix && f.syncDir {
|
||||
f.syncDir = false
|
||||
d, err := os.Open(filepath.Dir(f.File.Name()))
|
||||
if err != nil {
|
||||
|
@ -200,12 +206,15 @@ func (f *vfsFile) SectorSize() int {
|
|||
|
||||
func (f *vfsFile) DeviceCharacteristics() DeviceCharacteristic {
|
||||
ret := IOCAP_SUBPAGE_READ
|
||||
if osBatchAtomic(f.File) {
|
||||
if f.atomic {
|
||||
ret |= IOCAP_BATCH_ATOMIC
|
||||
}
|
||||
if f.psow {
|
||||
ret |= IOCAP_POWERSAFE_OVERWRITE
|
||||
}
|
||||
if runtime.GOOS == "windows" {
|
||||
ret |= IOCAP_UNDELETABLE_WHEN_OPEN
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
|
@ -214,6 +223,9 @@ func (f *vfsFile) SizeHint(size int64) error {
|
|||
}
|
||||
|
||||
func (f *vfsFile) HasMoved() (bool, error) {
|
||||
if runtime.GOOS == "windows" {
|
||||
return false, nil
|
||||
}
|
||||
fi, err := f.Stat()
|
||||
if err != nil {
|
||||
return false, err
|
||||
|
|
27
vendor/github.com/ncruces/go-sqlite3/vfs/os_bsd.go
generated
vendored
27
vendor/github.com/ncruces/go-sqlite3/vfs/os_bsd.go
generated
vendored
|
@ -50,11 +50,15 @@ func osDowngradeLock(file *os.File, _ LockLevel) _ErrorCode {
|
|||
}
|
||||
|
||||
func osReleaseLock(file *os.File, _ LockLevel) _ErrorCode {
|
||||
err := unix.Flock(int(file.Fd()), unix.LOCK_UN)
|
||||
if err != nil {
|
||||
return _IOERR_UNLOCK
|
||||
for {
|
||||
err := unix.Flock(int(file.Fd()), unix.LOCK_UN)
|
||||
if err == nil {
|
||||
return _OK
|
||||
}
|
||||
if err != unix.EINTR {
|
||||
return _IOERR_UNLOCK
|
||||
}
|
||||
}
|
||||
return _OK
|
||||
}
|
||||
|
||||
func osCheckReservedLock(file *os.File) (bool, _ErrorCode) {
|
||||
|
@ -89,13 +93,18 @@ func osLock(file *os.File, typ int16, start, len int64, def _ErrorCode) _ErrorCo
|
|||
}
|
||||
|
||||
func osUnlock(file *os.File, start, len int64) _ErrorCode {
|
||||
err := unix.FcntlFlock(file.Fd(), unix.F_SETLK, &unix.Flock_t{
|
||||
lock := unix.Flock_t{
|
||||
Type: unix.F_UNLCK,
|
||||
Start: start,
|
||||
Len: len,
|
||||
})
|
||||
if err != nil {
|
||||
return _IOERR_UNLOCK
|
||||
}
|
||||
return _OK
|
||||
for {
|
||||
err := unix.FcntlFlock(file.Fd(), unix.F_SETLK, &lock)
|
||||
if err == nil {
|
||||
return _OK
|
||||
}
|
||||
if err != unix.EINTR {
|
||||
return _IOERR_UNLOCK
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
22
vendor/github.com/ncruces/go-sqlite3/vfs/os_darwin.go
generated
vendored
22
vendor/github.com/ncruces/go-sqlite3/vfs/os_darwin.go
generated
vendored
|
@ -27,7 +27,12 @@ func osSync(file *os.File, fullsync, _ /*dataonly*/ bool) error {
|
|||
if fullsync {
|
||||
return file.Sync()
|
||||
}
|
||||
return unix.Fsync(int(file.Fd()))
|
||||
for {
|
||||
err := unix.Fsync(int(file.Fd()))
|
||||
if err != unix.EINTR {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func osAllocate(file *os.File, size int64) error {
|
||||
|
@ -85,13 +90,18 @@ func osLock(file *os.File, typ int16, start, len int64, timeout time.Duration, d
|
|||
}
|
||||
|
||||
func osUnlock(file *os.File, start, len int64) _ErrorCode {
|
||||
err := unix.FcntlFlock(file.Fd(), _F_OFD_SETLK, &unix.Flock_t{
|
||||
lock := unix.Flock_t{
|
||||
Type: unix.F_UNLCK,
|
||||
Start: start,
|
||||
Len: len,
|
||||
})
|
||||
if err != nil {
|
||||
return _IOERR_UNLOCK
|
||||
}
|
||||
return _OK
|
||||
for {
|
||||
err := unix.FcntlFlock(file.Fd(), _F_OFD_SETLK, &lock)
|
||||
if err == nil {
|
||||
return _OK
|
||||
}
|
||||
if err != unix.EINTR {
|
||||
return _IOERR_UNLOCK
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
46
vendor/github.com/ncruces/go-sqlite3/vfs/os_linux.go
generated
vendored
46
vendor/github.com/ncruces/go-sqlite3/vfs/os_linux.go
generated
vendored
|
@ -3,6 +3,7 @@
|
|||
package vfs
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
|
@ -11,14 +12,36 @@ import (
|
|||
|
||||
func osSync(file *os.File, _ /*fullsync*/, _ /*dataonly*/ bool) error {
|
||||
// SQLite trusts Linux's fdatasync for all fsync's.
|
||||
return unix.Fdatasync(int(file.Fd()))
|
||||
for {
|
||||
err := unix.Fdatasync(int(file.Fd()))
|
||||
if err != unix.EINTR {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func osAllocate(file *os.File, size int64) error {
|
||||
if size == 0 {
|
||||
return nil
|
||||
}
|
||||
return unix.Fallocate(int(file.Fd()), 0, 0, size)
|
||||
for {
|
||||
err := unix.Fallocate(int(file.Fd()), 0, 0, size)
|
||||
if err == unix.EOPNOTSUPP {
|
||||
break
|
||||
}
|
||||
if err != unix.EINTR {
|
||||
return err
|
||||
}
|
||||
}
|
||||
off, err := file.Seek(0, io.SeekEnd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if size <= off {
|
||||
return nil
|
||||
}
|
||||
return file.Truncate(size)
|
||||
|
||||
}
|
||||
|
||||
func osReadLock(file *os.File, start, len int64, timeout time.Duration) _ErrorCode {
|
||||
|
@ -37,22 +60,27 @@ func osLock(file *os.File, typ int16, start, len int64, timeout time.Duration, d
|
|||
}
|
||||
var err error
|
||||
switch {
|
||||
case timeout < 0:
|
||||
err = unix.FcntlFlock(file.Fd(), unix.F_OFD_SETLKW, &lock)
|
||||
default:
|
||||
err = unix.FcntlFlock(file.Fd(), unix.F_OFD_SETLK, &lock)
|
||||
case timeout < 0:
|
||||
err = unix.FcntlFlock(file.Fd(), unix.F_OFD_SETLKW, &lock)
|
||||
}
|
||||
return osLockErrorCode(err, def)
|
||||
}
|
||||
|
||||
func osUnlock(file *os.File, start, len int64) _ErrorCode {
|
||||
err := unix.FcntlFlock(file.Fd(), unix.F_OFD_SETLK, &unix.Flock_t{
|
||||
lock := unix.Flock_t{
|
||||
Type: unix.F_UNLCK,
|
||||
Start: start,
|
||||
Len: len,
|
||||
})
|
||||
if err != nil {
|
||||
return _IOERR_UNLOCK
|
||||
}
|
||||
return _OK
|
||||
for {
|
||||
err := unix.FcntlFlock(file.Fd(), unix.F_OFD_SETLK, &lock)
|
||||
if err == nil {
|
||||
return _OK
|
||||
}
|
||||
if err != unix.EINTR {
|
||||
return _IOERR_UNLOCK
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
2
vendor/github.com/ncruces/go-sqlite3/vfs/os_std.go
generated
vendored
2
vendor/github.com/ncruces/go-sqlite3/vfs/os_std.go
generated
vendored
|
@ -8,8 +8,8 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
isUnix = false
|
||||
_O_NOFOLLOW = 0
|
||||
canSyncDirs = false
|
||||
)
|
||||
|
||||
func osAccess(path string, flags AccessFlag) error {
|
||||
|
|
13
vendor/github.com/ncruces/go-sqlite3/vfs/os_unix.go
generated
vendored
13
vendor/github.com/ncruces/go-sqlite3/vfs/os_unix.go
generated
vendored
|
@ -10,8 +10,8 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
isUnix = true
|
||||
_O_NOFOLLOW = unix.O_NOFOLLOW
|
||||
canSyncDirs = true
|
||||
)
|
||||
|
||||
func osAccess(path string, flags AccessFlag) error {
|
||||
|
@ -65,10 +65,15 @@ func osTestLock(file *os.File, start, len int64) (int16, _ErrorCode) {
|
|||
Start: start,
|
||||
Len: len,
|
||||
}
|
||||
if unix.FcntlFlock(file.Fd(), unix.F_GETLK, &lock) != nil {
|
||||
return 0, _IOERR_CHECKRESERVEDLOCK
|
||||
for {
|
||||
err := unix.FcntlFlock(file.Fd(), unix.F_GETLK, &lock)
|
||||
if err == nil {
|
||||
return lock.Type, _OK
|
||||
}
|
||||
if err != unix.EINTR {
|
||||
return 0, _IOERR_CHECKRESERVEDLOCK
|
||||
}
|
||||
}
|
||||
return lock.Type, _OK
|
||||
}
|
||||
|
||||
func osLockErrorCode(err error, def _ErrorCode) _ErrorCode {
|
||||
|
|
35
vendor/github.com/ncruces/go-sqlite3/vfs/os_windows.go
generated
vendored
35
vendor/github.com/ncruces/go-sqlite3/vfs/os_windows.go
generated
vendored
|
@ -135,12 +135,10 @@ func osWriteLock(file *os.File, start, len uint32, timeout time.Duration) _Error
|
|||
func osLock(file *os.File, flags, start, len uint32, timeout time.Duration, def _ErrorCode) _ErrorCode {
|
||||
var err error
|
||||
switch {
|
||||
case timeout == 0:
|
||||
default:
|
||||
err = osLockEx(file, flags|windows.LOCKFILE_FAIL_IMMEDIATELY, start, len)
|
||||
case timeout < 0:
|
||||
err = osLockEx(file, flags, start, len)
|
||||
default:
|
||||
err = osLockExTimeout(file, flags, start, len, timeout)
|
||||
}
|
||||
return osLockErrorCode(err, def)
|
||||
}
|
||||
|
@ -162,37 +160,6 @@ func osLockEx(file *os.File, flags, start, len uint32) error {
|
|||
0, len, 0, &windows.Overlapped{Offset: start})
|
||||
}
|
||||
|
||||
func osLockExTimeout(file *os.File, flags, start, len uint32, timeout time.Duration) error {
|
||||
event, err := windows.CreateEvent(nil, 1, 0, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer windows.CloseHandle(event)
|
||||
|
||||
fd := windows.Handle(file.Fd())
|
||||
overlapped := &windows.Overlapped{
|
||||
Offset: start,
|
||||
HEvent: event,
|
||||
}
|
||||
|
||||
err = windows.LockFileEx(fd, flags, 0, len, 0, overlapped)
|
||||
if err != windows.ERROR_IO_PENDING {
|
||||
return err
|
||||
}
|
||||
|
||||
ms := (timeout + time.Millisecond - 1) / time.Millisecond
|
||||
rc, err := windows.WaitForSingleObject(event, uint32(ms))
|
||||
if rc == windows.WAIT_OBJECT_0 {
|
||||
return nil
|
||||
}
|
||||
defer windows.CancelIoEx(fd, overlapped)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return windows.Errno(rc)
|
||||
}
|
||||
|
||||
func osLockErrorCode(err error, def _ErrorCode) _ErrorCode {
|
||||
if err == nil {
|
||||
return _OK
|
||||
|
|
15
vendor/github.com/ncruces/go-sqlite3/vfs/shm_bsd.go
generated
vendored
15
vendor/github.com/ncruces/go-sqlite3/vfs/shm_bsd.go
generated
vendored
|
@ -68,16 +68,11 @@ func (s *vfsShm) Close() error {
|
|||
panic(util.AssertErr())
|
||||
}
|
||||
|
||||
func (s *vfsShm) shmOpen() _ErrorCode {
|
||||
func (s *vfsShm) shmOpen() (rc _ErrorCode) {
|
||||
if s.vfsShmParent != nil {
|
||||
return _OK
|
||||
}
|
||||
|
||||
var f *os.File
|
||||
// Close file on error.
|
||||
// Keep this here to avoid confusing checklocks.
|
||||
defer func() { f.Close() }()
|
||||
|
||||
vfsShmListMtx.Lock()
|
||||
defer vfsShmListMtx.Unlock()
|
||||
|
||||
|
@ -98,11 +93,16 @@ func (s *vfsShm) shmOpen() _ErrorCode {
|
|||
}
|
||||
|
||||
// Always open file read-write, as it will be shared.
|
||||
f, err = os.OpenFile(s.path,
|
||||
f, err := os.OpenFile(s.path,
|
||||
os.O_RDWR|os.O_CREATE|_O_NOFOLLOW, 0666)
|
||||
if err != nil {
|
||||
return _CANTOPEN
|
||||
}
|
||||
defer func() {
|
||||
if rc != _OK {
|
||||
f.Close()
|
||||
}
|
||||
}()
|
||||
|
||||
// Dead man's switch.
|
||||
if lock, rc := osTestLock(f, _SHM_DMS, 1); rc != _OK {
|
||||
|
@ -131,7 +131,6 @@ func (s *vfsShm) shmOpen() _ErrorCode {
|
|||
File: f,
|
||||
info: fi,
|
||||
}
|
||||
f = nil // Don't close the file.
|
||||
for i, g := range vfsShmList {
|
||||
if g == nil {
|
||||
vfsShmList[i] = s.vfsShmParent
|
||||
|
|
23
vendor/github.com/ncruces/go-sqlite3/vfs/shm_windows.go
generated
vendored
23
vendor/github.com/ncruces/go-sqlite3/vfs/shm_windows.go
generated
vendored
|
@ -7,14 +7,11 @@ import (
|
|||
"io"
|
||||
"os"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/tetratelabs/wazero/api"
|
||||
"golang.org/x/sys/windows"
|
||||
|
||||
"github.com/ncruces/go-sqlite3/internal/util"
|
||||
"github.com/ncruces/go-sqlite3/util/osutil"
|
||||
)
|
||||
|
||||
type vfsShm struct {
|
||||
|
@ -33,8 +30,6 @@ type vfsShm struct {
|
|||
sync.Mutex
|
||||
}
|
||||
|
||||
var _ blockingSharedMemory = &vfsShm{}
|
||||
|
||||
func (s *vfsShm) Close() error {
|
||||
// Unmap regions.
|
||||
for _, r := range s.regions {
|
||||
|
@ -48,8 +43,7 @@ func (s *vfsShm) Close() error {
|
|||
|
||||
func (s *vfsShm) shmOpen() _ErrorCode {
|
||||
if s.File == nil {
|
||||
f, err := osutil.OpenFile(s.path,
|
||||
os.O_RDWR|os.O_CREATE|syscall.O_NONBLOCK, 0666)
|
||||
f, err := os.OpenFile(s.path, os.O_RDWR|os.O_CREATE, 0666)
|
||||
if err != nil {
|
||||
return _CANTOPEN
|
||||
}
|
||||
|
@ -67,7 +61,7 @@ func (s *vfsShm) shmOpen() _ErrorCode {
|
|||
return _IOERR_SHMOPEN
|
||||
}
|
||||
}
|
||||
rc := osReadLock(s.File, _SHM_DMS, 1, time.Millisecond)
|
||||
rc := osReadLock(s.File, _SHM_DMS, 1, 0)
|
||||
s.fileLock = rc == _OK
|
||||
return rc
|
||||
}
|
||||
|
@ -135,11 +129,6 @@ func (s *vfsShm) shmMap(ctx context.Context, mod api.Module, id, size int32, ext
|
|||
}
|
||||
|
||||
func (s *vfsShm) shmLock(offset, n int32, flags _ShmFlag) (rc _ErrorCode) {
|
||||
var timeout time.Duration
|
||||
if s.blocking {
|
||||
timeout = time.Millisecond
|
||||
}
|
||||
|
||||
switch {
|
||||
case flags&_SHM_LOCK != 0:
|
||||
defer s.shmAcquire(&rc)
|
||||
|
@ -151,9 +140,9 @@ func (s *vfsShm) shmLock(offset, n int32, flags _ShmFlag) (rc _ErrorCode) {
|
|||
case flags&_SHM_UNLOCK != 0:
|
||||
return osUnlock(s.File, _SHM_BASE+uint32(offset), uint32(n))
|
||||
case flags&_SHM_SHARED != 0:
|
||||
return osReadLock(s.File, _SHM_BASE+uint32(offset), uint32(n), timeout)
|
||||
return osReadLock(s.File, _SHM_BASE+uint32(offset), uint32(n), 0)
|
||||
case flags&_SHM_EXCLUSIVE != 0:
|
||||
return osWriteLock(s.File, _SHM_BASE+uint32(offset), uint32(n), timeout)
|
||||
return osWriteLock(s.File, _SHM_BASE+uint32(offset), uint32(n), 0)
|
||||
default:
|
||||
panic(util.AssertErr())
|
||||
}
|
||||
|
@ -184,7 +173,3 @@ func (s *vfsShm) shmUnmap(delete bool) {
|
|||
os.Remove(s.path)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *vfsShm) shmEnableBlocking(block bool) {
|
||||
s.blocking = block
|
||||
}
|
||||
|
|
29
vendor/github.com/ncruces/go-sqlite3/vtab.go
generated
vendored
29
vendor/github.com/ncruces/go-sqlite3/vtab.go
generated
vendored
|
@ -79,9 +79,12 @@ func implements[T any](typ reflect.Type) bool {
|
|||
//
|
||||
// https://sqlite.org/c3ref/declare_vtab.html
|
||||
func (c *Conn) DeclareVTab(sql string) error {
|
||||
if c.interrupt.Err() != nil {
|
||||
return INTERRUPT
|
||||
}
|
||||
defer c.arena.mark()()
|
||||
sqlPtr := c.arena.string(sql)
|
||||
rc := res_t(c.call("sqlite3_declare_vtab", stk_t(c.handle), stk_t(sqlPtr)))
|
||||
textPtr := c.arena.string(sql)
|
||||
rc := res_t(c.call("sqlite3_declare_vtab", stk_t(c.handle), stk_t(textPtr)))
|
||||
return c.error(rc)
|
||||
}
|
||||
|
||||
|
@ -162,6 +165,7 @@ type VTabDestroyer interface {
|
|||
}
|
||||
|
||||
// A VTabUpdater allows a virtual table to be updated.
|
||||
// Implementations must not retain arg.
|
||||
type VTabUpdater interface {
|
||||
VTab
|
||||
// https://sqlite.org/vtab.html#xupdate
|
||||
|
@ -241,6 +245,7 @@ type VTabSavepointer interface {
|
|||
// to loop through the virtual table.
|
||||
// A VTabCursor may optionally implement
|
||||
// [io.Closer] to free resources.
|
||||
// Implementations of Filter must not retain arg.
|
||||
//
|
||||
// https://sqlite.org/c3ref/vtab_cursor.html
|
||||
type VTabCursor interface {
|
||||
|
@ -489,12 +494,12 @@ func vtabBestIndexCallback(ctx context.Context, mod api.Module, pVTab, pIdxInfo
|
|||
}
|
||||
|
||||
func vtabUpdateCallback(ctx context.Context, mod api.Module, pVTab ptr_t, nArg int32, pArg, pRowID ptr_t) res_t {
|
||||
vtab := vtabGetHandle(ctx, mod, pVTab).(VTabUpdater)
|
||||
|
||||
db := ctx.Value(connKey{}).(*Conn)
|
||||
args := make([]Value, nArg)
|
||||
callbackArgs(db, args, pArg)
|
||||
rowID, err := vtab.Update(args...)
|
||||
args := callbackArgs(db, nArg, pArg)
|
||||
defer returnArgs(args)
|
||||
|
||||
vtab := vtabGetHandle(ctx, mod, pVTab).(VTabUpdater)
|
||||
rowID, err := vtab.Update(*args...)
|
||||
if err == nil {
|
||||
util.Write64(mod, pRowID, rowID)
|
||||
}
|
||||
|
@ -593,15 +598,17 @@ func cursorCloseCallback(ctx context.Context, mod api.Module, pCur ptr_t) res_t
|
|||
}
|
||||
|
||||
func cursorFilterCallback(ctx context.Context, mod api.Module, pCur ptr_t, idxNum int32, idxStr ptr_t, nArg int32, pArg ptr_t) res_t {
|
||||
cursor := vtabGetHandle(ctx, mod, pCur).(VTabCursor)
|
||||
db := ctx.Value(connKey{}).(*Conn)
|
||||
args := make([]Value, nArg)
|
||||
callbackArgs(db, args, pArg)
|
||||
args := callbackArgs(db, nArg, pArg)
|
||||
defer returnArgs(args)
|
||||
|
||||
var idxName string
|
||||
if idxStr != 0 {
|
||||
idxName = util.ReadString(mod, idxStr, _MAX_LENGTH)
|
||||
}
|
||||
err := cursor.Filter(int(idxNum), idxName, args...)
|
||||
|
||||
cursor := vtabGetHandle(ctx, mod, pCur).(VTabCursor)
|
||||
err := cursor.Filter(int(idxNum), idxName, *args...)
|
||||
return vtabError(ctx, mod, pCur, _CURSOR_ERROR, err)
|
||||
}
|
||||
|
||||
|
|
3
vendor/modules.txt
vendored
3
vendor/modules.txt
vendored
|
@ -691,7 +691,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.24.0
|
||||
# github.com/ncruces/go-sqlite3 v0.25.0
|
||||
## explicit; go 1.23.0
|
||||
github.com/ncruces/go-sqlite3
|
||||
github.com/ncruces/go-sqlite3/driver
|
||||
|
@ -699,7 +699,6 @@ github.com/ncruces/go-sqlite3/embed
|
|||
github.com/ncruces/go-sqlite3/internal/alloc
|
||||
github.com/ncruces/go-sqlite3/internal/dotlk
|
||||
github.com/ncruces/go-sqlite3/internal/util
|
||||
github.com/ncruces/go-sqlite3/util/osutil
|
||||
github.com/ncruces/go-sqlite3/util/sql3util
|
||||
github.com/ncruces/go-sqlite3/vfs
|
||||
github.com/ncruces/go-sqlite3/vfs/memdb
|
||||
|
|
Loading…
Reference in a new issue