Merge branch 'develop'

This commit is contained in:
John Guo
2022-03-18 11:52:02 +08:00
70 changed files with 816 additions and 301 deletions

View File

@ -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 (

View File

@ -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
}

View File

@ -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.

View File

@ -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)),
)

View File

@ -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.

View File

@ -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())
}

View 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
}

View File

@ -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) {

View File

@ -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 {

View File

@ -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.

View File

@ -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 {

View 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`)
}
})
}

View File

@ -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)

View File

@ -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)

View File

@ -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, "")