Documentation
¶
Overview ¶
Package vtab defines a Go-facing API for implementing SQLite virtual table modules on top of the modernc.org/sqlite driver.
It is intentionally small and generic so that external projects can implement virtual tables without depending on the translated C internals.
Index ¶
- Constants
- Variables
- func RegisterModule(db *sql.DB, name string, m Module) error
- func SetRegisterFunc(fn func(name string, m Module) error)
- type Constraint
- type ConstraintOp
- type Context
- type Cursor
- type IndexInfo
- type Module
- type OrderBy
- type Renamer
- type Table
- type Transactional
- type Updater
- type Value
- type VolatileArgsOpter
Constants ¶
const ( // IndexScanUnique mirrors SQLITE_INDEX_SCAN_UNIQUE and indicates that the // chosen plan will visit at most one row. IndexScanUnique = 1 )
Index flag values for IndexInfo.IdxFlags.
Variables ¶
var ErrNotImplemented = errors.New("vtab: RegisterModule not wired into engine")
ErrNotImplemented is returned by RegisterModule when the underlying engine has not yet installed a registration hook. External projects can depend on the vtab API surface before the low-level bridge to sqlite3_create_module is fully wired; once the engine sets the hook via SetRegisterFunc, RegisterModule will forward calls to it.
Functions ¶
func RegisterModule ¶
RegisterModule registers a virtual table module with the provided *sql.DB.
Registration applies to new connections only. Existing open connections will not be updated to include newly registered modules.
Registration is performed when opening a new connection. If the underlying sqlite3_create_module_v2 call fails, opening the connection fails and returns that error. This fail-fast behavior prevents partially-initialized connections when a module cannot be installed.
The db parameter is currently unused by the engine; it is available so module implementations can capture it if they need a *sql.DB for their own internal queries.
func SetRegisterFunc ¶
SetRegisterFunc is intended to be called by the engine package to provide the concrete implementation of module registration. External callers should use RegisterModule instead.
Types ¶
type Constraint ¶
type Constraint struct {
Column int
Op ConstraintOp
Usable bool
// ArgIndex selects which position in argv[] (0-based) should contain the
// RHS value for this constraint when Filter is called. Set to -1 to ignore.
ArgIndex int
// Omit requests SQLite to omit the corresponding constraint from the
// parent query if the virtual table fully handles it.
Omit bool
}
Constraint describes a single WHERE-clause constraint that SQLite is considering pushing down to the virtual table.
type ConstraintOp ¶
type ConstraintOp int
ConstraintOp describes the operator used in a constraint on a virtual table column. It loosely mirrors the op field of sqlite3_index_constraint.
const ( // OpUnknown indicates an operator that is not recognized or not mapped. // Modules should treat this conservatively. OpUnknown ConstraintOp = iota OpEQ OpGT OpLE OpLT OpGE OpMATCH // "MATCH" operator (e.g. for FTS or KNN semantics) OpNE OpIS OpISNOT OpISNULL OpISNOTNULL OpLIKE OpGLOB OpREGEXP OpFUNCTION OpLIMIT OpOFFSET )
type Context ¶
type Context struct {
// contains filtered or unexported fields
}
Context carries information that a Module may need when creating or connecting a table instance. It intentionally does not expose *sql.DB to avoid leaking database/sql internals into the vtab API. Additional fields may be added in the future as needed.
func NewContext ¶
NewContext is used by the engine to create a Context bound to the current xCreate/xConnect call. External modules should not need to call this.
func NewContextWithConfig ¶ added in v1.45.0
func NewContextWithConfig(declare func(string) error, constraintSupport func() error, config func(op int32, args ...int32) error) Context
NewContextWithConfig is used by the engine to create a Context that can enable constraint support and other sqlite3_vtab_config options.
func NewContextWithConstraintSupport ¶ added in v1.45.0
func NewContextWithConstraintSupport(declare func(string) error, constraintSupport func() error) Context
NewContextWithConstraintSupport is used by the engine to create a Context that can enable constraint support.
func (Context) Config ¶ added in v1.45.0
Config forwards sqlite3_vtab_config options to SQLite. This must be called from within Create or Connect.
func (Context) Declare ¶
Declare must be called by a module from within Create or Connect to declare the schema of the virtual table. The provided SQL must be a CREATE TABLE statement describing the exposed columns.
The engine installs this callback so that the declaration is executed in the correct context. Calling Declare outside of Create/Connect may fail.
func (Context) EnableConstraintSupport ¶ added in v1.45.0
EnableConstraintSupport enables virtual table constraint support in SQLite. This must be called from within Create or Connect.
type Cursor ¶
type Cursor interface {
// Filter corresponds to xFilter. idxNum and idxStr are the chosen index
// number and string; vals are the constraint arguments. The vals slice
// and its entries are not valid past the return of this method;
// implementations must copy any value they wish to retain.
Filter(idxNum int, idxStr string, vals []Value) error
// Next advances the cursor to the next row (xNext).
Next() error
// Eof reports whether the cursor is past the last row (xEof != 0).
Eof() bool
// Column returns the value of the specified column in the current row
// (xColumn).
Column(col int) (Value, error)
// Rowid returns the current rowid (xRowid).
Rowid() (int64, error)
// Close closes the cursor (xClose).
Close() error
}
Cursor represents a cursor over a virtual table (sqlite3_vtab_cursor).
type IndexInfo ¶
type IndexInfo struct {
Constraints []Constraint
OrderBy []OrderBy
// IdxNum selects the query plan chosen in BestIndex. This value is passed
// back to Cursor.Filter. Note: SQLite stores this as a 32-bit signed
// integer (int32). Implementations must ensure IdxNum fits within the
// int32 range; values outside of int32 will cause an error in the driver
// to avoid silent truncation.
IdxNum int64
IdxStr string
// IdxFlags provides extra information about the chosen plan.
// Set to IndexScanUnique to indicate the plan visits at most one row.
IdxFlags int
OrderByConsumed bool
EstimatedCost float64
EstimatedRows int64
// ColUsed is a bitmask indicating which columns are used by the query.
// Bit N is set if column N is referenced.
ColUsed uint64
}
IndexInfo holds information about constraints and orderings for a virtual table query. It is the Go analogue of sqlite3_index_info.
type Module ¶
type Module interface {
// Create is called to create a new virtual table. args corresponds to the
// argv array passed to xCreate in the SQLite C API: it contains the module
// name, the database name, the table name, and module arguments.
Create(ctx Context, args []string) (Table, error)
// Connect is called to connect to an existing virtual table. Its
// semantics mirror xConnect in the SQLite C API.
Connect(ctx Context, args []string) (Table, error)
}
Module represents a virtual table module, analogous to sqlite3_module in the SQLite C API. Implementations are responsible for creating and connecting table instances.
type Table ¶
type Table interface {
// BestIndex allows the virtual table to inform SQLite about which
// constraints and orderings it can efficiently support. The IndexInfo
// structure mirrors sqlite3_index_info.
BestIndex(info *IndexInfo) error
// Open creates a new cursor for scanning the table.
Open() (Cursor, error)
// Disconnect is called to disconnect from a table instance (xDisconnect).
Disconnect() error
// Destroy is called when a table is dropped (xDestroy).
Destroy() error
}
Table represents a single virtual table instance (the Go analogue of sqlite3_vtab and its associated methods).
type Transactional ¶
type Transactional interface {
Begin() error
Sync() error
Commit() error
Rollback() error
Savepoint(i int) error
Release(i int) error
RollbackTo(i int) error
}
Transactional can be implemented by a Table to handle transaction-related callbacks. Methods are optional; unimplemented methods are treated as no-op.
type Updater ¶
type Updater interface {
Insert(cols []Value, rowid *int64) error
Update(oldRowid int64, cols []Value, newRowid *int64) error
Delete(oldRowid int64) error
}
Updater can be implemented by a Table to support writes via xUpdate.
Semantics follow SQLite's xUpdate:
- Delete: Delete(oldRowid) is called.
- Insert: Insert(cols, rowid) is called. *rowid may contain a desired rowid (if provided by SQL) and should be set to the final rowid of the new row.
- Update: Update(oldRowid, cols, newRowid) is called. *newRowid may be set to the final rowid of the updated row when changed.
For Insert and Update, the cols slice and its entries are not valid past the return of the method; implementations must copy any value they wish to retain.
type Value ¶
Value is the value type passed to and from virtual table cursors. It aliases database/sql/driver.Value to avoid exposing low-level details to module authors while remaining compatible with the driver.
type VolatileArgsOpter ¶ added in v1.51.0
type VolatileArgsOpter interface {
VolatileArgs() bool
}
VolatileArgsOpter is an optional interface implemented by a Module to opt into zero-copy access for string and []byte values passed to Cursor.Filter and Updater.Insert / Updater.Update. When VolatileArgs returns true, the engine hands those arguments to the module as direct views into SQLite-owned memory instead of Go-allocated copies, saving one allocation per TEXT or BLOB argument per row.
The opt-in is read once when the module is registered on a connection and is sticky for the lifetime of that registration; it covers every Filter, Insert, and Update call routed to tables created from this module.
The safety contract mirrors modernc.org/sqlite.FunctionImpl.VolatileArgs:
The string and []byte values inside the vals / cols slice are valid only for the duration of the call. They must not be retained past the return of the method, directly (stored in a struct, map, channel, or outer-scope variable) or indirectly (passed to anything that captures them).
Retaining a volatile argument produces silent data corruption: SQLite reuses the underlying buffer for the next row, so a retained value will later appear to hold a different row's bytes. The race detector cannot catch this; callbacks run sequentially on a single goroutine.
To keep a value across rows, copy it:
saved := append([]byte(nil), v.([]byte)...) // BLOB saved := string(append([]byte(nil), v.(string)...)) // TEXT, no aliasing
Do not re-enter SQLite on the same connection while a volatile argument is in scope. A nested Query/Exec can cause SQLite to reuse the underlying value buffers, so a volatile string or []byte read before the nested call may alias different bytes after it returns.
VolatileArgs has no effect on integer, float, time, or NULL arguments. When in doubt, leave it returning false: the engine already pools the argument-slice header, so the per-row overhead in the default mode is one libc.GoString per TEXT column and one make([]byte) per BLOB column, not a fresh slice header.