From d998b5f36c30796c3476936027e532779154019a Mon Sep 17 00:00:00 2001 From: Nuno Cruces Date: Thu, 18 Jan 2024 15:53:00 +0000 Subject: [PATCH] Limits, and tweaks. --- README.md | 2 +- backup.go | 4 ++-- config.go | 11 ++++++++++- conn.go | 2 +- const.go | 20 ++++++++++++++++++++ context.go | 2 +- stmt.go | 3 +++ tests/conn_test.go | 31 +++++++++++++++++++++++++++++++ value.go | 4 ++-- vtab.go | 2 +- 10 files changed, 72 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 7d8680b..c4ba326 100644 --- a/README.md +++ b/README.md @@ -113,7 +113,7 @@ with `nolock=1` you must disable connection pooling by calling ### Testing This project aims for [high test coverage](https://github.com/ncruces/go-sqlite3/wiki/Test-coverage-report). -It also benefits greatly from [SQLite's](https://www.sqlite.org/testing.html) and +It also benefits greatly from [SQLite's](https://sqlite.org/testing.html) and [wazero's](https://tetrate.io/blog/introducing-wazero-from-tetrate/#:~:text=Rock%2Dsolid%20test%20approach) thorough testing. The pure Go VFS is tested by running SQLite's diff --git a/backup.go b/backup.go index 5fc5cf1..b16c751 100644 --- a/backup.go +++ b/backup.go @@ -121,7 +121,7 @@ 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(r) + return int(int32(r)) } // PageCount returns the total number of pages in the source database @@ -130,5 +130,5 @@ 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(r) + return int(int32(r)) } diff --git a/config.go b/config.go index e68935e..a21337b 100644 --- a/config.go +++ b/config.go @@ -35,7 +35,7 @@ func (c *Conn) Config(op DBConfig, arg ...bool) (bool, error) { // ConfigLog sets up the error logging callback for the connection. // -// https://www.sqlite.org/errlog.html +// https://sqlite.org/errlog.html func (c *Conn) ConfigLog(cb func(code ExtendedErrorCode, msg string)) error { var enable uint64 if cb != nil { @@ -55,3 +55,12 @@ func logCallback(ctx context.Context, mod api.Module, _, iCode, zMsg uint32) { c.log(xErrorCode(iCode), msg) } } + +// Limit allows the size of various constructs to be +// limited on a connection by connection basis. +// +// 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)) +} diff --git a/conn.go b/conn.go index ad30ed8..6b1b867 100644 --- a/conn.go +++ b/conn.go @@ -217,7 +217,7 @@ func (c *Conn) ReadOnly(schema string) (ro bool, ok bool) { ptr = c.arena.string(schema) } r := c.call("sqlite3_db_readonly", uint64(c.handle), uint64(ptr)) - return int8(r) > 0, int8(r) < 0 + return int32(r) > 0, int32(r) < 0 } // GetAutocommit tests the connection for auto-commit mode. diff --git a/const.go b/const.go index 2210210..49ac2d1 100644 --- a/const.go +++ b/const.go @@ -229,6 +229,26 @@ const ( DBCONFIG_REVERSE_SCANORDER DBConfig = 1019 ) +// LimitCategory are the available run-time limit categories. +// +// https://sqlite.org/c3ref/c_limit_attached.html +type LimitCategory uint32 + +const ( + LIMIT_LENGTH LimitCategory = 0 + LIMIT_SQL_LENGTH LimitCategory = 1 + LIMIT_COLUMN LimitCategory = 2 + LIMIT_EXPR_DEPTH LimitCategory = 3 + LIMIT_COMPOUND_SELECT LimitCategory = 4 + LIMIT_VDBE_OP LimitCategory = 5 + LIMIT_FUNCTION_ARG LimitCategory = 6 + LIMIT_ATTACHED LimitCategory = 7 + LIMIT_LIKE_PATTERN_LENGTH LimitCategory = 8 + LIMIT_VARIABLE_NUMBER LimitCategory = 9 + LIMIT_TRIGGER_DEPTH LimitCategory = 10 + LIMIT_WORKER_THREADS LimitCategory = 11 +) + // TxnState are the allowed return values from [Conn.TxnState]. // // https://sqlite.org/c3ref/c_txn_none.html diff --git a/context.go b/context.go index a3c82e5..8d7604c 100644 --- a/context.go +++ b/context.go @@ -222,7 +222,7 @@ func (ctx Context) ResultError(err error) { // VTabNoChange may return true if a column is being fetched as part // of an update during which the column value will not change. // -// https://www.sqlite.org/c3ref/vtab_nochange.html +// 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 diff --git a/stmt.go b/stmt.go index 4837760..9c20b30 100644 --- a/stmt.go +++ b/stmt.go @@ -111,6 +111,9 @@ func (s *Stmt) Exec() error { // // https://sqlite.org/c3ref/stmt_status.html func (s *Stmt) Status(op StmtStatus, reset bool) int { + if op > STMTSTATUS_FILTER_HIT && op != STMTSTATUS_MEMUSED { + return 0 + } var i uint64 if reset { i = 1 diff --git a/tests/conn_test.go b/tests/conn_test.go index ed1fda8..72583c6 100644 --- a/tests/conn_test.go +++ b/tests/conn_test.go @@ -3,6 +3,7 @@ package tests import ( "context" "errors" + "math" "os" "path/filepath" "reflect" @@ -365,6 +366,36 @@ func TestConn_ConfigLog(t *testing.T) { } } +func TestConn_Limit(t *testing.T) { + t.Parallel() + + db, err := sqlite3.Open(":memory:") + if err != nil { + t.Fatal(err) + } + defer db.Close() + + l := db.Limit(sqlite3.LIMIT_COLUMN, -1) + if l != 2000 { + t.Errorf("got %d, want 2000", l) + } + + l = db.Limit(sqlite3.LIMIT_COLUMN, 100) + if l != 2000 { + t.Errorf("got %d, want 2000", l) + } + + l = db.Limit(sqlite3.LIMIT_COLUMN, -1) + if l != 100 { + t.Errorf("got %d, want 100", l) + } + + l = db.Limit(math.MaxUint32, -1) + if l != -1 { + t.Errorf("got %d, want -1", l) + } +} + func TestConn_ReleaseMemory(t *testing.T) { t.Parallel() diff --git a/value.go b/value.go index 0ae2ed4..61d3cbf 100644 --- a/value.go +++ b/value.go @@ -204,7 +204,7 @@ func (v Value) NoChange() bool { // InFirst returns the first element // on the right-hand side of an IN constraint. // -// https://www.sqlite.org/c3ref/vtab_in_first.html +// https://sqlite.org/c3ref/vtab_in_first.html func (v Value) InFirst() (Value, error) { defer v.c.arena.mark()() valPtr := v.c.arena.new(ptrlen) @@ -221,7 +221,7 @@ func (v Value) InFirst() (Value, error) { // InNext returns the next element // on the right-hand side of an IN constraint. // -// https://www.sqlite.org/c3ref/vtab_in_first.html +// https://sqlite.org/c3ref/vtab_in_first.html func (v Value) InNext() (Value, error) { defer v.c.arena.mark()() valPtr := v.c.arena.new(ptrlen) diff --git a/vtab.go b/vtab.go index b9a52fe..0455ead 100644 --- a/vtab.go +++ b/vtab.go @@ -78,7 +78,7 @@ func (c *Conn) DeclareVTab(sql string) error { // VTabConflictMode is a virtual table conflict resolution mode. // -// https://www.sqlite.org/c3ref/c_fail.html +// https://sqlite.org/c3ref/c_fail.html type VTabConflictMode uint8 const (