Add Serializer Scan support
This commit is contained in:
parent
6393ad5222
commit
bf85076f0e
@ -40,14 +40,17 @@ type SavePointerDialectorInterface interface {
|
||||
RollbackTo(tx *DB, name string) error
|
||||
}
|
||||
|
||||
// TxBeginner tx beginner
|
||||
type TxBeginner interface {
|
||||
BeginTx(ctx context.Context, opts *sql.TxOptions) (*sql.Tx, error)
|
||||
}
|
||||
|
||||
// ConnPoolBeginner conn pool beginner
|
||||
type ConnPoolBeginner interface {
|
||||
BeginTx(ctx context.Context, opts *sql.TxOptions) (ConnPool, error)
|
||||
}
|
||||
|
||||
// TxCommitter tx commiter
|
||||
type TxCommitter interface {
|
||||
Commit() error
|
||||
Rollback() error
|
||||
@ -58,6 +61,7 @@ type Valuer interface {
|
||||
GormValue(context.Context, *DB) clause.Expr
|
||||
}
|
||||
|
||||
// GetDBConnector SQL db connector
|
||||
type GetDBConnector interface {
|
||||
GetDBConn() (*sql.DB, error)
|
||||
}
|
||||
|
@ -84,6 +84,7 @@ type Field struct {
|
||||
ReflectValueOf func(context.Context, reflect.Value) reflect.Value
|
||||
ValueOf func(context.Context, reflect.Value) (value interface{}, zero bool)
|
||||
Set func(context.Context, reflect.Value, interface{}) error
|
||||
Serializer SerializerInterface
|
||||
NewValuePool FieldNewValuePool
|
||||
}
|
||||
|
||||
@ -168,10 +169,16 @@ func (schema *Schema) ParseField(fieldStruct reflect.StructField) *Field {
|
||||
}
|
||||
}
|
||||
|
||||
if _, isSerializer := fieldValue.Interface().(SerializerInterface); isSerializer {
|
||||
if v, isSerializer := fieldValue.Interface().(SerializerInterface); isSerializer {
|
||||
field.DataType = String
|
||||
} else if _, ok := field.TagSettings["SERIALIZER"]; ok {
|
||||
field.Serializer = v
|
||||
} else if name, ok := field.TagSettings["SERIALIZER"]; ok {
|
||||
field.DataType = String
|
||||
if strings.ToLower(name) == "json" {
|
||||
field.Serializer = &JSONSerializer{}
|
||||
} else {
|
||||
schema.err = fmt.Errorf("invalid serializer type %v", name)
|
||||
}
|
||||
}
|
||||
|
||||
if num, ok := field.TagSettings["AUTOINCREMENTINCREMENT"]; ok {
|
||||
@ -413,69 +420,21 @@ func (schema *Schema) ParseField(fieldStruct reflect.StructField) *Field {
|
||||
return field
|
||||
}
|
||||
|
||||
// sync pools
|
||||
var (
|
||||
normalPool sync.Map
|
||||
stringPool = &sync.Pool{
|
||||
New: func() interface{} {
|
||||
var v string
|
||||
ptrV := &v
|
||||
return &ptrV
|
||||
},
|
||||
}
|
||||
intPool = &sync.Pool{
|
||||
New: func() interface{} {
|
||||
var v int64
|
||||
ptrV := &v
|
||||
return &ptrV
|
||||
},
|
||||
}
|
||||
uintPool = &sync.Pool{
|
||||
New: func() interface{} {
|
||||
var v uint64
|
||||
ptrV := &v
|
||||
return &ptrV
|
||||
},
|
||||
}
|
||||
floatPool = &sync.Pool{
|
||||
New: func() interface{} {
|
||||
var v float64
|
||||
ptrV := &v
|
||||
return &ptrV
|
||||
},
|
||||
}
|
||||
boolPool = &sync.Pool{
|
||||
New: func() interface{} {
|
||||
var v bool
|
||||
ptrV := &v
|
||||
return &ptrV
|
||||
},
|
||||
}
|
||||
timePool = &sync.Pool{
|
||||
New: func() interface{} {
|
||||
var v time.Time
|
||||
ptrV := &v
|
||||
return &ptrV
|
||||
},
|
||||
}
|
||||
poolInitializer = func(reflectType reflect.Type) FieldNewValuePool {
|
||||
if v, ok := normalPool.Load(reflectType); ok {
|
||||
return v.(FieldNewValuePool)
|
||||
}
|
||||
|
||||
v, _ := normalPool.LoadOrStore(reflectType, &sync.Pool{
|
||||
New: func() interface{} {
|
||||
return reflect.New(reflectType).Interface()
|
||||
},
|
||||
})
|
||||
return v.(FieldNewValuePool)
|
||||
}
|
||||
)
|
||||
|
||||
// create valuer, setter when parse struct
|
||||
func (field *Field) setupValuerAndSetter() {
|
||||
// Setup NewValuePool
|
||||
if _, ok := reflect.New(field.IndirectFieldType).Interface().(sql.Scanner); !ok {
|
||||
var fieldValue = reflect.New(field.FieldType).Interface()
|
||||
if field.Serializer != nil {
|
||||
field.NewValuePool = &sync.Pool{
|
||||
New: func() interface{} {
|
||||
return &Serializer{
|
||||
Field: field,
|
||||
Interface: reflect.New(reflect.ValueOf(field.Serializer).Type()).Interface().(SerializerInterface),
|
||||
}
|
||||
},
|
||||
}
|
||||
} else if _, ok := fieldValue.(sql.Scanner); !ok {
|
||||
// set default NewValuePool
|
||||
switch field.IndirectFieldType.Kind() {
|
||||
case reflect.String:
|
||||
field.NewValuePool = stringPool
|
||||
@ -595,6 +554,19 @@ func (field *Field) setupValuerAndSetter() {
|
||||
}
|
||||
|
||||
// Set
|
||||
if field.Serializer != nil {
|
||||
field.Set = func(ctx context.Context, value reflect.Value, v interface{}) error {
|
||||
if serializer, ok := v.(*Serializer); ok {
|
||||
serializer.Interface.Scan(ctx, field, value, serializer.value)
|
||||
fallbackSetter(ctx, value, serializer.Interface, field.Set)
|
||||
serializer.Interface = reflect.New(reflect.ValueOf(field.Serializer).Type()).Interface().(SerializerInterface)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
switch field.FieldType.Kind() {
|
||||
case reflect.Bool:
|
||||
field.Set = func(ctx context.Context, value reflect.Value, v interface{}) error {
|
||||
|
@ -3,6 +3,9 @@ package schema
|
||||
import (
|
||||
"context"
|
||||
"database/sql/driver"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"gorm.io/gorm/clause"
|
||||
@ -19,30 +22,19 @@ type FieldNewValuePool interface {
|
||||
Put(interface{})
|
||||
}
|
||||
|
||||
type fieldNewValuePool struct {
|
||||
getter func() interface{}
|
||||
putter func(interface{})
|
||||
}
|
||||
|
||||
func (fp fieldNewValuePool) Get() interface{} {
|
||||
return fp.getter()
|
||||
}
|
||||
|
||||
func (fp fieldNewValuePool) Put(v interface{}) {
|
||||
fp.putter(v)
|
||||
}
|
||||
|
||||
// Serializer field value serializer
|
||||
type Serializer struct {
|
||||
Field *Field
|
||||
Interface SerializerInterface
|
||||
Destination reflect.Value
|
||||
Context context.Context
|
||||
value interface{}
|
||||
}
|
||||
|
||||
// Scan implements sql.Scanner interface
|
||||
func (s *Serializer) Scan(value interface{}) error {
|
||||
return s.Interface.Scan(s.Context, s.Field, s.Destination, value)
|
||||
s.value = value
|
||||
return nil
|
||||
}
|
||||
|
||||
// Value implements driver.Valuer interface
|
||||
@ -56,6 +48,38 @@ type SerializerInterface interface {
|
||||
Value(ctx context.Context, field *Field, dst reflect.Value) (interface{}, error)
|
||||
}
|
||||
|
||||
// JSONSerializer json serializer
|
||||
type JSONSerializer struct {
|
||||
}
|
||||
|
||||
// Scan implements serializer interface
|
||||
func (JSONSerializer) Scan(ctx context.Context, field *Field, dst reflect.Value, dbValue interface{}) (err error) {
|
||||
fieldValue := reflect.New(field.FieldType)
|
||||
|
||||
if dbValue != nil {
|
||||
var bytes []byte
|
||||
switch v := dbValue.(type) {
|
||||
case []byte:
|
||||
bytes = v
|
||||
case string:
|
||||
bytes = []byte(v)
|
||||
default:
|
||||
return errors.New(fmt.Sprint("Failed to unmarshal JSONB value:", dbValue))
|
||||
}
|
||||
|
||||
err = json.Unmarshal(bytes, fieldValue.Interface())
|
||||
}
|
||||
|
||||
field.ReflectValueOf(ctx, dst).Set(fieldValue.Elem())
|
||||
return
|
||||
}
|
||||
|
||||
// Value implements serializer interface
|
||||
func (JSONSerializer) Value(ctx context.Context, field *Field, dst reflect.Value) (interface{}, error) {
|
||||
fv, _ := field.ValueOf(ctx, dst)
|
||||
return fv, nil
|
||||
}
|
||||
|
||||
// CreateClausesInterface create clauses interface
|
||||
type CreateClausesInterface interface {
|
||||
CreateClauses(*Field) []clause.Interface
|
||||
|
62
schema/pool.go
Normal file
62
schema/pool.go
Normal file
@ -0,0 +1,62 @@
|
||||
package schema
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// sync pools
|
||||
var (
|
||||
normalPool sync.Map
|
||||
stringPool = &sync.Pool{
|
||||
New: func() interface{} {
|
||||
var v string
|
||||
ptrV := &v
|
||||
return &ptrV
|
||||
},
|
||||
}
|
||||
intPool = &sync.Pool{
|
||||
New: func() interface{} {
|
||||
var v int64
|
||||
ptrV := &v
|
||||
return &ptrV
|
||||
},
|
||||
}
|
||||
uintPool = &sync.Pool{
|
||||
New: func() interface{} {
|
||||
var v uint64
|
||||
ptrV := &v
|
||||
return &ptrV
|
||||
},
|
||||
}
|
||||
floatPool = &sync.Pool{
|
||||
New: func() interface{} {
|
||||
var v float64
|
||||
ptrV := &v
|
||||
return &ptrV
|
||||
},
|
||||
}
|
||||
boolPool = &sync.Pool{
|
||||
New: func() interface{} {
|
||||
var v bool
|
||||
ptrV := &v
|
||||
return &ptrV
|
||||
},
|
||||
}
|
||||
timePool = &sync.Pool{
|
||||
New: func() interface{} {
|
||||
var v time.Time
|
||||
ptrV := &v
|
||||
return &ptrV
|
||||
},
|
||||
}
|
||||
poolInitializer = func(reflectType reflect.Type) FieldNewValuePool {
|
||||
v, _ := normalPool.LoadOrStore(reflectType, &sync.Pool{
|
||||
New: func() interface{} {
|
||||
return reflect.New(reflectType).Interface()
|
||||
},
|
||||
})
|
||||
return v.(FieldNewValuePool)
|
||||
}
|
||||
)
|
Loading…
x
Reference in New Issue
Block a user