2019-10-11 10:15:36 +02:00
|
|
|
package qrm
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"database/sql"
|
|
|
|
|
"fmt"
|
2023-07-21 14:11:31 +02:00
|
|
|
"github.com/go-jet/jet/v2/internal/utils/must"
|
|
|
|
|
"github.com/go-jet/jet/v2/internal/utils/strslice"
|
2020-11-16 15:51:32 -05:00
|
|
|
"github.com/go-jet/jet/v2/qrm/internal"
|
|
|
|
|
"github.com/google/uuid"
|
2019-10-11 10:15:36 +02:00
|
|
|
"reflect"
|
|
|
|
|
"strings"
|
|
|
|
|
"time"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
var scannerInterfaceType = reflect.TypeOf((*sql.Scanner)(nil)).Elem()
|
|
|
|
|
|
|
|
|
|
func implementsScannerType(fieldType reflect.Type) bool {
|
|
|
|
|
if fieldType.Implements(scannerInterfaceType) {
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
|
2022-02-09 12:34:10 +01:00
|
|
|
fieldTypePtr := reflect.New(fieldType).Type()
|
2019-10-11 10:15:36 +02:00
|
|
|
|
2022-02-09 12:34:10 +01:00
|
|
|
return fieldTypePtr.Implements(scannerInterfaceType)
|
2019-10-11 10:15:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func getScanner(value reflect.Value) sql.Scanner {
|
|
|
|
|
if scanner, ok := value.Interface().(sql.Scanner); ok {
|
|
|
|
|
return scanner
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return value.Addr().Interface().(sql.Scanner)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func getSliceElemType(slicePtrValue reflect.Value) reflect.Type {
|
|
|
|
|
sliceTypePtr := slicePtrValue.Type()
|
2019-10-12 18:45:09 +02:00
|
|
|
elemType := indirectType(sliceTypePtr).Elem()
|
2019-10-11 10:15:36 +02:00
|
|
|
|
2019-10-12 18:45:09 +02:00
|
|
|
return indirectType(elemType)
|
2019-10-11 10:15:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func getSliceElemPtrAt(slicePtrValue reflect.Value, index int) reflect.Value {
|
|
|
|
|
sliceValue := slicePtrValue.Elem()
|
|
|
|
|
elem := sliceValue.Index(index)
|
|
|
|
|
|
|
|
|
|
if elem.Kind() == reflect.Ptr {
|
|
|
|
|
return elem
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return elem.Addr()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func appendElemToSlice(slicePtrValue reflect.Value, objPtrValue reflect.Value) error {
|
2023-07-21 14:11:31 +02:00
|
|
|
must.BeTrue(!slicePtrValue.IsNil(), "jet: internal, slice is nil")
|
2019-10-18 10:09:56 +02:00
|
|
|
|
2019-10-11 10:15:36 +02:00
|
|
|
sliceValue := slicePtrValue.Elem()
|
|
|
|
|
sliceElemType := sliceValue.Type().Elem()
|
|
|
|
|
|
2021-10-21 13:28:01 +02:00
|
|
|
var newSliceElemValue reflect.Value
|
2019-10-11 10:15:36 +02:00
|
|
|
|
2021-10-21 13:28:01 +02:00
|
|
|
if objPtrValue.Type().AssignableTo(sliceElemType) {
|
|
|
|
|
newSliceElemValue = objPtrValue
|
|
|
|
|
} else if objPtrValue.Elem().Type().AssignableTo(sliceElemType) {
|
|
|
|
|
newSliceElemValue = objPtrValue.Elem()
|
2021-10-15 17:43:10 +02:00
|
|
|
} else {
|
2021-10-21 13:28:01 +02:00
|
|
|
newSliceElemValue = reflect.New(sliceElemType).Elem()
|
|
|
|
|
|
|
|
|
|
var err error
|
|
|
|
|
|
|
|
|
|
if newSliceElemValue.Kind() == reflect.Ptr {
|
|
|
|
|
newSliceElemValue.Set(reflect.New(newSliceElemValue.Type().Elem()))
|
2022-02-09 12:34:10 +01:00
|
|
|
err = assign(objPtrValue.Elem(), newSliceElemValue.Elem())
|
2021-10-21 13:28:01 +02:00
|
|
|
} else {
|
2022-02-09 12:34:10 +01:00
|
|
|
err = assign(objPtrValue.Elem(), newSliceElemValue)
|
2021-10-21 13:28:01 +02:00
|
|
|
}
|
2019-10-11 10:15:36 +02:00
|
|
|
|
2021-10-21 13:28:01 +02:00
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("can't append %T to %T slice: %w", objPtrValue.Elem().Interface(), sliceValue.Interface(), err)
|
|
|
|
|
}
|
2019-10-11 10:15:36 +02:00
|
|
|
}
|
|
|
|
|
|
2021-10-15 17:43:10 +02:00
|
|
|
sliceValue.Set(reflect.Append(sliceValue, newSliceElemValue))
|
2019-10-11 10:15:36 +02:00
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func newElemPtrValueForSlice(slicePtrValue reflect.Value) reflect.Value {
|
|
|
|
|
destinationSliceType := slicePtrValue.Type().Elem()
|
|
|
|
|
elemType := indirectType(destinationSliceType.Elem())
|
|
|
|
|
|
|
|
|
|
return reflect.New(elemType)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func getTypeName(structType reflect.Type, parentField *reflect.StructField) string {
|
|
|
|
|
if parentField == nil {
|
|
|
|
|
return structType.Name()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
aliasTag := parentField.Tag.Get("alias")
|
|
|
|
|
|
|
|
|
|
if aliasTag == "" {
|
|
|
|
|
return structType.Name()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
aliasParts := strings.Split(aliasTag, ".")
|
|
|
|
|
|
|
|
|
|
return toCommonIdentifier(aliasParts[0])
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func getTypeAndFieldName(structType string, field reflect.StructField) (string, string) {
|
|
|
|
|
aliasTag := field.Tag.Get("alias")
|
|
|
|
|
|
|
|
|
|
if aliasTag == "" {
|
|
|
|
|
return structType, field.Name
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
aliasParts := strings.Split(aliasTag, ".")
|
|
|
|
|
|
|
|
|
|
if len(aliasParts) == 1 {
|
|
|
|
|
return structType, toCommonIdentifier(aliasParts[0])
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return toCommonIdentifier(aliasParts[0]), toCommonIdentifier(aliasParts[1])
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var replacer = strings.NewReplacer(" ", "", "-", "", "_", "")
|
|
|
|
|
|
|
|
|
|
func toCommonIdentifier(name string) string {
|
|
|
|
|
return strings.ToLower(replacer.Replace(name))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func initializeValueIfNilPtr(value reflect.Value) {
|
|
|
|
|
if !value.IsValid() || !value.CanSet() {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if value.Kind() == reflect.Ptr && value.IsNil() {
|
|
|
|
|
value.Set(reflect.New(value.Type().Elem()))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var timeType = reflect.TypeOf(time.Now())
|
|
|
|
|
var uuidType = reflect.TypeOf(uuid.New())
|
2019-10-18 09:56:38 +02:00
|
|
|
var byteArrayType = reflect.TypeOf([]byte(""))
|
2019-10-11 10:15:36 +02:00
|
|
|
|
|
|
|
|
func isSimpleModelType(objType reflect.Type) bool {
|
|
|
|
|
objType = indirectType(objType)
|
|
|
|
|
|
|
|
|
|
switch objType.Kind() {
|
|
|
|
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
|
|
|
|
|
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
|
|
|
|
|
reflect.Float32, reflect.Float64,
|
|
|
|
|
reflect.String,
|
|
|
|
|
reflect.Bool:
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-18 09:56:38 +02:00
|
|
|
return objType == timeType || objType == uuidType || objType == byteArrayType
|
2019-10-11 10:15:36 +02:00
|
|
|
}
|
|
|
|
|
|
2022-02-09 12:34:10 +01:00
|
|
|
// source can't be pointer
|
|
|
|
|
// destination can be pointer
|
|
|
|
|
func assign(source, destination reflect.Value) error {
|
|
|
|
|
if destination.Kind() == reflect.Ptr {
|
|
|
|
|
if destination.IsNil() {
|
|
|
|
|
initializeValueIfNilPtr(destination)
|
|
|
|
|
}
|
2021-10-21 13:28:01 +02:00
|
|
|
|
2022-02-09 12:34:10 +01:00
|
|
|
destination = destination.Elem()
|
2021-10-21 13:28:01 +02:00
|
|
|
}
|
|
|
|
|
|
2022-02-09 12:34:10 +01:00
|
|
|
err := tryAssign(source, destination)
|
2021-10-21 13:28:01 +02:00
|
|
|
|
2022-02-09 12:34:10 +01:00
|
|
|
if err != nil {
|
|
|
|
|
// needs for the type conversions are rare, so we leave conversion as a last assign step if everything else fails
|
|
|
|
|
if tryConvert(source, destination) {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return err
|
2019-10-11 10:15:36 +02:00
|
|
|
}
|
|
|
|
|
|
2022-02-09 12:34:10 +01:00
|
|
|
return nil
|
2019-10-11 10:15:36 +02:00
|
|
|
}
|
|
|
|
|
|
2022-02-04 12:31:08 +01:00
|
|
|
func assignIfAssignable(source, destination reflect.Value) bool {
|
2022-02-09 12:34:10 +01:00
|
|
|
sourceType := source.Type()
|
|
|
|
|
if sourceType.AssignableTo(destination.Type()) {
|
|
|
|
|
switch sourceType {
|
2022-02-04 12:31:08 +01:00
|
|
|
case byteArrayType:
|
|
|
|
|
destination.SetBytes(cloneBytes(source.Interface().([]byte)))
|
|
|
|
|
default:
|
|
|
|
|
destination.Set(source)
|
|
|
|
|
}
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
2022-02-09 12:34:10 +01:00
|
|
|
// source and destination are non-ptr values
|
2021-10-15 17:43:10 +02:00
|
|
|
func tryAssign(source, destination reflect.Value) error {
|
2021-05-21 16:09:29 +02:00
|
|
|
|
2022-02-04 12:31:08 +01:00
|
|
|
if assignIfAssignable(source, destination) {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2021-10-15 17:43:10 +02:00
|
|
|
sourceInterface := source.Interface()
|
|
|
|
|
|
2022-02-09 12:34:10 +01:00
|
|
|
switch destination.Type().Kind() {
|
|
|
|
|
case reflect.Bool:
|
2021-10-15 17:43:10 +02:00
|
|
|
var nullBool internal.NullBool
|
|
|
|
|
|
|
|
|
|
err := nullBool.Scan(sourceInterface)
|
|
|
|
|
|
2020-11-16 15:33:34 -05:00
|
|
|
if err != nil {
|
2021-10-15 17:43:10 +02:00
|
|
|
return err
|
2020-11-16 15:33:34 -05:00
|
|
|
}
|
2021-05-21 16:09:29 +02:00
|
|
|
|
2021-10-15 17:43:10 +02:00
|
|
|
destination.SetBool(nullBool.Bool)
|
|
|
|
|
|
2022-02-09 12:34:10 +01:00
|
|
|
case reflect.Float32, reflect.Float64:
|
2021-10-15 17:43:10 +02:00
|
|
|
var nullFloat sql.NullFloat64
|
|
|
|
|
|
|
|
|
|
err := nullFloat.Scan(sourceInterface)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
2021-05-21 16:09:29 +02:00
|
|
|
}
|
2020-11-16 15:33:34 -05:00
|
|
|
|
2021-10-15 17:43:10 +02:00
|
|
|
if nullFloat.Valid {
|
|
|
|
|
destination.SetFloat(nullFloat.Float64)
|
|
|
|
|
}
|
2022-02-09 12:34:10 +01:00
|
|
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
2021-10-15 17:43:10 +02:00
|
|
|
var integer sql.NullInt64
|
2019-10-11 10:15:36 +02:00
|
|
|
|
2021-10-15 17:43:10 +02:00
|
|
|
err := integer.Scan(sourceInterface)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
2019-10-11 10:15:36 +02:00
|
|
|
|
2021-10-15 17:43:10 +02:00
|
|
|
if integer.Valid {
|
|
|
|
|
destination.SetInt(integer.Int64)
|
|
|
|
|
}
|
2019-10-11 10:15:36 +02:00
|
|
|
|
2022-02-09 12:34:10 +01:00
|
|
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
2021-10-15 17:43:10 +02:00
|
|
|
var uInt internal.NullUInt64
|
2019-10-11 10:15:36 +02:00
|
|
|
|
2021-10-15 17:43:10 +02:00
|
|
|
err := uInt.Scan(sourceInterface)
|
2019-10-11 10:15:36 +02:00
|
|
|
|
2021-10-15 17:43:10 +02:00
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
2019-10-11 10:15:36 +02:00
|
|
|
|
2021-10-15 17:43:10 +02:00
|
|
|
if uInt.Valid {
|
|
|
|
|
destination.SetUint(uInt.UInt64)
|
2019-10-11 10:15:36 +02:00
|
|
|
}
|
2021-10-15 17:43:10 +02:00
|
|
|
|
2022-02-09 12:34:10 +01:00
|
|
|
case reflect.String:
|
2021-10-15 17:43:10 +02:00
|
|
|
var str sql.NullString
|
|
|
|
|
|
|
|
|
|
err := str.Scan(sourceInterface)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
2019-10-11 10:15:36 +02:00
|
|
|
}
|
|
|
|
|
|
2021-10-15 17:43:10 +02:00
|
|
|
if str.Valid {
|
|
|
|
|
destination.SetString(str.String)
|
2019-10-11 10:15:36 +02:00
|
|
|
}
|
2021-10-15 17:43:10 +02:00
|
|
|
|
|
|
|
|
default:
|
2022-02-09 12:34:10 +01:00
|
|
|
switch destination.Interface().(type) {
|
|
|
|
|
case time.Time:
|
|
|
|
|
var nullTime internal.NullTime
|
|
|
|
|
|
|
|
|
|
err := nullTime.Scan(sourceInterface)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if nullTime.Valid {
|
|
|
|
|
destination.Set(reflect.ValueOf(nullTime.Time))
|
|
|
|
|
}
|
|
|
|
|
default:
|
|
|
|
|
return fmt.Errorf("can't assign %T to %T", sourceInterface, destination.Interface())
|
|
|
|
|
}
|
2019-10-11 10:15:36 +02:00
|
|
|
}
|
|
|
|
|
|
2021-10-15 17:43:10 +02:00
|
|
|
return nil
|
2019-10-11 10:15:36 +02:00
|
|
|
}
|
|
|
|
|
|
2022-02-09 12:34:10 +01:00
|
|
|
func tryConvert(source, destination reflect.Value) bool {
|
|
|
|
|
destinationType := destination.Type()
|
2019-10-11 10:15:36 +02:00
|
|
|
|
2022-02-09 12:34:10 +01:00
|
|
|
if source.Type().ConvertibleTo(destinationType) {
|
|
|
|
|
source = source.Convert(destinationType)
|
|
|
|
|
return assignIfAssignable(source, destination)
|
2022-02-04 12:31:08 +01:00
|
|
|
}
|
|
|
|
|
|
2022-02-09 12:34:10 +01:00
|
|
|
return false
|
|
|
|
|
}
|
2019-10-11 10:15:36 +02:00
|
|
|
|
2022-02-09 12:34:10 +01:00
|
|
|
func setZeroValue(value reflect.Value) {
|
|
|
|
|
if !value.IsZero() {
|
|
|
|
|
value.Set(reflect.Zero(value.Type()))
|
2019-10-11 10:15:36 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func isPrimaryKey(field reflect.StructField, primaryKeyOverwrites []string) bool {
|
|
|
|
|
if len(primaryKeyOverwrites) > 0 {
|
2023-07-21 14:11:31 +02:00
|
|
|
return strslice.Contains(primaryKeyOverwrites, field.Name)
|
2019-10-11 10:15:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sqlTag := field.Tag.Get("sql")
|
|
|
|
|
|
|
|
|
|
return sqlTag == "primary_key"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func parentFieldPrimaryKeyOverwrite(parentField *reflect.StructField) []string {
|
|
|
|
|
if parentField == nil {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sqlTag := parentField.Tag.Get("sql")
|
|
|
|
|
|
|
|
|
|
if !strings.HasPrefix(sqlTag, "primary_key") {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
parts := strings.Split(sqlTag, "=")
|
|
|
|
|
|
|
|
|
|
if len(parts) < 2 {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return strings.Split(parts[1], ",")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func indirectType(reflectType reflect.Type) reflect.Type {
|
|
|
|
|
if reflectType.Kind() != reflect.Ptr {
|
|
|
|
|
return reflectType
|
|
|
|
|
}
|
|
|
|
|
return reflectType.Elem()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func fieldToString(field *reflect.StructField) string {
|
|
|
|
|
if field == nil {
|
|
|
|
|
return ""
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return " at '" + field.Name + " " + field.Type.String() + "'"
|
|
|
|
|
}
|
2021-10-15 17:43:10 +02:00
|
|
|
|
|
|
|
|
func cloneBytes(b []byte) []byte {
|
|
|
|
|
if b == nil {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
c := make([]byte, len(b))
|
|
|
|
|
copy(c, b)
|
|
|
|
|
return c
|
|
|
|
|
}
|
2022-02-09 12:34:10 +01:00
|
|
|
|
|
|
|
|
func concat(stringList ...string) string {
|
|
|
|
|
var b strings.Builder
|
2023-04-14 12:20:36 +02:00
|
|
|
|
2023-04-17 11:12:37 +02:00
|
|
|
var length int
|
|
|
|
|
for i := 0; i < len(stringList); i++ {
|
|
|
|
|
length += len(stringList[i])
|
2022-02-09 12:34:10 +01:00
|
|
|
}
|
2023-04-14 12:20:36 +02:00
|
|
|
|
2023-04-17 11:12:37 +02:00
|
|
|
b.Grow(length)
|
2023-04-14 12:20:36 +02:00
|
|
|
|
2023-04-17 11:12:37 +02:00
|
|
|
for _, str := range stringList {
|
|
|
|
|
b.WriteString(str)
|
2023-04-14 12:20:36 +02:00
|
|
|
}
|
2023-04-17 11:12:37 +02:00
|
|
|
return b.String()
|
2023-04-14 12:20:36 +02:00
|
|
|
}
|