database/sql: support returning query database types

Creates a ColumnType structure that can be extended in to future.
Allow drivers to implement what makes sense for the database.

Fixes #16652

Change-Id: Ieb1fd64eac1460107b1d3474eba5201fa300a4ec
Reviewed-on: https://go-review.googlesource.com/29961
Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
This commit is contained in:
Daniel Theophanes 2016-09-27 13:27:02 -07:00 committed by Brad Fitzpatrick
parent 2ecaaf18f9
commit 2a85578b0e
4 changed files with 259 additions and 11 deletions

View file

@ -11,6 +11,7 @@ import (
"fmt"
"io"
"log"
"reflect"
"sort"
"strconv"
"strings"
@ -405,6 +406,7 @@ func (c *fakeConn) prepareSelect(stmt *fakeStmt, parts []string) (*fakeStmt, err
return nil, errf("invalid SELECT syntax with %d parts; want 3", len(parts))
}
stmt.table = parts[0]
stmt.colName = strings.Split(parts[1], ",")
for n, colspec := range strings.Split(parts[2], ",") {
if colspec == "" {
@ -725,6 +727,7 @@ func (s *fakeStmt) QueryContext(ctx context.Context, args []driver.NamedValue) (
setMRows := make([][]*row, 0, 1)
setColumns := make([][]string, 0, 1)
setColType := make([][]string, 0, 1)
for {
db.mu.Lock()
@ -794,10 +797,16 @@ func (s *fakeStmt) QueryContext(ctx context.Context, args []driver.NamedValue) (
mrows = append(mrows, mrow)
}
var colType []string
for _, column := range s.colName {
colType = append(colType, t.coltype[t.columnIndex(column)])
}
t.mu.Unlock()
setMRows = append(setMRows, mrows)
setColumns = append(setColumns, s.colName)
setColType = append(setColType, colType)
if s.next == nil {
break
@ -806,10 +815,11 @@ func (s *fakeStmt) QueryContext(ctx context.Context, args []driver.NamedValue) (
}
cursor := &rowsCursor{
posRow: -1,
rows: setMRows,
cols: setColumns,
errPos: -1,
posRow: -1,
rows: setMRows,
cols: setColumns,
colType: setColType,
errPos: -1,
}
return cursor, nil
}
@ -844,11 +854,12 @@ func (tx *fakeTx) Rollback() error {
}
type rowsCursor struct {
cols [][]string
posSet int
posRow int
rows [][]*row
closed bool
cols [][]string
colType [][]string
posSet int
posRow int
rows [][]*row
closed bool
// errPos and err are for making Next return early with error.
errPos int
@ -874,6 +885,10 @@ func (rc *rowsCursor) Columns() []string {
return rc.cols[rc.posSet]
}
func (rc *rowsCursor) ColumnTypeScanType(index int) reflect.Type {
return colTypeToReflectType(rc.colType[rc.posSet][index])
}
var rowsCursorNextHook func(dest []driver.Value) error
func (rc *rowsCursor) Next(dest []driver.Value) error {
@ -980,3 +995,29 @@ func converterForType(typ string) driver.ValueConverter {
}
panic("invalid fakedb column type of " + typ)
}
func colTypeToReflectType(typ string) reflect.Type {
switch typ {
case "bool":
return reflect.TypeOf(false)
case "nullbool":
return reflect.TypeOf(NullBool{})
case "int32":
return reflect.TypeOf(int32(0))
case "string":
return reflect.TypeOf("")
case "nullstring":
return reflect.TypeOf(NullString{})
case "int64":
return reflect.TypeOf(int64(0))
case "nullint64":
return reflect.TypeOf(NullInt64{})
case "float64":
return reflect.TypeOf(float64(0))
case "nullfloat64":
return reflect.TypeOf(NullFloat64{})
case "datetime":
return reflect.TypeOf(time.Time{})
}
panic("invalid fakedb column type of " + typ)
}