update go-sqlite3 to v0.18.0 (#3204)
This commit is contained in:
parent
09f24e0446
commit
586639ccf0
4
go.mod
4
go.mod
|
@ -45,7 +45,7 @@ require (
|
||||||
github.com/miekg/dns v1.1.61
|
github.com/miekg/dns v1.1.61
|
||||||
github.com/minio/minio-go/v7 v7.0.74
|
github.com/minio/minio-go/v7 v7.0.74
|
||||||
github.com/mitchellh/mapstructure v1.5.0
|
github.com/mitchellh/mapstructure v1.5.0
|
||||||
github.com/ncruces/go-sqlite3 v0.17.1
|
github.com/ncruces/go-sqlite3 v0.18.0
|
||||||
github.com/oklog/ulid v1.3.1
|
github.com/oklog/ulid v1.3.1
|
||||||
github.com/prometheus/client_golang v1.19.1
|
github.com/prometheus/client_golang v1.19.1
|
||||||
github.com/spf13/cobra v1.8.1
|
github.com/spf13/cobra v1.8.1
|
||||||
|
@ -215,7 +215,7 @@ require (
|
||||||
golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 // indirect
|
golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 // indirect
|
||||||
golang.org/x/mod v0.18.0 // indirect
|
golang.org/x/mod v0.18.0 // indirect
|
||||||
golang.org/x/sync v0.8.0 // indirect
|
golang.org/x/sync v0.8.0 // indirect
|
||||||
golang.org/x/sys v0.23.0 // indirect
|
golang.org/x/sys v0.24.0 // indirect
|
||||||
golang.org/x/tools v0.22.0 // indirect
|
golang.org/x/tools v0.22.0 // indirect
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20240311132316-a219d84964c2 // indirect
|
google.golang.org/genproto/googleapis/api v0.0.0-20240311132316-a219d84964c2 // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240401170217-c3f982113cda // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240401170217-c3f982113cda // indirect
|
||||||
|
|
8
go.sum
8
go.sum
|
@ -438,8 +438,8 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G
|
||||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||||
github.com/moul/http2curl v1.0.0 h1:dRMWoAtb+ePxMlLkrCbAqh4TlPHXvoGUSQ323/9Zahs=
|
github.com/moul/http2curl v1.0.0 h1:dRMWoAtb+ePxMlLkrCbAqh4TlPHXvoGUSQ323/9Zahs=
|
||||||
github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ=
|
github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ=
|
||||||
github.com/ncruces/go-sqlite3 v0.17.1 h1:VxTjDpCn87FaFlKMaAYC1jP7ND0d4UNj+6G4IQDHbgI=
|
github.com/ncruces/go-sqlite3 v0.18.0 h1:aH7WGzOC0CYpUPG1LdFg7JApybiuXgYUE2itzLBwhPM=
|
||||||
github.com/ncruces/go-sqlite3 v0.17.1/go.mod h1:FnCyui8SlDoL0mQZ5dTouNo7s7jXS0kJv9lBt1GlM9w=
|
github.com/ncruces/go-sqlite3 v0.18.0/go.mod h1:eEOyZnW1dGTJ+zDpMuzfYamEUBtdFz5zeYhqLBtHxvM=
|
||||||
github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
|
github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
|
||||||
github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
|
github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
|
||||||
github.com/ncruces/julianday v1.0.0 h1:fH0OKwa7NWvniGQtxdJRxAgkBMolni2BjDHaWTxqt7M=
|
github.com/ncruces/julianday v1.0.0 h1:fH0OKwa7NWvniGQtxdJRxAgkBMolni2BjDHaWTxqt7M=
|
||||||
|
@ -805,8 +805,8 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||||
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM=
|
golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg=
|
||||||
golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
|
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
|
||||||
|
|
|
@ -93,7 +93,7 @@ This project aims for [high test coverage](https://github.com/ncruces/go-sqlite3
|
||||||
It also benefits greatly from [SQLite's](https://sqlite.org/testing.html) and
|
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](.github/workflows/test.yml) on
|
Every commit is [tested](https://github.com/ncruces/go-sqlite3/wiki/Test-matrix) on
|
||||||
Linux (amd64/arm64/386/riscv64/s390x), macOS (amd64/arm64),
|
Linux (amd64/arm64/386/riscv64/s390x), macOS (amd64/arm64),
|
||||||
Windows (amd64), FreeBSD (amd64), OpenBSD (amd64), NetBSD (amd64),
|
Windows (amd64), FreeBSD (amd64), OpenBSD (amd64), NetBSD (amd64),
|
||||||
illumos (amd64), and Solaris (amd64).
|
illumos (amd64), and Solaris (amd64).
|
||||||
|
|
|
@ -143,6 +143,7 @@ func (b *Blob) WriteTo(w io.Writer) (n int64, err error) {
|
||||||
return n, err
|
return n, err
|
||||||
}
|
}
|
||||||
if int64(m) != want {
|
if int64(m) != want {
|
||||||
|
// notest // Write misbehaving
|
||||||
return n, io.ErrShortWrite
|
return n, io.ErrShortWrite
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
"github.com/ncruces/go-sqlite3/internal/util"
|
"github.com/ncruces/go-sqlite3/internal/util"
|
||||||
|
"github.com/ncruces/go-sqlite3/vfs"
|
||||||
"github.com/tetratelabs/wazero/api"
|
"github.com/tetratelabs/wazero/api"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -56,6 +57,99 @@ func logCallback(ctx context.Context, mod api.Module, _, iCode, zMsg uint32) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FileControl allows low-level control of database files.
|
||||||
|
// Only a subset of opcodes are supported.
|
||||||
|
//
|
||||||
|
// https://sqlite.org/c3ref/file_control.html
|
||||||
|
func (c *Conn) FileControl(schema string, op FcntlOpcode, arg ...any) (any, error) {
|
||||||
|
defer c.arena.mark()()
|
||||||
|
|
||||||
|
var schemaPtr uint32
|
||||||
|
if schema != "" {
|
||||||
|
schemaPtr = c.arena.string(schema)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch op {
|
||||||
|
case FCNTL_RESET_CACHE:
|
||||||
|
r := c.call("sqlite3_file_control",
|
||||||
|
uint64(c.handle), uint64(schemaPtr),
|
||||||
|
uint64(op), 0)
|
||||||
|
return nil, c.error(r)
|
||||||
|
|
||||||
|
case FCNTL_PERSIST_WAL, FCNTL_POWERSAFE_OVERWRITE:
|
||||||
|
var flag int
|
||||||
|
switch {
|
||||||
|
case len(arg) == 0:
|
||||||
|
flag = -1
|
||||||
|
case arg[0]:
|
||||||
|
flag = 1
|
||||||
|
}
|
||||||
|
ptr := c.arena.new(4)
|
||||||
|
util.WriteUint32(c.mod, ptr, uint32(flag))
|
||||||
|
r := c.call("sqlite3_file_control",
|
||||||
|
uint64(c.handle), uint64(schemaPtr),
|
||||||
|
uint64(op), uint64(ptr))
|
||||||
|
return util.ReadUint32(c.mod, ptr) != 0, c.error(r)
|
||||||
|
|
||||||
|
case FCNTL_CHUNK_SIZE:
|
||||||
|
ptr := c.arena.new(4)
|
||||||
|
util.WriteUint32(c.mod, ptr, uint32(arg[0].(int)))
|
||||||
|
r := c.call("sqlite3_file_control",
|
||||||
|
uint64(c.handle), uint64(schemaPtr),
|
||||||
|
uint64(op), uint64(ptr))
|
||||||
|
return nil, c.error(r)
|
||||||
|
|
||||||
|
case FCNTL_RESERVE_BYTES:
|
||||||
|
bytes := -1
|
||||||
|
if len(arg) > 0 {
|
||||||
|
bytes = arg[0].(int)
|
||||||
|
}
|
||||||
|
ptr := c.arena.new(4)
|
||||||
|
util.WriteUint32(c.mod, ptr, uint32(bytes))
|
||||||
|
r := c.call("sqlite3_file_control",
|
||||||
|
uint64(c.handle), uint64(schemaPtr),
|
||||||
|
uint64(op), uint64(ptr))
|
||||||
|
return int(util.ReadUint32(c.mod, ptr)), c.error(r)
|
||||||
|
|
||||||
|
case FCNTL_DATA_VERSION:
|
||||||
|
ptr := c.arena.new(4)
|
||||||
|
r := c.call("sqlite3_file_control",
|
||||||
|
uint64(c.handle), uint64(schemaPtr),
|
||||||
|
uint64(op), uint64(ptr))
|
||||||
|
return util.ReadUint32(c.mod, ptr), c.error(r)
|
||||||
|
|
||||||
|
case FCNTL_LOCKSTATE:
|
||||||
|
ptr := c.arena.new(4)
|
||||||
|
r := c.call("sqlite3_file_control",
|
||||||
|
uint64(c.handle), uint64(schemaPtr),
|
||||||
|
uint64(op), uint64(ptr))
|
||||||
|
return vfs.LockLevel(util.ReadUint32(c.mod, ptr)), c.error(r)
|
||||||
|
|
||||||
|
case FCNTL_VFS_POINTER:
|
||||||
|
ptr := c.arena.new(4)
|
||||||
|
r := c.call("sqlite3_file_control",
|
||||||
|
uint64(c.handle), uint64(schemaPtr),
|
||||||
|
uint64(op), uint64(ptr))
|
||||||
|
const zNameOffset = 16
|
||||||
|
ptr = util.ReadUint32(c.mod, ptr)
|
||||||
|
ptr = util.ReadUint32(c.mod, ptr+zNameOffset)
|
||||||
|
name := util.ReadString(c.mod, ptr, _MAX_NAME)
|
||||||
|
return vfs.Find(name), c.error(r)
|
||||||
|
|
||||||
|
case FCNTL_FILE_POINTER, FCNTL_JOURNAL_POINTER:
|
||||||
|
ptr := c.arena.new(4)
|
||||||
|
r := c.call("sqlite3_file_control",
|
||||||
|
uint64(c.handle), uint64(schemaPtr),
|
||||||
|
uint64(op), uint64(ptr))
|
||||||
|
const fileHandleOffset = 4
|
||||||
|
ptr = util.ReadUint32(c.mod, ptr)
|
||||||
|
ptr = util.ReadUint32(c.mod, ptr+fileHandleOffset)
|
||||||
|
return util.GetHandle(c.ctx, ptr), c.error(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, MISUSE
|
||||||
|
}
|
||||||
|
|
||||||
// Limit allows the size of various constructs to be
|
// Limit allows the size of various constructs to be
|
||||||
// limited on a connection by connection basis.
|
// limited on a connection by connection basis.
|
||||||
//
|
//
|
||||||
|
@ -68,7 +162,7 @@ func (c *Conn) Limit(id LimitCategory, value int) int {
|
||||||
// SetAuthorizer registers an authorizer callback with the database connection.
|
// SetAuthorizer registers an authorizer callback with the database connection.
|
||||||
//
|
//
|
||||||
// https://sqlite.org/c3ref/set_authorizer.html
|
// https://sqlite.org/c3ref/set_authorizer.html
|
||||||
func (c *Conn) SetAuthorizer(cb func(action AuthorizerActionCode, name3rd, name4th, schema, nameInner string) AuthorizerReturnCode) error {
|
func (c *Conn) SetAuthorizer(cb func(action AuthorizerActionCode, name3rd, name4th, schema, inner string) AuthorizerReturnCode) error {
|
||||||
var enable uint64
|
var enable uint64
|
||||||
if cb != nil {
|
if cb != nil {
|
||||||
enable = 1
|
enable = 1
|
||||||
|
@ -82,9 +176,9 @@ func (c *Conn) SetAuthorizer(cb func(action AuthorizerActionCode, name3rd, name4
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func authorizerCallback(ctx context.Context, mod api.Module, pDB uint32, action AuthorizerActionCode, zName3rd, zName4th, zSchema, zNameInner uint32) (rc AuthorizerReturnCode) {
|
func authorizerCallback(ctx context.Context, mod api.Module, pDB uint32, action AuthorizerActionCode, zName3rd, zName4th, zSchema, zInner uint32) (rc AuthorizerReturnCode) {
|
||||||
if c, ok := ctx.Value(connKey{}).(*Conn); ok && c.handle == pDB && c.authorizer != nil {
|
if c, ok := ctx.Value(connKey{}).(*Conn); ok && c.handle == pDB && c.authorizer != nil {
|
||||||
var name3rd, name4th, schema, nameInner string
|
var name3rd, name4th, schema, inner string
|
||||||
if zName3rd != 0 {
|
if zName3rd != 0 {
|
||||||
name3rd = util.ReadString(mod, zName3rd, _MAX_NAME)
|
name3rd = util.ReadString(mod, zName3rd, _MAX_NAME)
|
||||||
}
|
}
|
||||||
|
@ -94,10 +188,48 @@ func authorizerCallback(ctx context.Context, mod api.Module, pDB uint32, action
|
||||||
if zSchema != 0 {
|
if zSchema != 0 {
|
||||||
schema = util.ReadString(mod, zSchema, _MAX_NAME)
|
schema = util.ReadString(mod, zSchema, _MAX_NAME)
|
||||||
}
|
}
|
||||||
if zNameInner != 0 {
|
if zInner != 0 {
|
||||||
nameInner = util.ReadString(mod, zNameInner, _MAX_NAME)
|
inner = util.ReadString(mod, zInner, _MAX_NAME)
|
||||||
|
}
|
||||||
|
rc = c.authorizer(action, name3rd, name4th, schema, inner)
|
||||||
|
}
|
||||||
|
return rc
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trace registers a trace callback function against the database connection.
|
||||||
|
//
|
||||||
|
// https://sqlite.org/c3ref/trace_v2.html
|
||||||
|
func (c *Conn) Trace(mask TraceEvent, cb func(evt TraceEvent, arg1 any, arg2 any) error) error {
|
||||||
|
r := c.call("sqlite3_trace_go", uint64(c.handle), uint64(mask))
|
||||||
|
if err := c.error(r); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
c.trace = cb
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func traceCallback(ctx context.Context, mod api.Module, evt TraceEvent, pDB, pArg1, pArg2 uint32) (rc uint32) {
|
||||||
|
if c, ok := ctx.Value(connKey{}).(*Conn); ok && c.handle == pDB && c.trace != nil {
|
||||||
|
var arg1, arg2 any
|
||||||
|
if evt == TRACE_CLOSE {
|
||||||
|
arg1 = c
|
||||||
|
} else {
|
||||||
|
for _, s := range c.stmts {
|
||||||
|
if pArg1 == s.handle {
|
||||||
|
arg1 = s
|
||||||
|
switch evt {
|
||||||
|
case TRACE_STMT:
|
||||||
|
arg2 = s.SQL()
|
||||||
|
case TRACE_PROFILE:
|
||||||
|
arg2 = int64(util.ReadUint64(mod, pArg2))
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if arg1 != nil {
|
||||||
|
_, rc = errorCode(c.trace(evt, arg1, arg2), ERROR)
|
||||||
}
|
}
|
||||||
rc = c.authorizer(action, name3rd, name4th, schema, nameInner)
|
|
||||||
}
|
}
|
||||||
return rc
|
return rc
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,14 +22,16 @@ type Conn struct {
|
||||||
|
|
||||||
interrupt context.Context
|
interrupt context.Context
|
||||||
pending *Stmt
|
pending *Stmt
|
||||||
|
stmts []*Stmt
|
||||||
busy func(int) bool
|
busy func(int) bool
|
||||||
log func(xErrorCode, string)
|
log func(xErrorCode, string)
|
||||||
collation func(*Conn, string)
|
collation func(*Conn, string)
|
||||||
|
wal func(*Conn, string, int) error
|
||||||
|
trace func(TraceEvent, any, any) error
|
||||||
authorizer func(AuthorizerActionCode, string, string, string, string) AuthorizerReturnCode
|
authorizer func(AuthorizerActionCode, string, string, string, string) AuthorizerReturnCode
|
||||||
update func(AuthorizerActionCode, string, string, int64)
|
update func(AuthorizerActionCode, string, string, int64)
|
||||||
commit func() bool
|
commit func() bool
|
||||||
rollback func()
|
rollback func()
|
||||||
wal func(*Conn, string, int) error
|
|
||||||
arena arena
|
arena arena
|
||||||
|
|
||||||
handle uint32
|
handle uint32
|
||||||
|
@ -202,6 +204,7 @@ func (c *Conn) PrepareFlags(sql string, flags PrepareFlag) (stmt *Stmt, tail str
|
||||||
if stmt.handle == 0 {
|
if stmt.handle == 0 {
|
||||||
return nil, "", nil
|
return nil, "", nil
|
||||||
}
|
}
|
||||||
|
c.stmts = append(c.stmts, stmt)
|
||||||
return stmt, tail, nil
|
return stmt, tail, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -227,9 +230,8 @@ func (c *Conn) Filename(schema string) *vfs.Filename {
|
||||||
defer c.arena.mark()()
|
defer c.arena.mark()()
|
||||||
ptr = c.arena.string(schema)
|
ptr = c.arena.string(schema)
|
||||||
}
|
}
|
||||||
|
|
||||||
r := c.call("sqlite3_db_filename", uint64(c.handle), uint64(ptr))
|
r := c.call("sqlite3_db_filename", uint64(c.handle), uint64(ptr))
|
||||||
return vfs.OpenFilename(c.ctx, c.mod, uint32(r), vfs.OPEN_MAIN_DB)
|
return vfs.GetFilename(c.ctx, c.mod, uint32(r), vfs.OPEN_MAIN_DB)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadOnly determines if a database is read-only.
|
// ReadOnly determines if a database is read-only.
|
||||||
|
@ -327,7 +329,12 @@ func (c *Conn) SetInterrupt(ctx context.Context) (old context.Context) {
|
||||||
// A busy SQL statement prevents SQLite from ignoring an interrupt
|
// A busy SQL statement prevents SQLite from ignoring an interrupt
|
||||||
// that comes before any other statements are started.
|
// that comes before any other statements are started.
|
||||||
if c.pending == nil {
|
if c.pending == nil {
|
||||||
c.pending, _, _ = c.Prepare(`WITH RECURSIVE c(x) AS (VALUES(0) UNION ALL SELECT x FROM c) SELECT x FROM c`)
|
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", uint64(c.handle), uint64(loopPtr), math.MaxUint64, 0, uint64(stmtPtr), 0)
|
||||||
|
c.pending = &Stmt{c: c}
|
||||||
|
c.pending.handle = util.ReadUint32(c.mod, stmtPtr)
|
||||||
}
|
}
|
||||||
|
|
||||||
old = c.interrupt
|
old = c.interrupt
|
||||||
|
@ -415,10 +422,74 @@ func busyCallback(ctx context.Context, mod api.Module, pDB uint32, count int32)
|
||||||
return retry
|
return retry
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Status retrieves runtime status information about a database connection.
|
||||||
|
//
|
||||||
|
// https://sqlite.org/c3ref/db_status.html
|
||||||
|
func (c *Conn) Status(op DBStatus, reset bool) (current, highwater int, err error) {
|
||||||
|
defer c.arena.mark()()
|
||||||
|
hiPtr := c.arena.new(4)
|
||||||
|
curPtr := c.arena.new(4)
|
||||||
|
|
||||||
|
var i uint64
|
||||||
|
if reset {
|
||||||
|
i = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
r := c.call("sqlite3_db_status", uint64(c.handle),
|
||||||
|
uint64(op), uint64(curPtr), uint64(hiPtr), i)
|
||||||
|
if err = c.error(r); err == nil {
|
||||||
|
current = int(util.ReadUint32(c.mod, curPtr))
|
||||||
|
highwater = int(util.ReadUint32(c.mod, hiPtr))
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// TableColumnMetadata extracts metadata about a column of a table.
|
||||||
|
//
|
||||||
|
// https://sqlite.org/c3ref/table_column_metadata.html
|
||||||
|
func (c *Conn) TableColumnMetadata(schema, table, column string) (declType, collSeq string, notNull, primaryKey, autoInc bool, err error) {
|
||||||
|
defer c.arena.mark()()
|
||||||
|
|
||||||
|
var schemaPtr, columnPtr uint32
|
||||||
|
declTypePtr := c.arena.new(ptrlen)
|
||||||
|
collSeqPtr := c.arena.new(ptrlen)
|
||||||
|
notNullPtr := c.arena.new(ptrlen)
|
||||||
|
primaryKeyPtr := c.arena.new(ptrlen)
|
||||||
|
autoIncPtr := c.arena.new(ptrlen)
|
||||||
|
if schema != "" {
|
||||||
|
schemaPtr = c.arena.string(schema)
|
||||||
|
}
|
||||||
|
tablePtr := c.arena.string(table)
|
||||||
|
if column != "" {
|
||||||
|
columnPtr = c.arena.string(column)
|
||||||
|
}
|
||||||
|
|
||||||
|
r := c.call("sqlite3_table_column_metadata", uint64(c.handle),
|
||||||
|
uint64(schemaPtr), uint64(tablePtr), uint64(columnPtr),
|
||||||
|
uint64(declTypePtr), uint64(collSeqPtr),
|
||||||
|
uint64(notNullPtr), uint64(primaryKeyPtr), uint64(autoIncPtr))
|
||||||
|
if err = c.error(r); err == nil && column != "" {
|
||||||
|
declType = util.ReadString(c.mod, util.ReadUint32(c.mod, declTypePtr), _MAX_NAME)
|
||||||
|
collSeq = util.ReadString(c.mod, util.ReadUint32(c.mod, collSeqPtr), _MAX_NAME)
|
||||||
|
notNull = util.ReadUint32(c.mod, notNullPtr) != 0
|
||||||
|
autoInc = util.ReadUint32(c.mod, autoIncPtr) != 0
|
||||||
|
primaryKey = util.ReadUint32(c.mod, primaryKeyPtr) != 0
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Conn) error(rc uint64, sql ...string) error {
|
func (c *Conn) error(rc uint64, sql ...string) error {
|
||||||
return c.sqlite.error(rc, c.handle, sql...)
|
return c.sqlite.error(rc, c.handle, sql...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Conn) stmtsIter(yield func(*Stmt) bool) {
|
||||||
|
for _, s := range c.stmts {
|
||||||
|
if !yield(s) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// DriverConn is implemented by the SQLite [database/sql] driver connection.
|
// DriverConn is implemented by the SQLite [database/sql] driver connection.
|
||||||
//
|
//
|
||||||
// It can be used to access SQLite features like [online backup].
|
// It can be used to access SQLite features like [online backup].
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
//go:build (go1.23 || goexperiment.rangefunc) && !vet
|
||||||
|
|
||||||
|
package sqlite3
|
||||||
|
|
||||||
|
import "iter"
|
||||||
|
|
||||||
|
// Stmts returns an iterator for the prepared statements
|
||||||
|
// associated with the database connection.
|
||||||
|
//
|
||||||
|
// https://sqlite.org/c3ref/next_stmt.html
|
||||||
|
func (c *Conn) Stmts() iter.Seq[*Stmt] { return c.stmtsIter }
|
|
@ -0,0 +1,9 @@
|
||||||
|
//go:build !(go1.23 || goexperiment.rangefunc) || vet
|
||||||
|
|
||||||
|
package sqlite3
|
||||||
|
|
||||||
|
// Stmts returns an iterator for the prepared statements
|
||||||
|
// associated with the database connection.
|
||||||
|
//
|
||||||
|
// https://sqlite.org/c3ref/next_stmt.html
|
||||||
|
func (c *Conn) Stmts() func(func(*Stmt) bool) { return c.stmtsIter }
|
|
@ -109,7 +109,7 @@ const (
|
||||||
CANTOPEN_ISDIR ExtendedErrorCode = xErrorCode(CANTOPEN) | (2 << 8)
|
CANTOPEN_ISDIR ExtendedErrorCode = xErrorCode(CANTOPEN) | (2 << 8)
|
||||||
CANTOPEN_FULLPATH ExtendedErrorCode = xErrorCode(CANTOPEN) | (3 << 8)
|
CANTOPEN_FULLPATH ExtendedErrorCode = xErrorCode(CANTOPEN) | (3 << 8)
|
||||||
CANTOPEN_CONVPATH ExtendedErrorCode = xErrorCode(CANTOPEN) | (4 << 8)
|
CANTOPEN_CONVPATH ExtendedErrorCode = xErrorCode(CANTOPEN) | (4 << 8)
|
||||||
CANTOPEN_DIRTYWAL ExtendedErrorCode = xErrorCode(CANTOPEN) | (5 << 8) /* Not Used */
|
// CANTOPEN_DIRTYWAL ExtendedErrorCode = xErrorCode(CANTOPEN) | (5 << 8) /* Not Used */
|
||||||
CANTOPEN_SYMLINK ExtendedErrorCode = xErrorCode(CANTOPEN) | (6 << 8)
|
CANTOPEN_SYMLINK ExtendedErrorCode = xErrorCode(CANTOPEN) | (6 << 8)
|
||||||
CORRUPT_VTAB ExtendedErrorCode = xErrorCode(CORRUPT) | (1 << 8)
|
CORRUPT_VTAB ExtendedErrorCode = xErrorCode(CORRUPT) | (1 << 8)
|
||||||
CORRUPT_SEQUENCE ExtendedErrorCode = xErrorCode(CORRUPT) | (2 << 8)
|
CORRUPT_SEQUENCE ExtendedErrorCode = xErrorCode(CORRUPT) | (2 << 8)
|
||||||
|
@ -179,9 +179,9 @@ type FunctionFlag uint32
|
||||||
const (
|
const (
|
||||||
DETERMINISTIC FunctionFlag = 0x000000800
|
DETERMINISTIC FunctionFlag = 0x000000800
|
||||||
DIRECTONLY FunctionFlag = 0x000080000
|
DIRECTONLY FunctionFlag = 0x000080000
|
||||||
SUBTYPE FunctionFlag = 0x000100000
|
|
||||||
INNOCUOUS FunctionFlag = 0x000200000
|
INNOCUOUS FunctionFlag = 0x000200000
|
||||||
RESULT_SUBTYPE FunctionFlag = 0x001000000
|
// SUBTYPE FunctionFlag = 0x000100000
|
||||||
|
// RESULT_SUBTYPE FunctionFlag = 0x001000000
|
||||||
)
|
)
|
||||||
|
|
||||||
// StmtStatus name counter values associated with the [Stmt.Status] method.
|
// StmtStatus name counter values associated with the [Stmt.Status] method.
|
||||||
|
@ -201,6 +201,27 @@ const (
|
||||||
STMTSTATUS_MEMUSED StmtStatus = 99
|
STMTSTATUS_MEMUSED StmtStatus = 99
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// DBStatus are the available "verbs" that can be passed to the [Conn.Status] method.
|
||||||
|
//
|
||||||
|
// https://sqlite.org/c3ref/c_dbstatus_options.html
|
||||||
|
type DBStatus uint32
|
||||||
|
|
||||||
|
const (
|
||||||
|
DBSTATUS_LOOKASIDE_USED DBStatus = 0
|
||||||
|
DBSTATUS_CACHE_USED DBStatus = 1
|
||||||
|
DBSTATUS_SCHEMA_USED DBStatus = 2
|
||||||
|
DBSTATUS_STMT_USED DBStatus = 3
|
||||||
|
DBSTATUS_LOOKASIDE_HIT DBStatus = 4
|
||||||
|
DBSTATUS_LOOKASIDE_MISS_SIZE DBStatus = 5
|
||||||
|
DBSTATUS_LOOKASIDE_MISS_FULL DBStatus = 6
|
||||||
|
DBSTATUS_CACHE_HIT DBStatus = 7
|
||||||
|
DBSTATUS_CACHE_MISS DBStatus = 8
|
||||||
|
DBSTATUS_CACHE_WRITE DBStatus = 9
|
||||||
|
DBSTATUS_DEFERRED_FKS DBStatus = 10
|
||||||
|
DBSTATUS_CACHE_USED_SHARED DBStatus = 11
|
||||||
|
DBSTATUS_CACHE_SPILL DBStatus = 12
|
||||||
|
)
|
||||||
|
|
||||||
// DBConfig are the available database connection configuration options.
|
// DBConfig are the available database connection configuration options.
|
||||||
//
|
//
|
||||||
// https://sqlite.org/c3ref/c_dbconfig_defensive.html
|
// https://sqlite.org/c3ref/c_dbconfig_defensive.html
|
||||||
|
@ -229,6 +250,24 @@ const (
|
||||||
DBCONFIG_REVERSE_SCANORDER DBConfig = 1019
|
DBCONFIG_REVERSE_SCANORDER DBConfig = 1019
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// FcntlOpcode are the available opcodes for [Conn.FileControl].
|
||||||
|
//
|
||||||
|
// https://sqlite.org/c3ref/c_fcntl_begin_atomic_write.html
|
||||||
|
type FcntlOpcode uint32
|
||||||
|
|
||||||
|
const (
|
||||||
|
FCNTL_LOCKSTATE FcntlOpcode = 1
|
||||||
|
FCNTL_CHUNK_SIZE FcntlOpcode = 6
|
||||||
|
FCNTL_FILE_POINTER FcntlOpcode = 7
|
||||||
|
FCNTL_PERSIST_WAL FcntlOpcode = 10
|
||||||
|
FCNTL_POWERSAFE_OVERWRITE FcntlOpcode = 13
|
||||||
|
FCNTL_VFS_POINTER FcntlOpcode = 27
|
||||||
|
FCNTL_JOURNAL_POINTER FcntlOpcode = 28
|
||||||
|
FCNTL_DATA_VERSION FcntlOpcode = 35
|
||||||
|
FCNTL_RESERVE_BYTES FcntlOpcode = 38
|
||||||
|
FCNTL_RESET_CACHE FcntlOpcode = 42
|
||||||
|
)
|
||||||
|
|
||||||
// LimitCategory are the available run-time limit categories.
|
// LimitCategory are the available run-time limit categories.
|
||||||
//
|
//
|
||||||
// https://sqlite.org/c3ref/c_limit_attached.html
|
// https://sqlite.org/c3ref/c_limit_attached.html
|
||||||
|
@ -289,8 +328,8 @@ const (
|
||||||
AUTH_DROP_VTABLE AuthorizerActionCode = 30 /* Table Name Module Name */
|
AUTH_DROP_VTABLE AuthorizerActionCode = 30 /* Table Name Module Name */
|
||||||
AUTH_FUNCTION AuthorizerActionCode = 31 /* NULL Function Name */
|
AUTH_FUNCTION AuthorizerActionCode = 31 /* NULL Function Name */
|
||||||
AUTH_SAVEPOINT AuthorizerActionCode = 32 /* Operation Savepoint Name */
|
AUTH_SAVEPOINT AuthorizerActionCode = 32 /* Operation Savepoint Name */
|
||||||
AUTH_COPY AuthorizerActionCode = 0 /* No longer used */
|
|
||||||
AUTH_RECURSIVE AuthorizerActionCode = 33 /* NULL NULL */
|
AUTH_RECURSIVE AuthorizerActionCode = 33 /* NULL NULL */
|
||||||
|
// AUTH_COPY AuthorizerActionCode = 0 /* No longer used */
|
||||||
)
|
)
|
||||||
|
|
||||||
// AuthorizerReturnCode are the integer codes
|
// AuthorizerReturnCode are the integer codes
|
||||||
|
@ -328,6 +367,18 @@ const (
|
||||||
TXN_WRITE TxnState = 2
|
TXN_WRITE TxnState = 2
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// TraceEvent identify classes of events that can be monitored with [Conn.Trace].
|
||||||
|
//
|
||||||
|
// https://sqlite.org/c3ref/c_trace.html
|
||||||
|
type TraceEvent uint32
|
||||||
|
|
||||||
|
const (
|
||||||
|
TRACE_STMT TraceEvent = 0x01
|
||||||
|
TRACE_PROFILE TraceEvent = 0x02
|
||||||
|
TRACE_ROW TraceEvent = 0x04
|
||||||
|
TRACE_CLOSE TraceEvent = 0x08
|
||||||
|
)
|
||||||
|
|
||||||
// Datatype is a fundamental datatype of SQLite.
|
// Datatype is a fundamental datatype of SQLite.
|
||||||
//
|
//
|
||||||
// https://sqlite.org/c3ref/c_blob.html
|
// https://sqlite.org/c3ref/c_blob.html
|
||||||
|
|
|
@ -130,7 +130,8 @@ func (ctx Context) ResultNull() {
|
||||||
//
|
//
|
||||||
// https://sqlite.org/c3ref/result_blob.html
|
// https://sqlite.org/c3ref/result_blob.html
|
||||||
func (ctx Context) ResultTime(value time.Time, format TimeFormat) {
|
func (ctx Context) ResultTime(value time.Time, format TimeFormat) {
|
||||||
if format == TimeFormatDefault {
|
switch format {
|
||||||
|
case TimeFormatDefault, TimeFormatAuto, time.RFC3339Nano:
|
||||||
ctx.resultRFC3339Nano(value)
|
ctx.resultRFC3339Nano(value)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -165,7 +166,8 @@ func (ctx Context) resultRFC3339Nano(value time.Time) {
|
||||||
// https://sqlite.org/c3ref/result_blob.html
|
// https://sqlite.org/c3ref/result_blob.html
|
||||||
func (ctx Context) ResultPointer(ptr any) {
|
func (ctx Context) ResultPointer(ptr any) {
|
||||||
valPtr := util.AddHandle(ctx.c.ctx, ptr)
|
valPtr := util.AddHandle(ctx.c.ctx, ptr)
|
||||||
ctx.c.call("sqlite3_result_pointer_go", uint64(valPtr))
|
ctx.c.call("sqlite3_result_pointer_go",
|
||||||
|
uint64(ctx.handle), uint64(valPtr))
|
||||||
}
|
}
|
||||||
|
|
||||||
// ResultJSON sets the result of the function to the JSON encoding of value.
|
// ResultJSON sets the result of the function to the JSON encoding of value.
|
||||||
|
@ -175,7 +177,7 @@ func (ctx Context) ResultJSON(value any) {
|
||||||
data, err := json.Marshal(value)
|
data, err := json.Marshal(value)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.ResultError(err)
|
ctx.ResultError(err)
|
||||||
return
|
return // notest
|
||||||
}
|
}
|
||||||
ctx.ResultRawText(data)
|
ctx.ResultRawText(data)
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,21 +8,50 @@
|
||||||
//
|
//
|
||||||
// The data source name for "sqlite3" databases can be a filename or a "file:" [URI].
|
// The data source name for "sqlite3" databases can be a filename or a "file:" [URI].
|
||||||
//
|
//
|
||||||
|
// # Default transaction mode
|
||||||
|
//
|
||||||
// The [TRANSACTION] mode can be specified using "_txlock":
|
// The [TRANSACTION] mode can be specified using "_txlock":
|
||||||
//
|
//
|
||||||
// sql.Open("sqlite3", "file:demo.db?_txlock=immediate")
|
// sql.Open("sqlite3", "file:demo.db?_txlock=immediate")
|
||||||
//
|
//
|
||||||
// Possible values are: "deferred", "immediate", "exclusive".
|
// Possible values are: "deferred" (the default), "immediate", "exclusive".
|
||||||
// A [read-only] transaction is always "deferred", regardless of "_txlock".
|
// Regardless of "_txlock":
|
||||||
|
// - a [linearizable] transaction is always "exclusive";
|
||||||
|
// - a [serializable] transaction is always "immediate";
|
||||||
|
// - a [read-only] transaction is always "deferred".
|
||||||
|
//
|
||||||
|
// # Working with time
|
||||||
//
|
//
|
||||||
// The time encoding/decoding format can be specified using "_timefmt":
|
// The time encoding/decoding format can be specified using "_timefmt":
|
||||||
//
|
//
|
||||||
// sql.Open("sqlite3", "file:demo.db?_timefmt=sqlite")
|
// sql.Open("sqlite3", "file:demo.db?_timefmt=sqlite")
|
||||||
//
|
//
|
||||||
// Possible values are: "auto" (the default), "sqlite", "rfc3339";
|
// Possible values are: "auto" (the default), "sqlite", "rfc3339";
|
||||||
// "auto" encodes as RFC 3339 and decodes any [format] supported by SQLite;
|
// - "auto" encodes as RFC 3339 and decodes any [format] supported by SQLite;
|
||||||
// "sqlite" encodes as SQLite 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.
|
// - "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.
|
||||||
|
//
|
||||||
|
// To scan values in other formats, [sqlite3.TimeFormat.Scanner] may be helpful.
|
||||||
|
// To bind values in other 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].
|
||||||
|
//
|
||||||
|
// The Value method should ideally serialise to a time [format] supported by SQLite.
|
||||||
|
// This ensures SQL date and time functions work as they should,
|
||||||
|
// and that your schema works with other SQLite tools.
|
||||||
|
// [sqlite3.TimeFormat.Encode] may help.
|
||||||
|
//
|
||||||
|
// 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, nil,
|
||||||
|
// depending on the column type and what whoever wrote the value.
|
||||||
|
// [sqlite3.TimeFormat.Decode] may help.
|
||||||
|
//
|
||||||
|
// # Setting PRAGMAs
|
||||||
//
|
//
|
||||||
// [PRAGMA] statements can be specified using "_pragma":
|
// [PRAGMA] statements can be specified using "_pragma":
|
||||||
//
|
//
|
||||||
|
@ -31,13 +60,17 @@
|
||||||
// If no PRAGMAs are specified, a busy timeout of 1 minute is set.
|
// If no PRAGMAs are specified, a busy timeout of 1 minute is set.
|
||||||
//
|
//
|
||||||
// Order matters:
|
// Order matters:
|
||||||
// busy timeout and locking mode should be the first PRAGMAs set, in that order.
|
// encryption keys, busy timeout and locking mode should be the first PRAGMAs set,
|
||||||
|
// in that order.
|
||||||
//
|
//
|
||||||
// [URI]: https://sqlite.org/uri.html
|
// [URI]: https://sqlite.org/uri.html
|
||||||
// [PRAGMA]: https://sqlite.org/pragma.html
|
// [PRAGMA]: https://sqlite.org/pragma.html
|
||||||
// [format]: https://sqlite.org/lang_datefunc.html#time_values
|
|
||||||
// [TRANSACTION]: https://sqlite.org/lang_transaction.html#deferred_immediate_and_exclusive_transactions
|
// [TRANSACTION]: https://sqlite.org/lang_transaction.html#deferred_immediate_and_exclusive_transactions
|
||||||
|
// [linearizable]: https://pkg.go.dev/database/sql#TxOptions
|
||||||
|
// [serializable]: https://pkg.go.dev/database/sql#TxOptions
|
||||||
// [read-only]: https://pkg.go.dev/database/sql#TxOptions
|
// [read-only]: https://pkg.go.dev/database/sql#TxOptions
|
||||||
|
// [format]: https://sqlite.org/lang_datefunc.html#time_values
|
||||||
|
// [collating sequence]: https://sqlite.org/datatype3.html#collating_sequences
|
||||||
package driver
|
package driver
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -69,11 +102,22 @@ func init() {
|
||||||
|
|
||||||
// Open opens the SQLite database specified by dataSourceName as a [database/sql.DB].
|
// Open opens the SQLite database specified by dataSourceName as a [database/sql.DB].
|
||||||
//
|
//
|
||||||
// The init function is called by the driver on new connections.
|
// Open accepts zero, one, or two callbacks (nil callbacks are ignored).
|
||||||
|
// The first callback is called when the driver opens a new connection.
|
||||||
|
// The second callback is called before the driver closes a connection.
|
||||||
// The [sqlite3.Conn] can be used to execute queries, register functions, etc.
|
// The [sqlite3.Conn] can be used to execute queries, register functions, etc.
|
||||||
// Any error returned closes the connection and is returned to [database/sql].
|
func Open(dataSourceName string, fn ...func(*sqlite3.Conn) error) (*sql.DB, error) {
|
||||||
func Open(dataSourceName string, init func(*sqlite3.Conn) error) (*sql.DB, error) {
|
var drv SQLite
|
||||||
c, err := (&SQLite{Init: init}).OpenConnector(dataSourceName)
|
if len(fn) > 2 {
|
||||||
|
return nil, sqlite3.MISUSE
|
||||||
|
}
|
||||||
|
if len(fn) > 1 {
|
||||||
|
drv.term = fn[1]
|
||||||
|
}
|
||||||
|
if len(fn) > 0 {
|
||||||
|
drv.init = fn[0]
|
||||||
|
}
|
||||||
|
c, err := drv.OpenConnector(dataSourceName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -82,10 +126,8 @@ func Open(dataSourceName string, init func(*sqlite3.Conn) error) (*sql.DB, error
|
||||||
|
|
||||||
// SQLite implements [database/sql/driver.Driver].
|
// SQLite implements [database/sql/driver.Driver].
|
||||||
type SQLite struct {
|
type SQLite struct {
|
||||||
// Init function is called by the driver on new connections.
|
init func(*sqlite3.Conn) error
|
||||||
// The [sqlite3.Conn] can be used to execute queries, register functions, etc.
|
term func(*sqlite3.Conn) error
|
||||||
// Any error returned closes the connection and is returned to [database/sql].
|
|
||||||
Init func(*sqlite3.Conn) error
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Open implements [database/sql/driver.Driver].
|
// Open implements [database/sql/driver.Driver].
|
||||||
|
@ -119,10 +161,8 @@ func (d *SQLite) newConnector(name string) (*connector, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
switch txlock {
|
switch txlock {
|
||||||
case "":
|
case "", "deferred", "concurrent", "immediate", "exclusive":
|
||||||
c.txBegin = "BEGIN"
|
c.txLock = txlock
|
||||||
case "deferred", "immediate", "exclusive":
|
|
||||||
c.txBegin = "BEGIN " + txlock
|
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("sqlite3: invalid _txlock: %s", txlock)
|
return nil, fmt.Errorf("sqlite3: invalid _txlock: %s", txlock)
|
||||||
}
|
}
|
||||||
|
@ -147,7 +187,7 @@ func (d *SQLite) newConnector(name string) (*connector, error) {
|
||||||
type connector struct {
|
type connector struct {
|
||||||
driver *SQLite
|
driver *SQLite
|
||||||
name string
|
name string
|
||||||
txBegin string
|
txLock string
|
||||||
tmRead sqlite3.TimeFormat
|
tmRead sqlite3.TimeFormat
|
||||||
tmWrite sqlite3.TimeFormat
|
tmWrite sqlite3.TimeFormat
|
||||||
pragmas bool
|
pragmas bool
|
||||||
|
@ -159,7 +199,7 @@ func (n *connector) Driver() driver.Driver {
|
||||||
|
|
||||||
func (n *connector) Connect(ctx context.Context) (_ driver.Conn, err error) {
|
func (n *connector) Connect(ctx context.Context) (_ driver.Conn, err error) {
|
||||||
c := &conn{
|
c := &conn{
|
||||||
txBegin: n.txBegin,
|
txLock: n.txLock,
|
||||||
tmRead: n.tmRead,
|
tmRead: n.tmRead,
|
||||||
tmWrite: n.tmWrite,
|
tmWrite: n.tmWrite,
|
||||||
}
|
}
|
||||||
|
@ -178,18 +218,18 @@ func (n *connector) Connect(ctx context.Context) (_ driver.Conn, err error) {
|
||||||
defer c.Conn.SetInterrupt(old)
|
defer c.Conn.SetInterrupt(old)
|
||||||
|
|
||||||
if !n.pragmas {
|
if !n.pragmas {
|
||||||
err = c.Conn.BusyTimeout(60 * time.Second)
|
err = c.Conn.BusyTimeout(time.Minute)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if n.driver.Init != nil {
|
if n.driver.init != nil {
|
||||||
err = n.driver.Init(c.Conn)
|
err = n.driver.init(c.Conn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if n.pragmas || n.driver.Init != nil {
|
if n.pragmas || n.driver.init != nil {
|
||||||
s, _, err := c.Conn.Prepare(`PRAGMA query_only`)
|
s, _, err := c.Conn.Prepare(`PRAGMA query_only`)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -204,14 +244,21 @@ func (n *connector) Connect(ctx context.Context) (_ driver.Conn, err error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if n.driver.term != nil {
|
||||||
|
err = c.Conn.Trace(sqlite3.TRACE_CLOSE, func(sqlite3.TraceEvent, any, any) error {
|
||||||
|
return n.driver.term(c.Conn)
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
return c, nil
|
return c, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type conn struct {
|
type conn struct {
|
||||||
*sqlite3.Conn
|
*sqlite3.Conn
|
||||||
txBegin string
|
txLock string
|
||||||
txCommit string
|
txReset string
|
||||||
txRollback string
|
|
||||||
tmRead sqlite3.TimeFormat
|
tmRead sqlite3.TimeFormat
|
||||||
tmWrite sqlite3.TimeFormat
|
tmWrite sqlite3.TimeFormat
|
||||||
readOnly byte
|
readOnly byte
|
||||||
|
@ -231,31 +278,30 @@ func (c *conn) Raw() *sqlite3.Conn {
|
||||||
|
|
||||||
// Deprecated: use BeginTx instead.
|
// Deprecated: use BeginTx instead.
|
||||||
func (c *conn) Begin() (driver.Tx, error) {
|
func (c *conn) Begin() (driver.Tx, error) {
|
||||||
|
// notest
|
||||||
return c.BeginTx(context.Background(), driver.TxOptions{})
|
return c.BeginTx(context.Background(), driver.TxOptions{})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *conn) BeginTx(ctx context.Context, opts driver.TxOptions) (driver.Tx, error) {
|
func (c *conn) BeginTx(ctx context.Context, opts driver.TxOptions) (driver.Tx, error) {
|
||||||
txBegin := c.txBegin
|
var txLock string
|
||||||
c.txCommit = `COMMIT`
|
|
||||||
c.txRollback = `ROLLBACK`
|
|
||||||
|
|
||||||
if opts.ReadOnly {
|
|
||||||
txBegin = `
|
|
||||||
BEGIN deferred;
|
|
||||||
PRAGMA query_only=on`
|
|
||||||
c.txRollback = `
|
|
||||||
ROLLBACK;
|
|
||||||
PRAGMA query_only=` + string(c.readOnly)
|
|
||||||
c.txCommit = c.txRollback
|
|
||||||
}
|
|
||||||
|
|
||||||
switch opts.Isolation {
|
switch opts.Isolation {
|
||||||
default:
|
default:
|
||||||
return nil, util.IsolationErr
|
return nil, util.IsolationErr
|
||||||
case
|
case driver.IsolationLevel(sql.LevelLinearizable):
|
||||||
driver.IsolationLevel(sql.LevelDefault),
|
txLock = "exclusive"
|
||||||
driver.IsolationLevel(sql.LevelSerializable):
|
case driver.IsolationLevel(sql.LevelSerializable):
|
||||||
break
|
txLock = "immediate"
|
||||||
|
case driver.IsolationLevel(sql.LevelDefault):
|
||||||
|
if !opts.ReadOnly {
|
||||||
|
txLock = c.txLock
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
c.txReset = ``
|
||||||
|
txBegin := `BEGIN ` + txLock
|
||||||
|
if opts.ReadOnly {
|
||||||
|
txBegin += ` ; PRAGMA query_only=on`
|
||||||
|
c.txReset = `; PRAGMA query_only=` + string(c.readOnly)
|
||||||
}
|
}
|
||||||
|
|
||||||
old := c.Conn.SetInterrupt(ctx)
|
old := c.Conn.SetInterrupt(ctx)
|
||||||
|
@ -269,7 +315,7 @@ func (c *conn) BeginTx(ctx context.Context, opts driver.TxOptions) (driver.Tx, e
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *conn) Commit() error {
|
func (c *conn) Commit() error {
|
||||||
err := c.Conn.Exec(c.txCommit)
|
err := c.Conn.Exec(`COMMIT` + c.txReset)
|
||||||
if err != nil && !c.Conn.GetAutocommit() {
|
if err != nil && !c.Conn.GetAutocommit() {
|
||||||
c.Rollback()
|
c.Rollback()
|
||||||
}
|
}
|
||||||
|
@ -277,16 +323,17 @@ func (c *conn) Commit() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *conn) Rollback() error {
|
func (c *conn) Rollback() error {
|
||||||
err := c.Conn.Exec(c.txRollback)
|
err := c.Conn.Exec(`ROLLBACK` + c.txReset)
|
||||||
if errors.Is(err, sqlite3.INTERRUPT) {
|
if errors.Is(err, sqlite3.INTERRUPT) {
|
||||||
old := c.Conn.SetInterrupt(context.Background())
|
old := c.Conn.SetInterrupt(context.Background())
|
||||||
defer c.Conn.SetInterrupt(old)
|
defer c.Conn.SetInterrupt(old)
|
||||||
err = c.Conn.Exec(c.txRollback)
|
err = c.Conn.Exec(`ROLLBACK` + c.txReset)
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *conn) Prepare(query string) (driver.Stmt, error) {
|
func (c *conn) Prepare(query string) (driver.Stmt, error) {
|
||||||
|
// notest
|
||||||
return c.PrepareContext(context.Background(), query)
|
return c.PrepareContext(context.Background(), query)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -329,6 +376,8 @@ func (c *conn) ExecContext(ctx context.Context, query string, args []driver.Name
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *conn) CheckNamedValue(arg *driver.NamedValue) error {
|
func (c *conn) CheckNamedValue(arg *driver.NamedValue) error {
|
||||||
|
// Fast path: short circuit argument verification.
|
||||||
|
// Arguments will be rejected by conn.ExecContext.
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -363,11 +412,13 @@ func (s *stmt) NumInput() int {
|
||||||
|
|
||||||
// Deprecated: use ExecContext instead.
|
// Deprecated: use ExecContext instead.
|
||||||
func (s *stmt) Exec(args []driver.Value) (driver.Result, error) {
|
func (s *stmt) Exec(args []driver.Value) (driver.Result, error) {
|
||||||
|
// notest
|
||||||
return s.ExecContext(context.Background(), namedValues(args))
|
return s.ExecContext(context.Background(), namedValues(args))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Deprecated: use QueryContext instead.
|
// Deprecated: use QueryContext instead.
|
||||||
func (s *stmt) Query(args []driver.Value) (driver.Rows, error) {
|
func (s *stmt) Query(args []driver.Value) (driver.Rows, error) {
|
||||||
|
// notest
|
||||||
return s.QueryContext(context.Background(), namedValues(args))
|
return s.QueryContext(context.Background(), namedValues(args))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -561,7 +612,8 @@ func (r *rows) Next(dest []driver.Value) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *rows) decodeTime(i int, v any) (_ time.Time, ok bool) {
|
func (r *rows) decodeTime(i int, v any) (_ time.Time, ok bool) {
|
||||||
if r.tmRead == sqlite3.TimeFormatDefault {
|
switch r.tmRead {
|
||||||
|
case sqlite3.TimeFormatDefault, time.RFC3339Nano:
|
||||||
// handled by maybeTime
|
// handled by maybeTime
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,12 +16,25 @@ func Savepoint(tx *sql.Tx) sqlite3.Savepoint {
|
||||||
return ctx.Savepoint
|
return ctx.Savepoint
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A saveptCtx is never canceled, has no values, and has no deadline.
|
||||||
type saveptCtx struct{ sqlite3.Savepoint }
|
type saveptCtx struct{ sqlite3.Savepoint }
|
||||||
|
|
||||||
func (*saveptCtx) Deadline() (deadline time.Time, ok bool) { return }
|
func (*saveptCtx) Deadline() (deadline time.Time, ok bool) {
|
||||||
|
// notest
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func (*saveptCtx) Done() <-chan struct{} { return nil }
|
func (*saveptCtx) Done() <-chan struct{} {
|
||||||
|
// notest
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (*saveptCtx) Err() error { return nil }
|
func (*saveptCtx) Err() error {
|
||||||
|
// notest
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (*saveptCtx) Value(key any) any { return nil }
|
func (*saveptCtx) Value(key any) any {
|
||||||
|
// notest
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# Embeddable Wasm build of SQLite
|
# Embeddable Wasm build of SQLite
|
||||||
|
|
||||||
This folder includes an embeddable Wasm build of SQLite 3.46.0 for use with
|
This folder includes an embeddable Wasm build of SQLite 3.46.1 for use with
|
||||||
[`github.com/ncruces/go-sqlite3`](https://pkg.go.dev/github.com/ncruces/go-sqlite3).
|
[`github.com/ncruces/go-sqlite3`](https://pkg.go.dev/github.com/ncruces/go-sqlite3).
|
||||||
|
|
||||||
The following optional features are compiled in:
|
The following optional features are compiled in:
|
||||||
|
@ -17,10 +17,9 @@ The following optional features are compiled in:
|
||||||
- [regexp](https://github.com/sqlite/sqlite/blob/master/ext/misc/regexp.c)
|
- [regexp](https://github.com/sqlite/sqlite/blob/master/ext/misc/regexp.c)
|
||||||
- [series](https://github.com/sqlite/sqlite/blob/master/ext/misc/series.c)
|
- [series](https://github.com/sqlite/sqlite/blob/master/ext/misc/series.c)
|
||||||
- [uint](https://github.com/sqlite/sqlite/blob/master/ext/misc/uint.c)
|
- [uint](https://github.com/sqlite/sqlite/blob/master/ext/misc/uint.c)
|
||||||
- [uuid](https://github.com/sqlite/sqlite/blob/master/ext/misc/uuid.c)
|
|
||||||
- [time](../sqlite3/time.c)
|
- [time](../sqlite3/time.c)
|
||||||
|
|
||||||
See the [configuration options](../sqlite3/sqlite_cfg.h),
|
See the [configuration options](../sqlite3/sqlite_opt.h),
|
||||||
and [patches](../sqlite3) applied.
|
and [patches](../sqlite3) applied.
|
||||||
|
|
||||||
Built using [`wasi-sdk`](https://github.com/WebAssembly/wasi-sdk),
|
Built using [`wasi-sdk`](https://github.com/WebAssembly/wasi-sdk),
|
||||||
|
@ -28,3 +27,14 @@ and [`binaryen`](https://github.com/WebAssembly/binaryen).
|
||||||
|
|
||||||
The build is easily reproducible, and verifiable, using
|
The build is easily reproducible, and verifiable, using
|
||||||
[Artifact Attestations](https://github.com/ncruces/go-sqlite3/attestations).
|
[Artifact Attestations](https://github.com/ncruces/go-sqlite3/attestations).
|
||||||
|
|
||||||
|
### Customizing the build
|
||||||
|
|
||||||
|
You can use your own custom build of SQLite.
|
||||||
|
|
||||||
|
Examples of custom builds of SQLite are:
|
||||||
|
- [`github.com/ncruces/go-sqlite3/embed/bcw2`](https://github.com/ncruces/go-sqlite3/tree/main/embed/bcw2)
|
||||||
|
built from a branch supporting [`BEGIN CONCURRENT`](https://sqlite.org/src/doc/begin-concurrent/doc/begin_concurrent.md)
|
||||||
|
and [Wal2](https://www.sqlite.org/cgi/src/doc/wal2/doc/wal2.md).
|
||||||
|
- [`github.com/asg017/sqlite-vec-go-bindings/ncruces`](https://github.com/asg017/sqlite-vec-go-bindings)
|
||||||
|
which includes the [`sqlite-vec`](https://github.com/asg017/sqlite-vec) vector search extension.
|
|
@ -4,26 +4,27 @@ set -euo pipefail
|
||||||
cd -P -- "$(dirname -- "$0")"
|
cd -P -- "$(dirname -- "$0")"
|
||||||
|
|
||||||
ROOT=../
|
ROOT=../
|
||||||
BINARYEN="$ROOT/tools/binaryen-version_117/bin"
|
BINARYEN="$ROOT/tools/binaryen/bin"
|
||||||
WASI_SDK="$ROOT/tools/wasi-sdk-22.0/bin"
|
WASI_SDK="$ROOT/tools/wasi-sdk/bin"
|
||||||
|
|
||||||
"$WASI_SDK/clang" --target=wasm32-wasi -std=c23 -flto -g0 -O2 \
|
trap 'rm -f sqlite3.tmp' EXIT
|
||||||
|
|
||||||
|
"$WASI_SDK/clang" --target=wasm32-wasi -std=c23 -g0 -O2 \
|
||||||
-Wall -Wextra -Wno-unused-parameter -Wno-unused-function \
|
-Wall -Wextra -Wno-unused-parameter -Wno-unused-function \
|
||||||
-o sqlite3.wasm "$ROOT/sqlite3/main.c" \
|
-o sqlite3.wasm "$ROOT/sqlite3/main.c" \
|
||||||
-I"$ROOT/sqlite3" \
|
-I"$ROOT/sqlite3" \
|
||||||
-mexec-model=reactor \
|
-mexec-model=reactor \
|
||||||
-msimd128 -mmutable-globals \
|
-matomics -msimd128 -mmutable-globals \
|
||||||
-mbulk-memory -mreference-types \
|
-mbulk-memory -mreference-types \
|
||||||
-mnontrapping-fptoint -msign-ext \
|
-mnontrapping-fptoint -msign-ext \
|
||||||
-fno-stack-protector -fno-stack-clash-protection \
|
-fno-stack-protector -fno-stack-clash-protection \
|
||||||
-Wl,--initial-memory=327680 \
|
|
||||||
-Wl,--stack-first \
|
-Wl,--stack-first \
|
||||||
-Wl,--import-undefined \
|
-Wl,--import-undefined \
|
||||||
|
-Wl,--initial-memory=327680 \
|
||||||
-D_HAVE_SQLITE_CONFIG_H \
|
-D_HAVE_SQLITE_CONFIG_H \
|
||||||
-DSQLITE_CUSTOM_INCLUDE=sqlite_opt.h \
|
-DSQLITE_CUSTOM_INCLUDE=sqlite_opt.h \
|
||||||
$(awk '{print "-Wl,--export="$0}' exports.txt)
|
$(awk '{print "-Wl,--export="$0}' exports.txt)
|
||||||
|
|
||||||
trap 'rm -f sqlite3.tmp' EXIT
|
|
||||||
"$BINARYEN/wasm-ctor-eval" -g -c _initialize sqlite3.wasm -o sqlite3.tmp
|
"$BINARYEN/wasm-ctor-eval" -g -c _initialize sqlite3.wasm -o sqlite3.tmp
|
||||||
"$BINARYEN/wasm-opt" -g --strip --strip-producers -c -O3 \
|
"$BINARYEN/wasm-opt" -g --strip --strip-producers -c -O3 \
|
||||||
sqlite3.tmp -o sqlite3.wasm \
|
sqlite3.tmp -o sqlite3.wasm \
|
||||||
|
|
|
@ -55,17 +55,21 @@ sqlite3_create_function_go
|
||||||
sqlite3_create_module_go
|
sqlite3_create_module_go
|
||||||
sqlite3_create_window_function_go
|
sqlite3_create_window_function_go
|
||||||
sqlite3_database_file_object
|
sqlite3_database_file_object
|
||||||
|
sqlite3_db_cacheflush
|
||||||
sqlite3_db_config
|
sqlite3_db_config
|
||||||
sqlite3_db_filename
|
sqlite3_db_filename
|
||||||
sqlite3_db_name
|
sqlite3_db_name
|
||||||
sqlite3_db_readonly
|
sqlite3_db_readonly
|
||||||
sqlite3_db_release_memory
|
sqlite3_db_release_memory
|
||||||
|
sqlite3_db_status
|
||||||
sqlite3_declare_vtab
|
sqlite3_declare_vtab
|
||||||
sqlite3_errcode
|
sqlite3_errcode
|
||||||
sqlite3_errmsg
|
sqlite3_errmsg
|
||||||
sqlite3_error_offset
|
sqlite3_error_offset
|
||||||
sqlite3_errstr
|
sqlite3_errstr
|
||||||
sqlite3_exec
|
sqlite3_exec
|
||||||
|
sqlite3_expanded_sql
|
||||||
|
sqlite3_file_control
|
||||||
sqlite3_filename_database
|
sqlite3_filename_database
|
||||||
sqlite3_filename_journal
|
sqlite3_filename_journal
|
||||||
sqlite3_filename_wal
|
sqlite3_filename_wal
|
||||||
|
@ -100,16 +104,18 @@ sqlite3_step
|
||||||
sqlite3_stmt_busy
|
sqlite3_stmt_busy
|
||||||
sqlite3_stmt_readonly
|
sqlite3_stmt_readonly
|
||||||
sqlite3_stmt_status
|
sqlite3_stmt_status
|
||||||
|
sqlite3_table_column_metadata
|
||||||
sqlite3_total_changes64
|
sqlite3_total_changes64
|
||||||
|
sqlite3_trace_go
|
||||||
sqlite3_txn_state
|
sqlite3_txn_state
|
||||||
sqlite3_update_hook_go
|
sqlite3_update_hook_go
|
||||||
sqlite3_uri_key
|
sqlite3_uri_key
|
||||||
sqlite3_uri_parameter
|
|
||||||
sqlite3_value_blob
|
sqlite3_value_blob
|
||||||
sqlite3_value_bytes
|
sqlite3_value_bytes
|
||||||
sqlite3_value_double
|
sqlite3_value_double
|
||||||
sqlite3_value_dup
|
sqlite3_value_dup
|
||||||
sqlite3_value_free
|
sqlite3_value_free
|
||||||
|
sqlite3_value_frombind
|
||||||
sqlite3_value_int64
|
sqlite3_value_int64
|
||||||
sqlite3_value_nochange
|
sqlite3_value_nochange
|
||||||
sqlite3_value_numeric_type
|
sqlite3_value_numeric_type
|
||||||
|
|
Binary file not shown.
|
@ -3,5 +3,7 @@ golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||||
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||||
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
|
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
|
||||||
golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0=
|
golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0=
|
||||||
|
golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4=
|
||||||
|
golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk=
|
||||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
|
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
|
||||||
|
|
|
@ -32,7 +32,7 @@ func (s *mmapState) new(ctx context.Context, mod api.Module, size int32) *Mapped
|
||||||
|
|
||||||
// Allocate page aligned memmory.
|
// Allocate page aligned memmory.
|
||||||
alloc := mod.ExportedFunction("aligned_alloc")
|
alloc := mod.ExportedFunction("aligned_alloc")
|
||||||
stack := [2]uint64{
|
stack := [...]uint64{
|
||||||
uint64(unix.Getpagesize()),
|
uint64(unix.Getpagesize()),
|
||||||
uint64(size),
|
uint64(size),
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@ import (
|
||||||
"github.com/ncruces/go-sqlite3/vfs"
|
"github.com/ncruces/go-sqlite3/vfs"
|
||||||
"github.com/tetratelabs/wazero"
|
"github.com/tetratelabs/wazero"
|
||||||
"github.com/tetratelabs/wazero/api"
|
"github.com/tetratelabs/wazero/api"
|
||||||
|
"github.com/tetratelabs/wazero/experimental"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Configure SQLite Wasm.
|
// Configure SQLite Wasm.
|
||||||
|
@ -44,12 +45,14 @@ var instance struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func compileSQLite() {
|
func compileSQLite() {
|
||||||
if RuntimeConfig == nil {
|
ctx := context.Background()
|
||||||
RuntimeConfig = wazero.NewRuntimeConfig()
|
cfg := RuntimeConfig
|
||||||
|
if cfg == nil {
|
||||||
|
cfg = wazero.NewRuntimeConfig()
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx := context.Background()
|
instance.runtime = wazero.NewRuntimeWithConfig(ctx,
|
||||||
instance.runtime = wazero.NewRuntimeWithConfig(ctx, RuntimeConfig)
|
cfg.WithCoreFeatures(api.CoreFeaturesV2|experimental.CoreFeaturesThreads))
|
||||||
|
|
||||||
env := instance.runtime.NewHostModuleBuilder("env")
|
env := instance.runtime.NewHostModuleBuilder("env")
|
||||||
env = vfs.ExportHostFunctions(env)
|
env = vfs.ExportHostFunctions(env)
|
||||||
|
@ -82,7 +85,7 @@ type sqlite struct {
|
||||||
id [32]*byte
|
id [32]*byte
|
||||||
mask uint32
|
mask uint32
|
||||||
}
|
}
|
||||||
stack [8]uint64
|
stack [9]uint64
|
||||||
freer uint32
|
freer uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -303,6 +306,7 @@ func exportCallbacks(env wazero.HostModuleBuilder) wazero.HostModuleBuilder {
|
||||||
util.ExportFuncVI(env, "go_rollback_hook", rollbackCallback)
|
util.ExportFuncVI(env, "go_rollback_hook", rollbackCallback)
|
||||||
util.ExportFuncVIIIIJ(env, "go_update_hook", updateCallback)
|
util.ExportFuncVIIIIJ(env, "go_update_hook", updateCallback)
|
||||||
util.ExportFuncIIIII(env, "go_wal_hook", walCallback)
|
util.ExportFuncIIIII(env, "go_wal_hook", walCallback)
|
||||||
|
util.ExportFuncIIIII(env, "go_trace", traceCallback)
|
||||||
util.ExportFuncIIIIII(env, "go_autovacuum_pages", autoVacuumCallback)
|
util.ExportFuncIIIIII(env, "go_autovacuum_pages", autoVacuumCallback)
|
||||||
util.ExportFuncIIIIIII(env, "go_authorizer", authorizerCallback)
|
util.ExportFuncIIIIIII(env, "go_authorizer", authorizerCallback)
|
||||||
util.ExportFuncVIII(env, "go_log", logCallback)
|
util.ExportFuncVIII(env, "go_log", logCallback)
|
||||||
|
|
|
@ -15,6 +15,7 @@ import (
|
||||||
type Stmt struct {
|
type Stmt struct {
|
||||||
c *Conn
|
c *Conn
|
||||||
err error
|
err error
|
||||||
|
sql string
|
||||||
handle uint32
|
handle uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,6 +30,15 @@ func (s *Stmt) Close() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
r := s.c.call("sqlite3_finalize", uint64(s.handle))
|
r := s.c.call("sqlite3_finalize", uint64(s.handle))
|
||||||
|
for i := range s.c.stmts {
|
||||||
|
if s == s.c.stmts[i] {
|
||||||
|
l := len(s.c.stmts) - 1
|
||||||
|
s.c.stmts[i] = s.c.stmts[l]
|
||||||
|
s.c.stmts[l] = nil
|
||||||
|
s.c.stmts = s.c.stmts[:l]
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
s.handle = 0
|
s.handle = 0
|
||||||
return s.c.error(r)
|
return s.c.error(r)
|
||||||
|
@ -41,6 +51,24 @@ func (s *Stmt) Conn() *Conn {
|
||||||
return s.c
|
return s.c
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SQL returns the SQL text used to create the prepared statement.
|
||||||
|
//
|
||||||
|
// https://sqlite.org/c3ref/expanded_sql.html
|
||||||
|
func (s *Stmt) SQL() string {
|
||||||
|
return s.sql
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExpandedSQL returns the SQL text of the prepared statement
|
||||||
|
// with bound parameters expanded.
|
||||||
|
//
|
||||||
|
// https://sqlite.org/c3ref/expanded_sql.html
|
||||||
|
func (s *Stmt) ExpandedSQL() string {
|
||||||
|
r := s.c.call("sqlite3_expanded_sql", uint64(s.handle))
|
||||||
|
sql := util.ReadString(s.c.mod, uint32(r), _MAX_SQL_LENGTH)
|
||||||
|
s.c.free(uint32(r))
|
||||||
|
return sql
|
||||||
|
}
|
||||||
|
|
||||||
// ReadOnly returns true if and only if the statement
|
// ReadOnly returns true if and only if the statement
|
||||||
// makes no direct changes to the content of the database file.
|
// makes no direct changes to the content of the database file.
|
||||||
//
|
//
|
||||||
|
@ -283,7 +311,8 @@ func (s *Stmt) BindNull(param int) error {
|
||||||
//
|
//
|
||||||
// https://sqlite.org/c3ref/bind_blob.html
|
// https://sqlite.org/c3ref/bind_blob.html
|
||||||
func (s *Stmt) BindTime(param int, value time.Time, format TimeFormat) error {
|
func (s *Stmt) BindTime(param int, value time.Time, format TimeFormat) error {
|
||||||
if format == TimeFormatDefault {
|
switch format {
|
||||||
|
case TimeFormatDefault, TimeFormatAuto, time.RFC3339Nano:
|
||||||
return s.bindRFC3339Nano(param, value)
|
return s.bindRFC3339Nano(param, value)
|
||||||
}
|
}
|
||||||
switch v := format.Encode(value).(type) {
|
switch v := format.Encode(value).(type) {
|
||||||
|
|
|
@ -32,6 +32,19 @@ func (c *Conn) Begin() Txn {
|
||||||
return Txn{c}
|
return Txn{c}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BeginConcurrent starts a concurrent transaction.
|
||||||
|
//
|
||||||
|
// Experimental: requires a custom build of SQLite.
|
||||||
|
//
|
||||||
|
// https://sqlite.org/cgi/src/doc/begin-concurrent/doc/begin_concurrent.md
|
||||||
|
func (c *Conn) BeginConcurrent() (Txn, error) {
|
||||||
|
err := c.Exec(`BEGIN CONCURRENT`)
|
||||||
|
if err != nil {
|
||||||
|
return Txn{}, err
|
||||||
|
}
|
||||||
|
return Txn{c}, nil
|
||||||
|
}
|
||||||
|
|
||||||
// BeginImmediate starts an immediate transaction.
|
// BeginImmediate starts an immediate transaction.
|
||||||
//
|
//
|
||||||
// https://sqlite.org/lang_transaction.html
|
// https://sqlite.org/lang_transaction.html
|
||||||
|
@ -217,7 +230,7 @@ func (c *Conn) txnExecInterrupted(sql string) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// TxnState starts a deferred transaction.
|
// TxnState determines the transaction state of a database.
|
||||||
//
|
//
|
||||||
// https://sqlite.org/c3ref/txn_state.html
|
// https://sqlite.org/c3ref/txn_state.html
|
||||||
func (c *Conn) TxnState(schema string) TxnState {
|
func (c *Conn) TxnState(schema string) TxnState {
|
||||||
|
@ -292,3 +305,11 @@ func updateCallback(ctx context.Context, mod api.Module, pDB uint32, action Auth
|
||||||
c.update(action, schema, table, int64(rowid))
|
c.update(action, schema, table, int64(rowid))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CacheFlush flushes caches to disk mid-transaction.
|
||||||
|
//
|
||||||
|
// https://sqlite.org/c3ref/db_cacheflush.html
|
||||||
|
func (c *Conn) CacheFlush() error {
|
||||||
|
r := c.call("sqlite3_db_cacheflush", uint64(c.handle))
|
||||||
|
return c.error(r)
|
||||||
|
}
|
||||||
|
|
|
@ -201,6 +201,14 @@ func (v Value) NoChange() bool {
|
||||||
return r != 0
|
return r != 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FromBind returns true if value originated from a bound parameter.
|
||||||
|
//
|
||||||
|
// https://sqlite.org/c3ref/value_blob.html
|
||||||
|
func (v Value) FromBind() bool {
|
||||||
|
r := v.c.call("sqlite3_value_frombind", v.protected())
|
||||||
|
return r != 0
|
||||||
|
}
|
||||||
|
|
||||||
// InFirst returns the first element
|
// InFirst returns the first element
|
||||||
// on the right-hand side of an IN constraint.
|
// on the right-hand side of an IN constraint.
|
||||||
//
|
//
|
||||||
|
|
|
@ -69,6 +69,7 @@ func (vfsOS) Access(name string, flags AccessFlag) (bool, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (vfsOS) Open(name string, flags OpenFlag) (File, OpenFlag, error) {
|
func (vfsOS) Open(name string, flags OpenFlag) (File, OpenFlag, error) {
|
||||||
|
// notest // OpenFilename is called instead
|
||||||
return nil, 0, _CANTOPEN
|
return nil, 0, _CANTOPEN
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,8 +20,8 @@ type Filename struct {
|
||||||
stack [2]uint64
|
stack [2]uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
// OpenFilename is an internal API users should not call directly.
|
// GetFilename is an internal API users should not call directly.
|
||||||
func OpenFilename(ctx context.Context, mod api.Module, id uint32, flags OpenFlag) *Filename {
|
func GetFilename(ctx context.Context, mod api.Module, id uint32, flags OpenFlag) *Filename {
|
||||||
if id == 0 {
|
if id == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -66,6 +66,10 @@ func (n *Filename) path(method string) string {
|
||||||
if n == nil || n.zPath == 0 {
|
if n == nil || n.zPath == 0 {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
if n.flags&(OPEN_MAIN_DB|OPEN_MAIN_JOURNAL|OPEN_WAL) == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
n.stack[0] = uint64(n.zPath)
|
n.stack[0] = uint64(n.zPath)
|
||||||
fn := n.mod.ExportedFunction(method)
|
fn := n.mod.ExportedFunction(method)
|
||||||
if err := fn.CallWithStack(n.ctx, n.stack[:]); err != nil {
|
if err := fn.CallWithStack(n.ctx, n.stack[:]); err != nil {
|
||||||
|
|
|
@ -10,7 +10,10 @@
|
||||||
package memdb
|
package memdb
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/url"
|
||||||
"sync"
|
"sync"
|
||||||
|
"testing"
|
||||||
|
|
||||||
"github.com/ncruces/go-sqlite3/vfs"
|
"github.com/ncruces/go-sqlite3/vfs"
|
||||||
)
|
)
|
||||||
|
@ -39,8 +42,9 @@ func Create(name string, data []byte) {
|
||||||
size: int64(len(data)),
|
size: int64(len(data)),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert data from WAL to rollback journal.
|
// Convert data from WAL/2 to rollback journal.
|
||||||
if len(data) >= 20 && data[18] == 2 && data[19] == 2 {
|
if len(data) >= 20 && (data[18] == 2 && data[19] == 2 ||
|
||||||
|
data[18] == 3 && data[19] == 3) {
|
||||||
data[18] = 1
|
data[18] = 1
|
||||||
data[19] = 1
|
data[19] = 1
|
||||||
}
|
}
|
||||||
|
@ -66,3 +70,30 @@ func Delete(name string) {
|
||||||
defer memoryMtx.Unlock()
|
defer memoryMtx.Unlock()
|
||||||
delete(memoryDBs, name)
|
delete(memoryDBs, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestDB creates an empty shared memory database for the test to use.
|
||||||
|
// The database is automatically deleted when the test and all its subtests complete.
|
||||||
|
// Each subsequent call to TestDB returns a unique database.
|
||||||
|
func TestDB(tb testing.TB, params ...url.Values) string {
|
||||||
|
tb.Helper()
|
||||||
|
|
||||||
|
name := fmt.Sprintf("%s_%p", tb.Name(), tb)
|
||||||
|
tb.Cleanup(func() { Delete(name) })
|
||||||
|
Create(name, nil)
|
||||||
|
|
||||||
|
p := url.Values{"vfs": {"memdb"}}
|
||||||
|
for _, v := range params {
|
||||||
|
for k, v := range v {
|
||||||
|
for _, v := range v {
|
||||||
|
p.Add(k, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (&url.URL{
|
||||||
|
Scheme: "file",
|
||||||
|
OmitHost: true,
|
||||||
|
Path: "/" + name,
|
||||||
|
RawQuery: p.Encode(),
|
||||||
|
}).String()
|
||||||
|
}
|
||||||
|
|
|
@ -30,6 +30,7 @@ func (memVFS) Open(name string, flags vfs.OpenFlag) (vfs.File, vfs.OpenFlag, err
|
||||||
vfs.OPEN_TEMP_DB |
|
vfs.OPEN_TEMP_DB |
|
||||||
vfs.OPEN_TEMP_JOURNAL
|
vfs.OPEN_TEMP_JOURNAL
|
||||||
if flags&types == 0 {
|
if flags&types == 0 {
|
||||||
|
// notest // OPEN_MEMORY
|
||||||
return nil, flags, sqlite3.CANTOPEN
|
return nil, flags, sqlite3.CANTOPEN
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,7 +83,7 @@ type memDB struct {
|
||||||
size int64
|
size int64
|
||||||
|
|
||||||
// +checklocks:lockMtx
|
// +checklocks:lockMtx
|
||||||
shared int
|
shared int32
|
||||||
// +checklocks:lockMtx
|
// +checklocks:lockMtx
|
||||||
reserved bool
|
reserved bool
|
||||||
// +checklocks:lockMtx
|
// +checklocks:lockMtx
|
||||||
|
@ -136,7 +137,7 @@ func (m *memFile) ReadAt(b []byte, off int64) (n int, err error) {
|
||||||
}
|
}
|
||||||
n = copy(b, (*m.data[base])[rest:have])
|
n = copy(b, (*m.data[base])[rest:have])
|
||||||
if n < len(b) {
|
if n < len(b) {
|
||||||
// Assume reads are page aligned.
|
// notest // assume reads are page aligned
|
||||||
return 0, io.ErrNoProgress
|
return 0, io.ErrNoProgress
|
||||||
}
|
}
|
||||||
return n, nil
|
return n, nil
|
||||||
|
@ -153,7 +154,7 @@ func (m *memFile) WriteAt(b []byte, off int64) (n int, err error) {
|
||||||
}
|
}
|
||||||
n = copy((*m.data[base])[rest:], b)
|
n = copy((*m.data[base])[rest:], b)
|
||||||
if n < len(b) {
|
if n < len(b) {
|
||||||
// Assume writes are page aligned.
|
// notest // assume writes are page aligned
|
||||||
return n, io.ErrShortWrite
|
return n, io.ErrShortWrite
|
||||||
}
|
}
|
||||||
if size := off + int64(len(b)); size > m.size {
|
if size := off + int64(len(b)); size > m.size {
|
||||||
|
@ -226,9 +227,6 @@ func (m *memFile) Lock(lock vfs.LockLevel) error {
|
||||||
|
|
||||||
case vfs.LOCK_EXCLUSIVE:
|
case vfs.LOCK_EXCLUSIVE:
|
||||||
if m.lock < vfs.LOCK_PENDING {
|
if m.lock < vfs.LOCK_PENDING {
|
||||||
if m.pending {
|
|
||||||
return sqlite3.BUSY
|
|
||||||
}
|
|
||||||
m.lock = vfs.LOCK_PENDING
|
m.lock = vfs.LOCK_PENDING
|
||||||
m.pending = true
|
m.pending = true
|
||||||
}
|
}
|
||||||
|
@ -269,6 +267,7 @@ func (m *memFile) Unlock(lock vfs.LockLevel) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *memFile) CheckReservedLock() (bool, error) {
|
func (m *memFile) CheckReservedLock() (bool, error) {
|
||||||
|
// notest // OPEN_MEMORY
|
||||||
if m.lock >= vfs.LOCK_RESERVED {
|
if m.lock >= vfs.LOCK_RESERVED {
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
@ -278,6 +277,7 @@ func (m *memFile) CheckReservedLock() (bool, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *memFile) SectorSize() int {
|
func (m *memFile) SectorSize() int {
|
||||||
|
// notest // IOCAP_POWERSAFE_OVERWRITE
|
||||||
return sectorSize
|
return sectorSize
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@ package vfs
|
||||||
import (
|
import (
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
"runtime"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"golang.org/x/sys/unix"
|
"golang.org/x/sys/unix"
|
||||||
|
@ -68,7 +69,7 @@ func osUnlock(file *os.File, start, len int64) _ErrorCode {
|
||||||
}
|
}
|
||||||
|
|
||||||
func osLock(file *os.File, typ int16, start, len int64, timeout time.Duration, def _ErrorCode) _ErrorCode {
|
func osLock(file *os.File, typ int16, start, len int64, timeout time.Duration, def _ErrorCode) _ErrorCode {
|
||||||
lock := flocktimeout_t{fl: unix.Flock_t{
|
lock := &flocktimeout_t{fl: unix.Flock_t{
|
||||||
Type: typ,
|
Type: typ,
|
||||||
Start: start,
|
Start: start,
|
||||||
Len: len,
|
Len: len,
|
||||||
|
@ -82,6 +83,7 @@ func osLock(file *os.File, typ int16, start, len int64, timeout time.Duration, d
|
||||||
default:
|
default:
|
||||||
lock.timeout = unix.NsecToTimespec(int64(timeout / time.Nanosecond))
|
lock.timeout = unix.NsecToTimespec(int64(timeout / time.Nanosecond))
|
||||||
err = unix.FcntlFlock(file.Fd(), _F_OFD_SETLKWTIMEOUT, &lock.fl)
|
err = unix.FcntlFlock(file.Fd(), _F_OFD_SETLKWTIMEOUT, &lock.fl)
|
||||||
|
runtime.KeepAlive(lock)
|
||||||
}
|
}
|
||||||
return osLockErrorCode(err, def)
|
return osLockErrorCode(err, def)
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,8 @@ const (
|
||||||
_F2FS_FEATURE_ATOMIC_WRITE = 4
|
_F2FS_FEATURE_ATOMIC_WRITE = 4
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// notest
|
||||||
|
|
||||||
func osBatchAtomic(file *os.File) bool {
|
func osBatchAtomic(file *os.File) bool {
|
||||||
flags, err := unix.IoctlGetInt(int(file.Fd()), _F2FS_IOC_GET_FEATURES)
|
flags, err := unix.IoctlGetInt(int(file.Fd()), _F2FS_IOC_GET_FEATURES)
|
||||||
return err == nil && flags&_F2FS_FEATURE_ATOMIC_WRITE != 0
|
return err == nil && flags&_F2FS_FEATURE_ATOMIC_WRITE != 0
|
||||||
|
|
|
@ -50,6 +50,7 @@ func osDowngradeLock(file *os.File, state LockLevel) _ErrorCode {
|
||||||
// indicates that the other process is not following the locking
|
// indicates that the other process is not following the locking
|
||||||
// protocol. If this happens, return IOERR_RDLOCK. Returning
|
// protocol. If this happens, return IOERR_RDLOCK. Returning
|
||||||
// BUSY would confuse the upper layer.
|
// BUSY would confuse the upper layer.
|
||||||
|
// notest
|
||||||
return _IOERR_RDLOCK
|
return _IOERR_RDLOCK
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -98,6 +99,7 @@ func osLockErrorCode(err error, def _ErrorCode) _ErrorCode {
|
||||||
case unix.EPERM:
|
case unix.EPERM:
|
||||||
return _PERM
|
return _PERM
|
||||||
}
|
}
|
||||||
|
// notest // usually EWOULDBLOCK == EAGAIN
|
||||||
if errno == unix.EWOULDBLOCK && unix.EWOULDBLOCK != unix.EAGAIN {
|
if errno == unix.EWOULDBLOCK && unix.EWOULDBLOCK != unix.EAGAIN {
|
||||||
return _BUSY
|
return _BUSY
|
||||||
}
|
}
|
||||||
|
|
|
@ -66,6 +66,7 @@ func osDowngradeLock(file *os.File, state LockLevel) _ErrorCode {
|
||||||
if rc := osReadLock(file, _SHARED_FIRST, _SHARED_SIZE, 0); rc != _OK {
|
if rc := osReadLock(file, _SHARED_FIRST, _SHARED_SIZE, 0); rc != _OK {
|
||||||
// This should never happen.
|
// This should never happen.
|
||||||
// We should always be able to reacquire the read lock.
|
// We should always be able to reacquire the read lock.
|
||||||
|
// notest
|
||||||
return _IOERR_RDLOCK
|
return _IOERR_RDLOCK
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -72,29 +72,29 @@ func (s *vfsShm) Close() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unlock everything.
|
|
||||||
s.shmLock(0, _SHM_NLOCK, _SHM_UNLOCK)
|
|
||||||
|
|
||||||
vfsShmFilesMtx.Lock()
|
vfsShmFilesMtx.Lock()
|
||||||
defer vfsShmFilesMtx.Unlock()
|
defer vfsShmFilesMtx.Unlock()
|
||||||
|
|
||||||
|
// Unlock everything.
|
||||||
|
s.shmLock(0, _SHM_NLOCK, _SHM_UNLOCK)
|
||||||
|
|
||||||
// Decrease reference count.
|
// Decrease reference count.
|
||||||
if s.vfsShmFile.refs > 1 {
|
if s.vfsShmFile.refs > 1 {
|
||||||
s.vfsShmFile.refs--
|
s.vfsShmFile.refs--
|
||||||
s.vfsShmFile = nil
|
s.vfsShmFile = nil
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err := s.File.Close()
|
||||||
for i, g := range vfsShmFiles {
|
for i, g := range vfsShmFiles {
|
||||||
if g == s.vfsShmFile {
|
if g == s.vfsShmFile {
|
||||||
vfsShmFiles[i] = nil
|
vfsShmFiles[i] = nil
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
err := s.File.Close()
|
|
||||||
s.vfsShmFile = nil
|
s.vfsShmFile = nil
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
panic(util.AssertErr())
|
||||||
|
}
|
||||||
|
|
||||||
func (s *vfsShm) shmOpen() (rc _ErrorCode) {
|
func (s *vfsShm) shmOpen() (rc _ErrorCode) {
|
||||||
if s.vfsShmFile != nil {
|
if s.vfsShmFile != nil {
|
||||||
|
@ -234,6 +234,8 @@ func (s *vfsShm) shmLock(offset, n int32, flags _ShmFlag) _ErrorCode {
|
||||||
s.vfsShmFile.lock[i] = -1
|
s.vfsShmFile.lock[i] = -1
|
||||||
s.lock[i] = true
|
s.lock[i] = true
|
||||||
}
|
}
|
||||||
|
default:
|
||||||
|
panic(util.AssertErr())
|
||||||
}
|
}
|
||||||
|
|
||||||
return _OK
|
return _OK
|
||||||
|
@ -256,5 +258,4 @@ func (s *vfsShm) shmUnmap(delete bool) {
|
||||||
os.Remove(s.path)
|
os.Remove(s.path)
|
||||||
}
|
}
|
||||||
s.Close()
|
s.Close()
|
||||||
s.vfsShmFile = nil
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -132,26 +132,20 @@ func vfsAccess(ctx context.Context, mod api.Module, pVfs, zPath uint32, flags Ac
|
||||||
|
|
||||||
func vfsOpen(ctx context.Context, mod api.Module, pVfs, zPath, pFile uint32, flags OpenFlag, pOutFlags, pOutVFS uint32) _ErrorCode {
|
func vfsOpen(ctx context.Context, mod api.Module, pVfs, zPath, pFile uint32, flags OpenFlag, pOutFlags, pOutVFS uint32) _ErrorCode {
|
||||||
vfs := vfsGet(mod, pVfs)
|
vfs := vfsGet(mod, pVfs)
|
||||||
|
name := GetFilename(ctx, mod, zPath, flags)
|
||||||
var path string
|
|
||||||
if zPath != 0 {
|
|
||||||
path = util.ReadString(mod, zPath, _MAX_PATHNAME)
|
|
||||||
}
|
|
||||||
|
|
||||||
var file File
|
var file File
|
||||||
var err error
|
var err error
|
||||||
if ffs, ok := vfs.(VFSFilename); ok {
|
if ffs, ok := vfs.(VFSFilename); ok {
|
||||||
name := OpenFilename(ctx, mod, zPath, flags)
|
|
||||||
file, flags, err = ffs.OpenFilename(name, flags)
|
file, flags, err = ffs.OpenFilename(name, flags)
|
||||||
} else {
|
} else {
|
||||||
file, flags, err = vfs.Open(path, flags)
|
file, flags, err = vfs.Open(name.String(), flags)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return vfsErrorCode(err, _CANTOPEN)
|
return vfsErrorCode(err, _CANTOPEN)
|
||||||
}
|
}
|
||||||
|
|
||||||
if file, ok := file.(FilePowersafeOverwrite); ok {
|
if file, ok := file.(FilePowersafeOverwrite); ok {
|
||||||
name := OpenFilename(ctx, mod, zPath, flags)
|
|
||||||
if b, ok := util.ParseBool(name.URIParameter("psow")); ok {
|
if b, ok := util.ParseBool(name.URIParameter("psow")); ok {
|
||||||
file.SetPowersafeOverwrite(b)
|
file.SetPowersafeOverwrite(b)
|
||||||
}
|
}
|
||||||
|
@ -169,11 +163,8 @@ func vfsOpen(ctx context.Context, mod api.Module, pVfs, zPath, pFile uint32, fla
|
||||||
|
|
||||||
func vfsClose(ctx context.Context, mod api.Module, pFile uint32) _ErrorCode {
|
func vfsClose(ctx context.Context, mod api.Module, pFile uint32) _ErrorCode {
|
||||||
err := vfsFileClose(ctx, mod, pFile)
|
err := vfsFileClose(ctx, mod, pFile)
|
||||||
if err != nil {
|
|
||||||
return vfsErrorCode(err, _IOERR_CLOSE)
|
return vfsErrorCode(err, _IOERR_CLOSE)
|
||||||
}
|
}
|
||||||
return _OK
|
|
||||||
}
|
|
||||||
|
|
||||||
func vfsRead(ctx context.Context, mod api.Module, pFile, zBuf uint32, iAmt int32, iOfst int64) _ErrorCode {
|
func vfsRead(ctx context.Context, mod api.Module, pFile, zBuf uint32, iAmt int32, iOfst int64) _ErrorCode {
|
||||||
file := vfsFileGet(ctx, mod, pFile).(File)
|
file := vfsFileGet(ctx, mod, pFile).(File)
|
||||||
|
@ -195,11 +186,8 @@ func vfsWrite(ctx context.Context, mod api.Module, pFile, zBuf uint32, iAmt int3
|
||||||
buf := util.View(mod, zBuf, uint64(iAmt))
|
buf := util.View(mod, zBuf, uint64(iAmt))
|
||||||
|
|
||||||
_, err := file.WriteAt(buf, iOfst)
|
_, err := file.WriteAt(buf, iOfst)
|
||||||
if err != nil {
|
|
||||||
return vfsErrorCode(err, _IOERR_WRITE)
|
return vfsErrorCode(err, _IOERR_WRITE)
|
||||||
}
|
}
|
||||||
return _OK
|
|
||||||
}
|
|
||||||
|
|
||||||
func vfsTruncate(ctx context.Context, mod api.Module, pFile uint32, nByte int64) _ErrorCode {
|
func vfsTruncate(ctx context.Context, mod api.Module, pFile uint32, nByte int64) _ErrorCode {
|
||||||
file := vfsFileGet(ctx, mod, pFile).(File)
|
file := vfsFileGet(ctx, mod, pFile).(File)
|
||||||
|
|
|
@ -247,7 +247,7 @@ type VTabCursor interface {
|
||||||
// https://sqlite.org/vtab.html#xeof
|
// https://sqlite.org/vtab.html#xeof
|
||||||
EOF() bool
|
EOF() bool
|
||||||
// https://sqlite.org/vtab.html#xcolumn
|
// https://sqlite.org/vtab.html#xcolumn
|
||||||
Column(ctx *Context, n int) error
|
Column(ctx Context, n int) error
|
||||||
// https://sqlite.org/vtab.html#xrowid
|
// https://sqlite.org/vtab.html#xrowid
|
||||||
RowID() (int64, error)
|
RowID() (int64, error)
|
||||||
}
|
}
|
||||||
|
@ -618,7 +618,7 @@ func cursorNextCallback(ctx context.Context, mod api.Module, pCur uint32) uint32
|
||||||
func cursorColumnCallback(ctx context.Context, mod api.Module, pCur, pCtx uint32, n int32) uint32 {
|
func cursorColumnCallback(ctx context.Context, mod api.Module, pCur, pCtx uint32, n int32) uint32 {
|
||||||
cursor := vtabGetHandle(ctx, mod, pCur).(VTabCursor)
|
cursor := vtabGetHandle(ctx, mod, pCur).(VTabCursor)
|
||||||
db := ctx.Value(connKey{}).(*Conn)
|
db := ctx.Value(connKey{}).(*Conn)
|
||||||
err := cursor.Column(&Context{db, pCtx}, int(n))
|
err := cursor.Column(Context{db, pCtx}, int(n))
|
||||||
return vtabError(ctx, mod, pCur, _CURSOR_ERROR, err)
|
return vtabError(ctx, mod, pCur, _CURSOR_ERROR, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3807,6 +3807,9 @@ const (
|
||||||
ETHTOOL_MSG_PSE_GET_REPLY = 0x25
|
ETHTOOL_MSG_PSE_GET_REPLY = 0x25
|
||||||
ETHTOOL_MSG_RSS_GET_REPLY = 0x26
|
ETHTOOL_MSG_RSS_GET_REPLY = 0x26
|
||||||
ETHTOOL_MSG_KERNEL_MAX = 0x2b
|
ETHTOOL_MSG_KERNEL_MAX = 0x2b
|
||||||
|
ETHTOOL_FLAG_COMPACT_BITSETS = 0x1
|
||||||
|
ETHTOOL_FLAG_OMIT_REPLY = 0x2
|
||||||
|
ETHTOOL_FLAG_STATS = 0x4
|
||||||
ETHTOOL_A_HEADER_UNSPEC = 0x0
|
ETHTOOL_A_HEADER_UNSPEC = 0x0
|
||||||
ETHTOOL_A_HEADER_DEV_INDEX = 0x1
|
ETHTOOL_A_HEADER_DEV_INDEX = 0x1
|
||||||
ETHTOOL_A_HEADER_DEV_NAME = 0x2
|
ETHTOOL_A_HEADER_DEV_NAME = 0x2
|
||||||
|
|
|
@ -2031,6 +2031,50 @@ const (
|
||||||
IF_TYPE_IEEE1394 = 144
|
IF_TYPE_IEEE1394 = 144
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Enum NL_PREFIX_ORIGIN for [IpAdapterUnicastAddress], see
|
||||||
|
// https://learn.microsoft.com/en-us/windows/win32/api/nldef/ne-nldef-nl_prefix_origin
|
||||||
|
const (
|
||||||
|
IpPrefixOriginOther = 0
|
||||||
|
IpPrefixOriginManual = 1
|
||||||
|
IpPrefixOriginWellKnown = 2
|
||||||
|
IpPrefixOriginDhcp = 3
|
||||||
|
IpPrefixOriginRouterAdvertisement = 4
|
||||||
|
IpPrefixOriginUnchanged = 1 << 4
|
||||||
|
)
|
||||||
|
|
||||||
|
// Enum NL_SUFFIX_ORIGIN for [IpAdapterUnicastAddress], see
|
||||||
|
// https://learn.microsoft.com/en-us/windows/win32/api/nldef/ne-nldef-nl_suffix_origin
|
||||||
|
const (
|
||||||
|
NlsoOther = 0
|
||||||
|
NlsoManual = 1
|
||||||
|
NlsoWellKnown = 2
|
||||||
|
NlsoDhcp = 3
|
||||||
|
NlsoLinkLayerAddress = 4
|
||||||
|
NlsoRandom = 5
|
||||||
|
IpSuffixOriginOther = 0
|
||||||
|
IpSuffixOriginManual = 1
|
||||||
|
IpSuffixOriginWellKnown = 2
|
||||||
|
IpSuffixOriginDhcp = 3
|
||||||
|
IpSuffixOriginLinkLayerAddress = 4
|
||||||
|
IpSuffixOriginRandom = 5
|
||||||
|
IpSuffixOriginUnchanged = 1 << 4
|
||||||
|
)
|
||||||
|
|
||||||
|
// Enum NL_DAD_STATE for [IpAdapterUnicastAddress], see
|
||||||
|
// https://learn.microsoft.com/en-us/windows/win32/api/nldef/ne-nldef-nl_dad_state
|
||||||
|
const (
|
||||||
|
NldsInvalid = 0
|
||||||
|
NldsTentative = 1
|
||||||
|
NldsDuplicate = 2
|
||||||
|
NldsDeprecated = 3
|
||||||
|
NldsPreferred = 4
|
||||||
|
IpDadStateInvalid = 0
|
||||||
|
IpDadStateTentative = 1
|
||||||
|
IpDadStateDuplicate = 2
|
||||||
|
IpDadStateDeprecated = 3
|
||||||
|
IpDadStatePreferred = 4
|
||||||
|
)
|
||||||
|
|
||||||
type SocketAddress struct {
|
type SocketAddress struct {
|
||||||
Sockaddr *syscall.RawSockaddrAny
|
Sockaddr *syscall.RawSockaddrAny
|
||||||
SockaddrLength int32
|
SockaddrLength int32
|
||||||
|
|
|
@ -516,7 +516,7 @@ github.com/modern-go/concurrent
|
||||||
# github.com/modern-go/reflect2 v1.0.2
|
# github.com/modern-go/reflect2 v1.0.2
|
||||||
## explicit; go 1.12
|
## explicit; go 1.12
|
||||||
github.com/modern-go/reflect2
|
github.com/modern-go/reflect2
|
||||||
# github.com/ncruces/go-sqlite3 v0.17.1
|
# github.com/ncruces/go-sqlite3 v0.18.0
|
||||||
## explicit; go 1.21
|
## explicit; go 1.21
|
||||||
github.com/ncruces/go-sqlite3
|
github.com/ncruces/go-sqlite3
|
||||||
github.com/ncruces/go-sqlite3/driver
|
github.com/ncruces/go-sqlite3/driver
|
||||||
|
@ -1124,7 +1124,7 @@ golang.org/x/oauth2/internal
|
||||||
## explicit; go 1.18
|
## explicit; go 1.18
|
||||||
golang.org/x/sync/errgroup
|
golang.org/x/sync/errgroup
|
||||||
golang.org/x/sync/semaphore
|
golang.org/x/sync/semaphore
|
||||||
# golang.org/x/sys v0.23.0
|
# golang.org/x/sys v0.24.0
|
||||||
## explicit; go 1.18
|
## explicit; go 1.18
|
||||||
golang.org/x/sys/cpu
|
golang.org/x/sys/cpu
|
||||||
golang.org/x/sys/unix
|
golang.org/x/sys/unix
|
||||||
|
|
Loading…
Reference in New Issue