246 lines
8.4 KiB
Go
246 lines
8.4 KiB
Go
package vtab
|
|
|
|
import (
|
|
"database/sql"
|
|
"database/sql/driver"
|
|
"errors"
|
|
)
|
|
|
|
// 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 Value = driver.Value
|
|
|
|
// 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.
|
|
type Context struct {
|
|
declare func(string) error
|
|
}
|
|
|
|
// 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 (c Context) Declare(schema string) error {
|
|
if c.declare == nil {
|
|
return errors.New("vtab: declare not available in this context")
|
|
}
|
|
return c.declare(schema)
|
|
}
|
|
|
|
// 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 NewContext(declare func(string) error) Context { return Context{declare: declare} }
|
|
|
|
// 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 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)
|
|
}
|
|
|
|
// Table represents a single virtual table instance (the Go analogue of
|
|
// sqlite3_vtab and its associated methods).
|
|
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
|
|
}
|
|
|
|
// Renamer can be implemented by a Table to handle xRename.
|
|
type Renamer interface {
|
|
Rename(newName string) error
|
|
}
|
|
|
|
// Transactional can be implemented by a Table to handle transaction-related
|
|
// callbacks. Methods are optional; unimplemented methods are treated as no-op.
|
|
type Transactional interface {
|
|
Begin() error
|
|
Sync() error
|
|
Commit() error
|
|
Rollback() error
|
|
Savepoint(i int) error
|
|
Release(i int) error
|
|
RollbackTo(i int) error
|
|
}
|
|
|
|
// Cursor represents a cursor over a virtual table (sqlite3_vtab_cursor).
|
|
type Cursor interface {
|
|
// Filter corresponds to xFilter. idxNum and idxStr are the chosen index
|
|
// number and string; vals are the constraint arguments.
|
|
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
|
|
}
|
|
|
|
// 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.
|
|
type Updater interface {
|
|
Insert(cols []Value, rowid *int64) error
|
|
Update(oldRowid int64, cols []Value, newRowid *int64) error
|
|
Delete(oldRowid int64) error
|
|
}
|
|
|
|
// ConstraintOp describes the operator used in a constraint on a virtual
|
|
// table column. It loosely mirrors the op field of sqlite3_index_constraint.
|
|
type ConstraintOp int
|
|
|
|
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
|
|
)
|
|
|
|
// Constraint describes a single WHERE-clause constraint that SQLite is
|
|
// considering pushing down to the virtual table.
|
|
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
|
|
}
|
|
|
|
// OrderBy describes a single ORDER BY term for a query involving a virtual
|
|
// table.
|
|
type OrderBy struct {
|
|
Column int
|
|
Desc bool
|
|
}
|
|
|
|
// IndexInfo holds information about constraints and orderings for a virtual
|
|
// table query. It is the Go analogue of sqlite3_index_info.
|
|
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
|
|
}
|
|
|
|
// Index flag values for IndexInfo.IdxFlags.
|
|
const (
|
|
// IndexScanUnique mirrors SQLITE_INDEX_SCAN_UNIQUE and indicates that the
|
|
// chosen plan will visit at most one row.
|
|
IndexScanUnique = 1
|
|
)
|
|
|
|
// 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.
|
|
var ErrNotImplemented = errors.New("vtab: RegisterModule not wired into engine")
|
|
|
|
// registerHook is installed by the engine package via SetRegisterFunc. It is
|
|
// invoked by RegisterModule to perform the actual module registration.
|
|
var registerHook func(name string, m Module) error
|
|
|
|
// SetRegisterFunc is intended to be called by the engine package to provide
|
|
// the concrete implementation of module registration. External callers
|
|
// should use RegisterModule instead.
|
|
func SetRegisterFunc(fn func(name string, m Module) error) { registerHook = fn }
|
|
|
|
// 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 RegisterModule(db *sql.DB, name string, m Module) error {
|
|
_ = db
|
|
if registerHook == nil {
|
|
return ErrNotImplemented
|
|
}
|
|
if name == "" {
|
|
return errors.New("vtab: module name must be non-empty")
|
|
}
|
|
if m == nil {
|
|
return errors.New("vtab: module implementation is nil")
|
|
}
|
|
return registerHook(name, m)
|
|
}
|