merge master

This commit is contained in:
John Guo
2021-07-15 21:23:36 +08:00
36 changed files with 992 additions and 206 deletions

2
.gitignore vendored
View File

@ -15,3 +15,5 @@ cbuild
**/.DS_Store
.vscode/
.example/other/
main
gf

View File

@ -7,92 +7,45 @@
package gvar
import (
"github.com/gogf/gf/internal/empty"
"reflect"
"github.com/gogf/gf/internal/utils"
)
// IsNil checks whether <v> is nil.
// IsNil checks whether `v` is nil.
func (v *Var) IsNil() bool {
return v.Val() == nil
return utils.IsNil(v.Val())
}
// IsEmpty checks whether <v> is empty.
// IsEmpty checks whether `v` is empty.
func (v *Var) IsEmpty() bool {
return empty.IsEmpty(v.Val())
return utils.IsEmpty(v.Val())
}
// IsInt checks whether <v> is type of int.
// IsInt checks whether `v` is type of int.
func (v *Var) IsInt() bool {
switch v.Val().(type) {
case int, *int, int8, *int8, int16, *int16, int32, *int32, int64, *int64:
return true
}
return false
return utils.IsInt(v.Val())
}
// IsUint checks whether <v> is type of uint.
// IsUint checks whether `v` is type of uint.
func (v *Var) IsUint() bool {
switch v.Val().(type) {
case uint, *uint, uint8, *uint8, uint16, *uint16, uint32, *uint32, uint64, *uint64:
return true
}
return false
return utils.IsUint(v.Val())
}
// IsFloat checks whether <v> is type of float.
// IsFloat checks whether `v` is type of float.
func (v *Var) IsFloat() bool {
switch v.Val().(type) {
case float32, *float32, float64, *float64:
return true
}
return false
return utils.IsFloat(v.Val())
}
// IsSlice checks whether <v> is type of slice.
// IsSlice checks whether `v` is type of slice.
func (v *Var) IsSlice() bool {
var (
reflectValue = reflect.ValueOf(v.Val())
reflectKind = reflectValue.Kind()
)
for reflectKind == reflect.Ptr {
reflectValue = reflectValue.Elem()
}
switch reflectKind {
case reflect.Slice, reflect.Array:
return true
}
return false
return utils.IsSlice(v.Val())
}
// IsMap checks whether <v> is type of map.
// IsMap checks whether `v` is type of map.
func (v *Var) IsMap() bool {
var (
reflectValue = reflect.ValueOf(v.Val())
reflectKind = reflectValue.Kind()
)
for reflectKind == reflect.Ptr {
reflectValue = reflectValue.Elem()
}
switch reflectKind {
case reflect.Map:
return true
}
return false
return utils.IsMap(v.Val())
}
// IsStruct checks whether <v> is type of struct.
// IsStruct checks whether `v` is type of struct.
func (v *Var) IsStruct() bool {
var (
reflectValue = reflect.ValueOf(v.Val())
reflectKind = reflectValue.Kind()
)
for reflectKind == reflect.Ptr {
reflectValue = reflectValue.Elem()
reflectKind = reflectValue.Kind()
}
switch reflectKind {
case reflect.Struct:
return true
}
return false
return utils.IsStruct(v.Val())
}

View File

@ -41,7 +41,7 @@ func (v *Var) MapDeep(tags ...string) map[string]interface{} {
return gconv.MapDeep(v.Val(), tags...)
}
// MapDeep converts and returns <v> as map[string]string recursively.
// MapStrStrDeep converts and returns <v> as map[string]string recursively.
func (v *Var) MapStrStrDeep(tags ...string) map[string]string {
return gconv.MapStrStrDeep(v.Val(), tags...)
}

View File

