MySQL execution and scan support.
This commit is contained in:
parent
5dda5e1e11
commit
bffa102849
34 changed files with 48216 additions and 337 deletions
|
|
@ -8,6 +8,8 @@ import (
|
|||
"fmt"
|
||||
"github.com/go-jet/jet/execution/internal"
|
||||
"github.com/go-jet/jet/internal/utils"
|
||||
"github.com/go-sql-driver/mysql"
|
||||
"github.com/google/uuid"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
|
@ -126,7 +128,7 @@ func mapRowToSlice(scanContext *scanContext, groupKey string, slicePtrValue refl
|
|||
|
||||
sliceElemType := getSliceElemType(slicePtrValue)
|
||||
|
||||
if isGoBaseType(sliceElemType) {
|
||||
if isSimpleModelType(sliceElemType) {
|
||||
updated, err = mapRowToBaseTypeSlice(scanContext, slicePtrValue, field)
|
||||
return
|
||||
}
|
||||
|
|
@ -226,7 +228,7 @@ func (s *scanContext) getTypeInfo(structType reflect.Type, parentField *reflect.
|
|||
|
||||
if implementsScannerType(field.Type) {
|
||||
fieldMap.implementsScanner = true
|
||||
} else if !isGoBaseType(field.Type) {
|
||||
} else if !isSimpleModelType(field.Type) {
|
||||
fieldMap.complexType = true
|
||||
}
|
||||
|
||||
|
|
@ -497,12 +499,34 @@ func valueToString(value reflect.Value) string {
|
|||
return fmt.Sprintf("%#v", valueInterface)
|
||||
}
|
||||
|
||||
func isGoBaseType(objType reflect.Type) bool {
|
||||
typeStr := objType.String()
|
||||
var timeType = reflect.TypeOf(time.Now())
|
||||
var uuidType = reflect.TypeOf(uuid.New())
|
||||
|
||||
switch typeStr {
|
||||
case "string", "int", "int16", "int32", "int64", "float32", "float64", "time.Time", "bool", "[]byte", "[]uint8",
|
||||
"*string", "*int", "*int16", "*int32", "*int64", "*float32", "*float64", "*time.Time", "*bool", "*[]byte", "*[]uint8":
|
||||
func isSimpleModelType(objType reflect.Type) bool {
|
||||
objType = indirectType(objType)
|
||||
|
||||
switch objType.Kind() {
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
|
||||
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
|
||||
reflect.Float32, reflect.Float64,
|
||||
reflect.String,
|
||||
reflect.Bool:
|
||||
return true
|
||||
case reflect.Slice:
|
||||
return objType.Elem().Kind() == reflect.Uint8 //[]byte
|
||||
case reflect.Struct:
|
||||
return objType == timeType || objType == uuidType // time.Time || uuid.UUID
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func tryAssign(source, destination reflect.Value) bool {
|
||||
if source.Type().ConvertibleTo(destination.Type()) {
|
||||
source = source.Convert(destination.Type())
|
||||
}
|
||||
if source.Type().AssignableTo(destination.Type()) {
|
||||
destination.Set(source)
|
||||
return true
|
||||
}
|
||||
|
||||
|
|
@ -510,35 +534,50 @@ func isGoBaseType(objType reflect.Type) bool {
|
|||
}
|
||||
|
||||
func setReflectValue(source, destination reflect.Value) error {
|
||||
var sourceElem reflect.Value
|
||||
|
||||
if tryAssign(source, destination) {
|
||||
return nil
|
||||
}
|
||||
|
||||
if destination.Kind() == reflect.Ptr {
|
||||
if source.Kind() == reflect.Ptr {
|
||||
sourceElem = source
|
||||
|
||||
if !source.IsNil() {
|
||||
if destination.IsNil() {
|
||||
initializeValueIfNilPtr(destination)
|
||||
}
|
||||
|
||||
tryAssign(source.Elem(), destination.Elem())
|
||||
}
|
||||
} else {
|
||||
if source.CanAddr() {
|
||||
sourceElem = source.Addr()
|
||||
source = source.Addr()
|
||||
} else {
|
||||
sourceCopy := reflect.New(source.Type())
|
||||
sourceCopy.Elem().Set(source)
|
||||
|
||||
sourceElem = sourceCopy
|
||||
source = sourceCopy
|
||||
}
|
||||
|
||||
if tryAssign(source, destination) {
|
||||
return nil
|
||||
}
|
||||
|
||||
if tryAssign(source.Elem(), destination.Elem()) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if source.Kind() == reflect.Ptr {
|
||||
sourceElem = source.Elem()
|
||||
} else {
|
||||
sourceElem = source
|
||||
source = source.Elem()
|
||||
}
|
||||
|
||||
if tryAssign(source, destination) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
if !sourceElem.Type().AssignableTo(destination.Type()) {
|
||||
return errors.New("jet: can't set " + sourceElem.Type().String() + " to " + destination.Type().String())
|
||||
}
|
||||
|
||||
destination.Set(sourceElem)
|
||||
|
||||
return nil
|
||||
return errors.New("jet: can't set " + source.Type().String() + " to " + destination.Type().String())
|
||||
}
|
||||
|
||||
func createScanValue(columnTypes []*sql.ColumnType) []interface{} {
|
||||
|
|
@ -555,33 +594,63 @@ func createScanValue(columnTypes []*sql.ColumnType) []interface{} {
|
|||
return values
|
||||
}
|
||||
|
||||
var nullFloatType = reflect.TypeOf(internal.NullFloat32{})
|
||||
var nullFloat64Type = reflect.TypeOf(sql.NullFloat64{})
|
||||
var int8Type = reflect.TypeOf(int8(1))
|
||||
var unit8Type = reflect.TypeOf(uint8(1))
|
||||
var int16Type = reflect.TypeOf(int16(1))
|
||||
var uint16Type = reflect.TypeOf(uint16(1))
|
||||
var int32Type = reflect.TypeOf(int32(1))
|
||||
var uint32Type = reflect.TypeOf(uint32(1))
|
||||
var int64Type = reflect.TypeOf(int64(1))
|
||||
var uint64Type = reflect.TypeOf(uint64(1))
|
||||
var float32Type = reflect.TypeOf(float32(1.1))
|
||||
var float64Type = reflect.TypeOf(float64(1.1))
|
||||
|
||||
var nullInt8Type = reflect.TypeOf(internal.NullInt8{})
|
||||
var nullUInt8Type = reflect.TypeOf(internal.NullUInt8{})
|
||||
var nullInt16Type = reflect.TypeOf(internal.NullInt16{})
|
||||
var nullUInt16Type = reflect.TypeOf(internal.NullUInt16{})
|
||||
var nullInt32Type = reflect.TypeOf(internal.NullInt32{})
|
||||
var nullUInt32Type = reflect.TypeOf(internal.NullUInt32{})
|
||||
var nullInt64Type = reflect.TypeOf(sql.NullInt64{})
|
||||
var nullUInt64Type = reflect.TypeOf(internal.NullUInt64{})
|
||||
|
||||
var nullFloat32Type = reflect.TypeOf(internal.NullFloat32{})
|
||||
var nullFloat64Type = reflect.TypeOf(sql.NullFloat64{})
|
||||
|
||||
var nullStringType = reflect.TypeOf(sql.NullString{})
|
||||
|
||||
var nullBoolType = reflect.TypeOf(sql.NullBool{})
|
||||
|
||||
var nullTimeType = reflect.TypeOf(internal.NullTime{})
|
||||
var mySQLNullTime = reflect.TypeOf(mysql.NullTime{})
|
||||
|
||||
var nullByteArrayType = reflect.TypeOf(internal.NullByteArray{})
|
||||
|
||||
func newScanType(columnType *sql.ColumnType) reflect.Type {
|
||||
|
||||
switch columnType.ScanType() {
|
||||
case mySQLNullTime:
|
||||
return mySQLNullTime
|
||||
}
|
||||
|
||||
switch columnType.DatabaseTypeName() {
|
||||
case "INT2":
|
||||
case "TINYINT":
|
||||
return nullInt8Type
|
||||
case "INT2", "SMALLINT", "YEAR":
|
||||
return nullInt16Type
|
||||
case "INT4":
|
||||
case "INT4", "MEDIUMINT", "INT":
|
||||
return nullInt32Type
|
||||
case "INT8":
|
||||
case "INT8", "BIGINT":
|
||||
return nullInt64Type
|
||||
case "VARCHAR", "TEXT", "", "_TEXT", "TSVECTOR", "BPCHAR", "UUID", "JSON", "JSONB", "INTERVAL", "POINT", "BIT", "VARBIT", "XML":
|
||||
case "CHAR", "VARCHAR", "TEXT", "", "_TEXT", "TSVECTOR", "BPCHAR", "UUID", "JSON", "JSONB", "INTERVAL", "POINT", "BIT", "VARBIT", "XML":
|
||||
return nullStringType
|
||||
case "FLOAT4":
|
||||
return nullFloatType
|
||||
case "FLOAT8", "NUMERIC", "DECIMAL":
|
||||
return nullFloat32Type
|
||||
case "FLOAT8", "NUMERIC", "DECIMAL", "FLOAT", "DOUBLE":
|
||||
return nullFloat64Type
|
||||
case "BOOL":
|
||||
return nullBoolType
|
||||
case "BYTEA":
|
||||
case "BYTEA", "BINARY", "VARBINARY", "BLOB":
|
||||
return nullByteArrayType
|
||||
case "DATE", "TIMESTAMP", "TIMESTAMPTZ", "TIME", "TIMETZ":
|
||||
return nullTimeType
|
||||
|
|
@ -697,7 +766,7 @@ func (s *scanContext) getGroupKeyInfo(structType reflect.Type, parentField *refl
|
|||
field := structType.Field(i)
|
||||
newTypeName, fieldName := getTypeAndFieldName(typeName, field)
|
||||
|
||||
if !isGoBaseType(field.Type) {
|
||||
if !isSimpleModelType(field.Type) {
|
||||
var structType reflect.Type
|
||||
if field.Type.Kind() == reflect.Struct {
|
||||
structType = field.Type
|
||||
|
|
|
|||
|
|
@ -2,9 +2,12 @@ package internal
|
|||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
//===============================================================//
|
||||
|
||||
// NullByteArray struct
|
||||
type NullByteArray struct {
|
||||
ByteArray []byte
|
||||
|
|
@ -31,6 +34,8 @@ func (nb NullByteArray) Value() (driver.Value, error) {
|
|||
return nb.ByteArray, nil
|
||||
}
|
||||
|
||||
//===============================================================//
|
||||
|
||||
// NullTime struct
|
||||
type NullTime struct {
|
||||
Time time.Time
|
||||
|
|
@ -51,24 +56,30 @@ func (nt NullTime) Value() (driver.Value, error) {
|
|||
return nt.Time, nil
|
||||
}
|
||||
|
||||
// NullInt32 struct
|
||||
type NullInt32 struct {
|
||||
Int32 int32
|
||||
Valid bool // Valid is true if Int64 is not NULL
|
||||
//===============================================================//
|
||||
|
||||
// NullInt8 struct
|
||||
type NullInt8 struct {
|
||||
Int8 int8
|
||||
Valid bool
|
||||
}
|
||||
|
||||
// Scan implements the Scanner interface.
|
||||
func (n *NullInt32) Scan(value interface{}) error {
|
||||
func (n *NullInt8) Scan(value interface{}) error {
|
||||
|
||||
switch v := value.(type) {
|
||||
case int64:
|
||||
n.Int32, n.Valid = int32(v), true
|
||||
n.Int8, n.Valid = int8(v), true
|
||||
return nil
|
||||
case int32:
|
||||
n.Int32, n.Valid = v, true
|
||||
return nil
|
||||
case uint8:
|
||||
n.Int32, n.Valid = int32(v), true
|
||||
case int8:
|
||||
n.Int8, n.Valid = v, true
|
||||
return nil
|
||||
case []byte:
|
||||
intV, err := strconv.ParseInt(string(v), 10, 8)
|
||||
if err == nil {
|
||||
n.Int8, n.Valid = int8(intV), true
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
n.Valid = false
|
||||
|
|
@ -77,21 +88,60 @@ func (n *NullInt32) Scan(value interface{}) error {
|
|||
}
|
||||
|
||||
// Value implements the driver Valuer interface.
|
||||
func (n NullInt32) Value() (driver.Value, error) {
|
||||
func (n NullInt8) Value() (driver.Value, error) {
|
||||
if !n.Valid {
|
||||
return nil, nil
|
||||
}
|
||||
return n.Int32, nil
|
||||
return n.Int8, nil
|
||||
}
|
||||
|
||||
//===============================================================//
|
||||
|
||||
// NullUInt8 struct
|
||||
type NullUInt8 struct {
|
||||
Uint8 uint8
|
||||
Valid bool
|
||||
}
|
||||
|
||||
// Scan implements the Scanner interface.
|
||||
func (n *NullUInt8) Scan(value interface{}) error {
|
||||
|
||||
switch v := value.(type) {
|
||||
case uint8:
|
||||
n.Uint8, n.Valid = v, true
|
||||
return nil
|
||||
case []byte:
|
||||
intV, err := strconv.ParseInt(string(v), 10, 8)
|
||||
if err == nil {
|
||||
n.Uint8, n.Valid = uint8(intV), true
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
n.Valid = false
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Value implements the driver Valuer interface.
|
||||
func (n NullUInt8) Value() (driver.Value, error) {
|
||||
if !n.Valid {
|
||||
return nil, nil
|
||||
}
|
||||
return n.Uint8, nil
|
||||
}
|
||||
|
||||
//===============================================================//
|
||||
|
||||
// NullInt16 struct
|
||||
type NullInt16 struct {
|
||||
Int16 int16
|
||||
Valid bool // Valid is true if Int64 is not NULL
|
||||
Valid bool
|
||||
}
|
||||
|
||||
// Scan implements the Scanner interface.
|
||||
func (n *NullInt16) Scan(value interface{}) error {
|
||||
|
||||
switch v := value.(type) {
|
||||
case int64:
|
||||
n.Int16, n.Valid = int16(v), true
|
||||
|
|
@ -99,9 +149,18 @@ func (n *NullInt16) Scan(value interface{}) error {
|
|||
case int16:
|
||||
n.Int16, n.Valid = v, true
|
||||
return nil
|
||||
case int8:
|
||||
n.Int16, n.Valid = int16(v), true
|
||||
return nil
|
||||
case uint8:
|
||||
n.Int16, n.Valid = int16(v), true
|
||||
return nil
|
||||
case []byte:
|
||||
intV, err := strconv.ParseInt(string(v), 10, 16)
|
||||
if err == nil {
|
||||
n.Int16, n.Valid = int16(intV), true
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
n.Valid = false
|
||||
|
|
@ -117,10 +176,259 @@ func (n NullInt16) Value() (driver.Value, error) {
|
|||
return n.Int16, nil
|
||||
}
|
||||
|
||||
//===============================================================//
|
||||
|
||||
// NullUInt16 struct
|
||||
type NullUInt16 struct {
|
||||
UInt16 uint16
|
||||
Valid bool
|
||||
}
|
||||
|
||||
// Scan implements the Scanner interface.
|
||||
func (n *NullUInt16) Scan(value interface{}) error {
|
||||
|
||||
switch v := value.(type) {
|
||||
case uint16:
|
||||
n.UInt16, n.Valid = v, true
|
||||
return nil
|
||||
case int8:
|
||||
n.UInt16, n.Valid = uint16(v), true
|
||||
return nil
|
||||
case uint8:
|
||||
n.UInt16, n.Valid = uint16(v), true
|
||||
return nil
|
||||
case []byte:
|
||||
intV, err := strconv.ParseInt(string(v), 10, 16)
|
||||
if err == nil {
|
||||
n.UInt16, n.Valid = uint16(intV), true
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
n.Valid = false
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Value implements the driver Valuer interface.
|
||||
func (n NullUInt16) Value() (driver.Value, error) {
|
||||
if !n.Valid {
|
||||
return nil, nil
|
||||
}
|
||||
return n.UInt16, nil
|
||||
}
|
||||
|
||||
//===============================================================//
|
||||
|
||||
// NullInt32 struct
|
||||
type NullInt32 struct {
|
||||
Int32 int32
|
||||
Valid bool
|
||||
}
|
||||
|
||||
// Scan implements the Scanner interface.
|
||||
func (n *NullInt32) Scan(value interface{}) error {
|
||||
|
||||
switch v := value.(type) {
|
||||
case int64:
|
||||
n.Int32, n.Valid = int32(v), true
|
||||
return nil
|
||||
case int32:
|
||||
n.Int32, n.Valid = v, true
|
||||
return nil
|
||||
case int16:
|
||||
n.Int32, n.Valid = int32(v), true
|
||||
return nil
|
||||
case uint16:
|
||||
n.Int32, n.Valid = int32(v), true
|
||||
return nil
|
||||
case int8:
|
||||
n.Int32, n.Valid = int32(v), true
|
||||
return nil
|
||||
case uint8:
|
||||
n.Int32, n.Valid = int32(v), true
|
||||
return nil
|
||||
case []byte:
|
||||
intV, err := strconv.ParseInt(string(v), 10, 32)
|
||||
if err == nil {
|
||||
n.Int32, n.Valid = int32(intV), true
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
n.Valid = false
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Value implements the driver Valuer interface.
|
||||
func (n NullInt32) Value() (driver.Value, error) {
|
||||
if !n.Valid {
|
||||
return nil, nil
|
||||
}
|
||||
return n.Int32, nil
|
||||
}
|
||||
|
||||
//===============================================================//
|
||||
|
||||
// NullInt32 struct
|
||||
type NullUInt32 struct {
|
||||
UInt32 uint32
|
||||
Valid bool
|
||||
}
|
||||
|
||||
// Scan implements the Scanner interface.
|
||||
func (n *NullUInt32) Scan(value interface{}) error {
|
||||
|
||||
switch v := value.(type) {
|
||||
case uint32:
|
||||
n.UInt32, n.Valid = v, true
|
||||
return nil
|
||||
case int16:
|
||||
n.UInt32, n.Valid = uint32(v), true
|
||||
return nil
|
||||
case uint16:
|
||||
n.UInt32, n.Valid = uint32(v), true
|
||||
return nil
|
||||
case int8:
|
||||
n.UInt32, n.Valid = uint32(v), true
|
||||
return nil
|
||||
case uint8:
|
||||
n.UInt32, n.Valid = uint32(v), true
|
||||
return nil
|
||||
case []byte:
|
||||
intV, err := strconv.ParseInt(string(v), 10, 32)
|
||||
if err == nil {
|
||||
n.UInt32, n.Valid = uint32(intV), true
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
n.Valid = false
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Value implements the driver Valuer interface.
|
||||
func (n NullUInt32) Value() (driver.Value, error) {
|
||||
if !n.Valid {
|
||||
return nil, nil
|
||||
}
|
||||
return n.UInt32, nil
|
||||
}
|
||||
|
||||
//===============================================================//
|
||||
|
||||
// NullInt32 struct
|
||||
type NullInt64 struct {
|
||||
Int64 int64
|
||||
Valid bool
|
||||
}
|
||||
|
||||
// Scan implements the Scanner interface.
|
||||
func (n *NullInt64) Scan(value interface{}) error {
|
||||
switch v := value.(type) {
|
||||
case int64:
|
||||
n.Int64, n.Valid = v, true
|
||||
return nil
|
||||
case int32:
|
||||
n.Int64, n.Valid = int64(v), true
|
||||
return nil
|
||||
case uint32:
|
||||
n.Int64, n.Valid = int64(v), true
|
||||
return nil
|
||||
case int16:
|
||||
n.Int64, n.Valid = int64(v), true
|
||||
return nil
|
||||
case uint16:
|
||||
n.Int64, n.Valid = int64(v), true
|
||||
return nil
|
||||
case int8:
|
||||
n.Int64, n.Valid = int64(v), true
|
||||
return nil
|
||||
case uint8:
|
||||
n.Int64, n.Valid = int64(v), true
|
||||
return nil
|
||||
case []byte:
|
||||
intV, err := strconv.ParseInt(string(v), 10, 32)
|
||||
if err == nil {
|
||||
n.Int64, n.Valid = int64(intV), true
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
n.Valid = false
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Value implements the driver Valuer interface.
|
||||
func (n NullInt64) Value() (driver.Value, error) {
|
||||
if !n.Valid {
|
||||
return nil, nil
|
||||
}
|
||||
return n.Int64, nil
|
||||
}
|
||||
|
||||
//===============================================================//
|
||||
|
||||
// NullInt32 struct
|
||||
type NullUInt64 struct {
|
||||
UInt64 uint64
|
||||
Valid bool
|
||||
}
|
||||
|
||||
// Scan implements the Scanner interface.
|
||||
func (n *NullUInt64) Scan(value interface{}) error {
|
||||
switch v := value.(type) {
|
||||
case uint64:
|
||||
n.UInt64, n.Valid = v, true
|
||||
return nil
|
||||
case int32:
|
||||
n.UInt64, n.Valid = uint64(v), true
|
||||
return nil
|
||||
case uint32:
|
||||
n.UInt64, n.Valid = uint64(v), true
|
||||
return nil
|
||||
case int16:
|
||||
n.UInt64, n.Valid = uint64(v), true
|
||||
return nil
|
||||
case uint16:
|
||||
n.UInt64, n.Valid = uint64(v), true
|
||||
return nil
|
||||
case int8:
|
||||
n.UInt64, n.Valid = uint64(v), true
|
||||
return nil
|
||||
case uint8:
|
||||
n.UInt64, n.Valid = uint64(v), true
|
||||
return nil
|
||||
case []byte:
|
||||
intV, err := strconv.ParseInt(string(v), 10, 32)
|
||||
if err == nil {
|
||||
n.UInt64, n.Valid = uint64(intV), true
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
n.Valid = false
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Value implements the driver Valuer interface.
|
||||
func (n NullUInt64) Value() (driver.Value, error) {
|
||||
if !n.Valid {
|
||||
return nil, nil
|
||||
}
|
||||
return n.UInt64, nil
|
||||
}
|
||||
|
||||
//===============================================================//
|
||||
|
||||
// NullFloat32 struct
|
||||
type NullFloat32 struct {
|
||||
Float32 float32
|
||||
Valid bool // Valid is true if Int64 is not NULL
|
||||
Valid bool
|
||||
}
|
||||
|
||||
// Scan implements the Scanner interface.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue