mirror of
1
Fork 0

bump ncruces/go-sqlite3 to v0.21.0 (#3621)

This commit is contained in:
kim 2024-12-12 19:44:53 +00:00 committed by GitHub
parent 60acdb21a9
commit fb12bbb10b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
42 changed files with 224 additions and 105 deletions

4
go.mod
View File

@ -62,7 +62,7 @@ require (
github.com/miekg/dns v1.1.62 github.com/miekg/dns v1.1.62
github.com/minio/minio-go/v7 v7.0.81 github.com/minio/minio-go/v7 v7.0.81
github.com/mitchellh/mapstructure v1.5.0 github.com/mitchellh/mapstructure v1.5.0
github.com/ncruces/go-sqlite3 v0.20.3 github.com/ncruces/go-sqlite3 v0.21.0
github.com/oklog/ulid v1.3.1 github.com/oklog/ulid v1.3.1
github.com/prometheus/client_golang v1.20.5 github.com/prometheus/client_golang v1.20.5
github.com/spf13/cobra v1.8.1 github.com/spf13/cobra v1.8.1
@ -91,7 +91,7 @@ require (
go.opentelemetry.io/otel/sdk/metric v1.32.0 go.opentelemetry.io/otel/sdk/metric v1.32.0
go.opentelemetry.io/otel/trace v1.32.0 go.opentelemetry.io/otel/trace v1.32.0
go.uber.org/automaxprocs v1.6.0 go.uber.org/automaxprocs v1.6.0
golang.org/x/crypto v0.30.0 golang.org/x/crypto v0.31.0
golang.org/x/image v0.23.0 golang.org/x/image v0.23.0
golang.org/x/net v0.32.0 golang.org/x/net v0.32.0
golang.org/x/oauth2 v0.24.0 golang.org/x/oauth2 v0.24.0

8
go.sum generated
View File

@ -434,8 +434,8 @@ github.com/moul/http2curl v1.0.0 h1:dRMWoAtb+ePxMlLkrCbAqh4TlPHXvoGUSQ323/9Zahs=
github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ= github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/ncruces/go-sqlite3 v0.20.3 h1:+4G4uEqOeusF0yRuQVUl9fuoEebUolwQSnBUjYBLYIw= github.com/ncruces/go-sqlite3 v0.21.0 h1:EwKFoy1hHEopN4sFZarmi+McXdbCcbTuLixhEayXVbQ=
github.com/ncruces/go-sqlite3 v0.20.3/go.mod h1:ojLIAB243gtz68Eo283Ps+k9PyR3dvzS+9/RgId4+AA= github.com/ncruces/go-sqlite3 v0.21.0/go.mod h1:zxMOaSG5kFYVFK4xQa0pdwIszqxqJ0W0BxBgwdrNjuA=
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=
@ -675,8 +675,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
golang.org/x/crypto v0.30.0 h1:RwoQn3GkWiMkzlX562cLB7OxWvjH1L8xutO2WoJcRoY= golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
golang.org/x/crypto v0.30.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=

View File

@ -10,7 +10,7 @@ as well as direct access to most of the [C SQLite API](https://sqlite.org/cintro
It wraps a [Wasm](https://webassembly.org/) [build](embed/) of SQLite, It wraps a [Wasm](https://webassembly.org/) [build](embed/) of SQLite,
and uses [wazero](https://wazero.io/) as the runtime.\ and uses [wazero](https://wazero.io/) as the runtime.\
Go, wazero and [`x/sys`](https://pkg.go.dev/golang.org/x/sys) are the _only_ runtime dependencies. Go, wazero and [`x/sys`](https://pkg.go.dev/golang.org/x/sys) are the _only_ direct dependencies.
### Getting started ### Getting started

View File

@ -501,8 +501,12 @@ func (c *Conn) TableColumnMetadata(schema, table, column string) (declType, coll
uint64(declTypePtr), uint64(collSeqPtr), uint64(declTypePtr), uint64(collSeqPtr),
uint64(notNullPtr), uint64(primaryKeyPtr), uint64(autoIncPtr)) uint64(notNullPtr), uint64(primaryKeyPtr), uint64(autoIncPtr))
if err = c.error(r); err == nil && column != "" { if err = c.error(r); err == nil && column != "" {
declType = util.ReadString(c.mod, util.ReadUint32(c.mod, declTypePtr), _MAX_NAME) if ptr := util.ReadUint32(c.mod, declTypePtr); ptr != 0 {
collSeq = util.ReadString(c.mod, util.ReadUint32(c.mod, collSeqPtr), _MAX_NAME) declType = util.ReadString(c.mod, ptr, _MAX_NAME)
}
if ptr := util.ReadUint32(c.mod, collSeqPtr); ptr != 0 {
collSeq = util.ReadString(c.mod, ptr, _MAX_NAME)
}
notNull = util.ReadUint32(c.mod, notNullPtr) != 0 notNull = util.ReadUint32(c.mod, notNullPtr) != 0
autoInc = util.ReadUint32(c.mod, autoIncPtr) != 0 autoInc = util.ReadUint32(c.mod, autoIncPtr) != 0
primaryKey = util.ReadUint32(c.mod, primaryKeyPtr) != 0 primaryKey = util.ReadUint32(c.mod, primaryKeyPtr) != 0

View File

@ -81,6 +81,7 @@ import (
"fmt" "fmt"
"io" "io"
"net/url" "net/url"
"reflect"
"strings" "strings"
"time" "time"
"unsafe" "unsafe"
@ -107,17 +108,17 @@ func init() {
// The second callback is called before the driver closes a 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.
func Open(dataSourceName string, fn ...func(*sqlite3.Conn) error) (*sql.DB, error) { func Open(dataSourceName string, fn ...func(*sqlite3.Conn) error) (*sql.DB, error) {
var drv SQLite
if len(fn) > 2 { if len(fn) > 2 {
return nil, sqlite3.MISUSE return nil, sqlite3.MISUSE
} }
var init, term func(*sqlite3.Conn) error
if len(fn) > 1 { if len(fn) > 1 {
drv.term = fn[1] term = fn[1]
} }
if len(fn) > 0 { if len(fn) > 0 {
drv.init = fn[0] init = fn[0]
} }
c, err := drv.OpenConnector(dataSourceName) c, err := newConnector(dataSourceName, init, term)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -125,10 +126,7 @@ func Open(dataSourceName string, fn ...func(*sqlite3.Conn) error) (*sql.DB, erro
} }
// SQLite implements [database/sql/driver.Driver]. // SQLite implements [database/sql/driver.Driver].
type SQLite struct { type SQLite struct{}
init func(*sqlite3.Conn) error
term func(*sqlite3.Conn) error
}
var ( var (
// Ensure these interfaces are implemented: // Ensure these interfaces are implemented:
@ -137,7 +135,7 @@ var (
// Open implements [database/sql/driver.Driver]. // Open implements [database/sql/driver.Driver].
func (d *SQLite) Open(name string) (driver.Conn, error) { func (d *SQLite) Open(name string) (driver.Conn, error) {
c, err := d.newConnector(name) c, err := newConnector(name, nil, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -146,11 +144,11 @@ func (d *SQLite) Open(name string) (driver.Conn, error) {
// OpenConnector implements [database/sql/driver.DriverContext]. // OpenConnector implements [database/sql/driver.DriverContext].
func (d *SQLite) OpenConnector(name string) (driver.Connector, error) { func (d *SQLite) OpenConnector(name string) (driver.Connector, error) {
return d.newConnector(name) return newConnector(name, nil, nil)
} }
func (d *SQLite) newConnector(name string) (*connector, error) { func newConnector(name string, init, term func(*sqlite3.Conn) error) (*connector, error) {
c := connector{driver: d, name: name} c := connector{name: name, init: init, term: term}
var txlock, timefmt string var txlock, timefmt string
if strings.HasPrefix(name, "file:") { if strings.HasPrefix(name, "file:") {
@ -190,7 +188,8 @@ func (d *SQLite) newConnector(name string) (*connector, error) {
} }
type connector struct { type connector struct {
driver *SQLite init func(*sqlite3.Conn) error
term func(*sqlite3.Conn) error
name string name string
txLock string txLock string
tmRead sqlite3.TimeFormat tmRead sqlite3.TimeFormat
@ -199,7 +198,7 @@ type connector struct {
} }
func (n *connector) Driver() driver.Driver { func (n *connector) Driver() driver.Driver {
return n.driver return &SQLite{}
} }
func (n *connector) Connect(ctx context.Context) (res driver.Conn, err error) { func (n *connector) Connect(ctx context.Context) (res driver.Conn, err error) {
@ -228,13 +227,13 @@ func (n *connector) Connect(ctx context.Context) (res driver.Conn, err error) {
return nil, err return nil, err
} }
} }
if n.driver.init != nil { if n.init != nil {
err = n.driver.init(c.Conn) err = n.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.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
@ -250,9 +249,9 @@ func (n *connector) Connect(ctx context.Context) (res driver.Conn, err error) {
return nil, err return nil, err
} }
} }
if n.driver.term != nil { if n.term != nil {
err = c.Conn.Trace(sqlite3.TRACE_CLOSE, func(sqlite3.TraceEvent, any, any) error { err = c.Conn.Trace(sqlite3.TRACE_CLOSE, func(sqlite3.TraceEvent, any, any) error {
return n.driver.term(c.Conn) return n.term(c.Conn)
}) })
if err != nil { if err != nil {
return nil, err return nil, err
@ -288,6 +287,8 @@ func (n *connector) Connect(ctx context.Context) (res driver.Conn, err error) {
type Conn interface { type Conn interface {
Raw() *sqlite3.Conn Raw() *sqlite3.Conn
driver.Conn driver.Conn
driver.ConnBeginTx
driver.ConnPrepareContext
} }
type conn struct { type conn struct {
@ -301,10 +302,8 @@ type conn struct {
var ( var (
// Ensure these interfaces are implemented: // Ensure these interfaces are implemented:
_ Conn = &conn{} _ Conn = &conn{}
_ driver.ConnBeginTx = &conn{} _ driver.ExecerContext = &conn{}
_ driver.ConnPrepareContext = &conn{}
_ driver.ExecerContext = &conn{}
) )
func (c *conn) Raw() *sqlite3.Conn { func (c *conn) Raw() *sqlite3.Conn {
@ -581,8 +580,22 @@ type rows struct {
names []string names []string
types []string types []string
nulls []bool nulls []bool
scans []scantype
} }
type scantype byte
const (
_ANY scantype = iota
_INT scantype = scantype(sqlite3.INTEGER)
_REAL scantype = scantype(sqlite3.FLOAT)
_TEXT scantype = scantype(sqlite3.TEXT)
_BLOB scantype = scantype(sqlite3.BLOB)
_NULL scantype = scantype(sqlite3.NULL)
_BOOL scantype = iota
_TIME
)
var ( var (
// Ensure these interfaces are implemented: // Ensure these interfaces are implemented:
_ driver.RowsColumnTypeDatabaseTypeName = &rows{} _ driver.RowsColumnTypeDatabaseTypeName = &rows{}
@ -606,21 +619,42 @@ func (r *rows) Columns() []string {
return r.names return r.names
} }
func (r *rows) loadTypes() { func (r *rows) loadColumnMetadata() {
if r.nulls == nil { if r.nulls == nil {
count := r.Stmt.ColumnCount() count := r.Stmt.ColumnCount()
nulls := make([]bool, count) nulls := make([]bool, count)
types := make([]string, count) types := make([]string, count)
scans := make([]scantype, count)
for i := range nulls { for i := range nulls {
if col := r.Stmt.ColumnOriginName(i); col != "" { if col := r.Stmt.ColumnOriginName(i); col != "" {
types[i], _, nulls[i], _, _, _ = r.Stmt.Conn().TableColumnMetadata( types[i], _, nulls[i], _, _, _ = r.Stmt.Conn().TableColumnMetadata(
r.Stmt.ColumnDatabaseName(i), r.Stmt.ColumnDatabaseName(i),
r.Stmt.ColumnTableName(i), r.Stmt.ColumnTableName(i),
col) 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
}
} }
} }
r.nulls = nulls r.nulls = nulls
r.types = types r.types = types
r.scans = scans
} }
} }
@ -637,7 +671,7 @@ func (r *rows) declType(index int) string {
} }
func (r *rows) ColumnTypeDatabaseTypeName(index int) string { func (r *rows) ColumnTypeDatabaseTypeName(index int) string {
r.loadTypes() r.loadColumnMetadata()
decltype := r.types[index] decltype := r.types[index]
if len := len(decltype); len > 0 && decltype[len-1] == ')' { if len := len(decltype); len > 0 && decltype[len-1] == ')' {
if i := strings.LastIndexByte(decltype, '('); i >= 0 { if i := strings.LastIndexByte(decltype, '('); i >= 0 {
@ -648,13 +682,57 @@ func (r *rows) ColumnTypeDatabaseTypeName(index int) string {
} }
func (r *rows) ColumnTypeNullable(index int) (nullable, ok bool) { func (r *rows) ColumnTypeNullable(index int) (nullable, ok bool) {
r.loadTypes() r.loadColumnMetadata()
if r.nulls[index] { if r.nulls[index] {
return false, true return false, true
} }
return true, false return true, false
} }
func (r *rows) ColumnTypeScanType(index int) (typ reflect.Type) {
r.loadColumnMetadata()
scan := r.scans[index]
if r.Stmt.Busy() {
// SQLite is dynamically typed and we now have a row.
// Always use the type of the value itself,
// unless the scan type is more specific
// and can scan the actual value.
val := scantype(r.Stmt.ColumnType(index))
useValType := true
switch {
case scan == _TIME && val != _BLOB && val != _NULL:
t := r.Stmt.ColumnTime(index, r.tmRead)
useValType = t == time.Time{}
case scan == _BOOL && val == _INT:
i := r.Stmt.ColumnInt64(index)
useValType = i != 0 && i != 1
case scan == _BLOB && val == _NULL:
useValType = false
}
if useValType {
scan = val
}
}
switch scan {
case _INT:
return reflect.TypeOf(int64(0))
case _REAL:
return reflect.TypeOf(float64(0))
case _TEXT:
return reflect.TypeOf("")
case _BLOB:
return reflect.TypeOf([]byte{})
case _BOOL:
return reflect.TypeOf(false)
case _TIME:
return reflect.TypeOf(time.Time{})
default:
return reflect.TypeOf((*any)(nil)).Elem()
}
}
func (r *rows) Next(dest []driver.Value) error { func (r *rows) Next(dest []driver.Value) error {
old := r.Stmt.Conn().SetInterrupt(r.ctx) old := r.Stmt.Conn().SetInterrupt(r.ctx)
defer r.Stmt.Conn().SetInterrupt(old) defer r.Stmt.Conn().SetInterrupt(old)
@ -667,7 +745,7 @@ func (r *rows) Next(dest []driver.Value) error {
} }
data := unsafe.Slice((*any)(unsafe.SliceData(dest)), len(dest)) data := unsafe.Slice((*any)(unsafe.SliceData(dest)), len(dest))
err := r.Stmt.Columns(data) err := r.Stmt.Columns(data...)
for i := range dest { for i := range dest {
if t, ok := r.decodeTime(i, dest[i]); ok { if t, ok := r.decodeTime(i, dest[i]); ok {
dest[i] = t dest[i] = t

View File

@ -1,8 +1,6 @@
package driver package driver
import ( import "time"
"time"
)
// Convert a string in [time.RFC3339Nano] format into a [time.Time] // Convert a string in [time.RFC3339Nano] format into a [time.Time]
// if it roundtrips back to the same string. // if it roundtrips back to the same string.

View File

@ -1,6 +1,6 @@
# Embeddable Wasm build of SQLite # Embeddable Wasm build of SQLite
This folder includes an embeddable Wasm build of SQLite 3.47.1 for use with This folder includes an embeddable Wasm build of SQLite 3.47.2 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:

Binary file not shown.

View File

@ -3,4 +3,5 @@ go 1.21
use ( use (
. .
./gormlite ./gormlite
./embed/bcw2
) )

View File

@ -10,5 +10,6 @@ golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk=
golang.org/x/term v0.24.0/go.mod h1:lOBK/LVxemqiMij05LGJ0tzNr8xlmwBRJ81PX6wVLH8= golang.org/x/term v0.24.0/go.mod h1:lOBK/LVxemqiMij05LGJ0tzNr8xlmwBRJ81PX6wVLH8=
golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M= golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M=
golang.org/x/term v0.26.0/go.mod h1:Si5m1o57C5nBNQo5z1iq+XDijt21BDBDp2bK0QI8e3E= golang.org/x/term v0.26.0/go.mod h1:Si5m1o57C5nBNQo5z1iq+XDijt21BDBDp2bK0QI8e3E=
golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=

View File

@ -1,4 +1,4 @@
//go:build !(unix || windows) || sqlite3_nosys //go:build !unix && !windows
package alloc package alloc

View File

@ -1,4 +1,4 @@
//go:build unix && !sqlite3_nosys //go:build unix
package alloc package alloc

View File

@ -1,5 +1,3 @@
//go:build !sqlite3_nosys
package alloc package alloc
import ( import (

View File

@ -1,4 +1,4 @@
//go:build !unix || sqlite3_nosys //go:build !unix
package util package util

View File

@ -1,4 +1,4 @@
//go:build unix && !sqlite3_nosys //go:build unix
package util package util

View File

@ -1,5 +1,3 @@
//go:build !sqlite3_nosys
package util package util
import ( import (

View File

@ -582,7 +582,9 @@ func (s *Stmt) ColumnRawBlob(col int) []byte {
func (s *Stmt) columnRawBytes(col int, ptr uint32) []byte { func (s *Stmt) columnRawBytes(col int, ptr uint32) []byte {
if ptr == 0 { if ptr == 0 {
r := s.c.call("sqlite3_errcode", uint64(s.c.handle)) r := s.c.call("sqlite3_errcode", uint64(s.c.handle))
s.err = s.c.error(r) if r != _ROW && r != _DONE {
s.err = s.c.error(r)
}
return nil return nil
} }
@ -637,7 +639,7 @@ func (s *Stmt) ColumnValue(col int) Value {
// [TEXT] as string, and [BLOB] as []byte. // [TEXT] as string, and [BLOB] as []byte.
// Any []byte are owned by SQLite and may be invalidated by // Any []byte are owned by SQLite and may be invalidated by
// subsequent calls to [Stmt] methods. // subsequent calls to [Stmt] methods.
func (s *Stmt) Columns(dest []any) error { func (s *Stmt) Columns(dest ...any) error {
defer s.c.arena.mark()() defer s.c.arena.mark()()
count := uint64(len(dest)) count := uint64(len(dest))
typePtr := s.c.arena.new(count) typePtr := s.c.arena.new(count)
@ -666,6 +668,10 @@ func (s *Stmt) Columns(dest []any) error {
dest[i] = nil dest[i] = nil
default: default:
ptr := util.ReadUint32(s.c.mod, dataPtr+0) ptr := util.ReadUint32(s.c.mod, dataPtr+0)
if ptr == 0 {
dest[i] = []byte{}
continue
}
len := util.ReadUint32(s.c.mod, dataPtr+4) len := util.ReadUint32(s.c.mod, dataPtr+4)
buf := util.View(s.c.mod, ptr, uint64(len)) buf := util.View(s.c.mod, ptr, uint64(len))
if types[i] == byte(TEXT) { if types[i] == byte(TEXT) {

View File

@ -101,6 +101,9 @@ func syscallOpen(path string, mode int, perm uint32) (fd Handle, err error) {
const _FILE_FLAG_WRITE_THROUGH = 0x80000000 const _FILE_FLAG_WRITE_THROUGH = 0x80000000
attrs |= _FILE_FLAG_WRITE_THROUGH attrs |= _FILE_FLAG_WRITE_THROUGH
} }
if mode&O_NONBLOCK != 0 {
attrs |= FILE_FLAG_OVERLAPPED
}
return CreateFile(pathp, access, sharemode, sa, createmode, attrs, 0) return CreateFile(pathp, access, sharemode, sa, createmode, attrs, 0)
} }

View File

@ -91,7 +91,6 @@ The implementation is compatible with SQLite's
The VFS can be customized with a few build tags: The VFS can be customized with a few build tags:
- `sqlite3_flock` forces the use of BSD locks. - `sqlite3_flock` forces the use of BSD locks.
- `sqlite3_dotlk` forces the use of dot-file locks. - `sqlite3_dotlk` forces the use of dot-file locks.
- `sqlite3_nosys` prevents importing [`x/sys`](https://pkg.go.dev/golang.org/x/sys).
> [!IMPORTANT] > [!IMPORTANT]
> The default configuration of this package is compatible with the standard > The default configuration of this package is compatible with the standard

View File

@ -1,4 +1,4 @@
//go:build ((linux || darwin || windows || freebsd || openbsd || netbsd || dragonfly || illumos) && !sqlite3_nosys) || sqlite3_flock || sqlite3_dotlk //go:build linux || darwin || windows || freebsd || openbsd || netbsd || dragonfly || illumos || sqlite3_flock || sqlite3_dotlk
package vfs package vfs

View File

@ -1,4 +1,4 @@
//go:build !(((linux || darwin || windows || freebsd || openbsd || netbsd || dragonfly || illumos) && !sqlite3_nosys) || sqlite3_flock || sqlite3_dotlk) //go:build !(linux || darwin || windows || freebsd || openbsd || netbsd || dragonfly || illumos || sqlite3_flock || sqlite3_dotlk)
package vfs package vfs

View File

@ -1,4 +1,4 @@
//go:build ((freebsd || openbsd || netbsd || dragonfly || illumos) && !(sqlite3_dotlk || sqlite3_nosys)) || sqlite3_flock //go:build ((freebsd || openbsd || netbsd || dragonfly || illumos) && !sqlite3_dotlk) || sqlite3_flock
package vfs package vfs

View File

@ -1,4 +1,4 @@
//go:build !(sqlite3_flock || sqlite3_nosys) //go:build !sqlite3_flock
package vfs package vfs

View File

@ -1,4 +1,4 @@
//go:build (amd64 || arm64 || riscv64) && !sqlite3_nosys //go:build amd64 || arm64 || riscv64
package vfs package vfs

View File

@ -1,4 +1,4 @@
//go:build !(sqlite3_flock || sqlite3_nosys) //go:build !sqlite3_flock
package vfs package vfs

View File

@ -1,4 +1,4 @@
//go:build (linux || darwin) && !(sqlite3_flock || sqlite3_dotlk || sqlite3_nosys) //go:build (linux || darwin) && !(sqlite3_flock || sqlite3_dotlk)
package vfs package vfs

View File

@ -1,4 +1,4 @@
//go:build !unix || sqlite3_nosys //go:build !unix
package vfs package vfs

View File

@ -1,4 +1,4 @@
//go:build !(linux || darwin) || sqlite3_flock || sqlite3_nosys //go:build !(linux || darwin) || sqlite3_flock
package vfs package vfs

View File

@ -1,4 +1,4 @@
//go:build !linux || !(amd64 || arm64 || riscv64) || sqlite3_nosys //go:build !linux || !(amd64 || arm64 || riscv64)
package vfs package vfs

View File

@ -1,4 +1,4 @@
//go:build !(linux || darwin) || sqlite3_flock || sqlite3_nosys //go:build !(linux || darwin) || sqlite3_flock
package vfs package vfs

View File

@ -1,4 +1,4 @@
//go:build unix && !sqlite3_nosys //go:build unix
package vfs package vfs

View File

@ -1,9 +1,8 @@
//go:build !(sqlite3_dotlk || sqlite3_nosys) //go:build !sqlite3_dotlk
package vfs package vfs
import ( import (
"math/rand"
"os" "os"
"time" "time"
@ -46,7 +45,7 @@ func osGetExclusiveLock(file *os.File, state *LockLevel) _ErrorCode {
osUnlock(file, _SHARED_FIRST, _SHARED_SIZE) osUnlock(file, _SHARED_FIRST, _SHARED_SIZE)
// Acquire the EXCLUSIVE lock. // Acquire the EXCLUSIVE lock.
rc := osWriteLock(file, _SHARED_FIRST, _SHARED_SIZE, time.Millisecond) rc := osWriteLock(file, _SHARED_FIRST, _SHARED_SIZE, 0)
if rc != _OK { if rc != _OK {
// Reacquire the SHARED lock. // Reacquire the SHARED lock.
@ -123,29 +122,40 @@ func osLock(file *os.File, flags, start, len uint32, timeout time.Duration, def
var err error var err error
switch { switch {
case timeout == 0: case timeout == 0:
err = osLockEx(file, flags|windows.LOCKFILE_FAIL_IMMEDIATELY, start, len) err = osLockEx(file, flags|windows.LOCKFILE_FAIL_IMMEDIATELY, start, len, 0)
case timeout < 0: case timeout < 0:
err = osLockEx(file, flags, start, len) err = osLockEx(file, flags, start, len, 0)
default: default:
before := time.Now() var event windows.Handle
for { event, err = windows.CreateEvent(nil, 1, 0, nil)
err = osLockEx(file, flags|windows.LOCKFILE_FAIL_IMMEDIATELY, start, len) if err != nil {
if errno, _ := err.(windows.Errno); errno != windows.ERROR_LOCK_VIOLATION { break
break }
defer windows.CloseHandle(event)
err = osLockEx(file, flags, start, len, event)
if err == windows.ERROR_IO_PENDING {
rc, serr := windows.WaitForSingleObject(event, uint32(timeout/time.Millisecond))
if rc == windows.WAIT_OBJECT_0 {
return _OK
} }
if time.Since(before) > timeout { if serr != nil {
break err = serr
} else {
err = windows.Errno(rc)
} }
const sleepIncrement = 1024*1024 - 1 // power of two, ~1ms windows.CancelIo(windows.Handle(file.Fd()))
time.Sleep(time.Duration(rand.Int63() & sleepIncrement))
} }
} }
return osLockErrorCode(err, def) return osLockErrorCode(err, def)
} }
func osLockEx(file *os.File, flags, start, len uint32) error { func osLockEx(file *os.File, flags, start, len uint32, event windows.Handle) error {
return windows.LockFileEx(windows.Handle(file.Fd()), flags, return windows.LockFileEx(windows.Handle(file.Fd()), flags,
0, len, 0, &windows.Overlapped{Offset: start}) 0, len, 0, &windows.Overlapped{
Offset: start,
HEvent: event,
})
} }
func osReadLock(file *os.File, start, len uint32, timeout time.Duration) _ErrorCode { func osReadLock(file *os.File, start, len uint32, timeout time.Duration) _ErrorCode {
@ -166,7 +176,8 @@ func osLockErrorCode(err error, def _ErrorCode) _ErrorCode {
case case
windows.ERROR_LOCK_VIOLATION, windows.ERROR_LOCK_VIOLATION,
windows.ERROR_IO_PENDING, windows.ERROR_IO_PENDING,
windows.ERROR_OPERATION_ABORTED: windows.ERROR_OPERATION_ABORTED,
windows.WAIT_TIMEOUT:
return _BUSY return _BUSY
} }
} }

View File

@ -1,4 +1,4 @@
//go:build ((linux || darwin || windows || freebsd || openbsd || netbsd || dragonfly || illumos) && (386 || arm || amd64 || arm64 || riscv64 || ppc64le) && !sqlite3_nosys) || sqlite3_flock || sqlite3_dotlk //go:build ((linux || darwin || windows || freebsd || openbsd || netbsd || dragonfly || illumos) && (386 || arm || amd64 || arm64 || riscv64 || ppc64le)) || sqlite3_flock || sqlite3_dotlk
package vfs package vfs

View File

@ -1,4 +1,4 @@
//go:build ((freebsd || openbsd || netbsd || dragonfly || illumos) && (386 || arm || amd64 || arm64 || riscv64 || ppc64le) && !(sqlite3_dotlk || sqlite3_nosys)) || sqlite3_flock //go:build ((freebsd || openbsd || netbsd || dragonfly || illumos) && (386 || arm || amd64 || arm64 || riscv64 || ppc64le) && !sqlite3_dotlk) || sqlite3_flock
package vfs package vfs
@ -73,7 +73,7 @@ func (s *vfsShm) shmOpen() _ErrorCode {
// Always open file read-write, as it will be shared. // Always open file read-write, as it will be shared.
f, err := os.OpenFile(s.path, f, err := os.OpenFile(s.path,
unix.O_RDWR|unix.O_CREAT|unix.O_NOFOLLOW, 0666) os.O_RDWR|os.O_CREATE|_O_NOFOLLOW, 0666)
if err != nil { if err != nil {
return _CANTOPEN return _CANTOPEN
} }

View File

@ -1,4 +1,4 @@
//go:build (windows && (386 || arm || amd64 || arm64 || riscv64 || ppc64le) && !sqlite3_nosys) || sqlite3_dotlk //go:build (windows && (386 || arm || amd64 || arm64 || riscv64 || ppc64le)) || sqlite3_dotlk
package vfs package vfs
@ -13,7 +13,7 @@ const (
_WALINDEX_PGSZ = 32768 _WALINDEX_PGSZ = 32768
) )
// This looks like a safe way of keeping the WAL-index in sync. // This seems a safe way of keeping the WAL-index in sync.
// //
// The WAL-index file starts with a header, // The WAL-index file starts with a header,
// and the index doesn't meaningfully change if the header doesn't change. // and the index doesn't meaningfully change if the header doesn't change.
@ -27,7 +27,7 @@ const (
// //
// Since all the data is either redundant+checksummed, // Since all the data is either redundant+checksummed,
// 4 byte aligned, or modified under an exclusive lock, // 4 byte aligned, or modified under an exclusive lock,
// the copies below should correctly keep copies in sync. // the copies below should correctly keep memory in sync.
// //
// https://sqlite.org/walformat.html#the_wal_index_file_format // https://sqlite.org/walformat.html#the_wal_index_file_format
@ -35,7 +35,7 @@ func (s *vfsShm) shmAcquire(ptr *_ErrorCode) {
if ptr != nil && *ptr != _OK { if ptr != nil && *ptr != _OK {
return return
} }
if len(s.ptrs) == 0 || shmUnmodified(s.shadow[0][:], s.shared[0][:]) { if len(s.ptrs) == 0 || shmEqual(s.shadow[0][:], s.shared[0][:]) {
return return
} }
// Copies modified words from shared to private memory. // Copies modified words from shared to private memory.
@ -53,7 +53,7 @@ func (s *vfsShm) shmAcquire(ptr *_ErrorCode) {
} }
func (s *vfsShm) shmRelease() { func (s *vfsShm) shmRelease() {
if len(s.ptrs) == 0 || shmUnmodified(s.shadow[0][:], util.View(s.mod, s.ptrs[0], _WALINDEX_HDR_SIZE)) { if len(s.ptrs) == 0 || shmEqual(s.shadow[0][:], util.View(s.mod, s.ptrs[0], _WALINDEX_HDR_SIZE)) {
return return
} }
// Copies modified words from private to shared memory. // Copies modified words from private to shared memory.
@ -82,6 +82,6 @@ func shmPage(s []byte) *[_WALINDEX_PGSZ / 4]uint32 {
return (*[_WALINDEX_PGSZ / 4]uint32)(unsafe.Slice(p, _WALINDEX_PGSZ/4)) return (*[_WALINDEX_PGSZ / 4]uint32)(unsafe.Slice(p, _WALINDEX_PGSZ/4))
} }
func shmUnmodified(v1, v2 []byte) bool { func shmEqual(v1, v2 []byte) bool {
return *(*[_WALINDEX_HDR_SIZE]byte)(v1[:]) == *(*[_WALINDEX_HDR_SIZE]byte)(v2[:]) return *(*[_WALINDEX_HDR_SIZE]byte)(v1[:]) == *(*[_WALINDEX_HDR_SIZE]byte)(v2[:])
} }

View File

@ -9,8 +9,9 @@ import (
"os" "os"
"sync" "sync"
"github.com/ncruces/go-sqlite3/internal/util"
"github.com/tetratelabs/wazero/api" "github.com/tetratelabs/wazero/api"
"github.com/ncruces/go-sqlite3/internal/util"
) )
type vfsShmParent struct { type vfsShmParent struct {

View File

@ -1,4 +1,4 @@
//go:build ((freebsd || openbsd || netbsd || dragonfly || illumos) && (386 || arm || amd64 || arm64 || riscv64 || ppc64le) && !sqlite3_nosys) || sqlite3_flock || sqlite3_dotlk //go:build ((freebsd || openbsd || netbsd || dragonfly || illumos) && (386 || arm || amd64 || arm64 || riscv64 || ppc64le)) || sqlite3_flock || sqlite3_dotlk
package vfs package vfs

View File

@ -1,4 +1,4 @@
//go:build (linux || darwin) && (386 || arm || amd64 || arm64 || riscv64 || ppc64le) && !(sqlite3_flock || sqlite3_dotlk || sqlite3_nosys) //go:build (linux || darwin) && (386 || arm || amd64 || arm64 || riscv64 || ppc64le) && !(sqlite3_flock || sqlite3_dotlk)
package vfs package vfs
@ -20,6 +20,7 @@ type vfsShm struct {
path string path string
regions []*util.MappedRegion regions []*util.MappedRegion
readOnly bool readOnly bool
fileLock bool
blocking bool blocking bool
sync.Mutex sync.Mutex
} }
@ -29,10 +30,10 @@ var _ blockingSharedMemory = &vfsShm{}
func (s *vfsShm) shmOpen() _ErrorCode { func (s *vfsShm) shmOpen() _ErrorCode {
if s.File == nil { if s.File == nil {
f, err := os.OpenFile(s.path, f, err := os.OpenFile(s.path,
unix.O_RDWR|unix.O_CREAT|unix.O_NOFOLLOW, 0666) os.O_RDWR|os.O_CREATE|_O_NOFOLLOW, 0666)
if err != nil { if err != nil {
f, err = os.OpenFile(s.path, f, err = os.OpenFile(s.path,
unix.O_RDONLY|unix.O_CREAT|unix.O_NOFOLLOW, 0666) os.O_RDONLY|os.O_CREATE|_O_NOFOLLOW, 0666)
s.readOnly = true s.readOnly = true
} }
if err != nil { if err != nil {
@ -40,6 +41,9 @@ func (s *vfsShm) shmOpen() _ErrorCode {
} }
s.File = f s.File = f
} }
if s.fileLock {
return _OK
}
// Dead man's switch. // Dead man's switch.
if lock, rc := osTestLock(s.File, _SHM_DMS, 1); rc != _OK { if lock, rc := osTestLock(s.File, _SHM_DMS, 1); rc != _OK {
@ -64,7 +68,9 @@ func (s *vfsShm) shmOpen() _ErrorCode {
return _IOERR_SHMOPEN return _IOERR_SHMOPEN
} }
} }
return osReadLock(s.File, _SHM_DMS, 1, time.Millisecond) rc := osReadLock(s.File, _SHM_DMS, 1, time.Millisecond)
s.fileLock = rc == _OK
return rc
} }
func (s *vfsShm) shmMap(ctx context.Context, mod api.Module, id, size int32, extend bool) (uint32, _ErrorCode) { func (s *vfsShm) shmMap(ctx context.Context, mod api.Module, id, size int32, extend bool) (uint32, _ErrorCode) {

View File

@ -1,4 +1,4 @@
//go:build !(((linux || darwin || windows || freebsd || openbsd || netbsd || dragonfly || illumos) && (386 || arm || amd64 || arm64 || riscv64 || ppc64le) && !sqlite3_nosys) || sqlite3_flock || sqlite3_dotlk) //go:build !(((linux || darwin || windows || freebsd || openbsd || netbsd || dragonfly || illumos) && (386 || arm || amd64 || arm64 || riscv64 || ppc64le)) || sqlite3_flock || sqlite3_dotlk)
package vfs package vfs

View File

@ -1,4 +1,4 @@
//go:build (386 || arm || amd64 || arm64 || riscv64 || ppc64le) && !(sqlite3_dotlk || sqlite3_nosys) //go:build (386 || arm || amd64 || arm64 || riscv64 || ppc64le) && !sqlite3_dotlk
package vfs package vfs
@ -7,6 +7,7 @@ import (
"io" "io"
"os" "os"
"sync" "sync"
"syscall"
"time" "time"
"github.com/tetratelabs/wazero/api" "github.com/tetratelabs/wazero/api"
@ -27,6 +28,7 @@ type vfsShm struct {
shadow [][_WALINDEX_PGSZ]byte shadow [][_WALINDEX_PGSZ]byte
ptrs []uint32 ptrs []uint32
stack [1]uint64 stack [1]uint64
fileLock bool
blocking bool blocking bool
sync.Mutex sync.Mutex
} }
@ -46,12 +48,16 @@ func (s *vfsShm) Close() error {
func (s *vfsShm) shmOpen() _ErrorCode { func (s *vfsShm) shmOpen() _ErrorCode {
if s.File == nil { if s.File == nil {
f, err := osutil.OpenFile(s.path, os.O_RDWR|os.O_CREATE, 0666) f, err := osutil.OpenFile(s.path,
os.O_RDWR|os.O_CREATE|syscall.O_NONBLOCK, 0666)
if err != nil { if err != nil {
return _CANTOPEN return _CANTOPEN
} }
s.File = f s.File = f
} }
if s.fileLock {
return _OK
}
// Dead man's switch. // Dead man's switch.
if rc := osWriteLock(s.File, _SHM_DMS, 1, 0); rc == _OK { if rc := osWriteLock(s.File, _SHM_DMS, 1, 0); rc == _OK {
@ -61,7 +67,9 @@ func (s *vfsShm) shmOpen() _ErrorCode {
return _IOERR_SHMOPEN return _IOERR_SHMOPEN
} }
} }
return osReadLock(s.File, _SHM_DMS, 1, time.Millisecond) rc := osReadLock(s.File, _SHM_DMS, 1, time.Millisecond)
s.fileLock = rc == _OK
return rc
} }
func (s *vfsShm) shmMap(ctx context.Context, mod api.Module, id, size int32, extend bool) (_ uint32, rc _ErrorCode) { func (s *vfsShm) shmMap(ctx context.Context, mod api.Module, id, size int32, extend bool) (_ uint32, rc _ErrorCode) {

View File

@ -149,7 +149,7 @@ func (s *ServerConfig) AddHostKey(key Signer) {
} }
// cachedPubKey contains the results of querying whether a public key is // cachedPubKey contains the results of querying whether a public key is
// acceptable for a user. // acceptable for a user. This is a FIFO cache.
type cachedPubKey struct { type cachedPubKey struct {
user string user string
pubKeyData []byte pubKeyData []byte
@ -157,7 +157,13 @@ type cachedPubKey struct {
perms *Permissions perms *Permissions
} }
const maxCachedPubKeys = 16 // maxCachedPubKeys is the number of cache entries we store.
//
// Due to consistent misuse of the PublicKeyCallback API, we have reduced this
// to 1, such that the only key in the cache is the most recently seen one. This
// forces the behavior that the last call to PublicKeyCallback will always be
// with the key that is used for authentication.
const maxCachedPubKeys = 1
// pubKeyCache caches tests for public keys. Since SSH clients // pubKeyCache caches tests for public keys. Since SSH clients
// will query whether a public key is acceptable before attempting to // will query whether a public key is acceptable before attempting to
@ -179,9 +185,10 @@ func (c *pubKeyCache) get(user string, pubKeyData []byte) (cachedPubKey, bool) {
// add adds the given tuple to the cache. // add adds the given tuple to the cache.
func (c *pubKeyCache) add(candidate cachedPubKey) { func (c *pubKeyCache) add(candidate cachedPubKey) {
if len(c.keys) < maxCachedPubKeys { if len(c.keys) >= maxCachedPubKeys {
c.keys = append(c.keys, candidate) c.keys = c.keys[1:]
} }
c.keys = append(c.keys, candidate)
} }
// ServerConn is an authenticated SSH connection, as seen from the // ServerConn is an authenticated SSH connection, as seen from the

4
vendor/modules.txt vendored
View File

@ -520,7 +520,7 @@ github.com/modern-go/reflect2
# github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 # github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822
## explicit ## explicit
github.com/munnerz/goautoneg github.com/munnerz/goautoneg
# github.com/ncruces/go-sqlite3 v0.20.3 # github.com/ncruces/go-sqlite3 v0.21.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
@ -1069,7 +1069,7 @@ go.uber.org/multierr
# golang.org/x/arch v0.8.0 # golang.org/x/arch v0.8.0
## explicit; go 1.18 ## explicit; go 1.18
golang.org/x/arch/x86/x86asm golang.org/x/arch/x86/x86asm
# golang.org/x/crypto v0.30.0 # golang.org/x/crypto v0.31.0
## explicit; go 1.20 ## explicit; go 1.20
golang.org/x/crypto/acme golang.org/x/crypto/acme
golang.org/x/crypto/acme/autocert golang.org/x/crypto/acme/autocert