improve data converting for package gdb, drivers/pgsql

This commit is contained in:
John Guo
2022-05-18 11:55:09 +08:00
parent 4556dda038
commit 3430cf1f17
6 changed files with 79 additions and 28 deletions

View File

@ -37,10 +37,10 @@ func Test_Func_ConvertDataForRecord(t *testing.T) {
}
gtest.C(t, func(t *gtest.T) {
c := &gdb.Core{}
m := c.ConvertDataForRecord(nil, new(Test))
m, err := c.ConvertDataForRecord(nil, new(Test))
t.AssertNil(err)
t.Assert(len(m), 1)
t.AssertNE(m["reset_password_token_at"], nil)
t.Assert(m["reset_password_token_at"], new(mysql.NullTime))
t.Assert(m["reset_password_token_at"], nil)
})
}

View File

@ -170,7 +170,7 @@ type DB interface {
GetChars() (charLeft string, charRight string) // See Core.GetChars.
Tables(ctx context.Context, schema ...string) (tables []string, err error) // See Core.Tables.
TableFields(ctx context.Context, table string, schema ...string) (map[string]*TableField, error) // See Core.TableFields.
ConvertDataForRecord(ctx context.Context, data interface{}) map[string]interface{} // See Core.ConvertDataForRecord
ConvertDataForRecord(ctx context.Context, data interface{}) (map[string]interface{}, error) // See Core.ConvertDataForRecord
FilteredLink() string // FilteredLink is used for filtering sensitive information in `Link` configuration before output it to tracing server.
}

View File

