mirror of
https://gitee.com/johng/gf
synced 2026-06-07 02:12:11 +08:00
Merge branch 'develop'
This commit is contained in:
@ -20,6 +20,7 @@ import (
|
||||
"github.com/gogf/gf/v2/internal/intlog"
|
||||
"github.com/gogf/gf/v2/os/gcache"
|
||||
"github.com/gogf/gf/v2/os/gcmd"
|
||||
"github.com/gogf/gf/v2/os/gctx"
|
||||
"github.com/gogf/gf/v2/os/glog"
|
||||
"github.com/gogf/gf/v2/util/grand"
|
||||
)
|
||||
@ -94,15 +95,18 @@ type DB interface {
|
||||
// Internal APIs for CURD, which can be overwritten by custom CURD implements.
|
||||
// ===========================================================================
|
||||
|
||||
DoGetAll(ctx context.Context, link Link, sql string, args ...interface{}) (result Result, err error) // See Core.DoGetAll.
|
||||
DoSelect(ctx context.Context, link Link, sql string, args ...interface{}) (result Result, err error) // See Core.DoSelect.
|
||||
DoInsert(ctx context.Context, link Link, table string, data List, option DoInsertOption) (result sql.Result, err error) // See Core.DoInsert.
|
||||
DoUpdate(ctx context.Context, link Link, table string, data interface{}, condition string, args ...interface{}) (result sql.Result, err error) // See Core.DoUpdate.
|
||||
DoDelete(ctx context.Context, link Link, table string, condition string, args ...interface{}) (result sql.Result, err error) // See Core.DoDelete.
|
||||
DoQuery(ctx context.Context, link Link, sql string, args ...interface{}) (result Result, err error) // See Core.DoQuery.
|
||||
DoExec(ctx context.Context, link Link, sql string, args ...interface{}) (result sql.Result, err error) // See Core.DoExec.
|
||||
DoFilter(ctx context.Context, link Link, sql string, args []interface{}) (newSql string, newArgs []interface{}, err error) // See Core.DoFilter.
|
||||
DoCommit(ctx context.Context, in DoCommitInput) (out DoCommitOutput, err error) // See Core.DoCommit.
|
||||
DoPrepare(ctx context.Context, link Link, sql string) (*Stmt, error) // See Core.DoPrepare.
|
||||
|
||||
DoQuery(ctx context.Context, link Link, sql string, args ...interface{}) (result Result, err error) // See Core.DoQuery.
|
||||
DoExec(ctx context.Context, link Link, sql string, args ...interface{}) (result sql.Result, err error) // See Core.DoExec.
|
||||
|
||||
DoFilter(ctx context.Context, link Link, sql string, args []interface{}) (newSql string, newArgs []interface{}, err error) // See Core.DoFilter.
|
||||
DoCommit(ctx context.Context, in DoCommitInput) (out DoCommitOutput, err error) // See Core.DoCommit.
|
||||
|
||||
DoPrepare(ctx context.Context, link Link, sql string) (*Stmt, error) // See Core.DoPrepare.
|
||||
|
||||
// ===========================================================================
|
||||
// Query APIs for convenience purpose.
|
||||
@ -212,9 +216,6 @@ type Driver interface {
|
||||
|
||||
// Link is a common database function wrapper interface.
|
||||
type Link interface {
|
||||
Query(sql string, args ...interface{}) (*sql.Rows, error)
|
||||
Exec(sql string, args ...interface{}) (sql.Result, error)
|
||||
Prepare(sql string) (*sql.Stmt, error)
|
||||
QueryContext(ctx context.Context, sql string, args ...interface{}) (*sql.Rows, error)
|
||||
ExecContext(ctx context.Context, sql string, args ...interface{}) (sql.Result, error)
|
||||
PrepareContext(ctx context.Context, sql string) (*sql.Stmt, error)
|
||||
@ -237,10 +238,10 @@ type Sql struct {
|
||||
|
||||
// DoInsertOption is the input struct for function DoInsert.
|
||||
type DoInsertOption struct {
|
||||
OnDuplicateStr string
|
||||
OnDuplicateMap map[string]interface{}
|
||||
InsertOption int // Insert operation.
|
||||
BatchCount int // Batch count for batch inserting.
|
||||
OnDuplicateStr string // Custom string for `on duplicated` statement.
|
||||
OnDuplicateMap map[string]interface{} // Custom key-value map from `OnDuplicateEx` function for `on duplicated` statement.
|
||||
InsertOption int // Insert operation in constant value.
|
||||
BatchCount int // Batch count for batch inserting.
|
||||
}
|
||||
|
||||
// TableField is the struct for table field.
|
||||
@ -284,9 +285,10 @@ const (
|
||||
ctxTimeoutTypeExec = iota
|
||||
ctxTimeoutTypeQuery
|
||||
ctxTimeoutTypePrepare
|
||||
commandEnvKeyForDryRun = "gf.gdb.dryrun"
|
||||
modelForDaoSuffix = `ForDao`
|
||||
dbRoleSlave = `slave`
|
||||
commandEnvKeyForDryRun = "gf.gdb.dryrun"
|
||||
modelForDaoSuffix = `ForDao`
|
||||
dbRoleSlave = `slave`
|
||||
contextKeyForDB gctx.StrKey = `DBInContext`
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
@ -45,7 +45,6 @@ func (c *Core) Ctx(ctx context.Context) DB {
|
||||
configNode = c.db.GetConfig()
|
||||
)
|
||||
*newCore = *c
|
||||
newCore.ctx = ctx
|
||||
// It creates a new DB object, which is commonly a wrapper for object `Core`.
|
||||
newCore.db, err = driverMap[configNode.Type].New(newCore, configNode)
|
||||
if err != nil {
|
||||
@ -53,6 +52,7 @@ func (c *Core) Ctx(ctx context.Context) DB {
|
||||
// Do not let it continue.
|
||||
panic(err)
|
||||
}
|
||||
newCore.ctx = WithDB(ctx, newCore.db)
|
||||
return newCore.db
|
||||
}
|
||||
|
||||
@ -145,11 +145,11 @@ func (c *Core) Slave(schema ...string) (*sql.DB, error) {
|
||||
|
||||
// GetAll queries and returns data records from database.
|
||||
func (c *Core) GetAll(ctx context.Context, sql string, args ...interface{}) (Result, error) {
|
||||
return c.db.DoGetAll(ctx, nil, sql, args...)
|
||||
return c.db.DoSelect(ctx, nil, sql, args...)
|
||||
}
|
||||
|
||||
// DoGetAll queries and returns data records from database.
|
||||
func (c *Core) DoGetAll(ctx context.Context, link Link, sql string, args ...interface{}) (result Result, err error) {
|
||||
// DoSelect queries and returns data records from database.
|
||||
func (c *Core) DoSelect(ctx context.Context, link Link, sql string, args ...interface{}) (result Result, err error) {
|
||||
return c.db.DoQuery(ctx, link, sql, args...)
|
||||
}
|
||||
|
||||
@ -168,7 +168,7 @@ func (c *Core) GetOne(ctx context.Context, sql string, args ...interface{}) (Rec
|
||||
// GetArray queries and returns data values as slice from database.
|
||||
// Note that if there are multiple columns in the result, it returns just one column values randomly.
|
||||
func (c *Core) GetArray(ctx context.Context, sql string, args ...interface{}) ([]Value, error) {
|
||||
all, err := c.db.DoGetAll(ctx, nil, sql, args...)
|
||||
all, err := c.db.DoSelect(ctx, nil, sql, args...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@ -8,12 +8,39 @@
|
||||
package gdb
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/gogf/gf/v2/errors/gcode"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/text/gregex"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
)
|
||||
|
||||
// WithDB injects given db object into context and returns a new context.
|
||||
func WithDB(ctx context.Context, db DB) context.Context {
|
||||
if db == nil {
|
||||
return ctx
|
||||
}
|
||||
dbCtx := db.GetCtx()
|
||||
if ctxDb := DBFromCtx(dbCtx); ctxDb != nil {
|
||||
return dbCtx
|
||||
}
|
||||
ctx = context.WithValue(ctx, contextKeyForDB, db)
|
||||
return ctx
|
||||
}
|
||||
|
||||
// DBFromCtx retrieves and returns DB object from context.
|
||||
func DBFromCtx(ctx context.Context) DB {
|
||||
if ctx == nil {
|
||||
return nil
|
||||
}
|
||||
v := ctx.Value(contextKeyForDB)
|
||||
if v != nil {
|
||||
return v.(DB)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// MasterLink acts like function Master but with additional `schema` parameter specifying
|
||||
// the schema for the connection. It is defined for internal usage.
|
||||
// Also see Master.
|
||||
|
||||
@ -100,7 +100,7 @@ func (d *DriverMysql) Tables(ctx context.Context, schema ...string) (tables []st
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result, err = d.DoGetAll(ctx, link, `SHOW TABLES`)
|
||||
result, err = d.DoSelect(ctx, link, `SHOW TABLES`)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@ -147,7 +147,7 @@ func (d *DriverMysql) TableFields(ctx context.Context, table string, schema ...s
|
||||
if link, err = d.SlaveLink(useSchema); err != nil {
|
||||
return nil
|
||||
}
|
||||
result, err = d.DoGetAll(
|
||||
result, err = d.DoSelect(
|
||||
ctx, link,
|
||||
fmt.Sprintf(`SHOW FULL COLUMNS FROM %s`, d.QuoteWord(table)),
|
||||
)
|
||||
|
||||
@ -44,6 +44,7 @@ type Model struct {
|
||||
lockInfo string // Lock for update or in shared lock.
|
||||
cacheEnabled bool // Enable sql result cache feature, which is mainly for indicating cache duration(especially 0) usage.
|
||||
cacheOption CacheOption // Cache option for query statement.
|
||||
hook HookHandler // Hook functions for model hook feature.
|
||||
unscoped bool // Disables soft deleting features when select/delete operations.
|
||||
safe bool // If true, it clones and returns a new model object whenever operation done; or else it changes the attribute of current model.
|
||||
onDuplicate interface{} // onDuplicate is used for ON "DUPLICATE KEY UPDATE" statement.
|
||||
|
||||
@ -34,18 +34,40 @@ func (m *Model) Delete(where ...interface{}) (result sql.Result, err error) {
|
||||
)
|
||||
// Soft deleting.
|
||||
if !m.unscoped && fieldNameDelete != "" {
|
||||
return m.db.DoUpdate(
|
||||
m.GetCtx(),
|
||||
m.getLink(true),
|
||||
m.tables,
|
||||
fmt.Sprintf(`%s=?`, m.db.GetCore().QuoteString(fieldNameDelete)),
|
||||
conditionWhere+conditionExtra,
|
||||
append([]interface{}{gtime.Now().String()}, conditionArgs...),
|
||||
)
|
||||
in := &HookUpdateInput{
|
||||
internalParamHookUpdate: internalParamHookUpdate{
|
||||
internalParamHook: internalParamHook{
|
||||
db: m.db,
|
||||
link: m.getLink(true),
|
||||
},
|
||||
handler: m.hook.Update,
|
||||
},
|
||||
Table: m.tables,
|
||||
Data: fmt.Sprintf(`%s=?`, m.db.GetCore().QuoteString(fieldNameDelete)),
|
||||
Condition: conditionWhere + conditionExtra,
|
||||
Args: append([]interface{}{gtime.Now().String()}, conditionArgs...),
|
||||
}
|
||||
return in.Next(m.GetCtx())
|
||||
}
|
||||
conditionStr := conditionWhere + conditionExtra
|
||||
if !gstr.ContainsI(conditionStr, " WHERE ") {
|
||||
return nil, gerror.NewCode(gcode.CodeMissingParameter, "there should be WHERE condition statement for DELETE operation")
|
||||
return nil, gerror.NewCode(
|
||||
gcode.CodeMissingParameter,
|
||||
"there should be WHERE condition statement for DELETE operation",
|
||||
)
|
||||
}
|
||||
return m.db.DoDelete(m.GetCtx(), m.getLink(true), m.tables, conditionStr, conditionArgs...)
|
||||
|
||||
in := &HookDeleteInput{
|
||||
internalParamHookDelete: internalParamHookDelete{
|
||||
internalParamHook: internalParamHook{
|
||||
db: m.db,
|
||||
link: m.getLink(true),
|
||||
},
|
||||
handler: m.hook.Delete,
|
||||
},
|
||||
Table: m.tables,
|
||||
Condition: conditionStr,
|
||||
Args: conditionArgs,
|
||||
}
|
||||
return in.Next(m.GetCtx())
|
||||
}
|
||||
|
||||
148
database/gdb/gdb_model_hook.go
Normal file
148
database/gdb/gdb_model_hook.go
Normal file
@ -0,0 +1,148 @@
|
||||
// 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"
|
||||
|
||||
"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 {
|
||||
db DB // Underlying DB object.
|
||||
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.
|
||||
}
|
||||
|
||||
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.
|
||||
type HookSelectInput struct {
|
||||
internalParamHookSelect
|
||||
Table string
|
||||
Sql string
|
||||
Args []interface{}
|
||||
}
|
||||
|
||||
// HookInsertInput holds the parameters for insert hook operation.
|
||||
type HookInsertInput struct {
|
||||
internalParamHookInsert
|
||||
Table string
|
||||
Data List
|
||||
Option DoInsertOption
|
||||
}
|
||||
|
||||
// HookUpdateInput holds the parameters for update hook operation.
|
||||
type HookUpdateInput struct {
|
||||
internalParamHookUpdate
|
||||
Table string
|
||||
Data interface{}
|
||||
Condition string
|
||||
Args []interface{}
|
||||
}
|
||||
|
||||
// HookDeleteInput holds the parameters for delete hook operation.
|
||||
type HookDeleteInput struct {
|
||||
internalParamHookDelete
|
||||
Table string
|
||||
Condition string
|
||||
Args []interface{}
|
||||
}
|
||||
|
||||
// Next calls the next hook handler.
|
||||
func (h *HookSelectInput) Next(ctx context.Context) (result Result, err error) {
|
||||
if h.handler != nil && !h.handlerCalled {
|
||||
h.handlerCalled = true
|
||||
return h.handler(ctx, h)
|
||||
}
|
||||
return h.db.DoSelect(ctx, h.link, h.Sql, h.Args...)
|
||||
}
|
||||
|
||||
// Next calls the next hook handler.
|
||||
func (h *HookInsertInput) Next(ctx context.Context) (result sql.Result, err error) {
|
||||
if h.handler != nil && !h.handlerCalled {
|
||||
h.handlerCalled = true
|
||||
return h.handler(ctx, h)
|
||||
}
|
||||
return h.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.handler != nil && !h.handlerCalled {
|
||||
h.handlerCalled = true
|
||||
if gstr.HasPrefix(h.Condition, " WHERE ") {
|
||||
h.removedWhere = true
|
||||
h.Condition = gstr.TrimLeftStr(h.Condition, " WHERE ")
|
||||
}
|
||||
return h.handler(ctx, h)
|
||||
}
|
||||
if h.removedWhere {
|
||||
h.Condition = " WHERE " + h.Condition
|
||||
}
|
||||
return h.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.handler != nil && !h.handlerCalled {
|
||||
h.handlerCalled = true
|
||||
if gstr.HasPrefix(h.Condition, " WHERE ") {
|
||||
h.removedWhere = true
|
||||
h.Condition = gstr.TrimLeftStr(h.Condition, " WHERE ")
|
||||
}
|
||||
return h.handler(ctx, h)
|
||||
}
|
||||
if h.removedWhere {
|
||||
h.Condition = " WHERE " + h.Condition
|
||||
}
|
||||
return h.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()
|
||||
model.hook = hook
|
||||
return model
|
||||
}
|
||||
@ -140,7 +140,7 @@ func (m *Model) OnDuplicate(onDuplicate ...interface{}) *Model {
|
||||
return model
|
||||
}
|
||||
|
||||
// OnDuplicateEx sets the excluding columns for operations when columns conflicts occurs.
|
||||
// OnDuplicateEx sets the excluding columns for operations when columns conflict occurs.
|
||||
// In MySQL, this is used for "ON DUPLICATE KEY UPDATE" statement.
|
||||
// The parameter `onDuplicateEx` can be type of string/map/slice.
|
||||
// Example:
|
||||
@ -310,7 +310,20 @@ func (m *Model) doInsertWithOption(insertOption int) (result sql.Result, err err
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
return m.db.DoInsert(m.GetCtx(), m.getLink(true), m.tables, list, doInsertOption)
|
||||
|
||||
in := &HookInsertInput{
|
||||
internalParamHookInsert: internalParamHookInsert{
|
||||
internalParamHook: internalParamHook{
|
||||
db: m.db,
|
||||
link: m.getLink(true),
|
||||
},
|
||||
handler: m.hook.Insert,
|
||||
},
|
||||
Table: m.tables,
|
||||
Data: list,
|
||||
Option: doInsertOption,
|
||||
}
|
||||
return in.Next(m.GetCtx())
|
||||
}
|
||||
|
||||
func (m *Model) formatDoInsertOption(insertOption int, columnNames []string) (option DoInsertOption, err error) {
|
||||
|
||||
@ -532,9 +532,21 @@ func (m *Model) doGetAllBySql(sql string, args ...interface{}) (result Result, e
|
||||
}
|
||||
}
|
||||
}
|
||||
result, err = m.db.DoGetAll(
|
||||
m.GetCtx(), m.getLink(false), sql, m.mergeArguments(args)...,
|
||||
)
|
||||
|
||||
in := &HookSelectInput{
|
||||
internalParamHookSelect: internalParamHookSelect{
|
||||
internalParamHook: internalParamHook{
|
||||
db: m.db,
|
||||
link: m.getLink(false),
|
||||
},
|
||||
handler: m.hook.Select,
|
||||
},
|
||||
Table: m.tables,
|
||||
Sql: sql,
|
||||
Args: m.mergeArguments(args),
|
||||
}
|
||||
result, err = in.Next(m.GetCtx())
|
||||
|
||||
// Cache the result.
|
||||
if cacheKey != "" && err == nil {
|
||||
if m.cacheOption.Duration < 0 {
|
||||
|
||||
@ -74,14 +74,21 @@ func (m *Model) Update(dataAndWhere ...interface{}) (result sql.Result, err erro
|
||||
if !gstr.ContainsI(conditionStr, " WHERE ") {
|
||||
return nil, gerror.NewCode(gcode.CodeMissingParameter, "there should be WHERE condition statement for UPDATE operation")
|
||||
}
|
||||
return m.db.DoUpdate(
|
||||
m.GetCtx(),
|
||||
m.getLink(true),
|
||||
m.tables,
|
||||
newData,
|
||||
conditionStr,
|
||||
m.mergeArguments(conditionArgs)...,
|
||||
)
|
||||
|
||||
in := &HookUpdateInput{
|
||||
internalParamHookUpdate: internalParamHookUpdate{
|
||||
internalParamHook: internalParamHook{
|
||||
db: m.db,
|
||||
link: m.getLink(true),
|
||||
},
|
||||
handler: m.hook.Update,
|
||||
},
|
||||
Table: m.tables,
|
||||
Data: newData,
|
||||
Condition: conditionStr,
|
||||
Args: m.mergeArguments(conditionArgs),
|
||||
}
|
||||
return in.Next(m.GetCtx())
|
||||
}
|
||||
|
||||
// Increment increments a column's value by a given amount.
|
||||
|
||||
@ -143,7 +143,7 @@ func (m *Model) doWithScanStruct(pointer interface{}) error {
|
||||
}
|
||||
|
||||
// Recursively with feature checks.
|
||||
model = m.db.With(field.Value)
|
||||
model = m.db.With(field.Value).Hook(m.hook)
|
||||
if m.withAll {
|
||||
model = model.WithAll()
|
||||
} else {
|
||||
@ -258,7 +258,7 @@ func (m *Model) doWithScanStructs(pointer interface{}) error {
|
||||
fieldKeys = structType.FieldKeys()
|
||||
}
|
||||
// Recursively with feature checks.
|
||||
model = m.db.With(field.Value)
|
||||
model = m.db.With(field.Value).Hook(m.hook)
|
||||
if m.withAll {
|
||||
model = model.WithAll()
|
||||
} else {
|
||||
|
||||
136
database/gdb/gdb_z_mysql_feature_hook_test.go
Normal file
136
database/gdb/gdb_z_mysql_feature_hook_test.go
Normal file
@ -0,0 +1,136 @@
|
||||
// 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_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/gogf/gf/v2/container/gvar"
|
||||
"github.com/gogf/gf/v2/database/gdb"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/test/gtest"
|
||||
)
|
||||
|
||||
func Test_Model_Hook_Select(t *testing.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := db.Model(table).Hook(gdb.HookHandler{
|
||||
Select: func(ctx context.Context, in *gdb.HookSelectInput) (result gdb.Result, err error) {
|
||||
result, err = in.Next(ctx)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
for i, record := range result {
|
||||
record["test"] = gvar.New(100 + record["id"].Int())
|
||||
result[i] = record
|
||||
}
|
||||
return
|
||||
},
|
||||
})
|
||||
all, err := m.Where(`id > 6`).OrderAsc(`id`).All()
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(all), 4)
|
||||
t.Assert(all[0]["id"].Int(), 7)
|
||||
t.Assert(all[0]["test"].Int(), 107)
|
||||
t.Assert(all[1]["test"].Int(), 108)
|
||||
t.Assert(all[2]["test"].Int(), 109)
|
||||
t.Assert(all[3]["test"].Int(), 110)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Model_Hook_Insert(t *testing.T) {
|
||||
table := createTable()
|
||||
defer dropTable(table)
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := db.Model(table).Hook(gdb.HookHandler{
|
||||
Insert: func(ctx context.Context, in *gdb.HookInsertInput) (result sql.Result, err error) {
|
||||
for i, item := range in.Data {
|
||||
item["passport"] = fmt.Sprintf(`test_port_%d`, item["id"])
|
||||
item["nickname"] = fmt.Sprintf(`test_name_%d`, item["id"])
|
||||
in.Data[i] = item
|
||||
}
|
||||
return in.Next(ctx)
|
||||
},
|
||||
})
|
||||
_, err := m.Insert(g.Map{
|
||||
"id": 1,
|
||||
"nickname": "name_1",
|
||||
})
|
||||
t.AssertNil(err)
|
||||
one, err := m.One()
|
||||
t.AssertNil(err)
|
||||
t.Assert(one["id"].Int(), 1)
|
||||
t.Assert(one["passport"], `test_port_1`)
|
||||
t.Assert(one["nickname"], `test_name_1`)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Model_Hook_Update(t *testing.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := db.Model(table).Hook(gdb.HookHandler{
|
||||
Update: func(ctx context.Context, in *gdb.HookUpdateInput) (result sql.Result, err error) {
|
||||
switch value := in.Data.(type) {
|
||||
case gdb.List:
|
||||
for i, data := range value {
|
||||
data["passport"] = `port`
|
||||
data["nickname"] = `name`
|
||||
value[i] = data
|
||||
}
|
||||
in.Data = value
|
||||
|
||||
case gdb.Map:
|
||||
value["passport"] = `port`
|
||||
value["nickname"] = `name`
|
||||
in.Data = value
|
||||
}
|
||||
return in.Next(ctx)
|
||||
},
|
||||
})
|
||||
_, err := m.Data(g.Map{
|
||||
"nickname": "name_1",
|
||||
}).WherePri(1).Update()
|
||||
t.AssertNil(err)
|
||||
|
||||
one, err := m.One()
|
||||
t.AssertNil(err)
|
||||
t.Assert(one["id"].Int(), 1)
|
||||
t.Assert(one["passport"], `port`)
|
||||
t.Assert(one["nickname"], `name`)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Model_Hook_Delete(t *testing.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := db.Model(table).Hook(gdb.HookHandler{
|
||||
Delete: func(ctx context.Context, in *gdb.HookDeleteInput) (result sql.Result, err error) {
|
||||
return db.Model(table).Data(g.Map{
|
||||
"nickname": `deleted`,
|
||||
}).Where(in.Condition).Update()
|
||||
},
|
||||
})
|
||||
_, err := m.Where(1).Delete()
|
||||
t.AssertNil(err)
|
||||
|
||||
all, err := m.All()
|
||||
t.AssertNil(err)
|
||||
for _, item := range all {
|
||||
t.Assert(item["nickname"].String(), `deleted`)
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -550,7 +550,7 @@ func Test_Scan_JsonAttributes(t *testing.T) {
|
||||
}
|
||||
|
||||
table := "jfy_gift"
|
||||
array := gstr.SplitAndTrim(gtest.TestDataContent(`issue1380.sql`), ";")
|
||||
array := gstr.SplitAndTrim(gtest.DataContent(`issue1380.sql`), ";")
|
||||
for _, v := range array {
|
||||
if _, err := db.Exec(ctx, v); err != nil {
|
||||
gtest.Error(err)
|
||||
|
||||
@ -10,7 +10,6 @@ import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/gogf/gf/v2/debug/gdebug"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
"github.com/gogf/gf/v2/test/gtest"
|
||||
@ -1644,7 +1643,7 @@ func Test_Table_Relation_With_MultipleDepends1(t *testing.T) {
|
||||
dropTable("table_b")
|
||||
dropTable("table_c")
|
||||
}()
|
||||
for _, v := range gstr.SplitAndTrim(gfile.GetContents(gdebug.TestDataPath("with_multiple_depends.sql")), ";") {
|
||||
for _, v := range gstr.SplitAndTrim(gfile.GetContents(gtest.DataPath("with_multiple_depends.sql")), ";") {
|
||||
if _, err := db.Exec(ctx, v); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
@ -1716,7 +1715,7 @@ func Test_Table_Relation_With_MultipleDepends2(t *testing.T) {
|
||||
dropTable("table_b")
|
||||
dropTable("table_c")
|
||||
}()
|
||||
for _, v := range gstr.SplitAndTrim(gfile.GetContents(gdebug.TestDataPath("with_multiple_depends.sql")), ";") {
|
||||
for _, v := range gstr.SplitAndTrim(gfile.GetContents(gtest.DataPath("with_multiple_depends.sql")), ";") {
|
||||
if _, err := db.Exec(ctx, v); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
@ -1803,7 +1802,7 @@ func Test_Table_Relation_With_MultipleDepends_Embedded(t *testing.T) {
|
||||
dropTable("table_b")
|
||||
dropTable("table_c")
|
||||
}()
|
||||
for _, v := range gstr.SplitAndTrim(gfile.GetContents(gdebug.TestDataPath("with_multiple_depends.sql")), ";") {
|
||||
for _, v := range gstr.SplitAndTrim(gfile.GetContents(gtest.DataPath("with_multiple_depends.sql")), ";") {
|
||||
if _, err := db.Exec(ctx, v); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
@ -1996,7 +1995,7 @@ func Test_With_Feature_Issue1401(t *testing.T) {
|
||||
table1 = "parcels"
|
||||
table2 = "parcel_items"
|
||||
)
|
||||
array := gstr.SplitAndTrim(gtest.TestDataContent(`issue1401.sql`), ";")
|
||||
array := gstr.SplitAndTrim(gtest.DataContent(`issue1401.sql`), ";")
|
||||
for _, v := range array {
|
||||
if _, err := db.Exec(ctx, v); err != nil {
|
||||
gtest.Error(err)
|
||||
@ -2038,7 +2037,7 @@ func Test_With_Feature_Issue1412(t *testing.T) {
|
||||
table1 = "parcels"
|
||||
table2 = "items"
|
||||
)
|
||||
array := gstr.SplitAndTrim(gtest.TestDataContent(`issue1412.sql`), ";")
|
||||
array := gstr.SplitAndTrim(gtest.DataContent(`issue1412.sql`), ";")
|
||||
for _, v := range array {
|
||||
if _, err := db.Exec(ctx, v); err != nil {
|
||||
gtest.Error(err)
|
||||
|
||||
@ -18,7 +18,6 @@ import (
|
||||
"github.com/gogf/gf/v2/container/garray"
|
||||
"github.com/gogf/gf/v2/container/gmap"
|
||||
"github.com/gogf/gf/v2/database/gdb"
|
||||
"github.com/gogf/gf/v2/debug/gdebug"
|
||||
"github.com/gogf/gf/v2/encoding/gjson"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
@ -2195,7 +2194,7 @@ func Test_Model_FieldsEx_WithReservedWords(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var (
|
||||
table = "fieldsex_test_table"
|
||||
sqlTpcPath = gdebug.TestDataPath("reservedwords_table_tpl.sql")
|
||||
sqlTpcPath = gtest.DataPath("reservedwords_table_tpl.sql")
|
||||
sqlContent = gfile.GetContents(sqlTpcPath)
|
||||
)
|
||||
t.AssertNE(sqlContent, "")
|
||||
|
||||
Reference in New Issue
Block a user