jet/qrm/scan_context.go

261 lines
5.8 KiB
Go
Raw Normal View History

2019-10-11 10:15:36 +02:00
package qrm
import (
"database/sql"
"fmt"
2019-10-11 10:15:36 +02:00
"reflect"
"strings"
)
type scanContext struct {
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
}
commonIdentToColumnIndex := map[string]int{}
2019-10-11 10:15:36 +02:00
for i, alias := range aliases {
names := strings.SplitN(alias, ".", 2)
commonIdentifier := toCommonIdentifier(names[0])
2019-10-11 10:15:36 +02:00
if len(names) > 1 {
commonIdentifier += "." + toCommonIdentifier(names[1])
2019-10-11 10:15:36 +02:00
}
commonIdentToColumnIndex[commonIdentifier] = i
2019-10-11 10:15:36 +02:00
}
return &scanContext{
row: createScanSlice(len(columnTypes)),
2019-10-11 10:15:36 +02:00
uniqueDestObjectsMap: make(map[string]int),
groupKeyInfoCache: make(map[string]groupKeyInfo),
commonIdentToColumnIndex: commonIdentToColumnIndex,
2019-10-11 10:15:36 +02:00
typeInfoMap: make(map[string]typeInfo),
}, nil
}
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 {
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, newTypeStack())
2019-10-11 10:15:36 +02:00
s.groupKeyInfoCache[mapKey] = groupKeyInfo
return s.constructGroupKey(groupKeyInfo)
}
func (s *scanContext) constructGroupKey(groupKeyInfo groupKeyInfo) string {
if len(groupKeyInfo.indexes) == 0 && len(groupKeyInfo.subTypes) == 0 {
return fmt.Sprintf("|ROW:%d|", s.rowNum)
2019-10-11 10:15:36 +02:00
}
var groupKeys []string
2019-10-11 10:15:36 +02:00
for _, index := range groupKeyInfo.indexes {
cellValue := s.rowElem(index)
subKey := valueToString(reflect.ValueOf(cellValue))
groupKeys = append(groupKeys, subKey)
}
var subTypesGroupKeys []string
2019-10-11 10:15:36 +02:00
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,
typeVisited *typeStack) groupKeyInfo {
2019-10-11 10:15:36 +02:00
ret := groupKeyInfo{typeName: structType.Name()}
if typeVisited.contains(&structType) {
return ret
}
typeVisited.push(&structType)
defer typeVisited.pop()
2019-10-11 10:15:36 +02:00
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, typeVisited)
2019-10-11 10:15:36 +02:00
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)
}
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{} {
cellValue := reflect.ValueOf(s.row[index])
2019-10-11 10:15:36 +02:00
if cellValue.IsValid() && !cellValue.IsNil() {
return cellValue.Elem().Interface()
}
2019-10-11 10:15:36 +02:00
return nil
2019-10-11 10:15:36 +02: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)
if !rowElemValue.IsValid() {
return reflect.Value{}
}
2019-10-11 10:15:36 +02:00
if rowElemValue.Kind() == reflect.Ptr {
return rowElemValue
2019-10-11 10:15:36 +02:00
}
if rowElemValue.CanAddr() {
return rowElemValue.Addr()
2019-10-11 10:15:36 +02:00
}
newElem := reflect.New(rowElemValue.Type())
newElem.Elem().Set(rowElemValue)
return newElem
2019-10-11 10:15:36 +02:00
}