Additional scan performance improvements
Move typeStack to ScanContext, so it is shared between rows.Scan calls. Use string.Builder for string concatenations. Simplify value assign logic. Move convert value to the last assign step (needs for type conversions are rare).
This commit is contained in:
parent
c10244aeab
commit
c86903fd1d
8 changed files with 428 additions and 174 deletions
|
|
@ -16,6 +16,8 @@ type ScanContext struct {
|
|||
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
|
||||
|
|
@ -39,7 +41,7 @@ func NewScanContext(rows *sql.Rows) (*ScanContext, error) {
|
|||
commonIdentifier := toCommonIdentifier(names[0])
|
||||
|
||||
if len(names) > 1 {
|
||||
commonIdentifier += "." + toCommonIdentifier(names[1])
|
||||
commonIdentifier = concat(commonIdentifier, ".", toCommonIdentifier(names[1]))
|
||||
}
|
||||
|
||||
commonIdentToColumnIndex[commonIdentifier] = i
|
||||
|
|
@ -53,15 +55,17 @@ func NewScanContext(rows *sql.Rows) (*ScanContext, error) {
|
|||
commonIdentToColumnIndex: commonIdentToColumnIndex,
|
||||
|
||||
typeInfoMap: make(map[string]typeInfo),
|
||||
|
||||
typesVisited: newTypeStack(),
|
||||
}, 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
|
||||
var a interface{}
|
||||
scanPtrSlice[i] = &a // if destination is pointer to interface sql.Scan will just forward driver value
|
||||
}
|
||||
|
||||
return scanPtrSlice
|
||||
|
|
@ -72,8 +76,8 @@ type typeInfo struct {
|
|||
}
|
||||
|
||||
type fieldMapping struct {
|
||||
complexType bool // slice or struct
|
||||
columnIndex int
|
||||
complexType bool // slice and struct are complex types
|
||||
rowIndex int // index in ScanContext.row
|
||||
implementsScanner bool
|
||||
}
|
||||
|
||||
|
|
@ -82,7 +86,7 @@ func (s *ScanContext) getTypeInfo(structType reflect.Type, parentField *reflect.
|
|||
typeMapKey := structType.String()
|
||||
|
||||
if parentField != nil {
|
||||
typeMapKey += string(parentField.Tag)
|
||||
typeMapKey = concat(typeMapKey, string(parentField.Tag))
|
||||
}
|
||||
|
||||
if typeInfo, ok := s.typeInfoMap[typeMapKey]; ok {
|
||||
|
|
@ -100,7 +104,7 @@ func (s *ScanContext) getTypeInfo(structType reflect.Type, parentField *reflect.
|
|||
columnIndex := s.typeToColumnIndex(newTypeName, fieldName)
|
||||
|
||||
fieldMap := fieldMapping{
|
||||
columnIndex: columnIndex,
|
||||
rowIndex: columnIndex,
|
||||
}
|
||||
|
||||
if implementsScannerType(field.Type) {
|
||||
|
|
@ -128,14 +132,15 @@ func (s *ScanContext) getGroupKey(structType reflect.Type, structField *reflect.
|
|||
mapKey := structType.Name()
|
||||
|
||||
if structField != nil {
|
||||
mapKey += structField.Type.String()
|
||||
mapKey = concat(mapKey, structField.Type.String())
|
||||
}
|
||||
|
||||
if groupKeyInfo, ok := s.groupKeyInfoCache[mapKey]; ok {
|
||||
return s.constructGroupKey(groupKeyInfo)
|
||||
}
|
||||
|
||||
groupKeyInfo := s.getGroupKeyInfo(structType, structField, newTypeStack())
|
||||
tempTypeStack := newTypeStack()
|
||||
groupKeyInfo := s.getGroupKeyInfo(structType, structField, &tempTypeStack)
|
||||
|
||||
s.groupKeyInfoCache[mapKey] = groupKeyInfo
|
||||
|
||||
|
|
@ -150,10 +155,7 @@ func (s *ScanContext) constructGroupKey(groupKeyInfo groupKeyInfo) string {
|
|||
var groupKeys []string
|
||||
|
||||
for _, index := range groupKeyInfo.indexes {
|
||||
cellValue := s.rowElem(index)
|
||||
subKey := valueToString(reflect.ValueOf(cellValue))
|
||||
|
||||
groupKeys = append(groupKeys, subKey)
|
||||
groupKeys = append(groupKeys, s.rowElemToString(index))
|
||||
}
|
||||
|
||||
var subTypesGroupKeys []string
|
||||
|
|
@ -161,7 +163,7 @@ func (s *ScanContext) constructGroupKey(groupKeyInfo groupKeyInfo) string {
|
|||
subTypesGroupKeys = append(subTypesGroupKeys, s.constructGroupKey(subType))
|
||||
}
|
||||
|
||||
return groupKeyInfo.typeName + "(" + strings.Join(groupKeys, ",") + strings.Join(subTypesGroupKeys, ",") + ")"
|
||||
return concat(groupKeyInfo.typeName, "(", strings.Join(groupKeys, ","), strings.Join(subTypesGroupKeys, ","), ")")
|
||||
}
|
||||
|
||||
func (s *ScanContext) getGroupKeyInfo(
|
||||
|
|
@ -231,32 +233,36 @@ func (s *ScanContext) typeToColumnIndex(typeName, fieldName string) int {
|
|||
return index
|
||||
}
|
||||
|
||||
func (s *ScanContext) rowElem(index int) interface{} {
|
||||
cellValue := reflect.ValueOf(s.row[index])
|
||||
|
||||
if cellValue.IsValid() && !cellValue.IsNil() {
|
||||
return cellValue.Elem().Interface()
|
||||
}
|
||||
|
||||
return nil
|
||||
// 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) rowElemValuePtr(index int) reflect.Value {
|
||||
rowElem := s.rowElem(index)
|
||||
rowElemValue := reflect.ValueOf(rowElem)
|
||||
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{}
|
||||
}
|
||||
|
||||
if rowElemValue.Kind() == reflect.Ptr {
|
||||
return rowElemValue
|
||||
}
|
||||
|
||||
if rowElemValue.CanAddr() {
|
||||
return rowElemValue.Addr()
|
||||
}
|
||||
|
||||
newElem := reflect.New(rowElemValue.Type())
|
||||
newElem.Elem().Set(rowElemValue)
|
||||
return newElem
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue