bump ncruces/go-sqlite3 to v0.21.0 (#3621)
This commit is contained in:
parent
60acdb21a9
commit
fb12bbb10b
4
go.mod
4
go.mod
|
@ -62,7 +62,7 @@ require (
|
|||
github.com/miekg/dns v1.1.62
|
||||
github.com/minio/minio-go/v7 v7.0.81
|
||||
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/prometheus/client_golang v1.20.5
|
||||
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/trace v1.32.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/net v0.32.0
|
||||
golang.org/x/oauth2 v0.24.0
|
||||
|
|
|
@ -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/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||
github.com/ncruces/go-sqlite3 v0.20.3 h1:+4G4uEqOeusF0yRuQVUl9fuoEebUolwQSnBUjYBLYIw=
|
||||
github.com/ncruces/go-sqlite3 v0.20.3/go.mod h1:ojLIAB243gtz68Eo283Ps+k9PyR3dvzS+9/RgId4+AA=
|
||||
github.com/ncruces/go-sqlite3 v0.21.0 h1:EwKFoy1hHEopN4sFZarmi+McXdbCcbTuLixhEayXVbQ=
|
||||
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/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
|
||||
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-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.30.0 h1:RwoQn3GkWiMkzlX562cLB7OxWvjH1L8xutO2WoJcRoY=
|
||||
golang.org/x/crypto v0.30.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
|
||||
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
|
||||
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-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
|
|
|
@ -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,
|
||||
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
|
||||
|
||||
|
|
|
@ -501,8 +501,12 @@ func (c *Conn) TableColumnMetadata(schema, table, column string) (declType, coll
|
|||
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)
|
||||
if ptr := util.ReadUint32(c.mod, declTypePtr); ptr != 0 {
|
||||
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
|
||||
autoInc = util.ReadUint32(c.mod, autoIncPtr) != 0
|
||||
primaryKey = util.ReadUint32(c.mod, primaryKeyPtr) != 0
|
||||
|
|
|
@ -81,6 +81,7 @@ import (
|
|||
"fmt"
|
||||
"io"
|
||||
"net/url"
|
||||
"reflect"
|
||||
"strings"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
@ -107,17 +108,17 @@ func init() {
|
|||
// The second callback is called before the driver closes a connection.
|
||||
// The [sqlite3.Conn] can be used to execute queries, register functions, etc.
|
||||
func Open(dataSourceName string, fn ...func(*sqlite3.Conn) error) (*sql.DB, error) {
|
||||
var drv SQLite
|
||||
if len(fn) > 2 {
|
||||
return nil, sqlite3.MISUSE
|
||||
}
|
||||
var init, term func(*sqlite3.Conn) error
|
||||
if len(fn) > 1 {
|
||||
drv.term = fn[1]
|
||||
term = fn[1]
|
||||
}
|
||||
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 {
|
||||
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].
|
||||
type SQLite struct {
|
||||
init func(*sqlite3.Conn) error
|
||||
term func(*sqlite3.Conn) error
|
||||
}
|
||||
type SQLite struct{}
|
||||
|
||||
var (
|
||||
// Ensure these interfaces are implemented:
|
||||
|
@ -137,7 +135,7 @@ var (
|
|||
|
||||
// Open implements [database/sql/driver.Driver].
|
||||
func (d *SQLite) Open(name string) (driver.Conn, error) {
|
||||
c, err := d.newConnector(name)
|
||||
c, err := newConnector(name, nil, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -146,11 +144,11 @@ func (d *SQLite) Open(name string) (driver.Conn, error) {
|
|||
|
||||
// OpenConnector implements [database/sql/driver.DriverContext].
|
||||
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) {
|
||||
c := connector{driver: d, name: name}
|
||||
func newConnector(name string, init, term func(*sqlite3.Conn) error) (*connector, error) {
|
||||
c := connector{name: name, init: init, term: term}
|
||||
|
||||
var txlock, timefmt string
|
||||
if strings.HasPrefix(name, "file:") {
|
||||
|
@ -190,7 +188,8 @@ func (d *SQLite) newConnector(name string) (*connector, error) {
|
|||
}
|
||||
|
||||
type connector struct {
|
||||
driver *SQLite
|
||||
init func(*sqlite3.Conn) error
|
||||
term func(*sqlite3.Conn) error
|
||||
name string
|
||||
txLock string
|
||||
tmRead sqlite3.TimeFormat
|
||||
|
@ -199,7 +198,7 @@ type connector struct {
|
|||
}
|
||||
|
||||
func (n *connector) Driver() driver.Driver {
|
||||
return n.driver
|
||||
return &SQLite{}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
if n.driver.init != nil {
|
||||
err = n.driver.init(c.Conn)
|
||||
if n.init != nil {
|
||||
err = n.init(c.Conn)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if n.pragmas || n.driver.init != nil {
|
||||
if n.pragmas || n.init != nil {
|
||||
s, _, err := c.Conn.Prepare(`PRAGMA query_only`)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -250,9 +249,9 @@ func (n *connector) Connect(ctx context.Context) (res driver.Conn, err error) {
|
|||
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 {
|
||||
return n.driver.term(c.Conn)
|
||||
return n.term(c.Conn)
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -288,6 +287,8 @@ func (n *connector) Connect(ctx context.Context) (res driver.Conn, err error) {
|
|||
type Conn interface {
|
||||
Raw() *sqlite3.Conn
|
||||
driver.Conn
|
||||
driver.ConnBeginTx
|
||||
driver.ConnPrepareContext
|
||||
}
|
||||
|
||||
type conn struct {
|
||||
|
@ -302,8 +303,6 @@ type conn struct {
|
|||
var (
|
||||
// Ensure these interfaces are implemented:
|
||||
_ Conn = &conn{}
|
||||
_ driver.ConnBeginTx = &conn{}
|
||||
_ driver.ConnPrepareContext = &conn{}
|
||||
_ driver.ExecerContext = &conn{}
|
||||
)
|
||||
|
||||
|
@ -581,8 +580,22 @@ type rows struct {
|
|||
names []string
|
||||
types []string
|
||||
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 (
|
||||
// Ensure these interfaces are implemented:
|
||||
_ driver.RowsColumnTypeDatabaseTypeName = &rows{}
|
||||
|
@ -606,21 +619,42 @@ func (r *rows) Columns() []string {
|
|||
return r.names
|
||||
}
|
||||
|
||||
func (r *rows) loadTypes() {
|
||||
func (r *rows) loadColumnMetadata() {
|
||||
if r.nulls == nil {
|
||||
count := r.Stmt.ColumnCount()
|
||||
nulls := make([]bool, count)
|
||||
types := make([]string, count)
|
||||
scans := make([]scantype, count)
|
||||
for i := range nulls {
|
||||
if col := r.Stmt.ColumnOriginName(i); col != "" {
|
||||
types[i], _, nulls[i], _, _, _ = r.Stmt.Conn().TableColumnMetadata(
|
||||
r.Stmt.ColumnDatabaseName(i),
|
||||
r.Stmt.ColumnTableName(i),
|
||||
col)
|
||||
types[i] = strings.ToUpper(types[i])
|
||||
// These types are only used before we have rows,
|
||||
// and otherwise as type hints.
|
||||
// The first few ensure STRICT tables are strictly typed.
|
||||
// The other two are type hints for booleans and time.
|
||||
switch types[i] {
|
||||
case "INT", "INTEGER":
|
||||
scans[i] = _INT
|
||||
case "REAL":
|
||||
scans[i] = _REAL
|
||||
case "TEXT":
|
||||
scans[i] = _TEXT
|
||||
case "BLOB":
|
||||
scans[i] = _BLOB
|
||||
case "BOOLEAN":
|
||||
scans[i] = _BOOL
|
||||
case "DATE", "TIME", "DATETIME", "TIMESTAMP":
|
||||
scans[i] = _TIME
|
||||
}
|
||||
}
|
||||
}
|
||||
r.nulls = nulls
|
||||
r.types = types
|
||||
r.scans = scans
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -637,7 +671,7 @@ func (r *rows) declType(index int) string {
|
|||
}
|
||||
|
||||
func (r *rows) ColumnTypeDatabaseTypeName(index int) string {
|
||||
r.loadTypes()
|
||||
r.loadColumnMetadata()
|
||||
decltype := r.types[index]
|
||||
if len := len(decltype); len > 0 && decltype[len-1] == ')' {
|
||||
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) {
|
||||
r.loadTypes()
|
||||
r.loadColumnMetadata()
|
||||
if r.nulls[index] {
|
||||
return false, true
|
||||
}
|
||||
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 {
|
||||
old := r.Stmt.Conn().SetInterrupt(r.ctx)
|
||||
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))
|
||||
err := r.Stmt.Columns(data)
|
||||
err := r.Stmt.Columns(data...)
|
||||
for i := range dest {
|
||||
if t, ok := r.decodeTime(i, dest[i]); ok {
|
||||
dest[i] = t
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
package driver
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
import "time"
|
||||
|
||||
// Convert a string in [time.RFC3339Nano] format into a [time.Time]
|
||||
// if it roundtrips back to the same string.
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# 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).
|
||||
|
||||
The following optional features are compiled in:
|
||||
|
|
Binary file not shown.
|
@ -3,4 +3,5 @@ go 1.21
|
|||
use (
|
||||
.
|
||||
./gormlite
|
||||
./embed/bcw2
|
||||
)
|
||||
|
|
|
@ -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.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M=
|
||||
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.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
//go:build !(unix || windows) || sqlite3_nosys
|
||||
//go:build !unix && !windows
|
||||
|
||||
package alloc
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
//go:build unix && !sqlite3_nosys
|
||||
//go:build unix
|
||||
|
||||
package alloc
|
||||
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
//go:build !sqlite3_nosys
|
||||
|
||||
package alloc
|
||||
|
||||
import (
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
//go:build !unix || sqlite3_nosys
|
||||
//go:build !unix
|
||||
|
||||
package util
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
//go:build unix && !sqlite3_nosys
|
||||
//go:build unix
|
||||
|
||||
package util
|
||||
|
|
@ -1,5 +1,3 @@
|
|||
//go:build !sqlite3_nosys
|
||||
|
||||
package util
|
||||
|
||||
import (
|
||||
|
|
|
@ -582,7 +582,9 @@ func (s *Stmt) ColumnRawBlob(col int) []byte {
|
|||
func (s *Stmt) columnRawBytes(col int, ptr uint32) []byte {
|
||||
if ptr == 0 {
|
||||
r := s.c.call("sqlite3_errcode", uint64(s.c.handle))
|
||||
if r != _ROW && r != _DONE {
|
||||
s.err = s.c.error(r)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -637,7 +639,7 @@ func (s *Stmt) ColumnValue(col int) Value {
|
|||
// [TEXT] as string, and [BLOB] as []byte.
|
||||
// Any []byte are owned by SQLite and may be invalidated by
|
||||
// subsequent calls to [Stmt] methods.
|
||||
func (s *Stmt) Columns(dest []any) error {
|
||||
func (s *Stmt) Columns(dest ...any) error {
|
||||
defer s.c.arena.mark()()
|
||||
count := uint64(len(dest))
|
||||
typePtr := s.c.arena.new(count)
|
||||
|
@ -666,6 +668,10 @@ func (s *Stmt) Columns(dest []any) error {
|
|||
dest[i] = nil
|
||||
default:
|
||||
ptr := util.ReadUint32(s.c.mod, dataPtr+0)
|
||||
if ptr == 0 {
|
||||
dest[i] = []byte{}
|
||||
continue
|
||||
}
|
||||
len := util.ReadUint32(s.c.mod, dataPtr+4)
|
||||
buf := util.View(s.c.mod, ptr, uint64(len))
|
||||
if types[i] == byte(TEXT) {
|
||||
|
|
|
@ -101,6 +101,9 @@ func syscallOpen(path string, mode int, perm uint32) (fd Handle, err error) {
|
|||
const _FILE_FLAG_WRITE_THROUGH = 0x80000000
|
||||
attrs |= _FILE_FLAG_WRITE_THROUGH
|
||||
}
|
||||
if mode&O_NONBLOCK != 0 {
|
||||
attrs |= FILE_FLAG_OVERLAPPED
|
||||
}
|
||||
return CreateFile(pathp, access, sharemode, sa, createmode, attrs, 0)
|
||||
}
|
||||
|
||||
|
|
|
@ -91,7 +91,6 @@ The implementation is compatible with SQLite's
|
|||
The VFS can be customized with a few build tags:
|
||||
- `sqlite3_flock` forces the use of BSD 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]
|
||||
> The default configuration of this package is compatible with the standard
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
//go:build !(sqlite3_flock || sqlite3_nosys)
|
||||
//go:build !sqlite3_flock
|
||||
|
||||
package vfs
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
//go:build (amd64 || arm64 || riscv64) && !sqlite3_nosys
|
||||
//go:build amd64 || arm64 || riscv64
|
||||
|
||||
package vfs
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
//go:build !(sqlite3_flock || sqlite3_nosys)
|
||||
//go:build !sqlite3_flock
|
||||
|
||||
package vfs
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
//go:build (linux || darwin) && !(sqlite3_flock || sqlite3_dotlk || sqlite3_nosys)
|
||||
//go:build (linux || darwin) && !(sqlite3_flock || sqlite3_dotlk)
|
||||
|
||||
package vfs
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
//go:build !unix || sqlite3_nosys
|
||||
//go:build !unix
|
||||
|
||||
package vfs
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
//go:build !(linux || darwin) || sqlite3_flock || sqlite3_nosys
|
||||
//go:build !(linux || darwin) || sqlite3_flock
|
||||
|
||||
package vfs
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
//go:build !linux || !(amd64 || arm64 || riscv64) || sqlite3_nosys
|
||||
//go:build !linux || !(amd64 || arm64 || riscv64)
|
||||
|
||||
package vfs
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
//go:build !(linux || darwin) || sqlite3_flock || sqlite3_nosys
|
||||
//go:build !(linux || darwin) || sqlite3_flock
|
||||
|
||||
package vfs
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
//go:build unix && !sqlite3_nosys
|
||||
//go:build unix
|
||||
|
||||
package vfs
|
||||
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
//go:build !(sqlite3_dotlk || sqlite3_nosys)
|
||||
//go:build !sqlite3_dotlk
|
||||
|
||||
package vfs
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
|
@ -46,7 +45,7 @@ func osGetExclusiveLock(file *os.File, state *LockLevel) _ErrorCode {
|
|||
osUnlock(file, _SHARED_FIRST, _SHARED_SIZE)
|
||||
|
||||
// Acquire the EXCLUSIVE lock.
|
||||
rc := osWriteLock(file, _SHARED_FIRST, _SHARED_SIZE, time.Millisecond)
|
||||
rc := osWriteLock(file, _SHARED_FIRST, _SHARED_SIZE, 0)
|
||||
|
||||
if rc != _OK {
|
||||
// Reacquire the SHARED lock.
|
||||
|
@ -123,29 +122,40 @@ func osLock(file *os.File, flags, start, len uint32, timeout time.Duration, def
|
|||
var err error
|
||||
switch {
|
||||
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:
|
||||
err = osLockEx(file, flags, start, len)
|
||||
err = osLockEx(file, flags, start, len, 0)
|
||||
default:
|
||||
before := time.Now()
|
||||
for {
|
||||
err = osLockEx(file, flags|windows.LOCKFILE_FAIL_IMMEDIATELY, start, len)
|
||||
if errno, _ := err.(windows.Errno); errno != windows.ERROR_LOCK_VIOLATION {
|
||||
var event windows.Handle
|
||||
event, err = windows.CreateEvent(nil, 1, 0, nil)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
if time.Since(before) > timeout {
|
||||
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
|
||||
}
|
||||
const sleepIncrement = 1024*1024 - 1 // power of two, ~1ms
|
||||
time.Sleep(time.Duration(rand.Int63() & sleepIncrement))
|
||||
if serr != nil {
|
||||
err = serr
|
||||
} else {
|
||||
err = windows.Errno(rc)
|
||||
}
|
||||
windows.CancelIo(windows.Handle(file.Fd()))
|
||||
}
|
||||
}
|
||||
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,
|
||||
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 {
|
||||
|
@ -166,7 +176,8 @@ func osLockErrorCode(err error, def _ErrorCode) _ErrorCode {
|
|||
case
|
||||
windows.ERROR_LOCK_VIOLATION,
|
||||
windows.ERROR_IO_PENDING,
|
||||
windows.ERROR_OPERATION_ABORTED:
|
||||
windows.ERROR_OPERATION_ABORTED,
|
||||
windows.WAIT_TIMEOUT:
|
||||
return _BUSY
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
@ -73,7 +73,7 @@ func (s *vfsShm) shmOpen() _ErrorCode {
|
|||
|
||||
// Always open file read-write, as it will be shared.
|
||||
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 {
|
||||
return _CANTOPEN
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
|
@ -13,7 +13,7 @@ const (
|
|||
_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,
|
||||
// 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,
|
||||
// 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
|
||||
|
||||
|
@ -35,7 +35,7 @@ func (s *vfsShm) shmAcquire(ptr *_ErrorCode) {
|
|||
if ptr != nil && *ptr != _OK {
|
||||
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
|
||||
}
|
||||
// Copies modified words from shared to private memory.
|
||||
|
@ -53,7 +53,7 @@ func (s *vfsShm) shmAcquire(ptr *_ErrorCode) {
|
|||
}
|
||||
|
||||
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
|
||||
}
|
||||
// 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))
|
||||
}
|
||||
|
||||
func shmUnmodified(v1, v2 []byte) bool {
|
||||
func shmEqual(v1, v2 []byte) bool {
|
||||
return *(*[_WALINDEX_HDR_SIZE]byte)(v1[:]) == *(*[_WALINDEX_HDR_SIZE]byte)(v2[:])
|
||||
}
|
||||
|
|
|
@ -9,8 +9,9 @@ import (
|
|||
"os"
|
||||
"sync"
|
||||
|
||||
"github.com/ncruces/go-sqlite3/internal/util"
|
||||
"github.com/tetratelabs/wazero/api"
|
||||
|
||||
"github.com/ncruces/go-sqlite3/internal/util"
|
||||
)
|
||||
|
||||
type vfsShmParent struct {
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
@ -20,6 +20,7 @@ type vfsShm struct {
|
|||
path string
|
||||
regions []*util.MappedRegion
|
||||
readOnly bool
|
||||
fileLock bool
|
||||
blocking bool
|
||||
sync.Mutex
|
||||
}
|
||||
|
@ -29,10 +30,10 @@ var _ blockingSharedMemory = &vfsShm{}
|
|||
func (s *vfsShm) shmOpen() _ErrorCode {
|
||||
if s.File == nil {
|
||||
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 {
|
||||
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
|
||||
}
|
||||
if err != nil {
|
||||
|
@ -40,6 +41,9 @@ func (s *vfsShm) shmOpen() _ErrorCode {
|
|||
}
|
||||
s.File = f
|
||||
}
|
||||
if s.fileLock {
|
||||
return _OK
|
||||
}
|
||||
|
||||
// Dead man's switch.
|
||||
if lock, rc := osTestLock(s.File, _SHM_DMS, 1); rc != _OK {
|
||||
|
@ -64,7 +68,9 @@ func (s *vfsShm) shmOpen() _ErrorCode {
|
|||
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) {
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
@ -7,6 +7,7 @@ import (
|
|||
"io"
|
||||
"os"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/tetratelabs/wazero/api"
|
||||
|
@ -27,6 +28,7 @@ type vfsShm struct {
|
|||
shadow [][_WALINDEX_PGSZ]byte
|
||||
ptrs []uint32
|
||||
stack [1]uint64
|
||||
fileLock bool
|
||||
blocking bool
|
||||
sync.Mutex
|
||||
}
|
||||
|
@ -46,12 +48,16 @@ func (s *vfsShm) Close() error {
|
|||
|
||||
func (s *vfsShm) shmOpen() _ErrorCode {
|
||||
if s.File == nil {
|
||||
f, err := osutil.OpenFile(s.path, os.O_RDWR|os.O_CREATE, 0666)
|
||||
f, err := osutil.OpenFile(s.path,
|
||||
os.O_RDWR|os.O_CREATE|syscall.O_NONBLOCK, 0666)
|
||||
if err != nil {
|
||||
return _CANTOPEN
|
||||
}
|
||||
s.File = f
|
||||
}
|
||||
if s.fileLock {
|
||||
return _OK
|
||||
}
|
||||
|
||||
// Dead man's switch.
|
||||
if rc := osWriteLock(s.File, _SHM_DMS, 1, 0); rc == _OK {
|
||||
|
@ -61,7 +67,9 @@ func (s *vfsShm) shmOpen() _ErrorCode {
|
|||
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) {
|
||||
|
|
|
@ -149,7 +149,7 @@ func (s *ServerConfig) AddHostKey(key Signer) {
|
|||
}
|
||||
|
||||
// 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 {
|
||||
user string
|
||||
pubKeyData []byte
|
||||
|
@ -157,7 +157,13 @@ type cachedPubKey struct {
|
|||
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
|
||||
// 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.
|
||||
func (c *pubKeyCache) add(candidate cachedPubKey) {
|
||||
if len(c.keys) < maxCachedPubKeys {
|
||||
c.keys = append(c.keys, candidate)
|
||||
if len(c.keys) >= maxCachedPubKeys {
|
||||
c.keys = c.keys[1:]
|
||||
}
|
||||
c.keys = append(c.keys, candidate)
|
||||
}
|
||||
|
||||
// ServerConn is an authenticated SSH connection, as seen from the
|
||||
|
|
|
@ -520,7 +520,7 @@ github.com/modern-go/reflect2
|
|||
# github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822
|
||||
## explicit
|
||||
github.com/munnerz/goautoneg
|
||||
# github.com/ncruces/go-sqlite3 v0.20.3
|
||||
# github.com/ncruces/go-sqlite3 v0.21.0
|
||||
## explicit; go 1.21
|
||||
github.com/ncruces/go-sqlite3
|
||||
github.com/ncruces/go-sqlite3/driver
|
||||
|
@ -1069,7 +1069,7 @@ go.uber.org/multierr
|
|||
# golang.org/x/arch v0.8.0
|
||||
## explicit; go 1.18
|
||||
golang.org/x/arch/x86/x86asm
|
||||
# golang.org/x/crypto v0.30.0
|
||||
# golang.org/x/crypto v0.31.0
|
||||
## explicit; go 1.20
|
||||
golang.org/x/crypto/acme
|
||||
golang.org/x/crypto/acme/autocert
|
||||
|
|
Loading…
Reference in New Issue