@ -353,7 +353,7 @@ func (c *Core) Save(table string, data interface{}, batch ...int) (sql.Result, e
return c.Model(table).Data(data).Save()
}
// DoInsert inserts or updates data for given table.
// DoInsert inserts or updates data forF given table.
// This function is usually used for custom interface definition, you do not need call it manually.
// The parameter `data` can be type of map/gmap/struct/*struct/[]map/[]struct, etc.
// Eg:
@ -510,29 +510,34 @@ func (c *Core) DoUpdate(ctx context.Context, link Link, table string, data inter
switch kind {
case reflect.Map, reflect.Struct:
var (
fields []string
dataMap = ConvertDataForTableRecord(data)
fields []string
dataMap = ConvertDataForTableRecord(data)
counterHandler = func(column string, counter Counter) {
if counter.Value != 0 {
var (
column = c.QuoteWord(column)
columnRef = c.QuoteWord(counter.Field)
columnVal = counter.Value
operator = "+"
)
if columnVal < 0 {
operator = "-"
columnVal = -columnVal
}
fields = append(fields, fmt.Sprintf("%s=%s%s?", column, columnRef, operator))
params = append(params, columnVal)
}
}
)
for k, v := range dataMap {
switch value := v.(type) {
case *Counter:
if value.Value != 0 {
column := k
if value.Field != "" {
column = c.QuoteWord(value.Field)
}
fields = append(fields, fmt.Sprintf("%s=%s+?", column, column))
params = append(params, value.Value)
}
counterHandler(k, *value)
case Counter:
if value.Value != 0 {
column := k
if value.Field != "" {
column = c.QuoteWord(value.Field)
}
fields = append(fields, fmt.Sprintf("%s=%s+?", column, column))
params = append(params, value.Value)
}
counterHandler(k, value)
default:
if s, ok := v.(Raw); ok {
fields = append(fields, c.QuoteWord(k)+"="+gconv.String(s))
@ -543,6 +548,7 @@ func (c *Core) DoUpdate(ctx context.Context, link Link, table string, data inter
}
}
updates = strings.Join(fields, ",")
default:
updates = gconv.String(data)
}

View File

@ -287,3 +287,17 @@ ORDER BY a.id,a.colorder`,
}
return
}
// DoInsert is not supported in mssql.
func (d *DriverMssql) DoInsert(ctx context.Context, link Link, table string, list List, option DoInsertOption) (result sql.Result, err error) {
switch option.InsertOption {
case insertOptionSave:
return nil, gerror.New(`Save operation is not supported by mssql driver`)
case insertOptionReplace:
return nil, gerror.New(`Replace operation is not supported by mssql driver`)
default:
return d.Core.DoInsert(ctx, link, table, list, option)
}
}

View File

@ -263,7 +263,27 @@ func (d *DriverOracle) getTableUniqueIndex(table string) (fields map[string]map[
return
}
// DoInsert inserts or updates data for given table.
// This function is usually used for custom interface definition, you do not need call it manually.
// The parameter `data` can be type of map/gmap/struct/*struct/[]map/[]struct, etc.
// Eg:
// Data(g.Map{"uid": 10000, "name":"john"})
// Data(g.Slice{g.Map{"uid": 10000, "name":"john"}, g.Map{"uid": 20000, "name":"smith"})
//
// The parameter `option` values are as follows:
// 0: insert: just insert, if there's unique/primary key in the data, it returns error;
// 1: replace: if there's unique/primary key in the data, it deletes it from table and inserts a new one;
// 2: save: if there's unique/primary key in the data, it updates it or else inserts a new one;
// 3: ignore: if there's unique/primary key in the data, it ignores the inserting;
func (d *DriverOracle) DoInsert(ctx context.Context, link Link, table string, list List, option DoInsertOption) (result sql.Result, err error) {
switch option.InsertOption {
case insertOptionSave:
return nil, gerror.New(`Save operation is not supported by mssql driver`)
case insertOptionReplace:
return nil, gerror.New(`Replace operation is not supported by mssql driver`)
}
var (
keys []string
values []string

View File

@ -142,9 +142,18 @@ func (d *DriverPgsql) TableFields(ctx context.Context, table string, schema ...s
result Result
link, err = d.SlaveLink(useSchema)
structureSql = fmt.Sprintf(`
SELECT a.attname AS field, t.typname AS type,b.description as comment FROM pg_class c, pg_attribute a
LEFT OUTER JOIN pg_description b ON a.attrelid=b.objoid AND a.attnum = b.objsubid,pg_type t
WHERE c.relname = '%s' and a.attnum > 0 and a.attrelid = c.oid and a.atttypid = t.oid
SELECT a.attname AS field, t.typname AS type,a.attnotnull as null,
(case when d.contype is not null then 'pri' else '' end) as key
,ic.column_default as default_value,b.description as comment
,coalesce(character_maximum_length, numeric_precision, -1) as length
,numeric_scale as scale
FROM pg_attribute a
left join pg_class c on a.attrelid = c.oid
left join pg_constraint d on d.conrelid = c.oid and a.attnum = d.conkey[1]
left join pg_description b ON a.attrelid=b.objoid AND a.attnum = b.objsubid
left join pg_type t ON a.atttypid = t.oid
left join information_schema.columns ic on ic.column_name = a.attname and ic.table_name = c.relname
WHERE c.relname = '%s' and a.attnum > 0
ORDER BY a.attnum`,
strings.ToLower(table),
)
@ -163,6 +172,9 @@ ORDER BY a.attnum`,
Index: i,
Name: m["field"].String(),
Type: m["type"].String(),
Null: m["null"].Bool(),
Key: m["key"].String(),
Default: m["default_value"].Val(),
Comment: m["comment"].String(),
}
}
@ -173,3 +185,17 @@ ORDER BY a.attnum`,
}
return
}
// DoInsert is not supported in pgsql.
func (d *DriverPgsql) DoInsert(ctx context.Context, link Link, table string, list List, option DoInsertOption) (result sql.Result, err error) {
switch option.InsertOption {
case insertOptionSave:
return nil, gerror.New(`Save operation is not supported by pgsql driver`)
case insertOptionReplace:
return nil, gerror.New(`Replace operation is not supported by pgsql driver`)
default:
return d.Core.DoInsert(ctx, link, table, list, option)
}
}

View File

@ -136,3 +136,17 @@ func (d *DriverSqlite) TableFields(ctx context.Context, table string, schema ...
}
return
}
// DoInsert is not supported in sqlite.
func (d *DriverSqlite) DoInsert(ctx context.Context, link Link, table string, list List, option DoInsertOption) (result sql.Result, err error) {
switch option.InsertOption {
case insertOptionSave:
return nil, gerror.New(`Save operation is not supported by sqlite driver`)
case insertOptionReplace:
return nil, gerror.New(`Replace operation is not supported by sqlite driver`)
default:
return d.Core.DoInsert(ctx, link, table, list, option)
}
}

View File

@ -70,6 +70,31 @@ var (
structTagPriority = append([]string{OrmTagForStruct}, gconv.StructTagPriority...)
)
// guessPrimaryTableName parses and returns the primary table name.
func (m *Model) guessPrimaryTableName(tableStr string) string {
if tableStr == "" {
return ""
}
var (
guessedTableName = ""
array1 = gstr.SplitAndTrim(tableStr, ",")
array2 = gstr.SplitAndTrim(array1[0], " ")
array3 = gstr.SplitAndTrim(array2[0], ".")
)
if len(array3) >= 2 {
guessedTableName = array3[1]
}
guessedTableName = array3[0]
charL, charR := m.db.GetChars()
if charL != "" || charR != "" {
guessedTableName = gstr.Trim(guessedTableName, charL+charR)
}
if !gregex.IsMatchString(regularFieldNameRegPattern, guessedTableName) {
return ""
}
return guessedTableName
}
// getTableNameFromOrmTag retrieves and returns the table name from struct object.
func getTableNameFromOrmTag(object interface{}) string {
var tableName string
@ -239,7 +264,7 @@ func DataToMapDeep(value interface{}) map[string]interface{} {
name = ""
fieldTag = rtField.Tag
for _, tag := range structTagPriority {
if s := fieldTag.Get(tag); s != "" && gregex.IsMatchString(regularFieldNameWithoutDotRegPattern, s) {
if s := fieldTag.Get(tag); s != "" {
name = s
break
}
@ -789,7 +814,9 @@ func formatError(err error, sql string, args ...interface{}) error {
func FormatSqlWithArgs(sql string, args []interface{}) string {
index := -1
newQuery, _ := gregex.ReplaceStringFunc(
`(\?|:v\d+|\$\d+|@p\d+)`, sql, func(s string) string {
`(\?|:v\d+|\$\d+|@p\d+)`,
sql,
func(s string) string {
index++
if len(args) > index {
if args[index] == nil {
@ -809,6 +836,7 @@ func FormatSqlWithArgs(sql string, args []interface{}) string {
switch kind {
case reflect.String, reflect.Map, reflect.Slice, reflect.Array:
return `'` + gstr.QuoteMeta(gconv.String(args[index]), `'`) + `'`
case reflect.Struct:
if t, ok := args[index].(time.Time); ok {
return `'` + t.Format(`2006-01-02 15:04:05`) + `'`

View File

@ -206,7 +206,7 @@ func (m *Model) As(as string) *Model {
if m.tables != "" {
model := m.getModel()
split := " JOIN "
if gstr.Contains(model.tables, split) {
if gstr.ContainsI(model.tables, split) {
// For join table.
array := gstr.Split(model.tables, split)
array[len(array)-1], _ = gregex.ReplaceString(`(.+) ON`, fmt.Sprintf(`$1 AS %s ON`, as), array[len(array)-1])

View File

@ -90,13 +90,13 @@ func (m *Model) FieldsStr(prefix ...string) string {
}
// GetFieldsStr retrieves and returns all fields from the table, joined with char ','.
// The optional parameter `prefix` specifies the prefix for each field, eg: FieldsStr("u.").
// The optional parameter `prefix` specifies the prefix for each field, eg: GetFieldsStr("u.").
func (m *Model) GetFieldsStr(prefix ...string) string {
prefixStr := ""
if len(prefix) > 0 {
prefixStr = prefix[0]
}
tableFields, err := m.TableFields(m.tables)
tableFields, err := m.TableFields(m.tablesInit)
if err != nil {
panic(err)
}
@ -136,7 +136,7 @@ func (m *Model) GetFieldsExStr(fields string, prefix ...string) string {
if len(prefix) > 0 {
prefixStr = prefix[0]
}
tableFields, err := m.TableFields(m.tables)
tableFields, err := m.TableFields(m.tablesInit)
if err != nil {
panic(err)
}
@ -164,7 +164,7 @@ func (m *Model) GetFieldsExStr(fields string, prefix ...string) string {
// HasField determine whether the field exists in the table.
func (m *Model) HasField(field string) (bool, error) {
tableFields, err := m.TableFields(m.tables)
tableFields, err := m.TableFields(m.tablesInit)
if err != nil {
return false, err
}

View File

@ -76,7 +76,7 @@ func (m *Model) getFieldsFiltered() string {
panic("function FieldsEx supports only single table operations")
}
// Filter table fields with fieldEx.
tableFields, err := m.TableFields(m.tables)
tableFields, err := m.TableFields(m.tablesInit)
if err != nil {
panic(err)
}
@ -358,11 +358,11 @@ func (m *Model) Scan(pointer interface{}, where ...interface{}) error {
// parameter.
// See the example or unit testing cases for clear understanding for this function.
func (m *Model) ScanList(listPointer interface{}, attributeName string, relation ...string) (err error) {
all, err := m.All()
result, err := m.All()
if err != nil {
return err
}
return all.ScanList(listPointer, attributeName, relation...)
return doScanList(m, result, listPointer, attributeName, relation...)
}
// Count does "SELECT COUNT(x) FROM ..." statement for the model.

View File

@ -40,7 +40,7 @@ func (m *Model) getSoftFieldNameCreated(table ...string) string {
if len(table) > 0 {
tableName = table[0]
} else {
tableName = m.getPrimaryTableName()
tableName = m.tablesInit
}
config := m.db.GetConfig()
if config.CreatedAt != "" {
@ -61,7 +61,7 @@ func (m *Model) getSoftFieldNameUpdated(table ...string) (field string) {
if len(table) > 0 {
tableName = table[0]
} else {
tableName = m.getPrimaryTableName()
tableName = m.tablesInit
}
config := m.db.GetConfig()
if config.UpdatedAt != "" {
@ -82,7 +82,7 @@ func (m *Model) getSoftFieldNameDeleted(table ...string) (field string) {
if len(table) > 0 {
tableName = table[0]
} else {
tableName = m.getPrimaryTableName()
tableName = m.tablesInit
}
config := m.db.GetConfig()
if config.UpdatedAt != "" {
@ -170,17 +170,3 @@ func (m *Model) getConditionOfTableStringForSoftDeleting(s string) string {
}
return fmt.Sprintf(`%s.%s IS NULL`, m.db.GetCore().QuoteWord(table), m.db.GetCore().QuoteWord(field))
}
// getPrimaryTableName parses and returns the primary table name.
func (m *Model) getPrimaryTableName() string {
if m.tables == "" {
return ""
}
array1 := gstr.SplitAndTrim(m.tables, ",")
array2 := gstr.SplitAndTrim(array1[0], " ")
array3 := gstr.SplitAndTrim(array2[0], ".")
if len(array3) >= 2 {
return array3[1]
}
return array3[0]
}

View File

@ -21,15 +21,12 @@ import (
// schema.
//
// Also see DriverMysql.TableFields.
func (m *Model) TableFields(table string, schema ...string) (fields map[string]*TableField, err error) {
charL, charR := m.db.GetChars()
if charL != "" || charR != "" {
table = gstr.Trim(table, charL+charR)
func (m *Model) TableFields(tableStr string, schema ...string) (fields map[string]*TableField, err error) {
useSchema := m.schema
if len(schema) > 0 && schema[0] != "" {
useSchema = schema[0]
}
if !gregex.IsMatchString(regularFieldNameRegPattern, table) {
return nil, nil
}
return m.db.TableFields(m.GetCtx(), table, schema...)
return m.db.TableFields(m.GetCtx(), m.guessPrimaryTableName(tableStr), useSchema)
}
// getModel creates and returns a cloned model of current model if `safe` is true, or else it returns
@ -47,7 +44,7 @@ func (m *Model) getModel() *Model {
// ID -> id
// NICK_Name -> nickname
func (m *Model) mappingAndFilterToTableFields(fields []string, filter bool) []string {
fieldsMap, err := m.TableFields(m.tables)
fieldsMap, err := m.TableFields(m.tablesInit)
if err != nil || len(fieldsMap) == 0 {
return fields
}
@ -201,7 +198,7 @@ func (m *Model) getLink(master bool) Link {
// It parses m.tables to retrieve the primary table name, supporting m.tables like:
// "user", "user u", "user as u, user_detail as ud".
func (m *Model) getPrimaryKey() string {
table := gstr.SplitAndTrim(m.tables, " ")[0]
table := gstr.SplitAndTrim(m.tablesInit, " ")[0]
tableFields, err := m.TableFields(table)
if err != nil {
return ""

View File

@ -39,7 +39,10 @@ func (m *Model) With(objects ...interface{}) *Model {
model := m.getModel()
for _, object := range objects {
if m.tables == "" {
m.tables = m.db.GetCore().QuotePrefixTableName(getTableNameFromOrmTag(object))
m.tablesInit = m.db.GetCore().QuotePrefixTableName(
getTableNameFromOrmTag(object),
)
m.tables = m.tablesInit
return model
}
model.withArray = append(model.withArray, object)
@ -163,6 +166,10 @@ func (m *Model) doWithScanStruct(pointer interface{}) error {
// doWithScanStructs handles model association operations feature for struct slice.
// Also see doWithScanStruct.
func (m *Model) doWithScanStructs(pointer interface{}) error {
if v, ok := pointer.(reflect.Value); ok {
pointer = v.Interface()
}
var (
err error
allowedTypeStrArray = make([]string, 0)

View File

@ -33,7 +33,10 @@ func (r *SqlResult) MustGetInsertId() int64 {
return id
}
// see sql.Result.RowsAffected
// RowsAffected returns the number of rows affected by an
// update, insert, or delete. Not every database or database
// driver may support this.
// Also See sql.Result.
func (r *SqlResult) RowsAffected() (int64, error) {
if r.affected > 0 {
return r.affected, nil
@ -44,7 +47,12 @@ func (r *SqlResult) RowsAffected() (int64, error) {
return r.result.RowsAffected()
}
// see sql.Result.LastInsertId
// LastInsertId returns the integer generated by the database
// in response to a command. Typically this will be from an
// "auto increment" column when inserting a new row. Not all
// databases support this feature, and the syntax of such
// statements varies.
// Also See sql.Result.
func (r *SqlResult) LastInsertId() (int64, error) {
if r.result == nil {
return 0, nil

View File

@ -14,6 +14,11 @@ import (
"github.com/gogf/gf/util/gconv"
)
// Interface converts and returns `r` as type of interface{}.
func (r Record) Interface() interface{} {
return r
}
// Json converts `r` to JSON format content.
func (r Record) Json() string {
content, _ := gparser.VarToJson(r.Map())

View File

@ -13,6 +13,11 @@ import (
"math"
)
// Interface converts and returns `r` as type of interface{}.
func (r Result) Interface() interface{} {
return r
}
// IsEmpty checks and returns whether `r` is empty.
func (r Result) IsEmpty() bool {
return r.Len() == 0

View File

@ -42,21 +42,21 @@ import (
//
// See the example or unit testing cases for clear understanding for this function.
func (r Result) ScanList(listPointer interface{}, bindToAttrName string, relationKV ...string) (err error) {
if r.IsEmpty() {
return doScanList(nil, r, listPointer, bindToAttrName, relationKV...)
}
// doScanList converts `result` to struct slice which contains other complex struct attributes recursively.
// The parameter `model` is used for recursively scanning purpose, which means, it can scans the attribute struct/structs recursively but
// it needs the Model for database accessing.
// Note that the parameter `listPointer` should be type of *[]struct/*[]*struct.
func doScanList(model *Model, result Result, listPointer interface{}, bindToAttrName string, relationKV ...string) (err error) {
if result.IsEmpty() {
return nil
}
// Necessary checks for parameters.
if bindToAttrName == "" {
return gerror.New(`bindToAttrName should not be empty`)
}
//if len(relation) > 0 {
// if len(relation) < 2 {
// return gerror.New(`relation name and key should are both necessary`)
// }
// if relation[0] == "" || relation[1] == "" {
// return gerror.New(`relation name and key should not be empty`)
// }
//}
var (
reflectValue = reflect.ValueOf(listPointer)
@ -67,14 +67,14 @@ func (r Result) ScanList(listPointer interface{}, bindToAttrName string, relatio
reflectKind = reflectValue.Kind()
}
if reflectKind != reflect.Ptr {
return gerror.Newf("parameter should be type of *[]struct/*[]*struct, but got: %v", reflectKind)
return gerror.Newf("listPointer should be type of *[]struct/*[]*struct, but got: %v", reflectKind)
}
reflectValue = reflectValue.Elem()
reflectKind = reflectValue.Kind()
if reflectKind != reflect.Slice && reflectKind != reflect.Array {
return gerror.Newf("parameter should be type of *[]struct/*[]*struct, but got: %v", reflectKind)
return gerror.Newf("listPointer should be type of *[]struct/*[]*struct, but got: %v", reflectKind)
}
length := len(r)
length := len(result)
if length == 0 {
// The pointed slice is not empty.
if reflectValue.Len() > 0 {
@ -134,7 +134,7 @@ func (r Result) ScanList(listPointer interface{}, bindToAttrName string, relatio
// uid:UserId
relationResultFieldName = array[0]
relationBindToSubAttrName = array[1]
if key, _ := gutil.MapPossibleItemByKey(r[0].Map(), relationResultFieldName); key == "" {
if key, _ := gutil.MapPossibleItemByKey(result[0].Map(), relationResultFieldName); key == "" {
return gerror.Newf(
`cannot find possible related table field name "%s" from given relation key "%s"`,
relationResultFieldName,
@ -147,7 +147,8 @@ func (r Result) ScanList(listPointer interface{}, bindToAttrName string, relatio
return gerror.New(`parameter relationKV should be format of "ResultFieldName:BindToAttrName"`)
}
if relationResultFieldName != "" {
relationDataMap = r.MapKeyValue(relationResultFieldName)
// Note that the value might be type of slice.
relationDataMap = result.MapKeyValue(relationResultFieldName)
}
if len(relationDataMap) == 0 {
return gerror.Newf(`cannot find the relation data map, maybe invalid relation given "%v"`, relationKV)
@ -239,12 +240,19 @@ func (r Result) ScanList(listPointer interface{}, bindToAttrName string, relatio
if len(relationDataMap) > 0 {
relationFromAttrField = relationFromAttrValue.FieldByName(relationBindToSubAttrName)
if relationFromAttrField.IsValid() {
if err = gconv.Structs(
relationDataMap[gconv.String(relationFromAttrField.Interface())],
bindToAttrValue.Addr(),
); err != nil {
results := make(Result, 0)
for _, v := range relationDataMap[gconv.String(relationFromAttrField.Interface())].Slice() {
results = append(results, v.(Record))
}
if err = results.Structs(bindToAttrValue.Addr()); err != nil {
return err
}
// Recursively Scan.
if model != nil {
if err = model.doWithScanStructs(bindToAttrValue.Addr()); err != nil {
return nil
}
}
} else {
// May be the attribute does not exist yet.
return gerror.Newf(`invalid relation specified: "%v"`, relationKV)
@ -268,24 +276,36 @@ func (r Result) ScanList(listPointer interface{}, bindToAttrName string, relatio
// There's no relational data.
continue
}
if err = gconv.Struct(v, element); err != nil {
return err
if v.IsSlice() {
if err = v.Slice()[0].(Record).Struct(element); err != nil {
return err
}
} else {
if err = v.Val().(Record).Struct(element); err != nil {
return err
}
}
} else {
// May be the attribute does not exist yet.
return gerror.Newf(`invalid relation specified: "%v"`, relationKV)
}
} else {
if i >= len(r) {
if i >= len(result) {
// There's no relational data.
continue
}
v := r[i]
v := result[i]
if v == nil {
// There's no relational data.
continue
}
if err = gconv.Struct(v, element); err != nil {
if err = v.Struct(element); err != nil {
return err
}
}
// Recursively Scan.
if model != nil {
if err = model.doWithScanStruct(element); err != nil {
return err
}
}
@ -300,24 +320,36 @@ func (r Result) ScanList(listPointer interface{}, bindToAttrName string, relatio
// There's no relational data.
continue
}
if err = gconv.Struct(relationDataItem, bindToAttrValue); err != nil {
return err
if relationDataItem.IsSlice() {
if err = relationDataItem.Slice()[0].(Record).Struct(bindToAttrValue); err != nil {
return err
}
} else {
if err = relationDataItem.Val().(Record).Struct(bindToAttrValue); err != nil {
return err
}
}
} else {
// May be the attribute does not exist yet.
return gerror.Newf(`invalid relation specified: "%v"`, relationKV)
}
} else {
if i >= len(r) {
if i >= len(result) {
// There's no relational data.
continue
}
relationDataItem := r[i]
relationDataItem := result[i]
if relationDataItem == nil {
// There's no relational data.
continue
}
if err = gconv.Struct(relationDataItem, bindToAttrValue); err != nil {
if err = relationDataItem.Struct(bindToAttrValue); err != nil {
return err
}
}
// Recursively Scan.
if model != nil {
if err = model.doWithScanStruct(bindToAttrValue); err != nil {
return err
}
}

View File

@ -8,8 +8,11 @@ package gdb_test
import (
"fmt"
"github.com/gogf/gf/debug/gdebug"
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/os/gfile"
"github.com/gogf/gf/test/gtest"
"github.com/gogf/gf/text/gstr"
"github.com/gogf/gf/util/gmeta"
"testing"
)
@ -782,6 +785,122 @@ PRIMARY KEY (id)
})
}
func Test_Table_Relation_WithAll_Embedded_WithoutMeta(t *testing.T) {
var (
tableUser = "user"
tableUserDetail = "user_detail"
tableUserScores = "user_scores"
)
if _, err := db.Exec(fmt.Sprintf(`
CREATE TABLE IF NOT EXISTS %s (
id int(10) unsigned NOT NULL AUTO_INCREMENT,
name varchar(45) NOT NULL,
PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
`, tableUser)); err != nil {
gtest.Error(err)
}
defer dropTable(tableUser)
if _, err := db.Exec(fmt.Sprintf(`
CREATE TABLE IF NOT EXISTS %s (
uid int(10) unsigned NOT NULL AUTO_INCREMENT,
address varchar(45) NOT NULL,
PRIMARY KEY (uid)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
`, tableUserDetail)); err != nil {
gtest.Error(err)
}
defer dropTable(tableUserDetail)
if _, err := db.Exec(fmt.Sprintf(`
CREATE TABLE IF NOT EXISTS %s (
id int(10) unsigned NOT NULL AUTO_INCREMENT,
uid int(10) unsigned NOT NULL,
score int(10) unsigned NOT NULL,
PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
`, tableUserScores)); err != nil {
gtest.Error(err)
}
defer dropTable(tableUserScores)
type UserDetailBase struct {
Uid int `json:"uid"`
Address string `json:"address"`
}
type UserDetail struct {
UserDetailBase
}
type UserScores struct {
Id int `json:"id"`
Uid int `json:"uid"`
Score int `json:"score"`
}
type User struct {
*UserDetail `orm:"with:uid=id"`
Id int `json:"id"`
Name string `json:"name"`
UserScores []*UserScores `orm:"with:uid=id"`
}
// Initialize the data.
var err error
for i := 1; i <= 5; i++ {
// User.
_, err = db.Insert(tableUser, g.Map{
"id": i,
"name": fmt.Sprintf(`name_%d`, i),
})
gtest.Assert(err, nil)
// Detail.
_, err = db.Insert(tableUserDetail, g.Map{
"uid": i,
"address": fmt.Sprintf(`address_%d`, i),
})
gtest.Assert(err, nil)
// Scores.
for j := 1; j <= 5; j++ {
_, err = db.Insert(tableUserScores, g.Map{
"uid": i,
"score": j,
})
gtest.Assert(err, nil)
}
}
gtest.C(t, func(t *gtest.T) {
var user *User
err := db.Model(tableUser).WithAll().Where("id", 3).Scan(&user)
t.AssertNil(err)
t.Assert(user.Id, 3)
t.AssertNE(user.UserDetail, nil)
t.Assert(user.UserDetail.Uid, 3)
t.Assert(user.UserDetail.Address, `address_3`)
t.Assert(len(user.UserScores), 5)
t.Assert(user.UserScores[0].Uid, 3)
t.Assert(user.UserScores[0].Score, 1)
t.Assert(user.UserScores[4].Uid, 3)
t.Assert(user.UserScores[4].Score, 5)
})
gtest.C(t, func(t *gtest.T) {
var user User
err := db.Model(tableUser).WithAll().Where("id", 4).Scan(&user)
t.AssertNil(err)
t.Assert(user.Id, 4)
t.AssertNE(user.UserDetail, nil)
t.Assert(user.UserDetail.Uid, 4)
t.Assert(user.UserDetail.Address, `address_4`)
t.Assert(len(user.UserScores), 5)
t.Assert(user.UserScores[0].Uid, 4)
t.Assert(user.UserScores[0].Score, 1)
t.Assert(user.UserScores[4].Uid, 4)
t.Assert(user.UserScores[4].Score, 5)
})
}
func Test_Table_Relation_WithAll_AttributeStructAlsoHasWithTag(t *testing.T) {
var (
tableUser = "user"
@ -1189,3 +1308,232 @@ PRIMARY KEY (id)
t.Assert(user.UserDetail.UserScores[4].Score, 5)
})
}
func Test_Table_Relation_With_MultipleDepends1(t *testing.T) {
defer func() {
dropTable("table_a")
dropTable("table_b")
dropTable("table_c")
}()
for _, v := range gstr.SplitAndTrim(gfile.GetContents(gdebug.TestDataPath("with_multiple_depends.sql")), ";") {
if _, err := db.Exec(v); err != nil {
gtest.Error(err)
}
}
type TableC struct {
gmeta.Meta `orm:"table_c"`
Id int `orm:"id,primary" json:"id"`
TableBId int `orm:"table_b_id" json:"table_b_id"`
}
type TableB struct {
gmeta.Meta `orm:"table_b"`
Id int `orm:"id,primary" json:"id"`
TableAId int `orm:"table_a_id" json:"table_a_id"`
TableC *TableC `orm:"with:table_b_id=id" json:"table_c"`
}
type TableA struct {
gmeta.Meta `orm:"table_a"`
Id int `orm:"id,primary" json:"id"`
TableB *TableB `orm:"with:table_a_id=id" json:"table_b"`
}
db.SetDebug(true)
defer db.SetDebug(false)
// Struct.
gtest.C(t, func(t *gtest.T) {
var tableA *TableA
err := db.Model("table_a").WithAll().Scan(&tableA)
//g.Dump(tableA)
t.AssertNil(err)
t.AssertNE(tableA, nil)
t.Assert(tableA.Id, 1)
t.AssertNE(tableA.TableB, nil)
t.AssertNE(tableA.TableB.TableC, nil)
t.Assert(tableA.TableB.TableAId, 1)
t.Assert(tableA.TableB.TableC.Id, 100)
t.Assert(tableA.TableB.TableC.TableBId, 10)
})
// Structs
gtest.C(t, func(t *gtest.T) {
var tableA []*TableA
err := db.Model("table_a").WithAll().OrderAsc("id").Scan(&tableA)
//g.Dump(tableA)
t.AssertNil(err)
t.Assert(len(tableA), 2)
t.AssertNE(tableA[0].TableB, nil)
t.AssertNE(tableA[1].TableB, nil)
t.AssertNE(tableA[0].TableB.TableC, nil)
t.AssertNE(tableA[1].TableB.TableC, nil)
t.Assert(tableA[0].Id, 1)
t.Assert(tableA[0].TableB.Id, 10)
t.Assert(tableA[0].TableB.TableC.Id, 100)
t.Assert(tableA[1].Id, 2)
t.Assert(tableA[1].TableB.Id, 20)
t.Assert(tableA[1].TableB.TableC.Id, 300)
})
}
func Test_Table_Relation_With_MultipleDepends2(t *testing.T) {
defer func() {
dropTable("table_a")
dropTable("table_b")
dropTable("table_c")
}()
for _, v := range gstr.SplitAndTrim(gfile.GetContents(gdebug.TestDataPath("with_multiple_depends.sql")), ";") {
if _, err := db.Exec(v); err != nil {
gtest.Error(err)
}
}
type TableC struct {
gmeta.Meta `orm:"table_c"`
Id int `orm:"id,primary" json:"id"`
TableBId int `orm:"table_b_id" json:"table_b_id"`
}
type TableB struct {
gmeta.Meta `orm:"table_b"`
Id int `orm:"id,primary" json:"id"`
TableAId int `orm:"table_a_id" json:"table_a_id"`
TableC []*TableC `orm:"with:table_b_id=id" json:"table_c"`
}
type TableA struct {
gmeta.Meta `orm:"table_a"`
Id int `orm:"id,primary" json:"id"`
TableB []*TableB `orm:"with:table_a_id=id" json:"table_b"`
}
db.SetDebug(true)
defer db.SetDebug(false)
// Struct.
gtest.C(t, func(t *gtest.T) {
var tableA *TableA
err := db.Model("table_a").WithAll().Scan(&tableA)
//g.Dump(tableA)
t.AssertNil(err)
t.AssertNE(tableA, nil)
t.Assert(tableA.Id, 1)
t.Assert(len(tableA.TableB), 2)
t.Assert(tableA.TableB[0].Id, 10)
t.Assert(tableA.TableB[1].Id, 30)
t.Assert(len(tableA.TableB[0].TableC), 2)
t.Assert(len(tableA.TableB[1].TableC), 1)
t.Assert(tableA.TableB[0].TableC[0].Id, 100)
t.Assert(tableA.TableB[0].TableC[0].TableBId, 10)
t.Assert(tableA.TableB[0].TableC[1].Id, 200)
t.Assert(tableA.TableB[0].TableC[1].TableBId, 10)
t.Assert(tableA.TableB[1].TableC[0].Id, 400)
t.Assert(tableA.TableB[1].TableC[0].TableBId, 30)
})
// Structs
gtest.C(t, func(t *gtest.T) {
var tableA []*TableA
err := db.Model("table_a").WithAll().OrderAsc("id").Scan(&tableA)
//g.Dump(tableA)
t.AssertNil(err)
t.Assert(len(tableA), 2)
t.Assert(len(tableA[0].TableB), 2)
t.Assert(tableA[0].TableB[0].Id, 10)
t.Assert(tableA[0].TableB[1].Id, 30)
t.Assert(len(tableA[0].TableB[0].TableC), 2)
t.Assert(len(tableA[0].TableB[1].TableC), 1)
t.Assert(tableA[0].TableB[0].TableC[0].Id, 100)
t.Assert(tableA[0].TableB[0].TableC[0].TableBId, 10)
t.Assert(tableA[0].TableB[0].TableC[1].Id, 200)
t.Assert(tableA[0].TableB[0].TableC[1].TableBId, 10)
t.Assert(tableA[0].TableB[1].TableC[0].Id, 400)
t.Assert(tableA[0].TableB[1].TableC[0].TableBId, 30)
t.Assert(tableA[1].TableB[0].TableC[0].Id, 300)
t.Assert(tableA[1].TableB[0].TableC[0].TableBId, 20)
t.Assert(tableA[1].TableB[1].Id, 40)
t.Assert(tableA[1].TableB[1].TableAId, 2)
t.Assert(tableA[1].TableB[1].TableC, nil)
})
}
func Test_Table_Relation_With_MultipleDepends_Embedded(t *testing.T) {
defer func() {
dropTable("table_a")
dropTable("table_b")
dropTable("table_c")
}()
for _, v := range gstr.SplitAndTrim(gfile.GetContents(gdebug.TestDataPath("with_multiple_depends.sql")), ";") {
if _, err := db.Exec(v); err != nil {
gtest.Error(err)
}
}
type TableC struct {
gmeta.Meta `orm:"table_c"`
Id int `orm:"id,primary" json:"id"`
TableBId int `orm:"table_b_id" json:"table_b_id"`
}
type TableB struct {
gmeta.Meta `orm:"table_b"`
Id int `orm:"id,primary" json:"id"`
TableAId int `orm:"table_a_id" json:"table_a_id"`
*TableC `orm:"with:table_b_id=id" json:"table_c"`
}
type TableA struct {
gmeta.Meta `orm:"table_a"`
Id int `orm:"id,primary" json:"id"`
*TableB `orm:"with:table_a_id=id" json:"table_b"`
}
db.SetDebug(true)
defer db.SetDebug(false)
// Struct.
gtest.C(t, func(t *gtest.T) {
var tableA *TableA
err := db.Model("table_a").WithAll().Scan(&tableA)
//g.Dump(tableA)
t.AssertNil(err)
t.AssertNE(tableA, nil)
t.Assert(tableA.Id, 1)
t.AssertNE(tableA.TableB, nil)
t.AssertNE(tableA.TableB.TableC, nil)
t.Assert(tableA.TableB.TableAId, 1)
t.Assert(tableA.TableB.TableC.Id, 100)
t.Assert(tableA.TableB.TableC.TableBId, 10)
})
// Structs
gtest.C(t, func(t *gtest.T) {
var tableA []*TableA
err := db.Model("table_a").WithAll().OrderAsc("id").Scan(&tableA)
//g.Dump(tableA)
t.AssertNil(err)
t.Assert(len(tableA), 2)
t.AssertNE(tableA[0].TableB, nil)
t.AssertNE(tableA[1].TableB, nil)
t.AssertNE(tableA[0].TableB.TableC, nil)
t.AssertNE(tableA[1].TableB.TableC, nil)
t.Assert(tableA[0].Id, 1)
t.Assert(tableA[0].TableB.Id, 10)
t.Assert(tableA[0].TableB.TableC.Id, 100)
t.Assert(tableA[1].Id, 2)
t.Assert(tableA[1].TableB.Id, 20)
t.Assert(tableA[1].TableB.TableC.Id, 300)
})
}

View File

@ -1436,7 +1436,7 @@ func Test_DB_UpdateCounter(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
gdbCounter := &gdb.Counter{
Field: "views",
Field: "id",
Value: 1,
}
updateData := g.Map{
@ -1449,7 +1449,7 @@ func Test_DB_UpdateCounter(t *testing.T) {
one, err := db.Model(tableName).Where("id", 1).One()
t.AssertNil(err)
t.Assert(one["id"].Int(), 1)
t.Assert(one["views"].Int(), 1)
t.Assert(one["views"].Int(), 2)
})
gtest.C(t, func(t *gtest.T) {
@ -1468,7 +1468,7 @@ func Test_DB_UpdateCounter(t *testing.T) {
one, err := db.Model(tableName).Where("id", 1).One()
t.AssertNil(err)
t.Assert(one["id"].Int(), 1)
t.Assert(one["views"].Int(), 0)
t.Assert(one["views"].Int(), 1)
})
}

View File

@ -0,0 +1,33 @@
CREATE TABLE `table_a` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`alias` varchar(255) NULL DEFAULT '',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB;
INSERT INTO `table_a` VALUES (1, 'table_a_test1');
INSERT INTO `table_a` VALUES (2, 'table_a_test2');
CREATE TABLE `table_b` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`table_a_id` int(11) NOT NULL,
`alias` varchar(255) NULL DEFAULT '',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB;
INSERT INTO `table_b` VALUES (10, 1, 'table_b_test1');
INSERT INTO `table_b` VALUES (20, 2, 'table_b_test2');
INSERT INTO `table_b` VALUES (30, 1, 'table_b_test3');
INSERT INTO `table_b` VALUES (40, 2, 'table_b_test4');
CREATE TABLE `table_c` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`table_b_id` int(11) NOT NULL,
`alias` varchar(255) NULL DEFAULT '',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB;
INSERT INTO `table_c` VALUES (100, 10, 'table_c_test1');
INSERT INTO `table_c` VALUES (200, 10, 'table_c_test2');
INSERT INTO `table_c` VALUES (300, 20, 'table_c_test3');
INSERT INTO `table_c` VALUES (400, 30, 'table_c_test4');

View File

@ -8,6 +8,7 @@
package gjson
import (
"github.com/gogf/gf/internal/utils"
"reflect"
"strconv"
"strings"
@ -37,11 +38,23 @@ type Options struct {
StrNumber bool // StrNumber causes the Decoder to unmarshal a number into an interface{} as a string instead of as a float64.
}
// apiInterface is used for type assert api for Interface().
type apiInterface interface {
Interface() interface{}
}
// setValue sets <value> to <j> by <pattern>.
// Note:
// 1. If value is nil and removed is true, means deleting this value;
// 2. It's quite complicated in hierarchical data search, node creating and data assignment;
func (j *Json) setValue(pattern string, value interface{}, removed bool) error {
if value != nil {
if utils.IsStruct(value) {
if v, ok := value.(apiInterface); ok {
value = v.Interface()
}
}
}
array := strings.Split(pattern, string(j.c))
length := len(array)
value = j.convertValue(value)

View File

@ -18,7 +18,13 @@ import (
)
// Value returns the json value.
// Deprecated, use Interface instead.
func (j *Json) Value() interface{} {
return j.Interface()
}
// Interface returns the json value.
func (j *Json) Interface() interface{} {
j.mu.RLock()
defer j.mu.RUnlock()
return *(j.p)

View File

@ -485,3 +485,17 @@ func TestJson_IsNil(t *testing.T) {
t.Assert(j.IsNil(), true)
})
}
func TestJson_Set_With_Struct(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
v := gjson.New(g.Map{
"user1": g.Map{"name": "user1"},
"user2": g.Map{"name": "user2"},
"user3": g.Map{"name": "user3"},
})
user1 := v.GetJson("user1")
user1.Set("id", 111)
v.Set("user1", user1)
t.Assert(v.Get("user1.id"), 111)
})
}

View File

@ -71,8 +71,9 @@ func doPrint(ctx context.Context, content string, stack bool) {
buffer.WriteString(now())
buffer.WriteString(" [INTE] ")
buffer.WriteString(file())
buffer.WriteString(" ")
if s := traceIdStr(ctx); s != "" {
buffer.WriteString(" " + s)
buffer.WriteString(s + " ")
}
buffer.WriteString(content)
buffer.WriteString("\n")

View File

@ -0,0 +1,98 @@
// 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 utils
import (
"github.com/gogf/gf/internal/empty"
"reflect"
)
// IsNil checks whether `value` is nil.
func IsNil(value interface{}) bool {
return value == nil
}
// IsEmpty checks whether `value` is empty.
func IsEmpty(value interface{}) bool {
return empty.IsEmpty(value)
}
// IsInt checks whether `value` is type of int.
func IsInt(value interface{}) bool {
switch value.(type) {
case int, *int, int8, *int8, int16, *int16, int32, *int32, int64, *int64:
return true
}
return false
}
// IsUint checks whether `value` is type of uint.
func IsUint(value interface{}) bool {
switch value.(type) {
case uint, *uint, uint8, *uint8, uint16, *uint16, uint32, *uint32, uint64, *uint64:
return true
}
return false
}
// IsFloat checks whether `value` is type of float.
func IsFloat(value interface{}) bool {
switch value.(type) {
case float32, *float32, float64, *float64:
return true
}
return false
}
// IsSlice checks whether `value` is type of slice.
func IsSlice(value interface{}) bool {
var (
reflectValue = reflect.ValueOf(value)
reflectKind = reflectValue.Kind()
)
for reflectKind == reflect.Ptr {
reflectValue = reflectValue.Elem()
}
switch reflectKind {
case reflect.Slice, reflect.Array:
return true
}
return false
}
// IsMap checks whether `value` is type of map.
func IsMap(value interface{}) bool {
var (
reflectValue = reflect.ValueOf(value)
reflectKind = reflectValue.Kind()
)
for reflectKind == reflect.Ptr {
reflectValue = reflectValue.Elem()
}
switch reflectKind {
case reflect.Map:
return true
}
return false
}
// IsStruct checks whether `value` is type of struct.
func IsStruct(value interface{}) bool {
var (
reflectValue = reflect.ValueOf(value)
reflectKind = reflectValue.Kind()
)
for reflectKind == reflect.Ptr {
reflectValue = reflectValue.Elem()
reflectKind = reflectValue.Kind()
}
switch reflectKind {
case reflect.Struct:
return true
}
return false
}

View File

@ -0,0 +1,171 @@
// 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 utils_test
import (
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/internal/utils"
"github.com/gogf/gf/test/gtest"
"testing"
)
func TestVar_IsNil(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
t.Assert(utils.IsNil(0), false)
t.Assert(utils.IsNil(nil), true)
t.Assert(utils.IsNil(g.Map{}), false)
t.Assert(utils.IsNil(g.Slice{}), false)
})
gtest.C(t, func(t *gtest.T) {
t.Assert(utils.IsNil(1), false)
t.Assert(utils.IsNil(0.1), false)
t.Assert(utils.IsNil(g.Map{"k": "v"}), false)
t.Assert(utils.IsNil(g.Slice{0}), false)
})
}
func TestVar_IsEmpty(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
t.Assert(utils.IsEmpty(0), true)
t.Assert(utils.IsEmpty(nil), true)
t.Assert(utils.IsEmpty(g.Map{}), true)
t.Assert(utils.IsEmpty(g.Slice{}), true)
})
gtest.C(t, func(t *gtest.T) {
t.Assert(utils.IsEmpty(1), false)
t.Assert(utils.IsEmpty(0.1), false)
t.Assert(utils.IsEmpty(g.Map{"k": "v"}), false)
t.Assert(utils.IsEmpty(g.Slice{0}), false)
})
}
func TestVar_IsInt(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
t.Assert(utils.IsInt(0), true)
t.Assert(utils.IsInt(nil), false)
t.Assert(utils.IsInt(g.Map{}), false)
t.Assert(utils.IsInt(g.Slice{}), false)
})
gtest.C(t, func(t *gtest.T) {
t.Assert(utils.IsInt(1), true)
t.Assert(utils.IsInt(-1), true)
t.Assert(utils.IsInt(0.1), false)
t.Assert(utils.IsInt(g.Map{"k": "v"}), false)
t.Assert(utils.IsInt(g.Slice{0}), false)
})
gtest.C(t, func(t *gtest.T) {
t.Assert(utils.IsInt(int8(1)), true)
t.Assert(utils.IsInt(uint8(1)), false)
})
}
func TestVar_IsUint(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
t.Assert(utils.IsUint(0), false)
t.Assert(utils.IsUint(nil), false)
t.Assert(utils.IsUint(g.Map{}), false)
t.Assert(utils.IsUint(g.Slice{}), false)
})
gtest.C(t, func(t *gtest.T) {
t.Assert(utils.IsUint(1), false)
t.Assert(utils.IsUint(-1), false)
t.Assert(utils.IsUint(0.1), false)
t.Assert(utils.IsUint(g.Map{"k": "v"}), false)
t.Assert(utils.IsUint(g.Slice{0}), false)
})
gtest.C(t, func(t *gtest.T) {
t.Assert(utils.IsUint(int8(1)), false)
t.Assert(utils.IsUint(uint8(1)), true)
})
}
func TestVar_IsFloat(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
t.Assert(utils.IsFloat(0), false)
t.Assert(utils.IsFloat(nil), false)
t.Assert(utils.IsFloat(g.Map{}), false)
t.Assert(utils.IsFloat(g.Slice{}), false)
})
gtest.C(t, func(t *gtest.T) {
t.Assert(utils.IsFloat(1), false)
t.Assert(utils.IsFloat(-1), false)
t.Assert(utils.IsFloat(0.1), true)
t.Assert(utils.IsFloat(float64(1)), true)
t.Assert(utils.IsFloat(g.Map{"k": "v"}), false)
t.Assert(utils.IsFloat(g.Slice{0}), false)
})
gtest.C(t, func(t *gtest.T) {
t.Assert(utils.IsFloat(int8(1)), false)
t.Assert(utils.IsFloat(uint8(1)), false)
})
}
func TestVar_IsSlice(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
t.Assert(utils.IsSlice(0), false)
t.Assert(utils.IsSlice(nil), false)
t.Assert(utils.IsSlice(g.Map{}), false)
t.Assert(utils.IsSlice(g.Slice{}), true)
})
gtest.C(t, func(t *gtest.T) {
t.Assert(utils.IsSlice(1), false)
t.Assert(utils.IsSlice(-1), false)
t.Assert(utils.IsSlice(0.1), false)
t.Assert(utils.IsSlice(float64(1)), false)
t.Assert(utils.IsSlice(g.Map{"k": "v"}), false)
t.Assert(utils.IsSlice(g.Slice{0}), true)
})
gtest.C(t, func(t *gtest.T) {
t.Assert(utils.IsSlice(int8(1)), false)
t.Assert(utils.IsSlice(uint8(1)), false)
})
}
func TestVar_IsMap(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
t.Assert(utils.IsMap(0), false)
t.Assert(utils.IsMap(nil), false)
t.Assert(utils.IsMap(g.Map{}), true)
t.Assert(utils.IsMap(g.Slice{}), false)
})
gtest.C(t, func(t *gtest.T) {
t.Assert(utils.IsMap(1), false)
t.Assert(utils.IsMap(-1), false)
t.Assert(utils.IsMap(0.1), false)
t.Assert(utils.IsMap(float64(1)), false)
t.Assert(utils.IsMap(g.Map{"k": "v"}), true)
t.Assert(utils.IsMap(g.Slice{0}), false)
})
gtest.C(t, func(t *gtest.T) {
t.Assert(utils.IsMap(int8(1)), false)
t.Assert(utils.IsMap(uint8(1)), false)
})
}
func TestVar_IsStruct(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
t.Assert(utils.IsStruct(0), false)
t.Assert(utils.IsStruct(nil), false)
t.Assert(utils.IsStruct(g.Map{}), false)
t.Assert(utils.IsStruct(g.Slice{}), false)
})
gtest.C(t, func(t *gtest.T) {
t.Assert(utils.IsStruct(1), false)
t.Assert(utils.IsStruct(-1), false)
t.Assert(utils.IsStruct(0.1), false)
t.Assert(utils.IsStruct(float64(1)), false)
t.Assert(utils.IsStruct(g.Map{"k": "v"}), false)
t.Assert(utils.IsStruct(g.Slice{0}), false)
})
gtest.C(t, func(t *gtest.T) {
a := &struct {
}{}
t.Assert(utils.IsStruct(a), true)
t.Assert(utils.IsStruct(*a), true)
t.Assert(utils.IsStruct(&a), true)
})
}

View File

@ -10,6 +10,7 @@ package ghttp
import (
"github.com/gogf/gf/os/gcfg"
"github.com/gogf/gf/os/gview"
"github.com/gogf/gf/util/gconv"
"github.com/gogf/gf/util/gmode"
"github.com/gogf/gf/util/gutil"
)
@ -62,7 +63,7 @@ func (r *Response) ParseTpl(tpl string, params ...gview.Params) (string, error)
return r.Request.GetView().Parse(r.Request.Context(), tpl, r.buildInVars(params...))
}
// ParseDefault parses the default template file with params.
// ParseTplDefault parses the default template file with params.
func (r *Response) ParseTplDefault(params ...gview.Params) (string, error) {
return r.Request.GetView().ParseDefault(r.Request.Context(), r.buildInVars(params...))
}
@ -81,17 +82,18 @@ func (r *Response) buildInVars(params ...map[string]interface{}) map[string]inte
gutil.MapMerge(m, params[0])
}
// Retrieve custom template variables from request object.
sessionMap := gconv.MapDeep(r.Request.Session.Map())
gutil.MapMerge(m, map[string]interface{}{
"Form": r.Request.GetFormMap(),
"Query": r.Request.GetQueryMap(),
"Request": r.Request.GetMap(),
"Cookie": r.Request.Cookie.Map(),
"Session": r.Request.Session.Map(),
"Session": sessionMap,
})
// Note that it should assign no Config variable to template
// if there's no configuration file.
if c := gcfg.Instance(); c.Available() {
m["Config"] = c.GetMap(".")
m["Config"] = c.Map()
}
return m
}

View File

@ -10,7 +10,7 @@ import (
"context"
"github.com/gogf/gf/container/gmap"
"github.com/gogf/gf/container/gvar"
"go.opentelemetry.io/otel/attribute"
"github.com/gogf/gf/util/gconv"
"go.opentelemetry.io/otel/baggage"
)
@ -37,39 +37,37 @@ func (b *Baggage) Ctx() context.Context {
// SetValue is a convenient function for adding one key-value pair to baggage.
// Note that it uses attribute.Any to set the key-value pair.
func (b *Baggage) SetValue(key string, value interface{}) context.Context {
b.ctx = baggage.ContextWithValues(b.ctx, attribute.Any(key, value))
member, _ := baggage.NewMember(key, gconv.String(value))
bag, _ := baggage.New(member)
b.ctx = baggage.ContextWithBaggage(b.ctx, bag)
return b.ctx
}
// SetMap is a convenient function for adding map key-value pairs to baggage.
// Note that it uses attribute.Any to set the key-value pair.
func (b *Baggage) SetMap(data map[string]interface{}) context.Context {
pairs := make([]attribute.KeyValue, 0)
members := make([]baggage.Member, 0)
for k, v := range data {
pairs = append(pairs, attribute.Any(k, v))
member, _ := baggage.NewMember(k, gconv.String(v))
members = append(members, member)
}
b.ctx = baggage.ContextWithValues(b.ctx, pairs...)
bag, _ := baggage.New(members...)
b.ctx = baggage.ContextWithBaggage(b.ctx, bag)
return b.ctx
}
// GetMap retrieves and returns the baggage values as map.
func (b *Baggage) GetMap() *gmap.StrAnyMap {
m := gmap.NewStrAnyMap()
set := baggage.Set(b.ctx)
if length := set.Len(); length > 0 {
if length == 0 {
return m
}
inter := set.Iter()
for inter.Next() {
m.Set(string(inter.Label().Key), inter.Label().Value.AsInterface())
}
members := baggage.FromContext(b.ctx).Members()
for i := range members {
m.Set(members[i].Key(), members[i].Value())
}
return m
}
// GetVar retrieves value and returns a *gvar.Var for specified key from baggage.
func (b *Baggage) GetVar(key string) *gvar.Var {
value := baggage.Value(b.ctx, attribute.Key(key))
return gvar.New(value.AsInterface())
value := baggage.FromContext(b.ctx).Member(key).Value()
return gvar.New(value)
}

View File

@ -16,7 +16,7 @@ type Span struct {
}
// NewSpan creates a span using default tracer.
func NewSpan(ctx context.Context, spanName string, opts ...trace.SpanOption) (context.Context, *Span) {
func NewSpan(ctx context.Context, spanName string, opts ...trace.SpanStartOption) (context.Context, *Span) {
ctx, span := NewTracer().Start(ctx, spanName, opts...)
return ctx, &Span{
Span: span,

View File

@ -57,7 +57,7 @@ func TestNewCarrier(t *testing.T) {
t.Assert(carrier1.String(), `{"traceparent":"00-4bf92f3577b34da6a3ce929d0e0e4736-0000000000000002-01","tracestate":""}`)
ctx = otel.GetTextMapPropagator().Extract(ctx, carrier1)
gotSc := trace.RemoteSpanContextFromContext(ctx)
gotSc := trace.SpanContextFromContext(ctx)
t.Assert(gotSc.TraceID().String(), traceID.String())
t.Assert(gotSc.SpanID().String(), "0000000000000002")
})

View File

@ -96,8 +96,8 @@ func New() (*Watcher, error) {
return w, nil
}
// Add monitors <path> using default watcher with callback function <callbackFunc>.
// The optional parameter <recursive> specifies whether monitoring the <path> recursively, which is true in default.
// Add monitors `path` using default watcher with callback function `callbackFunc`.
// The optional parameter `recursive` specifies whether monitoring the `path` recursively, which is true in default.
func Add(path string, callbackFunc func(event *Event), recursive ...bool) (callback *Callback, err error) {
w, err := getDefaultWatcher()
if err != nil {
@ -106,11 +106,11 @@ func Add(path string, callbackFunc func(event *Event), recursive ...bool) (callb
return w.Add(path, callbackFunc, recursive...)
}
// AddOnce monitors <path> using default watcher with callback function <callbackFunc> only once using unique name <name>.
// If AddOnce is called multiple times with the same <name> parameter, <path> is only added to monitor once. It returns error
// if it's called twice with the same <name>.
// AddOnce monitors `path` using default watcher with callback function `callbackFunc` only once using unique name `name`.
// If AddOnce is called multiple times with the same `name` parameter, `path` is only added to monitor once. It returns error
// if it's called twice with the same `name`.
//
// The optional parameter <recursive> specifies whether monitoring the <path> recursively, which is true in default.
// The optional parameter `recursive` specifies whether monitoring the `path` recursively, which is true in default.
func AddOnce(name, path string, callbackFunc func(event *Event), recursive ...bool) (callback *Callback, err error) {
w, err := getDefaultWatcher()
if err != nil {
@ -119,7 +119,7 @@ func AddOnce(name, path string, callbackFunc func(event *Event), recursive ...bo
return w.AddOnce(name, path, callbackFunc, recursive...)
}
// Remove removes all monitoring callbacks of given <path> from watcher recursively.
// Remove removes all monitoring callbacks of given `path` from watcher recursively.
func Remove(path string) error {
w, err := getDefaultWatcher()
if err != nil {

View File

@ -24,7 +24,7 @@ func fileDir(path string) string {
return filepath.Dir(path)
}
// fileRealPath converts the given <path> to its absolute path
// fileRealPath converts the given `path` to its absolute path
// and checks if the file path exists.
// If the file does not exist, return an empty string.
func fileRealPath(path string) string {
@ -38,7 +38,7 @@ func fileRealPath(path string) string {
return p
}
// fileExists checks whether given <path> exist.
// fileExists checks whether given `path` exist.
func fileExists(path string) bool {
if stat, err := os.Stat(path); stat != nil && !os.IsNotExist(err) {
return true
@ -46,7 +46,7 @@ func fileExists(path string) bool {
return false
}
// fileIsDir checks whether given <path> a directory.
// fileIsDir checks whether given `path` a directory.
func fileIsDir(path string) bool {
s, err := os.Stat(path)
if err != nil {
@ -55,7 +55,7 @@ func fileIsDir(path string) bool {
return s.IsDir()
}
// fileAllDirs returns all sub-folders including itself of given <path> recursively.
// fileAllDirs returns all sub-folders including itself of given `path` recursively.
func fileAllDirs(path string) (list []string) {
list = []string{path}
file, err := os.Open(path)
@ -78,8 +78,8 @@ func fileAllDirs(path string) (list []string) {
return
}
// fileScanDir returns all sub-files with absolute paths of given <path>,
// It scans directory recursively if given parameter <recursive> is true.
// fileScanDir returns all sub-files with absolute paths of given `path`,
// It scans directory recursively if given parameter `recursive` is true.
func fileScanDir(path string, pattern string, recursive ...bool) ([]string, error) {
list, err := doFileScanDir(path, pattern, recursive...)
if err != nil {
@ -94,10 +94,10 @@ func fileScanDir(path string, pattern string, recursive ...bool) ([]string, erro
// doFileScanDir is an internal method which scans directory
// and returns the absolute path list of files that are not sorted.
//
// The pattern parameter <pattern> supports multiple file name patterns,
// The pattern parameter `pattern` supports multiple file name patterns,
// using the ',' symbol to separate multiple patterns.
//
// It scans directory recursively if given parameter <recursive> is true.
// It scans directory recursively if given parameter `recursive` is true.
func doFileScanDir(path string, pattern string, recursive ...bool) ([]string, error) {
list := ([]string)(nil)
file, err := os.Open(path)

View File

@ -14,19 +14,20 @@ import (
"github.com/gogf/gf/container/glist"
)
// Add monitors <path> with callback function <callbackFunc> to the watcher.
// The optional parameter <recursive> specifies whether monitoring the <path> recursively,
// Add monitors `path` with callback function `callbackFunc` to the watcher.
// The optional parameter `recursive` specifies whether monitoring the `path` recursively,
// which is true in default.
func (w *Watcher) Add(path string, callbackFunc func(event *Event), recursive ...bool) (callback *Callback, err error) {
return w.AddOnce("", path, callbackFunc, recursive...)
}
// AddOnce monitors <path> with callback function <callbackFunc> only once using unique name
// <name> to the watcher. If AddOnce is called multiple times with the same <name> parameter,
// <path> is only added to monitor once.
// It returns error if it's called twice with the same <name>.
// AddOnce monitors `path` with callback function `callbackFunc` only once using unique name
// `name` to the watcher. If AddOnce is called multiple times with the same `name` parameter,
// `path` is only added to monitor once.
//
// The optional parameter <recursive> specifies whether monitoring the <path> recursively,
// It returns error if it's called twice with the same `name`.
//
// The optional parameter `recursive` specifies whether monitoring the `path` recursively,
// which is true in default.
func (w *Watcher) AddOnce(name, path string, callbackFunc func(event *Event), recursive ...bool) (callback *Callback, err error) {
w.nameSet.AddIfNotExistFuncLock(name, func() bool {
@ -99,8 +100,6 @@ func (w *Watcher) addWithCallbackFunc(name, path string, callbackFunc func(event
}
// Add the callback to global callback map.
callbackIdMap.Set(callback.Id, callback)
//intlog.Print("addWithCallbackFunc", name, path, callback.recursive)
return
}
@ -113,7 +112,7 @@ func (w *Watcher) Close() {
close(w.closeChan)
}
// Remove removes monitor and all callbacks associated with the <path> recursively.
// Remove removes monitor and all callbacks associated with the `path` recursively.
func (w *Watcher) Remove(path string) error {
// Firstly remove the callbacks of the path.
if r := w.callbacks.Remove(path); r != nil {

View File

@ -134,7 +134,7 @@ func (w *Watcher) eventLoop() {
}()
}
// getCallbacks searches and returns all callbacks with given <path>.
// getCallbacks searches and returns all callbacks with given `path`.
// It also searches its parents for callbacks if they're recursive.
func (w *Watcher) getCallbacks(path string) (callbacks []*Callback) {
// Firstly add the callbacks of itself.