Compare commits

...

43 Commits

Author SHA1 Message Date
Nuno Cruces
83744cb9c8 More sqlite-createtable-parser. 2026-01-14 18:11:59 +00:00
Nuno Cruces
a36d72c2dc Stricter vtabs, sqlite-createtable-parser. 2026-01-13 14:23:55 +00:00
Nuno Cruces
e7f5604199 Stricter vtabs, get VFS name. 2026-01-13 11:25:25 +00:00
dependabot[bot]
e50083912c Bump golang.org/x/crypto from 0.46.0 to 0.47.0 (#348)
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.46.0 to 0.47.0.
- [Commits](https://github.com/golang/crypto/compare/v0.46.0...v0.47.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-version: 0.47.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-13 08:03:31 +00:00
dependabot[bot]
d4764fb2fa Bump golang.org/x/sys from 0.39.0 to 0.40.0 (#346)
Bumps [golang.org/x/sys](https://github.com/golang/sys) from 0.39.0 to 0.40.0.
- [Commits](https://github.com/golang/sys/compare/v0.39.0...v0.40.0)

---
updated-dependencies:
- dependency-name: golang.org/x/sys
  dependency-version: 0.40.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-08 23:12:26 +00:00
dependabot[bot]
42df71f3ff Bump github.com/ncruces/wbt from 0.2.0 to 1.0.0 (#345)
Bumps [github.com/ncruces/wbt](https://github.com/ncruces/wbt) from 0.2.0 to 1.0.0.
- [Release notes](https://github.com/ncruces/wbt/releases)
- [Commits](https://github.com/ncruces/wbt/compare/v0.2.0...v1.0.0)

---
updated-dependencies:
- dependency-name: github.com/ncruces/wbt
  dependency-version: 1.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-06 23:55:44 +00:00
Nuno Cruces
91e969c06b Example. 2026-01-05 12:49:21 +00:00
Nuno Cruces
8ab0ddf53e More temp files. 2025-12-29 20:45:44 +00:00
Nuno Cruces
74d22ded0a Move Litestream. 2025-12-29 12:20:05 +00:00
Nuno Cruces
d962611796 Bump cross-platform-actions/action from 0.31.0 to 0.32.0 2025-12-21 13:39:41 +00:00
Nuno Cruces
7df3814c34 Improved driver metadata. 2025-12-20 22:23:05 +00:00
Nuno Cruces
c5f49b835a Reduce mutex scope, use temp files. 2025-12-19 16:37:47 +00:00
Nuno Cruces
0e55451a0b Deps. 2025-12-19 12:28:57 +00:00
Nuno Cruces
ea9a58ab19 Remove singleflight. 2025-12-17 13:17:50 +00:00
Nuno Cruces
0b46e74ea6 Bump cross-platform-actions/action from 0.30.0 to 0.31.0 2025-12-16 12:34:55 +00:00
Nuno Cruces
8dca850bee Stricter floats. 2025-12-12 17:21:33 +00:00
Nuno Cruces
5b78823416 ScanColumn dropped. 2025-12-12 13:06:37 +00:00
Nuno Cruces
1764a571da Litestream v0.5.3. 2025-12-12 12:44:57 +00:00
Nuno Cruces
9837310af7 Fuzz time shift. 2025-12-10 16:15:44 +00:00
dependabot[bot]
ec8961a621 Bump golang.org/x/crypto from 0.45.0 to 0.46.0 (#341)
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.45.0 to 0.46.0.
- [Commits](https://github.com/golang/crypto/compare/v0.45.0...v0.46.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-version: 0.46.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-09 00:49:00 +00:00
Nuno Cruces
8c37aa2d97 Remove inprocess primary. 2025-12-04 16:31:41 +00:00
Nuno Cruces
ca93c498e7 Relative time, fixes. 2025-12-04 15:55:39 +00:00
Nuno Cruces
15e9087fa8 Time travel pragma. 2025-12-03 15:01:04 +00:00
Nuno Cruces
7028e3a5b9 SQLite 3.51.1. 2025-11-30 10:24:34 +00:00
Nuno Cruces
03bb20de6e Lock tweaks. 2025-11-29 16:12:29 +00:00
Nuno Cruces
20a51a344e Remove MinLevel. 2025-11-27 17:29:47 +00:00
Nuno Cruces
2dbcc480f7 Initial pragma support. 2025-11-27 16:28:16 +00:00
Nuno Cruces
0f0716c438 Inprocess Litestream primary. 2025-11-26 14:07:44 +00:00
Nuno Cruces
0286e50e25 Experimental file control opcode for write transaction (#339) 2025-11-21 12:48:31 +00:00
dependabot[bot]
8ac10eb8b4 Bump actions/checkout from 5 to 6 (#338)
Bumps [actions/checkout](https://github.com/actions/checkout) from 5 to 6.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v5...v6)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-20 22:48:29 +00:00
Nuno Cruces
0ff41bb966 Deps. 2025-11-20 18:47:59 +00:00
Nuno Cruces
ba9caf0405 Shared page cache. 2025-11-20 18:30:51 +00:00
Nuno Cruces
2c167dd116 Avoid polling intermediate levels. 2025-11-19 16:22:10 +00:00
Nuno Cruces
ce0da893b4 Fix #335. 2025-11-19 11:27:42 +00:00
Nuno Cruces
9bbbab77f6 Fix define. 2025-11-18 17:58:56 +00:00
Nuno Cruces
bab2d26652 Deps. 2025-11-11 23:03:31 +00:00
Nuno Cruces
3132b272de Test more, log less. 2025-11-10 11:26:28 +00:00
Nuno Cruces
8f9a6ca4c1 Litestream lightweight read-replicas. (#328) 2025-11-09 13:16:08 +00:00
Nuno Cruces
99b097de3b Windows ARM runners. 2025-11-09 12:44:32 +00:00
Nuno Cruces
4a956e80a2 wasi-sdk-28. 2025-11-09 01:32:25 +00:00
Nuno Cruces
5f4ff03f6f wazero v1.10.0. 2025-11-09 01:32:15 +00:00
Nuno Cruces
5890049488 Shim modernc. 2025-11-09 01:32:14 +00:00
Nuno Cruces
5e73c5d714 Issue #330. 2025-11-06 12:07:37 +00:00
79 changed files with 1105 additions and 1039 deletions

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env bash
set -euo pipefail
echo 'set -eu' > test.sh
echo 'set -eux' > test.sh
for p in $(go list ./...); do
dir=".${p#github.com/ncruces/go-sqlite3}"

View File

@@ -14,7 +14,7 @@ jobs:
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- uses: actions/setup-go@v6
with: { go-version: stable }

View File

@@ -17,7 +17,7 @@ jobs:
steps:
- uses: ilammy/msvc-dev-cmd@v1
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- name: Build
shell: bash

View File

@@ -30,7 +30,7 @@ jobs:
contents: write
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- uses: actions/setup-go@v6
with: { go-version: stable }
@@ -51,30 +51,30 @@ jobs:
run: go vet ./...
- name: Build
run: go build -v ./...
run: go build ./...
- name: Test
run: go test -v ./... -bench . -benchtime=1x
run: go test ./... -bench . -benchtime=1x
- name: Test BSD locks
run: go test -v -tags sqlite3_flock ./...
run: go test -tags sqlite3_flock ./...
if: matrix.os != 'windows-latest'
- name: Test dot locks
run: go test -v -tags sqlite3_dotlk ./...
run: go test -tags sqlite3_dotlk ./...
if: matrix.os != 'windows-latest'
- name: Test modules
shell: bash
run: |
go work init .
go work use -r embed gormlite
go test -v ./embed/bcw2/...
go work use -r embed/bcw2 gormlite
go test ./embed/bcw2 ./gormlite
- name: Test GORM
shell: bash
run: gormlite/test.sh
if: matrix.os != 'windows-latest'
if: matrix.os == 'ubuntu-latest'
- name: Collect coverage
run: |
@@ -93,44 +93,45 @@ jobs:
github.event_name == 'push' &&
matrix.os == 'ubuntu-latest'
test-bsd:
test-cross:
strategy:
matrix:
os:
- name: freebsd
version: '14.3'
flags: '-test.v'
version: '15.0'
- name: netbsd
version: '10.1'
flags: '-test.v'
- name: freebsd
arch: arm64
version: '14.3'
flags: '-test.v -test.short'
- name: netbsd
arch: arm64
version: '10.1'
flags: '-test.v -test.short'
- name: illumos
action: omnios
version: 'r151056'
- name: openbsd
version: '7.8'
flags: '-test.v -test.short'
tflags: '-test.short'
- name: freebsd
arch: arm64
version: '15.0'
tflags: '-test.short'
- name: netbsd
arch: arm64
version: '10.1'
tflags: '-test.short'
runs-on: ubuntu-latest
needs: test
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- name: Build
env:
GOOS: ${{ matrix.os.name }}
GOARCH: ${{ matrix.os.arch }}
TESTFLAGS: ${{ matrix.os.flags }}
TESTFLAGS: ${{ matrix.os.tflags }}
run: .github/workflows/build-test.sh
- name: Test
uses: cross-platform-actions/action@v0.30.0
uses: cross-platform-actions/action@v0.32.0
with:
operating_system: ${{ matrix.os.name }}
operating_system: ${{ matrix.os.action || matrix.os.name }}
architecture: ${{ matrix.os.arch }}
version: ${{ matrix.os.version }}
shell: bash
@@ -143,19 +144,16 @@ jobs:
os:
- name: dragonfly
action: 'vmactions/dragonflybsd-vm@v1'
tflags: '-test.v'
- name: illumos
action: 'vmactions/omnios-vm@v1'
tflags: '-test.v'
action: 'vmactions/openindiana-vm@v0'
- name: solaris
action: 'vmactions/solaris-vm@v1'
bflags: '-tags sqlite3_dotlk'
tflags: '-test.v'
runs-on: ubuntu-latest
needs: test
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- name: Build
env:
@@ -174,7 +172,7 @@ jobs:
steps:
- uses: bytecodealliance/actions/wasmtime/setup@v1
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- uses: actions/setup-go@v6
with: { go-version: stable }
@@ -187,7 +185,7 @@ jobs:
GOARCH: wasm
GOWASIRUNTIME: wasmtime
GOWASIRUNTIMEARGS: '--env CI=true'
run: go test -v -short -tags sqlite3_dotlk -skip Example ./...
run: go test -short -tags sqlite3_dotlk -skip Example ./...
test-qemu:
runs-on: ubuntu-latest
@@ -195,42 +193,60 @@ jobs:
steps:
- uses: docker/setup-qemu-action@v3
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- uses: actions/setup-go@v6
with: { go-version: stable }
- name: Test 386 (32-bit)
run: GOARCH=386 go test -v -short ./...
run: GOARCH=386 go test -short ./...
- name: Test riscv64 (interpreter)
run: GOARCH=riscv64 go test -v -short ./...
run: GOARCH=riscv64 go test -short ./...
- name: Test ppc64le (interpreter)
run: GOARCH=ppc64le go test -v -short ./...
run: GOARCH=ppc64le go test -short ./...
- name: Test loong64 (interpreter)
run: GOARCH=loong64 go test -short ./...
- name: Test s390x (big-endian)
run: GOARCH=s390x go test -v -short -tags sqlite3_dotlk ./...
run: GOARCH=s390x go test -short -tags sqlite3_dotlk ./...
test-linuxarm:
runs-on: ubuntu-24.04-arm
needs: test
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- uses: actions/setup-go@v6
with: { go-version: stable }
- name: Test
run: go test -v ./...
run: go test ./...
- name: Test arm (32-bit)
run: GOARCH=arm GOARM=7 go test -short ./...
test-macintel:
runs-on: macos-15-intel
needs: test
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- uses: actions/setup-go@v6
with: { go-version: stable }
- name: Test
run: go test -v ./...
run: go test ./...
test-winarm:
runs-on: windows-11-arm
needs: test
steps:
- uses: actions/checkout@v6
- uses: actions/setup-go@v6
with: { go-version: stable }
- name: Test
run: go test ./...

View File

@@ -84,14 +84,14 @@ It also benefits greatly from [SQLite's](https://sqlite.org/testing.html) and
thorough testing.
Every commit is tested on:
* Linux: amd64, arm64, 386, riscv64, ppc64le, s390x
* Linux: amd64, arm64, 386, arm, riscv64, ppc64le, loong64, s390x
* macOS: amd64, arm64
* Windows: amd64
* Windows: amd64, arm64
* BSD:
* FreeBSD: amd64, arm64
* OpenBSD: amd64
* NetBSD: amd64, arm64
* DragonFly BSD: amd64
* OpenBSD: amd64
* illumos: amd64
* Solaris: amd64

View File

@@ -157,16 +157,20 @@ func (c *Conn) FileControl(schema string, op FcntlOpcode, arg ...any) (any, erro
stk_t(op), stk_t(ptr)))
ret = util.Read32[vfs.LockLevel](c.mod, ptr)
case FCNTL_VFS_POINTER:
case FCNTL_VFSNAME, FCNTL_VFS_POINTER:
rc = res_t(c.call("sqlite3_file_control",
stk_t(c.handle), stk_t(schemaPtr),
stk_t(op), stk_t(ptr)))
stk_t(FCNTL_VFS_POINTER), stk_t(ptr)))
if rc == _OK {
const zNameOffset = 16
ptr = util.Read32[ptr_t](c.mod, ptr)
ptr = util.Read32[ptr_t](c.mod, ptr+zNameOffset)
name := util.ReadString(c.mod, ptr, _MAX_NAME)
ret = vfs.Find(name)
if op == FCNTL_VFS_POINTER {
ret = vfs.Find(name)
} else {
ret = name
}
}
case FCNTL_FILE_POINTER, FCNTL_JOURNAL_POINTER:

View File

@@ -173,7 +173,7 @@ const (
// PrepareFlag is a flag that can be passed to [Conn.PrepareFlags].
//
// https://sqlite.org/c3ref/c_prepare_normalize.html
// https://sqlite.org/c3ref/c_prepare_dont_log.html
type PrepareFlag uint32
const (
@@ -181,6 +181,7 @@ const (
PREPARE_NORMALIZE PrepareFlag = 0x02
PREPARE_NO_VTAB PrepareFlag = 0x04
PREPARE_DONT_LOG PrepareFlag = 0x10
PREPARE_FROM_DDL PrepareFlag = 0x20
)
// FunctionFlag is a flag that can be passed to
@@ -280,6 +281,7 @@ const (
FCNTL_CHUNK_SIZE FcntlOpcode = 6
FCNTL_FILE_POINTER FcntlOpcode = 7
FCNTL_PERSIST_WAL FcntlOpcode = 10
FCNTL_VFSNAME FcntlOpcode = 12
FCNTL_POWERSAFE_OVERWRITE FcntlOpcode = 13
FCNTL_VFS_POINTER FcntlOpcode = 27
FCNTL_JOURNAL_POINTER FcntlOpcode = 28
@@ -307,6 +309,7 @@ const (
LIMIT_VARIABLE_NUMBER LimitCategory = 9
LIMIT_TRIGGER_DEPTH LimitCategory = 10
LIMIT_WORKER_THREADS LimitCategory = 11
LIMIT_PARSER_DEPTH LimitCategory = 12
)
// AuthorizerActionCode are the integer action codes

View File

@@ -263,10 +263,8 @@ func (n *connector) Connect(ctx context.Context) (ret driver.Conn, err error) {
return nil, err
}
defer s.Close()
if s.Step() && s.ColumnBool(0) {
c.readOnly = '1'
} else {
c.readOnly = '0'
if s.Step() {
c.readOnly = s.ColumnBool(0)
}
err = s.Close()
if err != nil {
@@ -322,7 +320,7 @@ type conn struct {
txReset string
tmRead sqlite3.TimeFormat
tmWrite sqlite3.TimeFormat
readOnly byte
readOnly bool
}
var (
@@ -358,9 +356,9 @@ func (c *conn) BeginTx(ctx context.Context, opts driver.TxOptions) (driver.Tx, e
c.txReset = ``
txBegin := `BEGIN ` + txLock
if opts.ReadOnly {
if opts.ReadOnly && !c.readOnly {
txBegin += ` ; PRAGMA query_only=on`
c.txReset = `; PRAGMA query_only=` + string(c.readOnly)
c.txReset = `; PRAGMA query_only=off`
}
if old := c.Conn.SetInterrupt(ctx); old != ctx {
@@ -655,14 +653,12 @@ type rows struct {
names []string
types []string
scans []scantype
dest []driver.Value
}
var (
// Ensure these interfaces are implemented:
_ driver.RowsColumnTypeDatabaseTypeName = &rows{}
_ driver.RowsColumnTypeNullable = &rows{}
// _ driver.RowsColumnScanner = &rows{}
)
func (r *rows) Close() error {
@@ -702,17 +698,23 @@ func (r *rows) loadColumnMetadata() {
types := make([]string, count)
scans := make([]scantype, count)
for i := range types {
var notnull bool
if col := r.Stmt.ColumnOriginName(i); col != "" {
types[i], _, notnull, _, _, _ = c.TableColumnMetadata(
var declType string
var notNull, autoInc bool
if column := r.Stmt.ColumnOriginName(i); column != "" {
declType, _, notNull, _, autoInc, _ = c.TableColumnMetadata(
r.Stmt.ColumnDatabaseName(i),
r.Stmt.ColumnTableName(i),
col)
types[i] = strings.ToUpper(types[i])
scans[i] = scanFromDecl(types[i])
if notnull {
scans[i] |= _NOT_NULL
}
column)
} else {
declType = r.Stmt.ColumnDeclType(i)
}
if declType != "" {
declType = strings.ToUpper(declType)
scans[i] = scanFromDecl(declType)
types[i] = declType
}
if notNull || autoInc {
scans[i] |= _NOT_NULL
}
}
r.types = types
@@ -782,7 +784,6 @@ func (r *rows) ColumnTypeScanType(index int) (typ reflect.Type) {
}
func (r *rows) Next(dest []driver.Value) error {
r.dest = nil
c := r.Stmt.Conn()
if old := c.SetInterrupt(r.ctx); old != r.ctx {
defer c.SetInterrupt(old)
@@ -832,33 +833,5 @@ func (r *rows) Next(dest []driver.Value) error {
}
}
}
r.dest = dest
return nil
}
func (r *rows) ScanColumn(dest any, index int) (err error) {
// notest // Go 1.26
var tm *time.Time
var ok *bool
switch d := dest.(type) {
case *time.Time:
tm = d
case *sql.NullTime:
tm = &d.Time
ok = &d.Valid
case *sql.Null[time.Time]:
tm = &d.V
ok = &d.Valid
default:
return driver.ErrSkip
}
value := r.dest[index]
*tm, err = r.tmRead.Decode(value)
if ok != nil {
*ok = err == nil
if value == nil {
return nil
}
}
return err
}

View File

@@ -8,7 +8,6 @@ import (
"math"
"net/url"
"reflect"
"strings"
"testing"
"time"
@@ -521,39 +520,6 @@ func Test_ColumnType_ScanType(t *testing.T) {
}
}
func Test_rows_ScanColumn(t *testing.T) {
t.Parallel()
dsn := memdb.TestDB(t)
db, err := Open(dsn)
if err != nil {
t.Fatal(err)
}
defer db.Close()
var tm time.Time
err = db.QueryRow(`SELECT NULL`).Scan(&tm)
if err == nil {
t.Error("want error")
}
// Go 1.26
err = db.QueryRow(`SELECT datetime()`).Scan(&tm)
if err != nil && !strings.HasPrefix(err.Error(), "sql: Scan error") {
t.Error(err)
}
var nt sql.NullTime
err = db.QueryRow(`SELECT NULL`).Scan(&nt)
if err != nil {
t.Error(err)
}
// Go 1.26
err = db.QueryRow(`SELECT datetime()`).Scan(&nt)
if err != nil && !strings.HasPrefix(err.Error(), "sql: Scan error") {
t.Error(err)
}
}
func Benchmark_loop(b *testing.B) {
db, err := Open(":memory:")
if err != nil {

View File

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

View File

@@ -53,7 +53,7 @@ func Test_bcw2(t *testing.T) {
if err != nil {
t.Fatal(err)
}
if version != "3.51.0" {
if version != "3.52.0" {
t.Error(version)
}
}

View File

@@ -15,14 +15,15 @@ cp "$ROOT"/sqlite3/*.[ch] build/
cp "$ROOT"/sqlite3/*.patch build/
cd sqlite/
# https://sqlite.org/src/info/0e862bc9ed7aa9ae
curl -#L https://github.com/sqlite/sqlite/archive/0b99392.tar.gz | tar xz --strip-components=1
# curl -#L https://sqlite.org/src/tarball/sqlite.tar.gz?r=0e862bc9ed | tar xz --strip-components=1
# https://sqlite.org/src/info/f273f6b8245c5dca
curl -#L https://github.com/sqlite/sqlite/archive/7c126d7.tar.gz | tar xz --strip-components=1
# curl -#L https://sqlite.org/src/tarball/sqlite.tar.gz?r=f273f6b824 | tar xz --strip-components=1
if [[ "$OSTYPE" == "msys" || "$OSTYPE" == "cygwin" ]]; then
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 verify-source
OPTS=-DSQLITE_ENABLE_ORDERED_SET_AGGREGATES make sqlite3.c
fi
cd ~-
@@ -58,12 +59,14 @@ cd ~-
-Wl,--initial-memory=327680 \
-D_HAVE_SQLITE_CONFIG_H \
-DSQLITE_ENABLE_UPDATE_DELETE_LIMIT \
-DSQLITE_ENABLE_ORDERED_SET_AGGREGATES \
-DSQLITE_EXPERIMENTAL_PRAGMA_20251114 \
-DSQLITE_CUSTOM_INCLUDE=sqlite_opt.h \
$(awk '{print "-Wl,--export="$0}' ../exports.txt)
"$BINARYEN/wasm-ctor-eval" -g -c _initialize bcw2.wasm -o bcw2.tmp
"$BINARYEN/wasm-opt" -g bcw2.tmp -o bcw2.wasm \
--low-memory-unused --gufa --generate-global-effects --converge -O3 \
--gufa --generate-global-effects --low-memory-unused --converge -O3 \
--enable-mutable-globals --enable-nontrapping-float-to-int \
--enable-simd --enable-bulk-memory --enable-sign-ext \
--enable-reference-types --enable-multivalue \

View File

@@ -2,11 +2,11 @@ module github.com/ncruces/go-sqlite3/embed/bcw2
go 1.24.0
require github.com/ncruces/go-sqlite3 v0.29.0
require github.com/ncruces/go-sqlite3 v0.30.3
require (
github.com/ncruces/julianday v1.0.0 // indirect
github.com/ncruces/sort v0.1.5 // indirect
github.com/tetratelabs/wazero v1.9.0 // indirect
golang.org/x/sys v0.36.0 // indirect
github.com/ncruces/sort v0.1.6 // indirect
github.com/tetratelabs/wazero v1.11.0 // indirect
golang.org/x/sys v0.39.0 // indirect
)

View File

@@ -1,12 +1,12 @@
github.com/ncruces/go-sqlite3 v0.29.0 h1:1tsLiagCoqZEfcHDeKsNSv5jvrY/Iu393pAnw2wLNJU=
github.com/ncruces/go-sqlite3 v0.29.0/go.mod h1:r1hSvYKPNJ+OlUA1O3r8o9LAawzPAlqeZiIdxTBBBJ0=
github.com/ncruces/go-sqlite3 v0.30.3 h1:X/CgWW9GzmIAkEPrifhKqf0cC15DuOVxAJaHFTTAURQ=
github.com/ncruces/go-sqlite3 v0.30.3/go.mod h1:AxKu9sRxkludimFocbktlY6LiYSkxiI5gTA8r+os/Nw=
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.5 h1:fiFWXXAqKI8QckPf/6hu/bGFwcEPrirIOFaJqWujs4k=
github.com/ncruces/sort v0.1.5/go.mod h1:obJToO4rYr6VWP0Uw5FYymgYGt3Br4RXcs/JdKaXAPk=
github.com/tetratelabs/wazero v1.9.0 h1:IcZ56OuxrtaEz8UYNRHBrUa9bYeX9oVY93KspZZBf/I=
github.com/tetratelabs/wazero v1.9.0/go.mod h1:TSbcXCfFP0L2FGkRPxHphadXPjo1T6W+CseNNY7EkjM=
golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k=
golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk=
golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4=
github.com/ncruces/sort v0.1.6 h1:TrsJfGRH1AoWoaeB4/+gCohot9+cA6u/INaH5agIhNk=
github.com/ncruces/sort v0.1.6/go.mod h1:obJToO4rYr6VWP0Uw5FYymgYGt3Br4RXcs/JdKaXAPk=
github.com/tetratelabs/wazero v1.11.0 h1:+gKemEuKCTevU4d7ZTzlsvgd1uaToIDtlQlmNbwqYhA=
github.com/tetratelabs/wazero v1.11.0/go.mod h1:eV28rsN8Q+xwjogd7f4/Pp4xFxO7uOGbLcD/LzB1wiU=
golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=

View File

@@ -23,13 +23,14 @@ trap 'rm -f sqlite3.tmp' EXIT
-Wl,--import-undefined \
-Wl,--initial-memory=327680 \
-D_HAVE_SQLITE_CONFIG_H \
-DSQLITE_EXPERIMENTAL_PRAGMA_20251114 \
-DSQLITE_CUSTOM_INCLUDE=sqlite_opt.h \
$(awk '{print "-Wl,--export="$0}' exports.txt)
"$BINARYEN/wasm-ctor-eval" -g -c _initialize sqlite3.wasm -o sqlite3.tmp
"$BINARYEN/wasm-opt" -g sqlite3.tmp -o sqlite3.wasm \
--low-memory-unused --gufa --generate-global-effects --converge -O3 \
--gufa --generate-global-effects --low-memory-unused --converge -O3 \
--enable-mutable-globals --enable-nontrapping-float-to-int \
--enable-simd --enable-bulk-memory --enable-sign-ext \
--enable-reference-types --enable-multivalue \
--strip --strip-producers
--strip --strip-producers

View File

@@ -17,5 +17,7 @@ import (
var binary string
func init() {
sqlite3.Binary = unsafe.Slice(unsafe.StringData(binary), len(binary))
if sqlite3.Binary == nil {
sqlite3.Binary = unsafe.Slice(unsafe.StringData(binary), len(binary))
}
}

View File

@@ -19,7 +19,7 @@ func Test_init(t *testing.T) {
if err != nil {
t.Fatal(err)
}
if version != "3.51.0" {
if version != "3.51.1" {
t.Error(version)
}
}

Binary file not shown.

View File

@@ -16,6 +16,7 @@ import (
"github.com/ncruces/go-sqlite3"
"github.com/ncruces/go-sqlite3/internal/util"
"github.com/ncruces/go-sqlite3/util/sql3util"
)
// Register registers the bloom_filter virtual table:
@@ -34,6 +35,8 @@ type bloom struct {
hashes int
}
const vtab = `CREATE TABLE x(present, word TEXT HIDDEN NOT NULL PRIMARY KEY) WITHOUT ROWID`
func create(db *sqlite3.Conn, _, schema, table string, arg ...string) (_ *bloom, err error) {
b := bloom{
db: db,
@@ -55,11 +58,9 @@ func create(db *sqlite3.Conn, _, schema, table string, arg ...string) (_ *bloom,
}
if len(arg) > 1 {
b.prob, err = strconv.ParseFloat(arg[1], 64)
if err != nil {
return nil, err
}
if b.prob <= 0 || b.prob >= 1 {
var ok bool
b.prob, ok = sql3util.ParseFloat(arg[1])
if !ok || b.prob <= 0 || b.prob >= 1 {
return nil, util.ErrorString("bloom: probability must be in the range (0,1)")
}
} else {
@@ -80,8 +81,7 @@ func create(db *sqlite3.Conn, _, schema, table string, arg ...string) (_ *bloom,
b.bytes = numBytes(nelem, b.prob)
err = db.DeclareVTab(
`CREATE TABLE x(present, word HIDDEN NOT NULL PRIMARY KEY) WITHOUT ROWID`)
err = db.DeclareVTab(vtab)
if err != nil {
return nil, err
}
@@ -115,15 +115,15 @@ func connect(db *sqlite3.Conn, _, schema, table string, arg ...string) (_ *bloom
storage: table + "_storage",
}
err = db.DeclareVTab(
`CREATE TABLE x(present, word HIDDEN NOT NULL PRIMARY KEY) WITHOUT ROWID`)
err = db.DeclareVTab(vtab)
if err != nil {
return nil, err
}
load, _, err := db.Prepare(fmt.Sprintf(
load, _, err := db.PrepareFlags(fmt.Sprintf(
`SELECT m/8, p, k FROM %s.%s WHERE rowid = 1`,
sqlite3.QuoteIdentifier(b.schema), sqlite3.QuoteIdentifier(b.storage)))
sqlite3.QuoteIdentifier(b.schema), sqlite3.QuoteIdentifier(b.storage)),
sqlite3.PREPARE_DONT_LOG)
if err != nil {
return nil, err
}
@@ -166,9 +166,10 @@ func (t *bloom) ShadowTables() {
}
func (t *bloom) Integrity(schema, table string, flags int) error {
load, _, err := t.db.Prepare(fmt.Sprintf(
load, _, err := t.db.PrepareFlags(fmt.Sprintf(
`SELECT typeof(data), length(data), p, n, m, k FROM %s.%s WHERE rowid = 1`,
sqlite3.QuoteIdentifier(t.schema), sqlite3.QuoteIdentifier(t.storage)))
sqlite3.QuoteIdentifier(t.schema), sqlite3.QuoteIdentifier(t.storage)),
sqlite3.PREPARE_DONT_LOG)
if err != nil {
return fmt.Errorf("bloom: %v", err) // can't wrap!
}

View File

@@ -56,7 +56,7 @@ func Register(db *sqlite3.Conn) error {
done.Add(key)
}
err := db.DeclareVTab(`CREATE TABLE x(id,depth,root HIDDEN,tablename HIDDEN,idcolumn HIDDEN,parentcolumn HIDDEN)`)
err := db.DeclareVTab(`CREATE TABLE x(id INT,depth INT,root HIDDEN,tablename TEXT HIDDEN,idcolumn TEXT HIDDEN,parentcolumn TEXT HIDDEN)`)
if err != nil {
return nil, err
}
@@ -202,7 +202,7 @@ func (c *cursor) Filter(idxNum int, idxStr string, arg ...sqlite3.Value) error {
sqlite3.QuoteIdentifier(column),
sqlite3.QuoteIdentifier(parent),
)
stmt, _, err := c.db.Prepare(sql)
stmt, _, err := c.db.PrepareFlags(sql, sqlite3.PREPARE_DONT_LOG)
if err != nil {
return err
}

View File

@@ -100,7 +100,7 @@ func RegisterFS(db *sqlite3.Conn, fsys fs.FS) error {
}
schema = getSchema(header, columns, row)
} else {
t.typs, err = getColumnAffinities(schema)
t.typs, err = getColumnAffinities(db, schema)
if err != nil {
return nil, err
}
@@ -254,19 +254,15 @@ func (c *cursor) Column(ctx sqlite3.Context, col int) error {
switch typ {
case numeric, integer:
if strings.TrimLeft(txt, "+-0123456789") == "" {
if i, err := strconv.ParseInt(txt, 10, 64); err == nil {
ctx.ResultInt64(i)
return nil
}
if i, err := strconv.ParseInt(txt, 10, 64); err == nil {
ctx.ResultInt64(i)
return nil
}
fallthrough
case real:
if strings.TrimLeft(txt, "+-.0123456789Ee") == "" {
if f, err := strconv.ParseFloat(txt, 64); err == nil {
ctx.ResultFloat(f)
return nil
}
if f, ok := sql3util.ParseFloat(txt); ok {
ctx.ResultFloat(f)
return nil
}
fallthrough
default:

View File

@@ -3,6 +3,8 @@ package csv
import (
"strings"
"github.com/ncruces/go-sqlite3"
"github.com/ncruces/go-sqlite3/internal/util"
"github.com/ncruces/go-sqlite3/util/sql3util"
)
@@ -16,7 +18,17 @@ const (
real affinity = 4
)
func getColumnAffinities(schema string) ([]affinity, error) {
func getColumnAffinities(db *sqlite3.Conn, schema string) ([]affinity, error) {
stmt, tail, err := db.PrepareFlags(schema,
sqlite3.PREPARE_DONT_LOG|sqlite3.PREPARE_NO_VTAB|sqlite3.PREPARE_FROM_DDL)
if err != nil {
return nil, err
}
stmt.Close()
if tail != "" {
return nil, util.TailErr
}
tab, err := sql3util.ParseTable(schema)
if err != nil {
return nil, err

View File

@@ -30,7 +30,7 @@ func RegisterFS(db *sqlite3.Conn, fsys fs.FS) error {
db.CreateFunction("readfile", 1, sqlite3.DIRECTONLY, readfile(fsys)),
db.CreateFunction("lsmode", 1, sqlite3.DETERMINISTIC, lsmode),
sqlite3.CreateModule(db, "fsdir", nil, func(db *sqlite3.Conn, _, _, _ string, _ ...string) (fsdir, error) {
err := db.DeclareVTab(`CREATE TABLE x(name,mode,mtime TIMESTAMP,data,path HIDDEN,dir HIDDEN)`)
err := db.DeclareVTab(`CREATE TABLE x(name TEXT,mode INT,mtime TIMESTAMP,data BLOB,path HIDDEN,dir HIDDEN)`)
if err == nil {
err = db.VTabConfig(sqlite3.VTAB_DIRECTONLY)
}

View File

@@ -43,11 +43,14 @@ func declare(db *sqlite3.Conn, _, _, _ string, arg ...string) (ret *table, err e
// Row key query.
t.scan = "SELECT * FROM\n" + arg[0]
stmt, _, err := db.Prepare(t.scan)
stmt, tail, err := db.PrepareFlags(t.scan, sqlite3.PREPARE_FROM_DDL)
if err != nil {
return nil, err
}
defer stmt.Close()
if tail != "" {
return nil, util.TailErr
}
t.keys = make([]string, stmt.ColumnCount())
for i := range t.keys {
@@ -55,15 +58,20 @@ func declare(db *sqlite3.Conn, _, _, _ string, arg ...string) (ret *table, err e
t.keys[i] = name
create.WriteString(sep)
create.WriteString(name)
create.WriteString(" ")
create.WriteString(stmt.ColumnDeclType(i))
sep = ","
}
stmt.Close()
// Column definition query.
stmt, _, err = db.Prepare("SELECT * FROM\n" + arg[1])
stmt, tail, err = db.PrepareFlags("SELECT * FROM\n"+arg[1], sqlite3.PREPARE_FROM_DDL)
if err != nil {
return nil, err
}
if tail != "" {
return nil, util.TailErr
}
if stmt.ColumnCount() != 2 {
return nil, util.ErrorString("pivot: column definition query expects 2 result columns")
@@ -71,17 +79,23 @@ func declare(db *sqlite3.Conn, _, _, _ string, arg ...string) (ret *table, err e
for stmt.Step() {
name := sqlite3.QuoteIdentifier(stmt.ColumnText(1))
t.cols = append(t.cols, stmt.ColumnValue(0).Dup())
create.WriteString(",")
create.WriteString(sep)
create.WriteString(name)
create.WriteString(" ")
create.WriteString(stmt.ColumnDeclType(1))
sep = ","
}
stmt.Close()
// Pivot cell query.
t.cell = "SELECT * FROM\n" + arg[2]
stmt, _, err = db.Prepare(t.cell)
stmt, tail, err = db.PrepareFlags(t.cell, sqlite3.PREPARE_FROM_DDL)
if err != nil {
return nil, err
}
if tail != "" {
return nil, util.TailErr
}
if stmt.ColumnCount() != 1 {
return nil, util.ErrorString("pivot: cell query expects 1 result columns")
@@ -182,7 +196,9 @@ func (c *cursor) Filter(idxNum int, idxStr string, arg ...sqlite3.Value) error {
return err
}
c.scan, _, err = c.table.db.Prepare(idxStr)
const prepflags = sqlite3.PREPARE_DONT_LOG | sqlite3.PREPARE_FROM_DDL
c.scan, _, err = c.table.db.PrepareFlags(idxStr, prepflags)
if err != nil {
return err
}
@@ -194,7 +210,7 @@ func (c *cursor) Filter(idxNum int, idxStr string, arg ...sqlite3.Value) error {
}
if c.cell == nil {
c.cell, _, err = c.table.db.Prepare(c.table.cell)
c.cell, _, err = c.table.db.PrepareFlags(c.table.cell, prepflags)
if err != nil {
return err
}

View File

@@ -35,10 +35,15 @@ func declare(db *sqlite3.Conn, _, _, _ string, arg ...string) (*table, error) {
sql := "SELECT * FROM\n" + arg[0]
stmt, _, err := db.PrepareFlags(sql, sqlite3.PREPARE_PERSISTENT)
stmt, tail, err := db.PrepareFlags(sql,
sqlite3.PREPARE_PERSISTENT|sqlite3.PREPARE_FROM_DDL)
if err != nil {
return nil, err
}
if tail != "" {
stmt.Close()
return nil, util.TailErr
}
var sep string
var str strings.Builder
@@ -129,7 +134,8 @@ func (t *table) Open() (_ sqlite3.VTabCursor, err error) {
if !t.inuse {
t.inuse = true
} else {
stmt, _, err = t.stmt.Conn().Prepare(t.sql)
stmt, _, err = t.stmt.Conn().PrepareFlags(t.sql,
sqlite3.PREPARE_DONT_LOG|sqlite3.PREPARE_FROM_DDL)
if err != nil {
return nil, err
}

12
go.mod
View File

@@ -5,18 +5,18 @@ go 1.24.0
require (
github.com/ncruces/julianday v1.0.0
github.com/ncruces/sort v0.1.6
github.com/ncruces/wbt v0.2.0
github.com/tetratelabs/wazero v1.9.0
golang.org/x/sys v0.37.0
github.com/ncruces/wbt v1.0.0
github.com/tetratelabs/wazero v1.11.0
golang.org/x/sys v0.40.0
)
require (
github.com/dchest/siphash v1.2.3 // ext/bloom
github.com/google/uuid v1.6.0 // ext/uuid
github.com/psanford/httpreadat v0.1.0 // example
golang.org/x/crypto v0.43.0 // vfs/adiantum vfs/xts
golang.org/x/sync v0.17.0 // test
golang.org/x/text v0.30.0 // ext/unicode
golang.org/x/crypto v0.47.0 // vfs/adiantum vfs/xts
golang.org/x/sync v0.19.0 // test
golang.org/x/text v0.33.0 // ext/unicode
lukechampine.com/adiantum v1.1.1 // vfs/adiantum
)

24
go.sum
View File

@@ -6,19 +6,19 @@ github.com/ncruces/julianday v1.0.0 h1:fH0OKwa7NWvniGQtxdJRxAgkBMolni2BjDHaWTxqt
github.com/ncruces/julianday v1.0.0/go.mod h1:Dusn2KvZrrovOMJuOt0TNXL6tB7U2E8kvza5fFc9G7g=
github.com/ncruces/sort v0.1.6 h1:TrsJfGRH1AoWoaeB4/+gCohot9+cA6u/INaH5agIhNk=
github.com/ncruces/sort v0.1.6/go.mod h1:obJToO4rYr6VWP0Uw5FYymgYGt3Br4RXcs/JdKaXAPk=
github.com/ncruces/wbt v0.2.0 h1:Q9zlKOBSZc7Yy/R2cGa35g6RKUUE3BjNIW3tfGC4F04=
github.com/ncruces/wbt v0.2.0/go.mod h1:DtF92amvMxH69EmBFUSFWRDAlo6hOEfoNQnClxj9C/c=
github.com/ncruces/wbt v1.0.0 h1:8iBE7UPjTLUpzu3/FCRjAmuQjWzgxo10RGBgt3ooLSc=
github.com/ncruces/wbt v1.0.0/go.mod h1:DtF92amvMxH69EmBFUSFWRDAlo6hOEfoNQnClxj9C/c=
github.com/psanford/httpreadat v0.1.0 h1:VleW1HS2zO7/4c7c7zNl33fO6oYACSagjJIyMIwZLUE=
github.com/psanford/httpreadat v0.1.0/go.mod h1:Zg7P+TlBm3bYbyHTKv/EdtSJZn3qwbPwpfZ/I9GKCRE=
github.com/tetratelabs/wazero v1.9.0 h1:IcZ56OuxrtaEz8UYNRHBrUa9bYeX9oVY93KspZZBf/I=
github.com/tetratelabs/wazero v1.9.0/go.mod h1:TSbcXCfFP0L2FGkRPxHphadXPjo1T6W+CseNNY7EkjM=
golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04=
golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0=
golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug=
golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ=
golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k=
golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM=
github.com/tetratelabs/wazero v1.11.0 h1:+gKemEuKCTevU4d7ZTzlsvgd1uaToIDtlQlmNbwqYhA=
github.com/tetratelabs/wazero v1.11.0/go.mod h1:eV28rsN8Q+xwjogd7f4/Pp4xFxO7uOGbLcD/LzB1wiU=
golang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8=
golang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A=
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=
golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE=
golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8=
lukechampine.com/adiantum v1.1.1 h1:4fp6gTxWCqpEbLy40ExiYDDED3oUNWx5cTqBCtPdZqA=
lukechampine.com/adiantum v1.1.1/go.mod h1:LrAYVnTYLnUtE/yMp5bQr0HstAf060YUF8nM0B6+rUw=

View File

@@ -3,7 +3,7 @@ module github.com/ncruces/go-sqlite3/gormlite
go 1.24.0
require (
github.com/ncruces/go-sqlite3 v0.29.1
github.com/ncruces/go-sqlite3 v0.30.3
gorm.io/gorm v1.31.1
)
@@ -11,7 +11,7 @@ require (
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect
github.com/ncruces/julianday v1.0.0 // indirect
github.com/tetratelabs/wazero v1.9.0 // indirect
golang.org/x/sys v0.37.0 // indirect
golang.org/x/text v0.30.0 // indirect
github.com/tetratelabs/wazero v1.11.0 // indirect
golang.org/x/sys v0.39.0 // indirect
golang.org/x/text v0.32.0 // indirect
)

View File

@@ -2,15 +2,15 @@ github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/ncruces/go-sqlite3 v0.29.1 h1:NIi8AISWBToRHyoz01FXiTNvU147Tqdibgj2tFzJCqM=
github.com/ncruces/go-sqlite3 v0.29.1/go.mod h1:PpccBNNhvjwUOwDQEn2gXQPFPTWdlromj0+fSkd5KSg=
github.com/ncruces/go-sqlite3 v0.30.3 h1:X/CgWW9GzmIAkEPrifhKqf0cC15DuOVxAJaHFTTAURQ=
github.com/ncruces/go-sqlite3 v0.30.3/go.mod h1:AxKu9sRxkludimFocbktlY6LiYSkxiI5gTA8r+os/Nw=
github.com/ncruces/julianday v1.0.0 h1:fH0OKwa7NWvniGQtxdJRxAgkBMolni2BjDHaWTxqt7M=
github.com/ncruces/julianday v1.0.0/go.mod h1:Dusn2KvZrrovOMJuOt0TNXL6tB7U2E8kvza5fFc9G7g=
github.com/tetratelabs/wazero v1.9.0 h1:IcZ56OuxrtaEz8UYNRHBrUa9bYeX9oVY93KspZZBf/I=
github.com/tetratelabs/wazero v1.9.0/go.mod h1:TSbcXCfFP0L2FGkRPxHphadXPjo1T6W+CseNNY7EkjM=
golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ=
golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k=
golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM=
github.com/tetratelabs/wazero v1.11.0 h1:+gKemEuKCTevU4d7ZTzlsvgd1uaToIDtlQlmNbwqYhA=
github.com/tetratelabs/wazero v1.11.0/go.mod h1:eV28rsN8Q+xwjogd7f4/Pp4xFxO7uOGbLcD/LzB1wiU=
golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU=
golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY=
gorm.io/gorm v1.31.1 h1:7CA8FTFz/gRfgqgpeKIBcervUn3xSyPUmr6B2WXJ7kg=
gorm.io/gorm v1.31.1/go.mod h1:XyQVbO2k6YkOis7C2437jSit3SsDK72s7n7rsSHd+Gs=

View File

@@ -5,8 +5,9 @@ package alloc
import (
"math"
"github.com/tetratelabs/wazero/experimental"
"golang.org/x/sys/unix"
"github.com/tetratelabs/wazero/experimental"
)
func NewMemory(cap, max uint64) experimental.LinearMemory {

View File

@@ -5,8 +5,9 @@ import (
"reflect"
"unsafe"
"github.com/tetratelabs/wazero/experimental"
"golang.org/x/sys/windows"
"github.com/tetratelabs/wazero/experimental"
)
func NewMemory(cap, max uint64) experimental.LinearMemory {

View File

@@ -7,8 +7,9 @@ import (
"os"
"unsafe"
"github.com/tetratelabs/wazero/api"
"golang.org/x/sys/unix"
"github.com/tetratelabs/wazero/api"
)
type mmapState struct {

View File

@@ -1,12 +1,10 @@
package util
import (
"context"
"os"
"reflect"
"unsafe"
"github.com/tetratelabs/wazero/api"
"golang.org/x/sys/windows"
)
@@ -16,14 +14,21 @@ type MappedRegion struct {
addr uintptr
}
func MapRegion(ctx context.Context, mod api.Module, f *os.File, offset int64, size int32) (*MappedRegion, error) {
h, err := windows.CreateFileMapping(windows.Handle(f.Fd()), nil, windows.PAGE_READWRITE, 0, 0, nil)
func MapRegion(f *os.File, offset int64, size int32) (*MappedRegion, error) {
maxSize := offset + int64(size)
h, err := windows.CreateFileMapping(
windows.Handle(f.Fd()), nil, windows.PAGE_READWRITE,
uint32(maxSize>>32), uint32(maxSize), nil)
if h == 0 {
return nil, err
}
const allocationGranularity = 64 * 1024
align := offset % allocationGranularity
offset -= align
a, err := windows.MapViewOfFile(h, windows.FILE_MAP_WRITE,
uint32(offset>>32), uint32(offset), uintptr(size))
uint32(offset>>32), uint32(offset), uintptr(size)+uintptr(align))
if a == 0 {
windows.CloseHandle(h)
return nil, err
@@ -32,9 +37,9 @@ func MapRegion(ctx context.Context, mod api.Module, f *os.File, offset int64, si
ret := &MappedRegion{Handle: h, addr: a}
// SliceHeader, although deprecated, avoids a go vet warning.
sh := (*reflect.SliceHeader)(unsafe.Pointer(&ret.Data))
sh.Data = a + uintptr(align)
sh.Len = int(size)
sh.Cap = int(size)
sh.Data = a
return ret, nil
}

5
internal/util/page.go Normal file
View File

@@ -0,0 +1,5 @@
package util
func ValidPageSize(s int) bool {
return s&(s-1) == 0 && 512 <= s && s <= 65536
}

142
internal/util/parse.go Normal file
View File

@@ -0,0 +1,142 @@
package util
import (
"strconv"
"strings"
"time"
)
func ParseBool(s string) (b, ok bool) {
if len(s) == 0 {
return false, false
}
if s[0] == '0' {
return false, true
}
if '1' <= s[0] && s[0] <= '9' {
return true, true
}
switch strings.ToLower(s) {
case "true", "yes", "on":
return true, true
case "false", "no", "off":
return false, true
}
return false, false
}
func ParseFloat(s string) (f float64, ok bool) {
if strings.TrimLeft(s, "+-.0123456789Ee") != "" {
return
}
f, err := strconv.ParseFloat(s, 64)
return f, err == nil
}
func ParseTimeShift(s string) (years, months, days int, duration time.Duration, ok bool) {
// Sign part: ±
neg := strings.HasPrefix(s, "-")
sign := neg || strings.HasPrefix(s, "+")
if sign {
s = s[1:]
}
if ok = len(s) >= 5; !ok {
return // !ok
}
defer func() {
if neg {
years = -years
months = -months
days = -days
duration = -duration
}
}()
// Date part: YYYY-MM-DD
if s[4] == '-' {
if ok = sign && len(s) >= 10 && s[7] == '-'; !ok {
return // !ok
}
if years, ok = parseInt(s[0:4], 0); !ok {
return // !ok
}
if months, ok = parseInt(s[5:7], 12); !ok {
return // !ok
}
if days, ok = parseInt(s[8:10], 31); !ok {
return // !ok
}
if len(s) == 10 {
return
}
if ok = s[10] == ' '; !ok {
return // !ok
}
s = s[11:]
}
// Time part: HH:MM
if ok = len(s) >= 5 && s[2] == ':'; !ok {
return // !ok
}
var hours, minutes int
if hours, ok = parseInt(s[0:2], 24); !ok {
return
}
if minutes, ok = parseInt(s[3:5], 60); !ok {
return
}
duration = time.Duration(hours)*time.Hour + time.Duration(minutes)*time.Minute
if len(s) == 5 {
return
}
if ok = len(s) >= 8 && s[5] == ':'; !ok {
return // !ok
}
// Seconds part: HH:MM:SS
var seconds int
if seconds, ok = parseInt(s[6:8], 60); !ok {
return
}
duration += time.Duration(seconds) * time.Second
if len(s) == 8 {
return
}
if ok = len(s) >= 10 && s[8] == '.'; !ok {
return // !ok
}
s = s[9:]
// Nanosecond part: HH:MM:SS.SSS
var nanos int
if nanos, ok = parseInt(s[0:min(9, len(s))], 0); !ok {
return
}
for i := len(s); i < 9; i++ {
nanos *= 10
}
duration += time.Duration(nanos)
// Subnanosecond part.
if len(s) > 9 {
_, ok = parseInt(s[9:], 0)
}
return
}
func parseInt(s string, max int) (i int, _ bool) {
for _, r := range []byte(s) {
r -= '0'
if r > 9 {
return
}
i = i*10 + int(r)
}
return i, max == 0 || i < max
}

144
internal/util/parse_test.go Normal file
View File

@@ -0,0 +1,144 @@
package util_test
import (
"testing"
"time"
"github.com/ncruces/go-sqlite3"
_ "github.com/ncruces/go-sqlite3/embed"
"github.com/ncruces/go-sqlite3/internal/util"
)
func TestParseBool(t *testing.T) {
tests := []struct {
str string
val bool
ok bool
}{
{"", false, false},
{"0", false, true},
{"1", true, true},
{"9", true, true},
{"T", false, false},
{"true", true, true},
{"FALSE", false, true},
{"false?", false, false},
}
for _, tt := range tests {
t.Run(tt.str, func(t *testing.T) {
gotVal, gotOK := util.ParseBool(tt.str)
if gotVal != tt.val || gotOK != tt.ok {
t.Errorf("ParseBool(%q) = (%v, %v) want (%v, %v)", tt.str, gotVal, gotOK, tt.val, tt.ok)
}
})
}
}
func TestParseTimeShift(t *testing.T) {
epoch := time.Unix(0, 0)
tests := []struct {
str string
val time.Time
ok bool
}{
{"", epoch, false},
{"0001-11-30", epoch, false},
{"+_001-11-30", epoch, false},
{"+0001-_1-30", epoch.AddDate(1, 0, 0), false},
{"+0001-11-_0", epoch.AddDate(1, 11, 0), false},
{"+0001-11-30", epoch.AddDate(1, 11, 30), true},
{"-0001-11-30", epoch.AddDate(-1, -11, -30), true},
{"+0001-11-30T", epoch.AddDate(1, 11, 30), false},
{"+0001-11-30 12", epoch.AddDate(1, 11, 30), false},
{"+0001-11-30 _2:30", epoch.AddDate(1, 11, 30), false},
{"+0001-11-30 12:_0", epoch.AddDate(1, 11, 30), false},
{"+0001-11-30 12:30", epoch.AddDate(1, 11, 30).Add(12*time.Hour + 30*time.Minute), true},
{"+0001-11-30 12:30:", epoch.AddDate(1, 11, 30).Add(12*time.Hour + 30*time.Minute), false},
{"+0001-11-30 12:30:_0", epoch.AddDate(1, 11, 30).Add(12*time.Hour + 30*time.Minute), false},
{"+0001-11-30 12:30:59", epoch.AddDate(1, 11, 30).Add(12*time.Hour + 30*time.Minute + 59*time.Second), true},
{"+0001-11-30 12:30:59.", epoch.AddDate(1, 11, 30).Add(12*time.Hour + 30*time.Minute + 59*time.Second), false},
{"+0001-11-30 12:30:59._", epoch.AddDate(1, 11, 30).Add(12*time.Hour + 30*time.Minute + 59*time.Second), false},
{"+0001-11-30 12:30:59.1", epoch.AddDate(1, 11, 30).Add(12*time.Hour + 30*time.Minute + 59*time.Second + 100*time.Millisecond), true},
{"+0001-11-30 12:30:59.123456789_", epoch.AddDate(1, 11, 30).Add(12*time.Hour + 30*time.Minute + 59*time.Second + 123456789), false},
{"+0001-11-30 12:30:59.1234567890", epoch.AddDate(1, 11, 30).Add(12*time.Hour + 30*time.Minute + 59*time.Second + 123456789), true},
{"-12:30:59.1234567890", epoch.Add(-12*time.Hour - 30*time.Minute - 59*time.Second - 123456789), true},
}
for _, tt := range tests {
t.Run(tt.str, func(t *testing.T) {
years, months, days, duration, gotOK := util.ParseTimeShift(tt.str)
gotVal := epoch.AddDate(years, months, days).Add(duration)
if !gotVal.Equal(tt.val) || gotOK != tt.ok {
t.Errorf("ParseTimeShift(%q) = (%v, %v) want (%v, %v)", tt.str, gotVal, gotOK, tt.val, tt.ok)
}
})
}
}
func FuzzParseTimeShift(f *testing.F) {
f.Add("")
f.Add("0001-12-30")
f.Add("+_001-12-30")
f.Add("+0001-_2-30")
f.Add("+0001-12-_0")
f.Add("+0001-12-30")
f.Add("-0001-12-30")
f.Add("+0001-12-30T")
f.Add("+0001-12-30 12")
f.Add("+0001-12-30 _2:30")
f.Add("+0001-12-30 12:_0")
f.Add("+0001-12-30 12:30")
f.Add("+0001-12-30 12:30:")
f.Add("+0001-12-30 12:30:_0")
f.Add("+0001-12-30 12:30:60")
f.Add("+0001-12-30 12:30:60.")
f.Add("+0001-12-30 12:30:60._")
f.Add("+0001-12-30 12:30:60.1")
f.Add("+0001-12-30 12:30:60.123456789_")
f.Add("+0001-12-30 12:30:60.1234567890")
f.Add("-12:30:60.1234567890")
c, err := sqlite3.Open(":memory:")
if err != nil {
f.Fatal(err)
}
defer c.Close()
s, _, err := c.Prepare(`SELECT julianday('00:00', ?)`)
if err != nil {
f.Fatal(err)
}
defer s.Close()
// Default SQLite date.
epoch := time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC)
f.Fuzz(func(t *testing.T, str string) {
years, months, days, duration, ok := util.ParseTimeShift(str)
// Account for a full 400 year cycle.
if years < -200 || years > +200 {
t.Skip()
}
// SQLite only tracks milliseconds.
if duration != duration.Truncate(time.Millisecond) {
t.Skip()
}
if ok {
s.Reset()
s.BindText(1, str)
if !s.Step() {
t.Fail()
}
got := epoch.AddDate(years, months, days).Add(duration)
// Julian day introduces floating point inaccuracy.
want := s.ColumnTime(0, sqlite3.TimeFormatJulianDay)
want = want.Round(time.Millisecond)
if !got.Equal(want) {
t.Fatalf("with %q, got %v, want %v", str, got, want)
}
}
})
}

View File

@@ -14,8 +14,8 @@ var (
// https://sqlite.org/c3ref/auto_extension.html
func AutoExtension(entryPoint func(*Conn) error) {
extRegistryMtx.Lock()
defer extRegistryMtx.Unlock()
extRegistry = append(extRegistry, entryPoint)
extRegistryMtx.Unlock()
}
func initExtensions(c *Conn) error {

View File

@@ -5,12 +5,14 @@ import (
"context"
"math/bits"
"os"
"runtime"
"strings"
"sync"
"unsafe"
"github.com/tetratelabs/wazero"
"github.com/tetratelabs/wazero/api"
"github.com/tetratelabs/wazero/experimental"
"github.com/ncruces/go-sqlite3/internal/util"
"github.com/ncruces/go-sqlite3/vfs"
@@ -79,7 +81,9 @@ func compileSQLite() {
return
}
instance.compiled, instance.err = instance.runtime.CompileModule(ctx, bin)
instance.compiled, instance.err = instance.runtime.CompileModule(
experimental.WithCompilationWorkers(ctx, runtime.GOMAXPROCS(0)/4),
bin)
}
type sqlite struct {

View File

@@ -3,11 +3,11 @@ set -euo pipefail
cd -P -- "$(dirname -- "$0")"
curl -#OL "https://sqlite.org/2025/sqlite-autoconf-3510000.tar.gz"
curl -#OL "https://sqlite.org/2025/sqlite-autoconf-3510100.tar.gz"
# Verify download.
if hash=$(openssl dgst -sha3-256 sqlite-autoconf-*.tar.gz); then
if ! [[ $hash =~ fa52f9cc74dbca004aa650ae698036a3350611f672649e165078f4eae21d6a2e ]]; then
if ! [[ $hash =~ 9b2b1e73f577def1d5b75c5541555a7f42e6e073ad19f7a9118478389c9bbd9b ]]; then
echo $hash
exit 1
fi
@@ -23,7 +23,7 @@ mv sqlite-*/sqlite3.h .
mv sqlite-*/sqlite3ext.h .
rm -r sqlite-*
GITHUB_TAG="https://github.com/sqlite/sqlite/raw/version-3.51.0"
GITHUB_TAG="https://github.com/sqlite/sqlite/raw/version-3.51.1"
mkdir -p ext/
cd ext/

View File

@@ -39,12 +39,10 @@ EOF
-Wl,--export=strcspn \
-Wl,--export=strlen \
-Wl,--export=strrchr \
-Wl,--export=strspn \
-Wl,--export=qsort
-Wl,--export=strspn
"$BINARYEN/wasm-ctor-eval" -g -c _initialize libc.wasm -o libc.tmp
"$BINARYEN/wasm-opt" -g libc.tmp -o libc.wasm \
--low-memory-unused --generate-global-effects --converge -O3 \
"$BINARYEN/wasm-opt" -g libc.tmp -o libc.wasm --converge -O3 \
--enable-mutable-globals --enable-nontrapping-float-to-int \
--enable-simd --enable-bulk-memory --enable-sign-ext \
--enable-reference-types --enable-multivalue \

Binary file not shown.

View File

@@ -1,13 +1,11 @@
(module $libc.wasm
(type $0 (func (param i32 i32) (result i32)))
(type $1 (func (param i32 i32 i32) (result i32)))
(type $2 (func (param i32 i32 i32 i32)))
(type $3 (func (param i32) (result i32)))
(type $0 (func (param i32 i32 i32) (result i32)))
(type $1 (func (param i32 i32) (result i32)))
(type $2 (func (param i32) (result i32)))
(memory $0 256)
(data $0 (i32.const 4096) "\01")
(table $0 1 1 funcref)
(data $.data (i32.const 4097) "\10\00\00\01\00\00\00\00\00\00\00\0c\10\00\00\0c\10\00\00\0c\10")
(data $.data.1 (i32.const 4157) "\10\00\00\00\10")
(export "memory" (memory $0))
(export "qsort" (func $qsort))
(export "memset" (func $memset))
(export "memcpy" (func $memcpy))
(export "memmove" (func $memcpy))
@@ -20,423 +18,6 @@
(export "strrchr" (func $strrchr))
(export "strspn" (func $strspn))
(export "strcspn" (func $strcspn))
(func $qsort (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32)
(local $4 i32)
(local $5 i32)
(local $6 i32)
(local $7 i32)
(local $8 i32)
(local $9 i32)
(local $10 i32)
(local $11 i32)
(local $12 i32)
(local $13 i32)
(local $14 i32)
(local $15 i32)
(local $16 i32)
(local $17 i32)
(local $18 i32)
(local $19 i32)
(local $20 v128)
(local $scratch i32)
(if
(i32.ge_u
(local.get $1)
(i32.const 2)
)
(then
(local.set $14
(i32.mul
(local.get $1)
(local.get $2)
)
)
(local.set $15
(i32.and
(local.get $2)
(i32.const 15)
)
)
(local.set $8
(i32.and
(local.get $2)
(i32.const -16)
)
)
(local.set $16
(i32.add
(local.get $0)
(local.get $2)
)
)
(local.set $17
(i32.lt_u
(local.get $2)
(i32.const 16)
)
)
(loop $label5
(local.set $7
(i32.eq
(local.get $1)
(i32.const 2)
)
)
(local.set $18
(i32.add
(local.get $0)
(i32.mul
(local.get $2)
(i32.add
(local.tee $13
(select
(i32.const 1)
(local.tee $1
(i32.wrap_i64
(i64.div_u
(i64.sub
(i64.mul
(i64.extend_i32_u
(local.get $1)
)
(i64.const 5)
)
(i64.const 1)
)
(i64.const 11)
)
)
)
(local.get $7)
)
)
(i32.const 1)
)
)
)
)
(local.set $10
(local.tee $9
(i32.mul
(local.get $2)
(local.get $13)
)
)
)
(loop $label4
(block $block
(br_if $block
(i32.gt_u
(local.tee $4
(i32.sub
(local.get $10)
(local.get $9)
)
)
(local.get $10)
)
)
(loop $label3
(br_if $block
(i32.le_s
(call_indirect $0 (type $0)
(local.tee $5
(i32.add
(local.get $0)
(local.tee $11
(local.get $4)
)
)
)
(local.tee $4
(i32.add
(local.get $5)
(local.get $9)
)
)
(local.get $3)
)
(i32.const 0)
)
)
(block $block2
(block $block3
(block $block1
(br_if $block1
(local.get $17)
)
(br_if $block1
(i32.and
(i32.lt_u
(local.get $4)
(i32.add
(local.get $11)
(local.get $16)
)
)
(i32.gt_u
(i32.add
(local.get $11)
(local.get $18)
)
(local.get $5)
)
)
)
(local.set $4
(i32.add
(local.get $4)
(local.get $8)
)
)
(local.set $6
(i32.add
(local.get $5)
(local.get $8)
)
)
(local.set $7
(local.get $8)
)
(loop $label
(local.set $20
(v128.load align=1
(local.get $5)
)
)
(v128.store align=1
(local.get $5)
(v128.load align=1
(local.tee $12
(i32.add
(local.get $5)
(local.get $9)
)
)
)
)
(v128.store align=1
(local.get $12)
(local.get $20)
)
(local.set $5
(i32.add
(local.get $5)
(i32.const 16)
)
)
(br_if $label
(local.tee $7
(i32.sub
(local.get $7)
(i32.const 16)
)
)
)
)
(local.set $7
(local.get $15)
)
(br_if $block2
(i32.eq
(local.get $2)
(local.get $8)
)
)
(br $block3)
)
(local.set $6
(local.get $5)
)
(local.set $7
(local.get $2)
)
)
(br_if $block2
(i32.lt_u
(block (result i32)
(local.set $scratch
(i32.sub
(local.get $7)
(i32.const 1)
)
)
(if
(local.tee $5
(i32.and
(local.get $7)
(i32.const 3)
)
)
(then
(local.set $7
(i32.and
(local.get $7)
(i32.const -4)
)
)
(loop $label1
(local.set $19
(i32.load8_u
(local.get $6)
)
)
(i32.store8
(local.get $6)
(i32.load8_u
(local.get $4)
)
)
(i32.store8
(local.get $4)
(local.get $19)
)
(local.set $4
(i32.add
(local.get $4)
(i32.const 1)
)
)
(local.set $6
(i32.add
(local.get $6)
(i32.const 1)
)
)
(br_if $label1
(local.tee $5
(i32.sub
(local.get $5)
(i32.const 1)
)
)
)
)
)
)
(local.get $scratch)
)
(i32.const 3)
)
)
(loop $label2
(local.set $5
(i32.load8_u
(local.get $6)
)
)
(i32.store8
(local.get $6)
(i32.load8_u
(local.get $4)
)
)
(i32.store8
(local.get $4)
(local.get $5)
)
(local.set $5
(i32.load8_u offset=1
(local.get $6)
)
)
(i32.store8 offset=1
(local.get $6)
(i32.load8_u offset=1
(local.get $4)
)
)
(i32.store8 offset=1
(local.get $4)
(local.get $5)
)
(local.set $5
(i32.load8_u offset=2
(local.get $6)
)
)
(i32.store8 offset=2
(local.get $6)
(i32.load8_u offset=2
(local.get $4)
)
)
(i32.store8 offset=2
(local.get $4)
(local.get $5)
)
(local.set $5
(i32.load8_u offset=3
(local.get $6)
)
)
(i32.store8 offset=3
(local.get $6)
(i32.load8_u offset=3
(local.get $4)
)
)
(i32.store8 offset=3
(local.get $4)
(local.get $5)
)
(local.set $6
(i32.add
(local.get $6)
(i32.const 4)
)
)
(local.set $4
(i32.add
(local.get $4)
(i32.const 4)
)
)
(br_if $label2
(local.tee $7
(i32.sub
(local.get $7)
(i32.const 4)
)
)
)
)
)
(br_if $label3
(i32.le_u
(local.tee $4
(i32.sub
(local.get $11)
(local.get $9)
)
)
(local.get $11)
)
)
)
)
(br_if $label4
(i32.lt_u
(local.tee $10
(i32.add
(local.get $2)
(local.get $10)
)
)
(local.get $14)
)
)
)
(br_if $label5
(i32.ge_u
(local.get $13)
(i32.const 2)
)
)
)
)
)
)
(func $memset (param $0 i32) (param $1 i32) (param $2 i32) (result i32)
(if
(local.get $2)
@@ -1493,33 +1074,243 @@
(local $2 i32)
(local $3 i32)
(local $4 i32)
(local $5 i32)
(local $5 v128)
(local $6 v128)
(local $7 v128)
(local $8 v128)
(local $9 v128)
(block $block
(if
(local.tee $3
(i32.load8_u
(local.get $1)
(block $block2
(block $block
(br_if $block
(i32.eqz
(local.tee $3
(i32.load8_u
(local.get $1)
)
)
)
)
(then
(br_if $block
(br_if $block
(i32.eqz
(i32.load8_u offset=1
(local.get $1)
)
)
)
(loop $label
(v128.store
(i32.const 4080)
(local.get $6)
)
(i32.store8
(i32.or
(local.tee $3
(i32.and
(local.tee $2
(i32.load8_u
(local.get $1)
)
)
(i32.const 15)
)
)
(i32.const 4080)
)
(i32.or
(i32.load8_u
(i32.or
(local.get $3)
(i32.const 4080)
)
)
(i32.shl
(i32.const 1)
(i32.sub
(local.tee $4
(i32.shr_u
(local.get $2)
(i32.const 4)
)
)
(i32.const 8)
)
)
)
)
(v128.store
(i32.const 4064)
(local.get $5)
)
(i32.store8
(local.tee $3
(i32.or
(local.get $3)
(i32.const 4064)
)
)
(i32.or
(i32.load8_u
(local.get $3)
)
(i32.shl
(i32.const 1)
(local.get $4)
)
)
)
(local.set $1
(i32.add
(local.get $1)
(i32.const 1)
)
)
(local.set $6
(v128.load
(i32.const 4080)
)
)
(local.set $5
(v128.load
(i32.const 4064)
)
)
(br_if $label
(local.get $2)
)
)
(block $block1
(if
(v128.any_true
(local.tee $7
(v128.and
(v128.or
(i8x16.swizzle
(local.get $6)
(v128.xor
(local.tee $8
(v128.and
(local.tee $7
(v128.load
(local.tee $2
(i32.and
(local.get $0)
(i32.const -16)
)
)
)
)
(v128.const i32x4 0x8f8f8f8f 0x8f8f8f8f 0x8f8f8f8f 0x8f8f8f8f)
)
)
(v128.const i32x4 0x80808080 0x80808080 0x80808080 0x80808080)
)
)
(i8x16.swizzle
(local.get $5)
(local.get $8)
)
)
(i8x16.swizzle
(v128.const i32x4 0x08040201 0x80402010 0x08040201 0x80402010)
(i8x16.shr_u
(local.get $7)
(i32.const 4)
)
)
)
)
)
(then
(br_if $block1
(local.tee $1
(i32.and
(i32.xor
(i8x16.bitmask
(i8x16.eq
(local.get $7)
(v128.const i32x4 0x00000000 0x00000000 0x00000000 0x00000000)
)
)
(i32.const 65535)
)
(i32.shl
(i32.const -1)
(i32.and
(local.get $0)
(i32.const 15)
)
)
)
)
)
)
)
(loop $label1
(br_if $label1
(i32.eqz
(v128.any_true
(local.tee $7
(v128.and
(v128.or
(i8x16.swizzle
(local.get $6)
(v128.xor
(local.tee $8
(v128.and
(local.tee $7
(v128.load
(local.tee $2
(i32.add
(local.get $2)
(i32.const 16)
)
)
)
)
(v128.const i32x4 0x8f8f8f8f 0x8f8f8f8f 0x8f8f8f8f 0x8f8f8f8f)
)
)
(v128.const i32x4 0x80808080 0x80808080 0x80808080 0x80808080)
)
)
(i8x16.swizzle
(local.get $5)
(local.get $8)
)
)
(i8x16.swizzle
(v128.const i32x4 0x08040201 0x80402010 0x08040201 0x80402010)
(i8x16.shr_u
(local.get $7)
(i32.const 4)
)
)
)
)
)
)
)
)
(local.set $1
(i32.xor
(i8x16.bitmask
(i8x16.eq
(local.get $7)
(v128.const i32x4 0x00000000 0x00000000 0x00000000 0x00000000)
)
)
(i32.const 65535)
)
)
)
(br $block2)
)
(block $block1
(block $block3
(if
(v128.any_true
(local.tee $6
(local.tee $5
(v128.or
(i8x16.eq
(local.tee $7
(local.tee $6
(v128.load
(local.tee $2
(i32.and
@@ -1532,8 +1323,8 @@
(v128.const i32x4 0x00000000 0x00000000 0x00000000 0x00000000)
)
(i8x16.eq
(local.get $7)
(local.tee $7
(local.get $6)
(local.tee $6
(i8x16.splat
(local.get $3)
)
@@ -1543,11 +1334,11 @@
)
)
(then
(br_if $block1
(br_if $block3
(local.tee $1
(i32.and
(i8x16.bitmask
(local.get $6)
(local.get $5)
)
(i32.shl
(i32.const -1)
@@ -1561,14 +1352,14 @@
)
)
)
(loop $label
(br_if $label
(loop $label2
(br_if $label2
(i32.eqz
(v128.any_true
(local.tee $6
(local.tee $5
(v128.or
(i8x16.eq
(local.tee $6
(local.tee $5
(v128.load
(local.tee $2
(i32.add
@@ -1581,8 +1372,8 @@
(v128.const i32x4 0x00000000 0x00000000 0x00000000 0x00000000)
)
(i8x16.eq
(local.get $5)
(local.get $6)
(local.get $7)
)
)
)
@@ -1592,230 +1383,10 @@
)
(local.set $1
(i8x16.bitmask
(local.get $6)
)
)
)
(return
(i32.add
(i32.ctz
(local.get $1)
)
(i32.sub
(local.get $2)
(local.get $0)
)
)
)
)
(local.set $4
(i32.and
(local.get $0)
(i32.const 15)
)
)
(loop $label1
(v128.store
(i32.const 4080)
(local.get $7)
)
(i32.store8
(i32.or
(local.tee $3
(i32.and
(local.tee $2
(i32.load8_u
(local.get $1)
)
)
(i32.const 15)
)
)
(i32.const 4080)
)
(i32.or
(i32.load8_u
(i32.or
(local.get $3)
(i32.const 4080)
)
)
(i32.shl
(i32.const 1)
(i32.sub
(local.tee $5
(i32.shr_u
(local.get $2)
(i32.const 4)
)
)
(i32.const 8)
)
)
)
)
(v128.store
(i32.const 4064)
(local.get $6)
)
(i32.store8
(local.tee $3
(i32.or
(local.get $3)
(i32.const 4064)
)
)
(i32.or
(i32.load8_u
(local.get $3)
)
(i32.shl
(i32.const 1)
(local.get $5)
)
)
)
(local.set $1
(i32.add
(local.get $1)
(i32.const 1)
)
)
(local.set $7
(v128.load
(i32.const 4080)
)
)
(local.set $6
(v128.load
(i32.const 4064)
)
)
(br_if $label1
(local.get $2)
)
)
(block $block2
(if
(v128.any_true
(local.tee $8
(v128.and
(v128.or
(i8x16.swizzle
(local.get $7)
(v128.xor
(local.tee $9
(v128.and
(local.tee $8
(v128.load
(local.tee $2
(i32.and
(local.get $0)
(i32.const -16)
)
)
)
)
(v128.const i32x4 0x8f8f8f8f 0x8f8f8f8f 0x8f8f8f8f 0x8f8f8f8f)
)
)
(v128.const i32x4 0x80808080 0x80808080 0x80808080 0x80808080)
)
)
(i8x16.swizzle
(local.get $6)
(local.get $9)
)
)
(i8x16.swizzle
(v128.const i32x4 0x08040201 0x80402010 0x08040201 0x80402010)
(i8x16.shr_u
(local.get $8)
(i32.const 4)
)
)
)
)
)
(then
(br_if $block2
(local.tee $1
(i32.and
(i32.xor
(i8x16.bitmask
(i8x16.eq
(local.get $8)
(v128.const i32x4 0x00000000 0x00000000 0x00000000 0x00000000)
)
)
(i32.const 65535)
)
(i32.shl
(i32.const -1)
(local.get $4)
)
)
)
)
)
)
(loop $label2
(br_if $label2
(i32.eqz
(v128.any_true
(local.tee $8
(v128.and
(v128.or
(i8x16.swizzle
(local.get $7)
(v128.xor
(local.tee $9
(v128.and
(local.tee $8
(v128.load
(local.tee $2
(i32.add
(local.get $2)
(i32.const 16)
)
)
)
)
(v128.const i32x4 0x8f8f8f8f 0x8f8f8f8f 0x8f8f8f8f 0x8f8f8f8f)
)
)
(v128.const i32x4 0x80808080 0x80808080 0x80808080 0x80808080)
)
)
(i8x16.swizzle
(local.get $6)
(local.get $9)
)
)
(i8x16.swizzle
(v128.const i32x4 0x08040201 0x80402010 0x08040201 0x80402010)
(i8x16.shr_u
(local.get $8)
(i32.const 4)
)
)
)
)
)
)
)
)
(local.set $1
(i32.xor
(i8x16.bitmask
(i8x16.eq
(local.get $8)
(v128.const i32x4 0x00000000 0x00000000 0x00000000 0x00000000)
)
)
(i32.const 65535)
)
)
)
(i32.add
(i32.ctz

View File

@@ -26,8 +26,8 @@ elif [[ "$OSTYPE" == "darwin"* ]]; then
fi
fi
WASI_SDK="https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-27/wasi-sdk-27.0-$WASI_SDK.tar.gz"
BINARYEN="https://github.com/WebAssembly/binaryen/releases/download/version_124/binaryen-version_124-$BINARYEN.tar.gz"
WASI_SDK="https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-29/wasi-sdk-29.0-$WASI_SDK.tar.gz"
BINARYEN="https://github.com/WebAssembly/binaryen/releases/download/version_125/binaryen-version_125-$BINARYEN.tar.gz"
# Download tools
mkdir -p "$ROOT/tools"

View File

@@ -189,13 +189,23 @@ func TestConn_FileControl(t *testing.T) {
}
})
t.Run("FCNTL_VFSNAME", func(t *testing.T) {
o, err := db.FileControl("", sqlite3.FCNTL_VFSNAME)
if err != nil {
t.Fatal(err)
}
if o != "os" {
t.Errorf(`got %q, want "os"`, o)
}
})
t.Run("FCNTL_VFS_POINTER", func(t *testing.T) {
o, err := db.FileControl("", sqlite3.FCNTL_VFS_POINTER)
if err != nil {
t.Fatal(err)
}
if o != vfs.Find("os") {
t.Errorf("got %v, want os", o)
t.Errorf(`got %v, want "os"`, o)
}
})

14
time.go
View File

@@ -157,11 +157,13 @@ func (f TimeFormat) Decode(v any) (time.Time, error) {
case TimeFormatUnix, TimeFormatUnixFrac:
if s, ok := v.(string); ok {
f, err := strconv.ParseFloat(s, 64)
if err != nil {
return time.Time{}, err
if i, err := strconv.ParseInt(s, 10, 64); err == nil {
v = i
} else if f, ok := util.ParseFloat(s); ok {
v = f
} else {
return time.Time{}, util.TimeErr
}
v = f
}
switch v := v.(type) {
case float64:
@@ -234,8 +236,8 @@ func (f TimeFormat) Decode(v any) (time.Time, error) {
v = i
break
}
f, err := strconv.ParseFloat(s, 64)
if err == nil {
f, ok := util.ParseFloat(s)
if ok {
v = f
break
}

View File

@@ -49,6 +49,15 @@ const (
DEFTYPE_NOTDEFERRABLE_INITIALLY_IMMEDIATE
)
type ConstraintType uint32
const (
TABLECONSTRAINT_PRIMARYKEY ConstraintType = iota
TABLECONSTRAINT_UNIQUE
TABLECONSTRAINT_CHECK
TABLECONSTRAINT_FOREIGNKEY
)
type StatementType uint32
const (
@@ -59,3 +68,11 @@ const (
ALTER_ADD_COLUMN
ALTER_DROP_COLUMN
)
type GenType uint32
const (
GENTYPE_NONE GenType = iota
GENTYPE_STORED
GENTYPE_VIRTUA
)

View File

@@ -8,6 +8,7 @@ import (
"github.com/tetratelabs/wazero"
"github.com/tetratelabs/wazero/api"
"github.com/ncruces/go-sqlite3"
"github.com/ncruces/go-sqlite3/internal/util"
)
@@ -29,6 +30,10 @@ var (
// [CREATE]: https://sqlite.org/lang_createtable.html
// [ALTER TABLE]: https://sqlite.org/lang_altertable.html
func ParseTable(sql string) (_ *Table, err error) {
if len(sql) > 8192 {
return nil, sqlite3.TOOBIG
}
once.Do(func() {
ctx := context.Background()
cfg := wazero.NewRuntimeConfigInterpreter()
@@ -81,6 +86,7 @@ type Table struct {
IsWithoutRowID bool
IsStrict bool
Columns []Column
Constraints []TableConstraint
Type StatementType
CurrentName string
NewName string
@@ -96,9 +102,16 @@ func (t *Table) load(mod api.Module, ptr uint32, sql string) {
t.IsWithoutRowID = loadBool(mod, ptr+26)
t.IsStrict = loadBool(mod, ptr+27)
t.Columns = loadSlice(mod, ptr+28, func(ptr uint32, ret *Column) {
t.Columns = loadSlice(mod, ptr+28, func(ptr uint32, ret *Column) uint32 {
p, _ := mod.Memory().ReadUint32Le(ptr)
ret.load(mod, p, sql)
return 4
})
t.Constraints = loadSlice(mod, ptr+36, func(ptr uint32, ret *TableConstraint) uint32 {
p, _ := mod.Memory().ReadUint32Le(ptr)
ret.load(mod, p, sql)
return 4
})
t.Type = loadEnum[StatementType](mod, ptr+44)
@@ -125,6 +138,8 @@ type Column struct {
DefaultExpr string
CollateName string
ForeignKeyClause *ForeignKey
GeneratedExpr string
GeneratedType GenType
}
func (c *Column) load(mod api.Module, ptr uint32, sql string) {
@@ -152,8 +167,52 @@ func (c *Column) load(mod api.Module, ptr uint32, sql string) {
c.ForeignKeyClause = &ForeignKey{}
c.ForeignKeyClause.load(mod, ptr, sql)
}
c.GeneratedExpr = loadString(mod, ptr+88, sql)
c.GeneratedType = loadEnum[GenType](mod, ptr+96)
}
// TableConstraint holds metadata about a table key constraint.
type TableConstraint struct {
Type ConstraintType
Name string
// Type is TABLECONSTRAINT_PRIMARYKEY or TABLECONSTRAINT_UNIQUE
IndexedColumns []IdxColumn
ConflictClause ConflictClause
IsAutoIncrement bool
// Type is TABLECONSTRAINT_CHECK
Expr string
// Type is TABLECONSTRAINT_FOREIGNKEY
ForeignKeyNames []string
ForeignKeyClause *ForeignKey
}
func (c *TableConstraint) load(mod api.Module, ptr uint32, sql string) {
c.Type = loadEnum[ConstraintType](mod, ptr+0)
c.Name = loadString(mod, ptr+4, sql)
switch c.Type {
case TABLECONSTRAINT_PRIMARYKEY, TABLECONSTRAINT_UNIQUE:
c.IndexedColumns = loadSlice(mod, ptr+12, func(ptr uint32, ret *IdxColumn) uint32 {
ret.load(mod, ptr, sql)
return 20
})
c.ConflictClause = loadEnum[ConflictClause](mod, ptr+20)
c.IsAutoIncrement = loadBool(mod, ptr+24)
case TABLECONSTRAINT_CHECK:
c.Expr = loadString(mod, ptr+12, sql)
case TABLECONSTRAINT_FOREIGNKEY:
c.ForeignKeyNames = loadSlice(mod, ptr+12, func(ptr uint32, ret *string) uint32 {
*ret = loadString(mod, ptr, sql)
return 8
})
if ptr, _ := mod.Memory().ReadUint32Le(ptr + 20); ptr != 0 {
c.ForeignKeyClause = &ForeignKey{}
c.ForeignKeyClause.load(mod, ptr, sql)
}
}
}
// ForeignKey holds metadata about a foreign key constraint.
type ForeignKey struct {
Table string
Columns []string
@@ -166,8 +225,9 @@ type ForeignKey struct {
func (f *ForeignKey) load(mod api.Module, ptr uint32, sql string) {
f.Table = loadString(mod, ptr+0, sql)
f.Columns = loadSlice(mod, ptr+8, func(ptr uint32, ret *string) {
f.Columns = loadSlice(mod, ptr+8, func(ptr uint32, ret *string) uint32 {
*ret = loadString(mod, ptr, sql)
return 8
})
f.OnDelete = loadEnum[FKAction](mod, ptr+16)
@@ -176,6 +236,19 @@ func (f *ForeignKey) load(mod api.Module, ptr uint32, sql string) {
f.Deferrable = loadEnum[FKDefType](mod, ptr+32)
}
// IdxColumn holds metadata about an indexed column.
type IdxColumn struct {
Name string
CollateName string
Order OrderClause
}
func (c *IdxColumn) load(mod api.Module, ptr uint32, sql string) {
c.Name = loadString(mod, ptr+0, sql)
c.CollateName = loadString(mod, ptr+8, sql)
c.Order = loadEnum[OrderClause](mod, ptr+16)
}
func loadString(mod api.Module, ptr uint32, sql string) string {
off, _ := mod.Memory().ReadUint32Le(ptr + 0)
if off == 0 {
@@ -185,7 +258,7 @@ func loadString(mod api.Module, ptr uint32, sql string) string {
return sql[off-sqlp : off+len-sqlp]
}
func loadSlice[T any](mod api.Module, ptr uint32, fn func(uint32, *T)) []T {
func loadSlice[T any](mod api.Module, ptr uint32, fn func(uint32, *T) uint32) []T {
ref, _ := mod.Memory().ReadUint32Le(ptr + 4)
if ref == 0 {
return nil
@@ -193,8 +266,7 @@ func loadSlice[T any](mod api.Module, ptr uint32, fn func(uint32, *T)) []T {
len, _ := mod.Memory().ReadUint32Le(ptr + 0)
ret := make([]T, len)
for i := range ret {
fn(ref, &ret[i])
ref += 4
ref += fn(ref, &ret[i])
}
return ret
}

View File

@@ -6,7 +6,7 @@ import (
"github.com/ncruces/go-sqlite3/util/sql3util"
)
func TestParse(t *testing.T) {
func TestParse_references(t *testing.T) {
tab, err := sql3util.ParseTable(`CREATE TABLE child(x REFERENCES parent)`)
if err != nil {
t.Fatal(err)
@@ -15,10 +15,10 @@ func TestParse(t *testing.T) {
if got := tab.Name; got != "child" {
t.Errorf("got %s, want child", got)
}
if got := len(tab.Columns); got != 1 {
t.Errorf("got %d, want 1", got)
}
col := tab.Columns[0]
if got := col.Name; got != "x" {
t.Errorf("got %s, want x", got)
@@ -29,3 +29,77 @@ func TestParse(t *testing.T) {
t.Errorf("got %s, want parent", got)
}
}
func TestParse_constraint(t *testing.T) {
tab, err := sql3util.ParseTable(`CREATE TABLE child(x, y, PRIMARY KEY(x, y))`)
if err != nil {
t.Fatal(err)
}
if got := tab.Name; got != "child" {
t.Errorf("got %s, want child", got)
}
if got := len(tab.Columns); got != 2 {
t.Errorf("got %d, want 2", got)
}
if got := tab.Columns[0].Name; got != "x" {
t.Errorf("got %s, want x", got)
}
if got := tab.Columns[1].Name; got != "y" {
t.Errorf("got %s, want y", got)
}
if got := len(tab.Constraints); got != 1 {
t.Errorf("got %d, want 1", got)
}
if got := tab.Constraints[0].Type; got != sql3util.TABLECONSTRAINT_PRIMARYKEY {
t.Errorf("got %d, want primary key", got)
}
if got := len(tab.Constraints[0].IndexedColumns); got != 2 {
t.Errorf("got %d, want 2", got)
}
if got := tab.Constraints[0].IndexedColumns[0].Name; got != "x" {
t.Errorf("got %s, want x", got)
}
if got := tab.Constraints[0].IndexedColumns[1].Name; got != "y" {
t.Errorf("got %s, want y", got)
}
}
func TestParse_foreign(t *testing.T) {
tab, err := sql3util.ParseTable(`CREATE TABLE child(x, y, FOREIGN KEY (x, y) REFERENCES parent)`)
if err != nil {
t.Fatal(err)
}
if got := tab.Name; got != "child" {
t.Errorf("got %s, want child", got)
}
if got := len(tab.Columns); got != 2 {
t.Errorf("got %d, want 2", got)
}
if got := tab.Columns[0].Name; got != "x" {
t.Errorf("got %s, want x", got)
}
if got := tab.Columns[1].Name; got != "y" {
t.Errorf("got %s, want y", got)
}
if got := len(tab.Constraints); got != 1 {
t.Errorf("got %d, want 1", got)
}
if got := tab.Constraints[0].Type; got != sql3util.TABLECONSTRAINT_FOREIGNKEY {
t.Errorf("got %d, want foreign key", got)
}
if got := len(tab.Constraints[0].ForeignKeyNames); got != 2 {
t.Errorf("got %d, want 2", got)
}
if got := tab.Constraints[0].ForeignKeyNames[0]; got != "x" {
t.Errorf("got %s, want x", got)
}
if got := tab.Constraints[0].ForeignKeyNames[1]; got != "y" {
t.Errorf("got %s, want y", got)
}
}

View File

@@ -1,9 +0,0 @@
// Package sql3util implements SQLite utilities.
package sql3util
// ValidPageSize returns true if s is a valid page size.
//
// https://sqlite.org/fileformat.html#pages
func ValidPageSize(s int) bool {
return s&(s-1) == 0 && 512 <= s && s <= 65536
}

View File

@@ -1,16 +1,12 @@
// Package sql3util implements SQLite utilities.
package sql3util
import "strings"
import (
"strings"
"time"
// NamedArg splits an named arg into a key and value,
// around an equals sign.
// Spaces are trimmed around both key and value.
func NamedArg(arg string) (key, val string) {
key, val, _ = strings.Cut(arg, "=")
key = strings.TrimSpace(key)
val = strings.TrimSpace(val)
return
}
"github.com/ncruces/go-sqlite3/internal/util"
)
// Unquote unquotes a string.
//
@@ -42,24 +38,39 @@ func Unquote(val string) string {
return strings.ReplaceAll(rst, old, new)
}
// NamedArg splits an named arg into a key and value,
// around an equals sign.
// Spaces are trimmed around both key and value.
func NamedArg(arg string) (key, val string) {
key, val, _ = strings.Cut(arg, "=")
key = strings.TrimSpace(key)
val = strings.TrimSpace(val)
return
}
// ParseBool parses a boolean.
//
// https://sqlite.org/pragma.html#syntax
func ParseBool(s string) (b, ok bool) {
if len(s) == 0 {
return false, false
}
if s[0] == '0' {
return false, true
}
if '1' <= s[0] && s[0] <= '9' {
return true, true
}
switch strings.ToLower(s) {
case "true", "yes", "on":
return true, true
case "false", "no", "off":
return false, true
}
return false, false
return util.ParseBool(s)
}
// ParseFloat parses a decimal floating point number.
func ParseFloat(s string) (f float64, ok bool) {
return util.ParseFloat(s)
}
// ParseTimeShift parses a time shift modifier,
// also the output of timediff.
//
// https://sqlite.org/lang_datefunc.html
func ParseTimeShift(s string) (years, months, days int, duration time.Duration, ok bool) {
return util.ParseTimeShift(s)
}
// ValidPageSize returns true if s is a valid page size.
//
// https://sqlite.org/fileformat.html#pages
func ValidPageSize(s int) bool {
return util.ValidPageSize(s)
}

View File

@@ -3,6 +3,7 @@ package sql3util_test
import (
"testing"
_ "github.com/ncruces/go-sqlite3/embed"
"github.com/ncruces/go-sqlite3/util/sql3util"
)
@@ -28,28 +29,3 @@ func TestUnquote(t *testing.T) {
})
}
}
func TestParseBool(t *testing.T) {
tests := []struct {
str string
val bool
ok bool
}{
{"", false, false},
{"0", false, true},
{"1", true, true},
{"9", true, true},
{"T", false, false},
{"true", true, true},
{"FALSE", false, true},
{"false?", false, false},
}
for _, tt := range tests {
t.Run(tt.str, func(t *testing.T) {
gotVal, gotOK := sql3util.ParseBool(tt.str)
if gotVal != tt.val || gotOK != tt.ok {
t.Errorf("ParseBool(%q) = (%v, %v) want (%v, %v)", tt.str, gotVal, gotOK, tt.val, tt.ok)
}
})
}
}

View File

@@ -25,8 +25,8 @@ trap 'rm -f sql3parse_table.tmp' EXIT
"$BINARYEN/wasm-ctor-eval" -c _initialize sql3parse_table.wasm -o sql3parse_table.tmp
"$BINARYEN/wasm-opt" sql3parse_table.tmp -o sql3parse_table.wasm \
--low-memory-unused --gufa --generate-global-effects --converge -Oz \
--gufa --generate-global-effects --converge -Oz \
--enable-mutable-globals --enable-nontrapping-float-to-int \
--enable-simd --enable-bulk-memory --enable-sign-ext \
--enable-reference-types --enable-multivalue \
--strip --strip-debug --strip-producers
--strip --strip-producers

View File

@@ -11,6 +11,8 @@ static_assert(offsetof(sql3table, is_withoutrowid) == 26, "Unexpected offset");
static_assert(offsetof(sql3table, is_strict) == 27, "Unexpected offset");
static_assert(offsetof(sql3table, num_columns) == 28, "Unexpected offset");
static_assert(offsetof(sql3table, columns) == 32, "Unexpected offset");
static_assert(offsetof(sql3table, num_constraint) == 36, "Unexpected offset");
static_assert(offsetof(sql3table, constraints) == 40, "Unexpected offset");
static_assert(offsetof(sql3table, type) == 44, "Unexpected offset");
static_assert(offsetof(sql3table, current_name) == 48, "Unexpected offset");
static_assert(offsetof(sql3table, new_name) == 56, "Unexpected offset");
@@ -32,6 +34,8 @@ static_assert(offsetof(sql3column, check_expr) == 60, "Unexpected offset");
static_assert(offsetof(sql3column, default_expr) == 68, "Unexpected offset");
static_assert(offsetof(sql3column, collate_name) == 76, "Unexpected offset");
static_assert(offsetof(sql3column, foreignkey_clause) == 84, "Unexpected offset");
static_assert(offsetof(sql3column, generated_expr) == 88, "Unexpected offset");
static_assert(offsetof(sql3column, generated_type) == 96, "Unexpected offset");
static_assert(offsetof(sql3foreignkey, table) == 0, "Unexpected offset");
static_assert(offsetof(sql3foreignkey, num_columns) == 8, "Unexpected offset");
@@ -39,4 +43,20 @@ static_assert(offsetof(sql3foreignkey, column_name) == 12, "Unexpected offset");
static_assert(offsetof(sql3foreignkey, on_delete) == 16, "Unexpected offset");
static_assert(offsetof(sql3foreignkey, on_update) == 20, "Unexpected offset");
static_assert(offsetof(sql3foreignkey, match) == 24, "Unexpected offset");
static_assert(offsetof(sql3foreignkey, deferrable) == 32, "Unexpected offset");
static_assert(offsetof(sql3foreignkey, deferrable) == 32, "Unexpected offset");
static_assert(offsetof(sql3tableconstraint, type) == 0, "Unexpected offset");
static_assert(offsetof(sql3tableconstraint, name) == 4, "Unexpected offset");
static_assert(offsetof(sql3tableconstraint, num_indexed) == 12, "Unexpected offset");
static_assert(offsetof(sql3tableconstraint, indexed_columns) == 16, "Unexpected offset");
static_assert(offsetof(sql3tableconstraint, conflict_clause) == 20, "Unexpected offset");
static_assert(offsetof(sql3tableconstraint, is_autoincrement) == 24, "Unexpected offset");
static_assert(offsetof(sql3tableconstraint, check_expr) == 12, "Unexpected offset");
static_assert(offsetof(sql3tableconstraint, foreignkey_num) == 12, "Unexpected offset");
static_assert(offsetof(sql3tableconstraint, foreignkey_name) == 16, "Unexpected offset");
static_assert(offsetof(sql3tableconstraint, foreignkey_clause) == 20, "Unexpected offset");
static_assert(offsetof(sql3idxcolumn, name) == 0, "Unexpected offset");
static_assert(offsetof(sql3idxcolumn, collate_name) == 8, "Unexpected offset");
static_assert(offsetof(sql3idxcolumn, order) == 16, "Unexpected offset");
static_assert(sizeof(sql3idxcolumn) == 20, "Unexpected size");

View File

@@ -117,9 +117,13 @@ The VFS can be customized with a few build tags:
- [`github.com/ncruces/go-sqlite3/vfs/memdb`](https://pkg.go.dev/github.com/ncruces/go-sqlite3/vfs/memdb)
implements an in-memory VFS.
- [`github.com/ncruces/go-sqlite3/vfs/mvcc`](https://pkg.go.dev/github.com/ncruces/go-sqlite3/vfs/mvcc)
implements an in-memory MVCC VFS.
- [`github.com/ncruces/go-sqlite3/vfs/readervfs`](https://pkg.go.dev/github.com/ncruces/go-sqlite3/vfs/readervfs)
implements a VFS for immutable databases.
- [`github.com/ncruces/go-sqlite3/vfs/adiantum`](https://pkg.go.dev/github.com/ncruces/go-sqlite3/vfs/adiantum)
wraps a VFS to offer encryption at rest.
- [`github.com/ncruces/go-sqlite3/vfs/xts`](https://pkg.go.dev/github.com/ncruces/go-sqlite3/vfs/xts)
wraps a VFS to offer encryption at rest.
- [`github.com/ncruces/litestream`](https://pkg.go.dev/github.com/ncruces/litestream)
implements Litestream [lightweight read-replicas](https://fly.io/blog/litestream-revamped/#lightweight-read-replicas).

View File

@@ -4,6 +4,7 @@ import (
"crypto/rand"
"golang.org/x/crypto/argon2"
"lukechampine.com/adiantum"
"lukechampine.com/adiantum/hbsh"
)

View File

@@ -1,5 +1,3 @@
//go:build linux || darwin || windows || freebsd || openbsd || netbsd || dragonfly || illumos || sqlite3_flock || sqlite3_dotlk
package adiantum_test
import (
@@ -8,6 +6,7 @@ import (
"os"
"golang.org/x/crypto/argon2"
"lukechampine.com/adiantum/hbsh"
"lukechampine.com/adiantum/hpolyc"
@@ -16,7 +15,7 @@ import (
"github.com/ncruces/go-sqlite3/vfs/adiantum"
)
func ExampleRegister_hpolyc() {
func Example_hPolyC() {
vfs.Register("hpolyc", adiantum.Wrap(vfs.Find(""), hpolycCreator{}))
db, err := sqlite3.Open("file:demo.db?vfs=hpolyc" +

View File

@@ -9,7 +9,6 @@ import (
"github.com/tetratelabs/wazero/api"
"github.com/ncruces/go-sqlite3/internal/util"
"github.com/ncruces/go-sqlite3/util/sql3util"
)
func cksmWrapFile(file File, flags OpenFlag) File {
@@ -35,7 +34,7 @@ func (c *cksmFile) ReadAt(p []byte, off int64) (n int, err error) {
}
// Verify checksums.
if c.verifyCksm && sql3util.ValidPageSize(len(p)) {
if c.verifyCksm && util.ValidPageSize(len(p)) {
cksm1 := cksmCompute(p[:len(p)-8])
cksm2 := *(*[8]byte)(p[len(p)-8:])
if cksm1 != cksm2 {
@@ -51,7 +50,7 @@ func (c *cksmFile) WriteAt(p []byte, off int64) (n int, err error) {
}
// Compute checksums.
if c.computeCksm && sql3util.ValidPageSize(len(p)) {
if c.computeCksm && util.ValidPageSize(len(p)) {
*(*[8]byte)(p[len(p)-8:]) = cksmCompute(p[:len(p)-8])
}
@@ -61,7 +60,7 @@ func (c *cksmFile) WriteAt(p []byte, off int64) (n int, err error) {
func (c *cksmFile) Pragma(name string, value string) (string, error) {
switch name {
case "checksum_verification":
b, ok := sql3util.ParseBool(value)
b, ok := util.ParseBool(value)
if ok {
c.verifyCksm = b && c.computeCksm
}

View File

@@ -239,6 +239,7 @@ const (
_FCNTL_RESET_CACHE _FcntlOpcode = 42
_FCNTL_NULL_IO _FcntlOpcode = 43
_FCNTL_BLOCK_ON_CONNECT _FcntlOpcode = 44
_FCNTL_FILESTAT _FcntlOpcode = 45
)
// https://sqlite.org/c3ref/c_shm_exclusive.html

View File

@@ -34,9 +34,6 @@ var (
// The new database takes ownership of data,
// and the caller should not use data after this call.
func Create(name string, data []byte) {
memoryMtx.Lock()
defer memoryMtx.Unlock()
db := &memDB{
refs: 1,
name: name,
@@ -63,14 +60,16 @@ func Create(name string, data []byte) {
}
}
memoryMtx.Lock()
memoryDBs[name] = db
memoryMtx.Unlock()
}
// Delete deletes a shared memory database.
func Delete(name string) {
memoryMtx.Lock()
defer memoryMtx.Unlock()
delete(memoryDBs, name)
memoryMtx.Unlock()
}
// TestDB creates an empty shared memory database for the test to use.

View File

@@ -92,10 +92,10 @@ type memDB struct {
func (m *memDB) release() {
memoryMtx.Lock()
defer memoryMtx.Unlock()
if m.refs--; m.refs == 0 && m == memoryDBs[m.name] {
delete(memoryDBs, m.name)
}
memoryMtx.Unlock()
}
type memFile struct {
@@ -223,7 +223,7 @@ func (m *memFile) Lock(lock vfs.LockLevel) error {
m.reserved = true
case vfs.LOCK_EXCLUSIVE:
if m.lock < vfs.LOCK_PENDING {
if m.lock == vfs.LOCK_RESERVED {
m.lock = vfs.LOCK_PENDING
m.pending = true
}

View File

@@ -35,13 +35,12 @@ var (
// using a snapshot as its initial contents.
func Create(name string, snapshot Snapshot) {
memoryMtx.Lock()
defer memoryMtx.Unlock()
memoryDBs[name] = &mvccDB{
refs: 1,
name: name,
data: snapshot.Tree,
}
memoryMtx.Unlock()
}
// Delete deletes a shared memory database.
@@ -49,8 +48,8 @@ func Delete(name string) {
name = getName(name)
memoryMtx.Lock()
defer memoryMtx.Unlock()
delete(memoryDBs, name)
memoryMtx.Unlock()
}
// Snapshot represents a database snapshot.
@@ -83,8 +82,9 @@ func TakeSnapshot(name string) Snapshot {
name = getName(name)
memoryMtx.Lock()
defer memoryMtx.Unlock()
db := memoryDBs[name]
memoryMtx.Unlock()
if db == nil {
return Snapshot{}
}

View File

@@ -79,10 +79,10 @@ type mvccDB struct {
func (m *mvccDB) release() {
memoryMtx.Lock()
defer memoryMtx.Unlock()
if m.refs--; m.refs == 0 && m == memoryDBs[m.name] {
delete(memoryDBs, m.name)
}
memoryMtx.Unlock()
}
type mvccFile struct {
@@ -90,6 +90,7 @@ type mvccFile struct {
data *wbt.Tree[int64, string]
lock vfs.LockLevel
readOnly bool
wrflag bool
}
var (
@@ -104,10 +105,10 @@ func (m *mvccFile) Close() error {
m.data = nil
m.lock = vfs.LOCK_NONE
m.mtx.Lock()
defer m.mtx.Unlock()
if m.owner == m {
m.owner = nil
}
m.mtx.Unlock()
return nil
}
@@ -212,6 +213,14 @@ func (m *mvccFile) Truncate(size int64) error {
return nil
}
func (m *mvccFile) Pragma(name, value string) (string, error) {
// notest // https://sqlite.org/forum/forumpost/c4ca8e7f4a887aa4
if name == "experimental_pragma_20251114" {
m.wrflag = true
}
return "", sqlite3.NOTFOUND
}
func (m *mvccFile) Lock(lock vfs.LockLevel) error {
if m.lock >= lock {
return nil
@@ -225,7 +234,7 @@ func (m *mvccFile) Lock(lock vfs.LockLevel) error {
defer m.mtx.Unlock()
// Take a snapshot of the database.
if lock == vfs.LOCK_SHARED {
if lock == vfs.LOCK_SHARED && !m.wrflag {
m.data = m.mvccDB.data
m.lock = lock
return nil
@@ -243,9 +252,8 @@ func (m *mvccFile) Lock(lock vfs.LockLevel) error {
}
defer time.AfterFunc(time.Millisecond, m.waiter.Broadcast).Stop()
for m.owner != nil {
// Our snapshot is invalid.
if m.data != m.mvccDB.data {
return sqlite3.BUSY_SNAPSHOT
if m.data != nil && m.data != m.mvccDB.data {
return sqlite3.BUSY_SNAPSHOT // Our snapshot is invalid.
}
if time.Since(before) > time.Millisecond {
return sqlite3.BUSY
@@ -253,9 +261,11 @@ func (m *mvccFile) Lock(lock vfs.LockLevel) error {
m.waiter.Wait()
}
}
// Our snapshot is invalid.
if m.data != m.mvccDB.data {
return sqlite3.BUSY_SNAPSHOT
switch {
case m.data == nil:
m.data = m.mvccDB.data
case m.data != m.mvccDB.data:
return sqlite3.BUSY_SNAPSHOT // Our snapshot is invalid.
}
// Take ownership.
m.lock = lock
@@ -264,6 +274,7 @@ func (m *mvccFile) Lock(lock vfs.LockLevel) error {
}
func (m *mvccFile) Unlock(lock vfs.LockLevel) error {
m.wrflag = false // SQLite calls unlock even if locking is unsuccessful.
if m.lock <= lock {
return nil
}
@@ -274,7 +285,9 @@ func (m *mvccFile) Unlock(lock vfs.LockLevel) error {
// Relase ownership, commit changes.
if m.owner == m {
m.owner = nil
m.mvccDB.data = m.data
if m.lock == vfs.LOCK_EXCLUSIVE {
m.mvccDB.data = m.data
}
if m.waiter != nil {
m.waiter.Broadcast()
}
@@ -300,10 +313,10 @@ func (m *mvccFile) CommitPhaseTwo() error {
// Modified without lock, commit changes.
if m.lock > vfs.LOCK_EXCLUSIVE {
m.mtx.Lock()
defer m.mtx.Unlock()
m.mvccDB.data = m.data
m.lock = vfs.LOCK_NONE
m.data = nil
m.mtx.Unlock()
}
return nil
}

View File

@@ -30,13 +30,13 @@ var (
// otherwise SQLite might return incorrect query results and/or [sqlite3.CORRUPT] errors.
func Create(name string, reader ioutil.SizeReaderAt) {
readerMtx.Lock()
defer readerMtx.Unlock()
readerDBs[name] = reader
readerMtx.Unlock()
}
// Delete deletes a shared memory database.
func Delete(name string) {
readerMtx.Lock()
defer readerMtx.Unlock()
delete(readerDBs, name)
readerMtx.Unlock()
}

View File

@@ -3,16 +3,15 @@ package readervfs
import (
"github.com/ncruces/go-sqlite3"
"github.com/ncruces/go-sqlite3/util/ioutil"
"github.com/ncruces/go-sqlite3/util/vfsutil"
"github.com/ncruces/go-sqlite3/vfs"
)
type readerVFS struct{}
func (readerVFS) Open(name string, flags vfs.OpenFlag) (vfs.File, vfs.OpenFlag, error) {
// Temp journals, as used by the sorter, use SliceFile.
if flags&vfs.OPEN_TEMP_JOURNAL != 0 {
return &vfsutil.SliceFile{}, flags | vfs.OPEN_MEMORY, nil
// Temporary files use the default VFS.
if name == "" || flags&vfs.OPEN_DELETEONCLOSE != 0 {
return vfs.Find("").Open(name, flags)
}
// Refuse to open all other file types.
if flags&vfs.OPEN_MAIN_DB == 0 {

View File

@@ -10,13 +10,17 @@ var (
// Find returns a VFS given its name.
// If there is no match, nil is returned.
// If name is empty, the default VFS is returned.
// If name is empty or "os", the default VFS is returned.
//
// https://sqlite.org/c3ref/vfs_find.html
func Find(name string) VFS {
if name == "" || name == "os" {
return vfsOS{}
}
return find(name)
}
func find(name string) VFS {
vfsRegistryMtx.RLock()
defer vfsRegistryMtx.RUnlock()
return vfsRegistry[name]
@@ -31,11 +35,11 @@ func Register(name string, vfs VFS) {
return
}
vfsRegistryMtx.Lock()
defer vfsRegistryMtx.Unlock()
if vfsRegistry == nil {
vfsRegistry = map[string]VFS{}
}
vfsRegistry[name] = vfs
vfsRegistryMtx.Unlock()
}
// Unregister unregisters a VFS.
@@ -43,6 +47,6 @@ func Register(name string, vfs VFS) {
// https://sqlite.org/c3ref/vfs_find.html
func Unregister(name string) {
vfsRegistryMtx.Lock()
defer vfsRegistryMtx.Unlock()
delete(vfsRegistry, name)
vfsRegistryMtx.Unlock()
}

View File

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

View File

@@ -1,4 +1,4 @@
//go:build ((freebsd || openbsd || netbsd || dragonfly || illumos) && (386 || arm || amd64 || arm64 || riscv64 || ppc64le) && !sqlite3_dotlk) || sqlite3_flock
//go:build ((freebsd || openbsd || netbsd || dragonfly || illumos) && (386 || arm || amd64 || arm64 || riscv64 || ppc64le || loong64) && !sqlite3_dotlk) || sqlite3_flock
package vfs
@@ -11,9 +11,10 @@ import (
"os"
"sync"
"github.com/tetratelabs/wazero/api"
"golang.org/x/sys/unix"
"github.com/tetratelabs/wazero/api"
"github.com/ncruces/go-sqlite3/internal/util"
)

View File

@@ -1,4 +1,4 @@
//go:build (windows && (386 || arm || amd64 || arm64 || riscv64 || ppc64le)) || sqlite3_dotlk
//go:build (windows && (386 || arm || amd64 || arm64 || riscv64 || ppc64le || loong64)) || sqlite3_dotlk
package vfs

View File

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

View File

@@ -1,4 +1,4 @@
//go:build (linux || darwin) && (386 || arm || amd64 || arm64 || riscv64 || ppc64le) && !(sqlite3_flock || sqlite3_dotlk)
//go:build (linux || darwin) && (386 || arm || amd64 || arm64 || riscv64 || ppc64le || loong64) && !(sqlite3_flock || sqlite3_dotlk)
package vfs
@@ -9,9 +9,10 @@ import (
"sync"
"time"
"github.com/tetratelabs/wazero/api"
"golang.org/x/sys/unix"
"github.com/tetratelabs/wazero/api"
"github.com/ncruces/go-sqlite3/internal/util"
)

View File

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

View File

@@ -1,4 +1,4 @@
//go:build (386 || arm || amd64 || arm64 || riscv64 || ppc64le) && !sqlite3_dotlk
//go:build (386 || arm || amd64 || arm64 || riscv64 || ppc64le || loong64) && !sqlite3_dotlk
package vfs
@@ -8,9 +8,10 @@ import (
"os"
"sync"
"github.com/tetratelabs/wazero/api"
"golang.org/x/sys/windows"
"github.com/tetratelabs/wazero/api"
"github.com/ncruces/go-sqlite3/internal/util"
)
@@ -98,7 +99,7 @@ func (s *vfsShm) shmMap(ctx context.Context, mod api.Module, id, size int32, ext
// Maps regions into memory.
for int(id) >= len(s.shared) {
r, err := util.MapRegion(ctx, mod, s.File, int64(id)*int64(size), size)
r, err := util.MapRegion(s.File, int64(id)*int64(size), size)
if err != nil {
return 0, err
}

View File

@@ -27,7 +27,7 @@ WASI_SDK="$ROOT/tools/wasi-sdk/bin"
$(awk '{print "-Wl,--export="$0}' exports.txt)
"$BINARYEN/wasm-opt" -g mptest.wasm -o mptest.tmp \
--low-memory-unused --gufa --generate-global-effects --converge -O3 \
--gufa --generate-global-effects --low-memory-unused --converge -O3 \
--enable-mutable-globals --enable-nontrapping-float-to-int \
--enable-simd --enable-bulk-memory --enable-sign-ext \
--enable-reference-types --enable-multivalue \

Binary file not shown.

View File

@@ -22,7 +22,7 @@ WASI_SDK="$ROOT/tools/wasi-sdk/bin"
$(awk '{print "-Wl,--export="$0}' exports.txt)
"$BINARYEN/wasm-opt" -g speedtest1.wasm -o speedtest1.tmp \
--low-memory-unused --gufa --generate-global-effects --converge -O3 \
--gufa --generate-global-effects --low-memory-unused --converge -O3 \
--enable-mutable-globals --enable-nontrapping-float-to-int \
--enable-simd --enable-bulk-memory --enable-sign-ext \
--enable-reference-types --enable-multivalue \

View File

@@ -12,7 +12,6 @@ import (
"github.com/tetratelabs/wazero/api"
"github.com/ncruces/go-sqlite3/internal/util"
"github.com/ncruces/go-sqlite3/util/sql3util"
"github.com/ncruces/julianday"
)
@@ -50,8 +49,7 @@ func ExportHostFunctions(env wazero.HostModuleBuilder) wazero.HostModuleBuilder
}
func vfsFind(ctx context.Context, mod api.Module, zVfsName ptr_t) uint32 {
name := util.ReadString(mod, zVfsName, _MAX_NAME)
if vfs := Find(name); vfs != nil && vfs != (vfsOS{}) {
if find(util.ReadString(mod, zVfsName, _MAX_NAME)) != nil {
return 1
}
return 0
@@ -138,7 +136,7 @@ func vfsOpen(ctx context.Context, mod api.Module, pVfs, zPath, pFile ptr_t, flag
}
if file, ok := file.(FilePowersafeOverwrite); ok {
if b, ok := sql3util.ParseBool(name.URIParameter("psow")); ok {
if b, ok := util.ParseBool(name.URIParameter("psow")); ok {
file.SetPowersafeOverwrite(b)
}
}
@@ -329,9 +327,9 @@ func vfsFileControlImpl(ctx context.Context, mod api.Module, file File, op _Fcnt
case _FCNTL_PRAGMA:
if file, ok := file.(FilePragma); ok {
var value string
ptr := util.Read32[ptr_t](mod, pArg+1*ptrlen)
name := util.ReadString(mod, ptr, _MAX_SQL_LENGTH)
var value string
if ptr := util.Read32[ptr_t](mod, pArg+2*ptrlen); ptr != 0 {
value = util.ReadString(mod, ptr, _MAX_SQL_LENGTH)
}
@@ -470,6 +468,8 @@ func vfsErrorCode(ctx context.Context, err error, code _ErrorCode) _ErrorCode {
switch v := reflect.ValueOf(err); v.Kind() {
case reflect.Uint8, reflect.Uint16:
code = _ErrorCode(v.Uint())
default:
sys = err
}
}