2019-10-11 10:15:36 +02:00
|
|
|
package qrm
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"database/sql"
|
2019-10-12 18:45:09 +02:00
|
|
|
"fmt"
|
2019-10-11 10:15:36 +02:00
|
|
|
"reflect"
|
|
|
|
|
"strings"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
type scanContext struct {
|
2019-10-12 18:45:09 +02:00
|
|
|
rowNum int64
|
|
|
|
|
row []interface{}
|
|
|
|
|
uniqueDestObjectsMap map[string]int
|
|
|
|
|
commonIdentToColumnIndex map[string]int
|
|
|
|
|
groupKeyInfoCache map[string]groupKeyInfo
|
|
|
|
|
typeInfoMap map[string]typeInfo
|
2019-10-11 10:15:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-12 18:45:09 +02:00
|
|
|
commonIdentToColumnIndex := map[string]int{}
|
2019-10-11 10:15:36 +02:00
|
|
|
|
|
|
|
|
for i, alias := range aliases {
|
|
|
|
|
names := strings.SplitN(alias, ".", 2)
|
2019-10-12 18:45:09 +02:00
|
|
|
commonIdentifier := toCommonIdentifier(names[0])
|
2019-10-11 10:15:36 +02:00
|
|
|
|
|
|
|
|
if len(names) > 1 {
|
2019-10-12 18:45:09 +02:00
|
|
|
commonIdentifier += "." + toCommonIdentifier(names[1])
|
2019-10-11 10:15:36 +02:00
|
|
|
}
|
|
|
|
|
|
2019-10-12 18:45:09 +02:00
|
|
|
commonIdentToColumnIndex[commonIdentifier] = i
|
2019-10-11 10:15:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return &scanContext{
|
2021-10-15 17:43:10 +02:00
|
|
|
row: createScanSlice(len(columnTypes)),
|
2019-10-11 10:15:36 +02:00
|
|
|
uniqueDestObjectsMap: make(map[string]int),
|
|
|
|
|
|
2019-10-12 18:45:09 +02:00
|
|
|
groupKeyInfoCache: make(map[string]groupKeyInfo),
|
|
|
|
|
commonIdentToColumnIndex: commonIdentToColumnIndex,
|
2019-10-11 10:15:36 +02:00
|
|
|
|
|
|
|
|
typeInfoMap: make(map[string]typeInfo),
|
|
|
|
|
}, nil
|
|
|
|
|
}
|
|
|
|
|
|
2021-10-15 17:43:10 +02:00
|
|
|
func createScanSlice(columnCount int) []interface{} {
|
|
|
|
|
scanSlice := make([]interface{}, columnCount)
|
|
|
|
|
scanPtrSlice := make([]interface{}, columnCount)
|
|
|
|
|
|
|
|
|
|
for i := range scanPtrSlice {
|
|
|
|
|
scanPtrSlice[i] = &scanSlice[i] // if destination is pointer to interface sql.Scan will just forward driver value
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return scanPtrSlice
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-11 10:15:36 +02:00
|
|
|
type typeInfo struct {
|
|
|
|
|
fieldMappings []fieldMapping
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type fieldMapping struct {
|
2019-10-12 18:45:09 +02:00
|
|
|
complexType bool // slice or struct
|
2019-10-11 10:15:36 +02:00
|
|
|
columnIndex int
|
|
|
|
|
implementsScanner bool
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *scanContext) getTypeInfo(structType reflect.Type, parentField *reflect.StructField) typeInfo {
|
|
|
|
|
|
|
|
|
|
typeMapKey := structType.String()
|
|
|
|
|
|
|
|
|
|
if parentField != nil {
|
|
|
|
|
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{
|
|
|
|
|
columnIndex: 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
|
|
|
|
|
indexes []int
|
|
|
|
|
subTypes []groupKeyInfo
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *scanContext) getGroupKey(structType reflect.Type, structField *reflect.StructField) string {
|
|
|
|
|
|
|
|
|
|
mapKey := structType.Name()
|
|
|
|
|
|
|
|
|
|
if structField != nil {
|
|
|
|
|
mapKey += structField.Type.String()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if groupKeyInfo, ok := s.groupKeyInfoCache[mapKey]; ok {
|
|
|
|
|
return s.constructGroupKey(groupKeyInfo)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
groupKeyInfo := s.getGroupKeyInfo(structType, structField)
|
|
|
|
|
|
|
|
|
|
s.groupKeyInfoCache[mapKey] = groupKeyInfo
|
|
|
|
|
|
|
|
|
|
return s.constructGroupKey(groupKeyInfo)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *scanContext) constructGroupKey(groupKeyInfo groupKeyInfo) string {
|
|
|
|
|
if len(groupKeyInfo.indexes) == 0 && len(groupKeyInfo.subTypes) == 0 {
|
2019-10-12 18:45:09 +02:00
|
|
|
return fmt.Sprintf("|ROW:%d|", s.rowNum)
|
2019-10-11 10:15:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
groupKeys := []string{}
|
|
|
|
|
|
|
|
|
|
for _, index := range groupKeyInfo.indexes {
|
|
|
|
|
cellValue := s.rowElem(index)
|
|
|
|
|
subKey := valueToString(reflect.ValueOf(cellValue))
|
|
|
|
|
|
|
|
|
|
groupKeys = append(groupKeys, subKey)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
subTypesGroupKeys := []string{}
|
|
|
|
|
for _, subType := range groupKeyInfo.subTypes {
|
|
|
|
|
subTypesGroupKeys = append(subTypesGroupKeys, s.constructGroupKey(subType))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return groupKeyInfo.typeName + "(" + strings.Join(groupKeys, ",") + strings.Join(subTypesGroupKeys, ",") + ")"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *scanContext) getGroupKeyInfo(structType reflect.Type, parentField *reflect.StructField) groupKeyInfo {
|
|
|
|
|
ret := groupKeyInfo{typeName: structType.Name()}
|
|
|
|
|
|
|
|
|
|
typeName := getTypeName(structType, parentField)
|
|
|
|
|
primaryKeyOverwrites := parentFieldPrimaryKeyOverwrite(parentField)
|
|
|
|
|
|
|
|
|
|
for i := 0; i < structType.NumField(); i++ {
|
|
|
|
|
field := structType.Field(i)
|
|
|
|
|
fieldType := indirectType(field.Type)
|
|
|
|
|
|
|
|
|
|
if !isSimpleModelType(fieldType) {
|
|
|
|
|
if fieldType.Kind() != reflect.Struct {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
subType := s.getGroupKeyInfo(fieldType, &field)
|
|
|
|
|
|
|
|
|
|
if len(subType.indexes) != 0 || len(subType.subTypes) != 0 {
|
|
|
|
|
ret.subTypes = append(ret.subTypes, subType)
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if isPrimaryKey(field, primaryKeyOverwrites) {
|
|
|
|
|
newTypeName, fieldName := getTypeAndFieldName(typeName, field)
|
|
|
|
|
|
|
|
|
|
index := s.typeToColumnIndex(newTypeName, fieldName)
|
|
|
|
|
|
|
|
|
|
if index < 0 {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ret.indexes = append(ret.indexes, index)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-12 18:45:09 +02:00
|
|
|
index, ok := s.commonIdentToColumnIndex[key]
|
2019-10-11 10:15:36 +02:00
|
|
|
|
|
|
|
|
if !ok {
|
|
|
|
|
return -1
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return index
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *scanContext) rowElem(index int) interface{} {
|
2021-10-15 17:43:10 +02:00
|
|
|
cellValue := reflect.ValueOf(s.row[index])
|
2019-10-11 10:15:36 +02:00
|
|
|
|
2021-10-15 17:43:10 +02:00
|
|
|
if cellValue.IsValid() && !cellValue.IsNil() {
|
|
|
|
|
return cellValue.Elem().Interface()
|
|
|
|
|
}
|
2019-10-11 10:15:36 +02:00
|
|
|
|
2021-10-15 17:43:10 +02:00
|
|
|
return nil
|
2019-10-11 10:15:36 +02:00
|
|
|
}
|
|
|
|
|
|
2021-09-15 09:03:05 -04:00
|
|
|
func (s *scanContext) rowElemValuePtr(index int) reflect.Value {
|
2019-10-11 10:15:36 +02:00
|
|
|
rowElem := s.rowElem(index)
|
|
|
|
|
rowElemValue := reflect.ValueOf(rowElem)
|
|
|
|
|
|
2021-09-15 09:03:05 -04:00
|
|
|
if !rowElemValue.IsValid() {
|
|
|
|
|
return reflect.Value{}
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-11 10:15:36 +02:00
|
|
|
if rowElemValue.Kind() == reflect.Ptr {
|
2021-09-15 09:03:05 -04:00
|
|
|
return rowElemValue
|
2019-10-11 10:15:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if rowElemValue.CanAddr() {
|
2021-09-15 09:03:05 -04:00
|
|
|
return rowElemValue.Addr()
|
2019-10-11 10:15:36 +02:00
|
|
|
}
|
|
|
|
|
|
2021-09-15 09:03:05 -04:00
|
|
|
newElem := reflect.New(rowElemValue.Type())
|
|
|
|
|
newElem.Elem().Set(rowElemValue)
|
|
|
|
|
return newElem
|
2019-10-11 10:15:36 +02:00
|
|
|
}
|