20
20
// - a [serializable] transaction is always "immediate";
21
21
// - a [read-only] transaction is always "deferred".
22
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
+ //
23
36
// # Working with time
24
37
//
38
+ // Time values can similarly be stored on any column type.
25
39
// The time encoding/decoding format can be specified using "_timefmt":
26
40
//
27
41
// sql.Open("sqlite3", "file:demo.db?_timefmt=sqlite")
28
42
//
29
- // Possible values are: "auto" (the default), "sqlite", "rfc3339";
43
+ // Special values are: "auto" (the default), "sqlite", "rfc3339";
30
44
// - "auto" encodes as RFC 3339 and decodes any [format] supported by SQLite;
31
45
// - "sqlite" encodes as SQLite and decodes any [format] supported by SQLite;
32
46
// - "rfc3339" encodes and decodes RFC 3339 only.
33
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
+ //
34
53
// If you encode as RFC 3339 (the default),
35
- // consider using the TIME [collating sequence] to produce a time-ordered sequence.
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.
36
59
//
37
- // To scan values in other formats, [sqlite3.TimeFormat.Scanner] may be helpful.
38
- // To bind values in other formats, [sqlite3.TimeFormat.Encode] them before binding.
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.
39
62
//
40
63
// When using a custom time struct, you'll have to implement
41
64
// [database/sql/driver.Valuer] and [database/sql.Scanner].
48
71
// The Scan method needs to take into account that the value it receives can be of differing types.
49
72
// It can already be a [time.Time], if the driver decoded the value according to "_timefmt" rules.
50
73
// Or it can be a: string, int64, float64, []byte, or nil,
51
- // depending on the column type and what whoever wrote the value.
74
+ // depending on the column type and whoever wrote the value.
52
75
// [sqlite3.TimeFormat.Decode] may help.
53
76
//
54
77
// # Setting PRAGMAs
@@ -595,6 +618,28 @@ const (
595
618
_TIME
596
619
)
597
620
621
+ func 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
+
598
643
var (
599
644
// Ensure these interfaces are implemented:
600
645
_ driver.RowsColumnTypeDatabaseTypeName = & rows {}
@@ -619,6 +664,18 @@ func (r *rows) Columns() []string {
619
664
return r .names
620
665
}
621
666
667
+ func (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
+
622
679
func (r * rows ) loadColumnMetadata () {
623
680
if r .nulls == nil {
624
681
count := r .Stmt .ColumnCount ()
@@ -632,24 +689,7 @@ func (r *rows) loadColumnMetadata() {
632
689
r .Stmt .ColumnTableName (i ),
633
690
col )
634
691
types [i ] = strings .ToUpper (types [i ])
635
- // These types are only used before we have rows,
636
- // and otherwise as type hints.
637
- // The first few ensure STRICT tables are strictly typed.
638
- // The other two are type hints for booleans and time.
639
- switch types [i ] {
640
- case "INT" , "INTEGER" :
641
- scans [i ] = _INT
642
- case "REAL" :
643
- scans [i ] = _REAL
644
- case "TEXT" :
645
- scans [i ] = _TEXT
646
- case "BLOB" :
647
- scans [i ] = _BLOB
648
- case "BOOLEAN" :
649
- scans [i ] = _BOOL
650
- case "DATE" , "TIME" , "DATETIME" , "TIMESTAMP" :
651
- scans [i ] = _TIME
652
- }
692
+ scans [i ] = scanFromDecl (types [i ])
653
693
}
654
694
}
655
695
r .nulls = nulls
@@ -658,27 +698,15 @@ func (r *rows) loadColumnMetadata() {
658
698
}
659
699
}
660
700
661
- func (r * rows ) declType (index int ) string {
662
- if r .types == nil {
663
- count := r .Stmt .ColumnCount ()
664
- types := make ([]string , count )
665
- for i := range types {
666
- types [i ] = strings .ToUpper (r .Stmt .ColumnDeclType (i ))
667
- }
668
- r .types = types
669
- }
670
- return r .types [index ]
671
- }
672
-
673
701
func (r * rows ) ColumnTypeDatabaseTypeName (index int ) string {
674
702
r .loadColumnMetadata ()
675
- decltype := r .types [index ]
676
- if len := len (decltype ); len > 0 && decltype [len - 1 ] == ')' {
677
- if i := strings .LastIndexByte (decltype , '(' ); i >= 0 {
678
- decltype = decltype [:i ]
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 ]
679
707
}
680
708
}
681
- return strings .TrimSpace (decltype )
709
+ return strings .TrimSpace (decl )
682
710
}
683
711
684
712
func (r * rows ) ColumnTypeNullable (index int ) (nullable , ok bool ) {
@@ -745,36 +773,49 @@ func (r *rows) Next(dest []driver.Value) error {
745
773
}
746
774
747
775
data := unsafe .Slice ((* any )(unsafe .SliceData (dest )), len (dest ))
748
- err := r .Stmt .Columns (data ... )
749
- for i := range dest {
750
- if t , ok := r .decodeTime (i , dest [i ]); ok {
751
- dest [i ] = t
752
- }
776
+ if err := r .Stmt .ColumnsRaw (data ... ); err != nil {
777
+ return err
753
778
}
754
- return err
755
- }
756
-
757
- func (r * rows ) decodeTime (i int , v any ) (_ time.Time , ok bool ) {
758
- switch v := v .(type ) {
759
- case int64 , float64 :
760
- // could be a time value
761
- case string :
762
- if r .tmWrite != "" && r .tmWrite != time .RFC3339 && r .tmWrite != time .RFC3339Nano {
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 :
763
808
break
809
+ default :
810
+ continue
764
811
}
765
- t , ok := maybeTime (v )
766
- if ok {
767
- return t , true
812
+ if scan == _TIME {
813
+ t , err := r .tmRead .Decode (dest [i ])
814
+ if err == nil {
815
+ dest [i ] = t
816
+ continue
817
+ }
768
818
}
769
- default :
770
- return
771
819
}
772
- switch r .declType (i ) {
773
- case "DATE" , "TIME" , "DATETIME" , "TIMESTAMP" :
774
- // could be a time value
775
- default :
776
- return
777
- }
778
- t , err := r .tmRead .Decode (v )
779
- return t , err == nil
820
+ return nil
780
821
}
0 commit comments