diff options
Diffstat (limited to 'vendor/github.com/ncruces/go-sqlite3/driver/driver.go')
-rw-r--r-- | vendor/github.com/ncruces/go-sqlite3/driver/driver.go | 158 |
1 files changed, 105 insertions, 53 deletions
diff --git a/vendor/github.com/ncruces/go-sqlite3/driver/driver.go b/vendor/github.com/ncruces/go-sqlite3/driver/driver.go index e7863b1b8..c02ba4b4f 100644 --- a/vendor/github.com/ncruces/go-sqlite3/driver/driver.go +++ b/vendor/github.com/ncruces/go-sqlite3/driver/driver.go @@ -8,21 +8,50 @@ // // The data source name for "sqlite3" databases can be a filename or a "file:" [URI]. // +// # Default transaction mode +// // The [TRANSACTION] mode can be specified using "_txlock": // // sql.Open("sqlite3", "file:demo.db?_txlock=immediate") // -// Possible values are: "deferred", "immediate", "exclusive". -// A [read-only] transaction is always "deferred", regardless of "_txlock". +// Possible values are: "deferred" (the default), "immediate", "exclusive". +// Regardless of "_txlock": +// - a [linearizable] transaction is always "exclusive"; +// - a [serializable] transaction is always "immediate"; +// - a [read-only] transaction is always "deferred". +// +// # Working with time // // The time encoding/decoding format can be specified using "_timefmt": // // sql.Open("sqlite3", "file:demo.db?_timefmt=sqlite") // // Possible values are: "auto" (the default), "sqlite", "rfc3339"; -// "auto" encodes as RFC 3339 and decodes any [format] supported by SQLite; -// "sqlite" encodes as SQLite and decodes any [format] supported by SQLite; -// "rfc3339" encodes and decodes RFC 3339 only. +// - "auto" encodes as RFC 3339 and decodes any [format] supported by SQLite; +// - "sqlite" encodes as SQLite and decodes any [format] supported by SQLite; +// - "rfc3339" encodes and decodes RFC 3339 only. +// +// If you encode as RFC 3339 (the default), +// consider using the TIME [collating sequence] to produce a time-ordered sequence. +// +// To scan values in other formats, [sqlite3.TimeFormat.Scanner] may be helpful. +// To bind values in other formats, [sqlite3.TimeFormat.Encode] them before binding. +// +// When using a custom time struct, you'll have to implement +// [database/sql/driver.Valuer] and [database/sql.Scanner]. +// +// The Value method should ideally serialise to a time [format] supported by SQLite. +// This ensures SQL date and time functions work as they should, +// and that your schema works with other SQLite tools. +// [sqlite3.TimeFormat.Encode] may help. +// +// The Scan method needs to take into account that the value it receives can be of differing types. +// It can already be a [time.Time], if the driver decoded the value according to "_timefmt" rules. +// Or it can be a: string, int64, float64, []byte, nil, +// depending on the column type and what whoever wrote the value. +// [sqlite3.TimeFormat.Decode] may help. +// +// # Setting PRAGMAs // // [PRAGMA] statements can be specified using "_pragma": // @@ -31,13 +60,17 @@ // If no PRAGMAs are specified, a busy timeout of 1 minute is set. // // Order matters: -// busy timeout and locking mode should be the first PRAGMAs set, in that order. +// encryption keys, busy timeout and locking mode should be the first PRAGMAs set, +// in that order. // // [URI]: https://sqlite.org/uri.html // [PRAGMA]: https://sqlite.org/pragma.html -// [format]: https://sqlite.org/lang_datefunc.html#time_values // [TRANSACTION]: https://sqlite.org/lang_transaction.html#deferred_immediate_and_exclusive_transactions +// [linearizable]: https://pkg.go.dev/database/sql#TxOptions +// [serializable]: https://pkg.go.dev/database/sql#TxOptions // [read-only]: https://pkg.go.dev/database/sql#TxOptions +// [format]: https://sqlite.org/lang_datefunc.html#time_values +// [collating sequence]: https://sqlite.org/datatype3.html#collating_sequences package driver import ( @@ -69,11 +102,22 @@ func init() { // Open opens the SQLite database specified by dataSourceName as a [database/sql.DB]. // -// The init function is called by the driver on new connections. +// Open accepts zero, one, or two callbacks (nil callbacks are ignored). +// The first callback is called when the driver opens a new connection. +// The second callback is called before the driver closes a connection. // The [sqlite3.Conn] can be used to execute queries, register functions, etc. -// Any error returned closes the connection and is returned to [database/sql]. -func Open(dataSourceName string, init func(*sqlite3.Conn) error) (*sql.DB, error) { - c, err := (&SQLite{Init: init}).OpenConnector(dataSourceName) +func Open(dataSourceName string, fn ...func(*sqlite3.Conn) error) (*sql.DB, error) { + var drv SQLite + if len(fn) > 2 { + return nil, sqlite3.MISUSE + } + if len(fn) > 1 { + drv.term = fn[1] + } + if len(fn) > 0 { + drv.init = fn[0] + } + c, err := drv.OpenConnector(dataSourceName) if err != nil { return nil, err } @@ -82,10 +126,8 @@ func Open(dataSourceName string, init func(*sqlite3.Conn) error) (*sql.DB, error // SQLite implements [database/sql/driver.Driver]. type SQLite struct { - // Init function is called by the driver on new connections. - // The [sqlite3.Conn] can be used to execute queries, register functions, etc. - // Any error returned closes the connection and is returned to [database/sql]. - Init func(*sqlite3.Conn) error + init func(*sqlite3.Conn) error + term func(*sqlite3.Conn) error } // Open implements [database/sql/driver.Driver]. @@ -119,10 +161,8 @@ func (d *SQLite) newConnector(name string) (*connector, error) { } switch txlock { - case "": - c.txBegin = "BEGIN" - case "deferred", "immediate", "exclusive": - c.txBegin = "BEGIN " + txlock + case "", "deferred", "concurrent", "immediate", "exclusive": + c.txLock = txlock default: return nil, fmt.Errorf("sqlite3: invalid _txlock: %s", txlock) } @@ -147,7 +187,7 @@ func (d *SQLite) newConnector(name string) (*connector, error) { type connector struct { driver *SQLite name string - txBegin string + txLock string tmRead sqlite3.TimeFormat tmWrite sqlite3.TimeFormat pragmas bool @@ -159,7 +199,7 @@ func (n *connector) Driver() driver.Driver { func (n *connector) Connect(ctx context.Context) (_ driver.Conn, err error) { c := &conn{ - txBegin: n.txBegin, + txLock: n.txLock, tmRead: n.tmRead, tmWrite: n.tmWrite, } @@ -178,18 +218,18 @@ func (n *connector) Connect(ctx context.Context) (_ driver.Conn, err error) { defer c.Conn.SetInterrupt(old) if !n.pragmas { - err = c.Conn.BusyTimeout(60 * time.Second) + err = c.Conn.BusyTimeout(time.Minute) if err != nil { return nil, err } } - if n.driver.Init != nil { - err = n.driver.Init(c.Conn) + if n.driver.init != nil { + err = n.driver.init(c.Conn) if err != nil { return nil, err } } - if n.pragmas || n.driver.Init != nil { + if n.pragmas || n.driver.init != nil { s, _, err := c.Conn.Prepare(`PRAGMA query_only`) if err != nil { return nil, err @@ -204,17 +244,24 @@ func (n *connector) Connect(ctx context.Context) (_ driver.Conn, err error) { return nil, err } } + if n.driver.term != nil { + err = c.Conn.Trace(sqlite3.TRACE_CLOSE, func(sqlite3.TraceEvent, any, any) error { + return n.driver.term(c.Conn) + }) + if err != nil { + return nil, err + } + } return c, nil } type conn struct { *sqlite3.Conn - txBegin string - txCommit string - txRollback string - tmRead sqlite3.TimeFormat - tmWrite sqlite3.TimeFormat - readOnly byte + txLock string + txReset string + tmRead sqlite3.TimeFormat + tmWrite sqlite3.TimeFormat + readOnly byte } var ( @@ -231,31 +278,30 @@ func (c *conn) Raw() *sqlite3.Conn { // Deprecated: use BeginTx instead. func (c *conn) Begin() (driver.Tx, error) { + // notest return c.BeginTx(context.Background(), driver.TxOptions{}) } func (c *conn) BeginTx(ctx context.Context, opts driver.TxOptions) (driver.Tx, error) { - txBegin := c.txBegin - c.txCommit = `COMMIT` - c.txRollback = `ROLLBACK` - - if opts.ReadOnly { - txBegin = ` - BEGIN deferred; - PRAGMA query_only=on` - c.txRollback = ` - ROLLBACK; - PRAGMA query_only=` + string(c.readOnly) - c.txCommit = c.txRollback - } - + var txLock string switch opts.Isolation { default: return nil, util.IsolationErr - case - driver.IsolationLevel(sql.LevelDefault), - driver.IsolationLevel(sql.LevelSerializable): - break + case driver.IsolationLevel(sql.LevelLinearizable): + txLock = "exclusive" + case driver.IsolationLevel(sql.LevelSerializable): + txLock = "immediate" + case driver.IsolationLevel(sql.LevelDefault): + if !opts.ReadOnly { + txLock = c.txLock + } + } + + c.txReset = `` + txBegin := `BEGIN ` + txLock + if opts.ReadOnly { + txBegin += ` ; PRAGMA query_only=on` + c.txReset = `; PRAGMA query_only=` + string(c.readOnly) } old := c.Conn.SetInterrupt(ctx) @@ -269,7 +315,7 @@ func (c *conn) BeginTx(ctx context.Context, opts driver.TxOptions) (driver.Tx, e } func (c *conn) Commit() error { - err := c.Conn.Exec(c.txCommit) + err := c.Conn.Exec(`COMMIT` + c.txReset) if err != nil && !c.Conn.GetAutocommit() { c.Rollback() } @@ -277,16 +323,17 @@ func (c *conn) Commit() error { } func (c *conn) Rollback() error { - err := c.Conn.Exec(c.txRollback) + err := c.Conn.Exec(`ROLLBACK` + c.txReset) if errors.Is(err, sqlite3.INTERRUPT) { old := c.Conn.SetInterrupt(context.Background()) defer c.Conn.SetInterrupt(old) - err = c.Conn.Exec(c.txRollback) + err = c.Conn.Exec(`ROLLBACK` + c.txReset) } return err } func (c *conn) Prepare(query string) (driver.Stmt, error) { + // notest return c.PrepareContext(context.Background(), query) } @@ -329,6 +376,8 @@ func (c *conn) ExecContext(ctx context.Context, query string, args []driver.Name } func (c *conn) CheckNamedValue(arg *driver.NamedValue) error { + // Fast path: short circuit argument verification. + // Arguments will be rejected by conn.ExecContext. return nil } @@ -363,11 +412,13 @@ func (s *stmt) NumInput() int { // Deprecated: use ExecContext instead. func (s *stmt) Exec(args []driver.Value) (driver.Result, error) { + // notest return s.ExecContext(context.Background(), namedValues(args)) } // Deprecated: use QueryContext instead. func (s *stmt) Query(args []driver.Value) (driver.Rows, error) { + // notest return s.QueryContext(context.Background(), namedValues(args)) } @@ -561,7 +612,8 @@ func (r *rows) Next(dest []driver.Value) error { } func (r *rows) decodeTime(i int, v any) (_ time.Time, ok bool) { - if r.tmRead == sqlite3.TimeFormatDefault { + switch r.tmRead { + case sqlite3.TimeFormatDefault, time.RFC3339Nano: // handled by maybeTime return } |