From 3fb0eeec51d3eefbb545479e0d905123d30d3036 Mon Sep 17 00:00:00 2001 From: Nuno Cruces Date: Tue, 23 Apr 2024 11:43:14 +0100 Subject: [PATCH] Filename API (#82) Also remove VFSParams. --- conn.go | 15 ++++ embed/exports.txt | 4 ++ embed/sqlite3.wasm | Bin 1336790 -> 1337796 bytes tests/conn_test.go | 42 +++++++++++ vfs/adiantum/hbsh.go | 45 ++++++------ vfs/api.go | 20 ++---- vfs/file.go | 11 ++- vfs/filename.go | 166 +++++++++++++++++++++++++++++++++++++++++++ vfs/vfs.go | 95 +++++-------------------- 9 files changed, 277 insertions(+), 121 deletions(-) create mode 100644 vfs/filename.go diff --git a/conn.go b/conn.go index bb4cae2..d3fa380 100644 --- a/conn.go +++ b/conn.go @@ -10,6 +10,7 @@ import ( "time" "github.com/ncruces/go-sqlite3/internal/util" + "github.com/ncruces/go-sqlite3/vfs" "github.com/tetratelabs/wazero/api" ) @@ -216,6 +217,20 @@ func (c *Conn) DBName(n int) string { return util.ReadString(c.mod, ptr, _MAX_NAME) } +// Filename returns the filename for a database. +// +// https://sqlite.org/c3ref/db_filename.html +func (c *Conn) Filename(schema string) *vfs.Filename { + var ptr uint32 + if schema != "" { + defer c.arena.mark()() + ptr = c.arena.string(schema) + } + + r := c.call("sqlite3_db_filename", uint64(c.handle), uint64(ptr)) + return vfs.OpenFilename(c.ctx, c.mod, uint32(r), vfs.OPEN_MAIN_DB) +} + // ReadOnly determines if a database is read-only. // // https://sqlite.org/c3ref/db_readonly.html diff --git a/embed/exports.txt b/embed/exports.txt index 3b73ddb..da16316 100644 --- a/embed/exports.txt +++ b/embed/exports.txt @@ -53,6 +53,7 @@ sqlite3_create_module_go sqlite3_create_window_function_go sqlite3_database_file_object sqlite3_db_config +sqlite3_db_filename sqlite3_db_name sqlite3_db_readonly sqlite3_db_release_memory @@ -62,6 +63,9 @@ sqlite3_errmsg sqlite3_error_offset sqlite3_errstr sqlite3_exec +sqlite3_filename_database +sqlite3_filename_journal +sqlite3_filename_wal sqlite3_finalize sqlite3_get_autocommit sqlite3_get_auxdata diff --git a/embed/sqlite3.wasm b/embed/sqlite3.wasm index bb3a88853f896e452d8b74767b875d2aa608a527..2f36b2948d459958509396948142e5ce19a25291 100755 GIT binary patch delta 1535 zcmaKsT}&KR6vywG{eW`surLhl?6SbHDAr(!A8#f`RzuPSqc&-yZZNZ#Xv`sxv?ooMLYy|!LG~)Z2KCM)bJfL;|vHWAm-=6KZHPpgG zKC@qr3FMLcVM9GU;T>tYDL_7FyeBOuR5HQhet~}5&e~u<#mM{0nQGuMBSDN9x+oHr_j{nsoHwt8)(_T3) z5Z85-4p?xPl^86oe4{MEn~&)a;ZV3@gFdC3betV$m3yiv6RRUN%}t5M_yu;6aTdG8 z7TIOiqF!NF*$ZvS)|c8}ytXnI|5ylq`+NHK#Cl?CJpm{I6+i`1AygPufvQC9K~1q$Zcz#4j+M z!yE9Mw)9%)M*+fPJmg3!5Z>-cDKSM_i5f_e{bHDf_^=}#suTgu0IudXg7y54BPr=} zQV(Xr(bfQ7mjPA7xC|C@dxc;U%H2ranyJ8ofZkq#Bw-(k;KOnGHMA0{lBo?_THUbg zKG70CeIW$B|6OF|U#W9McLrhhE-bGutkta*pTpX{-BG*0GieXDf%Xh2k7Y#9=j`FX z^f9!NpNwVZM<02})jp=8ZjQcd<@4_wM#jiIK>}rcgGL4{4>E1U)`x}+`>E$R1!Kr} zHfc>MCyT?tx5in?I;Q7~hHo8b5?WdFm|;6xCW8G6f-Ouu1H5_id3>Mn@P?Bf-H3H0 zs=z6PyQ%T?u5h36*oJ9DJO1j~-O!%dk?dInyAjTLV8i)&r3vPc>ln;?T!ZUjomgO7 zqp>`a2CYmnZ{WnZz*D_)L-Zo6!F|9LMMFs;j=Y{@EROcJ`l^rG%h|Iz)8q5J74lf{L6$=I!T(4+F?1Cj7zG}EHj(f@YS*bJF}0CtMF0A9AMvZ4mQ!K@E|FLwWus*^6h#J zSBW}|xmvb7i|HgDW7WsHvoozOeTEAKx(R13Z%V3Zp+%G;F{)$&J^DwEN4@$l&hPId z9xIkiL^7we$nwKXs$VbjdeDH;TF3fr%%Cw+$8t7iNRN9%XjmWi`p}5+4SFGFlz6Q@ zQerGgg@hcHw8OK;^zYs(G_Dh#NArsn`~teRf#2n1=Qy_THr~Ozcn|01*rmgBrc6s`cv*lLg2@9Tb4OVCpo%Tb(^Th z(ThLm*-~iP{V7@b|IpV?o%y;s@~0cvDN5-Zax7LyX@o{LPcvW(7dFf0h=n#qOeCnX zZB|r`CalU1F3M!0k{Gi}yJqR|*^k!kVHc#LQi>`ng`C6O4ZO+-W;;0a3$^RTTmW@| zMir>j)X3g-WYKjPe>d@4GfgORvqDw%ghD-M%GC>Qc~Kv@<@5EQ<<2GIzJ#iLPEtl-$$UX2rd6@n8+O+D-3CW#wv98M~NoS>5RG`exWo()`?A{;bR zn%E?SWl5r8%?eBt&vEDQ4?B9n8RKO=%Ok^hU(c3S&l28>WwdTqqFUP8VvZC*FD^%f T2vs#jPS1n7-leKL>>U3OP$GQH diff --git a/tests/conn_test.go b/tests/conn_test.go index b768adb..61d9361 100644 --- a/tests/conn_test.go +++ b/tests/conn_test.go @@ -416,6 +416,48 @@ func TestConn_SetLastInsertRowID(t *testing.T) { } } +func TestConn_Filename(t *testing.T) { + t.Parallel() + + file := filepath.Join(t.TempDir(), "test.db") + db, err := sqlite3.Open(file) + if err != nil { + t.Fatal(err) + } + defer db.Close() + + n := db.Filename("") + if n.String() != file { + t.Errorf("got %v", n) + } + if n.Database() != file { + t.Errorf("got %v", n) + } + if n.DatabaseFile() == nil { + t.Errorf("got %v", n) + } + + n = db.Filename("xpto") + if n != nil { + t.Errorf("got %v", n) + } + if n.String() != "" { + t.Errorf("got %v", n) + } + if n.Database() != "" { + t.Errorf("got %v", n) + } + if n.Journal() != "" { + t.Errorf("got %v", n) + } + if n.WAL() != "" { + t.Errorf("got %v", n) + } + if n.DatabaseFile() != nil { + t.Errorf("got %v", n) + } +} + func TestConn_ReadOnly(t *testing.T) { t.Parallel() diff --git a/vfs/adiantum/hbsh.go b/vfs/adiantum/hbsh.go index 9d96508..aaebbda 100644 --- a/vfs/adiantum/hbsh.go +++ b/vfs/adiantum/hbsh.go @@ -4,7 +4,6 @@ import ( "encoding/binary" "encoding/hex" "io" - "net/url" "github.com/ncruces/go-sqlite3" "github.com/ncruces/go-sqlite3/internal/util" @@ -18,38 +17,38 @@ type hbshVFS struct { } func (h *hbshVFS) Open(name string, flags vfs.OpenFlag) (vfs.File, vfs.OpenFlag, error) { - return h.OpenParams(name, flags, nil) + return nil, 0, sqlite3.CANTOPEN } -func (h *hbshVFS) OpenParams(name string, flags vfs.OpenFlag, params url.Values) (file vfs.File, _ vfs.OpenFlag, err error) { +func (h *hbshVFS) OpenFilename(name *vfs.Filename, flags vfs.OpenFlag) (file vfs.File, _ vfs.OpenFlag, err error) { var hbsh *hbsh.HBSH // Encrypt everything except super journals. if flags&vfs.OPEN_SUPER_JOURNAL == 0 { - var key []byte - if name == "" { - key = h.hbsh.KDF("") // Temporary files get a random key. - } else if t, ok := params["key"]; ok { - key = []byte(t[0]) - } else if t, ok := params["hexkey"]; ok { - key, _ = hex.DecodeString(t[0]) - } else if t, ok := params["textkey"]; ok { - key = h.hbsh.KDF(t[0]) - } - - if hbsh = h.hbsh.HBSH(key); hbsh == nil { - // Can't open without a valid key. - return nil, flags, sqlite3.CANTOPEN + if f, ok := name.DatabaseFile().(*hbshFile); ok { + hbsh = f.hbsh + } else { + var key []byte + if params := name.URIParameters(); name == nil { + key = h.hbsh.KDF("") // Temporary files get a random key. + } else if t, ok := params["key"]; ok { + key = []byte(t[0]) + } else if t, ok := params["hexkey"]; ok { + key, _ = hex.DecodeString(t[0]) + } else if t, ok := params["textkey"]; ok { + key = h.hbsh.KDF(t[0]) + } + if hbsh = h.hbsh.HBSH(key); hbsh == nil { + // Can't open without a valid key. + return nil, flags, sqlite3.CANTOPEN + } } } - if h, ok := h.VFS.(vfs.VFSParams); ok { - delete(params, "key") - delete(params, "hexkey") - delete(params, "textkey") - file, flags, err = h.OpenParams(name, flags, params) + if h, ok := h.VFS.(vfs.VFSFilename); ok { + file, flags, err = h.OpenFilename(name, flags) } else { - file, flags, err = h.Open(name, flags) + file, flags, err = h.Open(name.String(), flags) } if err != nil || hbsh == nil || flags&vfs.OPEN_MEMORY != 0 { // Error, or no encryption (super journals, memory files). diff --git a/vfs/api.go b/vfs/api.go index f8e7195..24bbe06 100644 --- a/vfs/api.go +++ b/vfs/api.go @@ -3,7 +3,6 @@ package vfs import ( "context" - "net/url" "github.com/tetratelabs/wazero/api" ) @@ -20,22 +19,13 @@ type VFS interface { FullPathname(name string) (string, error) } -// VFSParams extends VFS with the ability to handle URI parameters -// through the OpenParams method. +// VFSFilename extends VFS with the ability to use Filename +// objects for opening files. // -// https://sqlite.org/c3ref/uri_boolean.html -type VFSParams interface { +// https://sqlite.org/c3ref/filename.html +type VFSFilename interface { VFS - OpenParams(name string, flags OpenFlag, params url.Values) (File, OpenFlag, error) -} - -// VFSJournal extends VFS with the ability to open journals -// that need a reference to their corresponding database files. -// -// https://sqlite.org/c3ref/database_file_object.html -type VFSJournal interface { - VFS - OpenJournal(name string, flags OpenFlag, db File) (File, OpenFlag, error) + OpenFilename(name *Filename, flags OpenFlag) (File, OpenFlag, error) } // A File represents an open file in the OS interface layer. diff --git a/vfs/file.go b/vfs/file.go index 4f2aa39..d34158f 100644 --- a/vfs/file.go +++ b/vfs/file.go @@ -4,7 +4,6 @@ import ( "errors" "io" "io/fs" - "net/url" "os" "path/filepath" "runtime" @@ -70,10 +69,10 @@ func (vfsOS) Access(name string, flags AccessFlag) (bool, error) { } func (vfsOS) Open(name string, flags OpenFlag) (File, OpenFlag, error) { - return vfsOS{}.OpenParams(name, flags, nil) + return nil, 0, _CANTOPEN } -func (vfsOS) OpenParams(name string, flags OpenFlag, params url.Values) (File, OpenFlag, error) { +func (vfsOS) OpenFilename(name *Filename, flags OpenFlag) (File, OpenFlag, error) { var oflags int if flags&OPEN_EXCLUSIVE != 0 { oflags |= os.O_EXCL @@ -90,10 +89,10 @@ func (vfsOS) OpenParams(name string, flags OpenFlag, params url.Values) (File, O var err error var f *os.File - if name == "" { + if name == nil { f, err = os.CreateTemp("", "*.db") } else { - f, err = osutil.OpenFile(name, oflags, 0666) + f, err = osutil.OpenFile(name.String(), oflags, 0666) } if err != nil { if errors.Is(err, syscall.EISDIR) { @@ -102,7 +101,7 @@ func (vfsOS) OpenParams(name string, flags OpenFlag, params url.Values) (File, O return nil, flags, err } - if modeof := params.Get("modeof"); modeof != "" { + if modeof := name.URIParameter("modeof"); modeof != "" { if err = osSetMode(f, modeof); err != nil { f.Close() return nil, flags, _IOERR_FSTAT diff --git a/vfs/filename.go b/vfs/filename.go new file mode 100644 index 0000000..3e3df14 --- /dev/null +++ b/vfs/filename.go @@ -0,0 +1,166 @@ +package vfs + +import ( + "context" + "net/url" + + "github.com/ncruces/go-sqlite3/internal/util" + "github.com/tetratelabs/wazero/api" +) + +// Filename is used by SQLite to pass filenames +// to the Open method of a VFS. +// +// https://sqlite.org/c3ref/filename.html +type Filename struct { + ctx context.Context + mod api.Module + zPath uint32 + flags OpenFlag + stack [2]uint64 +} + +// OpenFilename is an internal API users should not call directly. +func OpenFilename(ctx context.Context, mod api.Module, id uint32, flags OpenFlag) *Filename { + if id == 0 { + return nil + } + return &Filename{ + ctx: ctx, + mod: mod, + zPath: id, + flags: flags, + } +} + +// String returns this filename as a string. +func (n *Filename) String() string { + if n == nil || n.zPath == 0 { + return "" + } + return util.ReadString(n.mod, n.zPath, _MAX_PATHNAME) +} + +// Database returns the name of the corresponding database file. +// +// https://sqlite.org/c3ref/filename_database.html +func (n *Filename) Database() string { + return n.path("sqlite3_filename_database") +} + +// Journal returns the name of the corresponding rollback journal file. +// +// https://sqlite.org/c3ref/filename_database.html +func (n *Filename) Journal() string { + return n.path("sqlite3_filename_journal") +} + +// Journal returns the name of the corresponding WAL file. +// +// https://sqlite.org/c3ref/filename_database.html +func (n *Filename) WAL() string { + return n.path("sqlite3_filename_wal") +} + +func (n *Filename) path(method string) string { + if n == nil || n.zPath == 0 { + return "" + } + n.stack[0] = uint64(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) +} + +// DatabaseFile returns the main database [File] corresponding to a journal. +// +// https://sqlite.org/c3ref/database_file_object.html +func (n *Filename) DatabaseFile() File { + if n == nil || n.zPath == 0 { + return nil + } + if n.flags&(OPEN_MAIN_DB|OPEN_MAIN_JOURNAL|OPEN_WAL) == 0 { + return nil + } + + n.stack[0] = uint64(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) + return file +} + +// URIParameter returns the value of a URI parameter. +// +// https://sqlite.org/c3ref/uri_boolean.html +func (n *Filename) URIParameter(key string) string { + if n == nil || n.zPath == 0 { + return "" + } + + uriKey := n.mod.ExportedFunction("sqlite3_uri_key") + uriParam := n.mod.ExportedFunction("sqlite3_uri_parameter") + + for i := 0; ; i++ { + n.stack[1] = uint64(i) + n.stack[0] = uint64(n.zPath) + if err := uriKey.CallWithStack(n.ctx, n.stack[:]); err != nil { + panic(err) + } + if n.stack[0] == 0 { + return "" + } + if key != util.ReadString(n.mod, uint32(n.stack[0]), _MAX_NAME) { + continue + } + + n.stack[1] = n.stack[0] + n.stack[0] = uint64(n.zPath) + if err := uriParam.CallWithStack(n.ctx, n.stack[:]); err != nil { + panic(err) + } + return util.ReadString(n.mod, uint32(n.stack[0]), _MAX_NAME) + } +} + +// URIParameters obtains values for URI parameters. +// +// https://sqlite.org/c3ref/uri_boolean.html +func (n *Filename) URIParameters() url.Values { + if n == nil || n.zPath == 0 { + return nil + } + + var params url.Values + uriKey := n.mod.ExportedFunction("sqlite3_uri_key") + uriParam := n.mod.ExportedFunction("sqlite3_uri_parameter") + + for i := 0; ; i++ { + n.stack[1] = uint64(i) + n.stack[0] = uint64(n.zPath) + if err := uriKey.CallWithStack(n.ctx, n.stack[:]); err != nil { + panic(err) + } + if n.stack[0] == 0 { + return params + } + key := util.ReadString(n.mod, uint32(n.stack[0]), _MAX_NAME) + if params.Has(key) { + continue + } + + n.stack[1] = n.stack[0] + n.stack[0] = uint64(n.zPath) + if err := uriParam.CallWithStack(n.ctx, n.stack[:]); err != nil { + panic(err) + } + if params == nil { + params = url.Values{} + } + params.Set(key, util.ReadString(n.mod, uint32(n.stack[0]), _MAX_NAME)) + } +} diff --git a/vfs/vfs.go b/vfs/vfs.go index 33a7ee1..9080202 100644 --- a/vfs/vfs.go +++ b/vfs/vfs.go @@ -4,7 +4,6 @@ import ( "context" "crypto/rand" "io" - "net/url" "reflect" "sync" "time" @@ -141,15 +140,9 @@ func vfsOpen(ctx context.Context, mod api.Module, pVfs, zPath, pFile uint32, fla var file File var err error - var parsed bool - var params url.Values - if jfs, ok := vfs.(VFSJournal); ok && flags&(OPEN_WAL|OPEN_MAIN_JOURNAL) != 0 { - db := vfsDatabaseFileObject(ctx, mod, zPath) - file, flags, err = jfs.OpenJournal(path, flags, db) - } else if pfs, ok := vfs.(VFSParams); ok { - parsed = true - params = vfsURIParameters(ctx, mod, zPath, flags) - file, flags, err = pfs.OpenParams(path, flags, params) + if ffs, ok := vfs.(VFSFilename); ok { + name := OpenFilename(ctx, mod, zPath, flags) + file, flags, err = ffs.OpenFilename(name, flags) } else { file, flags, err = vfs.Open(path, flags) } @@ -159,10 +152,8 @@ func vfsOpen(ctx context.Context, mod api.Module, pVfs, zPath, pFile uint32, fla } if file, ok := file.(FilePowersafeOverwrite); ok { - if !parsed { - params = vfsURIParameters(ctx, mod, zPath, flags) - } - if b, ok := util.ParseBool(params.Get("psow")); ok { + name := OpenFilename(ctx, mod, zPath, flags) + if b, ok := util.ParseBool(name.URIParameter("psow")); ok { file.SetPowersafeOverwrite(b) } } @@ -190,7 +181,7 @@ func vfsClose(ctx context.Context, mod api.Module, pFile uint32) _ErrorCode { } func vfsRead(ctx context.Context, mod api.Module, pFile, zBuf uint32, iAmt int32, iOfst int64) _ErrorCode { - file := vfsFileGet(ctx, mod, pFile) + file := vfsFileGet(ctx, mod, pFile).(File) buf := util.View(mod, zBuf, uint64(iAmt)) n, err := file.ReadAt(buf, iOfst) @@ -205,7 +196,7 @@ func vfsRead(ctx context.Context, mod api.Module, pFile, zBuf uint32, iAmt int32 } func vfsWrite(ctx context.Context, mod api.Module, pFile, zBuf uint32, iAmt int32, iOfst int64) _ErrorCode { - file := vfsFileGet(ctx, mod, pFile) + file := vfsFileGet(ctx, mod, pFile).(File) buf := util.View(mod, zBuf, uint64(iAmt)) _, err := file.WriteAt(buf, iOfst) @@ -216,38 +207,38 @@ func vfsWrite(ctx context.Context, mod api.Module, pFile, zBuf uint32, iAmt int3 } func vfsTruncate(ctx context.Context, mod api.Module, pFile uint32, nByte int64) _ErrorCode { - file := vfsFileGet(ctx, mod, pFile) + 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 { - file := vfsFileGet(ctx, mod, pFile) + 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 { - file := vfsFileGet(ctx, mod, pFile) + file := vfsFileGet(ctx, mod, pFile).(File) size, err := file.Size() util.WriteUint64(mod, pSize, uint64(size)) return vfsErrorCode(err, _IOERR_SEEK) } func vfsLock(ctx context.Context, mod api.Module, pFile uint32, eLock LockLevel) _ErrorCode { - file := vfsFileGet(ctx, mod, pFile) + 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 { - file := vfsFileGet(ctx, mod, pFile) + 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 { - file := vfsFileGet(ctx, mod, pFile) + file := vfsFileGet(ctx, mod, pFile).(File) locked, err := file.CheckReservedLock() var res uint32 @@ -260,7 +251,7 @@ func vfsCheckReservedLock(ctx context.Context, mod api.Module, pFile, pResOut ui } func vfsFileControl(ctx context.Context, mod api.Module, pFile uint32, op _FcntlOpcode, pArg uint32) _ErrorCode { - file := vfsFileGet(ctx, mod, pFile) + file := vfsFileGet(ctx, mod, pFile).(File) switch op { case _FCNTL_LOCKSTATE: @@ -351,12 +342,12 @@ func vfsFileControl(ctx context.Context, mod api.Module, pFile uint32, op _Fcntl } func vfsSectorSize(ctx context.Context, mod api.Module, pFile uint32) uint32 { - file := vfsFileGet(ctx, mod, pFile) + file := vfsFileGet(ctx, mod, pFile).(File) return uint32(file.SectorSize()) } func vfsDeviceCharacteristics(ctx context.Context, mod api.Module, pFile uint32) DeviceCharacteristic { - file := vfsFileGet(ctx, mod, pFile) + file := vfsFileGet(ctx, mod, pFile).(File) return file.DeviceCharacteristics() } @@ -389,56 +380,6 @@ func vfsShmUnmap(ctx context.Context, mod api.Module, pFile, bDelete uint32) _Er return _OK } -func vfsURIParameters(ctx context.Context, mod api.Module, zPath uint32, flags OpenFlag) url.Values { - switch { - case flags&(OPEN_URI|OPEN_MAIN_DB) == OPEN_URI|OPEN_MAIN_DB: - // database file with URI - case flags&(OPEN_WAL|OPEN_MAIN_JOURNAL) != 0: - // journal or WAL file - default: - return nil - } - - var stack [2]uint64 - var params url.Values - uriKey := mod.ExportedFunction("sqlite3_uri_key") - uriParam := mod.ExportedFunction("sqlite3_uri_parameter") - - for i := 0; ; i++ { - stack[1] = uint64(i) - stack[0] = uint64(zPath) - if err := uriKey.CallWithStack(ctx, stack[:]); err != nil { - panic(err) - } - if stack[0] == 0 { - return params - } - key := util.ReadString(mod, uint32(stack[0]), _MAX_NAME) - if params.Has(key) { - continue - } - - stack[1] = stack[0] - stack[0] = uint64(zPath) - if err := uriParam.CallWithStack(ctx, stack[:]); err != nil { - panic(err) - } - if params == nil { - params = url.Values{} - } - params.Set(key, util.ReadString(mod, uint32(stack[0]), _MAX_NAME)) - } -} - -func vfsDatabaseFileObject(ctx context.Context, mod api.Module, zPath uint32) File { - stack := [...]uint64{uint64(zPath)} - fn := mod.ExportedFunction("sqlite3_database_file_object") - if err := fn.CallWithStack(ctx, stack[:]); err != nil { - panic(err) - } - return vfsFileGet(ctx, mod, uint32(stack[0])) -} - func vfsGet(mod api.Module, pVfs uint32) VFS { var name string if pVfs != 0 { @@ -457,10 +398,10 @@ func vfsFileRegister(ctx context.Context, mod api.Module, pFile uint32, file Fil util.WriteUint32(mod, pFile+fileHandleOffset, id) } -func vfsFileGet(ctx context.Context, mod api.Module, pFile uint32) File { +func vfsFileGet(ctx context.Context, mod api.Module, pFile uint32) any { const fileHandleOffset = 4 id := util.ReadUint32(mod, pFile+fileHandleOffset) - return util.GetHandle(ctx, id).(File) + return util.GetHandle(ctx, id) } func vfsFileClose(ctx context.Context, mod api.Module, pFile uint32) error {