Limits, and tweaks.

This commit is contained in:
Nuno Cruces
2024-01-18 15:53:00 +00:00
parent 9f58a5d669
commit d998b5f36c
10 changed files with 72 additions and 9 deletions

View File

@@ -113,7 +113,7 @@ with `nolock=1` you must disable connection pooling by calling
### Testing ### Testing
This project aims for [high test coverage](https://github.com/ncruces/go-sqlite3/wiki/Test-coverage-report). 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. [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 The pure Go VFS is tested by running SQLite's

View File

@@ -121,7 +121,7 @@ func (b *Backup) Step(nPage int) (done bool, err error) {
// https://sqlite.org/c3ref/backup_finish.html#sqlite3backupremaining // https://sqlite.org/c3ref/backup_finish.html#sqlite3backupremaining
func (b *Backup) Remaining() int { func (b *Backup) Remaining() int {
r := b.c.call("sqlite3_backup_remaining", uint64(b.handle)) 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 // 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 // https://sqlite.org/c3ref/backup_finish.html#sqlite3backuppagecount
func (b *Backup) PageCount() int { func (b *Backup) PageCount() int {
r := b.c.call("sqlite3_backup_pagecount", uint64(b.handle)) r := b.c.call("sqlite3_backup_pagecount", uint64(b.handle))
return int(r) return int(int32(r))
} }

View File

@@ -35,7 +35,7 @@ func (c *Conn) Config(op DBConfig, arg ...bool) (bool, error) {
// ConfigLog sets up the error logging callback for the connection. // 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 { func (c *Conn) ConfigLog(cb func(code ExtendedErrorCode, msg string)) error {
var enable uint64 var enable uint64
if cb != nil { if cb != nil {
@@ -55,3 +55,12 @@ func logCallback(ctx context.Context, mod api.Module, _, iCode, zMsg uint32) {
c.log(xErrorCode(iCode), msg) 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))
}

View File

@@ -217,7 +217,7 @@ func (c *Conn) ReadOnly(schema string) (ro bool, ok bool) {
ptr = c.arena.string(schema) ptr = c.arena.string(schema)
} }
r := c.call("sqlite3_db_readonly", uint64(c.handle), uint64(ptr)) 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. // GetAutocommit tests the connection for auto-commit mode.

View File

@@ -229,6 +229,26 @@ const (
DBCONFIG_REVERSE_SCANORDER DBConfig = 1019 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]. // TxnState are the allowed return values from [Conn.TxnState].
// //
// https://sqlite.org/c3ref/c_txn_none.html // https://sqlite.org/c3ref/c_txn_none.html

View File

@@ -222,7 +222,7 @@ func (ctx Context) ResultError(err error) {
// VTabNoChange may return true if a column is being fetched as part // VTabNoChange may return true if a column is being fetched as part
// of an update during which the column value will not change. // 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 { func (ctx Context) VTabNoChange() bool {
r := ctx.c.call("sqlite3_vtab_nochange", uint64(ctx.handle)) r := ctx.c.call("sqlite3_vtab_nochange", uint64(ctx.handle))
return r != 0 return r != 0

View File

@@ -111,6 +111,9 @@ func (s *Stmt) Exec() error {
// //
// https://sqlite.org/c3ref/stmt_status.html // https://sqlite.org/c3ref/stmt_status.html
func (s *Stmt) Status(op StmtStatus, reset bool) int { func (s *Stmt) Status(op StmtStatus, reset bool) int {
if op > STMTSTATUS_FILTER_HIT && op != STMTSTATUS_MEMUSED {
return 0
}
var i uint64 var i uint64
if reset { if reset {
i = 1 i = 1

View File

@@ -3,6 +3,7 @@ package tests
import ( import (
"context" "context"
"errors" "errors"
"math"
"os" "os"
"path/filepath" "path/filepath"
"reflect" "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) { func TestConn_ReleaseMemory(t *testing.T) {
t.Parallel() t.Parallel()

View File

@@ -204,7 +204,7 @@ func (v Value) NoChange() bool {
// InFirst returns the first element // InFirst returns the first element
// on the right-hand side of an IN constraint. // 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) { func (v Value) InFirst() (Value, error) {
defer v.c.arena.mark()() defer v.c.arena.mark()()
valPtr := v.c.arena.new(ptrlen) valPtr := v.c.arena.new(ptrlen)
@@ -221,7 +221,7 @@ func (v Value) InFirst() (Value, error) {
// InNext returns the next element // InNext returns the next element
// on the right-hand side of an IN constraint. // 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) { func (v Value) InNext() (Value, error) {
defer v.c.arena.mark()() defer v.c.arena.mark()()
valPtr := v.c.arena.new(ptrlen) valPtr := v.c.arena.new(ptrlen)

View File

@@ -78,7 +78,7 @@ func (c *Conn) DeclareVTab(sql string) error {
// VTabConflictMode is a virtual table conflict resolution mode. // 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 type VTabConflictMode uint8
const ( const (