context.go

  1package sqlite3
  2
  3import (
  4	"encoding/json"
  5	"errors"
  6	"math"
  7	"time"
  8
  9	"github.com/ncruces/go-sqlite3/internal/util"
 10)
 11
 12// Context is the context in which an SQL function executes.
 13// An SQLite [Context] is in no way related to a Go [context.Context].
 14//
 15// https://sqlite.org/c3ref/context.html
 16type Context struct {
 17	c      *Conn
 18	handle ptr_t
 19}
 20
 21// Conn returns the database connection of the
 22// [Conn.CreateFunction] or [Conn.CreateWindowFunction]
 23// routines that originally registered the application defined function.
 24//
 25// https://sqlite.org/c3ref/context_db_handle.html
 26func (ctx Context) Conn() *Conn {
 27	return ctx.c
 28}
 29
 30// SetAuxData saves metadata for argument n of the function.
 31//
 32// https://sqlite.org/c3ref/get_auxdata.html
 33func (ctx Context) SetAuxData(n int, data any) {
 34	ptr := util.AddHandle(ctx.c.ctx, data)
 35	ctx.c.call("sqlite3_set_auxdata_go", stk_t(ctx.handle), stk_t(n), stk_t(ptr))
 36}
 37
 38// GetAuxData returns metadata for argument n of the function.
 39//
 40// https://sqlite.org/c3ref/get_auxdata.html
 41func (ctx Context) GetAuxData(n int) any {
 42	ptr := ptr_t(ctx.c.call("sqlite3_get_auxdata", stk_t(ctx.handle), stk_t(n)))
 43	return util.GetHandle(ctx.c.ctx, ptr)
 44}
 45
 46// ResultBool sets the result of the function to a bool.
 47// SQLite does not have a separate boolean storage class.
 48// Instead, boolean values are stored as integers 0 (false) and 1 (true).
 49//
 50// https://sqlite.org/c3ref/result_blob.html
 51func (ctx Context) ResultBool(value bool) {
 52	var i int64
 53	if value {
 54		i = 1
 55	}
 56	ctx.ResultInt64(i)
 57}
 58
 59// ResultInt sets the result of the function to an int.
 60//
 61// https://sqlite.org/c3ref/result_blob.html
 62func (ctx Context) ResultInt(value int) {
 63	ctx.ResultInt64(int64(value))
 64}
 65
 66// ResultInt64 sets the result of the function to an int64.
 67//
 68// https://sqlite.org/c3ref/result_blob.html
 69func (ctx Context) ResultInt64(value int64) {
 70	ctx.c.call("sqlite3_result_int64",
 71		stk_t(ctx.handle), stk_t(value))
 72}
 73
 74// ResultFloat sets the result of the function to a float64.
 75//
 76// https://sqlite.org/c3ref/result_blob.html
 77func (ctx Context) ResultFloat(value float64) {
 78	ctx.c.call("sqlite3_result_double",
 79		stk_t(ctx.handle), stk_t(math.Float64bits(value)))
 80}
 81
 82// ResultText sets the result of the function to a string.
 83//
 84// https://sqlite.org/c3ref/result_blob.html
 85func (ctx Context) ResultText(value string) {
 86	ptr := ctx.c.newString(value)
 87	ctx.c.call("sqlite3_result_text_go",
 88		stk_t(ctx.handle), stk_t(ptr), stk_t(len(value)))
 89}
 90
 91// ResultRawText sets the text result of the function to a []byte.
 92//
 93// https://sqlite.org/c3ref/result_blob.html
 94func (ctx Context) ResultRawText(value []byte) {
 95	if len(value) == 0 {
 96		ctx.ResultText("")
 97		return
 98	}
 99	ptr := ctx.c.newBytes(value)
100	ctx.c.call("sqlite3_result_text_go",
101		stk_t(ctx.handle), stk_t(ptr), stk_t(len(value)))
102}
103
104// ResultBlob sets the result of the function to a []byte.
105//
106// https://sqlite.org/c3ref/result_blob.html
107func (ctx Context) ResultBlob(value []byte) {
108	if len(value) == 0 {
109		ctx.ResultZeroBlob(0)
110		return
111	}
112	ptr := ctx.c.newBytes(value)
113	ctx.c.call("sqlite3_result_blob_go",
114		stk_t(ctx.handle), stk_t(ptr), stk_t(len(value)))
115}
116
117// ResultZeroBlob sets the result of the function to a zero-filled, length n BLOB.
118//
119// https://sqlite.org/c3ref/result_blob.html
120func (ctx Context) ResultZeroBlob(n int64) {
121	ctx.c.call("sqlite3_result_zeroblob64",
122		stk_t(ctx.handle), stk_t(n))
123}
124
125// ResultNull sets the result of the function to NULL.
126//
127// https://sqlite.org/c3ref/result_blob.html
128func (ctx Context) ResultNull() {
129	ctx.c.call("sqlite3_result_null",
130		stk_t(ctx.handle))
131}
132
133// ResultTime sets the result of the function to a [time.Time].
134//
135// https://sqlite.org/c3ref/result_blob.html
136func (ctx Context) ResultTime(value time.Time, format TimeFormat) {
137	switch format {
138	case TimeFormatDefault, TimeFormatAuto, time.RFC3339Nano:
139		ctx.resultRFC3339Nano(value)
140		return
141	}
142	switch v := format.Encode(value).(type) {
143	case string:
144		ctx.ResultText(v)
145	case int64:
146		ctx.ResultInt64(v)
147	case float64:
148		ctx.ResultFloat(v)
149	default:
150		panic(util.AssertErr())
151	}
152}
153
154func (ctx Context) resultRFC3339Nano(value time.Time) {
155	const maxlen = int64(len(time.RFC3339Nano)) + 5
156
157	ptr := ctx.c.new(maxlen)
158	buf := util.View(ctx.c.mod, ptr, maxlen)
159	buf = value.AppendFormat(buf[:0], time.RFC3339Nano)
160
161	ctx.c.call("sqlite3_result_text_go",
162		stk_t(ctx.handle), stk_t(ptr), stk_t(len(buf)))
163}
164
165// ResultPointer sets the result of the function to NULL, just like [Context.ResultNull],
166// except that it also associates ptr with that NULL value such that it can be retrieved
167// within an application-defined SQL function using [Value.Pointer].
168//
169// https://sqlite.org/c3ref/result_blob.html
170func (ctx Context) ResultPointer(ptr any) {
171	valPtr := util.AddHandle(ctx.c.ctx, ptr)
172	ctx.c.call("sqlite3_result_pointer_go",
173		stk_t(ctx.handle), stk_t(valPtr))
174}
175
176// ResultJSON sets the result of the function to the JSON encoding of value.
177//
178// https://sqlite.org/c3ref/result_blob.html
179func (ctx Context) ResultJSON(value any) {
180	data, err := json.Marshal(value)
181	if err != nil {
182		ctx.ResultError(err)
183		return // notest
184	}
185	ctx.ResultRawText(data)
186}
187
188// ResultValue sets the result of the function to a copy of [Value].
189//
190// https://sqlite.org/c3ref/result_blob.html
191func (ctx Context) ResultValue(value Value) {
192	if value.c != ctx.c {
193		ctx.ResultError(MISUSE)
194		return
195	}
196	ctx.c.call("sqlite3_result_value",
197		stk_t(ctx.handle), stk_t(value.handle))
198}
199
200// ResultError sets the result of the function an error.
201//
202// https://sqlite.org/c3ref/result_blob.html
203func (ctx Context) ResultError(err error) {
204	if errors.Is(err, NOMEM) {
205		ctx.c.call("sqlite3_result_error_nomem", stk_t(ctx.handle))
206		return
207	}
208
209	if errors.Is(err, TOOBIG) {
210		ctx.c.call("sqlite3_result_error_toobig", stk_t(ctx.handle))
211		return
212	}
213
214	msg, code := errorCode(err, _OK)
215	if msg != "" {
216		defer ctx.c.arena.mark()()
217		ptr := ctx.c.arena.string(msg)
218		ctx.c.call("sqlite3_result_error",
219			stk_t(ctx.handle), stk_t(ptr), stk_t(len(msg)))
220	}
221	if code != _OK {
222		ctx.c.call("sqlite3_result_error_code",
223			stk_t(ctx.handle), stk_t(code))
224	}
225}
226
227// VTabNoChange may return true if a column is being fetched as part
228// of an update during which the column value will not change.
229//
230// https://sqlite.org/c3ref/vtab_nochange.html
231func (ctx Context) VTabNoChange() bool {
232	b := int32(ctx.c.call("sqlite3_vtab_nochange", stk_t(ctx.handle)))
233	return b != 0
234}