summaryrefslogtreecommitdiff
path: root/vendor/github.com/ncruces/go-sqlite3/stmt.go
diff options
context:
space:
mode:
authorLibravatar kim <89579420+NyaaaWhatsUpDoc@users.noreply.github.com>2024-05-27 15:46:15 +0000
committerLibravatar GitHub <noreply@github.com>2024-05-27 17:46:15 +0200
commit1e7b32490dfdccddd04f46d4b0416b48d749d51b (patch)
tree62a11365933a5a11e0800af64cbdf9172e5e6e7a /vendor/github.com/ncruces/go-sqlite3/stmt.go
parent[chore] Small styling + link issues (#2933) (diff)
downloadgotosocial-1e7b32490dfdccddd04f46d4b0416b48d749d51b.tar.xz
[experiment] add alternative wasm sqlite3 implementation available via build-tag (#2863)
This allows for building GoToSocial with [SQLite transpiled to WASM](https://github.com/ncruces/go-sqlite3) and accessed through [Wazero](https://wazero.io/).
Diffstat (limited to 'vendor/github.com/ncruces/go-sqlite3/stmt.go')
-rw-r--r--vendor/github.com/ncruces/go-sqlite3/stmt.go639
1 files changed, 639 insertions, 0 deletions
diff --git a/vendor/github.com/ncruces/go-sqlite3/stmt.go b/vendor/github.com/ncruces/go-sqlite3/stmt.go
new file mode 100644
index 000000000..63c2085d0
--- /dev/null
+++ b/vendor/github.com/ncruces/go-sqlite3/stmt.go
@@ -0,0 +1,639 @@
+package sqlite3
+
+import (
+ "encoding/json"
+ "math"
+ "strconv"
+ "time"
+
+ "github.com/ncruces/go-sqlite3/internal/util"
+)
+
+// Stmt is a prepared statement object.
+//
+// https://sqlite.org/c3ref/stmt.html
+type Stmt struct {
+ c *Conn
+ err error
+ handle uint32
+}
+
+// Close destroys the prepared statement object.
+//
+// It is safe to close a nil, zero or closed Stmt.
+//
+// https://sqlite.org/c3ref/finalize.html
+func (s *Stmt) Close() error {
+ if s == nil || s.handle == 0 {
+ return nil
+ }
+
+ r := s.c.call("sqlite3_finalize", uint64(s.handle))
+
+ s.handle = 0
+ return s.c.error(r)
+}
+
+// Conn returns the database connection to which the prepared statement belongs.
+//
+// https://sqlite.org/c3ref/db_handle.html
+func (s *Stmt) Conn() *Conn {
+ return s.c
+}
+
+// ReadOnly returns true if and only if the statement
+// makes no direct changes to the content of the database file.
+//
+// https://sqlite.org/c3ref/stmt_readonly.html
+func (s *Stmt) ReadOnly() bool {
+ r := s.c.call("sqlite3_stmt_readonly", uint64(s.handle))
+ return r != 0
+}
+
+// Reset resets the prepared statement object.
+//
+// https://sqlite.org/c3ref/reset.html
+func (s *Stmt) Reset() error {
+ r := s.c.call("sqlite3_reset", uint64(s.handle))
+ s.err = nil
+ return s.c.error(r)
+}
+
+// Busy determines if a prepared statement has been reset.
+//
+// https://sqlite.org/c3ref/stmt_busy.html
+func (s *Stmt) Busy() bool {
+ r := s.c.call("sqlite3_stmt_busy", uint64(s.handle))
+ return r != 0
+}
+
+// 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://sqlite.org/c3ref/step.html
+func (s *Stmt) Step() bool {
+ s.c.checkInterrupt()
+ r := s.c.call("sqlite3_step", uint64(s.handle))
+ switch r {
+ case _ROW:
+ s.err = nil
+ return true
+ case _DONE:
+ s.err = nil
+ default:
+ s.err = s.c.error(r)
+ }
+ return false
+}
+
+// Err gets the last error occurred during [Stmt.Step].
+// Err returns nil after [Stmt.Reset] is called.
+//
+// https://sqlite.org/c3ref/step.html
+func (s *Stmt) Err() error {
+ return s.err
+}
+
+// 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.
+func (s *Stmt) Exec() error {
+ for s.Step() {
+ }
+ return s.Reset()
+}
+
+// Status monitors the performance characteristics of prepared statements.
+//
+// https://sqlite.org/c3ref/stmt_status.html
+func (s *Stmt) Status(op StmtStatus, reset bool) int {
+ if op > STMTSTATUS_FILTER_HIT && op != STMTSTATUS_MEMUSED {
+ return 0
+ }
+ var i uint64
+ if reset {
+ i = 1
+ }
+ r := s.c.call("sqlite3_stmt_status", uint64(s.handle),
+ uint64(op), i)
+ return int(int32(r))
+}
+
+// ClearBindings resets all bindings on the prepared statement.
+//
+// https://sqlite.org/c3ref/clear_bindings.html
+func (s *Stmt) ClearBindings() error {
+ r := s.c.call("sqlite3_clear_bindings", uint64(s.handle))
+ return s.c.error(r)
+}
+
+// BindCount returns the number of SQL parameters in the prepared statement.
+//
+// https://sqlite.org/c3ref/bind_parameter_count.html
+func (s *Stmt) BindCount() int {
+ r := s.c.call("sqlite3_bind_parameter_count",
+ uint64(s.handle))
+ return int(int32(r))
+}
+
+// BindIndex returns the index of a parameter in the prepared statement
+// given its name.
+//
+// https://sqlite.org/c3ref/bind_parameter_index.html
+func (s *Stmt) BindIndex(name string) int {
+ defer s.c.arena.mark()()
+ namePtr := s.c.arena.string(name)
+ r := s.c.call("sqlite3_bind_parameter_index",
+ uint64(s.handle), uint64(namePtr))
+ return int(int32(r))
+}
+
+// BindName returns the name of a parameter in the prepared statement.
+// The leftmost SQL parameter has an index of 1.
+//
+// https://sqlite.org/c3ref/bind_parameter_name.html
+func (s *Stmt) BindName(param int) string {
+ r := s.c.call("sqlite3_bind_parameter_name",
+ uint64(s.handle), uint64(param))
+
+ ptr := uint32(r)
+ if ptr == 0 {
+ return ""
+ }
+ return util.ReadString(s.c.mod, ptr, _MAX_NAME)
+}
+
+// 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://sqlite.org/c3ref/bind_blob.html
+func (s *Stmt) BindBool(param int, value bool) error {
+ var i int64
+ if value {
+ i = 1
+ }
+ return s.BindInt64(param, i)
+}
+
+// BindInt binds an int to the prepared statement.
+// The leftmost SQL parameter has an index of 1.
+//
+// https://sqlite.org/c3ref/bind_blob.html
+func (s *Stmt) BindInt(param int, value int) error {
+ return s.BindInt64(param, int64(value))
+}
+
+// BindInt64 binds an int64 to the prepared statement.
+// The leftmost SQL parameter has an index of 1.
+//
+// https://sqlite.org/c3ref/bind_blob.html
+func (s *Stmt) BindInt64(param int, value int64) error {
+ r := s.c.call("sqlite3_bind_int64",
+ uint64(s.handle), uint64(param), uint64(value))
+ return s.c.error(r)
+}
+
+// BindFloat binds a float64 to the prepared statement.
+// The leftmost SQL parameter has an index of 1.
+//
+// https://sqlite.org/c3ref/bind_blob.html
+func (s *Stmt) BindFloat(param int, value float64) error {
+ r := s.c.call("sqlite3_bind_double",
+ uint64(s.handle), uint64(param), math.Float64bits(value))
+ return s.c.error(r)
+}
+
+// BindText binds a string to the prepared statement.
+// The leftmost SQL parameter has an index of 1.
+//
+// https://sqlite.org/c3ref/bind_blob.html
+func (s *Stmt) BindText(param int, value string) error {
+ if len(value) > _MAX_LENGTH {
+ return TOOBIG
+ }
+ ptr := s.c.newString(value)
+ r := s.c.call("sqlite3_bind_text64",
+ uint64(s.handle), uint64(param),
+ uint64(ptr), uint64(len(value)),
+ uint64(s.c.freer), _UTF8)
+ return s.c.error(r)
+}
+
+// BindRawText binds a []byte to the prepared statement as text.
+// The leftmost SQL parameter has an index of 1.
+//
+// https://sqlite.org/c3ref/bind_blob.html
+func (s *Stmt) BindRawText(param int, value []byte) error {
+ if len(value) > _MAX_LENGTH {
+ return TOOBIG
+ }
+ ptr := s.c.newBytes(value)
+ r := s.c.call("sqlite3_bind_text64",
+ uint64(s.handle), uint64(param),
+ uint64(ptr), uint64(len(value)),
+ uint64(s.c.freer), _UTF8)
+ return s.c.error(r)
+}
+
+// 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://sqlite.org/c3ref/bind_blob.html
+func (s *Stmt) BindBlob(param int, value []byte) error {
+ if len(value) > _MAX_LENGTH {
+ return TOOBIG
+ }
+ ptr := s.c.newBytes(value)
+ r := s.c.call("sqlite3_bind_blob64",
+ uint64(s.handle), uint64(param),
+ uint64(ptr), uint64(len(value)),
+ uint64(s.c.freer))
+ return s.c.error(r)
+}
+
+// BindZeroBlob binds a zero-filled, length n BLOB to the prepared statement.
+// The leftmost SQL parameter has an index of 1.
+//
+// https://sqlite.org/c3ref/bind_blob.html
+func (s *Stmt) BindZeroBlob(param int, n int64) error {
+ r := s.c.call("sqlite3_bind_zeroblob64",
+ uint64(s.handle), uint64(param), uint64(n))
+ return s.c.error(r)
+}
+
+// BindNull binds a NULL to the prepared statement.
+// The leftmost SQL parameter has an index of 1.
+//
+// https://sqlite.org/c3ref/bind_blob.html
+func (s *Stmt) BindNull(param int) error {
+ r := s.c.call("sqlite3_bind_null",
+ uint64(s.handle), uint64(param))
+ return s.c.error(r)
+}
+
+// BindTime binds a [time.Time] to the prepared statement.
+// The leftmost SQL parameter has an index of 1.
+//
+// https://sqlite.org/c3ref/bind_blob.html
+func (s *Stmt) BindTime(param int, value time.Time, format TimeFormat) error {
+ if format == TimeFormatDefault {
+ return s.bindRFC3339Nano(param, value)
+ }
+ 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:
+ panic(util.AssertErr())
+ }
+ return nil
+}
+
+func (s *Stmt) bindRFC3339Nano(param int, value time.Time) error {
+ const maxlen = uint64(len(time.RFC3339Nano)) + 5
+
+ ptr := s.c.new(maxlen)
+ buf := util.View(s.c.mod, ptr, maxlen)
+ buf = value.AppendFormat(buf[:0], time.RFC3339Nano)
+
+ r := s.c.call("sqlite3_bind_text64",
+ uint64(s.handle), uint64(param),
+ uint64(ptr), uint64(len(buf)),
+ uint64(s.c.freer), _UTF8)
+ return s.c.error(r)
+}
+
+// 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].
+// The leftmost SQL parameter has an index of 1.
+//
+// https://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("sqlite3_bind_pointer_go",
+ uint64(s.handle), uint64(param), uint64(valPtr))
+ return s.c.error(r)
+}
+
+// BindJSON binds the JSON encoding of value to the prepared statement.
+// The leftmost SQL parameter has an index of 1.
+//
+// https://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
+ }
+ return s.BindRawText(param, data)
+}
+
+// BindValue binds a copy of value to the prepared statement.
+// The leftmost SQL parameter has an index of 1.
+//
+// https://sqlite.org/c3ref/bind_blob.html
+func (s *Stmt) BindValue(param int, value Value) error {
+ if value.c != s.c {
+ return MISUSE
+ }
+ r := s.c.call("sqlite3_bind_value",
+ uint64(s.handle), uint64(param), uint64(value.handle))
+ return s.c.error(r)
+}
+
+// ColumnCount returns the number of columns in a result set.
+//
+// https://sqlite.org/c3ref/column_count.html
+func (s *Stmt) ColumnCount() int {
+ r := s.c.call("sqlite3_column_count",
+ uint64(s.handle))
+ return int(int32(r))
+}
+
+// ColumnName returns the name of the result column.
+// The leftmost column of the result set has the index 0.
+//
+// https://sqlite.org/c3ref/column_name.html
+func (s *Stmt) ColumnName(col int) string {
+ r := s.c.call("sqlite3_column_name",
+ uint64(s.handle), uint64(col))
+ if r == 0 {
+ panic(util.OOMErr)
+ }
+ return util.ReadString(s.c.mod, uint32(r), _MAX_NAME)
+}
+
+// ColumnType returns the initial [Datatype] of the result column.
+// The leftmost column of the result set has the index 0.
+//
+// https://sqlite.org/c3ref/column_blob.html
+func (s *Stmt) ColumnType(col int) Datatype {
+ r := s.c.call("sqlite3_column_type",
+ uint64(s.handle), uint64(col))
+ return Datatype(r)
+}
+
+// ColumnDeclType returns the declared datatype of the result column.
+// The leftmost column of the result set has the index 0.
+//
+// https://sqlite.org/c3ref/column_decltype.html
+func (s *Stmt) ColumnDeclType(col int) string {
+ r := s.c.call("sqlite3_column_decltype",
+ uint64(s.handle), uint64(col))
+ if r == 0 {
+ return ""
+ }
+ return util.ReadString(s.c.mod, uint32(r), _MAX_NAME)
+}
+
+// ColumnDatabaseName returns the name of the database
+// that is the origin of a particular result column.
+// The leftmost column of the result set has the index 0.
+//
+// https://sqlite.org/c3ref/column_database_name.html
+func (s *Stmt) ColumnDatabaseName(col int) string {
+ r := s.c.call("sqlite3_column_database_name",
+ uint64(s.handle), uint64(col))
+ if r == 0 {
+ return ""
+ }
+ return util.ReadString(s.c.mod, uint32(r), _MAX_NAME)
+}
+
+// ColumnTableName returns the name of the table
+// that is the origin of a particular result column.
+// The leftmost column of the result set has the index 0.
+//
+// https://sqlite.org/c3ref/column_database_name.html
+func (s *Stmt) ColumnTableName(col int) string {
+ r := s.c.call("sqlite3_column_table_name",
+ uint64(s.handle), uint64(col))
+ if r == 0 {
+ return ""
+ }
+ return util.ReadString(s.c.mod, uint32(r), _MAX_NAME)
+}
+
+// ColumnOriginName returns the name of the table column
+// that is the origin of a particular result column.
+// The leftmost column of the result set has the index 0.
+//
+// https://sqlite.org/c3ref/column_database_name.html
+func (s *Stmt) ColumnOriginName(col int) string {
+ r := s.c.call("sqlite3_column_origin_name",
+ uint64(s.handle), uint64(col))
+ if r == 0 {
+ return ""
+ }
+ return util.ReadString(s.c.mod, uint32(r), _MAX_NAME)
+}
+
+// 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://sqlite.org/c3ref/column_blob.html
+func (s *Stmt) ColumnBool(col int) bool {
+ return s.ColumnInt64(col) != 0
+}
+
+// ColumnInt returns the value of the result column as an int.
+// The leftmost column of the result set has the index 0.
+//
+// https://sqlite.org/c3ref/column_blob.html
+func (s *Stmt) ColumnInt(col int) int {
+ return int(s.ColumnInt64(col))
+}
+
+// ColumnInt64 returns the value of the result column as an int64.
+// The leftmost column of the result set has the index 0.
+//
+// https://sqlite.org/c3ref/column_blob.html
+func (s *Stmt) ColumnInt64(col int) int64 {
+ r := s.c.call("sqlite3_column_int64",
+ uint64(s.handle), uint64(col))
+ return int64(r)
+}
+
+// ColumnFloat returns the value of the result column as a float64.
+// The leftmost column of the result set has the index 0.
+//
+// https://sqlite.org/c3ref/column_blob.html
+func (s *Stmt) ColumnFloat(col int) float64 {
+ r := s.c.call("sqlite3_column_double",
+ uint64(s.handle), uint64(col))
+ return math.Float64frombits(r)
+}
+
+// ColumnTime returns the value of the result column as a [time.Time].
+// The leftmost column of the result set has the index 0.
+//
+// https://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:
+ panic(util.AssertErr())
+ }
+ t, err := format.Decode(v)
+ if err != nil {
+ s.err = err
+ }
+ return t
+}
+
+// ColumnText returns the value of the result column as a string.
+// The leftmost column of the result set has the index 0.
+//
+// https://sqlite.org/c3ref/column_blob.html
+func (s *Stmt) ColumnText(col int) string {
+ 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://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://sqlite.org/c3ref/column_blob.html
+func (s *Stmt) ColumnRawText(col int) []byte {
+ r := s.c.call("sqlite3_column_text",
+ uint64(s.handle), uint64(col))
+ return s.columnRawBytes(col, uint32(r))
+}
+
+// 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.
+// The leftmost column of the result set has the index 0.
+//
+// https://sqlite.org/c3ref/column_blob.html
+func (s *Stmt) ColumnRawBlob(col int) []byte {
+ r := s.c.call("sqlite3_column_blob",
+ uint64(s.handle), uint64(col))
+ return s.columnRawBytes(col, uint32(r))
+}
+
+func (s *Stmt) columnRawBytes(col int, ptr uint32) []byte {
+ if ptr == 0 {
+ r := s.c.call("sqlite3_errcode", uint64(s.c.handle))
+ s.err = s.c.error(r)
+ return nil
+ }
+
+ r := s.c.call("sqlite3_column_bytes",
+ uint64(s.handle), uint64(col))
+ return util.View(s.c.mod, ptr, r)
+}
+
+// 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://sqlite.org/c3ref/column_blob.html
+func (s *Stmt) ColumnJSON(col int, ptr any) error {
+ var data []byte
+ switch s.ColumnType(col) {
+ case NULL:
+ data = append(data, "null"...)
+ case TEXT:
+ data = s.ColumnRawText(col)
+ case BLOB:
+ 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)
+}
+
+// ColumnValue returns the unprotected value of the result column.
+// The leftmost column of the result set has the index 0.
+//
+// https://sqlite.org/c3ref/column_blob.html
+func (s *Stmt) ColumnValue(col int) Value {
+ r := s.c.call("sqlite3_column_value",
+ uint64(s.handle), uint64(col))
+ return Value{
+ c: s.c,
+ unprot: true,
+ handle: uint32(r),
+ }
+}
+
+// Columns populates result columns into the provided slice.
+// The slice must have [Stmt.ColumnCount] length.
+//
+// [INTEGER] columns will be retrieved as int64 values,
+// [FLOAT] as float64, [NULL] as nil,
+// [TEXT] as string, and [BLOB] as []byte.
+// Any []byte are owned by SQLite and may be invalidated by
+// subsequent calls to [Stmt] methods.
+func (s *Stmt) Columns(dest []any) error {
+ defer s.c.arena.mark()()
+ count := uint64(len(dest))
+ typePtr := s.c.arena.new(count)
+ dataPtr := s.c.arena.new(8 * count)
+
+ r := s.c.call("sqlite3_columns_go",
+ uint64(s.handle), count, uint64(typePtr), uint64(dataPtr))
+ if err := s.c.error(r); err != nil {
+ return err
+ }
+
+ types := util.View(s.c.mod, typePtr, count)
+ for i := range dest {
+ switch types[i] {
+ case byte(INTEGER):
+ dest[i] = int64(util.ReadUint64(s.c.mod, dataPtr+8*uint32(i)))
+ continue
+ case byte(FLOAT):
+ dest[i] = util.ReadFloat64(s.c.mod, dataPtr+8*uint32(i))
+ continue
+ case byte(NULL):
+ dest[i] = nil
+ continue
+ }
+ ptr := util.ReadUint32(s.c.mod, dataPtr+8*uint32(i)+0)
+ len := util.ReadUint32(s.c.mod, dataPtr+8*uint32(i)+4)
+ buf := util.View(s.c.mod, ptr, uint64(len))
+ if types[i] == byte(TEXT) {
+ dest[i] = string(buf)
+ } else {
+ dest[i] = buf
+ }
+ }
+ return nil
+}