mirror of
https://gitee.com/johng/gf
synced 2026-06-06 16:21:40 +08:00
This pull request standardizes the use of the Go 1.18+ `any` type alias
instead of `interface{}` throughout the codebase. The change improves
code readability and aligns with modern Go best practices. The update
touches many files, including core data structures, code generation
templates, logging utilities, and test data, ensuring consistency across
all usages.
**Type alias migration to `any`:**
* Replaced all instances of `interface{}` with `any` in core data
structures such as `garray` and in generated model structs (e.g.,
`TableUser`, `User1`, `User2`) to modernize type usage.
[[1]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L31-R31)
[[2]](diffhunk://#diff-6c19859cb32c7516ea95ddc8f8235460818eb2f24d2204308e0d9e1b19e7d90fL15-R19)
[[3]](diffhunk://#diff-a15ba2f5e830b4833c47b902515a4f9e5a4f83a3707698f3229b307ec3776b41L15-R18)
[[4]](diffhunk://#diff-52e0837e84d49221d1b810d88fdf78221f36cffcd664fb42f8aba49a79b974dcL15-R19)
[[5]](diffhunk://#diff-11c3457d1a23a4ca6ecd00d6b856289774936b6a708384cf03aff164044e7546L15-R19)
[[6]](diffhunk://#diff-2cff9cf8e6a0cc34087326d8c8149c3bbaf74c76fdbdf5a73daed13cc04249e1L15-R19)
* Updated function signatures, method parameters, and return types from
`interface{}` to `any` in various parts of the codebase, including code
generation, service logic, and logging utilities (e.g., `mlog`).
[[1]](diffhunk://#diff-175edfeea54490b8fe4e18ffcbea5835efaf8f0b8acf623359073987cae7eb76L48-R55)
[[2]](diffhunk://#diff-2b1953fb78cf3593d8c2c7d911e95b65fd0b847c30ed0b4d167d16fe6d781235L54-R74)
[[3]](diffhunk://#diff-e001b7a4b63603b9b14f00de78a4d570bb76c5f57d856a24643f071032e12356L66-R73)
[[4]](diffhunk://#diff-5582954e8a9983988dc8854ad82067fb2ac6269b988e07357ad8db1dfec5f1a0L39-R41)
[[5]](diffhunk://#diff-c5d51d56f487779a2b6207c7ad26c7a20bbadcc846ce094fe60ab4cabff58c51L107-R107)
[[6]](diffhunk://#diff-f96e6a9fdb416eb1804ceaba1fe0ac637bff22c43837f8bb849c2366ce72d4a1L116-R121)
[[7]](diffhunk://#diff-f94c83a1b08ae060d9346f4a6031fc4a7b9a0b894e02d9afaa09018b6598eac0L112-R112)
[[8]](diffhunk://#diff-748b11dbe8828dd4c040ec23cae0b8fe57ecf0a2d1b7694ea39102294e633c64L36-R36)
[[9]](diffhunk://#diff-748b11dbe8828dd4c040ec23cae0b8fe57ecf0a2d1b7694ea39102294e633c64L74-R74)
[[10]](diffhunk://#diff-748b11dbe8828dd4c040ec23cae0b8fe57ecf0a2d1b7694ea39102294e633c64L96-R96)
**Generated code and templates:**
* Adjusted generated files and code generation templates to output `any`
instead of `interface{}` for relevant struct fields and function
signatures, ensuring that new code generation aligns with the updated
convention.
[[1]](diffhunk://#diff-6c19859cb32c7516ea95ddc8f8235460818eb2f24d2204308e0d9e1b19e7d90fL15-R19)
[[2]](diffhunk://#diff-a15ba2f5e830b4833c47b902515a4f9e5a4f83a3707698f3229b307ec3776b41L15-R18)
[[3]](diffhunk://#diff-52e0837e84d49221d1b810d88fdf78221f36cffcd664fb42f8aba49a79b974dcL15-R19)
[[4]](diffhunk://#diff-11c3457d1a23a4ca6ecd00d6b856289774936b6a708384cf03aff164044e7546L15-R19)
[[5]](diffhunk://#diff-2cff9cf8e6a0cc34087326d8c8149c3bbaf74c76fdbdf5a73daed13cc04249e1L15-R19)
[[6]](diffhunk://#diff-175edfeea54490b8fe4e18ffcbea5835efaf8f0b8acf623359073987cae7eb76L48-R55)
[[7]](diffhunk://#diff-e001b7a4b63603b9b14f00de78a4d570bb76c5f57d856a24643f071032e12356L66-R73)
[[8]](diffhunk://#diff-5582954e8a9983988dc8854ad82067fb2ac6269b988e07357ad8db1dfec5f1a0L39-R41)
**Container and utility updates:**
* Refactored the `garray` container implementation and related
constructors/methods to use `[]any` instead of `[]interface{}`, along
with corresponding function signatures.
[[1]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L31-R31)
[[2]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L52-R52)
[[3]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L62-R62)
[[4]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L73-R86)
[[5]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L96-R97)
[[6]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L107-R114)
[[7]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L124-R124)
[[8]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L135-R143)
[[9]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L167-R167)
These changes collectively modernize the codebase and prepare it for
future Go developments by using the idiomatic `any` type.
306 lines
7.8 KiB
Go
306 lines
7.8 KiB
Go
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
|
//
|
|
// This Source Code Form is subject to the terms of the MIT License.
|
|
// If a copy of the MIT was not distributed with this file,
|
|
// You can obtain one at https://github.com/gogf/gf.
|
|
|
|
package gdb
|
|
|
|
import (
|
|
"time"
|
|
|
|
"github.com/gogf/gf/v2/container/gset"
|
|
"github.com/gogf/gf/v2/internal/empty"
|
|
"github.com/gogf/gf/v2/os/gtime"
|
|
"github.com/gogf/gf/v2/text/gregex"
|
|
"github.com/gogf/gf/v2/text/gstr"
|
|
"github.com/gogf/gf/v2/util/gconv"
|
|
"github.com/gogf/gf/v2/util/gutil"
|
|
)
|
|
|
|
// QuoteWord checks given string `s` a word,
|
|
// if true it quotes `s` with security chars of the database
|
|
// and returns the quoted string; or else it returns `s` without any change.
|
|
//
|
|
// The meaning of a `word` can be considered as a column name.
|
|
func (m *Model) QuoteWord(s string) string {
|
|
return m.db.GetCore().QuoteWord(s)
|
|
}
|
|
|
|
// TableFields retrieves and returns the fields' information of specified table of current
|
|
// schema.
|
|
//
|
|
// Also see DriverMysql.TableFields.
|
|
func (m *Model) TableFields(tableStr string, schema ...string) (fields map[string]*TableField, err error) {
|
|
var (
|
|
ctx = m.GetCtx()
|
|
usedTable = m.db.GetCore().guessPrimaryTableName(tableStr)
|
|
usedSchema = gutil.GetOrDefaultStr(m.schema, schema...)
|
|
)
|
|
// Sharding feature.
|
|
usedSchema, err = m.getActualSchema(ctx, usedSchema)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
usedTable, err = m.getActualTable(ctx, usedTable)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return m.db.TableFields(ctx, usedTable, usedSchema)
|
|
}
|
|
|
|
// getModel creates and returns a cloned model of current model if `safe` is true, or else it returns
|
|
// the current model.
|
|
func (m *Model) getModel() *Model {
|
|
if !m.safe {
|
|
return m
|
|
} else {
|
|
return m.Clone()
|
|
}
|
|
}
|
|
|
|
// mappingAndFilterToTableFields mappings and changes given field name to really table field name.
|
|
// Eg:
|
|
// ID -> id
|
|
// NICK_Name -> nickname.
|
|
func (m *Model) mappingAndFilterToTableFields(table string, fields []any, filter bool) []any {
|
|
var fieldsTable = table
|
|
if fieldsTable != "" {
|
|
hasTable, _ := m.db.GetCore().HasTable(fieldsTable)
|
|
if !hasTable {
|
|
fieldsTable = m.tablesInit
|
|
}
|
|
}
|
|
if fieldsTable == "" {
|
|
fieldsTable = m.tablesInit
|
|
}
|
|
|
|
fieldsMap, _ := m.TableFields(fieldsTable)
|
|
if len(fieldsMap) == 0 {
|
|
return fields
|
|
}
|
|
var outputFieldsArray = make([]any, 0)
|
|
fieldsKeyMap := make(map[string]any, len(fieldsMap))
|
|
for k := range fieldsMap {
|
|
fieldsKeyMap[k] = nil
|
|
}
|
|
for _, field := range fields {
|
|
var (
|
|
fieldStr = gconv.String(field)
|
|
inputFieldsArray []string
|
|
)
|
|
switch {
|
|
case gregex.IsMatchString(regularFieldNameWithoutDotRegPattern, fieldStr):
|
|
inputFieldsArray = append(inputFieldsArray, fieldStr)
|
|
|
|
case gregex.IsMatchString(regularFieldNameWithCommaRegPattern, fieldStr):
|
|
inputFieldsArray = gstr.SplitAndTrim(fieldStr, ",")
|
|
|
|
default:
|
|
// Example:
|
|
// user.id, user.name
|
|
// replace(concat_ws(',',lpad(s.id, 6, '0'),s.name),',','') `code`
|
|
outputFieldsArray = append(outputFieldsArray, field)
|
|
continue
|
|
}
|
|
for _, inputField := range inputFieldsArray {
|
|
if !gregex.IsMatchString(regularFieldNameWithoutDotRegPattern, inputField) {
|
|
outputFieldsArray = append(outputFieldsArray, inputField)
|
|
continue
|
|
}
|
|
if _, ok := fieldsKeyMap[inputField]; !ok {
|
|
// Example:
|
|
// id, name
|
|
if foundKey, _ := gutil.MapPossibleItemByKey(fieldsKeyMap, inputField); foundKey != "" {
|
|
outputFieldsArray = append(outputFieldsArray, foundKey)
|
|
} else if !filter {
|
|
outputFieldsArray = append(outputFieldsArray, inputField)
|
|
}
|
|
} else {
|
|
outputFieldsArray = append(outputFieldsArray, inputField)
|
|
}
|
|
}
|
|
}
|
|
return outputFieldsArray
|
|
}
|
|
|
|
// filterDataForInsertOrUpdate does filter feature with data for inserting/updating operations.
|
|
// Note that, it does not filter list item, which is also type of map, for "omit empty" feature.
|
|
func (m *Model) filterDataForInsertOrUpdate(data any) (any, error) {
|
|
var err error
|
|
switch value := data.(type) {
|
|
case List:
|
|
var omitEmpty bool
|
|
if m.option&optionOmitNilDataList > 0 {
|
|
omitEmpty = true
|
|
}
|
|
for k, item := range value {
|
|
value[k], err = m.doMappingAndFilterForInsertOrUpdateDataMap(item, omitEmpty)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
return value, nil
|
|
|
|
case Map:
|
|
return m.doMappingAndFilterForInsertOrUpdateDataMap(value, true)
|
|
|
|
default:
|
|
return data, nil
|
|
}
|
|
}
|
|
|
|
// doMappingAndFilterForInsertOrUpdateDataMap does the filter features for map.
|
|
// Note that, it does not filter list item, which is also type of map, for "omit empty" feature.
|
|
func (m *Model) doMappingAndFilterForInsertOrUpdateDataMap(data Map, allowOmitEmpty bool) (Map, error) {
|
|
var (
|
|
err error
|
|
ctx = m.GetCtx()
|
|
core = m.db.GetCore()
|
|
schema = m.schema
|
|
table = m.tablesInit
|
|
)
|
|
// Sharding feature.
|
|
schema, err = m.getActualSchema(ctx, schema)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
table, err = m.getActualTable(ctx, table)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
data, err = core.mappingAndFilterData(
|
|
ctx, schema, table, data, m.filter,
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
// Remove key-value pairs of which the value is nil.
|
|
if allowOmitEmpty && m.option&optionOmitNilData > 0 {
|
|
tempMap := make(Map, len(data))
|
|
for k, v := range data {
|
|
if empty.IsNil(v) {
|
|
continue
|
|
}
|
|
tempMap[k] = v
|
|
}
|
|
data = tempMap
|
|
}
|
|
|
|
// Remove key-value pairs of which the value is empty.
|
|
if allowOmitEmpty && m.option&optionOmitEmptyData > 0 {
|
|
tempMap := make(Map, len(data))
|
|
for k, v := range data {
|
|
if empty.IsEmpty(v) {
|
|
continue
|
|
}
|
|
// Special type filtering.
|
|
switch r := v.(type) {
|
|
case time.Time:
|
|
if r.IsZero() {
|
|
continue
|
|
}
|
|
case *time.Time:
|
|
if r.IsZero() {
|
|
continue
|
|
}
|
|
case gtime.Time:
|
|
if r.IsZero() {
|
|
continue
|
|
}
|
|
case *gtime.Time:
|
|
if r.IsZero() {
|
|
continue
|
|
}
|
|
}
|
|
tempMap[k] = v
|
|
}
|
|
data = tempMap
|
|
}
|
|
|
|
if len(m.fields) > 0 {
|
|
// Keep specified fields.
|
|
var (
|
|
fieldSet = gset.NewStrSetFrom(gconv.Strings(m.fields))
|
|
charL, charR = m.db.GetChars()
|
|
chars = charL + charR
|
|
)
|
|
fieldSet.Walk(func(item string) string {
|
|
return gstr.Trim(item, chars)
|
|
})
|
|
for k := range data {
|
|
k = gstr.Trim(k, chars)
|
|
if !fieldSet.Contains(k) {
|
|
delete(data, k)
|
|
}
|
|
}
|
|
} else if len(m.fieldsEx) > 0 {
|
|
// Filter specified fields.
|
|
for _, v := range m.fieldsEx {
|
|
delete(data, gconv.String(v))
|
|
}
|
|
}
|
|
return data, nil
|
|
}
|
|
|
|
// getLink returns the underlying database link object with configured `linkType` attribute.
|
|
// The parameter `master` specifies whether using the master node if master-slave configured.
|
|
func (m *Model) getLink(master bool) Link {
|
|
if m.tx != nil {
|
|
if sqlTx := m.tx.GetSqlTX(); sqlTx != nil {
|
|
return &txLink{sqlTx}
|
|
}
|
|
}
|
|
linkType := m.linkType
|
|
if linkType == 0 {
|
|
if master {
|
|
linkType = linkTypeMaster
|
|
} else {
|
|
linkType = linkTypeSlave
|
|
}
|
|
}
|
|
switch linkType {
|
|
case linkTypeMaster:
|
|
link, err := m.db.GetCore().MasterLink(m.schema)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return link
|
|
case linkTypeSlave:
|
|
link, err := m.db.GetCore().SlaveLink(m.schema)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return link
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// getPrimaryKey retrieves and returns the primary key name of the model table.
|
|
// It parses m.tables to retrieve the primary table name, supporting m.tables like:
|
|
// "user", "user u", "user as u, user_detail as ud".
|
|
func (m *Model) getPrimaryKey() string {
|
|
table := gstr.SplitAndTrim(m.tablesInit, " ")[0]
|
|
tableFields, err := m.TableFields(table)
|
|
if err != nil {
|
|
return ""
|
|
}
|
|
for name, field := range tableFields {
|
|
if gstr.ContainsI(field.Key, "pri") {
|
|
return name
|
|
}
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// mergeArguments creates and returns new arguments by merging `m.extraArgs` and given `args`.
|
|
func (m *Model) mergeArguments(args []any) []any {
|
|
if len(m.extraArgs) > 0 {
|
|
newArgs := make([]any, len(m.extraArgs)+len(args))
|
|
copy(newArgs, m.extraArgs)
|
|
copy(newArgs[len(m.extraArgs):], args)
|
|
return newArgs
|
|
}
|
|
return args
|
|
}
|