Files
sqlite3/stmt.go

474 lines
13 KiB
Go
Raw Permalink Normal View History

2023-01-15 04:35:37 +00:00
package sqlite3
2023-01-17 13:43:16 +00:00
import (
2023-10-13 17:06:05 +01:00
"encoding/json"
2023-01-17 13:43:16 +00:00
"math"
2023-10-13 17:06:05 +01:00
"strconv"
2023-02-21 04:30:24 +00:00
"time"
2023-03-29 15:01:25 +01:00
"github.com/ncruces/go-sqlite3/internal/util"
2023-01-17 13:43:16 +00:00
)
2023-01-15 04:35:37 +00:00
2023-02-10 14:14:19 +00:00
// Stmt is a prepared statement object.
//
// https://www.sqlite.org/c3ref/stmt.html
2023-01-15 04:35:37 +00:00
type Stmt struct {
c *Conn
2023-01-21 00:33:46 +00:00
err error
2023-04-11 15:33:38 +01:00
handle uint32
2023-01-15 04:35:37 +00:00
}
2023-02-10 14:14:19 +00:00
// Close destroys the prepared statement object.
//
2023-03-01 10:34:08 +00:00
// It is safe to close a nil, zero or closed Stmt.
//
2023-02-10 14:14:19 +00:00
// https://www.sqlite.org/c3ref/finalize.html
2023-01-15 04:35:37 +00:00
func (s *Stmt) Close() error {
2023-02-16 13:52:05 +00:00
if s == nil || s.handle == 0 {
2023-02-10 14:14:19 +00:00
return nil
}
2023-02-24 17:49:16 +00:00
r := s.c.call(s.c.api.finalize, uint64(s.handle))
2023-01-15 04:35:37 +00:00
s.handle = 0
2023-05-25 13:17:44 +01:00
return s.c.error(r)
2023-01-17 13:43:16 +00:00
}
2023-02-10 14:14:19 +00:00
// Reset resets the prepared statement object.
//
// https://www.sqlite.org/c3ref/reset.html
2023-01-17 15:01:30 +00:00
func (s *Stmt) Reset() error {
2023-02-24 17:49:16 +00:00
r := s.c.call(s.c.api.reset, uint64(s.handle))
2023-02-10 14:14:19 +00:00
s.err = nil
2023-05-25 13:17:44 +01:00
return s.c.error(r)
2023-01-17 15:01:30 +00:00
}
2023-02-10 14:14:19 +00:00
// ClearBindings resets all bindings on the prepared statement.
//
// https://www.sqlite.org/c3ref/clear_bindings.html
func (s *Stmt) ClearBindings() error {
2023-02-24 17:49:16 +00:00
r := s.c.call(s.c.api.clearBindings, uint64(s.handle))
2023-05-25 13:17:44 +01:00
return s.c.error(r)
2023-02-10 14:14:19 +00:00
}
// Step evaluates the SQL statement.
// If the SQL statement being executed returns any data,
// then true is returned each time a new row of data is ready for processing by the caller.
// The values may be accessed using the Column access functions.
// Step is called again to retrieve the next row of data.
// If an error has occurred, Step returns false;
// call [Stmt.Err] or [Stmt.Reset] to get the error.
//
// https://www.sqlite.org/c3ref/step.html
2023-01-21 00:33:46 +00:00
func (s *Stmt) Step() bool {
2023-02-24 14:31:41 +00:00
s.c.checkInterrupt()
2023-02-24 17:49:16 +00:00
r := s.c.call(s.c.api.step, uint64(s.handle))
2023-08-10 13:18:13 +01:00
switch r {
case _ROW:
2023-01-21 00:33:46 +00:00
return true
2023-08-10 13:18:13 +01:00
case _DONE:
2023-01-21 00:33:46 +00:00
s.err = nil
2023-08-10 13:18:13 +01:00
default:
2023-05-25 13:17:44 +01:00
s.err = s.c.error(r)
2023-01-17 15:01:30 +00:00
}
2023-01-21 00:33:46 +00:00
return false
}
2023-02-10 14:14:19 +00:00
// Err gets the last error occurred during [Stmt.Step].
// Err returns nil after [Stmt.Reset] is called.
//
// https://www.sqlite.org/c3ref/step.html
2023-01-21 00:33:46 +00:00
func (s *Stmt) Err() error {
return s.err
2023-01-17 15:01:30 +00:00
}
2023-02-10 14:14:19 +00:00
// Exec is a convenience function that repeatedly calls [Stmt.Step] until it returns false,
// then calls [Stmt.Reset] to reset the statement and get any error that occurred.
2023-02-08 00:00:53 +00:00
func (s *Stmt) Exec() error {
for s.Step() {
}
2023-02-10 14:14:19 +00:00
return s.Reset()
2023-02-08 00:00:53 +00:00
}
2023-02-18 00:47:56 +00:00
// BindCount returns the number of SQL parameters in the prepared statement.
2023-02-14 18:21:18 +00:00
//
// https://www.sqlite.org/c3ref/bind_parameter_count.html
func (s *Stmt) BindCount() int {
2023-02-24 17:49:16 +00:00
r := s.c.call(s.c.api.bindCount,
2023-02-14 18:21:18 +00:00
uint64(s.handle))
2023-05-25 13:17:44 +01:00
return int(r)
2023-02-14 18:21:18 +00:00
}
2023-02-18 00:47:56 +00:00
// BindIndex returns the index of a parameter in the prepared statement
// given its name.
//
// https://www.sqlite.org/c3ref/bind_parameter_index.html
func (s *Stmt) BindIndex(name string) int {
defer s.c.arena.reset()
namePtr := s.c.arena.string(name)
2023-02-24 17:49:16 +00:00
r := s.c.call(s.c.api.bindIndex,
2023-02-18 00:47:56 +00:00
uint64(s.handle), uint64(namePtr))
2023-05-25 13:17:44 +01:00
return int(r)
2023-02-18 00:47:56 +00:00
}
// BindName returns the name of a parameter in the prepared statement.
// The leftmost SQL parameter has an index of 1.
//
// https://www.sqlite.org/c3ref/bind_parameter_name.html
func (s *Stmt) BindName(param int) string {
2023-02-24 17:49:16 +00:00
r := s.c.call(s.c.api.bindName,
2023-02-18 00:47:56 +00:00
uint64(s.handle), uint64(param))
2023-05-25 13:17:44 +01:00
ptr := uint32(r)
2023-02-18 00:47:56 +00:00
if ptr == 0 {
return ""
}
2023-03-29 15:01:25 +01:00
return util.ReadString(s.c.mod, ptr, _MAX_STRING)
2023-02-18 00:47:56 +00:00
}
2023-02-10 14:14:19 +00:00
// BindBool binds a bool to the prepared statement.
// The leftmost SQL parameter has an index of 1.
// SQLite does not have a separate boolean storage class.
// Instead, boolean values are stored as integers 0 (false) and 1 (true).
//
// https://www.sqlite.org/c3ref/bind_blob.html
2023-01-17 13:43:16 +00:00
func (s *Stmt) BindBool(param int, value bool) error {
2023-06-30 11:48:54 +01:00
var i int64
2023-01-17 13:43:16 +00:00
if value {
2023-06-30 11:48:54 +01:00
i = 1
2023-01-17 13:43:16 +00:00
}
2023-06-30 11:48:54 +01:00
return s.BindInt64(param, i)
2023-01-17 13:43:16 +00:00
}
2023-02-10 14:14:19 +00:00
// BindInt binds an int to the prepared statement.
// The leftmost SQL parameter has an index of 1.
//
// https://www.sqlite.org/c3ref/bind_blob.html
2023-01-17 13:43:16 +00:00
func (s *Stmt) BindInt(param int, value int) error {
return s.BindInt64(param, int64(value))
}
2023-02-10 14:14:19 +00:00
// BindInt64 binds an int64 to the prepared statement.
// The leftmost SQL parameter has an index of 1.
//
// https://www.sqlite.org/c3ref/bind_blob.html
2023-01-17 13:43:16 +00:00
func (s *Stmt) BindInt64(param int, value int64) error {
2023-02-24 17:49:16 +00:00
r := s.c.call(s.c.api.bindInteger,
2023-01-17 13:43:16 +00:00
uint64(s.handle), uint64(param), uint64(value))
2023-05-25 13:17:44 +01:00
return s.c.error(r)
2023-01-17 13:43:16 +00:00
}
2023-02-10 14:14:19 +00:00
// BindFloat binds a float64 to the prepared statement.
// The leftmost SQL parameter has an index of 1.
//
// https://www.sqlite.org/c3ref/bind_blob.html
2023-01-17 13:43:16 +00:00
func (s *Stmt) BindFloat(param int, value float64) error {
2023-02-24 17:49:16 +00:00
r := s.c.call(s.c.api.bindFloat,
2023-01-17 13:43:16 +00:00
uint64(s.handle), uint64(param), math.Float64bits(value))
2023-05-25 13:17:44 +01:00
return s.c.error(r)
2023-01-17 13:43:16 +00:00
}
2023-02-10 14:14:19 +00:00
// BindText binds a string to the prepared statement.
// The leftmost SQL parameter has an index of 1.
//
// https://www.sqlite.org/c3ref/bind_blob.html
2023-01-17 13:43:16 +00:00
func (s *Stmt) BindText(param int, value string) error {
ptr := s.c.newString(value)
2023-02-24 17:49:16 +00:00
r := s.c.call(s.c.api.bindText,
2023-01-17 13:43:16 +00:00
uint64(s.handle), uint64(param),
uint64(ptr), uint64(len(value)),
2023-04-11 15:33:38 +01:00
uint64(s.c.api.destructor), _UTF8)
2023-05-25 13:17:44 +01:00
return s.c.error(r)
2023-01-17 13:43:16 +00:00
}
2023-02-10 14:14:19 +00:00
// BindBlob binds a []byte to the prepared statement.
// The leftmost SQL parameter has an index of 1.
// Binding a nil slice is the same as calling [Stmt.BindNull].
//
// https://www.sqlite.org/c3ref/bind_blob.html
2023-01-17 13:43:16 +00:00
func (s *Stmt) BindBlob(param int, value []byte) error {
ptr := s.c.newBytes(value)
2023-02-24 17:49:16 +00:00
r := s.c.call(s.c.api.bindBlob,
2023-01-17 13:43:16 +00:00
uint64(s.handle), uint64(param),
uint64(ptr), uint64(len(value)),
2023-04-11 15:33:38 +01:00
uint64(s.c.api.destructor))
2023-05-25 13:17:44 +01:00
return s.c.error(r)
2023-01-17 13:43:16 +00:00
}
2023-02-22 14:19:56 +00:00
// BindZeroBlob binds a zero-filled, length n BLOB to the prepared statement.
// The leftmost SQL parameter has an index of 1.
//
// https://www.sqlite.org/c3ref/bind_blob.html
func (s *Stmt) BindZeroBlob(param int, n int64) error {
2023-02-24 17:49:16 +00:00
r := s.c.call(s.c.api.bindZeroBlob,
2023-02-22 14:19:56 +00:00
uint64(s.handle), uint64(param), uint64(n))
2023-05-25 13:17:44 +01:00
return s.c.error(r)
2023-02-22 14:19:56 +00:00
}
2023-02-10 14:14:19 +00:00
// BindNull binds a NULL to the prepared statement.
// The leftmost SQL parameter has an index of 1.
//
// https://www.sqlite.org/c3ref/bind_blob.html
2023-01-17 13:43:16 +00:00
func (s *Stmt) BindNull(param int) error {
2023-02-24 17:49:16 +00:00
r := s.c.call(s.c.api.bindNull,
2023-01-17 13:43:16 +00:00
uint64(s.handle), uint64(param))
2023-05-25 13:17:44 +01:00
return s.c.error(r)
2023-01-15 04:35:37 +00:00
}
2023-01-17 18:31:46 +00:00
2023-02-21 04:30:24 +00:00
// BindTime binds a [time.Time] to the prepared statement.
// The leftmost SQL parameter has an index of 1.
//
// https://www.sqlite.org/c3ref/bind_blob.html
func (s *Stmt) BindTime(param int, value time.Time, format TimeFormat) error {
2023-04-18 01:00:59 +01:00
if format == TimeFormatDefault {
return s.bindRFC3339Nano(param, value)
}
2023-02-21 04:30:24 +00:00
switch v := format.Encode(value).(type) {
case string:
s.BindText(param, v)
case int64:
s.BindInt64(param, v)
case float64:
s.BindFloat(param, v)
default:
2023-03-29 15:01:25 +01:00
panic(util.AssertErr())
2023-02-21 04:30:24 +00:00
}
return nil
}
2023-04-18 01:00:59 +01:00
func (s *Stmt) bindRFC3339Nano(param int, value time.Time) error {
2023-10-19 16:46:58 +01:00
const maxlen = uint64(len(time.RFC3339Nano)) + 5
2023-04-18 01:00:59 +01:00
ptr := s.c.new(maxlen)
buf := util.View(s.c.mod, ptr, maxlen)
buf = value.AppendFormat(buf[:0], time.RFC3339Nano)
r := s.c.call(s.c.api.bindText,
uint64(s.handle), uint64(param),
uint64(ptr), uint64(len(buf)),
uint64(s.c.api.destructor), _UTF8)
2023-05-25 13:17:44 +01:00
return s.c.error(r)
2023-04-18 01:00:59 +01:00
}
2023-11-07 00:50:43 +00:00
// BindPointer binds a NULL to the prepared statement, just like [Stmt.BindNull],
// but it also associates ptr with that NULL value such that it can be retrieved
// within an application-defined SQL function using [Value.Pointer].
//
// https://www.sqlite.org/c3ref/bind_blob.html
func (s *Stmt) BindPointer(param int, ptr any) error {
valPtr := util.AddHandle(s.c.ctx, ptr)
r := s.c.call(s.c.api.bindPointer,
uint64(s.handle), uint64(param), uint64(valPtr))
return s.c.error(r)
}
2023-10-13 17:06:05 +01:00
// BindJSON binds the JSON encoding of value to the prepared statement.
// The leftmost SQL parameter has an index of 1.
//
// https://www.sqlite.org/c3ref/bind_blob.html
func (s *Stmt) BindJSON(param int, value any) error {
data, err := json.Marshal(value)
if err != nil {
return err
}
ptr := s.c.newBytes(data)
r := s.c.call(s.c.api.bindText,
uint64(s.handle), uint64(param),
uint64(ptr), uint64(len(data)),
2023-10-18 23:14:46 +01:00
uint64(s.c.api.destructor), _UTF8)
2023-10-13 17:06:05 +01:00
return s.c.error(r)
}
2023-02-17 02:21:07 +00:00
// ColumnCount returns the number of columns in a result set.
//
// https://www.sqlite.org/c3ref/column_count.html
func (s *Stmt) ColumnCount() int {
2023-02-24 17:49:16 +00:00
r := s.c.call(s.c.api.columnCount,
2023-02-17 02:21:07 +00:00
uint64(s.handle))
2023-05-25 13:17:44 +01:00
return int(r)
2023-02-17 02:21:07 +00:00
}
// ColumnName returns the name of the result column.
// The leftmost column of the result set has the index 0.
//
// https://www.sqlite.org/c3ref/column_name.html
func (s *Stmt) ColumnName(col int) string {
2023-02-24 17:49:16 +00:00
r := s.c.call(s.c.api.columnName,
2023-02-17 02:21:07 +00:00
uint64(s.handle), uint64(col))
2023-05-25 13:17:44 +01:00
ptr := uint32(r)
2023-02-17 02:21:07 +00:00
if ptr == 0 {
2023-03-29 15:01:25 +01:00
panic(util.OOMErr)
2023-02-17 02:21:07 +00:00
}
2023-03-29 15:01:25 +01:00
return util.ReadString(s.c.mod, ptr, _MAX_STRING)
2023-02-17 02:21:07 +00:00
}
2023-02-10 14:14:19 +00:00
// ColumnType returns the initial [Datatype] of the result column.
// The leftmost column of the result set has the index 0.
//
// https://www.sqlite.org/c3ref/column_blob.html
2023-02-08 00:00:53 +00:00
func (s *Stmt) ColumnType(col int) Datatype {
2023-02-24 17:49:16 +00:00
r := s.c.call(s.c.api.columnType,
2023-02-08 00:00:53 +00:00
uint64(s.handle), uint64(col))
2023-05-25 13:17:44 +01:00
return Datatype(r)
2023-02-08 00:00:53 +00:00
}
2023-02-10 14:14:19 +00:00
// ColumnBool returns the value of the result column as a bool.
// The leftmost column of the result set has the index 0.
// SQLite does not have a separate boolean storage class.
// Instead, boolean values are retrieved as integers,
// with 0 converted to false and any other value to true.
//
// https://www.sqlite.org/c3ref/column_blob.html
2023-01-17 18:31:46 +00:00
func (s *Stmt) ColumnBool(col int) bool {
if i := s.ColumnInt64(col); i != 0 {
return true
}
return false
}
2023-02-10 14:14:19 +00:00
// ColumnInt returns the value of the result column as an int.
// The leftmost column of the result set has the index 0.
//
// https://www.sqlite.org/c3ref/column_blob.html
2023-01-17 18:31:46 +00:00
func (s *Stmt) ColumnInt(col int) int {
return int(s.ColumnInt64(col))
}
2023-02-10 14:14:19 +00:00
// ColumnInt64 returns the value of the result column as an int64.
// The leftmost column of the result set has the index 0.
//
// https://www.sqlite.org/c3ref/column_blob.html
2023-01-17 18:31:46 +00:00
func (s *Stmt) ColumnInt64(col int) int64 {
2023-02-24 17:49:16 +00:00
r := s.c.call(s.c.api.columnInteger,
2023-01-17 18:31:46 +00:00
uint64(s.handle), uint64(col))
2023-05-25 13:17:44 +01:00
return int64(r)
2023-01-17 18:31:46 +00:00
}
2023-02-10 14:14:19 +00:00
// ColumnFloat returns the value of the result column as a float64.
// The leftmost column of the result set has the index 0.
//
// https://www.sqlite.org/c3ref/column_blob.html
2023-01-17 18:31:46 +00:00
func (s *Stmt) ColumnFloat(col int) float64 {
2023-02-24 17:49:16 +00:00
r := s.c.call(s.c.api.columnFloat,
2023-01-17 18:31:46 +00:00
uint64(s.handle), uint64(col))
2023-05-25 13:17:44 +01:00
return math.Float64frombits(r)
2023-01-17 18:31:46 +00:00
}
2023-02-21 04:30:24 +00:00
// ColumnTime returns the value of the result column as a [time.Time].
// The leftmost column of the result set has the index 0.
//
// https://www.sqlite.org/c3ref/column_blob.html
func (s *Stmt) ColumnTime(col int, format TimeFormat) time.Time {
var v any
switch s.ColumnType(col) {
case INTEGER:
v = s.ColumnInt64(col)
case FLOAT:
v = s.ColumnFloat(col)
case TEXT, BLOB:
v = s.ColumnText(col)
case NULL:
return time.Time{}
default:
2023-03-29 15:01:25 +01:00
panic(util.AssertErr())
2023-02-21 04:30:24 +00:00
}
t, err := format.Decode(v)
if err != nil {
s.err = err
}
return t
}
2023-02-10 14:14:19 +00:00
// ColumnText returns the value of the result column as a string.
// The leftmost column of the result set has the index 0.
//
// https://www.sqlite.org/c3ref/column_blob.html
2023-01-17 18:31:46 +00:00
func (s *Stmt) ColumnText(col int) string {
2023-03-20 02:16:42 +00:00
return string(s.ColumnRawText(col))
}
// ColumnBlob appends to buf and returns
// the value of the result column as a []byte.
// The leftmost column of the result set has the index 0.
//
// https://www.sqlite.org/c3ref/column_blob.html
func (s *Stmt) ColumnBlob(col int, buf []byte) []byte {
return append(buf, s.ColumnRawBlob(col)...)
}
// ColumnRawText returns the value of the result column as a []byte.
// The []byte is owned by SQLite and may be invalidated by
// subsequent calls to [Stmt] methods.
// The leftmost column of the result set has the index 0.
//
// https://www.sqlite.org/c3ref/column_blob.html
func (s *Stmt) ColumnRawText(col int) []byte {
2023-02-24 17:49:16 +00:00
r := s.c.call(s.c.api.columnText,
2023-01-17 18:31:46 +00:00
uint64(s.handle), uint64(col))
2023-06-30 10:45:16 +01:00
return s.columnRawBytes(col, uint32(r))
2023-01-17 18:31:46 +00:00
}
2023-03-20 02:16:42 +00:00
// ColumnRawBlob returns the value of the result column as a []byte.
// The []byte is owned by SQLite and may be invalidated by
// subsequent calls to [Stmt] methods.
2023-02-10 14:14:19 +00:00
// The leftmost column of the result set has the index 0.
//
// https://www.sqlite.org/c3ref/column_blob.html
2023-03-20 02:16:42 +00:00
func (s *Stmt) ColumnRawBlob(col int) []byte {
2023-02-24 17:49:16 +00:00
r := s.c.call(s.c.api.columnBlob,
2023-01-17 18:31:46 +00:00
uint64(s.handle), uint64(col))
2023-06-30 10:45:16 +01:00
return s.columnRawBytes(col, uint32(r))
}
2023-01-17 18:31:46 +00:00
2023-06-30 10:45:16 +01:00
func (s *Stmt) columnRawBytes(col int, ptr uint32) []byte {
2023-01-17 18:31:46 +00:00
if ptr == 0 {
2023-06-30 10:45:16 +01:00
r := s.c.call(s.c.api.errcode, uint64(s.c.handle))
2023-05-25 13:17:44 +01:00
s.err = s.c.error(r)
2023-03-20 02:16:42 +00:00
return nil
2023-01-17 18:31:46 +00:00
}
2023-06-30 10:45:16 +01:00
r := s.c.call(s.c.api.columnBytes,
2023-01-17 18:31:46 +00:00
uint64(s.handle), uint64(col))
2023-05-25 13:17:44 +01:00
return util.View(s.c.mod, ptr, r)
2023-01-17 18:31:46 +00:00
}
2023-02-24 15:06:19 +00:00
2023-10-13 17:06:05 +01:00
// ColumnJSON parses the JSON-encoded value of the result column
// and stores it in the value pointed to by ptr.
// The leftmost column of the result set has the index 0.
//
// https://www.sqlite.org/c3ref/column_blob.html
func (s *Stmt) ColumnJSON(col int, ptr any) error {
var data []byte
switch s.ColumnType(col) {
case NULL:
2023-10-19 16:46:58 +01:00
data = append(data, "null"...)
case TEXT:
data = s.ColumnRawText(col)
case BLOB:
2023-10-13 17:06:05 +01:00
data = s.ColumnRawBlob(col)
case INTEGER:
data = strconv.AppendInt(nil, s.ColumnInt64(col), 10)
case FLOAT:
data = strconv.AppendFloat(nil, s.ColumnFloat(col), 'g', -1, 64)
default:
panic(util.AssertErr())
}
return json.Unmarshal(data, ptr)
}
2023-02-24 15:06:19 +00:00
// Return true if stmt is an empty SQL statement.
// This is used as an optimization.
// It's OK to always return false here.
func emptyStatement(stmt string) bool {
for _, b := range []byte(stmt) {
switch b {
case ' ', '\n', '\r', '\t', '\v', '\f':
case ';':
default:
return false
}
}
return true
}