1package sqlite3
2
3import (
4 "encoding/json"
5 "math"
6 "strconv"
7 "time"
8
9 "github.com/ncruces/go-sqlite3/internal/util"
10)
11
12// Value is any value that can be stored in a database table.
13//
14// https://sqlite.org/c3ref/value.html
15type Value struct {
16 c *Conn
17 handle ptr_t
18 unprot bool
19 copied bool
20}
21
22func (v Value) protected() stk_t {
23 if v.unprot {
24 panic(util.ValueErr)
25 }
26 return stk_t(v.handle)
27}
28
29// Dup makes a copy of the SQL value and returns a pointer to that copy.
30//
31// https://sqlite.org/c3ref/value_dup.html
32func (v Value) Dup() *Value {
33 ptr := ptr_t(v.c.call("sqlite3_value_dup", stk_t(v.handle)))
34 return &Value{
35 c: v.c,
36 copied: true,
37 handle: ptr,
38 }
39}
40
41// Close frees an SQL value previously obtained by [Value.Dup].
42//
43// https://sqlite.org/c3ref/value_dup.html
44func (dup *Value) Close() error {
45 if !dup.copied {
46 panic(util.ValueErr)
47 }
48 dup.c.call("sqlite3_value_free", stk_t(dup.handle))
49 dup.handle = 0
50 return nil
51}
52
53// Type returns the initial datatype of the value.
54//
55// https://sqlite.org/c3ref/value_blob.html
56func (v Value) Type() Datatype {
57 return Datatype(v.c.call("sqlite3_value_type", v.protected()))
58}
59
60// Type returns the numeric datatype of the value.
61//
62// https://sqlite.org/c3ref/value_blob.html
63func (v Value) NumericType() Datatype {
64 return Datatype(v.c.call("sqlite3_value_numeric_type", v.protected()))
65}
66
67// Bool returns the value as a bool.
68// SQLite does not have a separate boolean storage class.
69// Instead, boolean values are retrieved as numbers,
70// with 0 converted to false and any other value to true.
71//
72// https://sqlite.org/c3ref/value_blob.html
73func (v Value) Bool() bool {
74 return v.Float() != 0
75}
76
77// Int returns the value as an int.
78//
79// https://sqlite.org/c3ref/value_blob.html
80func (v Value) Int() int {
81 return int(v.Int64())
82}
83
84// Int64 returns the value as an int64.
85//
86// https://sqlite.org/c3ref/value_blob.html
87func (v Value) Int64() int64 {
88 return int64(v.c.call("sqlite3_value_int64", v.protected()))
89}
90
91// Float returns the value as a float64.
92//
93// https://sqlite.org/c3ref/value_blob.html
94func (v Value) Float() float64 {
95 f := uint64(v.c.call("sqlite3_value_double", v.protected()))
96 return math.Float64frombits(f)
97}
98
99// Time returns the value as a [time.Time].
100//
101// https://sqlite.org/c3ref/value_blob.html
102func (v Value) Time(format TimeFormat) time.Time {
103 var a any
104 switch v.Type() {
105 case INTEGER:
106 a = v.Int64()
107 case FLOAT:
108 a = v.Float()
109 case TEXT, BLOB:
110 a = v.Text()
111 case NULL:
112 return time.Time{}
113 default:
114 panic(util.AssertErr())
115 }
116 t, _ := format.Decode(a)
117 return t
118}
119
120// Text returns the value as a string.
121//
122// https://sqlite.org/c3ref/value_blob.html
123func (v Value) Text() string {
124 return string(v.RawText())
125}
126
127// Blob appends to buf and returns
128// the value as a []byte.
129//
130// https://sqlite.org/c3ref/value_blob.html
131func (v Value) Blob(buf []byte) []byte {
132 return append(buf, v.RawBlob()...)
133}
134
135// RawText returns the value as a []byte.
136// The []byte is owned by SQLite and may be invalidated by
137// subsequent calls to [Value] methods.
138//
139// https://sqlite.org/c3ref/value_blob.html
140func (v Value) RawText() []byte {
141 ptr := ptr_t(v.c.call("sqlite3_value_text", v.protected()))
142 return v.rawBytes(ptr, 1)
143}
144
145// RawBlob returns the value as a []byte.
146// The []byte is owned by SQLite and may be invalidated by
147// subsequent calls to [Value] methods.
148//
149// https://sqlite.org/c3ref/value_blob.html
150func (v Value) RawBlob() []byte {
151 ptr := ptr_t(v.c.call("sqlite3_value_blob", v.protected()))
152 return v.rawBytes(ptr, 0)
153}
154
155func (v Value) rawBytes(ptr ptr_t, nul int32) []byte {
156 if ptr == 0 {
157 return nil
158 }
159
160 n := int32(v.c.call("sqlite3_value_bytes", v.protected()))
161 return util.View(v.c.mod, ptr, int64(n+nul))[:n]
162}
163
164// Pointer gets the pointer associated with this value,
165// or nil if it has no associated pointer.
166func (v Value) Pointer() any {
167 ptr := ptr_t(v.c.call("sqlite3_value_pointer_go", v.protected()))
168 return util.GetHandle(v.c.ctx, ptr)
169}
170
171// JSON parses a JSON-encoded value
172// and stores the result in the value pointed to by ptr.
173func (v Value) JSON(ptr any) error {
174 var data []byte
175 switch v.Type() {
176 case NULL:
177 data = []byte("null")
178 case TEXT:
179 data = v.RawText()
180 case BLOB:
181 data = v.RawBlob()
182 case INTEGER:
183 data = strconv.AppendInt(nil, v.Int64(), 10)
184 case FLOAT:
185 data = util.AppendNumber(nil, v.Float())
186 default:
187 panic(util.AssertErr())
188 }
189 return json.Unmarshal(data, ptr)
190}
191
192// NoChange returns true if and only if the value is unchanged
193// in a virtual table update operatiom.
194//
195// https://sqlite.org/c3ref/value_blob.html
196func (v Value) NoChange() bool {
197 b := int32(v.c.call("sqlite3_value_nochange", v.protected()))
198 return b != 0
199}
200
201// FromBind returns true if value originated from a bound parameter.
202//
203// https://sqlite.org/c3ref/value_blob.html
204func (v Value) FromBind() bool {
205 b := int32(v.c.call("sqlite3_value_frombind", v.protected()))
206 return b != 0
207}
208
209// InFirst returns the first element
210// on the right-hand side of an IN constraint.
211//
212// https://sqlite.org/c3ref/vtab_in_first.html
213func (v Value) InFirst() (Value, error) {
214 defer v.c.arena.mark()()
215 valPtr := v.c.arena.new(ptrlen)
216 rc := res_t(v.c.call("sqlite3_vtab_in_first", stk_t(v.handle), stk_t(valPtr)))
217 if err := v.c.error(rc); err != nil {
218 return Value{}, err
219 }
220 return Value{
221 c: v.c,
222 handle: util.Read32[ptr_t](v.c.mod, valPtr),
223 }, nil
224}
225
226// InNext returns the next element
227// on the right-hand side of an IN constraint.
228//
229// https://sqlite.org/c3ref/vtab_in_first.html
230func (v Value) InNext() (Value, error) {
231 defer v.c.arena.mark()()
232 valPtr := v.c.arena.new(ptrlen)
233 rc := res_t(v.c.call("sqlite3_vtab_in_next", stk_t(v.handle), stk_t(valPtr)))
234 if err := v.c.error(rc); err != nil {
235 return Value{}, err
236 }
237 return Value{
238 c: v.c,
239 handle: util.Read32[ptr_t](v.c.mod, valPtr),
240 }, nil
241}