@ -552,7 +552,7 @@ func (c *Core) DoUpdate(ctx context.Context, link Link, table string, data inter
case reflect.Map, reflect.Struct:
var (
fields []string
dataMap = c.db.ConvertDataForRecord(ctx, data)
dataMap map[string]interface{}
counterHandler = func(column string, counter Counter) {
if counter.Value != 0 {
column = c.QuoteWord(column)
@ -570,7 +570,10 @@ func (c *Core) DoUpdate(ctx context.Context, link Link, table string, data inter
}
}
)
dataMap, err = c.db.ConvertDataForRecord(ctx, data)
if err != nil {
return nil, err
}
for k, v := range dataMap {
switch value := v.(type) {
case *Counter:

View File

@ -14,6 +14,7 @@ import (
"time"
"github.com/gogf/gf/v2/encoding/gbinary"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/internal/json"
"github.com/gogf/gf/v2/os/gtime"
"github.com/gogf/gf/v2/text/gregex"
@ -27,27 +28,33 @@ import (
//
// The parameter `value` should be type of *map/map/*struct/struct.
// It supports embedded struct definition for struct.
func (c *Core) ConvertDataForRecord(ctx context.Context, value interface{}) map[string]interface{} {
var data = DataToMapDeep(value)
func (c *Core) ConvertDataForRecord(ctx context.Context, value interface{}) (map[string]interface{}, error) {
var (
err error
data = DataToMapDeep(value)
)
for k, v := range data {
data[k] = c.ConvertDataForRecordValue(ctx, v)
data[k], err = c.ConvertDataForRecordValue(ctx, v)
if err != nil {
return nil, gerror.Wrapf(err, `ConvertDataForRecordValue failed for value: %#v`, v)
}
}
return data
return data, nil
}
func (c *Core) ConvertDataForRecordValue(ctx context.Context, value interface{}) interface{} {
func (c *Core) ConvertDataForRecordValue(ctx context.Context, value interface{}) (interface{}, error) {
var (
err error
convertedValue interface{}
convertedValue = value
)
// If `value` implements interface `driver.Valuer`, it then uses the interface for value converting.
if valuer, ok := value.(driver.Valuer); ok {
if convertedValue, err = valuer.Value(); err != nil {
if err != nil {
return nil
return nil, err
}
}
return convertedValue
return convertedValue, nil
}
// Default value converting.
var (
@ -63,7 +70,10 @@ func (c *Core) ConvertDataForRecordValue(ctx context.Context, value interface{})
// It should ignore the bytes type.
if _, ok := value.([]byte); !ok {
// Convert the value to JSON.
convertedValue, _ = json.Marshal(value)
convertedValue, err = json.Marshal(value)
if err != nil {
return nil, err
}
}
case reflect.Struct:
@ -97,11 +107,14 @@ func (c *Core) ConvertDataForRecordValue(ctx context.Context, value interface{})
convertedValue = s.String()
} else {
// Convert the value to JSON.
convertedValue, _ = json.Marshal(value)
convertedValue, err = json.Marshal(value)
if err != nil {
return nil, err
}
}
}
}
return convertedValue
return convertedValue, nil
}
// convertFieldValueToLocalValue automatically checks and converts field value from database type

View File

@ -40,6 +40,7 @@ func (m *Model) Batch(batch int) *Model {
// Data(g.Slice{g.Map{"uid": 10000, "name":"john"}, g.Map{"uid": 20000, "name":"smith"}).
func (m *Model) Data(data ...interface{}) *Model {
var (
err error
ctx = m.GetCtx()
model = m.getModel()
)
@ -87,7 +88,10 @@ func (m *Model) Data(data ...interface{}) *Model {
}
list := make(List, reflectInfo.OriginValue.Len())
for i := 0; i < reflectInfo.OriginValue.Len(); i++ {
list[i] = m.db.ConvertDataForRecord(ctx, reflectInfo.OriginValue.Index(i).Interface())
list[i], err = m.db.ConvertDataForRecord(ctx, reflectInfo.OriginValue.Index(i).Interface())
if err != nil {
panic(err)
}
}
model.data = list
@ -104,15 +108,24 @@ func (m *Model) Data(data ...interface{}) *Model {
list = make(List, len(array))
)
for i := 0; i < len(array); i++ {
list[i] = m.db.ConvertDataForRecord(ctx, array[i])
list[i], err = m.db.ConvertDataForRecord(ctx, array[i])
if err != nil {
panic(err)
}
}
model.data = list
} else {
model.data = m.db.ConvertDataForRecord(ctx, data[0])
model.data, err = m.db.ConvertDataForRecord(ctx, data[0])
if err != nil {
panic(err)
}
}
case reflect.Map:
model.data = m.db.ConvertDataForRecord(ctx, data[0])
model.data, err = m.db.ConvertDataForRecord(ctx, data[0])
if err != nil {
panic(err)
}
default:
model.data = data[0]
@ -255,11 +268,18 @@ func (m *Model) doInsertWithOption(ctx context.Context, insertOption int) (resul
case List:
list = value
for i, v := range list {
list[i] = m.db.ConvertDataForRecord(ctx, v)
list[i], err = m.db.ConvertDataForRecord(ctx, v)
if err != nil {
return nil, err
}
}
case Map:
list = List{m.db.ConvertDataForRecord(ctx, value)}
var listItem map[string]interface{}
if listItem, err = m.db.ConvertDataForRecord(ctx, value); err != nil {
return nil, err
}
list = List{listItem}
default:
reflectInfo := reflection.OriginValueAndKind(newData)
@ -268,21 +288,32 @@ func (m *Model) doInsertWithOption(ctx context.Context, insertOption int) (resul
case reflect.Slice, reflect.Array:
list = make(List, reflectInfo.OriginValue.Len())
for i := 0; i < reflectInfo.OriginValue.Len(); i++ {
list[i] = m.db.ConvertDataForRecord(ctx, reflectInfo.OriginValue.Index(i).Interface())
list[i], err = m.db.ConvertDataForRecord(ctx, reflectInfo.OriginValue.Index(i).Interface())
}
case reflect.Map:
list = List{m.db.ConvertDataForRecord(ctx, value)}
var listItem map[string]interface{}
if listItem, err = m.db.ConvertDataForRecord(ctx, value); err != nil {
return nil, err
}
list = List{listItem}
case reflect.Struct:
if v, ok := value.(iInterfaces); ok {
array := v.Interfaces()
list = make(List, len(array))
for i := 0; i < len(array); i++ {
list[i] = m.db.ConvertDataForRecord(ctx, array[i])
list[i], err = m.db.ConvertDataForRecord(ctx, array[i])
if err != nil {
return nil, err
}
}
} else {
list = List{m.db.ConvertDataForRecord(ctx, value)}
var listItem map[string]interface{}
if listItem, err = m.db.ConvertDataForRecord(ctx, value); err != nil {
return nil, err
}
list = List{listItem}
}
default:

View File

@ -51,7 +51,11 @@ func (m *Model) Update(dataAndWhere ...interface{}) (result sql.Result, err erro
)
switch reflectInfo.OriginKind {
case reflect.Map, reflect.Struct:
dataMap := m.db.ConvertDataForRecord(ctx, m.data)
var dataMap map[string]interface{}
dataMap, err = m.db.ConvertDataForRecord(ctx, m.data)
if err != nil {
return nil, err
}
// Automatically update the record updating time.
if !m.unscoped && fieldNameUpdate != "" {
dataMap[fieldNameUpdate] = gtime.Now().String()