2020-03-09 08:03:59 +00:00
|
|
|
|
// Copyright 2017 The Xorm Authors. All rights reserved.
|
|
|
|
|
// Use of this source code is governed by a BSD-style
|
|
|
|
|
// license that can be found in the LICENSE file.
|
|
|
|
|
|
|
|
|
|
package statements
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"database/sql"
|
|
|
|
|
"database/sql/driver"
|
|
|
|
|
"fmt"
|
2021-07-07 06:00:16 +00:00
|
|
|
|
"math/big"
|
2020-03-09 08:03:59 +00:00
|
|
|
|
"reflect"
|
|
|
|
|
"time"
|
|
|
|
|
|
2021-08-10 15:20:53 +00:00
|
|
|
|
"xorm.io/xorm/convert"
|
2020-03-09 08:03:59 +00:00
|
|
|
|
"xorm.io/xorm/dialects"
|
|
|
|
|
"xorm.io/xorm/internal/json"
|
|
|
|
|
"xorm.io/xorm/schemas"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
var (
|
|
|
|
|
nullFloatType = reflect.TypeOf(sql.NullFloat64{})
|
2021-07-07 06:00:16 +00:00
|
|
|
|
bigFloatType = reflect.TypeOf(big.Float{})
|
2020-03-09 08:03:59 +00:00
|
|
|
|
)
|
|
|
|
|
|
2021-07-20 05:46:24 +00:00
|
|
|
|
// Value2Interface convert a field value of a struct to interface for putting into database
|
2020-03-09 08:03:59 +00:00
|
|
|
|
func (statement *Statement) Value2Interface(col *schemas.Column, fieldValue reflect.Value) (interface{}, error) {
|
|
|
|
|
if fieldValue.CanAddr() {
|
|
|
|
|
if fieldConvert, ok := fieldValue.Addr().Interface().(convert.Conversion); ok {
|
|
|
|
|
data, err := fieldConvert.ToDB()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
2021-07-11 13:33:01 +00:00
|
|
|
|
if data == nil {
|
|
|
|
|
if col.Nullable {
|
|
|
|
|
return nil, nil
|
|
|
|
|
}
|
|
|
|
|
data = []byte{}
|
|
|
|
|
}
|
2020-03-09 08:03:59 +00:00
|
|
|
|
if col.SQLType.IsBlob() {
|
|
|
|
|
return data, nil
|
|
|
|
|
}
|
|
|
|
|
return string(data), nil
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-08 15:40:26 +00:00
|
|
|
|
isNil := fieldValue.Kind() == reflect.Ptr && fieldValue.IsNil()
|
|
|
|
|
if !isNil {
|
|
|
|
|
if fieldConvert, ok := fieldValue.Interface().(convert.Conversion); ok {
|
|
|
|
|
data, err := fieldConvert.ToDB()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
2021-07-11 13:33:01 +00:00
|
|
|
|
if data == nil {
|
|
|
|
|
if col.Nullable {
|
|
|
|
|
return nil, nil
|
|
|
|
|
}
|
|
|
|
|
data = []byte{}
|
|
|
|
|
}
|
2020-09-08 15:40:26 +00:00
|
|
|
|
if col.SQLType.IsBlob() {
|
|
|
|
|
return data, nil
|
|
|
|
|
}
|
|
|
|
|
return string(data), nil
|
2020-03-10 03:02:31 +00:00
|
|
|
|
}
|
2020-03-09 08:03:59 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fieldType := fieldValue.Type()
|
|
|
|
|
k := fieldType.Kind()
|
|
|
|
|
if k == reflect.Ptr {
|
|
|
|
|
if fieldValue.IsNil() {
|
|
|
|
|
return nil, nil
|
|
|
|
|
} else if !fieldValue.IsValid() {
|
|
|
|
|
return nil, nil
|
|
|
|
|
} else {
|
|
|
|
|
// !nashtsai! deference pointer type to instance type
|
|
|
|
|
fieldValue = fieldValue.Elem()
|
|
|
|
|
fieldType = fieldValue.Type()
|
|
|
|
|
k = fieldType.Kind()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch k {
|
|
|
|
|
case reflect.Bool:
|
|
|
|
|
return fieldValue.Bool(), nil
|
|
|
|
|
case reflect.String:
|
|
|
|
|
return fieldValue.String(), nil
|
|
|
|
|
case reflect.Struct:
|
|
|
|
|
if fieldType.ConvertibleTo(schemas.TimeType) {
|
|
|
|
|
t := fieldValue.Convert(schemas.TimeType).Interface().(time.Time)
|
2021-08-04 08:12:10 +00:00
|
|
|
|
tf, err := dialects.FormatColumnTime(statement.dialect, statement.defaultTimeZone, col, t)
|
|
|
|
|
return tf, err
|
2020-03-09 08:03:59 +00:00
|
|
|
|
} else if fieldType.ConvertibleTo(nullFloatType) {
|
|
|
|
|
t := fieldValue.Convert(nullFloatType).Interface().(sql.NullFloat64)
|
|
|
|
|
if !t.Valid {
|
2023-09-01 15:06:41 +00:00
|
|
|
|
return (*float64)(nil), nil
|
2020-03-09 08:03:59 +00:00
|
|
|
|
}
|
|
|
|
|
return t.Float64, nil
|
2021-07-07 06:00:16 +00:00
|
|
|
|
} else if fieldType.ConvertibleTo(bigFloatType) {
|
|
|
|
|
t := fieldValue.Convert(bigFloatType).Interface().(big.Float)
|
|
|
|
|
return t.String(), nil
|
2023-09-01 15:06:41 +00:00
|
|
|
|
} else if fieldType.ConvertibleTo(schemas.IntervalType) {
|
|
|
|
|
t := fieldValue.Convert(schemas.IntervalType).Interface().(time.Duration)
|
|
|
|
|
return t, nil
|
|
|
|
|
} else if fieldType.ConvertibleTo(schemas.NullBoolType) {
|
|
|
|
|
t := fieldValue.Convert(schemas.NullBoolType).Interface().(sql.NullBool)
|
|
|
|
|
if !t.Valid {
|
|
|
|
|
return (*bool)(nil), nil
|
|
|
|
|
}
|
|
|
|
|
return t.Bool, nil
|
|
|
|
|
} else if fieldType.ConvertibleTo(schemas.NullFloat64Type) {
|
|
|
|
|
t := fieldValue.Convert(schemas.NullFloat64Type).Interface().(sql.NullFloat64)
|
|
|
|
|
if !t.Valid {
|
|
|
|
|
return (*float64)(nil), nil
|
|
|
|
|
}
|
|
|
|
|
return t.Float64, nil
|
|
|
|
|
} else if fieldType.ConvertibleTo(schemas.NullInt16Type) {
|
|
|
|
|
t := fieldValue.Convert(schemas.NullInt16Type).Interface().(sql.NullInt16)
|
|
|
|
|
if !t.Valid {
|
|
|
|
|
return (*int16)(nil), nil
|
|
|
|
|
}
|
|
|
|
|
return t.Int16, nil
|
|
|
|
|
} else if fieldType.ConvertibleTo(schemas.NullInt32Type) {
|
|
|
|
|
t := fieldValue.Convert(schemas.NullInt32Type).Interface().(sql.NullInt32)
|
|
|
|
|
if !t.Valid {
|
|
|
|
|
return (*int32)(nil), nil
|
|
|
|
|
}
|
|
|
|
|
return t.Int32, nil
|
|
|
|
|
} else if fieldType.ConvertibleTo(schemas.NullInt64Type) {
|
|
|
|
|
t := fieldValue.Convert(schemas.NullInt64Type).Interface().(sql.NullInt64)
|
|
|
|
|
if !t.Valid {
|
|
|
|
|
return (*int64)(nil), nil
|
|
|
|
|
}
|
|
|
|
|
return t.Int64, nil
|
|
|
|
|
} else if fieldType.ConvertibleTo(schemas.NullStringType) {
|
|
|
|
|
t := fieldValue.Convert(schemas.NullStringType).Interface().(sql.NullString)
|
|
|
|
|
if !t.Valid {
|
|
|
|
|
return (*string)(nil), nil
|
|
|
|
|
}
|
|
|
|
|
return t.String, nil
|
|
|
|
|
} else if fieldType.ConvertibleTo(schemas.NullTimeType) {
|
|
|
|
|
t := fieldValue.Convert(schemas.NullTimeType).Interface().(sql.NullTime)
|
|
|
|
|
if !t.Valid {
|
|
|
|
|
return (*time.Time)(nil), nil
|
|
|
|
|
}
|
|
|
|
|
return t.Time, nil
|
2020-03-09 08:03:59 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-11-03 09:10:35 +00:00
|
|
|
|
if !col.IsJSON {
|
2020-03-09 08:03:59 +00:00
|
|
|
|
// !<winxxp>! 增加支持driver.Valuer接口的结构,如sql.NullString
|
|
|
|
|
if v, ok := fieldValue.Interface().(driver.Valuer); ok {
|
|
|
|
|
return v.Value()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fieldTable, err := statement.tagParser.ParseWithCache(fieldValue)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
if len(fieldTable.PrimaryKeys) == 1 {
|
|
|
|
|
pkField := reflect.Indirect(fieldValue).FieldByName(fieldTable.PKColumns()[0].FieldName)
|
|
|
|
|
return pkField.Interface(), nil
|
|
|
|
|
}
|
|
|
|
|
return nil, fmt.Errorf("no primary key for col %v", col.Name)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if col.SQLType.IsText() {
|
|
|
|
|
bytes, err := json.DefaultJSONHandler.Marshal(fieldValue.Interface())
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
return string(bytes), nil
|
|
|
|
|
} else if col.SQLType.IsBlob() {
|
|
|
|
|
bytes, err := json.DefaultJSONHandler.Marshal(fieldValue.Interface())
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
return bytes, nil
|
|
|
|
|
}
|
|
|
|
|
return nil, fmt.Errorf("Unsupported type %v", fieldValue.Type())
|
|
|
|
|
case reflect.Complex64, reflect.Complex128:
|
|
|
|
|
bytes, err := json.DefaultJSONHandler.Marshal(fieldValue.Interface())
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
return string(bytes), nil
|
|
|
|
|
case reflect.Array, reflect.Slice, reflect.Map:
|
|
|
|
|
if !fieldValue.IsValid() {
|
|
|
|
|
return fieldValue.Interface(), nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if col.SQLType.IsText() {
|
|
|
|
|
bytes, err := json.DefaultJSONHandler.Marshal(fieldValue.Interface())
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
return string(bytes), nil
|
|
|
|
|
} else if col.SQLType.IsBlob() {
|
|
|
|
|
var bytes []byte
|
|
|
|
|
var err error
|
|
|
|
|
if (k == reflect.Slice) &&
|
|
|
|
|
(fieldValue.Type().Elem().Kind() == reflect.Uint8) {
|
|
|
|
|
bytes = fieldValue.Bytes()
|
|
|
|
|
} else {
|
|
|
|
|
bytes, err = json.DefaultJSONHandler.Marshal(fieldValue.Interface())
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return bytes, nil
|
|
|
|
|
}
|
|
|
|
|
return nil, ErrUnSupportedType
|
|
|
|
|
case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint:
|
2023-09-01 15:06:41 +00:00
|
|
|
|
val := fieldValue.Uint()
|
|
|
|
|
switch t := fieldValue.Kind(); t {
|
|
|
|
|
case reflect.Uint8:
|
|
|
|
|
return uint8(val), nil
|
|
|
|
|
case reflect.Uint16:
|
|
|
|
|
return uint16(val), nil
|
|
|
|
|
case reflect.Uint32:
|
|
|
|
|
return uint32(val), nil
|
|
|
|
|
case reflect.Uint64:
|
|
|
|
|
return uint64(val), nil
|
|
|
|
|
default:
|
|
|
|
|
return val, nil
|
|
|
|
|
}
|
|
|
|
|
case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
|
|
|
|
|
val := fieldValue.Int()
|
|
|
|
|
switch t := fieldValue.Kind(); t {
|
|
|
|
|
case reflect.Int8:
|
|
|
|
|
return int8(val), nil
|
|
|
|
|
case reflect.Int16:
|
|
|
|
|
return int16(val), nil
|
|
|
|
|
case reflect.Int32:
|
|
|
|
|
return int32(val), nil
|
|
|
|
|
case reflect.Int64:
|
|
|
|
|
return int64(val), nil
|
|
|
|
|
default:
|
|
|
|
|
return val, nil
|
|
|
|
|
}
|
2020-03-09 08:03:59 +00:00
|
|
|
|
default:
|
2023-09-01 15:06:41 +00:00
|
|
|
|
if fieldValue.Interface() == nil && statement.dialect.URI().DBType == schemas.YDB {
|
|
|
|
|
return (*string)(nil), nil
|
|
|
|
|
}
|
2020-03-09 08:03:59 +00:00
|
|
|
|
return fieldValue.Interface(), nil
|
|
|
|
|
}
|
|
|
|
|
}
|