Files
sqlite3/conn.go

497 lines
14 KiB
Go
Raw Permalink Normal View History

2023-01-12 05:57:09 +00:00
package sqlite3
2023-01-11 14:58:20 +00:00
import (
"context"
2023-02-28 16:03:31 +00:00
"fmt"
2025-02-21 12:17:08 +00:00
"iter"
2024-02-02 23:41:34 +00:00
"math"
2024-12-13 10:23:43 +00:00
"math/rand"
2023-02-28 16:03:31 +00:00
"net/url"
2025-01-07 16:31:12 +00:00
"runtime"
2023-02-28 16:03:31 +00:00
"strings"
2024-02-02 23:41:34 +00:00
"time"
2023-03-29 15:01:25 +01:00
2024-10-18 12:20:32 +01:00
"github.com/tetratelabs/wazero/api"
2023-03-29 15:01:25 +01:00
"github.com/ncruces/go-sqlite3/internal/util"
"github.com/ncruces/go-sqlite3/vfs"
2023-01-11 14:58:20 +00:00
)
2023-02-09 16:40:48 +00:00
// Conn is a database connection handle.
2023-03-01 10:34:08 +00:00
// A Conn is not safe for concurrent use by multiple goroutines.
2023-02-09 16:40:48 +00:00
//
2023-11-09 16:35:45 +00:00
// https://sqlite.org/c3ref/sqlite3.html
2023-01-12 05:57:09 +00:00
type Conn struct {
2023-07-03 17:21:35 +01:00
*sqlite
2023-02-14 18:21:18 +00:00
2024-01-27 10:57:46 +00:00
interrupt context.Context
stmts []*Stmt
2024-09-27 12:33:57 +01:00
busy func(context.Context, int) bool
2024-01-27 10:57:46 +00:00
log func(xErrorCode, string)
collation func(*Conn, string)
wal func(*Conn, string, int) error
trace func(TraceEvent, any, any) error
2024-01-27 10:57:46 +00:00
authorizer func(AuthorizerActionCode, string, string, string, string) AuthorizerReturnCode
update func(AuthorizerActionCode, string, string, int64)
commit func() bool
rollback func()
2023-04-11 15:33:38 +01:00
2024-12-13 16:04:37 +00:00
busy1st time.Time
busylst time.Time
2025-01-21 01:42:57 +00:00
arena arena
handle ptr_t
2025-03-23 11:04:10 +00:00
gosched uint8
2023-01-11 14:58:20 +00:00
}
2024-09-27 12:32:11 +01:00
// Open calls [OpenFlags] with [OPEN_READWRITE], [OPEN_CREATE] and [OPEN_URI].
func Open(filename string) (*Conn, error) {
2024-10-04 13:31:53 +01:00
return newConn(context.Background(), filename, OPEN_READWRITE|OPEN_CREATE|OPEN_URI)
}
// OpenContext is like [Open] but includes a context,
2025-03-11 21:47:06 +00:00
// which is used to interrupt the process of opening the connection.
2024-10-04 13:31:53 +01:00
func OpenContext(ctx context.Context, filename string) (*Conn, error) {
return newConn(ctx, filename, OPEN_READWRITE|OPEN_CREATE|OPEN_URI)
2023-01-16 12:54:24 +00:00
}
2023-02-09 16:40:48 +00:00
// OpenFlags opens an SQLite database file as specified by the filename argument.
//
2024-10-04 13:31:53 +01:00
// If none of the required flags are used, a combination of [OPEN_READWRITE] and [OPEN_CREATE] is used.
2023-02-28 16:03:31 +00:00
// If a URI filename is used, PRAGMA statements to execute can be specified using "_pragma":
//
2023-06-24 02:18:56 +01:00
// sqlite3.Open("file:demo.db?_pragma=busy_timeout(10000)")
2023-02-28 16:03:31 +00:00
//
2023-11-09 16:35:45 +00:00
// https://sqlite.org/c3ref/open.html
func OpenFlags(filename string, flags OpenFlag) (*Conn, error) {
2023-03-16 12:27:44 +00:00
if flags&(OPEN_READONLY|OPEN_READWRITE|OPEN_CREATE) == 0 {
flags |= OPEN_READWRITE | OPEN_CREATE
}
2024-10-04 13:31:53 +01:00
return newConn(context.Background(), filename, flags)
}
2025-01-13 13:45:41 +00:00
type connKey = util.ConnKey
2023-10-25 12:56:52 +01:00
2025-01-21 01:42:57 +00:00
func newConn(ctx context.Context, filename string, flags OpenFlag) (ret *Conn, _ error) {
2024-10-07 13:22:31 +01:00
err := ctx.Err()
2024-10-04 13:31:53 +01:00
if err != nil {
return nil, err
}
2024-10-07 13:22:31 +01:00
c := &Conn{interrupt: ctx}
c.sqlite, err = instantiateSQLite()
2023-01-11 14:58:20 +00:00
if err != nil {
2023-01-12 05:57:09 +00:00
return nil, err
2023-01-11 14:58:20 +00:00
}
2023-01-12 13:43:35 +00:00
defer func() {
2025-01-21 01:42:57 +00:00
if ret == nil {
2024-10-07 13:22:31 +01:00
c.Close()
c.sqlite.close()
} else {
c.interrupt = context.Background()
2023-01-12 13:43:35 +00:00
}
}()
2023-01-12 05:57:09 +00:00
2023-10-25 12:56:52 +01:00
c.ctx = context.WithValue(c.ctx, connKey{}, c)
2025-03-11 17:02:27 +00:00
if logger := defaultLogger.Load(); logger != nil {
c.ConfigLog(*logger)
}
2025-01-21 01:42:57 +00:00
c.arena = c.newArena()
2023-03-03 14:48:56 +00:00
c.handle, err = c.openDB(filename, flags)
2024-07-08 12:06:57 +01:00
if err == nil {
err = initExtensions(c)
}
2024-10-07 13:22:31 +01:00
if err != nil {
return nil, err
}
return c, nil
2023-03-03 14:48:56 +00:00
}
2023-02-10 16:42:49 +00:00
2025-01-21 01:42:57 +00:00
func (c *Conn) openDB(filename string, flags OpenFlag) (ptr_t, error) {
2023-11-29 10:38:03 +00:00
defer c.arena.mark()()
2023-02-14 11:34:24 +00:00
connPtr := c.arena.new(ptrlen)
namePtr := c.arena.string(filename)
2023-01-12 13:43:35 +00:00
2023-03-15 13:29:09 +00:00
flags |= OPEN_EXRESCODE
2025-01-21 01:42:57 +00:00
rc := res_t(c.call("sqlite3_open_v2", stk_t(namePtr), stk_t(connPtr), stk_t(flags), 0))
2023-01-11 14:58:20 +00:00
2025-01-21 01:42:57 +00:00
handle := util.Read32[ptr_t](c.mod, connPtr)
if err := c.sqlite.error(rc, handle); err != nil {
2023-03-03 14:48:56 +00:00
c.closeDB(handle)
return 0, err
2023-01-12 05:57:09 +00:00
}
2023-02-28 16:03:31 +00:00
c.call("sqlite3_progress_handler_go", stk_t(handle), 1000)
2023-02-28 16:03:31 +00:00
if flags|OPEN_URI != 0 && strings.HasPrefix(filename, "file:") {
var pragmas strings.Builder
if _, after, ok := strings.Cut(filename, "?"); ok {
query, _ := url.ParseQuery(after)
for _, p := range query["_pragma"] {
pragmas.WriteString(`PRAGMA `)
pragmas.WriteString(p)
2023-12-06 15:39:26 +00:00
pragmas.WriteString(`;`)
2023-02-28 16:03:31 +00:00
}
}
2024-04-26 00:07:04 +01:00
if pragmas.Len() != 0 {
pragmaPtr := c.arena.string(pragmas.String())
2025-01-21 01:42:57 +00:00
rc := res_t(c.call("sqlite3_exec", stk_t(handle), stk_t(pragmaPtr), 0, 0, 0))
if err := c.sqlite.error(rc, handle, pragmas.String()); err != nil {
2023-03-10 15:50:11 +00:00
err = fmt.Errorf("sqlite3: invalid _pragma: %w", err)
2024-04-26 00:07:04 +01:00
c.closeDB(handle)
return 0, err
2023-03-10 15:50:11 +00:00
}
2023-02-28 16:03:31 +00:00
}
}
2023-03-03 14:48:56 +00:00
return handle, nil
}
2025-01-21 01:42:57 +00:00
func (c *Conn) closeDB(handle ptr_t) {
rc := res_t(c.call("sqlite3_close_v2", stk_t(handle)))
if err := c.sqlite.error(rc, handle); err != nil {
2023-03-03 14:48:56 +00:00
panic(err)
}
2023-01-12 05:57:09 +00:00
}
2023-02-10 14:14:19 +00:00
// Close closes the database connection.
//
2023-02-09 16:40:48 +00:00
// If the database connection is associated with unfinalized prepared statements,
// open blob handles, and/or unfinished backup objects,
// Close will leave the database connection open and return [BUSY].
//
2023-03-01 10:34:08 +00:00
// It is safe to close a nil, zero or closed Conn.
//
2023-11-09 16:35:45 +00:00
// https://sqlite.org/c3ref/close.html
2023-01-12 13:43:35 +00:00
func (c *Conn) Close() error {
2023-02-16 13:52:05 +00:00
if c == nil || c.handle == 0 {
2023-02-10 14:14:19 +00:00
return nil
}
2025-01-21 01:42:57 +00:00
rc := res_t(c.call("sqlite3_close", stk_t(c.handle)))
if err := c.error(rc); err != nil {
2023-01-17 13:43:16 +00:00
return err
2023-01-12 13:43:35 +00:00
}
2023-02-10 14:14:19 +00:00
c.handle = 0
2023-07-03 17:21:35 +01:00
return c.close()
2023-01-11 14:58:20 +00:00
}
2023-02-24 15:06:19 +00:00
// Exec is a convenience function that allows an application to run
// multiple statements of SQL without having to use a lot of code.
//
2023-11-09 16:35:45 +00:00
// https://sqlite.org/c3ref/exec.html
2023-02-24 15:06:19 +00:00
func (c *Conn) Exec(sql string) error {
2025-03-26 11:39:06 +00:00
if c.interrupt.Err() != nil {
return INTERRUPT
}
return c.exec(sql)
}
func (c *Conn) exec(sql string) error {
2023-11-29 10:38:03 +00:00
defer c.arena.mark()()
2025-03-25 12:45:27 +00:00
textPtr := c.arena.string(sql)
rc := res_t(c.call("sqlite3_exec", stk_t(c.handle), stk_t(textPtr), 0, 0, 0))
2025-01-21 01:42:57 +00:00
return c.error(rc, sql)
2023-02-24 15:06:19 +00:00
}
// Prepare calls [Conn.PrepareFlags] with no flags.
func (c *Conn) Prepare(sql string) (stmt *Stmt, tail string, err error) {
return c.PrepareFlags(sql, 0)
}
// PrepareFlags compiles the first SQL statement in sql;
// tail is left pointing to what remains uncompiled.
// If the input text contains no SQL (if the input is an empty string or a comment),
// both stmt and err will be nil.
//
2023-11-09 16:35:45 +00:00
// https://sqlite.org/c3ref/prepare.html
2023-02-24 15:06:19 +00:00
func (c *Conn) PrepareFlags(sql string, flags PrepareFlag) (stmt *Stmt, tail string, err error) {
2024-04-25 13:29:19 +01:00
if len(sql) > _MAX_SQL_LENGTH {
2023-11-24 17:25:02 +00:00
return nil, "", TOOBIG
}
2025-03-26 11:39:06 +00:00
if c.interrupt.Err() != nil {
return nil, "", INTERRUPT
}
2023-02-24 15:06:19 +00:00
2023-11-29 10:38:03 +00:00
defer c.arena.mark()()
2023-02-24 15:06:19 +00:00
stmtPtr := c.arena.new(ptrlen)
tailPtr := c.arena.new(ptrlen)
2025-03-23 11:04:10 +00:00
textPtr := c.arena.string(sql)
2023-02-24 15:06:19 +00:00
2025-01-21 01:42:57 +00:00
rc := res_t(c.call("sqlite3_prepare_v3", stk_t(c.handle),
2025-03-23 11:04:10 +00:00
stk_t(textPtr), stk_t(len(sql)+1), stk_t(flags),
2025-01-21 01:42:57 +00:00
stk_t(stmtPtr), stk_t(tailPtr)))
2023-02-24 15:06:19 +00:00
2025-03-23 11:04:10 +00:00
stmt = &Stmt{c: c, sql: sql}
2025-01-21 01:42:57 +00:00
stmt.handle = util.Read32[ptr_t](c.mod, stmtPtr)
2025-03-23 11:04:10 +00:00
if sql := sql[util.Read32[ptr_t](c.mod, tailPtr)-textPtr:]; sql != "" {
2023-11-30 12:26:15 +00:00
tail = sql
}
2023-02-24 15:06:19 +00:00
2025-01-21 01:42:57 +00:00
if err := c.error(rc, sql); err != nil {
2023-02-24 15:06:19 +00:00
return nil, "", err
}
if stmt.handle == 0 {
return nil, "", nil
}
c.stmts = append(c.stmts, stmt)
2023-12-06 15:39:26 +00:00
return stmt, tail, nil
2023-02-24 15:06:19 +00:00
}
2024-01-17 15:39:13 +00:00
// DBName returns the schema name for n-th database on the database connection.
//
// https://sqlite.org/c3ref/db_name.html
func (c *Conn) DBName(n int) string {
2025-01-21 01:42:57 +00:00
ptr := ptr_t(c.call("sqlite3_db_name", stk_t(c.handle), stk_t(n)))
2024-01-17 15:39:13 +00:00
if ptr == 0 {
return ""
}
return util.ReadString(c.mod, ptr, _MAX_NAME)
}
// Filename returns the filename for a database.
//
// https://sqlite.org/c3ref/db_filename.html
func (c *Conn) Filename(schema string) *vfs.Filename {
2025-01-21 01:42:57 +00:00
var ptr ptr_t
if schema != "" {
defer c.arena.mark()()
ptr = c.arena.string(schema)
}
2025-01-21 01:42:57 +00:00
ptr = ptr_t(c.call("sqlite3_db_filename", stk_t(c.handle), stk_t(ptr)))
return vfs.GetFilename(c.ctx, c.mod, ptr, vfs.OPEN_MAIN_DB)
}
2024-01-17 15:39:13 +00:00
// ReadOnly determines if a database is read-only.
//
// https://sqlite.org/c3ref/db_readonly.html
func (c *Conn) ReadOnly(schema string) (ro bool, ok bool) {
2025-01-21 01:42:57 +00:00
var ptr ptr_t
2024-01-17 15:39:13 +00:00
if schema != "" {
defer c.arena.mark()()
ptr = c.arena.string(schema)
}
2025-01-21 01:42:57 +00:00
b := int32(c.call("sqlite3_db_readonly", stk_t(c.handle), stk_t(ptr)))
return b > 0, b < 0
2024-01-17 15:39:13 +00:00
}
2023-02-24 14:31:41 +00:00
// GetAutocommit tests the connection for auto-commit mode.
//
2023-11-09 16:35:45 +00:00
// https://sqlite.org/c3ref/get_autocommit.html
2023-02-24 14:31:41 +00:00
func (c *Conn) GetAutocommit() bool {
2025-01-21 01:42:57 +00:00
b := int32(c.call("sqlite3_get_autocommit", stk_t(c.handle)))
return b != 0
2023-02-24 14:31:41 +00:00
}
2023-02-24 15:06:19 +00:00
// LastInsertRowID returns the rowid of the most recent successful INSERT
// on the database connection.
//
2023-11-09 16:35:45 +00:00
// https://sqlite.org/c3ref/last_insert_rowid.html
2023-02-27 13:45:32 +00:00
func (c *Conn) LastInsertRowID() int64 {
2025-01-21 01:42:57 +00:00
return int64(c.call("sqlite3_last_insert_rowid", stk_t(c.handle)))
2023-02-24 15:06:19 +00:00
}
2024-01-17 15:39:13 +00:00
// SetLastInsertRowID allows the application to set the value returned by
// [Conn.LastInsertRowID].
//
// https://sqlite.org/c3ref/set_last_insert_rowid.html
func (c *Conn) SetLastInsertRowID(id int64) {
2025-01-21 01:42:57 +00:00
c.call("sqlite3_set_last_insert_rowid", stk_t(c.handle), stk_t(id))
2024-01-17 15:39:13 +00:00
}
2023-02-24 15:06:19 +00:00
// Changes returns the number of rows modified, inserted or deleted
// by the most recently completed INSERT, UPDATE or DELETE statement
// on the database connection.
//
2023-11-09 16:35:45 +00:00
// https://sqlite.org/c3ref/changes.html
2023-02-27 13:45:32 +00:00
func (c *Conn) Changes() int64 {
2025-01-21 01:42:57 +00:00
return int64(c.call("sqlite3_changes64", stk_t(c.handle)))
2023-02-24 15:06:19 +00:00
}
2024-01-17 15:39:13 +00:00
// TotalChanges returns the number of rows modified, inserted or deleted
// by all INSERT, UPDATE or DELETE statements completed
// since the database connection was opened.
//
// https://sqlite.org/c3ref/total_changes.html
func (c *Conn) TotalChanges() int64 {
2025-01-21 01:42:57 +00:00
return int64(c.call("sqlite3_total_changes64", stk_t(c.handle)))
2024-01-17 15:39:13 +00:00
}
// ReleaseMemory frees memory used by a database connection.
//
// https://sqlite.org/c3ref/db_release_memory.html
func (c *Conn) ReleaseMemory() error {
2025-01-21 01:42:57 +00:00
rc := res_t(c.call("sqlite3_db_release_memory", stk_t(c.handle)))
return c.error(rc)
2024-01-17 15:39:13 +00:00
}
2024-10-04 13:31:53 +01:00
// GetInterrupt gets the context set with [Conn.SetInterrupt].
2024-04-12 14:57:13 +01:00
func (c *Conn) GetInterrupt() context.Context {
return c.interrupt
}
2023-02-24 14:56:49 +00:00
// SetInterrupt interrupts a long-running query when a context is done.
2023-02-14 18:21:18 +00:00
//
// Subsequent uses of the connection will return [INTERRUPT]
2023-02-24 14:56:49 +00:00
// until the context is reset by another call to SetInterrupt.
2023-02-14 18:21:18 +00:00
//
2023-03-01 10:34:08 +00:00
// To associate a timeout with a connection:
2023-02-14 18:21:18 +00:00
//
// ctx, cancel := context.WithTimeout(context.TODO(), 100*time.Millisecond)
2023-02-24 14:56:49 +00:00
// conn.SetInterrupt(ctx)
2023-02-14 18:21:18 +00:00
// defer cancel()
//
2023-02-24 14:56:49 +00:00
// SetInterrupt returns the old context assigned to the connection.
//
2023-11-09 16:35:45 +00:00
// https://sqlite.org/c3ref/interrupt.html
2023-02-24 14:56:49 +00:00
func (c *Conn) SetInterrupt(ctx context.Context) (old context.Context) {
2025-03-28 11:10:51 +00:00
if ctx == nil {
panic("nil Context")
}
2024-10-04 13:31:53 +01:00
old = c.interrupt
c.interrupt = ctx
2023-10-25 12:56:52 +01:00
return old
}
2023-02-24 14:31:41 +00:00
2025-01-21 01:42:57 +00:00
func progressCallback(ctx context.Context, mod api.Module, _ ptr_t) (interrupt int32) {
2025-01-07 16:31:12 +00:00
if c, ok := ctx.Value(connKey{}).(*Conn); ok {
2025-03-23 11:04:10 +00:00
if c.gosched++; c.gosched%16 == 0 {
2025-01-07 16:31:12 +00:00
runtime.Gosched()
}
if c.interrupt.Err() != nil {
interrupt = 1
}
2023-10-25 12:56:52 +01:00
}
2024-04-12 14:57:13 +01:00
return interrupt
2023-02-14 18:21:18 +00:00
}
2024-02-02 23:41:34 +00:00
// BusyTimeout sets a busy timeout.
//
// https://sqlite.org/c3ref/busy_timeout.html
func (c *Conn) BusyTimeout(timeout time.Duration) error {
ms := min((timeout+time.Millisecond-1)/time.Millisecond, math.MaxInt32)
2025-01-21 01:42:57 +00:00
rc := res_t(c.call("sqlite3_busy_timeout", stk_t(c.handle), stk_t(ms)))
return c.error(rc)
2024-02-02 23:41:34 +00:00
}
2025-01-21 01:42:57 +00:00
func timeoutCallback(ctx context.Context, mod api.Module, count, tmout int32) (retry int32) {
2024-12-13 10:23:43 +00:00
// https://fractaledmind.github.io/2024/04/15/sqlite-on-rails-the-how-and-why-of-optimal-performance/
2024-10-04 13:31:53 +01:00
if c, ok := ctx.Value(connKey{}).(*Conn); ok && c.interrupt.Err() == nil {
2024-12-13 10:23:43 +00:00
switch {
case count == 0:
2024-12-13 16:04:37 +00:00
c.busy1st = time.Now()
case time.Since(c.busy1st) >= time.Duration(tmout)*time.Millisecond:
2024-12-13 10:23:43 +00:00
return 0
2024-05-19 01:04:56 +01:00
}
2024-12-13 16:04:37 +00:00
if time.Since(c.busylst) < time.Millisecond {
const sleepIncrement = 2*1024*1024 - 1 // power of two, ~2ms
time.Sleep(time.Duration(rand.Int63() & sleepIncrement))
}
c.busylst = time.Now()
2024-12-13 10:23:43 +00:00
return 1
2024-05-19 01:04:56 +01:00
}
2024-09-03 12:24:03 +01:00
return 0
2024-05-19 01:04:56 +01:00
}
2024-02-02 23:41:34 +00:00
// BusyHandler registers a callback to handle [BUSY] errors.
//
// https://sqlite.org/c3ref/busy_handler.html
2024-09-27 12:33:57 +01:00
func (c *Conn) BusyHandler(cb func(ctx context.Context, count int) (retry bool)) error {
2025-01-21 01:42:57 +00:00
var enable int32
2024-02-02 23:41:34 +00:00
if cb != nil {
enable = 1
}
2025-01-21 01:42:57 +00:00
rc := res_t(c.call("sqlite3_busy_handler_go", stk_t(c.handle), stk_t(enable)))
if err := c.error(rc); err != nil {
2024-02-02 23:41:34 +00:00
return err
}
c.busy = cb
return nil
}
2025-01-21 01:42:57 +00:00
func busyCallback(ctx context.Context, mod api.Module, pDB ptr_t, count int32) (retry int32) {
2024-09-27 12:33:57 +01:00
if c, ok := ctx.Value(connKey{}).(*Conn); ok && c.handle == pDB && c.busy != nil {
2025-03-28 11:10:51 +00:00
if interrupt := c.interrupt; interrupt.Err() == nil &&
c.busy(interrupt, int(count)) {
2024-04-12 14:57:13 +01:00
retry = 1
2024-02-02 23:41:34 +00:00
}
}
2024-04-12 14:57:13 +01:00
return retry
2024-02-02 23:41:34 +00:00
}
// Status retrieves runtime status information about a database connection.
//
// https://sqlite.org/c3ref/db_status.html
func (c *Conn) Status(op DBStatus, reset bool) (current, highwater int, err error) {
defer c.arena.mark()()
2024-10-22 13:08:31 +01:00
hiPtr := c.arena.new(intlen)
curPtr := c.arena.new(intlen)
2025-01-21 01:42:57 +00:00
var i int32
if reset {
i = 1
}
2025-01-21 01:42:57 +00:00
rc := res_t(c.call("sqlite3_db_status", stk_t(c.handle),
stk_t(op), stk_t(curPtr), stk_t(hiPtr), stk_t(i)))
if err = c.error(rc); err == nil {
current = int(util.Read32[int32](c.mod, curPtr))
highwater = int(util.Read32[int32](c.mod, hiPtr))
}
return
}
// TableColumnMetadata extracts metadata about a column of a table.
//
// https://sqlite.org/c3ref/table_column_metadata.html
func (c *Conn) TableColumnMetadata(schema, table, column string) (declType, collSeq string, notNull, primaryKey, autoInc bool, err error) {
defer c.arena.mark()()
2025-01-21 01:42:57 +00:00
var schemaPtr, columnPtr ptr_t
declTypePtr := c.arena.new(ptrlen)
collSeqPtr := c.arena.new(ptrlen)
notNullPtr := c.arena.new(ptrlen)
autoIncPtr := c.arena.new(ptrlen)
2024-10-22 13:08:31 +01:00
primaryKeyPtr := c.arena.new(ptrlen)
if schema != "" {
schemaPtr = c.arena.string(schema)
}
tablePtr := c.arena.string(table)
if column != "" {
columnPtr = c.arena.string(column)
}
2025-01-21 01:42:57 +00:00
rc := res_t(c.call("sqlite3_table_column_metadata", stk_t(c.handle),
stk_t(schemaPtr), stk_t(tablePtr), stk_t(columnPtr),
stk_t(declTypePtr), stk_t(collSeqPtr),
stk_t(notNullPtr), stk_t(primaryKeyPtr), stk_t(autoIncPtr)))
if err = c.error(rc); err == nil && column != "" {
if ptr := util.Read32[ptr_t](c.mod, declTypePtr); ptr != 0 {
2024-12-09 19:26:47 +00:00
declType = util.ReadString(c.mod, ptr, _MAX_NAME)
}
2025-01-21 01:42:57 +00:00
if ptr := util.Read32[ptr_t](c.mod, collSeqPtr); ptr != 0 {
2024-12-09 19:26:47 +00:00
collSeq = util.ReadString(c.mod, ptr, _MAX_NAME)
}
2025-02-17 12:00:55 +00:00
notNull = util.ReadBool(c.mod, notNullPtr)
autoInc = util.ReadBool(c.mod, autoIncPtr)
primaryKey = util.ReadBool(c.mod, primaryKeyPtr)
}
return
}
2025-01-21 01:42:57 +00:00
func (c *Conn) error(rc res_t, sql ...string) error {
2023-07-03 17:21:35 +01:00
return c.sqlite.error(rc, c.handle, sql...)
2023-03-03 14:48:56 +00:00
}
2025-02-21 12:17:08 +00:00
// Stmts returns an iterator for the prepared statements
// associated with the database connection.
//
// https://sqlite.org/c3ref/next_stmt.html
func (c *Conn) Stmts() iter.Seq[*Stmt] {
return func(yield func(*Stmt) bool) {
for _, s := range c.stmts {
if !yield(s) {
break
}
}
}
}