| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409 |
- // Copyright (c) 2018 David Crawshaw <david@zentus.com>
- // Copyright (c) 2021 Ross Light <ross@zombiezen.com>
- //
- // Permission to use, copy, modify, and distribute this software for any
- // purpose with or without fee is hereby granted, provided that the above
- // copyright notice and this permission notice appear in all copies.
- //
- // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- //
- // SPDX-License-Identifier: ISC
- package sqlite
- import (
- "bytes"
- "fmt"
- "strings"
- "sync"
- "time"
- "unsafe"
- "modernc.org/libc"
- "modernc.org/libc/sys/types"
- lib "modernc.org/sqlite/lib"
- )
- // Version is the SQLite version in the format "X.Y.Z" where X is the major
- // version number (always 3), Y is the minor version number, and Z is the
- // release number.
- const Version = lib.SQLITE_VERSION
- // VersionNumber is an integer with the value (X*1000000 + Y*1000 + Z) where X
- // is the major version number (always 3), Y is the minor version number, and Z
- // is the release number.
- const VersionNumber = lib.SQLITE_VERSION_NUMBER
- // Conn is an open connection to an SQLite3 database.
- //
- // A Conn can only be used by goroutine at a time.
- type Conn struct {
- tls *libc.TLS
- conn uintptr
- stmts map[string]*Stmt // query -> prepared statement
- closed bool
- cancelCh chan struct{}
- doneCh <-chan struct{}
- unlockNote uintptr
- file string
- line int
- }
- const ptrSize = types.Size_t(unsafe.Sizeof(uintptr(0)))
- // OpenConn opens a single SQLite database connection with the given flags.
- // No flags or a value of 0 defaults to the following:
- //
- // - [OpenReadWrite]
- // - [OpenCreate]
- // - [OpenWAL]
- // - [OpenURI]
- //
- // https://www.sqlite.org/c3ref/open.html
- func OpenConn(path string, flags ...OpenFlags) (*Conn, error) {
- var openFlags OpenFlags
- for _, f := range flags {
- openFlags |= f
- }
- if openFlags == 0 {
- openFlags = OpenReadWrite | OpenCreate | OpenWAL | OpenURI
- }
- c, err := openConn(path, openFlags&^(OpenWAL|OpenFullMutex)|OpenNoMutex)
- if err != nil {
- return nil, err
- }
- // Disable double-quoted string literals.
- varArgs := libc.NewVaList(int32(0), uintptr(0))
- if varArgs == 0 {
- c.Close()
- return nil, fmt.Errorf("sqlite: open %q: cannot allocate memory", path)
- }
- defer libc.Xfree(c.tls, varArgs)
- res := ResultCode(lib.Xsqlite3_db_config(
- c.tls,
- c.conn,
- lib.SQLITE_DBCONFIG_DQS_DDL,
- varArgs,
- ))
- if err := res.ToError(); err != nil {
- // Making error opaque because it's not part of the primary connection
- // opening and reflects an internal error.
- c.Close()
- return nil, fmt.Errorf("sqlite: open %q: disable double-quoted string literals: %v", path, err)
- }
- res = ResultCode(lib.Xsqlite3_db_config(
- c.tls,
- c.conn,
- lib.SQLITE_DBCONFIG_DQS_DML,
- varArgs,
- ))
- if err := res.ToError(); err != nil {
- // Making error opaque because it's not part of the primary connection
- // opening and reflects an internal error.
- c.Close()
- return nil, fmt.Errorf("sqlite: open %q: disable double-quoted string literals: %v", path, err)
- }
- if openFlags&OpenWAL != 0 {
- // Set timeout for enabling WAL.
- // See https://github.com/crawshaw/sqlite/pull/113 for details.
- // TODO(maybe): Pass in Context to OpenConn?
- c.SetBusyTimeout(10 * time.Second)
- stmt, _, err := c.PrepareTransient("PRAGMA journal_mode=wal;")
- if err != nil {
- c.Close()
- return nil, fmt.Errorf("sqlite: open %q: %w", path, err)
- }
- _, err = stmt.Step()
- stmt.Finalize()
- if err != nil {
- c.Close()
- return nil, fmt.Errorf("sqlite: open %q: enable wal: %w", path, err)
- }
- }
- c.SetBlockOnBusy()
- return c, nil
- }
- var allConns struct {
- mu sync.RWMutex
- table map[uintptr]*Conn
- }
- func openConn(path string, openFlags OpenFlags) (_ *Conn, err error) {
- tls := libc.NewTLS()
- defer func() {
- if err != nil {
- tls.Close()
- }
- }()
- unlockNote, err := allocUnlockNote(tls)
- if err != nil {
- return nil, fmt.Errorf("sqlite: open %q: %w", path, err)
- }
- defer func() {
- if err != nil {
- libc.Xfree(tls, unlockNote)
- }
- }()
- cpath, err := libc.CString(path)
- if err != nil {
- return nil, fmt.Errorf("sqlite: open %q: %w", path, err)
- }
- defer libc.Xfree(tls, cpath)
- connPtr, err := malloc(tls, ptrSize)
- if err != nil {
- return nil, fmt.Errorf("sqlite: open %q: %w", path, err)
- }
- defer libc.Xfree(tls, connPtr)
- res := ResultCode(lib.Xsqlite3_open_v2(tls, cpath, connPtr, int32(openFlags), 0))
- c := &Conn{
- tls: tls,
- conn: *(*uintptr)(unsafe.Pointer(connPtr)),
- stmts: make(map[string]*Stmt),
- unlockNote: unlockNote,
- }
- if c.conn == 0 {
- // Not enough memory to allocate the sqlite3 object.
- return nil, fmt.Errorf("sqlite: open %q: %w", path, sqliteError{res})
- }
- if res != ResultOK {
- // sqlite3_open_v2 may still return a sqlite3* just so we can extract the error.
- extres := ResultCode(lib.Xsqlite3_extended_errcode(tls, c.conn))
- if extres != 0 {
- res = extres
- }
- lib.Xsqlite3_close_v2(tls, c.conn)
- return nil, fmt.Errorf("sqlite: open %q: %w", path, sqliteError{res})
- }
- lib.Xsqlite3_extended_result_codes(tls, c.conn, 1)
- allConns.mu.Lock()
- if allConns.table == nil {
- allConns.table = make(map[uintptr]*Conn)
- }
- allConns.table[c.conn] = c
- allConns.mu.Unlock()
- return c, nil
- }
- // Close closes the database connection using sqlite3_close and finalizes
- // persistent prepared statements. https://www.sqlite.org/c3ref/close.html
- func (c *Conn) Close() error {
- if c == nil {
- return fmt.Errorf("sqlite: close: nil connection")
- }
- c.cancelInterrupt()
- c.closed = true
- for _, stmt := range c.stmts {
- stmt.Finalize()
- }
- res := ResultCode(lib.Xsqlite3_close(c.tls, c.conn))
- libc.Xfree(c.tls, c.unlockNote)
- c.unlockNote = 0
- c.tls.Close()
- c.tls = nil
- c.releaseAuthorizer()
- busyHandlers.Delete(c.conn)
- allConns.mu.Lock()
- delete(allConns.table, c.conn)
- allConns.mu.Unlock()
- if !res.IsSuccess() {
- return fmt.Errorf("sqlite: close: %w", sqliteError{res})
- }
- return nil
- }
- // AutocommitEnabled reports whether conn is in autocommit mode.
- // https://sqlite.org/c3ref/get_autocommit.html
- func (c *Conn) AutocommitEnabled() bool {
- if c == nil {
- return false
- }
- return lib.Xsqlite3_get_autocommit(c.tls, c.conn) != 0
- }
- // CheckReset reports whether any statement on this connection is in the process
- // of returning results.
- func (c *Conn) CheckReset() string {
- if c != nil {
- for _, stmt := range c.stmts {
- if stmt.lastHasRow {
- return stmt.query
- }
- }
- }
- return ""
- }
- // SetInterrupt assigns a channel to control connection execution lifetime.
- //
- // When doneCh is closed, the connection uses sqlite3_interrupt to
- // stop long-running queries and cancels any *Stmt.Step calls that
- // are blocked waiting for the database write lock.
- //
- // Subsequent uses of the connection will return SQLITE_INTERRUPT
- // errors until doneCh is reset with a subsequent call to SetInterrupt.
- //
- // Any busy statements at the time SetInterrupt is called will be reset.
- //
- // SetInterrupt returns the old doneCh assigned to the connection.
- func (c *Conn) SetInterrupt(doneCh <-chan struct{}) (oldDoneCh <-chan struct{}) {
- if c == nil {
- return nil
- }
- if c.closed {
- panic("sqlite.Conn is closed")
- }
- oldDoneCh = c.doneCh
- c.cancelInterrupt()
- c.doneCh = doneCh
- for _, stmt := range c.stmts {
- if stmt.lastHasRow {
- stmt.Reset()
- }
- }
- if doneCh == nil {
- return oldDoneCh
- }
- cancelCh := make(chan struct{})
- c.cancelCh = cancelCh
- go func() {
- select {
- case <-doneCh:
- lib.Xsqlite3_interrupt(c.tls, c.conn)
- fireUnlockNote(c.tls, c.unlockNote)
- <-cancelCh
- cancelCh <- struct{}{}
- case <-cancelCh:
- cancelCh <- struct{}{}
- }
- }()
- return oldDoneCh
- }
- // SetBusyTimeout sets a busy handler that sleeps for up to d to acquire a lock.
- // Passing a non-positive value will turn off all busy handlers.
- //
- // By default, connections are opened with SetBlockOnBusy,
- // with the assumption that programs use SetInterrupt to control timeouts.
- //
- // https://www.sqlite.org/c3ref/busy_timeout.html
- func (c *Conn) SetBusyTimeout(d time.Duration) {
- if c != nil {
- lib.Xsqlite3_busy_timeout(c.tls, c.conn, int32(d/time.Millisecond))
- busyHandlers.Delete(c.conn)
- }
- }
- // SetBlockOnBusy sets a busy handler that waits to acquire a lock
- // until the connection is interrupted (see SetInterrupt).
- //
- // By default, connections are opened with SetBlockOnBusy,
- // with the assumption that programs use SetInterrupt to control timeouts.
- //
- // https://www.sqlite.org/c3ref/busy_handler.html
- func (c *Conn) SetBlockOnBusy() {
- if c == nil {
- return
- }
- c.setBusyHandler(func(count int) bool {
- if count >= len(busyDelays) {
- count = len(busyDelays) - 1
- }
- t := time.NewTimer(busyDelays[count])
- defer t.Stop()
- select {
- case <-t.C:
- return true
- case <-c.doneCh:
- // ^ Assuming that doneCh won't be set by SetInterrupt concurrently
- // with other operations.
- return false
- }
- })
- }
- var busyDelays = [...]time.Duration{
- 1 * time.Second,
- 2 * time.Second,
- 5 * time.Second,
- 10 * time.Second,
- 15 * time.Second,
- 20 * time.Second,
- 25 * time.Second,
- 25 * time.Second,
- 25 * time.Second,
- 50 * time.Second,
- 50 * time.Second,
- 100 * time.Second,
- }
- var busyHandlers sync.Map // sqlite3* -> func(int) bool
- func (c *Conn) setBusyHandler(handler func(count int) bool) {
- if c == nil {
- return
- }
- if handler == nil {
- lib.Xsqlite3_busy_handler(c.tls, c.conn, 0, 0)
- busyHandlers.Delete(c.conn)
- return
- }
- busyHandlers.Store(c.conn, handler)
- xBusy := cFuncPointer(busyHandlerCallback)
- lib.Xsqlite3_busy_handler(c.tls, c.conn, xBusy, c.conn)
- }
- func busyHandlerCallback(tls *libc.TLS, pArg uintptr, count int32) int32 {
- val, _ := busyHandlers.Load(pArg)
- if val == nil {
- return 0
- }
- f := val.(func(int) bool)
- if !f(int(count)) {
- return 0
- }
- return 1
- }
- func (c *Conn) interrupted() error {
- select {
- case <-c.doneCh:
- return sqliteError{ResultInterrupt}
- default:
- return nil
- }
- }
- func (c *Conn) cancelInterrupt() {
- if c.cancelCh != nil {
- c.cancelCh <- struct{}{}
- <-c.cancelCh
- c.cancelCh = nil
- }
- }
- // Prep returns a persistent SQL statement.
- //
- // Any error in preparation will panic.
- //
- // Persistent prepared statements are cached by the query
- // string in a Conn. If Finalize is not called, then subsequent
- // calls to Prepare will return the same statement.
- //
- // https://www.sqlite.org/c3ref/prepare.html
- func (c *Conn) Prep(query string) *Stmt {
- stmt, err := c.Prepare(query)
- if err != nil {
- if ErrCode(err) == ResultInterrupt {
- return &Stmt{
- conn: c,
- query: query,
- colNames: make(map[string]int),
- prepInterrupt: true,
- }
- }
- panic(err)
- }
- return stmt
- }
- // Prepare prepares a persistent SQL statement.
- //
- // Persistent prepared statements are cached by the query
- // string in a Conn. If Finalize is not called, then subsequent
- // calls to Prepare will return the same statement.
- //
- // If the query has any unprocessed trailing bytes, Prepare
- // returns an error.
- //
- // https://www.sqlite.org/c3ref/prepare.html
- func (c *Conn) Prepare(query string) (*Stmt, error) {
- if c == nil {
- return nil, fmt.Errorf("sqlite: prepare %q: nil connection", query)
- }
- if stmt := c.stmts[query]; stmt != nil {
- if err := stmt.Reset(); err != nil {
- return nil, err
- }
- if err := stmt.ClearBindings(); err != nil {
- return nil, err
- }
- return stmt, nil
- }
- stmt, trailingBytes, err := c.prepare(query, lib.SQLITE_PREPARE_PERSISTENT)
- if err != nil {
- return nil, fmt.Errorf("sqlite: prepare %q: %w", query, err)
- }
- if trailingBytes != 0 {
- stmt.Finalize()
- return nil, fmt.Errorf("sqlite: prepare %q: statement has trailing bytes", query)
- }
- c.stmts[query] = stmt
- return stmt, nil
- }
- // PrepareTransient prepares an SQL statement that is not cached by
- // the Conn. Subsequent calls with the same query will create new Stmts.
- // Finalize must be called by the caller once done with the Stmt.
- //
- // The number of trailing bytes not consumed from query is returned.
- //
- // To run a sequence of queries once as part of a script,
- // the sqlitex package provides an ExecScript function built on this.
- //
- // https://www.sqlite.org/c3ref/prepare.html
- func (c *Conn) PrepareTransient(query string) (stmt *Stmt, trailingBytes int, err error) {
- if c == nil {
- return nil, 0, fmt.Errorf("sqlite: prepare %q: nil connection", query)
- }
- // TODO(soon)
- // if stmt != nil {
- // runtime.SetFinalizer(stmt, func(stmt *Stmt) {
- // if stmt.conn != nil {
- // panic("open *sqlite.Stmt \"" + query + "\" garbage collected, call Finalize")
- // }
- // })
- // }
- return c.prepare(query, 0)
- }
- func (c *Conn) prepare(query string, flags uint32) (*Stmt, int, error) {
- if err := c.interrupted(); err != nil {
- return nil, 0, err
- }
- cquery, err := libc.CString(query)
- if err != nil {
- return nil, 0, err
- }
- defer libc.Xfree(c.tls, cquery)
- ctrailingPtr, err := malloc(c.tls, ptrSize)
- if err != nil {
- return nil, 0, err
- }
- defer libc.Xfree(c.tls, ctrailingPtr)
- stmtPtr, err := malloc(c.tls, ptrSize)
- if err != nil {
- return nil, 0, err
- }
- defer libc.Xfree(c.tls, stmtPtr)
- res := ResultCode(lib.Xsqlite3_prepare_v3(c.tls, c.conn, cquery, -1, flags, stmtPtr, ctrailingPtr))
- if err := c.extreserr(res); err != nil {
- return nil, 0, err
- }
- ctrailing := *(*uintptr)(unsafe.Pointer(ctrailingPtr))
- trailingBytes := len(query) - int(ctrailing-cquery)
- stmt := &Stmt{
- conn: c,
- query: query,
- stmt: *(*uintptr)(unsafe.Pointer(stmtPtr)),
- }
- stmt.bindNames = make([]string, lib.Xsqlite3_bind_parameter_count(c.tls, stmt.stmt))
- for i := range stmt.bindNames {
- cname := lib.Xsqlite3_bind_parameter_name(c.tls, stmt.stmt, int32(i+1))
- if cname != 0 {
- stmt.bindNames[i] = libc.GoString(cname)
- }
- }
- colCount := int(lib.Xsqlite3_column_count(c.tls, stmt.stmt))
- stmt.colNames = make(map[string]int, colCount)
- for i := 0; i < colCount; i++ {
- cname := lib.Xsqlite3_column_name(c.tls, stmt.stmt, int32(i))
- if cname != 0 {
- stmt.colNames[libc.GoString(cname)] = i
- }
- }
- return stmt, trailingBytes, nil
- }
- // Changes reports the number of rows affected by the most recent statement.
- //
- // https://www.sqlite.org/c3ref/changes.html
- func (c *Conn) Changes() int {
- if c == nil {
- return 0
- }
- return int(lib.Xsqlite3_changes(c.tls, c.conn))
- }
- // LastInsertRowID reports the rowid of the most recently successful INSERT.
- //
- // https://www.sqlite.org/c3ref/last_insert_rowid.html
- func (c *Conn) LastInsertRowID() int64 {
- if c == nil {
- return 0
- }
- return lib.Xsqlite3_last_insert_rowid(c.tls, c.conn)
- }
- // Serialize serializes the database with the given name (e.g. "main" or "temp").
- func (c *Conn) Serialize(dbName string) ([]byte, error) {
- if c == nil {
- return nil, fmt.Errorf("sqlite: serialize %q: nil connection", dbName)
- }
- zSchema, cleanup, err := cDBName(dbName)
- if err != nil {
- return nil, fmt.Errorf("sqlite: serialize %q: %v", dbName, err)
- }
- defer cleanup()
- piSize := lib.Xsqlite3_malloc(c.tls, int32(unsafe.Sizeof(int64(0))))
- if piSize == 0 {
- return nil, fmt.Errorf("sqlite: serialize %q: memory allocation failure", dbName)
- }
- defer lib.Xsqlite3_free(c.tls, piSize)
- // Optimization: avoid copying if possible.
- p := lib.Xsqlite3_serialize(c.tls, c.conn, zSchema, piSize, lib.SQLITE_SERIALIZE_NOCOPY)
- if p == 0 {
- // Optimization impossible. Have SQLite allocate memory.
- p = lib.Xsqlite3_serialize(c.tls, c.conn, zSchema, piSize, 0)
- if p == 0 {
- return nil, fmt.Errorf("sqlite: serialize %q: unable to serialize", dbName)
- }
- defer lib.Xsqlite3_free(c.tls, p)
- }
- // Copy data into a Go byte slice.
- n := *(*int64)(unsafe.Pointer(piSize))
- goCopy := make([]byte, n)
- copy(goCopy, libc.GoBytes(p, int(n)))
- return goCopy, nil
- }
- // Deserialize disconnects the database with the given name (e.g. "main")
- // and reopens it as an in-memory database based on the serialized data.
- // The database name must already exist.
- // It is not possible to deserialize into the TEMP database.
- func (c *Conn) Deserialize(dbName string, data []byte) error {
- if c == nil {
- return fmt.Errorf("sqlite: deserialize to %q: nil connection", dbName)
- }
- zSchema, cleanup, err := cDBName(dbName)
- if err != nil {
- return fmt.Errorf("sqlite: deserialize to %q: %v", dbName, err)
- }
- defer cleanup()
- n := int64(len(data))
- pData := lib.Xsqlite3_malloc64(c.tls, uint64(n))
- if pData == 0 {
- return fmt.Errorf("sqlite: deserialize to %q: memory allocation failure", dbName)
- }
- copy(libc.GoBytes(pData, len(data)), data)
- res := ResultCode(lib.Xsqlite3_deserialize(c.tls, c.conn, zSchema, pData, n, n, lib.SQLITE_DESERIALIZE_FREEONCLOSE|lib.SQLITE_DESERIALIZE_RESIZEABLE))
- if !res.IsSuccess() {
- return fmt.Errorf("sqlite: deserialize to %q: %w", dbName, res.ToError())
- }
- return nil
- }
- // extreserr asks SQLite for a string explaining the error.
- // Only called for errors that are probably program bugs.
- func (c *Conn) extreserr(res ResultCode) error {
- if res.IsSuccess() {
- return nil
- }
- if msg := libc.GoString(lib.Xsqlite3_errmsg(c.tls, c.conn)); msg != "" {
- return fmt.Errorf("%w: %s", res.ToError(), msg)
- }
- return res.ToError()
- }
- // Stmt is an SQLite3 prepared statement.
- //
- // A Stmt is attached to a particular Conn
- // (and that Conn can only be used by a single goroutine).
- //
- // When a Stmt is no longer needed it should be cleaned up
- // by calling the Finalize method.
- type Stmt struct {
- conn *Conn
- stmt uintptr
- query string
- bindNames []string
- colNames map[string]int
- bindErr error
- prepInterrupt bool // set if Prep was interrupted
- lastHasRow bool // last bool returned by Step
- }
- func (stmt *Stmt) interrupted() error {
- if stmt.prepInterrupt {
- return sqliteError{ResultInterrupt}
- }
- return stmt.conn.interrupted()
- }
- // Finalize deletes a prepared statement.
- //
- // Be sure to always call Finalize when done with
- // a statement created using PrepareTransient.
- //
- // Do not call Finalize on a prepared statement that
- // you intend to prepare again in the future.
- //
- // https://www.sqlite.org/c3ref/finalize.html
- func (stmt *Stmt) Finalize() error {
- if ptr := stmt.conn.stmts[stmt.query]; ptr == stmt {
- delete(stmt.conn.stmts, stmt.query)
- }
- res := ResultCode(lib.Xsqlite3_finalize(stmt.conn.tls, stmt.stmt))
- stmt.conn = nil
- if err := res.ToError(); err != nil {
- return fmt.Errorf("sqlite: finalize: %w", err)
- }
- return nil
- }
- // Reset resets a prepared statement so it can be executed again.
- //
- // Note that any parameter values bound to the statement are retained.
- // To clear bound values, call ClearBindings.
- //
- // https://www.sqlite.org/c3ref/reset.html
- func (stmt *Stmt) Reset() error {
- stmt.lastHasRow = false
- var res ResultCode
- for {
- res = ResultCode(lib.Xsqlite3_reset(stmt.conn.tls, stmt.stmt))
- if res != ResultLockedSharedCache {
- break
- }
- // An SQLITE_LOCKED_SHAREDCACHE error has been seen from sqlite3_reset
- // in the wild, but so far has eluded exact test case replication.
- // TODO: write a test for this.
- if res := waitForUnlockNotify(stmt.conn.tls, stmt.conn.conn, stmt.conn.unlockNote); res != ResultOK {
- return fmt.Errorf("sqlite: reset: %w", stmt.conn.extreserr(res))
- }
- }
- if err := stmt.conn.extreserr(res); err != nil {
- return fmt.Errorf("sqlite: reset: %w", err)
- }
- return nil
- }
- // ClearBindings clears all bound parameter values on a statement.
- //
- // https://www.sqlite.org/c3ref/clear_bindings.html
- func (stmt *Stmt) ClearBindings() error {
- if err := stmt.interrupted(); err != nil {
- return fmt.Errorf("sqlite: clear bindings: %w", err)
- }
- res := ResultCode(lib.Xsqlite3_clear_bindings(stmt.conn.tls, stmt.stmt))
- if err := res.ToError(); err != nil {
- return fmt.Errorf("sqlite: clear bindings: %w", err)
- }
- return nil
- }
- // Step moves through the statement cursor using sqlite3_step.
- //
- // If a row of data is available, rowReturned is reported as true.
- // If the statement has reached the end of the available data then
- // rowReturned is false. Thus the status codes SQLITE_ROW and
- // SQLITE_DONE are reported by the rowReturned bool, and all other
- // non-OK status codes are reported as an error.
- //
- // If an error value is returned, then the statement has been reset.
- //
- // https://www.sqlite.org/c3ref/step.html
- //
- // # Shared cache
- //
- // As multiple writers are common in multi-threaded programs,
- // this Step method uses sqlite3_unlock_notify to handle any
- // SQLITE_LOCKED errors.
- //
- // Without the shared cache, SQLite will block for
- // several seconds while trying to acquire the write lock.
- // With the shared cache, it returns SQLITE_LOCKED immediately
- // if the write lock is held by another connection in this process.
- // Dealing with this correctly makes for an unpleasant programming
- // experience, so this package does it automatically by blocking
- // Step until the write lock is relinquished.
- //
- // This means Step can block for a very long time.
- // Use SetInterrupt to control how long Step will block.
- //
- // For far more details, see:
- //
- // http://www.sqlite.org/unlock_notify.html
- func (stmt *Stmt) Step() (rowReturned bool, err error) {
- if stmt.bindErr != nil {
- err = stmt.bindErr
- stmt.bindErr = nil
- stmt.Reset()
- return false, fmt.Errorf("sqlite: step: %w", err)
- }
- rowReturned, err = stmt.step()
- stmt.lastHasRow = rowReturned
- if err != nil {
- lib.Xsqlite3_reset(stmt.conn.tls, stmt.stmt)
- return rowReturned, fmt.Errorf("sqlite: step: %w", err)
- }
- return rowReturned, nil
- }
- func (stmt *Stmt) step() (bool, error) {
- for {
- if err := stmt.interrupted(); err != nil {
- return false, err
- }
- switch res := ResultCode(lib.Xsqlite3_step(stmt.conn.tls, stmt.stmt)); res.ToPrimary() {
- case ResultLocked:
- if res != ResultLockedSharedCache {
- // don't call waitForUnlockNotify as it might deadlock, see:
- // https://github.com/crawshaw/sqlite/issues/6
- return false, stmt.conn.extreserr(res)
- }
- if res := waitForUnlockNotify(stmt.conn.tls, stmt.conn.conn, stmt.conn.unlockNote); !res.IsSuccess() {
- return false, stmt.conn.extreserr(res)
- }
- lib.Xsqlite3_reset(stmt.conn.tls, stmt.stmt)
- // loop
- case ResultRow:
- return true, nil
- case ResultDone:
- return false, nil
- case ResultInterrupt:
- // TODO: embed some of these errors into the stmt for zero-alloc errors?
- return false, res.ToError()
- default:
- return false, stmt.conn.extreserr(res)
- }
- }
- }
- func (stmt *Stmt) handleBindErr(prefix string, res ResultCode) {
- if stmt.bindErr == nil && !res.IsSuccess() {
- stmt.bindErr = fmt.Errorf("%s: %w", prefix, res.ToError())
- }
- }
- // DataCount returns the number of columns in the current row of the result
- // set of prepared statement.
- //
- // https://sqlite.org/c3ref/data_count.html
- func (stmt *Stmt) DataCount() int {
- return int(lib.Xsqlite3_data_count(stmt.conn.tls, stmt.stmt))
- }
- // ColumnCount returns the number of columns in the result set returned by the
- // prepared statement.
- //
- // https://sqlite.org/c3ref/column_count.html
- func (stmt *Stmt) ColumnCount() int {
- return int(lib.Xsqlite3_column_count(stmt.conn.tls, stmt.stmt))
- }
- // ColumnName returns the name assigned to a particular column in the result
- // set of a SELECT statement.
- //
- // https://sqlite.org/c3ref/column_name.html
- func (stmt *Stmt) ColumnName(col int) string {
- return libc.GoString(lib.Xsqlite3_column_name(stmt.conn.tls, stmt.stmt, int32(col)))
- }
- // BindParamCount reports the number of parameters in stmt.
- //
- // https://www.sqlite.org/c3ref/bind_parameter_count.html
- func (stmt *Stmt) BindParamCount() int {
- if stmt.stmt == 0 {
- return 0
- }
- return len(stmt.bindNames)
- }
- // BindParamName returns the name of parameter or the empty string if the
- // parameter is nameless or i is out of range.
- //
- // Parameters indices start at 1.
- //
- // https://www.sqlite.org/c3ref/bind_parameter_name.html
- func (stmt *Stmt) BindParamName(i int) string {
- i-- // map from 1-based to 0-based
- if i < 0 || i >= len(stmt.bindNames) {
- return ""
- }
- return stmt.bindNames[i]
- }
- // BindInt64 binds value to a numbered stmt parameter.
- //
- // Parameter indices start at 1.
- //
- // https://www.sqlite.org/c3ref/bind_blob.html
- func (stmt *Stmt) BindInt64(param int, value int64) {
- if stmt.stmt == 0 {
- return
- }
- res := ResultCode(lib.Xsqlite3_bind_int64(stmt.conn.tls, stmt.stmt, int32(param), value))
- stmt.handleBindErr("bind int64", res)
- }
- // BindBool binds value (as an integer 0 or 1) to a numbered stmt parameter.
- //
- // Parameter indices start at 1.
- //
- // https://www.sqlite.org/c3ref/bind_blob.html
- func (stmt *Stmt) BindBool(param int, value bool) {
- if stmt.stmt == 0 {
- return
- }
- v := int64(0)
- if value {
- v = 1
- }
- res := ResultCode(lib.Xsqlite3_bind_int64(stmt.conn.tls, stmt.stmt, int32(param), v))
- stmt.handleBindErr("bind bool", res)
- }
- var emptyCString = mustCString("")
- const sqliteStatic uintptr = 0
- // BindBytes binds value to a numbered stmt parameter.
- //
- // In-memory copies of value are made using this interface.
- // For large blobs, consider using the streaming Blob object.
- //
- // Parameter indices start at 1.
- //
- // https://www.sqlite.org/c3ref/bind_blob.html
- func (stmt *Stmt) BindBytes(param int, value []byte) {
- if stmt.stmt == 0 {
- return
- }
- if len(value) == 0 {
- res := ResultCode(lib.Xsqlite3_bind_blob(stmt.conn.tls, stmt.stmt, int32(param), emptyCString, 0, sqliteStatic))
- stmt.handleBindErr("bind bytes", res)
- return
- }
- v, err := malloc(stmt.conn.tls, types.Size_t(len(value)))
- if err != nil {
- if stmt.bindErr == nil {
- stmt.bindErr = fmt.Errorf("bind bytes: %w", err)
- }
- return
- }
- for i, b := range value {
- *(*byte)(unsafe.Pointer(v + uintptr(i))) = b
- }
- res := ResultCode(lib.Xsqlite3_bind_blob(stmt.conn.tls, stmt.stmt, int32(param), v, int32(len(value)), freeFuncPtr))
- stmt.handleBindErr("bind bytes", res)
- }
- var freeFuncPtr = cFuncPointer(libc.Xfree)
- // BindText binds value to a numbered stmt parameter.
- //
- // Parameter indices start at 1.
- //
- // https://www.sqlite.org/c3ref/bind_blob.html
- func (stmt *Stmt) BindText(param int, value string) {
- if stmt.stmt == 0 {
- return
- }
- allocSize := types.Size_t(len(value))
- if allocSize == 0 {
- allocSize = 1
- }
- v, err := malloc(stmt.conn.tls, allocSize)
- if err != nil {
- if stmt.bindErr == nil {
- stmt.bindErr = fmt.Errorf("bind text: %w", err)
- }
- return
- }
- for i := 0; i < len(value); i++ {
- *(*byte)(unsafe.Pointer(v + uintptr(i))) = value[i]
- }
- res := ResultCode(lib.Xsqlite3_bind_text(stmt.conn.tls, stmt.stmt, int32(param), v, int32(len(value)), freeFuncPtr))
- stmt.handleBindErr("bind text", res)
- }
- // BindFloat binds value to a numbered stmt parameter.
- //
- // Parameter indices start at 1.
- //
- // https://www.sqlite.org/c3ref/bind_blob.html
- func (stmt *Stmt) BindFloat(param int, value float64) {
- if stmt.stmt == 0 {
- return
- }
- res := ResultCode(lib.Xsqlite3_bind_double(stmt.conn.tls, stmt.stmt, int32(param), value))
- stmt.handleBindErr("bind float", res)
- }
- // BindNull binds an SQL NULL value to a numbered stmt parameter.
- //
- // Parameter indices start at 1.
- //
- // https://www.sqlite.org/c3ref/bind_blob.html
- func (stmt *Stmt) BindNull(param int) {
- if stmt.stmt == 0 {
- return
- }
- res := ResultCode(lib.Xsqlite3_bind_null(stmt.conn.tls, stmt.stmt, int32(param)))
- stmt.handleBindErr("bind null", res)
- }
- // BindZeroBlob binds a blob of zeros of length len to a numbered stmt parameter.
- //
- // Parameter indices start at 1.
- //
- // https://www.sqlite.org/c3ref/bind_blob.html
- func (stmt *Stmt) BindZeroBlob(param int, len int64) {
- if stmt.stmt == 0 {
- return
- }
- res := ResultCode(lib.Xsqlite3_bind_zeroblob64(stmt.conn.tls, stmt.stmt, int32(param), uint64(len)))
- stmt.handleBindErr("bind zero blob", res)
- }
- func (stmt *Stmt) findBindName(prefix string, param string) int {
- for i, name := range stmt.bindNames {
- if name == param {
- return i + 1 // 1-based indices
- }
- }
- if stmt.bindErr == nil {
- stmt.bindErr = fmt.Errorf("%s: unknown parameter: %s", prefix, param)
- }
- return 0
- }
- // SetInt64 binds an int64 to a parameter using a column name.
- func (stmt *Stmt) SetInt64(param string, value int64) {
- stmt.BindInt64(stmt.findBindName("SetInt64", param), value)
- }
- // SetBool binds a value (as a 0 or 1) to a parameter using a column name.
- func (stmt *Stmt) SetBool(param string, value bool) {
- stmt.BindBool(stmt.findBindName("SetBool", param), value)
- }
- // SetBytes binds bytes to a parameter using a column name.
- // An invalid parameter name will cause the call to Step to return an error.
- func (stmt *Stmt) SetBytes(param string, value []byte) {
- stmt.BindBytes(stmt.findBindName("SetBytes", param), value)
- }
- // SetText binds text to a parameter using a column name.
- // An invalid parameter name will cause the call to Step to return an error.
- func (stmt *Stmt) SetText(param string, value string) {
- stmt.BindText(stmt.findBindName("SetText", param), value)
- }
- // SetFloat binds a float64 to a parameter using a column name.
- // An invalid parameter name will cause the call to Step to return an error.
- func (stmt *Stmt) SetFloat(param string, value float64) {
- stmt.BindFloat(stmt.findBindName("SetFloat", param), value)
- }
- // SetNull binds a null to a parameter using a column name.
- // An invalid parameter name will cause the call to Step to return an error.
- func (stmt *Stmt) SetNull(param string) {
- stmt.BindNull(stmt.findBindName("SetNull", param))
- }
- // SetZeroBlob binds a zero blob of length len to a parameter using a column name.
- // An invalid parameter name will cause the call to Step to return an error.
- func (stmt *Stmt) SetZeroBlob(param string, len int64) {
- stmt.BindZeroBlob(stmt.findBindName("SetZeroBlob", param), len)
- }
- // ColumnInt returns a query result value as an int.
- //
- // Note: this method calls sqlite3_column_int64 and then converts the
- // resulting 64-bits to an int.
- //
- // Column indices start at 0.
- //
- // https://www.sqlite.org/c3ref/column_blob.html
- func (stmt *Stmt) ColumnInt(col int) int {
- return int(stmt.ColumnInt64(col))
- }
- // ColumnInt32 returns a query result value as an int32.
- //
- // Column indices start at 0.
- //
- // https://www.sqlite.org/c3ref/column_blob.html
- func (stmt *Stmt) ColumnInt32(col int) int32 {
- return lib.Xsqlite3_column_int(stmt.conn.tls, stmt.stmt, int32(col))
- }
- // ColumnInt64 returns a query result value as an int64.
- //
- // Column indices start at 0.
- //
- // https://www.sqlite.org/c3ref/column_blob.html
- func (stmt *Stmt) ColumnInt64(col int) int64 {
- return lib.Xsqlite3_column_int64(stmt.conn.tls, stmt.stmt, int32(col))
- }
- // ColumnBool reports whether a query result value is non-zero.
- //
- // Column indices start at 0.
- //
- // https://www.sqlite.org/c3ref/column_blob.html
- func (stmt *Stmt) ColumnBool(col int) bool {
- return stmt.ColumnInt64(col) != 0
- }
- // ColumnBytes reads a query result into buf.
- // It reports the number of bytes read.
- //
- // Column indices start at 0.
- //
- // https://www.sqlite.org/c3ref/column_blob.html
- func (stmt *Stmt) ColumnBytes(col int, buf []byte) int {
- return copy(buf, stmt.columnBytes(col))
- }
- // ColumnReader creates a byte reader for a query result column.
- //
- // The reader directly references C-managed memory that stops
- // being valid as soon as the statement row resets.
- func (stmt *Stmt) ColumnReader(col int) *bytes.Reader {
- // Load the C memory directly into the Reader.
- // There is no exported method that lets it escape.
- return bytes.NewReader(stmt.columnBytes(col))
- }
- func (stmt *Stmt) columnBytes(col int) []byte {
- p := lib.Xsqlite3_column_blob(stmt.conn.tls, stmt.stmt, int32(col))
- if p == 0 {
- return nil
- }
- n := stmt.ColumnLen(col)
- return libc.GoBytes(p, n)
- }
- // ColumnType are codes for each of the SQLite fundamental datatypes:
- //
- // - 64-bit signed integer
- // - 64-bit IEEE floating point number
- // - string
- // - BLOB
- // - NULL
- //
- // https://www.sqlite.org/c3ref/c_blob.html
- type ColumnType int
- // Data types.
- const (
- TypeInteger ColumnType = lib.SQLITE_INTEGER
- TypeFloat ColumnType = lib.SQLITE_FLOAT
- TypeText ColumnType = lib.SQLITE_TEXT
- TypeBlob ColumnType = lib.SQLITE_BLOB
- TypeNull ColumnType = lib.SQLITE_NULL
- )
- // String returns the SQLite constant name of the type.
- func (t ColumnType) String() string {
- switch t {
- case TypeInteger:
- return "SQLITE_INTEGER"
- case TypeFloat:
- return "SQLITE_FLOAT"
- case TypeText:
- return "SQLITE_TEXT"
- case TypeBlob:
- return "SQLITE_BLOB"
- case TypeNull:
- return "SQLITE_NULL"
- default:
- return "<unknown sqlite datatype>"
- }
- }
- // ColumnType returns the datatype code for the initial data
- // type of the result column. The returned value is one of:
- //
- // - SQLITE_INTEGER
- // - SQLITE_FLOAT
- // - SQLITE_TEXT
- // - SQLITE_BLOB
- // - SQLITE_NULL
- //
- // Column indices start at 0.
- //
- // https://www.sqlite.org/c3ref/column_blob.html
- func (stmt *Stmt) ColumnType(col int) ColumnType {
- return ColumnType(lib.Xsqlite3_column_type(stmt.conn.tls, stmt.stmt, int32(col)))
- }
- // ColumnText returns a query result as a string.
- //
- // Column indices start at 0.
- //
- // https://www.sqlite.org/c3ref/column_blob.html
- func (stmt *Stmt) ColumnText(col int) string {
- n := stmt.ColumnLen(col)
- return goStringN(lib.Xsqlite3_column_text(stmt.conn.tls, stmt.stmt, int32(col)), n)
- }
- // ColumnFloat returns a query result as a float64.
- //
- // Column indices start at 0.
- //
- // https://www.sqlite.org/c3ref/column_blob.html
- func (stmt *Stmt) ColumnFloat(col int) float64 {
- return lib.Xsqlite3_column_double(stmt.conn.tls, stmt.stmt, int32(col))
- }
- // ColumnLen returns the number of bytes in a query result.
- //
- // Column indices start at 0.
- //
- // https://www.sqlite.org/c3ref/column_blob.html
- func (stmt *Stmt) ColumnLen(col int) int {
- return int(lib.Xsqlite3_column_bytes(stmt.conn.tls, stmt.stmt, int32(col)))
- }
- func (stmt *Stmt) ColumnDatabaseName(col int) string {
- return libc.GoString(lib.Xsqlite3_column_database_name(stmt.conn.tls, stmt.stmt, int32(col)))
- }
- func (stmt *Stmt) ColumnTableName(col int) string {
- return libc.GoString(lib.Xsqlite3_column_table_name(stmt.conn.tls, stmt.stmt, int32(col)))
- }
- // ColumnIndex returns the index of the column with the given name.
- //
- // If there is no column with the given name ColumnIndex returns -1.
- func (stmt *Stmt) ColumnIndex(colName string) int {
- col, found := stmt.colNames[colName]
- if !found {
- return -1
- }
- return col
- }
- // GetInt64 returns a query result value for colName as an int64.
- func (stmt *Stmt) GetInt64(colName string) int64 {
- col, found := stmt.colNames[colName]
- if !found {
- return 0
- }
- return stmt.ColumnInt64(col)
- }
- // GetBool reports whether the query result value for colName is non-zero.
- func (stmt *Stmt) GetBool(colName string) bool {
- return stmt.GetInt64(colName) != 0
- }
- // GetBytes reads a query result for colName into buf.
- // It reports the number of bytes read.
- func (stmt *Stmt) GetBytes(colName string, buf []byte) int {
- col, found := stmt.colNames[colName]
- if !found {
- return 0
- }
- return stmt.ColumnBytes(col, buf)
- }
- // GetReader creates a byte reader for colName.
- //
- // The reader directly references C-managed memory that stops
- // being valid as soon as the statement row resets.
- func (stmt *Stmt) GetReader(colName string) *bytes.Reader {
- col, found := stmt.colNames[colName]
- if !found {
- return bytes.NewReader(nil)
- }
- return stmt.ColumnReader(col)
- }
- // GetText returns a query result value for colName as a string.
- func (stmt *Stmt) GetText(colName string) string {
- col, found := stmt.colNames[colName]
- if !found {
- return ""
- }
- return stmt.ColumnText(col)
- }
- // GetFloat returns a query result value for colName as a float64.
- func (stmt *Stmt) GetFloat(colName string) float64 {
- col, found := stmt.colNames[colName]
- if !found {
- return 0
- }
- return stmt.ColumnFloat(col)
- }
- // GetLen returns the number of bytes in a query result for colName.
- func (stmt *Stmt) GetLen(colName string) int {
- col, found := stmt.colNames[colName]
- if !found {
- return 0
- }
- return stmt.ColumnLen(col)
- }
- func malloc(tls *libc.TLS, n types.Size_t) (uintptr, error) {
- p := libc.Xmalloc(tls, n)
- if p == 0 {
- return 0, fmt.Errorf("out of memory")
- }
- return p, nil
- }
- func mustCString(s string) uintptr {
- p, err := libc.CString(s)
- if err != nil {
- panic(err)
- }
- return p
- }
- func goStringN(s uintptr, n int) string {
- if s == 0 {
- return ""
- }
- var buf strings.Builder
- buf.Grow(n)
- for i := 0; i < n; i++ {
- buf.WriteByte(*(*byte)(unsafe.Pointer(s)))
- s++
- }
- return buf.String()
- }
- // cFuncPointer converts a function defined by a function declaration to a C pointer.
- // The result of using cFuncPointer on closures is undefined.
- func cFuncPointer[T any](f T) uintptr {
- // This assumes the memory representation described in https://golang.org/s/go11func.
- //
- // cFuncPointer does its conversion by doing the following in order:
- // 1) Create a Go struct containing a pointer to a pointer to
- // the function. It is assumed that the pointer to the function will be
- // stored in the read-only data section and thus will not move.
- // 2) Convert the pointer to the Go struct to a pointer to uintptr through
- // unsafe.Pointer. This is permitted via Rule #1 of unsafe.Pointer.
- // 3) Dereference the pointer to uintptr to obtain the function value as a
- // uintptr. This is safe as long as function values are passed as pointers.
- return *(*uintptr)(unsafe.Pointer(&struct{ f T }{f}))
- }
- // Limit is a category of performance limits.
- //
- // https://sqlite.org/c3ref/c_limit_attached.html
- type Limit int32
- // Limit categories.
- const (
- LimitLength Limit = lib.SQLITE_LIMIT_LENGTH
- LimitSQLLength Limit = lib.SQLITE_LIMIT_SQL_LENGTH
- LimitColumn Limit = lib.SQLITE_LIMIT_COLUMN
- LimitExprDepth Limit = lib.SQLITE_LIMIT_EXPR_DEPTH
- LimitCompoundSelect Limit = lib.SQLITE_LIMIT_COMPOUND_SELECT
- LimitVDBEOp Limit = lib.SQLITE_LIMIT_VDBE_OP
- LimitFunctionArg Limit = lib.SQLITE_LIMIT_FUNCTION_ARG
- LimitAttached Limit = lib.SQLITE_LIMIT_ATTACHED
- LimitLikePatternLength Limit = lib.SQLITE_LIMIT_LIKE_PATTERN_LENGTH
- LimitVariableNumber Limit = lib.SQLITE_LIMIT_VARIABLE_NUMBER
- LimitTriggerDepth Limit = lib.SQLITE_LIMIT_TRIGGER_DEPTH
- LimitWorkerThreads Limit = lib.SQLITE_LIMIT_WORKER_THREADS
- )
- // String returns the limit's C constant name.
- func (limit Limit) String() string {
- switch limit {
- case LimitLength:
- return "SQLITE_LIMIT_LENGTH"
- case LimitSQLLength:
- return "SQLITE_LIMIT_SQL_LENGTH"
- case LimitColumn:
- return "SQLITE_LIMIT_COLUMN"
- case LimitExprDepth:
- return "SQLITE_LIMIT_EXPR_DEPTH"
- case LimitCompoundSelect:
- return "SQLITE_LIMIT_COMPOUND_SELECT"
- case LimitVDBEOp:
- return "SQLITE_LIMIT_VDBE_OP"
- case LimitFunctionArg:
- return "SQLITE_LIMIT_FUNCTION_ARG"
- case LimitAttached:
- return "SQLITE_LIMIT_ATTACHED"
- case LimitLikePatternLength:
- return "SQLITE_LIMIT_LIKE_PATTERN_LENGTH"
- case LimitVariableNumber:
- return "SQLITE_LIMIT_VARIABLE_NUMBER"
- case LimitTriggerDepth:
- return "SQLITE_LIMIT_TRIGGER_DEPTH"
- case LimitWorkerThreads:
- return "SQLITE_LIMIT_WORKER_THREADS"
- default:
- return fmt.Sprintf("Limit(%d)", int32(limit))
- }
- }
- // Limit sets a runtime limit on the connection. The the previous value of the
- // limit is returned. Pass a negative value to check the limit without changing
- // it.
- //
- // https://sqlite.org/c3ref/limit.html
- func (c *Conn) Limit(id Limit, value int32) int32 {
- if c == nil {
- return 0
- }
- return lib.Xsqlite3_limit(c.tls, c.conn, int32(id), int32(value))
- }
- // SetDefensive sets the "defensive" flag for a database connection. When the
- // defensive flag is enabled, language features that allow ordinary SQL to
- // deliberately corrupt the database file are disabled. The disabled features
- // include but are not limited to the following:
- //
- // - The PRAGMA writable_schema=ON statement.
- // - The PRAGMA journal_mode=OFF statement.
- // - Writes to the sqlite_dbpage virtual table.
- // - Direct writes to shadow tables.
- func (c *Conn) SetDefensive(enabled bool) error {
- if c == nil {
- return fmt.Errorf("sqlite: set defensive=%t: nil connection", enabled)
- }
- enabledInt := int32(0)
- if enabled {
- enabledInt = 1
- }
- varArgs := libc.NewVaList(enabledInt)
- if varArgs == 0 {
- return fmt.Errorf("sqlite: set defensive=%t: cannot allocate memory", enabled)
- }
- defer libc.Xfree(c.tls, varArgs)
- res := ResultCode(lib.Xsqlite3_db_config(
- c.tls,
- c.conn,
- lib.SQLITE_DBCONFIG_DEFENSIVE,
- varArgs,
- ))
- if err := res.ToError(); err != nil {
- return fmt.Errorf("sqlite: set defensive=%t: %w", enabled, err)
- }
- return nil
- }
|