mirror of
https://github.com/ncruces/go-sqlite3.git
synced 2026-01-19 09:04:16 +00:00
Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
37a3ff37e8 | ||
|
|
d880d6842c | ||
|
|
bef46e7954 | ||
|
|
4e72b4d117 | ||
|
|
3b08d02a83 | ||
|
|
b19c12c4c7 | ||
|
|
859a21ef4e | ||
|
|
8ff0ee752f | ||
|
|
589ad86f76 | ||
|
|
1a3a1be1f6 | ||
|
|
222c217bc8 | ||
|
|
c1dc716391 | ||
|
|
71e1e5a8ee |
29
.github/workflows/go.yml
vendored
29
.github/workflows/go.yml
vendored
@@ -18,11 +18,27 @@ jobs:
|
||||
with:
|
||||
lfs: 'true'
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v3
|
||||
- name: Set up
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: stable
|
||||
cache: true
|
||||
|
||||
- name: Format
|
||||
run: gofmt -s -w . && git diff --exit-code
|
||||
if: matrix.os != 'windows-latest'
|
||||
|
||||
- name: Tidy
|
||||
run: go mod tidy && git diff --exit-code
|
||||
|
||||
- name: Download
|
||||
run: go mod download
|
||||
|
||||
- name: Verify
|
||||
run: go mod verify
|
||||
|
||||
- name: Vet
|
||||
run: go vet ./...
|
||||
continue-on-error: true
|
||||
|
||||
- name: Build
|
||||
run: go build -v ./...
|
||||
@@ -34,10 +50,15 @@ jobs:
|
||||
run: go test -v -race ./...
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
|
||||
- name: Update coverage report
|
||||
- name: Test BSD locks
|
||||
run: go test -v -tags sqlite3_bsd ./...
|
||||
if: matrix.os == 'macos-latest'
|
||||
|
||||
- name: Coverage report
|
||||
uses: ncruces/go-coverage-report@main
|
||||
with:
|
||||
chart: 'true'
|
||||
amend: 'true'
|
||||
if: |
|
||||
matrix.os == 'ubuntu-latest' &&
|
||||
github.event_name == 'push'
|
||||
|
||||
25
README.md
25
README.md
@@ -39,22 +39,25 @@ To open WAL databases, or use `EXCLUSIVE` locking mode,
|
||||
disable connection pooling by calling
|
||||
[`db.SetMaxOpenConns(1)`](https://pkg.go.dev/database/sql#DB.SetMaxOpenConns).
|
||||
|
||||
#### Open File Description Locks
|
||||
#### POSIX Advisory Locks
|
||||
|
||||
On Unix, this module uses [OFD locks](https://www.gnu.org/software/libc/manual/html_node/Open-File-Description-Locks.html)
|
||||
POSIX advisory locks, which SQLite uses, are
|
||||
[broken by design](https://www.sqlite.org/src/artifact/90c4fa?ln=1073-1161).
|
||||
|
||||
On Linux, macOS and illumos, this module uses
|
||||
[OFD locks](https://www.gnu.org/software/libc/manual/html_node/Open-File-Description-Locks.html)
|
||||
to synchronize access to database files.
|
||||
OFD locks are fully compatible with process-associated POSIX advisory locks.
|
||||
|
||||
POSIX advisory locks, which SQLite uses, are [broken by design](https://www.sqlite.org/src/artifact/90c4fa?ln=1073-1161).
|
||||
OFD locks are fully compatible with process-associated POSIX advisory locks,
|
||||
and are supported on Linux and macOS.
|
||||
As a work around for other Unixes, you can use [`nolock=1`](https://www.sqlite.org/uri.html).
|
||||
On BSD Unixes, this module uses
|
||||
[BSD locks](https://man.freebsd.org/cgi/man.cgi?query=flock&sektion=2).
|
||||
BSD locks may _not_ be compatible with process-associated POSIX advisory locks.
|
||||
|
||||
#### Testing
|
||||
|
||||
The pure Go VFS is stress tested by running an unmodified build of SQLite's
|
||||
[mptest](https://github.com/sqlite/sqlite/blob/master/mptest/mptest.c)
|
||||
on Linux, macOS and Windows.
|
||||
|
||||
Performance is tested by running
|
||||
[speedtest1](https://github.com/sqlite/sqlite/blob/master/test/speedtest1.c).
|
||||
|
||||
@@ -64,18 +67,14 @@ Performance is tested by running
|
||||
- [x] nested transactions
|
||||
- [x] incremental BLOB I/O
|
||||
- [x] online backup
|
||||
- [ ] snapshots
|
||||
- [ ] session extension
|
||||
- [ ] resumable bulk update
|
||||
- [ ] shared-cache mode
|
||||
- [ ] unlock-notify
|
||||
- [ ] custom SQL functions
|
||||
- [ ] custom VFSes
|
||||
- [ ] in-memory VFS
|
||||
- [ ] read-only VFS, wrapping an [`io.ReaderAt`](https://pkg.go.dev/io#ReaderAt)
|
||||
- [ ] in-memory VFS, wrapping a [`bytes.Buffer`](https://pkg.go.dev/bytes#Buffer)
|
||||
- [ ] cloud-based VFS, based on [Cloud Backed SQLite](https://sqlite.org/cloudsqlite/doc/trunk/www/index.wiki)
|
||||
- [ ] custom VFS API
|
||||
|
||||
|
||||
### Alternatives
|
||||
|
||||
- [`modernc.org/sqlite`](https://pkg.go.dev/modernc.org/sqlite)
|
||||
|
||||
1
conn.go
1
conn.go
@@ -105,7 +105,6 @@ func (c *Conn) openDB(filename string, flags OpenFlag) (uint32, error) {
|
||||
}
|
||||
}
|
||||
|
||||
c.call(c.api.timeCollation, uint64(handle))
|
||||
return handle, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -386,8 +386,7 @@ func (r rows) Next(dest []driver.Value) error {
|
||||
case sqlite3.TEXT:
|
||||
dest[i] = maybeTime(r.stmt.ColumnText(i))
|
||||
case sqlite3.BLOB:
|
||||
buf, _ := dest[i].([]byte)
|
||||
dest[i] = r.stmt.ColumnBlob(i, buf)
|
||||
dest[i] = r.stmt.ColumnRawBlob(i)
|
||||
case sqlite3.NULL:
|
||||
if buf, ok := dest[i].([]byte); ok {
|
||||
dest[i] = buf[0:0]
|
||||
|
||||
@@ -308,7 +308,7 @@ func Test_QueryRow_blob_null(t *testing.T) {
|
||||
|
||||
want := [][]byte{nil, {0xca, 0xfe}, {0xba, 0xbe}, nil}
|
||||
for i := 0; rows.Next(); i++ {
|
||||
var buf []byte
|
||||
var buf sql.RawBytes
|
||||
err = rows.Scan(&buf)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
||||
@@ -1,14 +1,21 @@
|
||||
# Embeddable WASM build of SQLite
|
||||
|
||||
This folder includes an embeddable WASM build of SQLite 3.41.0 for use with
|
||||
This folder includes an embeddable WASM build of SQLite 3.41.2 for use with
|
||||
[`github.com/ncruces/go-sqlite3`](https://pkg.go.dev/github.com/ncruces/go-sqlite3).
|
||||
|
||||
The following optional features are compiled in:
|
||||
- math functions
|
||||
- FTS3/4/5
|
||||
- JSON
|
||||
- R*Tree
|
||||
- GeoPoly
|
||||
- [math functions](https://www.sqlite.org/lang_mathfunc.html)
|
||||
- [FTS3/4](https://www.sqlite.org/fts3.html)/[5](https://www.sqlite.org/fts5.html)
|
||||
- [JSON](https://www.sqlite.org/json1.html)
|
||||
- [R*Tree](https://www.sqlite.org/rtree.html)
|
||||
- [GeoPoly](https://www.sqlite.org/geopoly.html)
|
||||
- [base64](https://github.com/sqlite/sqlite/blob/master/ext/misc/base64.c)
|
||||
- [decimal](https://github.com/sqlite/sqlite/blob/master/ext/misc/decimal.c)
|
||||
- [regexp](https://github.com/sqlite/sqlite/blob/master/ext/misc/regexp.c)
|
||||
- [series](https://github.com/sqlite/sqlite/blob/master/ext/misc/series.c)
|
||||
- [uint](https://github.com/sqlite/sqlite/blob/master/ext/misc/uint.c)
|
||||
- [uuid](https://github.com/sqlite/sqlite/blob/master/ext/misc/uuid.c)
|
||||
- [time](../sqlite3/time.c)
|
||||
|
||||
See the [configuration options](../sqlite3/sqlite_cfg.h).
|
||||
|
||||
|
||||
@@ -3,20 +3,13 @@ set -eo pipefail
|
||||
|
||||
cd -P -- "$(dirname -- "$0")"
|
||||
|
||||
# download SQLite
|
||||
../sqlite3/download.sh
|
||||
|
||||
# build SQLite
|
||||
zig cc --target=wasm32-wasi -flto -g0 -Os \
|
||||
zig cc --target=wasm32-wasi -flto -g0 -O2 \
|
||||
-o sqlite3.wasm ../sqlite3/main.c \
|
||||
-I../sqlite3/ \
|
||||
-mmutable-globals \
|
||||
-mbulk-memory -mreference-types \
|
||||
-mnontrapping-fptoint -msign-ext \
|
||||
-mexec-model=reactor \
|
||||
-D_HAVE_SQLITE_CONFIG_H \
|
||||
$(awk '{print "-Wl,--export="$0}' exports.txt)
|
||||
|
||||
# optimize SQLite
|
||||
if which wasm-opt; then
|
||||
wasm-opt -g -O -o sqlite3.tmp sqlite3.wasm
|
||||
mv sqlite3.tmp sqlite3.wasm
|
||||
fi
|
||||
$(awk '{print "-Wl,--export="$0}' exports.txt)
|
||||
@@ -45,5 +45,4 @@ sqlite3_backup_step
|
||||
sqlite3_backup_finish
|
||||
sqlite3_backup_remaining
|
||||
sqlite3_backup_pagecount
|
||||
sqlite3_time_collation
|
||||
sqlite3_interrupt_offset
|
||||
BIN
embed/sqlite3.wasm
Normal file → Executable file
BIN
embed/sqlite3.wasm
Normal file → Executable file
Binary file not shown.
1
error.go
1
error.go
@@ -202,7 +202,6 @@ const (
|
||||
noFuncErr = errorString("sqlite3: could not find function: ")
|
||||
binaryErr = errorString("sqlite3: no SQLite binary embed/set/loaded")
|
||||
timeErr = errorString("sqlite3: invalid time value")
|
||||
notImplErr = errorString("sqlite3: not implemented")
|
||||
whenceErr = errorString("sqlite3: invalid whence")
|
||||
offsetErr = errorString("sqlite3: invalid offset")
|
||||
)
|
||||
|
||||
2
go.mod
2
go.mod
@@ -4,7 +4,7 @@ go 1.19
|
||||
|
||||
require (
|
||||
github.com/ncruces/julianday v0.1.5
|
||||
github.com/tetratelabs/wazero v1.0.0-rc.2
|
||||
github.com/tetratelabs/wazero v1.0.0
|
||||
golang.org/x/sync v0.1.0
|
||||
golang.org/x/sys v0.6.0
|
||||
)
|
||||
|
||||
4
go.sum
4
go.sum
@@ -1,7 +1,7 @@
|
||||
github.com/ncruces/julianday v0.1.5 h1:hDJ9ejiMp3DHsoZ5KW4c1lwfMjbARS7u/gbYcd0FBZk=
|
||||
github.com/ncruces/julianday v0.1.5/go.mod h1:Dusn2KvZrrovOMJuOt0TNXL6tB7U2E8kvza5fFc9G7g=
|
||||
github.com/tetratelabs/wazero v1.0.0-rc.2 h1:OA3UUynnoqxrjCQ94mpAtdO4/oMxFQVNL2BXDMOc66Q=
|
||||
github.com/tetratelabs/wazero v1.0.0-rc.2/go.mod h1:wYx2gNRg8/WihJfSDxA1TIL8H+GkfLYm+bIfbblu9VQ=
|
||||
github.com/tetratelabs/wazero v1.0.0 h1:sCE9+mjFex95Ki6hdqwvhyF25x5WslADjDKIFU5BXzI=
|
||||
github.com/tetratelabs/wazero v1.0.0/go.mod h1:wYx2gNRg8/WihJfSDxA1TIL8H+GkfLYm+bIfbblu9VQ=
|
||||
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
|
||||
|
||||
22
module.go
22
module.go
@@ -3,14 +3,10 @@ package sqlite3
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"io"
|
||||
"math"
|
||||
"os"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/tetratelabs/wazero"
|
||||
"github.com/tetratelabs/wazero/api"
|
||||
@@ -28,11 +24,10 @@ var (
|
||||
)
|
||||
|
||||
var sqlite3 struct {
|
||||
once sync.Once
|
||||
runtime wazero.Runtime
|
||||
compiled wazero.CompiledModule
|
||||
instances atomic.Uint64
|
||||
err error
|
||||
once sync.Once
|
||||
runtime wazero.Runtime
|
||||
compiled wazero.CompiledModule
|
||||
err error
|
||||
}
|
||||
|
||||
func instantiateModule() (*module, error) {
|
||||
@@ -43,12 +38,7 @@ func instantiateModule() (*module, error) {
|
||||
return nil, sqlite3.err
|
||||
}
|
||||
|
||||
name := "sqlite3-" + strconv.FormatUint(sqlite3.instances.Add(1), 10)
|
||||
|
||||
cfg := wazero.NewModuleConfig().WithName(name).
|
||||
WithSysWalltime().WithSysNanotime().WithSysNanosleep().
|
||||
WithOsyield(runtime.Gosched).
|
||||
WithRandSource(rand.Reader)
|
||||
cfg := wazero.NewModuleConfig().WithStartFunctions("_initialize")
|
||||
|
||||
mod, err := sqlite3.runtime.InstantiateModule(ctx, sqlite3.compiled, cfg)
|
||||
if err != nil {
|
||||
@@ -155,7 +145,6 @@ func newModule(mod api.Module) (m *module, err error) {
|
||||
backupFinish: getFun("sqlite3_backup_finish"),
|
||||
backupRemaining: getFun("sqlite3_backup_remaining"),
|
||||
backupPageCount: getFun("sqlite3_backup_pagecount"),
|
||||
timeCollation: getFun("sqlite3_time_collation"),
|
||||
interrupt: getVal("sqlite3_interrupt_offset"),
|
||||
}
|
||||
if err != nil {
|
||||
@@ -349,6 +338,5 @@ type sqliteAPI struct {
|
||||
backupFinish api.Function
|
||||
backupRemaining api.Function
|
||||
backupPageCount api.Function
|
||||
timeCollation api.Function
|
||||
interrupt uint32
|
||||
}
|
||||
|
||||
@@ -3,11 +3,29 @@ set -eo pipefail
|
||||
|
||||
cd -P -- "$(dirname -- "$0")"
|
||||
|
||||
if [ ! -f "sqlite3.c" ]; then
|
||||
url="https://sqlite.org/2023/sqlite-amalgamation-3410100.zip"
|
||||
curl "$url" > sqlite.zip
|
||||
unzip -d . sqlite.zip
|
||||
mv sqlite-amalgamation-*/sqlite3* .
|
||||
rm -rf sqlite-amalgamation-*
|
||||
rm sqlite.zip
|
||||
fi
|
||||
curl -#OL "https://sqlite.org/2023/sqlite-amalgamation-3410200.zip"
|
||||
unzip -d . sqlite-amalgamation-*.zip
|
||||
mv sqlite-amalgamation-*/sqlite3* .
|
||||
rm -rf sqlite-amalgamation-*
|
||||
|
||||
cd ext/
|
||||
curl -#OL "https://github.com/sqlite/sqlite/raw/version-3.41.2/ext/misc/decimal.c"
|
||||
curl -#OL "https://github.com/sqlite/sqlite/raw/version-3.41.2/ext/misc/uint.c"
|
||||
curl -#OL "https://github.com/sqlite/sqlite/raw/version-3.41.2/ext/misc/uuid.c"
|
||||
curl -#OL "https://github.com/sqlite/sqlite/raw/version-3.41.2/ext/misc/base64.c"
|
||||
curl -#OL "https://github.com/sqlite/sqlite/raw/version-3.41.2/ext/misc/regexp.c"
|
||||
curl -#OL "https://github.com/sqlite/sqlite/raw/version-3.41.2/ext/misc/series.c"
|
||||
cd ~-
|
||||
|
||||
cd ../tests/mptest/testdata/
|
||||
curl -#OL "https://github.com/sqlite/sqlite/raw/version-3.41.2/mptest/mptest.c"
|
||||
curl -#OL "https://github.com/sqlite/sqlite/raw/version-3.41.2/mptest/config01.test"
|
||||
curl -#OL "https://github.com/sqlite/sqlite/raw/version-3.41.2/mptest/config02.test"
|
||||
curl -#OL "https://github.com/sqlite/sqlite/raw/version-3.41.2/mptest/crash01.test"
|
||||
curl -#OL "https://github.com/sqlite/sqlite/raw/version-3.41.2/mptest/crash02.subtest"
|
||||
curl -#OL "https://github.com/sqlite/sqlite/raw/version-3.41.2/mptest/multiwrite01.test"
|
||||
cd ~-
|
||||
|
||||
cd ../tests/speedtest1/testdata/
|
||||
curl -#OL "https://github.com/sqlite/sqlite/raw/version-3.41.2/test/speedtest1.c"
|
||||
cd ~-
|
||||
1
sqlite3/ext/.gitignore
vendored
Normal file
1
sqlite3/ext/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
*.c
|
||||
@@ -5,6 +5,13 @@
|
||||
//
|
||||
#include "os.c"
|
||||
#include "qsort.c"
|
||||
//
|
||||
#include "ext/base64.c"
|
||||
#include "ext/decimal.c"
|
||||
#include "ext/regexp.c"
|
||||
#include "ext/series.c"
|
||||
#include "ext/uint.c"
|
||||
#include "ext/uuid.c"
|
||||
#include "time.c"
|
||||
|
||||
sqlite3_destructor_type malloc_destructor = &free;
|
||||
@@ -14,7 +21,13 @@ int sqlite3_os_init() {
|
||||
return sqlite3_vfs_register(os_vfs(), /*default=*/true);
|
||||
}
|
||||
|
||||
int main() {
|
||||
int rc = sqlite3_initialize();
|
||||
if (rc != SQLITE_OK) return 1;
|
||||
__attribute__((constructor)) void premain() {
|
||||
sqlite3_initialize();
|
||||
sqlite3_auto_extension((void (*)(void))sqlite3_base_init);
|
||||
sqlite3_auto_extension((void (*)(void))sqlite3_decimal_init);
|
||||
sqlite3_auto_extension((void (*)(void))sqlite3_regexp_init);
|
||||
sqlite3_auto_extension((void (*)(void))sqlite3_series_init);
|
||||
sqlite3_auto_extension((void (*)(void))sqlite3_uint_init);
|
||||
sqlite3_auto_extension((void (*)(void))sqlite3_uuid_init);
|
||||
sqlite3_auto_extension((void (*)(void))sqlite3_time_init);
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
#include "sqlite3.h"
|
||||
|
||||
int os_localtime(sqlite3_int64, struct tm *);
|
||||
int os_localtime(struct tm *, sqlite3_int64);
|
||||
|
||||
int os_randomness(sqlite3_vfs *, int nByte, char *zOut);
|
||||
int os_sleep(sqlite3_vfs *, int microseconds);
|
||||
@@ -140,5 +140,5 @@ sqlite3_vfs *os_vfs() {
|
||||
}
|
||||
|
||||
int localtime_s(struct tm *const pTm, time_t const *const pTime) {
|
||||
return os_localtime((sqlite3_int64)*pTime, pTm);
|
||||
return os_localtime(pTm, (sqlite3_int64)*pTime);
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ static int time_collation(void *pArg, int nKey1, const void *pKey1, int nKey2,
|
||||
return rc;
|
||||
}
|
||||
|
||||
int sqlite3_time_collation(sqlite3 *db) {
|
||||
return sqlite3_create_collation(db, "TIME", SQLITE_UTF8, 0, time_collation);
|
||||
int sqlite3_time_init(sqlite3 *db, char **pzErrMsg,
|
||||
const sqlite3_api_routines *pApi) {
|
||||
return sqlite3_create_collation(db, "time", SQLITE_UTF8, 0, time_collation);
|
||||
}
|
||||
54
stmt.go
54
stmt.go
@@ -334,21 +334,7 @@ func (s *Stmt) ColumnTime(col int, format TimeFormat) time.Time {
|
||||
//
|
||||
// https://www.sqlite.org/c3ref/column_blob.html
|
||||
func (s *Stmt) ColumnText(col int) string {
|
||||
r := s.c.call(s.c.api.columnText,
|
||||
uint64(s.handle), uint64(col))
|
||||
|
||||
ptr := uint32(r[0])
|
||||
if ptr == 0 {
|
||||
r = s.c.call(s.c.api.errcode, uint64(s.c.handle))
|
||||
s.err = s.c.error(r[0])
|
||||
return ""
|
||||
}
|
||||
|
||||
r = s.c.call(s.c.api.columnBytes,
|
||||
uint64(s.handle), uint64(col))
|
||||
|
||||
mem := s.c.mem.view(ptr, r[0])
|
||||
return string(mem)
|
||||
return string(s.ColumnRawText(col))
|
||||
}
|
||||
|
||||
// ColumnBlob appends to buf and returns
|
||||
@@ -357,6 +343,39 @@ func (s *Stmt) ColumnText(col int) string {
|
||||
//
|
||||
// https://www.sqlite.org/c3ref/column_blob.html
|
||||
func (s *Stmt) ColumnBlob(col int, buf []byte) []byte {
|
||||
return append(buf, s.ColumnRawBlob(col)...)
|
||||
}
|
||||
|
||||
// ColumnRawText returns the value of the result column as a []byte.
|
||||
// The []byte is owned by SQLite and may be invalidated by
|
||||
// subsequent calls to [Stmt] methods.
|
||||
// The leftmost column of the result set has the index 0.
|
||||
//
|
||||
// https://www.sqlite.org/c3ref/column_blob.html
|
||||
func (s *Stmt) ColumnRawText(col int) []byte {
|
||||
r := s.c.call(s.c.api.columnText,
|
||||
uint64(s.handle), uint64(col))
|
||||
|
||||
ptr := uint32(r[0])
|
||||
if ptr == 0 {
|
||||
r = s.c.call(s.c.api.errcode, uint64(s.c.handle))
|
||||
s.err = s.c.error(r[0])
|
||||
return nil
|
||||
}
|
||||
|
||||
r = s.c.call(s.c.api.columnBytes,
|
||||
uint64(s.handle), uint64(col))
|
||||
|
||||
return s.c.mem.view(ptr, r[0])
|
||||
}
|
||||
|
||||
// ColumnRawBlob returns the value of the result column as a []byte.
|
||||
// The []byte is owned by SQLite and may be invalidated by
|
||||
// subsequent calls to [Stmt] methods.
|
||||
// The leftmost column of the result set has the index 0.
|
||||
//
|
||||
// https://www.sqlite.org/c3ref/column_blob.html
|
||||
func (s *Stmt) ColumnRawBlob(col int) []byte {
|
||||
r := s.c.call(s.c.api.columnBlob,
|
||||
uint64(s.handle), uint64(col))
|
||||
|
||||
@@ -364,14 +383,13 @@ func (s *Stmt) ColumnBlob(col int, buf []byte) []byte {
|
||||
if ptr == 0 {
|
||||
r = s.c.call(s.c.api.errcode, uint64(s.c.handle))
|
||||
s.err = s.c.error(r[0])
|
||||
return buf[0:0]
|
||||
return nil
|
||||
}
|
||||
|
||||
r = s.c.call(s.c.api.columnBytes,
|
||||
uint64(s.handle), uint64(col))
|
||||
|
||||
mem := s.c.mem.view(ptr, r[0])
|
||||
return append(buf[0:0], mem...)
|
||||
return s.c.mem.view(ptr, r[0])
|
||||
}
|
||||
|
||||
// Return true if stmt is an empty SQL statement.
|
||||
|
||||
@@ -126,7 +126,7 @@ func testTxQuery(t params) {
|
||||
if r.Err() != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Fatal("expected one rows")
|
||||
t.Fatal("expected one row")
|
||||
}
|
||||
|
||||
var name string
|
||||
|
||||
77
tests/ext_test.go
Normal file
77
tests/ext_test.go
Normal file
@@ -0,0 +1,77 @@
|
||||
package tests
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/ncruces/go-sqlite3"
|
||||
)
|
||||
|
||||
func Test_base64(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
db, err := sqlite3.Open(":memory:")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
// base64
|
||||
stmt, _, err := db.Prepare(`SELECT base64('TWFueSBoYW5kcyBtYWtlIGxpZ2h0IHdvcmsu')`)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
defer stmt.Close()
|
||||
|
||||
if !stmt.Step() {
|
||||
t.Fatal("expected one row")
|
||||
}
|
||||
if got := stmt.ColumnText(0); got != "Many hands make light work." {
|
||||
t.Errorf("got %q", got)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_decimal(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
db, err := sqlite3.Open(":memory:")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
stmt, _, err := db.Prepare(`SELECT decimal_add(decimal('0.1'), decimal('0.2')) = decimal('0.3')`)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
defer stmt.Close()
|
||||
|
||||
if !stmt.Step() {
|
||||
t.Fatal("expected one row")
|
||||
}
|
||||
if !stmt.ColumnBool(0) {
|
||||
t.Error("want true")
|
||||
}
|
||||
}
|
||||
|
||||
func Test_uint(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
db, err := sqlite3.Open(":memory:")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
stmt, _, err := db.Prepare(`SELECT 'z2' < 'z11' COLLATE UINT`)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
defer stmt.Close()
|
||||
|
||||
if !stmt.Step() {
|
||||
t.Fatal("expected one row")
|
||||
}
|
||||
if !stmt.ColumnBool(0) {
|
||||
t.Error("want true")
|
||||
}
|
||||
}
|
||||
20
tests/mptest/testdata/build.sh
vendored
20
tests/mptest/testdata/build.sh
vendored
@@ -3,18 +3,9 @@ set -eo pipefail
|
||||
|
||||
cd -P -- "$(dirname -- "$0")"
|
||||
|
||||
if [ ! -f "mptest.c" ]; then
|
||||
curl -sOL "https://github.com/sqlite/sqlite/raw/version-3.41.1/mptest/mptest.c"
|
||||
curl -sOL "https://github.com/sqlite/sqlite/raw/version-3.41.1/mptest/config01.test"
|
||||
curl -sOL "https://github.com/sqlite/sqlite/raw/version-3.41.1/mptest/config02.test"
|
||||
curl -sOL "https://github.com/sqlite/sqlite/raw/version-3.41.1/mptest/crash01.test"
|
||||
curl -sOL "https://github.com/sqlite/sqlite/raw/version-3.41.1/mptest/crash02.subtest"
|
||||
curl -sOL "https://github.com/sqlite/sqlite/raw/version-3.41.1/mptest/multiwrite01.test"
|
||||
fi
|
||||
|
||||
zig cc --target=wasm32-wasi -flto -g0 -Os \
|
||||
zig cc --target=wasm32-wasi -flto -g0 -O2 \
|
||||
-o mptest.wasm main.c \
|
||||
-I../../../sqlite3 \
|
||||
-I../../../sqlite3/ \
|
||||
-mmutable-globals \
|
||||
-mbulk-memory -mreference-types \
|
||||
-mnontrapping-fptoint -msign-ext \
|
||||
@@ -23,9 +14,4 @@ zig cc --target=wasm32-wasi -flto -g0 -Os \
|
||||
-DSQLITE_DEFAULT_LOCKING_MODE=0 \
|
||||
-DHAVE_USLEEP -DSQLITE_NO_SYNC \
|
||||
-DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION \
|
||||
-D_WASI_EMULATED_GETPID -lwasi-emulated-getpid
|
||||
|
||||
if which wasm-opt; then
|
||||
wasm-opt -g -O -o mptest.tmp mptest.wasm
|
||||
mv mptest.tmp mptest.wasm
|
||||
fi
|
||||
-D_WASI_EMULATED_GETPID -lwasi-emulated-getpid
|
||||
1
tests/mptest/testdata/main.c
vendored
1
tests/mptest/testdata/main.c
vendored
@@ -5,7 +5,6 @@
|
||||
//
|
||||
#include "os.c"
|
||||
#include "qsort.c"
|
||||
#include "time.c"
|
||||
|
||||
sqlite3_destructor_type malloc_destructor = &free;
|
||||
size_t sqlite3_interrupt_offset = offsetof(sqlite3, u1.isInterrupted);
|
||||
|
||||
4
tests/mptest/testdata/mptest.wasm
vendored
Normal file → Executable file
4
tests/mptest/testdata/mptest.wasm
vendored
Normal file → Executable file
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:af307c3555fcf5f78e07a079d8f099400c3e013508f30f5efbe3b7f259be2092
|
||||
size 969309
|
||||
oid sha256:f81ce390812d944d1fa9b2cc607a3629febab0bc0e4473dad3170134509c1751
|
||||
size 1630826
|
||||
|
||||
15
tests/speedtest1/testdata/build.sh
vendored
15
tests/speedtest1/testdata/build.sh
vendored
@@ -3,19 +3,10 @@ set -eo pipefail
|
||||
|
||||
cd -P -- "$(dirname -- "$0")"
|
||||
|
||||
if [ ! -f "mptest.c" ]; then
|
||||
curl -sOL "https://github.com/sqlite/sqlite/raw/version-3.41.1/test/speedtest1.c"
|
||||
fi
|
||||
|
||||
zig cc --target=wasm32-wasi -flto -g0 -Os \
|
||||
zig cc --target=wasm32-wasi -flto -g0 -O2 \
|
||||
-o speedtest1.wasm main.c \
|
||||
-I../../../sqlite3 \
|
||||
-I../../../sqlite3/ \
|
||||
-mmutable-globals \
|
||||
-mbulk-memory -mreference-types \
|
||||
-mnontrapping-fptoint -msign-ext \
|
||||
-D_HAVE_SQLITE_CONFIG_H
|
||||
|
||||
if which wasm-opt; then
|
||||
wasm-opt -g -O -o speedtest1.tmp speedtest1.wasm
|
||||
mv speedtest1.tmp speedtest1.wasm
|
||||
fi
|
||||
-D_HAVE_SQLITE_CONFIG_H
|
||||
3
tests/speedtest1/testdata/main.c
vendored
3
tests/speedtest1/testdata/main.c
vendored
@@ -5,7 +5,6 @@
|
||||
//
|
||||
#include "os.c"
|
||||
#include "qsort.c"
|
||||
#include "time.c"
|
||||
|
||||
sqlite3_destructor_type malloc_destructor = &free;
|
||||
size_t sqlite3_interrupt_offset = offsetof(sqlite3, u1.isInterrupted);
|
||||
@@ -14,7 +13,5 @@ int sqlite3_os_init() {
|
||||
return sqlite3_vfs_register(os_vfs(), /*default=*/true);
|
||||
}
|
||||
|
||||
__attribute__((constructor)) void premain() { sqlite3_initialize(); }
|
||||
|
||||
#define randomFunc(args...) randomFunc2(args)
|
||||
#include "speedtest1.c"
|
||||
|
||||
4
tests/speedtest1/testdata/speedtest1.wasm
vendored
Normal file → Executable file
4
tests/speedtest1/testdata/speedtest1.wasm
vendored
Normal file → Executable file
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:996c2445ad12c91dce1a59ab5929cfaa3a09a4ac82859fecc7de5e5f9c955f80
|
||||
size 1001607
|
||||
oid sha256:0e02a26b86832a4703cd2a86c98ff8041d9d889b865a61f324b512a15d2d361e
|
||||
size 1676129
|
||||
|
||||
136
vfs.go
136
vfs.go
@@ -14,19 +14,11 @@ import (
|
||||
"github.com/ncruces/julianday"
|
||||
"github.com/tetratelabs/wazero"
|
||||
"github.com/tetratelabs/wazero/api"
|
||||
"github.com/tetratelabs/wazero/sys"
|
||||
)
|
||||
|
||||
func vfsInstantiate(ctx context.Context, r wazero.Runtime) {
|
||||
wasi := r.NewHostModuleBuilder("wasi_snapshot_preview1")
|
||||
wasi.NewFunctionBuilder().WithFunc(vfsExit).Export("proc_exit")
|
||||
_, err := wasi.Instantiate(ctx)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
env := vfsNewEnvModuleBuilder(r)
|
||||
_, err = env.Instantiate(ctx)
|
||||
_, err := env.Instantiate(ctx)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -34,25 +26,25 @@ func vfsInstantiate(ctx context.Context, r wazero.Runtime) {
|
||||
|
||||
func vfsNewEnvModuleBuilder(r wazero.Runtime) wazero.HostModuleBuilder {
|
||||
env := r.NewHostModuleBuilder("env")
|
||||
env.NewFunctionBuilder().WithFunc(vfsLocaltime).Export("os_localtime")
|
||||
env.NewFunctionBuilder().WithFunc(vfsRandomness).Export("os_randomness")
|
||||
env.NewFunctionBuilder().WithFunc(vfsSleep).Export("os_sleep")
|
||||
env.NewFunctionBuilder().WithFunc(vfsCurrentTime).Export("os_current_time")
|
||||
env.NewFunctionBuilder().WithFunc(vfsCurrentTime64).Export("os_current_time_64")
|
||||
env.NewFunctionBuilder().WithFunc(vfsFullPathname).Export("os_full_pathname")
|
||||
env.NewFunctionBuilder().WithFunc(vfsDelete).Export("os_delete")
|
||||
env.NewFunctionBuilder().WithFunc(vfsAccess).Export("os_access")
|
||||
env.NewFunctionBuilder().WithFunc(vfsOpen).Export("os_open")
|
||||
env.NewFunctionBuilder().WithFunc(vfsClose).Export("os_close")
|
||||
env.NewFunctionBuilder().WithFunc(vfsRead).Export("os_read")
|
||||
env.NewFunctionBuilder().WithFunc(vfsWrite).Export("os_write")
|
||||
env.NewFunctionBuilder().WithFunc(vfsTruncate).Export("os_truncate")
|
||||
env.NewFunctionBuilder().WithFunc(vfsSync).Export("os_sync")
|
||||
env.NewFunctionBuilder().WithFunc(vfsFileSize).Export("os_file_size")
|
||||
env.NewFunctionBuilder().WithFunc(vfsLock).Export("os_lock")
|
||||
env.NewFunctionBuilder().WithFunc(vfsUnlock).Export("os_unlock")
|
||||
env.NewFunctionBuilder().WithFunc(vfsCheckReservedLock).Export("os_check_reserved_lock")
|
||||
env.NewFunctionBuilder().WithFunc(vfsFileControl).Export("os_file_control")
|
||||
vfsRegisterFuncT(env, "os_localtime", vfsLocaltime)
|
||||
vfsRegisterFunc3(env, "os_randomness", vfsRandomness)
|
||||
vfsRegisterFunc2(env, "os_sleep", vfsSleep)
|
||||
vfsRegisterFunc2(env, "os_current_time", vfsCurrentTime)
|
||||
vfsRegisterFunc2(env, "os_current_time_64", vfsCurrentTime64)
|
||||
vfsRegisterFunc4(env, "os_full_pathname", vfsFullPathname)
|
||||
vfsRegisterFunc3(env, "os_delete", vfsDelete)
|
||||
vfsRegisterFunc4(env, "os_access", vfsAccess)
|
||||
vfsRegisterFunc5(env, "os_open", vfsOpen)
|
||||
vfsRegisterFunc1(env, "os_close", vfsClose)
|
||||
vfsRegisterFuncRW(env, "os_read", vfsRead)
|
||||
vfsRegisterFuncRW(env, "os_write", vfsWrite)
|
||||
vfsRegisterFuncT(env, "os_truncate", vfsTruncate)
|
||||
vfsRegisterFunc2(env, "os_sync", vfsSync)
|
||||
vfsRegisterFunc2(env, "os_file_size", vfsFileSize)
|
||||
vfsRegisterFunc2(env, "os_lock", vfsLock)
|
||||
vfsRegisterFunc2(env, "os_unlock", vfsUnlock)
|
||||
vfsRegisterFunc2(env, "os_check_reserved_lock", vfsCheckReservedLock)
|
||||
vfsRegisterFunc3(env, "os_file_control", vfsFileControl)
|
||||
return env
|
||||
}
|
||||
|
||||
@@ -87,14 +79,7 @@ func (vfs *vfsState) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func vfsExit(ctx context.Context, mod api.Module, exitCode uint32) {
|
||||
// Ensure other callers see the exit code.
|
||||
_ = mod.CloseWithExitCode(ctx, exitCode)
|
||||
// Prevent any code from executing after this function.
|
||||
panic(sys.NewExitError(mod.Name(), exitCode))
|
||||
}
|
||||
|
||||
func vfsLocaltime(ctx context.Context, mod api.Module, t uint64, pTm uint32) uint32 {
|
||||
func vfsLocaltime(ctx context.Context, mod api.Module, pTm uint32, t uint64) uint32 {
|
||||
tm := time.Unix(int64(t), 0)
|
||||
var isdst int
|
||||
if tm.IsDST() {
|
||||
@@ -121,7 +106,7 @@ func vfsRandomness(ctx context.Context, mod api.Module, pVfs, nByte, zByte uint3
|
||||
return uint32(n)
|
||||
}
|
||||
|
||||
func vfsSleep(ctx context.Context, pVfs, nMicro uint32) uint32 {
|
||||
func vfsSleep(ctx context.Context, mod api.Module, pVfs, nMicro uint32) uint32 {
|
||||
time.Sleep(time.Duration(nMicro) * time.Microsecond)
|
||||
return _OK
|
||||
}
|
||||
@@ -146,10 +131,6 @@ func vfsFullPathname(ctx context.Context, mod api.Module, pVfs, zRelative, nFull
|
||||
return uint32(CANTOPEN_FULLPATH)
|
||||
}
|
||||
|
||||
// Consider either using [filepath.EvalSymlinks] to canonicalize the path (as the Unix VFS does).
|
||||
// Or using [os.Readlink] to resolve a symbolic link (as the Unix VFS did).
|
||||
// This might be buggy on Windows (the Windows VFS doesn't try).
|
||||
|
||||
size := uint64(len(abs) + 1)
|
||||
if size > uint64(nFull) {
|
||||
return uint32(CANTOPEN_FULLPATH)
|
||||
@@ -342,9 +323,6 @@ func vfsSizeHint(ctx context.Context, mod api.Module, pFile, pArg uint32) uint32
|
||||
file := vfsFile.GetOS(ctx, mod, pFile)
|
||||
size := memory{mod}.readUint64(pArg)
|
||||
err := vfsOS.Allocate(file, int64(size))
|
||||
if err == notImplErr {
|
||||
return uint32(NOTFOUND)
|
||||
}
|
||||
if err != nil {
|
||||
return uint32(IOERR_TRUNCATE)
|
||||
}
|
||||
@@ -368,3 +346,73 @@ func vfsFileMoved(ctx context.Context, mod api.Module, pFile, pResOut uint32) ui
|
||||
memory{mod}.writeUint32(pResOut, res)
|
||||
return _OK
|
||||
}
|
||||
|
||||
func vfsRegisterFunc1(mod wazero.HostModuleBuilder, name string, fn func(ctx context.Context, mod api.Module, _ uint32) uint32) {
|
||||
mod.NewFunctionBuilder().
|
||||
WithGoModuleFunction(api.GoModuleFunc(
|
||||
func(ctx context.Context, mod api.Module, stack []uint64) {
|
||||
stack[0] = uint64(fn(ctx, mod, uint32(stack[0])))
|
||||
}),
|
||||
[]api.ValueType{api.ValueTypeI32}, []api.ValueType{api.ValueTypeI32}).
|
||||
Export(name)
|
||||
}
|
||||
|
||||
func vfsRegisterFunc2[T0, T1 ~uint32](mod wazero.HostModuleBuilder, name string, fn func(ctx context.Context, mod api.Module, _ T0, _ T1) uint32) {
|
||||
mod.NewFunctionBuilder().
|
||||
WithGoModuleFunction(api.GoModuleFunc(
|
||||
func(ctx context.Context, mod api.Module, stack []uint64) {
|
||||
stack[0] = uint64(fn(ctx, mod, T0(stack[0]), T1(stack[1])))
|
||||
}),
|
||||
[]api.ValueType{api.ValueTypeI32, api.ValueTypeI32}, []api.ValueType{api.ValueTypeI32}).
|
||||
Export(name)
|
||||
}
|
||||
|
||||
func vfsRegisterFunc3[T0, T1, T2 ~uint32](mod wazero.HostModuleBuilder, name string, fn func(ctx context.Context, mod api.Module, _ T0, _ T1, _ T2) uint32) {
|
||||
mod.NewFunctionBuilder().
|
||||
WithGoModuleFunction(api.GoModuleFunc(
|
||||
func(ctx context.Context, mod api.Module, stack []uint64) {
|
||||
stack[0] = uint64(fn(ctx, mod, T0(stack[0]), T1(stack[1]), T2(stack[2])))
|
||||
}),
|
||||
[]api.ValueType{api.ValueTypeI32, api.ValueTypeI32, api.ValueTypeI32}, []api.ValueType{api.ValueTypeI32}).
|
||||
Export(name)
|
||||
}
|
||||
|
||||
func vfsRegisterFunc4[T0, T1, T2, T3 ~uint32](mod wazero.HostModuleBuilder, name string, fn func(ctx context.Context, mod api.Module, _ T0, _ T1, _ T2, _ T3) uint32) {
|
||||
mod.NewFunctionBuilder().
|
||||
WithGoModuleFunction(api.GoModuleFunc(
|
||||
func(ctx context.Context, mod api.Module, stack []uint64) {
|
||||
stack[0] = uint64(fn(ctx, mod, T0(stack[0]), T1(stack[1]), T2(stack[2]), T3(stack[3])))
|
||||
}),
|
||||
[]api.ValueType{api.ValueTypeI32, api.ValueTypeI32, api.ValueTypeI32, api.ValueTypeI32}, []api.ValueType{api.ValueTypeI32}).
|
||||
Export(name)
|
||||
}
|
||||
|
||||
func vfsRegisterFunc5[T0, T1, T2, T3, T4 ~uint32](mod wazero.HostModuleBuilder, name string, fn func(ctx context.Context, mod api.Module, _ T0, _ T1, _ T2, _ T3, _ T4) uint32) {
|
||||
mod.NewFunctionBuilder().
|
||||
WithGoModuleFunction(api.GoModuleFunc(
|
||||
func(ctx context.Context, mod api.Module, stack []uint64) {
|
||||
stack[0] = uint64(fn(ctx, mod, T0(stack[0]), T1(stack[1]), T2(stack[2]), T3(stack[3]), T4(stack[4])))
|
||||
}),
|
||||
[]api.ValueType{api.ValueTypeI32, api.ValueTypeI32, api.ValueTypeI32, api.ValueTypeI32, api.ValueTypeI32}, []api.ValueType{api.ValueTypeI32}).
|
||||
Export(name)
|
||||
}
|
||||
|
||||
func vfsRegisterFuncRW(mod wazero.HostModuleBuilder, name string, fn func(ctx context.Context, mod api.Module, _, _, _ uint32, _ uint64) uint32) {
|
||||
mod.NewFunctionBuilder().
|
||||
WithGoModuleFunction(api.GoModuleFunc(
|
||||
func(ctx context.Context, mod api.Module, stack []uint64) {
|
||||
stack[0] = uint64(fn(ctx, mod, uint32(stack[0]), uint32(stack[1]), uint32(stack[2]), stack[3]))
|
||||
}),
|
||||
[]api.ValueType{api.ValueTypeI32, api.ValueTypeI32, api.ValueTypeI32, api.ValueTypeI64}, []api.ValueType{api.ValueTypeI32}).
|
||||
Export(name)
|
||||
}
|
||||
|
||||
func vfsRegisterFuncT(mod wazero.HostModuleBuilder, name string, fn func(ctx context.Context, mod api.Module, _ uint32, _ uint64) uint32) {
|
||||
mod.NewFunctionBuilder().
|
||||
WithGoModuleFunction(api.GoModuleFunc(
|
||||
func(ctx context.Context, mod api.Module, stack []uint64) {
|
||||
stack[0] = uint64(fn(ctx, mod, uint32(stack[0]), stack[1]))
|
||||
}),
|
||||
[]api.ValueType{api.ValueTypeI32, api.ValueTypeI64}, []api.ValueType{api.ValueTypeI32}).
|
||||
Export(name)
|
||||
}
|
||||
|
||||
29
vfs_lock.go
29
vfs_lock.go
@@ -89,10 +89,6 @@ func vfsLock(ctx context.Context, mod api.Module, pFile uint32, eLock vfsLockSta
|
||||
if cLock != _NO_LOCK {
|
||||
panic(assertErr())
|
||||
}
|
||||
// Test the PENDING lock before acquiring a new SHARED lock.
|
||||
if locked, _ := vfsOS.CheckPendingLock(file); locked {
|
||||
return uint32(BUSY)
|
||||
}
|
||||
if rc := vfsOS.GetSharedLock(file, timeout); rc != _OK {
|
||||
return uint32(rc)
|
||||
}
|
||||
@@ -172,8 +168,21 @@ func vfsUnlock(ctx context.Context, mod api.Module, pFile uint32, eLock vfsLockS
|
||||
|
||||
func vfsCheckReservedLock(ctx context.Context, mod api.Module, pFile, pResOut uint32) uint32 {
|
||||
file := vfsFile.GetOS(ctx, mod, pFile)
|
||||
cLock := vfsFile.GetLock(ctx, mod, pFile)
|
||||
|
||||
// Connection state check.
|
||||
if cLock < _NO_LOCK || cLock > _EXCLUSIVE_LOCK {
|
||||
panic(assertErr())
|
||||
}
|
||||
|
||||
var locked bool
|
||||
var rc xErrorCode
|
||||
if cLock >= _RESERVED_LOCK {
|
||||
locked = true
|
||||
} else {
|
||||
locked, rc = vfsOS.CheckReservedLock(file)
|
||||
}
|
||||
|
||||
locked, rc := vfsOS.CheckReservedLock(file)
|
||||
var res uint32
|
||||
if locked {
|
||||
res = 1
|
||||
@@ -182,11 +191,6 @@ func vfsCheckReservedLock(ctx context.Context, mod api.Module, pFile, pResOut ui
|
||||
return uint32(rc)
|
||||
}
|
||||
|
||||
func (vfsOSMethods) GetSharedLock(file *os.File, timeout time.Duration) xErrorCode {
|
||||
// Acquire the SHARED lock.
|
||||
return vfsOS.readLock(file, _SHARED_FIRST, _SHARED_SIZE, timeout)
|
||||
}
|
||||
|
||||
func (vfsOSMethods) GetReservedLock(file *os.File, timeout time.Duration) xErrorCode {
|
||||
// Acquire the RESERVED lock.
|
||||
return vfsOS.writeLock(file, _RESERVED_BYTE, 1, timeout)
|
||||
@@ -201,8 +205,3 @@ func (vfsOSMethods) CheckReservedLock(file *os.File) (bool, xErrorCode) {
|
||||
// Test the RESERVED lock.
|
||||
return vfsOS.checkLock(file, _RESERVED_BYTE, 1)
|
||||
}
|
||||
|
||||
func (vfsOSMethods) CheckPendingLock(file *os.File) (bool, xErrorCode) {
|
||||
// Test the PENDING lock.
|
||||
return vfsOS.checkLock(file, _PENDING_BYTE, 1)
|
||||
}
|
||||
|
||||
@@ -51,6 +51,13 @@ func Test_vfsLock(t *testing.T) {
|
||||
if got := mem.readUint32(pOutput); got != 0 {
|
||||
t.Error("file was locked")
|
||||
}
|
||||
rc = vfsCheckReservedLock(ctx, mem.mod, pFile2, pOutput)
|
||||
if rc != _OK {
|
||||
t.Fatal("returned", rc)
|
||||
}
|
||||
if got := mem.readUint32(pOutput); got != 0 {
|
||||
t.Error("file was locked")
|
||||
}
|
||||
|
||||
rc = vfsLock(ctx, mem.mod, pFile2, _SHARED_LOCK)
|
||||
if rc != _OK {
|
||||
@@ -64,6 +71,13 @@ func Test_vfsLock(t *testing.T) {
|
||||
if got := mem.readUint32(pOutput); got != 0 {
|
||||
t.Error("file was locked")
|
||||
}
|
||||
rc = vfsCheckReservedLock(ctx, mem.mod, pFile2, pOutput)
|
||||
if rc != _OK {
|
||||
t.Fatal("returned", rc)
|
||||
}
|
||||
if got := mem.readUint32(pOutput); got != 0 {
|
||||
t.Error("file was locked")
|
||||
}
|
||||
|
||||
rc = vfsLock(ctx, mem.mod, pFile2, _RESERVED_LOCK)
|
||||
if rc != _OK {
|
||||
@@ -81,6 +95,13 @@ func Test_vfsLock(t *testing.T) {
|
||||
if got := mem.readUint32(pOutput); got == 0 {
|
||||
t.Error("file wasn't locked")
|
||||
}
|
||||
rc = vfsCheckReservedLock(ctx, mem.mod, pFile2, pOutput)
|
||||
if rc != _OK {
|
||||
t.Fatal("returned", rc)
|
||||
}
|
||||
if got := mem.readUint32(pOutput); got == 0 {
|
||||
t.Error("file wasn't locked")
|
||||
}
|
||||
|
||||
rc = vfsLock(ctx, mem.mod, pFile2, _EXCLUSIVE_LOCK)
|
||||
if rc != _OK {
|
||||
@@ -94,6 +115,13 @@ func Test_vfsLock(t *testing.T) {
|
||||
if got := mem.readUint32(pOutput); got == 0 {
|
||||
t.Error("file wasn't locked")
|
||||
}
|
||||
rc = vfsCheckReservedLock(ctx, mem.mod, pFile2, pOutput)
|
||||
if rc != _OK {
|
||||
t.Fatal("returned", rc)
|
||||
}
|
||||
if got := mem.readUint32(pOutput); got == 0 {
|
||||
t.Error("file wasn't locked")
|
||||
}
|
||||
|
||||
rc = vfsLock(ctx, mem.mod, pFile1, _SHARED_LOCK)
|
||||
if rc == _OK {
|
||||
@@ -107,6 +135,13 @@ func Test_vfsLock(t *testing.T) {
|
||||
if got := mem.readUint32(pOutput); got == 0 {
|
||||
t.Error("file wasn't locked")
|
||||
}
|
||||
rc = vfsCheckReservedLock(ctx, mem.mod, pFile2, pOutput)
|
||||
if rc != _OK {
|
||||
t.Fatal("returned", rc)
|
||||
}
|
||||
if got := mem.readUint32(pOutput); got == 0 {
|
||||
t.Error("file wasn't locked")
|
||||
}
|
||||
|
||||
rc = vfsUnlock(ctx, mem.mod, pFile2, _SHARED_LOCK)
|
||||
if rc != _OK {
|
||||
@@ -120,6 +155,13 @@ func Test_vfsLock(t *testing.T) {
|
||||
if got := mem.readUint32(pOutput); got != 0 {
|
||||
t.Error("file was locked")
|
||||
}
|
||||
rc = vfsCheckReservedLock(ctx, mem.mod, pFile2, pOutput)
|
||||
if rc != _OK {
|
||||
t.Fatal("returned", rc)
|
||||
}
|
||||
if got := mem.readUint32(pOutput); got != 0 {
|
||||
t.Error("file was locked")
|
||||
}
|
||||
|
||||
rc = vfsLock(ctx, mem.mod, pFile1, _SHARED_LOCK)
|
||||
if rc != _OK {
|
||||
|
||||
56
vfs_os_bsd.go
Normal file
56
vfs_os_bsd.go
Normal file
@@ -0,0 +1,56 @@
|
||||
//go:build freebsd || openbsd || netbsd || dragonfly || (darwin && sqlite3_bsd)
|
||||
|
||||
package sqlite3
|
||||
|
||||
import (
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func (vfsOSMethods) unlock(file *os.File, start, len int64) xErrorCode {
|
||||
if start == 0 && len == 0 {
|
||||
err := unix.Flock(int(file.Fd()), unix.LOCK_UN)
|
||||
if err != nil {
|
||||
return IOERR_UNLOCK
|
||||
}
|
||||
}
|
||||
return _OK
|
||||
}
|
||||
|
||||
func (vfsOSMethods) lock(file *os.File, how int, timeout time.Duration, def xErrorCode) xErrorCode {
|
||||
var err error
|
||||
for {
|
||||
err = unix.Flock(int(file.Fd()), how)
|
||||
if errno, _ := err.(unix.Errno); errno != unix.EAGAIN {
|
||||
break
|
||||
}
|
||||
if timeout < time.Millisecond {
|
||||
break
|
||||
}
|
||||
timeout -= time.Millisecond
|
||||
time.Sleep(time.Millisecond)
|
||||
}
|
||||
return vfsOS.lockErrorCode(err, def)
|
||||
}
|
||||
|
||||
func (vfsOSMethods) readLock(file *os.File, start, len int64, timeout time.Duration) xErrorCode {
|
||||
return vfsOS.lock(file, unix.LOCK_SH|unix.LOCK_NB, timeout, IOERR_RDLOCK)
|
||||
}
|
||||
|
||||
func (vfsOSMethods) writeLock(file *os.File, start, len int64, timeout time.Duration) xErrorCode {
|
||||
return vfsOS.lock(file, unix.LOCK_EX|unix.LOCK_NB, timeout, IOERR_LOCK)
|
||||
}
|
||||
|
||||
func (vfsOSMethods) checkLock(file *os.File, start, len int64) (bool, xErrorCode) {
|
||||
lock := unix.Flock_t{
|
||||
Type: unix.F_RDLCK,
|
||||
Start: start,
|
||||
Len: len,
|
||||
}
|
||||
if unix.FcntlFlock(file.Fd(), unix.F_GETLK, &lock) != nil {
|
||||
return false, IOERR_CHECKRESERVEDLOCK
|
||||
}
|
||||
return lock.Type != unix.F_UNLCK, _OK
|
||||
}
|
||||
@@ -1,20 +1,44 @@
|
||||
//go:build !sqlite3_bsd
|
||||
|
||||
package sqlite3
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
const (
|
||||
// https://github.com/apple/darwin-xnu/blob/main/bsd/sys/fcntl.h
|
||||
_F_OFD_SETLK = 90
|
||||
_F_OFD_SETLKW = 91
|
||||
_F_OFD_GETLK = 92
|
||||
_F_OFD_SETLKWTIMEOUT = 93
|
||||
)
|
||||
|
||||
type flocktimeout_t struct {
|
||||
fl unix.Flock_t
|
||||
timeout unix.Timespec
|
||||
}
|
||||
|
||||
func (vfsOSMethods) Sync(file *os.File, fullsync, dataonly bool) error {
|
||||
if !fullsync {
|
||||
return unix.Fsync(int(file.Fd()))
|
||||
if fullsync {
|
||||
return file.Sync()
|
||||
}
|
||||
return file.Sync()
|
||||
return unix.Fsync(int(file.Fd()))
|
||||
}
|
||||
|
||||
func (vfsOSMethods) Allocate(file *os.File, size int64) error {
|
||||
off, err := file.Seek(0, io.SeekEnd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if size <= off {
|
||||
return nil
|
||||
}
|
||||
|
||||
// https://stackoverflow.com/a/11497568/867786
|
||||
store := unix.Fstore_t{
|
||||
Flags: unix.F_ALLOCATECONTIG,
|
||||
@@ -24,37 +48,59 @@ func (vfsOSMethods) Allocate(file *os.File, size int64) error {
|
||||
}
|
||||
|
||||
// Try to get a continous chunk of disk space.
|
||||
err := unix.FcntlFstore(file.Fd(), unix.F_PREALLOCATE, &store)
|
||||
err = unix.FcntlFstore(file.Fd(), unix.F_PREALLOCATE, &store)
|
||||
if err != nil {
|
||||
// OK, perhaps we are too fragmented, allocate non-continuous.
|
||||
store.Flags = unix.F_ALLOCATEALL
|
||||
return unix.FcntlFstore(file.Fd(), unix.F_PREALLOCATE, &store)
|
||||
unix.FcntlFstore(file.Fd(), unix.F_PREALLOCATE, &store)
|
||||
}
|
||||
return nil
|
||||
return file.Truncate(size)
|
||||
}
|
||||
|
||||
func (vfsOSMethods) fcntlGetLock(file *os.File, lock *unix.Flock_t) error {
|
||||
const F_OFD_GETLK = 92 // https://github.com/apple/darwin-xnu/blob/main/bsd/sys/fcntl.h
|
||||
return unix.FcntlFlock(file.Fd(), F_OFD_GETLK, lock)
|
||||
func (vfsOSMethods) unlock(file *os.File, start, len int64) xErrorCode {
|
||||
err := unix.FcntlFlock(file.Fd(), _F_OFD_SETLK, &unix.Flock_t{
|
||||
Type: unix.F_UNLCK,
|
||||
Start: start,
|
||||
Len: len,
|
||||
})
|
||||
if err != nil {
|
||||
return IOERR_UNLOCK
|
||||
}
|
||||
return _OK
|
||||
}
|
||||
|
||||
func (vfsOSMethods) fcntlSetLock(file *os.File, lock unix.Flock_t) error {
|
||||
const F_OFD_SETLK = 90 // https://github.com/apple/darwin-xnu/blob/main/bsd/sys/fcntl.h
|
||||
return unix.FcntlFlock(file.Fd(), F_OFD_SETLK, &lock)
|
||||
}
|
||||
|
||||
func (vfsOSMethods) fcntlSetLockTimeout(file *os.File, lock unix.Flock_t, timeout time.Duration) error {
|
||||
func (vfsOSMethods) lock(file *os.File, typ int16, start, len int64, timeout time.Duration, def xErrorCode) xErrorCode {
|
||||
lock := flocktimeout_t{fl: unix.Flock_t{
|
||||
Type: typ,
|
||||
Start: start,
|
||||
Len: len,
|
||||
}}
|
||||
var err error
|
||||
if timeout == 0 {
|
||||
return vfsOS.fcntlSetLock(file, lock)
|
||||
err = unix.FcntlFlock(file.Fd(), _F_OFD_SETLK, &lock.fl)
|
||||
} else {
|
||||
lock.timeout = unix.NsecToTimespec(int64(timeout / time.Nanosecond))
|
||||
err = unix.FcntlFlock(file.Fd(), _F_OFD_SETLKWTIMEOUT, &lock.fl)
|
||||
}
|
||||
|
||||
const F_OFD_SETLKWTIMEOUT = 93 // https://github.com/apple/darwin-xnu/blob/main/bsd/sys/fcntl.h
|
||||
flocktimeout := &struct {
|
||||
unix.Flock_t
|
||||
unix.Timespec
|
||||
}{
|
||||
Flock_t: lock,
|
||||
Timespec: unix.NsecToTimespec(int64(timeout / time.Nanosecond)),
|
||||
}
|
||||
return unix.FcntlFlock(file.Fd(), F_OFD_SETLKWTIMEOUT, &flocktimeout.Flock_t)
|
||||
return vfsOS.lockErrorCode(err, def)
|
||||
}
|
||||
|
||||
func (vfsOSMethods) readLock(file *os.File, start, len int64, timeout time.Duration) xErrorCode {
|
||||
return vfsOS.lock(file, unix.F_RDLCK, start, len, timeout, IOERR_RDLOCK)
|
||||
}
|
||||
|
||||
func (vfsOSMethods) writeLock(file *os.File, start, len int64, timeout time.Duration) xErrorCode {
|
||||
return vfsOS.lock(file, unix.F_WRLCK, start, len, timeout, IOERR_LOCK)
|
||||
}
|
||||
|
||||
func (vfsOSMethods) checkLock(file *os.File, start, len int64) (bool, xErrorCode) {
|
||||
lock := unix.Flock_t{
|
||||
Type: unix.F_RDLCK,
|
||||
Start: start,
|
||||
Len: len,
|
||||
}
|
||||
if unix.FcntlFlock(file.Fd(), _F_OFD_GETLK, &lock) != nil {
|
||||
return false, IOERR_CHECKRESERVEDLOCK
|
||||
}
|
||||
return lock.Type != unix.F_UNLCK, _OK
|
||||
}
|
||||
|
||||
@@ -2,14 +2,12 @@ package sqlite3
|
||||
|
||||
import (
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func (vfsOSMethods) Sync(file *os.File, fullsync, dataonly bool) error {
|
||||
if dataonly {
|
||||
//lint:ignore SA1019 OK on linux
|
||||
_, _, err := unix.Syscall(unix.SYS_FDATASYNC, file.Fd(), 0, 0)
|
||||
if err != 0 {
|
||||
return err
|
||||
@@ -25,25 +23,3 @@ func (vfsOSMethods) Allocate(file *os.File, size int64) error {
|
||||
}
|
||||
return unix.Fallocate(int(file.Fd()), 0, 0, size)
|
||||
}
|
||||
|
||||
func (vfsOSMethods) fcntlGetLock(file *os.File, lock *unix.Flock_t) error {
|
||||
return unix.FcntlFlock(file.Fd(), unix.F_OFD_GETLK, lock)
|
||||
}
|
||||
|
||||
func (vfsOSMethods) fcntlSetLock(file *os.File, lock unix.Flock_t) error {
|
||||
return unix.FcntlFlock(file.Fd(), unix.F_OFD_SETLK, &lock)
|
||||
}
|
||||
|
||||
func (vfsOSMethods) fcntlSetLockTimeout(file *os.File, lock unix.Flock_t, timeout time.Duration) error {
|
||||
for {
|
||||
err := unix.FcntlFlock(file.Fd(), unix.F_OFD_SETLK, &lock)
|
||||
if errno, _ := err.(unix.Errno); errno != unix.EAGAIN {
|
||||
return err
|
||||
}
|
||||
if timeout < time.Millisecond {
|
||||
return err
|
||||
}
|
||||
timeout -= time.Millisecond
|
||||
time.Sleep(time.Millisecond)
|
||||
}
|
||||
}
|
||||
|
||||
63
vfs_os_ofd.go
Normal file
63
vfs_os_ofd.go
Normal file
@@ -0,0 +1,63 @@
|
||||
//go:build linux || illumos
|
||||
|
||||
package sqlite3
|
||||
|
||||
import (
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func (vfsOSMethods) unlock(file *os.File, start, len int64) xErrorCode {
|
||||
err := unix.FcntlFlock(file.Fd(), unix.F_OFD_SETLK, &unix.Flock_t{
|
||||
Type: unix.F_UNLCK,
|
||||
Start: start,
|
||||
Len: len,
|
||||
})
|
||||
if err != nil {
|
||||
return IOERR_UNLOCK
|
||||
}
|
||||
return _OK
|
||||
}
|
||||
|
||||
func (vfsOSMethods) lock(file *os.File, typ int16, start, len int64, timeout time.Duration, def xErrorCode) xErrorCode {
|
||||
lock := unix.Flock_t{
|
||||
Type: typ,
|
||||
Start: start,
|
||||
Len: len,
|
||||
}
|
||||
var err error
|
||||
for {
|
||||
err = unix.FcntlFlock(file.Fd(), unix.F_OFD_SETLK, &lock)
|
||||
if errno, _ := err.(unix.Errno); errno != unix.EAGAIN {
|
||||
break
|
||||
}
|
||||
if timeout < time.Millisecond {
|
||||
break
|
||||
}
|
||||
timeout -= time.Millisecond
|
||||
time.Sleep(time.Millisecond)
|
||||
}
|
||||
return vfsOS.lockErrorCode(err, def)
|
||||
}
|
||||
|
||||
func (vfsOSMethods) readLock(file *os.File, start, len int64, timeout time.Duration) xErrorCode {
|
||||
return vfsOS.lock(file, unix.F_RDLCK, start, len, timeout, IOERR_RDLOCK)
|
||||
}
|
||||
|
||||
func (vfsOSMethods) writeLock(file *os.File, start, len int64, timeout time.Duration) xErrorCode {
|
||||
return vfsOS.lock(file, unix.F_WRLCK, start, len, timeout, IOERR_LOCK)
|
||||
}
|
||||
|
||||
func (vfsOSMethods) checkLock(file *os.File, start, len int64) (bool, xErrorCode) {
|
||||
lock := unix.Flock_t{
|
||||
Type: unix.F_RDLCK,
|
||||
Start: start,
|
||||
Len: len,
|
||||
}
|
||||
if unix.FcntlFlock(file.Fd(), unix.F_OFD_GETLK, &lock) != nil {
|
||||
return false, IOERR_CHECKRESERVEDLOCK
|
||||
}
|
||||
return lock.Type != unix.F_UNLCK, _OK
|
||||
}
|
||||
23
vfs_os_other.go
Normal file
23
vfs_os_other.go
Normal file
@@ -0,0 +1,23 @@
|
||||
//go:build !linux && (!darwin || sqlite3_bsd)
|
||||
|
||||
package sqlite3
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
)
|
||||
|
||||
func (vfsOSMethods) Sync(file *os.File, fullsync, dataonly bool) error {
|
||||
return file.Sync()
|
||||
}
|
||||
|
||||
func (vfsOSMethods) Allocate(file *os.File, size int64) error {
|
||||
off, err := file.Seek(0, io.SeekEnd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if size <= off {
|
||||
return nil
|
||||
}
|
||||
return file.Truncate(size)
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
//go:build !windows && !linux && !darwin
|
||||
|
||||
package sqlite3
|
||||
|
||||
import (
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func (vfsOSMethods) Sync(file *os.File, fullsync, dataonly bool) error {
|
||||
return file.Sync()
|
||||
}
|
||||
|
||||
func (vfsOSMethods) Allocate(file *os.File, size int64) error {
|
||||
return notImplErr
|
||||
}
|
||||
|
||||
func (vfsOSMethods) fcntlGetLock(file *os.File, lock *unix.Flock_t) error {
|
||||
return notImplErr
|
||||
}
|
||||
|
||||
func (vfsOSMethods) fcntlSetLock(file *os.File, lock unix.Flock_t) error {
|
||||
return notImplErr
|
||||
}
|
||||
|
||||
func (vfsOSMethods) fcntlSetLockTimeout(file *os.File, lock unix.Flock_t, timeout time.Duration) error {
|
||||
return notImplErr
|
||||
}
|
||||
@@ -15,7 +15,7 @@ func (vfsOSMethods) OpenFile(name string, flag int, perm fs.FileMode) (*os.File,
|
||||
}
|
||||
|
||||
func (vfsOSMethods) Access(path string, flags _AccessFlag) error {
|
||||
var access uint32 = unix.F_OK
|
||||
var access uint32 // unix.F_OK
|
||||
switch flags {
|
||||
case _ACCESS_READWRITE:
|
||||
access = unix.R_OK | unix.W_OK
|
||||
@@ -25,6 +25,15 @@ func (vfsOSMethods) Access(path string, flags _AccessFlag) error {
|
||||
return unix.Access(path, access)
|
||||
}
|
||||
|
||||
func (vfsOSMethods) GetSharedLock(file *os.File, timeout time.Duration) xErrorCode {
|
||||
// Test the PENDING lock before acquiring a new SHARED lock.
|
||||
if pending, _ := vfsOS.checkLock(file, _PENDING_BYTE, 1); pending {
|
||||
return xErrorCode(BUSY)
|
||||
}
|
||||
// Acquire the SHARED lock.
|
||||
return vfsOS.readLock(file, _SHARED_FIRST, _SHARED_SIZE, timeout)
|
||||
}
|
||||
|
||||
func (vfsOSMethods) GetExclusiveLock(file *os.File, timeout time.Duration) xErrorCode {
|
||||
if timeout == 0 {
|
||||
timeout = time.Millisecond
|
||||
@@ -55,47 +64,6 @@ func (vfsOSMethods) ReleaseLock(file *os.File, _ vfsLockState) xErrorCode {
|
||||
return vfsOS.unlock(file, 0, 0)
|
||||
}
|
||||
|
||||
func (vfsOSMethods) unlock(file *os.File, start, len int64) xErrorCode {
|
||||
err := vfsOS.fcntlSetLock(file, unix.Flock_t{
|
||||
Type: unix.F_UNLCK,
|
||||
Start: start,
|
||||
Len: len,
|
||||
})
|
||||
if err != nil {
|
||||
return IOERR_UNLOCK
|
||||
}
|
||||
return _OK
|
||||
}
|
||||
|
||||
func (vfsOSMethods) readLock(file *os.File, start, len int64, timeout time.Duration) xErrorCode {
|
||||
return vfsOS.lockErrorCode(vfsOS.fcntlSetLockTimeout(file, unix.Flock_t{
|
||||
Type: unix.F_RDLCK,
|
||||
Start: start,
|
||||
Len: len,
|
||||
}, timeout), IOERR_RDLOCK)
|
||||
}
|
||||
|
||||
func (vfsOSMethods) writeLock(file *os.File, start, len int64, timeout time.Duration) xErrorCode {
|
||||
// TODO: implement timeouts.
|
||||
return vfsOS.lockErrorCode(vfsOS.fcntlSetLockTimeout(file, unix.Flock_t{
|
||||
Type: unix.F_WRLCK,
|
||||
Start: start,
|
||||
Len: len,
|
||||
}, timeout), IOERR_LOCK)
|
||||
}
|
||||
|
||||
func (vfsOSMethods) checkLock(file *os.File, start, len int64) (bool, xErrorCode) {
|
||||
lock := unix.Flock_t{
|
||||
Type: unix.F_RDLCK,
|
||||
Start: start,
|
||||
Len: len,
|
||||
}
|
||||
if vfsOS.fcntlGetLock(file, &lock) != nil {
|
||||
return false, IOERR_CHECKRESERVEDLOCK
|
||||
}
|
||||
return lock.Type != unix.F_UNLCK, _OK
|
||||
}
|
||||
|
||||
func (vfsOSMethods) lockErrorCode(err error, def xErrorCode) xErrorCode {
|
||||
if err == nil {
|
||||
return _OK
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package sqlite3
|
||||
|
||||
import (
|
||||
"io"
|
||||
"io/fs"
|
||||
"os"
|
||||
"syscall"
|
||||
@@ -48,19 +47,18 @@ func (vfsOSMethods) Access(path string, flags _AccessFlag) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (vfsOSMethods) Sync(file *os.File, fullsync, dataonly bool) error {
|
||||
return file.Sync()
|
||||
}
|
||||
func (vfsOSMethods) GetSharedLock(file *os.File, timeout time.Duration) xErrorCode {
|
||||
// Acquire the PENDING lock temporarily before acquiring a new SHARED lock.
|
||||
rc := vfsOS.readLock(file, _PENDING_BYTE, 1, timeout)
|
||||
|
||||
func (vfsOSMethods) Allocate(file *os.File, size int64) error {
|
||||
off, err := file.Seek(0, io.SeekEnd)
|
||||
if err != nil {
|
||||
return err
|
||||
if rc == _OK {
|
||||
// Acquire the SHARED lock.
|
||||
rc = vfsOS.readLock(file, _SHARED_FIRST, _SHARED_SIZE, 0)
|
||||
|
||||
// Release the PENDING lock.
|
||||
vfsOS.unlock(file, _PENDING_BYTE, 1)
|
||||
}
|
||||
if size > off {
|
||||
return file.Truncate(size)
|
||||
}
|
||||
return nil
|
||||
return rc
|
||||
}
|
||||
|
||||
func (vfsOSMethods) GetExclusiveLock(file *os.File, timeout time.Duration) xErrorCode {
|
||||
@@ -74,8 +72,8 @@ func (vfsOSMethods) GetExclusiveLock(file *os.File, timeout time.Duration) xErro
|
||||
// Acquire the EXCLUSIVE lock.
|
||||
rc := vfsOS.writeLock(file, _SHARED_FIRST, _SHARED_SIZE, timeout)
|
||||
|
||||
// Reacquire the SHARED lock.
|
||||
if rc != _OK {
|
||||
// Reacquire the SHARED lock.
|
||||
vfsOS.readLock(file, _SHARED_FIRST, _SHARED_SIZE, 0)
|
||||
}
|
||||
return rc
|
||||
@@ -131,18 +129,20 @@ func (vfsOSMethods) unlock(file *os.File, start, len uint32) xErrorCode {
|
||||
}
|
||||
|
||||
func (vfsOSMethods) lock(file *os.File, flags, start, len uint32, timeout time.Duration, def xErrorCode) xErrorCode {
|
||||
var err error
|
||||
for {
|
||||
err := windows.LockFileEx(windows.Handle(file.Fd()), flags,
|
||||
err = windows.LockFileEx(windows.Handle(file.Fd()), flags,
|
||||
0, len, 0, &windows.Overlapped{Offset: start})
|
||||
if errno, _ := err.(windows.Errno); errno != windows.ERROR_LOCK_VIOLATION {
|
||||
return vfsOS.lockErrorCode(err, def)
|
||||
break
|
||||
}
|
||||
if timeout < time.Millisecond {
|
||||
return vfsOS.lockErrorCode(err, def)
|
||||
break
|
||||
}
|
||||
timeout -= time.Millisecond
|
||||
time.Sleep(time.Millisecond)
|
||||
}
|
||||
return vfsOS.lockErrorCode(err, def)
|
||||
}
|
||||
|
||||
func (vfsOSMethods) readLock(file *os.File, start, len uint32, timeout time.Duration) xErrorCode {
|
||||
|
||||
16
vfs_test.go
16
vfs_test.go
@@ -14,19 +14,11 @@ import (
|
||||
"github.com/ncruces/julianday"
|
||||
)
|
||||
|
||||
func Test_vfsExit(t *testing.T) {
|
||||
mem := newMemory(128)
|
||||
ctx := context.TODO()
|
||||
defer func() { _ = recover() }()
|
||||
vfsExit(ctx, mem.mod, 1)
|
||||
t.Error("want panic")
|
||||
}
|
||||
|
||||
func Test_vfsLocaltime(t *testing.T) {
|
||||
mem := newMemory(128)
|
||||
ctx := context.TODO()
|
||||
|
||||
rc := vfsLocaltime(ctx, mem.mod, 0, 4)
|
||||
rc := vfsLocaltime(ctx, mem.mod, 4, 0)
|
||||
if rc != 0 {
|
||||
t.Fatal("returned", rc)
|
||||
}
|
||||
@@ -60,8 +52,9 @@ func Test_vfsLocaltime(t *testing.T) {
|
||||
|
||||
func Test_vfsRandomness(t *testing.T) {
|
||||
mem := newMemory(128)
|
||||
ctx := context.TODO()
|
||||
|
||||
rc := vfsRandomness(context.TODO(), mem.mod, 0, 16, 4)
|
||||
rc := vfsRandomness(ctx, mem.mod, 0, 16, 4)
|
||||
if rc != 16 {
|
||||
t.Fatal("returned", rc)
|
||||
}
|
||||
@@ -73,10 +66,11 @@ func Test_vfsRandomness(t *testing.T) {
|
||||
}
|
||||
|
||||
func Test_vfsSleep(t *testing.T) {
|
||||
mem := newMemory(128)
|
||||
ctx := context.TODO()
|
||||
|
||||
now := time.Now()
|
||||
rc := vfsSleep(ctx, 0, 123456)
|
||||
rc := vfsSleep(ctx, mem.mod, 0, 123456)
|
||||
if rc != 0 {
|
||||
t.Fatal("returned", rc)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user