From 70756b0eef4a636cbc810f629611eb519ab4a1ab Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Wed, 7 Jul 2021 12:16:07 +0800 Subject: [PATCH 1/2] refactor slice2Bean --- convert.go | 2 + session.go | 815 +++++++++++++++++++++++++++-------------------------- 2 files changed, 417 insertions(+), 400 deletions(-) diff --git a/convert.go b/convert.go index f7d733ad..67183098 100644 --- a/convert.go +++ b/convert.go @@ -238,6 +238,8 @@ func asBytes(buf []byte, rv reflect.Value) (b []byte, ok bool) { func convertAssign(dest, src interface{}, originalLocation *time.Location, convertedLocation *time.Location) error { // Common cases, without reflect. switch s := src.(type) { + case *interface{}: + return convertAssign(dest, *s, originalLocation, convertedLocation) case string: switch d := dest.(type) { case *string: diff --git a/session.go b/session.go index 6df9e20d..1e33541a 100644 --- a/session.go +++ b/session.go @@ -436,6 +436,412 @@ func (session *Session) row2Slice(rows *core.Rows, fields []string, bean interfa return scanResults, nil } +func (session *Session) convertBeanField(col *schemas.Column, fieldValue *reflect.Value, + scanResult interface{}, dataStruct *reflect.Value, table *schemas.Table) error { + rawValue := reflect.Indirect(reflect.ValueOf(scanResult)) + + // if row is null then ignore + if rawValue.Interface() == nil { + return nil + } + + if fieldValue.CanAddr() { + if structConvert, ok := fieldValue.Addr().Interface().(convert.Conversion); ok { + data, err := value2Bytes(&rawValue) + if err != nil { + return err + } + if err := structConvert.FromDB(data); err != nil { + return err + } + return nil + } + } + + if _, ok := fieldValue.Interface().(convert.Conversion); ok { + if data, err := value2Bytes(&rawValue); err == nil { + if fieldValue.Kind() == reflect.Ptr && fieldValue.IsNil() { + fieldValue.Set(reflect.New(fieldValue.Type().Elem())) + } + fieldValue.Interface().(convert.Conversion).FromDB(data) + } else { + return err + } + return nil + } + + rawValueType := reflect.TypeOf(rawValue.Interface()) + vv := reflect.ValueOf(rawValue.Interface()) + + fieldType := fieldValue.Type() + hasAssigned := false + + if col.IsJSON { + var bs []byte + if rawValueType.Kind() == reflect.String { + bs = []byte(vv.String()) + } else if rawValueType.ConvertibleTo(schemas.BytesType) { + bs = vv.Bytes() + } else { + return fmt.Errorf("unsupported database data type: %s %v", col.Name, rawValueType.Kind()) + } + + hasAssigned = true + + if len(bs) > 0 { + if fieldType.Kind() == reflect.String { + fieldValue.SetString(string(bs)) + return nil + } + if fieldValue.CanAddr() { + err := json.DefaultJSONHandler.Unmarshal(bs, fieldValue.Addr().Interface()) + if err != nil { + return err + } + } else { + x := reflect.New(fieldType) + err := json.DefaultJSONHandler.Unmarshal(bs, x.Interface()) + if err != nil { + return err + } + fieldValue.Set(x.Elem()) + } + } + + return nil + } + + switch fieldType.Kind() { + case reflect.Complex64, reflect.Complex128: + // TODO: reimplement this + var bs []byte + if rawValueType.Kind() == reflect.String { + bs = []byte(vv.String()) + } else if rawValueType.ConvertibleTo(schemas.BytesType) { + bs = vv.Bytes() + } + + hasAssigned = true + if len(bs) > 0 { + if fieldValue.CanAddr() { + err := json.DefaultJSONHandler.Unmarshal(bs, fieldValue.Addr().Interface()) + if err != nil { + return err + } + } else { + x := reflect.New(fieldType) + err := json.DefaultJSONHandler.Unmarshal(bs, x.Interface()) + if err != nil { + return err + } + fieldValue.Set(x.Elem()) + } + } + case reflect.Slice, reflect.Array: + switch rawValueType.Kind() { + case reflect.Slice, reflect.Array: + switch rawValueType.Elem().Kind() { + case reflect.Uint8: + if fieldType.Elem().Kind() == reflect.Uint8 { + hasAssigned = true + if col.SQLType.IsText() { + x := reflect.New(fieldType) + err := json.DefaultJSONHandler.Unmarshal(vv.Bytes(), x.Interface()) + if err != nil { + return err + } + fieldValue.Set(x.Elem()) + } else { + if fieldValue.Len() > 0 { + for i := 0; i < fieldValue.Len(); i++ { + if i < vv.Len() { + fieldValue.Index(i).Set(vv.Index(i)) + } + } + } else { + for i := 0; i < vv.Len(); i++ { + fieldValue.Set(reflect.Append(*fieldValue, vv.Index(i))) + } + } + } + } + } + } + case reflect.String: + if rawValueType.Kind() == reflect.String { + hasAssigned = true + fieldValue.SetString(vv.String()) + } + case reflect.Bool: + if rawValueType.Kind() == reflect.Bool { + hasAssigned = true + fieldValue.SetBool(vv.Bool()) + } + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + switch rawValueType.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + hasAssigned = true + fieldValue.SetInt(vv.Int()) + } + case reflect.Float32, reflect.Float64: + switch rawValueType.Kind() { + case reflect.Float32, reflect.Float64: + hasAssigned = true + fieldValue.SetFloat(vv.Float()) + } + case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint: + switch rawValueType.Kind() { + case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint: + hasAssigned = true + fieldValue.SetUint(vv.Uint()) + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + hasAssigned = true + fieldValue.SetUint(uint64(vv.Int())) + } + case reflect.Struct: + if fieldType.ConvertibleTo(schemas.TimeType) { + dbTZ := session.engine.DatabaseTZ + if col.TimeZone != nil { + dbTZ = col.TimeZone + } + + if rawValueType == schemas.TimeType { + hasAssigned = true + + t := vv.Convert(schemas.TimeType).Interface().(time.Time) + + z, _ := t.Zone() + // set new location if database don't save timezone or give an incorrect timezone + if len(z) == 0 || t.Year() == 0 || t.Location().String() != dbTZ.String() { // !nashtsai! HACK tmp work around for lib/pq doesn't properly time with location + session.engine.logger.Debugf("empty zone key[%v] : %v | zone: %v | location: %+v\n", col.Name, t, z, *t.Location()) + t = time.Date(t.Year(), t.Month(), t.Day(), t.Hour(), + t.Minute(), t.Second(), t.Nanosecond(), dbTZ) + } + + t = t.In(session.engine.TZLocation) + fieldValue.Set(reflect.ValueOf(t).Convert(fieldType)) + } else if rawValueType == schemas.IntType || rawValueType == schemas.Int64Type || + rawValueType == schemas.Int32Type { + hasAssigned = true + + t := time.Unix(vv.Int(), 0).In(session.engine.TZLocation) + fieldValue.Set(reflect.ValueOf(t).Convert(fieldType)) + } else { + if d, ok := vv.Interface().([]uint8); ok { + hasAssigned = true + t, err := session.byte2Time(col, d) + if err != nil { + session.engine.logger.Errorf("byte2Time error: %v", err) + hasAssigned = false + } else { + fieldValue.Set(reflect.ValueOf(t).Convert(fieldType)) + } + } else if d, ok := vv.Interface().(string); ok { + hasAssigned = true + t, err := session.str2Time(col, d) + if err != nil { + session.engine.logger.Errorf("byte2Time error: %v", err) + hasAssigned = false + } else { + fieldValue.Set(reflect.ValueOf(t).Convert(fieldType)) + } + } else { + return fmt.Errorf("rawValueType is %v, value is %v", rawValueType, vv.Interface()) + } + } + } else if nulVal, ok := fieldValue.Addr().Interface().(sql.Scanner); ok { + // !! 增加支持sql.Scanner接口的结构,如sql.NullString + hasAssigned = true + if err := nulVal.Scan(vv.Interface()); err != nil { + session.engine.logger.Errorf("sql.Sanner error: %v", err) + hasAssigned = false + } + } else if col.IsJSON { + if rawValueType.Kind() == reflect.String { + hasAssigned = true + x := reflect.New(fieldType) + if len([]byte(vv.String())) > 0 { + err := json.DefaultJSONHandler.Unmarshal([]byte(vv.String()), x.Interface()) + if err != nil { + return err + } + fieldValue.Set(x.Elem()) + } + } else if rawValueType.Kind() == reflect.Slice { + hasAssigned = true + x := reflect.New(fieldType) + if len(vv.Bytes()) > 0 { + err := json.DefaultJSONHandler.Unmarshal(vv.Bytes(), x.Interface()) + if err != nil { + return err + } + fieldValue.Set(x.Elem()) + } + } + } else if session.statement.UseCascade { + table, err := session.engine.tagParser.ParseWithCache(*fieldValue) + if err != nil { + return err + } + + hasAssigned = true + if len(table.PrimaryKeys) != 1 { + return errors.New("unsupported non or composited primary key cascade") + } + var pk = make(schemas.PK, len(table.PrimaryKeys)) + pk[0], err = asKind(vv, rawValueType) + if err != nil { + return err + } + + if !pk.IsZero() { + // !nashtsai! TODO for hasOne relationship, it's preferred to use join query for eager fetch + // however, also need to consider adding a 'lazy' attribute to xorm tag which allow hasOne + // property to be fetched lazily + structInter := reflect.New(fieldValue.Type()) + has, err := session.ID(pk).NoCascade().get(structInter.Interface()) + if err != nil { + return err + } + if has { + fieldValue.Set(structInter.Elem()) + } else { + return errors.New("cascade obj is not exist") + } + } + } + case reflect.Ptr: + // !nashtsai! TODO merge duplicated codes above + switch fieldType { + // following types case matching ptr's native type, therefore assign ptr directly + case schemas.PtrStringType: + if rawValueType.Kind() == reflect.String { + x := vv.String() + hasAssigned = true + fieldValue.Set(reflect.ValueOf(&x)) + } + case schemas.PtrBoolType: + if rawValueType.Kind() == reflect.Bool { + x := vv.Bool() + hasAssigned = true + fieldValue.Set(reflect.ValueOf(&x)) + } + case schemas.PtrTimeType: + if rawValueType == schemas.PtrTimeType { + hasAssigned = true + var x = rawValue.Interface().(time.Time) + fieldValue.Set(reflect.ValueOf(&x)) + } + case schemas.PtrFloat64Type: + if rawValueType.Kind() == reflect.Float64 { + x := vv.Float() + hasAssigned = true + fieldValue.Set(reflect.ValueOf(&x)) + } + case schemas.PtrUint64Type: + if rawValueType.Kind() == reflect.Int64 { + var x = uint64(vv.Int()) + hasAssigned = true + fieldValue.Set(reflect.ValueOf(&x)) + } + case schemas.PtrInt64Type: + if rawValueType.Kind() == reflect.Int64 { + x := vv.Int() + hasAssigned = true + fieldValue.Set(reflect.ValueOf(&x)) + } + case schemas.PtrFloat32Type: + if rawValueType.Kind() == reflect.Float64 { + var x = float32(vv.Float()) + hasAssigned = true + fieldValue.Set(reflect.ValueOf(&x)) + } + case schemas.PtrIntType: + if rawValueType.Kind() == reflect.Int64 { + var x = int(vv.Int()) + hasAssigned = true + fieldValue.Set(reflect.ValueOf(&x)) + } + case schemas.PtrInt32Type: + if rawValueType.Kind() == reflect.Int64 { + var x = int32(vv.Int()) + hasAssigned = true + fieldValue.Set(reflect.ValueOf(&x)) + } + case schemas.PtrInt8Type: + if rawValueType.Kind() == reflect.Int64 { + var x = int8(vv.Int()) + hasAssigned = true + fieldValue.Set(reflect.ValueOf(&x)) + } + case schemas.PtrInt16Type: + if rawValueType.Kind() == reflect.Int64 { + var x = int16(vv.Int()) + hasAssigned = true + fieldValue.Set(reflect.ValueOf(&x)) + } + case schemas.PtrUintType: + if rawValueType.Kind() == reflect.Int64 { + var x = uint(vv.Int()) + hasAssigned = true + fieldValue.Set(reflect.ValueOf(&x)) + } + case schemas.PtrUint32Type: + if rawValueType.Kind() == reflect.Int64 { + var x = uint32(vv.Int()) + hasAssigned = true + fieldValue.Set(reflect.ValueOf(&x)) + } + case schemas.Uint8Type: + if rawValueType.Kind() == reflect.Int64 { + var x = uint8(vv.Int()) + hasAssigned = true + fieldValue.Set(reflect.ValueOf(&x)) + } + case schemas.Uint16Type: + if rawValueType.Kind() == reflect.Int64 { + var x = uint16(vv.Int()) + hasAssigned = true + fieldValue.Set(reflect.ValueOf(&x)) + } + case schemas.Complex64Type: + var x complex64 + if len([]byte(vv.String())) > 0 { + err := json.DefaultJSONHandler.Unmarshal([]byte(vv.String()), &x) + if err != nil { + return err + } + fieldValue.Set(reflect.ValueOf(&x)) + } + hasAssigned = true + case schemas.Complex128Type: + var x complex128 + if len([]byte(vv.String())) > 0 { + err := json.DefaultJSONHandler.Unmarshal([]byte(vv.String()), &x) + if err != nil { + return err + } + fieldValue.Set(reflect.ValueOf(&x)) + } + hasAssigned = true + } // switch fieldType + } // switch fieldType.Kind() + + if hasAssigned { + return nil + } + + data, err := value2Bytes(&rawValue) + if err != nil { + return err + } + + if err = session.bytes2Value(col, fieldValue, data); err != nil { + return err + } + return nil +} + func (session *Session) slice2Bean(scanResults []interface{}, fields []string, bean interface{}, dataStruct *reflect.Value, table *schemas.Table) (schemas.PK, error) { defer func() { executeAfterSet(bean, fields, scanResults) @@ -447,14 +853,19 @@ func (session *Session) slice2Bean(scanResults []interface{}, fields []string, b var pk schemas.PK for ii, key := range fields { var idx int - var ok bool var lKey = strings.ToLower(key) + var ok bool + if idx, ok = tempMap[lKey]; !ok { idx = 0 } else { idx = idx + 1 } + tempMap[lKey] = idx + col := table.GetColumnIdx(key, idx) + + var scanResult = scanResults[ii] fieldValue, err := session.getField(dataStruct, key, table, idx) if err != nil { @@ -466,408 +877,12 @@ func (session *Session) slice2Bean(scanResults []interface{}, fields []string, b if fieldValue == nil { continue } - rawValue := reflect.Indirect(reflect.ValueOf(scanResults[ii])) - // if row is null then ignore - if rawValue.Interface() == nil { - continue + if err := session.convertBeanField(col, fieldValue, scanResult, dataStruct, table); err != nil { + return nil, err } - - if fieldValue.CanAddr() { - if structConvert, ok := fieldValue.Addr().Interface().(convert.Conversion); ok { - if data, err := value2Bytes(&rawValue); err == nil { - if err := structConvert.FromDB(data); err != nil { - return nil, err - } - } else { - return nil, err - } - continue - } - } - - if _, ok := fieldValue.Interface().(convert.Conversion); ok { - if data, err := value2Bytes(&rawValue); err == nil { - if fieldValue.Kind() == reflect.Ptr && fieldValue.IsNil() { - fieldValue.Set(reflect.New(fieldValue.Type().Elem())) - } - fieldValue.Interface().(convert.Conversion).FromDB(data) - } else { - return nil, err - } - continue - } - - rawValueType := reflect.TypeOf(rawValue.Interface()) - vv := reflect.ValueOf(rawValue.Interface()) - col := table.GetColumnIdx(key, idx) if col.IsPrimaryKey { - pk = append(pk, rawValue.Interface()) - } - fieldType := fieldValue.Type() - hasAssigned := false - - if col.IsJSON { - var bs []byte - if rawValueType.Kind() == reflect.String { - bs = []byte(vv.String()) - } else if rawValueType.ConvertibleTo(schemas.BytesType) { - bs = vv.Bytes() - } else { - return nil, fmt.Errorf("unsupported database data type: %s %v", key, rawValueType.Kind()) - } - - hasAssigned = true - - if len(bs) > 0 { - if fieldType.Kind() == reflect.String { - fieldValue.SetString(string(bs)) - continue - } - if fieldValue.CanAddr() { - err := json.DefaultJSONHandler.Unmarshal(bs, fieldValue.Addr().Interface()) - if err != nil { - return nil, err - } - } else { - x := reflect.New(fieldType) - err := json.DefaultJSONHandler.Unmarshal(bs, x.Interface()) - if err != nil { - return nil, err - } - fieldValue.Set(x.Elem()) - } - } - - continue - } - - switch fieldType.Kind() { - case reflect.Complex64, reflect.Complex128: - // TODO: reimplement this - var bs []byte - if rawValueType.Kind() == reflect.String { - bs = []byte(vv.String()) - } else if rawValueType.ConvertibleTo(schemas.BytesType) { - bs = vv.Bytes() - } - - hasAssigned = true - if len(bs) > 0 { - if fieldValue.CanAddr() { - err := json.DefaultJSONHandler.Unmarshal(bs, fieldValue.Addr().Interface()) - if err != nil { - return nil, err - } - } else { - x := reflect.New(fieldType) - err := json.DefaultJSONHandler.Unmarshal(bs, x.Interface()) - if err != nil { - return nil, err - } - fieldValue.Set(x.Elem()) - } - } - case reflect.Slice, reflect.Array: - switch rawValueType.Kind() { - case reflect.Slice, reflect.Array: - switch rawValueType.Elem().Kind() { - case reflect.Uint8: - if fieldType.Elem().Kind() == reflect.Uint8 { - hasAssigned = true - if col.SQLType.IsText() { - x := reflect.New(fieldType) - err := json.DefaultJSONHandler.Unmarshal(vv.Bytes(), x.Interface()) - if err != nil { - return nil, err - } - fieldValue.Set(x.Elem()) - } else { - if fieldValue.Len() > 0 { - for i := 0; i < fieldValue.Len(); i++ { - if i < vv.Len() { - fieldValue.Index(i).Set(vv.Index(i)) - } - } - } else { - for i := 0; i < vv.Len(); i++ { - fieldValue.Set(reflect.Append(*fieldValue, vv.Index(i))) - } - } - } - } - } - } - case reflect.String: - if rawValueType.Kind() == reflect.String { - hasAssigned = true - fieldValue.SetString(vv.String()) - } - case reflect.Bool: - if rawValueType.Kind() == reflect.Bool { - hasAssigned = true - fieldValue.SetBool(vv.Bool()) - } - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - switch rawValueType.Kind() { - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - hasAssigned = true - fieldValue.SetInt(vv.Int()) - } - case reflect.Float32, reflect.Float64: - switch rawValueType.Kind() { - case reflect.Float32, reflect.Float64: - hasAssigned = true - fieldValue.SetFloat(vv.Float()) - } - case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint: - switch rawValueType.Kind() { - case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint: - hasAssigned = true - fieldValue.SetUint(vv.Uint()) - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - hasAssigned = true - fieldValue.SetUint(uint64(vv.Int())) - } - case reflect.Struct: - if fieldType.ConvertibleTo(schemas.TimeType) { - dbTZ := session.engine.DatabaseTZ - if col.TimeZone != nil { - dbTZ = col.TimeZone - } - - if rawValueType == schemas.TimeType { - hasAssigned = true - - t := vv.Convert(schemas.TimeType).Interface().(time.Time) - - z, _ := t.Zone() - // set new location if database don't save timezone or give an incorrect timezone - if len(z) == 0 || t.Year() == 0 || t.Location().String() != dbTZ.String() { // !nashtsai! HACK tmp work around for lib/pq doesn't properly time with location - session.engine.logger.Debugf("empty zone key[%v] : %v | zone: %v | location: %+v\n", key, t, z, *t.Location()) - t = time.Date(t.Year(), t.Month(), t.Day(), t.Hour(), - t.Minute(), t.Second(), t.Nanosecond(), dbTZ) - } - - t = t.In(session.engine.TZLocation) - fieldValue.Set(reflect.ValueOf(t).Convert(fieldType)) - } else if rawValueType == schemas.IntType || rawValueType == schemas.Int64Type || - rawValueType == schemas.Int32Type { - hasAssigned = true - - t := time.Unix(vv.Int(), 0).In(session.engine.TZLocation) - fieldValue.Set(reflect.ValueOf(t).Convert(fieldType)) - } else { - if d, ok := vv.Interface().([]uint8); ok { - hasAssigned = true - t, err := session.byte2Time(col, d) - if err != nil { - session.engine.logger.Errorf("byte2Time error: %v", err) - hasAssigned = false - } else { - fieldValue.Set(reflect.ValueOf(t).Convert(fieldType)) - } - } else if d, ok := vv.Interface().(string); ok { - hasAssigned = true - t, err := session.str2Time(col, d) - if err != nil { - session.engine.logger.Errorf("byte2Time error: %v", err) - hasAssigned = false - } else { - fieldValue.Set(reflect.ValueOf(t).Convert(fieldType)) - } - } else { - return nil, fmt.Errorf("rawValueType is %v, value is %v", rawValueType, vv.Interface()) - } - } - } else if nulVal, ok := fieldValue.Addr().Interface().(sql.Scanner); ok { - // !! 增加支持sql.Scanner接口的结构,如sql.NullString - hasAssigned = true - if err := nulVal.Scan(vv.Interface()); err != nil { - session.engine.logger.Errorf("sql.Sanner error: %v", err) - hasAssigned = false - } - } else if col.IsJSON { - if rawValueType.Kind() == reflect.String { - hasAssigned = true - x := reflect.New(fieldType) - if len([]byte(vv.String())) > 0 { - err := json.DefaultJSONHandler.Unmarshal([]byte(vv.String()), x.Interface()) - if err != nil { - return nil, err - } - fieldValue.Set(x.Elem()) - } - } else if rawValueType.Kind() == reflect.Slice { - hasAssigned = true - x := reflect.New(fieldType) - if len(vv.Bytes()) > 0 { - err := json.DefaultJSONHandler.Unmarshal(vv.Bytes(), x.Interface()) - if err != nil { - return nil, err - } - fieldValue.Set(x.Elem()) - } - } - } else if session.statement.UseCascade { - table, err := session.engine.tagParser.ParseWithCache(*fieldValue) - if err != nil { - return nil, err - } - - hasAssigned = true - if len(table.PrimaryKeys) != 1 { - return nil, errors.New("unsupported non or composited primary key cascade") - } - var pk = make(schemas.PK, len(table.PrimaryKeys)) - pk[0], err = asKind(vv, rawValueType) - if err != nil { - return nil, err - } - - if !pk.IsZero() { - // !nashtsai! TODO for hasOne relationship, it's preferred to use join query for eager fetch - // however, also need to consider adding a 'lazy' attribute to xorm tag which allow hasOne - // property to be fetched lazily - structInter := reflect.New(fieldValue.Type()) - has, err := session.ID(pk).NoCascade().get(structInter.Interface()) - if err != nil { - return nil, err - } - if has { - fieldValue.Set(structInter.Elem()) - } else { - return nil, errors.New("cascade obj is not exist") - } - } - } - case reflect.Ptr: - // !nashtsai! TODO merge duplicated codes above - switch fieldType { - // following types case matching ptr's native type, therefore assign ptr directly - case schemas.PtrStringType: - if rawValueType.Kind() == reflect.String { - x := vv.String() - hasAssigned = true - fieldValue.Set(reflect.ValueOf(&x)) - } - case schemas.PtrBoolType: - if rawValueType.Kind() == reflect.Bool { - x := vv.Bool() - hasAssigned = true - fieldValue.Set(reflect.ValueOf(&x)) - } - case schemas.PtrTimeType: - if rawValueType == schemas.PtrTimeType { - hasAssigned = true - var x = rawValue.Interface().(time.Time) - fieldValue.Set(reflect.ValueOf(&x)) - } - case schemas.PtrFloat64Type: - if rawValueType.Kind() == reflect.Float64 { - x := vv.Float() - hasAssigned = true - fieldValue.Set(reflect.ValueOf(&x)) - } - case schemas.PtrUint64Type: - if rawValueType.Kind() == reflect.Int64 { - var x = uint64(vv.Int()) - hasAssigned = true - fieldValue.Set(reflect.ValueOf(&x)) - } - case schemas.PtrInt64Type: - if rawValueType.Kind() == reflect.Int64 { - x := vv.Int() - hasAssigned = true - fieldValue.Set(reflect.ValueOf(&x)) - } - case schemas.PtrFloat32Type: - if rawValueType.Kind() == reflect.Float64 { - var x = float32(vv.Float()) - hasAssigned = true - fieldValue.Set(reflect.ValueOf(&x)) - } - case schemas.PtrIntType: - if rawValueType.Kind() == reflect.Int64 { - var x = int(vv.Int()) - hasAssigned = true - fieldValue.Set(reflect.ValueOf(&x)) - } - case schemas.PtrInt32Type: - if rawValueType.Kind() == reflect.Int64 { - var x = int32(vv.Int()) - hasAssigned = true - fieldValue.Set(reflect.ValueOf(&x)) - } - case schemas.PtrInt8Type: - if rawValueType.Kind() == reflect.Int64 { - var x = int8(vv.Int()) - hasAssigned = true - fieldValue.Set(reflect.ValueOf(&x)) - } - case schemas.PtrInt16Type: - if rawValueType.Kind() == reflect.Int64 { - var x = int16(vv.Int()) - hasAssigned = true - fieldValue.Set(reflect.ValueOf(&x)) - } - case schemas.PtrUintType: - if rawValueType.Kind() == reflect.Int64 { - var x = uint(vv.Int()) - hasAssigned = true - fieldValue.Set(reflect.ValueOf(&x)) - } - case schemas.PtrUint32Type: - if rawValueType.Kind() == reflect.Int64 { - var x = uint32(vv.Int()) - hasAssigned = true - fieldValue.Set(reflect.ValueOf(&x)) - } - case schemas.Uint8Type: - if rawValueType.Kind() == reflect.Int64 { - var x = uint8(vv.Int()) - hasAssigned = true - fieldValue.Set(reflect.ValueOf(&x)) - } - case schemas.Uint16Type: - if rawValueType.Kind() == reflect.Int64 { - var x = uint16(vv.Int()) - hasAssigned = true - fieldValue.Set(reflect.ValueOf(&x)) - } - case schemas.Complex64Type: - var x complex64 - if len([]byte(vv.String())) > 0 { - err := json.DefaultJSONHandler.Unmarshal([]byte(vv.String()), &x) - if err != nil { - return nil, err - } - fieldValue.Set(reflect.ValueOf(&x)) - } - hasAssigned = true - case schemas.Complex128Type: - var x complex128 - if len([]byte(vv.String())) > 0 { - err := json.DefaultJSONHandler.Unmarshal([]byte(vv.String()), &x) - if err != nil { - return nil, err - } - fieldValue.Set(reflect.ValueOf(&x)) - } - hasAssigned = true - } // switch fieldType - } // switch fieldType.Kind() - - // !nashtsai! for value can't be assigned directly fallback to convert to []byte then back to value - if !hasAssigned { - data, err := value2Bytes(&rawValue) - if err != nil { - return nil, err - } - - if err = session.bytes2Value(col, fieldValue, data); err != nil { - return nil, err - } + pk = append(pk, scanResult) } } return pk, nil -- 2.40.1 From 10dee93362a60ddefa08eadc51e5856821195828 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Wed, 7 Jul 2021 12:24:57 +0800 Subject: [PATCH 2/2] improve code --- session.go | 95 +++++++++++++++++++++++------------------------------- 1 file changed, 40 insertions(+), 55 deletions(-) diff --git a/session.go b/session.go index 1e33541a..3fb92991 100644 --- a/session.go +++ b/session.go @@ -437,7 +437,7 @@ func (session *Session) row2Slice(rows *core.Rows, fields []string, bean interfa } func (session *Session) convertBeanField(col *schemas.Column, fieldValue *reflect.Value, - scanResult interface{}, dataStruct *reflect.Value, table *schemas.Table) error { + scanResult interface{}, table *schemas.Table) error { rawValue := reflect.Indirect(reflect.ValueOf(scanResult)) // if row is null then ignore @@ -474,7 +474,6 @@ func (session *Session) convertBeanField(col *schemas.Column, fieldValue *reflec vv := reflect.ValueOf(rawValue.Interface()) fieldType := fieldValue.Type() - hasAssigned := false if col.IsJSON { var bs []byte @@ -486,8 +485,6 @@ func (session *Session) convertBeanField(col *schemas.Column, fieldValue *reflec return fmt.Errorf("unsupported database data type: %s %v", col.Name, rawValueType.Kind()) } - hasAssigned = true - if len(bs) > 0 { if fieldType.Kind() == reflect.String { fieldValue.SetString(string(bs)) @@ -507,7 +504,6 @@ func (session *Session) convertBeanField(col *schemas.Column, fieldValue *reflec fieldValue.Set(x.Elem()) } } - return nil } @@ -521,7 +517,6 @@ func (session *Session) convertBeanField(col *schemas.Column, fieldValue *reflec bs = vv.Bytes() } - hasAssigned = true if len(bs) > 0 { if fieldValue.CanAddr() { err := json.DefaultJSONHandler.Unmarshal(bs, fieldValue.Addr().Interface()) @@ -537,13 +532,13 @@ func (session *Session) convertBeanField(col *schemas.Column, fieldValue *reflec fieldValue.Set(x.Elem()) } } + return nil case reflect.Slice, reflect.Array: switch rawValueType.Kind() { case reflect.Slice, reflect.Array: switch rawValueType.Elem().Kind() { case reflect.Uint8: if fieldType.Elem().Kind() == reflect.Uint8 { - hasAssigned = true if col.SQLType.IsText() { x := reflect.New(fieldType) err := json.DefaultJSONHandler.Unmarshal(vv.Bytes(), x.Interface()) @@ -564,39 +559,40 @@ func (session *Session) convertBeanField(col *schemas.Column, fieldValue *reflec } } } + return nil } } } case reflect.String: if rawValueType.Kind() == reflect.String { - hasAssigned = true fieldValue.SetString(vv.String()) + return nil } case reflect.Bool: if rawValueType.Kind() == reflect.Bool { - hasAssigned = true fieldValue.SetBool(vv.Bool()) + return nil } case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: switch rawValueType.Kind() { case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - hasAssigned = true fieldValue.SetInt(vv.Int()) + return nil } case reflect.Float32, reflect.Float64: switch rawValueType.Kind() { case reflect.Float32, reflect.Float64: - hasAssigned = true fieldValue.SetFloat(vv.Float()) + return nil } case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint: switch rawValueType.Kind() { case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint: - hasAssigned = true fieldValue.SetUint(vv.Uint()) + return nil case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - hasAssigned = true fieldValue.SetUint(uint64(vv.Int())) + return nil } case reflect.Struct: if fieldType.ConvertibleTo(schemas.TimeType) { @@ -606,8 +602,6 @@ func (session *Session) convertBeanField(col *schemas.Column, fieldValue *reflec } if rawValueType == schemas.TimeType { - hasAssigned = true - t := vv.Convert(schemas.TimeType).Interface().(time.Time) z, _ := t.Zone() @@ -620,45 +614,42 @@ func (session *Session) convertBeanField(col *schemas.Column, fieldValue *reflec t = t.In(session.engine.TZLocation) fieldValue.Set(reflect.ValueOf(t).Convert(fieldType)) + return nil } else if rawValueType == schemas.IntType || rawValueType == schemas.Int64Type || rawValueType == schemas.Int32Type { - hasAssigned = true - t := time.Unix(vv.Int(), 0).In(session.engine.TZLocation) fieldValue.Set(reflect.ValueOf(t).Convert(fieldType)) + return nil } else { if d, ok := vv.Interface().([]uint8); ok { - hasAssigned = true t, err := session.byte2Time(col, d) if err != nil { session.engine.logger.Errorf("byte2Time error: %v", err) - hasAssigned = false } else { fieldValue.Set(reflect.ValueOf(t).Convert(fieldType)) + return nil } + } else if d, ok := vv.Interface().(string); ok { - hasAssigned = true t, err := session.str2Time(col, d) if err != nil { session.engine.logger.Errorf("byte2Time error: %v", err) - hasAssigned = false } else { fieldValue.Set(reflect.ValueOf(t).Convert(fieldType)) + return nil } } else { return fmt.Errorf("rawValueType is %v, value is %v", rawValueType, vv.Interface()) } } } else if nulVal, ok := fieldValue.Addr().Interface().(sql.Scanner); ok { - // !! 增加支持sql.Scanner接口的结构,如sql.NullString - hasAssigned = true - if err := nulVal.Scan(vv.Interface()); err != nil { - session.engine.logger.Errorf("sql.Sanner error: %v", err) - hasAssigned = false + err := nulVal.Scan(vv.Interface()) + if err == nil { + return nil } + session.engine.logger.Errorf("sql.Sanner error: %v", err) } else if col.IsJSON { if rawValueType.Kind() == reflect.String { - hasAssigned = true x := reflect.New(fieldType) if len([]byte(vv.String())) > 0 { err := json.DefaultJSONHandler.Unmarshal([]byte(vv.String()), x.Interface()) @@ -667,8 +658,8 @@ func (session *Session) convertBeanField(col *schemas.Column, fieldValue *reflec } fieldValue.Set(x.Elem()) } + return nil } else if rawValueType.Kind() == reflect.Slice { - hasAssigned = true x := reflect.New(fieldType) if len(vv.Bytes()) > 0 { err := json.DefaultJSONHandler.Unmarshal(vv.Bytes(), x.Interface()) @@ -677,6 +668,7 @@ func (session *Session) convertBeanField(col *schemas.Column, fieldValue *reflec } fieldValue.Set(x.Elem()) } + return nil } } else if session.statement.UseCascade { table, err := session.engine.tagParser.ParseWithCache(*fieldValue) @@ -684,7 +676,6 @@ func (session *Session) convertBeanField(col *schemas.Column, fieldValue *reflec return err } - hasAssigned = true if len(table.PrimaryKeys) != 1 { return errors.New("unsupported non or composited primary key cascade") } @@ -709,6 +700,7 @@ func (session *Session) convertBeanField(col *schemas.Column, fieldValue *reflec return errors.New("cascade obj is not exist") } } + return nil } case reflect.Ptr: // !nashtsai! TODO merge duplicated codes above @@ -717,92 +709,92 @@ func (session *Session) convertBeanField(col *schemas.Column, fieldValue *reflec case schemas.PtrStringType: if rawValueType.Kind() == reflect.String { x := vv.String() - hasAssigned = true fieldValue.Set(reflect.ValueOf(&x)) + return nil } case schemas.PtrBoolType: if rawValueType.Kind() == reflect.Bool { x := vv.Bool() - hasAssigned = true fieldValue.Set(reflect.ValueOf(&x)) + return nil } case schemas.PtrTimeType: if rawValueType == schemas.PtrTimeType { - hasAssigned = true var x = rawValue.Interface().(time.Time) fieldValue.Set(reflect.ValueOf(&x)) + return nil } case schemas.PtrFloat64Type: if rawValueType.Kind() == reflect.Float64 { x := vv.Float() - hasAssigned = true fieldValue.Set(reflect.ValueOf(&x)) + return nil } case schemas.PtrUint64Type: if rawValueType.Kind() == reflect.Int64 { var x = uint64(vv.Int()) - hasAssigned = true fieldValue.Set(reflect.ValueOf(&x)) + return nil } case schemas.PtrInt64Type: if rawValueType.Kind() == reflect.Int64 { x := vv.Int() - hasAssigned = true fieldValue.Set(reflect.ValueOf(&x)) + return nil } case schemas.PtrFloat32Type: if rawValueType.Kind() == reflect.Float64 { var x = float32(vv.Float()) - hasAssigned = true fieldValue.Set(reflect.ValueOf(&x)) + return nil } case schemas.PtrIntType: if rawValueType.Kind() == reflect.Int64 { var x = int(vv.Int()) - hasAssigned = true fieldValue.Set(reflect.ValueOf(&x)) + return nil } case schemas.PtrInt32Type: if rawValueType.Kind() == reflect.Int64 { var x = int32(vv.Int()) - hasAssigned = true fieldValue.Set(reflect.ValueOf(&x)) + return nil } case schemas.PtrInt8Type: if rawValueType.Kind() == reflect.Int64 { var x = int8(vv.Int()) - hasAssigned = true fieldValue.Set(reflect.ValueOf(&x)) + return nil } case schemas.PtrInt16Type: if rawValueType.Kind() == reflect.Int64 { var x = int16(vv.Int()) - hasAssigned = true fieldValue.Set(reflect.ValueOf(&x)) + return nil } case schemas.PtrUintType: if rawValueType.Kind() == reflect.Int64 { var x = uint(vv.Int()) - hasAssigned = true fieldValue.Set(reflect.ValueOf(&x)) + return nil } case schemas.PtrUint32Type: if rawValueType.Kind() == reflect.Int64 { var x = uint32(vv.Int()) - hasAssigned = true fieldValue.Set(reflect.ValueOf(&x)) + return nil } case schemas.Uint8Type: if rawValueType.Kind() == reflect.Int64 { var x = uint8(vv.Int()) - hasAssigned = true fieldValue.Set(reflect.ValueOf(&x)) + return nil } case schemas.Uint16Type: if rawValueType.Kind() == reflect.Int64 { var x = uint16(vv.Int()) - hasAssigned = true fieldValue.Set(reflect.ValueOf(&x)) + return nil } case schemas.Complex64Type: var x complex64 @@ -813,7 +805,7 @@ func (session *Session) convertBeanField(col *schemas.Column, fieldValue *reflec } fieldValue.Set(reflect.ValueOf(&x)) } - hasAssigned = true + return nil case schemas.Complex128Type: var x complex128 if len([]byte(vv.String())) > 0 { @@ -823,23 +815,16 @@ func (session *Session) convertBeanField(col *schemas.Column, fieldValue *reflec } fieldValue.Set(reflect.ValueOf(&x)) } - hasAssigned = true + return nil } // switch fieldType } // switch fieldType.Kind() - if hasAssigned { - return nil - } - data, err := value2Bytes(&rawValue) if err != nil { return err } - if err = session.bytes2Value(col, fieldValue, data); err != nil { - return err - } - return nil + return session.bytes2Value(col, fieldValue, data) } func (session *Session) slice2Bean(scanResults []interface{}, fields []string, bean interface{}, dataStruct *reflect.Value, table *schemas.Table) (schemas.PK, error) { @@ -878,7 +863,7 @@ func (session *Session) slice2Bean(scanResults []interface{}, fields []string, b continue } - if err := session.convertBeanField(col, fieldValue, scanResult, dataStruct, table); err != nil { + if err := session.convertBeanField(col, fieldValue, scanResult, table); err != nil { return nil, err } if col.IsPrimaryKey { -- 2.40.1