-
-
Notifications
You must be signed in to change notification settings - Fork 11
Expand file tree
/
Copy pathrows.go
More file actions
353 lines (319 loc) · 10.3 KB
/
Copy pathrows.go
File metadata and controls
353 lines (319 loc) · 10.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
// Copyright 2025 The Sqlite Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package sqlite // import "modernc.org/sqlite"
import (
"database/sql/driver"
"fmt"
"io"
"math"
"reflect"
"strings"
"time"
sqlite3 "modernc.org/sqlite/lib"
)
type rows struct {
allocs []uintptr
c *conn
columns []string
// decltypes holds the uppercased declared types for every result column,
// captured once at newRows time. The declared type of a column is fixed
// for the lifetime of a prepared statement, so caching the result of
// strings.ToUpper(sqlite3_column_decltype(...)) here removes a per-row
// libc.GoString + strings.ToUpper from the Next() hot path for callers
// that hit the time-conversion branches (_texttotime, _time_format).
decltypes []string
// parseFmtIdx caches, per column, the index into parseTimeFormats that
// matched the first successful (*conn).parseTime call on that column.
// Subsequent rows reuse the saved index as the first attempt instead of
// re-walking the format list from the top. Slot value -1 means no match
// has been recorded yet (either parseTime has not run on this column, or
// the parseTimeString / m= branch matched, which is not in
// parseTimeFormats). The cache is sticky: once a successful index is
// stored it is not overwritten if a later row happens to match a
// different format. A steady-format column therefore wins on every row
// after the first; a mixed-format column falls through as before, paying
// at most one extra format probe on rows whose matching format precedes
// the cached one.
parseFmtIdx []int8
pstmt uintptr
doStep bool
empty bool
reuseStmt bool // If true, Close() resets instead of finalizing
}
func newRows(c *conn, pstmt uintptr, allocs *[]uintptr, empty bool) (r *rows, err error) {
var a []uintptr
if allocs != nil {
a = *allocs
*allocs = nil
}
r = &rows{c: c, pstmt: pstmt, allocs: a, empty: empty}
defer func() {
if err != nil {
r.Close()
r = nil
}
}()
n, err := c.columnCount(pstmt)
if err != nil {
return nil, err
}
r.columns = make([]string, n)
r.decltypes = make([]string, n)
r.parseFmtIdx = make([]int8, n)
for i := range r.columns {
if r.columns[i], err = r.c.columnName(pstmt, i); err != nil {
return nil, err
}
r.decltypes[i] = strings.ToUpper(r.c.columnDeclType(pstmt, i))
r.parseFmtIdx[i] = -1
}
return r, nil
}
// Close closes the rows iterator.
func (r *rows) Close() (err error) {
r.c.freeAllocs(r.allocs)
r.allocs = nil
if r.reuseStmt {
// Reset the statement for reuse instead of finalizing it
if e := r.c.reset(r.pstmt); e != nil {
return e
}
return r.c.clearBindings(r.pstmt)
}
return r.c.finalize(r.pstmt)
}
// Columns returns the names of the columns. The number of columns of the
// result is inferred from the length of the slice. If a particular column name
// isn't known, an empty string should be returned for that entry.
func (r *rows) Columns() (c []string) {
return r.columns
}
// Next is called to populate the next row of data into the provided slice. The
// provided slice will be the same size as the Columns() are wide.
//
// Next should return io.EOF when there are no more rows.
func (r *rows) Next(dest []driver.Value) (err error) {
if r.empty {
return io.EOF
}
rc := sqlite3.SQLITE_ROW
if r.doStep {
if rc, err = r.c.step(r.pstmt); err != nil {
return err
}
}
r.doStep = true
switch rc {
case sqlite3.SQLITE_ROW:
if g, e := len(dest), len(r.columns); g != e {
return fmt.Errorf("sqlite: Next: have %v destination values, expected %v", g, e)
}
for i := range dest {
ct, err := r.c.columnType(r.pstmt, i)
if err != nil {
return err
}
switch ct {
case sqlite3.SQLITE_INTEGER:
v, err := r.c.columnInt64(r.pstmt, i)
if err != nil {
return err
}
if !r.c.intToTime {
dest[i] = v
} else {
// Inspired by mattn/go-sqlite3:
// https://github.com/mattn/go-sqlite3/blob/f76bae4b0044cbba8fb2c72b8e4559e8fbcffd86/sqlite3.go#L2254-L2262
// but we put make this compatibility optional behind a DSN
// query parameter, because this changes API behavior, so an
// opt-in is needed.
switch r.ColumnTypeDatabaseTypeName(i) {
case "DATE", "DATETIME", "TIMESTAMP":
// Check for explicit opt-in first. This fixes the bug for micro/nano users
// without breaking the legacy heuristic for existing users.
switch r.c.integerTimeFormat {
case "unix_micro":
dest[i] = r.c.applyTimezone(time.UnixMicro(v).UTC())
continue
case "unix_nano":
dest[i] = r.c.applyTimezone(time.Unix(0, v).UTC())
continue
}
// Legacy Heuristic (mattn/go-sqlite3 compatibility). NOTE: This heuristic
// fails for Millisecond timestamps representing dates before Sept 9, 2001
// (value < 1e12).
// Is it a seconds timestamp or a milliseconds
// timestamp?
if v > 1e12 || v < -1e12 {
// Milliseconds
dest[i] = r.c.applyTimezone(time.UnixMilli(v).UTC())
} else {
// Seconds
dest[i] = r.c.applyTimezone(time.Unix(v, 0).UTC())
}
default:
dest[i] = v
}
}
case sqlite3.SQLITE_FLOAT:
v, err := r.c.columnDouble(r.pstmt, i)
if err != nil {
return err
}
dest[i] = v
case sqlite3.SQLITE_TEXT:
v, err := r.c.columnText(r.pstmt, i)
if err != nil {
return err
}
switch r.ColumnTypeDatabaseTypeName(i) {
case "DATE", "DATETIME", "TIMESTAMP":
val, ok, idx := r.c.parseTime(v, int(r.parseFmtIdx[i]))
if ok && r.parseFmtIdx[i] < 0 && idx >= 0 {
r.parseFmtIdx[i] = int8(idx)
}
dest[i] = val
default:
// A TEXT column with no declared type. SQLite reports an
// empty decltype not only for aggregates and expressions
// over a date column (MAX/MIN/COALESCE, upper(x), x||''),
// but also for subqueries and for typeless real columns
// (CREATE TABLE t(x)). Any of these would be delivered as
// a raw string that Scan cannot store into *time.Time
// (#248). Under _texttotime, best-effort parse the value:
// on success deliver time.Time, otherwise fall back to the
// original string, so no Scan that worked before can newly
// fail.
if r.c.textToTime && r.decltypes[i] == "" {
if val, ok, idx := r.c.parseTime(v, int(r.parseFmtIdx[i])); ok {
if r.parseFmtIdx[i] < 0 && idx >= 0 {
r.parseFmtIdx[i] = int8(idx)
}
dest[i] = val
continue
}
}
dest[i] = v
}
case sqlite3.SQLITE_BLOB:
v, err := r.c.columnBlob(r.pstmt, i)
if err != nil {
return err
}
dest[i] = v
case sqlite3.SQLITE_NULL:
dest[i] = nil
default:
return fmt.Errorf("internal error: rc %d", rc)
}
}
return nil
case sqlite3.SQLITE_DONE:
return io.EOF
default:
return r.c.errstr(int32(rc))
}
}
// RowsColumnTypeDatabaseTypeName may be implemented by Rows. It should return
// the database system type name without the length. Type names should be
// uppercase. Examples of returned types: "VARCHAR", "NVARCHAR", "VARCHAR2",
// "CHAR", "TEXT", "DECIMAL", "SMALLINT", "INT", "BIGINT", "BOOL", "[]BIGINT",
// "JSONB", "XML", "TIMESTAMP".
func (r *rows) ColumnTypeDatabaseTypeName(index int) string {
return r.decltypes[index]
}
// RowsColumnTypeLength may be implemented by Rows. It should return the length
// of the column type if the column is a variable length type. If the column is
// not a variable length type ok should return false. If length is not limited
// other than system limits, it should return math.MaxInt64. The following are
// examples of returned values for various types:
//
// TEXT (math.MaxInt64, true)
// varchar(10) (10, true)
// nvarchar(10) (10, true)
// decimal (0, false)
// int (0, false)
// bytea(30) (30, true)
func (r *rows) ColumnTypeLength(index int) (length int64, ok bool) {
t, err := r.c.columnType(r.pstmt, index)
if err != nil {
return 0, false
}
switch t {
case sqlite3.SQLITE_INTEGER:
return 0, false
case sqlite3.SQLITE_FLOAT:
return 0, false
case sqlite3.SQLITE_TEXT:
return math.MaxInt64, true
case sqlite3.SQLITE_BLOB:
return math.MaxInt64, true
case sqlite3.SQLITE_NULL:
return 0, false
default:
return 0, false
}
}
// RowsColumnTypeNullable may be implemented by Rows. The nullable value should
// be true if it is known the column may be null, or false if the column is
// known to be not nullable. If the column nullability is unknown, ok should be
// false.
func (r *rows) ColumnTypeNullable(index int) (nullable, ok bool) {
return true, true
}
// RowsColumnTypePrecisionScale may be implemented by Rows. It should return
// the precision and scale for decimal types. If not applicable, ok should be
// false. The following are examples of returned values for various types:
//
// decimal(38, 4) (38, 4, true)
// int (0, 0, false)
// decimal (math.MaxInt64, math.MaxInt64, true)
func (r *rows) ColumnTypePrecisionScale(index int) (precision, scale int64, ok bool) {
return 0, 0, false
}
// RowsColumnTypeScanType may be implemented by Rows. It should return the
// value type that can be used to scan types into. For example, the database
// column type "bigint" this should return "reflect.TypeOf(int64(0))".
func (r *rows) ColumnTypeScanType(index int) reflect.Type {
t, err := r.c.columnType(r.pstmt, index)
if err != nil {
return reflect.TypeOf("")
}
switch t {
case sqlite3.SQLITE_INTEGER:
switch r.decltypes[index] {
case "BOOLEAN":
return reflect.TypeOf(false)
case "DATE", "DATETIME", "TIME", "TIMESTAMP":
return reflect.TypeOf(time.Time{})
default:
return reflect.TypeOf(int64(0))
}
case sqlite3.SQLITE_FLOAT:
return reflect.TypeOf(float64(0))
case sqlite3.SQLITE_TEXT:
if r.c.textToTime {
switch r.decltypes[index] {
case "DATE", "DATETIME", "TIME", "TIMESTAMP":
return reflect.TypeOf(time.Time{})
}
}
return reflect.TypeOf("")
case sqlite3.SQLITE_BLOB:
return reflect.TypeOf([]byte(nil))
case sqlite3.SQLITE_NULL:
return reflect.TypeOf(nil)
default:
return reflect.TypeOf("")
}
}
// C documentation
//
// int sqlite3_reset(sqlite3_stmt *pStmt);
func (c *conn) reset(pstmt uintptr) error {
if rc := sqlite3.Xsqlite3_reset(c.tls, pstmt); rc != sqlite3.SQLITE_OK {
return c.errstr(rc)
}
return nil
}