1 // Copyright 2011 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
25 // fakeDriver is a fake database that implements Go's driver.Driver
26 // interface, just for testing.
28 // It speaks a query language that's semantically similar to but
29 // syntactically different and simpler than SQL. The syntax is as
33 // CREATE|<tablename>|<col>=<type>,<col>=<type>,...
34 // where types are: "string", [u]int{8,16,32,64}, "bool"
35 // INSERT|<tablename>|col=val,col2=val2,col3=?
36 // SELECT|<tablename>|projectcol1,projectcol2|filtercol=?,filtercol2=?
37 // SELECT|<tablename>|projectcol1,projectcol2|filtercol=?param1,filtercol2=?param2
39 // Any of these can be preceded by PANIC|<method>|, to cause the
40 // named method on fakeStmt to panic.
42 // Any of these can be proceeded by WAIT|<duration>|, to cause the
43 // named method on fakeStmt to sleep for the specified duration.
45 // Multiple of these can be combined when separated with a semicolon.
47 // When opening a fakeDriver's database, it starts empty with no
48 // tables. All tables and data are stored in memory only.
49 type fakeDriver
struct {
50 mu sync
.Mutex
// guards 3 following fields
51 openCount
int // conn opens
52 closeCount
int // conn closes
54 waitingCh
chan struct{}
55 dbs
map[string]*fakeDB
62 tables
map[string]*table
73 func (t
*table
) columnIndex(name
string) int {
74 for n
, nname
:= range t
.colname
{
83 cols
[]interface{} // must be same size as its table colname + coltype
86 type fakeConn
struct {
87 db
*fakeDB
// where to return ourselves to
97 // bad connection tests; see isBad()
102 func (c
*fakeConn
) incrStat(v
*int) {
112 type boundCol
struct {
118 type fakeStmt
struct {
120 q
string // just for debugging
127 next
*fakeStmt
// used for returning multiple results.
131 colName
[]string // used by CREATE, INSERT, SELECT (selected columns)
132 colType
[]string // used by CREATE
133 colValue
[]interface{} // used by INSERT (mix of strings and "?" for bound params)
134 placeholders
int // used by INSERT/SELECT: number of ? params
136 whereCol
[]boundCol
// used by SELECT (all placeholders)
138 placeholderConverter
[]driver
.ValueConverter
// used by INSERT
141 var fdriver driver
.Driver
= &fakeDriver
{}
144 Register("test", fdriver
)
147 func contains(list
[]string, y
string) bool {
148 for _
, x
:= range list
{
160 func TestDrivers(t
*testing
.T
) {
161 unregisterAllDrivers()
162 Register("test", fdriver
)
163 Register("invalid", Dummy
{})
165 if len(all
) < 2 ||
!sort
.StringsAreSorted(all
) ||
!contains(all
, "test") ||
!contains(all
, "invalid") {
166 t
.Fatalf("Drivers = %v, want sorted list with at least [invalid, test]", all
)
170 // hook to simulate connection failures
171 var hookOpenErr
struct {
176 func setHookOpenErr(fn
func() error
) {
178 defer hookOpenErr
.Unlock()
182 // Supports dsn forms:
184 // <dbname>;<opts> (only currently supported option is `badConn`,
185 // which causes driver.ErrBadConn to be returned on
186 // every other conn.Begin())
187 func (d
*fakeDriver
) Open(dsn
string) (driver
.Conn
, error
) {
192 if err
:= fn(); err
!= nil {
196 parts
:= strings
.Split(dsn
, ";")
198 return nil, errors
.New("fakedb: no database name")
207 conn
:= &fakeConn
{db
: db
}
209 if len(parts
) >= 2 && parts
[1] == "badConn" {
213 d
.waitingCh
<- struct{}{}
221 func (d
*fakeDriver
) getDB(name
string) *fakeDB
{
225 d
.dbs
= make(map[string]*fakeDB
)
227 db
, ok
:= d
.dbs
[name
]
229 db
= &fakeDB
{name
: name
}
235 func (db
*fakeDB
) wipe() {
241 func (db
*fakeDB
) createTable(name
string, columnNames
, columnTypes
[]string) error
{
244 if db
.tables
== nil {
245 db
.tables
= make(map[string]*table
)
247 if _
, exist
:= db
.tables
[name
]; exist
{
248 return fmt
.Errorf("table %q already exists", name
)
250 if len(columnNames
) != len(columnTypes
) {
251 return fmt
.Errorf("create table of %q len(names) != len(types): %d vs %d",
252 name
, len(columnNames
), len(columnTypes
))
254 db
.tables
[name
] = &table
{colname
: columnNames
, coltype
: columnTypes
}
258 // must be called with db.mu lock held
259 func (db
*fakeDB
) table(table
string) (*table
, bool) {
260 if db
.tables
== nil {
263 t
, ok
:= db
.tables
[table
]
267 func (db
*fakeDB
) columnType(table
, column
string) (typ
string, ok
bool) {
270 t
, ok
:= db
.table(table
)
274 for n
, cname
:= range t
.colname
{
276 return t
.coltype
[n
], true
282 func (c
*fakeConn
) isBad() bool {
286 // alternate between bad conn and not bad conn
287 c
.db
.badConn
= !c
.db
.badConn
294 func (c
*fakeConn
) Begin() (driver
.Tx
, error
) {
296 return nil, driver
.ErrBadConn
299 return nil, errors
.New("already in a transaction")
301 c
.currTx
= &fakeTx
{c
: c
}
305 var hookPostCloseConn
struct {
307 fn
func(*fakeConn
, error
)
310 func setHookpostCloseConn(fn
func(*fakeConn
, error
)) {
311 hookPostCloseConn
.Lock()
312 defer hookPostCloseConn
.Unlock()
313 hookPostCloseConn
.fn
= fn
316 var testStrictClose
*testing
.T
318 // setStrictFakeConnClose sets the t to Errorf on when fakeConn.Close
319 // fails to close. If nil, the check is disabled.
320 func setStrictFakeConnClose(t
*testing
.T
) {
324 func (c
*fakeConn
) Close() (err error
) {
325 drv
:= fdriver
.(*fakeDriver
)
327 if err
!= nil && testStrictClose
!= nil {
328 testStrictClose
.Errorf("failed to close a test fakeConn: %v", err
)
330 hookPostCloseConn
.Lock()
331 fn
:= hookPostCloseConn
.fn
332 hookPostCloseConn
.Unlock()
343 return errors
.New("can't close fakeConn; in a Transaction")
346 return errors
.New("can't close fakeConn; already closed")
348 if c
.stmtsMade
> c
.stmtsClosed
{
349 return errors
.New("can't close; dangling statement(s)")
355 func checkSubsetTypes(args
[]driver
.NamedValue
) error
{
356 for _
, arg
:= range args
{
357 switch arg
.Value
.(type) {
358 case int64, float64, bool, nil, []byte, string, time
.Time
:
360 return fmt
.Errorf("fakedb_test: invalid argument ordinal %[1]d: %[2]v, type %[2]T", arg
.Ordinal
, arg
.Value
)
366 func (c
*fakeConn
) Exec(query
string, args
[]driver
.Value
) (driver
.Result
, error
) {
367 // Ensure that ExecContext is called if available.
368 panic("ExecContext was not called.")
371 func (c
*fakeConn
) ExecContext(ctx context
.Context
, query
string, args
[]driver
.NamedValue
) (driver
.Result
, error
) {
372 // This is an optional interface, but it's implemented here
373 // just to check that all the args are of the proper types.
374 // ErrSkip is returned so the caller acts as if we didn't
375 // implement this at all.
376 err
:= checkSubsetTypes(args
)
380 return nil, driver
.ErrSkip
383 func (c
*fakeConn
) Query(query
string, args
[]driver
.Value
) (driver
.Rows
, error
) {
384 // Ensure that ExecContext is called if available.
385 panic("QueryContext was not called.")
388 func (c
*fakeConn
) QueryContext(ctx context
.Context
, query
string, args
[]driver
.NamedValue
) (driver
.Rows
, error
) {
389 // This is an optional interface, but it's implemented here
390 // just to check that all the args are of the proper types.
391 // ErrSkip is returned so the caller acts as if we didn't
392 // implement this at all.
393 err
:= checkSubsetTypes(args
)
397 return nil, driver
.ErrSkip
400 func errf(msg
string, args
...interface{}) error
{
401 return errors
.New("fakedb: " + fmt
.Sprintf(msg
, args
...))
404 // parts are table|selectCol1,selectCol2|whereCol=?,whereCol2=?
405 // (note that where columns must always contain ? marks,
406 // just a limitation for fakedb)
407 func (c
*fakeConn
) prepareSelect(stmt
*fakeStmt
, parts
[]string) (*fakeStmt
, error
) {
410 return nil, errf("invalid SELECT syntax with %d parts; want 3", len(parts
))
412 stmt
.table
= parts
[0]
414 stmt
.colName
= strings
.Split(parts
[1], ",")
415 for n
, colspec
:= range strings
.Split(parts
[2], ",") {
419 nameVal
:= strings
.Split(colspec
, "=")
420 if len(nameVal
) != 2 {
422 return nil, errf("SELECT on table %q has invalid column spec of %q (index %d)", stmt
.table
, colspec
, n
)
424 column
, value
:= nameVal
[0], nameVal
[1]
425 _
, ok
:= c
.db
.columnType(stmt
.table
, column
)
428 return nil, errf("SELECT on table %q references non-existent column %q", stmt
.table
, column
)
430 if !strings
.HasPrefix(value
, "?") {
432 return nil, errf("SELECT on table %q has pre-bound value for where column %q; need a question mark",
436 stmt
.whereCol
= append(stmt
.whereCol
, boundCol
{Column
: column
, Placeholder
: value
, Ordinal
: stmt
.placeholders
})
441 // parts are table|col=type,col2=type2
442 func (c
*fakeConn
) prepareCreate(stmt
*fakeStmt
, parts
[]string) (*fakeStmt
, error
) {
445 return nil, errf("invalid CREATE syntax with %d parts; want 2", len(parts
))
447 stmt
.table
= parts
[0]
448 for n
, colspec
:= range strings
.Split(parts
[1], ",") {
449 nameType
:= strings
.Split(colspec
, "=")
450 if len(nameType
) != 2 {
452 return nil, errf("CREATE table %q has invalid column spec of %q (index %d)", stmt
.table
, colspec
, n
)
454 stmt
.colName
= append(stmt
.colName
, nameType
[0])
455 stmt
.colType
= append(stmt
.colType
, nameType
[1])
460 // parts are table|col=?,col2=val
461 func (c
*fakeConn
) prepareInsert(stmt
*fakeStmt
, parts
[]string) (*fakeStmt
, error
) {
464 return nil, errf("invalid INSERT syntax with %d parts; want 2", len(parts
))
466 stmt
.table
= parts
[0]
467 for n
, colspec
:= range strings
.Split(parts
[1], ",") {
468 nameVal
:= strings
.Split(colspec
, "=")
469 if len(nameVal
) != 2 {
471 return nil, errf("INSERT table %q has invalid column spec of %q (index %d)", stmt
.table
, colspec
, n
)
473 column
, value
:= nameVal
[0], nameVal
[1]
474 ctype
, ok
:= c
.db
.columnType(stmt
.table
, column
)
477 return nil, errf("INSERT table %q references non-existent column %q", stmt
.table
, column
)
479 stmt
.colName
= append(stmt
.colName
, column
)
481 if !strings
.HasPrefix(value
, "?") {
482 var subsetVal
interface{}
483 // Convert to driver subset type
486 subsetVal
= []byte(value
)
488 subsetVal
= []byte(value
)
490 i
, err
:= strconv
.Atoi(value
)
493 return nil, errf("invalid conversion to int32 from %q", value
)
495 subsetVal
= int64(i
) // int64 is a subset type, but not int32
498 return nil, errf("unsupported conversion for pre-bound parameter %q to type %q", value
, ctype
)
500 stmt
.colValue
= append(stmt
.colValue
, subsetVal
)
503 stmt
.placeholderConverter
= append(stmt
.placeholderConverter
, converterForType(ctype
))
504 stmt
.colValue
= append(stmt
.colValue
, value
)
510 // hook to simulate broken connections
511 var hookPrepareBadConn
func() bool
513 func (c
*fakeConn
) Prepare(query
string) (driver
.Stmt
, error
) {
514 panic("use PrepareContext")
517 func (c
*fakeConn
) PrepareContext(ctx context
.Context
, query
string) (driver
.Stmt
, error
) {
520 panic("nil c.db; conn = " + fmt
.Sprintf("%#v", c
))
523 if c
.stickyBad ||
(hookPrepareBadConn
!= nil && hookPrepareBadConn()) {
524 return nil, driver
.ErrBadConn
527 var firstStmt
, prev
*fakeStmt
528 for _
, query
:= range strings
.Split(query
, ";") {
529 parts
:= strings
.Split(query
, "|")
531 return nil, errf("empty query")
533 stmt
:= &fakeStmt
{q
: query
, c
: c
}
534 if firstStmt
== nil {
540 stmt
.panic = parts
[1]
543 wait
, err
:= time
.ParseDuration(parts
[1])
545 return nil, errf("expected section after WAIT to be a duration, got %q %v", parts
[1], err
)
556 wait
:= time
.NewTimer(stmt
.wait
)
561 return nil, ctx
.Err()
565 c
.incrStat(&c
.stmtsMade
)
571 stmt
, err
= c
.prepareSelect(stmt
, parts
)
573 stmt
, err
= c
.prepareCreate(stmt
, parts
)
575 stmt
, err
= c
.prepareInsert(stmt
, parts
)
577 // Do all the prep-work like for an INSERT but don't actually insert the row.
578 // Used for some of the concurrent tests.
579 stmt
, err
= c
.prepareInsert(stmt
, parts
)
582 return nil, errf("unsupported command type %q", cmd
)
592 return firstStmt
, nil
595 func (s
*fakeStmt
) ColumnConverter(idx
int) driver
.ValueConverter
{
596 if s
.panic == "ColumnConverter" {
599 if len(s
.placeholderConverter
) == 0 {
600 return driver
.DefaultParameterConverter
602 return s
.placeholderConverter
[idx
]
605 func (s
*fakeStmt
) Close() error
{
606 if s
.panic == "Close" {
610 panic("nil conn in fakeStmt.Close")
613 panic("in fakeStmt.Close, conn's db is nil (already closed)")
616 s
.c
.incrStat(&s
.c
.stmtsClosed
)
625 var errClosed
= errors
.New("fakedb: statement has been closed")
627 // hook to simulate broken connections
628 var hookExecBadConn
func() bool
630 func (s
*fakeStmt
) Exec(args
[]driver
.Value
) (driver
.Result
, error
) {
631 panic("Using ExecContext")
633 func (s
*fakeStmt
) ExecContext(ctx context
.Context
, args
[]driver
.NamedValue
) (driver
.Result
, error
) {
634 if s
.panic == "Exec" {
638 return nil, errClosed
641 if s
.c
.stickyBad ||
(hookExecBadConn
!= nil && hookExecBadConn()) {
642 return nil, driver
.ErrBadConn
645 err
:= checkSubsetTypes(args
)
657 return nil, ctx
.Err()
664 return driver
.ResultNoRows
, nil
666 if err
:= db
.createTable(s
.table
, s
.colName
, s
.colType
); err
!= nil {
669 return driver
.ResultNoRows
, nil
671 return s
.execInsert(args
, true)
673 // Do all the prep-work like for an INSERT but don't actually insert the row.
674 // Used for some of the concurrent tests.
675 return s
.execInsert(args
, false)
677 fmt
.Printf("EXEC statement, cmd=%q: %#v\n", s
.cmd
, s
)
678 return nil, fmt
.Errorf("unimplemented statement Exec command type of %q", s
.cmd
)
681 // When doInsert is true, add the row to the table.
682 // When doInsert is false do prep-work and error checking, but don't
683 // actually add the row to the table.
684 func (s
*fakeStmt
) execInsert(args
[]driver
.NamedValue
, doInsert
bool) (driver
.Result
, error
) {
686 if len(args
) != s
.placeholders
{
687 panic("error in pkg db; should only get here if size is correct")
690 t
, ok
:= db
.table(s
.table
)
693 return nil, fmt
.Errorf("fakedb: table %q doesn't exist", s
.table
)
699 var cols
[]interface{}
701 cols
= make([]interface{}, len(t
.colname
))
704 for n
, colname
:= range s
.colName
{
705 colidx
:= t
.columnIndex(colname
)
707 return nil, fmt
.Errorf("fakedb: column %q doesn't exist or dropped since prepared statement was created", colname
)
710 if strvalue
, ok
:= s
.colValue
[n
].(string); ok
&& strings
.HasPrefix(strvalue
, "?") {
712 val
= args
[argPos
].Value
714 // Assign value from argument placeholder name.
715 for _
, a
:= range args
{
716 if a
.Name
== strvalue
[1:] {
732 t
.rows
= append(t
.rows
, &row
{cols
: cols
})
734 return driver
.RowsAffected(1), nil
737 // hook to simulate broken connections
738 var hookQueryBadConn
func() bool
740 func (s
*fakeStmt
) Query(args
[]driver
.Value
) (driver
.Rows
, error
) {
741 panic("Use QueryContext")
744 func (s
*fakeStmt
) QueryContext(ctx context
.Context
, args
[]driver
.NamedValue
) (driver
.Rows
, error
) {
745 if s
.panic == "Query" {
749 return nil, errClosed
752 if s
.c
.stickyBad ||
(hookQueryBadConn
!= nil && hookQueryBadConn()) {
753 return nil, driver
.ErrBadConn
756 err
:= checkSubsetTypes(args
)
762 if len(args
) != s
.placeholders
{
763 panic("error in pkg db; should only get here if size is correct")
766 setMRows
:= make([][]*row
, 0, 1)
767 setColumns
:= make([][]string, 0, 1)
768 setColType
:= make([][]string, 0, 1)
772 t
, ok
:= db
.table(s
.table
)
775 return nil, fmt
.Errorf("fakedb: table %q doesn't exist", s
.table
)
778 if s
.table
== "magicquery" {
779 if len(s
.whereCol
) == 2 && s
.whereCol
[0].Column
== "op" && s
.whereCol
[1].Column
== "millis" {
780 if args
[0].Value
== "sleep" {
781 time
.Sleep(time
.Duration(args
[1].Value
.(int64)) * time
.Millisecond
)
788 colIdx
:= make(map[string]int) // select column name -> column index in table
789 for _
, name
:= range s
.colName
{
790 idx
:= t
.columnIndex(name
)
793 return nil, fmt
.Errorf("fakedb: unknown column name %q", name
)
800 for _
, trow
:= range t
.rows
{
801 // Process the where clause, skipping non-match rows. This is lazy
802 // and just uses fmt.Sprintf("%v") to test equality. Good enough
804 for _
, wcol
:= range s
.whereCol
{
805 idx
:= t
.columnIndex(wcol
.Column
)
808 return nil, fmt
.Errorf("db: invalid where clause column %q", wcol
)
810 tcol
:= trow
.cols
[idx
]
811 if bs
, ok
:= tcol
.([]byte); ok
{
812 // lazy hack to avoid sprintf %v on a []byte
815 var argValue
interface{}
816 if wcol
.Placeholder
== "?" {
817 argValue
= args
[wcol
.Ordinal
-1].Value
819 // Assign arg value from placeholder name.
820 for _
, a
:= range args
{
821 if a
.Name
== wcol
.Placeholder
[1:] {
827 if fmt
.Sprintf("%v", tcol
) != fmt
.Sprintf("%v", argValue
) {
831 mrow
:= &row
{cols
: make([]interface{}, len(s
.colName
))}
832 for seli
, name
:= range s
.colName
{
833 mrow
.cols
[seli
] = trow
.cols
[colIdx
[name
]]
835 mrows
= append(mrows
, mrow
)
839 for _
, column
:= range s
.colName
{
840 colType
= append(colType
, t
.coltype
[t
.columnIndex(column
)])
845 setMRows
= append(setMRows
, mrows
)
846 setColumns
= append(setColumns
, s
.colName
)
847 setColType
= append(setColType
, colType
)
855 cursor
:= &rowsCursor
{
865 func (s
*fakeStmt
) NumInput() int {
866 if s
.panic == "NumInput" {
869 return s
.placeholders
872 // hook to simulate broken connections
873 var hookCommitBadConn
func() bool
875 func (tx
*fakeTx
) Commit() error
{
877 if hookCommitBadConn
!= nil && hookCommitBadConn() {
878 return driver
.ErrBadConn
883 // hook to simulate broken connections
884 var hookRollbackBadConn
func() bool
886 func (tx
*fakeTx
) Rollback() error
{
888 if hookRollbackBadConn
!= nil && hookRollbackBadConn() {
889 return driver
.ErrBadConn
894 type rowsCursor
struct {
902 // errPos and err are for making Next return early with error.
906 // a clone of slices to give out to clients, indexed by the
907 // the original slice's first byte address. we clone them
908 // just so we're able to corrupt them on close.
909 bytesClone
map[*byte][]byte
912 func (rc
*rowsCursor
) Close() error
{
914 for _
, bs
:= range rc
.bytesClone
{
915 bs
[0] = 255 // first byte corrupted
922 func (rc
*rowsCursor
) Columns() []string {
923 return rc
.cols
[rc
.posSet
]
926 func (rc
*rowsCursor
) ColumnTypeScanType(index
int) reflect
.Type
{
927 return colTypeToReflectType(rc
.colType
[rc
.posSet
][index
])
930 var rowsCursorNextHook
func(dest
[]driver
.Value
) error
932 func (rc
*rowsCursor
) Next(dest
[]driver
.Value
) error
{
933 if rowsCursorNextHook
!= nil {
934 return rowsCursorNextHook(dest
)
938 return errors
.New("fakedb: cursor is closed")
941 if rc
.posRow
== rc
.errPos
{
944 if rc
.posRow
>= len(rc
.rows
[rc
.posSet
]) {
945 return io
.EOF
// per interface spec
947 for i
, v
:= range rc
.rows
[rc
.posSet
][rc
.posRow
].cols
{
948 // TODO(bradfitz): convert to subset types? naah, I
949 // think the subset types should only be input to
950 // driver, but the sql package should be able to handle
951 // a wider range of types coming out of drivers. all
952 // for ease of drivers, and to prevent drivers from
953 // messing up conversions or doing them differently.
956 if bs
, ok
:= v
.([]byte); ok
{
957 if rc
.bytesClone
== nil {
958 rc
.bytesClone
= make(map[*byte][]byte)
960 clone
, ok
:= rc
.bytesClone
[&bs
[0]]
962 clone
= make([]byte, len(bs
))
964 rc
.bytesClone
[&bs
[0]] = clone
972 func (rc
*rowsCursor
) HasNextResultSet() bool {
973 return rc
.posSet
< len(rc
.rows
)-1
976 func (rc
*rowsCursor
) NextResultSet() error
{
977 if rc
.HasNextResultSet() {
982 return io
.EOF
// Per interface spec.
985 // fakeDriverString is like driver.String, but indirects pointers like
986 // DefaultValueConverter.
988 // This could be surprising behavior to retroactively apply to
989 // driver.String now that Go1 is out, but this is convenient for
990 // our TestPointerParamsAndScans.
992 type fakeDriverString
struct{}
994 func (fakeDriverString
) ConvertValue(v
interface{}) (driver
.Value
, error
) {
995 switch c
:= v
.(type) {
1004 return fmt
.Sprintf("%v", v
), nil
1007 func converterForType(typ
string) driver
.ValueConverter
{
1012 return driver
.Null
{Converter
: driver
.Bool
}
1016 return driver
.NotNull
{Converter
: fakeDriverString
{}}
1018 return driver
.Null
{Converter
: fakeDriverString
{}}
1020 // TODO(coopernurse): add type-specific converter
1021 return driver
.NotNull
{Converter
: driver
.DefaultParameterConverter
}
1023 // TODO(coopernurse): add type-specific converter
1024 return driver
.Null
{Converter
: driver
.DefaultParameterConverter
}
1026 // TODO(coopernurse): add type-specific converter
1027 return driver
.NotNull
{Converter
: driver
.DefaultParameterConverter
}
1029 // TODO(coopernurse): add type-specific converter
1030 return driver
.Null
{Converter
: driver
.DefaultParameterConverter
}
1032 return driver
.DefaultParameterConverter
1034 panic("invalid fakedb column type of " + typ
)
1037 func colTypeToReflectType(typ
string) reflect
.Type
{
1040 return reflect
.TypeOf(false)
1042 return reflect
.TypeOf(NullBool
{})
1044 return reflect
.TypeOf(int32(0))
1046 return reflect
.TypeOf("")
1048 return reflect
.TypeOf(NullString
{})
1050 return reflect
.TypeOf(int64(0))
1052 return reflect
.TypeOf(NullInt64
{})
1054 return reflect
.TypeOf(float64(0))
1056 return reflect
.TypeOf(NullFloat64
{})
1058 return reflect
.TypeOf(time
.Time
{})
1060 panic("invalid fakedb column type of " + typ
)