mirror of
https://github.com/ncruces/go-sqlite3.git
synced 2026-01-12 22:19:14 +00:00
Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d9b37307e7 | ||
|
|
3bae1d7d4b | ||
|
|
8887036c20 | ||
|
|
ccb3dcd097 | ||
|
|
a9f33cc2b0 | ||
|
|
f025ffb385 | ||
|
|
aa4357a78f | ||
|
|
aef7f051a8 | ||
|
|
a79ee4c2c6 | ||
|
|
7424747338 | ||
|
|
11830e05a6 | ||
|
|
7dc4520690 |
@@ -76,7 +76,7 @@ It also benefits greatly from [SQLite's](https://sqlite.org/testing.html) and
|
||||
|
||||
Every commit is [tested](https://github.com/ncruces/go-sqlite3/wiki/Support-matrix) on
|
||||
Linux (amd64/arm64/386/riscv64/ppc64le/s390x), macOS (amd64/arm64),
|
||||
Windows (amd64), FreeBSD (amd64), OpenBSD (amd64), NetBSD (amd64),
|
||||
Windows (amd64), FreeBSD (amd64/arm64), OpenBSD (amd64), NetBSD (amd64/arm64),
|
||||
DragonFly BSD (amd64), illumos (amd64), and Solaris (amd64).
|
||||
|
||||
The Go VFS is tested by running SQLite's
|
||||
|
||||
2
conn.go
2
conn.go
@@ -68,7 +68,7 @@ func OpenFlags(filename string, flags OpenFlag) (*Conn, error) {
|
||||
return newConn(context.Background(), filename, flags)
|
||||
}
|
||||
|
||||
type connKey struct{}
|
||||
type connKey = util.ConnKey
|
||||
|
||||
func newConn(ctx context.Context, filename string, flags OpenFlag) (res *Conn, _ error) {
|
||||
err := ctx.Err()
|
||||
|
||||
2
const.go
2
const.go
@@ -166,6 +166,7 @@ const (
|
||||
PREPARE_PERSISTENT PrepareFlag = 0x01
|
||||
PREPARE_NORMALIZE PrepareFlag = 0x02
|
||||
PREPARE_NO_VTAB PrepareFlag = 0x04
|
||||
PREPARE_DONT_LOG PrepareFlag = 0x10
|
||||
)
|
||||
|
||||
// FunctionFlag is a flag that can be passed to
|
||||
@@ -219,6 +220,7 @@ const (
|
||||
DBSTATUS_DEFERRED_FKS DBStatus = 10
|
||||
DBSTATUS_CACHE_USED_SHARED DBStatus = 11
|
||||
DBSTATUS_CACHE_SPILL DBStatus = 12
|
||||
// DBSTATUS_MAX DBStatus = 12
|
||||
)
|
||||
|
||||
// DBConfig are the available database connection configuration options.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Embeddable Wasm build of SQLite
|
||||
|
||||
This folder includes an embeddable Wasm build of SQLite 3.47.2 for use with
|
||||
This folder includes an embeddable Wasm build of SQLite 3.48.0 for use with
|
||||
[`github.com/ncruces/go-sqlite3`](https://pkg.go.dev/github.com/ncruces/go-sqlite3).
|
||||
|
||||
The following optional features are compiled in:
|
||||
|
||||
@@ -1,13 +1,19 @@
|
||||
# Embeddable Wasm build of SQLite
|
||||
|
||||
This folder includes an embeddable Wasm build of SQLite, including the experimental
|
||||
This folder includes an alternative embeddable Wasm build of SQLite,
|
||||
which includes the experimental
|
||||
[`BEGIN CONCURRENT`](https://sqlite.org/src/doc/begin-concurrent/doc/begin_concurrent.md) and
|
||||
[Wal2](https://sqlite.org/cgi/src/doc/wal2/doc/wal2.md) patches.
|
||||
|
||||
It also enables the optional
|
||||
[`UPDATE … ORDER BY … LIMIT`](https://sqlite.org/lang_update.html#optional_limit_and_order_by_clauses) and
|
||||
[`DELETE … ORDER BY … LIMIT`](https://sqlite.org/lang_delete.html#optional_limit_and_order_by_clauses) clauses,
|
||||
and the [`WITHIN GROUP ORDER BY`](https://sqlite.org/compile.html#enable_ordered_set_aggregates) aggregate syntax.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> This package is experimental.
|
||||
> It is built from the `bedrock` branch of SQLite,
|
||||
> since that is _currently_ the most stable, maintained branch to include both features.
|
||||
> since that is _currently_ the most stable, maintained branch to include these features.
|
||||
|
||||
> [!CAUTION]
|
||||
> The Wal2 journaling mode creates databases that other versions of SQLite cannot access.
|
||||
|
||||
Binary file not shown.
@@ -5,6 +5,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/ncruces/go-sqlite3/driver"
|
||||
"github.com/ncruces/go-sqlite3/ext/stats"
|
||||
"github.com/ncruces/go-sqlite3/vfs"
|
||||
)
|
||||
|
||||
@@ -15,7 +16,7 @@ func Test_bcw2(t *testing.T) {
|
||||
|
||||
tmp := filepath.ToSlash(filepath.Join(t.TempDir(), "test.db"))
|
||||
|
||||
db, err := driver.Open("file:" + tmp + "?_pragma=journal_mode(wal2)&_txlock=concurrent")
|
||||
db, err := driver.Open("file:"+tmp+"?_pragma=journal_mode(wal2)&_txlock=concurrent", stats.Register)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -37,6 +38,11 @@ func Test_bcw2(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
_, err = tx.Exec(`SELECT median() WITHIN GROUP (ORDER BY col) FROM test`)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = tx.Commit()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
||||
@@ -13,14 +13,15 @@ mkdir -p build/ext/
|
||||
cp "$ROOT"/sqlite3/*.[ch] build/
|
||||
cp "$ROOT"/sqlite3/*.patch build/
|
||||
|
||||
# https://sqlite.org/src/info/ec5d7025cba9f4ac
|
||||
curl -# https://sqlite.org/src/tarball/sqlite.tar.gz?r=ec5d7025 | tar xz
|
||||
# https://sqlite.org/src/info/fab341c829554573
|
||||
curl -# https://sqlite.org/src/tarball/sqlite.tar.gz?r=fab341c8 | tar xz
|
||||
|
||||
cd sqlite
|
||||
if [[ "$OSTYPE" == "msys" || "$OSTYPE" == "cygwin" ]]; then
|
||||
MSYS_NO_PATHCONV=1 nmake /f makefile.msc sqlite3.c OPTS=-DSQLITE_ENABLE_UPDATE_DELETE_LIMIT
|
||||
MSYS_NO_PATHCONV=1 nmake /f makefile.msc sqlite3.c "OPTS=-DSQLITE_ENABLE_UPDATE_DELETE_LIMIT -DSQLITE_ENABLE_ORDERED_SET_AGGREGATES"
|
||||
else
|
||||
sh configure --enable-update-limit && make sqlite3.c
|
||||
sh configure --enable-update-limit
|
||||
OPTS=-DSQLITE_ENABLE_ORDERED_SET_AGGREGATES make sqlite3.c
|
||||
fi
|
||||
cd ~-
|
||||
|
||||
@@ -37,7 +38,7 @@ mv sqlite/ext/misc/spellfix.c build/ext/
|
||||
mv sqlite/ext/misc/uint.c build/ext/
|
||||
|
||||
cd build
|
||||
cat *.patch | patch --no-backup-if-mismatch
|
||||
cat *.patch | patch -p0 --no-backup-if-mismatch
|
||||
cd ~-
|
||||
|
||||
"$WASI_SDK/clang" --target=wasm32-wasi -std=c23 -g0 -O2 \
|
||||
|
||||
@@ -8,6 +8,7 @@ require github.com/ncruces/go-sqlite3 v0.21.3
|
||||
|
||||
require (
|
||||
github.com/ncruces/julianday v1.0.0 // indirect
|
||||
github.com/ncruces/sort v0.1.2 // indirect
|
||||
github.com/tetratelabs/wazero v1.8.2 // indirect
|
||||
golang.org/x/sys v0.29.0 // indirect
|
||||
)
|
||||
|
||||
@@ -2,6 +2,8 @@ github.com/ncruces/go-sqlite3 v0.21.3 h1:hHkfNQLcbnxPJZhC/RGw9SwP3bfkv/Y0xUHWsr1
|
||||
github.com/ncruces/go-sqlite3 v0.21.3/go.mod h1:zxMOaSG5kFYVFK4xQa0pdwIszqxqJ0W0BxBgwdrNjuA=
|
||||
github.com/ncruces/julianday v1.0.0 h1:fH0OKwa7NWvniGQtxdJRxAgkBMolni2BjDHaWTxqt7M=
|
||||
github.com/ncruces/julianday v1.0.0/go.mod h1:Dusn2KvZrrovOMJuOt0TNXL6tB7U2E8kvza5fFc9G7g=
|
||||
github.com/ncruces/sort v0.1.2 h1:zKQ9CA4fpHPF6xsUhRTfi5EEryspuBpe/QA4VWQOV1U=
|
||||
github.com/ncruces/sort v0.1.2/go.mod h1:vEJUTBJtebIuCMmXD18GKo5GJGhsay+xZFOoBEIXFmE=
|
||||
github.com/tetratelabs/wazero v1.8.2 h1:yIgLR/b2bN31bjxwXHD8a3d+BogigR952csSDdLYEv4=
|
||||
github.com/tetratelabs/wazero v1.8.2/go.mod h1:yAI0XTsMBhREkM/YDAK/zNou3GoiAce1P6+rp/wQhjs=
|
||||
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
|
||||
|
||||
@@ -11,13 +11,14 @@ package bcw2
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
"unsafe"
|
||||
|
||||
"github.com/ncruces/go-sqlite3"
|
||||
)
|
||||
|
||||
//go:embed bcw2.wasm
|
||||
var binary []byte
|
||||
var binary string
|
||||
|
||||
func init() {
|
||||
sqlite3.Binary = binary
|
||||
sqlite3.Binary = unsafe.Slice(unsafe.StringData(binary), len(binary))
|
||||
}
|
||||
|
||||
@@ -77,6 +77,7 @@ sqlite3_get_autocommit
|
||||
sqlite3_get_auxdata
|
||||
sqlite3_hard_heap_limit64
|
||||
sqlite3_interrupt
|
||||
sqlite3_invoke_busy_handler_go
|
||||
sqlite3_last_insert_rowid
|
||||
sqlite3_limit
|
||||
sqlite3_malloc64
|
||||
|
||||
@@ -8,13 +8,14 @@ package embed
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
"unsafe"
|
||||
|
||||
"github.com/ncruces/go-sqlite3"
|
||||
)
|
||||
|
||||
//go:embed sqlite3.wasm
|
||||
var binary []byte
|
||||
var binary string
|
||||
|
||||
func init() {
|
||||
sqlite3.Binary = binary
|
||||
sqlite3.Binary = unsafe.Slice(unsafe.StringData(binary), len(binary))
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ func Test_init(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if version != "3.47.2" {
|
||||
if version != "3.48.0" {
|
||||
t.Error(version)
|
||||
}
|
||||
}
|
||||
|
||||
Binary file not shown.
136
ext/serdes/serdes.go
Normal file
136
ext/serdes/serdes.go
Normal file
@@ -0,0 +1,136 @@
|
||||
// Package serdes provides functions to (de)serialize databases.
|
||||
package serdes
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"github.com/ncruces/go-sqlite3"
|
||||
"github.com/ncruces/go-sqlite3/vfs"
|
||||
)
|
||||
|
||||
func init() {
|
||||
vfs.Register(vfsName, sliceVFS{})
|
||||
}
|
||||
|
||||
// Serialize backs up a database into a byte slice.
|
||||
//
|
||||
// https://sqlite.org/c3ref/serialize.html
|
||||
func Serialize(db *sqlite3.Conn, schema string) ([]byte, error) {
|
||||
var file sliceFile
|
||||
fileToOpen <- &file
|
||||
err := db.Backup(schema, "file:db?vfs="+vfsName)
|
||||
return file.data, err
|
||||
}
|
||||
|
||||
// Deserialize restores a database from a byte slice,
|
||||
// DESTROYING any contents previously stored in schema.
|
||||
//
|
||||
// To non-destructively open a database from a byte slice,
|
||||
// consider alternatives like the ["reader"] or ["memdb"] VFSes.
|
||||
//
|
||||
// This differs from the similarly named SQLite API
|
||||
// in that it DOES NOT disconnect from schema
|
||||
// to reopen as an in-memory database.
|
||||
//
|
||||
// https://sqlite.org/c3ref/deserialize.html
|
||||
//
|
||||
// ["memdb"]: https://pkg.go.dev/github.com/ncruces/go-sqlite3/vfs/memdb
|
||||
// ["reader"]: https://pkg.go.dev/github.com/ncruces/go-sqlite3/vfs/readervfs
|
||||
func Deserialize(db *sqlite3.Conn, schema string, data []byte) error {
|
||||
fileToOpen <- &sliceFile{data}
|
||||
return db.Restore(schema, "file:db?vfs="+vfsName)
|
||||
}
|
||||
|
||||
var fileToOpen = make(chan *sliceFile, 1)
|
||||
|
||||
const vfsName = "github.com/ncruces/go-sqlite3/ext/deserialize.sliceVFS"
|
||||
|
||||
type sliceVFS struct{}
|
||||
|
||||
func (sliceVFS) Open(name string, flags vfs.OpenFlag) (vfs.File, vfs.OpenFlag, error) {
|
||||
if flags&vfs.OPEN_MAIN_DB == 0 {
|
||||
// notest // OPEN_MEMORY
|
||||
return nil, flags, sqlite3.CANTOPEN
|
||||
}
|
||||
return <-fileToOpen, flags | vfs.OPEN_MEMORY, nil
|
||||
}
|
||||
|
||||
func (sliceVFS) Delete(name string, dirSync bool) error {
|
||||
// notest // OPEN_MEMORY
|
||||
return sqlite3.IOERR_DELETE
|
||||
}
|
||||
|
||||
func (sliceVFS) Access(name string, flag vfs.AccessFlag) (bool, error) {
|
||||
return name == "db", nil
|
||||
}
|
||||
|
||||
func (sliceVFS) FullPathname(name string) (string, error) {
|
||||
return name, nil
|
||||
}
|
||||
|
||||
type sliceFile struct{ data []byte }
|
||||
|
||||
func (f *sliceFile) ReadAt(b []byte, off int64) (n int, err error) {
|
||||
if d := f.data; off < int64(len(d)) {
|
||||
n = copy(b, d[off:])
|
||||
}
|
||||
if n == 0 {
|
||||
err = io.EOF
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (f *sliceFile) WriteAt(b []byte, off int64) (n int, err error) {
|
||||
if d := f.data; off > int64(len(d)) {
|
||||
f.data = append(d, make([]byte, off-int64(len(d)))...)
|
||||
}
|
||||
d := append(f.data[:off], b...)
|
||||
if len(d) > len(f.data) {
|
||||
f.data = d
|
||||
}
|
||||
return len(b), nil
|
||||
}
|
||||
|
||||
func (f *sliceFile) Size() (int64, error) {
|
||||
return int64(len(f.data)), nil
|
||||
}
|
||||
|
||||
func (f *sliceFile) Truncate(size int64) error {
|
||||
if d := f.data; size < int64(len(d)) {
|
||||
f.data = d[:size]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *sliceFile) SizeHint(size int64) error {
|
||||
if d := f.data; size > int64(len(d)) {
|
||||
f.data = append(d, make([]byte, size-int64(len(d)))...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (*sliceFile) Close() error { return nil }
|
||||
|
||||
func (*sliceFile) Sync(flag vfs.SyncFlag) error { return nil }
|
||||
|
||||
func (*sliceFile) Lock(lock vfs.LockLevel) error { return nil }
|
||||
|
||||
func (*sliceFile) Unlock(lock vfs.LockLevel) error { return nil }
|
||||
|
||||
func (*sliceFile) CheckReservedLock() (bool, error) {
|
||||
// notest // OPEN_MEMORY
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func (*sliceFile) SectorSize() int {
|
||||
// notest // IOCAP_POWERSAFE_OVERWRITE
|
||||
return 0
|
||||
}
|
||||
|
||||
func (*sliceFile) DeviceCharacteristics() vfs.DeviceCharacteristic {
|
||||
return vfs.IOCAP_ATOMIC |
|
||||
vfs.IOCAP_SAFE_APPEND |
|
||||
vfs.IOCAP_SEQUENTIAL |
|
||||
vfs.IOCAP_POWERSAFE_OVERWRITE |
|
||||
vfs.IOCAP_SUBPAGE_READ
|
||||
}
|
||||
68
ext/serdes/serdes_test.go
Normal file
68
ext/serdes/serdes_test.go
Normal file
@@ -0,0 +1,68 @@
|
||||
package serdes_test
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/ncruces/go-sqlite3"
|
||||
_ "github.com/ncruces/go-sqlite3/embed"
|
||||
"github.com/ncruces/go-sqlite3/ext/serdes"
|
||||
)
|
||||
|
||||
func TestDeserialize(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("skipping in short mode")
|
||||
}
|
||||
|
||||
input, err := httpGet()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
db, err := sqlite3.Open(":memory:")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
err = serdes.Deserialize(db, "temp", input)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
output, err := serdes.Serialize(db, "temp")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if len(input) != len(output) {
|
||||
t.Fatal("lengths are different")
|
||||
}
|
||||
for i := range input {
|
||||
// These may be different.
|
||||
switch {
|
||||
case 24 <= i && i < 28:
|
||||
// File change counter.
|
||||
continue
|
||||
case 40 <= i && i < 44:
|
||||
// Schema cookie.
|
||||
continue
|
||||
case 92 <= i && i < 100:
|
||||
// SQLite version that wrote the file.
|
||||
continue
|
||||
}
|
||||
if input[i] != output[i] {
|
||||
t.Errorf("difference at %d: %d %d", i, input[i], output[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func httpGet() ([]byte, error) {
|
||||
res, err := http.Get("https://raw.githubusercontent.com/jpwhite3/northwind-SQLite3/refs/heads/main/dist/northwind.db")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer res.Body.Close()
|
||||
return io.ReadAll(res.Body)
|
||||
}
|
||||
@@ -8,6 +8,8 @@ import (
|
||||
"github.com/ncruces/go-sqlite3/internal/alloc"
|
||||
)
|
||||
|
||||
type ConnKey struct{}
|
||||
|
||||
type moduleKey struct{}
|
||||
type moduleState struct {
|
||||
mmapState
|
||||
|
||||
11
sqlite3/base64.patch
Normal file
11
sqlite3/base64.patch
Normal file
@@ -0,0 +1,11 @@
|
||||
--- ext/base64.c.orig
|
||||
+++ ext/base64.c
|
||||
@@ -198,7 +198,7 @@
|
||||
deliberate_fall_through; /* FALLTHRU */
|
||||
case 1:
|
||||
pOut[0] = (qv>>16) & 0xff;
|
||||
- deliberate_fall_through; /* FALLTHRU */
|
||||
+ break; /* FALLTHRU */
|
||||
}
|
||||
pOut += nbo;
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
# handle, and interrupt, sqlite3_busy_timeout.
|
||||
--- sqlite3.c.orig
|
||||
+++ sqlite3.c
|
||||
@@ -182928,7 +182928,7 @@
|
||||
@@ -183054,7 +183054,7 @@
|
||||
if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT;
|
||||
#endif
|
||||
if( ms>0 ){
|
||||
|
||||
@@ -3,7 +3,7 @@ set -euo pipefail
|
||||
|
||||
cd -P -- "$(dirname -- "$0")"
|
||||
|
||||
curl -#OL "https://sqlite.org/2024/sqlite-amalgamation-3470200.zip"
|
||||
curl -#OL "https://sqlite.org/2025/sqlite-amalgamation-3480000.zip"
|
||||
unzip -d . sqlite-amalgamation-*.zip
|
||||
mv sqlite-amalgamation-*/sqlite3.c .
|
||||
mv sqlite-amalgamation-*/sqlite3.h .
|
||||
@@ -17,32 +17,32 @@ rm -rf sqlite-amalgamation-*
|
||||
# mv sqlite-snapshot-*/sqlite3ext.h .
|
||||
# rm -rf sqlite-snapshot-*
|
||||
|
||||
cat *.patch | patch --no-backup-if-mismatch
|
||||
|
||||
mkdir -p ext/
|
||||
cd ext/
|
||||
curl -#OL "https://github.com/sqlite/sqlite/raw/version-3.47.2/ext/misc/anycollseq.c"
|
||||
curl -#OL "https://github.com/sqlite/sqlite/raw/version-3.47.2/ext/misc/base64.c"
|
||||
curl -#OL "https://github.com/sqlite/sqlite/raw/version-3.47.2/ext/misc/decimal.c"
|
||||
curl -#OL "https://github.com/sqlite/sqlite/raw/version-3.47.2/ext/misc/ieee754.c"
|
||||
curl -#OL "https://github.com/sqlite/sqlite/raw/version-3.47.2/ext/misc/regexp.c"
|
||||
curl -#OL "https://github.com/sqlite/sqlite/raw/version-3.47.2/ext/misc/series.c"
|
||||
curl -#OL "https://github.com/sqlite/sqlite/raw/version-3.47.2/ext/misc/spellfix.c"
|
||||
curl -#OL "https://github.com/sqlite/sqlite/raw/version-3.47.2/ext/misc/uint.c"
|
||||
curl -#OL "https://github.com/sqlite/sqlite/raw/version-3.48.0/ext/misc/anycollseq.c"
|
||||
curl -#OL "https://github.com/sqlite/sqlite/raw/version-3.48.0/ext/misc/base64.c"
|
||||
curl -#OL "https://github.com/sqlite/sqlite/raw/version-3.48.0/ext/misc/decimal.c"
|
||||
curl -#OL "https://github.com/sqlite/sqlite/raw/version-3.48.0/ext/misc/ieee754.c"
|
||||
curl -#OL "https://github.com/sqlite/sqlite/raw/version-3.48.0/ext/misc/regexp.c"
|
||||
curl -#OL "https://github.com/sqlite/sqlite/raw/version-3.48.0/ext/misc/series.c"
|
||||
curl -#OL "https://github.com/sqlite/sqlite/raw/version-3.48.0/ext/misc/spellfix.c"
|
||||
curl -#OL "https://github.com/sqlite/sqlite/raw/version-3.48.0/ext/misc/uint.c"
|
||||
cd ~-
|
||||
|
||||
cd ../vfs/tests/mptest/testdata/
|
||||
curl -#OL "https://github.com/sqlite/sqlite/raw/version-3.47.2/mptest/config01.test"
|
||||
curl -#OL "https://github.com/sqlite/sqlite/raw/version-3.47.2/mptest/config02.test"
|
||||
curl -#OL "https://github.com/sqlite/sqlite/raw/version-3.47.2/mptest/crash01.test"
|
||||
curl -#OL "https://github.com/sqlite/sqlite/raw/version-3.47.2/mptest/crash02.subtest"
|
||||
curl -#OL "https://github.com/sqlite/sqlite/raw/version-3.47.2/mptest/multiwrite01.test"
|
||||
curl -#OL "https://github.com/sqlite/sqlite/raw/version-3.48.0/mptest/config01.test"
|
||||
curl -#OL "https://github.com/sqlite/sqlite/raw/version-3.48.0/mptest/config02.test"
|
||||
curl -#OL "https://github.com/sqlite/sqlite/raw/version-3.48.0/mptest/crash01.test"
|
||||
curl -#OL "https://github.com/sqlite/sqlite/raw/version-3.48.0/mptest/crash02.subtest"
|
||||
curl -#OL "https://github.com/sqlite/sqlite/raw/version-3.48.0/mptest/multiwrite01.test"
|
||||
cd ~-
|
||||
|
||||
cd ../vfs/tests/mptest/wasm/
|
||||
curl -#OL "https://github.com/sqlite/sqlite/raw/version-3.47.2/mptest/mptest.c"
|
||||
curl -#OL "https://github.com/sqlite/sqlite/raw/version-3.48.0/mptest/mptest.c"
|
||||
cd ~-
|
||||
|
||||
cd ../vfs/tests/speedtest1/wasm/
|
||||
curl -#OL "https://github.com/sqlite/sqlite/raw/version-3.47.2/test/speedtest1.c"
|
||||
cd ~-
|
||||
curl -#OL "https://github.com/sqlite/sqlite/raw/version-3.48.0/test/speedtest1.c"
|
||||
cd ~-
|
||||
|
||||
cat *.patch | patch -p0 --no-backup-if-mismatch
|
||||
@@ -18,8 +18,6 @@
|
||||
#define HAVE_STDINT_H 1
|
||||
#define HAVE_INTTYPES_H 1
|
||||
|
||||
#define LONGDOUBLE_TYPE double
|
||||
|
||||
#define HAVE_LOG2 1
|
||||
#define HAVE_LOG10 1
|
||||
#define HAVE_ISNAN 1
|
||||
@@ -35,10 +33,6 @@
|
||||
#define HAVE_MALLOC_H 1
|
||||
#define HAVE_MALLOC_USABLE_SIZE 1
|
||||
|
||||
// Because Wasm does not support shared memory,
|
||||
// SQLite disables WAL for Wasm builds.
|
||||
#undef SQLITE_OMIT_WAL
|
||||
|
||||
// Implemented in vfs.c.
|
||||
int localtime_s(struct tm *const pTm, time_t const *const pTime);
|
||||
|
||||
|
||||
@@ -162,5 +162,10 @@ int sqlite3_os_init() {
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
int sqlite3_invoke_busy_handler_go(sqlite3_int64 token) {
|
||||
void **ap = (void **)&token;
|
||||
return ((int(*)(void *))(ap[0]))(ap[1]);
|
||||
}
|
||||
|
||||
static_assert(offsetof(sqlite3_vfs, zName) == 16, "Unexpected offset");
|
||||
static_assert(offsetof(struct go_file, handle) == 4, "Unexpected offset");
|
||||
@@ -1,7 +1,7 @@
|
||||
# Remove VFS registration. Go handles it.
|
||||
--- sqlite3.c.orig
|
||||
+++ sqlite3.c
|
||||
@@ -26603,7 +26603,7 @@
|
||||
@@ -26623,7 +26623,7 @@
|
||||
sqlite3_free(p);
|
||||
return sqlite3_os_init();
|
||||
}
|
||||
@@ -10,7 +10,7 @@
|
||||
/*
|
||||
** The list of all registered VFS implementations.
|
||||
*/
|
||||
@@ -26700,7 +26700,7 @@
|
||||
@@ -26720,7 +26720,7 @@
|
||||
sqlite3_mutex_leave(mutex);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
@@ -38,18 +38,18 @@ func WrapLockState(f vfs.File) vfs.LockLevel {
|
||||
return vfs.LOCK_EXCLUSIVE + 1 // UNKNOWN_LOCK
|
||||
}
|
||||
|
||||
// WrapPersistentWAL helps wrap [vfs.FilePersistentWAL].
|
||||
func WrapPersistentWAL(f vfs.File) bool {
|
||||
if f, ok := f.(vfs.FilePersistentWAL); ok {
|
||||
return f.PersistentWAL()
|
||||
// WrapPersistWAL helps wrap [vfs.FilePersistWAL].
|
||||
func WrapPersistWAL(f vfs.File) bool {
|
||||
if f, ok := f.(vfs.FilePersistWAL); ok {
|
||||
return f.PersistWAL()
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// WrapSetPersistentWAL helps wrap [vfs.FilePersistentWAL].
|
||||
func WrapSetPersistentWAL(f vfs.File, keepWAL bool) {
|
||||
if f, ok := f.(vfs.FilePersistentWAL); ok {
|
||||
f.SetPersistentWAL(keepWAL)
|
||||
// WrapSetPersistWAL helps wrap [vfs.FilePersistWAL].
|
||||
func WrapSetPersistWAL(f vfs.File, keepWAL bool) {
|
||||
if f, ok := f.(vfs.FilePersistWAL); ok {
|
||||
f.SetPersistWAL(keepWAL)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,6 +99,14 @@ func WrapOverwrite(f vfs.File) error {
|
||||
return sqlite3.NOTFOUND
|
||||
}
|
||||
|
||||
// WrapSyncSuper helps wrap [vfs.FileSync].
|
||||
func WrapSyncSuper(f vfs.File, super string) error {
|
||||
if f, ok := f.(vfs.FileSync); ok {
|
||||
return f.SyncSuper(super)
|
||||
}
|
||||
return sqlite3.NOTFOUND
|
||||
}
|
||||
|
||||
// WrapCommitPhaseTwo helps wrap [vfs.FileCommitPhaseTwo].
|
||||
func WrapCommitPhaseTwo(f vfs.File) error {
|
||||
if f, ok := f.(vfs.FileCommitPhaseTwo); ok {
|
||||
@@ -153,6 +161,13 @@ func WrapPragma(f vfs.File, name, value string) (string, error) {
|
||||
return "", sqlite3.NOTFOUND
|
||||
}
|
||||
|
||||
// WrapBusyHandler helps wrap [vfs.FilePragma].
|
||||
func WrapBusyHandler(f vfs.File, handler func() bool) {
|
||||
if f, ok := f.(vfs.FileBusyHandler); ok {
|
||||
f.BusyHandler(handler)
|
||||
}
|
||||
}
|
||||
|
||||
// WrapSharedMemory helps wrap [vfs.FileSharedMemory].
|
||||
func WrapSharedMemory(f vfs.File) vfs.SharedMemory {
|
||||
if f, ok := f.(vfs.FileSharedMemory); ok {
|
||||
|
||||
@@ -238,11 +238,11 @@ func (h *hbshFile) LockState() vfs.LockLevel {
|
||||
}
|
||||
|
||||
func (h *hbshFile) PersistentWAL() bool {
|
||||
return vfsutil.WrapPersistentWAL(h.File) // notest
|
||||
return vfsutil.WrapPersistWAL(h.File) // notest
|
||||
}
|
||||
|
||||
func (h *hbshFile) SetPersistentWAL(keepWAL bool) {
|
||||
vfsutil.WrapSetPersistentWAL(h.File, keepWAL) // notest
|
||||
vfsutil.WrapSetPersistWAL(h.File, keepWAL) // notest
|
||||
}
|
||||
|
||||
func (h *hbshFile) HasMoved() (bool, error) {
|
||||
@@ -253,6 +253,10 @@ func (h *hbshFile) Overwrite() error {
|
||||
return vfsutil.WrapOverwrite(h.File) // notest
|
||||
}
|
||||
|
||||
func (h *hbshFile) SyncSuper(super string) error {
|
||||
return vfsutil.WrapSyncSuper(h.File, super) // notest
|
||||
}
|
||||
|
||||
func (h *hbshFile) CommitPhaseTwo() error {
|
||||
return vfsutil.WrapCommitPhaseTwo(h.File) // notest
|
||||
}
|
||||
@@ -276,3 +280,7 @@ func (h *hbshFile) CheckpointStart() {
|
||||
func (h *hbshFile) CheckpointDone() {
|
||||
vfsutil.WrapCheckpointDone(h.File) // notest
|
||||
}
|
||||
|
||||
func (h *hbshFile) BusyHandler(handler func() bool) {
|
||||
vfsutil.WrapBusyHandler(h.File, handler) // notest
|
||||
}
|
||||
|
||||
31
vfs/api.go
31
vfs/api.go
@@ -65,14 +65,14 @@ type FileLockState interface {
|
||||
LockState() LockLevel
|
||||
}
|
||||
|
||||
// FilePersistentWAL extends File to implement the
|
||||
// FilePersistWAL extends File to implement the
|
||||
// SQLITE_FCNTL_PERSIST_WAL file control opcode.
|
||||
//
|
||||
// https://sqlite.org/c3ref/c_fcntl_begin_atomic_write.html#sqlitefcntlpersistwal
|
||||
type FilePersistentWAL interface {
|
||||
type FilePersistWAL interface {
|
||||
File
|
||||
PersistentWAL() bool
|
||||
SetPersistentWAL(bool)
|
||||
PersistWAL() bool
|
||||
SetPersistWAL(bool)
|
||||
}
|
||||
|
||||
// FilePowersafeOverwrite extends File to implement the
|
||||
@@ -121,6 +121,15 @@ type FileOverwrite interface {
|
||||
Overwrite() error
|
||||
}
|
||||
|
||||
// FileSync extends File to implement the
|
||||
// SQLITE_FCNTL_SYNC file control opcode.
|
||||
//
|
||||
// https://sqlite.org/c3ref/c_fcntl_begin_atomic_write.html#sqlitefcntlsync
|
||||
type FileSync interface {
|
||||
File
|
||||
SyncSuper(super string) error
|
||||
}
|
||||
|
||||
// FileCommitPhaseTwo extends File to implement the
|
||||
// SQLITE_FCNTL_COMMIT_PHASETWO file control opcode.
|
||||
//
|
||||
@@ -162,6 +171,15 @@ type FilePragma interface {
|
||||
Pragma(name, value string) (string, error)
|
||||
}
|
||||
|
||||
// FileBusyHandler extends File to implement the
|
||||
// SQLITE_FCNTL_BUSYHANDLER file control opcode.
|
||||
//
|
||||
// https://sqlite.org/c3ref/c_fcntl_begin_atomic_write.html#sqlitefcntlbusyhandler
|
||||
type FileBusyHandler interface {
|
||||
File
|
||||
BusyHandler(func() bool)
|
||||
}
|
||||
|
||||
// FileSharedMemory extends File to possibly implement
|
||||
// shared-memory for the WAL-index.
|
||||
// The same shared-memory instance must be returned
|
||||
@@ -191,3 +209,8 @@ type fileControl interface {
|
||||
File
|
||||
fileControl(ctx context.Context, mod api.Module, op _FcntlOpcode, pArg uint32) _ErrorCode
|
||||
}
|
||||
|
||||
type filePDB interface {
|
||||
File
|
||||
SetDB(any)
|
||||
}
|
||||
|
||||
@@ -225,6 +225,7 @@ const (
|
||||
_FCNTL_EXTERNAL_READER _FcntlOpcode = 40
|
||||
_FCNTL_CKSM_FILE _FcntlOpcode = 41
|
||||
_FCNTL_RESET_CACHE _FcntlOpcode = 42
|
||||
_FCNTL_NULL_IO _FcntlOpcode = 43
|
||||
)
|
||||
|
||||
// https://sqlite.org/c3ref/c_shm_exclusive.html
|
||||
|
||||
@@ -142,7 +142,7 @@ var (
|
||||
_ FileLockState = &vfsFile{}
|
||||
_ FileHasMoved = &vfsFile{}
|
||||
_ FileSizeHint = &vfsFile{}
|
||||
_ FilePersistentWAL = &vfsFile{}
|
||||
_ FilePersistWAL = &vfsFile{}
|
||||
_ FilePowersafeOverwrite = &vfsFile{}
|
||||
)
|
||||
|
||||
@@ -217,6 +217,6 @@ func (f *vfsFile) HasMoved() (bool, error) {
|
||||
|
||||
func (f *vfsFile) LockState() LockLevel { return f.lock }
|
||||
func (f *vfsFile) PowersafeOverwrite() bool { return f.psow }
|
||||
func (f *vfsFile) PersistentWAL() bool { return f.keepWAL }
|
||||
func (f *vfsFile) PersistWAL() bool { return f.keepWAL }
|
||||
func (f *vfsFile) SetPowersafeOverwrite(psow bool) { f.psow = psow }
|
||||
func (f *vfsFile) SetPersistentWAL(keepWAL bool) { f.keepWAL = keepWAL }
|
||||
func (f *vfsFile) SetPersistWAL(keepWAL bool) { f.keepWAL = keepWAL }
|
||||
|
||||
@@ -62,11 +62,11 @@ func (memVFS) Open(name string, flags vfs.OpenFlag) (vfs.File, vfs.OpenFlag, err
|
||||
}
|
||||
|
||||
func (memVFS) Delete(name string, dirSync bool) error {
|
||||
return sqlite3.IOERR_DELETE
|
||||
return sqlite3.IOERR_DELETE_NOENT // used to delete journals
|
||||
}
|
||||
|
||||
func (memVFS) Access(name string, flag vfs.AccessFlag) (bool, error) {
|
||||
return false, nil
|
||||
return false, nil // used to check for journals
|
||||
}
|
||||
|
||||
func (memVFS) FullPathname(name string) (string, error) {
|
||||
|
||||
Binary file not shown.
Binary file not shown.
40
vfs/vfs.go
40
vfs/vfs.go
@@ -255,10 +255,10 @@ func vfsFileControlImpl(ctx context.Context, mod api.Module, file File, op _Fcnt
|
||||
}
|
||||
|
||||
case _FCNTL_PERSIST_WAL:
|
||||
if file, ok := file.(FilePersistentWAL); ok {
|
||||
if file, ok := file.(FilePersistWAL); ok {
|
||||
if i := util.ReadUint32(mod, pArg); int32(i) >= 0 {
|
||||
file.SetPersistentWAL(i != 0)
|
||||
} else if file.PersistentWAL() {
|
||||
file.SetPersistWAL(i != 0)
|
||||
} else if file.PersistWAL() {
|
||||
util.WriteUint32(mod, pArg, 1)
|
||||
} else {
|
||||
util.WriteUint32(mod, pArg, 0)
|
||||
@@ -309,6 +309,16 @@ func vfsFileControlImpl(ctx context.Context, mod api.Module, file File, op _Fcnt
|
||||
return vfsErrorCode(err, _IOERR)
|
||||
}
|
||||
|
||||
case _FCNTL_SYNC:
|
||||
if file, ok := file.(FileSync); ok {
|
||||
var name string
|
||||
if pArg != 0 {
|
||||
name = util.ReadString(mod, pArg, _MAX_PATHNAME)
|
||||
}
|
||||
err := file.SyncSuper(name)
|
||||
return vfsErrorCode(err, _IOERR)
|
||||
}
|
||||
|
||||
case _FCNTL_COMMIT_PHASETWO:
|
||||
if file, ok := file.(FileCommitPhaseTwo); ok {
|
||||
err := file.CommitPhaseTwo()
|
||||
@@ -369,6 +379,20 @@ func vfsFileControlImpl(ctx context.Context, mod api.Module, file File, op _Fcnt
|
||||
return ret
|
||||
}
|
||||
|
||||
case _FCNTL_BUSYHANDLER:
|
||||
if file, ok := file.(FileBusyHandler); ok {
|
||||
arg := util.ReadUint64(mod, pArg)
|
||||
fn := mod.ExportedFunction("sqlite3_invoke_busy_handler_go")
|
||||
file.BusyHandler(func() bool {
|
||||
stack := [...]uint64{arg}
|
||||
if err := fn.CallWithStack(ctx, stack[:]); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return uint32(stack[0]) != 0
|
||||
})
|
||||
return _OK
|
||||
}
|
||||
|
||||
case _FCNTL_LOCK_TIMEOUT:
|
||||
if file, ok := file.(FileSharedMemory); ok {
|
||||
if shm, ok := file.SharedMemory().(blockingSharedMemory); ok {
|
||||
@@ -376,12 +400,14 @@ func vfsFileControlImpl(ctx context.Context, mod api.Module, file File, op _Fcnt
|
||||
return _OK
|
||||
}
|
||||
}
|
||||
|
||||
case _FCNTL_PDB:
|
||||
if file, ok := file.(filePDB); ok {
|
||||
file.SetDB(ctx.Value(util.ConnKey{}))
|
||||
return _OK
|
||||
}
|
||||
}
|
||||
|
||||
// Consider also implementing these opcodes (in use by SQLite):
|
||||
// _FCNTL_BUSYHANDLER
|
||||
// _FCNTL_LAST_ERRNO
|
||||
// _FCNTL_SYNC
|
||||
return _NOTFOUND
|
||||
}
|
||||
|
||||
|
||||
@@ -234,11 +234,11 @@ func (x *xtsFile) LockState() vfs.LockLevel {
|
||||
}
|
||||
|
||||
func (x *xtsFile) PersistentWAL() bool {
|
||||
return vfsutil.WrapPersistentWAL(x.File) // notest
|
||||
return vfsutil.WrapPersistWAL(x.File) // notest
|
||||
}
|
||||
|
||||
func (x *xtsFile) SetPersistentWAL(keepWAL bool) {
|
||||
vfsutil.WrapSetPersistentWAL(x.File, keepWAL) // notest
|
||||
vfsutil.WrapSetPersistWAL(x.File, keepWAL) // notest
|
||||
}
|
||||
|
||||
func (x *xtsFile) HasMoved() (bool, error) {
|
||||
@@ -249,6 +249,10 @@ func (x *xtsFile) Overwrite() error {
|
||||
return vfsutil.WrapOverwrite(x.File) // notest
|
||||
}
|
||||
|
||||
func (x *xtsFile) SyncSuper(super string) error {
|
||||
return vfsutil.WrapSyncSuper(x.File, super) // notest
|
||||
}
|
||||
|
||||
func (x *xtsFile) CommitPhaseTwo() error {
|
||||
return vfsutil.WrapCommitPhaseTwo(x.File) // notest
|
||||
}
|
||||
@@ -272,3 +276,7 @@ func (x *xtsFile) CheckpointStart() {
|
||||
func (x *xtsFile) CheckpointDone() {
|
||||
vfsutil.WrapCheckpointDone(x.File) // notest
|
||||
}
|
||||
|
||||
func (x *xtsFile) BusyHandler(handler func() bool) {
|
||||
vfsutil.WrapBusyHandler(x.File, handler) // notest
|
||||
}
|
||||
|
||||
2
vtab.go
2
vtab.go
@@ -242,7 +242,7 @@ type VTabSavepointer interface {
|
||||
// A VTabCursor may optionally implement
|
||||
// [io.Closer] to free resources.
|
||||
//
|
||||
// http://sqlite.org/c3ref/vtab_cursor.html
|
||||
// https://sqlite.org/c3ref/vtab_cursor.html
|
||||
type VTabCursor interface {
|
||||
// https://sqlite.org/vtab.html#xfilter
|
||||
Filter(idxNum int, idxStr string, arg ...Value) error
|
||||
|
||||
Reference in New Issue
Block a user