Skip to content

Commit

Permalink
fix: cache value assertion panic if the cache adapter is not in-memor…
Browse files Browse the repository at this point in the history
…y for soft time feature of `package gdb`; improve converting performance for `gconv.Scan` (#3351)
  • Loading branch information
gqcn authored Mar 7, 2024
1 parent cab6b89 commit 607f079
Show file tree
Hide file tree
Showing 16 changed files with 797 additions and 709 deletions.
2 changes: 2 additions & 0 deletions database/gdb/gdb.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
// You can obtain one at https://github.com/gogf/gf.

// Package gdb provides ORM features for popular relationship databases.
//
// TODO use context.Context as required parameter for all DB operations.
package gdb

import (
Expand Down
9 changes: 1 addition & 8 deletions database/gdb/gdb_model_cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import (
"time"

"github.com/gogf/gf/v2/internal/intlog"
"github.com/gogf/gf/v2/internal/json"
)

// CacheOption is options for model cache control in query.
Expand Down Expand Up @@ -67,7 +66,6 @@ func (m *Model) getSelectResultFromCache(ctx context.Context, sql string, args .
return
}
var (
ok bool
cacheItem *selectCacheItem
cacheKey = m.makeSelectCacheKey(sql, args...)
cacheObj = m.db.GetCache()
Expand All @@ -82,12 +80,7 @@ func (m *Model) getSelectResultFromCache(ctx context.Context, sql string, args .
}
}()
if v, _ := cacheObj.Get(ctx, cacheKey); !v.IsNil() {
if cacheItem, ok = v.Val().(*selectCacheItem); ok {
// In-memory cache.
return cacheItem.Result, nil
}
// Other cache, it needs conversion.
if err = json.UnmarshalUseNumber(v.Bytes(), &cacheItem); err != nil {
if err = v.Scan(&cacheItem); err != nil {
return nil, err
}
return cacheItem.Result, nil
Expand Down
5 changes: 4 additions & 1 deletion database/gdb/gdb_model_soft_time.go
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,10 @@ func (m *softTimeMaintainer) getSoftFieldNameAndType(
intlog.Error(ctx, err)
}
if result != nil {
var cacheItem = result.Val().(getSoftFieldNameAndTypeCacheItem)
var cacheItem getSoftFieldNameAndTypeCacheItem
if err = result.Scan(&cacheItem); err != nil {
return "", ""
}
fieldName = cacheItem.FieldName
fieldType = cacheItem.FieldType
}
Expand Down
36 changes: 9 additions & 27 deletions util/gconv/gconv_maptomap.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,13 @@ import (

"github.com/gogf/gf/v2/errors/gcode"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/internal/json"
)

// MapToMap converts any map type variable `params` to another map type variable `pointer`
// using reflect.
// See doMapToMap.
func MapToMap(params interface{}, pointer interface{}, mapping ...map[string]string) error {
return doMapToMap(params, pointer, mapping...)
return Scan(params, pointer, mapping...)
}

// doMapToMap converts any map type variable `params` to another map type variable `pointer`.
Expand All @@ -32,29 +31,6 @@ func MapToMap(params interface{}, pointer interface{}, mapping ...map[string]str
// The optional parameter `mapping` is used for struct attribute to map key mapping, which makes
// sense only if the items of original map `params` is type struct.
func doMapToMap(params interface{}, pointer interface{}, mapping ...map[string]string) (err error) {
// If given `params` is JSON, it then uses json.Unmarshal doing the converting.
switch r := params.(type) {
case []byte:
if json.Valid(r) {
if rv, ok := pointer.(reflect.Value); ok {
if rv.Kind() == reflect.Ptr {
return json.UnmarshalUseNumber(r, rv.Interface())
}
} else {
return json.UnmarshalUseNumber(r, pointer)
}
}
case string:
if paramsBytes := []byte(r); json.Valid(paramsBytes) {
if rv, ok := pointer.(reflect.Value); ok {
if rv.Kind() == reflect.Ptr {
return json.UnmarshalUseNumber(paramsBytes, rv.Interface())
}
} else {
return json.UnmarshalUseNumber(paramsBytes, pointer)
}
}
}
var (
paramsRv reflect.Value
paramsKind reflect.Kind
Expand Down Expand Up @@ -92,7 +68,11 @@ func doMapToMap(params interface{}, pointer interface{}, mapping ...map[string]s
pointerKind = pointerRv.Kind()
}
if pointerKind != reflect.Map {
return gerror.NewCodef(gcode.CodeInvalidParameter, "pointer should be type of *map, but got:%s", pointerKind)
return gerror.NewCodef(
gcode.CodeInvalidParameter,
`destination pointer should be type of *map, but got: %s`,
pointerKind,
)
}
defer func() {
// Catch the panic, especially the reflection operation panics.
Expand All @@ -119,7 +99,9 @@ func doMapToMap(params interface{}, pointer interface{}, mapping ...map[string]s
mapValue := reflect.New(pointerValueType).Elem()
switch pointerValueKind {
case reflect.Map, reflect.Struct:
if err = doStruct(paramsRv.MapIndex(key).Interface(), mapValue, keyToAttributeNameMapping, ""); err != nil {
if err = doStruct(
paramsRv.MapIndex(key).Interface(), mapValue, keyToAttributeNameMapping, "",
); err != nil {
return err
}
default:
Expand Down
41 changes: 13 additions & 28 deletions util/gconv/gconv_maptomaps.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,12 @@ import (

"github.com/gogf/gf/v2/errors/gcode"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/internal/json"
)

// MapToMaps converts any slice type variable `params` to another map slice type variable `pointer`.
// See doMapToMaps.
func MapToMaps(params interface{}, pointer interface{}, mapping ...map[string]string) error {
return doMapToMaps(params, pointer, mapping...)
return Scan(params, pointer, mapping...)
}

// doMapToMaps converts any map type variable `params` to another map slice variable `pointer`.
Expand All @@ -29,29 +28,6 @@ func MapToMaps(params interface{}, pointer interface{}, mapping ...map[string]st
// The optional parameter `mapping` is used for struct attribute to map key mapping, which makes
// sense only if the item of `params` is type struct.
func doMapToMaps(params interface{}, pointer interface{}, paramKeyToAttrMap ...map[string]string) (err error) {
// If given `params` is JSON, it then uses json.Unmarshal doing the converting.
switch r := params.(type) {
case []byte:
if json.Valid(r) {
if rv, ok := pointer.(reflect.Value); ok {
if rv.Kind() == reflect.Ptr {
return json.UnmarshalUseNumber(r, rv.Interface())
}
} else {
return json.UnmarshalUseNumber(r, pointer)
}
}
case string:
if paramsBytes := []byte(r); json.Valid(paramsBytes) {
if rv, ok := pointer.(reflect.Value); ok {
if rv.Kind() == reflect.Ptr {
return json.UnmarshalUseNumber(paramsBytes, rv.Interface())
}
} else {
return json.UnmarshalUseNumber(paramsBytes, pointer)
}
}
}
// Params and its element type check.
var (
paramsRv reflect.Value
Expand All @@ -68,7 +44,10 @@ func doMapToMaps(params interface{}, pointer interface{}, paramKeyToAttrMap ...m
paramsKind = paramsRv.Kind()
}
if paramsKind != reflect.Array && paramsKind != reflect.Slice {
return gerror.NewCode(gcode.CodeInvalidParameter, "params should be type of slice, eg: []map/[]*map/[]struct/[]*struct")
return gerror.NewCode(
gcode.CodeInvalidParameter,
"params should be type of slice, example: []map/[]*map/[]struct/[]*struct",
)
}
var (
paramsElem = paramsRv.Type().Elem()
Expand All @@ -78,8 +57,14 @@ func doMapToMaps(params interface{}, pointer interface{}, paramKeyToAttrMap ...m
paramsElem = paramsElem.Elem()
paramsElemKind = paramsElem.Kind()
}
if paramsElemKind != reflect.Map && paramsElemKind != reflect.Struct && paramsElemKind != reflect.Interface {
return gerror.NewCodef(gcode.CodeInvalidParameter, "params element should be type of map/*map/struct/*struct, but got: %s", paramsElemKind)
if paramsElemKind != reflect.Map &&
paramsElemKind != reflect.Struct &&
paramsElemKind != reflect.Interface {
return gerror.NewCodef(
gcode.CodeInvalidParameter,
"params element should be type of map/*map/struct/*struct, but got: %s",
paramsElemKind,
)
}
// Empty slice, no need continue.
if paramsRv.Len() == 0 {
Expand Down
Loading

0 comments on commit 607f079

Please sign in to comment.