Documentation ¶
Overview ¶
Package sqlite implements a driver over the SQLite database engine.
To get started, the Open and Conn.Exec methods should be sufficient, but do note that OpenPool is used throughout the server code.
The package API is designed to expose as possible of the underlying engine as possible, including the option to expose functions and data structures via Register.
Example ¶
A quick tour of the most important functions in the library
ctn, err := Open(MemoryPath) if err != nil { log.Fatal("cannot open a temporary database", err) } ctx := context.Background() sts := []string{ "create table tbl1 (a primary key, b)", "insert into tbl1 (a, b) values ('hello','world'), ('bonjour','monde'), ('hola','mundo')", } for _, st := range sts { // Exec can be used for direct statements if err := ctn.Exec(ctx, st).Err(); err != nil { log.Fatal("cannot create table", err) } } // Scan to iterate over all results rows := ctn.Exec(ctx, "select a, b from tbl1") for rows.Next() { var a, b string rows.Scan(&a, &b) fmt.Println(a, b) } if rows.Err() != nil { log.Fatal("cannot query tbl1", rows.Err()) } // ScanOne is a handy shortcut var b string if err := ctn.Exec(ctx, "select b from tbl1 where a = ?", "bonjour").ScanOne(&b); err != nil { log.Fatal("cannot find value matching \"bonjour\"") } fmt.Println(b)
Output: hello world bonjour monde hola mundo monde
Index ¶
- Constants
- Variables
- func BackupDB(pool *Connections, dest string) error
- func GetBuffer() *bytes.Buffer
- func HashString(s string) int64
- func IsComplete(st string) bool
- func LoadExtensions(db SQLITE3)
- func Register(f ...func(SQLITE3))
- func RegisterConstant(name string, value any) func(SQLITE3)
- func RegisterFunc(name string, t any) func(SQLITE3)
- func RegisterTable[T Filtrer[T]](name string, vt T, opts ...VirtualTableOption) func(SQLITE3)
- func RememberQuery(q string)
- func ReturnBuffer(b *bytes.Buffer)
- type ColStrings
- type Conn
- func (c *Conn) Close() error
- func (cn *Conn) CreateBLOB(database, table, column string, size int) (*DirectBLOB, error)
- func (c *Conn) Exec(ctx context.Context, cmd string, args ...any) *Rows
- func (cn *Conn) OpenBLOB(database, table, column string, rownum int) (*DirectBLOB, error)
- func (ctn *Conn) Release(ctx context.Context) error
- func (ctn *Conn) Rollback(ctx context.Context) error
- func (ctn *Conn) Savepoint(ctx context.Context) (context.Context, error)
- type Connections
- func (p *Connections) Close() error
- func (p *Connections) Exec(ctx context.Context, cmd string, args ...any) *Rows
- func (p *Connections) Release(ctx context.Context) error
- func (p *Connections) Rollback(ctx context.Context) error
- func (p *Connections) Savepoint(ctx context.Context) (context.Context, error)
- type Constraint
- type Constraints
- type DirectBLOB
- type Filtrer
- type Hasher
- type IndexState
- type Indexer
- type Iter
- type MultiString
- type NullString
- type Op
- type PointerValue
- type Rows
- func (r *Rows) Bytecode() string
- func (rows *Rows) ColumnName(i int) string
- func (r *Rows) Err() error
- func (r *Rows) ExplainQueryPlan() string
- func (r *Rows) IsExplain() bool
- func (rows *Rows) Next() bool
- func (rows *Rows) NumColumn() int
- func (rows *Rows) Scan(dst ...any)
- func (r *Rows) ScanOne(dst ...any) error
- type SQLITE3
- type Updater
- type VirtualTableOption
Examples ¶
Constants ¶
const MemoryPath = "file::memory:?mode=memory"
const NoMoreRow C.int = -1
Variables ¶
var BusyTransaction = errText[C.SQLITE_BUSY]
var DBName = "main"
DBName is the name of the database used in the application (main by default).
It can be changed before a back-up is started to reflect the application specificities.
var DuplicateRecords = errors.New("duplicate records in scanone")
var ErrNull = errors.New("null SQL value")
ErrNull is a sentinel value that can be returned by functions that want to return (or otherwise handle) null values
var HashSeed = maphash.MakeSeed()
HashSeed is a convenience seed to use when implementing the Hasher interface.
var NumThreads = 32
var ObsLastQueries = RegisterTable("obs_lastqueries", queryRecord{})
Functions ¶
func BackupDB ¶
func BackupDB(pool *Connections, dest string) error
BackupDB performs an [online backup] to dest
[online backup] https://www.sqlite.org/backup.html
func GetBuffer ¶
GetBuffer returns a bytes buffer from a system-wide pool. It is useful to avoid too much allocation when serializing. The buffer must be release with ReturnBuffer.
func HashString ¶
HashString returns a unique hash, valid as an index
func IsComplete ¶
IsComplete returns true iff the statement is complete
func LoadExtensions ¶
func LoadExtensions(db SQLITE3)
LoadExtensions loads all registered extensions against the database db. This function is automatically called when Open is, and is made available only for modules compiled as a shared library.
func Register ¶
func Register(f ...func(SQLITE3))
Register adds a new statically compiled extension to register when a new SQLite connection is opened. It should be called in a sub-package init function.
Most of the time, f is going to be returned from RegisterTable or RegisterFunc.
If not, due to the way CGO handles names bug-13467, callers need to wrap this in an unsafe pointer:
(*C.sqlite3)(unsafe.Pointer(db))
func RegisterConstant ¶
RegisterConstants creates a virtual table which always returns the same value
func RegisterFunc ¶
RegisterFunc binds a Go function as an application-defined SQL function. Functions can only be exposed if:
- Their inputs arguments are integer, text, bytes or a pointer to an arbitrary type
- Their name is not a registered SQL keyword
- They return either (1) no argument, (2) a single argument of type integer, text, bytes, error or PointerValue or (3) 2 arguments of which the first argument must be of type integer, text, bytes or PointerValue the second argument must be of type erorr.
func RegisterTable ¶
func RegisterTable[T Filtrer[T]](name string, vt T, opts ...VirtualTableOption) func(SQLITE3)
RegisterTable creates a virtual table at name. The virtual table definition is read from T, see Filtrer for more details.
Example ¶
Virtual tables can represent much more than data on disk
In this example a virtual table is used to get the time in a local-dependent fashion.
package main import ( "context" "fmt" "log" "time" "github.com/TroutSoftware/sqlite" ) // whatTimeIsIt defines the structure of the virtual table. // Location is set as: // - filtered, so it will be made available to the Filter method // - hidden, so it can be called as a function in SQL // // Typical code will want to annotate those structures with a call to generate accessors to all interesting fields. // // //go:generate constrainer whatTimeIsIt type whatTimeIsIt struct { Location string `vtab:"location,filtered,hidden"` Time string `vtab:"time"` } var epoch = time.Date(2000, time.January, 1, 0, 0, 0, 0, time.UTC) // WhatTimeIsItAPI provides the (very important) service to know a point in time at a given location. func WhatTimeIsItAPI(where *time.Location) string { return epoch.In(where).Format(time.Kitchen) } // This function is typically generated in its own file whatTimeIsIt_access.go. func (r whatTimeIsIt) GetLocation(cs sqlite.Constraints) (v string, ok bool) { match := 0 for _, c := range cs { if c.Column == "location" { if c.Operation == sqlite.ConstraintEQ { v = c.ValueString() match++ } else { panic("Value getter with non-constrained values") } } } if match == 0 { return v, false } if match == 1 { return v, true } panic("more than one match") } func (wtc whatTimeIsIt) Filter(_ int, cs sqlite.Constraints) sqlite.Iter[whatTimeIsIt] { loc := time.UTC if lspec, ok := wtc.GetLocation(cs); ok { var err error if loc, err = time.LoadLocation(lspec); err != nil { return sqlite.FromError[whatTimeIsIt](err) } } now := WhatTimeIsItAPI(loc) return sqlite.FromOne(whatTimeIsIt{ Location: loc.String(), Time: now, }) } /* Virtual tables can represent much more than data on disk In this example a virtual table is used to get the time in a local-dependent fashion. */ func main() { db, err := sqlite.Open(sqlite.MemoryPath, sqlite.RegisterTable("whattimeisit", whatTimeIsIt{})) if err != nil { log.Fatal(err) } var kitchenclock string err = db.Exec(context.Background(), "select time from whattimeisit('Europe/Dublin')").ScanOne(&kitchenclock) if err != nil { log.Fatal(err) } fmt.Println("epoch time", kitchenclock) }
Output: epoch time 12:00AM
func RememberQuery ¶
func RememberQuery(q string)
RememberQuery can be used to expose any SQL query to the internal `obs_lastqueries` virtual table. The last 30 queries will be displayed there. This is particularly useful when debugging automatically generated queries.
func ReturnBuffer ¶
ReturnBuffer returns the buffer to the pool. The buffer must not be used afterwards (this also mean the bytes returned from [bytes.buffer.Bytes]).
Types ¶
type ColStrings ¶
ColStrings augments MultiString with the name of the columns. the pointer type is required to comply with Go’s addressability constraints on maps.
type Conn ¶
type Conn struct {
// contains filtered or unexported fields
}
Conn is a connection to a given database. Conn is safe for concurrent use.
Internally, Conn maps to an sqlite3 object, and present the same characteristics when not documented otherwise.
func (*Conn) CreateBLOB ¶
func (cn *Conn) CreateBLOB(database, table, column string, size int) (*DirectBLOB, error)
CreateBLOB creates a new incremental I/O buffer. The database, table, and column are only quoted, and therefore should not be from untrusted inputs.
func (*Conn) Exec ¶
Exec executes cmd, optionally binding args to parameters in the query. Rows are a a cursor over the results, and the first row is already executed: commands needs not call Rows.Next afterwards. Arguments are matched by position.
This function will panic if the command is invalid SQL. This is intented for static SQL statements (written in your code), not for untrusted SQL.
func (*Conn) OpenBLOB ¶
func (cn *Conn) OpenBLOB(database, table, column string, rownum int) (*DirectBLOB, error)
func (*Conn) Rollback ¶
Rollback returns the given savepoint. It is safe to call this after Rollback
func (*Conn) Savepoint ¶
Savepoint creates a new savepoint in transaction (think about begin). It is the responsibility of the caller to Conn.Release it.
Most of the time, the implementation of [Pool.Savepoint] should be preferred.
type Connections ¶
type Connections struct {
// contains filtered or unexported fields
}
func OpenPool ¶
func OpenPool(name string, exts ...func(SQLITE3)) (*Connections, error)
OpenPool ceates a new connection pool TODO(rdo) check if WAL by default is right
func (*Connections) Close ¶
func (p *Connections) Close() error
Close closes all connections in the pool. It can be safely called concurrently Connections.Savepoint, Connections.Exec and Connections.Release but note that calls to Connections.Savepoint or Connections.Exec that happen after Close might block forever. The mechanism to terminate other connections has to be done out of band.
type Constraint ¶
type Constraint struct { Column string Operation Op Value any // contains filtered or unexported fields }
A constraint is a filter (technically can also be a join, …) passed by the engine to the API implementation. The Value is only set at filter stage, not when the plan is created. Following SQLite weakly typed system, it is not possible to know the Value without checking its type explicitly, or instead using the accessor functions.
func (Constraint) ValueBool ¶
func (v Constraint) ValueBool() bool
func (Constraint) ValueBytes ¶
func (v Constraint) ValueBytes() []byte
func (Constraint) ValueInt ¶
func (v Constraint) ValueInt() int
func (Constraint) ValueString ¶
func (v Constraint) ValueString() string
type Constraints ¶
type Constraints []Constraint
Constraints is a type wrapper around an array of constraints.
type DirectBLOB ¶
type DirectBLOB struct { RowNumber int // contains filtered or unexported fields }
ReadWriterAt is an incremental I/O buffer. It is mostly useful to work directly with binary formats in SQLite – so akin to mmap.
func (*DirectBLOB) Close ¶
func (r *DirectBLOB) Close() error
func (*DirectBLOB) Size ¶
func (r *DirectBLOB) Size() int
type Filtrer ¶
type Filtrer[T any] interface { Filter(int, Constraints) Iter[T] }
A resource represents an abstraction over a signal that can be represented as a virtual table.
The T type is bound in the RegisterTable method to the values returned in the iterator. We use tags to associate values automatically with virtual table columns:
// Field will be bound as column “myName” Field int `vtab:"myName"` // Field will be bound as “myName” and // the query will fail if “myName” is not specified in the WHERE clause Field int `vtab:"myName,required" // Field is ignored by this package Field int `vtab:"-"
The columns values allocated by standard SQLite affinity.
Query plan ¶
Resources interact with query plan generation through a simple algorithm: each column filtered reduces by a constant factor the cost of the query (thus having multiple columns filtered increases the chance of the plan being selected). Filtered, required and hidden field are considered equal for the query plan. Furthermore, only ConstraintEQ match are accepted.
For more control over the plan, implement directly Indexer instead.
Concurrency ¶
Filter is called against the global variable used in registering the virtual table. The global variable can be used to set constant values, including channels if needed.
Mutation in the global variable should be avoided in the general case; the implementation does not try to limit or otherwise protect concurrent access.
Avoid blocking in the Filter method, as this would impact all virtual tables. Instead, if a form of rate limit is required, prefer returning a well-known error.
type Hasher ¶
type Hasher interface {
Hash64() int64
}
Hasher can be implemented by resources that exhibit referential transparency.
Databases use the idea of a primary key (or row ID), a unique identifier for the relation. When the relation is dynamic, it is not generally possible to express this identity; and by default, all relations in a virtual table are considered to have a different primary key. Implementations that want to overwrite this behavior should implement this interface.
type IndexState ¶
IndexState is used by the indexer to specify fields for a given access
type Indexer ¶
type Indexer interface { // The [IndexState] parameter will configure which constraints are passed to [Filter]: // - the index number (arbitrary from the point of the of the driver) set by [IndexState.SelectIndex] // - the arguments from columns in the order from [IndexState.Use] BestIndex(IndexState, Constraints) (estimatedCost, estimatedRows int64) }
Indexer allow types to implement their own index access method.
type Iter ¶
Iter represents an iterator over multiple results of an API. Implementations must be safe for concurrent use. The iterator is going to be primed by calling Next before any value is requested.
func TransformArray ¶
TransformArray iterates over applying trans to values
type MultiString ¶
type MultiString []string
MultiString is used to read all values of a statement as string. This is useful if you don’t know ahead of time the values returned.
type NullString ¶
type NullString string
NullString as a marker type: strings of length 0 will be set as null instead
type Op ¶
type Op uint8
Op are the type of filtering operations that the engine can ask
const ( ConstraintEQ Op = 2 ConstraintGT Op = 4 ConstraintLE Op = 8 ConstraintLT Op = 16 ConstraintGE Op = 32 ConstraintMATCH Op = 64 ConstraintLIKE Op = 65 ConstraintGLOB Op = 66 ConstraintREGEXP Op = 67 ConstraintNE Op = 68 ConstraintISNOT Op = 69 ConstraintISNOTNULL Op = 70 ConstraintISNULL Op = 71 ConstraintIS Op = 72 ConstraintLIMIT Op = 73 ConstraintOFFSET Op = 74 ConstraintFUNCTION Op = 150 )
type PointerValue ¶
type PointerValue struct {
// contains filtered or unexported fields
}
Marker type for values that should be passed as pointer. Most users will prefer AsPointer.
func AsPointer ¶
func AsPointer(v any) PointerValue
AsPointer is used to pass the value using the SQLite pointer passing interface. This interface is only useful for reading the value in virtual tables or functions. The “pointer type” parameter will be derived from the underlying data type name.
type Rows ¶
type Rows struct {
// contains filtered or unexported fields
}
Rows are iterator structure over the underlying database statement results.
Rows are lightweigth object, and should not be reused after Rows.Err or Rows.ScanOne calls.
func (*Rows) Bytecode ¶
Bytecodes outputs the detailed query plan through the SQLite bytecode operands. This is a fairly low-level operation, and is only really useful for troubleshooting. Even then, Rows.ExplainQueryPlan is usually a much better option.
func (*Rows) ColumnName ¶
ColumnName return the ith (zero-based) column name. This is mostly convenient in a loop:
for i := 0; i < st.NumColumn(); i++ { buf.WriteString(st.ColumnName(i) + "\t") }
func (*Rows) Err ¶
Err finalizes the statement, and return an error if any. Rows should not be used after this.
func (*Rows) ExplainQueryPlan ¶
QueryPlan returns the query plan of the existing statement under a graphical form.
func (*Rows) IsExplain ¶
IsExplain returns true iff the statement is an explain query plan command. This is the most important interface for debugging.
func (*Rows) Next ¶
Next advances the cursor to the next result in the set. It returns false if there are no more results, or if an error, or a timeout occur. Use Rows.Err to disambiguate between those cases.
func (*Rows) NumColumn ¶
NumColumn returns the count of columns returned by the current statement. Use Rows.ColumnName if the name is useful.
func (*Rows) Scan ¶
Scan unmarshals the underlying SQLite value into a Go value. Values in dst are matched by position against the columns in the query, e.g.
rows := ctn.Exec(ctx, "select a, b from t") for rows.Next() { var c, d string rows.Scan(&c, &d) // c -> column a // d -> column b }
Scan defers errors to the Rows.Err method (but note that Rows.Next will stop at the first error).
Conversion is done depending on the SQLite type affinity and the type in Go. Richer Go types (e.g. bytes.Buffer, or time.Time) are automatically read too. Read the code for the full map – but if you’re relying on this, you are probably already doing something too smart.
type Updater ¶
Updater exposes types to modifications through SQL commands.
Depending on the operation, not all fields are present:
- during an insert, only the value is set
- during a delete, only the index is set
- during an update, both the index and the value is set
The first context carries the current connection, it must be used if the update requires writing to the DB.
type VirtualTableOption ¶
type VirtualTableOption func(*rVM)
VirtualTableOption allows customisation of the virtual table behavior.
func OverloadFunc ¶
func OverloadFunc(name string, fn any) VirtualTableOption
OverloadFunc permits function overloading in a virtual table.
If the function takes two arguments, and returns either a boolean, or a boolean or an error, it will be used as a hint to filtering. In this case, the operation in the constraint will be equal to ConstraintFunc + index, with index the position in the overloaded functions (_including_ non-indexing ones).
Source Files ¶
Directories ¶
Path | Synopsis |
---|---|
cmd
|
|
constrainer
Constrainer is a tool to generate acces methods from virtual tables types definitions.
|
Constrainer is a tool to generate acces methods from virtual tables types definitions. |
Package stability provides an API to validate the stability of persisted data structures.
|
Package stability provides an API to validate the stability of persisted data structures. |