diff --git a/ext/bloom/bloom.go b/ext/bloom/bloom.go index 1d1c32e..71d4bc7 100644 --- a/ext/bloom/bloom.go +++ b/ext/bloom/bloom.go @@ -87,6 +87,9 @@ func create(db *sqlite3.Conn, _, schema, table string, arg ...string) (_ *bloom, return nil, err } + id := db.LastInsertRowID() + defer db.SetLastInsertRowID(id) + err = db.Exec(fmt.Sprintf( `INSERT INTO %s.%s (rowid, data, p, n, m, k) VALUES (1, zeroblob(%d), %f, %d, %d, %d)`, @@ -163,13 +166,14 @@ func (b *bloom) BestIndex(idx *sqlite3.IndexInfo) error { if cst.Usable && cst.Column == 1 && cst.Op == sqlite3.INDEX_CONSTRAINT_EQ { idx.ConstraintUsage[n].ArgvIndex = 1 + idx.OrderByConsumed = true + idx.EstimatedRows = 1 + idx.EstimatedCost = float64(b.hashes) + idx.IdxFlags = sqlite3.INDEX_SCAN_UNIQUE + return nil } } - idx.OrderByConsumed = true - idx.EstimatedRows = 1 - idx.EstimatedCost = float64(b.hashes) - idx.IdxFlags = sqlite3.INDEX_SCAN_UNIQUE - return nil + return sqlite3.CONSTRAINT } func (b *bloom) Update(arg ...sqlite3.Value) (rowid int64, err error) { diff --git a/ext/csv/csv.go b/ext/csv/csv.go index b5fbcc6..027def4 100644 --- a/ext/csv/csv.go +++ b/ext/csv/csv.go @@ -96,7 +96,11 @@ func RegisterFS(db *sqlite3.Conn, fsys fs.FS) { } schema = getSchema(header, columns, row) } else { - table.typs = getColumnAffinities(schema) + defer func() { + if err == nil { + table.typs, err = getColumnAffinities(schema) + } + }() } err = db.DeclareVTab(schema) diff --git a/ext/csv/types.go b/ext/csv/types.go index 967b4e9..e7697e5 100644 --- a/ext/csv/types.go +++ b/ext/csv/types.go @@ -17,10 +17,10 @@ const ( real affinity = 4 ) -func getColumnAffinities(schema string) []affinity { +func getColumnAffinities(schema string) ([]affinity, error) { tab, err := vtabutil.Parse(schema) if err != nil { - return nil + return nil, err } defer tab.Close() @@ -29,7 +29,7 @@ func getColumnAffinities(schema string) []affinity { col := tab.Column(i) types[i] = getAffinity(col.Type()) } - return types + return types, nil } func getAffinity(declType string) affinity { diff --git a/util/vtabutil/parse.go b/util/vtabutil/parse.go index 56154ca..0b40772 100644 --- a/util/vtabutil/parse.go +++ b/util/vtabutil/parse.go @@ -12,8 +12,13 @@ import ( ) const ( - code = 4 - base = 8 + _NONE = iota + _MEMORY + _SYNTAX + _UNSUPPORTEDSQL + + codeptr = 4 + baseptr = 8 ) var ( @@ -22,6 +27,7 @@ var ( ctx context.Context once sync.Once runtime wazero.Runtime + module wazero.CompiledModule ) // Table holds metadata about a table. @@ -35,32 +41,38 @@ type Table struct { // // [CREATE]: https://sqlite.org/lang_createtable.html // [ALTER TABLE]: https://sqlite.org/lang_altertable.html -func Parse(sql string) (*Table, error) { +func Parse(sql string) (_ *Table, err error) { once.Do(func() { ctx = context.Background() cfg := wazero.NewRuntimeConfigInterpreter().WithDebugInfoEnabled(false) runtime = wazero.NewRuntimeWithConfig(ctx, cfg) + module, err = runtime.CompileModule(ctx, binary) }) - - mod, err := runtime.InstantiateWithConfig(ctx, binary, wazero.NewModuleConfig().WithName("")) if err != nil { return nil, err } - if buf, ok := mod.Memory().Read(base, uint32(len(sql))); ok { + mod, err := runtime.InstantiateModule(ctx, module, wazero.NewModuleConfig().WithName("")) + if err != nil { + return nil, err + } + + if buf, ok := mod.Memory().Read(baseptr, uint32(len(sql))); ok { copy(buf, sql) } - r, err := mod.ExportedFunction("sql3parse_table").Call(ctx, base, uint64(len(sql)), code) + r, err := mod.ExportedFunction("sql3parse_table").Call(ctx, baseptr, uint64(len(sql)), codeptr) if err != nil { return nil, err } - c, _ := mod.Memory().ReadUint32Le(code) - if c == uint32(_MEMORY) { + c, _ := mod.Memory().ReadUint32Le(codeptr) + switch c { + case _MEMORY: panic(util.OOMErr) - } - if c != uint32(_NONE) { - return nil, ecode(c) + case _SYNTAX: + return nil, util.ErrorString("sql3parse: invalid syntax") + case _UNSUPPORTEDSQL: + return nil, util.ErrorString("sql3parse: unsupported SQL") } if r[0] == 0 { return nil, nil @@ -102,6 +114,15 @@ func (t *Table) Column(i int) Column { } } +func (t *Table) string(ptr uint32) string { + if ptr == 0 { + return "" + } + off, _ := t.mod.Memory().ReadUint32Le(ptr + 0) + len, _ := t.mod.Memory().ReadUint32Le(ptr + 4) + return t.sql[off-baseptr : off+len-baseptr] +} + // Column holds metadata about a column. type Column struct { tab *Table @@ -116,30 +137,5 @@ func (c Column) Type() string { if err != nil { panic(err) } - if r[0] == 0 { - return "" - } - off, _ := c.tab.mod.Memory().ReadUint32Le(uint32(r[0]) + 0) - len, _ := c.tab.mod.Memory().ReadUint32Le(uint32(r[0]) + 4) - return c.tab.sql[off-base : off+len-base] -} - -type ecode uint32 - -const ( - _NONE ecode = iota - _MEMORY - _SYNTAX - _UNSUPPORTEDSQL -) - -func (e ecode) Error() string { - switch e { - case _SYNTAX: - return "sql3parse: invalid syntax" - case _UNSUPPORTEDSQL: - return "sql3parse: unsupported SQL" - default: - panic(util.AssertErr()) - } + return c.tab.string(uint32(r[0])) }