mirror of
https://github.com/ncruces/go-sqlite3.git
synced 2026-01-12 22:19:14 +00:00
Compare commits
33 Commits
v0.22.0
...
gormlite/v
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4142680d5a | ||
|
|
9f4fe6f27c | ||
|
|
7870ce0690 | ||
|
|
ec3226e16e | ||
|
|
4dd7bd0ff2 | ||
|
|
975feb2fd4 | ||
|
|
58f8c2d33e | ||
|
|
019660eed6 | ||
|
|
30c1bcdbe9 | ||
|
|
9b4002f5ac | ||
|
|
2a78d4bc2b | ||
|
|
c09623a903 | ||
|
|
fa613f9ddb | ||
|
|
57997201ee | ||
|
|
6995cca5c0 | ||
|
|
a10eef3ac8 | ||
|
|
d627ca3dc1 | ||
|
|
b2f7ab8335 | ||
|
|
c9135b9823 | ||
|
|
0d9ed94aad | ||
|
|
1d951ecd18 | ||
|
|
c0298ad274 | ||
|
|
42bad5891a | ||
|
|
40090d8250 | ||
|
|
d2f162972d | ||
|
|
e2da469834 | ||
|
|
1677b97fa4 | ||
|
|
407e13d238 | ||
|
|
9132f74b69 | ||
|
|
c024121fd2 | ||
|
|
aa8287f8e7 | ||
|
|
ab09da7136 | ||
|
|
a159b548ed |
7
.github/actions/vmactions/template.yml
vendored
7
.github/actions/vmactions/template.yml
vendored
@@ -1,11 +1,6 @@
|
||||
name: VM Actions matrix
|
||||
description: VM Actions matrix template
|
||||
|
||||
inputs:
|
||||
run:
|
||||
description: The CI command to run
|
||||
required: true
|
||||
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
@@ -13,4 +8,4 @@ runs:
|
||||
with:
|
||||
usesh: true
|
||||
copyback: false
|
||||
run: ${{inputs.run}}
|
||||
run: . ./test.sh
|
||||
6
.github/workflows/repro.sh
vendored
6
.github/workflows/repro.sh
vendored
@@ -3,13 +3,13 @@ set -euo pipefail
|
||||
|
||||
if [[ "$OSTYPE" == "linux"* ]]; then
|
||||
WASI_SDK="https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-25/wasi-sdk-25.0-x86_64-linux.tar.gz"
|
||||
BINARYEN="https://github.com/WebAssembly/binaryen/releases/download/version_121/binaryen-version_121-x86_64-linux.tar.gz"
|
||||
BINARYEN="https://github.com/WebAssembly/binaryen/releases/download/version_122/binaryen-version_122-x86_64-linux.tar.gz"
|
||||
elif [[ "$OSTYPE" == "darwin"* ]]; then
|
||||
WASI_SDK="https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-25/wasi-sdk-25.0-arm64-macos.tar.gz"
|
||||
BINARYEN="https://github.com/WebAssembly/binaryen/releases/download/version_121/binaryen-version_121-arm64-macos.tar.gz"
|
||||
BINARYEN="https://github.com/WebAssembly/binaryen/releases/download/version_122/binaryen-version_122-arm64-macos.tar.gz"
|
||||
elif [[ "$OSTYPE" == "msys" || "$OSTYPE" == "cygwin" ]]; then
|
||||
WASI_SDK="https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-25/wasi-sdk-25.0-x86_64-windows.tar.gz"
|
||||
BINARYEN="https://github.com/WebAssembly/binaryen/releases/download/version_121/binaryen-version_121-x86_64-windows.tar.gz"
|
||||
BINARYEN="https://github.com/WebAssembly/binaryen/releases/download/version_122/binaryen-version_122-x86_64-windows.tar.gz"
|
||||
fi
|
||||
|
||||
# Download tools
|
||||
|
||||
45
.github/workflows/test.yml
vendored
45
.github/workflows/test.yml
vendored
@@ -7,12 +7,14 @@ on:
|
||||
- '**.go'
|
||||
- '**.mod'
|
||||
- '**.wasm'
|
||||
- '**.yml'
|
||||
pull_request:
|
||||
branches: [ 'main' ]
|
||||
paths:
|
||||
- '**.go'
|
||||
- '**.mod'
|
||||
- '**.wasm'
|
||||
- '**.yml'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
@@ -57,16 +59,22 @@ jobs:
|
||||
run: go test -v -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/...
|
||||
|
||||
- name: Test GORM
|
||||
shell: bash
|
||||
run: gormlite/test.sh
|
||||
|
||||
- name: Test modules
|
||||
shell: bash
|
||||
run: go test -v ./embed/bcw2/...
|
||||
if: matrix.os != 'windows-latest'
|
||||
|
||||
- name: Collect coverage
|
||||
run: go run github.com/dave/courtney@latest
|
||||
run: |
|
||||
go get -tool github.com/dave/courtney@v0.4.4
|
||||
go tool courtney
|
||||
if: |
|
||||
github.event_name == 'push' &&
|
||||
matrix.os == 'ubuntu-latest'
|
||||
@@ -88,7 +96,7 @@ jobs:
|
||||
version: '14.2'
|
||||
flags: '-test.v'
|
||||
- name: netbsd
|
||||
version: '10.0'
|
||||
version: '10.1'
|
||||
flags: '-test.v'
|
||||
- name: freebsd
|
||||
arch: arm64
|
||||
@@ -96,7 +104,7 @@ jobs:
|
||||
flags: '-test.v -test.short'
|
||||
- name: netbsd
|
||||
arch: arm64
|
||||
version: '10.0'
|
||||
version: '10.1'
|
||||
flags: '-test.v -test.short'
|
||||
- name: openbsd
|
||||
version: '7.6'
|
||||
@@ -115,7 +123,7 @@ jobs:
|
||||
run: .github/workflows/build-test.sh
|
||||
|
||||
- name: Test
|
||||
uses: cross-platform-actions/action@v0.26.0
|
||||
uses: cross-platform-actions/action@v0.27.0
|
||||
with:
|
||||
operating_system: ${{ matrix.os.name }}
|
||||
architecture: ${{ matrix.os.arch }}
|
||||
@@ -154,10 +162,6 @@ jobs:
|
||||
|
||||
- name: Test
|
||||
uses: ./.github/actions/vmactions
|
||||
with:
|
||||
usesh: true
|
||||
copyback: false
|
||||
run: . ./test.sh
|
||||
|
||||
test-wasip1:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -170,7 +174,7 @@ jobs:
|
||||
with: { go-version: stable }
|
||||
|
||||
- name: Set path
|
||||
run: echo "$(go env GOROOT)/misc/wasm" >> "$GITHUB_PATH"
|
||||
run: echo "$(go env GOROOT)/lib/wasm" >> "$GITHUB_PATH"
|
||||
|
||||
- name: Test wasmtime
|
||||
env:
|
||||
@@ -193,9 +197,6 @@ jobs:
|
||||
- name: Test 386 (32-bit)
|
||||
run: GOARCH=386 go test -v -short ./...
|
||||
|
||||
- name: Test arm64 (compiler)
|
||||
run: GOARCH=arm64 go test -v -short ./...
|
||||
|
||||
- name: Test riscv64 (interpreter)
|
||||
run: GOARCH=riscv64 go test -v -short ./...
|
||||
|
||||
@@ -205,6 +206,18 @@ jobs:
|
||||
- name: Test s390x (big-endian)
|
||||
run: GOARCH=s390x go test -v -short -tags sqlite3_dotlk ./...
|
||||
|
||||
test-linuxarm:
|
||||
runs-on: ubuntu-24.04-arm
|
||||
needs: test
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with: { go-version: stable }
|
||||
|
||||
- name: Test
|
||||
run: go test -v ./...
|
||||
|
||||
test-macintel:
|
||||
runs-on: macos-13
|
||||
needs: test
|
||||
|
||||
9
.gitignore
vendored
9
.gitignore
vendored
@@ -13,4 +13,11 @@
|
||||
|
||||
# Dependency directories (remove the comment below to include it)
|
||||
# vendor/
|
||||
tools
|
||||
tools
|
||||
|
||||
# Go workspace file
|
||||
go.work
|
||||
go.work.sum
|
||||
|
||||
# env file
|
||||
.env
|
||||
38
backup.go
38
backup.go
@@ -5,8 +5,8 @@ package sqlite3
|
||||
// https://sqlite.org/c3ref/backup.html
|
||||
type Backup struct {
|
||||
c *Conn
|
||||
handle uint32
|
||||
otherc uint32
|
||||
handle ptr_t
|
||||
otherc ptr_t
|
||||
}
|
||||
|
||||
// Backup backs up srcDB on the src connection to the "main" database in dstURI.
|
||||
@@ -61,7 +61,7 @@ func (src *Conn) BackupInit(srcDB, dstURI string) (*Backup, error) {
|
||||
return src.backupInit(dst, "main", src.handle, srcDB)
|
||||
}
|
||||
|
||||
func (c *Conn) backupInit(dst uint32, dstName string, src uint32, srcName string) (*Backup, error) {
|
||||
func (c *Conn) backupInit(dst ptr_t, dstName string, src ptr_t, srcName string) (*Backup, error) {
|
||||
defer c.arena.mark()()
|
||||
dstPtr := c.arena.string(dstName)
|
||||
srcPtr := c.arena.string(srcName)
|
||||
@@ -71,19 +71,19 @@ func (c *Conn) backupInit(dst uint32, dstName string, src uint32, srcName string
|
||||
other = src
|
||||
}
|
||||
|
||||
r := c.call("sqlite3_backup_init",
|
||||
uint64(dst), uint64(dstPtr),
|
||||
uint64(src), uint64(srcPtr))
|
||||
if r == 0 {
|
||||
ptr := ptr_t(c.call("sqlite3_backup_init",
|
||||
stk_t(dst), stk_t(dstPtr),
|
||||
stk_t(src), stk_t(srcPtr)))
|
||||
if ptr == 0 {
|
||||
defer c.closeDB(other)
|
||||
r = c.call("sqlite3_errcode", uint64(dst))
|
||||
return nil, c.sqlite.error(r, dst)
|
||||
rc := res_t(c.call("sqlite3_errcode", stk_t(dst)))
|
||||
return nil, c.sqlite.error(rc, dst)
|
||||
}
|
||||
|
||||
return &Backup{
|
||||
c: c,
|
||||
otherc: other,
|
||||
handle: uint32(r),
|
||||
handle: ptr,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -97,10 +97,10 @@ func (b *Backup) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
r := b.c.call("sqlite3_backup_finish", uint64(b.handle))
|
||||
rc := res_t(b.c.call("sqlite3_backup_finish", stk_t(b.handle)))
|
||||
b.c.closeDB(b.otherc)
|
||||
b.handle = 0
|
||||
return b.c.error(r)
|
||||
return b.c.error(rc)
|
||||
}
|
||||
|
||||
// Step copies up to nPage pages between the source and destination databases.
|
||||
@@ -108,11 +108,11 @@ func (b *Backup) Close() error {
|
||||
//
|
||||
// https://sqlite.org/c3ref/backup_finish.html#sqlite3backupstep
|
||||
func (b *Backup) Step(nPage int) (done bool, err error) {
|
||||
r := b.c.call("sqlite3_backup_step", uint64(b.handle), uint64(nPage))
|
||||
if r == _DONE {
|
||||
rc := res_t(b.c.call("sqlite3_backup_step", stk_t(b.handle), stk_t(nPage)))
|
||||
if rc == _DONE {
|
||||
return true, nil
|
||||
}
|
||||
return false, b.c.error(r)
|
||||
return false, b.c.error(rc)
|
||||
}
|
||||
|
||||
// Remaining returns the number of pages still to be backed up
|
||||
@@ -120,8 +120,8 @@ func (b *Backup) Step(nPage int) (done bool, err error) {
|
||||
//
|
||||
// https://sqlite.org/c3ref/backup_finish.html#sqlite3backupremaining
|
||||
func (b *Backup) Remaining() int {
|
||||
r := b.c.call("sqlite3_backup_remaining", uint64(b.handle))
|
||||
return int(int32(r))
|
||||
n := int32(b.c.call("sqlite3_backup_remaining", stk_t(b.handle)))
|
||||
return int(n)
|
||||
}
|
||||
|
||||
// PageCount returns the total number of pages in the source database
|
||||
@@ -129,6 +129,6 @@ func (b *Backup) Remaining() int {
|
||||
//
|
||||
// https://sqlite.org/c3ref/backup_finish.html#sqlite3backuppagecount
|
||||
func (b *Backup) PageCount() int {
|
||||
r := b.c.call("sqlite3_backup_pagecount", uint64(b.handle))
|
||||
return int(int32(r))
|
||||
n := int32(b.c.call("sqlite3_backup_pagecount", stk_t(b.handle)))
|
||||
return int(n)
|
||||
}
|
||||
|
||||
64
blob.go
64
blob.go
@@ -20,8 +20,8 @@ type Blob struct {
|
||||
c *Conn
|
||||
bytes int64
|
||||
offset int64
|
||||
handle uint32
|
||||
bufptr uint32
|
||||
handle ptr_t
|
||||
bufptr ptr_t
|
||||
buflen int64
|
||||
}
|
||||
|
||||
@@ -37,23 +37,23 @@ func (c *Conn) OpenBlob(db, table, column string, row int64, write bool) (*Blob,
|
||||
tablePtr := c.arena.string(table)
|
||||
columnPtr := c.arena.string(column)
|
||||
|
||||
var flags uint64
|
||||
var flags int32
|
||||
if write {
|
||||
flags = 1
|
||||
}
|
||||
|
||||
c.checkInterrupt(c.handle)
|
||||
r := c.call("sqlite3_blob_open", uint64(c.handle),
|
||||
uint64(dbPtr), uint64(tablePtr), uint64(columnPtr),
|
||||
uint64(row), flags, uint64(blobPtr))
|
||||
rc := res_t(c.call("sqlite3_blob_open", stk_t(c.handle),
|
||||
stk_t(dbPtr), stk_t(tablePtr), stk_t(columnPtr),
|
||||
stk_t(row), stk_t(flags), stk_t(blobPtr)))
|
||||
|
||||
if err := c.error(r); err != nil {
|
||||
if err := c.error(rc); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
blob := Blob{c: c}
|
||||
blob.handle = util.ReadUint32(c.mod, blobPtr)
|
||||
blob.bytes = int64(c.call("sqlite3_blob_bytes", uint64(blob.handle)))
|
||||
blob.handle = util.Read32[ptr_t](c.mod, blobPtr)
|
||||
blob.bytes = int64(int32(c.call("sqlite3_blob_bytes", stk_t(blob.handle))))
|
||||
return &blob, nil
|
||||
}
|
||||
|
||||
@@ -67,10 +67,10 @@ func (b *Blob) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
r := b.c.call("sqlite3_blob_close", uint64(b.handle))
|
||||
rc := res_t(b.c.call("sqlite3_blob_close", stk_t(b.handle)))
|
||||
b.c.free(b.bufptr)
|
||||
b.handle = 0
|
||||
return b.c.error(r)
|
||||
return b.c.error(rc)
|
||||
}
|
||||
|
||||
// Size returns the size of the BLOB in bytes.
|
||||
@@ -94,13 +94,13 @@ func (b *Blob) Read(p []byte) (n int, err error) {
|
||||
want = avail
|
||||
}
|
||||
if want > b.buflen {
|
||||
b.bufptr = b.c.realloc(b.bufptr, uint64(want))
|
||||
b.bufptr = b.c.realloc(b.bufptr, want)
|
||||
b.buflen = want
|
||||
}
|
||||
|
||||
r := b.c.call("sqlite3_blob_read", uint64(b.handle),
|
||||
uint64(b.bufptr), uint64(want), uint64(b.offset))
|
||||
err = b.c.error(r)
|
||||
rc := res_t(b.c.call("sqlite3_blob_read", stk_t(b.handle),
|
||||
stk_t(b.bufptr), stk_t(want), stk_t(b.offset)))
|
||||
err = b.c.error(rc)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
@@ -109,7 +109,7 @@ func (b *Blob) Read(p []byte) (n int, err error) {
|
||||
err = io.EOF
|
||||
}
|
||||
|
||||
copy(p, util.View(b.c.mod, b.bufptr, uint64(want)))
|
||||
copy(p, util.View(b.c.mod, b.bufptr, want))
|
||||
return int(want), err
|
||||
}
|
||||
|
||||
@@ -127,19 +127,19 @@ func (b *Blob) WriteTo(w io.Writer) (n int64, err error) {
|
||||
want = avail
|
||||
}
|
||||
if want > b.buflen {
|
||||
b.bufptr = b.c.realloc(b.bufptr, uint64(want))
|
||||
b.bufptr = b.c.realloc(b.bufptr, want)
|
||||
b.buflen = want
|
||||
}
|
||||
|
||||
for want > 0 {
|
||||
r := b.c.call("sqlite3_blob_read", uint64(b.handle),
|
||||
uint64(b.bufptr), uint64(want), uint64(b.offset))
|
||||
err = b.c.error(r)
|
||||
rc := res_t(b.c.call("sqlite3_blob_read", stk_t(b.handle),
|
||||
stk_t(b.bufptr), stk_t(want), stk_t(b.offset)))
|
||||
err = b.c.error(rc)
|
||||
if err != nil {
|
||||
return n, err
|
||||
}
|
||||
|
||||
mem := util.View(b.c.mod, b.bufptr, uint64(want))
|
||||
mem := util.View(b.c.mod, b.bufptr, want)
|
||||
m, err := w.Write(mem[:want])
|
||||
b.offset += int64(m)
|
||||
n += int64(m)
|
||||
@@ -165,14 +165,14 @@ func (b *Blob) WriteTo(w io.Writer) (n int64, err error) {
|
||||
func (b *Blob) Write(p []byte) (n int, err error) {
|
||||
want := int64(len(p))
|
||||
if want > b.buflen {
|
||||
b.bufptr = b.c.realloc(b.bufptr, uint64(want))
|
||||
b.bufptr = b.c.realloc(b.bufptr, want)
|
||||
b.buflen = want
|
||||
}
|
||||
util.WriteBytes(b.c.mod, b.bufptr, p)
|
||||
|
||||
r := b.c.call("sqlite3_blob_write", uint64(b.handle),
|
||||
uint64(b.bufptr), uint64(want), uint64(b.offset))
|
||||
err = b.c.error(r)
|
||||
rc := res_t(b.c.call("sqlite3_blob_write", stk_t(b.handle),
|
||||
stk_t(b.bufptr), stk_t(want), stk_t(b.offset)))
|
||||
err = b.c.error(rc)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
@@ -196,17 +196,17 @@ func (b *Blob) ReadFrom(r io.Reader) (n int64, err error) {
|
||||
want = 1
|
||||
}
|
||||
if want > b.buflen {
|
||||
b.bufptr = b.c.realloc(b.bufptr, uint64(want))
|
||||
b.bufptr = b.c.realloc(b.bufptr, want)
|
||||
b.buflen = want
|
||||
}
|
||||
|
||||
for {
|
||||
mem := util.View(b.c.mod, b.bufptr, uint64(want))
|
||||
mem := util.View(b.c.mod, b.bufptr, want)
|
||||
m, err := r.Read(mem[:want])
|
||||
if m > 0 {
|
||||
r := b.c.call("sqlite3_blob_write", uint64(b.handle),
|
||||
uint64(b.bufptr), uint64(m), uint64(b.offset))
|
||||
err := b.c.error(r)
|
||||
rc := res_t(b.c.call("sqlite3_blob_write", stk_t(b.handle),
|
||||
stk_t(b.bufptr), stk_t(m), stk_t(b.offset)))
|
||||
err := b.c.error(rc)
|
||||
if err != nil {
|
||||
return n, err
|
||||
}
|
||||
@@ -254,8 +254,8 @@ func (b *Blob) Seek(offset int64, whence int) (int64, error) {
|
||||
// https://sqlite.org/c3ref/blob_reopen.html
|
||||
func (b *Blob) Reopen(row int64) error {
|
||||
b.c.checkInterrupt(b.c.handle)
|
||||
err := b.c.error(b.c.call("sqlite3_blob_reopen", uint64(b.handle), uint64(row)))
|
||||
b.bytes = int64(b.c.call("sqlite3_blob_bytes", uint64(b.handle)))
|
||||
err := b.c.error(res_t(b.c.call("sqlite3_blob_reopen", stk_t(b.handle), stk_t(row))))
|
||||
b.bytes = int64(int32(b.c.call("sqlite3_blob_bytes", stk_t(b.handle))))
|
||||
b.offset = 0
|
||||
return err
|
||||
}
|
||||
|
||||
158
config.go
158
config.go
@@ -32,7 +32,7 @@ func (c *Conn) Config(op DBConfig, arg ...bool) (bool, error) {
|
||||
defer c.arena.mark()()
|
||||
argsPtr := c.arena.new(intlen + ptrlen)
|
||||
|
||||
var flag int
|
||||
var flag int32
|
||||
switch {
|
||||
case len(arg) == 0:
|
||||
flag = -1
|
||||
@@ -40,31 +40,31 @@ func (c *Conn) Config(op DBConfig, arg ...bool) (bool, error) {
|
||||
flag = 1
|
||||
}
|
||||
|
||||
util.WriteUint32(c.mod, argsPtr+0*ptrlen, uint32(flag))
|
||||
util.WriteUint32(c.mod, argsPtr+1*ptrlen, argsPtr)
|
||||
util.Write32(c.mod, argsPtr+0*ptrlen, flag)
|
||||
util.Write32(c.mod, argsPtr+1*ptrlen, argsPtr)
|
||||
|
||||
r := c.call("sqlite3_db_config", uint64(c.handle),
|
||||
uint64(op), uint64(argsPtr))
|
||||
return util.ReadUint32(c.mod, argsPtr) != 0, c.error(r)
|
||||
rc := res_t(c.call("sqlite3_db_config", stk_t(c.handle),
|
||||
stk_t(op), stk_t(argsPtr)))
|
||||
return util.ReadBool(c.mod, argsPtr), c.error(rc)
|
||||
}
|
||||
|
||||
// ConfigLog sets up the error logging callback for the connection.
|
||||
//
|
||||
// https://sqlite.org/errlog.html
|
||||
func (c *Conn) ConfigLog(cb func(code ExtendedErrorCode, msg string)) error {
|
||||
var enable uint64
|
||||
var enable int32
|
||||
if cb != nil {
|
||||
enable = 1
|
||||
}
|
||||
r := c.call("sqlite3_config_log_go", enable)
|
||||
if err := c.error(r); err != nil {
|
||||
rc := res_t(c.call("sqlite3_config_log_go", stk_t(enable)))
|
||||
if err := c.error(rc); err != nil {
|
||||
return err
|
||||
}
|
||||
c.log = cb
|
||||
return nil
|
||||
}
|
||||
|
||||
func logCallback(ctx context.Context, mod api.Module, _, iCode, zMsg uint32) {
|
||||
func logCallback(ctx context.Context, mod api.Module, _ ptr_t, iCode res_t, zMsg ptr_t) {
|
||||
if c, ok := ctx.Value(connKey{}).(*Conn); ok && c.log != nil {
|
||||
msg := util.ReadString(mod, zMsg, _MAX_LENGTH)
|
||||
c.log(xErrorCode(iCode), msg)
|
||||
@@ -88,93 +88,93 @@ func (c *Conn) FileControl(schema string, op FcntlOpcode, arg ...any) (any, erro
|
||||
defer c.arena.mark()()
|
||||
ptr := c.arena.new(max(ptrlen, intlen))
|
||||
|
||||
var schemaPtr uint32
|
||||
var schemaPtr ptr_t
|
||||
if schema != "" {
|
||||
schemaPtr = c.arena.string(schema)
|
||||
}
|
||||
|
||||
var rc uint64
|
||||
var res any
|
||||
var rc res_t
|
||||
var ret any
|
||||
switch op {
|
||||
default:
|
||||
return nil, MISUSE
|
||||
|
||||
case FCNTL_RESET_CACHE:
|
||||
rc = c.call("sqlite3_file_control",
|
||||
uint64(c.handle), uint64(schemaPtr),
|
||||
uint64(op), 0)
|
||||
rc = res_t(c.call("sqlite3_file_control",
|
||||
stk_t(c.handle), stk_t(schemaPtr),
|
||||
stk_t(op), 0))
|
||||
|
||||
case FCNTL_PERSIST_WAL, FCNTL_POWERSAFE_OVERWRITE:
|
||||
var flag int
|
||||
var flag int32
|
||||
switch {
|
||||
case len(arg) == 0:
|
||||
flag = -1
|
||||
case arg[0]:
|
||||
flag = 1
|
||||
}
|
||||
util.WriteUint32(c.mod, ptr, uint32(flag))
|
||||
rc = c.call("sqlite3_file_control",
|
||||
uint64(c.handle), uint64(schemaPtr),
|
||||
uint64(op), uint64(ptr))
|
||||
res = util.ReadUint32(c.mod, ptr) != 0
|
||||
util.Write32(c.mod, ptr, flag)
|
||||
rc = res_t(c.call("sqlite3_file_control",
|
||||
stk_t(c.handle), stk_t(schemaPtr),
|
||||
stk_t(op), stk_t(ptr)))
|
||||
ret = util.ReadBool(c.mod, ptr)
|
||||
|
||||
case FCNTL_CHUNK_SIZE:
|
||||
util.WriteUint32(c.mod, ptr, uint32(arg[0].(int)))
|
||||
rc = c.call("sqlite3_file_control",
|
||||
uint64(c.handle), uint64(schemaPtr),
|
||||
uint64(op), uint64(ptr))
|
||||
util.Write32(c.mod, ptr, int32(arg[0].(int)))
|
||||
rc = res_t(c.call("sqlite3_file_control",
|
||||
stk_t(c.handle), stk_t(schemaPtr),
|
||||
stk_t(op), stk_t(ptr)))
|
||||
|
||||
case FCNTL_RESERVE_BYTES:
|
||||
bytes := -1
|
||||
if len(arg) > 0 {
|
||||
bytes = arg[0].(int)
|
||||
}
|
||||
util.WriteUint32(c.mod, ptr, uint32(bytes))
|
||||
rc = c.call("sqlite3_file_control",
|
||||
uint64(c.handle), uint64(schemaPtr),
|
||||
uint64(op), uint64(ptr))
|
||||
res = int(util.ReadUint32(c.mod, ptr))
|
||||
util.Write32(c.mod, ptr, int32(bytes))
|
||||
rc = res_t(c.call("sqlite3_file_control",
|
||||
stk_t(c.handle), stk_t(schemaPtr),
|
||||
stk_t(op), stk_t(ptr)))
|
||||
ret = int(util.Read32[int32](c.mod, ptr))
|
||||
|
||||
case FCNTL_DATA_VERSION:
|
||||
rc = c.call("sqlite3_file_control",
|
||||
uint64(c.handle), uint64(schemaPtr),
|
||||
uint64(op), uint64(ptr))
|
||||
res = util.ReadUint32(c.mod, ptr)
|
||||
rc = res_t(c.call("sqlite3_file_control",
|
||||
stk_t(c.handle), stk_t(schemaPtr),
|
||||
stk_t(op), stk_t(ptr)))
|
||||
ret = util.Read32[uint32](c.mod, ptr)
|
||||
|
||||
case FCNTL_LOCKSTATE:
|
||||
rc = c.call("sqlite3_file_control",
|
||||
uint64(c.handle), uint64(schemaPtr),
|
||||
uint64(op), uint64(ptr))
|
||||
res = vfs.LockLevel(util.ReadUint32(c.mod, ptr))
|
||||
rc = res_t(c.call("sqlite3_file_control",
|
||||
stk_t(c.handle), stk_t(schemaPtr),
|
||||
stk_t(op), stk_t(ptr)))
|
||||
ret = util.Read32[vfs.LockLevel](c.mod, ptr)
|
||||
|
||||
case FCNTL_VFS_POINTER:
|
||||
rc = c.call("sqlite3_file_control",
|
||||
uint64(c.handle), uint64(schemaPtr),
|
||||
uint64(op), uint64(ptr))
|
||||
rc = res_t(c.call("sqlite3_file_control",
|
||||
stk_t(c.handle), stk_t(schemaPtr),
|
||||
stk_t(op), stk_t(ptr)))
|
||||
if rc == _OK {
|
||||
const zNameOffset = 16
|
||||
ptr = util.ReadUint32(c.mod, ptr)
|
||||
ptr = util.ReadUint32(c.mod, ptr+zNameOffset)
|
||||
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)
|
||||
res = vfs.Find(name)
|
||||
ret = vfs.Find(name)
|
||||
}
|
||||
|
||||
case FCNTL_FILE_POINTER, FCNTL_JOURNAL_POINTER:
|
||||
rc = c.call("sqlite3_file_control",
|
||||
uint64(c.handle), uint64(schemaPtr),
|
||||
uint64(op), uint64(ptr))
|
||||
rc = res_t(c.call("sqlite3_file_control",
|
||||
stk_t(c.handle), stk_t(schemaPtr),
|
||||
stk_t(op), stk_t(ptr)))
|
||||
if rc == _OK {
|
||||
const fileHandleOffset = 4
|
||||
ptr = util.ReadUint32(c.mod, ptr)
|
||||
ptr = util.ReadUint32(c.mod, ptr+fileHandleOffset)
|
||||
res = util.GetHandle(c.ctx, ptr)
|
||||
ptr = util.Read32[ptr_t](c.mod, ptr)
|
||||
ptr = util.Read32[ptr_t](c.mod, ptr+fileHandleOffset)
|
||||
ret = util.GetHandle(c.ctx, ptr)
|
||||
}
|
||||
}
|
||||
|
||||
if err := c.error(rc); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return res, nil
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// Limit allows the size of various constructs to be
|
||||
@@ -182,20 +182,20 @@ func (c *Conn) FileControl(schema string, op FcntlOpcode, arg ...any) (any, erro
|
||||
//
|
||||
// https://sqlite.org/c3ref/limit.html
|
||||
func (c *Conn) Limit(id LimitCategory, value int) int {
|
||||
r := c.call("sqlite3_limit", uint64(c.handle), uint64(id), uint64(value))
|
||||
return int(int32(r))
|
||||
v := int32(c.call("sqlite3_limit", stk_t(c.handle), stk_t(id), stk_t(value)))
|
||||
return int(v)
|
||||
}
|
||||
|
||||
// SetAuthorizer registers an authorizer callback with the database connection.
|
||||
//
|
||||
// https://sqlite.org/c3ref/set_authorizer.html
|
||||
func (c *Conn) SetAuthorizer(cb func(action AuthorizerActionCode, name3rd, name4th, schema, inner string) AuthorizerReturnCode) error {
|
||||
var enable uint64
|
||||
var enable int32
|
||||
if cb != nil {
|
||||
enable = 1
|
||||
}
|
||||
r := c.call("sqlite3_set_authorizer_go", uint64(c.handle), enable)
|
||||
if err := c.error(r); err != nil {
|
||||
rc := res_t(c.call("sqlite3_set_authorizer_go", stk_t(c.handle), stk_t(enable)))
|
||||
if err := c.error(rc); err != nil {
|
||||
return err
|
||||
}
|
||||
c.authorizer = cb
|
||||
@@ -203,7 +203,7 @@ func (c *Conn) SetAuthorizer(cb func(action AuthorizerActionCode, name3rd, name4
|
||||
|
||||
}
|
||||
|
||||
func authorizerCallback(ctx context.Context, mod api.Module, pDB uint32, action AuthorizerActionCode, zName3rd, zName4th, zSchema, zInner uint32) (rc AuthorizerReturnCode) {
|
||||
func authorizerCallback(ctx context.Context, mod api.Module, pDB ptr_t, action AuthorizerActionCode, zName3rd, zName4th, zSchema, zInner ptr_t) (rc AuthorizerReturnCode) {
|
||||
if c, ok := ctx.Value(connKey{}).(*Conn); ok && c.handle == pDB && c.authorizer != nil {
|
||||
var name3rd, name4th, schema, inner string
|
||||
if zName3rd != 0 {
|
||||
@@ -227,15 +227,15 @@ func authorizerCallback(ctx context.Context, mod api.Module, pDB uint32, action
|
||||
//
|
||||
// https://sqlite.org/c3ref/trace_v2.html
|
||||
func (c *Conn) Trace(mask TraceEvent, cb func(evt TraceEvent, arg1 any, arg2 any) error) error {
|
||||
r := c.call("sqlite3_trace_go", uint64(c.handle), uint64(mask))
|
||||
if err := c.error(r); err != nil {
|
||||
rc := res_t(c.call("sqlite3_trace_go", stk_t(c.handle), stk_t(mask)))
|
||||
if err := c.error(rc); err != nil {
|
||||
return err
|
||||
}
|
||||
c.trace = cb
|
||||
return nil
|
||||
}
|
||||
|
||||
func traceCallback(ctx context.Context, mod api.Module, evt TraceEvent, pDB, pArg1, pArg2 uint32) (rc uint32) {
|
||||
func traceCallback(ctx context.Context, mod api.Module, evt TraceEvent, pDB, pArg1, pArg2 ptr_t) (rc res_t) {
|
||||
if c, ok := ctx.Value(connKey{}).(*Conn); ok && c.handle == pDB && c.trace != nil {
|
||||
var arg1, arg2 any
|
||||
if evt == TRACE_CLOSE {
|
||||
@@ -248,7 +248,7 @@ func traceCallback(ctx context.Context, mod api.Module, evt TraceEvent, pDB, pAr
|
||||
case TRACE_STMT:
|
||||
arg2 = s.SQL()
|
||||
case TRACE_PROFILE:
|
||||
arg2 = int64(util.ReadUint64(mod, pArg2))
|
||||
arg2 = util.Read64[int64](mod, pArg2)
|
||||
}
|
||||
break
|
||||
}
|
||||
@@ -269,20 +269,20 @@ func (c *Conn) WALCheckpoint(schema string, mode CheckpointMode) (nLog, nCkpt in
|
||||
nLogPtr := c.arena.new(ptrlen)
|
||||
nCkptPtr := c.arena.new(ptrlen)
|
||||
schemaPtr := c.arena.string(schema)
|
||||
r := c.call("sqlite3_wal_checkpoint_v2",
|
||||
uint64(c.handle), uint64(schemaPtr), uint64(mode),
|
||||
uint64(nLogPtr), uint64(nCkptPtr))
|
||||
nLog = int(int32(util.ReadUint32(c.mod, nLogPtr)))
|
||||
nCkpt = int(int32(util.ReadUint32(c.mod, nCkptPtr)))
|
||||
return nLog, nCkpt, c.error(r)
|
||||
rc := res_t(c.call("sqlite3_wal_checkpoint_v2",
|
||||
stk_t(c.handle), stk_t(schemaPtr), stk_t(mode),
|
||||
stk_t(nLogPtr), stk_t(nCkptPtr)))
|
||||
nLog = int(util.Read32[int32](c.mod, nLogPtr))
|
||||
nCkpt = int(util.Read32[int32](c.mod, nCkptPtr))
|
||||
return nLog, nCkpt, c.error(rc)
|
||||
}
|
||||
|
||||
// WALAutoCheckpoint configures WAL auto-checkpoints.
|
||||
//
|
||||
// https://sqlite.org/c3ref/wal_autocheckpoint.html
|
||||
func (c *Conn) WALAutoCheckpoint(pages int) error {
|
||||
r := c.call("sqlite3_wal_autocheckpoint", uint64(c.handle), uint64(pages))
|
||||
return c.error(r)
|
||||
rc := res_t(c.call("sqlite3_wal_autocheckpoint", stk_t(c.handle), stk_t(pages)))
|
||||
return c.error(rc)
|
||||
}
|
||||
|
||||
// WALHook registers a callback function to be invoked
|
||||
@@ -290,15 +290,15 @@ func (c *Conn) WALAutoCheckpoint(pages int) error {
|
||||
//
|
||||
// https://sqlite.org/c3ref/wal_hook.html
|
||||
func (c *Conn) WALHook(cb func(db *Conn, schema string, pages int) error) {
|
||||
var enable uint64
|
||||
var enable int32
|
||||
if cb != nil {
|
||||
enable = 1
|
||||
}
|
||||
c.call("sqlite3_wal_hook_go", uint64(c.handle), enable)
|
||||
c.call("sqlite3_wal_hook_go", stk_t(c.handle), stk_t(enable))
|
||||
c.wal = cb
|
||||
}
|
||||
|
||||
func walCallback(ctx context.Context, mod api.Module, _, pDB, zSchema uint32, pages int32) (rc uint32) {
|
||||
func walCallback(ctx context.Context, mod api.Module, _, pDB, zSchema ptr_t, pages int32) (rc res_t) {
|
||||
if c, ok := ctx.Value(connKey{}).(*Conn); ok && c.handle == pDB && c.wal != nil {
|
||||
schema := util.ReadString(mod, zSchema, _MAX_NAME)
|
||||
err := c.wal(c, schema, int(pages))
|
||||
@@ -311,15 +311,15 @@ func walCallback(ctx context.Context, mod api.Module, _, pDB, zSchema uint32, pa
|
||||
//
|
||||
// https://sqlite.org/c3ref/autovacuum_pages.html
|
||||
func (c *Conn) AutoVacuumPages(cb func(schema string, dbPages, freePages, bytesPerPage uint) uint) error {
|
||||
var funcPtr uint32
|
||||
var funcPtr ptr_t
|
||||
if cb != nil {
|
||||
funcPtr = util.AddHandle(c.ctx, cb)
|
||||
}
|
||||
r := c.call("sqlite3_autovacuum_pages_go", uint64(c.handle), uint64(funcPtr))
|
||||
return c.error(r)
|
||||
rc := res_t(c.call("sqlite3_autovacuum_pages_go", stk_t(c.handle), stk_t(funcPtr)))
|
||||
return c.error(rc)
|
||||
}
|
||||
|
||||
func autoVacuumCallback(ctx context.Context, mod api.Module, pApp, zSchema, nDbPage, nFreePage, nBytePerPage uint32) uint32 {
|
||||
func autoVacuumCallback(ctx context.Context, mod api.Module, pApp, zSchema ptr_t, nDbPage, nFreePage, nBytePerPage uint32) uint32 {
|
||||
fn := util.GetHandle(ctx, pApp).(func(schema string, dbPages, freePages, bytesPerPage uint) uint)
|
||||
schema := util.ReadString(mod, zSchema, _MAX_NAME)
|
||||
return uint32(fn(schema, uint(nDbPage), uint(nFreePage), uint(nBytePerPage)))
|
||||
@@ -329,14 +329,14 @@ func autoVacuumCallback(ctx context.Context, mod api.Module, pApp, zSchema, nDbP
|
||||
//
|
||||
// https://sqlite.org/c3ref/hard_heap_limit64.html
|
||||
func (c *Conn) SoftHeapLimit(n int64) int64 {
|
||||
return int64(c.call("sqlite3_soft_heap_limit64", uint64(n)))
|
||||
return int64(c.call("sqlite3_soft_heap_limit64", stk_t(n)))
|
||||
}
|
||||
|
||||
// HardHeapLimit imposes a hard limit on heap size.
|
||||
//
|
||||
// https://sqlite.org/c3ref/hard_heap_limit64.html
|
||||
func (c *Conn) HardHeapLimit(n int64) int64 {
|
||||
return int64(c.call("sqlite3_hard_heap_limit64", uint64(n)))
|
||||
return int64(c.call("sqlite3_hard_heap_limit64", stk_t(n)))
|
||||
}
|
||||
|
||||
// EnableChecksums enables checksums on a database.
|
||||
|
||||
147
conn.go
147
conn.go
@@ -35,11 +35,11 @@ type Conn struct {
|
||||
update func(AuthorizerActionCode, string, string, int64)
|
||||
commit func() bool
|
||||
rollback func()
|
||||
arena arena
|
||||
|
||||
busy1st time.Time
|
||||
busylst time.Time
|
||||
handle uint32
|
||||
arena arena
|
||||
handle ptr_t
|
||||
}
|
||||
|
||||
// Open calls [OpenFlags] with [OPEN_READWRITE], [OPEN_CREATE] and [OPEN_URI].
|
||||
@@ -70,7 +70,7 @@ func OpenFlags(filename string, flags OpenFlag) (*Conn, error) {
|
||||
|
||||
type connKey = util.ConnKey
|
||||
|
||||
func newConn(ctx context.Context, filename string, flags OpenFlag) (res *Conn, _ error) {
|
||||
func newConn(ctx context.Context, filename string, flags OpenFlag) (ret *Conn, _ error) {
|
||||
err := ctx.Err()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -82,7 +82,7 @@ func newConn(ctx context.Context, filename string, flags OpenFlag) (res *Conn, _
|
||||
return nil, err
|
||||
}
|
||||
defer func() {
|
||||
if res == nil {
|
||||
if ret == nil {
|
||||
c.Close()
|
||||
c.sqlite.close()
|
||||
} else {
|
||||
@@ -91,7 +91,7 @@ func newConn(ctx context.Context, filename string, flags OpenFlag) (res *Conn, _
|
||||
}()
|
||||
|
||||
c.ctx = context.WithValue(c.ctx, connKey{}, c)
|
||||
c.arena = c.newArena(1024)
|
||||
c.arena = c.newArena()
|
||||
c.handle, err = c.openDB(filename, flags)
|
||||
if err == nil {
|
||||
err = initExtensions(c)
|
||||
@@ -102,21 +102,21 @@ func newConn(ctx context.Context, filename string, flags OpenFlag) (res *Conn, _
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func (c *Conn) openDB(filename string, flags OpenFlag) (uint32, error) {
|
||||
func (c *Conn) openDB(filename string, flags OpenFlag) (ptr_t, error) {
|
||||
defer c.arena.mark()()
|
||||
connPtr := c.arena.new(ptrlen)
|
||||
namePtr := c.arena.string(filename)
|
||||
|
||||
flags |= OPEN_EXRESCODE
|
||||
r := c.call("sqlite3_open_v2", uint64(namePtr), uint64(connPtr), uint64(flags), 0)
|
||||
rc := res_t(c.call("sqlite3_open_v2", stk_t(namePtr), stk_t(connPtr), stk_t(flags), 0))
|
||||
|
||||
handle := util.ReadUint32(c.mod, connPtr)
|
||||
if err := c.sqlite.error(r, handle); err != nil {
|
||||
handle := util.Read32[ptr_t](c.mod, connPtr)
|
||||
if err := c.sqlite.error(rc, handle); err != nil {
|
||||
c.closeDB(handle)
|
||||
return 0, err
|
||||
}
|
||||
|
||||
c.call("sqlite3_progress_handler_go", uint64(handle), 100)
|
||||
c.call("sqlite3_progress_handler_go", stk_t(handle), 100)
|
||||
if flags|OPEN_URI != 0 && strings.HasPrefix(filename, "file:") {
|
||||
var pragmas strings.Builder
|
||||
if _, after, ok := strings.Cut(filename, "?"); ok {
|
||||
@@ -130,8 +130,8 @@ func (c *Conn) openDB(filename string, flags OpenFlag) (uint32, error) {
|
||||
if pragmas.Len() != 0 {
|
||||
c.checkInterrupt(handle)
|
||||
pragmaPtr := c.arena.string(pragmas.String())
|
||||
r := c.call("sqlite3_exec", uint64(handle), uint64(pragmaPtr), 0, 0, 0)
|
||||
if err := c.sqlite.error(r, handle, pragmas.String()); err != nil {
|
||||
rc := res_t(c.call("sqlite3_exec", stk_t(handle), stk_t(pragmaPtr), 0, 0, 0))
|
||||
if err := c.sqlite.error(rc, handle, pragmas.String()); err != nil {
|
||||
err = fmt.Errorf("sqlite3: invalid _pragma: %w", err)
|
||||
c.closeDB(handle)
|
||||
return 0, err
|
||||
@@ -141,9 +141,9 @@ func (c *Conn) openDB(filename string, flags OpenFlag) (uint32, error) {
|
||||
return handle, nil
|
||||
}
|
||||
|
||||
func (c *Conn) closeDB(handle uint32) {
|
||||
r := c.call("sqlite3_close_v2", uint64(handle))
|
||||
if err := c.sqlite.error(r, handle); err != nil {
|
||||
func (c *Conn) closeDB(handle ptr_t) {
|
||||
rc := res_t(c.call("sqlite3_close_v2", stk_t(handle)))
|
||||
if err := c.sqlite.error(rc, handle); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
@@ -165,8 +165,8 @@ func (c *Conn) Close() error {
|
||||
c.pending.Close()
|
||||
c.pending = nil
|
||||
|
||||
r := c.call("sqlite3_close", uint64(c.handle))
|
||||
if err := c.error(r); err != nil {
|
||||
rc := res_t(c.call("sqlite3_close", stk_t(c.handle)))
|
||||
if err := c.error(rc); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -183,8 +183,8 @@ func (c *Conn) Exec(sql string) error {
|
||||
sqlPtr := c.arena.string(sql)
|
||||
|
||||
c.checkInterrupt(c.handle)
|
||||
r := c.call("sqlite3_exec", uint64(c.handle), uint64(sqlPtr), 0, 0, 0)
|
||||
return c.error(r, sql)
|
||||
rc := res_t(c.call("sqlite3_exec", stk_t(c.handle), stk_t(sqlPtr), 0, 0, 0))
|
||||
return c.error(rc, sql)
|
||||
}
|
||||
|
||||
// Prepare calls [Conn.PrepareFlags] with no flags.
|
||||
@@ -209,17 +209,17 @@ func (c *Conn) PrepareFlags(sql string, flags PrepareFlag) (stmt *Stmt, tail str
|
||||
sqlPtr := c.arena.string(sql)
|
||||
|
||||
c.checkInterrupt(c.handle)
|
||||
r := c.call("sqlite3_prepare_v3", uint64(c.handle),
|
||||
uint64(sqlPtr), uint64(len(sql)+1), uint64(flags),
|
||||
uint64(stmtPtr), uint64(tailPtr))
|
||||
rc := res_t(c.call("sqlite3_prepare_v3", stk_t(c.handle),
|
||||
stk_t(sqlPtr), stk_t(len(sql)+1), stk_t(flags),
|
||||
stk_t(stmtPtr), stk_t(tailPtr)))
|
||||
|
||||
stmt = &Stmt{c: c}
|
||||
stmt.handle = util.ReadUint32(c.mod, stmtPtr)
|
||||
if sql := sql[util.ReadUint32(c.mod, tailPtr)-sqlPtr:]; sql != "" {
|
||||
stmt.handle = util.Read32[ptr_t](c.mod, stmtPtr)
|
||||
if sql := sql[util.Read32[ptr_t](c.mod, tailPtr)-sqlPtr:]; sql != "" {
|
||||
tail = sql
|
||||
}
|
||||
|
||||
if err := c.error(r, sql); err != nil {
|
||||
if err := c.error(rc, sql); err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
if stmt.handle == 0 {
|
||||
@@ -233,9 +233,7 @@ func (c *Conn) PrepareFlags(sql string, flags PrepareFlag) (stmt *Stmt, tail str
|
||||
//
|
||||
// https://sqlite.org/c3ref/db_name.html
|
||||
func (c *Conn) DBName(n int) string {
|
||||
r := c.call("sqlite3_db_name", uint64(c.handle), uint64(n))
|
||||
|
||||
ptr := uint32(r)
|
||||
ptr := ptr_t(c.call("sqlite3_db_name", stk_t(c.handle), stk_t(n)))
|
||||
if ptr == 0 {
|
||||
return ""
|
||||
}
|
||||
@@ -246,34 +244,34 @@ func (c *Conn) DBName(n int) string {
|
||||
//
|
||||
// https://sqlite.org/c3ref/db_filename.html
|
||||
func (c *Conn) Filename(schema string) *vfs.Filename {
|
||||
var ptr uint32
|
||||
var ptr ptr_t
|
||||
if schema != "" {
|
||||
defer c.arena.mark()()
|
||||
ptr = c.arena.string(schema)
|
||||
}
|
||||
r := c.call("sqlite3_db_filename", uint64(c.handle), uint64(ptr))
|
||||
return vfs.GetFilename(c.ctx, c.mod, uint32(r), vfs.OPEN_MAIN_DB)
|
||||
ptr = ptr_t(c.call("sqlite3_db_filename", stk_t(c.handle), stk_t(ptr)))
|
||||
return vfs.GetFilename(c.ctx, c.mod, ptr, vfs.OPEN_MAIN_DB)
|
||||
}
|
||||
|
||||
// ReadOnly determines if a database is read-only.
|
||||
//
|
||||
// https://sqlite.org/c3ref/db_readonly.html
|
||||
func (c *Conn) ReadOnly(schema string) (ro bool, ok bool) {
|
||||
var ptr uint32
|
||||
var ptr ptr_t
|
||||
if schema != "" {
|
||||
defer c.arena.mark()()
|
||||
ptr = c.arena.string(schema)
|
||||
}
|
||||
r := c.call("sqlite3_db_readonly", uint64(c.handle), uint64(ptr))
|
||||
return int32(r) > 0, int32(r) < 0
|
||||
b := int32(c.call("sqlite3_db_readonly", stk_t(c.handle), stk_t(ptr)))
|
||||
return b > 0, b < 0
|
||||
}
|
||||
|
||||
// GetAutocommit tests the connection for auto-commit mode.
|
||||
//
|
||||
// https://sqlite.org/c3ref/get_autocommit.html
|
||||
func (c *Conn) GetAutocommit() bool {
|
||||
r := c.call("sqlite3_get_autocommit", uint64(c.handle))
|
||||
return r != 0
|
||||
b := int32(c.call("sqlite3_get_autocommit", stk_t(c.handle)))
|
||||
return b != 0
|
||||
}
|
||||
|
||||
// LastInsertRowID returns the rowid of the most recent successful INSERT
|
||||
@@ -281,8 +279,7 @@ func (c *Conn) GetAutocommit() bool {
|
||||
//
|
||||
// https://sqlite.org/c3ref/last_insert_rowid.html
|
||||
func (c *Conn) LastInsertRowID() int64 {
|
||||
r := c.call("sqlite3_last_insert_rowid", uint64(c.handle))
|
||||
return int64(r)
|
||||
return int64(c.call("sqlite3_last_insert_rowid", stk_t(c.handle)))
|
||||
}
|
||||
|
||||
// SetLastInsertRowID allows the application to set the value returned by
|
||||
@@ -290,7 +287,7 @@ func (c *Conn) LastInsertRowID() int64 {
|
||||
//
|
||||
// https://sqlite.org/c3ref/set_last_insert_rowid.html
|
||||
func (c *Conn) SetLastInsertRowID(id int64) {
|
||||
c.call("sqlite3_set_last_insert_rowid", uint64(c.handle), uint64(id))
|
||||
c.call("sqlite3_set_last_insert_rowid", stk_t(c.handle), stk_t(id))
|
||||
}
|
||||
|
||||
// Changes returns the number of rows modified, inserted or deleted
|
||||
@@ -299,8 +296,7 @@ func (c *Conn) SetLastInsertRowID(id int64) {
|
||||
//
|
||||
// https://sqlite.org/c3ref/changes.html
|
||||
func (c *Conn) Changes() int64 {
|
||||
r := c.call("sqlite3_changes64", uint64(c.handle))
|
||||
return int64(r)
|
||||
return int64(c.call("sqlite3_changes64", stk_t(c.handle)))
|
||||
}
|
||||
|
||||
// TotalChanges returns the number of rows modified, inserted or deleted
|
||||
@@ -309,16 +305,15 @@ func (c *Conn) Changes() int64 {
|
||||
//
|
||||
// https://sqlite.org/c3ref/total_changes.html
|
||||
func (c *Conn) TotalChanges() int64 {
|
||||
r := c.call("sqlite3_total_changes64", uint64(c.handle))
|
||||
return int64(r)
|
||||
return int64(c.call("sqlite3_total_changes64", stk_t(c.handle)))
|
||||
}
|
||||
|
||||
// ReleaseMemory frees memory used by a database connection.
|
||||
//
|
||||
// https://sqlite.org/c3ref/db_release_memory.html
|
||||
func (c *Conn) ReleaseMemory() error {
|
||||
r := c.call("sqlite3_db_release_memory", uint64(c.handle))
|
||||
return c.error(r)
|
||||
rc := res_t(c.call("sqlite3_db_release_memory", stk_t(c.handle)))
|
||||
return c.error(rc)
|
||||
}
|
||||
|
||||
// GetInterrupt gets the context set with [Conn.SetInterrupt].
|
||||
@@ -354,10 +349,10 @@ func (c *Conn) SetInterrupt(ctx context.Context) (old context.Context) {
|
||||
defer c.arena.mark()()
|
||||
stmtPtr := c.arena.new(ptrlen)
|
||||
loopPtr := c.arena.string(`WITH RECURSIVE c(x) AS (VALUES(0) UNION ALL SELECT x FROM c) SELECT x FROM c`)
|
||||
c.call("sqlite3_prepare_v3", uint64(c.handle), uint64(loopPtr), math.MaxUint64,
|
||||
uint64(PREPARE_PERSISTENT), uint64(stmtPtr), 0)
|
||||
c.call("sqlite3_prepare_v3", stk_t(c.handle), stk_t(loopPtr), math.MaxUint64,
|
||||
stk_t(PREPARE_PERSISTENT), stk_t(stmtPtr), 0)
|
||||
c.pending = &Stmt{c: c}
|
||||
c.pending.handle = util.ReadUint32(c.mod, stmtPtr)
|
||||
c.pending.handle = util.Read32[ptr_t](c.mod, stmtPtr)
|
||||
}
|
||||
|
||||
if old.Done() != nil && ctx.Err() == nil {
|
||||
@@ -369,13 +364,13 @@ func (c *Conn) SetInterrupt(ctx context.Context) (old context.Context) {
|
||||
return old
|
||||
}
|
||||
|
||||
func (c *Conn) checkInterrupt(handle uint32) {
|
||||
func (c *Conn) checkInterrupt(handle ptr_t) {
|
||||
if c.interrupt.Err() != nil {
|
||||
c.call("sqlite3_interrupt", uint64(handle))
|
||||
c.call("sqlite3_interrupt", stk_t(handle))
|
||||
}
|
||||
}
|
||||
|
||||
func progressCallback(ctx context.Context, mod api.Module, _ uint32) (interrupt uint32) {
|
||||
func progressCallback(ctx context.Context, mod api.Module, _ ptr_t) (interrupt int32) {
|
||||
if c, ok := ctx.Value(connKey{}).(*Conn); ok {
|
||||
if c.interrupt.Done() != nil {
|
||||
runtime.Gosched()
|
||||
@@ -392,11 +387,11 @@ func progressCallback(ctx context.Context, mod api.Module, _ uint32) (interrupt
|
||||
// https://sqlite.org/c3ref/busy_timeout.html
|
||||
func (c *Conn) BusyTimeout(timeout time.Duration) error {
|
||||
ms := min((timeout+time.Millisecond-1)/time.Millisecond, math.MaxInt32)
|
||||
r := c.call("sqlite3_busy_timeout", uint64(c.handle), uint64(ms))
|
||||
return c.error(r)
|
||||
rc := res_t(c.call("sqlite3_busy_timeout", stk_t(c.handle), stk_t(ms)))
|
||||
return c.error(rc)
|
||||
}
|
||||
|
||||
func timeoutCallback(ctx context.Context, mod api.Module, count, tmout int32) (retry uint32) {
|
||||
func timeoutCallback(ctx context.Context, mod api.Module, count, tmout int32) (retry int32) {
|
||||
// https://fractaledmind.github.io/2024/04/15/sqlite-on-rails-the-how-and-why-of-optimal-performance/
|
||||
if c, ok := ctx.Value(connKey{}).(*Conn); ok && c.interrupt.Err() == nil {
|
||||
switch {
|
||||
@@ -419,19 +414,19 @@ func timeoutCallback(ctx context.Context, mod api.Module, count, tmout int32) (r
|
||||
//
|
||||
// https://sqlite.org/c3ref/busy_handler.html
|
||||
func (c *Conn) BusyHandler(cb func(ctx context.Context, count int) (retry bool)) error {
|
||||
var enable uint64
|
||||
var enable int32
|
||||
if cb != nil {
|
||||
enable = 1
|
||||
}
|
||||
r := c.call("sqlite3_busy_handler_go", uint64(c.handle), enable)
|
||||
if err := c.error(r); err != nil {
|
||||
rc := res_t(c.call("sqlite3_busy_handler_go", stk_t(c.handle), stk_t(enable)))
|
||||
if err := c.error(rc); err != nil {
|
||||
return err
|
||||
}
|
||||
c.busy = cb
|
||||
return nil
|
||||
}
|
||||
|
||||
func busyCallback(ctx context.Context, mod api.Module, pDB uint32, count int32) (retry uint32) {
|
||||
func busyCallback(ctx context.Context, mod api.Module, pDB ptr_t, count int32) (retry int32) {
|
||||
if c, ok := ctx.Value(connKey{}).(*Conn); ok && c.handle == pDB && c.busy != nil {
|
||||
interrupt := c.interrupt
|
||||
if interrupt == nil {
|
||||
@@ -452,16 +447,16 @@ func (c *Conn) Status(op DBStatus, reset bool) (current, highwater int, err erro
|
||||
hiPtr := c.arena.new(intlen)
|
||||
curPtr := c.arena.new(intlen)
|
||||
|
||||
var i uint64
|
||||
var i int32
|
||||
if reset {
|
||||
i = 1
|
||||
}
|
||||
|
||||
r := c.call("sqlite3_db_status", uint64(c.handle),
|
||||
uint64(op), uint64(curPtr), uint64(hiPtr), i)
|
||||
if err = c.error(r); err == nil {
|
||||
current = int(util.ReadUint32(c.mod, curPtr))
|
||||
highwater = int(util.ReadUint32(c.mod, hiPtr))
|
||||
rc := res_t(c.call("sqlite3_db_status", stk_t(c.handle),
|
||||
stk_t(op), stk_t(curPtr), stk_t(hiPtr), stk_t(i)))
|
||||
if err = c.error(rc); err == nil {
|
||||
current = int(util.Read32[int32](c.mod, curPtr))
|
||||
highwater = int(util.Read32[int32](c.mod, hiPtr))
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -472,7 +467,7 @@ func (c *Conn) Status(op DBStatus, reset bool) (current, highwater int, err erro
|
||||
func (c *Conn) TableColumnMetadata(schema, table, column string) (declType, collSeq string, notNull, primaryKey, autoInc bool, err error) {
|
||||
defer c.arena.mark()()
|
||||
|
||||
var schemaPtr, columnPtr uint32
|
||||
var schemaPtr, columnPtr ptr_t
|
||||
declTypePtr := c.arena.new(ptrlen)
|
||||
collSeqPtr := c.arena.new(ptrlen)
|
||||
notNullPtr := c.arena.new(ptrlen)
|
||||
@@ -486,25 +481,25 @@ func (c *Conn) TableColumnMetadata(schema, table, column string) (declType, coll
|
||||
columnPtr = c.arena.string(column)
|
||||
}
|
||||
|
||||
r := c.call("sqlite3_table_column_metadata", uint64(c.handle),
|
||||
uint64(schemaPtr), uint64(tablePtr), uint64(columnPtr),
|
||||
uint64(declTypePtr), uint64(collSeqPtr),
|
||||
uint64(notNullPtr), uint64(primaryKeyPtr), uint64(autoIncPtr))
|
||||
if err = c.error(r); err == nil && column != "" {
|
||||
if ptr := util.ReadUint32(c.mod, declTypePtr); ptr != 0 {
|
||||
rc := res_t(c.call("sqlite3_table_column_metadata", stk_t(c.handle),
|
||||
stk_t(schemaPtr), stk_t(tablePtr), stk_t(columnPtr),
|
||||
stk_t(declTypePtr), stk_t(collSeqPtr),
|
||||
stk_t(notNullPtr), stk_t(primaryKeyPtr), stk_t(autoIncPtr)))
|
||||
if err = c.error(rc); err == nil && column != "" {
|
||||
if ptr := util.Read32[ptr_t](c.mod, declTypePtr); ptr != 0 {
|
||||
declType = util.ReadString(c.mod, ptr, _MAX_NAME)
|
||||
}
|
||||
if ptr := util.ReadUint32(c.mod, collSeqPtr); ptr != 0 {
|
||||
if ptr := util.Read32[ptr_t](c.mod, collSeqPtr); ptr != 0 {
|
||||
collSeq = util.ReadString(c.mod, ptr, _MAX_NAME)
|
||||
}
|
||||
notNull = util.ReadUint32(c.mod, notNullPtr) != 0
|
||||
autoInc = util.ReadUint32(c.mod, autoIncPtr) != 0
|
||||
primaryKey = util.ReadUint32(c.mod, primaryKeyPtr) != 0
|
||||
notNull = util.ReadBool(c.mod, notNullPtr)
|
||||
autoInc = util.ReadBool(c.mod, autoIncPtr)
|
||||
primaryKey = util.ReadBool(c.mod, primaryKeyPtr)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c *Conn) error(rc uint64, sql ...string) error {
|
||||
func (c *Conn) error(rc res_t, sql ...string) error {
|
||||
return c.sqlite.error(rc, c.handle, sql...)
|
||||
}
|
||||
|
||||
|
||||
21
const.go
21
const.go
@@ -1,6 +1,10 @@
|
||||
package sqlite3
|
||||
|
||||
import "strconv"
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"github.com/ncruces/go-sqlite3/internal/util"
|
||||
)
|
||||
|
||||
const (
|
||||
_OK = 0 /* Successful result */
|
||||
@@ -12,8 +16,14 @@ const (
|
||||
_MAX_SQL_LENGTH = 1e9
|
||||
_MAX_FUNCTION_ARG = 100
|
||||
|
||||
ptrlen = 4
|
||||
intlen = 4
|
||||
ptrlen = util.PtrLen
|
||||
intlen = util.IntLen
|
||||
)
|
||||
|
||||
type (
|
||||
stk_t = util.Stk_t
|
||||
ptr_t = util.Ptr_t
|
||||
res_t = util.Res_t
|
||||
)
|
||||
|
||||
// ErrorCode is a result code that [Error.Code] might return.
|
||||
@@ -249,7 +259,10 @@ const (
|
||||
DBCONFIG_TRUSTED_SCHEMA DBConfig = 1017
|
||||
DBCONFIG_STMT_SCANSTATUS DBConfig = 1018
|
||||
DBCONFIG_REVERSE_SCANORDER DBConfig = 1019
|
||||
// DBCONFIG_MAX DBConfig = 1019
|
||||
DBCONFIG_ENABLE_ATTACH_CREATE DBConfig = 1020
|
||||
DBCONFIG_ENABLE_ATTACH_WRITE DBConfig = 1021
|
||||
DBCONFIG_ENABLE_COMMENTS DBConfig = 1022
|
||||
// DBCONFIG_MAX DBConfig = 1022
|
||||
)
|
||||
|
||||
// FcntlOpcode are the available opcodes for [Conn.FileControl].
|
||||
|
||||
40
context.go
40
context.go
@@ -15,7 +15,7 @@ import (
|
||||
// https://sqlite.org/c3ref/context.html
|
||||
type Context struct {
|
||||
c *Conn
|
||||
handle uint32
|
||||
handle ptr_t
|
||||
}
|
||||
|
||||
// Conn returns the database connection of the
|
||||
@@ -32,14 +32,14 @@ func (ctx Context) Conn() *Conn {
|
||||
// https://sqlite.org/c3ref/get_auxdata.html
|
||||
func (ctx Context) SetAuxData(n int, data any) {
|
||||
ptr := util.AddHandle(ctx.c.ctx, data)
|
||||
ctx.c.call("sqlite3_set_auxdata_go", uint64(ctx.handle), uint64(n), uint64(ptr))
|
||||
ctx.c.call("sqlite3_set_auxdata_go", stk_t(ctx.handle), stk_t(n), stk_t(ptr))
|
||||
}
|
||||
|
||||
// GetAuxData returns metadata for argument n of the function.
|
||||
//
|
||||
// https://sqlite.org/c3ref/get_auxdata.html
|
||||
func (ctx Context) GetAuxData(n int) any {
|
||||
ptr := uint32(ctx.c.call("sqlite3_get_auxdata", uint64(ctx.handle), uint64(n)))
|
||||
ptr := ptr_t(ctx.c.call("sqlite3_get_auxdata", stk_t(ctx.handle), stk_t(n)))
|
||||
return util.GetHandle(ctx.c.ctx, ptr)
|
||||
}
|
||||
|
||||
@@ -68,7 +68,7 @@ func (ctx Context) ResultInt(value int) {
|
||||
// https://sqlite.org/c3ref/result_blob.html
|
||||
func (ctx Context) ResultInt64(value int64) {
|
||||
ctx.c.call("sqlite3_result_int64",
|
||||
uint64(ctx.handle), uint64(value))
|
||||
stk_t(ctx.handle), stk_t(value))
|
||||
}
|
||||
|
||||
// ResultFloat sets the result of the function to a float64.
|
||||
@@ -76,7 +76,7 @@ func (ctx Context) ResultInt64(value int64) {
|
||||
// https://sqlite.org/c3ref/result_blob.html
|
||||
func (ctx Context) ResultFloat(value float64) {
|
||||
ctx.c.call("sqlite3_result_double",
|
||||
uint64(ctx.handle), math.Float64bits(value))
|
||||
stk_t(ctx.handle), stk_t(math.Float64bits(value)))
|
||||
}
|
||||
|
||||
// ResultText sets the result of the function to a string.
|
||||
@@ -85,7 +85,7 @@ func (ctx Context) ResultFloat(value float64) {
|
||||
func (ctx Context) ResultText(value string) {
|
||||
ptr := ctx.c.newString(value)
|
||||
ctx.c.call("sqlite3_result_text_go",
|
||||
uint64(ctx.handle), uint64(ptr), uint64(len(value)))
|
||||
stk_t(ctx.handle), stk_t(ptr), stk_t(len(value)))
|
||||
}
|
||||
|
||||
// ResultRawText sets the text result of the function to a []byte.
|
||||
@@ -95,7 +95,7 @@ func (ctx Context) ResultText(value string) {
|
||||
func (ctx Context) ResultRawText(value []byte) {
|
||||
ptr := ctx.c.newBytes(value)
|
||||
ctx.c.call("sqlite3_result_text_go",
|
||||
uint64(ctx.handle), uint64(ptr), uint64(len(value)))
|
||||
stk_t(ctx.handle), stk_t(ptr), stk_t(len(value)))
|
||||
}
|
||||
|
||||
// ResultBlob sets the result of the function to a []byte.
|
||||
@@ -105,7 +105,7 @@ func (ctx Context) ResultRawText(value []byte) {
|
||||
func (ctx Context) ResultBlob(value []byte) {
|
||||
ptr := ctx.c.newBytes(value)
|
||||
ctx.c.call("sqlite3_result_blob_go",
|
||||
uint64(ctx.handle), uint64(ptr), uint64(len(value)))
|
||||
stk_t(ctx.handle), stk_t(ptr), stk_t(len(value)))
|
||||
}
|
||||
|
||||
// ResultZeroBlob sets the result of the function to a zero-filled, length n BLOB.
|
||||
@@ -113,7 +113,7 @@ func (ctx Context) ResultBlob(value []byte) {
|
||||
// https://sqlite.org/c3ref/result_blob.html
|
||||
func (ctx Context) ResultZeroBlob(n int64) {
|
||||
ctx.c.call("sqlite3_result_zeroblob64",
|
||||
uint64(ctx.handle), uint64(n))
|
||||
stk_t(ctx.handle), stk_t(n))
|
||||
}
|
||||
|
||||
// ResultNull sets the result of the function to NULL.
|
||||
@@ -121,7 +121,7 @@ func (ctx Context) ResultZeroBlob(n int64) {
|
||||
// https://sqlite.org/c3ref/result_blob.html
|
||||
func (ctx Context) ResultNull() {
|
||||
ctx.c.call("sqlite3_result_null",
|
||||
uint64(ctx.handle))
|
||||
stk_t(ctx.handle))
|
||||
}
|
||||
|
||||
// ResultTime sets the result of the function to a [time.Time].
|
||||
@@ -146,14 +146,14 @@ func (ctx Context) ResultTime(value time.Time, format TimeFormat) {
|
||||
}
|
||||
|
||||
func (ctx Context) resultRFC3339Nano(value time.Time) {
|
||||
const maxlen = uint64(len(time.RFC3339Nano)) + 5
|
||||
const maxlen = int64(len(time.RFC3339Nano)) + 5
|
||||
|
||||
ptr := ctx.c.new(maxlen)
|
||||
buf := util.View(ctx.c.mod, ptr, maxlen)
|
||||
buf = value.AppendFormat(buf[:0], time.RFC3339Nano)
|
||||
|
||||
ctx.c.call("sqlite3_result_text_go",
|
||||
uint64(ctx.handle), uint64(ptr), uint64(len(buf)))
|
||||
stk_t(ctx.handle), stk_t(ptr), stk_t(len(buf)))
|
||||
}
|
||||
|
||||
// ResultPointer sets the result of the function to NULL, just like [Context.ResultNull],
|
||||
@@ -164,7 +164,7 @@ func (ctx Context) resultRFC3339Nano(value time.Time) {
|
||||
func (ctx Context) ResultPointer(ptr any) {
|
||||
valPtr := util.AddHandle(ctx.c.ctx, ptr)
|
||||
ctx.c.call("sqlite3_result_pointer_go",
|
||||
uint64(ctx.handle), uint64(valPtr))
|
||||
stk_t(ctx.handle), stk_t(valPtr))
|
||||
}
|
||||
|
||||
// ResultJSON sets the result of the function to the JSON encoding of value.
|
||||
@@ -188,7 +188,7 @@ func (ctx Context) ResultValue(value Value) {
|
||||
return
|
||||
}
|
||||
ctx.c.call("sqlite3_result_value",
|
||||
uint64(ctx.handle), uint64(value.handle))
|
||||
stk_t(ctx.handle), stk_t(value.handle))
|
||||
}
|
||||
|
||||
// ResultError sets the result of the function an error.
|
||||
@@ -196,12 +196,12 @@ func (ctx Context) ResultValue(value Value) {
|
||||
// https://sqlite.org/c3ref/result_blob.html
|
||||
func (ctx Context) ResultError(err error) {
|
||||
if errors.Is(err, NOMEM) {
|
||||
ctx.c.call("sqlite3_result_error_nomem", uint64(ctx.handle))
|
||||
ctx.c.call("sqlite3_result_error_nomem", stk_t(ctx.handle))
|
||||
return
|
||||
}
|
||||
|
||||
if errors.Is(err, TOOBIG) {
|
||||
ctx.c.call("sqlite3_result_error_toobig", uint64(ctx.handle))
|
||||
ctx.c.call("sqlite3_result_error_toobig", stk_t(ctx.handle))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -210,11 +210,11 @@ func (ctx Context) ResultError(err error) {
|
||||
defer ctx.c.arena.mark()()
|
||||
ptr := ctx.c.arena.string(msg)
|
||||
ctx.c.call("sqlite3_result_error",
|
||||
uint64(ctx.handle), uint64(ptr), uint64(len(msg)))
|
||||
stk_t(ctx.handle), stk_t(ptr), stk_t(len(msg)))
|
||||
}
|
||||
if code != _OK {
|
||||
ctx.c.call("sqlite3_result_error_code",
|
||||
uint64(ctx.handle), uint64(code))
|
||||
stk_t(ctx.handle), stk_t(code))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -223,6 +223,6 @@ func (ctx Context) ResultError(err error) {
|
||||
//
|
||||
// https://sqlite.org/c3ref/vtab_nochange.html
|
||||
func (ctx Context) VTabNoChange() bool {
|
||||
r := ctx.c.call("sqlite3_vtab_nochange", uint64(ctx.handle))
|
||||
return r != 0
|
||||
b := int32(ctx.c.call("sqlite3_vtab_nochange", stk_t(ctx.handle)))
|
||||
return b != 0
|
||||
}
|
||||
|
||||
@@ -201,7 +201,7 @@ func (n *connector) Driver() driver.Driver {
|
||||
return &SQLite{}
|
||||
}
|
||||
|
||||
func (n *connector) Connect(ctx context.Context) (res driver.Conn, err error) {
|
||||
func (n *connector) Connect(ctx context.Context) (ret driver.Conn, err error) {
|
||||
c := &conn{
|
||||
txLock: n.txLock,
|
||||
tmRead: n.tmRead,
|
||||
@@ -213,7 +213,7 @@ func (n *connector) Connect(ctx context.Context) (res driver.Conn, err error) {
|
||||
return nil, err
|
||||
}
|
||||
defer func() {
|
||||
if res == nil {
|
||||
if ret == nil {
|
||||
c.Close()
|
||||
}
|
||||
}()
|
||||
@@ -466,8 +466,9 @@ func (s *stmt) ExecContext(ctx context.Context, args []driver.NamedValue) (drive
|
||||
old := s.Stmt.Conn().SetInterrupt(ctx)
|
||||
defer s.Stmt.Conn().SetInterrupt(old)
|
||||
|
||||
err = s.Stmt.Exec()
|
||||
s.Stmt.ClearBindings()
|
||||
err = errors.Join(
|
||||
s.Stmt.Exec(),
|
||||
s.Stmt.ClearBindings())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -604,8 +605,9 @@ var (
|
||||
)
|
||||
|
||||
func (r *rows) Close() error {
|
||||
r.Stmt.ClearBindings()
|
||||
return r.Stmt.Reset()
|
||||
return errors.Join(
|
||||
r.Stmt.Reset(),
|
||||
r.Stmt.ClearBindings())
|
||||
}
|
||||
|
||||
func (r *rows) Columns() []string {
|
||||
@@ -718,19 +720,19 @@ func (r *rows) ColumnTypeScanType(index int) (typ reflect.Type) {
|
||||
|
||||
switch scan {
|
||||
case _INT:
|
||||
return reflect.TypeOf(int64(0))
|
||||
return reflect.TypeFor[int64]()
|
||||
case _REAL:
|
||||
return reflect.TypeOf(float64(0))
|
||||
return reflect.TypeFor[float64]()
|
||||
case _TEXT:
|
||||
return reflect.TypeOf("")
|
||||
return reflect.TypeFor[string]()
|
||||
case _BLOB:
|
||||
return reflect.TypeOf([]byte{})
|
||||
return reflect.TypeFor[[]byte]()
|
||||
case _BOOL:
|
||||
return reflect.TypeOf(false)
|
||||
return reflect.TypeFor[bool]()
|
||||
case _TIME:
|
||||
return reflect.TypeOf(time.Time{})
|
||||
return reflect.TypeFor[time.Time]()
|
||||
default:
|
||||
return reflect.TypeOf((*any)(nil)).Elem()
|
||||
return reflect.TypeFor[any]()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -369,13 +369,13 @@ func Test_time(t *testing.T) {
|
||||
|
||||
func Test_ColumnType_ScanType(t *testing.T) {
|
||||
var (
|
||||
INT = reflect.TypeOf(int64(0))
|
||||
REAL = reflect.TypeOf(float64(0))
|
||||
TEXT = reflect.TypeOf("")
|
||||
BLOB = reflect.TypeOf([]byte{})
|
||||
BOOL = reflect.TypeOf(false)
|
||||
TIME = reflect.TypeOf(time.Time{})
|
||||
ANY = reflect.TypeOf((*any)(nil)).Elem()
|
||||
INT = reflect.TypeFor[int64]()
|
||||
REAL = reflect.TypeFor[float64]()
|
||||
TEXT = reflect.TypeFor[string]()
|
||||
BLOB = reflect.TypeFor[[]byte]()
|
||||
BOOL = reflect.TypeFor[bool]()
|
||||
TIME = reflect.TypeFor[time.Time]()
|
||||
ANY = reflect.TypeFor[any]()
|
||||
)
|
||||
|
||||
t.Parallel()
|
||||
|
||||
@@ -3,7 +3,7 @@ package driver
|
||||
import (
|
||||
"context"
|
||||
"database/sql/driver"
|
||||
"reflect"
|
||||
"slices"
|
||||
"testing"
|
||||
|
||||
_ "github.com/ncruces/go-sqlite3/embed"
|
||||
@@ -16,7 +16,7 @@ func Test_namedValues(t *testing.T) {
|
||||
{Ordinal: 2, Value: false},
|
||||
}
|
||||
got := namedValues([]driver.Value{true, false})
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
if !slices.Equal(got, want) {
|
||||
t.Errorf("got %v, want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Embeddable Wasm build of SQLite
|
||||
|
||||
This folder includes an embeddable Wasm build of SQLite 3.48.0 for use with
|
||||
This folder includes an embeddable Wasm build of SQLite 3.49.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.
@@ -53,7 +53,7 @@ func Test_bcw2(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if version != "3.48.0" {
|
||||
if version != "3.50.0" {
|
||||
t.Error(version)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,8 +13,8 @@ mkdir -p build/ext/
|
||||
cp "$ROOT"/sqlite3/*.[ch] build/
|
||||
cp "$ROOT"/sqlite3/*.patch build/
|
||||
|
||||
# https://sqlite.org/src/info/fab341c829554573
|
||||
curl -# https://sqlite.org/src/tarball/sqlite.tar.gz?r=fab341c8 | tar xz
|
||||
# https://sqlite.org/src/info/c09656c62155a6e8
|
||||
curl -# https://sqlite.org/src/tarball/sqlite.tar.gz?r=c09656c6 | tar xz
|
||||
|
||||
cd sqlite
|
||||
if [[ "$OSTYPE" == "msys" || "$OSTYPE" == "cygwin" ]]; then
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
module github.com/ncruces/go-sqlite3/embed/bcw2
|
||||
|
||||
go 1.21
|
||||
go 1.22.0
|
||||
|
||||
toolchain go1.23.0
|
||||
toolchain go1.24.0
|
||||
|
||||
require github.com/ncruces/go-sqlite3 v0.21.3
|
||||
require github.com/ncruces/go-sqlite3 v0.23.1
|
||||
|
||||
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
|
||||
github.com/ncruces/sort v0.1.5 // indirect
|
||||
github.com/tetratelabs/wazero v1.9.0 // indirect
|
||||
golang.org/x/sys v0.30.0 // indirect
|
||||
)
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
github.com/ncruces/go-sqlite3 v0.21.3 h1:hHkfNQLcbnxPJZhC/RGw9SwP3bfkv/Y0xUHWsr1CdMQ=
|
||||
github.com/ncruces/go-sqlite3 v0.21.3/go.mod h1:zxMOaSG5kFYVFK4xQa0pdwIszqxqJ0W0BxBgwdrNjuA=
|
||||
github.com/ncruces/go-sqlite3 v0.23.1 h1:zGAd76q+Tr18z/xKGatUlzBQdjR3J+rexfANUcjAgkY=
|
||||
github.com/ncruces/go-sqlite3 v0.23.1/go.mod h1:Xg3FyAZl25HcBSFmcbymdfoTqD7jRnBUmv1jSrbIjdE=
|
||||
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=
|
||||
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
|
||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||
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.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
|
||||
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
|
||||
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
|
||||
|
||||
@@ -19,7 +19,7 @@ func Test_init(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if version != "3.48.0" {
|
||||
if version != "3.49.1" {
|
||||
t.Error(version)
|
||||
}
|
||||
}
|
||||
|
||||
Binary file not shown.
16
error.go
16
error.go
@@ -15,7 +15,7 @@ type Error struct {
|
||||
str string
|
||||
msg string
|
||||
sql string
|
||||
code uint64
|
||||
code res_t
|
||||
}
|
||||
|
||||
// Code returns the primary error code for this error.
|
||||
@@ -146,27 +146,27 @@ func (e ExtendedErrorCode) Code() ErrorCode {
|
||||
return ErrorCode(e)
|
||||
}
|
||||
|
||||
func errorCode(err error, def ErrorCode) (msg string, code uint32) {
|
||||
func errorCode(err error, def ErrorCode) (msg string, code res_t) {
|
||||
switch code := err.(type) {
|
||||
case nil:
|
||||
return "", _OK
|
||||
case ErrorCode:
|
||||
return "", uint32(code)
|
||||
return "", res_t(code)
|
||||
case xErrorCode:
|
||||
return "", uint32(code)
|
||||
return "", res_t(code)
|
||||
case *Error:
|
||||
return code.msg, uint32(code.code)
|
||||
return code.msg, res_t(code.code)
|
||||
}
|
||||
|
||||
var ecode ErrorCode
|
||||
var xcode xErrorCode
|
||||
switch {
|
||||
case errors.As(err, &xcode):
|
||||
code = uint32(xcode)
|
||||
code = res_t(xcode)
|
||||
case errors.As(err, &ecode):
|
||||
code = uint32(ecode)
|
||||
code = res_t(ecode)
|
||||
default:
|
||||
code = uint32(def)
|
||||
code = res_t(def)
|
||||
}
|
||||
return err.Error(), code
|
||||
}
|
||||
|
||||
@@ -59,14 +59,14 @@ func TestError_Temporary(t *testing.T) {
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
code uint64
|
||||
code res_t
|
||||
want bool
|
||||
}{
|
||||
{"ERROR", uint64(ERROR), false},
|
||||
{"BUSY", uint64(BUSY), true},
|
||||
{"BUSY_RECOVERY", uint64(BUSY_RECOVERY), true},
|
||||
{"BUSY_SNAPSHOT", uint64(BUSY_SNAPSHOT), true},
|
||||
{"BUSY_TIMEOUT", uint64(BUSY_TIMEOUT), true},
|
||||
{"ERROR", res_t(ERROR), false},
|
||||
{"BUSY", res_t(BUSY), true},
|
||||
{"BUSY_RECOVERY", res_t(BUSY_RECOVERY), true},
|
||||
{"BUSY_SNAPSHOT", res_t(BUSY_SNAPSHOT), true},
|
||||
{"BUSY_TIMEOUT", res_t(BUSY_TIMEOUT), true},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
@@ -97,14 +97,14 @@ func TestError_Timeout(t *testing.T) {
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
code uint64
|
||||
code res_t
|
||||
want bool
|
||||
}{
|
||||
{"ERROR", uint64(ERROR), false},
|
||||
{"BUSY", uint64(BUSY), false},
|
||||
{"BUSY_RECOVERY", uint64(BUSY_RECOVERY), false},
|
||||
{"BUSY_SNAPSHOT", uint64(BUSY_SNAPSHOT), false},
|
||||
{"BUSY_TIMEOUT", uint64(BUSY_TIMEOUT), true},
|
||||
{"ERROR", res_t(ERROR), false},
|
||||
{"BUSY", res_t(BUSY), false},
|
||||
{"BUSY_RECOVERY", res_t(BUSY_RECOVERY), false},
|
||||
{"BUSY_SNAPSHOT", res_t(BUSY_SNAPSHOT), false},
|
||||
{"BUSY_TIMEOUT", res_t(BUSY_TIMEOUT), true},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
@@ -136,8 +136,8 @@ func Test_ErrorCode_Error(t *testing.T) {
|
||||
// Test all error codes.
|
||||
for i := 0; i == int(ErrorCode(i)); i++ {
|
||||
want := "sqlite3: "
|
||||
r := db.call("sqlite3_errstr", uint64(i))
|
||||
want += util.ReadString(db.mod, uint32(r), _MAX_NAME)
|
||||
ptr := ptr_t(db.call("sqlite3_errstr", stk_t(i)))
|
||||
want += util.ReadString(db.mod, ptr, _MAX_NAME)
|
||||
|
||||
got := ErrorCode(i).Error()
|
||||
if got != want {
|
||||
@@ -158,8 +158,8 @@ func Test_ExtendedErrorCode_Error(t *testing.T) {
|
||||
// Test all extended error codes.
|
||||
for i := 0; i == int(ExtendedErrorCode(i)); i++ {
|
||||
want := "sqlite3: "
|
||||
r := db.call("sqlite3_errstr", uint64(i))
|
||||
want += util.ReadString(db.mod, uint32(r), _MAX_NAME)
|
||||
ptr := ptr_t(db.call("sqlite3_errstr", stk_t(i)))
|
||||
want += util.ReadString(db.mod, ptr, _MAX_NAME)
|
||||
|
||||
got := ExtendedErrorCode(i).Error()
|
||||
if got != want {
|
||||
@@ -172,7 +172,7 @@ func Test_errorCode(t *testing.T) {
|
||||
tests := []struct {
|
||||
arg error
|
||||
wantMsg string
|
||||
wantCode uint32
|
||||
wantCode res_t
|
||||
}{
|
||||
{nil, "", _OK},
|
||||
{ERROR, "", util.ERROR},
|
||||
@@ -190,7 +190,7 @@ func Test_errorCode(t *testing.T) {
|
||||
if gotMsg != tt.wantMsg {
|
||||
t.Errorf("errorCode() gotMsg = %q, want %q", gotMsg, tt.wantMsg)
|
||||
}
|
||||
if gotCode != uint32(tt.wantCode) {
|
||||
if gotCode != tt.wantCode {
|
||||
t.Errorf("errorCode() gotCode = %d, want %d", gotCode, tt.wantCode)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -25,6 +25,8 @@ you can load into your database connections.
|
||||
creates [pivot tables](https://github.com/jakethaw/pivot_vtab).
|
||||
- [`github.com/ncruces/go-sqlite3/ext/regexp`](https://pkg.go.dev/github.com/ncruces/go-sqlite3/ext/regexp)
|
||||
provides regular expression functions.
|
||||
- [`github.com/ncruces/go-sqlite3/ext/serdes`](https://pkg.go.dev/github.com/ncruces/go-sqlite3/ext/serdes)
|
||||
(de)serializes databases.
|
||||
- [`github.com/ncruces/go-sqlite3/ext/statement`](https://pkg.go.dev/github.com/ncruces/go-sqlite3/ext/statement)
|
||||
creates [parameterized views](https://github.com/0x09/sqlite-statement-vtab).
|
||||
- [`github.com/ncruces/go-sqlite3/ext/stats`](https://pkg.go.dev/github.com/ncruces/go-sqlite3/ext/stats)
|
||||
@@ -34,4 +36,13 @@ you can load into your database connections.
|
||||
- [`github.com/ncruces/go-sqlite3/ext/uuid`](https://pkg.go.dev/github.com/ncruces/go-sqlite3/ext/uuid)
|
||||
generates [UUIDs](https://en.wikipedia.org/wiki/Universally_unique_identifier).
|
||||
- [`github.com/ncruces/go-sqlite3/ext/zorder`](https://pkg.go.dev/github.com/ncruces/go-sqlite3/ext/zorder)
|
||||
maps multidimensional data to one dimension.
|
||||
maps multidimensional data to one dimension.
|
||||
|
||||
### Pakages
|
||||
|
||||
These packages may also be useful to work with SQLite:
|
||||
|
||||
- [`github.com/ncruces/decimal`](https://pkg.go.dev/github.com/ncruces/decimal)
|
||||
decimal arithmetic.
|
||||
- [`github.com/ncruces/julianday`](https://pkg.go.dev/github.com/ncruces/julianday)
|
||||
Julian day math.
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"reflect"
|
||||
"slices"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
@@ -278,7 +278,7 @@ func Test_openblob(t *testing.T) {
|
||||
}
|
||||
|
||||
want := []string{"\xca\xfe", "\xba\xbe"}
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
if !slices.Equal(got, want) {
|
||||
t.Errorf("got %v, want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -232,7 +232,7 @@ func (b *bloom) Update(arg ...sqlite3.Value) (rowid int64, err error) {
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
for n := 0; n < b.hashes; n++ {
|
||||
for n := range b.hashes {
|
||||
hash := calcHash(n, blob)
|
||||
hash %= uint64(b.bytes * 8)
|
||||
bitpos := byte(hash % 8)
|
||||
|
||||
@@ -210,12 +210,14 @@ func (c *cursor) Filter(idxNum int, idxStr string, arg ...sqlite3.Value) error {
|
||||
c.nodes = []node{{root, 0}}
|
||||
set := util.Set[int64]{}
|
||||
set.Add(root)
|
||||
for i := 0; i < len(c.nodes); i++ {
|
||||
for i := range c.nodes {
|
||||
curr := c.nodes[i]
|
||||
if curr.depth >= maxDepth {
|
||||
continue
|
||||
}
|
||||
stmt.BindInt64(1, curr.id)
|
||||
if err := stmt.BindInt64(1, curr.id); err != nil {
|
||||
return err
|
||||
}
|
||||
for stmt.Step() {
|
||||
if stmt.ColumnType(0) == sqlite3.INTEGER {
|
||||
next := stmt.ColumnInt64(0)
|
||||
@@ -225,7 +227,9 @@ func (c *cursor) Filter(idxNum int, idxStr string, arg ...sqlite3.Value) error {
|
||||
}
|
||||
}
|
||||
}
|
||||
stmt.Reset()
|
||||
if err := stmt.Reset(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ func Register(db *sqlite3.Conn) error {
|
||||
// RegisterFS registers the CSV virtual table.
|
||||
// If a filename is specified, fsys is used to open the file.
|
||||
func RegisterFS(db *sqlite3.Conn, fsys fs.FS) error {
|
||||
declare := func(db *sqlite3.Conn, _, _, _ string, arg ...string) (res *table, err error) {
|
||||
declare := func(db *sqlite3.Conn, _, _, _ string, arg ...string) (_ *table, err error) {
|
||||
var (
|
||||
filename string
|
||||
data string
|
||||
@@ -214,7 +214,10 @@ func (c *cursor) Filter(idxNum int, idxStr string, arg ...sqlite3.Value) error {
|
||||
return err
|
||||
}
|
||||
if c.table.header {
|
||||
c.Next() // skip header
|
||||
err = c.Next() // skip header
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
c.rowID = 0
|
||||
return c.Next()
|
||||
|
||||
@@ -25,14 +25,14 @@ type table struct {
|
||||
cols []*sqlite3.Value
|
||||
}
|
||||
|
||||
func declare(db *sqlite3.Conn, _, _, _ string, arg ...string) (res *table, err error) {
|
||||
func declare(db *sqlite3.Conn, _, _, _ string, arg ...string) (ret *table, err error) {
|
||||
if len(arg) != 3 {
|
||||
return nil, fmt.Errorf("pivot: wrong number of arguments")
|
||||
}
|
||||
|
||||
t := &table{db: db}
|
||||
defer func() {
|
||||
if res == nil {
|
||||
if ret == nil {
|
||||
t.Close()
|
||||
}
|
||||
}()
|
||||
@@ -99,10 +99,11 @@ func declare(db *sqlite3.Conn, _, _, _ string, arg ...string) (res *table, err e
|
||||
}
|
||||
|
||||
func (t *table) Close() error {
|
||||
var errs []error
|
||||
for _, c := range t.cols {
|
||||
c.Close()
|
||||
errs = append(errs, c.Close())
|
||||
}
|
||||
return nil
|
||||
return errors.Join(errs...)
|
||||
}
|
||||
|
||||
func (t *table) BestIndex(idx *sqlite3.IndexInfo) error {
|
||||
@@ -206,7 +207,7 @@ func (c *cursor) Filter(idxNum int, idxStr string, arg ...sqlite3.Value) error {
|
||||
func (c *cursor) Next() error {
|
||||
if c.scan.Step() {
|
||||
count := c.scan.ColumnCount()
|
||||
for i := 0; i < count; i++ {
|
||||
for i := range count {
|
||||
err := c.cell.BindValue(i+1, c.scan.ColumnValue(i))
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -16,7 +16,9 @@ package regexp
|
||||
import (
|
||||
"errors"
|
||||
"regexp"
|
||||
"regexp/syntax"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/ncruces/go-sqlite3"
|
||||
)
|
||||
@@ -50,16 +52,63 @@ func Register(db *sqlite3.Conn) error {
|
||||
// SELECT column WHERE column GLOB :glob_prefix AND column REGEXP :regexp
|
||||
//
|
||||
// [LIKE optimization]: https://sqlite.org/optoverview.html#the_like_optimization
|
||||
func GlobPrefix(re *regexp.Regexp) string {
|
||||
prefix, complete := re.LiteralPrefix()
|
||||
i := strings.IndexAny(prefix, "*?[")
|
||||
if i < 0 {
|
||||
if complete {
|
||||
return prefix
|
||||
}
|
||||
i = len(prefix)
|
||||
func GlobPrefix(expr string) string {
|
||||
re, err := syntax.Parse(expr, syntax.Perl)
|
||||
if err != nil {
|
||||
return "" // no match possible
|
||||
}
|
||||
return prefix[:i] + "*"
|
||||
prog, err := syntax.Compile(re.Simplify())
|
||||
if err != nil {
|
||||
return "" // notest
|
||||
}
|
||||
|
||||
i := &prog.Inst[prog.Start]
|
||||
|
||||
var empty syntax.EmptyOp
|
||||
loop1:
|
||||
for {
|
||||
switch i.Op {
|
||||
case syntax.InstFail:
|
||||
return "" // notest
|
||||
case syntax.InstCapture, syntax.InstNop:
|
||||
// skip
|
||||
case syntax.InstEmptyWidth:
|
||||
empty |= syntax.EmptyOp(i.Arg)
|
||||
default:
|
||||
break loop1
|
||||
}
|
||||
i = &prog.Inst[i.Out]
|
||||
}
|
||||
if empty&syntax.EmptyBeginText == 0 {
|
||||
return "*" // not anchored
|
||||
}
|
||||
|
||||
var glob strings.Builder
|
||||
loop2:
|
||||
for {
|
||||
switch i.Op {
|
||||
case syntax.InstFail:
|
||||
return "" // notest
|
||||
case syntax.InstCapture, syntax.InstEmptyWidth, syntax.InstNop:
|
||||
// skip
|
||||
case syntax.InstRune, syntax.InstRune1:
|
||||
if len(i.Rune) != 1 || syntax.Flags(i.Arg)&syntax.FoldCase != 0 {
|
||||
break loop2
|
||||
}
|
||||
switch r := i.Rune[0]; r {
|
||||
case '*', '?', '[', utf8.RuneError:
|
||||
break loop2
|
||||
default:
|
||||
glob.WriteRune(r)
|
||||
}
|
||||
default:
|
||||
break loop2
|
||||
}
|
||||
i = &prog.Inst[i.Out]
|
||||
}
|
||||
|
||||
glob.WriteByte('*')
|
||||
return glob.String()
|
||||
}
|
||||
|
||||
func load(ctx sqlite3.Context, i int, expr string) (*regexp.Regexp, error) {
|
||||
|
||||
@@ -3,6 +3,7 @@ package regexp
|
||||
import (
|
||||
"database/sql"
|
||||
"regexp"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/ncruces/go-sqlite3/driver"
|
||||
@@ -108,19 +109,55 @@ func TestGlobPrefix(t *testing.T) {
|
||||
re string
|
||||
want string
|
||||
}{
|
||||
{``, ""},
|
||||
{`a`, "a"},
|
||||
{`a*`, "*"},
|
||||
{`a+`, "a*"},
|
||||
{`ab*`, "a*"},
|
||||
{`ab+`, "ab*"},
|
||||
{`a\?b`, "a*"},
|
||||
{`[`, ""},
|
||||
{``, "*"},
|
||||
{`^`, "*"},
|
||||
{`a`, "*"},
|
||||
{`ab`, "*"},
|
||||
{`^a`, "a*"},
|
||||
{`^a*`, "*"},
|
||||
{`^a+`, "a*"},
|
||||
{`^ab*`, "a*"},
|
||||
{`^ab+`, "ab*"},
|
||||
{`^a\?b`, "a*"},
|
||||
{`^[a-z]`, "*"},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.re, func(t *testing.T) {
|
||||
if got := GlobPrefix(regexp.MustCompile(tt.re)); got != tt.want {
|
||||
t.Errorf("GlobPrefix() = %v, want %v", got, tt.want)
|
||||
if got := GlobPrefix(tt.re); got != tt.want {
|
||||
t.Errorf("GlobPrefix(%v) = %v, want %v", tt.re, got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func FuzzGlobPrefix(f *testing.F) {
|
||||
f.Add(``, ``)
|
||||
f.Add(`[`, ``)
|
||||
f.Add(`^`, ``)
|
||||
f.Add(`a`, `a`)
|
||||
f.Add(`ab`, `b`)
|
||||
f.Add(`^a`, `a`)
|
||||
f.Add(`^a*`, `ab`)
|
||||
f.Add(`^a+`, `ab`)
|
||||
f.Add(`^ab*`, `ab`)
|
||||
f.Add(`^ab+`, `ab`)
|
||||
f.Add(`^a\?b`, `ab`)
|
||||
f.Add(`^[a-z]`, `ab`)
|
||||
|
||||
f.Fuzz(func(t *testing.T, lit, str string) {
|
||||
re, err := regexp.Compile(lit)
|
||||
if err != nil {
|
||||
t.SkipNow()
|
||||
}
|
||||
if re.MatchString(str) {
|
||||
prefix, ok := strings.CutSuffix(GlobPrefix(lit), "*")
|
||||
if !ok {
|
||||
t.Fatalf("missing * after %q for %q with %q", prefix, lit, str)
|
||||
}
|
||||
if !strings.HasPrefix(str, prefix) {
|
||||
t.Fatalf("missing prefix %q for %q with %q", prefix, lit, str)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -8,17 +8,21 @@ import (
|
||||
"github.com/ncruces/go-sqlite3/vfs"
|
||||
)
|
||||
|
||||
const vfsName = "github.com/ncruces/go-sqlite3/ext/serdes.sliceVFS"
|
||||
|
||||
func init() {
|
||||
vfs.Register(vfsName, sliceVFS{})
|
||||
}
|
||||
|
||||
var fileToOpen = make(chan *sliceFile, 1)
|
||||
|
||||
// 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)
|
||||
err := db.Backup(schema, "file:serdes.db?vfs="+vfsName)
|
||||
return file.data, err
|
||||
}
|
||||
|
||||
@@ -38,21 +42,21 @@ func Serialize(db *sqlite3.Conn, schema string) ([]byte, error) {
|
||||
// ["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)
|
||||
return db.Restore(schema, "file:serdes.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
|
||||
if flags&vfs.OPEN_MAIN_DB == 0 || name != "serdes.db" {
|
||||
return nil, flags, sqlite3.CANTOPEN
|
||||
}
|
||||
return <-fileToOpen, flags | vfs.OPEN_MEMORY, nil
|
||||
select {
|
||||
case file := <-fileToOpen:
|
||||
return file, flags | vfs.OPEN_MEMORY, nil
|
||||
default:
|
||||
return nil, flags, sqlite3.MISUSE
|
||||
}
|
||||
}
|
||||
|
||||
func (sliceVFS) Delete(name string, dirSync bool) error {
|
||||
@@ -61,7 +65,7 @@ func (sliceVFS) Delete(name string, dirSync bool) error {
|
||||
}
|
||||
|
||||
func (sliceVFS) Access(name string, flag vfs.AccessFlag) (bool, error) {
|
||||
return name == "db", nil
|
||||
return name == "serdes.db", nil
|
||||
}
|
||||
|
||||
func (sliceVFS) FullPathname(name string) (string, error) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package serdes_test
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"net/http"
|
||||
"testing"
|
||||
@@ -66,3 +67,21 @@ func httpGet() ([]byte, error) {
|
||||
defer res.Body.Close()
|
||||
return io.ReadAll(res.Body)
|
||||
}
|
||||
|
||||
func TestOpen_errors(t *testing.T) {
|
||||
_, err := sqlite3.Open("file:test.db?vfs=github.com/ncruces/go-sqlite3/ext/serdes.sliceVFS")
|
||||
if err == nil {
|
||||
t.Error("want error")
|
||||
}
|
||||
if !errors.Is(err, sqlite3.CANTOPEN) {
|
||||
t.Errorf("got %v, want sqlite3.CANTOPEN", err)
|
||||
}
|
||||
|
||||
_, err = sqlite3.Open("file:serdes.db?vfs=github.com/ncruces/go-sqlite3/ext/serdes.sliceVFS")
|
||||
if err == nil {
|
||||
t.Error("want error")
|
||||
}
|
||||
if !errors.Is(err, sqlite3.MISUSE) {
|
||||
t.Errorf("got %v, want sqlite3.MISUSE", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ package statement
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unsafe"
|
||||
@@ -43,7 +44,7 @@ func declare(db *sqlite3.Conn, _, _, _ string, arg ...string) (*table, error) {
|
||||
var str strings.Builder
|
||||
str.WriteString("CREATE TABLE x(")
|
||||
outputs := stmt.ColumnCount()
|
||||
for i := 0; i < outputs; i++ {
|
||||
for i := range outputs {
|
||||
name := sqlite3.QuoteIdentifier(stmt.ColumnName(i))
|
||||
str.WriteString(sep)
|
||||
str.WriteString(name)
|
||||
@@ -150,8 +151,9 @@ type cursor struct {
|
||||
func (c *cursor) Close() error {
|
||||
if c.stmt == c.table.stmt {
|
||||
c.table.inuse = false
|
||||
c.stmt.ClearBindings()
|
||||
return c.stmt.Reset()
|
||||
return errors.Join(
|
||||
c.stmt.Reset(),
|
||||
c.stmt.ClearBindings())
|
||||
}
|
||||
return c.stmt.Close()
|
||||
}
|
||||
@@ -159,8 +161,10 @@ func (c *cursor) Close() error {
|
||||
func (c *cursor) Filter(idxNum int, idxStr string, arg ...sqlite3.Value) error {
|
||||
c.arg = arg
|
||||
c.rowID = 0
|
||||
c.stmt.ClearBindings()
|
||||
if err := c.stmt.Reset(); err != nil {
|
||||
err := errors.Join(
|
||||
c.stmt.Reset(),
|
||||
c.stmt.ClearBindings())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
19
ext/stats/kahan.go
Normal file
19
ext/stats/kahan.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package stats
|
||||
|
||||
// https://en.wikipedia.org/wiki/Kahan_summation_algorithm
|
||||
|
||||
type kahan struct{ hi, lo float64 }
|
||||
|
||||
func (k *kahan) add(x float64) {
|
||||
y := k.lo + x
|
||||
t := k.hi + y
|
||||
k.lo = y - (t - k.hi)
|
||||
k.hi = t
|
||||
}
|
||||
|
||||
func (k *kahan) sub(x float64) {
|
||||
y := k.lo - x
|
||||
t := k.hi + y
|
||||
k.lo = y - (t - k.hi)
|
||||
k.hi = t
|
||||
}
|
||||
112
ext/stats/mode.go
Normal file
112
ext/stats/mode.go
Normal file
@@ -0,0 +1,112 @@
|
||||
package stats
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
|
||||
"github.com/ncruces/go-sqlite3"
|
||||
)
|
||||
|
||||
func newMode() sqlite3.AggregateFunction {
|
||||
return &mode{}
|
||||
}
|
||||
|
||||
type mode struct {
|
||||
ints counter[int64]
|
||||
reals counter[float64]
|
||||
texts counter[string]
|
||||
blobs counter[string]
|
||||
}
|
||||
|
||||
func (m mode) Value(ctx sqlite3.Context) {
|
||||
var (
|
||||
max = 0
|
||||
typ = sqlite3.NULL
|
||||
i64 int64
|
||||
f64 float64
|
||||
str string
|
||||
)
|
||||
for k, v := range m.ints {
|
||||
if v > max || v == max && k < i64 {
|
||||
typ = sqlite3.INTEGER
|
||||
max = v
|
||||
i64 = k
|
||||
}
|
||||
}
|
||||
f64 = float64(i64)
|
||||
for k, v := range m.reals {
|
||||
if v > max || v == max && k < f64 {
|
||||
typ = sqlite3.FLOAT
|
||||
max = v
|
||||
f64 = k
|
||||
}
|
||||
}
|
||||
for k, v := range m.texts {
|
||||
if v > max || v == max && typ == sqlite3.TEXT && k < str {
|
||||
typ = sqlite3.TEXT
|
||||
max = v
|
||||
str = k
|
||||
}
|
||||
}
|
||||
for k, v := range m.blobs {
|
||||
if v > max || v == max && typ == sqlite3.BLOB && k < str {
|
||||
typ = sqlite3.BLOB
|
||||
max = v
|
||||
str = k
|
||||
}
|
||||
}
|
||||
switch typ {
|
||||
case sqlite3.INTEGER:
|
||||
ctx.ResultInt64(i64)
|
||||
case sqlite3.FLOAT:
|
||||
ctx.ResultFloat(f64)
|
||||
case sqlite3.TEXT:
|
||||
ctx.ResultText(str)
|
||||
case sqlite3.BLOB:
|
||||
ctx.ResultBlob(unsafe.Slice(unsafe.StringData(str), len(str)))
|
||||
}
|
||||
}
|
||||
|
||||
func (b *mode) Step(ctx sqlite3.Context, arg ...sqlite3.Value) {
|
||||
switch arg[0].Type() {
|
||||
case sqlite3.INTEGER:
|
||||
b.ints.add(arg[0].Int64())
|
||||
case sqlite3.FLOAT:
|
||||
b.reals.add(arg[0].Float())
|
||||
case sqlite3.TEXT:
|
||||
b.texts.add(arg[0].Text())
|
||||
case sqlite3.BLOB:
|
||||
b.blobs.add(string(arg[0].RawBlob()))
|
||||
}
|
||||
}
|
||||
|
||||
func (b *mode) Inverse(ctx sqlite3.Context, arg ...sqlite3.Value) {
|
||||
switch arg[0].Type() {
|
||||
case sqlite3.INTEGER:
|
||||
b.ints.del(arg[0].Int64())
|
||||
case sqlite3.FLOAT:
|
||||
b.reals.del(arg[0].Float())
|
||||
case sqlite3.TEXT:
|
||||
b.texts.del(arg[0].Text())
|
||||
case sqlite3.BLOB:
|
||||
b.blobs.del(string(arg[0].RawBlob()))
|
||||
}
|
||||
}
|
||||
|
||||
type counter[T comparable] map[T]int
|
||||
|
||||
func (c *counter[T]) add(k T) {
|
||||
if (*c) == nil {
|
||||
(*c) = make(counter[T])
|
||||
}
|
||||
(*c)[k]++
|
||||
}
|
||||
|
||||
func (c counter[T]) del(k T) {
|
||||
switch n := c[k]; n {
|
||||
default:
|
||||
c[k] = n - 1
|
||||
case 1:
|
||||
delete(c, k)
|
||||
case 0:
|
||||
}
|
||||
}
|
||||
85
ext/stats/mode_test.go
Normal file
85
ext/stats/mode_test.go
Normal file
@@ -0,0 +1,85 @@
|
||||
package stats_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/ncruces/go-sqlite3"
|
||||
_ "github.com/ncruces/go-sqlite3/embed"
|
||||
_ "github.com/ncruces/go-sqlite3/internal/testcfg"
|
||||
)
|
||||
|
||||
func TestRegister_mode(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
db, err := sqlite3.Open(":memory:")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
stmt, _, err := db.Prepare(`SELECT mode(column1) FROM (VALUES (NULL), (1), (NULL), (2), (NULL), (3), (3))`)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if stmt.Step() {
|
||||
if got := stmt.ColumnInt(0); got != 3 {
|
||||
t.Errorf("got %v, want 3", got)
|
||||
}
|
||||
}
|
||||
stmt.Close()
|
||||
|
||||
stmt, _, err = db.Prepare(`SELECT mode(column1) FROM (VALUES (1), (1), (2), (2), (3))`)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if stmt.Step() {
|
||||
if got := stmt.ColumnInt(0); got != 1 {
|
||||
t.Errorf("got %v, want 1", got)
|
||||
}
|
||||
}
|
||||
stmt.Close()
|
||||
|
||||
stmt, _, err = db.Prepare(`SELECT mode(column1) FROM (VALUES (0.5), (1), (2.5), (2), (2.5))`)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if stmt.Step() {
|
||||
if got := stmt.ColumnFloat(0); got != 2.5 {
|
||||
t.Errorf("got %v, want 2.5", got)
|
||||
}
|
||||
}
|
||||
stmt.Close()
|
||||
|
||||
stmt, _, err = db.Prepare(`SELECT mode(column1) FROM (VALUES ('red'), ('green'), ('blue'), ('red'))`)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if stmt.Step() {
|
||||
if got := stmt.ColumnText(0); got != "red" {
|
||||
t.Errorf("got %q, want red", got)
|
||||
}
|
||||
}
|
||||
stmt.Close()
|
||||
|
||||
stmt, _, err = db.Prepare(`SELECT mode(column1) FROM (VALUES (X'cafebabe'), ('green'), ('blue'), (X'cafebabe'))`)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if stmt.Step() {
|
||||
if got := stmt.ColumnText(0); got != "\xca\xfe\xba\xbe" {
|
||||
t.Errorf("got %q, want cafebabe", got)
|
||||
}
|
||||
}
|
||||
stmt.Close()
|
||||
|
||||
stmt, _, err = db.Prepare(`
|
||||
SELECT mode(column1) OVER (ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING)
|
||||
FROM (VALUES (1), (1), (2.5), ('blue'), (X'cafebabe'), (1), (1))
|
||||
`)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for stmt.Step() {
|
||||
}
|
||||
stmt.Close()
|
||||
}
|
||||
101
ext/stats/moments.go
Normal file
101
ext/stats/moments.go
Normal file
@@ -0,0 +1,101 @@
|
||||
package stats
|
||||
|
||||
import "math"
|
||||
|
||||
// Fisher–Pearson skewness and kurtosis using
|
||||
// Terriberry's algorithm with Kahan summation:
|
||||
// https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#Higher-order_statistics
|
||||
|
||||
type moments struct {
|
||||
m1, m2, m3, m4 kahan
|
||||
n int64
|
||||
}
|
||||
|
||||
func (m moments) mean() float64 {
|
||||
return m.m1.hi
|
||||
}
|
||||
|
||||
func (m moments) var_pop() float64 {
|
||||
return m.m2.hi / float64(m.n)
|
||||
}
|
||||
|
||||
func (m moments) var_samp() float64 {
|
||||
return m.m2.hi / float64(m.n-1) // Bessel's correction
|
||||
}
|
||||
|
||||
func (m moments) stddev_pop() float64 {
|
||||
return math.Sqrt(m.var_pop())
|
||||
}
|
||||
|
||||
func (m moments) stddev_samp() float64 {
|
||||
return math.Sqrt(m.var_samp())
|
||||
}
|
||||
|
||||
func (m moments) skewness_pop() float64 {
|
||||
m2 := m.m2.hi
|
||||
if div := m2 * m2 * m2; div != 0 {
|
||||
return m.m3.hi * math.Sqrt(float64(m.n)/div)
|
||||
}
|
||||
return math.NaN()
|
||||
}
|
||||
|
||||
func (m moments) skewness_samp() float64 {
|
||||
n := m.n
|
||||
// https://mathworks.com/help/stats/skewness.html#f1132178
|
||||
return m.skewness_pop() * math.Sqrt(float64(n*(n-1))) / float64(n-2)
|
||||
}
|
||||
|
||||
func (m moments) kurtosis_pop() float64 {
|
||||
return m.raw_kurtosis_pop() - 3
|
||||
}
|
||||
|
||||
func (m moments) raw_kurtosis_pop() float64 {
|
||||
m2 := m.m2.hi
|
||||
if div := m2 * m2; div != 0 {
|
||||
return m.m4.hi * float64(m.n) / div
|
||||
}
|
||||
return math.NaN()
|
||||
}
|
||||
|
||||
func (m moments) kurtosis_samp() float64 {
|
||||
n := m.n
|
||||
k := math.FMA(m.raw_kurtosis_pop(), float64(n+1), float64(3-3*n))
|
||||
return k * float64(n-1) / float64((n-2)*(n-3))
|
||||
}
|
||||
|
||||
func (m moments) raw_kurtosis_samp() float64 {
|
||||
n := m.n
|
||||
// https://mathworks.com/help/stats/kurtosis.html#f4975293
|
||||
k := math.FMA(m.raw_kurtosis_pop(), float64(n+1), float64(3-3*n))
|
||||
return math.FMA(k, float64(n-1)/float64((n-2)*(n-3)), 3)
|
||||
}
|
||||
|
||||
func (m *moments) enqueue(x float64) {
|
||||
n := m.n + 1
|
||||
m.n = n
|
||||
d1 := x - m.m1.hi - m.m1.lo
|
||||
dn := d1 / float64(n)
|
||||
d2 := dn * dn
|
||||
t1 := d1 * dn * float64(n-1)
|
||||
m.m4.add(t1*d2*float64(n*n-3*n+3) + 6*d2*m.m2.hi - 4*dn*m.m3.hi)
|
||||
m.m3.add(t1*dn*float64(n-2) - 3*dn*m.m2.hi)
|
||||
m.m2.add(t1)
|
||||
m.m1.add(dn)
|
||||
}
|
||||
|
||||
func (m *moments) dequeue(x float64) {
|
||||
n := m.n - 1
|
||||
if n <= 0 {
|
||||
*m = moments{}
|
||||
return
|
||||
}
|
||||
m.n = n
|
||||
d1 := x - m.m1.hi - m.m1.lo
|
||||
dn := d1 / float64(n)
|
||||
d2 := dn * dn
|
||||
t1 := d1 * dn * float64(n+1)
|
||||
m.m4.sub(t1*d2*float64(n*n+3*n+3) - 6*d2*m.m2.hi - 4*dn*m.m3.hi)
|
||||
m.m3.sub(t1*dn*float64(n+2) - 3*dn*m.m2.hi)
|
||||
m.m2.sub(t1)
|
||||
m.m1.sub(dn)
|
||||
}
|
||||
87
ext/stats/moments_test.go
Normal file
87
ext/stats/moments_test.go
Normal file
@@ -0,0 +1,87 @@
|
||||
package stats
|
||||
|
||||
import (
|
||||
"math"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_moments(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var s1 moments
|
||||
s1.enqueue(1)
|
||||
s1.dequeue(1)
|
||||
if !math.IsNaN(s1.skewness_pop()) {
|
||||
t.Errorf("want NaN")
|
||||
}
|
||||
if !math.IsNaN(s1.raw_kurtosis_pop()) {
|
||||
t.Errorf("want NaN")
|
||||
}
|
||||
|
||||
s1.enqueue(+0.5377)
|
||||
s1.enqueue(+1.8339)
|
||||
s1.enqueue(-2.2588)
|
||||
s1.enqueue(+0.8622)
|
||||
s1.enqueue(+0.3188)
|
||||
s1.enqueue(-1.3077)
|
||||
s1.enqueue(-0.4336)
|
||||
s1.enqueue(+0.3426)
|
||||
s1.enqueue(+3.5784)
|
||||
s1.enqueue(+2.7694)
|
||||
|
||||
if got := s1.skewness_pop(); float32(got) != 0.106098293 {
|
||||
t.Errorf("got %v, want 0.1061", got)
|
||||
}
|
||||
if got := s1.skewness_samp(); float32(got) != 0.1258171 {
|
||||
t.Errorf("got %v, want 0.1258", got)
|
||||
}
|
||||
if got := s1.raw_kurtosis_pop(); float32(got) != 2.3121266 {
|
||||
t.Errorf("got %v, want 2.3121", got)
|
||||
}
|
||||
if got := s1.raw_kurtosis_samp(); float32(got) != 2.7482237 {
|
||||
t.Errorf("got %v, want 2.7483", got)
|
||||
}
|
||||
|
||||
var s2 welford
|
||||
|
||||
s2.enqueue(+0.5377)
|
||||
s2.enqueue(+1.8339)
|
||||
s2.enqueue(-2.2588)
|
||||
s2.enqueue(+0.8622)
|
||||
s2.enqueue(+0.3188)
|
||||
s2.enqueue(-1.3077)
|
||||
s2.enqueue(-0.4336)
|
||||
s2.enqueue(+0.3426)
|
||||
s2.enqueue(+3.5784)
|
||||
s2.enqueue(+2.7694)
|
||||
|
||||
if got, want := s1.mean(), s2.mean(); got != want {
|
||||
t.Errorf("got %v, want %v", got, want)
|
||||
}
|
||||
if got, want := s1.stddev_pop(), s2.stddev_pop(); got != want {
|
||||
t.Errorf("got %v, want %v", got, want)
|
||||
}
|
||||
if got, want := s1.stddev_samp(), s2.stddev_samp(); got != want {
|
||||
t.Errorf("got %v, want %v", got, want)
|
||||
}
|
||||
|
||||
s1.enqueue(math.Pi)
|
||||
s1.enqueue(math.Sqrt2)
|
||||
s1.enqueue(math.E)
|
||||
s1.dequeue(math.Pi)
|
||||
s1.dequeue(math.E)
|
||||
s1.dequeue(math.Sqrt2)
|
||||
|
||||
if got := s1.skewness_pop(); float32(got) != 0.106098293 {
|
||||
t.Errorf("got %v, want 0.1061", got)
|
||||
}
|
||||
if got := s1.skewness_samp(); float32(got) != 0.1258171 {
|
||||
t.Errorf("got %v, want 0.1258", got)
|
||||
}
|
||||
if got := s1.raw_kurtosis_pop(); float32(got) != 2.3121266 {
|
||||
t.Errorf("got %v, want 2.3121", got)
|
||||
}
|
||||
if got := s1.raw_kurtosis_samp(); float32(got) != 2.7482237 {
|
||||
t.Errorf("got %v, want 2.7483", got)
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,9 @@ import (
|
||||
"github.com/ncruces/sort/quick"
|
||||
)
|
||||
|
||||
// Compatible with:
|
||||
// https://sqlite.org/src/file/ext/misc/percentile.c
|
||||
|
||||
const (
|
||||
median = iota
|
||||
percentile_100
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
// Package stats provides aggregate functions for statistics.
|
||||
//
|
||||
// Provided functions:
|
||||
// - stddev_pop: population standard deviation
|
||||
// - stddev_samp: sample standard deviation
|
||||
// - var_pop: population variance
|
||||
// - var_samp: sample variance
|
||||
// - stddev_pop: population standard deviation
|
||||
// - stddev_samp: sample standard deviation
|
||||
// - skewness_pop: Pearson population skewness
|
||||
// - skewness_samp: Pearson sample skewness
|
||||
// - kurtosis_pop: Fisher population excess kurtosis
|
||||
// - kurtosis_samp: Fisher sample excess kurtosis
|
||||
// - covar_pop: population covariance
|
||||
// - covar_samp: sample covariance
|
||||
// - corr: correlation coefficient
|
||||
// - corr: Pearson correlation coefficient
|
||||
// - regr_r2: correlation coefficient squared
|
||||
// - regr_avgx: average of the independent variable
|
||||
// - regr_avgy: average of the dependent variable
|
||||
@@ -17,10 +21,12 @@
|
||||
// - regr_count: count non-null pairs of variables
|
||||
// - regr_slope: slope of the least-squares-fit linear equation
|
||||
// - regr_intercept: y-intercept of the least-squares-fit linear equation
|
||||
// - regr_json: all regr stats in a JSON object
|
||||
// - percentile_disc: discrete percentile
|
||||
// - percentile_cont: continuous percentile
|
||||
// - median: median value
|
||||
// - regr_json: all regr stats as a JSON object
|
||||
// - percentile_disc: discrete quantile
|
||||
// - percentile_cont: continuous quantile
|
||||
// - percentile: continuous percentile
|
||||
// - median: middle value
|
||||
// - mode: most frequent value
|
||||
// - every: boolean and
|
||||
// - some: boolean or
|
||||
//
|
||||
@@ -59,6 +65,10 @@ func Register(db *sqlite3.Conn) error {
|
||||
db.CreateWindowFunction("var_samp", 1, flags, newVariance(var_samp)),
|
||||
db.CreateWindowFunction("stddev_pop", 1, flags, newVariance(stddev_pop)),
|
||||
db.CreateWindowFunction("stddev_samp", 1, flags, newVariance(stddev_samp)),
|
||||
db.CreateWindowFunction("skewness_pop", 1, flags, newMoments(skewness_pop)),
|
||||
db.CreateWindowFunction("skewness_samp", 1, flags, newMoments(skewness_samp)),
|
||||
db.CreateWindowFunction("kurtosis_pop", 1, flags, newMoments(kurtosis_pop)),
|
||||
db.CreateWindowFunction("kurtosis_samp", 1, flags, newMoments(kurtosis_samp)),
|
||||
db.CreateWindowFunction("covar_pop", 2, flags, newCovariance(var_pop)),
|
||||
db.CreateWindowFunction("covar_samp", 2, flags, newCovariance(var_samp)),
|
||||
db.CreateWindowFunction("corr", 2, flags, newCovariance(corr)),
|
||||
@@ -77,7 +87,8 @@ func Register(db *sqlite3.Conn) error {
|
||||
db.CreateWindowFunction("percentile_cont", 2, order, newPercentile(percentile_cont)),
|
||||
db.CreateWindowFunction("percentile_disc", 2, order, newPercentile(percentile_disc)),
|
||||
db.CreateWindowFunction("every", 1, flags, newBoolean(every)),
|
||||
db.CreateWindowFunction("some", 1, flags, newBoolean(some)))
|
||||
db.CreateWindowFunction("some", 1, flags, newBoolean(some)),
|
||||
db.CreateWindowFunction("mode", 1, order, newMode))
|
||||
}
|
||||
|
||||
const (
|
||||
@@ -85,6 +96,10 @@ const (
|
||||
var_samp
|
||||
stddev_pop
|
||||
stddev_samp
|
||||
skewness_pop
|
||||
skewness_samp
|
||||
kurtosis_pop
|
||||
kurtosis_samp
|
||||
corr
|
||||
regr_r2
|
||||
regr_sxx
|
||||
@@ -98,6 +113,23 @@ const (
|
||||
regr_json
|
||||
)
|
||||
|
||||
func special(kind int, n int64) (null, zero bool) {
|
||||
switch kind {
|
||||
case var_pop, stddev_pop, regr_sxx, regr_syy, regr_sxy:
|
||||
return n <= 0, n == 1
|
||||
case regr_avgx, regr_avgy:
|
||||
return n <= 0, false
|
||||
case kurtosis_samp:
|
||||
return n <= 3, false
|
||||
case skewness_samp:
|
||||
return n <= 2, false
|
||||
case skewness_pop:
|
||||
return n <= 1, n == 2
|
||||
default:
|
||||
return n <= 1, false
|
||||
}
|
||||
}
|
||||
|
||||
func newVariance(kind int) func() sqlite3.AggregateFunction {
|
||||
return func() sqlite3.AggregateFunction { return &variance{kind: kind} }
|
||||
}
|
||||
@@ -108,6 +140,14 @@ type variance struct {
|
||||
}
|
||||
|
||||
func (fn *variance) Value(ctx sqlite3.Context) {
|
||||
switch null, zero := special(fn.kind, fn.n); {
|
||||
case zero:
|
||||
ctx.ResultFloat(0)
|
||||
return
|
||||
case null:
|
||||
return
|
||||
}
|
||||
|
||||
var r float64
|
||||
switch fn.kind {
|
||||
case var_pop:
|
||||
@@ -148,6 +188,18 @@ type covariance struct {
|
||||
}
|
||||
|
||||
func (fn *covariance) Value(ctx sqlite3.Context) {
|
||||
if fn.kind == regr_count {
|
||||
ctx.ResultInt64(fn.regr_count())
|
||||
return
|
||||
}
|
||||
switch null, zero := special(fn.kind, fn.n); {
|
||||
case zero:
|
||||
ctx.ResultFloat(0)
|
||||
return
|
||||
case null:
|
||||
return
|
||||
}
|
||||
|
||||
var r float64
|
||||
switch fn.kind {
|
||||
case var_pop:
|
||||
@@ -172,11 +224,9 @@ func (fn *covariance) Value(ctx sqlite3.Context) {
|
||||
r = fn.regr_slope()
|
||||
case regr_intercept:
|
||||
r = fn.regr_intercept()
|
||||
case regr_count:
|
||||
ctx.ResultInt64(fn.regr_count())
|
||||
return
|
||||
case regr_json:
|
||||
ctx.ResultText(fn.regr_json())
|
||||
var buf [128]byte
|
||||
ctx.ResultRawText(fn.regr_json(buf[:0]))
|
||||
return
|
||||
}
|
||||
ctx.ResultFloat(r)
|
||||
@@ -203,3 +253,51 @@ func (fn *covariance) Inverse(ctx sqlite3.Context, arg ...sqlite3.Value) {
|
||||
fn.dequeue(fa, fb)
|
||||
}
|
||||
}
|
||||
|
||||
func newMoments(kind int) func() sqlite3.AggregateFunction {
|
||||
return func() sqlite3.AggregateFunction { return &momentfn{kind: kind} }
|
||||
}
|
||||
|
||||
type momentfn struct {
|
||||
kind int
|
||||
moments
|
||||
}
|
||||
|
||||
func (fn *momentfn) Value(ctx sqlite3.Context) {
|
||||
switch null, zero := special(fn.kind, fn.n); {
|
||||
case zero:
|
||||
ctx.ResultFloat(0)
|
||||
return
|
||||
case null:
|
||||
return
|
||||
}
|
||||
|
||||
var r float64
|
||||
switch fn.kind {
|
||||
case skewness_pop:
|
||||
r = fn.skewness_pop()
|
||||
case skewness_samp:
|
||||
r = fn.skewness_samp()
|
||||
case kurtosis_pop:
|
||||
r = fn.kurtosis_pop()
|
||||
case kurtosis_samp:
|
||||
r = fn.kurtosis_samp()
|
||||
}
|
||||
ctx.ResultFloat(r)
|
||||
}
|
||||
|
||||
func (fn *momentfn) Step(ctx sqlite3.Context, arg ...sqlite3.Value) {
|
||||
a := arg[0]
|
||||
f := a.Float()
|
||||
if f != 0.0 || a.NumericType() != sqlite3.NULL {
|
||||
fn.enqueue(f)
|
||||
}
|
||||
}
|
||||
|
||||
func (fn *momentfn) Inverse(ctx sqlite3.Context, arg ...sqlite3.Value) {
|
||||
a := arg[0]
|
||||
f := a.Float()
|
||||
if f != 0.0 || a.NumericType() != sqlite3.NULL {
|
||||
fn.dequeue(f)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,16 +29,29 @@ func TestRegister_variance(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
stmt, _, err := db.Prepare(`SELECT stddev_pop(x) FROM data`)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if stmt.Step() {
|
||||
if got := stmt.ColumnType(0); got != sqlite3.NULL {
|
||||
t.Errorf("got %v, want NULL", got)
|
||||
}
|
||||
}
|
||||
stmt.Close()
|
||||
|
||||
err = db.Exec(`INSERT INTO data (x) VALUES (4), (7.0), ('13'), (NULL), (16)`)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
stmt, _, err := db.Prepare(`
|
||||
stmt, _, err = db.Prepare(`
|
||||
SELECT
|
||||
sum(x), avg(x),
|
||||
var_samp(x), var_pop(x),
|
||||
stddev_samp(x), stddev_pop(x)
|
||||
stddev_samp(x), stddev_pop(x),
|
||||
skewness_samp(x), skewness_pop(x),
|
||||
kurtosis_samp(x), kurtosis_pop(x)
|
||||
FROM data`)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -62,10 +75,27 @@ func TestRegister_variance(t *testing.T) {
|
||||
if got := stmt.ColumnFloat(5); got != math.Sqrt(22.5) {
|
||||
t.Errorf("got %v, want √22.5", got)
|
||||
}
|
||||
if got := stmt.ColumnFloat(6); got != 0 {
|
||||
t.Errorf("got %v, want zero", got)
|
||||
}
|
||||
if got := stmt.ColumnFloat(7); got != 0 {
|
||||
t.Errorf("got %v, want zero", got)
|
||||
}
|
||||
if got := stmt.ColumnFloat(8); float32(got) != -3.3 {
|
||||
t.Errorf("got %v, want -3.3", got)
|
||||
}
|
||||
if got := stmt.ColumnFloat(9); got != -1.64 {
|
||||
t.Errorf("got %v, want -1.64", got)
|
||||
}
|
||||
}
|
||||
stmt.Close()
|
||||
|
||||
stmt, _, err = db.Prepare(`SELECT var_samp(x) OVER (ROWS 1 PRECEDING) FROM data`)
|
||||
stmt, _, err = db.Prepare(`
|
||||
SELECT
|
||||
var_samp(x) OVER (ROWS 1 PRECEDING),
|
||||
var_pop(x) OVER (ROWS 1 PRECEDING),
|
||||
skewness_pop(x) OVER (ROWS 1 PRECEDING)
|
||||
FROM data`)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -96,12 +126,26 @@ func TestRegister_covariance(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
stmt, _, err := db.Prepare(`SELECT regr_count(y, x), regr_json(y, x) FROM data`)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if stmt.Step() {
|
||||
if got := stmt.ColumnInt(0); got != 0 {
|
||||
t.Errorf("got %v, want 0", got)
|
||||
}
|
||||
if got := stmt.ColumnType(1); got != sqlite3.NULL {
|
||||
t.Errorf("got %v, want NULL", got)
|
||||
}
|
||||
}
|
||||
stmt.Close()
|
||||
|
||||
err = db.Exec(`INSERT INTO data (y, x) VALUES (3, 70), (5, 80), (2, 60), (7, 90), (4, 75)`)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
stmt, _, err := db.Prepare(`SELECT
|
||||
stmt, _, err = db.Prepare(`SELECT
|
||||
corr(y, x), covar_samp(y, x), covar_pop(y, x),
|
||||
regr_avgy(y, x), regr_avgx(y, x),
|
||||
regr_syy(y, x), regr_sxx(y, x), regr_sxy(y, x),
|
||||
@@ -157,7 +201,12 @@ func TestRegister_covariance(t *testing.T) {
|
||||
}
|
||||
stmt.Close()
|
||||
|
||||
stmt, _, err = db.Prepare(`SELECT covar_samp(y, x) OVER (ROWS 1 PRECEDING) FROM data`)
|
||||
stmt, _, err = db.Prepare(`
|
||||
SELECT
|
||||
covar_samp(y, x) OVER (ROWS 1 PRECEDING),
|
||||
covar_pop(y, x) OVER (ROWS 1 PRECEDING),
|
||||
regr_avgx(y, x) OVER (ROWS 1 PRECEDING)
|
||||
FROM data`)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -171,6 +220,9 @@ func TestRegister_covariance(t *testing.T) {
|
||||
t.Errorf("got %v, want %v", got, want[i])
|
||||
}
|
||||
}
|
||||
if stmt.Err() != nil {
|
||||
t.Fatal(stmt.Err())
|
||||
}
|
||||
stmt.Close()
|
||||
}
|
||||
|
||||
|
||||
@@ -3,22 +3,20 @@ package stats
|
||||
import (
|
||||
"math"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/ncruces/go-sqlite3/internal/util"
|
||||
)
|
||||
|
||||
// Welford's algorithm with Kahan summation:
|
||||
// The effect of truncation in statistical computation [van Reeken, AJ 1970]
|
||||
// https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#Welford's_online_algorithm
|
||||
// https://en.wikipedia.org/wiki/Kahan_summation_algorithm
|
||||
|
||||
// See also:
|
||||
// https://duckdb.org/docs/sql/aggregates.html#statistical-aggregates
|
||||
|
||||
type welford struct {
|
||||
m1, m2 kahan
|
||||
n int64
|
||||
}
|
||||
|
||||
func (w welford) average() float64 {
|
||||
func (w welford) mean() float64 {
|
||||
return w.m1.hi
|
||||
}
|
||||
|
||||
@@ -39,17 +37,23 @@ func (w welford) stddev_samp() float64 {
|
||||
}
|
||||
|
||||
func (w *welford) enqueue(x float64) {
|
||||
w.n++
|
||||
n := w.n + 1
|
||||
w.n = n
|
||||
d1 := x - w.m1.hi - w.m1.lo
|
||||
w.m1.add(d1 / float64(w.n))
|
||||
w.m1.add(d1 / float64(n))
|
||||
d2 := x - w.m1.hi - w.m1.lo
|
||||
w.m2.add(d1 * d2)
|
||||
}
|
||||
|
||||
func (w *welford) dequeue(x float64) {
|
||||
w.n--
|
||||
n := w.n - 1
|
||||
if n <= 0 {
|
||||
*w = welford{}
|
||||
return
|
||||
}
|
||||
w.n = n
|
||||
d1 := x - w.m1.hi - w.m1.lo
|
||||
w.m1.sub(d1 / float64(w.n))
|
||||
w.m1.sub(d1 / float64(n))
|
||||
d2 := x - w.m1.hi - w.m1.lo
|
||||
w.m2.sub(d1 * d2)
|
||||
}
|
||||
@@ -112,38 +116,35 @@ func (w welford2) regr_r2() float64 {
|
||||
return w.cov.hi * w.cov.hi / (w.m2y.hi * w.m2x.hi)
|
||||
}
|
||||
|
||||
func (w welford2) regr_json() string {
|
||||
var json strings.Builder
|
||||
var num [32]byte
|
||||
json.Grow(128)
|
||||
json.WriteString(`{"count":`)
|
||||
json.Write(strconv.AppendInt(num[:0], w.regr_count(), 10))
|
||||
json.WriteString(`,"avgy":`)
|
||||
json.Write(strconv.AppendFloat(num[:0], w.regr_avgy(), 'g', -1, 64))
|
||||
json.WriteString(`,"avgx":`)
|
||||
json.Write(strconv.AppendFloat(num[:0], w.regr_avgx(), 'g', -1, 64))
|
||||
json.WriteString(`,"syy":`)
|
||||
json.Write(strconv.AppendFloat(num[:0], w.regr_syy(), 'g', -1, 64))
|
||||
json.WriteString(`,"sxx":`)
|
||||
json.Write(strconv.AppendFloat(num[:0], w.regr_sxx(), 'g', -1, 64))
|
||||
json.WriteString(`,"sxy":`)
|
||||
json.Write(strconv.AppendFloat(num[:0], w.regr_sxy(), 'g', -1, 64))
|
||||
json.WriteString(`,"slope":`)
|
||||
json.Write(strconv.AppendFloat(num[:0], w.regr_slope(), 'g', -1, 64))
|
||||
json.WriteString(`,"intercept":`)
|
||||
json.Write(strconv.AppendFloat(num[:0], w.regr_intercept(), 'g', -1, 64))
|
||||
json.WriteString(`,"r2":`)
|
||||
json.Write(strconv.AppendFloat(num[:0], w.regr_r2(), 'g', -1, 64))
|
||||
json.WriteByte('}')
|
||||
return json.String()
|
||||
func (w welford2) regr_json(dst []byte) []byte {
|
||||
dst = append(dst, `{"count":`...)
|
||||
dst = strconv.AppendInt(dst, w.regr_count(), 10)
|
||||
dst = append(dst, `,"avgy":`...)
|
||||
dst = util.AppendNumber(dst, w.regr_avgy())
|
||||
dst = append(dst, `,"avgx":`...)
|
||||
dst = util.AppendNumber(dst, w.regr_avgx())
|
||||
dst = append(dst, `,"syy":`...)
|
||||
dst = util.AppendNumber(dst, w.regr_syy())
|
||||
dst = append(dst, `,"sxx":`...)
|
||||
dst = util.AppendNumber(dst, w.regr_sxx())
|
||||
dst = append(dst, `,"sxy":`...)
|
||||
dst = util.AppendNumber(dst, w.regr_sxy())
|
||||
dst = append(dst, `,"slope":`...)
|
||||
dst = util.AppendNumber(dst, w.regr_slope())
|
||||
dst = append(dst, `,"intercept":`...)
|
||||
dst = util.AppendNumber(dst, w.regr_intercept())
|
||||
dst = append(dst, `,"r2":`...)
|
||||
dst = util.AppendNumber(dst, w.regr_r2())
|
||||
return append(dst, '}')
|
||||
}
|
||||
|
||||
func (w *welford2) enqueue(y, x float64) {
|
||||
w.n++
|
||||
n := w.n + 1
|
||||
w.n = n
|
||||
d1y := y - w.m1y.hi - w.m1y.lo
|
||||
d1x := x - w.m1x.hi - w.m1x.lo
|
||||
w.m1y.add(d1y / float64(w.n))
|
||||
w.m1x.add(d1x / float64(w.n))
|
||||
w.m1y.add(d1y / float64(n))
|
||||
w.m1x.add(d1x / float64(n))
|
||||
d2y := y - w.m1y.hi - w.m1y.lo
|
||||
d2x := x - w.m1x.hi - w.m1x.lo
|
||||
w.m2y.add(d1y * d2y)
|
||||
@@ -152,30 +153,19 @@ func (w *welford2) enqueue(y, x float64) {
|
||||
}
|
||||
|
||||
func (w *welford2) dequeue(y, x float64) {
|
||||
w.n--
|
||||
n := w.n - 1
|
||||
if n <= 0 {
|
||||
*w = welford2{}
|
||||
return
|
||||
}
|
||||
w.n = n
|
||||
d1y := y - w.m1y.hi - w.m1y.lo
|
||||
d1x := x - w.m1x.hi - w.m1x.lo
|
||||
w.m1y.sub(d1y / float64(w.n))
|
||||
w.m1x.sub(d1x / float64(w.n))
|
||||
w.m1y.sub(d1y / float64(n))
|
||||
w.m1x.sub(d1x / float64(n))
|
||||
d2y := y - w.m1y.hi - w.m1y.lo
|
||||
d2x := x - w.m1x.hi - w.m1x.lo
|
||||
w.m2y.sub(d1y * d2y)
|
||||
w.m2x.sub(d1x * d2x)
|
||||
w.cov.sub(d1y * d2x)
|
||||
}
|
||||
|
||||
type kahan struct{ hi, lo float64 }
|
||||
|
||||
func (k *kahan) add(x float64) {
|
||||
y := k.lo + x
|
||||
t := k.hi + y
|
||||
k.lo = y - (t - k.hi)
|
||||
k.hi = t
|
||||
}
|
||||
|
||||
func (k *kahan) sub(x float64) {
|
||||
y := k.lo - x
|
||||
t := k.hi + y
|
||||
k.lo = y - (t - k.hi)
|
||||
k.hi = t
|
||||
}
|
||||
|
||||
@@ -9,12 +9,14 @@ func Test_welford(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var s1, s2 welford
|
||||
s1.enqueue(1)
|
||||
s1.dequeue(1)
|
||||
|
||||
s1.enqueue(4)
|
||||
s1.enqueue(7)
|
||||
s1.enqueue(13)
|
||||
s1.enqueue(16)
|
||||
if got := s1.average(); got != 10 {
|
||||
if got := s1.mean(); got != 10 {
|
||||
t.Errorf("got %v, want 10", got)
|
||||
}
|
||||
if got := s1.var_samp(); got != 30 {
|
||||
@@ -43,6 +45,8 @@ func Test_covar(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var c1, c2 welford2
|
||||
c1.enqueue(1, 1)
|
||||
c1.dequeue(1, 1)
|
||||
|
||||
c1.enqueue(3, 70)
|
||||
c1.enqueue(5, 80)
|
||||
|
||||
@@ -5,16 +5,18 @@
|
||||
// - LIKE and REGEXP operators,
|
||||
// - collation sequences.
|
||||
//
|
||||
// It also provides, from PostgreSQL:
|
||||
// - unaccent(),
|
||||
// - initcap().
|
||||
//
|
||||
// The implementation is not 100% compatible with the [ICU extension]:
|
||||
// - upper() and lower() use [strings.ToUpper], [strings.ToLower] and [cases];
|
||||
// - the LIKE operator follows [strings.EqualFold] rules;
|
||||
// - the REGEXP operator uses Go [regexp/syntax];
|
||||
// - collation sequences use [collate].
|
||||
//
|
||||
// It also provides (approximately) from PostgreSQL:
|
||||
// - casefold(),
|
||||
// - initcap(),
|
||||
// - normalize(),
|
||||
// - unaccent().
|
||||
//
|
||||
// Expect subtle differences (e.g.) in the handling of Turkish case folding.
|
||||
//
|
||||
// [ICU extension]: https://sqlite.org/src/dir/ext/icu
|
||||
@@ -48,13 +50,13 @@ var RegisterLike = true
|
||||
// Register registers Unicode aware functions for a database connection.
|
||||
func Register(db *sqlite3.Conn) error {
|
||||
const flags = sqlite3.DETERMINISTIC | sqlite3.INNOCUOUS
|
||||
var errs util.ErrorJoiner
|
||||
var lkfn sqlite3.ScalarFunction
|
||||
if RegisterLike {
|
||||
errs.Join(
|
||||
db.CreateFunction("like", 2, flags, like),
|
||||
db.CreateFunction("like", 3, flags, like))
|
||||
lkfn = like
|
||||
}
|
||||
errs.Join(
|
||||
return errors.Join(
|
||||
db.CreateFunction("like", 2, flags, lkfn),
|
||||
db.CreateFunction("like", 3, flags, lkfn),
|
||||
db.CreateFunction("upper", 1, flags, upper),
|
||||
db.CreateFunction("upper", 2, flags, upper),
|
||||
db.CreateFunction("lower", 1, flags, lower),
|
||||
@@ -62,7 +64,10 @@ func Register(db *sqlite3.Conn) error {
|
||||
db.CreateFunction("regexp", 2, flags, regex),
|
||||
db.CreateFunction("initcap", 1, flags, initcap),
|
||||
db.CreateFunction("initcap", 2, flags, initcap),
|
||||
db.CreateFunction("casefold", 1, flags, casefold),
|
||||
db.CreateFunction("unaccent", 1, flags, unaccent),
|
||||
db.CreateFunction("normalize", 1, flags, normalize),
|
||||
db.CreateFunction("normalize", 2, flags, normalize),
|
||||
db.CreateFunction("icu_load_collation", 2, sqlite3.DIRECTONLY,
|
||||
func(ctx sqlite3.Context, arg ...sqlite3.Value) {
|
||||
name := arg[1].Text()
|
||||
@@ -76,7 +81,6 @@ func Register(db *sqlite3.Conn) error {
|
||||
return // notest
|
||||
}
|
||||
}))
|
||||
return errors.Join(errs...)
|
||||
}
|
||||
|
||||
// RegisterCollation registers a Unicode collation sequence for a database connection.
|
||||
@@ -154,6 +158,10 @@ func initcap(ctx sqlite3.Context, arg ...sqlite3.Value) {
|
||||
ctx.ResultRawText(cs.Bytes(arg[0].RawText()))
|
||||
}
|
||||
|
||||
func casefold(ctx sqlite3.Context, arg ...sqlite3.Value) {
|
||||
ctx.ResultRawText(cases.Fold().Bytes(arg[0].RawText()))
|
||||
}
|
||||
|
||||
func unaccent(ctx sqlite3.Context, arg ...sqlite3.Value) {
|
||||
unaccent := transform.Chain(norm.NFD, runes.Remove(runes.In(unicode.Mn)), norm.NFC)
|
||||
res, _, err := transform.Bytes(unaccent, arg[0].RawText())
|
||||
@@ -164,6 +172,31 @@ func unaccent(ctx sqlite3.Context, arg ...sqlite3.Value) {
|
||||
}
|
||||
}
|
||||
|
||||
func normalize(ctx sqlite3.Context, arg ...sqlite3.Value) {
|
||||
form := norm.NFC
|
||||
if len(arg) > 1 {
|
||||
switch strings.ToUpper(arg[1].Text()) {
|
||||
case "NFC":
|
||||
//
|
||||
case "NFD":
|
||||
form = norm.NFD
|
||||
case "NFKC":
|
||||
form = norm.NFKC
|
||||
case "NFKD":
|
||||
form = norm.NFKD
|
||||
default:
|
||||
ctx.ResultError(util.ErrorString("unicode: invalid form"))
|
||||
return
|
||||
}
|
||||
}
|
||||
res, _, err := transform.Bytes(form, arg[0].RawText())
|
||||
if err != nil {
|
||||
ctx.ResultError(err) // notest
|
||||
} else {
|
||||
ctx.ResultRawText(res)
|
||||
}
|
||||
}
|
||||
|
||||
func regex(ctx sqlite3.Context, arg ...sqlite3.Value) {
|
||||
re, ok := ctx.GetAuxData(0).(*regexp.Regexp)
|
||||
if !ok {
|
||||
|
||||
@@ -2,7 +2,7 @@ package unicode
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"reflect"
|
||||
"slices"
|
||||
"testing"
|
||||
|
||||
"github.com/ncruces/go-sqlite3"
|
||||
@@ -49,6 +49,12 @@ func TestRegister(t *testing.T) {
|
||||
{`upper('Dünyanın İlk Borsası', 'tr-TR')`, "DÜNYANIN İLK BORSASI"},
|
||||
{`initcap('Kad je hladno Marko nosi džemper')`, "Kad Je Hladno Marko Nosi Džemper"},
|
||||
{`initcap('Kad je hladno Marko nosi džemper', 'hr-HR')`, "Kad Je Hladno Marko Nosi Džemper"},
|
||||
{`normalize(X'61cc88')`, "ä"},
|
||||
{`normalize(X'61cc88', 'NFC' )`, "ä"},
|
||||
{`normalize(X'61cc88', 'NFKC')`, "ä"},
|
||||
{`normalize('ä', 'NFD' )`, "\x61\xcc\x88"},
|
||||
{`normalize('ä', 'NFKD')`, "\x61\xcc\x88"},
|
||||
{`casefold('Maße')`, "masse"},
|
||||
{`unaccent('Hôtel')`, "Hotel"},
|
||||
{`'Hello' REGEXP 'ell'`, "1"},
|
||||
{`'Hello' REGEXP 'el.'`, "1"},
|
||||
@@ -115,7 +121,7 @@ func TestRegister_collation(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
if !slices.Equal(got, want) {
|
||||
t.Error("not equal")
|
||||
}
|
||||
|
||||
@@ -166,7 +172,7 @@ func TestRegisterCollationsNeeded(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
if !slices.Equal(got, want) {
|
||||
t.Error("not equal")
|
||||
}
|
||||
|
||||
@@ -208,6 +214,14 @@ func TestRegister_error(t *testing.T) {
|
||||
t.Errorf("got %v, want sqlite3.ERROR", err)
|
||||
}
|
||||
|
||||
err = db.Exec(`SELECT normalize('', 'NF')`)
|
||||
if err == nil {
|
||||
t.Error("want error")
|
||||
}
|
||||
if !errors.Is(err, sqlite3.ERROR) {
|
||||
t.Errorf("got %v, want sqlite3.ERROR", err)
|
||||
}
|
||||
|
||||
err = db.Exec(`SELECT 'hello' REGEXP '\'`)
|
||||
if err == nil {
|
||||
t.Error("want error")
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
|
||||
@@ -35,7 +36,9 @@ func Register(db *sqlite3.Conn) error {
|
||||
db.CreateFunction("uuid", 2, sqlite3.INNOCUOUS, generate),
|
||||
db.CreateFunction("uuid", 3, sqlite3.INNOCUOUS, generate),
|
||||
db.CreateFunction("uuid_str", 1, flags, toString),
|
||||
db.CreateFunction("uuid_blob", 1, flags, toBlob))
|
||||
db.CreateFunction("uuid_blob", 1, flags, toBlob),
|
||||
db.CreateFunction("uuid_extract_version", 1, flags, version),
|
||||
db.CreateFunction("uuid_extract_timestamp", 1, flags, timestamp))
|
||||
}
|
||||
|
||||
func generate(ctx sqlite3.Context, arg ...sqlite3.Value) {
|
||||
@@ -167,3 +170,30 @@ func toString(ctx sqlite3.Context, arg ...sqlite3.Value) {
|
||||
ctx.ResultText(u.String())
|
||||
}
|
||||
}
|
||||
|
||||
func version(ctx sqlite3.Context, arg ...sqlite3.Value) {
|
||||
u, err := fromValue(arg[0])
|
||||
if err != nil {
|
||||
ctx.ResultError(err)
|
||||
return // notest
|
||||
}
|
||||
if u.Variant() == uuid.RFC4122 {
|
||||
ctx.ResultInt64(int64(u.Version()))
|
||||
}
|
||||
}
|
||||
|
||||
func timestamp(ctx sqlite3.Context, arg ...sqlite3.Value) {
|
||||
u, err := fromValue(arg[0])
|
||||
if err != nil {
|
||||
ctx.ResultError(err)
|
||||
return // notest
|
||||
}
|
||||
if u.Variant() == uuid.RFC4122 {
|
||||
switch u.Version() {
|
||||
case 1, 2, 6, 7:
|
||||
ctx.ResultTime(
|
||||
time.Unix(u.Time().UnixTime()),
|
||||
sqlite3.TimeFormatDefault)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package uuid
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
|
||||
@@ -106,7 +107,26 @@ func Test_generate(t *testing.T) {
|
||||
t.Error("want error")
|
||||
}
|
||||
|
||||
hash := []struct {
|
||||
var tstamp time.Time
|
||||
var version uuid.Version
|
||||
err = db.QueryRow(`
|
||||
SELECT
|
||||
column1,
|
||||
uuid_extract_version(column1),
|
||||
uuid_extract_timestamp(column1)
|
||||
FROM (VALUES (uuid(7)))
|
||||
`).Scan(&u, &version, &tstamp)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got := u.Version(); got != version {
|
||||
t.Errorf("got %d, want %d", got, version)
|
||||
}
|
||||
if got := time.Unix(u.Time().UnixTime()); !got.Equal(tstamp) {
|
||||
t.Errorf("got %v, want %v", got, tstamp)
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
ver uuid.Version
|
||||
ns any
|
||||
data string
|
||||
@@ -120,7 +140,7 @@ func Test_generate(t *testing.T) {
|
||||
{3, "url", "https://www.php.net", uuid.MustParse("3f703955-aaba-3e70-a3cb-baff6aa3b28f")},
|
||||
{5, "url", "https://www.php.net", uuid.MustParse("a8f6ae40-d8a7-58f0-be05-a22f94eca9ec")},
|
||||
}
|
||||
for _, tt := range hash {
|
||||
for _, tt := range tests {
|
||||
err = db.QueryRow(`SELECT uuid(?, ?, ?)`, tt.ver, tt.ns, tt.data).Scan(&u)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -142,14 +162,14 @@ func Test_convert(t *testing.T) {
|
||||
defer db.Close()
|
||||
|
||||
var u uuid.UUID
|
||||
lits := []string{
|
||||
tests := []string{
|
||||
"'6ba7b8119dad11d180b400c04fd430c8'",
|
||||
"'6ba7b811-9dad-11d1-80b4-00c04fd430c8'",
|
||||
"'{6ba7b811-9dad-11d1-80b4-00c04fd430c8}'",
|
||||
"X'6ba7b8119dad11d180b400c04fd430c8'",
|
||||
}
|
||||
|
||||
for _, tt := range lits {
|
||||
for _, tt := range tests {
|
||||
err = db.QueryRow(`SELECT uuid_str(` + tt + `)`).Scan(&u)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -159,7 +179,7 @@ func Test_convert(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
for _, tt := range lits {
|
||||
for _, tt := range tests {
|
||||
err = db.QueryRow(`SELECT uuid_blob(` + tt + `)`).Scan(&u)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -178,4 +198,14 @@ func Test_convert(t *testing.T) {
|
||||
if err == nil {
|
||||
t.Fatal("want error")
|
||||
}
|
||||
|
||||
err = db.QueryRow(`SELECT uuid_extract_version(X'cafe')`).Scan(&u)
|
||||
if err == nil {
|
||||
t.Fatal("want error")
|
||||
}
|
||||
|
||||
err = db.QueryRow(`SELECT uuid_extract_timestamp(X'cafe')`).Scan(&u)
|
||||
if err == nil {
|
||||
t.Fatal("want error")
|
||||
}
|
||||
}
|
||||
|
||||
77
func.go
77
func.go
@@ -14,12 +14,12 @@ import (
|
||||
//
|
||||
// https://sqlite.org/c3ref/collation_needed.html
|
||||
func (c *Conn) CollationNeeded(cb func(db *Conn, name string)) error {
|
||||
var enable uint64
|
||||
var enable int32
|
||||
if cb != nil {
|
||||
enable = 1
|
||||
}
|
||||
r := c.call("sqlite3_collation_needed_go", uint64(c.handle), enable)
|
||||
if err := c.error(r); err != nil {
|
||||
rc := res_t(c.call("sqlite3_collation_needed_go", stk_t(c.handle), stk_t(enable)))
|
||||
if err := c.error(rc); err != nil {
|
||||
return err
|
||||
}
|
||||
c.collation = cb
|
||||
@@ -33,8 +33,8 @@ func (c *Conn) CollationNeeded(cb func(db *Conn, name string)) error {
|
||||
// This can be used to load schemas that contain
|
||||
// one or more unknown collating sequences.
|
||||
func (c Conn) AnyCollationNeeded() error {
|
||||
r := c.call("sqlite3_anycollseq_init", uint64(c.handle), 0, 0)
|
||||
if err := c.error(r); err != nil {
|
||||
rc := res_t(c.call("sqlite3_anycollseq_init", stk_t(c.handle), 0, 0))
|
||||
if err := c.error(rc); err != nil {
|
||||
return err
|
||||
}
|
||||
c.collation = nil
|
||||
@@ -45,31 +45,31 @@ func (c Conn) AnyCollationNeeded() error {
|
||||
//
|
||||
// https://sqlite.org/c3ref/create_collation.html
|
||||
func (c *Conn) CreateCollation(name string, fn func(a, b []byte) int) error {
|
||||
var funcPtr uint32
|
||||
var funcPtr ptr_t
|
||||
defer c.arena.mark()()
|
||||
namePtr := c.arena.string(name)
|
||||
if fn != nil {
|
||||
funcPtr = util.AddHandle(c.ctx, fn)
|
||||
}
|
||||
r := c.call("sqlite3_create_collation_go",
|
||||
uint64(c.handle), uint64(namePtr), uint64(funcPtr))
|
||||
return c.error(r)
|
||||
rc := res_t(c.call("sqlite3_create_collation_go",
|
||||
stk_t(c.handle), stk_t(namePtr), stk_t(funcPtr)))
|
||||
return c.error(rc)
|
||||
}
|
||||
|
||||
// CreateFunction defines a new scalar SQL function.
|
||||
//
|
||||
// https://sqlite.org/c3ref/create_function.html
|
||||
func (c *Conn) CreateFunction(name string, nArg int, flag FunctionFlag, fn ScalarFunction) error {
|
||||
var funcPtr uint32
|
||||
var funcPtr ptr_t
|
||||
defer c.arena.mark()()
|
||||
namePtr := c.arena.string(name)
|
||||
if fn != nil {
|
||||
funcPtr = util.AddHandle(c.ctx, fn)
|
||||
}
|
||||
r := c.call("sqlite3_create_function_go",
|
||||
uint64(c.handle), uint64(namePtr), uint64(nArg),
|
||||
uint64(flag), uint64(funcPtr))
|
||||
return c.error(r)
|
||||
rc := res_t(c.call("sqlite3_create_function_go",
|
||||
stk_t(c.handle), stk_t(namePtr), stk_t(nArg),
|
||||
stk_t(flag), stk_t(funcPtr)))
|
||||
return c.error(rc)
|
||||
}
|
||||
|
||||
// ScalarFunction is the type of a scalar SQL function.
|
||||
@@ -82,7 +82,7 @@ type ScalarFunction func(ctx Context, arg ...Value)
|
||||
//
|
||||
// https://sqlite.org/c3ref/create_function.html
|
||||
func (c *Conn) CreateWindowFunction(name string, nArg int, flag FunctionFlag, fn func() AggregateFunction) error {
|
||||
var funcPtr uint32
|
||||
var funcPtr ptr_t
|
||||
defer c.arena.mark()()
|
||||
namePtr := c.arena.string(name)
|
||||
if fn != nil {
|
||||
@@ -92,10 +92,10 @@ func (c *Conn) CreateWindowFunction(name string, nArg int, flag FunctionFlag, fn
|
||||
if _, ok := fn().(WindowFunction); ok {
|
||||
call = "sqlite3_create_window_function_go"
|
||||
}
|
||||
r := c.call(call,
|
||||
uint64(c.handle), uint64(namePtr), uint64(nArg),
|
||||
uint64(flag), uint64(funcPtr))
|
||||
return c.error(r)
|
||||
rc := res_t(c.call(call,
|
||||
stk_t(c.handle), stk_t(namePtr), stk_t(nArg),
|
||||
stk_t(flag), stk_t(funcPtr)))
|
||||
return c.error(rc)
|
||||
}
|
||||
|
||||
// AggregateFunction is the interface an aggregate function should implement.
|
||||
@@ -129,28 +129,28 @@ type WindowFunction interface {
|
||||
func (c *Conn) OverloadFunction(name string, nArg int) error {
|
||||
defer c.arena.mark()()
|
||||
namePtr := c.arena.string(name)
|
||||
r := c.call("sqlite3_overload_function",
|
||||
uint64(c.handle), uint64(namePtr), uint64(nArg))
|
||||
return c.error(r)
|
||||
rc := res_t(c.call("sqlite3_overload_function",
|
||||
stk_t(c.handle), stk_t(namePtr), stk_t(nArg)))
|
||||
return c.error(rc)
|
||||
}
|
||||
|
||||
func destroyCallback(ctx context.Context, mod api.Module, pApp uint32) {
|
||||
func destroyCallback(ctx context.Context, mod api.Module, pApp ptr_t) {
|
||||
util.DelHandle(ctx, pApp)
|
||||
}
|
||||
|
||||
func collationCallback(ctx context.Context, mod api.Module, pArg, pDB, eTextRep, zName uint32) {
|
||||
func collationCallback(ctx context.Context, mod api.Module, pArg, pDB ptr_t, eTextRep uint32, zName ptr_t) {
|
||||
if c, ok := ctx.Value(connKey{}).(*Conn); ok && c.handle == pDB && c.collation != nil {
|
||||
name := util.ReadString(mod, zName, _MAX_NAME)
|
||||
c.collation(c, name)
|
||||
}
|
||||
}
|
||||
|
||||
func compareCallback(ctx context.Context, mod api.Module, pApp, nKey1, pKey1, nKey2, pKey2 uint32) uint32 {
|
||||
func compareCallback(ctx context.Context, mod api.Module, pApp ptr_t, nKey1 int32, pKey1 ptr_t, nKey2 int32, pKey2 ptr_t) uint32 {
|
||||
fn := util.GetHandle(ctx, pApp).(func(a, b []byte) int)
|
||||
return uint32(fn(util.View(mod, pKey1, uint64(nKey1)), util.View(mod, pKey2, uint64(nKey2))))
|
||||
return uint32(fn(util.View(mod, pKey1, int64(nKey1)), util.View(mod, pKey2, int64(nKey2))))
|
||||
}
|
||||
|
||||
func funcCallback(ctx context.Context, mod api.Module, pCtx, pApp, nArg, pArg uint32) {
|
||||
func funcCallback(ctx context.Context, mod api.Module, pCtx, pApp ptr_t, nArg int32, pArg ptr_t) {
|
||||
args := getFuncArgs()
|
||||
defer putFuncArgs(args)
|
||||
db := ctx.Value(connKey{}).(*Conn)
|
||||
@@ -159,7 +159,7 @@ func funcCallback(ctx context.Context, mod api.Module, pCtx, pApp, nArg, pArg ui
|
||||
fn(Context{db, pCtx}, args[:nArg]...)
|
||||
}
|
||||
|
||||
func stepCallback(ctx context.Context, mod api.Module, pCtx, pAgg, pApp, nArg, pArg uint32) {
|
||||
func stepCallback(ctx context.Context, mod api.Module, pCtx, pAgg, pApp ptr_t, nArg int32, pArg ptr_t) {
|
||||
args := getFuncArgs()
|
||||
defer putFuncArgs(args)
|
||||
db := ctx.Value(connKey{}).(*Conn)
|
||||
@@ -168,20 +168,23 @@ func stepCallback(ctx context.Context, mod api.Module, pCtx, pAgg, pApp, nArg, p
|
||||
fn.Step(Context{db, pCtx}, args[:nArg]...)
|
||||
}
|
||||
|
||||
func finalCallback(ctx context.Context, mod api.Module, pCtx, pAgg, pApp uint32) {
|
||||
func finalCallback(ctx context.Context, mod api.Module, pCtx, pAgg, pApp ptr_t) {
|
||||
db := ctx.Value(connKey{}).(*Conn)
|
||||
fn, handle := callbackAggregate(db, pAgg, pApp)
|
||||
fn.Value(Context{db, pCtx})
|
||||
util.DelHandle(ctx, handle)
|
||||
if err := util.DelHandle(ctx, handle); err != nil {
|
||||
Context{db, pCtx}.ResultError(err)
|
||||
return // notest
|
||||
}
|
||||
}
|
||||
|
||||
func valueCallback(ctx context.Context, mod api.Module, pCtx, pAgg uint32) {
|
||||
func valueCallback(ctx context.Context, mod api.Module, pCtx, pAgg ptr_t) {
|
||||
db := ctx.Value(connKey{}).(*Conn)
|
||||
fn := util.GetHandle(db.ctx, pAgg).(AggregateFunction)
|
||||
fn.Value(Context{db, pCtx})
|
||||
}
|
||||
|
||||
func inverseCallback(ctx context.Context, mod api.Module, pCtx, pAgg, nArg, pArg uint32) {
|
||||
func inverseCallback(ctx context.Context, mod api.Module, pCtx, pAgg ptr_t, nArg int32, pArg ptr_t) {
|
||||
args := getFuncArgs()
|
||||
defer putFuncArgs(args)
|
||||
db := ctx.Value(connKey{}).(*Conn)
|
||||
@@ -190,9 +193,9 @@ func inverseCallback(ctx context.Context, mod api.Module, pCtx, pAgg, nArg, pArg
|
||||
fn.Inverse(Context{db, pCtx}, args[:nArg]...)
|
||||
}
|
||||
|
||||
func callbackAggregate(db *Conn, pAgg, pApp uint32) (AggregateFunction, uint32) {
|
||||
func callbackAggregate(db *Conn, pAgg, pApp ptr_t) (AggregateFunction, ptr_t) {
|
||||
if pApp == 0 {
|
||||
handle := util.ReadUint32(db.mod, pAgg)
|
||||
handle := util.Read32[ptr_t](db.mod, pAgg)
|
||||
return util.GetHandle(db.ctx, handle).(AggregateFunction), handle
|
||||
}
|
||||
|
||||
@@ -200,17 +203,17 @@ func callbackAggregate(db *Conn, pAgg, pApp uint32) (AggregateFunction, uint32)
|
||||
fn := util.GetHandle(db.ctx, pApp).(func() AggregateFunction)()
|
||||
if pAgg != 0 {
|
||||
handle := util.AddHandle(db.ctx, fn)
|
||||
util.WriteUint32(db.mod, pAgg, handle)
|
||||
util.Write32(db.mod, pAgg, handle)
|
||||
return fn, handle
|
||||
}
|
||||
return fn, 0
|
||||
}
|
||||
|
||||
func callbackArgs(db *Conn, arg []Value, pArg uint32) {
|
||||
func callbackArgs(db *Conn, arg []Value, pArg ptr_t) {
|
||||
for i := range arg {
|
||||
arg[i] = Value{
|
||||
c: db,
|
||||
handle: util.ReadUint32(db.mod, pArg+ptrlen*uint32(i)),
|
||||
handle: util.Read32[ptr_t](db.mod, pArg+ptr_t(i)*ptrlen),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
16
go.mod
16
go.mod
@@ -1,23 +1,23 @@
|
||||
module github.com/ncruces/go-sqlite3
|
||||
|
||||
go 1.21
|
||||
go 1.22.0
|
||||
|
||||
toolchain go1.23.0
|
||||
toolchain go1.24.0
|
||||
|
||||
require (
|
||||
github.com/ncruces/julianday v1.0.0
|
||||
github.com/ncruces/sort v0.1.2
|
||||
github.com/tetratelabs/wazero v1.8.2
|
||||
golang.org/x/crypto v0.32.0
|
||||
golang.org/x/sys v0.29.0
|
||||
github.com/ncruces/sort v0.1.5
|
||||
github.com/tetratelabs/wazero v1.9.0
|
||||
golang.org/x/crypto v0.33.0
|
||||
golang.org/x/sys v0.30.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/sync v0.10.0 // test
|
||||
golang.org/x/text v0.21.0 // ext/unicode
|
||||
golang.org/x/sync v0.11.0 // test
|
||||
golang.org/x/text v0.22.0 // ext/unicode
|
||||
lukechampine.com/adiantum v1.1.1 // vfs/adiantum
|
||||
)
|
||||
|
||||
|
||||
24
go.sum
24
go.sum
@@ -4,19 +4,19 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
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/ncruces/sort v0.1.5 h1:fiFWXXAqKI8QckPf/6hu/bGFwcEPrirIOFaJqWujs4k=
|
||||
github.com/ncruces/sort v0.1.5/go.mod h1:obJToO4rYr6VWP0Uw5FYymgYGt3Br4RXcs/JdKaXAPk=
|
||||
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.8.2 h1:yIgLR/b2bN31bjxwXHD8a3d+BogigR952csSDdLYEv4=
|
||||
github.com/tetratelabs/wazero v1.8.2/go.mod h1:yAI0XTsMBhREkM/YDAK/zNou3GoiAce1P6+rp/wQhjs=
|
||||
golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
|
||||
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
|
||||
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
|
||||
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
|
||||
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
|
||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||
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.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus=
|
||||
golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
|
||||
golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=
|
||||
golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
|
||||
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
|
||||
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
|
||||
lukechampine.com/adiantum v1.1.1 h1:4fp6gTxWCqpEbLy40ExiYDDED3oUNWx5cTqBCtPdZqA=
|
||||
lukechampine.com/adiantum v1.1.1/go.mod h1:LrAYVnTYLnUtE/yMp5bQr0HstAf060YUF8nM0B6+rUw=
|
||||
|
||||
17
go.work.sum
17
go.work.sum
@@ -1,17 +0,0 @@
|
||||
github.com/ncruces/go-sqlite3 v0.21.0/go.mod h1:zxMOaSG5kFYVFK4xQa0pdwIszqxqJ0W0BxBgwdrNjuA=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
||||
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
|
||||
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
|
||||
golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0=
|
||||
golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4=
|
||||
golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk=
|
||||
golang.org/x/term v0.24.0/go.mod h1:lOBK/LVxemqiMij05LGJ0tzNr8xlmwBRJ81PX6wVLH8=
|
||||
golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M=
|
||||
golang.org/x/term v0.26.0/go.mod h1:Si5m1o57C5nBNQo5z1iq+XDijt21BDBDp2bK0QI8e3E=
|
||||
golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
|
||||
golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
|
||||
@@ -1,11 +1,11 @@
|
||||
module github.com/ncruces/go-sqlite3/gormlite
|
||||
|
||||
go 1.21
|
||||
go 1.22.0
|
||||
|
||||
toolchain go1.23.0
|
||||
toolchain go1.24.0
|
||||
|
||||
require (
|
||||
github.com/ncruces/go-sqlite3 v0.21.3
|
||||
github.com/ncruces/go-sqlite3 v0.23.1
|
||||
gorm.io/gorm v1.25.12
|
||||
)
|
||||
|
||||
@@ -13,7 +13,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.8.2 // indirect
|
||||
golang.org/x/sys v0.29.0 // indirect
|
||||
golang.org/x/text v0.21.0 // indirect
|
||||
github.com/tetratelabs/wazero v1.9.0 // indirect
|
||||
golang.org/x/sys v0.30.0 // indirect
|
||||
golang.org/x/text v0.22.0 // indirect
|
||||
)
|
||||
|
||||
@@ -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.21.3 h1:hHkfNQLcbnxPJZhC/RGw9SwP3bfkv/Y0xUHWsr1CdMQ=
|
||||
github.com/ncruces/go-sqlite3 v0.21.3/go.mod h1:zxMOaSG5kFYVFK4xQa0pdwIszqxqJ0W0BxBgwdrNjuA=
|
||||
github.com/ncruces/go-sqlite3 v0.23.1 h1:zGAd76q+Tr18z/xKGatUlzBQdjR3J+rexfANUcjAgkY=
|
||||
github.com/ncruces/go-sqlite3 v0.23.1/go.mod h1:Xg3FyAZl25HcBSFmcbymdfoTqD7jRnBUmv1jSrbIjdE=
|
||||
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.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=
|
||||
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
|
||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||
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.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
|
||||
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
|
||||
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
|
||||
gorm.io/gorm v1.25.12 h1:I0u8i2hWQItBq1WfE0o2+WuL9+8L21K9e2HHSTE/0f8=
|
||||
gorm.io/gorm v1.25.12/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ=
|
||||
|
||||
@@ -7,18 +7,12 @@ import (
|
||||
"github.com/tetratelabs/wazero"
|
||||
|
||||
"github.com/ncruces/go-sqlite3"
|
||||
"github.com/ncruces/go-sqlite3/internal/util"
|
||||
)
|
||||
|
||||
// notest
|
||||
|
||||
func init() {
|
||||
if util.CompilerSupported() {
|
||||
sqlite3.RuntimeConfig = wazero.NewRuntimeConfigCompiler()
|
||||
} else {
|
||||
sqlite3.RuntimeConfig = wazero.NewRuntimeConfigInterpreter()
|
||||
}
|
||||
sqlite3.RuntimeConfig = sqlite3.RuntimeConfig.WithMemoryLimitPages(512)
|
||||
sqlite3.RuntimeConfig = wazero.NewRuntimeConfig().WithMemoryLimitPages(512)
|
||||
if os.Getenv("CI") != "" {
|
||||
path := filepath.Join(os.TempDir(), "wazero")
|
||||
if err := os.MkdirAll(path, 0777); err == nil {
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
|
||||
"golang.org/x/sys/cpu"
|
||||
)
|
||||
|
||||
func CompilerSupported() bool {
|
||||
switch runtime.GOOS {
|
||||
case "linux", "android",
|
||||
"windows", "darwin",
|
||||
"freebsd", "netbsd", "dragonfly",
|
||||
"solaris", "illumos":
|
||||
break
|
||||
default:
|
||||
return false
|
||||
}
|
||||
switch runtime.GOARCH {
|
||||
case "amd64":
|
||||
return cpu.X86.HasSSE41
|
||||
case "arm64":
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
@@ -7,9 +7,6 @@ import (
|
||||
"github.com/tetratelabs/wazero/api"
|
||||
)
|
||||
|
||||
type i32 interface{ ~int32 | ~uint32 }
|
||||
type i64 interface{ ~int64 | ~uint64 }
|
||||
|
||||
type funcVI[T0 i32] func(context.Context, api.Module, T0)
|
||||
|
||||
func (fn funcVI[T0]) Call(ctx context.Context, mod api.Module, stack []uint64) {
|
||||
|
||||
@@ -20,7 +20,7 @@ func (s *handleState) CloseNotify(ctx context.Context, exitCode uint32) {
|
||||
s.holes = 0
|
||||
}
|
||||
|
||||
func GetHandle(ctx context.Context, id uint32) any {
|
||||
func GetHandle(ctx context.Context, id Ptr_t) any {
|
||||
if id == 0 {
|
||||
return nil
|
||||
}
|
||||
@@ -28,14 +28,14 @@ func GetHandle(ctx context.Context, id uint32) any {
|
||||
return s.handles[^id]
|
||||
}
|
||||
|
||||
func DelHandle(ctx context.Context, id uint32) error {
|
||||
func DelHandle(ctx context.Context, id Ptr_t) error {
|
||||
if id == 0 {
|
||||
return nil
|
||||
}
|
||||
s := ctx.Value(moduleKey{}).(*moduleState)
|
||||
a := s.handles[^id]
|
||||
s.handles[^id] = nil
|
||||
if l := uint32(len(s.handles)); l == ^id {
|
||||
if l := Ptr_t(len(s.handles)); l == ^id {
|
||||
s.handles = s.handles[:l-1]
|
||||
} else {
|
||||
s.holes++
|
||||
@@ -46,7 +46,7 @@ func DelHandle(ctx context.Context, id uint32) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func AddHandle(ctx context.Context, a any) uint32 {
|
||||
func AddHandle(ctx context.Context, a any) Ptr_t {
|
||||
if a == nil {
|
||||
panic(NilErr)
|
||||
}
|
||||
@@ -59,12 +59,12 @@ func AddHandle(ctx context.Context, a any) uint32 {
|
||||
if h == nil {
|
||||
s.holes--
|
||||
s.handles[id] = a
|
||||
return ^uint32(id)
|
||||
return ^Ptr_t(id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add a new slot.
|
||||
s.handles = append(s.handles, a)
|
||||
return -uint32(len(s.handles))
|
||||
return -Ptr_t(len(s.handles))
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package util
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"math"
|
||||
"strconv"
|
||||
"time"
|
||||
"unsafe"
|
||||
@@ -20,7 +21,7 @@ func (j JSON) Scan(value any) error {
|
||||
case int64:
|
||||
buf = strconv.AppendInt(nil, v, 10)
|
||||
case float64:
|
||||
buf = strconv.AppendFloat(nil, v, 'g', -1, 64)
|
||||
buf = AppendNumber(nil, v)
|
||||
case time.Time:
|
||||
buf = append(buf, '"')
|
||||
buf = v.AppendFormat(buf, time.RFC3339Nano)
|
||||
@@ -33,3 +34,17 @@ func (j JSON) Scan(value any) error {
|
||||
|
||||
return json.Unmarshal(buf, j.Value)
|
||||
}
|
||||
|
||||
func AppendNumber(dst []byte, f float64) []byte {
|
||||
switch {
|
||||
case math.IsNaN(f):
|
||||
dst = append(dst, "null"...)
|
||||
case math.IsInf(f, 1):
|
||||
dst = append(dst, "9.0e999"...)
|
||||
case math.IsInf(f, -1):
|
||||
dst = append(dst, "-9.0e999"...)
|
||||
default:
|
||||
return strconv.AppendFloat(dst, f, 'g', -1, 64)
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
@@ -7,110 +7,130 @@ import (
|
||||
"github.com/tetratelabs/wazero/api"
|
||||
)
|
||||
|
||||
func View(mod api.Module, ptr uint32, size uint64) []byte {
|
||||
const (
|
||||
PtrLen = 4
|
||||
IntLen = 4
|
||||
)
|
||||
|
||||
type (
|
||||
i8 interface{ ~int8 | ~uint8 }
|
||||
i32 interface{ ~int32 | ~uint32 }
|
||||
i64 interface{ ~int64 | ~uint64 }
|
||||
|
||||
Stk_t = uint64
|
||||
Ptr_t uint32
|
||||
Res_t int32
|
||||
)
|
||||
|
||||
func View(mod api.Module, ptr Ptr_t, size int64) []byte {
|
||||
if ptr == 0 {
|
||||
panic(NilErr)
|
||||
}
|
||||
if size > math.MaxUint32 {
|
||||
if uint64(size) > math.MaxUint32 {
|
||||
panic(RangeErr)
|
||||
}
|
||||
if size == 0 {
|
||||
return nil
|
||||
}
|
||||
buf, ok := mod.Memory().Read(ptr, uint32(size))
|
||||
buf, ok := mod.Memory().Read(uint32(ptr), uint32(size))
|
||||
if !ok {
|
||||
panic(RangeErr)
|
||||
}
|
||||
return buf
|
||||
}
|
||||
|
||||
func ReadUint8(mod api.Module, ptr uint32) uint8 {
|
||||
func Read[T i8](mod api.Module, ptr Ptr_t) T {
|
||||
if ptr == 0 {
|
||||
panic(NilErr)
|
||||
}
|
||||
v, ok := mod.Memory().ReadByte(ptr)
|
||||
v, ok := mod.Memory().ReadByte(uint32(ptr))
|
||||
if !ok {
|
||||
panic(RangeErr)
|
||||
}
|
||||
return v
|
||||
return T(v)
|
||||
}
|
||||
|
||||
func ReadUint32(mod api.Module, ptr uint32) uint32 {
|
||||
func Write[T i8](mod api.Module, ptr Ptr_t, v T) {
|
||||
if ptr == 0 {
|
||||
panic(NilErr)
|
||||
}
|
||||
v, ok := mod.Memory().ReadUint32Le(ptr)
|
||||
if !ok {
|
||||
panic(RangeErr)
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
func WriteUint8(mod api.Module, ptr uint32, v uint8) {
|
||||
if ptr == 0 {
|
||||
panic(NilErr)
|
||||
}
|
||||
ok := mod.Memory().WriteByte(ptr, v)
|
||||
ok := mod.Memory().WriteByte(uint32(ptr), uint8(v))
|
||||
if !ok {
|
||||
panic(RangeErr)
|
||||
}
|
||||
}
|
||||
|
||||
func WriteUint32(mod api.Module, ptr uint32, v uint32) {
|
||||
func Read32[T i32](mod api.Module, ptr Ptr_t) T {
|
||||
if ptr == 0 {
|
||||
panic(NilErr)
|
||||
}
|
||||
ok := mod.Memory().WriteUint32Le(ptr, v)
|
||||
v, ok := mod.Memory().ReadUint32Le(uint32(ptr))
|
||||
if !ok {
|
||||
panic(RangeErr)
|
||||
}
|
||||
return T(v)
|
||||
}
|
||||
|
||||
func Write32[T i32](mod api.Module, ptr Ptr_t, v T) {
|
||||
if ptr == 0 {
|
||||
panic(NilErr)
|
||||
}
|
||||
ok := mod.Memory().WriteUint32Le(uint32(ptr), uint32(v))
|
||||
if !ok {
|
||||
panic(RangeErr)
|
||||
}
|
||||
}
|
||||
|
||||
func ReadUint64(mod api.Module, ptr uint32) uint64 {
|
||||
func Read64[T i64](mod api.Module, ptr Ptr_t) T {
|
||||
if ptr == 0 {
|
||||
panic(NilErr)
|
||||
}
|
||||
v, ok := mod.Memory().ReadUint64Le(ptr)
|
||||
v, ok := mod.Memory().ReadUint64Le(uint32(ptr))
|
||||
if !ok {
|
||||
panic(RangeErr)
|
||||
}
|
||||
return v
|
||||
return T(v)
|
||||
}
|
||||
|
||||
func WriteUint64(mod api.Module, ptr uint32, v uint64) {
|
||||
func Write64[T i64](mod api.Module, ptr Ptr_t, v T) {
|
||||
if ptr == 0 {
|
||||
panic(NilErr)
|
||||
}
|
||||
ok := mod.Memory().WriteUint64Le(ptr, v)
|
||||
ok := mod.Memory().WriteUint64Le(uint32(ptr), uint64(v))
|
||||
if !ok {
|
||||
panic(RangeErr)
|
||||
}
|
||||
}
|
||||
|
||||
func ReadFloat64(mod api.Module, ptr uint32) float64 {
|
||||
return math.Float64frombits(ReadUint64(mod, ptr))
|
||||
func ReadFloat64(mod api.Module, ptr Ptr_t) float64 {
|
||||
return math.Float64frombits(Read64[uint64](mod, ptr))
|
||||
}
|
||||
|
||||
func WriteFloat64(mod api.Module, ptr uint32, v float64) {
|
||||
WriteUint64(mod, ptr, math.Float64bits(v))
|
||||
func WriteFloat64(mod api.Module, ptr Ptr_t, v float64) {
|
||||
Write64(mod, ptr, math.Float64bits(v))
|
||||
}
|
||||
|
||||
func ReadString(mod api.Module, ptr, maxlen uint32) string {
|
||||
func ReadBool(mod api.Module, ptr Ptr_t) bool {
|
||||
return Read32[int32](mod, ptr) != 0
|
||||
}
|
||||
|
||||
func WriteBool(mod api.Module, ptr Ptr_t, v bool) {
|
||||
var i int32
|
||||
if v {
|
||||
i = 1
|
||||
}
|
||||
Write32(mod, ptr, i)
|
||||
}
|
||||
|
||||
func ReadString(mod api.Module, ptr Ptr_t, maxlen int64) string {
|
||||
if ptr == 0 {
|
||||
panic(NilErr)
|
||||
}
|
||||
switch maxlen {
|
||||
case 0:
|
||||
if maxlen <= 0 {
|
||||
return ""
|
||||
case math.MaxUint32:
|
||||
// avoid overflow
|
||||
default:
|
||||
maxlen = maxlen + 1
|
||||
}
|
||||
mem := mod.Memory()
|
||||
buf, ok := mem.Read(ptr, maxlen)
|
||||
maxlen = min(maxlen, math.MaxInt32-1) + 1
|
||||
buf, ok := mem.Read(uint32(ptr), uint32(maxlen))
|
||||
if !ok {
|
||||
buf, ok = mem.Read(ptr, mem.Size()-ptr)
|
||||
buf, ok = mem.Read(uint32(ptr), mem.Size()-uint32(ptr))
|
||||
if !ok {
|
||||
panic(RangeErr)
|
||||
}
|
||||
@@ -122,13 +142,13 @@ func ReadString(mod api.Module, ptr, maxlen uint32) string {
|
||||
}
|
||||
}
|
||||
|
||||
func WriteBytes(mod api.Module, ptr uint32, b []byte) {
|
||||
buf := View(mod, ptr, uint64(len(b)))
|
||||
func WriteBytes(mod api.Module, ptr Ptr_t, b []byte) {
|
||||
buf := View(mod, ptr, int64(len(b)))
|
||||
copy(buf, b)
|
||||
}
|
||||
|
||||
func WriteString(mod api.Module, ptr uint32, s string) {
|
||||
buf := View(mod, ptr, uint64(len(s)+1))
|
||||
func WriteString(mod api.Module, ptr Ptr_t, s string) {
|
||||
buf := View(mod, ptr, int64(len(s))+1)
|
||||
buf[len(s)] = 0
|
||||
copy(buf, s)
|
||||
}
|
||||
|
||||
@@ -31,90 +31,90 @@ func TestView_overflow(t *testing.T) {
|
||||
func TestReadUint8_nil(t *testing.T) {
|
||||
defer func() { _ = recover() }()
|
||||
mock := wazerotest.NewModule(wazerotest.NewFixedMemory(wazerotest.PageSize))
|
||||
ReadUint8(mock, 0)
|
||||
Read[byte](mock, 0)
|
||||
t.Error("want panic")
|
||||
}
|
||||
|
||||
func TestReadUint8_range(t *testing.T) {
|
||||
defer func() { _ = recover() }()
|
||||
mock := wazerotest.NewModule(wazerotest.NewFixedMemory(wazerotest.PageSize))
|
||||
ReadUint8(mock, wazerotest.PageSize)
|
||||
Read[byte](mock, wazerotest.PageSize)
|
||||
t.Error("want panic")
|
||||
}
|
||||
|
||||
func TestReadUint32_nil(t *testing.T) {
|
||||
defer func() { _ = recover() }()
|
||||
mock := wazerotest.NewModule(wazerotest.NewFixedMemory(wazerotest.PageSize))
|
||||
ReadUint32(mock, 0)
|
||||
Read32[uint32](mock, 0)
|
||||
t.Error("want panic")
|
||||
}
|
||||
|
||||
func TestReadUint32_range(t *testing.T) {
|
||||
defer func() { _ = recover() }()
|
||||
mock := wazerotest.NewModule(wazerotest.NewFixedMemory(wazerotest.PageSize))
|
||||
ReadUint32(mock, wazerotest.PageSize-2)
|
||||
Read32[uint32](mock, wazerotest.PageSize-2)
|
||||
t.Error("want panic")
|
||||
}
|
||||
|
||||
func TestReadUint64_nil(t *testing.T) {
|
||||
defer func() { _ = recover() }()
|
||||
mock := wazerotest.NewModule(wazerotest.NewFixedMemory(wazerotest.PageSize))
|
||||
ReadUint64(mock, 0)
|
||||
Read64[uint64](mock, 0)
|
||||
t.Error("want panic")
|
||||
}
|
||||
|
||||
func TestReadUint64_range(t *testing.T) {
|
||||
defer func() { _ = recover() }()
|
||||
mock := wazerotest.NewModule(wazerotest.NewFixedMemory(wazerotest.PageSize))
|
||||
ReadUint64(mock, wazerotest.PageSize-2)
|
||||
Read64[uint64](mock, wazerotest.PageSize-2)
|
||||
t.Error("want panic")
|
||||
}
|
||||
|
||||
func TestWriteUint8_nil(t *testing.T) {
|
||||
defer func() { _ = recover() }()
|
||||
mock := wazerotest.NewModule(wazerotest.NewFixedMemory(wazerotest.PageSize))
|
||||
WriteUint8(mock, 0, 1)
|
||||
Write[byte](mock, 0, 1)
|
||||
t.Error("want panic")
|
||||
}
|
||||
|
||||
func TestWriteUint8_range(t *testing.T) {
|
||||
defer func() { _ = recover() }()
|
||||
mock := wazerotest.NewModule(wazerotest.NewFixedMemory(wazerotest.PageSize))
|
||||
WriteUint8(mock, wazerotest.PageSize, 1)
|
||||
Write[byte](mock, wazerotest.PageSize, 1)
|
||||
t.Error("want panic")
|
||||
}
|
||||
|
||||
func TestWriteUint32_nil(t *testing.T) {
|
||||
defer func() { _ = recover() }()
|
||||
mock := wazerotest.NewModule(wazerotest.NewFixedMemory(wazerotest.PageSize))
|
||||
WriteUint32(mock, 0, 1)
|
||||
Write32[uint32](mock, 0, 1)
|
||||
t.Error("want panic")
|
||||
}
|
||||
|
||||
func TestWriteUint32_range(t *testing.T) {
|
||||
defer func() { _ = recover() }()
|
||||
mock := wazerotest.NewModule(wazerotest.NewFixedMemory(wazerotest.PageSize))
|
||||
WriteUint32(mock, wazerotest.PageSize-2, 1)
|
||||
Write32[uint32](mock, wazerotest.PageSize-2, 1)
|
||||
t.Error("want panic")
|
||||
}
|
||||
|
||||
func TestWriteUint64_nil(t *testing.T) {
|
||||
defer func() { _ = recover() }()
|
||||
mock := wazerotest.NewModule(wazerotest.NewFixedMemory(wazerotest.PageSize))
|
||||
WriteUint64(mock, 0, 1)
|
||||
Write64[uint64](mock, 0, 1)
|
||||
t.Error("want panic")
|
||||
}
|
||||
|
||||
func TestWriteUint64_range(t *testing.T) {
|
||||
defer func() { _ = recover() }()
|
||||
mock := wazerotest.NewModule(wazerotest.NewFixedMemory(wazerotest.PageSize))
|
||||
WriteUint64(mock, wazerotest.PageSize-2, 1)
|
||||
Write64[uint64](mock, wazerotest.PageSize-2, 1)
|
||||
t.Error("want panic")
|
||||
}
|
||||
|
||||
func TestReadString_range(t *testing.T) {
|
||||
defer func() { _ = recover() }()
|
||||
mock := wazerotest.NewModule(wazerotest.NewFixedMemory(wazerotest.PageSize))
|
||||
ReadString(mock, wazerotest.PageSize+2, math.MaxUint32)
|
||||
ReadString(mock, wazerotest.PageSize+2, math.MaxInt)
|
||||
t.Error("want panic")
|
||||
}
|
||||
|
||||
@@ -25,9 +25,9 @@ func (s *mmapState) new(ctx context.Context, mod api.Module, size int32) *Mapped
|
||||
|
||||
// Allocate page aligned memmory.
|
||||
alloc := mod.ExportedFunction("aligned_alloc")
|
||||
stack := [...]uint64{
|
||||
uint64(unix.Getpagesize()),
|
||||
uint64(size),
|
||||
stack := [...]Stk_t{
|
||||
Stk_t(unix.Getpagesize()),
|
||||
Stk_t(size),
|
||||
}
|
||||
if err := alloc.CallWithStack(ctx, stack[:]); err != nil {
|
||||
panic(err)
|
||||
@@ -37,20 +37,20 @@ func (s *mmapState) new(ctx context.Context, mod api.Module, size int32) *Mapped
|
||||
}
|
||||
|
||||
// Save the newly allocated region.
|
||||
ptr := uint32(stack[0])
|
||||
buf := View(mod, ptr, uint64(size))
|
||||
res := &MappedRegion{
|
||||
ptr := Ptr_t(stack[0])
|
||||
buf := View(mod, ptr, int64(size))
|
||||
ret := &MappedRegion{
|
||||
Ptr: ptr,
|
||||
size: size,
|
||||
addr: unsafe.Pointer(&buf[0]),
|
||||
}
|
||||
s.regions = append(s.regions, res)
|
||||
return res
|
||||
s.regions = append(s.regions, ret)
|
||||
return ret
|
||||
}
|
||||
|
||||
type MappedRegion struct {
|
||||
addr unsafe.Pointer
|
||||
Ptr uint32
|
||||
Ptr Ptr_t
|
||||
size int32
|
||||
used bool
|
||||
}
|
||||
|
||||
@@ -29,13 +29,13 @@ func MapRegion(ctx context.Context, mod api.Module, f *os.File, offset int64, si
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res := &MappedRegion{Handle: h, addr: a}
|
||||
ret := &MappedRegion{Handle: h, addr: a}
|
||||
// SliceHeader, although deprecated, avoids a go vet warning.
|
||||
sh := (*reflect.SliceHeader)(unsafe.Pointer(&res.Data))
|
||||
sh := (*reflect.SliceHeader)(unsafe.Pointer(&ret.Data))
|
||||
sh.Len = int(size)
|
||||
sh.Cap = int(size)
|
||||
sh.Data = a
|
||||
return res, nil
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (r *MappedRegion) Unmap() error {
|
||||
|
||||
85
sqlite.go
85
sqlite.go
@@ -3,7 +3,6 @@ package sqlite3
|
||||
|
||||
import (
|
||||
"context"
|
||||
"math"
|
||||
"math/bits"
|
||||
"os"
|
||||
"sync"
|
||||
@@ -48,18 +47,14 @@ func compileSQLite() {
|
||||
ctx := context.Background()
|
||||
cfg := RuntimeConfig
|
||||
if cfg == nil {
|
||||
if util.CompilerSupported() {
|
||||
cfg = wazero.NewRuntimeConfigCompiler()
|
||||
} else {
|
||||
cfg = wazero.NewRuntimeConfigInterpreter()
|
||||
}
|
||||
cfg = wazero.NewRuntimeConfig()
|
||||
if bits.UintSize < 64 {
|
||||
cfg = cfg.WithMemoryLimitPages(512) // 32MB
|
||||
} else {
|
||||
cfg = cfg.WithMemoryLimitPages(4096) // 256MB
|
||||
}
|
||||
cfg = cfg.WithCoreFeatures(api.CoreFeaturesV2)
|
||||
}
|
||||
cfg = cfg.WithCoreFeatures(api.CoreFeaturesV2)
|
||||
|
||||
instance.runtime = wazero.NewRuntimeWithConfig(ctx, cfg)
|
||||
|
||||
@@ -94,7 +89,7 @@ type sqlite struct {
|
||||
id [32]*byte
|
||||
mask uint32
|
||||
}
|
||||
stack [9]uint64
|
||||
stack [9]stk_t
|
||||
}
|
||||
|
||||
func instantiateSQLite() (sqlt *sqlite, err error) {
|
||||
@@ -120,7 +115,7 @@ func (sqlt *sqlite) close() error {
|
||||
return sqlt.mod.Close(sqlt.ctx)
|
||||
}
|
||||
|
||||
func (sqlt *sqlite) error(rc uint64, handle uint32, sql ...string) error {
|
||||
func (sqlt *sqlite) error(rc res_t, handle ptr_t, sql ...string) error {
|
||||
if rc == _OK {
|
||||
return nil
|
||||
}
|
||||
@@ -131,18 +126,18 @@ func (sqlt *sqlite) error(rc uint64, handle uint32, sql ...string) error {
|
||||
panic(util.OOMErr)
|
||||
}
|
||||
|
||||
if r := sqlt.call("sqlite3_errstr", rc); r != 0 {
|
||||
err.str = util.ReadString(sqlt.mod, uint32(r), _MAX_NAME)
|
||||
if ptr := ptr_t(sqlt.call("sqlite3_errstr", stk_t(rc))); ptr != 0 {
|
||||
err.str = util.ReadString(sqlt.mod, ptr, _MAX_NAME)
|
||||
}
|
||||
|
||||
if handle != 0 {
|
||||
if r := sqlt.call("sqlite3_errmsg", uint64(handle)); r != 0 {
|
||||
err.msg = util.ReadString(sqlt.mod, uint32(r), _MAX_LENGTH)
|
||||
if ptr := ptr_t(sqlt.call("sqlite3_errmsg", stk_t(handle))); ptr != 0 {
|
||||
err.msg = util.ReadString(sqlt.mod, ptr, _MAX_LENGTH)
|
||||
}
|
||||
|
||||
if len(sql) != 0 {
|
||||
if r := sqlt.call("sqlite3_error_offset", uint64(handle)); r != math.MaxUint32 {
|
||||
err.sql = sql[0][r:]
|
||||
if i := int32(sqlt.call("sqlite3_error_offset", stk_t(handle))); i != -1 {
|
||||
err.sql = sql[0][i:]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -182,7 +177,7 @@ func (sqlt *sqlite) putfn(name string, fn api.Function) {
|
||||
}
|
||||
}
|
||||
|
||||
func (sqlt *sqlite) call(name string, params ...uint64) uint64 {
|
||||
func (sqlt *sqlite) call(name string, params ...stk_t) stk_t {
|
||||
copy(sqlt.stack[:], params)
|
||||
fn := sqlt.getfn(name)
|
||||
err := fn.CallWithStack(sqlt.ctx, sqlt.stack[:])
|
||||
@@ -190,33 +185,33 @@ func (sqlt *sqlite) call(name string, params ...uint64) uint64 {
|
||||
panic(err)
|
||||
}
|
||||
sqlt.putfn(name, fn)
|
||||
return sqlt.stack[0]
|
||||
return stk_t(sqlt.stack[0])
|
||||
}
|
||||
|
||||
func (sqlt *sqlite) free(ptr uint32) {
|
||||
func (sqlt *sqlite) free(ptr ptr_t) {
|
||||
if ptr == 0 {
|
||||
return
|
||||
}
|
||||
sqlt.call("sqlite3_free", uint64(ptr))
|
||||
sqlt.call("sqlite3_free", stk_t(ptr))
|
||||
}
|
||||
|
||||
func (sqlt *sqlite) new(size uint64) uint32 {
|
||||
ptr := uint32(sqlt.call("sqlite3_malloc64", size))
|
||||
func (sqlt *sqlite) new(size int64) ptr_t {
|
||||
ptr := ptr_t(sqlt.call("sqlite3_malloc64", stk_t(size)))
|
||||
if ptr == 0 && size != 0 {
|
||||
panic(util.OOMErr)
|
||||
}
|
||||
return ptr
|
||||
}
|
||||
|
||||
func (sqlt *sqlite) realloc(ptr uint32, size uint64) uint32 {
|
||||
ptr = uint32(sqlt.call("sqlite3_realloc64", uint64(ptr), size))
|
||||
func (sqlt *sqlite) realloc(ptr ptr_t, size int64) ptr_t {
|
||||
ptr = ptr_t(sqlt.call("sqlite3_realloc64", stk_t(ptr), stk_t(size)))
|
||||
if ptr == 0 && size != 0 {
|
||||
panic(util.OOMErr)
|
||||
}
|
||||
return ptr
|
||||
}
|
||||
|
||||
func (sqlt *sqlite) newBytes(b []byte) uint32 {
|
||||
func (sqlt *sqlite) newBytes(b []byte) ptr_t {
|
||||
if (*[0]byte)(b) == nil {
|
||||
return 0
|
||||
}
|
||||
@@ -224,33 +219,31 @@ func (sqlt *sqlite) newBytes(b []byte) uint32 {
|
||||
if size == 0 {
|
||||
size = 1
|
||||
}
|
||||
ptr := sqlt.new(uint64(size))
|
||||
ptr := sqlt.new(int64(size))
|
||||
util.WriteBytes(sqlt.mod, ptr, b)
|
||||
return ptr
|
||||
}
|
||||
|
||||
func (sqlt *sqlite) newString(s string) uint32 {
|
||||
ptr := sqlt.new(uint64(len(s) + 1))
|
||||
func (sqlt *sqlite) newString(s string) ptr_t {
|
||||
ptr := sqlt.new(int64(len(s)) + 1)
|
||||
util.WriteString(sqlt.mod, ptr, s)
|
||||
return ptr
|
||||
}
|
||||
|
||||
func (sqlt *sqlite) newArena(size uint64) arena {
|
||||
// Ensure the arena's size is a multiple of 8.
|
||||
size = (size + 7) &^ 7
|
||||
const arenaSize = 4096
|
||||
|
||||
func (sqlt *sqlite) newArena() arena {
|
||||
return arena{
|
||||
sqlt: sqlt,
|
||||
size: uint32(size),
|
||||
base: sqlt.new(size),
|
||||
base: sqlt.new(arenaSize),
|
||||
}
|
||||
}
|
||||
|
||||
type arena struct {
|
||||
sqlt *sqlite
|
||||
ptrs []uint32
|
||||
base uint32
|
||||
next uint32
|
||||
size uint32
|
||||
ptrs []ptr_t
|
||||
base ptr_t
|
||||
next int32
|
||||
}
|
||||
|
||||
func (a *arena) free() {
|
||||
@@ -277,34 +270,34 @@ func (a *arena) mark() (reset func()) {
|
||||
}
|
||||
}
|
||||
|
||||
func (a *arena) new(size uint64) uint32 {
|
||||
func (a *arena) new(size int64) ptr_t {
|
||||
// Align the next address, to 4 or 8 bytes.
|
||||
if size&7 != 0 {
|
||||
a.next = (a.next + 3) &^ 3
|
||||
} else {
|
||||
a.next = (a.next + 7) &^ 7
|
||||
}
|
||||
if size <= uint64(a.size-a.next) {
|
||||
ptr := a.base + a.next
|
||||
a.next += uint32(size)
|
||||
return ptr
|
||||
if size <= arenaSize-int64(a.next) {
|
||||
ptr := a.base + ptr_t(a.next)
|
||||
a.next += int32(size)
|
||||
return ptr_t(ptr)
|
||||
}
|
||||
ptr := a.sqlt.new(size)
|
||||
a.ptrs = append(a.ptrs, ptr)
|
||||
return ptr
|
||||
return ptr_t(ptr)
|
||||
}
|
||||
|
||||
func (a *arena) bytes(b []byte) uint32 {
|
||||
func (a *arena) bytes(b []byte) ptr_t {
|
||||
if (*[0]byte)(b) == nil {
|
||||
return 0
|
||||
}
|
||||
ptr := a.new(uint64(len(b)))
|
||||
ptr := a.new(int64(len(b)))
|
||||
util.WriteBytes(a.sqlt.mod, ptr, b)
|
||||
return ptr
|
||||
}
|
||||
|
||||
func (a *arena) string(s string) uint32 {
|
||||
ptr := a.new(uint64(len(s) + 1))
|
||||
func (a *arena) string(s string) ptr_t {
|
||||
ptr := a.new(int64(len(s)) + 1)
|
||||
util.WriteString(a.sqlt.mod, ptr, s)
|
||||
return ptr
|
||||
}
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
--- 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
|
||||
@@ -183054,7 +183054,7 @@
|
||||
@@ -183355,7 +183355,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/2025/sqlite-amalgamation-3480000.zip"
|
||||
curl -#OL "https://sqlite.org/2025/sqlite-amalgamation-3490100.zip"
|
||||
unzip -d . sqlite-amalgamation-*.zip
|
||||
mv sqlite-amalgamation-*/sqlite3.c .
|
||||
mv sqlite-amalgamation-*/sqlite3.h .
|
||||
@@ -19,30 +19,30 @@ rm -rf sqlite-amalgamation-*
|
||||
|
||||
mkdir -p ext/
|
||||
cd ext/
|
||||
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"
|
||||
curl -#OL "https://github.com/sqlite/sqlite/raw/version-3.49.1/ext/misc/anycollseq.c"
|
||||
curl -#OL "https://github.com/sqlite/sqlite/raw/version-3.49.1/ext/misc/base64.c"
|
||||
curl -#OL "https://github.com/sqlite/sqlite/raw/version-3.49.1/ext/misc/decimal.c"
|
||||
curl -#OL "https://github.com/sqlite/sqlite/raw/version-3.49.1/ext/misc/ieee754.c"
|
||||
curl -#OL "https://github.com/sqlite/sqlite/raw/version-3.49.1/ext/misc/regexp.c"
|
||||
curl -#OL "https://github.com/sqlite/sqlite/raw/version-3.49.1/ext/misc/series.c"
|
||||
curl -#OL "https://github.com/sqlite/sqlite/raw/version-3.49.1/ext/misc/spellfix.c"
|
||||
curl -#OL "https://github.com/sqlite/sqlite/raw/version-3.49.1/ext/misc/uint.c"
|
||||
cd ~-
|
||||
|
||||
cd ../vfs/tests/mptest/testdata/
|
||||
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"
|
||||
curl -#OL "https://github.com/sqlite/sqlite/raw/version-3.49.1/mptest/config01.test"
|
||||
curl -#OL "https://github.com/sqlite/sqlite/raw/version-3.49.1/mptest/config02.test"
|
||||
curl -#OL "https://github.com/sqlite/sqlite/raw/version-3.49.1/mptest/crash01.test"
|
||||
curl -#OL "https://github.com/sqlite/sqlite/raw/version-3.49.1/mptest/crash02.subtest"
|
||||
curl -#OL "https://github.com/sqlite/sqlite/raw/version-3.49.1/mptest/multiwrite01.test"
|
||||
cd ~-
|
||||
|
||||
cd ../vfs/tests/mptest/wasm/
|
||||
curl -#OL "https://github.com/sqlite/sqlite/raw/version-3.48.0/mptest/mptest.c"
|
||||
curl -#OL "https://github.com/sqlite/sqlite/raw/version-3.49.1/mptest/mptest.c"
|
||||
cd ~-
|
||||
|
||||
cd ../vfs/tests/speedtest1/wasm/
|
||||
curl -#OL "https://github.com/sqlite/sqlite/raw/version-3.48.0/test/speedtest1.c"
|
||||
curl -#OL "https://github.com/sqlite/sqlite/raw/version-3.49.1/test/speedtest1.c"
|
||||
cd ~-
|
||||
|
||||
cat *.patch | patch -p0 --no-backup-if-mismatch
|
||||
@@ -65,8 +65,8 @@ int sqlite3_autovacuum_pages_go(sqlite3 *db, go_handle app) {
|
||||
|
||||
#ifndef sqliteBusyCallback
|
||||
|
||||
static int sqliteBusyCallback(sqlite3 *db, int count) {
|
||||
return go_busy_timeout(count, db->busyTimeout);
|
||||
static int sqliteBusyCallback(void *ptr, int count) {
|
||||
return go_busy_timeout(count, ((sqlite3 *)ptr)->busyTimeout);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -33,10 +33,8 @@
|
||||
#define HAVE_MALLOC_H 1
|
||||
#define HAVE_MALLOC_USABLE_SIZE 1
|
||||
|
||||
// Implemented in vfs.c.
|
||||
int localtime_s(struct tm *const pTm, time_t const *const pTime);
|
||||
|
||||
// Implemented in hooks.c.
|
||||
#ifndef sqliteBusyCallback
|
||||
static int sqliteBusyCallback(sqlite3 *, int);
|
||||
#endif
|
||||
static int sqliteBusyCallback(void *, int);
|
||||
|
||||
// Implemented in vfs.c.
|
||||
int localtime_s(struct tm *const pTm, time_t const *const pTime);
|
||||
@@ -90,6 +90,7 @@ struct go_file {
|
||||
};
|
||||
|
||||
sqlite3_vfs *sqlite3_vfs_find(const char *zVfsName) {
|
||||
// The default VFS.
|
||||
if (!zVfsName || !strcmp(zVfsName, "os")) {
|
||||
static sqlite3_vfs os_vfs = {
|
||||
.iVersion = 2,
|
||||
@@ -109,18 +110,21 @@ sqlite3_vfs *sqlite3_vfs_find(const char *zVfsName) {
|
||||
return &os_vfs;
|
||||
}
|
||||
|
||||
// Check if a Go VFS exists.
|
||||
if (!go_vfs_find(zVfsName)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static sqlite3_vfs *go_vfs_list;
|
||||
|
||||
// Do we already have a C wrapper for the Go VFS?
|
||||
for (sqlite3_vfs *it = go_vfs_list; it; it = it->pNext) {
|
||||
if (!strcmp(zVfsName, it->zName)) {
|
||||
return it;
|
||||
}
|
||||
}
|
||||
|
||||
// Delete C wrappers that are no longer needed.
|
||||
for (sqlite3_vfs **ptr = &go_vfs_list; *ptr;) {
|
||||
sqlite3_vfs *it = *ptr;
|
||||
if (go_vfs_find(it->zName)) {
|
||||
@@ -131,6 +135,7 @@ sqlite3_vfs *sqlite3_vfs_find(const char *zVfsName) {
|
||||
}
|
||||
}
|
||||
|
||||
// Create a new C wrapper.
|
||||
sqlite3_vfs *head = go_vfs_list;
|
||||
go_vfs_list = malloc(sizeof(sqlite3_vfs) + strlen(zVfsName) + 1);
|
||||
char *name = (char *)(go_vfs_list + 1);
|
||||
@@ -158,13 +163,11 @@ int localtime_s(struct tm *const pTm, time_t const *const pTime) {
|
||||
return go_localtime(pTm, (sqlite3_int64)*pTime);
|
||||
}
|
||||
|
||||
int sqlite3_os_init() {
|
||||
return SQLITE_OK;
|
||||
}
|
||||
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]);
|
||||
return ((int (*)(void *))(ap[0]))(ap[1]);
|
||||
}
|
||||
|
||||
static_assert(offsetof(sqlite3_vfs, zName) == 16, "Unexpected offset");
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Remove VFS registration. Go handles it.
|
||||
--- sqlite3.c.orig
|
||||
+++ sqlite3.c
|
||||
@@ -26623,7 +26623,7 @@
|
||||
@@ -26725,7 +26725,7 @@
|
||||
sqlite3_free(p);
|
||||
return sqlite3_os_init();
|
||||
}
|
||||
@@ -10,7 +10,7 @@
|
||||
/*
|
||||
** The list of all registered VFS implementations.
|
||||
*/
|
||||
@@ -26720,7 +26720,7 @@
|
||||
@@ -26822,7 +26822,7 @@
|
||||
sqlite3_mutex_leave(mutex);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ func Test_sqlite_error_OOM(t *testing.T) {
|
||||
defer sqlite.close()
|
||||
|
||||
defer func() { _ = recover() }()
|
||||
sqlite.error(uint64(NOMEM), 0)
|
||||
sqlite.error(res_t(NOMEM), 0)
|
||||
t.Error("want panic")
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@ func Test_sqlite_newArena(t *testing.T) {
|
||||
}
|
||||
defer sqlite.close()
|
||||
|
||||
arena := sqlite.newArena(16)
|
||||
arena := sqlite.newArena()
|
||||
defer arena.free()
|
||||
|
||||
const title = "Lorem ipsum"
|
||||
@@ -73,7 +73,7 @@ func Test_sqlite_newArena(t *testing.T) {
|
||||
if ptr == 0 {
|
||||
t.Fatalf("got nullptr")
|
||||
}
|
||||
if got := util.ReadString(sqlite.mod, ptr, math.MaxUint32); got != title {
|
||||
if got := util.ReadString(sqlite.mod, ptr, math.MaxInt); got != title {
|
||||
t.Errorf("got %q, want %q", got, title)
|
||||
}
|
||||
|
||||
@@ -82,7 +82,7 @@ func Test_sqlite_newArena(t *testing.T) {
|
||||
if ptr == 0 {
|
||||
t.Fatalf("got nullptr")
|
||||
}
|
||||
if got := util.ReadString(sqlite.mod, ptr, math.MaxUint32); got != body {
|
||||
if got := util.ReadString(sqlite.mod, ptr, math.MaxInt); got != body {
|
||||
t.Errorf("got %q, want %q", got, body)
|
||||
}
|
||||
|
||||
@@ -94,7 +94,7 @@ func Test_sqlite_newArena(t *testing.T) {
|
||||
if ptr == 0 {
|
||||
t.Fatalf("got nullptr")
|
||||
}
|
||||
if got := util.View(sqlite.mod, ptr, uint64(len(title))); string(got) != title {
|
||||
if got := util.View(sqlite.mod, ptr, int64(len(title))); string(got) != title {
|
||||
t.Errorf("got %q, want %q", got, title)
|
||||
}
|
||||
|
||||
@@ -122,7 +122,7 @@ func Test_sqlite_newBytes(t *testing.T) {
|
||||
}
|
||||
|
||||
want := buf
|
||||
if got := util.View(sqlite.mod, ptr, uint64(len(want))); !bytes.Equal(got, want) {
|
||||
if got := util.View(sqlite.mod, ptr, int64(len(want))); !bytes.Equal(got, want) {
|
||||
t.Errorf("got %q, want %q", got, want)
|
||||
}
|
||||
|
||||
@@ -130,10 +130,6 @@ func Test_sqlite_newBytes(t *testing.T) {
|
||||
if ptr == 0 {
|
||||
t.Fatal("got nullptr, want a pointer")
|
||||
}
|
||||
|
||||
if got := util.View(sqlite.mod, ptr, 0); got != nil {
|
||||
t.Errorf("got %q, want nil", got)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_sqlite_newString(t *testing.T) {
|
||||
@@ -157,7 +153,7 @@ func Test_sqlite_newString(t *testing.T) {
|
||||
}
|
||||
|
||||
want := str + "\000"
|
||||
if got := util.View(sqlite.mod, ptr, uint64(len(want))); string(got) != want {
|
||||
if got := util.View(sqlite.mod, ptr, int64(len(want))); string(got) != want {
|
||||
t.Errorf("got %q, want %q", got, want)
|
||||
}
|
||||
}
|
||||
@@ -183,7 +179,7 @@ func Test_sqlite_getString(t *testing.T) {
|
||||
}
|
||||
|
||||
want := "sqlite3"
|
||||
if got := util.ReadString(sqlite.mod, ptr, math.MaxUint32); got != want {
|
||||
if got := util.ReadString(sqlite.mod, ptr, math.MaxInt); got != want {
|
||||
t.Errorf("got %q, want %q", got, want)
|
||||
}
|
||||
if got := util.ReadString(sqlite.mod, ptr, 0); got != "" {
|
||||
@@ -192,13 +188,13 @@ func Test_sqlite_getString(t *testing.T) {
|
||||
|
||||
func() {
|
||||
defer func() { _ = recover() }()
|
||||
util.ReadString(sqlite.mod, ptr, uint32(len(want)/2))
|
||||
util.ReadString(sqlite.mod, ptr, int64(len(want))/2)
|
||||
t.Error("want panic")
|
||||
}()
|
||||
|
||||
func() {
|
||||
defer func() { _ = recover() }()
|
||||
util.ReadString(sqlite.mod, 0, math.MaxUint32)
|
||||
util.ReadString(sqlite.mod, 0, math.MaxInt)
|
||||
t.Error("want panic")
|
||||
}()
|
||||
}
|
||||
|
||||
256
stmt.go
256
stmt.go
@@ -16,7 +16,7 @@ type Stmt struct {
|
||||
c *Conn
|
||||
err error
|
||||
sql string
|
||||
handle uint32
|
||||
handle ptr_t
|
||||
}
|
||||
|
||||
// Close destroys the prepared statement object.
|
||||
@@ -29,7 +29,7 @@ func (s *Stmt) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
r := s.c.call("sqlite3_finalize", uint64(s.handle))
|
||||
rc := res_t(s.c.call("sqlite3_finalize", stk_t(s.handle)))
|
||||
stmts := s.c.stmts
|
||||
for i := range stmts {
|
||||
if s == stmts[i] {
|
||||
@@ -42,7 +42,7 @@ func (s *Stmt) Close() error {
|
||||
}
|
||||
|
||||
s.handle = 0
|
||||
return s.c.error(r)
|
||||
return s.c.error(rc)
|
||||
}
|
||||
|
||||
// Conn returns the database connection to which the prepared statement belongs.
|
||||
@@ -64,9 +64,9 @@ func (s *Stmt) SQL() string {
|
||||
//
|
||||
// https://sqlite.org/c3ref/expanded_sql.html
|
||||
func (s *Stmt) ExpandedSQL() string {
|
||||
r := s.c.call("sqlite3_expanded_sql", uint64(s.handle))
|
||||
sql := util.ReadString(s.c.mod, uint32(r), _MAX_SQL_LENGTH)
|
||||
s.c.free(uint32(r))
|
||||
ptr := ptr_t(s.c.call("sqlite3_expanded_sql", stk_t(s.handle)))
|
||||
sql := util.ReadString(s.c.mod, ptr, _MAX_SQL_LENGTH)
|
||||
s.c.free(ptr)
|
||||
return sql
|
||||
}
|
||||
|
||||
@@ -75,25 +75,25 @@ func (s *Stmt) ExpandedSQL() string {
|
||||
//
|
||||
// https://sqlite.org/c3ref/stmt_readonly.html
|
||||
func (s *Stmt) ReadOnly() bool {
|
||||
r := s.c.call("sqlite3_stmt_readonly", uint64(s.handle))
|
||||
return r != 0
|
||||
b := int32(s.c.call("sqlite3_stmt_readonly", stk_t(s.handle)))
|
||||
return b != 0
|
||||
}
|
||||
|
||||
// Reset resets the prepared statement object.
|
||||
//
|
||||
// https://sqlite.org/c3ref/reset.html
|
||||
func (s *Stmt) Reset() error {
|
||||
r := s.c.call("sqlite3_reset", uint64(s.handle))
|
||||
rc := res_t(s.c.call("sqlite3_reset", stk_t(s.handle)))
|
||||
s.err = nil
|
||||
return s.c.error(r)
|
||||
return s.c.error(rc)
|
||||
}
|
||||
|
||||
// Busy determines if a prepared statement has been reset.
|
||||
//
|
||||
// https://sqlite.org/c3ref/stmt_busy.html
|
||||
func (s *Stmt) Busy() bool {
|
||||
r := s.c.call("sqlite3_stmt_busy", uint64(s.handle))
|
||||
return r != 0
|
||||
rc := res_t(s.c.call("sqlite3_stmt_busy", stk_t(s.handle)))
|
||||
return rc != 0
|
||||
}
|
||||
|
||||
// Step evaluates the SQL statement.
|
||||
@@ -107,15 +107,15 @@ func (s *Stmt) Busy() bool {
|
||||
// https://sqlite.org/c3ref/step.html
|
||||
func (s *Stmt) Step() bool {
|
||||
s.c.checkInterrupt(s.c.handle)
|
||||
r := s.c.call("sqlite3_step", uint64(s.handle))
|
||||
switch r {
|
||||
rc := res_t(s.c.call("sqlite3_step", stk_t(s.handle)))
|
||||
switch rc {
|
||||
case _ROW:
|
||||
s.err = nil
|
||||
return true
|
||||
case _DONE:
|
||||
s.err = nil
|
||||
default:
|
||||
s.err = s.c.error(r)
|
||||
s.err = s.c.error(rc)
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -143,30 +143,30 @@ func (s *Stmt) Status(op StmtStatus, reset bool) int {
|
||||
if op > STMTSTATUS_FILTER_HIT && op != STMTSTATUS_MEMUSED {
|
||||
return 0
|
||||
}
|
||||
var i uint64
|
||||
var i int32
|
||||
if reset {
|
||||
i = 1
|
||||
}
|
||||
r := s.c.call("sqlite3_stmt_status", uint64(s.handle),
|
||||
uint64(op), i)
|
||||
return int(int32(r))
|
||||
n := int32(s.c.call("sqlite3_stmt_status", stk_t(s.handle),
|
||||
stk_t(op), stk_t(i)))
|
||||
return int(n)
|
||||
}
|
||||
|
||||
// ClearBindings resets all bindings on the prepared statement.
|
||||
//
|
||||
// https://sqlite.org/c3ref/clear_bindings.html
|
||||
func (s *Stmt) ClearBindings() error {
|
||||
r := s.c.call("sqlite3_clear_bindings", uint64(s.handle))
|
||||
return s.c.error(r)
|
||||
rc := res_t(s.c.call("sqlite3_clear_bindings", stk_t(s.handle)))
|
||||
return s.c.error(rc)
|
||||
}
|
||||
|
||||
// BindCount returns the number of SQL parameters in the prepared statement.
|
||||
//
|
||||
// https://sqlite.org/c3ref/bind_parameter_count.html
|
||||
func (s *Stmt) BindCount() int {
|
||||
r := s.c.call("sqlite3_bind_parameter_count",
|
||||
uint64(s.handle))
|
||||
return int(int32(r))
|
||||
n := int32(s.c.call("sqlite3_bind_parameter_count",
|
||||
stk_t(s.handle)))
|
||||
return int(n)
|
||||
}
|
||||
|
||||
// BindIndex returns the index of a parameter in the prepared statement
|
||||
@@ -176,9 +176,9 @@ func (s *Stmt) BindCount() int {
|
||||
func (s *Stmt) BindIndex(name string) int {
|
||||
defer s.c.arena.mark()()
|
||||
namePtr := s.c.arena.string(name)
|
||||
r := s.c.call("sqlite3_bind_parameter_index",
|
||||
uint64(s.handle), uint64(namePtr))
|
||||
return int(int32(r))
|
||||
i := int32(s.c.call("sqlite3_bind_parameter_index",
|
||||
stk_t(s.handle), stk_t(namePtr)))
|
||||
return int(i)
|
||||
}
|
||||
|
||||
// BindName returns the name of a parameter in the prepared statement.
|
||||
@@ -186,10 +186,8 @@ func (s *Stmt) BindIndex(name string) int {
|
||||
//
|
||||
// https://sqlite.org/c3ref/bind_parameter_name.html
|
||||
func (s *Stmt) BindName(param int) string {
|
||||
r := s.c.call("sqlite3_bind_parameter_name",
|
||||
uint64(s.handle), uint64(param))
|
||||
|
||||
ptr := uint32(r)
|
||||
ptr := ptr_t(s.c.call("sqlite3_bind_parameter_name",
|
||||
stk_t(s.handle), stk_t(param)))
|
||||
if ptr == 0 {
|
||||
return ""
|
||||
}
|
||||
@@ -223,9 +221,9 @@ func (s *Stmt) BindInt(param int, value int) error {
|
||||
//
|
||||
// https://sqlite.org/c3ref/bind_blob.html
|
||||
func (s *Stmt) BindInt64(param int, value int64) error {
|
||||
r := s.c.call("sqlite3_bind_int64",
|
||||
uint64(s.handle), uint64(param), uint64(value))
|
||||
return s.c.error(r)
|
||||
rc := res_t(s.c.call("sqlite3_bind_int64",
|
||||
stk_t(s.handle), stk_t(param), stk_t(value)))
|
||||
return s.c.error(rc)
|
||||
}
|
||||
|
||||
// BindFloat binds a float64 to the prepared statement.
|
||||
@@ -233,9 +231,10 @@ func (s *Stmt) BindInt64(param int, value int64) error {
|
||||
//
|
||||
// https://sqlite.org/c3ref/bind_blob.html
|
||||
func (s *Stmt) BindFloat(param int, value float64) error {
|
||||
r := s.c.call("sqlite3_bind_double",
|
||||
uint64(s.handle), uint64(param), math.Float64bits(value))
|
||||
return s.c.error(r)
|
||||
rc := res_t(s.c.call("sqlite3_bind_double",
|
||||
stk_t(s.handle), stk_t(param),
|
||||
stk_t(math.Float64bits(value))))
|
||||
return s.c.error(rc)
|
||||
}
|
||||
|
||||
// BindText binds a string to the prepared statement.
|
||||
@@ -247,10 +246,10 @@ func (s *Stmt) BindText(param int, value string) error {
|
||||
return TOOBIG
|
||||
}
|
||||
ptr := s.c.newString(value)
|
||||
r := s.c.call("sqlite3_bind_text_go",
|
||||
uint64(s.handle), uint64(param),
|
||||
uint64(ptr), uint64(len(value)))
|
||||
return s.c.error(r)
|
||||
rc := res_t(s.c.call("sqlite3_bind_text_go",
|
||||
stk_t(s.handle), stk_t(param),
|
||||
stk_t(ptr), stk_t(len(value))))
|
||||
return s.c.error(rc)
|
||||
}
|
||||
|
||||
// BindRawText binds a []byte to the prepared statement as text.
|
||||
@@ -263,10 +262,10 @@ func (s *Stmt) BindRawText(param int, value []byte) error {
|
||||
return TOOBIG
|
||||
}
|
||||
ptr := s.c.newBytes(value)
|
||||
r := s.c.call("sqlite3_bind_text_go",
|
||||
uint64(s.handle), uint64(param),
|
||||
uint64(ptr), uint64(len(value)))
|
||||
return s.c.error(r)
|
||||
rc := res_t(s.c.call("sqlite3_bind_text_go",
|
||||
stk_t(s.handle), stk_t(param),
|
||||
stk_t(ptr), stk_t(len(value))))
|
||||
return s.c.error(rc)
|
||||
}
|
||||
|
||||
// BindBlob binds a []byte to the prepared statement.
|
||||
@@ -279,10 +278,10 @@ func (s *Stmt) BindBlob(param int, value []byte) error {
|
||||
return TOOBIG
|
||||
}
|
||||
ptr := s.c.newBytes(value)
|
||||
r := s.c.call("sqlite3_bind_blob_go",
|
||||
uint64(s.handle), uint64(param),
|
||||
uint64(ptr), uint64(len(value)))
|
||||
return s.c.error(r)
|
||||
rc := res_t(s.c.call("sqlite3_bind_blob_go",
|
||||
stk_t(s.handle), stk_t(param),
|
||||
stk_t(ptr), stk_t(len(value))))
|
||||
return s.c.error(rc)
|
||||
}
|
||||
|
||||
// BindZeroBlob binds a zero-filled, length n BLOB to the prepared statement.
|
||||
@@ -290,9 +289,9 @@ func (s *Stmt) BindBlob(param int, value []byte) error {
|
||||
//
|
||||
// https://sqlite.org/c3ref/bind_blob.html
|
||||
func (s *Stmt) BindZeroBlob(param int, n int64) error {
|
||||
r := s.c.call("sqlite3_bind_zeroblob64",
|
||||
uint64(s.handle), uint64(param), uint64(n))
|
||||
return s.c.error(r)
|
||||
rc := res_t(s.c.call("sqlite3_bind_zeroblob64",
|
||||
stk_t(s.handle), stk_t(param), stk_t(n)))
|
||||
return s.c.error(rc)
|
||||
}
|
||||
|
||||
// BindNull binds a NULL to the prepared statement.
|
||||
@@ -300,9 +299,9 @@ func (s *Stmt) BindZeroBlob(param int, n int64) error {
|
||||
//
|
||||
// https://sqlite.org/c3ref/bind_blob.html
|
||||
func (s *Stmt) BindNull(param int) error {
|
||||
r := s.c.call("sqlite3_bind_null",
|
||||
uint64(s.handle), uint64(param))
|
||||
return s.c.error(r)
|
||||
rc := res_t(s.c.call("sqlite3_bind_null",
|
||||
stk_t(s.handle), stk_t(param)))
|
||||
return s.c.error(rc)
|
||||
}
|
||||
|
||||
// BindTime binds a [time.Time] to the prepared statement.
|
||||
@@ -316,28 +315,27 @@ func (s *Stmt) BindTime(param int, value time.Time, format TimeFormat) error {
|
||||
}
|
||||
switch v := format.Encode(value).(type) {
|
||||
case string:
|
||||
s.BindText(param, v)
|
||||
return s.BindText(param, v)
|
||||
case int64:
|
||||
s.BindInt64(param, v)
|
||||
return s.BindInt64(param, v)
|
||||
case float64:
|
||||
s.BindFloat(param, v)
|
||||
return s.BindFloat(param, v)
|
||||
default:
|
||||
panic(util.AssertErr())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Stmt) bindRFC3339Nano(param int, value time.Time) error {
|
||||
const maxlen = uint64(len(time.RFC3339Nano)) + 5
|
||||
const maxlen = int64(len(time.RFC3339Nano)) + 5
|
||||
|
||||
ptr := s.c.new(maxlen)
|
||||
buf := util.View(s.c.mod, ptr, maxlen)
|
||||
buf = value.AppendFormat(buf[:0], time.RFC3339Nano)
|
||||
|
||||
r := s.c.call("sqlite3_bind_text_go",
|
||||
uint64(s.handle), uint64(param),
|
||||
uint64(ptr), uint64(len(buf)))
|
||||
return s.c.error(r)
|
||||
rc := res_t(s.c.call("sqlite3_bind_text_go",
|
||||
stk_t(s.handle), stk_t(param),
|
||||
stk_t(ptr), stk_t(len(buf))))
|
||||
return s.c.error(rc)
|
||||
}
|
||||
|
||||
// BindPointer binds a NULL to the prepared statement, just like [Stmt.BindNull],
|
||||
@@ -348,9 +346,9 @@ func (s *Stmt) bindRFC3339Nano(param int, value time.Time) error {
|
||||
// https://sqlite.org/c3ref/bind_blob.html
|
||||
func (s *Stmt) BindPointer(param int, ptr any) error {
|
||||
valPtr := util.AddHandle(s.c.ctx, ptr)
|
||||
r := s.c.call("sqlite3_bind_pointer_go",
|
||||
uint64(s.handle), uint64(param), uint64(valPtr))
|
||||
return s.c.error(r)
|
||||
rc := res_t(s.c.call("sqlite3_bind_pointer_go",
|
||||
stk_t(s.handle), stk_t(param), stk_t(valPtr)))
|
||||
return s.c.error(rc)
|
||||
}
|
||||
|
||||
// BindJSON binds the JSON encoding of value to the prepared statement.
|
||||
@@ -373,27 +371,27 @@ func (s *Stmt) BindValue(param int, value Value) error {
|
||||
if value.c != s.c {
|
||||
return MISUSE
|
||||
}
|
||||
r := s.c.call("sqlite3_bind_value",
|
||||
uint64(s.handle), uint64(param), uint64(value.handle))
|
||||
return s.c.error(r)
|
||||
rc := res_t(s.c.call("sqlite3_bind_value",
|
||||
stk_t(s.handle), stk_t(param), stk_t(value.handle)))
|
||||
return s.c.error(rc)
|
||||
}
|
||||
|
||||
// DataCount resets the number of columns in a result set.
|
||||
//
|
||||
// https://sqlite.org/c3ref/data_count.html
|
||||
func (s *Stmt) DataCount() int {
|
||||
r := s.c.call("sqlite3_data_count",
|
||||
uint64(s.handle))
|
||||
return int(int32(r))
|
||||
n := int32(s.c.call("sqlite3_data_count",
|
||||
stk_t(s.handle)))
|
||||
return int(n)
|
||||
}
|
||||
|
||||
// ColumnCount returns the number of columns in a result set.
|
||||
//
|
||||
// https://sqlite.org/c3ref/column_count.html
|
||||
func (s *Stmt) ColumnCount() int {
|
||||
r := s.c.call("sqlite3_column_count",
|
||||
uint64(s.handle))
|
||||
return int(int32(r))
|
||||
n := int32(s.c.call("sqlite3_column_count",
|
||||
stk_t(s.handle)))
|
||||
return int(n)
|
||||
}
|
||||
|
||||
// ColumnName returns the name of the result column.
|
||||
@@ -401,12 +399,12 @@ func (s *Stmt) ColumnCount() int {
|
||||
//
|
||||
// https://sqlite.org/c3ref/column_name.html
|
||||
func (s *Stmt) ColumnName(col int) string {
|
||||
r := s.c.call("sqlite3_column_name",
|
||||
uint64(s.handle), uint64(col))
|
||||
if r == 0 {
|
||||
ptr := ptr_t(s.c.call("sqlite3_column_name",
|
||||
stk_t(s.handle), stk_t(col)))
|
||||
if ptr == 0 {
|
||||
panic(util.OOMErr)
|
||||
}
|
||||
return util.ReadString(s.c.mod, uint32(r), _MAX_NAME)
|
||||
return util.ReadString(s.c.mod, ptr, _MAX_NAME)
|
||||
}
|
||||
|
||||
// ColumnType returns the initial [Datatype] of the result column.
|
||||
@@ -414,9 +412,8 @@ func (s *Stmt) ColumnName(col int) string {
|
||||
//
|
||||
// https://sqlite.org/c3ref/column_blob.html
|
||||
func (s *Stmt) ColumnType(col int) Datatype {
|
||||
r := s.c.call("sqlite3_column_type",
|
||||
uint64(s.handle), uint64(col))
|
||||
return Datatype(r)
|
||||
return Datatype(s.c.call("sqlite3_column_type",
|
||||
stk_t(s.handle), stk_t(col)))
|
||||
}
|
||||
|
||||
// ColumnDeclType returns the declared datatype of the result column.
|
||||
@@ -424,12 +421,12 @@ func (s *Stmt) ColumnType(col int) Datatype {
|
||||
//
|
||||
// https://sqlite.org/c3ref/column_decltype.html
|
||||
func (s *Stmt) ColumnDeclType(col int) string {
|
||||
r := s.c.call("sqlite3_column_decltype",
|
||||
uint64(s.handle), uint64(col))
|
||||
if r == 0 {
|
||||
ptr := ptr_t(s.c.call("sqlite3_column_decltype",
|
||||
stk_t(s.handle), stk_t(col)))
|
||||
if ptr == 0 {
|
||||
return ""
|
||||
}
|
||||
return util.ReadString(s.c.mod, uint32(r), _MAX_NAME)
|
||||
return util.ReadString(s.c.mod, ptr, _MAX_NAME)
|
||||
}
|
||||
|
||||
// ColumnDatabaseName returns the name of the database
|
||||
@@ -438,12 +435,12 @@ func (s *Stmt) ColumnDeclType(col int) string {
|
||||
//
|
||||
// https://sqlite.org/c3ref/column_database_name.html
|
||||
func (s *Stmt) ColumnDatabaseName(col int) string {
|
||||
r := s.c.call("sqlite3_column_database_name",
|
||||
uint64(s.handle), uint64(col))
|
||||
if r == 0 {
|
||||
ptr := ptr_t(s.c.call("sqlite3_column_database_name",
|
||||
stk_t(s.handle), stk_t(col)))
|
||||
if ptr == 0 {
|
||||
return ""
|
||||
}
|
||||
return util.ReadString(s.c.mod, uint32(r), _MAX_NAME)
|
||||
return util.ReadString(s.c.mod, ptr, _MAX_NAME)
|
||||
}
|
||||
|
||||
// ColumnTableName returns the name of the table
|
||||
@@ -452,12 +449,12 @@ func (s *Stmt) ColumnDatabaseName(col int) string {
|
||||
//
|
||||
// https://sqlite.org/c3ref/column_database_name.html
|
||||
func (s *Stmt) ColumnTableName(col int) string {
|
||||
r := s.c.call("sqlite3_column_table_name",
|
||||
uint64(s.handle), uint64(col))
|
||||
if r == 0 {
|
||||
ptr := ptr_t(s.c.call("sqlite3_column_table_name",
|
||||
stk_t(s.handle), stk_t(col)))
|
||||
if ptr == 0 {
|
||||
return ""
|
||||
}
|
||||
return util.ReadString(s.c.mod, uint32(r), _MAX_NAME)
|
||||
return util.ReadString(s.c.mod, ptr, _MAX_NAME)
|
||||
}
|
||||
|
||||
// ColumnOriginName returns the name of the table column
|
||||
@@ -466,12 +463,12 @@ func (s *Stmt) ColumnTableName(col int) string {
|
||||
//
|
||||
// https://sqlite.org/c3ref/column_database_name.html
|
||||
func (s *Stmt) ColumnOriginName(col int) string {
|
||||
r := s.c.call("sqlite3_column_origin_name",
|
||||
uint64(s.handle), uint64(col))
|
||||
if r == 0 {
|
||||
ptr := ptr_t(s.c.call("sqlite3_column_origin_name",
|
||||
stk_t(s.handle), stk_t(col)))
|
||||
if ptr == 0 {
|
||||
return ""
|
||||
}
|
||||
return util.ReadString(s.c.mod, uint32(r), _MAX_NAME)
|
||||
return util.ReadString(s.c.mod, ptr, _MAX_NAME)
|
||||
}
|
||||
|
||||
// ColumnBool returns the value of the result column as a bool.
|
||||
@@ -498,9 +495,8 @@ func (s *Stmt) ColumnInt(col int) int {
|
||||
//
|
||||
// https://sqlite.org/c3ref/column_blob.html
|
||||
func (s *Stmt) ColumnInt64(col int) int64 {
|
||||
r := s.c.call("sqlite3_column_int64",
|
||||
uint64(s.handle), uint64(col))
|
||||
return int64(r)
|
||||
return int64(s.c.call("sqlite3_column_int64",
|
||||
stk_t(s.handle), stk_t(col)))
|
||||
}
|
||||
|
||||
// ColumnFloat returns the value of the result column as a float64.
|
||||
@@ -508,9 +504,9 @@ func (s *Stmt) ColumnInt64(col int) int64 {
|
||||
//
|
||||
// https://sqlite.org/c3ref/column_blob.html
|
||||
func (s *Stmt) ColumnFloat(col int) float64 {
|
||||
r := s.c.call("sqlite3_column_double",
|
||||
uint64(s.handle), uint64(col))
|
||||
return math.Float64frombits(r)
|
||||
f := uint64(s.c.call("sqlite3_column_double",
|
||||
stk_t(s.handle), stk_t(col)))
|
||||
return math.Float64frombits(f)
|
||||
}
|
||||
|
||||
// ColumnTime returns the value of the result column as a [time.Time].
|
||||
@@ -562,9 +558,9 @@ func (s *Stmt) ColumnBlob(col int, buf []byte) []byte {
|
||||
//
|
||||
// https://sqlite.org/c3ref/column_blob.html
|
||||
func (s *Stmt) ColumnRawText(col int) []byte {
|
||||
r := s.c.call("sqlite3_column_text",
|
||||
uint64(s.handle), uint64(col))
|
||||
return s.columnRawBytes(col, uint32(r))
|
||||
ptr := ptr_t(s.c.call("sqlite3_column_text",
|
||||
stk_t(s.handle), stk_t(col)))
|
||||
return s.columnRawBytes(col, ptr)
|
||||
}
|
||||
|
||||
// ColumnRawBlob returns the value of the result column as a []byte.
|
||||
@@ -574,23 +570,23 @@ func (s *Stmt) ColumnRawText(col int) []byte {
|
||||
//
|
||||
// https://sqlite.org/c3ref/column_blob.html
|
||||
func (s *Stmt) ColumnRawBlob(col int) []byte {
|
||||
r := s.c.call("sqlite3_column_blob",
|
||||
uint64(s.handle), uint64(col))
|
||||
return s.columnRawBytes(col, uint32(r))
|
||||
ptr := ptr_t(s.c.call("sqlite3_column_blob",
|
||||
stk_t(s.handle), stk_t(col)))
|
||||
return s.columnRawBytes(col, ptr)
|
||||
}
|
||||
|
||||
func (s *Stmt) columnRawBytes(col int, ptr uint32) []byte {
|
||||
func (s *Stmt) columnRawBytes(col int, ptr ptr_t) []byte {
|
||||
if ptr == 0 {
|
||||
r := s.c.call("sqlite3_errcode", uint64(s.c.handle))
|
||||
if r != _ROW && r != _DONE {
|
||||
s.err = s.c.error(r)
|
||||
rc := res_t(s.c.call("sqlite3_errcode", stk_t(s.c.handle)))
|
||||
if rc != _ROW && rc != _DONE {
|
||||
s.err = s.c.error(rc)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
r := s.c.call("sqlite3_column_bytes",
|
||||
uint64(s.handle), uint64(col))
|
||||
return util.View(s.c.mod, ptr, r)
|
||||
n := int32(s.c.call("sqlite3_column_bytes",
|
||||
stk_t(s.handle), stk_t(col)))
|
||||
return util.View(s.c.mod, ptr, int64(n))
|
||||
}
|
||||
|
||||
// ColumnJSON parses the JSON-encoded value of the result column
|
||||
@@ -610,7 +606,7 @@ func (s *Stmt) ColumnJSON(col int, ptr any) error {
|
||||
case INTEGER:
|
||||
data = strconv.AppendInt(nil, s.ColumnInt64(col), 10)
|
||||
case FLOAT:
|
||||
data = strconv.AppendFloat(nil, s.ColumnFloat(col), 'g', -1, 64)
|
||||
data = util.AppendNumber(nil, s.ColumnFloat(col))
|
||||
default:
|
||||
panic(util.AssertErr())
|
||||
}
|
||||
@@ -622,12 +618,12 @@ func (s *Stmt) ColumnJSON(col int, ptr any) error {
|
||||
//
|
||||
// https://sqlite.org/c3ref/column_blob.html
|
||||
func (s *Stmt) ColumnValue(col int) Value {
|
||||
r := s.c.call("sqlite3_column_value",
|
||||
uint64(s.handle), uint64(col))
|
||||
ptr := ptr_t(s.c.call("sqlite3_column_value",
|
||||
stk_t(s.handle), stk_t(col)))
|
||||
return Value{
|
||||
c: s.c,
|
||||
unprot: true,
|
||||
handle: uint32(r),
|
||||
handle: ptr,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -641,13 +637,13 @@ func (s *Stmt) ColumnValue(col int) Value {
|
||||
// subsequent calls to [Stmt] methods.
|
||||
func (s *Stmt) Columns(dest ...any) error {
|
||||
defer s.c.arena.mark()()
|
||||
count := uint64(len(dest))
|
||||
count := int64(len(dest))
|
||||
typePtr := s.c.arena.new(count)
|
||||
dataPtr := s.c.arena.new(count * 8)
|
||||
|
||||
r := s.c.call("sqlite3_columns_go",
|
||||
uint64(s.handle), count, uint64(typePtr), uint64(dataPtr))
|
||||
if err := s.c.error(r); err != nil {
|
||||
rc := res_t(s.c.call("sqlite3_columns_go",
|
||||
stk_t(s.handle), stk_t(count), stk_t(typePtr), stk_t(dataPtr)))
|
||||
if err := s.c.error(rc); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -661,19 +657,19 @@ func (s *Stmt) Columns(dest ...any) error {
|
||||
for i := range dest {
|
||||
switch types[i] {
|
||||
case byte(INTEGER):
|
||||
dest[i] = int64(util.ReadUint64(s.c.mod, dataPtr))
|
||||
dest[i] = util.Read64[int64](s.c.mod, dataPtr)
|
||||
case byte(FLOAT):
|
||||
dest[i] = util.ReadFloat64(s.c.mod, dataPtr)
|
||||
case byte(NULL):
|
||||
dest[i] = nil
|
||||
default:
|
||||
ptr := util.ReadUint32(s.c.mod, dataPtr+0)
|
||||
ptr := util.Read32[ptr_t](s.c.mod, dataPtr+0)
|
||||
if ptr == 0 {
|
||||
dest[i] = []byte{}
|
||||
continue
|
||||
}
|
||||
len := util.ReadUint32(s.c.mod, dataPtr+4)
|
||||
buf := util.View(s.c.mod, ptr, uint64(len))
|
||||
len := util.Read32[int32](s.c.mod, dataPtr+4)
|
||||
buf := util.View(s.c.mod, ptr, int64(len))
|
||||
if types[i] == byte(TEXT) {
|
||||
dest[i] = string(buf)
|
||||
} else {
|
||||
|
||||
@@ -365,7 +365,7 @@ func TestBlob_Reopen(t *testing.T) {
|
||||
}
|
||||
|
||||
var rowids []int64
|
||||
for i := 0; i < 100; i++ {
|
||||
for range 100 {
|
||||
err = db.Exec(`INSERT INTO test VALUES (zeroblob(10))`)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
||||
@@ -92,7 +92,7 @@ func testManyQueryRow(t params) {
|
||||
t.mustExec("create table " + TablePrefix + "foo (id integer primary key, name varchar(50))")
|
||||
t.mustExec("insert into "+TablePrefix+"foo (id, name) values(?,?)", 1, "bob")
|
||||
var name string
|
||||
for i := 0; i < 10000; i++ {
|
||||
for i := range 10000 {
|
||||
err := t.QueryRow("select name from "+TablePrefix+"foo where id = ?", 1).Scan(&name)
|
||||
if err != nil || name != "bob" {
|
||||
t.Fatalf("on query %d: err=%v, name=%q", i, err, name)
|
||||
@@ -164,11 +164,11 @@ func testPreparedStmt(t params) {
|
||||
|
||||
const nRuns = 10
|
||||
var wg sync.WaitGroup
|
||||
for i := 0; i < nRuns; i++ {
|
||||
for range nRuns {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
for j := 0; j < 10; j++ {
|
||||
for range 10 {
|
||||
count := 0
|
||||
if err := sel.QueryRow().Scan(&count); err != nil && err != sql.ErrNoRows {
|
||||
t.Errorf("Query: %v", err)
|
||||
|
||||
@@ -372,7 +372,7 @@ func testParallel(t testing.TB, name string, n int) {
|
||||
|
||||
var group errgroup.Group
|
||||
group.SetLimit(6)
|
||||
for i := 0; i < n; i++ {
|
||||
for i := range n {
|
||||
if i&7 != 7 {
|
||||
group.Go(reader)
|
||||
} else {
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"math"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -52,8 +51,7 @@ func TestQuote(t *testing.T) {
|
||||
}
|
||||
}()
|
||||
|
||||
got := sqlite3.Quote(tt.val)
|
||||
if !reflect.DeepEqual(got, tt.want) {
|
||||
if got := sqlite3.Quote(tt.val); got != tt.want {
|
||||
t.Errorf("Quote(%v) = %q, want %q", tt.val, got, tt.want)
|
||||
}
|
||||
})
|
||||
@@ -81,8 +79,7 @@ func TestQuoteIdentifier(t *testing.T) {
|
||||
}
|
||||
}()
|
||||
|
||||
got := sqlite3.QuoteIdentifier(tt.id)
|
||||
if !reflect.DeepEqual(got, tt.want) {
|
||||
if got := sqlite3.QuoteIdentifier(tt.id); got != tt.want {
|
||||
t.Errorf("QuoteIdentifier(%v) = %q, want %q", tt.id, got, tt.want)
|
||||
}
|
||||
})
|
||||
|
||||
29
txn.go
29
txn.go
@@ -229,13 +229,12 @@ func (c *Conn) txnExecInterrupted(sql string) error {
|
||||
//
|
||||
// https://sqlite.org/c3ref/txn_state.html
|
||||
func (c *Conn) TxnState(schema string) TxnState {
|
||||
var ptr uint32
|
||||
var ptr ptr_t
|
||||
if schema != "" {
|
||||
defer c.arena.mark()()
|
||||
ptr = c.arena.string(schema)
|
||||
}
|
||||
r := c.call("sqlite3_txn_state", uint64(c.handle), uint64(ptr))
|
||||
return TxnState(r)
|
||||
return TxnState(c.call("sqlite3_txn_state", stk_t(c.handle), stk_t(ptr)))
|
||||
}
|
||||
|
||||
// CommitHook registers a callback function to be invoked
|
||||
@@ -244,11 +243,11 @@ func (c *Conn) TxnState(schema string) TxnState {
|
||||
//
|
||||
// https://sqlite.org/c3ref/commit_hook.html
|
||||
func (c *Conn) CommitHook(cb func() (ok bool)) {
|
||||
var enable uint64
|
||||
var enable int32
|
||||
if cb != nil {
|
||||
enable = 1
|
||||
}
|
||||
c.call("sqlite3_commit_hook_go", uint64(c.handle), enable)
|
||||
c.call("sqlite3_commit_hook_go", stk_t(c.handle), stk_t(enable))
|
||||
c.commit = cb
|
||||
}
|
||||
|
||||
@@ -257,11 +256,11 @@ func (c *Conn) CommitHook(cb func() (ok bool)) {
|
||||
//
|
||||
// https://sqlite.org/c3ref/commit_hook.html
|
||||
func (c *Conn) RollbackHook(cb func()) {
|
||||
var enable uint64
|
||||
var enable int32
|
||||
if cb != nil {
|
||||
enable = 1
|
||||
}
|
||||
c.call("sqlite3_rollback_hook_go", uint64(c.handle), enable)
|
||||
c.call("sqlite3_rollback_hook_go", stk_t(c.handle), stk_t(enable))
|
||||
c.rollback = cb
|
||||
}
|
||||
|
||||
@@ -270,15 +269,15 @@ func (c *Conn) RollbackHook(cb func()) {
|
||||
//
|
||||
// https://sqlite.org/c3ref/update_hook.html
|
||||
func (c *Conn) UpdateHook(cb func(action AuthorizerActionCode, schema, table string, rowid int64)) {
|
||||
var enable uint64
|
||||
var enable int32
|
||||
if cb != nil {
|
||||
enable = 1
|
||||
}
|
||||
c.call("sqlite3_update_hook_go", uint64(c.handle), enable)
|
||||
c.call("sqlite3_update_hook_go", stk_t(c.handle), stk_t(enable))
|
||||
c.update = cb
|
||||
}
|
||||
|
||||
func commitCallback(ctx context.Context, mod api.Module, pDB uint32) (rollback uint32) {
|
||||
func commitCallback(ctx context.Context, mod api.Module, pDB ptr_t) (rollback int32) {
|
||||
if c, ok := ctx.Value(connKey{}).(*Conn); ok && c.handle == pDB && c.commit != nil {
|
||||
if !c.commit() {
|
||||
rollback = 1
|
||||
@@ -287,17 +286,17 @@ func commitCallback(ctx context.Context, mod api.Module, pDB uint32) (rollback u
|
||||
return rollback
|
||||
}
|
||||
|
||||
func rollbackCallback(ctx context.Context, mod api.Module, pDB uint32) {
|
||||
func rollbackCallback(ctx context.Context, mod api.Module, pDB ptr_t) {
|
||||
if c, ok := ctx.Value(connKey{}).(*Conn); ok && c.handle == pDB && c.rollback != nil {
|
||||
c.rollback()
|
||||
}
|
||||
}
|
||||
|
||||
func updateCallback(ctx context.Context, mod api.Module, pDB uint32, action AuthorizerActionCode, zSchema, zTabName uint32, rowid uint64) {
|
||||
func updateCallback(ctx context.Context, mod api.Module, pDB ptr_t, action AuthorizerActionCode, zSchema, zTabName ptr_t, rowid int64) {
|
||||
if c, ok := ctx.Value(connKey{}).(*Conn); ok && c.handle == pDB && c.update != nil {
|
||||
schema := util.ReadString(mod, zSchema, _MAX_NAME)
|
||||
table := util.ReadString(mod, zTabName, _MAX_NAME)
|
||||
c.update(action, schema, table, int64(rowid))
|
||||
c.update(action, schema, table, rowid)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -305,6 +304,6 @@ func updateCallback(ctx context.Context, mod api.Module, pDB uint32, action Auth
|
||||
//
|
||||
// https://sqlite.org/c3ref/db_cacheflush.html
|
||||
func (c *Conn) CacheFlush() error {
|
||||
r := c.call("sqlite3_db_cacheflush", uint64(c.handle))
|
||||
return c.error(r)
|
||||
rc := res_t(c.call("sqlite3_db_cacheflush", stk_t(c.handle)))
|
||||
return c.error(rc)
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ func ParseTable(sql string) (_ *Table, err error) {
|
||||
copy(buf, sql)
|
||||
}
|
||||
|
||||
stack := [...]uint64{sqlp, uint64(len(sql)), errp}
|
||||
stack := [...]util.Stk_t{sqlp, util.Stk_t(len(sql)), errp}
|
||||
err = mod.ExportedFunction("sql3parse_table").CallWithStack(ctx, stack[:])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -96,9 +96,9 @@ 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, res *Column) {
|
||||
t.Columns = loadSlice(mod, ptr+28, func(ptr uint32, ret *Column) {
|
||||
p, _ := mod.Memory().ReadUint32Le(ptr)
|
||||
res.load(mod, p, sql)
|
||||
ret.load(mod, p, sql)
|
||||
})
|
||||
|
||||
t.Type = loadEnum[StatementType](mod, ptr+44)
|
||||
@@ -166,8 +166,8 @@ 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, res *string) {
|
||||
*res = loadString(mod, ptr, sql)
|
||||
f.Columns = loadSlice(mod, ptr+8, func(ptr uint32, ret *string) {
|
||||
*ret = loadString(mod, ptr, sql)
|
||||
})
|
||||
|
||||
f.OnDelete = loadEnum[FKAction](mod, ptr+16)
|
||||
@@ -191,12 +191,12 @@ func loadSlice[T any](mod api.Module, ptr uint32, fn func(uint32, *T)) []T {
|
||||
return nil
|
||||
}
|
||||
len, _ := mod.Memory().ReadUint32Le(ptr + 0)
|
||||
res := make([]T, len)
|
||||
for i := range res {
|
||||
fn(ref, &res[i])
|
||||
ret := make([]T, len)
|
||||
for i := range ret {
|
||||
fn(ref, &ret[i])
|
||||
ref += 4
|
||||
}
|
||||
return res
|
||||
return ret
|
||||
}
|
||||
|
||||
func loadEnum[T ~uint32](mod api.Module, ptr uint32) T {
|
||||
|
||||
65
value.go
65
value.go
@@ -14,27 +14,27 @@ import (
|
||||
// https://sqlite.org/c3ref/value.html
|
||||
type Value struct {
|
||||
c *Conn
|
||||
handle uint32
|
||||
handle ptr_t
|
||||
unprot bool
|
||||
copied bool
|
||||
}
|
||||
|
||||
func (v Value) protected() uint64 {
|
||||
func (v Value) protected() stk_t {
|
||||
if v.unprot {
|
||||
panic(util.ValueErr)
|
||||
}
|
||||
return uint64(v.handle)
|
||||
return stk_t(v.handle)
|
||||
}
|
||||
|
||||
// Dup makes a copy of the SQL value and returns a pointer to that copy.
|
||||
//
|
||||
// https://sqlite.org/c3ref/value_dup.html
|
||||
func (v Value) Dup() *Value {
|
||||
r := v.c.call("sqlite3_value_dup", uint64(v.handle))
|
||||
ptr := ptr_t(v.c.call("sqlite3_value_dup", stk_t(v.handle)))
|
||||
return &Value{
|
||||
c: v.c,
|
||||
copied: true,
|
||||
handle: uint32(r),
|
||||
handle: ptr,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ func (dup *Value) Close() error {
|
||||
if !dup.copied {
|
||||
panic(util.ValueErr)
|
||||
}
|
||||
dup.c.call("sqlite3_value_free", uint64(dup.handle))
|
||||
dup.c.call("sqlite3_value_free", stk_t(dup.handle))
|
||||
dup.handle = 0
|
||||
return nil
|
||||
}
|
||||
@@ -54,16 +54,14 @@ func (dup *Value) Close() error {
|
||||
//
|
||||
// https://sqlite.org/c3ref/value_blob.html
|
||||
func (v Value) Type() Datatype {
|
||||
r := v.c.call("sqlite3_value_type", v.protected())
|
||||
return Datatype(r)
|
||||
return Datatype(v.c.call("sqlite3_value_type", v.protected()))
|
||||
}
|
||||
|
||||
// Type returns the numeric datatype of the value.
|
||||
//
|
||||
// https://sqlite.org/c3ref/value_blob.html
|
||||
func (v Value) NumericType() Datatype {
|
||||
r := v.c.call("sqlite3_value_numeric_type", v.protected())
|
||||
return Datatype(r)
|
||||
return Datatype(v.c.call("sqlite3_value_numeric_type", v.protected()))
|
||||
}
|
||||
|
||||
// Bool returns the value as a bool.
|
||||
@@ -87,16 +85,15 @@ func (v Value) Int() int {
|
||||
//
|
||||
// https://sqlite.org/c3ref/value_blob.html
|
||||
func (v Value) Int64() int64 {
|
||||
r := v.c.call("sqlite3_value_int64", v.protected())
|
||||
return int64(r)
|
||||
return int64(v.c.call("sqlite3_value_int64", v.protected()))
|
||||
}
|
||||
|
||||
// Float returns the value as a float64.
|
||||
//
|
||||
// https://sqlite.org/c3ref/value_blob.html
|
||||
func (v Value) Float() float64 {
|
||||
r := v.c.call("sqlite3_value_double", v.protected())
|
||||
return math.Float64frombits(r)
|
||||
f := uint64(v.c.call("sqlite3_value_double", v.protected()))
|
||||
return math.Float64frombits(f)
|
||||
}
|
||||
|
||||
// Time returns the value as a [time.Time].
|
||||
@@ -141,8 +138,8 @@ func (v Value) Blob(buf []byte) []byte {
|
||||
//
|
||||
// https://sqlite.org/c3ref/value_blob.html
|
||||
func (v Value) RawText() []byte {
|
||||
r := v.c.call("sqlite3_value_text", v.protected())
|
||||
return v.rawBytes(uint32(r))
|
||||
ptr := ptr_t(v.c.call("sqlite3_value_text", v.protected()))
|
||||
return v.rawBytes(ptr)
|
||||
}
|
||||
|
||||
// RawBlob returns the value as a []byte.
|
||||
@@ -151,24 +148,24 @@ func (v Value) RawText() []byte {
|
||||
//
|
||||
// https://sqlite.org/c3ref/value_blob.html
|
||||
func (v Value) RawBlob() []byte {
|
||||
r := v.c.call("sqlite3_value_blob", v.protected())
|
||||
return v.rawBytes(uint32(r))
|
||||
ptr := ptr_t(v.c.call("sqlite3_value_blob", v.protected()))
|
||||
return v.rawBytes(ptr)
|
||||
}
|
||||
|
||||
func (v Value) rawBytes(ptr uint32) []byte {
|
||||
func (v Value) rawBytes(ptr ptr_t) []byte {
|
||||
if ptr == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
r := v.c.call("sqlite3_value_bytes", v.protected())
|
||||
return util.View(v.c.mod, ptr, r)
|
||||
n := int32(v.c.call("sqlite3_value_bytes", v.protected()))
|
||||
return util.View(v.c.mod, ptr, int64(n))
|
||||
}
|
||||
|
||||
// Pointer gets the pointer associated with this value,
|
||||
// or nil if it has no associated pointer.
|
||||
func (v Value) Pointer() any {
|
||||
r := v.c.call("sqlite3_value_pointer_go", v.protected())
|
||||
return util.GetHandle(v.c.ctx, uint32(r))
|
||||
ptr := ptr_t(v.c.call("sqlite3_value_pointer_go", v.protected()))
|
||||
return util.GetHandle(v.c.ctx, ptr)
|
||||
}
|
||||
|
||||
// JSON parses a JSON-encoded value
|
||||
@@ -185,7 +182,7 @@ func (v Value) JSON(ptr any) error {
|
||||
case INTEGER:
|
||||
data = strconv.AppendInt(nil, v.Int64(), 10)
|
||||
case FLOAT:
|
||||
data = strconv.AppendFloat(nil, v.Float(), 'g', -1, 64)
|
||||
data = util.AppendNumber(nil, v.Float())
|
||||
default:
|
||||
panic(util.AssertErr())
|
||||
}
|
||||
@@ -197,16 +194,16 @@ func (v Value) JSON(ptr any) error {
|
||||
//
|
||||
// https://sqlite.org/c3ref/value_blob.html
|
||||
func (v Value) NoChange() bool {
|
||||
r := v.c.call("sqlite3_value_nochange", v.protected())
|
||||
return r != 0
|
||||
b := int32(v.c.call("sqlite3_value_nochange", v.protected()))
|
||||
return b != 0
|
||||
}
|
||||
|
||||
// FromBind returns true if value originated from a bound parameter.
|
||||
//
|
||||
// https://sqlite.org/c3ref/value_blob.html
|
||||
func (v Value) FromBind() bool {
|
||||
r := v.c.call("sqlite3_value_frombind", v.protected())
|
||||
return r != 0
|
||||
b := int32(v.c.call("sqlite3_value_frombind", v.protected()))
|
||||
return b != 0
|
||||
}
|
||||
|
||||
// InFirst returns the first element
|
||||
@@ -216,13 +213,13 @@ func (v Value) FromBind() bool {
|
||||
func (v Value) InFirst() (Value, error) {
|
||||
defer v.c.arena.mark()()
|
||||
valPtr := v.c.arena.new(ptrlen)
|
||||
r := v.c.call("sqlite3_vtab_in_first", uint64(v.handle), uint64(valPtr))
|
||||
if err := v.c.error(r); err != nil {
|
||||
rc := res_t(v.c.call("sqlite3_vtab_in_first", stk_t(v.handle), stk_t(valPtr)))
|
||||
if err := v.c.error(rc); err != nil {
|
||||
return Value{}, err
|
||||
}
|
||||
return Value{
|
||||
c: v.c,
|
||||
handle: util.ReadUint32(v.c.mod, valPtr),
|
||||
handle: util.Read32[ptr_t](v.c.mod, valPtr),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -233,12 +230,12 @@ func (v Value) InFirst() (Value, error) {
|
||||
func (v Value) InNext() (Value, error) {
|
||||
defer v.c.arena.mark()()
|
||||
valPtr := v.c.arena.new(ptrlen)
|
||||
r := v.c.call("sqlite3_vtab_in_next", uint64(v.handle), uint64(valPtr))
|
||||
if err := v.c.error(r); err != nil {
|
||||
rc := res_t(v.c.call("sqlite3_vtab_in_next", stk_t(v.handle), stk_t(valPtr)))
|
||||
if err := v.c.error(rc); err != nil {
|
||||
return Value{}, err
|
||||
}
|
||||
return Value{
|
||||
c: v.c,
|
||||
handle: util.ReadUint32(v.c.mod, valPtr),
|
||||
handle: util.Read32[ptr_t](v.c.mod, valPtr),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ func Benchmark_nokey(b *testing.B) {
|
||||
sqlite3.Initialize()
|
||||
b.ResetTimer()
|
||||
|
||||
for n := 0; n < b.N; n++ {
|
||||
for range b.N {
|
||||
db, err := sqlite3.Open("file:" + filepath.ToSlash(tmp) + "?nolock=1")
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
@@ -70,7 +70,7 @@ func Benchmark_hexkey(b *testing.B) {
|
||||
sqlite3.Initialize()
|
||||
b.ResetTimer()
|
||||
|
||||
for n := 0; n < b.N; n++ {
|
||||
for range b.N {
|
||||
db, err := sqlite3.Open("file:" + filepath.ToSlash(tmp) + "?nolock=1" +
|
||||
"&vfs=adiantum&hexkey=e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855")
|
||||
if err != nil {
|
||||
@@ -85,7 +85,7 @@ func Benchmark_textkey(b *testing.B) {
|
||||
sqlite3.Initialize()
|
||||
b.ResetTimer()
|
||||
|
||||
for n := 0; n < b.N; n++ {
|
||||
for range b.N {
|
||||
db, err := sqlite3.Open("file:" + filepath.ToSlash(tmp) + "?nolock=1" +
|
||||
"&vfs=adiantum&textkey=correct+horse+battery+staple")
|
||||
if err != nil {
|
||||
|
||||
@@ -193,7 +193,7 @@ type FileSharedMemory interface {
|
||||
// SharedMemory is a shared-memory WAL-index implementation.
|
||||
// Use [NewSharedMemory] to create a shared-memory.
|
||||
type SharedMemory interface {
|
||||
shmMap(context.Context, api.Module, int32, int32, bool) (uint32, _ErrorCode)
|
||||
shmMap(context.Context, api.Module, int32, int32, bool) (ptr_t, _ErrorCode)
|
||||
shmLock(int32, int32, _ShmFlag) _ErrorCode
|
||||
shmUnmap(bool)
|
||||
shmBarrier()
|
||||
@@ -207,7 +207,7 @@ type blockingSharedMemory interface {
|
||||
|
||||
type fileControl interface {
|
||||
File
|
||||
fileControl(ctx context.Context, mod api.Module, op _FcntlOpcode, pArg uint32) _ErrorCode
|
||||
fileControl(ctx context.Context, mod api.Module, op _FcntlOpcode, pArg ptr_t) _ErrorCode
|
||||
}
|
||||
|
||||
type filePDB interface {
|
||||
|
||||
@@ -102,14 +102,14 @@ func (c cksmFile) Pragma(name string, value string) (string, error) {
|
||||
}
|
||||
|
||||
func (c cksmFile) DeviceCharacteristics() DeviceCharacteristic {
|
||||
res := c.File.DeviceCharacteristics()
|
||||
ret := c.File.DeviceCharacteristics()
|
||||
if c.verifyCksm {
|
||||
res &^= IOCAP_SUBPAGE_READ
|
||||
ret &^= IOCAP_SUBPAGE_READ
|
||||
}
|
||||
return res
|
||||
return ret
|
||||
}
|
||||
|
||||
func (c cksmFile) fileControl(ctx context.Context, mod api.Module, op _FcntlOpcode, pArg uint32) _ErrorCode {
|
||||
func (c cksmFile) fileControl(ctx context.Context, mod api.Module, op _FcntlOpcode, pArg ptr_t) _ErrorCode {
|
||||
switch op {
|
||||
case _FCNTL_CKPT_START:
|
||||
c.inCkpt = true
|
||||
|
||||
@@ -8,7 +8,12 @@ const (
|
||||
_MAX_PATHNAME = 1024
|
||||
_DEFAULT_SECTOR_SIZE = 4096
|
||||
|
||||
ptrlen = 4
|
||||
ptrlen = util.PtrLen
|
||||
)
|
||||
|
||||
type (
|
||||
stk_t = util.Stk_t
|
||||
ptr_t = util.Ptr_t
|
||||
)
|
||||
|
||||
// https://sqlite.org/rescode.html
|
||||
|
||||
@@ -186,14 +186,14 @@ func (f *vfsFile) SectorSize() int {
|
||||
}
|
||||
|
||||
func (f *vfsFile) DeviceCharacteristics() DeviceCharacteristic {
|
||||
res := IOCAP_SUBPAGE_READ
|
||||
ret := IOCAP_SUBPAGE_READ
|
||||
if osBatchAtomic(f.File) {
|
||||
res |= IOCAP_BATCH_ATOMIC
|
||||
ret |= IOCAP_BATCH_ATOMIC
|
||||
}
|
||||
if f.psow {
|
||||
res |= IOCAP_POWERSAFE_OVERWRITE
|
||||
ret |= IOCAP_POWERSAFE_OVERWRITE
|
||||
}
|
||||
return res
|
||||
return ret
|
||||
}
|
||||
|
||||
func (f *vfsFile) SizeHint(size int64) error {
|
||||
|
||||
@@ -16,13 +16,13 @@ import (
|
||||
type Filename struct {
|
||||
ctx context.Context
|
||||
mod api.Module
|
||||
zPath uint32
|
||||
zPath ptr_t
|
||||
flags OpenFlag
|
||||
stack [2]uint64
|
||||
stack [2]stk_t
|
||||
}
|
||||
|
||||
// GetFilename is an internal API users should not call directly.
|
||||
func GetFilename(ctx context.Context, mod api.Module, id uint32, flags OpenFlag) *Filename {
|
||||
func GetFilename(ctx context.Context, mod api.Module, id ptr_t, flags OpenFlag) *Filename {
|
||||
if id == 0 {
|
||||
return nil
|
||||
}
|
||||
@@ -71,12 +71,12 @@ func (n *Filename) path(method string) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
n.stack[0] = uint64(n.zPath)
|
||||
n.stack[0] = stk_t(n.zPath)
|
||||
fn := n.mod.ExportedFunction(method)
|
||||
if err := fn.CallWithStack(n.ctx, n.stack[:]); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return util.ReadString(n.mod, uint32(n.stack[0]), _MAX_PATHNAME)
|
||||
return util.ReadString(n.mod, ptr_t(n.stack[0]), _MAX_PATHNAME)
|
||||
}
|
||||
|
||||
// DatabaseFile returns the main database [File] corresponding to a journal.
|
||||
@@ -90,12 +90,12 @@ func (n *Filename) DatabaseFile() File {
|
||||
return nil
|
||||
}
|
||||
|
||||
n.stack[0] = uint64(n.zPath)
|
||||
n.stack[0] = stk_t(n.zPath)
|
||||
fn := n.mod.ExportedFunction("sqlite3_database_file_object")
|
||||
if err := fn.CallWithStack(n.ctx, n.stack[:]); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
file, _ := vfsFileGet(n.ctx, n.mod, uint32(n.stack[0])).(File)
|
||||
file, _ := vfsFileGet(n.ctx, n.mod, ptr_t(n.stack[0])).(File)
|
||||
return file
|
||||
}
|
||||
|
||||
@@ -108,13 +108,13 @@ func (n *Filename) URIParameter(key string) string {
|
||||
}
|
||||
|
||||
uriKey := n.mod.ExportedFunction("sqlite3_uri_key")
|
||||
n.stack[0] = uint64(n.zPath)
|
||||
n.stack[1] = uint64(0)
|
||||
n.stack[0] = stk_t(n.zPath)
|
||||
n.stack[1] = stk_t(0)
|
||||
if err := uriKey.CallWithStack(n.ctx, n.stack[:]); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
ptr := uint32(n.stack[0])
|
||||
ptr := ptr_t(n.stack[0])
|
||||
if ptr == 0 {
|
||||
return ""
|
||||
}
|
||||
@@ -127,13 +127,13 @@ func (n *Filename) URIParameter(key string) string {
|
||||
if k == "" {
|
||||
return ""
|
||||
}
|
||||
ptr += uint32(len(k)) + 1
|
||||
ptr += ptr_t(len(k)) + 1
|
||||
|
||||
v := util.ReadString(n.mod, ptr, _MAX_NAME)
|
||||
if k == key {
|
||||
return v
|
||||
}
|
||||
ptr += uint32(len(v)) + 1
|
||||
ptr += ptr_t(len(v)) + 1
|
||||
}
|
||||
}
|
||||
|
||||
@@ -146,13 +146,13 @@ func (n *Filename) URIParameters() url.Values {
|
||||
}
|
||||
|
||||
uriKey := n.mod.ExportedFunction("sqlite3_uri_key")
|
||||
n.stack[0] = uint64(n.zPath)
|
||||
n.stack[1] = uint64(0)
|
||||
n.stack[0] = stk_t(n.zPath)
|
||||
n.stack[1] = stk_t(0)
|
||||
if err := uriKey.CallWithStack(n.ctx, n.stack[:]); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
ptr := uint32(n.stack[0])
|
||||
ptr := ptr_t(n.stack[0])
|
||||
if ptr == 0 {
|
||||
return nil
|
||||
}
|
||||
@@ -167,13 +167,13 @@ func (n *Filename) URIParameters() url.Values {
|
||||
if k == "" {
|
||||
return params
|
||||
}
|
||||
ptr += uint32(len(k)) + 1
|
||||
ptr += ptr_t(len(k)) + 1
|
||||
|
||||
v := util.ReadString(n.mod, ptr, _MAX_NAME)
|
||||
if params == nil {
|
||||
params = url.Values{}
|
||||
}
|
||||
params.Add(k, v)
|
||||
ptr += uint32(len(v)) + 1
|
||||
ptr += ptr_t(len(v)) + 1
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,21 +47,21 @@ func Test_vfsLock(t *testing.T) {
|
||||
if rc != _OK {
|
||||
t.Fatal("returned", rc)
|
||||
}
|
||||
if got := util.ReadUint32(mod, pOutput); got != 0 {
|
||||
if got := util.ReadBool(mod, pOutput); got {
|
||||
t.Error("file was locked")
|
||||
}
|
||||
rc = vfsCheckReservedLock(ctx, mod, pFile2, pOutput)
|
||||
if rc != _OK {
|
||||
t.Fatal("returned", rc)
|
||||
}
|
||||
if got := util.ReadUint32(mod, pOutput); got != 0 {
|
||||
if got := util.ReadBool(mod, pOutput); got {
|
||||
t.Error("file was locked")
|
||||
}
|
||||
rc = vfsFileControl(ctx, mod, pFile2, _FCNTL_LOCKSTATE, pOutput)
|
||||
if rc != _OK {
|
||||
t.Fatal("returned", rc)
|
||||
}
|
||||
if got := util.ReadUint32(mod, pOutput); got != uint32(LOCK_NONE) {
|
||||
if got := util.Read32[LockLevel](mod, pOutput); got != LOCK_NONE {
|
||||
t.Error("invalid lock state", got)
|
||||
}
|
||||
|
||||
@@ -74,21 +74,21 @@ func Test_vfsLock(t *testing.T) {
|
||||
if rc != _OK {
|
||||
t.Fatal("returned", rc)
|
||||
}
|
||||
if got := util.ReadUint32(mod, pOutput); got != 0 {
|
||||
if got := util.ReadBool(mod, pOutput); got {
|
||||
t.Error("file was locked")
|
||||
}
|
||||
rc = vfsCheckReservedLock(ctx, mod, pFile2, pOutput)
|
||||
if rc != _OK {
|
||||
t.Fatal("returned", rc)
|
||||
}
|
||||
if got := util.ReadUint32(mod, pOutput); got != 0 {
|
||||
if got := util.ReadBool(mod, pOutput); got {
|
||||
t.Error("file was locked")
|
||||
}
|
||||
rc = vfsFileControl(ctx, mod, pFile2, _FCNTL_LOCKSTATE, pOutput)
|
||||
if rc != _OK {
|
||||
t.Fatal("returned", rc)
|
||||
}
|
||||
if got := util.ReadUint32(mod, pOutput); got != uint32(LOCK_SHARED) {
|
||||
if got := util.Read32[LockLevel](mod, pOutput); got != LOCK_SHARED {
|
||||
t.Error("invalid lock state", got)
|
||||
}
|
||||
|
||||
@@ -105,21 +105,21 @@ func Test_vfsLock(t *testing.T) {
|
||||
if rc != _OK {
|
||||
t.Fatal("returned", rc)
|
||||
}
|
||||
if got := util.ReadUint32(mod, pOutput); got == 0 {
|
||||
if got := util.ReadBool(mod, pOutput); !got {
|
||||
t.Log("file wasn't locked, locking is incompatible with SQLite")
|
||||
}
|
||||
rc = vfsCheckReservedLock(ctx, mod, pFile2, pOutput)
|
||||
if rc != _OK {
|
||||
t.Fatal("returned", rc)
|
||||
}
|
||||
if got := util.ReadUint32(mod, pOutput); got == 0 {
|
||||
if got := util.ReadBool(mod, pOutput); !got {
|
||||
t.Error("file wasn't locked")
|
||||
}
|
||||
rc = vfsFileControl(ctx, mod, pFile2, _FCNTL_LOCKSTATE, pOutput)
|
||||
if rc != _OK {
|
||||
t.Fatal("returned", rc)
|
||||
}
|
||||
if got := util.ReadUint32(mod, pOutput); got != uint32(LOCK_RESERVED) {
|
||||
if got := util.Read32[LockLevel](mod, pOutput); got != LOCK_RESERVED {
|
||||
t.Error("invalid lock state", got)
|
||||
}
|
||||
|
||||
@@ -132,21 +132,21 @@ func Test_vfsLock(t *testing.T) {
|
||||
if rc != _OK {
|
||||
t.Fatal("returned", rc)
|
||||
}
|
||||
if got := util.ReadUint32(mod, pOutput); got == 0 {
|
||||
if got := util.ReadBool(mod, pOutput); !got {
|
||||
t.Log("file wasn't locked, locking is incompatible with SQLite")
|
||||
}
|
||||
rc = vfsCheckReservedLock(ctx, mod, pFile2, pOutput)
|
||||
if rc != _OK {
|
||||
t.Fatal("returned", rc)
|
||||
}
|
||||
if got := util.ReadUint32(mod, pOutput); got == 0 {
|
||||
if got := util.ReadBool(mod, pOutput); !got {
|
||||
t.Error("file wasn't locked")
|
||||
}
|
||||
rc = vfsFileControl(ctx, mod, pFile2, _FCNTL_LOCKSTATE, pOutput)
|
||||
if rc != _OK {
|
||||
t.Fatal("returned", rc)
|
||||
}
|
||||
if got := util.ReadUint32(mod, pOutput); got != uint32(LOCK_EXCLUSIVE) {
|
||||
if got := util.Read32[LockLevel](mod, pOutput); got != LOCK_EXCLUSIVE {
|
||||
t.Error("invalid lock state", got)
|
||||
}
|
||||
|
||||
@@ -159,21 +159,21 @@ func Test_vfsLock(t *testing.T) {
|
||||
if rc != _OK {
|
||||
t.Fatal("returned", rc)
|
||||
}
|
||||
if got := util.ReadUint32(mod, pOutput); got == 0 {
|
||||
if got := util.ReadBool(mod, pOutput); !got {
|
||||
t.Log("file wasn't locked, locking is incompatible with SQLite")
|
||||
}
|
||||
rc = vfsCheckReservedLock(ctx, mod, pFile2, pOutput)
|
||||
if rc != _OK {
|
||||
t.Fatal("returned", rc)
|
||||
}
|
||||
if got := util.ReadUint32(mod, pOutput); got == 0 {
|
||||
if got := util.ReadBool(mod, pOutput); !got {
|
||||
t.Error("file wasn't locked")
|
||||
}
|
||||
rc = vfsFileControl(ctx, mod, pFile1, _FCNTL_LOCKSTATE, pOutput)
|
||||
if rc != _OK {
|
||||
t.Fatal("returned", rc)
|
||||
}
|
||||
if got := util.ReadUint32(mod, pOutput); got != uint32(LOCK_NONE) {
|
||||
if got := util.Read32[LockLevel](mod, pOutput); got != LOCK_NONE {
|
||||
t.Error("invalid lock state", got)
|
||||
}
|
||||
|
||||
@@ -186,14 +186,14 @@ func Test_vfsLock(t *testing.T) {
|
||||
if rc != _OK {
|
||||
t.Fatal("returned", rc)
|
||||
}
|
||||
if got := util.ReadUint32(mod, pOutput); got != 0 {
|
||||
if got := util.ReadBool(mod, pOutput); got {
|
||||
t.Error("file was locked")
|
||||
}
|
||||
rc = vfsCheckReservedLock(ctx, mod, pFile2, pOutput)
|
||||
if rc != _OK {
|
||||
t.Fatal("returned", rc)
|
||||
}
|
||||
if got := util.ReadUint32(mod, pOutput); got != 0 {
|
||||
if got := util.ReadBool(mod, pOutput); got {
|
||||
t.Error("file was locked")
|
||||
}
|
||||
|
||||
@@ -205,7 +205,7 @@ func Test_vfsLock(t *testing.T) {
|
||||
if rc != _OK {
|
||||
t.Fatal("returned", rc)
|
||||
}
|
||||
if got := util.ReadUint32(mod, pOutput); got != uint32(LOCK_SHARED) {
|
||||
if got := util.Read32[LockLevel](mod, pOutput); got != LOCK_SHARED {
|
||||
t.Error("invalid lock state", got)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,9 +10,11 @@ import (
|
||||
"github.com/ncruces/go-sqlite3/vfs"
|
||||
)
|
||||
|
||||
// Must be a multiple of 64K (the largest page size).
|
||||
const sectorSize = 65536
|
||||
|
||||
// Ensure sectorSize is a multiple of 64K (the largest page size).
|
||||
var _ [0]struct{} = [sectorSize & 65535]struct{}{}
|
||||
|
||||
type memVFS struct{}
|
||||
|
||||
func (memVFS) Open(name string, flags vfs.OpenFlag) (vfs.File, vfs.OpenFlag, error) {
|
||||
|
||||
@@ -142,7 +142,7 @@ func (s *vfsShm) shmOpen() _ErrorCode {
|
||||
return _OK
|
||||
}
|
||||
|
||||
func (s *vfsShm) shmMap(ctx context.Context, mod api.Module, id, size int32, extend bool) (uint32, _ErrorCode) {
|
||||
func (s *vfsShm) shmMap(ctx context.Context, mod api.Module, id, size int32, extend bool) (ptr_t, _ErrorCode) {
|
||||
// Ensure size is a multiple of the OS page size.
|
||||
if int(size)&(unix.Getpagesize()-1) != 0 {
|
||||
return 0, _IOERR_SHMMAP
|
||||
|
||||
@@ -35,8 +35,8 @@ type vfsShm struct {
|
||||
free api.Function
|
||||
path string
|
||||
shadow [][_WALINDEX_PGSZ]byte
|
||||
ptrs []uint32
|
||||
stack [1]uint64
|
||||
ptrs []ptr_t
|
||||
stack [1]stk_t
|
||||
lock [_SHM_NLOCK]bool
|
||||
}
|
||||
|
||||
@@ -96,7 +96,7 @@ func (s *vfsShm) shmOpen() _ErrorCode {
|
||||
return _OK
|
||||
}
|
||||
|
||||
func (s *vfsShm) shmMap(ctx context.Context, mod api.Module, id, size int32, extend bool) (uint32, _ErrorCode) {
|
||||
func (s *vfsShm) shmMap(ctx context.Context, mod api.Module, id, size int32, extend bool) (ptr_t, _ErrorCode) {
|
||||
if size != _WALINDEX_PGSZ {
|
||||
return 0, _IOERR_SHMMAP
|
||||
}
|
||||
@@ -128,15 +128,15 @@ func (s *vfsShm) shmMap(ctx context.Context, mod api.Module, id, size int32, ext
|
||||
|
||||
// Allocate local memory.
|
||||
for int(id) >= len(s.ptrs) {
|
||||
s.stack[0] = uint64(size)
|
||||
s.stack[0] = stk_t(size)
|
||||
if err := s.alloc.CallWithStack(ctx, s.stack[:]); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if s.stack[0] == 0 {
|
||||
panic(util.OOMErr)
|
||||
}
|
||||
clear(util.View(s.mod, uint32(s.stack[0]), _WALINDEX_PGSZ))
|
||||
s.ptrs = append(s.ptrs, uint32(s.stack[0]))
|
||||
clear(util.View(s.mod, ptr_t(s.stack[0]), _WALINDEX_PGSZ))
|
||||
s.ptrs = append(s.ptrs, ptr_t(s.stack[0]))
|
||||
}
|
||||
|
||||
s.shadow[0][4] = 1
|
||||
@@ -168,7 +168,7 @@ func (s *vfsShm) shmUnmap(delete bool) {
|
||||
defer s.Unlock()
|
||||
|
||||
for _, p := range s.ptrs {
|
||||
s.stack[0] = uint64(p)
|
||||
s.stack[0] = stk_t(p)
|
||||
if err := s.free.CallWithStack(context.Background(), s.stack[:]); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
@@ -73,7 +73,7 @@ func (s *vfsShm) shmOpen() _ErrorCode {
|
||||
return rc
|
||||
}
|
||||
|
||||
func (s *vfsShm) shmMap(ctx context.Context, mod api.Module, id, size int32, extend bool) (uint32, _ErrorCode) {
|
||||
func (s *vfsShm) shmMap(ctx context.Context, mod api.Module, id, size int32, extend bool) (ptr_t, _ErrorCode) {
|
||||
// Ensure size is a multiple of the OS page size.
|
||||
if int(size)&(unix.Getpagesize()-1) != 0 {
|
||||
return 0, _IOERR_SHMMAP
|
||||
|
||||
@@ -26,8 +26,8 @@ type vfsShm struct {
|
||||
regions []*util.MappedRegion
|
||||
shared [][]byte
|
||||
shadow [][_WALINDEX_PGSZ]byte
|
||||
ptrs []uint32
|
||||
stack [1]uint64
|
||||
ptrs []ptr_t
|
||||
stack [1]stk_t
|
||||
fileLock bool
|
||||
blocking bool
|
||||
sync.Mutex
|
||||
@@ -72,7 +72,7 @@ func (s *vfsShm) shmOpen() _ErrorCode {
|
||||
return rc
|
||||
}
|
||||
|
||||
func (s *vfsShm) shmMap(ctx context.Context, mod api.Module, id, size int32, extend bool) (_ uint32, rc _ErrorCode) {
|
||||
func (s *vfsShm) shmMap(ctx context.Context, mod api.Module, id, size int32, extend bool) (_ ptr_t, rc _ErrorCode) {
|
||||
// Ensure size is a multiple of the OS page size.
|
||||
if size != _WALINDEX_PGSZ || (windows.Getpagesize()-1)&_WALINDEX_PGSZ != 0 {
|
||||
return 0, _IOERR_SHMMAP
|
||||
@@ -119,15 +119,15 @@ func (s *vfsShm) shmMap(ctx context.Context, mod api.Module, id, size int32, ext
|
||||
|
||||
// Allocate local memory.
|
||||
for int(id) >= len(s.ptrs) {
|
||||
s.stack[0] = uint64(size)
|
||||
s.stack[0] = stk_t(size)
|
||||
if err := s.alloc.CallWithStack(ctx, s.stack[:]); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if s.stack[0] == 0 {
|
||||
panic(util.OOMErr)
|
||||
}
|
||||
clear(util.View(s.mod, uint32(s.stack[0]), _WALINDEX_PGSZ))
|
||||
s.ptrs = append(s.ptrs, uint32(s.stack[0]))
|
||||
clear(util.View(s.mod, ptr_t(s.stack[0]), _WALINDEX_PGSZ))
|
||||
s.ptrs = append(s.ptrs, ptr_t(s.stack[0]))
|
||||
}
|
||||
|
||||
s.shadow[0][4] = 1
|
||||
@@ -168,7 +168,7 @@ func (s *vfsShm) shmUnmap(delete bool) {
|
||||
|
||||
// Free local memory.
|
||||
for _, p := range s.ptrs {
|
||||
s.stack[0] = uint64(p)
|
||||
s.stack[0] = stk_t(p)
|
||||
if err := s.free.CallWithStack(context.Background(), s.stack[:]); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include <unistd.h>
|
||||
|
||||
// Use the default callback, not the Go one we patched in.
|
||||
#define sqliteBusyCallback sqliteDefaultBusyCallback
|
||||
|
||||
// Amalgamation
|
||||
@@ -9,8 +10,10 @@
|
||||
|
||||
__attribute__((constructor)) void init() { sqlite3_initialize(); }
|
||||
|
||||
// Ignore these.
|
||||
#define sqlite3_enable_load_extension(...)
|
||||
#define sqlite3_trace(...)
|
||||
#define unlink(...) (0)
|
||||
#undef UNUSED_PARAMETER
|
||||
|
||||
#include "mptest.c"
|
||||
Binary file not shown.
@@ -1,3 +1,4 @@
|
||||
// Use the default callback, not the Go one we patched in.
|
||||
#define sqliteBusyCallback sqliteDefaultBusyCallback
|
||||
|
||||
// Amalgamation
|
||||
@@ -5,5 +6,7 @@
|
||||
// VFS
|
||||
#include "vfs.c"
|
||||
|
||||
#define randomFunc randomFunc2
|
||||
// Can't have two functions with the same name.
|
||||
#define randomFunc randomFuncRepeatable
|
||||
|
||||
#include "speedtest1.c"
|
||||
Binary file not shown.
174
vfs/vfs.go
174
vfs/vfs.go
@@ -49,7 +49,7 @@ func ExportHostFunctions(env wazero.HostModuleBuilder) wazero.HostModuleBuilder
|
||||
return env
|
||||
}
|
||||
|
||||
func vfsFind(ctx context.Context, mod api.Module, zVfsName uint32) uint32 {
|
||||
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{}) {
|
||||
return 1
|
||||
@@ -57,46 +57,41 @@ func vfsFind(ctx context.Context, mod api.Module, zVfsName uint32) uint32 {
|
||||
return 0
|
||||
}
|
||||
|
||||
func vfsLocaltime(ctx context.Context, mod api.Module, pTm uint32, t int64) _ErrorCode {
|
||||
tm := time.Unix(t, 0)
|
||||
var isdst int
|
||||
if tm.IsDST() {
|
||||
isdst = 1
|
||||
}
|
||||
|
||||
func vfsLocaltime(ctx context.Context, mod api.Module, pTm ptr_t, t int64) _ErrorCode {
|
||||
const size = 32 / 8
|
||||
tm := time.Unix(t, 0)
|
||||
// https://pubs.opengroup.org/onlinepubs/7908799/xsh/time.h.html
|
||||
util.WriteUint32(mod, pTm+0*size, uint32(tm.Second()))
|
||||
util.WriteUint32(mod, pTm+1*size, uint32(tm.Minute()))
|
||||
util.WriteUint32(mod, pTm+2*size, uint32(tm.Hour()))
|
||||
util.WriteUint32(mod, pTm+3*size, uint32(tm.Day()))
|
||||
util.WriteUint32(mod, pTm+4*size, uint32(tm.Month()-time.January))
|
||||
util.WriteUint32(mod, pTm+5*size, uint32(tm.Year()-1900))
|
||||
util.WriteUint32(mod, pTm+6*size, uint32(tm.Weekday()-time.Sunday))
|
||||
util.WriteUint32(mod, pTm+7*size, uint32(tm.YearDay()-1))
|
||||
util.WriteUint32(mod, pTm+8*size, uint32(isdst))
|
||||
util.Write32(mod, pTm+0*size, int32(tm.Second()))
|
||||
util.Write32(mod, pTm+1*size, int32(tm.Minute()))
|
||||
util.Write32(mod, pTm+2*size, int32(tm.Hour()))
|
||||
util.Write32(mod, pTm+3*size, int32(tm.Day()))
|
||||
util.Write32(mod, pTm+4*size, int32(tm.Month()-time.January))
|
||||
util.Write32(mod, pTm+5*size, int32(tm.Year()-1900))
|
||||
util.Write32(mod, pTm+6*size, int32(tm.Weekday()-time.Sunday))
|
||||
util.Write32(mod, pTm+7*size, int32(tm.YearDay()-1))
|
||||
util.WriteBool(mod, pTm+8*size, tm.IsDST())
|
||||
return _OK
|
||||
}
|
||||
|
||||
func vfsRandomness(ctx context.Context, mod api.Module, pVfs uint32, nByte int32, zByte uint32) uint32 {
|
||||
mem := util.View(mod, zByte, uint64(nByte))
|
||||
func vfsRandomness(ctx context.Context, mod api.Module, pVfs ptr_t, nByte int32, zByte ptr_t) uint32 {
|
||||
mem := util.View(mod, zByte, int64(nByte))
|
||||
n, _ := rand.Reader.Read(mem)
|
||||
return uint32(n)
|
||||
}
|
||||
|
||||
func vfsSleep(ctx context.Context, mod api.Module, pVfs uint32, nMicro int32) _ErrorCode {
|
||||
func vfsSleep(ctx context.Context, mod api.Module, pVfs ptr_t, nMicro int32) _ErrorCode {
|
||||
time.Sleep(time.Duration(nMicro) * time.Microsecond)
|
||||
return _OK
|
||||
}
|
||||
|
||||
func vfsCurrentTime64(ctx context.Context, mod api.Module, pVfs, piNow uint32) _ErrorCode {
|
||||
func vfsCurrentTime64(ctx context.Context, mod api.Module, pVfs, piNow ptr_t) _ErrorCode {
|
||||
day, nsec := julianday.Date(time.Now())
|
||||
msec := day*86_400_000 + nsec/1_000_000
|
||||
util.WriteUint64(mod, piNow, uint64(msec))
|
||||
util.Write64(mod, piNow, msec)
|
||||
return _OK
|
||||
}
|
||||
|
||||
func vfsFullPathname(ctx context.Context, mod api.Module, pVfs, zRelative uint32, nFull int32, zFull uint32) _ErrorCode {
|
||||
func vfsFullPathname(ctx context.Context, mod api.Module, pVfs, zRelative ptr_t, nFull int32, zFull ptr_t) _ErrorCode {
|
||||
vfs := vfsGet(mod, pVfs)
|
||||
path := util.ReadString(mod, zRelative, _MAX_PATHNAME)
|
||||
|
||||
@@ -110,7 +105,7 @@ func vfsFullPathname(ctx context.Context, mod api.Module, pVfs, zRelative uint32
|
||||
return vfsErrorCode(err, _CANTOPEN_FULLPATH)
|
||||
}
|
||||
|
||||
func vfsDelete(ctx context.Context, mod api.Module, pVfs, zPath, syncDir uint32) _ErrorCode {
|
||||
func vfsDelete(ctx context.Context, mod api.Module, pVfs, zPath ptr_t, syncDir int32) _ErrorCode {
|
||||
vfs := vfsGet(mod, pVfs)
|
||||
path := util.ReadString(mod, zPath, _MAX_PATHNAME)
|
||||
|
||||
@@ -118,21 +113,16 @@ func vfsDelete(ctx context.Context, mod api.Module, pVfs, zPath, syncDir uint32)
|
||||
return vfsErrorCode(err, _IOERR_DELETE)
|
||||
}
|
||||
|
||||
func vfsAccess(ctx context.Context, mod api.Module, pVfs, zPath uint32, flags AccessFlag, pResOut uint32) _ErrorCode {
|
||||
func vfsAccess(ctx context.Context, mod api.Module, pVfs, zPath ptr_t, flags AccessFlag, pResOut ptr_t) _ErrorCode {
|
||||
vfs := vfsGet(mod, pVfs)
|
||||
path := util.ReadString(mod, zPath, _MAX_PATHNAME)
|
||||
|
||||
ok, err := vfs.Access(path, flags)
|
||||
var res uint32
|
||||
if ok {
|
||||
res = 1
|
||||
}
|
||||
|
||||
util.WriteUint32(mod, pResOut, res)
|
||||
util.WriteBool(mod, pResOut, ok)
|
||||
return vfsErrorCode(err, _IOERR_ACCESS)
|
||||
}
|
||||
|
||||
func vfsOpen(ctx context.Context, mod api.Module, pVfs, zPath, pFile uint32, flags OpenFlag, pOutFlags, pOutVFS uint32) _ErrorCode {
|
||||
func vfsOpen(ctx context.Context, mod api.Module, pVfs, zPath, pFile ptr_t, flags OpenFlag, pOutFlags, pOutVFS ptr_t) _ErrorCode {
|
||||
vfs := vfsGet(mod, pVfs)
|
||||
name := GetFilename(ctx, mod, zPath, flags)
|
||||
|
||||
@@ -152,26 +142,25 @@ func vfsOpen(ctx context.Context, mod api.Module, pVfs, zPath, pFile uint32, fla
|
||||
file.SetPowersafeOverwrite(b)
|
||||
}
|
||||
}
|
||||
if file, ok := file.(FileSharedMemory); ok &&
|
||||
pOutVFS != 0 && file.SharedMemory() != nil {
|
||||
util.WriteUint32(mod, pOutVFS, 1)
|
||||
if file, ok := file.(FileSharedMemory); ok && pOutVFS != 0 {
|
||||
util.WriteBool(mod, pOutVFS, file.SharedMemory() != nil)
|
||||
}
|
||||
if pOutFlags != 0 {
|
||||
util.WriteUint32(mod, pOutFlags, uint32(flags))
|
||||
util.Write32(mod, pOutFlags, flags)
|
||||
}
|
||||
file = cksmWrapFile(name, flags, file)
|
||||
vfsFileRegister(ctx, mod, pFile, file)
|
||||
return _OK
|
||||
}
|
||||
|
||||
func vfsClose(ctx context.Context, mod api.Module, pFile uint32) _ErrorCode {
|
||||
func vfsClose(ctx context.Context, mod api.Module, pFile ptr_t) _ErrorCode {
|
||||
err := vfsFileClose(ctx, mod, pFile)
|
||||
return vfsErrorCode(err, _IOERR_CLOSE)
|
||||
}
|
||||
|
||||
func vfsRead(ctx context.Context, mod api.Module, pFile, zBuf uint32, iAmt int32, iOfst int64) _ErrorCode {
|
||||
func vfsRead(ctx context.Context, mod api.Module, pFile, zBuf ptr_t, iAmt int32, iOfst int64) _ErrorCode {
|
||||
file := vfsFileGet(ctx, mod, pFile).(File)
|
||||
buf := util.View(mod, zBuf, uint64(iAmt))
|
||||
buf := util.View(mod, zBuf, int64(iAmt))
|
||||
|
||||
n, err := file.ReadAt(buf, iOfst)
|
||||
if n == int(iAmt) {
|
||||
@@ -184,59 +173,53 @@ func vfsRead(ctx context.Context, mod api.Module, pFile, zBuf uint32, iAmt int32
|
||||
return _IOERR_SHORT_READ
|
||||
}
|
||||
|
||||
func vfsWrite(ctx context.Context, mod api.Module, pFile, zBuf uint32, iAmt int32, iOfst int64) _ErrorCode {
|
||||
func vfsWrite(ctx context.Context, mod api.Module, pFile, zBuf ptr_t, iAmt int32, iOfst int64) _ErrorCode {
|
||||
file := vfsFileGet(ctx, mod, pFile).(File)
|
||||
buf := util.View(mod, zBuf, uint64(iAmt))
|
||||
buf := util.View(mod, zBuf, int64(iAmt))
|
||||
|
||||
_, err := file.WriteAt(buf, iOfst)
|
||||
return vfsErrorCode(err, _IOERR_WRITE)
|
||||
}
|
||||
|
||||
func vfsTruncate(ctx context.Context, mod api.Module, pFile uint32, nByte int64) _ErrorCode {
|
||||
func vfsTruncate(ctx context.Context, mod api.Module, pFile ptr_t, nByte int64) _ErrorCode {
|
||||
file := vfsFileGet(ctx, mod, pFile).(File)
|
||||
err := file.Truncate(nByte)
|
||||
return vfsErrorCode(err, _IOERR_TRUNCATE)
|
||||
}
|
||||
|
||||
func vfsSync(ctx context.Context, mod api.Module, pFile uint32, flags SyncFlag) _ErrorCode {
|
||||
func vfsSync(ctx context.Context, mod api.Module, pFile ptr_t, flags SyncFlag) _ErrorCode {
|
||||
file := vfsFileGet(ctx, mod, pFile).(File)
|
||||
err := file.Sync(flags)
|
||||
return vfsErrorCode(err, _IOERR_FSYNC)
|
||||
}
|
||||
|
||||
func vfsFileSize(ctx context.Context, mod api.Module, pFile, pSize uint32) _ErrorCode {
|
||||
func vfsFileSize(ctx context.Context, mod api.Module, pFile, pSize ptr_t) _ErrorCode {
|
||||
file := vfsFileGet(ctx, mod, pFile).(File)
|
||||
size, err := file.Size()
|
||||
util.WriteUint64(mod, pSize, uint64(size))
|
||||
util.Write64(mod, pSize, size)
|
||||
return vfsErrorCode(err, _IOERR_SEEK)
|
||||
}
|
||||
|
||||
func vfsLock(ctx context.Context, mod api.Module, pFile uint32, eLock LockLevel) _ErrorCode {
|
||||
func vfsLock(ctx context.Context, mod api.Module, pFile ptr_t, eLock LockLevel) _ErrorCode {
|
||||
file := vfsFileGet(ctx, mod, pFile).(File)
|
||||
err := file.Lock(eLock)
|
||||
return vfsErrorCode(err, _IOERR_LOCK)
|
||||
}
|
||||
|
||||
func vfsUnlock(ctx context.Context, mod api.Module, pFile uint32, eLock LockLevel) _ErrorCode {
|
||||
func vfsUnlock(ctx context.Context, mod api.Module, pFile ptr_t, eLock LockLevel) _ErrorCode {
|
||||
file := vfsFileGet(ctx, mod, pFile).(File)
|
||||
err := file.Unlock(eLock)
|
||||
return vfsErrorCode(err, _IOERR_UNLOCK)
|
||||
}
|
||||
|
||||
func vfsCheckReservedLock(ctx context.Context, mod api.Module, pFile, pResOut uint32) _ErrorCode {
|
||||
func vfsCheckReservedLock(ctx context.Context, mod api.Module, pFile, pResOut ptr_t) _ErrorCode {
|
||||
file := vfsFileGet(ctx, mod, pFile).(File)
|
||||
locked, err := file.CheckReservedLock()
|
||||
|
||||
var res uint32
|
||||
if locked {
|
||||
res = 1
|
||||
}
|
||||
|
||||
util.WriteUint32(mod, pResOut, res)
|
||||
util.WriteBool(mod, pResOut, locked)
|
||||
return vfsErrorCode(err, _IOERR_CHECKRESERVEDLOCK)
|
||||
}
|
||||
|
||||
func vfsFileControl(ctx context.Context, mod api.Module, pFile uint32, op _FcntlOpcode, pArg uint32) _ErrorCode {
|
||||
func vfsFileControl(ctx context.Context, mod api.Module, pFile ptr_t, op _FcntlOpcode, pArg ptr_t) _ErrorCode {
|
||||
file := vfsFileGet(ctx, mod, pFile).(File)
|
||||
if file, ok := file.(fileControl); ok {
|
||||
return file.fileControl(ctx, mod, op, pArg)
|
||||
@@ -244,62 +227,54 @@ func vfsFileControl(ctx context.Context, mod api.Module, pFile uint32, op _Fcntl
|
||||
return vfsFileControlImpl(ctx, mod, file, op, pArg)
|
||||
}
|
||||
|
||||
func vfsFileControlImpl(ctx context.Context, mod api.Module, file File, op _FcntlOpcode, pArg uint32) _ErrorCode {
|
||||
func vfsFileControlImpl(ctx context.Context, mod api.Module, file File, op _FcntlOpcode, pArg ptr_t) _ErrorCode {
|
||||
switch op {
|
||||
case _FCNTL_LOCKSTATE:
|
||||
if file, ok := file.(FileLockState); ok {
|
||||
if lk := file.LockState(); lk <= LOCK_EXCLUSIVE {
|
||||
util.WriteUint32(mod, pArg, uint32(lk))
|
||||
util.Write32(mod, pArg, lk)
|
||||
return _OK
|
||||
}
|
||||
}
|
||||
|
||||
case _FCNTL_PERSIST_WAL:
|
||||
if file, ok := file.(FilePersistWAL); ok {
|
||||
if i := util.ReadUint32(mod, pArg); int32(i) >= 0 {
|
||||
file.SetPersistWAL(i != 0)
|
||||
} else if file.PersistWAL() {
|
||||
util.WriteUint32(mod, pArg, 1)
|
||||
if i := util.Read32[int32](mod, pArg); i < 0 {
|
||||
util.WriteBool(mod, pArg, file.PersistWAL())
|
||||
} else {
|
||||
util.WriteUint32(mod, pArg, 0)
|
||||
file.SetPersistWAL(i != 0)
|
||||
}
|
||||
return _OK
|
||||
}
|
||||
|
||||
case _FCNTL_POWERSAFE_OVERWRITE:
|
||||
if file, ok := file.(FilePowersafeOverwrite); ok {
|
||||
if i := util.ReadUint32(mod, pArg); int32(i) >= 0 {
|
||||
file.SetPowersafeOverwrite(i != 0)
|
||||
} else if file.PowersafeOverwrite() {
|
||||
util.WriteUint32(mod, pArg, 1)
|
||||
if i := util.Read32[int32](mod, pArg); i < 0 {
|
||||
util.WriteBool(mod, pArg, file.PowersafeOverwrite())
|
||||
} else {
|
||||
util.WriteUint32(mod, pArg, 0)
|
||||
file.SetPowersafeOverwrite(i != 0)
|
||||
}
|
||||
return _OK
|
||||
}
|
||||
|
||||
case _FCNTL_CHUNK_SIZE:
|
||||
if file, ok := file.(FileChunkSize); ok {
|
||||
size := util.ReadUint32(mod, pArg)
|
||||
size := util.Read32[int32](mod, pArg)
|
||||
file.ChunkSize(int(size))
|
||||
return _OK
|
||||
}
|
||||
|
||||
case _FCNTL_SIZE_HINT:
|
||||
if file, ok := file.(FileSizeHint); ok {
|
||||
size := util.ReadUint64(mod, pArg)
|
||||
err := file.SizeHint(int64(size))
|
||||
size := util.Read64[int64](mod, pArg)
|
||||
err := file.SizeHint(size)
|
||||
return vfsErrorCode(err, _IOERR_TRUNCATE)
|
||||
}
|
||||
|
||||
case _FCNTL_HAS_MOVED:
|
||||
if file, ok := file.(FileHasMoved); ok {
|
||||
moved, err := file.HasMoved()
|
||||
var res uint32
|
||||
if moved {
|
||||
res = 1
|
||||
}
|
||||
util.WriteUint32(mod, pArg, res)
|
||||
util.WriteBool(mod, pArg, moved)
|
||||
return vfsErrorCode(err, _IOERR_FSTAT)
|
||||
}
|
||||
|
||||
@@ -354,10 +329,10 @@ func vfsFileControlImpl(ctx context.Context, mod api.Module, file File, op _Fcnt
|
||||
|
||||
case _FCNTL_PRAGMA:
|
||||
if file, ok := file.(FilePragma); ok {
|
||||
ptr := util.ReadUint32(mod, pArg+1*ptrlen)
|
||||
ptr := util.Read32[ptr_t](mod, pArg+1*ptrlen)
|
||||
name := util.ReadString(mod, ptr, _MAX_SQL_LENGTH)
|
||||
var value string
|
||||
if ptr := util.ReadUint32(mod, pArg+2*ptrlen); ptr != 0 {
|
||||
if ptr := util.Read32[ptr_t](mod, pArg+2*ptrlen); ptr != 0 {
|
||||
value = util.ReadString(mod, ptr, _MAX_SQL_LENGTH)
|
||||
}
|
||||
|
||||
@@ -369,22 +344,22 @@ func vfsFileControlImpl(ctx context.Context, mod api.Module, file File, op _Fcnt
|
||||
}
|
||||
if out != "" {
|
||||
fn := mod.ExportedFunction("sqlite3_malloc64")
|
||||
stack := [...]uint64{uint64(len(out) + 1)}
|
||||
stack := [...]stk_t{stk_t(len(out) + 1)}
|
||||
if err := fn.CallWithStack(ctx, stack[:]); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
util.WriteUint32(mod, pArg, uint32(stack[0]))
|
||||
util.WriteString(mod, uint32(stack[0]), out)
|
||||
util.Write32(mod, pArg, ptr_t(stack[0]))
|
||||
util.WriteString(mod, ptr_t(stack[0]), out)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
case _FCNTL_BUSYHANDLER:
|
||||
if file, ok := file.(FileBusyHandler); ok {
|
||||
arg := util.ReadUint64(mod, pArg)
|
||||
arg := util.Read64[stk_t](mod, pArg)
|
||||
fn := mod.ExportedFunction("sqlite3_invoke_busy_handler_go")
|
||||
file.BusyHandler(func() bool {
|
||||
stack := [...]uint64{arg}
|
||||
stack := [...]stk_t{arg}
|
||||
if err := fn.CallWithStack(ctx, stack[:]); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -396,7 +371,7 @@ func vfsFileControlImpl(ctx context.Context, mod api.Module, file File, op _Fcnt
|
||||
case _FCNTL_LOCK_TIMEOUT:
|
||||
if file, ok := file.(FileSharedMemory); ok {
|
||||
if shm, ok := file.SharedMemory().(blockingSharedMemory); ok {
|
||||
shm.shmEnableBlocking(util.ReadUint32(mod, pArg) != 0)
|
||||
shm.shmEnableBlocking(util.ReadBool(mod, pArg))
|
||||
return _OK
|
||||
}
|
||||
}
|
||||
@@ -411,44 +386,45 @@ func vfsFileControlImpl(ctx context.Context, mod api.Module, file File, op _Fcnt
|
||||
return _NOTFOUND
|
||||
}
|
||||
|
||||
func vfsSectorSize(ctx context.Context, mod api.Module, pFile uint32) uint32 {
|
||||
func vfsSectorSize(ctx context.Context, mod api.Module, pFile ptr_t) uint32 {
|
||||
file := vfsFileGet(ctx, mod, pFile).(File)
|
||||
return uint32(file.SectorSize())
|
||||
}
|
||||
|
||||
func vfsDeviceCharacteristics(ctx context.Context, mod api.Module, pFile uint32) DeviceCharacteristic {
|
||||
func vfsDeviceCharacteristics(ctx context.Context, mod api.Module, pFile ptr_t) DeviceCharacteristic {
|
||||
file := vfsFileGet(ctx, mod, pFile).(File)
|
||||
return file.DeviceCharacteristics()
|
||||
}
|
||||
|
||||
func vfsShmBarrier(ctx context.Context, mod api.Module, pFile uint32) {
|
||||
func vfsShmBarrier(ctx context.Context, mod api.Module, pFile ptr_t) {
|
||||
shm := vfsFileGet(ctx, mod, pFile).(FileSharedMemory).SharedMemory()
|
||||
shm.shmBarrier()
|
||||
}
|
||||
|
||||
func vfsShmMap(ctx context.Context, mod api.Module, pFile uint32, iRegion, szRegion int32, bExtend, pp uint32) _ErrorCode {
|
||||
func vfsShmMap(ctx context.Context, mod api.Module, pFile ptr_t, iRegion, szRegion, bExtend int32, pp ptr_t) _ErrorCode {
|
||||
shm := vfsFileGet(ctx, mod, pFile).(FileSharedMemory).SharedMemory()
|
||||
p, rc := shm.shmMap(ctx, mod, iRegion, szRegion, bExtend != 0)
|
||||
util.WriteUint32(mod, pp, p)
|
||||
util.Write32(mod, pp, p)
|
||||
return rc
|
||||
}
|
||||
|
||||
func vfsShmLock(ctx context.Context, mod api.Module, pFile uint32, offset, n int32, flags _ShmFlag) _ErrorCode {
|
||||
func vfsShmLock(ctx context.Context, mod api.Module, pFile ptr_t, offset, n int32, flags _ShmFlag) _ErrorCode {
|
||||
shm := vfsFileGet(ctx, mod, pFile).(FileSharedMemory).SharedMemory()
|
||||
return shm.shmLock(offset, n, flags)
|
||||
}
|
||||
|
||||
func vfsShmUnmap(ctx context.Context, mod api.Module, pFile, bDelete uint32) _ErrorCode {
|
||||
func vfsShmUnmap(ctx context.Context, mod api.Module, pFile ptr_t, bDelete int32) _ErrorCode {
|
||||
shm := vfsFileGet(ctx, mod, pFile).(FileSharedMemory).SharedMemory()
|
||||
shm.shmUnmap(bDelete != 0)
|
||||
return _OK
|
||||
}
|
||||
|
||||
func vfsGet(mod api.Module, pVfs uint32) VFS {
|
||||
func vfsGet(mod api.Module, pVfs ptr_t) VFS {
|
||||
var name string
|
||||
if pVfs != 0 {
|
||||
const zNameOffset = 16
|
||||
name = util.ReadString(mod, util.ReadUint32(mod, pVfs+zNameOffset), _MAX_NAME)
|
||||
ptr := util.Read32[ptr_t](mod, pVfs+zNameOffset)
|
||||
name = util.ReadString(mod, ptr, _MAX_NAME)
|
||||
}
|
||||
if vfs := Find(name); vfs != nil {
|
||||
return vfs
|
||||
@@ -456,21 +432,21 @@ func vfsGet(mod api.Module, pVfs uint32) VFS {
|
||||
panic(util.NoVFSErr + util.ErrorString(name))
|
||||
}
|
||||
|
||||
func vfsFileRegister(ctx context.Context, mod api.Module, pFile uint32, file File) {
|
||||
func vfsFileRegister(ctx context.Context, mod api.Module, pFile ptr_t, file File) {
|
||||
const fileHandleOffset = 4
|
||||
id := util.AddHandle(ctx, file)
|
||||
util.WriteUint32(mod, pFile+fileHandleOffset, id)
|
||||
util.Write32(mod, pFile+fileHandleOffset, id)
|
||||
}
|
||||
|
||||
func vfsFileGet(ctx context.Context, mod api.Module, pFile uint32) any {
|
||||
func vfsFileGet(ctx context.Context, mod api.Module, pFile ptr_t) any {
|
||||
const fileHandleOffset = 4
|
||||
id := util.ReadUint32(mod, pFile+fileHandleOffset)
|
||||
id := util.Read32[ptr_t](mod, pFile+fileHandleOffset)
|
||||
return util.GetHandle(ctx, id)
|
||||
}
|
||||
|
||||
func vfsFileClose(ctx context.Context, mod api.Module, pFile uint32) error {
|
||||
func vfsFileClose(ctx context.Context, mod api.Module, pFile ptr_t) error {
|
||||
const fileHandleOffset = 4
|
||||
id := util.ReadUint32(mod, pFile+fileHandleOffset)
|
||||
id := util.Read32[ptr_t](mod, pFile+fileHandleOffset)
|
||||
return util.DelHandle(ctx, id)
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"io/fs"
|
||||
"math"
|
||||
"os"
|
||||
"os/user"
|
||||
"path/filepath"
|
||||
@@ -29,28 +28,28 @@ func Test_vfsLocaltime(t *testing.T) {
|
||||
t.Fatal("returned", rc)
|
||||
}
|
||||
|
||||
if s := util.ReadUint32(mod, 4+0*4); int(s) != tm.Second() {
|
||||
if s := util.Read32[int32](mod, 4+0*4); int(s) != tm.Second() {
|
||||
t.Error("wrong second")
|
||||
}
|
||||
if m := util.ReadUint32(mod, 4+1*4); int(m) != tm.Minute() {
|
||||
if m := util.Read32[int32](mod, 4+1*4); int(m) != tm.Minute() {
|
||||
t.Error("wrong minute")
|
||||
}
|
||||
if h := util.ReadUint32(mod, 4+2*4); int(h) != tm.Hour() {
|
||||
if h := util.Read32[int32](mod, 4+2*4); int(h) != tm.Hour() {
|
||||
t.Error("wrong hour")
|
||||
}
|
||||
if d := util.ReadUint32(mod, 4+3*4); int(d) != tm.Day() {
|
||||
if d := util.Read32[int32](mod, 4+3*4); int(d) != tm.Day() {
|
||||
t.Error("wrong day")
|
||||
}
|
||||
if m := util.ReadUint32(mod, 4+4*4); time.Month(1+m) != tm.Month() {
|
||||
if m := util.Read32[int32](mod, 4+4*4); time.Month(1+m) != tm.Month() {
|
||||
t.Error("wrong month")
|
||||
}
|
||||
if y := util.ReadUint32(mod, 4+5*4); 1900+int(y) != tm.Year() {
|
||||
if y := util.Read32[int32](mod, 4+5*4); 1900+int(y) != tm.Year() {
|
||||
t.Error("wrong year")
|
||||
}
|
||||
if w := util.ReadUint32(mod, 4+6*4); time.Weekday(w) != tm.Weekday() {
|
||||
if w := util.Read32[int32](mod, 4+6*4); time.Weekday(w) != tm.Weekday() {
|
||||
t.Error("wrong weekday")
|
||||
}
|
||||
if d := util.ReadUint32(mod, 4+7*4); int(d) != tm.YearDay()-1 {
|
||||
if d := util.Read32[int32](mod, 4+7*4); int(d) != tm.YearDay()-1 {
|
||||
t.Error("wrong yearday")
|
||||
}
|
||||
}
|
||||
@@ -99,7 +98,7 @@ func Test_vfsCurrentTime64(t *testing.T) {
|
||||
|
||||
day, nsec := julianday.Date(now)
|
||||
want := day*86_400_000 + nsec/1_000_000
|
||||
if got := util.ReadUint64(mod, 4); float32(got) != float32(want) {
|
||||
if got := util.Read64[int64](mod, 4); float32(got) != float32(want) {
|
||||
t.Errorf("got %v, want %v", got, want)
|
||||
}
|
||||
}
|
||||
@@ -173,7 +172,7 @@ func Test_vfsAccess(t *testing.T) {
|
||||
if rc != _OK {
|
||||
t.Fatal("returned", rc)
|
||||
}
|
||||
if got := util.ReadUint32(mod, 4); got != 1 {
|
||||
if got := util.ReadBool(mod, 4); !got {
|
||||
t.Error("directory did not exist")
|
||||
}
|
||||
|
||||
@@ -181,7 +180,7 @@ func Test_vfsAccess(t *testing.T) {
|
||||
if rc != _OK {
|
||||
t.Fatal("returned", rc)
|
||||
}
|
||||
if got := util.ReadUint32(mod, 4); got != 1 {
|
||||
if got := util.ReadBool(mod, 4); !got {
|
||||
t.Error("can't access directory")
|
||||
}
|
||||
|
||||
@@ -190,7 +189,7 @@ func Test_vfsAccess(t *testing.T) {
|
||||
if rc != _OK {
|
||||
t.Fatal("returned", rc)
|
||||
}
|
||||
if got := util.ReadUint32(mod, 4); got != 1 {
|
||||
if got := util.ReadBool(mod, 4); !got {
|
||||
t.Error("can't access file")
|
||||
}
|
||||
|
||||
@@ -208,7 +207,7 @@ func Test_vfsAccess(t *testing.T) {
|
||||
if rc != _OK {
|
||||
t.Fatal("returned", rc)
|
||||
}
|
||||
if got := util.ReadUint32(mod, 4); got != 0 {
|
||||
if got := util.ReadBool(mod, 4); got {
|
||||
t.Error("can access file")
|
||||
}
|
||||
}
|
||||
@@ -241,7 +240,7 @@ func Test_vfsFile(t *testing.T) {
|
||||
if rc != _OK {
|
||||
t.Fatal("returned", rc)
|
||||
}
|
||||
if got := util.ReadUint32(mod, 16); got != uint32(len(text)) {
|
||||
if got := util.Read64[int64](mod, 16); got != int64(len(text)) {
|
||||
t.Errorf("got %d", got)
|
||||
}
|
||||
|
||||
@@ -265,7 +264,7 @@ func Test_vfsFile(t *testing.T) {
|
||||
if rc != _OK {
|
||||
t.Fatal("returned", rc)
|
||||
}
|
||||
if got := util.ReadUint32(mod, 16); got != 4 {
|
||||
if got := util.Read64[int64](mod, 16); got != 4 {
|
||||
t.Errorf("got %d", got)
|
||||
}
|
||||
|
||||
@@ -296,46 +295,46 @@ func Test_vfsFile_psow(t *testing.T) {
|
||||
}
|
||||
|
||||
// Read powersafe overwrite.
|
||||
util.WriteUint32(mod, 16, math.MaxUint32)
|
||||
util.Write32(mod, 16, int32(-1))
|
||||
rc = vfsFileControl(ctx, mod, 4, _FCNTL_POWERSAFE_OVERWRITE, 16)
|
||||
if rc != _OK {
|
||||
t.Fatal("returned", rc)
|
||||
}
|
||||
if got := util.ReadUint32(mod, 16); got == 0 {
|
||||
if got := util.Read32[int32](mod, 16); got == 0 {
|
||||
t.Error("psow disabled")
|
||||
}
|
||||
|
||||
// Unset powersafe overwrite.
|
||||
util.WriteUint32(mod, 16, 0)
|
||||
util.Write32(mod, 16, int32(0))
|
||||
rc = vfsFileControl(ctx, mod, 4, _FCNTL_POWERSAFE_OVERWRITE, 16)
|
||||
if rc != _OK {
|
||||
t.Fatal("returned", rc)
|
||||
}
|
||||
|
||||
// Read powersafe overwrite.
|
||||
util.WriteUint32(mod, 16, math.MaxUint32)
|
||||
util.Write32(mod, 16, int32(-1))
|
||||
rc = vfsFileControl(ctx, mod, 4, _FCNTL_POWERSAFE_OVERWRITE, 16)
|
||||
if rc != _OK {
|
||||
t.Fatal("returned", rc)
|
||||
}
|
||||
if got := util.ReadUint32(mod, 16); got != 0 {
|
||||
if got := util.Read32[int32](mod, 16); got != 0 {
|
||||
t.Error("psow enabled")
|
||||
}
|
||||
|
||||
// Set powersafe overwrite.
|
||||
util.WriteUint32(mod, 16, 1)
|
||||
util.Write32(mod, 16, int32(1))
|
||||
rc = vfsFileControl(ctx, mod, 4, _FCNTL_POWERSAFE_OVERWRITE, 16)
|
||||
if rc != _OK {
|
||||
t.Fatal("returned", rc)
|
||||
}
|
||||
|
||||
// Read powersafe overwrite.
|
||||
util.WriteUint32(mod, 16, math.MaxUint32)
|
||||
util.Write32(mod, 16, int32(-1))
|
||||
rc = vfsFileControl(ctx, mod, 4, _FCNTL_POWERSAFE_OVERWRITE, 16)
|
||||
if rc != _OK {
|
||||
t.Fatal("returned", rc)
|
||||
}
|
||||
if got := util.ReadUint32(mod, 16); got == 0 {
|
||||
if got := util.Read32[int32](mod, 16); got == 0 {
|
||||
t.Error("psow disabled")
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user