diff --git a/embed/exports.txt b/embed/exports.txt index 5460195..597c795 100644 --- a/embed/exports.txt +++ b/embed/exports.txt @@ -77,6 +77,7 @@ sqlite3_get_autocommit sqlite3_get_auxdata sqlite3_hard_heap_limit64 sqlite3_interrupt +sqlite3_invoke_busy_handler_go sqlite3_last_insert_rowid sqlite3_limit sqlite3_malloc64 diff --git a/sqlite3/vfs.c b/sqlite3/vfs.c index 0a15aaa..d28633e 100644 --- a/sqlite3/vfs.c +++ b/sqlite3/vfs.c @@ -162,5 +162,10 @@ int sqlite3_os_init() { return SQLITE_OK; } +int sqlite3_invoke_busy_handler_go(sqlite3_int64 token) { + void **ap = (void **)&token; + return ((int(*)(void *))(ap[0]))(ap[1]); +} + static_assert(offsetof(sqlite3_vfs, zName) == 16, "Unexpected offset"); static_assert(offsetof(struct go_file, handle) == 4, "Unexpected offset"); \ No newline at end of file diff --git a/util/vfsutil/wrap.go b/util/vfsutil/wrap.go index 875ea69..b51a404 100644 --- a/util/vfsutil/wrap.go +++ b/util/vfsutil/wrap.go @@ -46,8 +46,8 @@ func WrapPersistWAL(f vfs.File) bool { return false } -// WrapSetPersistentWAL helps wrap [vfs.FilePersistWAL]. -func WrapSetPersistentWAL(f vfs.File, keepWAL bool) { +// WrapSetPersistWAL helps wrap [vfs.FilePersistWAL]. +func WrapSetPersistWAL(f vfs.File, keepWAL bool) { if f, ok := f.(vfs.FilePersistWAL); ok { f.SetPersistWAL(keepWAL) } @@ -99,6 +99,14 @@ func WrapOverwrite(f vfs.File) error { return sqlite3.NOTFOUND } +// WrapSyncSuper helps wrap [vfs.FileSync]. +func WrapSyncSuper(f vfs.File, super string) error { + if f, ok := f.(vfs.FileSync); ok { + return f.SyncSuper(super) + } + return sqlite3.NOTFOUND +} + // WrapCommitPhaseTwo helps wrap [vfs.FileCommitPhaseTwo]. func WrapCommitPhaseTwo(f vfs.File) error { if f, ok := f.(vfs.FileCommitPhaseTwo); ok { @@ -153,6 +161,13 @@ func WrapPragma(f vfs.File, name, value string) (string, error) { return "", sqlite3.NOTFOUND } +// WrapBusyHandler helps wrap [vfs.FilePragma]. +func WrapBusyHandler(f vfs.File, handler func() bool) { + if f, ok := f.(vfs.FileBusyHandler); ok { + f.BusyHandler(handler) + } +} + // WrapSharedMemory helps wrap [vfs.FileSharedMemory]. func WrapSharedMemory(f vfs.File) vfs.SharedMemory { if f, ok := f.(vfs.FileSharedMemory); ok { diff --git a/vfs/adiantum/hbsh.go b/vfs/adiantum/hbsh.go index 283c645..b5e96be 100644 --- a/vfs/adiantum/hbsh.go +++ b/vfs/adiantum/hbsh.go @@ -242,7 +242,7 @@ func (h *hbshFile) PersistentWAL() bool { } func (h *hbshFile) SetPersistentWAL(keepWAL bool) { - vfsutil.WrapSetPersistentWAL(h.File, keepWAL) // notest + vfsutil.WrapSetPersistWAL(h.File, keepWAL) // notest } func (h *hbshFile) HasMoved() (bool, error) { @@ -253,6 +253,10 @@ func (h *hbshFile) Overwrite() error { return vfsutil.WrapOverwrite(h.File) // notest } +func (h *hbshFile) SyncSuper(super string) error { + return vfsutil.WrapSyncSuper(h.File, super) // notest +} + func (h *hbshFile) CommitPhaseTwo() error { return vfsutil.WrapCommitPhaseTwo(h.File) // notest } @@ -276,3 +280,7 @@ func (h *hbshFile) CheckpointStart() { func (h *hbshFile) CheckpointDone() { vfsutil.WrapCheckpointDone(h.File) // notest } + +func (h *hbshFile) BusyHandler(handler func() bool) { + vfsutil.WrapBusyHandler(h.File, handler) // notest +} diff --git a/vfs/api.go b/vfs/api.go index 082da35..f2531f2 100644 --- a/vfs/api.go +++ b/vfs/api.go @@ -171,6 +171,15 @@ type FilePragma interface { Pragma(name, value string) (string, error) } +// FileBusyHandler extends File to implement the +// SQLITE_FCNTL_BUSYHANDLER file control opcode. +// +// https://sqlite.org/c3ref/c_fcntl_begin_atomic_write.html#sqlitefcntlbusyhandler +type FileBusyHandler interface { + File + BusyHandler(func() bool) +} + // FileSharedMemory extends File to possibly implement // shared-memory for the WAL-index. // The same shared-memory instance must be returned diff --git a/vfs/vfs.go b/vfs/vfs.go index ec2898a..d8816e4 100644 --- a/vfs/vfs.go +++ b/vfs/vfs.go @@ -379,6 +379,20 @@ func vfsFileControlImpl(ctx context.Context, mod api.Module, file File, op _Fcnt return ret } + case _FCNTL_BUSYHANDLER: + if file, ok := file.(FileBusyHandler); ok { + arg := util.ReadUint64(mod, pArg) + fn := mod.ExportedFunction("sqlite3_invoke_busy_handler_go") + file.BusyHandler(func() bool { + stack := [...]uint64{arg} + if err := fn.CallWithStack(ctx, stack[:]); err != nil { + panic(err) + } + return uint32(stack[0]) != 0 + }) + return _OK + } + case _FCNTL_LOCK_TIMEOUT: if file, ok := file.(FileSharedMemory); ok { if shm, ok := file.SharedMemory().(blockingSharedMemory); ok { diff --git a/vfs/xts/xts.go b/vfs/xts/xts.go index 007c0ce..6a59e9f 100644 --- a/vfs/xts/xts.go +++ b/vfs/xts/xts.go @@ -238,7 +238,7 @@ func (x *xtsFile) PersistentWAL() bool { } func (x *xtsFile) SetPersistentWAL(keepWAL bool) { - vfsutil.WrapSetPersistentWAL(x.File, keepWAL) // notest + vfsutil.WrapSetPersistWAL(x.File, keepWAL) // notest } func (x *xtsFile) HasMoved() (bool, error) { @@ -249,6 +249,10 @@ func (x *xtsFile) Overwrite() error { return vfsutil.WrapOverwrite(x.File) // notest } +func (x *xtsFile) SyncSuper(super string) error { + return vfsutil.WrapSyncSuper(x.File, super) // notest +} + func (x *xtsFile) CommitPhaseTwo() error { return vfsutil.WrapCommitPhaseTwo(x.File) // notest } @@ -272,3 +276,7 @@ func (x *xtsFile) CheckpointStart() { func (x *xtsFile) CheckpointDone() { vfsutil.WrapCheckpointDone(x.File) // notest } + +func (x *xtsFile) BusyHandler(handler func() bool) { + vfsutil.WrapBusyHandler(x.File, handler) // notest +}