init - add project files
This commit is contained in:
25
vendor/github.com/go-jet/jet/v2/qrm/db.go
generated
vendored
Normal file
25
vendor/github.com/go-jet/jet/v2/qrm/db.go
generated
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
package qrm
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
)
|
||||
|
||||
// DB is common database interface used by query result mapping
|
||||
// Both *sql.DB and *sql.Tx implements DB interface
|
||||
type DB interface {
|
||||
Exec(query string, args ...interface{}) (sql.Result, error)
|
||||
ExecContext(ctx context.Context, query string, args ...interface{}) (sql.Result, error)
|
||||
Query(query string, args ...interface{}) (*sql.Rows, error)
|
||||
QueryContext(ctx context.Context, query string, args ...interface{}) (*sql.Rows, error)
|
||||
}
|
||||
|
||||
// Queryable interface for sql QueryContext method
|
||||
type Queryable interface {
|
||||
QueryContext(ctx context.Context, query string, args ...interface{}) (*sql.Rows, error)
|
||||
}
|
||||
|
||||
// Executable interface for sql ExecContext method
|
||||
type Executable interface {
|
||||
ExecContext(ctx context.Context, query string, args ...interface{}) (sql.Result, error)
|
||||
}
|
||||
194
vendor/github.com/go-jet/jet/v2/qrm/internal/null_types.go
generated
vendored
Normal file
194
vendor/github.com/go-jet/jet/v2/qrm/internal/null_types.go
generated
vendored
Normal file
@@ -0,0 +1,194 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"database/sql/driver"
|
||||
"fmt"
|
||||
"github.com/go-jet/jet/v2/internal/utils/min"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
castOverFlowError = fmt.Errorf("cannot cast a negative value to an unsigned value, buffer overflow error")
|
||||
)
|
||||
|
||||
// NullBool struct
|
||||
type NullBool struct {
|
||||
sql.NullBool
|
||||
}
|
||||
|
||||
// Scan implements the Scanner interface.
|
||||
func (nb *NullBool) Scan(value interface{}) error {
|
||||
switch v := value.(type) {
|
||||
case bool:
|
||||
nb.Bool, nb.Valid = v, true
|
||||
case int8, int16, int32, int64, int:
|
||||
intVal := reflect.ValueOf(v).Int()
|
||||
|
||||
if intVal != 0 && intVal != 1 {
|
||||
return fmt.Errorf("can't assign %T(%d) to bool", value, value)
|
||||
}
|
||||
|
||||
nb.Bool = intVal == 1
|
||||
nb.Valid = true
|
||||
case uint8, uint16, uint32, uint64, uint:
|
||||
uintVal := reflect.ValueOf(v).Uint()
|
||||
|
||||
if uintVal != 0 && uintVal != 1 {
|
||||
return fmt.Errorf("can't assign %T(%d) to bool", value, value)
|
||||
}
|
||||
|
||||
nb.Bool = uintVal == 1
|
||||
nb.Valid = true
|
||||
default:
|
||||
return nb.NullBool.Scan(value)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// NullTime struct
|
||||
type NullTime struct {
|
||||
sql.NullTime
|
||||
}
|
||||
|
||||
// Scan implements the Scanner interface.
|
||||
func (nt *NullTime) Scan(value interface{}) error {
|
||||
err := nt.NullTime.Scan(value)
|
||||
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Some of the drivers (pgx, mysql) are not parsing all of the time formats(date, time with time zone,...) and are just forwarding string value.
|
||||
// At this point we try to parse those values using some of the predefined formats
|
||||
nt.Time, nt.Valid = tryParseAsTime(value)
|
||||
|
||||
if !nt.Valid {
|
||||
return fmt.Errorf("can't scan time.Time from %q", value)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var formats = []string{
|
||||
"2006-01-02 15:04:05-07:00", // sqlite
|
||||
"2006-01-02 15:04:05.999999", // go-sql-driver/mysql
|
||||
"15:04:05-07", // pgx
|
||||
"15:04:05.999999", // pgx
|
||||
}
|
||||
|
||||
func tryParseAsTime(value interface{}) (time.Time, bool) {
|
||||
|
||||
var timeStr string
|
||||
|
||||
switch v := value.(type) {
|
||||
case string:
|
||||
timeStr = v
|
||||
case []byte:
|
||||
timeStr = string(v)
|
||||
case int64:
|
||||
return time.Unix(v, 0), true // sqlite
|
||||
default:
|
||||
return time.Time{}, false
|
||||
}
|
||||
|
||||
for _, format := range formats {
|
||||
formatLen := min.Int(len(format), len(timeStr))
|
||||
t, err := time.Parse(format[:formatLen], timeStr)
|
||||
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
return t, true
|
||||
}
|
||||
|
||||
return time.Time{}, false
|
||||
}
|
||||
|
||||
// NullUInt64 struct
|
||||
type NullUInt64 struct {
|
||||
UInt64 uint64
|
||||
Valid bool
|
||||
}
|
||||
|
||||
// Scan implements the Scanner interface.
|
||||
func (n *NullUInt64) Scan(value interface{}) error {
|
||||
var stringValue string
|
||||
switch v := value.(type) {
|
||||
case nil:
|
||||
n.Valid = false
|
||||
return nil
|
||||
case int64:
|
||||
if v < 0 {
|
||||
return castOverFlowError
|
||||
}
|
||||
n.UInt64, n.Valid = uint64(v), true
|
||||
return nil
|
||||
case int32:
|
||||
if v < 0 {
|
||||
return castOverFlowError
|
||||
}
|
||||
n.UInt64, n.Valid = uint64(v), true
|
||||
return nil
|
||||
case int16:
|
||||
if v < 0 {
|
||||
return castOverFlowError
|
||||
}
|
||||
n.UInt64, n.Valid = uint64(v), true
|
||||
return nil
|
||||
case int8:
|
||||
if v < 0 {
|
||||
return castOverFlowError
|
||||
}
|
||||
n.UInt64, n.Valid = uint64(v), true
|
||||
return nil
|
||||
case int:
|
||||
if v < 0 {
|
||||
return castOverFlowError
|
||||
}
|
||||
n.UInt64, n.Valid = uint64(v), true
|
||||
return nil
|
||||
case uint64:
|
||||
n.UInt64, n.Valid = v, true
|
||||
return nil
|
||||
case uint32:
|
||||
n.UInt64, n.Valid = uint64(v), true
|
||||
return nil
|
||||
case uint16:
|
||||
n.UInt64, n.Valid = uint64(v), true
|
||||
return nil
|
||||
case uint8:
|
||||
n.UInt64, n.Valid = uint64(v), true
|
||||
return nil
|
||||
case uint:
|
||||
n.UInt64, n.Valid = uint64(v), true
|
||||
return nil
|
||||
case []byte:
|
||||
stringValue = string(v)
|
||||
case string:
|
||||
stringValue = v
|
||||
default:
|
||||
return fmt.Errorf("can't scan uint64 from %v", value)
|
||||
}
|
||||
|
||||
uintV, err := strconv.ParseUint(stringValue, 10, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
n.UInt64 = uintV
|
||||
n.Valid = true
|
||||
|
||||
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
|
||||
}
|
||||
337
vendor/github.com/go-jet/jet/v2/qrm/qrm.go
generated
vendored
Normal file
337
vendor/github.com/go-jet/jet/v2/qrm/qrm.go
generated
vendored
Normal file
@@ -0,0 +1,337 @@
|
||||
package qrm
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/go-jet/jet/v2/internal/utils/must"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// ErrNoRows is returned by Query when query result set is empty
|
||||
var ErrNoRows = errors.New("qrm: no rows in result set")
|
||||
|
||||
// Query executes Query Result Mapping (QRM) of `query` with list of parametrized arguments `arg` over database connection `db`
|
||||
// using context `ctx` into destination `destPtr`.
|
||||
// Destination can be either pointer to struct or pointer to slice of structs.
|
||||
// If destination is pointer to struct and query result set is empty, method returns qrm.ErrNoRows.
|
||||
func Query(ctx context.Context, db Queryable, query string, args []interface{}, destPtr interface{}) (rowsProcessed int64, err error) {
|
||||
|
||||
must.BeInitializedPtr(db, "jet: db is nil")
|
||||
must.BeInitializedPtr(destPtr, "jet: destination is nil")
|
||||
must.BeTypeKind(destPtr, reflect.Ptr, "jet: destination has to be a pointer to slice or pointer to struct")
|
||||
|
||||
destinationPtrType := reflect.TypeOf(destPtr)
|
||||
|
||||
if destinationPtrType.Elem().Kind() == reflect.Slice {
|
||||
rowsProcessed, err := queryToSlice(ctx, db, query, args, destPtr)
|
||||
if err != nil {
|
||||
return rowsProcessed, fmt.Errorf("jet: %w", err)
|
||||
}
|
||||
return rowsProcessed, nil
|
||||
} else if destinationPtrType.Elem().Kind() == reflect.Struct {
|
||||
tempSlicePtrValue := reflect.New(reflect.SliceOf(destinationPtrType))
|
||||
tempSliceValue := tempSlicePtrValue.Elem()
|
||||
|
||||
rowsProcessed, err := queryToSlice(ctx, db, query, args, tempSlicePtrValue.Interface())
|
||||
|
||||
if err != nil {
|
||||
return rowsProcessed, fmt.Errorf("jet: %w", err)
|
||||
}
|
||||
|
||||
if rowsProcessed == 0 {
|
||||
return 0, ErrNoRows
|
||||
}
|
||||
|
||||
// edge case when row result set contains only NULLs.
|
||||
if tempSliceValue.Len() == 0 {
|
||||
return rowsProcessed, nil
|
||||
}
|
||||
|
||||
structValue := reflect.ValueOf(destPtr).Elem()
|
||||
firstTempStruct := tempSliceValue.Index(0).Elem()
|
||||
|
||||
if structValue.Type().AssignableTo(firstTempStruct.Type()) {
|
||||
structValue.Set(tempSliceValue.Index(0).Elem())
|
||||
}
|
||||
return rowsProcessed, nil
|
||||
} else {
|
||||
panic("jet: destination has to be a pointer to slice or pointer to struct")
|
||||
}
|
||||
}
|
||||
|
||||
// ScanOneRowToDest will scan one row into struct destination
|
||||
func ScanOneRowToDest(scanContext *ScanContext, rows *sql.Rows, destPtr interface{}) error {
|
||||
must.BeInitializedPtr(destPtr, "jet: destination is nil")
|
||||
must.BeTypeKind(destPtr, reflect.Ptr, "jet: destination has to be a pointer to slice or pointer to struct")
|
||||
|
||||
if len(scanContext.row) == 0 {
|
||||
return errors.New("empty row slice")
|
||||
}
|
||||
|
||||
err := rows.Scan(scanContext.row...)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("jet: rows scan error, %w", err)
|
||||
}
|
||||
|
||||
destValuePtr := reflect.ValueOf(destPtr)
|
||||
|
||||
_, err = mapRowToStruct(scanContext, "", destValuePtr, nil)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("jet: failed to scan a row into destination, %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func queryToSlice(ctx context.Context, db Queryable, query string, args []interface{}, slicePtr interface{}) (rowsProcessed int64, err error) {
|
||||
if ctx == nil {
|
||||
ctx = context.Background()
|
||||
}
|
||||
|
||||
rows, err := db.QueryContext(ctx, query, args...)
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
scanContext, err := NewScanContext(rows)
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if len(scanContext.row) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
slicePtrValue := reflect.ValueOf(slicePtr)
|
||||
|
||||
for rows.Next() {
|
||||
err = rows.Scan(scanContext.row...)
|
||||
|
||||
if err != nil {
|
||||
return scanContext.rowNum, err
|
||||
}
|
||||
|
||||
scanContext.rowNum++
|
||||
|
||||
_, err = mapRowToSlice(scanContext, "", slicePtrValue, nil)
|
||||
|
||||
if err != nil {
|
||||
return scanContext.rowNum, err
|
||||
}
|
||||
}
|
||||
|
||||
err = rows.Close()
|
||||
if err != nil {
|
||||
return scanContext.rowNum, err
|
||||
}
|
||||
|
||||
return scanContext.rowNum, rows.Err()
|
||||
}
|
||||
|
||||
func mapRowToSlice(
|
||||
scanContext *ScanContext,
|
||||
groupKey string,
|
||||
slicePtrValue reflect.Value,
|
||||
field *reflect.StructField) (updated bool, err error) {
|
||||
|
||||
sliceElemType := getSliceElemType(slicePtrValue)
|
||||
|
||||
if isSimpleModelType(sliceElemType) {
|
||||
updated, err = mapRowToBaseTypeSlice(scanContext, slicePtrValue, field)
|
||||
return
|
||||
}
|
||||
|
||||
must.TypeBeOfKind(sliceElemType, reflect.Struct, "jet: unsupported slice element type"+fieldToString(field))
|
||||
|
||||
structGroupKey := scanContext.getGroupKey(sliceElemType, field)
|
||||
|
||||
groupKey = concat(groupKey, ",", structGroupKey)
|
||||
|
||||
index, ok := scanContext.uniqueDestObjectsMap[groupKey]
|
||||
|
||||
if ok {
|
||||
structPtrValue := getSliceElemPtrAt(slicePtrValue, index)
|
||||
|
||||
return mapRowToStruct(scanContext, groupKey, structPtrValue, field, true)
|
||||
}
|
||||
|
||||
destinationStructPtr := newElemPtrValueForSlice(slicePtrValue)
|
||||
|
||||
updated, err = mapRowToStruct(scanContext, groupKey, destinationStructPtr, field)
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if updated {
|
||||
scanContext.uniqueDestObjectsMap[groupKey] = slicePtrValue.Elem().Len()
|
||||
err = appendElemToSlice(slicePtrValue, destinationStructPtr)
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func mapRowToBaseTypeSlice(scanContext *ScanContext, slicePtrValue reflect.Value, field *reflect.StructField) (updated bool, err error) {
|
||||
index := 0
|
||||
if field != nil {
|
||||
typeName, columnName := getTypeAndFieldName("", *field)
|
||||
if index = scanContext.typeToColumnIndex(typeName, columnName); index < 0 {
|
||||
return
|
||||
}
|
||||
}
|
||||
rowElemPtr := scanContext.rowElemValueClonePtr(index)
|
||||
|
||||
if rowElemPtr.IsValid() && !rowElemPtr.IsNil() {
|
||||
updated = true
|
||||
err = appendElemToSlice(slicePtrValue, rowElemPtr)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func mapRowToStruct(
|
||||
scanContext *ScanContext,
|
||||
groupKey string,
|
||||
structPtrValue reflect.Value,
|
||||
parentField *reflect.StructField,
|
||||
onlySlices ...bool, // small optimization, not to assign to already assigned struct fields
|
||||
) (updated bool, err error) {
|
||||
|
||||
mapOnlySlices := len(onlySlices) > 0
|
||||
structType := structPtrValue.Type().Elem()
|
||||
|
||||
if scanContext.typesVisited.contains(&structType) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
scanContext.typesVisited.push(&structType)
|
||||
defer scanContext.typesVisited.pop()
|
||||
|
||||
typeInf := scanContext.getTypeInfo(structType, parentField)
|
||||
|
||||
structValue := structPtrValue.Elem()
|
||||
|
||||
for i := 0; i < structValue.NumField(); i++ {
|
||||
field := structType.Field(i)
|
||||
fieldValue := structValue.Field(i)
|
||||
|
||||
if !fieldValue.CanSet() { // private field
|
||||
continue
|
||||
}
|
||||
|
||||
fieldMap := typeInf.fieldMappings[i]
|
||||
|
||||
if fieldMap.complexType {
|
||||
var changed bool
|
||||
changed, err = mapRowToDestinationValue(scanContext, concat(groupKey, ":", field.Name), fieldValue, &field)
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if changed {
|
||||
updated = true
|
||||
}
|
||||
|
||||
} else {
|
||||
if mapOnlySlices || fieldMap.rowIndex == -1 {
|
||||
continue
|
||||
}
|
||||
|
||||
scannedValue := scanContext.rowElemValue(fieldMap.rowIndex)
|
||||
|
||||
if !scannedValue.IsValid() {
|
||||
setZeroValue(fieldValue) // scannedValue is nil, destination should be set to zero value
|
||||
continue
|
||||
}
|
||||
|
||||
updated = true
|
||||
|
||||
if fieldMap.implementsScanner {
|
||||
initializeValueIfNilPtr(fieldValue)
|
||||
fieldScanner := getScanner(fieldValue)
|
||||
|
||||
value := scannedValue.Interface()
|
||||
|
||||
err := fieldScanner.Scan(value)
|
||||
|
||||
if err != nil {
|
||||
return updated, fmt.Errorf(`can't scan %T(%q) to '%s %s': %w`, value, value, field.Name, field.Type.String(), err)
|
||||
}
|
||||
} else {
|
||||
err := assign(scannedValue, fieldValue)
|
||||
|
||||
if err != nil {
|
||||
return updated, fmt.Errorf(`can't assign %T(%q) to '%s %s': %w`, scannedValue.Interface(), scannedValue.Interface(),
|
||||
field.Name, field.Type.String(), err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func mapRowToDestinationValue(
|
||||
scanContext *ScanContext,
|
||||
groupKey string,
|
||||
dest reflect.Value,
|
||||
structField *reflect.StructField) (updated bool, err error) {
|
||||
|
||||
var destPtrValue reflect.Value
|
||||
|
||||
if dest.Kind() != reflect.Ptr {
|
||||
destPtrValue = dest.Addr()
|
||||
} else {
|
||||
if dest.IsNil() {
|
||||
destPtrValue = reflect.New(dest.Type().Elem())
|
||||
} else {
|
||||
destPtrValue = dest
|
||||
}
|
||||
}
|
||||
|
||||
updated, err = mapRowToDestinationPtr(scanContext, groupKey, destPtrValue, structField)
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if dest.Kind() == reflect.Ptr && dest.IsNil() && updated {
|
||||
dest.Set(destPtrValue)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func mapRowToDestinationPtr(
|
||||
scanContext *ScanContext,
|
||||
groupKey string,
|
||||
destPtrValue reflect.Value,
|
||||
structField *reflect.StructField) (updated bool, err error) {
|
||||
|
||||
must.ValueBeOfTypeKind(destPtrValue, reflect.Ptr, "jet: internal error. Destination is not pointer.")
|
||||
|
||||
destValueKind := destPtrValue.Elem().Kind()
|
||||
|
||||
if destValueKind == reflect.Struct {
|
||||
return mapRowToStruct(scanContext, groupKey, destPtrValue, structField)
|
||||
} else if destValueKind == reflect.Slice {
|
||||
return mapRowToSlice(scanContext, groupKey, destPtrValue, structField)
|
||||
} else {
|
||||
panic("jet: unsupported dest type: " + structField.Name + " " + structField.Type.String())
|
||||
}
|
||||
}
|
||||
265
vendor/github.com/go-jet/jet/v2/qrm/scan_context.go
generated
vendored
Normal file
265
vendor/github.com/go-jet/jet/v2/qrm/scan_context.go
generated
vendored
Normal file
@@ -0,0 +1,265 @@
|
||||
package qrm
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// ScanContext contains information about current row processed, mapping from the row to the
|
||||
// destination types and type grouping information.
|
||||
type ScanContext struct {
|
||||
rowNum int64
|
||||
row []interface{}
|
||||
uniqueDestObjectsMap map[string]int
|
||||
commonIdentToColumnIndex map[string]int
|
||||
groupKeyInfoCache map[string]groupKeyInfo
|
||||
typeInfoMap map[string]typeInfo
|
||||
|
||||
typesVisited typeStack // to prevent circular dependency scan
|
||||
}
|
||||
|
||||
// NewScanContext creates new ScanContext from rows
|
||||
func NewScanContext(rows *sql.Rows) (*ScanContext, error) {
|
||||
aliases, err := rows.Columns()
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
columnTypes, err := rows.ColumnTypes()
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
commonIdentToColumnIndex := map[string]int{}
|
||||
|
||||
for i, alias := range aliases {
|
||||
names := strings.SplitN(alias, ".", 2)
|
||||
commonIdentifier := toCommonIdentifier(names[0])
|
||||
|
||||
if len(names) > 1 {
|
||||
commonIdentifier = concat(commonIdentifier, ".", toCommonIdentifier(names[1]))
|
||||
}
|
||||
|
||||
commonIdentToColumnIndex[commonIdentifier] = i
|
||||
}
|
||||
|
||||
return &ScanContext{
|
||||
row: createScanSlice(len(columnTypes)),
|
||||
uniqueDestObjectsMap: make(map[string]int),
|
||||
|
||||
groupKeyInfoCache: make(map[string]groupKeyInfo),
|
||||
commonIdentToColumnIndex: commonIdentToColumnIndex,
|
||||
|
||||
typeInfoMap: make(map[string]typeInfo),
|
||||
|
||||
typesVisited: newTypeStack(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func createScanSlice(columnCount int) []interface{} {
|
||||
scanPtrSlice := make([]interface{}, columnCount)
|
||||
|
||||
for i := range scanPtrSlice {
|
||||
var a interface{}
|
||||
scanPtrSlice[i] = &a // if destination is pointer to interface sql.Scan will just forward driver value
|
||||
}
|
||||
|
||||
return scanPtrSlice
|
||||
}
|
||||
|
||||
type typeInfo struct {
|
||||
fieldMappings []fieldMapping
|
||||
}
|
||||
|
||||
type fieldMapping struct {
|
||||
complexType bool // slice and struct are complex types
|
||||
rowIndex int // index in ScanContext.row
|
||||
implementsScanner bool
|
||||
}
|
||||
|
||||
func (s *ScanContext) getTypeInfo(structType reflect.Type, parentField *reflect.StructField) typeInfo {
|
||||
|
||||
typeMapKey := structType.String()
|
||||
|
||||
if parentField != nil {
|
||||
typeMapKey = concat(typeMapKey, string(parentField.Tag))
|
||||
}
|
||||
|
||||
if typeInfo, ok := s.typeInfoMap[typeMapKey]; ok {
|
||||
return typeInfo
|
||||
}
|
||||
|
||||
typeName := getTypeName(structType, parentField)
|
||||
|
||||
newTypeInfo := typeInfo{}
|
||||
|
||||
for i := 0; i < structType.NumField(); i++ {
|
||||
field := structType.Field(i)
|
||||
|
||||
newTypeName, fieldName := getTypeAndFieldName(typeName, field)
|
||||
columnIndex := s.typeToColumnIndex(newTypeName, fieldName)
|
||||
|
||||
fieldMap := fieldMapping{
|
||||
rowIndex: columnIndex,
|
||||
}
|
||||
|
||||
if implementsScannerType(field.Type) {
|
||||
fieldMap.implementsScanner = true
|
||||
} else if !isSimpleModelType(field.Type) {
|
||||
fieldMap.complexType = true
|
||||
}
|
||||
|
||||
newTypeInfo.fieldMappings = append(newTypeInfo.fieldMappings, fieldMap)
|
||||
}
|
||||
|
||||
s.typeInfoMap[typeMapKey] = newTypeInfo
|
||||
|
||||
return newTypeInfo
|
||||
}
|
||||
|
||||
type groupKeyInfo struct {
|
||||
typeName string
|
||||
pkIndexes []int
|
||||
subTypes []groupKeyInfo
|
||||
}
|
||||
|
||||
func (s *ScanContext) getGroupKey(structType reflect.Type, structField *reflect.StructField) string {
|
||||
|
||||
mapKey := structType.Name()
|
||||
|
||||
if structField != nil {
|
||||
mapKey = concat(mapKey, structField.Type.String(), string(structField.Tag))
|
||||
}
|
||||
|
||||
if groupKeyInfo, ok := s.groupKeyInfoCache[mapKey]; ok {
|
||||
return s.constructGroupKey(groupKeyInfo)
|
||||
}
|
||||
|
||||
tempTypeStack := newTypeStack()
|
||||
groupKeyInfo := s.getGroupKeyInfo(structType, structField, &tempTypeStack)
|
||||
|
||||
s.groupKeyInfoCache[mapKey] = groupKeyInfo
|
||||
|
||||
return s.constructGroupKey(groupKeyInfo)
|
||||
}
|
||||
|
||||
func (s *ScanContext) constructGroupKey(groupKeyInfo groupKeyInfo) string {
|
||||
if len(groupKeyInfo.pkIndexes) == 0 && len(groupKeyInfo.subTypes) == 0 {
|
||||
return fmt.Sprintf("|ROW:%d|", s.rowNum)
|
||||
}
|
||||
|
||||
var groupKeys []string
|
||||
|
||||
for _, index := range groupKeyInfo.pkIndexes {
|
||||
groupKeys = append(groupKeys, s.rowElemToString(index))
|
||||
}
|
||||
|
||||
var subTypesGroupKeys []string
|
||||
for _, subType := range groupKeyInfo.subTypes {
|
||||
subTypesGroupKeys = append(subTypesGroupKeys, s.constructGroupKey(subType))
|
||||
}
|
||||
|
||||
return concat(groupKeyInfo.typeName, "(", strings.Join(groupKeys, ","), strings.Join(subTypesGroupKeys, ","), ")")
|
||||
}
|
||||
|
||||
func (s *ScanContext) getGroupKeyInfo(
|
||||
structType reflect.Type,
|
||||
parentField *reflect.StructField,
|
||||
typeVisited *typeStack) groupKeyInfo {
|
||||
|
||||
ret := groupKeyInfo{typeName: structType.Name()}
|
||||
|
||||
if typeVisited.contains(&structType) {
|
||||
return ret
|
||||
}
|
||||
|
||||
typeVisited.push(&structType)
|
||||
defer typeVisited.pop()
|
||||
|
||||
typeName := getTypeName(structType, parentField)
|
||||
primaryKeyOverwrites := parentFieldPrimaryKeyOverwrite(parentField)
|
||||
|
||||
for i := 0; i < structType.NumField(); i++ {
|
||||
field := structType.Field(i)
|
||||
fieldType := indirectType(field.Type)
|
||||
|
||||
if isPrimaryKey(field, primaryKeyOverwrites) {
|
||||
newTypeName, fieldName := getTypeAndFieldName(typeName, field)
|
||||
|
||||
pkIndex := s.typeToColumnIndex(newTypeName, fieldName)
|
||||
|
||||
if pkIndex < 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
ret.pkIndexes = append(ret.pkIndexes, pkIndex)
|
||||
|
||||
} else if fieldType.Kind() == reflect.Struct && fieldType != timeType {
|
||||
|
||||
subType := s.getGroupKeyInfo(fieldType, &field, typeVisited)
|
||||
|
||||
if len(subType.pkIndexes) != 0 || len(subType.subTypes) != 0 {
|
||||
ret.subTypes = append(ret.subTypes, subType)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
func (s *ScanContext) typeToColumnIndex(typeName, fieldName string) int {
|
||||
var key string
|
||||
|
||||
if typeName != "" {
|
||||
key = strings.ToLower(typeName + "." + fieldName)
|
||||
} else {
|
||||
key = strings.ToLower(fieldName)
|
||||
}
|
||||
|
||||
index, ok := s.commonIdentToColumnIndex[key]
|
||||
|
||||
if !ok {
|
||||
return -1
|
||||
}
|
||||
|
||||
return index
|
||||
}
|
||||
|
||||
// rowElemValue always returns non-ptr value,
|
||||
// invalid value is nil
|
||||
func (s *ScanContext) rowElemValue(index int) reflect.Value {
|
||||
scannedValue := reflect.ValueOf(s.row[index])
|
||||
return scannedValue.Elem().Elem() // no need to check validity of Elem, because s.row[index] always contains interface in interface
|
||||
}
|
||||
|
||||
func (s *ScanContext) rowElemToString(index int) string {
|
||||
value := s.rowElemValue(index)
|
||||
|
||||
if !value.IsValid() {
|
||||
return "nil"
|
||||
}
|
||||
|
||||
valueInterface := value.Interface()
|
||||
|
||||
if t, ok := valueInterface.(fmt.Stringer); ok {
|
||||
return t.String()
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%#v", valueInterface)
|
||||
}
|
||||
|
||||
func (s *ScanContext) rowElemValueClonePtr(index int) reflect.Value {
|
||||
rowElemValue := s.rowElemValue(index)
|
||||
|
||||
if !rowElemValue.IsValid() {
|
||||
return reflect.Value{}
|
||||
}
|
||||
|
||||
newElem := reflect.New(rowElemValue.Type())
|
||||
newElem.Elem().Set(rowElemValue)
|
||||
return newElem
|
||||
}
|
||||
40
vendor/github.com/go-jet/jet/v2/qrm/type_stack.go
generated
vendored
Normal file
40
vendor/github.com/go-jet/jet/v2/qrm/type_stack.go
generated
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
package qrm
|
||||
|
||||
import "reflect"
|
||||
|
||||
type typeStack []*reflect.Type
|
||||
|
||||
func newTypeStack() typeStack {
|
||||
stack := make(typeStack, 0, 20)
|
||||
return stack
|
||||
}
|
||||
|
||||
func (s *typeStack) isEmpty() bool {
|
||||
return len(*s) == 0
|
||||
}
|
||||
|
||||
func (s *typeStack) push(t *reflect.Type) {
|
||||
*s = append(*s, t)
|
||||
}
|
||||
|
||||
func (s *typeStack) pop() bool {
|
||||
if s.isEmpty() {
|
||||
return false
|
||||
}
|
||||
*s = (*s)[:len(*s)-1]
|
||||
return true
|
||||
}
|
||||
|
||||
func (s *typeStack) contains(t *reflect.Type) bool {
|
||||
if s.isEmpty() {
|
||||
return false
|
||||
}
|
||||
|
||||
for _, typ := range *s {
|
||||
if *typ == *t {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
376
vendor/github.com/go-jet/jet/v2/qrm/utill.go
generated
vendored
Normal file
376
vendor/github.com/go-jet/jet/v2/qrm/utill.go
generated
vendored
Normal file
@@ -0,0 +1,376 @@
|
||||
package qrm
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"github.com/go-jet/jet/v2/internal/utils/must"
|
||||
"github.com/go-jet/jet/v2/internal/utils/strslice"
|
||||
"github.com/go-jet/jet/v2/qrm/internal"
|
||||
"github.com/google/uuid"
|
||||
"reflect"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
var scannerInterfaceType = reflect.TypeOf((*sql.Scanner)(nil)).Elem()
|
||||
|
||||
func implementsScannerType(fieldType reflect.Type) bool {
|
||||
if fieldType.Implements(scannerInterfaceType) {
|
||||
return true
|
||||
}
|
||||
|
||||
fieldTypePtr := reflect.New(fieldType).Type()
|
||||
|
||||
return fieldTypePtr.Implements(scannerInterfaceType)
|
||||
}
|
||||
|
||||
func getScanner(value reflect.Value) sql.Scanner {
|
||||
if scanner, ok := value.Interface().(sql.Scanner); ok {
|
||||
return scanner
|
||||
}
|
||||
|
||||
return value.Addr().Interface().(sql.Scanner)
|
||||
}
|
||||
|
||||
func getSliceElemType(slicePtrValue reflect.Value) reflect.Type {
|
||||
sliceTypePtr := slicePtrValue.Type()
|
||||
elemType := indirectType(sliceTypePtr).Elem()
|
||||
|
||||
return indirectType(elemType)
|
||||
}
|
||||
|
||||
func getSliceElemPtrAt(slicePtrValue reflect.Value, index int) reflect.Value {
|
||||
sliceValue := slicePtrValue.Elem()
|
||||
elem := sliceValue.Index(index)
|
||||
|
||||
if elem.Kind() == reflect.Ptr {
|
||||
return elem
|
||||
}
|
||||
|
||||
return elem.Addr()
|
||||
}
|
||||
|
||||
func appendElemToSlice(slicePtrValue reflect.Value, objPtrValue reflect.Value) error {
|
||||
must.BeTrue(!slicePtrValue.IsNil(), "jet: internal, slice is nil")
|
||||
|
||||
sliceValue := slicePtrValue.Elem()
|
||||
sliceElemType := sliceValue.Type().Elem()
|
||||
|
||||
var newSliceElemValue reflect.Value
|
||||
|
||||
if objPtrValue.Type().AssignableTo(sliceElemType) {
|
||||
newSliceElemValue = objPtrValue
|
||||
} else if objPtrValue.Elem().Type().AssignableTo(sliceElemType) {
|
||||
newSliceElemValue = objPtrValue.Elem()
|
||||
} else {
|
||||
newSliceElemValue = reflect.New(sliceElemType).Elem()
|
||||
|
||||
var err error
|
||||
|
||||
if newSliceElemValue.Kind() == reflect.Ptr {
|
||||
newSliceElemValue.Set(reflect.New(newSliceElemValue.Type().Elem()))
|
||||
err = assign(objPtrValue.Elem(), newSliceElemValue.Elem())
|
||||
} else {
|
||||
err = assign(objPtrValue.Elem(), newSliceElemValue)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("can't append %T to %T slice: %w", objPtrValue.Elem().Interface(), sliceValue.Interface(), err)
|
||||
}
|
||||
}
|
||||
|
||||
sliceValue.Set(reflect.Append(sliceValue, newSliceElemValue))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func newElemPtrValueForSlice(slicePtrValue reflect.Value) reflect.Value {
|
||||
destinationSliceType := slicePtrValue.Type().Elem()
|
||||
elemType := indirectType(destinationSliceType.Elem())
|
||||
|
||||
return reflect.New(elemType)
|
||||
}
|
||||
|
||||
func getTypeName(structType reflect.Type, parentField *reflect.StructField) string {
|
||||
if parentField == nil {
|
||||
return structType.Name()
|
||||
}
|
||||
|
||||
aliasTag := parentField.Tag.Get("alias")
|
||||
|
||||
if aliasTag == "" {
|
||||
return structType.Name()
|
||||
}
|
||||
|
||||
aliasParts := strings.Split(aliasTag, ".")
|
||||
|
||||
return toCommonIdentifier(aliasParts[0])
|
||||
}
|
||||
|
||||
func getTypeAndFieldName(structType string, field reflect.StructField) (string, string) {
|
||||
aliasTag := field.Tag.Get("alias")
|
||||
|
||||
if aliasTag == "" {
|
||||
return structType, field.Name
|
||||
}
|
||||
|
||||
aliasParts := strings.Split(aliasTag, ".")
|
||||
|
||||
if len(aliasParts) == 1 {
|
||||
return structType, toCommonIdentifier(aliasParts[0])
|
||||
}
|
||||
|
||||
return toCommonIdentifier(aliasParts[0]), toCommonIdentifier(aliasParts[1])
|
||||
}
|
||||
|
||||
var replacer = strings.NewReplacer(" ", "", "-", "", "_", "")
|
||||
|
||||
func toCommonIdentifier(name string) string {
|
||||
return strings.ToLower(replacer.Replace(name))
|
||||
}
|
||||
|
||||
func initializeValueIfNilPtr(value reflect.Value) {
|
||||
if !value.IsValid() || !value.CanSet() {
|
||||
return
|
||||
}
|
||||
|
||||
if value.Kind() == reflect.Ptr && value.IsNil() {
|
||||
value.Set(reflect.New(value.Type().Elem()))
|
||||
}
|
||||
}
|
||||
|
||||
var timeType = reflect.TypeOf(time.Now())
|
||||
var uuidType = reflect.TypeOf(uuid.New())
|
||||
var byteArrayType = reflect.TypeOf([]byte(""))
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
return objType == timeType || objType == uuidType || objType == byteArrayType
|
||||
}
|
||||
|
||||
// source can't be pointer
|
||||
// destination can be pointer
|
||||
func assign(source, destination reflect.Value) error {
|
||||
if destination.Kind() == reflect.Ptr {
|
||||
if destination.IsNil() {
|
||||
initializeValueIfNilPtr(destination)
|
||||
}
|
||||
|
||||
destination = destination.Elem()
|
||||
}
|
||||
|
||||
err := tryAssign(source, destination)
|
||||
|
||||
if err != nil {
|
||||
// needs for the type conversions are rare, so we leave conversion as a last assign step if everything else fails
|
||||
if tryConvert(source, destination) {
|
||||
return nil
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func assignIfAssignable(source, destination reflect.Value) bool {
|
||||
sourceType := source.Type()
|
||||
if sourceType.AssignableTo(destination.Type()) {
|
||||
switch sourceType {
|
||||
case byteArrayType:
|
||||
destination.SetBytes(cloneBytes(source.Interface().([]byte)))
|
||||
default:
|
||||
destination.Set(source)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// source and destination are non-ptr values
|
||||
func tryAssign(source, destination reflect.Value) error {
|
||||
|
||||
if assignIfAssignable(source, destination) {
|
||||
return nil
|
||||
}
|
||||
|
||||
sourceInterface := source.Interface()
|
||||
|
||||
switch destination.Type().Kind() {
|
||||
case reflect.Bool:
|
||||
var nullBool internal.NullBool
|
||||
|
||||
err := nullBool.Scan(sourceInterface)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
destination.SetBool(nullBool.Bool)
|
||||
|
||||
case reflect.Float32, reflect.Float64:
|
||||
var nullFloat sql.NullFloat64
|
||||
|
||||
err := nullFloat.Scan(sourceInterface)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if nullFloat.Valid {
|
||||
destination.SetFloat(nullFloat.Float64)
|
||||
}
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
var integer sql.NullInt64
|
||||
|
||||
err := integer.Scan(sourceInterface)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if integer.Valid {
|
||||
destination.SetInt(integer.Int64)
|
||||
}
|
||||
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
var uInt internal.NullUInt64
|
||||
|
||||
err := uInt.Scan(sourceInterface)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if uInt.Valid {
|
||||
destination.SetUint(uInt.UInt64)
|
||||
}
|
||||
|
||||
case reflect.String:
|
||||
var str sql.NullString
|
||||
|
||||
err := str.Scan(sourceInterface)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if str.Valid {
|
||||
destination.SetString(str.String)
|
||||
}
|
||||
|
||||
default:
|
||||
switch destination.Interface().(type) {
|
||||
case time.Time:
|
||||
var nullTime internal.NullTime
|
||||
|
||||
err := nullTime.Scan(sourceInterface)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if nullTime.Valid {
|
||||
destination.Set(reflect.ValueOf(nullTime.Time))
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("can't assign %T to %T", sourceInterface, destination.Interface())
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func tryConvert(source, destination reflect.Value) bool {
|
||||
destinationType := destination.Type()
|
||||
|
||||
if source.Type().ConvertibleTo(destinationType) {
|
||||
source = source.Convert(destinationType)
|
||||
return assignIfAssignable(source, destination)
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func setZeroValue(value reflect.Value) {
|
||||
if !value.IsZero() {
|
||||
value.Set(reflect.Zero(value.Type()))
|
||||
}
|
||||
}
|
||||
|
||||
func isPrimaryKey(field reflect.StructField, primaryKeyOverwrites []string) bool {
|
||||
if len(primaryKeyOverwrites) > 0 {
|
||||
return strslice.Contains(primaryKeyOverwrites, field.Name)
|
||||
}
|
||||
|
||||
sqlTag := field.Tag.Get("sql")
|
||||
|
||||
return sqlTag == "primary_key"
|
||||
}
|
||||
|
||||
func parentFieldPrimaryKeyOverwrite(parentField *reflect.StructField) []string {
|
||||
if parentField == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
sqlTag := parentField.Tag.Get("sql")
|
||||
|
||||
if !strings.HasPrefix(sqlTag, "primary_key") {
|
||||
return nil
|
||||
}
|
||||
|
||||
parts := strings.Split(sqlTag, "=")
|
||||
|
||||
if len(parts) < 2 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return strings.Split(parts[1], ",")
|
||||
}
|
||||
|
||||
func indirectType(reflectType reflect.Type) reflect.Type {
|
||||
if reflectType.Kind() != reflect.Ptr {
|
||||
return reflectType
|
||||
}
|
||||
return reflectType.Elem()
|
||||
}
|
||||
|
||||
func fieldToString(field *reflect.StructField) string {
|
||||
if field == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
return " at '" + field.Name + " " + field.Type.String() + "'"
|
||||
}
|
||||
|
||||
func cloneBytes(b []byte) []byte {
|
||||
if b == nil {
|
||||
return nil
|
||||
}
|
||||
c := make([]byte, len(b))
|
||||
copy(c, b)
|
||||
return c
|
||||
}
|
||||
|
||||
func concat(stringList ...string) string {
|
||||
var b strings.Builder
|
||||
|
||||
var length int
|
||||
for i := 0; i < len(stringList); i++ {
|
||||
length += len(stringList[i])
|
||||
}
|
||||
|
||||
b.Grow(length)
|
||||
|
||||
for _, str := range stringList {
|
||||
b.WriteString(str)
|
||||
}
|
||||
return b.String()
|
||||
}
|
||||
Reference in New Issue
Block a user