diff --git a/config.go b/config.go index 7f6bd85..1d437e0 100644 --- a/config.go +++ b/config.go @@ -16,8 +16,19 @@ import ( // // https://sqlite.org/c3ref/db_config.html func (c *Conn) Config(op DBConfig, arg ...bool) (bool, error) { + if op < DBCONFIG_ENABLE_FKEY || op > DBCONFIG_REVERSE_SCANORDER { + return false, MISUSE + } + + // We need to call sqlite3_db_config, a variadic function. + // We only support the `int int*` variants. + // The int is a three-valued bool: -1 queries, 0/1 sets false/true. + // The int* points to where new state will be written to. + // The vararg is a pointer to an array containing these arguments: + // an int and an int* pointing to that int. + defer c.arena.mark()() - argsPtr := c.arena.new(2 * ptrlen) + argsPtr := c.arena.new(intlen + ptrlen) var flag int switch { @@ -64,18 +75,23 @@ func logCallback(ctx context.Context, mod api.Module, _, iCode, zMsg uint32) { // https://sqlite.org/c3ref/file_control.html func (c *Conn) FileControl(schema string, op FcntlOpcode, arg ...any) (any, error) { defer c.arena.mark()() + ptr := c.arena.new(max(ptrlen, intlen)) var schemaPtr uint32 if schema != "" { schemaPtr = c.arena.string(schema) } + var rc uint64 + var res any switch op { + default: + return nil, MISUSE + case FCNTL_RESET_CACHE: - r := c.call("sqlite3_file_control", + rc = c.call("sqlite3_file_control", uint64(c.handle), uint64(schemaPtr), uint64(op), 0) - return nil, c.error(r) case FCNTL_PERSIST_WAL, FCNTL_POWERSAFE_OVERWRITE: var flag int @@ -85,70 +101,69 @@ func (c *Conn) FileControl(schema string, op FcntlOpcode, arg ...any) (any, erro case arg[0]: flag = 1 } - ptr := c.arena.new(4) util.WriteUint32(c.mod, ptr, uint32(flag)) - r := c.call("sqlite3_file_control", + rc = c.call("sqlite3_file_control", uint64(c.handle), uint64(schemaPtr), uint64(op), uint64(ptr)) - return util.ReadUint32(c.mod, ptr) != 0, c.error(r) + res = util.ReadUint32(c.mod, ptr) != 0 case FCNTL_CHUNK_SIZE: - ptr := c.arena.new(4) util.WriteUint32(c.mod, ptr, uint32(arg[0].(int))) - r := c.call("sqlite3_file_control", + rc = c.call("sqlite3_file_control", uint64(c.handle), uint64(schemaPtr), uint64(op), uint64(ptr)) - return nil, c.error(r) case FCNTL_RESERVE_BYTES: bytes := -1 if len(arg) > 0 { bytes = arg[0].(int) } - ptr := c.arena.new(4) util.WriteUint32(c.mod, ptr, uint32(bytes)) - r := c.call("sqlite3_file_control", + rc = c.call("sqlite3_file_control", uint64(c.handle), uint64(schemaPtr), uint64(op), uint64(ptr)) - return int(util.ReadUint32(c.mod, ptr)), c.error(r) + res = int(util.ReadUint32(c.mod, ptr)) case FCNTL_DATA_VERSION: - ptr := c.arena.new(4) - r := c.call("sqlite3_file_control", + rc = c.call("sqlite3_file_control", uint64(c.handle), uint64(schemaPtr), uint64(op), uint64(ptr)) - return util.ReadUint32(c.mod, ptr), c.error(r) + res = util.ReadUint32(c.mod, ptr) case FCNTL_LOCKSTATE: - ptr := c.arena.new(4) - r := c.call("sqlite3_file_control", + rc = c.call("sqlite3_file_control", uint64(c.handle), uint64(schemaPtr), uint64(op), uint64(ptr)) - return vfs.LockLevel(util.ReadUint32(c.mod, ptr)), c.error(r) + res = vfs.LockLevel(util.ReadUint32(c.mod, ptr)) case FCNTL_VFS_POINTER: - ptr := c.arena.new(4) - r := c.call("sqlite3_file_control", + rc = c.call("sqlite3_file_control", uint64(c.handle), uint64(schemaPtr), uint64(op), uint64(ptr)) - const zNameOffset = 16 - ptr = util.ReadUint32(c.mod, ptr) - ptr = util.ReadUint32(c.mod, ptr+zNameOffset) - name := util.ReadString(c.mod, ptr, _MAX_NAME) - return vfs.Find(name), c.error(r) + if rc == _OK { + const zNameOffset = 16 + ptr = util.ReadUint32(c.mod, ptr) + ptr = util.ReadUint32(c.mod, ptr+zNameOffset) + name := util.ReadString(c.mod, ptr, _MAX_NAME) + res = vfs.Find(name) + } case FCNTL_FILE_POINTER, FCNTL_JOURNAL_POINTER: - ptr := c.arena.new(4) - r := c.call("sqlite3_file_control", + rc = c.call("sqlite3_file_control", uint64(c.handle), uint64(schemaPtr), uint64(op), uint64(ptr)) - const fileHandleOffset = 4 - ptr = util.ReadUint32(c.mod, ptr) - ptr = util.ReadUint32(c.mod, ptr+fileHandleOffset) - return util.GetHandle(c.ctx, ptr), c.error(r) + 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) + } } - return nil, MISUSE + if err := c.error(rc); err != nil { + return nil, err + } + return res, nil } // Limit allows the size of various constructs to be diff --git a/conn.go b/conn.go index 7635e58..d09a765 100644 --- a/conn.go +++ b/conn.go @@ -459,8 +459,8 @@ func busyCallback(ctx context.Context, mod api.Module, pDB uint32, count int32) // https://sqlite.org/c3ref/db_status.html func (c *Conn) Status(op DBStatus, reset bool) (current, highwater int, err error) { defer c.arena.mark()() - hiPtr := c.arena.new(4) - curPtr := c.arena.new(4) + hiPtr := c.arena.new(intlen) + curPtr := c.arena.new(intlen) var i uint64 if reset { @@ -486,8 +486,8 @@ func (c *Conn) TableColumnMetadata(schema, table, column string) (declType, coll declTypePtr := c.arena.new(ptrlen) collSeqPtr := c.arena.new(ptrlen) notNullPtr := c.arena.new(ptrlen) - primaryKeyPtr := c.arena.new(ptrlen) autoIncPtr := c.arena.new(ptrlen) + primaryKeyPtr := c.arena.new(ptrlen) if schema != "" { schemaPtr = c.arena.string(schema) } diff --git a/const.go b/const.go index 6b9665f..3a6a8cd 100644 --- a/const.go +++ b/const.go @@ -13,6 +13,7 @@ const ( _MAX_FUNCTION_ARG = 100 ptrlen = 4 + intlen = 4 ) // ErrorCode is a result code that [Error.Code] might return. @@ -246,6 +247,7 @@ const ( DBCONFIG_TRUSTED_SCHEMA DBConfig = 1017 DBCONFIG_STMT_SCANSTATUS DBConfig = 1018 DBCONFIG_REVERSE_SCANORDER DBConfig = 1019 + // DBCONFIG_MAX DBConfig = 1019 ) // FcntlOpcode are the available opcodes for [Conn.FileControl]. diff --git a/tests/config_test.go b/tests/config_test.go index da2dbee..a7ddb92 100644 --- a/tests/config_test.go +++ b/tests/config_test.go @@ -60,6 +60,11 @@ func TestConn_Config(t *testing.T) { if o != false { t.Error("want false") } + + _, err = db.Config(0) + if !errors.Is(err, sqlite3.MISUSE) { + t.Errorf("got %v, want MISUSE", err) + } } func TestConn_ConfigLog(t *testing.T) {