1// Package driver provides a database/sql driver for SQLite.
2//
3// Importing package driver registers a [database/sql] driver named "sqlite3".
4// You may also need to import package embed.
5//
6// import _ "github.com/ncruces/go-sqlite3/driver"
7// import _ "github.com/ncruces/go-sqlite3/embed"
8//
9// The data source name for "sqlite3" databases can be a filename or a "file:" [URI].
10//
11// # Default transaction mode
12//
13// The [TRANSACTION] mode can be specified using "_txlock":
14//
15// sql.Open("sqlite3", "file:demo.db?_txlock=immediate")
16//
17// Possible values are: "deferred" (the default), "immediate", "exclusive".
18// Regardless of "_txlock":
19// - a [linearizable] transaction is always "exclusive";
20// - a [serializable] transaction is always "immediate";
21// - a [read-only] transaction is always "deferred".
22//
23// # Datatypes In SQLite
24//
25// SQLite is dynamically typed.
26// Columns can mostly hold any value regardless of their declared type.
27// SQLite supports most [driver.Value] types out of the box,
28// but bool and [time.Time] require special care.
29//
30// Booleans can be stored on any column type and scanned back to a *bool.
31// However, if scanned to a *any, booleans may either become an
32// int64, string or bool, depending on the declared type of the column.
33// If you use BOOLEAN for your column type,
34// 1 and 0 will always scan as true and false.
35//
36// # Working with time
37//
38// Time values can similarly be stored on any column type.
39// The time encoding/decoding format can be specified using "_timefmt":
40//
41// sql.Open("sqlite3", "file:demo.db?_timefmt=sqlite")
42//
43// Special values are: "auto" (the default), "sqlite", "rfc3339";
44// - "auto" encodes as RFC 3339 and decodes any [format] supported by SQLite;
45// - "sqlite" encodes as SQLite and decodes any [format] supported by SQLite;
46// - "rfc3339" encodes and decodes RFC 3339 only.
47//
48// You can also set "_timefmt" to an arbitrary [sqlite3.TimeFormat] or [time.Layout].
49//
50// If you encode as RFC 3339 (the default),
51// consider using the TIME [collating sequence] to produce time-ordered sequences.
52//
53// If you encode as RFC 3339 (the default),
54// time values will scan back to a *time.Time unless your column type is TEXT.
55// Otherwise, if scanned to a *any, time values may either become an
56// int64, float64 or string, depending on the time format and declared type of the column.
57// If you use DATE, TIME, DATETIME, or TIMESTAMP for your column type,
58// "_timefmt" will be used to decode values.
59//
60// To scan values in custom formats, [sqlite3.TimeFormat.Scanner] may be helpful.
61// To bind values in custom formats, [sqlite3.TimeFormat.Encode] them before binding.
62//
63// When using a custom time struct, you'll have to implement
64// [database/sql/driver.Valuer] and [database/sql.Scanner].
65//
66// The Value method should ideally encode to a time [format] supported by SQLite.
67// This ensures SQL date and time functions work as they should,
68// and that your schema works with other SQLite tools.
69// [sqlite3.TimeFormat.Encode] may help.
70//
71// The Scan method needs to take into account that the value it receives can be of differing types.
72// It can already be a [time.Time], if the driver decoded the value according to "_timefmt" rules.
73// Or it can be a: string, int64, float64, []byte, or nil,
74// depending on the column type and whoever wrote the value.
75// [sqlite3.TimeFormat.Decode] may help.
76//
77// # Setting PRAGMAs
78//
79// [PRAGMA] statements can be specified using "_pragma":
80//
81// sql.Open("sqlite3", "file:demo.db?_pragma=busy_timeout(10000)")
82//
83// If no PRAGMAs are specified, a busy timeout of 1 minute is set.
84//
85// Order matters:
86// encryption keys, busy timeout and locking mode should be the first PRAGMAs set,
87// in that order.
88//
89// [URI]: https://sqlite.org/uri.html
90// [PRAGMA]: https://sqlite.org/pragma.html
91// [TRANSACTION]: https://sqlite.org/lang_transaction.html#deferred_immediate_and_exclusive_transactions
92// [linearizable]: https://pkg.go.dev/database/sql#TxOptions
93// [serializable]: https://pkg.go.dev/database/sql#TxOptions
94// [read-only]: https://pkg.go.dev/database/sql#TxOptions
95// [format]: https://sqlite.org/lang_datefunc.html#time_values
96// [collating sequence]: https://sqlite.org/datatype3.html#collating_sequences
97package driver
98
99import (
100 "context"
101 "database/sql"
102 "database/sql/driver"
103 "errors"
104 "fmt"
105 "io"
106 "net/url"
107 "reflect"
108 "strings"
109 "time"
110 "unsafe"
111
112 "github.com/ncruces/go-sqlite3"
113 "github.com/ncruces/go-sqlite3/internal/util"
114)
115
116// This variable can be replaced with -ldflags:
117//
118// go build -ldflags="-X github.com/ncruces/go-sqlite3/driver.driverName=sqlite"
119var driverName = "sqlite3"
120
121func init() {
122 if driverName != "" {
123 sql.Register(driverName, &SQLite{})
124 }
125}
126
127// Open opens the SQLite database specified by dataSourceName as a [database/sql.DB].
128//
129// Open accepts zero, one, or two callbacks (nil callbacks are ignored).
130// The first callback is called when the driver opens a new connection.
131// The second callback is called before the driver closes a connection.
132// The [sqlite3.Conn] can be used to execute queries, register functions, etc.
133func Open(dataSourceName string, fn ...func(*sqlite3.Conn) error) (*sql.DB, error) {
134 if len(fn) > 2 {
135 return nil, sqlite3.MISUSE
136 }
137 var init, term func(*sqlite3.Conn) error
138 if len(fn) > 1 {
139 term = fn[1]
140 }
141 if len(fn) > 0 {
142 init = fn[0]
143 }
144 c, err := newConnector(dataSourceName, init, term)
145 if err != nil {
146 return nil, err
147 }
148 return sql.OpenDB(c), nil
149}
150
151// SQLite implements [database/sql/driver.Driver].
152type SQLite struct{}
153
154var (
155 // Ensure these interfaces are implemented:
156 _ driver.DriverContext = &SQLite{}
157)
158
159// Open implements [database/sql/driver.Driver].
160func (d *SQLite) Open(name string) (driver.Conn, error) {
161 c, err := newConnector(name, nil, nil)
162 if err != nil {
163 return nil, err
164 }
165 return c.Connect(context.Background())
166}
167
168// OpenConnector implements [database/sql/driver.DriverContext].
169func (d *SQLite) OpenConnector(name string) (driver.Connector, error) {
170 return newConnector(name, nil, nil)
171}
172
173func newConnector(name string, init, term func(*sqlite3.Conn) error) (*connector, error) {
174 c := connector{name: name, init: init, term: term}
175
176 var txlock, timefmt string
177 if strings.HasPrefix(name, "file:") {
178 if _, after, ok := strings.Cut(name, "?"); ok {
179 query, err := url.ParseQuery(after)
180 if err != nil {
181 return nil, err
182 }
183 txlock = query.Get("_txlock")
184 timefmt = query.Get("_timefmt")
185 c.pragmas = query.Has("_pragma")
186 }
187 }
188
189 switch txlock {
190 case "", "deferred", "concurrent", "immediate", "exclusive":
191 c.txLock = txlock
192 default:
193 return nil, fmt.Errorf("sqlite3: invalid _txlock: %s", txlock)
194 }
195
196 switch timefmt {
197 case "":
198 c.tmRead = sqlite3.TimeFormatAuto
199 c.tmWrite = sqlite3.TimeFormatDefault
200 case "sqlite":
201 c.tmRead = sqlite3.TimeFormatAuto
202 c.tmWrite = sqlite3.TimeFormat3
203 case "rfc3339":
204 c.tmRead = sqlite3.TimeFormatDefault
205 c.tmWrite = sqlite3.TimeFormatDefault
206 default:
207 c.tmRead = sqlite3.TimeFormat(timefmt)
208 c.tmWrite = sqlite3.TimeFormat(timefmt)
209 }
210 return &c, nil
211}
212
213type connector struct {
214 init func(*sqlite3.Conn) error
215 term func(*sqlite3.Conn) error
216 name string
217 txLock string
218 tmRead sqlite3.TimeFormat
219 tmWrite sqlite3.TimeFormat
220 pragmas bool
221}
222
223func (n *connector) Driver() driver.Driver {
224 return &SQLite{}
225}
226
227func (n *connector) Connect(ctx context.Context) (ret driver.Conn, err error) {
228 c := &conn{
229 txLock: n.txLock,
230 tmRead: n.tmRead,
231 tmWrite: n.tmWrite,
232 }
233
234 c.Conn, err = sqlite3.OpenContext(ctx, n.name)
235 if err != nil {
236 return nil, err
237 }
238 defer func() {
239 if ret == nil {
240 c.Close()
241 }
242 }()
243
244 old := c.Conn.SetInterrupt(ctx)
245 defer c.Conn.SetInterrupt(old)
246
247 if !n.pragmas {
248 err = c.Conn.BusyTimeout(time.Minute)
249 if err != nil {
250 return nil, err
251 }
252 }
253 if n.init != nil {
254 err = n.init(c.Conn)
255 if err != nil {
256 return nil, err
257 }
258 }
259 if n.pragmas || n.init != nil {
260 s, _, err := c.Conn.Prepare(`PRAGMA query_only`)
261 if err != nil {
262 return nil, err
263 }
264 defer s.Close()
265 if s.Step() && s.ColumnBool(0) {
266 c.readOnly = '1'
267 } else {
268 c.readOnly = '0'
269 }
270 err = s.Close()
271 if err != nil {
272 return nil, err
273 }
274 }
275 if n.term != nil {
276 err = c.Conn.Trace(sqlite3.TRACE_CLOSE, func(sqlite3.TraceEvent, any, any) error {
277 return n.term(c.Conn)
278 })
279 if err != nil {
280 return nil, err
281 }
282 }
283 return c, nil
284}
285
286// Conn is implemented by the SQLite [database/sql] driver connections.
287//
288// It can be used to access SQLite features like [online backup]:
289//
290// db, err := driver.Open("temp.db")
291// if err != nil {
292// log.Fatal(err)
293// }
294// defer db.Close()
295//
296// conn, err := db.Conn(context.TODO())
297// if err != nil {
298// log.Fatal(err)
299// }
300// defer conn.Close()
301//
302// err = conn.Raw(func(driverConn any) error {
303// conn := driverConn.(driver.Conn)
304// return conn.Raw().Backup("main", "backup.db")
305// })
306// if err != nil {
307// log.Fatal(err)
308// }
309//
310// [online backup]: https://sqlite.org/backup.html
311type Conn interface {
312 Raw() *sqlite3.Conn
313 driver.Conn
314 driver.ConnBeginTx
315 driver.ConnPrepareContext
316}
317
318type conn struct {
319 *sqlite3.Conn
320 txLock string
321 txReset string
322 tmRead sqlite3.TimeFormat
323 tmWrite sqlite3.TimeFormat
324 readOnly byte
325}
326
327var (
328 // Ensure these interfaces are implemented:
329 _ Conn = &conn{}
330 _ driver.ExecerContext = &conn{}
331)
332
333func (c *conn) Raw() *sqlite3.Conn {
334 return c.Conn
335}
336
337// Deprecated: use BeginTx instead.
338func (c *conn) Begin() (driver.Tx, error) {
339 // notest
340 return c.BeginTx(context.Background(), driver.TxOptions{})
341}
342
343func (c *conn) BeginTx(ctx context.Context, opts driver.TxOptions) (driver.Tx, error) {
344 var txLock string
345 switch opts.Isolation {
346 default:
347 return nil, util.IsolationErr
348 case driver.IsolationLevel(sql.LevelLinearizable):
349 txLock = "exclusive"
350 case driver.IsolationLevel(sql.LevelSerializable):
351 txLock = "immediate"
352 case driver.IsolationLevel(sql.LevelDefault):
353 if !opts.ReadOnly {
354 txLock = c.txLock
355 }
356 }
357
358 c.txReset = ``
359 txBegin := `BEGIN ` + txLock
360 if opts.ReadOnly {
361 txBegin += ` ; PRAGMA query_only=on`
362 c.txReset = `; PRAGMA query_only=` + string(c.readOnly)
363 }
364
365 old := c.Conn.SetInterrupt(ctx)
366 defer c.Conn.SetInterrupt(old)
367
368 err := c.Conn.Exec(txBegin)
369 if err != nil {
370 return nil, err
371 }
372 return c, nil
373}
374
375func (c *conn) Commit() error {
376 err := c.Conn.Exec(`COMMIT` + c.txReset)
377 if err != nil && !c.Conn.GetAutocommit() {
378 c.Rollback()
379 }
380 return err
381}
382
383func (c *conn) Rollback() error {
384 // ROLLBACK even if interrupted.
385 old := c.Conn.SetInterrupt(context.Background())
386 defer c.Conn.SetInterrupt(old)
387 return c.Conn.Exec(`ROLLBACK` + c.txReset)
388}
389
390func (c *conn) Prepare(query string) (driver.Stmt, error) {
391 // notest
392 return c.PrepareContext(context.Background(), query)
393}
394
395func (c *conn) PrepareContext(ctx context.Context, query string) (driver.Stmt, error) {
396 old := c.Conn.SetInterrupt(ctx)
397 defer c.Conn.SetInterrupt(old)
398
399 s, tail, err := c.Conn.Prepare(query)
400 if err != nil {
401 return nil, err
402 }
403 if notWhitespace(tail) {
404 s.Close()
405 return nil, util.TailErr
406 }
407 return &stmt{Stmt: s, tmRead: c.tmRead, tmWrite: c.tmWrite, inputs: -2}, nil
408}
409
410func (c *conn) ExecContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Result, error) {
411 if len(args) != 0 {
412 // Slow path.
413 return nil, driver.ErrSkip
414 }
415
416 if savept, ok := ctx.(*saveptCtx); ok {
417 // Called from driver.Savepoint.
418 savept.Savepoint = c.Conn.Savepoint()
419 return resultRowsAffected(0), nil
420 }
421
422 old := c.Conn.SetInterrupt(ctx)
423 defer c.Conn.SetInterrupt(old)
424
425 err := c.Conn.Exec(query)
426 if err != nil {
427 return nil, err
428 }
429
430 return newResult(c.Conn), nil
431}
432
433func (c *conn) CheckNamedValue(arg *driver.NamedValue) error {
434 // Fast path: short circuit argument verification.
435 // Arguments will be rejected by conn.ExecContext.
436 return nil
437}
438
439type stmt struct {
440 *sqlite3.Stmt
441 tmWrite sqlite3.TimeFormat
442 tmRead sqlite3.TimeFormat
443 inputs int
444}
445
446var (
447 // Ensure these interfaces are implemented:
448 _ driver.StmtExecContext = &stmt{}
449 _ driver.StmtQueryContext = &stmt{}
450 _ driver.NamedValueChecker = &stmt{}
451)
452
453func (s *stmt) NumInput() int {
454 if s.inputs >= -1 {
455 return s.inputs
456 }
457 n := s.Stmt.BindCount()
458 for i := 1; i <= n; i++ {
459 if s.Stmt.BindName(i) != "" {
460 s.inputs = -1
461 return -1
462 }
463 }
464 s.inputs = n
465 return n
466}
467
468// Deprecated: use ExecContext instead.
469func (s *stmt) Exec(args []driver.Value) (driver.Result, error) {
470 // notest
471 return s.ExecContext(context.Background(), namedValues(args))
472}
473
474// Deprecated: use QueryContext instead.
475func (s *stmt) Query(args []driver.Value) (driver.Rows, error) {
476 // notest
477 return s.QueryContext(context.Background(), namedValues(args))
478}
479
480func (s *stmt) ExecContext(ctx context.Context, args []driver.NamedValue) (driver.Result, error) {
481 err := s.setupBindings(args)
482 if err != nil {
483 return nil, err
484 }
485
486 old := s.Stmt.Conn().SetInterrupt(ctx)
487 defer s.Stmt.Conn().SetInterrupt(old)
488
489 err = errors.Join(
490 s.Stmt.Exec(),
491 s.Stmt.ClearBindings())
492 if err != nil {
493 return nil, err
494 }
495
496 return newResult(s.Stmt.Conn()), nil
497}
498
499func (s *stmt) QueryContext(ctx context.Context, args []driver.NamedValue) (driver.Rows, error) {
500 err := s.setupBindings(args)
501 if err != nil {
502 return nil, err
503 }
504 return &rows{ctx: ctx, stmt: s}, nil
505}
506
507func (s *stmt) setupBindings(args []driver.NamedValue) (err error) {
508 var ids [3]int
509 for _, arg := range args {
510 ids := ids[:0]
511 if arg.Name == "" {
512 ids = append(ids, arg.Ordinal)
513 } else {
514 for _, prefix := range [...]string{":", "@", "$"} {
515 if id := s.Stmt.BindIndex(prefix + arg.Name); id != 0 {
516 ids = append(ids, id)
517 }
518 }
519 }
520
521 for _, id := range ids {
522 switch a := arg.Value.(type) {
523 case bool:
524 err = s.Stmt.BindBool(id, a)
525 case int:
526 err = s.Stmt.BindInt(id, a)
527 case int64:
528 err = s.Stmt.BindInt64(id, a)
529 case float64:
530 err = s.Stmt.BindFloat(id, a)
531 case string:
532 err = s.Stmt.BindText(id, a)
533 case []byte:
534 err = s.Stmt.BindBlob(id, a)
535 case sqlite3.ZeroBlob:
536 err = s.Stmt.BindZeroBlob(id, int64(a))
537 case time.Time:
538 err = s.Stmt.BindTime(id, a, s.tmWrite)
539 case util.JSON:
540 err = s.Stmt.BindJSON(id, a.Value)
541 case util.PointerUnwrap:
542 err = s.Stmt.BindPointer(id, util.UnwrapPointer(a))
543 case nil:
544 err = s.Stmt.BindNull(id)
545 default:
546 panic(util.AssertErr())
547 }
548 if err != nil {
549 return err
550 }
551 }
552 }
553 return nil
554}
555
556func (s *stmt) CheckNamedValue(arg *driver.NamedValue) error {
557 switch arg.Value.(type) {
558 case bool, int, int64, float64, string, []byte,
559 time.Time, sqlite3.ZeroBlob,
560 util.JSON, util.PointerUnwrap,
561 nil:
562 return nil
563 default:
564 return driver.ErrSkip
565 }
566}
567
568func newResult(c *sqlite3.Conn) driver.Result {
569 rows := c.Changes()
570 if rows != 0 {
571 id := c.LastInsertRowID()
572 if id != 0 {
573 return result{id, rows}
574 }
575 }
576 return resultRowsAffected(rows)
577}
578
579type result struct{ lastInsertId, rowsAffected int64 }
580
581func (r result) LastInsertId() (int64, error) {
582 return r.lastInsertId, nil
583}
584
585func (r result) RowsAffected() (int64, error) {
586 return r.rowsAffected, nil
587}
588
589type resultRowsAffected int64
590
591func (r resultRowsAffected) LastInsertId() (int64, error) {
592 return 0, nil
593}
594
595func (r resultRowsAffected) RowsAffected() (int64, error) {
596 return int64(r), nil
597}
598
599type rows struct {
600 ctx context.Context
601 *stmt
602 names []string
603 types []string
604 nulls []bool
605 scans []scantype
606}
607
608type scantype byte
609
610const (
611 _ANY scantype = iota
612 _INT scantype = scantype(sqlite3.INTEGER)
613 _REAL scantype = scantype(sqlite3.FLOAT)
614 _TEXT scantype = scantype(sqlite3.TEXT)
615 _BLOB scantype = scantype(sqlite3.BLOB)
616 _NULL scantype = scantype(sqlite3.NULL)
617 _BOOL scantype = iota
618 _TIME
619)
620
621func scanFromDecl(decl string) scantype {
622 // These types are only used before we have rows,
623 // and otherwise as type hints.
624 // The first few ensure STRICT tables are strictly typed.
625 // The other two are type hints for booleans and time.
626 switch decl {
627 case "INT", "INTEGER":
628 return _INT
629 case "REAL":
630 return _REAL
631 case "TEXT":
632 return _TEXT
633 case "BLOB":
634 return _BLOB
635 case "BOOLEAN":
636 return _BOOL
637 case "DATE", "TIME", "DATETIME", "TIMESTAMP":
638 return _TIME
639 }
640 return _ANY
641}
642
643var (
644 // Ensure these interfaces are implemented:
645 _ driver.RowsColumnTypeDatabaseTypeName = &rows{}
646 _ driver.RowsColumnTypeNullable = &rows{}
647)
648
649func (r *rows) Close() error {
650 return errors.Join(
651 r.Stmt.Reset(),
652 r.Stmt.ClearBindings())
653}
654
655func (r *rows) Columns() []string {
656 if r.names == nil {
657 count := r.Stmt.ColumnCount()
658 names := make([]string, count)
659 for i := range names {
660 names[i] = r.Stmt.ColumnName(i)
661 }
662 r.names = names
663 }
664 return r.names
665}
666
667func (r *rows) scanType(index int) scantype {
668 if r.scans == nil {
669 count := r.Stmt.ColumnCount()
670 scans := make([]scantype, count)
671 for i := range scans {
672 scans[i] = scanFromDecl(strings.ToUpper(r.Stmt.ColumnDeclType(i)))
673 }
674 r.scans = scans
675 }
676 return r.scans[index]
677}
678
679func (r *rows) loadColumnMetadata() {
680 if r.nulls == nil {
681 count := r.Stmt.ColumnCount()
682 nulls := make([]bool, count)
683 types := make([]string, count)
684 scans := make([]scantype, count)
685 for i := range nulls {
686 if col := r.Stmt.ColumnOriginName(i); col != "" {
687 types[i], _, nulls[i], _, _, _ = r.Stmt.Conn().TableColumnMetadata(
688 r.Stmt.ColumnDatabaseName(i),
689 r.Stmt.ColumnTableName(i),
690 col)
691 types[i] = strings.ToUpper(types[i])
692 scans[i] = scanFromDecl(types[i])
693 }
694 }
695 r.nulls = nulls
696 r.types = types
697 r.scans = scans
698 }
699}
700
701func (r *rows) ColumnTypeDatabaseTypeName(index int) string {
702 r.loadColumnMetadata()
703 decl := r.types[index]
704 if len := len(decl); len > 0 && decl[len-1] == ')' {
705 if i := strings.LastIndexByte(decl, '('); i >= 0 {
706 decl = decl[:i]
707 }
708 }
709 return strings.TrimSpace(decl)
710}
711
712func (r *rows) ColumnTypeNullable(index int) (nullable, ok bool) {
713 r.loadColumnMetadata()
714 if r.nulls[index] {
715 return false, true
716 }
717 return true, false
718}
719
720func (r *rows) ColumnTypeScanType(index int) (typ reflect.Type) {
721 r.loadColumnMetadata()
722 scan := r.scans[index]
723
724 if r.Stmt.Busy() {
725 // SQLite is dynamically typed and we now have a row.
726 // Always use the type of the value itself,
727 // unless the scan type is more specific
728 // and can scan the actual value.
729 val := scantype(r.Stmt.ColumnType(index))
730 useValType := true
731 switch {
732 case scan == _TIME && val != _BLOB && val != _NULL:
733 t := r.Stmt.ColumnTime(index, r.tmRead)
734 useValType = t == time.Time{}
735 case scan == _BOOL && val == _INT:
736 i := r.Stmt.ColumnInt64(index)
737 useValType = i != 0 && i != 1
738 case scan == _BLOB && val == _NULL:
739 useValType = false
740 }
741 if useValType {
742 scan = val
743 }
744 }
745
746 switch scan {
747 case _INT:
748 return reflect.TypeFor[int64]()
749 case _REAL:
750 return reflect.TypeFor[float64]()
751 case _TEXT:
752 return reflect.TypeFor[string]()
753 case _BLOB:
754 return reflect.TypeFor[[]byte]()
755 case _BOOL:
756 return reflect.TypeFor[bool]()
757 case _TIME:
758 return reflect.TypeFor[time.Time]()
759 default:
760 return reflect.TypeFor[any]()
761 }
762}
763
764func (r *rows) Next(dest []driver.Value) error {
765 old := r.Stmt.Conn().SetInterrupt(r.ctx)
766 defer r.Stmt.Conn().SetInterrupt(old)
767
768 if !r.Stmt.Step() {
769 if err := r.Stmt.Err(); err != nil {
770 return err
771 }
772 return io.EOF
773 }
774
775 data := unsafe.Slice((*any)(unsafe.SliceData(dest)), len(dest))
776 if err := r.Stmt.ColumnsRaw(data...); err != nil {
777 return err
778 }
779 for i := range dest {
780 scan := r.scanType(i)
781 switch v := dest[i].(type) {
782 case int64:
783 if scan == _BOOL {
784 switch v {
785 case 1:
786 dest[i] = true
787 case 0:
788 dest[i] = false
789 }
790 continue
791 }
792 case []byte:
793 if len(v) == cap(v) { // a BLOB
794 continue
795 }
796 if scan != _TEXT {
797 switch r.tmWrite {
798 case "", time.RFC3339, time.RFC3339Nano:
799 t, ok := maybeTime(v)
800 if ok {
801 dest[i] = t
802 continue
803 }
804 }
805 }
806 dest[i] = string(v)
807 case float64:
808 break
809 default:
810 continue
811 }
812 if scan == _TIME {
813 t, err := r.tmRead.Decode(dest[i])
814 if err == nil {
815 dest[i] = t
816 continue
817 }
818 }
819 }
820 return nil
821}