Compare commits

...

11 Commits

18 changed files with 337 additions and 119 deletions

View File

@ -605,7 +605,7 @@ func (c *Core) convertRowsToResult(rows *sql.Rows) (Result, error) {
}
var (
values = make([]interface{}, len(columnNames))
records = make(Result, 0)
result = make(Result, 0)
scanArgs = make([]interface{}, len(values))
)
for i := range values {
@ -613,22 +613,22 @@ func (c *Core) convertRowsToResult(rows *sql.Rows) (Result, error) {
}
for {
if err := rows.Scan(scanArgs...); err != nil {
return records, err
return result, err
}
row := make(Record)
record := Record{}
for i, value := range values {
if value == nil {
row[columnNames[i]] = gvar.New(nil)
record[columnNames[i]] = gvar.New(nil)
} else {
row[columnNames[i]] = gvar.New(c.convertFieldValueToLocalValue(value, columnTypes[i]))
record[columnNames[i]] = gvar.New(c.convertFieldValueToLocalValue(value, columnTypes[i]))
}
}
records = append(records, row)
result = append(result, record)
if !rows.Next() {
break
}
}
return records, nil
return result, nil
}
// MarshalJSON implements the interface MarshalJSON for json.Marshal.

View File

@ -28,6 +28,7 @@ type TX struct {
master *sql.DB // master is the raw and underlying database manager.
transactionId string // transactionId is an unique id generated by this object for this transaction.
transactionCount int // transactionCount marks the times that Begins.
isClosed bool // isClosed marks this transaction has already been committed or rolled back.
}
const (
@ -162,6 +163,9 @@ func TXFromCtx(ctx context.Context, group string) *TX {
v := ctx.Value(transactionKeyForContext(group))
if v != nil {
tx := v.(*TX)
if tx.IsClosed() {
return nil
}
tx.ctx = ctx
return tx
}
@ -210,6 +214,7 @@ func (tx *TX) Commit() error {
IsTransaction: true,
}
)
tx.isClosed = true
tx.db.GetCore().addSqlToTracing(tx.ctx, sqlObj)
if tx.db.GetDebug() {
tx.db.GetCore().writeSqlToLogger(tx.ctx, sqlObj)
@ -243,6 +248,7 @@ func (tx *TX) Rollback() error {
IsTransaction: true,
}
)
tx.isClosed = true
tx.db.GetCore().addSqlToTracing(tx.ctx, sqlObj)
if tx.db.GetDebug() {
tx.db.GetCore().writeSqlToLogger(tx.ctx, sqlObj)
@ -250,6 +256,11 @@ func (tx *TX) Rollback() error {
return err
}
// IsClosed checks and returns this transaction has already been committed or rolled back.
func (tx *TX) IsClosed() bool {
return tx.isClosed
}
// Begin starts a nested transaction procedure.
func (tx *TX) Begin() error {
_, err := tx.Exec("SAVEPOINT " + tx.transactionKeyForNestedPoint())

View File

@ -448,7 +448,7 @@ func formatSql(sql string, args []interface{}) (newSql string, newArgs []interfa
}
// formatWhere formats where statement and its arguments for `Where` and `Having` statements.
func formatWhere(db DB, where interface{}, args []interface{}, omitEmpty bool) (newWhere string, newArgs []interface{}) {
func formatWhere(db DB, where interface{}, args []interface{}, omitEmpty bool, schema, table string) (newWhere string, newArgs []interface{}) {
var (
buffer = bytes.NewBuffer(nil)
rv = reflect.ValueOf(where)
@ -486,7 +486,12 @@ func formatWhere(db DB, where interface{}, args []interface{}, omitEmpty bool) (
})
break
}
for key, value := range DataToMapDeep(where) {
// Automatically mapping and filtering the struct attribute.
data := DataToMapDeep(where)
if table != "" {
data, _ = db.GetCore().mappingAndFilterData(schema, table, data, true)
}
for key, value := range data {
if omitEmpty && empty.IsEmpty(value) {
continue
}

View File

@ -99,7 +99,7 @@ func (c *Core) Model(tableNameQueryOrStruct ...interface{}) *Model {
conditionStr := gconv.String(tableNameQueryOrStruct[0])
if gstr.Contains(conditionStr, "?") {
tableStr, extraArgs = formatWhere(
c.db, conditionStr, tableNameQueryOrStruct[1:], false,
c.db, conditionStr, tableNameQueryOrStruct[1:], false, "", "",
)
}
}

View File

@ -277,6 +277,9 @@ func (m *Model) Order(orderBy ...string) *Model {
return m
}
model := m.getModel()
if model.orderBy != "" {
model.orderBy += ","
}
model.orderBy = m.db.GetCore().QuoteString(strings.Join(orderBy, " "))
return model
}
@ -287,6 +290,9 @@ func (m *Model) OrderAsc(column string) *Model {
return m
}
model := m.getModel()
if model.orderBy != "" {
model.orderBy += ","
}
model.orderBy = m.db.GetCore().QuoteWord(column) + " ASC"
return model
}
@ -297,6 +303,9 @@ func (m *Model) OrderDesc(column string) *Model {
return m
}
model := m.getModel()
if model.orderBy != "" {
model.orderBy += ","
}
model.orderBy = m.db.GetCore().QuoteWord(column) + " DESC"
return model
}
@ -377,7 +386,7 @@ func (m *Model) formatCondition(limit1 bool, isCountStatement bool) (conditionWh
case whereHolderWhere:
if conditionWhere == "" {
newWhere, newArgs := formatWhere(
m.db, v.where, v.args, m.option&OptionOmitEmpty > 0,
m.db, v.where, v.args, m.option&OptionOmitEmpty > 0, m.schema, m.tables,
)
if len(newWhere) > 0 {
conditionWhere = newWhere
@ -389,7 +398,7 @@ func (m *Model) formatCondition(limit1 bool, isCountStatement bool) (conditionWh
case whereHolderAnd:
newWhere, newArgs := formatWhere(
m.db, v.where, v.args, m.option&OptionOmitEmpty > 0,
m.db, v.where, v.args, m.option&OptionOmitEmpty > 0, m.schema, m.tables,
)
if len(newWhere) > 0 {
if len(conditionWhere) == 0 {
@ -404,7 +413,7 @@ func (m *Model) formatCondition(limit1 bool, isCountStatement bool) (conditionWh
case whereHolderOr:
newWhere, newArgs := formatWhere(
m.db, v.where, v.args, m.option&OptionOmitEmpty > 0,
m.db, v.where, v.args, m.option&OptionOmitEmpty > 0, m.schema, m.tables,
)
if len(newWhere) > 0 {
if len(conditionWhere) == 0 {
@ -446,7 +455,7 @@ func (m *Model) formatCondition(limit1 bool, isCountStatement bool) (conditionWh
// HAVING.
if len(m.having) > 0 {
havingStr, havingArgs := formatWhere(
m.db, m.having[0], gconv.Interfaces(m.having[1]), m.option&OptionOmitEmpty > 0,
m.db, m.having[0], gconv.Interfaces(m.having[1]), m.option&OptionOmitEmpty > 0, m.schema, m.tables,
)
if len(havingStr) > 0 {
conditionExtra += " HAVING " + havingStr

View File

@ -8,6 +8,7 @@ package gdb
import (
"fmt"
"github.com/gogf/gf/errors/gerror"
"reflect"
"github.com/gogf/gf/container/gset"
@ -195,6 +196,15 @@ func (m *Model) Array(fieldsAndWhere ...interface{}) ([]Value, error) {
return all.Array(), nil
}
// Struct retrieves one record from table and converts it into given struct.
// The parameter `pointer` should be type of *struct/**struct. If type **struct is given,
// it can create the struct internally during converting.
//
// Deprecated, use Scan instead.
func (m *Model) Struct(pointer interface{}, where ...interface{}) error {
return m.doStruct(pointer, where...)
}
// Struct retrieves one record from table and converts it into given struct.
// The parameter `pointer` should be type of *struct/**struct. If type **struct is given,
// it can create the struct internally during converting.
@ -202,24 +212,38 @@ func (m *Model) Array(fieldsAndWhere ...interface{}) ([]Value, error) {
// The optional parameter `where` is the same as the parameter of Model.Where function,
// see Model.Where.
//
// Note that it returns sql.ErrNoRows if there's no record retrieved with the given conditions
// from table and `pointer` is not nil.
// Note that it returns sql.ErrNoRows if the given parameter `pointer` pointed to a variable that has
// default value and there's no record retrieved with the given conditions from table.
//
// Eg:
// Example:
// user := new(User)
// err := db.Model("user").Where("id", 1).Struct(user)
// err := db.Model("user").Where("id", 1).Scan(user)
//
// user := (*User)(nil)
// err := db.Model("user").Where("id", 1).Struct(&user)
func (m *Model) Struct(pointer interface{}, where ...interface{}) error {
one, err := m.One(where...)
// err := db.Model("user").Where("id", 1).Scan(&user)
func (m *Model) doStruct(pointer interface{}, where ...interface{}) error {
model := m
// Auto selecting fields by struct attributes.
if model.fieldsEx == "" && (model.fields == "" || model.fields == "*") {
model = m.Fields(pointer)
}
one, err := model.One(where...)
if err != nil {
return err
}
if err = one.Struct(pointer); err != nil {
return err
}
return m.doWithScanStruct(pointer)
return model.doWithScanStruct(pointer)
}
// Structs retrieves records from table and converts them into given struct slice.
// The parameter `pointer` should be type of *[]struct/*[]*struct. It can create and fill the struct
// slice internally during converting.
//
// Deprecated, use Scan instead.
func (m *Model) Structs(pointer interface{}, where ...interface{}) error {
return m.doStructs(pointer, where...)
}
// Structs retrieves records from table and converts them into given struct slice.
@ -229,37 +253,45 @@ func (m *Model) Struct(pointer interface{}, where ...interface{}) error {
// The optional parameter `where` is the same as the parameter of Model.Where function,
// see Model.Where.
//
// Note that it returns sql.ErrNoRows if there's no record retrieved with the given conditions
// from table and `pointer` is not empty.
// Note that it returns sql.ErrNoRows if the given parameter `pointer` pointed to a variable that has
// default value and there's no record retrieved with the given conditions from table.
//
// Eg:
// Example:
// users := ([]User)(nil)
// err := db.Model("user").Structs(&users)
// err := db.Model("user").Scan(&users)
//
// users := ([]*User)(nil)
// err := db.Model("user").Structs(&users)
func (m *Model) Structs(pointer interface{}, where ...interface{}) error {
all, err := m.All(where...)
// err := db.Model("user").Scan(&users)
func (m *Model) doStructs(pointer interface{}, where ...interface{}) error {
model := m
// Auto selecting fields by struct attributes.
if model.fieldsEx == "" && (model.fields == "" || model.fields == "*") {
model = m.Fields(
reflect.New(
reflect.ValueOf(pointer).Elem().Type().Elem(),
).Interface(),
)
}
all, err := model.All(where...)
if err != nil {
return err
}
if err = all.Structs(pointer); err != nil {
return err
}
return m.doWithScanStructs(pointer)
return model.doWithScanStructs(pointer)
}
// Scan automatically calls Struct or Structs function according to the type of parameter `pointer`.
// It calls function Struct if `pointer` is type of *struct/**struct.
// It calls function Structs if `pointer` is type of *[]struct/*[]*struct.
// It calls function doStruct if `pointer` is type of *struct/**struct.
// It calls function doStructs if `pointer` is type of *[]struct/*[]*struct.
//
// The optional parameter `where` is the same as the parameter of Model.Where function,
// see Model.Where.
// The optional parameter `where` is the same as the parameter of Model.Where function, see Model.Where.
//
// Note that it returns sql.ErrNoRows if there's no record retrieved with the given conditions
// from table.
// Note that it returns sql.ErrNoRows if the given parameter `pointer` pointed to a variable that has
// default value and there's no record retrieved with the given conditions from table.
//
// Eg:
// Example:
// user := new(User)
// err := db.Model("user").Where("id", 1).Scan(user)
//
@ -272,16 +304,35 @@ func (m *Model) Structs(pointer interface{}, where ...interface{}) error {
// users := ([]*User)(nil)
// err := db.Model("user").Scan(&users)
func (m *Model) Scan(pointer interface{}, where ...interface{}) error {
var reflectType reflect.Type
var (
reflectValue reflect.Value
reflectKind reflect.Kind
)
if v, ok := pointer.(reflect.Value); ok {
reflectType = v.Type()
reflectValue = v
} else {
reflectType = reflect.TypeOf(pointer)
reflectValue = reflect.ValueOf(pointer)
}
if gstr.Contains(reflectType.String(), "[]") {
return m.Structs(pointer, where...)
reflectKind = reflectValue.Kind()
if reflectKind != reflect.Ptr {
return gerror.New(`the parameter "pointer" for function Scan should type of pointer`)
}
for reflectKind == reflect.Ptr {
reflectValue = reflectValue.Elem()
reflectKind = reflectValue.Kind()
}
switch reflectKind {
case reflect.Slice, reflect.Array:
return m.doStructs(pointer, where...)
case reflect.Struct, reflect.Invalid:
return m.doStruct(pointer, where...)
default:
return gerror.New(`element of parameter "pointer" for function Scan should type of struct/*struct/[]struct/[]*struct`)
}
return m.Struct(pointer, where...)
}
// ScanList converts `r` to struct slice which contains other complex struct attributes.
@ -515,6 +566,11 @@ func (m *Model) getFormattedSqlAndArgs(queryType int, limit1 bool) (sqlWithHolde
// DISTINCT t.user_id uid
countFields = fmt.Sprintf(`COUNT(%s%s)`, m.distinct, m.fields)
}
// Raw SQL Model.
if m.rawSql != "" {
sqlWithHolder = fmt.Sprintf("SELECT %s FROM (%s) AS T", countFields, m.rawSql)
return sqlWithHolder, nil
}
conditionWhere, conditionExtra, conditionArgs := m.formatCondition(false, true)
sqlWithHolder = fmt.Sprintf("SELECT %s FROM %s%s", countFields, m.tables, conditionWhere+conditionExtra)
if len(m.groupBy) > 0 {

View File

@ -93,17 +93,19 @@ func (m *Model) Update(dataAndWhere ...interface{}) (result sql.Result, err erro
}
// Increment increments a column's value by a given amount.
func (m *Model) Increment(column string, amount float64) (sql.Result, error) {
// The parameter `amount` can be type of float or integer.
func (m *Model) Increment(column string, amount interface{}) (sql.Result, error) {
return m.getModel().Data(column, &Counter{
Field: column,
Value: amount,
Value: gconv.Float64(amount),
}).Update()
}
// Decrement decrements a column's value by a given amount.
func (m *Model) Decrement(column string, amount float64) (sql.Result, error) {
// The parameter `amount` can be type of float or integer.
func (m *Model) Decrement(column string, amount interface{}) (sql.Result, error) {
return m.getModel().Data(column, &Counter{
Field: column,
Value: -amount,
Value: -gconv.Float64(amount),
}).Update()
}

View File

@ -153,7 +153,7 @@ func (r Result) MapKeyUint(key string) map[uint]Map {
return m
}
// RecordKeyInt converts `r` to a map[int]Record of which key is specified by `key`.
// RecordKeyStr converts `r` to a map[string]Record of which key is specified by `key`.
func (r Result) RecordKeyStr(key string) map[string]Record {
m := make(map[string]Record)
for _, item := range r {

View File

@ -899,7 +899,7 @@ func Test_Model_Struct(t *testing.T) {
CreateTime gtime.Time
}
user := new(User)
err := db.Model(table).Where("id=1").Struct(user)
err := db.Model(table).Where("id=1").Scan(user)
t.AssertNil(err)
t.Assert(user.NickName, "name_1")
t.Assert(user.CreateTime.String(), "2018-10-24 10:00:00")
@ -913,7 +913,7 @@ func Test_Model_Struct(t *testing.T) {
CreateTime *gtime.Time
}
user := new(User)
err := db.Model(table).Where("id=1").Struct(user)
err := db.Model(table).Where("id=1").Scan(user)
t.AssertNil(err)
t.Assert(user.NickName, "name_1")
t.Assert(user.CreateTime.String(), "2018-10-24 10:00:00")
@ -928,7 +928,7 @@ func Test_Model_Struct(t *testing.T) {
CreateTime *gtime.Time
}
user := (*User)(nil)
err := db.Model(table).Where("id=1").Struct(&user)
err := db.Model(table).Where("id=1").Scan(&user)
t.AssertNil(err)
t.Assert(user.NickName, "name_1")
t.Assert(user.CreateTime.String(), "2018-10-24 10:00:00")
@ -960,7 +960,7 @@ func Test_Model_Struct(t *testing.T) {
CreateTime *gtime.Time
}
user := new(User)
err := db.Model(table).Where("id=-1").Struct(user)
err := db.Model(table).Where("id=-1").Scan(user)
t.Assert(err, sql.ErrNoRows)
})
gtest.C(t, func(t *gtest.T) {
@ -972,7 +972,7 @@ func Test_Model_Struct(t *testing.T) {
CreateTime *gtime.Time
}
var user *User
err := db.Model(table).Where("id=-1").Struct(&user)
err := db.Model(table).Where("id=-1").Scan(&user)
t.AssertNil(err)
})
}
@ -992,7 +992,7 @@ func Test_Model_Struct_CustomType(t *testing.T) {
CreateTime gtime.Time
}
user := new(User)
err := db.Model(table).Where("id=1").Struct(user)
err := db.Model(table).Where("id=1").Scan(user)
t.AssertNil(err)
t.Assert(user.NickName, "name_1")
t.Assert(user.CreateTime.String(), "2018-10-24 10:00:00")
@ -1012,7 +1012,7 @@ func Test_Model_Structs(t *testing.T) {
CreateTime gtime.Time
}
var users []User
err := db.Model(table).Order("id asc").Structs(&users)
err := db.Model(table).Order("id asc").Scan(&users)
if err != nil {
gtest.Error(err)
}
@ -1035,7 +1035,7 @@ func Test_Model_Structs(t *testing.T) {
CreateTime *gtime.Time
}
var users []*User
err := db.Model(table).Order("id asc").Structs(&users)
err := db.Model(table).Order("id asc").Scan(&users)
if err != nil {
gtest.Error(err)
}
@ -1081,38 +1081,40 @@ func Test_Model_Structs(t *testing.T) {
CreateTime *gtime.Time
}
var users []*User
err := db.Model(table).Where("id<0").Structs(&users)
err := db.Model(table).Where("id<0").Scan(&users)
t.AssertNil(err)
})
}
func Test_Model_StructsWithJsonTag(t *testing.T) {
table := createInitTable()
defer dropTable(table)
gtest.C(t, func(t *gtest.T) {
type User struct {
Uid int `json:"id"`
Passport string
Password string
Name string `json:"nick_name"`
Time gtime.Time `json:"create_time"`
}
var users []User
err := db.Model(table).Order("id asc").Structs(&users)
if err != nil {
gtest.Error(err)
}
t.Assert(len(users), TableSize)
t.Assert(users[0].Uid, 1)
t.Assert(users[1].Uid, 2)
t.Assert(users[2].Uid, 3)
t.Assert(users[0].Name, "name_1")
t.Assert(users[1].Name, "name_2")
t.Assert(users[2].Name, "name_3")
t.Assert(users[0].Time.String(), "2018-10-24 10:00:00")
})
}
// JSON tag is only used for JSON Marshal/Unmarshal, DO NOT use it in multiple purposes!
//func Test_Model_StructsWithJsonTag(t *testing.T) {
// table := createInitTable()
// defer dropTable(table)
//
// db.SetDebug(true)
// gtest.C(t, func(t *gtest.T) {
// type User struct {
// Uid int `json:"id"`
// Passport string
// Password string
// Name string `json:"nick_name"`
// Time gtime.Time `json:"create_time"`
// }
// var users []User
// err := db.Model(table).Order("id asc").Scan(&users)
// if err != nil {
// gtest.Error(err)
// }
// t.Assert(len(users), TableSize)
// t.Assert(users[0].Uid, 1)
// t.Assert(users[1].Uid, 2)
// t.Assert(users[2].Uid, 3)
// t.Assert(users[0].Name, "name_1")
// t.Assert(users[1].Name, "name_2")
// t.Assert(users[2].Name, "name_3")
// t.Assert(users[0].Time.String(), "2018-10-24 10:00:00")
// })
//}
func Test_Model_Scan(t *testing.T) {
table := createInitTable()
@ -1469,11 +1471,11 @@ func Test_Model_Where(t *testing.T) {
t.Assert(len(result), 3)
t.Assert(result[0]["id"].Int(), 1)
})
// struct
// struct, automatic mapping and filtering.
gtest.C(t, func(t *gtest.T) {
type User struct {
Id int `json:"id"`
Nickname string `gconv:"nickname"`
Id int
Nickname string
}
result, err := db.Model(table).Where(User{3, "name_3"}).One()
t.AssertNil(err)
@ -3098,7 +3100,7 @@ func Test_TimeZoneInsert(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
_, _ = db.Model(tableName).Unscoped().Insert(u)
userEntity := &User{}
err := db.Model(tableName).Where("id", 1).Unscoped().Struct(&userEntity)
err := db.Model(tableName).Where("id", 1).Unscoped().Scan(&userEntity)
t.AssertNil(err)
t.Assert(userEntity.CreatedAt.String(), "2020-11-22 04:23:45")
t.Assert(userEntity.UpdatedAt.String(), "2020-11-22 05:23:45")
@ -3129,7 +3131,7 @@ func Test_Model_Fields_Map_Struct(t *testing.T) {
XXX_TYPE int
}
var a = A{}
err := db.Model(table).Fields(a).Where("id", 1).Struct(&a)
err := db.Model(table).Fields(a).Where("id", 1).Scan(&a)
t.AssertNil(err)
t.Assert(a.ID, 1)
t.Assert(a.PASSPORT, "user_1")
@ -3143,7 +3145,7 @@ func Test_Model_Fields_Map_Struct(t *testing.T) {
XXX_TYPE int
}
var a *A
err := db.Model(table).Fields(a).Where("id", 1).Struct(&a)
err := db.Model(table).Fields(a).Where("id", 1).Scan(&a)
t.AssertNil(err)
t.Assert(a.ID, 1)
t.Assert(a.PASSPORT, "user_1")
@ -3157,7 +3159,7 @@ func Test_Model_Fields_Map_Struct(t *testing.T) {
XXX_TYPE int
}
var a *A
err := db.Model(table).Fields(&a).Where("id", 1).Struct(&a)
err := db.Model(table).Fields(&a).Where("id", 1).Scan(&a)
t.AssertNil(err)
t.Assert(a.ID, 1)
t.Assert(a.PASSPORT, "user_1")
@ -3714,4 +3716,16 @@ func Test_Model_Raw(t *testing.T) {
t.Assert(all[0]["id"], 7)
t.Assert(all[1]["id"], 5)
})
gtest.C(t, func(t *gtest.T) {
count, err := db.
Raw(fmt.Sprintf("select * from %s where id in (?)", table), g.Slice{1, 5, 7, 8, 9, 10}).
WhereLT("id", 8).
WhereIn("id", g.Slice{1, 2, 3, 4, 5, 6, 7}).
OrderDesc("id").
Limit(2).
Count()
t.AssertNil(err)
t.Assert(count, 6)
})
}

View File

@ -121,7 +121,7 @@ func Test_Struct_Pointer_Attribute(t *testing.T) {
})
gtest.C(t, func(t *gtest.T) {
user := new(User)
err := db.Model(table).Struct(user, "id=1")
err := db.Model(table).Scan(user, "id=1")
t.AssertNil(err)
t.Assert(*user.Id, 1)
t.Assert(*user.Passport, "user_1")
@ -130,7 +130,7 @@ func Test_Struct_Pointer_Attribute(t *testing.T) {
})
gtest.C(t, func(t *gtest.T) {
var user *User
err := db.Model(table).Struct(&user, "id=1")
err := db.Model(table).Scan(&user, "id=1")
t.AssertNil(err)
t.Assert(*user.Id, 1)
t.Assert(*user.Passport, "user_1")
@ -201,7 +201,7 @@ func Test_Structs_Pointer_Attribute(t *testing.T) {
// Structs
gtest.C(t, func(t *gtest.T) {
users := make([]User, 0)
err := db.Model(table).Structs(&users, "id < 3")
err := db.Model(table).Scan(&users, "id < 3")
t.AssertNil(err)
t.Assert(len(users), 2)
t.Assert(*users[0].Id, 1)
@ -211,7 +211,7 @@ func Test_Structs_Pointer_Attribute(t *testing.T) {
})
gtest.C(t, func(t *gtest.T) {
users := make([]*User, 0)
err := db.Model(table).Structs(&users, "id < 3")
err := db.Model(table).Scan(&users, "id < 3")
t.AssertNil(err)
t.Assert(len(users), 2)
t.Assert(*users[0].Id, 1)
@ -221,7 +221,7 @@ func Test_Structs_Pointer_Attribute(t *testing.T) {
})
gtest.C(t, func(t *gtest.T) {
var users []User
err := db.Model(table).Structs(&users, "id < 3")
err := db.Model(table).Scan(&users, "id < 3")
t.AssertNil(err)
t.Assert(len(users), 2)
t.Assert(*users[0].Id, 1)
@ -231,7 +231,7 @@ func Test_Structs_Pointer_Attribute(t *testing.T) {
})
gtest.C(t, func(t *gtest.T) {
var users []*User
err := db.Model(table).Structs(&users, "id < 3")
err := db.Model(table).Scan(&users, "id < 3")
t.AssertNil(err)
t.Assert(len(users), 2)
t.Assert(*users[0].Id, 1)
@ -254,7 +254,7 @@ func Test_Struct_Empty(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
user := new(User)
err := db.Model(table).Where("id=100").Struct(user)
err := db.Model(table).Where("id=100").Scan(user)
t.Assert(err, sql.ErrNoRows)
t.AssertNE(user, nil)
})
@ -269,7 +269,7 @@ func Test_Struct_Empty(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
var user *User
err := db.Model(table).Where("id=100").Struct(&user)
err := db.Model(table).Where("id=100").Scan(&user)
t.AssertNil(err)
t.Assert(user, nil)
})
@ -452,3 +452,27 @@ func Test_Model_Scan_Map(t *testing.T) {
t.Assert(users[9].CreateTime.String(), CreateTime)
})
}
func Test_Scan_AutoFilteringByStructAttributes(t *testing.T) {
table := createInitTable()
defer dropTable(table)
type User struct {
Id int
Passport string
}
//db.SetDebug(true)
gtest.C(t, func(t *gtest.T) {
var user *User
err := db.Model(table).OrderAsc("id").Scan(&user)
t.AssertNil(err)
t.Assert(user.Id, 1)
})
gtest.C(t, func(t *gtest.T) {
var users []User
err := db.Model(table).OrderAsc("id").Scan(&users)
t.AssertNil(err)
t.Assert(len(users), TableSize)
t.Assert(users[0].Id, 1)
})
}

View File

@ -666,7 +666,6 @@ func Test_TX_GetScan(t *testing.T) {
}
func Test_TX_Delete(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
table := createInitTable()
defer dropTable(table)
@ -685,6 +684,8 @@ func Test_TX_Delete(t *testing.T) {
} else {
t.Assert(n, 0)
}
t.Assert(tx.IsClosed(), true)
})
gtest.C(t, func(t *gtest.T) {
@ -711,6 +712,8 @@ func Test_TX_Delete(t *testing.T) {
t.Assert(n, TableSize)
t.AssertNE(n, 0)
}
t.Assert(tx.IsClosed(), true)
})
}
@ -721,7 +724,7 @@ func Test_Transaction(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
ctx := context.TODO()
err := db.Transaction(ctx, func(ctx context.Context, tx *gdb.TX) error {
if _, err := tx.Replace(table, g.Map{
if _, err := tx.Ctx(ctx).Replace(table, g.Map{
"id": 1,
"passport": "USER_1",
"password": "PASS_1",
@ -730,11 +733,12 @@ func Test_Transaction(t *testing.T) {
}); err != nil {
t.Error(err)
}
t.Assert(tx.IsClosed(), false)
return gerror.New("error")
})
t.AssertNE(err, nil)
if value, err := db.Model(table).Fields("nickname").Where("id", 1).Value(); err != nil {
if value, err := db.Model(table).Ctx(ctx).Fields("nickname").Where("id", 1).Value(); err != nil {
gtest.Error(err)
} else {
t.Assert(value.String(), "name_1")

View File

@ -87,7 +87,7 @@ func Test_Types(t *testing.T) {
TinyInt bool
}
var obj *T
err = db.Model("types").Struct(&obj)
err = db.Model("types").Scan(&obj)
t.AssertNil(err)
t.Assert(obj.Id, 1)
t.Assert(obj.Blob, data["blob"])

View File

@ -8,6 +8,7 @@ package gdebug
import (
"fmt"
"github.com/gogf/gf/internal/utils"
"os"
"os/exec"
"path/filepath"
@ -53,11 +54,13 @@ func Caller(skip ...int) (function string, path string, line int) {
//
// The parameter <filter> is used to filter the path of the caller.
func CallerWithFilter(filter string, skip ...int) (function string, path string, line int) {
number := 0
var (
number = 0
ok = true
)
if len(skip) > 0 {
number = skip[0]
}
ok := true
pc, file, line, start := callerFromIndex([]string{filter})
if start != -1 {
for i := start + number; i < maxCallerDepth; i++ {
@ -65,12 +68,6 @@ func CallerWithFilter(filter string, skip ...int) (function string, path string,
pc, file, line, ok = runtime.Caller(i)
}
if ok {
if filter != "" && strings.Contains(file, filter) {
continue
}
if strings.Contains(file, stackFilterKey) {
continue
}
function := ""
if fn := runtime.FuncForPC(pc); fn == nil {
function = "unknown"
@ -104,8 +101,14 @@ func callerFromIndex(filters []string) (pc uintptr, file string, line int, index
if filtered {
continue
}
if strings.Contains(file, stackFilterKey) {
continue
if !utils.IsDebugEnabled() {
if strings.Contains(file, utils.StackFilterKeyForGoFrame) {
continue
}
} else {
if strings.Contains(file, stackFilterKey) {
continue
}
}
if index > 0 {
index--

View File

@ -80,14 +80,17 @@ func StackWithFilters(filters []string, skip ...int) string {
if filtered {
continue
}
if strings.Contains(file, stackFilterKey) {
continue
}
if !utils.IsDebugEnabled() {
if strings.Contains(file, utils.StackFilterKeyForGoFrame) {
continue
}
} else {
if strings.Contains(file, stackFilterKey) {
continue
}
}
if fn := runtime.FuncForPC(pc); fn == nil {
name = "unknown"
} else {

View File

@ -11,8 +11,8 @@ import (
)
const (
debugKey = "gf.debug" // Debug key for checking if in debug mode.
StackFilterKeyForGoFrame = "/github.com/gogf/gf/" // Stack filtering key for all GoFrame module paths.
debugKey = "gf.debug" // Debug key for checking if in debug mode.
StackFilterKeyForGoFrame = "github.com/gogf/gf@" // Stack filtering key for all GoFrame module paths.
)
var (

View File

@ -621,3 +621,89 @@ func Test_Params_Parse_Validation(t *testing.T) {
t.Assert(client.GetContent("/parse?name=john11&password1=123456&password2=123456"), `ok`)
})
}
func Test_Params_Parse_EmbeddedWithAliasName1(t *testing.T) {
// 获取内容列表
type ContentGetListInput struct {
Type string
CategoryId uint
Page int
Size int
Sort int
UserId uint
}
// 获取内容列表
type ContentGetListReq struct {
ContentGetListInput
CategoryId uint `p:"cate"`
Page int `d:"1" v:"min:0#分页号码错误"`
Size int `d:"10" v:"max:50#分页数量最大50条"`
}
p, _ := ports.PopRand()
s := g.Server(p)
s.BindHandler("/parse", func(r *ghttp.Request) {
var req *ContentGetListReq
if err := r.Parse(&req); err != nil {
r.Response.Write(err)
} else {
r.Response.Write(req.ContentGetListInput)
}
})
s.SetPort(p)
s.SetDumpRouterMap(false)
s.Start()
defer s.Shutdown()
time.Sleep(100 * time.Millisecond)
gtest.C(t, func(t *gtest.T) {
prefix := fmt.Sprintf("http://127.0.0.1:%d", p)
client := g.Client()
client.SetPrefix(prefix)
t.Assert(client.GetContent("/parse?cate=1&page=2&size=10"), `{"Type":"","CategoryId":0,"Page":2,"Size":10,"Sort":0,"UserId":0}`)
})
}
func Test_Params_Parse_EmbeddedWithAliasName2(t *testing.T) {
// 获取内容列表
type ContentGetListInput struct {
Type string
CategoryId uint `p:"cate"`
Page int
Size int
Sort int
UserId uint
}
// 获取内容列表
type ContentGetListReq struct {
ContentGetListInput
CategoryId uint `p:"cate"`
Page int `d:"1" v:"min:0#分页号码错误"`
Size int `d:"10" v:"max:50#分页数量最大50条"`
}
p, _ := ports.PopRand()
s := g.Server(p)
s.BindHandler("/parse", func(r *ghttp.Request) {
var req *ContentGetListReq
if err := r.Parse(&req); err != nil {
r.Response.Write(err)
} else {
r.Response.Write(req.ContentGetListInput)
}
})
s.SetPort(p)
s.SetDumpRouterMap(false)
s.Start()
defer s.Shutdown()
time.Sleep(100 * time.Millisecond)
gtest.C(t, func(t *gtest.T) {
prefix := fmt.Sprintf("http://127.0.0.1:%d", p)
client := g.Client()
client.SetPrefix(prefix)
t.Assert(client.GetContent("/parse?cate=1&page=2&size=10"), `{"Type":"","CategoryId":1,"Page":2,"Size":10,"Sort":0,"UserId":0}`)
})
}

View File

@ -25,6 +25,7 @@ const (
)
var (
// Note that `currentMode` is not concurrent safe.
currentMode = NOT_SET
)

View File

@ -1,4 +1,4 @@
package gf
const VERSION = "v1.16.3"
const VERSION = "v1.16.4"
const AUTHORS = "john<john@goframe.org>"