orm with feature

This commit is contained in:
John Guo
2021-02-08 17:57:21 +08:00
parent b4d5335e43
commit acf47f3907
50 changed files with 890 additions and 560 deletions

View File

@ -37,9 +37,23 @@ type DB interface {
// "Table" is not proper for that purpose any more.
// Deprecated, use Model instead.
Table(table ...string) *Model
// Model creates and returns a new ORM model from given schema.
// The parameter `table` can be more than one table names, and also alias name, like:
// 1. Model names:
// Model("user")
// Model("user u")
// Model("user, user_detail")
// Model("user u, user_detail ud")
// 2. Model name with alias: Model("user", "u")
Model(table ...string) *Model
// Schema creates and returns a schema.
Schema(schema string) *Schema
// With creates and returns an ORM model based on meta data of given object.
With(object interface{}) *Model
// Open creates a raw connection object for database with given node configuration.
// Note that it is not recommended using the this function manually.
Open(config *ConfigNode) (*sql.DB, error)
@ -158,9 +172,9 @@ type DB interface {
FilteredLinkInfo() string
// HandleSqlBeforeCommit is a hook function, which deals with the sql string before
// it's committed to underlying driver. The parameter <link> specifies the current
// database connection operation object. You can modify the sql string <sql> and its
// arguments <args> as you wish before they're committed to driver.
// it's committed to underlying driver. The parameter `link` specifies the current
// database connection operation object. You can modify the sql string `sql` and its
// arguments `args` as you wish before they're committed to driver.
HandleSqlBeforeCommit(link Link, sql string, args []interface{}) (string, []interface{})
// ===========================================================================
@ -174,14 +188,14 @@ type DB interface {
// Core is the base struct for database management.
type Core struct {
DB DB // DB interface object.
db DB // DB interface object.
ctx context.Context // Context for chaining operation only.
group string // Configuration group name.
debug *gtype.Bool // Enable debug mode for the database, which can be changed in runtime.
cache *gcache.Cache // Cache manager, SQL result cache only.
schema *gtype.String // Custom schema for this object.
logger *glog.Logger // Logger.
config *ConfigNode // Current config node.
ctx context.Context // Context for chaining operation only.
}
// Driver is the interface for integrating sql drivers into package gdb.
@ -302,7 +316,7 @@ func Register(name string, driver Driver) error {
}
// New creates and returns an ORM object with global configurations.
// The parameter <name> specifies the configuration group name,
// The parameter `name` specifies the configuration group name,
// which is DefaultGroupName in default.
func New(group ...string) (db DB, err error) {
groupName := configs.group
@ -326,11 +340,11 @@ func New(group ...string) (db DB, err error) {
config: node,
}
if v, ok := driverMap[node.Type]; ok {
c.DB, err = v.New(c, node)
c.db, err = v.New(c, node)
if err != nil {
return nil, err
}
return c.DB, nil
return c.db, nil
} else {
return nil, gerror.New(fmt.Sprintf(`unsupported database type "%s"`, node.Type))
}
@ -343,7 +357,7 @@ func New(group ...string) (db DB, err error) {
}
// Instance returns an instance for DB operations.
// The parameter <name> specifies the configuration group name,
// The parameter `name` specifies the configuration group name,
// which is DefaultGroupName in default.
func Instance(name ...string) (db DB, err error) {
group := configs.group
@ -363,7 +377,7 @@ func Instance(name ...string) (db DB, err error) {
// getConfigNodeByGroup calculates and returns a configuration node of given group. It
// calculates the value internally using weight algorithm for load balance.
//
// The parameter <master> specifies whether retrieving a master node, or else a slave node
// The parameter `master` specifies whether retrieving a master node, or else a slave node
// if master-slave configured.
func getConfigNodeByGroup(group string, master bool) (*ConfigNode, error) {
if list, ok := configs.config[group]; ok {
@ -432,7 +446,7 @@ func getConfigNodeByWeight(cg ConfigGroup) *ConfigNode {
}
// getSqlDb retrieves and returns a underlying database connection object.
// The parameter <master> specifies whether retrieves master node connection if
// The parameter `master` specifies whether retrieves master node connection if
// master-slave nodes are configured.
func (c *Core) getSqlDb(master bool, schema ...string) (sqlDb *sql.DB, err error) {
// Load balance.
@ -457,7 +471,7 @@ func (c *Core) getSqlDb(master bool, schema ...string) (sqlDb *sql.DB, err error
}
// Cache the underlying connection pool object by node.
v, _ := internalCache.GetOrSetFuncLock(node.String(), func() (interface{}, error) {
sqlDb, err = c.DB.Open(node)
sqlDb, err = c.db.Open(node)
if err != nil {
intlog.Printf("DB open failed: %v, %+v", err, node)
return nil, err
@ -489,10 +503,10 @@ func (c *Core) getSqlDb(master bool, schema ...string) (sqlDb *sql.DB, err error
sqlDb = v.(*sql.DB)
}
if node.Debug {
c.DB.SetDebug(node.Debug)
c.db.SetDebug(node.Debug)
}
if node.DryRun {
c.DB.SetDryRun(node.DryRun)
c.db.SetDryRun(node.DryRun)
}
return
}

View File

@ -30,21 +30,21 @@ import (
// a global or package variable for long using.
func (c *Core) Ctx(ctx context.Context) DB {
if ctx == nil {
return c.DB
return c.db
}
var (
err error
newCore = &Core{}
configNode = c.DB.GetConfig()
configNode = c.db.GetConfig()
)
*newCore = *c
newCore.ctx = ctx
newCore.DB, err = driverMap[configNode.Type].New(newCore, configNode)
newCore.db, err = driverMap[configNode.Type].New(newCore, configNode)
// Seldom error, just log it.
if err != nil {
c.DB.GetLogger().Ctx(ctx).Error(err)
c.db.GetLogger().Ctx(ctx).Error(err)
}
return newCore.DB
return newCore.db
}
// GetCtx returns the context for current DB.
@ -59,22 +59,22 @@ func (c *Core) GetCtx() context.Context {
// GetCtxTimeout returns the context and cancel function for specified timeout type.
func (c *Core) GetCtxTimeout(timeoutType int, ctx context.Context) (context.Context, context.CancelFunc) {
if ctx == nil {
ctx = c.DB.GetCtx()
ctx = c.db.GetCtx()
} else {
ctx = context.WithValue(ctx, "WrappedByGetCtxTimeout", nil)
}
switch timeoutType {
case ctxTimeoutTypeExec:
if c.DB.GetConfig().ExecTimeout > 0 {
return context.WithTimeout(ctx, c.DB.GetConfig().ExecTimeout)
if c.db.GetConfig().ExecTimeout > 0 {
return context.WithTimeout(ctx, c.db.GetConfig().ExecTimeout)
}
case ctxTimeoutTypeQuery:
if c.DB.GetConfig().QueryTimeout > 0 {
return context.WithTimeout(ctx, c.DB.GetConfig().QueryTimeout)
if c.db.GetConfig().QueryTimeout > 0 {
return context.WithTimeout(ctx, c.db.GetConfig().QueryTimeout)
}
case ctxTimeoutTypePrepare:
if c.DB.GetConfig().PrepareTimeout > 0 {
return context.WithTimeout(ctx, c.DB.GetConfig().PrepareTimeout)
if c.db.GetConfig().PrepareTimeout > 0 {
return context.WithTimeout(ctx, c.db.GetConfig().PrepareTimeout)
}
default:
panic(gerror.Newf("invalid context timeout type: %d", timeoutType))
@ -97,19 +97,19 @@ func (c *Core) Slave() (*sql.DB, error) {
// Query commits one query SQL to underlying driver and returns the execution result.
// It is most commonly used for data querying.
func (c *Core) Query(sql string, args ...interface{}) (rows *sql.Rows, err error) {
link, err := c.DB.Slave()
link, err := c.db.Slave()
if err != nil {
return nil, err
}
return c.DB.DoQuery(link, sql, args...)
return c.db.DoQuery(link, sql, args...)
}
// DoQuery commits the sql string and its arguments to underlying driver
// through given link object and returns the execution result.
func (c *Core) DoQuery(link Link, sql string, args ...interface{}) (rows *sql.Rows, err error) {
sql, args = formatSql(sql, args)
sql, args = c.DB.HandleSqlBeforeCommit(link, sql, args)
ctx := c.DB.GetCtx()
sql, args = c.db.HandleSqlBeforeCommit(link, sql, args)
ctx := c.db.GetCtx()
if c.GetConfig().QueryTimeout > 0 {
var cancelFunc context.CancelFunc
ctx, cancelFunc = context.WithTimeout(ctx, c.GetConfig().QueryTimeout)
@ -127,10 +127,10 @@ func (c *Core) DoQuery(link Link, sql string, args ...interface{}) (rows *sql.Ro
Error: err,
Start: mTime1,
End: mTime2,
Group: c.DB.GetGroup(),
Group: c.db.GetGroup(),
}
c.addSqlToTracing(ctx, sqlObj)
if c.DB.GetDebug() {
if c.db.GetDebug() {
c.writeSqlToLogger(sqlObj)
}
if err == nil {
@ -144,19 +144,19 @@ func (c *Core) DoQuery(link Link, sql string, args ...interface{}) (rows *sql.Ro
// Exec commits one query SQL to underlying driver and returns the execution result.
// It is most commonly used for data inserting and updating.
func (c *Core) Exec(sql string, args ...interface{}) (result sql.Result, err error) {
link, err := c.DB.Master()
link, err := c.db.Master()
if err != nil {
return nil, err
}
return c.DB.DoExec(link, sql, args...)
return c.db.DoExec(link, sql, args...)
}
// DoExec commits the sql string and its arguments to underlying driver
// through given link object and returns the execution result.
func (c *Core) DoExec(link Link, sql string, args ...interface{}) (result sql.Result, err error) {
sql, args = formatSql(sql, args)
sql, args = c.DB.HandleSqlBeforeCommit(link, sql, args)
ctx := c.DB.GetCtx()
sql, args = c.db.HandleSqlBeforeCommit(link, sql, args)
ctx := c.db.GetCtx()
if c.GetConfig().ExecTimeout > 0 {
var cancelFunc context.CancelFunc
ctx, cancelFunc = context.WithTimeout(ctx, c.GetConfig().ExecTimeout)
@ -164,7 +164,7 @@ func (c *Core) DoExec(link Link, sql string, args ...interface{}) (result sql.Re
}
mTime1 := gtime.TimestampMilli()
if !c.DB.GetDryRun() {
if !c.db.GetDryRun() {
result, err = link.ExecContext(ctx, sql, args...)
} else {
result = new(SqlResult)
@ -178,10 +178,10 @@ func (c *Core) DoExec(link Link, sql string, args ...interface{}) (result sql.Re
Error: err,
Start: mTime1,
End: mTime2,
Group: c.DB.GetGroup(),
Group: c.db.GetGroup(),
}
c.addSqlToTracing(ctx, sqlObj)
if c.DB.GetDebug() {
if c.db.GetDebug() {
c.writeSqlToLogger(sqlObj)
}
return result, formatError(err, sql, args...)
@ -193,7 +193,7 @@ func (c *Core) DoExec(link Link, sql string, args ...interface{}) (result sql.Re
// The caller must call the statement's Close method
// when the statement is no longer needed.
//
// The parameter <execOnMaster> specifies whether executing the sql on master node,
// The parameter `execOnMaster` specifies whether executing the sql on master node,
// or else it executes the sql on slave node if master-slave configured.
func (c *Core) Prepare(sql string, execOnMaster ...bool) (*Stmt, error) {
var (
@ -201,20 +201,20 @@ func (c *Core) Prepare(sql string, execOnMaster ...bool) (*Stmt, error) {
link Link
)
if len(execOnMaster) > 0 && execOnMaster[0] {
if link, err = c.DB.Master(); err != nil {
if link, err = c.db.Master(); err != nil {
return nil, err
}
} else {
if link, err = c.DB.Slave(); err != nil {
if link, err = c.db.Slave(); err != nil {
return nil, err
}
}
return c.DB.DoPrepare(link, sql)
return c.db.DoPrepare(link, sql)
}
// doPrepare calls prepare function on given link object and returns the statement object.
func (c *Core) DoPrepare(link Link, sql string) (*Stmt, error) {
ctx := c.DB.GetCtx()
ctx := c.db.GetCtx()
if c.GetConfig().PrepareTimeout > 0 {
// DO NOT USE cancel function in prepare statement.
ctx, _ = context.WithTimeout(ctx, c.GetConfig().PrepareTimeout)
@ -231,11 +231,11 @@ func (c *Core) DoPrepare(link Link, sql string) (*Stmt, error) {
Error: err,
Start: mTime1,
End: mTime2,
Group: c.DB.GetGroup(),
Group: c.db.GetGroup(),
}
)
c.addSqlToTracing(ctx, sqlObj)
if c.DB.GetDebug() {
if c.db.GetDebug() {
c.writeSqlToLogger(sqlObj)
}
return &Stmt{
@ -247,28 +247,28 @@ func (c *Core) DoPrepare(link Link, sql string) (*Stmt, error) {
// GetAll queries and returns data records from database.
func (c *Core) GetAll(sql string, args ...interface{}) (Result, error) {
return c.DB.DoGetAll(nil, sql, args...)
return c.db.DoGetAll(nil, sql, args...)
}
// DoGetAll queries and returns data records from database.
func (c *Core) DoGetAll(link Link, sql string, args ...interface{}) (result Result, err error) {
if link == nil {
link, err = c.DB.Slave()
link, err = c.db.Slave()
if err != nil {
return nil, err
}
}
rows, err := c.DB.DoQuery(link, sql, args...)
rows, err := c.db.DoQuery(link, sql, args...)
if err != nil || rows == nil {
return nil, err
}
defer rows.Close()
return c.DB.convertRowsToResult(rows)
return c.db.convertRowsToResult(rows)
}
// GetOne queries and returns one record from database.
func (c *Core) GetOne(sql string, args ...interface{}) (Record, error) {
list, err := c.DB.GetAll(sql, args...)
list, err := c.db.GetAll(sql, args...)
if err != nil {
return nil, err
}
@ -281,7 +281,7 @@ func (c *Core) GetOne(sql string, args ...interface{}) (Record, error) {
// GetArray queries and returns data values as slice from database.
// Note that if there're multiple columns in the result, it returns just one column values randomly.
func (c *Core) GetArray(sql string, args ...interface{}) ([]Value, error) {
all, err := c.DB.DoGetAll(nil, sql, args...)
all, err := c.db.DoGetAll(nil, sql, args...)
if err != nil {
return nil, err
}
@ -289,9 +289,9 @@ func (c *Core) GetArray(sql string, args ...interface{}) ([]Value, error) {
}
// GetStruct queries one record from database and converts it to given struct.
// The parameter <pointer> should be a pointer to struct.
// The parameter `pointer` should be a pointer to struct.
func (c *Core) GetStruct(pointer interface{}, sql string, args ...interface{}) error {
one, err := c.DB.GetOne(sql, args...)
one, err := c.db.GetOne(sql, args...)
if err != nil {
return err
}
@ -299,9 +299,9 @@ func (c *Core) GetStruct(pointer interface{}, sql string, args ...interface{}) e
}
// GetStructs queries records from database and converts them to given struct.
// The parameter <pointer> should be type of struct slice: []struct/[]*struct.
// The parameter `pointer` should be type of struct slice: []struct/[]*struct.
func (c *Core) GetStructs(pointer interface{}, sql string, args ...interface{}) error {
all, err := c.DB.GetAll(sql, args...)
all, err := c.db.GetAll(sql, args...)
if err != nil {
return err
}
@ -311,8 +311,8 @@ func (c *Core) GetStructs(pointer interface{}, sql string, args ...interface{})
// GetScan queries one or more records from database and converts them to given struct or
// struct array.
//
// If parameter <pointer> is type of struct pointer, it calls GetStruct internally for
// the conversion. If parameter <pointer> is type of slice, it calls GetStructs internally
// If parameter `pointer` is type of struct pointer, it calls GetStruct internally for
// the conversion. If parameter `pointer` is type of slice, it calls GetStructs internally
// for conversion.
func (c *Core) GetScan(pointer interface{}, sql string, args ...interface{}) error {
t := reflect.TypeOf(pointer)
@ -323,9 +323,9 @@ func (c *Core) GetScan(pointer interface{}, sql string, args ...interface{}) err
k = t.Elem().Kind()
switch k {
case reflect.Array, reflect.Slice:
return c.DB.GetStructs(pointer, sql, args...)
return c.db.GetStructs(pointer, sql, args...)
case reflect.Struct:
return c.DB.GetStruct(pointer, sql, args...)
return c.db.GetStruct(pointer, sql, args...)
}
return fmt.Errorf("element type should be type of struct/slice, unsupported: %v", k)
}
@ -334,7 +334,7 @@ func (c *Core) GetScan(pointer interface{}, sql string, args ...interface{}) err
// The sql should queries only one field from database, or else it returns only one
// field of the result.
func (c *Core) GetValue(sql string, args ...interface{}) (Value, error) {
one, err := c.DB.GetOne(sql, args...)
one, err := c.db.GetOne(sql, args...)
if err != nil {
return gvar.New(nil), err
}
@ -351,7 +351,7 @@ func (c *Core) GetCount(sql string, args ...interface{}) (int, error) {
if !gregex.IsMatchString(`(?i)SELECT\s+COUNT\(.+\)\s+FROM`, sql) {
sql, _ = gregex.ReplaceString(`(?i)(SELECT)\s+(.+)\s+(FROM)`, `$1 COUNT($2) $3`, sql)
}
value, err := c.DB.GetValue(sql, args...)
value, err := c.db.GetValue(sql, args...)
if err != nil {
return 0, err
}
@ -360,7 +360,7 @@ func (c *Core) GetCount(sql string, args ...interface{}) (int, error) {
// PingMaster pings the master node to check authentication or keeps the connection alive.
func (c *Core) PingMaster() error {
if master, err := c.DB.Master(); err != nil {
if master, err := c.db.Master(); err != nil {
return err
} else {
return master.Ping()
@ -369,7 +369,7 @@ func (c *Core) PingMaster() error {
// PingSlave pings the slave node to check authentication or keeps the connection alive.
func (c *Core) PingSlave() error {
if slave, err := c.DB.Slave(); err != nil {
if slave, err := c.db.Slave(); err != nil {
return err
} else {
return slave.Ping()
@ -381,10 +381,10 @@ func (c *Core) PingSlave() error {
// if you no longer use the transaction. Commit or Rollback functions will also
// close the transaction automatically.
func (c *Core) Begin() (*TX, error) {
if master, err := c.DB.Master(); err != nil {
if master, err := c.db.Master(); err != nil {
return nil, err
} else {
ctx := c.DB.GetCtx()
ctx := c.db.GetCtx()
if c.GetConfig().TranTimeout > 0 {
var cancelFunc context.CancelFunc
ctx, cancelFunc = context.WithTimeout(ctx, c.GetConfig().TranTimeout)
@ -392,7 +392,7 @@ func (c *Core) Begin() (*TX, error) {
}
if tx, err := master.BeginTx(ctx, nil); err == nil {
return &TX{
db: c.DB,
db: c.db,
tx: tx,
master: master,
}, nil
@ -402,16 +402,16 @@ func (c *Core) Begin() (*TX, error) {
}
}
// Transaction wraps the transaction logic using function <f>.
// It rollbacks the transaction and returns the error from function <f> if
// Transaction wraps the transaction logic using function `f`.
// It rollbacks the transaction and returns the error from function `f` if
// it returns non-nil error. It commits the transaction and returns nil if
// function <f> returns nil.
// function `f` returns nil.
//
// Note that, you should not Commit or Rollback the transaction in function <f>
// Note that, you should not Commit or Rollback the transaction in function `f`
// as it is automatically handled by this function.
func (c *Core) Transaction(f func(tx *TX) error) (err error) {
var tx *TX
tx, err = c.DB.Begin()
tx, err = c.db.Begin()
if err != nil {
return err
}
@ -438,12 +438,12 @@ func (c *Core) Transaction(f func(tx *TX) error) (err error) {
// Insert does "INSERT INTO ..." statement for the table.
// If there's already one unique record of the data in the table, it returns error.
//
// The parameter <data> can be type of map/gmap/struct/*struct/[]map/[]struct, etc.
// 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 <batch> specifies the batch operation count when given data is slice.
// The parameter `batch` specifies the batch operation count when given data is slice.
func (c *Core) Insert(table string, data interface{}, batch ...int) (sql.Result, error) {
if len(batch) > 0 {
return c.Model(table).Data(data).Batch(batch[0]).Insert()
@ -454,12 +454,12 @@ func (c *Core) Insert(table string, data interface{}, batch ...int) (sql.Result,
// InsertIgnore does "INSERT IGNORE INTO ..." statement for the table.
// If there's already one unique record of the data in the table, it ignores the inserting.
//
// The parameter <data> can be type of map/gmap/struct/*struct/[]map/[]struct, etc.
// 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 <batch> specifies the batch operation count when given data is slice.
// The parameter `batch` specifies the batch operation count when given data is slice.
func (c *Core) InsertIgnore(table string, data interface{}, batch ...int) (sql.Result, error) {
if len(batch) > 0 {
return c.Model(table).Data(data).Batch(batch[0]).InsertIgnore()
@ -471,14 +471,14 @@ func (c *Core) InsertIgnore(table string, data interface{}, batch ...int) (sql.R
// If there's already one unique record of the data in the table, it deletes the record
// and inserts a new one.
//
// The parameter <data> can be type of map/gmap/struct/*struct/[]map/[]struct, etc.
// 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 <data> can be type of map/gmap/struct/*struct/[]map/[]struct, etc.
// The parameter `data` can be type of map/gmap/struct/*struct/[]map/[]struct, etc.
// If given data is type of slice, it then does batch replacing, and the optional parameter
// <batch> specifies the batch operation count.
// `batch` specifies the batch operation count.
func (c *Core) Replace(table string, data interface{}, batch ...int) (sql.Result, error) {
if len(batch) > 0 {
return c.Model(table).Data(data).Batch(batch[0]).Replace()
@ -490,13 +490,13 @@ func (c *Core) Replace(table string, data interface{}, batch ...int) (sql.Result
// It updates the record if there's primary or unique index in the saving data,
// or else it inserts a new record into the table.
//
// The parameter <data> can be type of map/gmap/struct/*struct/[]map/[]struct, etc.
// 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"})
//
// If given data is type of slice, it then does batch saving, and the optional parameter
// <batch> specifies the batch operation count.
// `batch` specifies the batch operation count.
func (c *Core) Save(table string, data interface{}, batch ...int) (sql.Result, error) {
if len(batch) > 0 {
return c.Model(table).Data(data).Batch(batch[0]).Save()
@ -506,18 +506,18 @@ func (c *Core) Save(table string, data interface{}, batch ...int) (sql.Result, e
// 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.
// 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:
// 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 (c *Core) DoInsert(link Link, table string, data interface{}, option int, batch ...int) (result sql.Result, err error) {
table = c.DB.QuotePrefixTableName(table)
table = c.db.QuotePrefixTableName(table)
var (
fields []string
values []string
@ -532,10 +532,10 @@ func (c *Core) DoInsert(link Link, table string, data interface{}, option int, b
}
switch reflectKind {
case reflect.Slice, reflect.Array:
return c.DB.DoBatchInsert(link, table, data, option, batch...)
return c.db.DoBatchInsert(link, table, data, option, batch...)
case reflect.Struct:
if _, ok := data.(apiInterfaces); ok {
return c.DB.DoBatchInsert(link, table, data, option, batch...)
return c.db.DoBatchInsert(link, table, data, option, batch...)
} else {
dataMap = ConvertDataForTableRecord(data)
}
@ -548,7 +548,7 @@ func (c *Core) DoInsert(link Link, table string, data interface{}, option int, b
return nil, gerror.New("data cannot be empty")
}
var (
charL, charR = c.DB.GetChars()
charL, charR = c.db.GetChars()
operation = GetInsertOperationByOption(option)
updateStr = ""
)
@ -580,11 +580,11 @@ func (c *Core) DoInsert(link Link, table string, data interface{}, option int, b
updateStr = fmt.Sprintf("ON DUPLICATE KEY UPDATE %s", updateStr)
}
if link == nil {
if link, err = c.DB.Master(); err != nil {
if link, err = c.db.Master(); err != nil {
return nil, err
}
}
return c.DB.DoExec(
return c.db.DoExec(
link,
fmt.Sprintf(
"%s INTO %s(%s) VALUES(%s) %s",
@ -596,7 +596,7 @@ func (c *Core) DoInsert(link Link, table string, data interface{}, option int, b
}
// BatchInsert batch inserts data.
// The parameter <list> must be type of slice of map or struct.
// The parameter `list` must be type of slice of map or struct.
func (c *Core) BatchInsert(table string, list interface{}, batch ...int) (sql.Result, error) {
if len(batch) > 0 {
return c.Model(table).Data(list).Batch(batch[0]).Insert()
@ -605,7 +605,7 @@ func (c *Core) BatchInsert(table string, list interface{}, batch ...int) (sql.Re
}
// BatchInsertIgnore batch inserts data with ignore option.
// The parameter <list> must be type of slice of map or struct.
// The parameter `list` must be type of slice of map or struct.
func (c *Core) BatchInsertIgnore(table string, list interface{}, batch ...int) (sql.Result, error) {
if len(batch) > 0 {
return c.Model(table).Data(list).Batch(batch[0]).InsertIgnore()
@ -614,7 +614,7 @@ func (c *Core) BatchInsertIgnore(table string, list interface{}, batch ...int) (
}
// BatchReplace batch replaces data.
// The parameter <list> must be type of slice of map or struct.
// The parameter `list` must be type of slice of map or struct.
func (c *Core) BatchReplace(table string, list interface{}, batch ...int) (sql.Result, error) {
if len(batch) > 0 {
return c.Model(table).Data(list).Batch(batch[0]).Replace()
@ -623,7 +623,7 @@ func (c *Core) BatchReplace(table string, list interface{}, batch ...int) (sql.R
}
// BatchSave batch replaces data.
// The parameter <list> must be type of slice of map or struct.
// The parameter `list` must be type of slice of map or struct.
func (c *Core) BatchSave(table string, list interface{}, batch ...int) (sql.Result, error) {
if len(batch) > 0 {
return c.Model(table).Data(list).Batch(batch[0]).Save()
@ -634,7 +634,7 @@ func (c *Core) BatchSave(table string, list interface{}, batch ...int) (sql.Resu
// DoBatchInsert batch inserts/replaces/saves data.
// This function is usually used for custom interface definition, you do not need call it manually.
func (c *Core) DoBatchInsert(link Link, table string, list interface{}, option int, batch ...int) (result sql.Result, err error) {
table = c.DB.QuotePrefixTableName(table)
table = c.db.QuotePrefixTableName(table)
var (
keys []string // Field names.
values []string // Value holder string array, like: (?,?,?)
@ -689,7 +689,7 @@ func (c *Core) DoBatchInsert(link Link, table string, list interface{}, option i
return result, gerror.New("data list cannot be empty")
}
if link == nil {
if link, err = c.DB.Master(); err != nil {
if link, err = c.db.Master(); err != nil {
return
}
}
@ -699,7 +699,7 @@ func (c *Core) DoBatchInsert(link Link, table string, list interface{}, option i
}
// Prepare the batch result pointer.
var (
charL, charR = c.DB.GetChars()
charL, charR = c.db.GetChars()
batchResult = new(SqlResult)
keysStr = charL + strings.Join(keys, charR+","+charL) + charR
operation = GetInsertOperationByOption(option)
@ -745,7 +745,7 @@ func (c *Core) DoBatchInsert(link Link, table string, list interface{}, option i
}
valueHolder = append(valueHolder, "("+gstr.Join(values, ",")+")")
if len(valueHolder) == batchNum || (i == listMapLen-1 && len(valueHolder) > 0) {
r, err := c.DB.DoExec(
r, err := c.db.DoExec(
link,
fmt.Sprintf(
"%s INTO %s(%s) VALUES%s %s",
@ -773,11 +773,11 @@ func (c *Core) DoBatchInsert(link Link, table string, list interface{}, option i
// Update does "UPDATE ... " statement for the table.
//
// The parameter <data> can be type of string/map/gmap/struct/*struct, etc.
// The parameter `data` can be type of string/map/gmap/struct/*struct, etc.
// Eg: "uid=10000", "uid", 10000, g.Map{"uid": 10000, "name":"john"}
//
// The parameter <condition> can be type of string/map/gmap/slice/struct/*struct, etc.
// It is commonly used with parameter <args>.
// The parameter `condition` can be type of string/map/gmap/slice/struct/*struct, etc.
// It is commonly used with parameter `args`.
// Eg:
// "uid=10000",
// "uid", 10000
@ -792,7 +792,7 @@ func (c *Core) Update(table string, data interface{}, condition interface{}, arg
// doUpdate does "UPDATE ... " statement for the table.
// This function is usually used for custom interface definition, you do not need call it manually.
func (c *Core) DoUpdate(link Link, table string, data interface{}, condition string, args ...interface{}) (result sql.Result, err error) {
table = c.DB.QuotePrefixTableName(table)
table = c.db.QuotePrefixTableName(table)
var (
rv = reflect.ValueOf(data)
kind = rv.Kind()
@ -815,21 +815,21 @@ func (c *Core) DoUpdate(link Link, table string, data interface{}, condition str
switch value := v.(type) {
case *Counter:
if value.Value != 0 {
column := c.DB.QuoteWord(value.Field)
column := c.db.QuoteWord(value.Field)
fields = append(fields, fmt.Sprintf("%s=%s+?", column, column))
params = append(params, value.Value)
}
case Counter:
if value.Value != 0 {
column := c.DB.QuoteWord(value.Field)
column := c.db.QuoteWord(value.Field)
fields = append(fields, fmt.Sprintf("%s=%s+?", column, column))
params = append(params, value.Value)
}
default:
if s, ok := v.(Raw); ok {
fields = append(fields, c.DB.QuoteWord(k)+"="+gconv.String(s))
fields = append(fields, c.db.QuoteWord(k)+"="+gconv.String(s))
} else {
fields = append(fields, c.DB.QuoteWord(k)+"=?")
fields = append(fields, c.db.QuoteWord(k)+"=?")
params = append(params, v)
}
@ -847,11 +847,11 @@ func (c *Core) DoUpdate(link Link, table string, data interface{}, condition str
}
// If no link passed, it then uses the master link.
if link == nil {
if link, err = c.DB.Master(); err != nil {
if link, err = c.db.Master(); err != nil {
return nil, err
}
}
return c.DB.DoExec(
return c.db.DoExec(
link,
fmt.Sprintf("UPDATE %s SET %s%s", table, updates, condition),
args...,
@ -860,8 +860,8 @@ func (c *Core) DoUpdate(link Link, table string, data interface{}, condition str
// Delete does "DELETE FROM ... " statement for the table.
//
// The parameter <condition> can be type of string/map/gmap/slice/struct/*struct, etc.
// It is commonly used with parameter <args>.
// The parameter `condition` can be type of string/map/gmap/slice/struct/*struct, etc.
// It is commonly used with parameter `args`.
// Eg:
// "uid=10000",
// "uid", 10000
@ -877,12 +877,12 @@ func (c *Core) Delete(table string, condition interface{}, args ...interface{})
// This function is usually used for custom interface definition, you do not need call it manually.
func (c *Core) DoDelete(link Link, table string, condition string, args ...interface{}) (result sql.Result, err error) {
if link == nil {
if link, err = c.DB.Master(); err != nil {
if link, err = c.db.Master(); err != nil {
return nil, err
}
}
table = c.DB.QuotePrefixTableName(table)
return c.DB.DoExec(link, fmt.Sprintf("DELETE FROM %s%s", table, condition), args...)
table = c.db.QuotePrefixTableName(table)
return c.db.DoExec(link, fmt.Sprintf("DELETE FROM %s%s", table, condition), args...)
}
// convertRowsToResult converts underlying data record type sql.Rows to Result type.
@ -918,7 +918,7 @@ func (c *Core) convertRowsToResult(rows *sql.Rows) (Result, error) {
if value == nil {
row[columnNames[i]] = gvar.New(nil)
} else {
row[columnNames[i]] = gvar.New(c.DB.convertFieldValueToLocalValue(value, columnTypes[i]))
row[columnNames[i]] = gvar.New(c.db.convertFieldValueToLocalValue(value, columnTypes[i]))
}
}
records = append(records, row)
@ -944,15 +944,15 @@ func (c *Core) writeSqlToLogger(v *Sql) {
s := fmt.Sprintf("[%3d ms] [%s] %s", v.End-v.Start, v.Group, v.Format)
if v.Error != nil {
s += "\nError: " + v.Error.Error()
c.logger.Ctx(c.DB.GetCtx()).Error(s)
c.logger.Ctx(c.db.GetCtx()).Error(s)
} else {
c.logger.Ctx(c.DB.GetCtx()).Debug(s)
c.logger.Ctx(c.db.GetCtx()).Debug(s)
}
}
// HasTable determine whether the table name exists in the database.
func (c *Core) HasTable(name string) (bool, error) {
tableList, err := c.DB.Tables()
tableList, err := c.db.Tables()
if err != nil {
return false, err
}
@ -969,7 +969,7 @@ func (c *Core) isSoftCreatedFiledName(fieldName string) bool {
if fieldName == "" {
return false
}
if config := c.DB.GetConfig(); config.CreatedAt != "" {
if config := c.db.GetConfig(); config.CreatedAt != "" {
if utils.EqualFoldWithoutChars(fieldName, config.CreatedAt) {
return true
}

View File

@ -152,7 +152,7 @@ func (c *Core) SetMaxOpenConnCount(n int) {
}
// SetMaxConnLifetime sets the connection TTL for underlying connection pool.
// If parameter <d> <= 0, it means the connection never expires.
// If parameter `d` <= 0, it means the connection never expires.
func (c *Core) SetMaxConnLifetime(d time.Duration) {
c.config.MaxConnLifetime = d
}

View File

@ -24,8 +24,8 @@ import (
// convertFieldValueToLocalValue automatically checks and converts field value from database type
// to golang variable type.
func (c *Core) convertFieldValueToLocalValue(fieldValue interface{}, fieldType string) interface{} {
// If there's no type retrieved, it returns the <fieldValue> directly
// to use its original data type, as <fieldValue> is type of interface{}.
// If there's no type retrieved, it returns the `fieldValue` directly
// to use its original data type, as `fieldValue` is type of interface{}.
if fieldType == "" {
return fieldValue
}
@ -149,7 +149,7 @@ func (c *Core) convertFieldValueToLocalValue(fieldValue interface{}, fieldType s
// mappingAndFilterData automatically mappings the map key to table field and removes
// all key-value pairs that are not the field of given table.
func (c *Core) mappingAndFilterData(schema, table string, data map[string]interface{}, filter bool) (map[string]interface{}, error) {
if fieldsMap, err := c.DB.TableFields(table, schema); err == nil {
if fieldsMap, err := c.db.TableFields(table, schema); err == nil {
fieldsKeyMap := make(map[string]interface{}, len(fieldsMap))
for k, _ := range fieldsMap {
fieldsKeyMap[k] = nil
@ -182,7 +182,7 @@ func (c *Core) mappingAndFilterData(schema, table string, data map[string]interf
//func (c *Core) filterFields(schema, table string, data map[string]interface{}) map[string]interface{} {
// // It must use data copy here to avoid its changing the origin data map.
// newDataMap := make(map[string]interface{}, len(data))
// if fields, err := c.DB.TableFields(table, schema); err == nil {
// if fields, err := c.db.TableFields(table, schema); err == nil {
// for k, v := range data {
// if _, ok := fields[k]; ok {
// newDataMap[k] = v

View File

@ -51,24 +51,24 @@ func (c *Core) addSqlToTracing(ctx context.Context, sql *Sql) {
labels := make([]label.KeyValue, 0)
labels = append(labels, gtrace.CommonLabels()...)
labels = append(labels,
label.String(tracingAttrDbType, c.DB.GetConfig().Type),
label.String(tracingAttrDbType, c.db.GetConfig().Type),
)
if c.DB.GetConfig().Host != "" {
labels = append(labels, label.String(tracingAttrDbHost, c.DB.GetConfig().Host))
if c.db.GetConfig().Host != "" {
labels = append(labels, label.String(tracingAttrDbHost, c.db.GetConfig().Host))
}
if c.DB.GetConfig().Port != "" {
labels = append(labels, label.String(tracingAttrDbPort, c.DB.GetConfig().Port))
if c.db.GetConfig().Port != "" {
labels = append(labels, label.String(tracingAttrDbPort, c.db.GetConfig().Port))
}
if c.DB.GetConfig().Name != "" {
labels = append(labels, label.String(tracingAttrDbName, c.DB.GetConfig().Name))
if c.db.GetConfig().Name != "" {
labels = append(labels, label.String(tracingAttrDbName, c.db.GetConfig().Name))
}
if c.DB.GetConfig().User != "" {
labels = append(labels, label.String(tracingAttrDbUser, c.DB.GetConfig().User))
if c.db.GetConfig().User != "" {
labels = append(labels, label.String(tracingAttrDbUser, c.db.GetConfig().User))
}
if filteredLinkInfo := c.DB.FilteredLinkInfo(); filteredLinkInfo != "" {
labels = append(labels, label.String(tracingAttrDbLink, c.DB.FilteredLinkInfo()))
if filteredLinkInfo := c.db.FilteredLinkInfo(); filteredLinkInfo != "" {
labels = append(labels, label.String(tracingAttrDbLink, c.db.FilteredLinkInfo()))
}
if group := c.DB.GetGroup(); group != "" {
if group := c.db.GetGroup(); group != "" {
labels = append(labels, label.String(tracingAttrDbGroup, group))
}
span.SetAttributes(labels...)

View File

@ -11,31 +11,31 @@ import (
"database/sql"
)
// GetMaster acts like function Master but with additional <schema> parameter specifying
// GetMaster acts like function Master but with additional `schema` parameter specifying
// the schema for the connection. It is defined for internal usage.
// Also see Master.
func (c *Core) GetMaster(schema ...string) (*sql.DB, error) {
return c.getSqlDb(true, schema...)
}
// GetSlave acts like function Slave but with additional <schema> parameter specifying
// GetSlave acts like function Slave but with additional `schema` parameter specifying
// the schema for the connection. It is defined for internal usage.
// Also see Slave.
func (c *Core) GetSlave(schema ...string) (*sql.DB, error) {
return c.getSqlDb(false, schema...)
}
// QuoteWord checks given string <s> a word, if true quotes it with security chars of the database
// and returns the quoted string; or else return <s> without any change.
// QuoteWord checks given string `s` a word, if true quotes it with security chars of the database
// and returns the quoted string; or else return `s` without any change.
func (c *Core) QuoteWord(s string) string {
charLeft, charRight := c.DB.GetChars()
charLeft, charRight := c.db.GetChars()
return doQuoteWord(s, charLeft, charRight)
}
// QuoteString quotes string with quote chars. Strings like:
// "user", "user u", "user,user_detail", "user u, user_detail ut", "u.id asc".
func (c *Core) QuoteString(s string) string {
charLeft, charRight := c.DB.GetChars()
charLeft, charRight := c.db.GetChars()
return doQuoteString(s, charLeft, charRight)
}
@ -49,8 +49,8 @@ func (c *Core) QuoteString(s string) string {
// Note that, this will automatically checks the table prefix whether already added,
// if true it does nothing to the table name, or else adds the prefix to the table name.
func (c *Core) QuotePrefixTableName(table string) string {
charLeft, charRight := c.DB.GetChars()
return doHandleTableName(table, c.DB.GetPrefix(), charLeft, charRight)
charLeft, charRight := c.db.GetChars()
return doHandleTableName(table, c.db.GetPrefix(), charLeft, charRight)
}
// GetChars returns the security char for current database.

View File

@ -185,12 +185,12 @@ func (d *DriverMssql) parseSql(sql string) string {
// It's mainly used in cli tool chain for automatically generating the models.
func (d *DriverMssql) Tables(schema ...string) (tables []string, err error) {
var result Result
link, err := d.DB.GetSlave(schema...)
link, err := d.db.GetSlave(schema...)
if err != nil {
return nil, err
}
result, err = d.DB.DoGetAll(link, `SELECT NAME FROM SYSOBJECTS WHERE XTYPE='U' AND STATUS >= 0 ORDER BY NAME`)
result, err = d.db.DoGetAll(link, `SELECT NAME FROM SYSOBJECTS WHERE XTYPE='U' AND STATUS >= 0 ORDER BY NAME`)
if err != nil {
return
}
@ -209,7 +209,7 @@ func (d *DriverMssql) TableFields(table string, schema ...string) (fields map[st
if gstr.Contains(table, " ") {
return nil, gerror.New("function TableFields supports only single table operations")
}
checkSchema := d.DB.GetSchema()
checkSchema := d.db.GetSchema()
if len(schema) > 0 && schema[0] != "" {
checkSchema = schema[0]
}
@ -220,7 +220,7 @@ func (d *DriverMssql) TableFields(table string, schema ...string) (fields map[st
result Result
link *sql.DB
)
link, err = d.DB.GetSlave(checkSchema)
link, err = d.db.GetSlave(checkSchema)
if err != nil {
return nil, err
}
@ -255,7 +255,7 @@ ORDER BY a.id,a.colorder`,
strings.ToUpper(table),
)
structureSql, _ = gregex.ReplaceString(`[\n\r\s]+`, " ", gstr.Trim(structureSql))
result, err = d.DB.DoGetAll(link, structureSql)
result, err = d.db.DoGetAll(link, structureSql)
if err != nil {
return nil, err
}

View File

@ -83,11 +83,11 @@ func (d *DriverMysql) HandleSqlBeforeCommit(link Link, sql string, args []interf
// It's mainly used in cli tool chain for automatically generating the models.
func (d *DriverMysql) Tables(schema ...string) (tables []string, err error) {
var result Result
link, err := d.DB.GetSlave(schema...)
link, err := d.db.GetSlave(schema...)
if err != nil {
return nil, err
}
result, err = d.DB.DoGetAll(link, `SHOW TABLES`)
result, err = d.db.DoGetAll(link, `SHOW TABLES`)
if err != nil {
return
}
@ -125,13 +125,13 @@ func (d *DriverMysql) TableFields(table string, schema ...string) (fields map[st
result Result
link *sql.DB
)
link, err = d.DB.GetSlave(checkSchema)
link, err = d.db.GetSlave(checkSchema)
if err != nil {
return nil, err
}
result, err = d.DB.DoGetAll(
result, err = d.db.DoGetAll(
link,
fmt.Sprintf(`SHOW FULL COLUMNS FROM %s`, d.DB.QuoteWord(table)),
fmt.Sprintf(`SHOW FULL COLUMNS FROM %s`, d.db.QuoteWord(table)),
)
if err != nil {
return nil, err

View File

@ -49,7 +49,6 @@ func (d *DriverOracle) Open(config *ConfigNode) (*sql.DB, error) {
if config.LinkInfo != "" {
source = config.LinkInfo
} else {
// 账号/密码@地址:端口/数据库名称
source = fmt.Sprintf(
"%s/%s@%s:%s/%s",
config.User, config.Pass, config.Host, config.Port, config.Name,
@ -164,10 +163,10 @@ func (d *DriverOracle) parseSql(sql string) string {
// Tables retrieves and returns the tables of current schema.
// It's mainly used in cli tool chain for automatically generating the models.
// Note that it ignores the parameter <schema> in oracle database, as it is not necessary.
// Note that it ignores the parameter `schema` in oracle database, as it is not necessary.
func (d *DriverOracle) Tables(schema ...string) (tables []string, err error) {
var result Result
result, err = d.DB.DoGetAll(nil, "SELECT TABLE_NAME FROM USER_TABLES ORDER BY TABLE_NAME")
result, err = d.db.DoGetAll(nil, "SELECT TABLE_NAME FROM USER_TABLES ORDER BY TABLE_NAME")
if err != nil {
return
}
@ -186,7 +185,7 @@ func (d *DriverOracle) TableFields(table string, schema ...string) (fields map[s
if gstr.Contains(table, " ") {
return nil, gerror.New("function TableFields supports only single table operations")
}
checkSchema := d.DB.GetSchema()
checkSchema := d.db.GetSchema()
if len(schema) > 0 && schema[0] != "" {
checkSchema = schema[0]
}
@ -205,7 +204,7 @@ FROM USER_TAB_COLUMNS WHERE TABLE_NAME = '%s' ORDER BY COLUMN_ID`,
strings.ToUpper(table),
)
structureSql, _ = gregex.ReplaceString(`[\n\r\s]+`, " ", gstr.Trim(structureSql))
result, err = d.DB.GetAll(structureSql)
result, err = d.db.GetAll(structureSql)
if err != nil {
return nil, err
}
@ -231,7 +230,7 @@ func (d *DriverOracle) getTableUniqueIndex(table string) (fields map[string]map[
"table_unique_index_"+table,
func() (interface{}, error) {
res := (Result)(nil)
res, err = d.DB.GetAll(fmt.Sprintf(`
res, err = d.db.GetAll(fmt.Sprintf(`
SELECT INDEX_NAME,COLUMN_NAME,CHAR_LENGTH FROM USER_IND_COLUMNS
WHERE TABLE_NAME = '%s'
AND INDEX_NAME IN(SELECT INDEX_NAME FROM USER_INDEXES WHERE TABLE_NAME='%s' AND UNIQUENESS='UNIQUE')
@ -268,7 +267,7 @@ func (d *DriverOracle) DoInsert(link Link, table string, data interface{}, optio
}
switch kind {
case reflect.Slice, reflect.Array:
return d.DB.DoBatchInsert(link, table, data, option, batch...)
return d.db.DoBatchInsert(link, table, data, option, batch...)
case reflect.Map:
fallthrough
case reflect.Struct:
@ -303,7 +302,7 @@ func (d *DriverOracle) DoInsert(link Link, table string, data interface{}, optio
onStr = make([]string, 0)
updateStr = make([]string, 0)
)
charL, charR := d.DB.GetChars()
charL, charR := d.db.GetChars()
for k, v := range dataMap {
k = strings.ToUpper(k)
@ -327,7 +326,7 @@ func (d *DriverOracle) DoInsert(link Link, table string, data interface{}, optio
}
if link == nil {
if link, err = d.DB.Master(); err != nil {
if link, err = d.db.Master(); err != nil {
return nil, err
}
}
@ -342,10 +341,10 @@ func (d *DriverOracle) DoInsert(link Link, table string, data interface{}, optio
table, tableAlias1, strings.Join(subSqlStr, ","), tableAlias2,
strings.Join(onStr, "AND"), strings.Join(updateStr, ","), strings.Join(fields, ","), strings.Join(values, ","),
)
return d.DB.DoExec(link, tmp, params...)
return d.db.DoExec(link, tmp, params...)
case insertOptionIgnore:
return d.DB.DoExec(link,
return d.db.DoExec(link,
fmt.Sprintf(
"INSERT /*+ IGNORE_ROW_ON_DUPKEY_INDEX(%s(%s)) */ INTO %s(%s) VALUES(%s)",
table, strings.Join(indexes, ","), table, strings.Join(fields, ","), strings.Join(values, ","),
@ -354,7 +353,7 @@ func (d *DriverOracle) DoInsert(link Link, table string, data interface{}, optio
}
}
return d.DB.DoExec(
return d.db.DoExec(
link,
fmt.Sprintf(
"INSERT INTO %s(%s) VALUES(%s)",
@ -406,7 +405,7 @@ func (d *DriverOracle) DoBatchInsert(link Link, table string, list interface{},
return result, gerror.New("empty data list")
}
if link == nil {
if link, err = d.DB.Master(); err != nil {
if link, err = d.db.Master(); err != nil {
return
}
}
@ -418,13 +417,13 @@ func (d *DriverOracle) DoBatchInsert(link Link, table string, list interface{},
}
var (
batchResult = new(SqlResult)
charL, charR = d.DB.GetChars()
charL, charR = d.db.GetChars()
keyStr = charL + strings.Join(keys, charL+","+charR) + charR
valueHolderStr = strings.Join(holders, ",")
)
if option != insertOptionDefault {
for _, v := range listMap {
r, err := d.DB.DoInsert(link, table, v, option, 1)
r, err := d.db.DoInsert(link, table, v, option, 1)
if err != nil {
return r, err
}
@ -452,7 +451,7 @@ func (d *DriverOracle) DoBatchInsert(link Link, table string, list interface{},
values = append(values, valueHolderStr)
intoStr = append(intoStr, fmt.Sprintf(" INTO %s(%s) VALUES(%s) ", table, keyStr, valueHolderStr))
if len(intoStr) == batchNum {
r, err := d.DB.DoExec(link, fmt.Sprintf("INSERT ALL %s SELECT * FROM DUAL", strings.Join(intoStr, " ")), params...)
r, err := d.db.DoExec(link, fmt.Sprintf("INSERT ALL %s SELECT * FROM DUAL", strings.Join(intoStr, " ")), params...)
if err != nil {
return r, err
}
@ -468,7 +467,7 @@ func (d *DriverOracle) DoBatchInsert(link Link, table string, list interface{},
}
// The leftover data.
if len(intoStr) > 0 {
r, err := d.DB.DoExec(link, fmt.Sprintf("INSERT ALL %s SELECT * FROM DUAL", strings.Join(intoStr, " ")), params...)
r, err := d.db.DoExec(link, fmt.Sprintf("INSERT ALL %s SELECT * FROM DUAL", strings.Join(intoStr, " ")), params...)
if err != nil {
return r, err
}

View File

@ -90,7 +90,7 @@ func (d *DriverPgsql) HandleSqlBeforeCommit(link Link, sql string, args []interf
// It's mainly used in cli tool chain for automatically generating the models.
func (d *DriverPgsql) Tables(schema ...string) (tables []string, err error) {
var result Result
link, err := d.DB.GetSlave(schema...)
link, err := d.db.GetSlave(schema...)
if err != nil {
return nil, err
}
@ -98,7 +98,7 @@ func (d *DriverPgsql) Tables(schema ...string) (tables []string, err error) {
if len(schema) > 0 && schema[0] != "" {
query = fmt.Sprintf("SELECT TABLENAME FROM PG_TABLES WHERE SCHEMANAME = '%s' ORDER BY TABLENAME", schema[0])
}
result, err = d.DB.DoGetAll(link, query)
result, err = d.db.DoGetAll(link, query)
if err != nil {
return
}
@ -118,7 +118,7 @@ func (d *DriverPgsql) TableFields(table string, schema ...string) (fields map[st
return nil, gerror.New("function TableFields supports only single table operations")
}
table, _ = gregex.ReplaceString("\"", "", table)
checkSchema := d.DB.GetSchema()
checkSchema := d.db.GetSchema()
if len(schema) > 0 && schema[0] != "" {
checkSchema = schema[0]
}
@ -129,7 +129,7 @@ func (d *DriverPgsql) TableFields(table string, schema ...string) (fields map[st
result Result
link *sql.DB
)
link, err = d.DB.GetSlave(checkSchema)
link, err = d.db.GetSlave(checkSchema)
if err != nil {
return nil, err
}
@ -141,7 +141,7 @@ ORDER BY a.attnum`,
strings.ToLower(table),
)
structureSql, _ = gregex.ReplaceString(`[\n\r\s]+`, " ", gstr.Trim(structureSql))
result, err = d.DB.DoGetAll(link, structureSql)
result, err = d.db.DoGetAll(link, structureSql)
if err != nil {
return nil, err
}

View File

@ -75,12 +75,12 @@ func (d *DriverSqlite) HandleSqlBeforeCommit(link Link, sql string, args []inter
// It's mainly used in cli tool chain for automatically generating the models.
func (d *DriverSqlite) Tables(schema ...string) (tables []string, err error) {
var result Result
link, err := d.DB.GetSlave(schema...)
link, err := d.db.GetSlave(schema...)
if err != nil {
return nil, err
}
result, err = d.DB.DoGetAll(link, `SELECT NAME FROM SQLITE_MASTER WHERE TYPE='table' ORDER BY NAME`)
result, err = d.db.DoGetAll(link, `SELECT NAME FROM SQLITE_MASTER WHERE TYPE='table' ORDER BY NAME`)
if err != nil {
return
}
@ -99,7 +99,7 @@ func (d *DriverSqlite) TableFields(table string, schema ...string) (fields map[s
if gstr.Contains(table, " ") {
return nil, gerror.New("function TableFields supports only single table operations")
}
checkSchema := d.DB.GetSchema()
checkSchema := d.db.GetSchema()
if len(schema) > 0 && schema[0] != "" {
checkSchema = schema[0]
}
@ -110,11 +110,11 @@ func (d *DriverSqlite) TableFields(table string, schema ...string) (fields map[s
result Result
link *sql.DB
)
link, err = d.DB.GetSlave(checkSchema)
link, err = d.db.GetSlave(checkSchema)
if err != nil {
return nil, err
}
result, err = d.DB.DoGetAll(link, fmt.Sprintf(`PRAGMA TABLE_INFO(%s)`, table))
result, err = d.db.DoGetAll(link, fmt.Sprintf(`PRAGMA TABLE_INFO(%s)`, table))
if err != nil {
return nil, err
}

View File

@ -54,10 +54,11 @@ type apiTableName interface {
}
const (
OrmTagForStruct = "orm"
OrmTagForUnique = "unique"
OrmTagForPrimary = "primary"
metaDataNameForTable = "table"
OrmTagForStruct = "orm"
OrmTagForUnique = "unique"
OrmTagForPrimary = "primary"
OrmTagForTable = "table"
OrmTagForWith = "with"
)
var (
@ -68,50 +69,62 @@ var (
structTagPriority = append([]string{OrmTagForStruct}, gconv.StructTagPriority...)
)
// getTableNameFromObject retrieves and returns the table name from struct object.
func getTableNameFromObject(object interface{}) string {
// getTableNameFromOrmTag retrieves and returns the table name from struct object.
func getTableNameFromOrmTag(object interface{}) string {
var tableName string
// Use the interface value.
if r, ok := object.(apiTableName); ok {
// Use the interface value.
return r.TableName()
} else if table := gmeta.Get(object, metaDataNameForTable); !table.IsEmpty() {
// User meta data tag "table".
return table.String()
} else {
// Use the struct name of snake case.
tableName = r.TableName()
}
// User meta data tag "orm".
if tableName == "" {
if ormTag := gmeta.Get(object, OrmTagForStruct); !ormTag.IsEmpty() {
match, _ := gregex.MatchString(
fmt.Sprintf(`%s\s*:\s*([^,]+)`, OrmTagForTable),
ormTag.String(),
)
if len(match) > 1 {
tableName = match[1]
}
}
}
// Use the struct name of snake case.
if tableName == "" {
if t, err := structs.StructType(object); err != nil {
panic(err)
} else {
return gstr.CaseSnakeFirstUpper(
tableName = gstr.CaseSnakeFirstUpper(
gstr.StrEx(t.String(), "."),
)
}
}
return tableName
}
// ListItemValues retrieves and returns the elements of all item struct/map with key <key>.
// Note that the parameter <list> should be type of slice which contains elements of map or struct,
// ListItemValues retrieves and returns the elements of all item struct/map with key `key`.
// Note that the parameter `list` should be type of slice which contains elements of map or struct,
// or else it returns an empty slice.
//
// The parameter <list> supports types like:
// The parameter `list` supports types like:
// []map[string]interface{}
// []map[string]sub-map
// []struct
// []struct:sub-struct
// Note that the sub-map/sub-struct makes sense only if the optional parameter <subKey> is given.
// Note that the sub-map/sub-struct makes sense only if the optional parameter `subKey` is given.
// See gutil.ListItemValues.
func ListItemValues(list interface{}, key interface{}, subKey ...interface{}) (values []interface{}) {
return gutil.ListItemValues(list, key, subKey...)
}
// ListItemValuesUnique retrieves and returns the unique elements of all struct/map with key <key>.
// Note that the parameter <list> should be type of slice which contains elements of map or struct,
// ListItemValuesUnique retrieves and returns the unique elements of all struct/map with key `key`.
// Note that the parameter `list` should be type of slice which contains elements of map or struct,
// or else it returns an empty slice.
// See gutil.ListItemValuesUnique.
func ListItemValuesUnique(list interface{}, key string, subKey ...interface{}) []interface{} {
return gutil.ListItemValuesUnique(list, key, subKey...)
}
// GetInsertOperationByOption returns proper insert option with given parameter <option>.
// GetInsertOperationByOption returns proper insert option with given parameter `option`.
func GetInsertOperationByOption(option int) string {
var operator string
switch option {
@ -128,7 +141,7 @@ func GetInsertOperationByOption(option int) string {
// ConvertDataForTableRecord is a very important function, which does converting for any data that
// will be inserted into table as a record.
//
// The parameter <obj> should be type of *map/map/*struct/struct.
// The parameter `obj` should be type of *map/map/*struct/struct.
// It supports inherit struct definition for struct.
func ConvertDataForTableRecord(value interface{}) map[string]interface{} {
var (
@ -170,8 +183,8 @@ func ConvertDataForTableRecord(value interface{}) map[string]interface{} {
return data
}
// DataToMapDeep converts <value> to map type recursively.
// The parameter <value> should be type of *map/map/*struct/struct.
// DataToMapDeep converts `value` to map type recursively.
// The parameter `value` should be type of *map/map/*struct/struct.
// It supports inherit struct definition for struct.
func DataToMapDeep(value interface{}) map[string]interface{} {
if v, ok := value.(apiMapStrAny); ok {
@ -193,7 +206,7 @@ func DataToMapDeep(value interface{}) map[string]interface{} {
rvValue = rvValue.Elem()
rvKind = rvValue.Kind()
}
// If given <value> is not a struct, it uses gconv.Map for converting.
// If given `value` is not a struct, it uses gconv.Map for converting.
if rvKind != reflect.Struct {
return gconv.Map(value, structTagPriority...)
}
@ -302,8 +315,8 @@ func doHandleTableName(table, prefix, charLeft, charRight string) string {
return gstr.Join(array1, ",")
}
// doQuoteWord checks given string <s> a word, if true quotes it with <charLeft> and <charRight>
// and returns the quoted string; or else returns <s> without any change.
// doQuoteWord checks given string `s` a word, if true quotes it with `charLeft` and `charRight`
// and returns the quoted string; or else returns `s` without any change.
func doQuoteWord(s, charLeft, charRight string) string {
if quoteWordReg.MatchString(s) && !gstr.ContainsAny(s, charLeft+charRight) {
return charLeft + s + charRight
@ -350,13 +363,13 @@ func GetWhereConditionOfStruct(pointer interface{}) (where string, args []interf
for _, field := range tagField {
array = strings.Split(field.TagValue, ",")
if len(array) > 1 && gstr.InArray([]string{OrmTagForUnique, OrmTagForPrimary}, array[1]) {
return array[0], []interface{}{field.Value()}, nil
return array[0], []interface{}{field.Value.Interface()}, nil
}
if len(where) > 0 {
where += " AND "
}
where += field.TagValue + "=?"
args = append(args, field.Value())
args = append(args, field.Value.Interface())
}
return
}
@ -378,7 +391,7 @@ func GetPrimaryKey(pointer interface{}) (string, error) {
}
// GetPrimaryKeyCondition returns a new where condition by primary field name.
// The optional parameter <where> is like follows:
// The optional parameter `where` is like follows:
// 123 => primary=123
// []int{1, 2, 3} => primary IN(1,2,3)
// "john" => primary='john'
@ -387,8 +400,8 @@ func GetPrimaryKey(pointer interface{}) (string, error) {
// g.Map{"id": 1, "name": "john"} => id=1 AND name='john'
// etc.
//
// Note that it returns the given <where> parameter directly if the <primary> is empty
// or length of <where> > 1.
// Note that it returns the given `where` parameter directly if the `primary` is empty
// or length of `where` > 1.
func GetPrimaryKeyCondition(primary string, where ...interface{}) (newWhereCondition []interface{}) {
if len(where) == 0 {
return nil
@ -407,7 +420,7 @@ func GetPrimaryKeyCondition(primary string, where ...interface{}) (newWhereCondi
}
switch kind {
case reflect.Map, reflect.Struct:
// Ignore the parameter <primary>.
// Ignore the parameter `primary`.
break
default:
@ -454,7 +467,7 @@ func formatWhere(db DB, where interface{}, args []interface{}, omitEmpty bool) (
}
case reflect.Struct:
// If <where> struct implements apiIterator interface,
// If `where` struct implements apiIterator interface,
// it then uses its Iterate function to iterates its key-value pairs.
// For example, ListMap and TreeMap are ordered map,
// which implement apiIterator interface and are index-friendly for where conditions.
@ -517,7 +530,7 @@ func formatWhere(db DB, where interface{}, args []interface{}, omitEmpty bool) (
return handleArguments(newWhere, newArgs)
}
// formatWhereInterfaces formats <where> as []interface{}.
// formatWhereInterfaces formats `where` as []interface{}.
func formatWhereInterfaces(db DB, where []interface{}, buffer *bytes.Buffer, newArgs []interface{}) []interface{} {
if len(where) == 0 {
return newArgs
@ -771,8 +784,8 @@ func FormatSqlWithArgs(sql string, args []interface{}) string {
return newQuery
}
// convertMapToStruct maps the <data> to given struct.
// Note that the given parameter <pointer> should be a pointer to s struct.
// convertMapToStruct maps the `data` to given struct.
// Note that the given parameter `pointer` should be a pointer to s struct.
func convertMapToStruct(data map[string]interface{}, pointer interface{}) error {
tagNameMap, err := structs.TagMapName(pointer, []string{OrmTagForStruct})
if err != nil {

View File

@ -54,29 +54,26 @@ type whereHolder struct {
}
const (
// Deprecated, use OptionOmitEmpty instead.
OPTION_OMITEMPTY = 1
// Deprecated, use OptionAllowEmpty instead..
OPTION_ALLOWEMPTY = 2
OptionOmitEmpty = 1
OptionAllowEmpty = 2
linkTypeMaster = 1
linkTypeSlave = 2
whereHolderWhere = 1
whereHolderAnd = 2
whereHolderOr = 3
OPTION_OMITEMPTY = 1 // Deprecated, use OptionOmitEmpty instead.
OPTION_ALLOWEMPTY = 2 // Deprecated, use OptionAllowEmpty instead.
OptionOmitEmpty = 1
OptionAllowEmpty = 2
linkTypeMaster = 1
linkTypeSlave = 2
whereHolderWhere = 1
whereHolderAnd = 2
whereHolderOr = 3
)
// Table is alias of Core.Model.
// See Core.Model.
// Deprecated, use Model instead.
func (c *Core) Table(table ...string) *Model {
return c.DB.Model(table...)
return c.db.Model(table...)
}
// Model creates and returns a new ORM model from given schema.
// The parameter <table> can be more than one table names, and also alias name, like:
// The parameter `table` can be more than one table names, and also alias name, like:
// 1. Model names:
// Model("user")
// Model("user u")
@ -87,13 +84,13 @@ func (c *Core) Model(table ...string) *Model {
tables := ""
if len(table) > 1 {
tables = fmt.Sprintf(
`%s AS %s`, c.DB.QuotePrefixTableName(table[0]), c.DB.QuoteWord(table[1]),
`%s AS %s`, c.db.QuotePrefixTableName(table[0]), c.db.QuoteWord(table[1]),
)
} else if len(table) == 1 {
tables = c.DB.QuotePrefixTableName(table[0])
tables = c.db.QuotePrefixTableName(table[0])
}
return &Model{
db: c.DB,
db: c.db,
tablesInit: tables,
tables: tables,
fields: "*",
@ -103,6 +100,11 @@ func (c *Core) Model(table ...string) *Model {
}
}
// With creates and returns an ORM model based on meta data of given object.
func (c *Core) With(object interface{}) *Model {
return c.db.Model().With(object)
}
// Table is alias of tx.Model.
// Deprecated, use Model instead.
func (tx *TX) Table(table ...string) *Model {
@ -118,6 +120,12 @@ func (tx *TX) Model(table ...string) *Model {
return model
}
// With acts like Core.With except it operates on transaction.
// See Core.With.
func (tx *TX) With(object interface{}) *Model {
return tx.Model().With(object)
}
// Ctx sets the context for current operation.
func (m *Model) Ctx(ctx context.Context) *Model {
if ctx == nil {

View File

@ -14,13 +14,13 @@ import (
// if there's another same sql request, it just reads and returns the result from cache, it
// but not committed and executed into the database.
//
// If the parameter <duration> < 0, which means it clear the cache with given <name>.
// If the parameter <duration> = 0, which means it never expires.
// If the parameter <duration> > 0, which means it expires after <duration>.
// If the parameter `duration` < 0, which means it clear the cache with given `name`.
// If the parameter `duration` = 0, which means it never expires.
// If the parameter `duration` > 0, which means it expires after `duration`.
//
// The optional parameter <name> is used to bind a name to the cache, which means you can
// later control the cache like changing the <duration> or clearing the cache with specified
// <name>.
// The optional parameter `name` is used to bind a name to the cache, which means you can
// later control the cache like changing the `duration` or clearing the cache with specified
// `name`.
//
// Note that, the cache feature is disabled if the model is performing select statement
// on a transaction.

View File

@ -10,7 +10,7 @@ import (
"strings"
)
// Where sets the condition statement for the model. The parameter <where> can be type of
// Where sets the condition statement for the model. The parameter `where` can be type of
// string/map/gmap/slice/struct/*struct, etc. Note that, if it's called more than one times,
// multiple conditions will be joined into where statement using "AND".
// Eg:
@ -45,9 +45,9 @@ func (m *Model) Having(having interface{}, args ...interface{}) *Model {
return model
}
// WherePri does the same logic as Model.Where except that if the parameter <where>
// WherePri does the same logic as Model.Where except that if the parameter `where`
// is a single condition like int/string/float/slice, it treats the condition as the primary
// key value. That is, if primary key is "id" and given <where> parameter as "123", the
// key value. That is, if primary key is "id" and given `where` parameter as "123", the
// WherePri function treats the condition as "id=123", but Model.Where treats the condition
// as string "123".
func (m *Model) WherePri(where interface{}, args ...interface{}) *Model {
@ -115,7 +115,7 @@ func (m *Model) OrderBy(orderBy string) *Model {
}
// Limit sets the "LIMIT" statement for the model.
// The parameter <limit> can be either one or two number, if passed two number is passed,
// The parameter `limit` can be either one or two number, if passed two number is passed,
// it then sets "LIMIT limit[0],limit[1]" statement for the model, or else it sets "LIMIT limit[0]"
// statement.
func (m *Model) Limit(limit ...int) *Model {
@ -139,7 +139,7 @@ func (m *Model) Offset(offset int) *Model {
}
// Page sets the paging number for the model.
// The parameter <page> is started from 1 for paging.
// The parameter `page` is started from 1 for paging.
// Note that, it differs that the Limit function starts from 0 for "LIMIT" statement.
func (m *Model) Page(page, limit int) *Model {
model := m.getModel()

View File

@ -15,7 +15,7 @@ import (
)
// Delete does "DELETE FROM ... " statement for the model.
// The optional parameter <where> is the same as the parameter of Model.Where function,
// The optional parameter `where` is the same as the parameter of Model.Where function,
// see Model.Where.
func (m *Model) Delete(where ...interface{}) (result sql.Result, err error) {
if len(where) > 0 {

View File

@ -26,7 +26,7 @@ func (m *Model) Filter() *Model {
}
// Fields sets the operation fields of the model, multiple fields joined using char ','.
// The parameter <fieldNamesOrMapStruct> can be type of string/map/*map/struct/*struct.
// The parameter `fieldNamesOrMapStruct` can be type of string/map/*map/struct/*struct.
func (m *Model) Fields(fieldNamesOrMapStruct ...interface{}) *Model {
length := len(fieldNamesOrMapStruct)
if length == 0 {
@ -56,7 +56,7 @@ func (m *Model) Fields(fieldNamesOrMapStruct ...interface{}) *Model {
// FieldsEx sets the excluded operation fields of the model, multiple fields joined using char ','.
// Note that this function supports only single table operations.
// The parameter <fieldNamesOrMapStruct> can be type of string/map/*map/struct/*struct.
// The parameter `fieldNamesOrMapStruct` can be type of string/map/*map/struct/*struct.
func (m *Model) FieldsEx(fieldNamesOrMapStruct ...interface{}) *Model {
length := len(fieldNamesOrMapStruct)
if length == 0 {
@ -88,7 +88,7 @@ func (m *Model) FieldsStr(prefix ...string) string {
}
// FieldsStr 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: FieldsStr("u.").
func (m *Model) GetFieldsStr(prefix ...string) string {
prefixStr := ""
if len(prefix) > 0 {
@ -122,10 +122,10 @@ func (m *Model) FieldsExStr(fields string, prefix ...string) string {
return m.GetFieldsExStr(fields, prefix...)
}
// FieldsExStr retrieves and returns fields which are not in parameter <fields> from the table,
// FieldsExStr retrieves and returns fields which are not in parameter `fields` from the table,
// joined with char ','.
// The parameter <fields> specifies the fields that are excluded.
// The optional parameter <prefix> specifies the prefix for each field, eg: FieldsExStr("id", "u.").
// The parameter `fields` specifies the fields that are excluded.
// The optional parameter `prefix` specifies the prefix for each field, eg: FieldsExStr("id", "u.").
func (m *Model) GetFieldsExStr(fields string, prefix ...string) string {
prefixStr := ""
if len(prefix) > 0 {

View File

@ -24,7 +24,7 @@ func (m *Model) Batch(batch int) *Model {
}
// Data sets the operation data for the model.
// The parameter <data> can be type of string/map/gmap/slice/struct/*struct, etc.
// The parameter `data` can be type of string/map/gmap/slice/struct/*struct, etc.
// Note that, it uses shallow value copying for `data` if `data` is type of map/slice
// to avoid changing it inside function.
// Eg:
@ -100,7 +100,7 @@ func (m *Model) Data(data ...interface{}) *Model {
}
// Insert does "INSERT INTO ..." statement for the model.
// The optional parameter <data> is the same as the parameter of Model.Data function,
// The optional parameter `data` is the same as the parameter of Model.Data function,
// see Model.Data.
func (m *Model) Insert(data ...interface{}) (result sql.Result, err error) {
if len(data) > 0 {
@ -110,7 +110,7 @@ func (m *Model) Insert(data ...interface{}) (result sql.Result, err error) {
}
// InsertIgnore does "INSERT IGNORE INTO ..." statement for the model.
// The optional parameter <data> is the same as the parameter of Model.Data function,
// The optional parameter `data` is the same as the parameter of Model.Data function,
// see Model.Data.
func (m *Model) InsertIgnore(data ...interface{}) (result sql.Result, err error) {
if len(data) > 0 {
@ -120,7 +120,7 @@ func (m *Model) InsertIgnore(data ...interface{}) (result sql.Result, err error)
}
// Replace does "REPLACE INTO ..." statement for the model.
// The optional parameter <data> is the same as the parameter of Model.Data function,
// The optional parameter `data` is the same as the parameter of Model.Data function,
// see Model.Data.
func (m *Model) Replace(data ...interface{}) (result sql.Result, err error) {
if len(data) > 0 {
@ -130,7 +130,7 @@ func (m *Model) Replace(data ...interface{}) (result sql.Result, err error) {
}
// Save does "INSERT INTO ... ON DUPLICATE KEY UPDATE..." statement for the model.
// The optional parameter <data> is the same as the parameter of Model.Data function,
// The optional parameter `data` is the same as the parameter of Model.Data function,
// see Model.Data.
//
// It updates the record if there's primary or unique index in the saving data,

View File

@ -23,7 +23,7 @@ func isSubQuery(s string) bool {
}
// LeftJoin does "LEFT JOIN ... ON ..." statement on the model.
// The parameter <table> can be joined table and its joined condition,
// The parameter `table` can be joined table and its joined condition,
// and also with its alias name, like:
// Table("user").LeftJoin("user_detail", "user_detail.uid=user.uid")
// Table("user", "u").LeftJoin("user_detail", "ud", "ud.uid=u.uid")
@ -33,7 +33,7 @@ func (m *Model) LeftJoin(table ...string) *Model {
}
// RightJoin does "RIGHT JOIN ... ON ..." statement on the model.
// The parameter <table> can be joined table and its joined condition,
// The parameter `table` can be joined table and its joined condition,
// and also with its alias name, like:
// Table("user").RightJoin("user_detail", "user_detail.uid=user.uid")
// Table("user", "u").RightJoin("user_detail", "ud", "ud.uid=u.uid")
@ -43,7 +43,7 @@ func (m *Model) RightJoin(table ...string) *Model {
}
// InnerJoin does "INNER JOIN ... ON ..." statement on the model.
// The parameter <table> can be joined table and its joined condition,
// The parameter `table` can be joined table and its joined condition,
// and also with its alias name, like:
// Table("user").InnerJoin("user_detail", "user_detail.uid=user.uid")
// Table("user", "u").InnerJoin("user_detail", "ud", "ud.uid=u.uid")
@ -53,7 +53,7 @@ func (m *Model) InnerJoin(table ...string) *Model {
}
// doJoin does "LEFT/RIGHT/INNER JOIN ... ON ..." statement on the model.
// The parameter <table> can be joined table and its joined condition,
// The parameter `table` can be joined table and its joined condition,
// and also with its alias name, like:
// Table("user").InnerJoin("user_detail", "user_detail.uid=user.uid")
// Table("user", "u").InnerJoin("user_detail", "ud", "ud.uid=u.uid")

View File

@ -28,7 +28,7 @@ func (m *Model) Select(where ...interface{}) (Result, error) {
// It retrieves the records from table and returns the result as slice type.
// It returns nil if there's no record retrieved with the given conditions from table.
//
// The optional parameter <where> is the same as the parameter of Model.Where function,
// The optional parameter `where` is the same as the parameter of Model.Where function,
// see Model.Where.
func (m *Model) All(where ...interface{}) (Result, error) {
return m.doGetAll(false, where...)
@ -38,8 +38,8 @@ func (m *Model) All(where ...interface{}) (Result, error) {
// It retrieves the records from table and returns the result as slice type.
// It returns nil if there's no record retrieved with the given conditions from table.
//
// The parameter <limit1> specifies whether limits querying only one record if m.limit is not set.
// The optional parameter <where> is the same as the parameter of Model.Where function,
// The parameter `limit1` specifies whether limits querying only one record if m.limit is not set.
// The optional parameter `where` is the same as the parameter of Model.Where function,
// see Model.Where.
func (m *Model) doGetAll(limit1 bool, where ...interface{}) (Result, error) {
if len(where) > 0 {
@ -139,7 +139,7 @@ func (m *Model) Chunk(limit int, callback func(result Result, err error) bool) {
// One retrieves one record from table and returns the result as map type.
// It returns nil if there's no record retrieved with the given conditions from table.
//
// The optional parameter <where> is the same as the parameter of Model.Where function,
// The optional parameter `where` is the same as the parameter of Model.Where function,
// see Model.Where.
func (m *Model) One(where ...interface{}) (Record, error) {
if len(where) > 0 {
@ -158,7 +158,7 @@ func (m *Model) One(where ...interface{}) (Record, error) {
// Value retrieves a specified record value from table and returns the result as interface type.
// It returns nil if there's no record found with the given conditions from table.
//
// If the optional parameter <fieldsAndWhere> is given, the fieldsAndWhere[0] is the selected fields
// If the optional parameter `fieldsAndWhere` is given, the fieldsAndWhere[0] is the selected fields
// and fieldsAndWhere[1:] is treated as where condition fields.
// Also see Model.Fields and Model.Where functions.
func (m *Model) Value(fieldsAndWhere ...interface{}) (Value, error) {
@ -184,7 +184,7 @@ func (m *Model) Value(fieldsAndWhere ...interface{}) (Value, error) {
// Array queries and returns data values as slice from database.
// Note that if there're multiple columns in the result, it returns just one column values randomly.
//
// If the optional parameter <fieldsAndWhere> is given, the fieldsAndWhere[0] is the selected fields
// If the optional parameter `fieldsAndWhere` is given, the fieldsAndWhere[0] is the selected fields
// and fieldsAndWhere[1:] is treated as where condition fields.
// Also see Model.Fields and Model.Where functions.
func (m *Model) Array(fieldsAndWhere ...interface{}) ([]Value, error) {
@ -205,14 +205,14 @@ func (m *Model) Array(fieldsAndWhere ...interface{}) ([]Value, error) {
}
// 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,
// The parameter `pointer` should be type of *struct/**struct. If type **struct is given,
// it can create the struct internally during converting.
//
// The optional parameter <where> is the same as the parameter of Model.Where function,
// 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.
// from table and `pointer` is not nil.
//
// Eg:
// user := new(User)
@ -225,18 +225,21 @@ func (m *Model) Struct(pointer interface{}, where ...interface{}) error {
if err != nil {
return err
}
return one.Struct(pointer)
if err = one.Struct(pointer); err != nil {
return err
}
return m.doWithScan(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
// The parameter `pointer` should be type of *[]struct/*[]*struct. It can create and fill the struct
// slice internally during converting.
//
// The optional parameter <where> is the same as the parameter of Model.Where function,
// 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.
// from table and `pointer` is not empty.
//
// Eg:
// users := ([]User)(nil)
@ -252,11 +255,11 @@ func (m *Model) Structs(pointer interface{}, where ...interface{}) error {
return all.Structs(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.
// 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.
//
// The optional parameter <where> is the same as the parameter of Model.Where function,
// 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
@ -275,21 +278,20 @@ 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 {
t := reflect.TypeOf(pointer)
k := t.Kind()
if k != reflect.Ptr {
return fmt.Errorf("params should be type of pointer, but got: %v", k)
var reflectType reflect.Type
if v, ok := pointer.(reflect.Value); ok {
reflectType = v.Type()
} else {
reflectType = reflect.TypeOf(pointer)
}
switch t.Elem().Kind() {
case reflect.Array, reflect.Slice:
if gstr.Contains(reflectType.String(), "[]") {
return m.Structs(pointer, where...)
default:
return m.Struct(pointer, where...)
}
return m.Struct(pointer, where...)
}
// ScanList converts <r> to struct slice which contains other complex struct attributes.
// Note that the parameter <listPointer> should be type of *[]struct/*[]*struct.
// ScanList converts `r` to struct slice which contains other complex struct attributes.
// Note that the parameter `listPointer` should be type of *[]struct/*[]*struct.
// Usage example:
//
// type Entity struct {
@ -307,7 +309,7 @@ func (m *Model) Scan(pointer interface{}, where ...interface{}) error {
// The parameters "User"/"UserDetail"/"UserScores" in the example codes specify the target attribute struct
// that current result will be bound to.
// The "uid" in the example codes is the table field name of the result, and the "Uid" is the relational
// struct attribute name. It automatically calculates the HasOne/HasMany relationship with given <relation>
// struct attribute name. It automatically calculates the HasOne/HasMany relationship with given `relation`
// 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) {
@ -319,7 +321,7 @@ func (m *Model) ScanList(listPointer interface{}, attributeName string, relation
}
// Count does "SELECT COUNT(x) FROM ..." statement for the model.
// The optional parameter <where> is the same as the parameter of Model.Where function,
// The optional parameter `where` is the same as the parameter of Model.Where function,
// see Model.Where.
func (m *Model) Count(where ...interface{}) (int, error) {
if len(where) > 0 {

View File

@ -19,7 +19,7 @@ import (
// Update does "UPDATE ... " statement for the model.
//
// If the optional parameter <dataAndWhere> is given, the dataAndWhere[0] is the updated data field,
// If the optional parameter `dataAndWhere` is given, the dataAndWhere[0] is the updated data field,
// and dataAndWhere[1:] is treated as where condition fields.
// Also see Model.Data and Model.Where functions.
func (m *Model) Update(dataAndWhere ...interface{}) (result sql.Result, err error) {

View File

@ -18,7 +18,7 @@ import (
"time"
)
// getModel creates and returns a cloned model of current model if <safe> is true, or else it returns
// getModel creates and returns a cloned model of current model if `safe` is true, or else it returns
// the current model.
func (m *Model) getModel() *Model {
if !m.safe {
@ -147,8 +147,8 @@ func (m *Model) doMappingAndFilterForInsertOrUpdateDataMap(data Map, allowOmitEm
return data, nil
}
// getLink returns the underlying database link object with configured <linkType> attribute.
// The parameter <master> specifies whether using the master node if master-slave configured.
// getLink returns the underlying database link object with configured `linkType` attribute.
// The parameter `master` specifies whether using the master node if master-slave configured.
func (m *Model) getLink(master bool) Link {
if m.tx != nil {
return m.tx.tx
@ -196,9 +196,9 @@ func (m *Model) getPrimaryKey() string {
}
// formatCondition formats where arguments of the model and returns a new condition sql and its arguments.
// Note that this function does not change any attribute value of the <m>.
// Note that this function does not change any attribute value of the `m`.
//
// The parameter <limit1> specifies whether limits querying only one record if m.limit is not set.
// The parameter `limit1` specifies whether limits querying only one record if m.limit is not set.
func (m *Model) formatCondition(limit1 bool, isCountStatement bool) (conditionWhere string, conditionExtra string, conditionArgs []interface{}) {
if len(m.whereHolder) > 0 {
for _, v := range m.whereHolder {
@ -302,7 +302,7 @@ func (m *Model) formatCondition(limit1 bool, isCountStatement bool) (conditionWh
return
}
// mergeArguments creates and returns new arguments by merging <m.extraArgs> and given <args>.
// mergeArguments creates and returns new arguments by merging <m.extraArgs> and given `args`.
func (m *Model) mergeArguments(args []interface{}) []interface{} {
if len(m.extraArgs) > 0 {
newArgs := make([]interface{}, len(m.extraArgs)+len(args))

View File

@ -6,12 +6,90 @@
package gdb
import (
"fmt"
"github.com/gogf/gf/errors/gerror"
"github.com/gogf/gf/internal/structs"
"github.com/gogf/gf/internal/utils"
"github.com/gogf/gf/text/gregex"
"github.com/gogf/gf/text/gstr"
)
func (m *Model) With(structAttrPointer interface{}) *Model {
model := m.getModel()
if m.tables == "" {
m.tables = getTableNameFromObject(structAttrPointer)
m.tables = m.db.QuotePrefixTableName(getTableNameFromOrmTag(structAttrPointer))
return model
}
model.withArray = append(model.withArray, structAttrPointer)
return model
}
func (m *Model) doWithScan(pointer interface{}) error {
if len(m.withArray) == 0 {
return nil
}
fieldMap, err := structs.FieldMap(pointer, nil)
if err != nil {
return err
}
for _, withItem := range m.withArray {
withItemReflectValueType, err := structs.StructType(withItem)
if err != nil {
return err
}
withItemReflectValueTypeStr := gstr.TrimAll(withItemReflectValueType.String(), "*[]")
for _, fieldValue := range fieldMap {
var (
fieldType = fieldValue.Type()
fieldTypeStr = gstr.TrimAll(fieldType.String(), "*[]")
)
if gstr.Compare(fieldTypeStr, withItemReflectValueTypeStr) == 0 {
var (
withTag string
ormTag = fieldValue.Tag(OrmTagForStruct)
match, _ = gregex.MatchString(
fmt.Sprintf(`%s\s*:\s*([^,]+)`, OrmTagForWith),
ormTag,
)
)
if len(match) > 1 {
withTag = match[1]
}
if withTag == "" {
continue
}
array := gstr.SplitAndTrim(withTag, "=")
if len(array) != 2 {
return gerror.Newf(`invalid with tag "%s"`, withTag)
}
var (
relatedFieldName = array[0]
relatedAttrName = array[1]
relatedFieldValue interface{}
)
// Find the value of related attribute from `pointer`.
for attributeName, attributeValue := range fieldMap {
if utils.EqualFoldWithoutChars(attributeName, relatedAttrName) {
relatedFieldValue = attributeValue.Value.Interface()
break
}
}
if relatedFieldValue == nil {
return gerror.Newf(
`cannot find the related value for attribute name "%s" of with tag "%s"`,
relatedAttrName, withTag,
)
}
err = m.db.With(fieldValue.Value).
Fields(withItemReflectValueType.FieldKeys()).
Where(relatedFieldName, relatedFieldValue).
Scan(fieldValue.Value)
if err != nil {
return err
}
}
}
}
return nil
}

View File

@ -16,7 +16,7 @@ type Schema struct {
// Schema creates and returns a schema.
func (c *Core) Schema(schema string) *Schema {
return &Schema{
db: c.DB,
db: c.db,
schema: schema,
}
}
@ -31,7 +31,7 @@ func (tx *TX) Schema(schema string) *Schema {
}
// Table creates and returns a new ORM model.
// The parameter <tables> can be more than one table names, like :
// The parameter `tables` can be more than one table names, like :
// "user", "user u", "user, user_detail", "user u, user_detail ud"
func (s *Schema) Table(table string) *Model {
var m *Model

View File

@ -69,11 +69,11 @@ func (s *Stmt) doStmtCommit(stmtType string, ctx context.Context, args ...interf
Error: err,
Start: timestampMilli1,
End: timestampMilli2,
Group: s.core.DB.GetGroup(),
Group: s.core.db.GetGroup(),
}
)
s.core.addSqlToTracing(ctx, sqlObj)
if s.core.DB.GetDebug() {
if s.core.db.GetDebug() {
s.core.writeSqlToLogger(sqlObj)
}
return result, err

View File

@ -75,7 +75,7 @@ func (tx *TX) GetOne(sql string, args ...interface{}) (Record, error) {
}
// GetStruct queries one record from database and converts it to given struct.
// The parameter <pointer> should be a pointer to struct.
// The parameter `pointer` should be a pointer to struct.
func (tx *TX) GetStruct(obj interface{}, sql string, args ...interface{}) error {
one, err := tx.GetOne(sql, args...)
if err != nil {
@ -85,7 +85,7 @@ func (tx *TX) GetStruct(obj interface{}, sql string, args ...interface{}) error
}
// GetStructs queries records from database and converts them to given struct.
// The parameter <pointer> should be type of struct slice: []struct/[]*struct.
// The parameter `pointer` should be type of struct slice: []struct/[]*struct.
func (tx *TX) GetStructs(objPointerSlice interface{}, sql string, args ...interface{}) error {
all, err := tx.GetAll(sql, args...)
if err != nil {
@ -97,8 +97,8 @@ func (tx *TX) GetStructs(objPointerSlice interface{}, sql string, args ...interf
// GetScan queries one or more records from database and converts them to given struct or
// struct array.
//
// If parameter <pointer> is type of struct pointer, it calls GetStruct internally for
// the conversion. If parameter <pointer> is type of slice, it calls GetStructs internally
// If parameter `pointer` is type of struct pointer, it calls GetStruct internally for
// the conversion. If parameter `pointer` is type of slice, it calls GetStructs internally
// for conversion.
func (tx *TX) GetScan(objPointer interface{}, sql string, args ...interface{}) error {
t := reflect.TypeOf(objPointer)
@ -146,12 +146,12 @@ func (tx *TX) GetCount(sql string, args ...interface{}) (int, error) {
// Insert does "INSERT INTO ..." statement for the table.
// If there's already one unique record of the data in the table, it returns error.
//
// The parameter <data> can be type of map/gmap/struct/*struct/[]map/[]struct, etc.
// 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 <batch> specifies the batch operation count when given data is slice.
// The parameter `batch` specifies the batch operation count when given data is slice.
func (tx *TX) Insert(table string, data interface{}, batch ...int) (sql.Result, error) {
if len(batch) > 0 {
return tx.Model(table).Data(data).Batch(batch[0]).Insert()
@ -162,12 +162,12 @@ func (tx *TX) Insert(table string, data interface{}, batch ...int) (sql.Result,
// InsertIgnore does "INSERT IGNORE INTO ..." statement for the table.
// If there's already one unique record of the data in the table, it ignores the inserting.
//
// The parameter <data> can be type of map/gmap/struct/*struct/[]map/[]struct, etc.
// 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 <batch> specifies the batch operation count when given data is slice.
// The parameter `batch` specifies the batch operation count when given data is slice.
func (tx *TX) InsertIgnore(table string, data interface{}, batch ...int) (sql.Result, error) {
if len(batch) > 0 {
return tx.Model(table).Data(data).Batch(batch[0]).InsertIgnore()
@ -179,14 +179,14 @@ func (tx *TX) InsertIgnore(table string, data interface{}, batch ...int) (sql.Re
// If there's already one unique record of the data in the table, it deletes the record
// and inserts a new one.
//
// The parameter <data> can be type of map/gmap/struct/*struct/[]map/[]struct, etc.
// 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 <data> can be type of map/gmap/struct/*struct/[]map/[]struct, etc.
// The parameter `data` can be type of map/gmap/struct/*struct/[]map/[]struct, etc.
// If given data is type of slice, it then does batch replacing, and the optional parameter
// <batch> specifies the batch operation count.
// `batch` specifies the batch operation count.
func (tx *TX) Replace(table string, data interface{}, batch ...int) (sql.Result, error) {
if len(batch) > 0 {
return tx.Model(table).Data(data).Batch(batch[0]).Replace()
@ -198,13 +198,13 @@ func (tx *TX) Replace(table string, data interface{}, batch ...int) (sql.Result,
// It updates the record if there's primary or unique index in the saving data,
// or else it inserts a new record into the table.
//
// The parameter <data> can be type of map/gmap/struct/*struct/[]map/[]struct, etc.
// 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"})
//
// If given data is type of slice, it then does batch saving, and the optional parameter
// <batch> specifies the batch operation count.
// `batch` specifies the batch operation count.
func (tx *TX) Save(table string, data interface{}, batch ...int) (sql.Result, error) {
if len(batch) > 0 {
return tx.Model(table).Data(data).Batch(batch[0]).Save()
@ -213,7 +213,7 @@ func (tx *TX) Save(table string, data interface{}, batch ...int) (sql.Result, er
}
// BatchInsert batch inserts data.
// The parameter <list> must be type of slice of map or struct.
// The parameter `list` must be type of slice of map or struct.
func (tx *TX) BatchInsert(table string, list interface{}, batch ...int) (sql.Result, error) {
if len(batch) > 0 {
return tx.Model(table).Data(list).Batch(batch[0]).Insert()
@ -222,7 +222,7 @@ func (tx *TX) BatchInsert(table string, list interface{}, batch ...int) (sql.Res
}
// BatchInsert batch inserts data with ignore option.
// The parameter <list> must be type of slice of map or struct.
// The parameter `list` must be type of slice of map or struct.
func (tx *TX) BatchInsertIgnore(table string, list interface{}, batch ...int) (sql.Result, error) {
if len(batch) > 0 {
return tx.Model(table).Data(list).Batch(batch[0]).InsertIgnore()
@ -231,7 +231,7 @@ func (tx *TX) BatchInsertIgnore(table string, list interface{}, batch ...int) (s
}
// BatchReplace batch replaces data.
// The parameter <list> must be type of slice of map or struct.
// The parameter `list` must be type of slice of map or struct.
func (tx *TX) BatchReplace(table string, list interface{}, batch ...int) (sql.Result, error) {
if len(batch) > 0 {
return tx.Model(table).Data(list).Batch(batch[0]).Replace()
@ -240,7 +240,7 @@ func (tx *TX) BatchReplace(table string, list interface{}, batch ...int) (sql.Re
}
// BatchSave batch replaces data.
// The parameter <list> must be type of slice of map or struct.
// The parameter `list` must be type of slice of map or struct.
func (tx *TX) BatchSave(table string, list interface{}, batch ...int) (sql.Result, error) {
if len(batch) > 0 {
return tx.Model(table).Data(list).Batch(batch[0]).Save()
@ -250,11 +250,11 @@ func (tx *TX) BatchSave(table string, list interface{}, batch ...int) (sql.Resul
// Update does "UPDATE ... " statement for the table.
//
// The parameter <data> can be type of string/map/gmap/struct/*struct, etc.
// The parameter `data` can be type of string/map/gmap/struct/*struct, etc.
// Eg: "uid=10000", "uid", 10000, g.Map{"uid": 10000, "name":"john"}
//
// The parameter <condition> can be type of string/map/gmap/slice/struct/*struct, etc.
// It is commonly used with parameter <args>.
// The parameter `condition` can be type of string/map/gmap/slice/struct/*struct, etc.
// It is commonly used with parameter `args`.
// Eg:
// "uid=10000",
// "uid", 10000
@ -268,8 +268,8 @@ func (tx *TX) Update(table string, data interface{}, condition interface{}, args
// Delete does "DELETE FROM ... " statement for the table.
//
// The parameter <condition> can be type of string/map/gmap/slice/struct/*struct, etc.
// It is commonly used with parameter <args>.
// The parameter `condition` can be type of string/map/gmap/slice/struct/*struct, etc.
// It is commonly used with parameter `args`.
// Eg:
// "uid=10000",
// "uid", 10000

View File

@ -16,19 +16,19 @@ import (
"reflect"
)
// Json converts <r> to JSON format content.
// Json converts `r` to JSON format content.
func (r Record) Json() string {
content, _ := gparser.VarToJson(r.Map())
return gconv.UnsafeBytesToStr(content)
}
// Xml converts <r> to XML format content.
// Xml converts `r` to XML format content.
func (r Record) Xml(rootTag ...string) string {
content, _ := gparser.VarToXml(r.Map(), rootTag...)
return gconv.UnsafeBytesToStr(content)
}
// Map converts <r> to map[string]interface{}.
// Map converts `r` to map[string]interface{}.
func (r Record) Map() Map {
m := make(map[string]interface{})
for k, v := range r {
@ -37,15 +37,15 @@ func (r Record) Map() Map {
return m
}
// GMap converts <r> to a gmap.
// GMap converts `r` to a gmap.
func (r Record) GMap() *gmap.StrAnyMap {
return gmap.NewStrAnyMapFrom(r.Map())
}
// Struct converts <r> to a struct.
// Note that the parameter <pointer> should be type of *struct/**struct.
// Struct converts `r` to a struct.
// Note that the parameter `pointer` should be type of *struct/**struct.
//
// Note that it returns sql.ErrNoRows if <r> is empty.
// Note that it returns sql.ErrNoRows if `r` is empty.
func (r Record) Struct(pointer interface{}) error {
// If the record is empty, it returns error.
if r.IsEmpty() {
@ -76,7 +76,7 @@ func (r Record) Struct(pointer interface{}) error {
return convertMapToStruct(r.Map(), pointer)
}
// IsEmpty checks and returns whether <r> is empty.
// IsEmpty checks and returns whether `r` is empty.
func (r Record) IsEmpty() bool {
return len(r) == 0
}

View File

@ -16,7 +16,7 @@ import (
"github.com/gogf/gf/encoding/gparser"
)
// IsEmpty checks and returns whether <r> is empty.
// IsEmpty checks and returns whether `r` is empty.
func (r Result) IsEmpty() bool {
return r.Len() == 0
}
@ -32,7 +32,7 @@ func (r Result) Size() int {
}
// Chunk splits an Result into multiple Results,
// the size of each array is determined by <size>.
// the size of each array is determined by `size`.
// The last chunk may contain less than size elements.
func (r Result) Chunk(size int) []Result {
if size < 1 {
@ -52,19 +52,19 @@ func (r Result) Chunk(size int) []Result {
return n
}
// Json converts <r> to JSON format content.
// Json converts `r` to JSON format content.
func (r Result) Json() string {
content, _ := gparser.VarToJson(r.List())
return string(content)
}
// Xml converts <r> to XML format content.
// Xml converts `r` to XML format content.
func (r Result) Xml(rootTag ...string) string {
content, _ := gparser.VarToXml(r.List(), rootTag...)
return string(content)
}
// List converts <r> to a List.
// List converts `r` to a List.
func (r Result) List() List {
list := make(List, len(r))
for k, v := range r {
@ -74,7 +74,7 @@ func (r Result) List() List {
}
// Array retrieves and returns specified column values as slice.
// The parameter <field> is optional is the column field is only one.
// The parameter `field` is optional is the column field is only one.
func (r Result) Array(field ...string) []Value {
array := make([]Value, len(r))
if len(r) == 0 {
@ -95,7 +95,7 @@ func (r Result) Array(field ...string) []Value {
return array
}
// MapKeyValue converts <r> to a map[string]Value of which key is specified by <key>.
// MapKeyValue converts `r` to a map[string]Value of which key is specified by `key`.
// Note that the item value may be type of slice.
func (r Result) MapKeyValue(key string) map[string]Value {
var (
@ -123,7 +123,7 @@ func (r Result) MapKeyValue(key string) map[string]Value {
return m
}
// MapKeyStr converts <r> to a map[string]Map of which key is specified by <key>.
// MapKeyStr converts `r` to a map[string]Map of which key is specified by `key`.
func (r Result) MapKeyStr(key string) map[string]Map {
m := make(map[string]Map)
for _, item := range r {
@ -134,7 +134,7 @@ func (r Result) MapKeyStr(key string) map[string]Map {
return m
}
// MapKeyInt converts <r> to a map[int]Map of which key is specified by <key>.
// MapKeyInt converts `r` to a map[int]Map of which key is specified by `key`.
func (r Result) MapKeyInt(key string) map[int]Map {
m := make(map[int]Map)
for _, item := range r {
@ -145,7 +145,7 @@ func (r Result) MapKeyInt(key string) map[int]Map {
return m
}
// MapKeyUint converts <r> to a map[uint]Map of which key is specified by <key>.
// MapKeyUint converts `r` to a map[uint]Map of which key is specified by `key`.
func (r Result) MapKeyUint(key string) map[uint]Map {
m := make(map[uint]Map)
for _, item := range r {
@ -156,7 +156,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>.
// RecordKeyInt converts `r` to a map[int]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 {
@ -167,7 +167,7 @@ func (r Result) RecordKeyStr(key string) map[string]Record {
return m
}
// RecordKeyInt converts <r> to a map[int]Record of which key is specified by <key>.
// RecordKeyInt converts `r` to a map[int]Record of which key is specified by `key`.
func (r Result) RecordKeyInt(key string) map[int]Record {
m := make(map[int]Record)
for _, item := range r {
@ -178,7 +178,7 @@ func (r Result) RecordKeyInt(key string) map[int]Record {
return m
}
// RecordKeyUint converts <r> to a map[uint]Record of which key is specified by <key>.
// RecordKeyUint converts `r` to a map[uint]Record of which key is specified by `key`.
func (r Result) RecordKeyUint(key string) map[uint]Record {
m := make(map[uint]Record)
for _, item := range r {
@ -189,8 +189,8 @@ func (r Result) RecordKeyUint(key string) map[uint]Record {
return m
}
// Structs converts <r> to struct slice.
// Note that the parameter <pointer> should be type of *[]struct/*[]*struct.
// Structs converts `r` to struct slice.
// Note that the parameter `pointer` should be type of *[]struct/*[]*struct.
func (r Result) Structs(pointer interface{}) (err error) {
var (
reflectValue = reflect.ValueOf(pointer)

View File

@ -15,8 +15,8 @@ import (
"reflect"
)
// ScanList converts <r> to struct slice which contains other complex struct attributes.
// Note that the parameter <listPointer> should be type of *[]struct/*[]*struct.
// ScanList converts `r` to struct slice which contains other complex struct attributes.
// Note that the parameter `listPointer` should be type of *[]struct/*[]*struct.
// Usage example:
//
// type Entity struct {
@ -38,7 +38,7 @@ import (
// The "uid" in the example codes is the table field name of the result, and the "Uid" is the relational
// struct attribute name - not the attribute name of the bound to target. In the example codes, it's attribute
// name "Uid" of "User" of entity "Entity". It automatically calculates the HasOne/HasMany relationship with
// given <relation> parameter.
// given `relation` parameter.
//
// 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) {

View File

@ -16,9 +16,9 @@ import (
func Test_Table_Relation_With(t *testing.T) {
var (
tableUser = "user_with"
tableUserDetail = "user_detail_with"
tableUserScores = "user_scores_with"
tableUser = "user"
tableUserDetail = "user_detail"
tableUserScores = "user_scores"
)
if _, err := db.Exec(fmt.Sprintf(`
CREATE TABLE IF NOT EXISTS %s (
@ -55,20 +55,20 @@ PRIMARY KEY (id)
defer dropTable(tableUserScores)
type UserDetail struct {
gmeta.Meta `table:"user_detail_with"`
gmeta.Meta `orm:"table:user_detail"`
Uid int `json:"uid"`
Address string `json:"address"`
}
type UserScores struct {
gmeta.Meta `table:"user_scores_with"`
gmeta.Meta `orm:"table:user_scores"`
Id int `json:"id"`
Uid int `json:"uid"`
Score int `json:"score"`
}
type User struct {
gmeta.Meta `table:"user_with"`
gmeta.Meta `orm:"table:user"`
Id int `json:"id"`
Name string `json:"name"`
UserDetail *UserDetail `orm:"with:uid=id"`
@ -101,8 +101,16 @@ PRIMARY KEY (id)
}
gtest.C(t, func(t *gtest.T) {
var user *User
err := db.Model().With(&user).Where("id", 3).Scan(&user)
db.SetDebug(true)
err := db.With(User{}).
With(User{}.UserDetail).
With(User{}.UserScores).
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`)
})
}

View File

@ -6,51 +6,57 @@
package g
import (
"github.com/gogf/gf/container/gvar"
)
import "github.com/gogf/gf/container/gvar"
// Var is a universal variable interface, like generics.
type Var = gvar.Var
// Frequently-used map type alias.
type Map = map[string]interface{}
type MapAnyAny = map[interface{}]interface{}
type MapAnyStr = map[interface{}]string
type MapAnyInt = map[interface{}]int
type MapStrAny = map[string]interface{}
type MapStrStr = map[string]string
type MapStrInt = map[string]int
type MapIntAny = map[int]interface{}
type MapIntStr = map[int]string
type MapIntInt = map[int]int
type MapAnyBool = map[interface{}]bool
type MapStrBool = map[string]bool
type MapIntBool = map[int]bool
// Frequently-used map alias.
type (
Map = map[string]interface{}
MapAnyAny = map[interface{}]interface{}
MapAnyStr = map[interface{}]string
MapAnyInt = map[interface{}]int
MapStrAny = map[string]interface{}
MapStrStr = map[string]string
MapStrInt = map[string]int
MapIntAny = map[int]interface{}
MapIntStr = map[int]string
MapIntInt = map[int]int
MapAnyBool = map[interface{}]bool
MapStrBool = map[string]bool
MapIntBool = map[int]bool
)
// Frequently-used slice type alias.
type List = []Map
type ListAnyAny = []MapAnyAny
type ListAnyStr = []MapAnyStr
type ListAnyInt = []MapAnyInt
type ListStrAny = []MapStrAny
type ListStrStr = []MapStrStr
type ListStrInt = []MapStrInt
type ListIntAny = []MapIntAny
type ListIntStr = []MapIntStr
type ListIntInt = []MapIntInt
type ListAnyBool = []MapAnyBool
type ListStrBool = []MapStrBool
type ListIntBool = []MapIntBool
// Frequently-used slice alias.
type (
List = []Map
ListAnyAny = []MapAnyAny
ListAnyStr = []MapAnyStr
ListAnyInt = []MapAnyInt
ListStrAny = []MapStrAny
ListStrStr = []MapStrStr
ListStrInt = []MapStrInt
ListIntAny = []MapIntAny
ListIntStr = []MapIntStr
ListIntInt = []MapIntInt
ListAnyBool = []MapAnyBool
ListStrBool = []MapStrBool
ListIntBool = []MapIntBool
)
// Frequently-used slice type alias.
type Slice = []interface{}
type SliceAny = []interface{}
type SliceStr = []string
type SliceInt = []int
// Frequently-used slice alias.
type (
Slice = []interface{}
SliceAny = []interface{}
SliceStr = []string
SliceInt = []int
)
// Array is alias of Slice.
type Array = []interface{}
type ArrayAny = []interface{}
type ArrayStr = []string
type ArrayInt = []int
type (
Array = []interface{}
ArrayAny = []interface{}
ArrayStr = []string
ArrayInt = []int
)

View File

@ -80,13 +80,14 @@ func Log(name ...string) *glog.Logger {
return gins.Log(name...)
}
// Database returns an instance of database ORM object with specified configuration group name.
// Database is alias of DB.
// See DB.
// Deprecated, use DB instead.
func Database(name ...string) gdb.DB {
return gins.Database(name...)
}
// DB is alias of Database.
// See Database.
// DB returns an instance of database ORM object with specified configuration group name.
func DB(name ...string) gdb.DB {
return gins.Database(name...)
}
@ -97,7 +98,7 @@ func DB(name ...string) gdb.DB {
// "Table" is not proper for that purpose any more.
// Deprecated, use Model instead.
func Table(tables ...string) *gdb.Model {
return DB().Table(tables...)
return DB().Model(tables...)
}
// Model creates and returns a model based on configuration of default database group.
@ -105,6 +106,11 @@ func Model(tables ...string) *gdb.Model {
return DB().Model(tables...)
}
// With creates and returns an ORM model based on meta data of given object.
func With(object interface{}) *gdb.Model {
return DB().With(object)
}
// Redis returns an instance of redis client with specified configuration group name.
func Redis(name ...string) *gredis.Redis {
return gins.Redis(name...)

View File

@ -59,7 +59,7 @@ func Init(args ...string) {
}
}
// GetOpt returns the option value named <name>.
// GetOpt returns the option value named `name`.
func GetOpt(name string, def ...string) string {
Init()
if v, ok := defaultParsedOptions[name]; ok {
@ -77,14 +77,14 @@ func GetOptAll() map[string]string {
return defaultParsedOptions
}
// ContainsOpt checks whether option named <name> exist in the arguments.
// ContainsOpt checks whether option named `name` exist in the arguments.
func ContainsOpt(name string) bool {
Init()
_, ok := defaultParsedOptions[name]
return ok
}
// GetArg returns the argument at <index>.
// GetArg returns the argument at `index`.
func GetArg(index int, def ...string) string {
Init()
if index < len(defaultParsedArgs) {
@ -102,9 +102,9 @@ func GetArgAll() []string {
return defaultParsedArgs
}
// GetOptWithEnv returns the command line argument of the specified <key>.
// If the argument does not exist, then it returns the environment variable with specified <key>.
// It returns the default value <def> if none of them exists.
// GetOptWithEnv returns the command line argument of the specified `key`.
// If the argument does not exist, then it returns the environment variable with specified `key`.
// It returns the default value `def` if none of them exists.
//
// Fetching Rules:
// 1. Command line arguments are in lowercase format, eg: gf.<package name>.<variable name>;

View File

@ -26,8 +26,8 @@ type apiMapStrAny interface {
MapStrAny() map[string]interface{}
}
// IsEmpty checks whether given <value> empty.
// It returns true if <value> is in: 0, nil, false, "", len(slice/map/chan) == 0,
// IsEmpty checks whether given `value` empty.
// It returns true if `value` is in: 0, nil, false, "", len(slice/map/chan) == 0,
// or else it returns false.
func IsEmpty(value interface{}) bool {
if value == nil {
@ -151,9 +151,9 @@ func IsEmpty(value interface{}) bool {
return false
}
// IsNil checks whether given <value> is nil.
// Parameter <traceSource> is used for tracing to the source variable if given <value> is type
// of a pinter that also points to a pointer. It returns nil if the source is nil when <traceSource>
// IsNil checks whether given `value` is nil.
// Parameter `traceSource` is used for tracing to the source variable if given `value` is type
// of a pinter that also points to a pointer. It returns nil if the source is nil when `traceSource`
// is true.
// Note that it might use reflect feature which affects performance a little bit.
func IsNil(value interface{}, traceSource ...bool) bool {

View File

@ -31,7 +31,7 @@ func init() {
// SetEnabled enables/disables the internal logging manually.
// Note that this function is not concurrent safe, be aware of the DATA RACE.
func SetEnabled(enabled bool) {
// If they're the same, it does not write the <isGFDebug> but only reading operation.
// If they're the same, it does not write the `isGFDebug` but only reading operation.
if isGFDebug != enabled {
isGFDebug = enabled
}
@ -42,8 +42,8 @@ func IsEnabled() bool {
return isGFDebug
}
// Print prints <v> with newline using fmt.Println.
// The parameter <v> can be multiple variables.
// Print prints `v` with newline using fmt.Println.
// The parameter `v` can be multiple variables.
func Print(v ...interface{}) {
if !isGFDebug {
return
@ -51,8 +51,8 @@ func Print(v ...interface{}) {
fmt.Println(append([]interface{}{now(), "[INTE]", file()}, v...)...)
}
// Printf prints <v> with format <format> using fmt.Printf.
// The parameter <v> can be multiple variables.
// Printf prints `v` with format `format` using fmt.Printf.
// The parameter `v` can be multiple variables.
func Printf(format string, v ...interface{}) {
if !isGFDebug {
return
@ -60,8 +60,8 @@ func Printf(format string, v ...interface{}) {
fmt.Printf(now()+" [INTE] "+file()+" "+format+"\n", v...)
}
// Error prints <v> with newline using fmt.Println.
// The parameter <v> can be multiple variables.
// Error prints `v` with newline using fmt.Println.
// The parameter `v` can be multiple variables.
func Error(v ...interface{}) {
if !isGFDebug {
return
@ -71,7 +71,7 @@ func Error(v ...interface{}) {
fmt.Println(array...)
}
// Errorf prints <v> with format <format> using fmt.Printf.
// Errorf prints `v` with format `format` using fmt.Printf.
func Errorf(format string, v ...interface{}) {
if !isGFDebug {
return

View File

@ -16,7 +16,7 @@ type Mutex struct {
}
// New creates and returns a new *Mutex.
// The parameter <safe> is used to specify whether using this mutex in concurrent-safety,
// The parameter `safe` is used to specify whether using this mutex in concurrent-safety,
// which is false in default.
func New(safe ...bool) *Mutex {
mu := new(Mutex)

View File

@ -17,7 +17,7 @@ type RWMutex struct {
}
// New creates and returns a new *RWMutex.
// The parameter <safe> is used to specify whether using this mutex in concurrent safety,
// The parameter `safe` is used to specify whether using this mutex in concurrent safety,
// which is false in default.
func New(safe ...bool) *RWMutex {
mu := Create(safe...)
@ -25,7 +25,7 @@ func New(safe ...bool) *RWMutex {
}
// Create creates and returns a new RWMutex object.
// The parameter <safe> is used to specify whether using this mutex in concurrent safety,
// The parameter `safe` is used to specify whether using this mutex in concurrent safety,
// which is false in default.
func Create(safe ...bool) RWMutex {
mu := RWMutex{}

View File

@ -20,36 +20,7 @@ type Type struct {
// Field contains information of a struct field .
type Field struct {
value reflect.Value
field reflect.StructField
// Retrieved tag value. There might be more than one tags in the field,
// but only one can be retrieved according to calling function rules.
TagValue string
}
// Tag returns the value associated with key in the tag string. If there is no
// such key in the tag, Tag returns the empty string.
func (f *Field) Tag(key string) string {
return f.field.Tag.Get(key)
}
// Value returns the underlying value of the field. It panics if the field
// is not exported.
func (f *Field) Value() interface{} {
return f.value.Interface()
}
// IsEmbedded returns true if the given field is an anonymous field (embedded)
func (f *Field) IsEmbedded() bool {
return f.field.Anonymous
}
// IsExported returns true if the given field is exported.
func (f *Field) IsExported() bool {
return f.field.PkgPath == ""
}
// Name returns the name of the given field
func (f *Field) Name() string {
return f.field.Name
Value reflect.Value // The underlying value of the field.
Field reflect.StructField // The underlying field of the field.
TagValue string // Retrieved tag value. There might be more than one tags in the field, but only one can be retrieved according to calling function rules.
}

View File

@ -6,14 +6,43 @@
package structs
// MapField retrieves struct field as map[name/tag]*Field from <pointer>, and returns the map.
// Tag returns the value associated with key in the tag string. If there is no
// such key in the tag, Tag returns the empty string.
func (f *Field) Tag(key string) string {
return f.Field.Tag.Get(key)
}
// IsEmbedded returns true if the given field is an anonymous field (embedded)
func (f *Field) IsEmbedded() bool {
return f.Field.Anonymous
}
// IsExported returns true if the given field is exported.
func (f *Field) IsExported() bool {
return f.Field.PkgPath == ""
}
// Name returns the name of the given field
func (f *Field) Name() string {
return f.Field.Name
}
// Type returns the type of the given field
func (f *Field) Type() Type {
return Type{
Type: f.Field.Type,
}
}
// FieldMap retrieves and returns struct field as map[name/tag]*Field from `pointer`.
//
// The parameter <pointer> should be type of struct/*struct.
// The parameter `pointer` should be type of struct/*struct.
//
// The parameter <priority> specifies the priority tag array for retrieving from high to low.
// The parameter `priority` specifies the priority tag array for retrieving from high to low.
// If it's given `nil`, it returns map[name]*Field, of which the `name` is attribute name.
//
// Note that it only retrieves the exported attributes with first letter up-case from struct.
func MapField(pointer interface{}, priority []string) (map[string]*Field, error) {
func FieldMap(pointer interface{}, priority []string) (map[string]*Field, error) {
fields, err := getFieldValues(pointer)
if err != nil {
return nil, err
@ -40,7 +69,7 @@ func MapField(pointer interface{}, priority []string) (map[string]*Field, error)
mapField[tagValue] = tempField
} else {
if field.IsEmbedded() {
m, err := MapField(field.value, priority)
m, err := FieldMap(field.Value, priority)
if err != nil {
return nil, err
}

View File

@ -9,20 +9,73 @@ package structs
import (
"errors"
"reflect"
"strconv"
)
// TagFields retrieves struct tags as []*Field from <pointer>, and returns it.
// ParseTag parses tag string into map.
func ParseTag(tag string) map[string]string {
var (
key string
data = make(map[string]string)
)
for tag != "" {
// Skip leading space.
i := 0
for i < len(tag) && tag[i] == ' ' {
i++
}
tag = tag[i:]
if tag == "" {
break
}
// Scan to colon. A space, a quote or a control character is a syntax error.
// Strictly speaking, control chars include the range [0x7f, 0x9f], not just
// [0x00, 0x1f], but in practice, we ignore the multi-byte control characters
// as it is simpler to inspect the tag's bytes than the tag's runes.
i = 0
for i < len(tag) && tag[i] > ' ' && tag[i] != ':' && tag[i] != '"' && tag[i] != 0x7f {
i++
}
if i == 0 || i+1 >= len(tag) || tag[i] != ':' || tag[i+1] != '"' {
break
}
key = tag[:i]
tag = tag[i+1:]
// Scan quoted string to find value.
i = 1
for i < len(tag) && tag[i] != '"' {
if tag[i] == '\\' {
i++
}
i++
}
if i >= len(tag) {
break
}
quotedValue := string(tag[:i+1])
tag = tag[i+1:]
value, err := strconv.Unquote(quotedValue)
if err != nil {
panic(err)
}
data[key] = value
}
return data
}
// TagFields retrieves and returns struct tags as []*Field from `pointer`.
//
// The parameter <pointer> should be type of struct/*struct.
// The parameter `pointer` should be type of struct/*struct.
//
// Note that it only retrieves the exported attributes with first letter up-case from struct.
func TagFields(pointer interface{}, priority []string) ([]*Field, error) {
return getFieldValuesByTagPriority(pointer, priority, map[string]struct{}{})
}
// TagMapName retrieves struct tags as map[tag]attribute from <pointer>, and returns it.
// TagMapName retrieves and returns struct tags as map[tag]attribute from `pointer`.
//
// The parameter <pointer> should be type of struct/*struct.
// The parameter `pointer` should be type of struct/*struct.
//
// Note that it only retrieves the exported attributes with first letter up-case from struct.
func TagMapName(pointer interface{}, priority []string) (map[string]string, error) {
@ -37,9 +90,9 @@ func TagMapName(pointer interface{}, priority []string) (map[string]string, erro
return tagMap, nil
}
// TagMapField retrieves struct tags as map[tag]*Field from <pointer>, and returns it.
// TagMapField retrieves struct tags as map[tag]*Field from `pointer`, and returns it.
//
// The parameter <pointer> should be type of struct/*struct.
// The parameter `pointer` should be type of struct/*struct.
//
// Note that it only retrieves the exported attributes with first letter up-case from struct.
func TagMapField(pointer interface{}, priority []string) (map[string]*Field, error) {
@ -88,8 +141,8 @@ func getFieldValues(value interface{}) ([]*Field, error) {
)
for i := 0; i < length; i++ {
fields[i] = &Field{
value: reflectValue.Field(i),
field: structType.Field(i),
Value: reflectValue.Field(i),
Field: structType.Field(i),
}
}
return fields, nil
@ -127,7 +180,7 @@ func getFieldValuesByTagPriority(pointer interface{}, priority []string, tagMap
}
// If this is an embedded attribute, it retrieves the tags recursively.
if field.IsEmbedded() {
if subTagFields, err := getFieldValuesByTagPriority(field.value, priority, tagMap); err != nil {
if subTagFields, err := getFieldValuesByTagPriority(field.Value, priority, tagMap); err != nil {
return nil, err
} else {
tagFields = append(tagFields, subTagFields...)

View File

@ -11,36 +11,45 @@ import (
"reflect"
)
// StructType retrieves and returns the Type of specified struct/*struct.
func StructType(structOrPointer interface{}) (*Type, error) {
// StructType retrieves and returns the struct Type of specified struct/*struct.
func StructType(object interface{}) (*Type, error) {
var (
reflectValue reflect.Value
reflectKind reflect.Kind
reflectType reflect.Type
)
if rv, ok := structOrPointer.(reflect.Value); ok {
if rv, ok := object.(reflect.Value); ok {
reflectValue = rv
} else {
reflectValue = reflect.ValueOf(structOrPointer)
reflectValue = reflect.ValueOf(object)
}
reflectKind = reflectValue.Kind()
for reflectKind == reflect.Ptr {
if !reflectValue.IsValid() || reflectValue.IsNil() {
// If pointer is type of *struct and nil, then automatically create a temporary struct.
for {
switch reflectKind {
case reflect.Ptr:
if !reflectValue.IsValid() || reflectValue.IsNil() {
// If pointer is type of *struct and nil, then automatically create a temporary struct.
reflectValue = reflect.New(reflectValue.Type().Elem()).Elem()
reflectKind = reflectValue.Kind()
} else {
reflectValue = reflectValue.Elem()
reflectKind = reflectValue.Kind()
}
case reflect.Array, reflect.Slice:
reflectValue = reflect.New(reflectValue.Type().Elem()).Elem()
reflectKind = reflectValue.Kind()
} else {
reflectValue = reflectValue.Elem()
reflectKind = reflectValue.Kind()
default:
goto exitLoop
}
}
exitLoop:
for reflectKind == reflect.Ptr {
reflectValue = reflectValue.Elem()
reflectKind = reflectValue.Kind()
}
if reflectKind != reflect.Struct {
return nil, gerror.Newf(
`invalid parameter kind "%s", kind of "struct" is required`,
`invalid object kind "%s", kind of "struct" is required`,
reflectKind,
)
}
@ -50,6 +59,16 @@ func StructType(structOrPointer interface{}) (*Type, error) {
}, nil
}
func (t *Type) Signature() string {
// Signature returns an unique string as this type.
func (t Type) Signature() string {
return t.PkgPath() + "/" + t.String()
}
// FieldKeys returns the keys of current struct/map.
func (t Type) FieldKeys() []string {
keys := make([]string, t.NumField())
for i := 0; i < t.NumField(); i++ {
keys[i] = t.Field(i).Name
}
return keys
}

View File

@ -102,7 +102,7 @@ func Test_StructOfNilPointer(t *testing.T) {
})
}
func Test_MapField(t *testing.T) {
func Test_FieldMap(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
type User struct {
Id int
@ -110,7 +110,7 @@ func Test_MapField(t *testing.T) {
Pass string `my-tag1:"pass1" my-tag2:"pass2" params:"pass"`
}
var user *User
m, _ := structs.MapField(user, []string{"params"})
m, _ := structs.FieldMap(user, []string{"params"})
t.Assert(len(m), 3)
_, ok := m["Id"]
t.Assert(ok, true)
@ -123,6 +123,26 @@ func Test_MapField(t *testing.T) {
_, ok = m["pass"]
t.Assert(ok, true)
})
gtest.C(t, func(t *gtest.T) {
type User struct {
Id int
Name string `params:"name"`
Pass string `my-tag1:"pass1" my-tag2:"pass2" params:"pass"`
}
var user *User
m, _ := structs.FieldMap(user, nil)
t.Assert(len(m), 3)
_, ok := m["Id"]
t.Assert(ok, true)
_, ok = m["Name"]
t.Assert(ok, true)
_, ok = m["name"]
t.Assert(ok, false)
_, ok = m["Pass"]
t.Assert(ok, true)
_, ok = m["pass"]
t.Assert(ok, false)
})
}
func Test_StructType(t *testing.T) {
@ -148,7 +168,6 @@ func Test_StructType(t *testing.T) {
t.AssertNil(err)
t.Assert(r.Signature(), `github.com/gogf/gf/internal/structs_test/structs_test.B`)
})
// Error.
gtest.C(t, func(t *gtest.T) {
type B struct {
Name string
@ -156,7 +175,71 @@ func Test_StructType(t *testing.T) {
type A struct {
*B
}
_, err := structs.StructType(new(A).B)
r, err := structs.StructType(new(A).B)
t.AssertNil(err)
t.Assert(r.String(), `structs_test.B`)
})
// Error.
gtest.C(t, func(t *gtest.T) {
type B struct {
Name string
}
type A struct {
*B
Id int
}
_, err := structs.StructType(new(A).Id)
t.AssertNE(err, nil)
})
}
func Test_StructTypeBySlice(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
type B struct {
Name string
}
type A struct {
Array []*B
}
r, err := structs.StructType(new(A).Array)
t.AssertNil(err)
t.Assert(r.Signature(), `github.com/gogf/gf/internal/structs_test/structs_test.B`)
})
gtest.C(t, func(t *gtest.T) {
type B struct {
Name string
}
type A struct {
Array []B
}
r, err := structs.StructType(new(A).Array)
t.AssertNil(err)
t.Assert(r.Signature(), `github.com/gogf/gf/internal/structs_test/structs_test.B`)
})
gtest.C(t, func(t *gtest.T) {
type B struct {
Name string
}
type A struct {
Array *[]B
}
r, err := structs.StructType(new(A).Array)
t.AssertNil(err)
t.Assert(r.Signature(), `github.com/gogf/gf/internal/structs_test/structs_test.B`)
})
}
func TestType_FieldKeys(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
type B struct {
Id int
Name string
}
type A struct {
Array []*B
}
r, err := structs.StructType(new(A).Array)
t.AssertNil(err)
t.Assert(r.FieldKeys(), g.Slice{"Id", "Name"})
})
}

View File

@ -67,7 +67,7 @@ func UcFirst(s string) string {
return s
}
// ReplaceByMap returns a copy of <origin>,
// ReplaceByMap returns a copy of `origin`,
// which is replaced by a map in unordered way, case-sensitively.
func ReplaceByMap(origin string, replaces map[string]string) string {
for k, v := range replaces {
@ -87,7 +87,7 @@ func RemoveSymbols(s string) string {
return string(b)
}
// EqualFoldWithoutChars checks string <s1> and <s2> equal case-insensitively,
// EqualFoldWithoutChars checks string `s1` and `s2` equal case-insensitively,
// with/without chars '-'/'_'/'.'/' '.
func EqualFoldWithoutChars(s1, s2 string) bool {
return strings.EqualFold(RemoveSymbols(s1), RemoveSymbols(s2))

View File

@ -23,6 +23,11 @@ import (
"github.com/gogf/gf/util/grand"
)
const (
// NotFoundIndex is the position index for string not found in searching functions.
NotFoundIndex = -1
)
// Replace returns a copy of the string <origin>
// in which string <search> replaced by <replace> case-sensitively.
func Replace(origin, search, replace string, count ...int) string {
@ -43,8 +48,10 @@ func ReplaceI(origin, search, replace string, count ...int) string {
if n == 0 {
return origin
}
length := len(search)
searchLower := strings.ToLower(search)
var (
length = len(search)
searchLower = strings.ToLower(search)
)
for {
originLower := strings.ToLower(origin)
if pos := strings.Index(originLower, searchLower); pos != -1 {
@ -465,11 +472,11 @@ func Str(haystack string, needle string) string {
if needle == "" {
return ""
}
idx := strings.Index(haystack, needle)
if idx == -1 {
pos := strings.Index(haystack, needle)
if pos == NotFoundIndex {
return ""
}
return haystack[idx+len([]byte(needle))-1:]
return haystack[pos+len([]byte(needle))-1:]
}
// StrEx returns part of <haystack> string starting from and excluding
@ -481,6 +488,26 @@ func StrEx(haystack string, needle string) string {
return ""
}
// StrTill returns part of <haystack> string ending to and including
// the first occurrence of <needle> from the start of <haystack>.
func StrTill(haystack string, needle string) string {
pos := strings.Index(haystack, needle)
if pos == NotFoundIndex || pos == 0 {
return ""
}
return haystack[:pos+1]
}
// StrTillEx returns part of <haystack> string ending to and excluding
// the first occurrence of <needle> from the start of <haystack>.
func StrTillEx(haystack string, needle string) string {
pos := strings.Index(haystack, needle)
if pos == NotFoundIndex || pos == 0 {
return ""
}
return haystack[:pos]
}
// Shuffle randomly shuffles a string.
// It considers parameter <str> as unicode string.
func Shuffle(str string) string {
@ -687,10 +714,10 @@ func SearchArray(a []string, s string) int {
return i
}
}
return -1
return NotFoundIndex
}
// InArray checks whether string <s> in slice <a>.
func InArray(a []string, s string) bool {
return SearchArray(a, s) != -1
return SearchArray(a, s) != NotFoundIndex
}

View File

@ -20,13 +20,12 @@ func Pos(haystack, needle string, startOffset ...int) int {
if length == 0 || offset > length || -offset > length {
return -1
}
if offset < 0 {
offset += length
}
pos := strings.Index(haystack[offset:], needle)
if pos == -1 {
return -1
if pos == NotFoundIndex {
return NotFoundIndex
}
return pos + offset
}

View File

@ -28,11 +28,11 @@ var (
// Trim strips whitespace (or other characters) from the beginning and end of a string.
// The optional parameter <characterMask> specifies the additional stripped characters.
func Trim(str string, characterMask ...string) string {
if len(characterMask) == 0 {
return strings.Trim(str, defaultTrimChars)
} else {
return strings.Trim(str, defaultTrimChars+characterMask[0])
trimChars := defaultTrimChars
if len(characterMask) > 0 {
trimChars += characterMask[0]
}
return strings.Trim(str, trimChars)
}
// TrimStr strips all of the given <cut> string from the beginning and end of a string.
@ -43,11 +43,11 @@ func TrimStr(str string, cut string, count ...int) string {
// TrimLeft strips whitespace (or other characters) from the beginning of a string.
func TrimLeft(str string, characterMask ...string) string {
if len(characterMask) == 0 {
return strings.TrimLeft(str, defaultTrimChars)
} else {
return strings.TrimLeft(str, defaultTrimChars+characterMask[0])
trimChars := defaultTrimChars
if len(characterMask) > 0 {
trimChars += characterMask[0]
}
return strings.TrimLeft(str, trimChars)
}
// TrimLeftStr strips all of the given <cut> string from the beginning of a string.
@ -69,11 +69,11 @@ func TrimLeftStr(str string, cut string, count ...int) string {
// TrimRight strips whitespace (or other characters) from the end of a string.
func TrimRight(str string, characterMask ...string) string {
if len(characterMask) == 0 {
return strings.TrimRight(str, defaultTrimChars)
} else {
return strings.TrimRight(str, defaultTrimChars+characterMask[0])
trimChars := defaultTrimChars
if len(characterMask) > 0 {
trimChars += characterMask[0]
}
return strings.TrimRight(str, trimChars)
}
// TrimRightStr strips all of the given <cut> string from the end of a string.
@ -94,3 +94,28 @@ func TrimRightStr(str string, cut string, count ...int) string {
}
return str
}
// TrimAll trims all characters in string `str`.
func TrimAll(str string, characterMask ...string) string {
trimChars := defaultTrimChars
if len(characterMask) > 0 {
trimChars += characterMask[0]
}
var (
filtered bool
slice = make([]rune, 0, len(str))
)
for _, char := range str {
filtered = false
for _, trimChar := range trimChars {
if char == trimChar {
filtered = true
break
}
}
if !filtered {
slice = append(slice, char)
}
}
return string(slice)
}

View File

@ -303,6 +303,22 @@ func Test_StrEx(t *testing.T) {
})
}
func Test_StrTill(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
t.Assert(gstr.StrTill("name@example.com", "@"), "name@")
t.Assert(gstr.StrTill("name@example.com", ""), "")
t.Assert(gstr.StrTill("name@example.com", "z"), "")
})
}
func Test_StrTillEx(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
t.Assert(gstr.StrTillEx("name@example.com", "@"), "name")
t.Assert(gstr.StrTillEx("name@example.com", ""), "")
t.Assert(gstr.StrTillEx("name@example.com", "z"), "")
})
}
func Test_Shuffle(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
t.Assert(len(gstr.Shuffle("123456")), 6)

View File

@ -81,3 +81,17 @@ func Test_TrimLeftStr(t *testing.T) {
t.Assert(gstr.TrimLeftStr("我爱中国人", "我爱中国"), "人")
})
}
func Test_TrimAll(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
t.Assert(gstr.TrimAll("gogo我go\n爱gogo\n", "go"), "我爱")
})
gtest.C(t, func(t *gtest.T) {
t.Assert(gstr.TrimAll("gogo\n我go爱gogo", "go"), "我爱")
t.Assert(gstr.TrimAll("gogo\n我go爱gogo\n", "go"), "我爱")
t.Assert(gstr.TrimAll("gogo\n我go\n爱gogo", "go"), "我爱")
})
gtest.C(t, func(t *gtest.T) {
t.Assert(gstr.TrimAll("啊我爱\n啊中国\n人啊", "啊"), "我爱中国人")
})
}

View File

@ -11,22 +11,23 @@ import (
"github.com/gogf/gf/container/gmap"
"github.com/gogf/gf/container/gvar"
"github.com/gogf/gf/internal/structs"
"strconv"
)
// Meta is used as an embedded attribute for struct to enabled meta data feature.
type Meta struct{}
const (
// metaAttributeName is the attribute name of meta data in struct.
metaAttributeName = "Meta"
)
var (
// metaDataCacheMap is a cache map for struct type to enhance the performance.
metaDataCacheMap = gmap.NewStrAnyMap(true)
)
// Data retrieves and returns all meta data from `object`.
// It automatically parses the tag string from "Mata" attribute as its meta data.
// It automatically parses and caches the tag string from "Mata" attribute as its meta data.
func Data(object interface{}) map[string]interface{} {
reflectType, err := structs.StructType(object)
if err != nil {
@ -35,52 +36,11 @@ func Data(object interface{}) map[string]interface{} {
return metaDataCacheMap.GetOrSetFuncLock(reflectType.Signature(), func() interface{} {
if field, ok := reflectType.FieldByName(metaAttributeName); ok {
var (
key string
tag = field.Tag
data = make(map[string]interface{})
tags = structs.ParseTag(string(field.Tag))
data = make(map[string]interface{}, len(tags))
)
for tag != "" {
// Skip leading space.
i := 0
for i < len(tag) && tag[i] == ' ' {
i++
}
tag = tag[i:]
if tag == "" {
break
}
// Scan to colon. A space, a quote or a control character is a syntax error.
// Strictly speaking, control chars include the range [0x7f, 0x9f], not just
// [0x00, 0x1f], but in practice, we ignore the multi-byte control characters
// as it is simpler to inspect the tag's bytes than the tag's runes.
i = 0
for i < len(tag) && tag[i] > ' ' && tag[i] != ':' && tag[i] != '"' && tag[i] != 0x7f {
i++
}
if i == 0 || i+1 >= len(tag) || tag[i] != ':' || tag[i+1] != '"' {
break
}
key = string(tag[:i])
tag = tag[i+1:]
// Scan quoted string to find value.
i = 1
for i < len(tag) && tag[i] != '"' {
if tag[i] == '\\' {
i++
}
i++
}
if i >= len(tag) {
break
}
quotedValue := string(tag[:i+1])
tag = tag[i+1:]
value, err := strconv.Unquote(quotedValue)
if err != nil {
panic(err)
}
data[key] = value
for k, v := range tags {
data[k] = v
}
return data
}

View File

@ -24,7 +24,7 @@ var (
// if <rules> is type of []string.
// The optional parameter <messages> specifies the custom error messages for specified keys and rules.
func (v *Validator) CheckStruct(object interface{}, rules interface{}, messages ...CustomMsg) *Error {
// It here must use structs.TagFields not structs.MapField to ensure error sequence.
// It here must use structs.TagFields not structs.FieldMap to ensure error sequence.
tagField, err := structs.TagFields(object, structTagPriority)
if err != nil {
return newErrorStr("invalid_object", err.Error())
@ -85,13 +85,13 @@ func (v *Validator) CheckStruct(object interface{}, rules interface{}, messages
return nil
}
// Checks and extends the parameters map with struct alias tag.
mapField, err := structs.MapField(object, aliasNameTagPriority)
mapField, err := structs.FieldMap(object, aliasNameTagPriority)
if err != nil {
return newErrorStr("invalid_object", err.Error())
}
for nameOrTag, field := range mapField {
params[nameOrTag] = field.Value()
params[field.Name()] = field.Value()
params[nameOrTag] = field.Value.Interface()
params[field.Name()] = field.Value.Interface()
}
for _, field := range tagField {
fieldName := field.Name()
@ -105,7 +105,7 @@ func (v *Validator) CheckStruct(object interface{}, rules interface{}, messages
}
// It here extends the params map using alias names.
if _, ok := params[name]; !ok {
params[name] = field.Value()
params[name] = field.Value.Interface()
}
if _, ok := checkRules[name]; !ok {
if _, ok := checkRules[fieldName]; ok {