mirror of
https://gitee.com/johng/gf
synced 2026-07-04 13:02:36 +08:00
orm with feature
This commit is contained in:
@ -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
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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...)
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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")
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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))
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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`)
|
||||
})
|
||||
}
|
||||
|
||||
86
frame/g/g.go
86
frame/g/g.go
@ -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
|
||||
)
|
||||
|
||||
@ -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...)
|
||||
|
||||
@ -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>;
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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{}
|
||||
|
||||
@ -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.
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
@ -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...)
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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"})
|
||||
})
|
||||
}
|
||||
|
||||
@ -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))
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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人啊", "啊"), "我爱中国人")
|
||||
})
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user