Files
gf/database/gdb/gdb_model_hook.go

310 lines
9.3 KiB
Go
Raw Permalink Normal View History

// 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 (
"context"
"database/sql"
"fmt"
"github.com/gogf/gf/v2/container/gvar"
"github.com/gogf/gf/v2/text/gregex"
"github.com/gogf/gf/v2/text/gstr"
)
type (
HookFuncSelect func(ctx context.Context, in *HookSelectInput) (result Result, err error)
HookFuncInsert func(ctx context.Context, in *HookInsertInput) (result sql.Result, err error)
HookFuncUpdate func(ctx context.Context, in *HookUpdateInput) (result sql.Result, err error)
HookFuncDelete func(ctx context.Context, in *HookDeleteInput) (result sql.Result, err error)
)
// HookHandler manages all supported hook functions for Model.
type HookHandler struct {
Select HookFuncSelect
Insert HookFuncInsert
Update HookFuncUpdate
Delete HookFuncDelete
}
// internalParamHook manages all internal parameters for hook operations.
// The `internal` obviously means you cannot access these parameters outside this package.
type internalParamHook struct {
link Link // Connection object from third party sql driver.
handlerCalled bool // Simple mark for custom handler called, in case of recursive calling.
removedWhere bool // Removed mark for condition string that was removed `WHERE` prefix.
originalTableName *gvar.Var // The original table name.
originalSchemaName *gvar.Var // The original schema name.
}
type internalParamHookSelect struct {
internalParamHook
handler HookFuncSelect
}
type internalParamHookInsert struct {
internalParamHook
handler HookFuncInsert
}
type internalParamHookUpdate struct {
internalParamHook
handler HookFuncUpdate
}
type internalParamHookDelete struct {
internalParamHook
handler HookFuncDelete
}
// HookSelectInput holds the parameters for select hook operation.
2022-03-23 16:17:18 +08:00
// Note that, COUNT statement will also be hooked by this feature,
// which is usually not be interesting for upper business hook handler.
type HookSelectInput struct {
internalParamHookSelect
refactor: interface{} to any and reflect.Ptr to reflect.Pointer (#4395) 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.
2025-08-28 16:53:19 +08:00
Model *Model // Current operation Model.
Table string // The table name that to be used. Update this attribute to change target table name.
Schema string // The schema name that to be used. Update this attribute to change target schema name.
Sql string // The sql string that to be committed.
Args []any // The arguments of sql.
SelectType SelectType // The type of this SELECT operation.
}
// HookInsertInput holds the parameters for insert hook operation.
type HookInsertInput struct {
internalParamHookInsert
Model *Model // Current operation Model.
Table string // The table name that to be used. Update this attribute to change target table name.
Schema string // The schema name that to be used. Update this attribute to change target schema name.
Data List // The data records list to be inserted/saved into table.
Option DoInsertOption // The extra option for data inserting.
}
// HookUpdateInput holds the parameters for update hook operation.
type HookUpdateInput struct {
internalParamHookUpdate
refactor: interface{} to any and reflect.Ptr to reflect.Pointer (#4395) 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.
2025-08-28 16:53:19 +08:00
Model *Model // Current operation Model.
Table string // The table name that to be used. Update this attribute to change target table name.
Schema string // The schema name that to be used. Update this attribute to change target schema name.
Data any // Data can be type of: map[string]any/string. You can use type assertion on `Data`.
Condition string // The where condition string for updating.
Args []any // The arguments for sql place-holders.
}
// HookDeleteInput holds the parameters for delete hook operation.
type HookDeleteInput struct {
internalParamHookDelete
refactor: interface{} to any and reflect.Ptr to reflect.Pointer (#4395) 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.
2025-08-28 16:53:19 +08:00
Model *Model // Current operation Model.
Table string // The table name that to be used. Update this attribute to change target table name.
Schema string // The schema name that to be used. Update this attribute to change target schema name.
Condition string // The where condition string for deleting.
Args []any // The arguments for sql place-holders.
}
2022-03-21 21:17:48 +08:00
const (
whereKeyInCondition = " WHERE "
)
// IsTransaction checks and returns whether current operation is during transaction.
func (h *internalParamHook) IsTransaction() bool {
return h.link.IsTransaction()
}
// Next calls the next hook handler.
func (h *HookSelectInput) Next(ctx context.Context) (result Result, err error) {
if h.originalTableName.IsNil() {
h.originalTableName = gvar.New(h.Table)
}
if h.originalSchemaName.IsNil() {
h.originalSchemaName = gvar.New(h.Schema)
}
// Sharding feature.
h.Schema, err = h.Model.getActualSchema(ctx, h.Schema)
if err != nil {
return nil, err
}
h.Table, err = h.Model.getActualTable(ctx, h.Table)
if err != nil {
return nil, err
}
// Custom hook handler call.
if h.handler != nil && !h.handlerCalled {
h.handlerCalled = true
return h.handler(ctx, h)
}
var toBeCommittedSql = h.Sql
// Table change.
if h.Table != h.originalTableName.String() {
toBeCommittedSql, err = gregex.ReplaceStringFuncMatch(
`(?i) FROM ([\S]+)`,
toBeCommittedSql,
func(match []string) string {
charL, charR := h.Model.db.GetChars()
return fmt.Sprintf(` FROM %s%s%s`, charL, h.Table, charR)
},
)
if err != nil {
return
}
}
// Schema change.
if h.Schema != "" && h.Schema != h.originalSchemaName.String() {
h.link, err = h.Model.db.GetCore().SlaveLink(h.Schema)
if err != nil {
return
}
fix(database/gdb): Resolved the schema error in the database output log when using the database sharding feature (#4319) error log ``` 2025-06-18T15:36:08.315+08:00 [DEBU] {10a3b0cfd9124a186b89b07f50e67ce6} [ 0 ms] [default] [db_0] [rows:1 ] SELECT `id`,`custom_name` FROM `custom` WHERE `id`=1 LIMIT 1 2025-06-18T15:36:09.259+08:00 [DEBU] {10a3b0cfd9124a186b89b07f50e67ce6} [ 1 ms] [default] [db_0] [rows:1 ] SELECT `id`,`custom_name`,`remark`FROM `custom` WHERE `id`=2 LIMIT 1 ``` right log ``` 2025-06-18T15:36:08.315+08:00 [DEBU] {10a3b0cfd9124a186b89b07f50e67ce6} [ 0 ms] [default] [db_0] [rows:1 ] SELECT `id`,`custom_name` FROM `custom` WHERE `id`=1 LIMIT 1 2025-06-18T15:36:09.259+08:00 [DEBU] {10a3b0cfd9124a186b89b07f50e67ce6} [ 1 ms] [default] [db_1] [rows:1 ] SELECT `id`,`custom_name`,`remark`FROM `custom` WHERE `id`=2 LIMIT 1 ``` ``` type DbShardingRule struct { } func (d *DbShardingRule) SchemaName(ctx context.Context, config gdb.ShardingSchemaConfig, value any) (string, error) { if name, ok := value.(string); ok && (len(name) > 0) && gstr.HasPrefix(name, config.Prefix) { return name, nil } return "default", nil } func (d *DbShardingRule) TableName(ctx context.Context, config gdb.ShardingTableConfig, value any) (string, error) { return "", nil } ``` ``` config := gdb.ShardingConfig{ Schema: gdb.ShardingSchemaConfig{ Enable: true, Prefix: "db_", Rule: &DbShardingRule{}, }, } dao.Custom.Ctx(ctx).Sharding(config).ShardingValue("db_0").Where("id", 1).One() dao.Custom.Ctx(ctx).Sharding(config).ShardingValue("db_1").Where("id", 2).One() ``` 我有两个完全一样的数据库db_0和db_1,两个custom表里都只有一条数据,id是1和2,执行上面两条查询得的结果是正确的, 输出日志中应该分别是`[default] [db_0]`和`[default] [db_1]`,但是实际输出的都是`[default] [db_0]`, 查看具体代码实现,该日志由Core实例中获取group和schema构成,而实际执行sql时如果使用了分库特性那么执行sql的link会使用当前面schema重新生成,但是没有重新赋给Core实例,所以输出日志的时候还是错的,只需要在生成link后生成日志前把schema赋给Core就行,方法执行完成后defer将schema重置回去,防止有人重复使用同一个gdb对象造成困扰 ![SCR-20250618-ovoc](https://github.com/user-attachments/assets/815c364e-939f-4a2c-9669-d5b7d2742511) ![SCR-20250618-ovvk](https://github.com/user-attachments/assets/e7d0e375-78e6-4748-90ac-d02dba18720f) ![SCR-20250618-ozsu](https://github.com/user-attachments/assets/faa6d69b-331e-476b-8bf8-f62e564b04d3) ![SCR-20250618-ozwj](https://github.com/user-attachments/assets/15c524dc-dc19-4499-a3d3-32bf1d918a3a)
2025-09-28 17:57:27 +08:00
h.Model.db.GetCore().schema = h.Schema
defer func() {
h.Model.db.GetCore().schema = h.originalSchemaName.String()
}()
}
return h.Model.db.DoSelect(ctx, h.link, toBeCommittedSql, h.Args...)
}
// Next calls the next hook handler.
func (h *HookInsertInput) Next(ctx context.Context) (result sql.Result, err error) {
if h.originalTableName.IsNil() {
h.originalTableName = gvar.New(h.Table)
}
if h.originalSchemaName.IsNil() {
h.originalSchemaName = gvar.New(h.Schema)
}
// Sharding feature.
h.Schema, err = h.Model.getActualSchema(ctx, h.Schema)
if err != nil {
return nil, err
}
h.Table, err = h.Model.getActualTable(ctx, h.Table)
if err != nil {
return nil, err
}
if h.handler != nil && !h.handlerCalled {
h.handlerCalled = true
return h.handler(ctx, h)
}
// No need to handle table change.
// Schema change.
if h.Schema != "" && h.Schema != h.originalSchemaName.String() {
h.link, err = h.Model.db.GetCore().MasterLink(h.Schema)
if err != nil {
return
}
fix(database/gdb): Resolved the schema error in the database output log when using the database sharding feature (#4319) error log ``` 2025-06-18T15:36:08.315+08:00 [DEBU] {10a3b0cfd9124a186b89b07f50e67ce6} [ 0 ms] [default] [db_0] [rows:1 ] SELECT `id`,`custom_name` FROM `custom` WHERE `id`=1 LIMIT 1 2025-06-18T15:36:09.259+08:00 [DEBU] {10a3b0cfd9124a186b89b07f50e67ce6} [ 1 ms] [default] [db_0] [rows:1 ] SELECT `id`,`custom_name`,`remark`FROM `custom` WHERE `id`=2 LIMIT 1 ``` right log ``` 2025-06-18T15:36:08.315+08:00 [DEBU] {10a3b0cfd9124a186b89b07f50e67ce6} [ 0 ms] [default] [db_0] [rows:1 ] SELECT `id`,`custom_name` FROM `custom` WHERE `id`=1 LIMIT 1 2025-06-18T15:36:09.259+08:00 [DEBU] {10a3b0cfd9124a186b89b07f50e67ce6} [ 1 ms] [default] [db_1] [rows:1 ] SELECT `id`,`custom_name`,`remark`FROM `custom` WHERE `id`=2 LIMIT 1 ``` ``` type DbShardingRule struct { } func (d *DbShardingRule) SchemaName(ctx context.Context, config gdb.ShardingSchemaConfig, value any) (string, error) { if name, ok := value.(string); ok && (len(name) > 0) && gstr.HasPrefix(name, config.Prefix) { return name, nil } return "default", nil } func (d *DbShardingRule) TableName(ctx context.Context, config gdb.ShardingTableConfig, value any) (string, error) { return "", nil } ``` ``` config := gdb.ShardingConfig{ Schema: gdb.ShardingSchemaConfig{ Enable: true, Prefix: "db_", Rule: &DbShardingRule{}, }, } dao.Custom.Ctx(ctx).Sharding(config).ShardingValue("db_0").Where("id", 1).One() dao.Custom.Ctx(ctx).Sharding(config).ShardingValue("db_1").Where("id", 2).One() ``` 我有两个完全一样的数据库db_0和db_1,两个custom表里都只有一条数据,id是1和2,执行上面两条查询得的结果是正确的, 输出日志中应该分别是`[default] [db_0]`和`[default] [db_1]`,但是实际输出的都是`[default] [db_0]`, 查看具体代码实现,该日志由Core实例中获取group和schema构成,而实际执行sql时如果使用了分库特性那么执行sql的link会使用当前面schema重新生成,但是没有重新赋给Core实例,所以输出日志的时候还是错的,只需要在生成link后生成日志前把schema赋给Core就行,方法执行完成后defer将schema重置回去,防止有人重复使用同一个gdb对象造成困扰 ![SCR-20250618-ovoc](https://github.com/user-attachments/assets/815c364e-939f-4a2c-9669-d5b7d2742511) ![SCR-20250618-ovvk](https://github.com/user-attachments/assets/e7d0e375-78e6-4748-90ac-d02dba18720f) ![SCR-20250618-ozsu](https://github.com/user-attachments/assets/faa6d69b-331e-476b-8bf8-f62e564b04d3) ![SCR-20250618-ozwj](https://github.com/user-attachments/assets/15c524dc-dc19-4499-a3d3-32bf1d918a3a)
2025-09-28 17:57:27 +08:00
h.Model.db.GetCore().schema = h.Schema
defer func() {
h.Model.db.GetCore().schema = h.originalSchemaName.String()
}()
}
2022-05-09 14:22:28 +08:00
return h.Model.db.DoInsert(ctx, h.link, h.Table, h.Data, h.Option)
}
// Next calls the next hook handler.
func (h *HookUpdateInput) Next(ctx context.Context) (result sql.Result, err error) {
if h.originalTableName.IsNil() {
h.originalTableName = gvar.New(h.Table)
}
if h.originalSchemaName.IsNil() {
h.originalSchemaName = gvar.New(h.Schema)
}
// Sharding feature.
h.Schema, err = h.Model.getActualSchema(ctx, h.Schema)
if err != nil {
return nil, err
}
h.Table, err = h.Model.getActualTable(ctx, h.Table)
if err != nil {
return nil, err
}
if h.handler != nil && !h.handlerCalled {
h.handlerCalled = true
2022-03-21 21:17:48 +08:00
if gstr.HasPrefix(h.Condition, whereKeyInCondition) {
h.removedWhere = true
2022-03-21 21:17:48 +08:00
h.Condition = gstr.TrimLeftStr(h.Condition, whereKeyInCondition)
}
return h.handler(ctx, h)
}
if h.removedWhere {
2022-03-21 21:17:48 +08:00
h.Condition = whereKeyInCondition + h.Condition
}
// No need to handle table change.
// Schema change.
if h.Schema != "" && h.Schema != h.originalSchemaName.String() {
h.link, err = h.Model.db.GetCore().MasterLink(h.Schema)
if err != nil {
return
}
fix(database/gdb): Resolved the schema error in the database output log when using the database sharding feature (#4319) error log ``` 2025-06-18T15:36:08.315+08:00 [DEBU] {10a3b0cfd9124a186b89b07f50e67ce6} [ 0 ms] [default] [db_0] [rows:1 ] SELECT `id`,`custom_name` FROM `custom` WHERE `id`=1 LIMIT 1 2025-06-18T15:36:09.259+08:00 [DEBU] {10a3b0cfd9124a186b89b07f50e67ce6} [ 1 ms] [default] [db_0] [rows:1 ] SELECT `id`,`custom_name`,`remark`FROM `custom` WHERE `id`=2 LIMIT 1 ``` right log ``` 2025-06-18T15:36:08.315+08:00 [DEBU] {10a3b0cfd9124a186b89b07f50e67ce6} [ 0 ms] [default] [db_0] [rows:1 ] SELECT `id`,`custom_name` FROM `custom` WHERE `id`=1 LIMIT 1 2025-06-18T15:36:09.259+08:00 [DEBU] {10a3b0cfd9124a186b89b07f50e67ce6} [ 1 ms] [default] [db_1] [rows:1 ] SELECT `id`,`custom_name`,`remark`FROM `custom` WHERE `id`=2 LIMIT 1 ``` ``` type DbShardingRule struct { } func (d *DbShardingRule) SchemaName(ctx context.Context, config gdb.ShardingSchemaConfig, value any) (string, error) { if name, ok := value.(string); ok && (len(name) > 0) && gstr.HasPrefix(name, config.Prefix) { return name, nil } return "default", nil } func (d *DbShardingRule) TableName(ctx context.Context, config gdb.ShardingTableConfig, value any) (string, error) { return "", nil } ``` ``` config := gdb.ShardingConfig{ Schema: gdb.ShardingSchemaConfig{ Enable: true, Prefix: "db_", Rule: &DbShardingRule{}, }, } dao.Custom.Ctx(ctx).Sharding(config).ShardingValue("db_0").Where("id", 1).One() dao.Custom.Ctx(ctx).Sharding(config).ShardingValue("db_1").Where("id", 2).One() ``` 我有两个完全一样的数据库db_0和db_1,两个custom表里都只有一条数据,id是1和2,执行上面两条查询得的结果是正确的, 输出日志中应该分别是`[default] [db_0]`和`[default] [db_1]`,但是实际输出的都是`[default] [db_0]`, 查看具体代码实现,该日志由Core实例中获取group和schema构成,而实际执行sql时如果使用了分库特性那么执行sql的link会使用当前面schema重新生成,但是没有重新赋给Core实例,所以输出日志的时候还是错的,只需要在生成link后生成日志前把schema赋给Core就行,方法执行完成后defer将schema重置回去,防止有人重复使用同一个gdb对象造成困扰 ![SCR-20250618-ovoc](https://github.com/user-attachments/assets/815c364e-939f-4a2c-9669-d5b7d2742511) ![SCR-20250618-ovvk](https://github.com/user-attachments/assets/e7d0e375-78e6-4748-90ac-d02dba18720f) ![SCR-20250618-ozsu](https://github.com/user-attachments/assets/faa6d69b-331e-476b-8bf8-f62e564b04d3) ![SCR-20250618-ozwj](https://github.com/user-attachments/assets/15c524dc-dc19-4499-a3d3-32bf1d918a3a)
2025-09-28 17:57:27 +08:00
h.Model.db.GetCore().schema = h.Schema
defer func() {
h.Model.db.GetCore().schema = h.originalSchemaName.String()
}()
}
2022-05-09 14:22:28 +08:00
return h.Model.db.DoUpdate(ctx, h.link, h.Table, h.Data, h.Condition, h.Args...)
}
// Next calls the next hook handler.
func (h *HookDeleteInput) Next(ctx context.Context) (result sql.Result, err error) {
if h.originalTableName.IsNil() {
h.originalTableName = gvar.New(h.Table)
}
if h.originalSchemaName.IsNil() {
h.originalSchemaName = gvar.New(h.Schema)
}
// Sharding feature.
h.Schema, err = h.Model.getActualSchema(ctx, h.Schema)
if err != nil {
return nil, err
}
h.Table, err = h.Model.getActualTable(ctx, h.Table)
if err != nil {
return nil, err
}
if h.handler != nil && !h.handlerCalled {
h.handlerCalled = true
2022-03-21 21:17:48 +08:00
if gstr.HasPrefix(h.Condition, whereKeyInCondition) {
h.removedWhere = true
2022-03-21 21:17:48 +08:00
h.Condition = gstr.TrimLeftStr(h.Condition, whereKeyInCondition)
}
return h.handler(ctx, h)
}
if h.removedWhere {
2022-03-21 21:17:48 +08:00
h.Condition = whereKeyInCondition + h.Condition
}
// No need to handle table change.
// Schema change.
if h.Schema != "" && h.Schema != h.originalSchemaName.String() {
h.link, err = h.Model.db.GetCore().MasterLink(h.Schema)
if err != nil {
return
}
fix(database/gdb): Resolved the schema error in the database output log when using the database sharding feature (#4319) error log ``` 2025-06-18T15:36:08.315+08:00 [DEBU] {10a3b0cfd9124a186b89b07f50e67ce6} [ 0 ms] [default] [db_0] [rows:1 ] SELECT `id`,`custom_name` FROM `custom` WHERE `id`=1 LIMIT 1 2025-06-18T15:36:09.259+08:00 [DEBU] {10a3b0cfd9124a186b89b07f50e67ce6} [ 1 ms] [default] [db_0] [rows:1 ] SELECT `id`,`custom_name`,`remark`FROM `custom` WHERE `id`=2 LIMIT 1 ``` right log ``` 2025-06-18T15:36:08.315+08:00 [DEBU] {10a3b0cfd9124a186b89b07f50e67ce6} [ 0 ms] [default] [db_0] [rows:1 ] SELECT `id`,`custom_name` FROM `custom` WHERE `id`=1 LIMIT 1 2025-06-18T15:36:09.259+08:00 [DEBU] {10a3b0cfd9124a186b89b07f50e67ce6} [ 1 ms] [default] [db_1] [rows:1 ] SELECT `id`,`custom_name`,`remark`FROM `custom` WHERE `id`=2 LIMIT 1 ``` ``` type DbShardingRule struct { } func (d *DbShardingRule) SchemaName(ctx context.Context, config gdb.ShardingSchemaConfig, value any) (string, error) { if name, ok := value.(string); ok && (len(name) > 0) && gstr.HasPrefix(name, config.Prefix) { return name, nil } return "default", nil } func (d *DbShardingRule) TableName(ctx context.Context, config gdb.ShardingTableConfig, value any) (string, error) { return "", nil } ``` ``` config := gdb.ShardingConfig{ Schema: gdb.ShardingSchemaConfig{ Enable: true, Prefix: "db_", Rule: &DbShardingRule{}, }, } dao.Custom.Ctx(ctx).Sharding(config).ShardingValue("db_0").Where("id", 1).One() dao.Custom.Ctx(ctx).Sharding(config).ShardingValue("db_1").Where("id", 2).One() ``` 我有两个完全一样的数据库db_0和db_1,两个custom表里都只有一条数据,id是1和2,执行上面两条查询得的结果是正确的, 输出日志中应该分别是`[default] [db_0]`和`[default] [db_1]`,但是实际输出的都是`[default] [db_0]`, 查看具体代码实现,该日志由Core实例中获取group和schema构成,而实际执行sql时如果使用了分库特性那么执行sql的link会使用当前面schema重新生成,但是没有重新赋给Core实例,所以输出日志的时候还是错的,只需要在生成link后生成日志前把schema赋给Core就行,方法执行完成后defer将schema重置回去,防止有人重复使用同一个gdb对象造成困扰 ![SCR-20250618-ovoc](https://github.com/user-attachments/assets/815c364e-939f-4a2c-9669-d5b7d2742511) ![SCR-20250618-ovvk](https://github.com/user-attachments/assets/e7d0e375-78e6-4748-90ac-d02dba18720f) ![SCR-20250618-ozsu](https://github.com/user-attachments/assets/faa6d69b-331e-476b-8bf8-f62e564b04d3) ![SCR-20250618-ozwj](https://github.com/user-attachments/assets/15c524dc-dc19-4499-a3d3-32bf1d918a3a)
2025-09-28 17:57:27 +08:00
h.Model.db.GetCore().schema = h.Schema
defer func() {
h.Model.db.GetCore().schema = h.originalSchemaName.String()
}()
}
2022-05-09 14:22:28 +08:00
return h.Model.db.DoDelete(ctx, h.link, h.Table, h.Condition, h.Args...)
}
// Hook sets the hook functions for current model.
func (m *Model) Hook(hook HookHandler) *Model {
model := m.getModel()
2022-03-21 14:26:56 +08:00
model.hookHandler = hook
return model
}