package util import ( "database/sql" "encoding/json" "reflect" "strings" "sync" "time" "git.fsdpf.net/go/db/internal/errors" ) const ( skipUpdateTagName = "skipupdate" skipInsertTagName = "skipinsert" defaultIfEmptyTagName = "defaultifempty" omitNilTagName = "omitnil" omitEmptyTagName = "omitempty" ) var scannerType = reflect.TypeOf((*sql.Scanner)(nil)).Elem() var timeType = reflect.TypeOf(time.Time{}) var timeParseFmts = []string{ time.DateTime, // "2006-01-02 15:04:05" time.DateOnly, // "2006-01-02" "2006-01-02 15:04:05.999999999", // MySQL 带微秒 "2006-01-02T15:04:05", // ISO8601 无时区 time.RFC3339Nano, time.RFC3339, } func parseTimeFromRaw(u *json.RawMessage) (time.Time, bool) { s := strings.Trim(string(*u), `"`) for _, f := range timeParseFmts { if t, err := time.Parse(f, s); err == nil { return t, true } } return time.Time{}, false } func IsUint(k reflect.Kind) bool { return (k == reflect.Uint) || (k == reflect.Uint8) || (k == reflect.Uint16) || (k == reflect.Uint32) || (k == reflect.Uint64) } func IsInt(k reflect.Kind) bool { return (k == reflect.Int) || (k == reflect.Int8) || (k == reflect.Int16) || (k == reflect.Int32) || (k == reflect.Int64) } func IsFloat(k reflect.Kind) bool { return (k == reflect.Float32) || (k == reflect.Float64) } func IsString(k reflect.Kind) bool { return k == reflect.String } func IsBool(k reflect.Kind) bool { return k == reflect.Bool } func IsSlice(k reflect.Kind) bool { return k == reflect.Slice } func IsStruct(k reflect.Kind) bool { return k == reflect.Struct } func IsMap(k reflect.Kind) bool { return k == reflect.Map } func IsInvalid(k reflect.Kind) bool { return k == reflect.Invalid } func IsPointer(k reflect.Kind) bool { return k == reflect.Ptr } func IsNil(v reflect.Value) bool { if !v.IsValid() { return true } switch v.Kind() { case reflect.Ptr, reflect.Interface, reflect.Slice, reflect.Map, reflect.Chan, reflect.Func: return v.IsNil() default: return false } } func IsEmptyValue(v reflect.Value) bool { return !v.IsValid() || v.IsZero() } var ( structMapCache = make(map[interface{}]ColumnMap) structMapCacheLock = sync.Mutex{} ) var ( DefaultColumnRenameFunction = strings.ToLower columnRenameFunction = DefaultColumnRenameFunction ignoreUntaggedFields = false ) func SetIgnoreUntaggedFields(ignore bool) { // If the value here is changing, reset the struct map cache if ignore != ignoreUntaggedFields { ignoreUntaggedFields = ignore structMapCacheLock.Lock() defer structMapCacheLock.Unlock() structMapCache = make(map[interface{}]ColumnMap) } } func SetColumnRenameFunction(newFunction func(string) string) { columnRenameFunction = newFunction } // GetSliceElementType returns the type for a slices elements. func GetSliceElementType(val reflect.Value) reflect.Type { elemType := val.Type().Elem() if elemType.Kind() == reflect.Ptr { elemType = elemType.Elem() } return elemType } func GetMapElementType(val reflect.Value) reflect.Type { elemType := val.Type().Elem() if elemType.Kind() == reflect.Ptr { elemType = elemType.Elem() } return elemType } // AppendSliceElement will append val to slice. Handles slice of pointers and // not pointers. Val needs to be a pointer. func AppendSliceElement(slice, val reflect.Value) { if slice.Type().Elem().Kind() == reflect.Ptr { slice.Set(reflect.Append(slice, val)) } else { slice.Set(reflect.Append(slice, reflect.Indirect(val))) } } func GetTypeInfo(i interface{}, val reflect.Value) (reflect.Type, reflect.Kind) { var t reflect.Type valKind := val.Kind() if valKind == reflect.Slice { if reflect.ValueOf(i).Kind() == reflect.Ptr { t = reflect.TypeOf(i).Elem().Elem() } else { t = reflect.TypeOf(i).Elem() } if t.Kind() == reflect.Ptr { t = t.Elem() } valKind = t.Kind() } else { t = val.Type() } return t, valKind } func SafeGetFieldByIndex(v reflect.Value, fieldIndex []int) (result reflect.Value, isAvailable bool) { switch len(fieldIndex) { case 0: return v, true case 1: return v.FieldByIndex(fieldIndex), true default: if f := reflect.Indirect(v.Field(fieldIndex[0])); f.IsValid() { return SafeGetFieldByIndex(f, fieldIndex[1:]) } } return reflect.ValueOf(nil), false } func SafeSetFieldByIndex(v reflect.Value, fieldIndex []int, src interface{}) (result reflect.Value) { v = reflect.Indirect(v) switch len(fieldIndex) { case 0: return v case 1: f := v.FieldByIndex(fieldIndex) if err := SafeSetVarValue(f, src); err != nil { panic(err) } default: f := v.Field(fieldIndex[0]) switch f.Kind() { case reflect.Ptr: s := f if f.IsNil() || !f.IsValid() { s = reflect.New(f.Type().Elem()) f.Set(s) } SafeSetFieldByIndex(reflect.Indirect(s), fieldIndex[1:], src) case reflect.Struct: SafeSetFieldByIndex(f, fieldIndex[1:], src) default: // use the original value } } return v } func SafeSetVarValue(v reflect.Value, src interface{}) error { srcReflect := reflect.ValueOf(src) // src 可能是 **T(createColumnScans 的扫描目标)或裸值(测试/直接调用) if srcReflect.Kind() != reflect.Ptr { if srcReflect.IsValid() { if v.Type().ConvertibleTo(srcReflect.Type()) { v.Set(srcReflect.Convert(v.Type())) } else if v.Kind() == reflect.Ptr && srcReflect.Type().ConvertibleTo(v.Type().Elem()) { // src = T,v = *T:分配新指针并赋值(如 time.Time → *time.Time) p := reflect.New(v.Type().Elem()) p.Elem().Set(srcReflect.Convert(v.Type().Elem())) v.Set(p) } } return nil } srcVal := srcReflect.Elem() // **T → *T;*T → T if IsNil(srcVal) { v.Set(reflect.Zero(v.Type())) return nil } if v.Kind() == reflect.Ptr { // v 是指针字段(如 *sql.NullString、*time.Time) if srcVal.Kind() == reflect.Ptr { // src = **T, srcVal = *T → v = *T if v.Type().ConvertibleTo(srcVal.Type()) { v.Set(srcVal.Convert(v.Type())) } else if u, ok := srcVal.Interface().(*json.RawMessage); ok && len(*u) >= 2 { if v.Type().Elem() == timeType { if t, ok := parseTimeFromRaw(u); ok { p := reflect.New(timeType) p.Elem().Set(reflect.ValueOf(t)) v.Set(p) } } else { p := reflect.New(v.Type().Elem()) if err := json.Unmarshal(*u, p.Interface()); err == nil { v.Set(p) } } } } else { // src = *T, srcVal = T → allocate new *T and set if v.Type().Elem().ConvertibleTo(srcVal.Type()) { p := reflect.New(v.Type().Elem()) p.Elem().Set(srcVal.Convert(v.Type().Elem())) v.Set(p) } } return nil } // v 是非指针字段 if srcVal.Kind() == reflect.Ptr { // srcVal = *T,取其值赋给 v if v.Type().ConvertibleTo(srcVal.Type().Elem()) { v.Set(srcVal.Elem().Convert(v.Type())) } else if u, ok := srcVal.Interface().(*json.RawMessage); ok && len(*u) >= 2 { if v.Type() == timeType { if t, ok := parseTimeFromRaw(u); ok { v.Set(reflect.ValueOf(t)) return nil } } if err := json.Unmarshal(*u, v.Addr().Interface()); err != nil { return err } } } else { // srcVal = T(*T 扫描目标的 default 分支) if v.Type().ConvertibleTo(srcVal.Type()) { v.Set(srcVal.Convert(v.Type())) } } return nil } type rowData = map[string]interface{} // AssignStructVals will assign the data from rd to i. func AssignStructVals(i interface{}, rd rowData, cm ColumnMap) { val := reflect.Indirect(reflect.ValueOf(i)) for name, data := range cm { src, ok := rd[name] if ok { SafeSetFieldByIndex(val, data.FieldIndex, src) } } } func GetColumnMap(i interface{}) (ColumnMap, error) { val := reflect.Indirect(reflect.ValueOf(i)) t, valKind := GetTypeInfo(i, val) if valKind != reflect.Struct { return nil, errors.New("cannot scan into this type: %v", t) // #nosec } structMapCacheLock.Lock() defer structMapCacheLock.Unlock() if _, ok := structMapCache[t]; !ok { structMapCache[t] = newColumnMap(t, []int{}, []string{}) } return structMapCache[t], nil }