From 44c3f9b4e7d8907de677b1694ed779540071f144 Mon Sep 17 00:00:00 2001 From: Nuno Cruces Date: Sun, 22 Sep 2024 12:09:23 +0100 Subject: [PATCH] Coverage, docs. --- README.md | 9 ++++----- ext/bloom/bloom.go | 4 ---- ext/closure/closure.go | 25 +++++++++---------------- ext/closure/closure_test.go | 32 ++++++++++++++++++++++++++++++++ ext/fileio/fsdir.go | 21 +++++++++++++++------ util/vtabutil/arg.go | 2 ++ 6 files changed, 62 insertions(+), 31 deletions(-) diff --git a/README.md b/README.md index 698b50b..7da1f43 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ as well as direct access to most of the [C SQLite API](https://sqlite.org/cintro It wraps a [Wasm](https://webassembly.org/) [build](embed/) of SQLite, and uses [wazero](https://wazero.io/) as the runtime.\ -Go, wazero and [`x/sys`](https://pkg.go.dev/golang.org/x/sys) are the _only_ runtime dependencies [^1]. +Go, wazero and [`x/sys`](https://pkg.go.dev/golang.org/x/sys) are the _only_ runtime dependencies. ### Getting started @@ -49,6 +49,8 @@ db.QueryRow(`SELECT sqlite_version()`).Scan(&version) simplifies [incremental BLOB I/O](https://sqlite.org/c3ref/blob_open.html). - [`github.com/ncruces/go-sqlite3/ext/bloom`](https://pkg.go.dev/github.com/ncruces/go-sqlite3/ext/bloom) provides a [Bloom filter](https://github.com/nalgeon/sqlean/issues/27#issuecomment-1002267134) virtual table. +- [`github.com/ncruces/go-sqlite3/ext/closure`](https://pkg.go.dev/github.com/ncruces/go-sqlite3/ext/closure) + provides a transitive closure virtual table. - [`github.com/ncruces/go-sqlite3/ext/csv`](https://pkg.go.dev/github.com/ncruces/go-sqlite3/ext/csv) reads [comma-separated values](https://sqlite.org/csv.html). - [`github.com/ncruces/go-sqlite3/ext/fileio`](https://pkg.go.dev/github.com/ncruces/go-sqlite3/ext/fileio) @@ -128,7 +130,4 @@ The Wasm and VFS layers are also tested by running SQLite's - [`modernc.org/sqlite`](https://pkg.go.dev/modernc.org/sqlite) - [`crawshaw.io/sqlite`](https://pkg.go.dev/crawshaw.io/sqlite) - [`github.com/mattn/go-sqlite3`](https://pkg.go.dev/github.com/mattn/go-sqlite3) -- [`github.com/zombiezen/go-sqlite`](https://pkg.go.dev/github.com/zombiezen/go-sqlite) - -[^1]: anything else you find in `go.mod` is either a test dependency, - or needed by one of the extensions. +- [`github.com/zombiezen/go-sqlite`](https://pkg.go.dev/github.com/zombiezen/go-sqlite) \ No newline at end of file diff --git a/ext/bloom/bloom.go b/ext/bloom/bloom.go index 1df49d9..ec8bbd5 100644 --- a/ext/bloom/bloom.go +++ b/ext/bloom/bloom.go @@ -272,10 +272,6 @@ type cursor struct { } func (c *cursor) Filter(idxNum int, idxStr string, arg ...sqlite3.Value) error { - if len(arg) != 1 { - return nil - } - c.eof = false c.arg = &arg[0] blob := arg[0].RawBlob() diff --git a/ext/closure/closure.go b/ext/closure/closure.go index d586bb9..d2c41b7 100644 --- a/ext/closure/closure.go +++ b/ext/closure/closure.go @@ -1,6 +1,6 @@ // Package closure provides a transitive closure virtual table. // -// The "transitive_closure" virtual table finds the transitive closure of +// The transitive_closure virtual table finds the transitive closure of // a parent/child relationship in a real table. // // https://sqlite.org/src/doc/tip/ext/misc/closure.c @@ -24,6 +24,9 @@ const ( _COL_PARENTCOLUMN = 5 ) +// Register registers the transitive_closure virtual table: +// +// CREATE VIRTUAL TABLE temp.closure USING transitive_closure; func Register(db *sqlite3.Conn) error { return sqlite3.CreateModule(db, "transitive_closure", nil, func(db *sqlite3.Conn, _, _, _ string, arg ...string) (*closure, error) { @@ -76,9 +79,9 @@ type closure struct { func (c *closure) Destroy() error { return nil } func (c *closure) BestIndex(idx *sqlite3.IndexInfo) error { - posi := 1 plan := 0 - cost := 10000000.0 + posi := 1 + cost := 1e7 for i, cst := range idx.Constraint { if !cst.Usable { @@ -140,17 +143,11 @@ func (c *closure) BestIndex(idx *sqlite3.IndexInfo) error { } } - if c.table == "" && plan&0xf00 == 0 || + if plan&1 == 0 || + c.table == "" && plan&0xf00 == 0 || c.column == "" && plan&0xf000 == 0 || c.parent == "" && plan&0xf0000 == 0 { - plan = 0 - } - if plan&1 == 0 { - plan = 0 - cost *= 1e30 - for i := range idx.Constraint { - idx.ConstraintUsage[i].ArgvIndex = 0 - } + return sqlite3.CONSTRAINT } idx.EstimatedCost = cost @@ -173,10 +170,6 @@ type node struct { } func (c *cursor) Filter(idxNum int, idxStr string, arg ...sqlite3.Value) error { - if idxNum&1 == 0 { - return nil - } - root := arg[0].Int64() maxDepth := math.MaxInt if idxNum&0xf0 != 0 { diff --git a/ext/closure/closure_test.go b/ext/closure/closure_test.go index f1b3da6..cbe2d9c 100644 --- a/ext/closure/closure_test.go +++ b/ext/closure/closure_test.go @@ -24,6 +24,8 @@ func Example() { } defer db.Close() + closure.Register(db) + err = db.Exec(` CREATE TABLE employees ( id INTEGER PRIMARY KEY, @@ -150,3 +152,33 @@ func TestRegister(t *testing.T) { t.Fatal(err) } } + +func Test_errors(t *testing.T) { + t.Parallel() + + db, err := sqlite3.Open(":memory:") + if err != nil { + t.Fatal(err) + } + defer db.Close() + + err = db.Exec(`CREATE VIRTUAL TABLE hierarchy USING transitive_closure(table='employees')`) + if err == nil { + t.Error("want error") + } + + err = db.Exec(`CREATE VIRTUAL TABLE hierarchy USING transitive_closure(tablename='employees', tablename="employees")`) + if err == nil { + t.Error("want error") + } + + err = db.Exec("CREATE VIRTUAL TABLE hierarchy USING transitive_closure(tablename=`employees`)") + if err != nil { + t.Error(err) + } + + err = db.Exec(`SELECT * FROM hierarchy`) + if err == nil { + t.Error("want error") + } +} diff --git a/ext/fileio/fsdir.go b/ext/fileio/fsdir.go index 77d0d49..a8881b8 100644 --- a/ext/fileio/fsdir.go +++ b/ext/fileio/fsdir.go @@ -10,13 +10,22 @@ import ( "github.com/ncruces/go-sqlite3" ) +const ( + _COL_NAME = 0 + _COL_MODE = 1 + _COL_TIME = 2 + _COL_DATA = 3 + _COL_ROOT = 4 + _COL_BASE = 5 +) + type fsdir struct{ fsys fs.FS } func (d fsdir) BestIndex(idx *sqlite3.IndexInfo) error { var root, base bool for i, cst := range idx.Constraint { switch cst.Column { - case 4: // root + case _COL_ROOT: if !cst.Usable || cst.Op != sqlite3.INDEX_CONSTRAINT_EQ { return sqlite3.CONSTRAINT } @@ -25,7 +34,7 @@ func (d fsdir) BestIndex(idx *sqlite3.IndexInfo) error { ArgvIndex: 1, } root = true - case 5: // base + case _COL_BASE: if !cst.Usable || cst.Op != sqlite3.INDEX_CONSTRAINT_EQ { return sqlite3.CONSTRAINT } @@ -116,25 +125,25 @@ func (c *cursor) RowID() (int64, error) { func (c *cursor) Column(ctx sqlite3.Context, n int) error { switch n { - case 0: // name + case _COL_NAME: name := strings.TrimPrefix(c.curr.path, c.base) ctx.ResultText(name) - case 1: // mode + case _COL_MODE: i, err := c.curr.Info() if err != nil { return err } ctx.ResultInt64(int64(i.Mode())) - case 2: // mtime + case _COL_TIME: i, err := c.curr.Info() if err != nil { return err } ctx.ResultTime(i.ModTime(), sqlite3.TimeFormatUnixFrac) - case 3: // data + case _COL_DATA: switch typ := c.curr.Type(); { case typ.IsRegular(): var data []byte diff --git a/util/vtabutil/arg.go b/util/vtabutil/arg.go index 7f8fac9..566c730 100644 --- a/util/vtabutil/arg.go +++ b/util/vtabutil/arg.go @@ -26,6 +26,8 @@ func Unquote(val string) string { return val case '"': old, new = `""`, `"` + case '`': + old, new = "``", "`" case '\'': old, new = `''`, `'` }