github.com/ncruces/go-sqlite3@v0.15.1-0.20240520133447-53eef1510ff0/context.go (about) 1 package sqlite3 2 3 import ( 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 16 type Context struct { 17 c *Conn 18 handle uint32 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 26 func (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 33 func (ctx Context) SetAuxData(n int, data any) { 34 ptr := util.AddHandle(ctx.c.ctx, data) 35 ctx.c.call("sqlite3_set_auxdata_go", uint64(ctx.handle), uint64(n), uint64(ptr)) 36 } 37 38 // GetAuxData returns metadata for argument n of the function. 39 // 40 // https://sqlite.org/c3ref/get_auxdata.html 41 func (ctx Context) GetAuxData(n int) any { 42 ptr := uint32(ctx.c.call("sqlite3_get_auxdata", uint64(ctx.handle), uint64(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 51 func (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 62 func (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 69 func (ctx Context) ResultInt64(value int64) { 70 ctx.c.call("sqlite3_result_int64", 71 uint64(ctx.handle), uint64(value)) 72 } 73 74 // ResultFloat sets the result of the function to a float64. 75 // 76 // https://sqlite.org/c3ref/result_blob.html 77 func (ctx Context) ResultFloat(value float64) { 78 ctx.c.call("sqlite3_result_double", 79 uint64(ctx.handle), 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 85 func (ctx Context) ResultText(value string) { 86 ptr := ctx.c.newString(value) 87 ctx.c.call("sqlite3_result_text64", 88 uint64(ctx.handle), uint64(ptr), uint64(len(value)), 89 uint64(ctx.c.freer), _UTF8) 90 } 91 92 // ResultRawText sets the text result of the function to a []byte. 93 // 94 // https://sqlite.org/c3ref/result_blob.html 95 func (ctx Context) ResultRawText(value []byte) { 96 ptr := ctx.c.newBytes(value) 97 ctx.c.call("sqlite3_result_text64", 98 uint64(ctx.handle), uint64(ptr), uint64(len(value)), 99 uint64(ctx.c.freer), _UTF8) 100 } 101 102 // ResultBlob sets the result of the function to a []byte. 103 // Returning a nil slice is the same as calling [Context.ResultNull]. 104 // 105 // https://sqlite.org/c3ref/result_blob.html 106 func (ctx Context) ResultBlob(value []byte) { 107 ptr := ctx.c.newBytes(value) 108 ctx.c.call("sqlite3_result_blob64", 109 uint64(ctx.handle), uint64(ptr), uint64(len(value)), 110 uint64(ctx.c.freer)) 111 } 112 113 // ResultZeroBlob sets the result of the function to a zero-filled, length n BLOB. 114 // 115 // https://sqlite.org/c3ref/result_blob.html 116 func (ctx Context) ResultZeroBlob(n int64) { 117 ctx.c.call("sqlite3_result_zeroblob64", 118 uint64(ctx.handle), uint64(n)) 119 } 120 121 // ResultNull sets the result of the function to NULL. 122 // 123 // https://sqlite.org/c3ref/result_blob.html 124 func (ctx Context) ResultNull() { 125 ctx.c.call("sqlite3_result_null", 126 uint64(ctx.handle)) 127 } 128 129 // ResultTime sets the result of the function to a [time.Time]. 130 // 131 // https://sqlite.org/c3ref/result_blob.html 132 func (ctx Context) ResultTime(value time.Time, format TimeFormat) { 133 if format == TimeFormatDefault { 134 ctx.resultRFC3339Nano(value) 135 return 136 } 137 switch v := format.Encode(value).(type) { 138 case string: 139 ctx.ResultText(v) 140 case int64: 141 ctx.ResultInt64(v) 142 case float64: 143 ctx.ResultFloat(v) 144 default: 145 panic(util.AssertErr()) 146 } 147 } 148 149 func (ctx Context) resultRFC3339Nano(value time.Time) { 150 const maxlen = uint64(len(time.RFC3339Nano)) + 5 151 152 ptr := ctx.c.new(maxlen) 153 buf := util.View(ctx.c.mod, ptr, maxlen) 154 buf = value.AppendFormat(buf[:0], time.RFC3339Nano) 155 156 ctx.c.call("sqlite3_result_text64", 157 uint64(ctx.handle), uint64(ptr), uint64(len(buf)), 158 uint64(ctx.c.freer), _UTF8) 159 } 160 161 // ResultPointer sets the result of the function to NULL, just like [Context.ResultNull], 162 // except that it also associates ptr with that NULL value such that it can be retrieved 163 // within an application-defined SQL function using [Value.Pointer]. 164 // 165 // https://sqlite.org/c3ref/result_blob.html 166 func (ctx Context) ResultPointer(ptr any) { 167 valPtr := util.AddHandle(ctx.c.ctx, ptr) 168 ctx.c.call("sqlite3_result_pointer_go", uint64(valPtr)) 169 } 170 171 // ResultJSON sets the result of the function to the JSON encoding of value. 172 // 173 // https://sqlite.org/c3ref/result_blob.html 174 func (ctx Context) ResultJSON(value any) { 175 data, err := json.Marshal(value) 176 if err != nil { 177 ctx.ResultError(err) 178 return 179 } 180 ctx.ResultRawText(data) 181 } 182 183 // ResultValue sets the result of the function to a copy of [Value]. 184 // 185 // https://sqlite.org/c3ref/result_blob.html 186 func (ctx Context) ResultValue(value Value) { 187 if value.c != ctx.c { 188 ctx.ResultError(MISUSE) 189 return 190 } 191 ctx.c.call("sqlite3_result_value", 192 uint64(ctx.handle), uint64(value.handle)) 193 } 194 195 // ResultError sets the result of the function an error. 196 // 197 // https://sqlite.org/c3ref/result_blob.html 198 func (ctx Context) ResultError(err error) { 199 if errors.Is(err, NOMEM) { 200 ctx.c.call("sqlite3_result_error_nomem", uint64(ctx.handle)) 201 return 202 } 203 204 if errors.Is(err, TOOBIG) { 205 ctx.c.call("sqlite3_result_error_toobig", uint64(ctx.handle)) 206 return 207 } 208 209 msg, code := errorCode(err, _OK) 210 if msg != "" { 211 defer ctx.c.arena.mark()() 212 ptr := ctx.c.arena.string(msg) 213 ctx.c.call("sqlite3_result_error", 214 uint64(ctx.handle), uint64(ptr), uint64(len(msg))) 215 } 216 if code != _OK { 217 ctx.c.call("sqlite3_result_error_code", 218 uint64(ctx.handle), uint64(code)) 219 } 220 } 221 222 // VTabNoChange may return true if a column is being fetched as part 223 // of an update during which the column value will not change. 224 // 225 // https://sqlite.org/c3ref/vtab_nochange.html 226 func (ctx Context) VTabNoChange() bool { 227 r := ctx.c.call("sqlite3_vtab_nochange", uint64(ctx.handle)) 228 return r != 0 229 }