mirror of
https://gitee.com/johng/gf
synced 2026-06-11 03:41:44 +08:00
Compare commits
56 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 19bfc48dca | |||
| ae0cc5a4b6 | |||
| ba74e0beb2 | |||
| e5ca4e788e | |||
| 1c7f034135 | |||
| 4c78ce6e3f | |||
| 7803d557b3 | |||
| 204fea395c | |||
| bd13de2b39 | |||
| d0f649b328 | |||
| 04e42d2175 | |||
| ffc88eaaa7 | |||
| 4b4cc5ebb9 | |||
| 0e6cddb547 | |||
| a69aec9070 | |||
| d2bd37962e | |||
| eb6763b0fd | |||
| d330afdd36 | |||
| 5db4bbc186 | |||
| e00f2666ff | |||
| 0238cdd5ec | |||
| 2c34d96b9d | |||
| 65b3630f6d | |||
| 54b629561e | |||
| 285ad36e7d | |||
| 5adde275fc | |||
| a3fa10d820 | |||
| acf47f3907 | |||
| b4d5335e43 | |||
| 39fb842e9b | |||
| 7a5d86b44d | |||
| 397e11e124 | |||
| f4314c318e | |||
| 9bb5536163 | |||
| 2344701f22 | |||
| 3bb909ba4f | |||
| 8ae0bd148b | |||
| 72251b880a | |||
| fefde4c290 | |||
| 3b2bae6128 | |||
| ae559b57de | |||
| 6135085d61 | |||
| 40bdc76af1 | |||
| d4f982a9cf | |||
| 813841bb68 | |||
| 930e63e6b9 | |||
| ce40d139e7 | |||
| 8368e11827 | |||
| e6b4662ec2 | |||
| 28f83d3d32 | |||
| 3e33d66ab4 | |||
| 13248d6736 | |||
| 8bd24724e7 | |||
| 80248e9a6e | |||
| 2734903886 | |||
| 2451b40d3e |
12
README.MD
12
README.MD
@ -1,7 +1,7 @@
|
||||
# GoFrame
|
||||
|
||||
[](https://godoc.org/github.com/gogf/gf)
|
||||
[](https://travis-ci.org/gogf/gf)
|
||||
[](https://travis-ci.org/gogf/gf)
|
||||
[](https://goreportcard.com/report/github.com/gogf/gf)
|
||||
[](https://codecov.io/gh/gogf/gf/branch/master)
|
||||
[](https://github.com/gogf/gf)
|
||||
@ -33,7 +33,7 @@ golang version >= 1.11
|
||||
|
||||
# Architecture
|
||||
<div align=center>
|
||||
<img src="https://itician.org/download/attachments/1114119/arch.png"/>
|
||||
<img src="https://goframe.org/download/attachments/1114119/arch.png"/>
|
||||
</div>
|
||||
|
||||
# Packages
|
||||
@ -80,7 +80,7 @@ The `Web` component performance of `GoFrame`, please refer to third-party projec
|
||||
- [XiMaLaYa](https://www.ximalaya.com)
|
||||
- [ZYBang](https://www.zybang.com/)
|
||||
|
||||
> We list part of the users here, if your company or products are using `GoFrame`, please let us know [here](https://itician.org/pages/viewpage.action?pageId=1114415).
|
||||
> We list part of the users here, if your company or products are using `GoFrame`, please let us know [here](https://goframe.org/pages/viewpage.action?pageId=1114415).
|
||||
|
||||
|
||||
# Contributors
|
||||
@ -90,7 +90,7 @@ This project exists thanks to all the people who contribute. [[Contributors](htt
|
||||
|
||||
# Donators
|
||||
|
||||
If you love `GF`, why not [buy developer a cup of coffee](https://itician.org/pages/viewpage.action?pageId=1115633)?
|
||||
If you love `GF`, why not [buy developer a cup of coffee](https://goframe.org/pages/viewpage.action?pageId=1115633)?
|
||||
|
||||
# Sponsors
|
||||
We appreciate any kind of sponsorship for `GF` development. If you've got some interesting, please contact WeChat `389961817` / Email `john@goframe.org`.
|
||||
@ -98,8 +98,8 @@ We appreciate any kind of sponsorship for `GF` development. If you've got some i
|
||||
|
||||
|
||||
# Thanks
|
||||
<a href="https://www.jetbrains.com/?from=GoFrame"><img src="https://itician.org/download/thumbnails/1114119/jetbrains.png?version=1&modificationDate=1608649325806&api=v2" height="120" alt="JetBrains"/></a>
|
||||
<a href="https://www.atlassian.com/?from=GoFrame"><img src="https://itician.org/download/thumbnails/1114119/u%3D605052180%2C3099422872%26fm%3D11%26gp%3D0.jpg?version=1&modificationDate=1608649343601&api=v2" height="120" alt="Atlassian"/></a>
|
||||
<a href="https://www.jetbrains.com/?from=GoFrame"><img src="https://goframe.org/download/thumbnails/1114119/jetbrains.png" height="120" alt="JetBrains"/></a>
|
||||
<a href="https://www.atlassian.com/?from=GoFrame"><img src="https://goframe.org/download/attachments/1114119/atlassian.jpg" height="120" alt="Atlassian"/></a>
|
||||
|
||||
|
||||
|
||||
|
||||
13
README_ZH.MD
13
README_ZH.MD
@ -1,6 +1,6 @@
|
||||
# GoFrame
|
||||
[](https://godoc.org/github.com/gogf/gf)
|
||||
[](https://travis-ci.org/gogf/gf)
|
||||
[](https://travis-ci.org/gogf/gf)
|
||||
[](https://goreportcard.com/report/github.com/gogf/gf)
|
||||
[](https://codecov.io/gh/gogf/gf/branch/master)
|
||||
[](https://github.com/gogf/gf)
|
||||
@ -49,7 +49,7 @@ golang版本 >= 1.11
|
||||
|
||||
# 架构
|
||||
<div align=center>
|
||||
<img src="https://itician.org/download/attachments/1114119/arch.png"/>
|
||||
<img src="https://goframe.org/download/attachments/1114119/arch.png"/>
|
||||
</div>
|
||||
|
||||
# 模块
|
||||
@ -95,7 +95,7 @@ golang版本 >= 1.11
|
||||
- [喜马拉雅](https://www.ximalaya.com)
|
||||
- [作业帮](https://www.zybang.com/)
|
||||
|
||||
> 在这里只列举了部分知名的用户,如果您的企业或者产品正在使用`GoFrame`,欢迎到 [这里](https://itician.org/pages/viewpage.action?pageId=1114415) 留言。
|
||||
> 在这里只列举了部分知名的用户,如果您的企业或者产品正在使用`GoFrame`,欢迎到 [这里](https://goframe.org/pages/viewpage.action?pageId=1114415) 留言。
|
||||
|
||||
# 贡献
|
||||
|
||||
@ -105,7 +105,7 @@ golang版本 >= 1.11
|
||||
|
||||
# 捐赠
|
||||
|
||||
如果您喜欢`GF`,要不给开发者 [来杯咖啡](https://itician.org/pages/viewpage.action?pageId=1115633) 吧!
|
||||
如果您喜欢`GF`,要不给开发者 [来杯咖啡](https://goframe.org/pages/viewpage.action?pageId=1115633) 吧!
|
||||
请在捐赠时备注您的`github`/`gitee`账号名称。
|
||||
|
||||
# 赞助
|
||||
@ -113,5 +113,6 @@ golang版本 >= 1.11
|
||||
赞助支持`GF`框架的快速研发,如果您感兴趣,请联系 微信 `389961817` / 邮件 `john@goframe.org`。
|
||||
|
||||
# 感谢
|
||||
<a href="https://www.jetbrains.com/?from=GoFrame"><img src="https://itician.org/download/thumbnails/1114119/jetbrains.png?version=1&modificationDate=1608649325806&api=v2" height="120" alt="JetBrains"/></a>
|
||||
<a href="https://www.atlassian.com/?from=GoFrame"><img src="https://itician.org/download/thumbnails/1114119/u%3D605052180%2C3099422872%26fm%3D11%26gp%3D0.jpg?version=1&modificationDate=1608649343601&api=v2" height="120" alt="Atlassian"/></a>
|
||||
<a href="https://www.jetbrains.com/?from=GoFrame"><img src="https://goframe.org/download/thumbnails/1114119/jetbrains.png" height="120" alt="JetBrains"/></a>
|
||||
<a href="https://www.atlassian.com/?from=GoFrame"><img src="https://goframe.org/download/attachments/1114119/atlassian.jpg" height="120" alt="Atlassian"/></a>
|
||||
|
||||
|
||||
@ -7,5 +7,4 @@
|
||||
// Package gtree provides concurrent-safe/unsafe tree containers.
|
||||
//
|
||||
// Some implements are from: https://github.com/emirpasic/gods
|
||||
// Thanks!
|
||||
package gtree
|
||||
|
||||
@ -245,12 +245,57 @@ func Test_Duration(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func Test_UnmarshalValue(t *testing.T) {
|
||||
type V struct {
|
||||
Name string
|
||||
Var *gvar.Var
|
||||
}
|
||||
func Test_UnmarshalJson(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type V struct {
|
||||
Name string
|
||||
Var *gvar.Var
|
||||
}
|
||||
var v *V
|
||||
err := gconv.Struct(map[string]interface{}{
|
||||
"name": "john",
|
||||
"var": "v",
|
||||
}, &v)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(v.Name, "john")
|
||||
t.Assert(v.Var.String(), "v")
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type V struct {
|
||||
Name string
|
||||
Var gvar.Var
|
||||
}
|
||||
var v *V
|
||||
err := gconv.Struct(map[string]interface{}{
|
||||
"name": "john",
|
||||
"var": "v",
|
||||
}, &v)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(v.Name, "john")
|
||||
t.Assert(v.Var.String(), "v")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_UnmarshalValue(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type V struct {
|
||||
Name string
|
||||
Var *gvar.Var
|
||||
}
|
||||
var v *V
|
||||
err := gconv.Struct(map[string]interface{}{
|
||||
"name": "john",
|
||||
"var": "v",
|
||||
}, &v)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(v.Name, "john")
|
||||
t.Assert(v.Var.String(), "v")
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type V struct {
|
||||
Name string
|
||||
Var gvar.Var
|
||||
}
|
||||
var v *V
|
||||
err := gconv.Struct(map[string]interface{}{
|
||||
"name": "john",
|
||||
|
||||
@ -35,10 +35,25 @@ type DB interface {
|
||||
// The DB interface is designed not only for
|
||||
// relational databases but also for NoSQL databases in the future. The name
|
||||
// "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)
|
||||
@ -157,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{})
|
||||
|
||||
// ===========================================================================
|
||||
@ -173,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.
|
||||
@ -291,7 +306,7 @@ var (
|
||||
|
||||
func init() {
|
||||
// allDryRun is initialized from environment or command options.
|
||||
allDryRun = gcmd.GetWithEnv("gf.gdb.dryrun", false).Bool()
|
||||
allDryRun = gcmd.GetOptWithEnv("gf.gdb.dryrun", false).Bool()
|
||||
}
|
||||
|
||||
// Register registers custom database driver to gdb.
|
||||
@ -301,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
|
||||
@ -325,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))
|
||||
}
|
||||
@ -342,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
|
||||
@ -362,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 {
|
||||
@ -431,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.
|
||||
@ -456,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
|
||||
@ -488,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.Debug {
|
||||
c.DB.SetDryRun(node.DryRun)
|
||||
if 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,23 +97,21 @@ 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)
|
||||
defer cancelFunc()
|
||||
ctx, _ = context.WithTimeout(ctx, c.GetConfig().QueryTimeout)
|
||||
}
|
||||
|
||||
mTime1 := gtime.TimestampMilli()
|
||||
@ -127,10 +125,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 +142,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 +162,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 +176,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 +191,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 +199,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 +229,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 +245,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 +279,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 +287,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 +297,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 +309,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 +321,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 +332,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 +349,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 +358,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 +367,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 +379,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 +390,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 +400,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 +436,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 +452,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 +469,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 +488,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 +504,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 +530,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 +546,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 +578,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 +594,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 +603,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 +612,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 +621,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 +632,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 +687,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 +697,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 +743,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 +771,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 +790,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 +813,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 +845,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 +858,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 +875,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 +916,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 +942,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 +967,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
|
||||
|
||||
@ -12,6 +12,7 @@ import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf"
|
||||
"github.com/gogf/gf/net/gtrace"
|
||||
"github.com/gogf/gf/os/gcmd"
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/codes"
|
||||
"go.opentelemetry.io/otel/label"
|
||||
@ -19,6 +20,7 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
tracingInstrumentName = "github.com/gogf/gf/database/gdb"
|
||||
tracingAttrDbType = "db.type"
|
||||
tracingAttrDbHost = "db.host"
|
||||
tracingAttrDbPort = "db.port"
|
||||
@ -32,14 +34,24 @@ const (
|
||||
tracingEventDbExecutionType = "db.execution.type"
|
||||
)
|
||||
|
||||
var (
|
||||
// tracingInternal enables tracing for internal type spans.
|
||||
// It's true in default.
|
||||
tracingInternal = true
|
||||
)
|
||||
|
||||
func init() {
|
||||
tracingInternal = gcmd.GetOptWithEnv("gf.tracing.internal", true).Bool()
|
||||
}
|
||||
|
||||
// addSqlToTracing adds sql information to tracer if it's enabled.
|
||||
func (c *Core) addSqlToTracing(ctx context.Context, sql *Sql) {
|
||||
if !gtrace.IsActivated(ctx) {
|
||||
if !tracingInternal || !gtrace.IsActivated(ctx) {
|
||||
return
|
||||
}
|
||||
tr := otel.GetTracerProvider().Tracer(
|
||||
"github.com/gogf/gf/database/gdb",
|
||||
trace.WithInstrumentationVersion(fmt.Sprintf(`%s`, gf.VERSION)),
|
||||
tracingInstrumentName,
|
||||
trace.WithInstrumentationVersion(gf.VERSION),
|
||||
)
|
||||
ctx, span := tr.Start(ctx, sql.Type, trace.WithSpanKind(trace.SpanKindInternal))
|
||||
defer span.End()
|
||||
@ -50,24 +62,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
|
||||
}
|
||||
|
||||
@ -14,6 +14,7 @@ import (
|
||||
"github.com/gogf/gf/internal/json"
|
||||
"github.com/gogf/gf/internal/utils"
|
||||
"github.com/gogf/gf/os/gtime"
|
||||
"github.com/gogf/gf/util/gmeta"
|
||||
"github.com/gogf/gf/util/gutil"
|
||||
"reflect"
|
||||
"regexp"
|
||||
@ -47,10 +48,17 @@ type apiMapStrAny interface {
|
||||
MapStrAny() map[string]interface{}
|
||||
}
|
||||
|
||||
// apiTableName is the interface for retrieving table name fro struct.
|
||||
type apiTableName interface {
|
||||
TableName() string
|
||||
}
|
||||
|
||||
const (
|
||||
OrmTagForStruct = "orm"
|
||||
OrmTagForUnique = "unique"
|
||||
OrmTagForPrimary = "primary"
|
||||
OrmTagForTable = "table"
|
||||
OrmTagForWith = "with"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -61,30 +69,62 @@ var (
|
||||
structTagPriority = append([]string{OrmTagForStruct}, gconv.StructTagPriority...)
|
||||
)
|
||||
|
||||
// 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,
|
||||
// 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 {
|
||||
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 {
|
||||
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,
|
||||
// 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 {
|
||||
@ -101,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 (
|
||||
@ -143,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 {
|
||||
@ -166,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...)
|
||||
}
|
||||
@ -275,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
|
||||
@ -323,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
|
||||
}
|
||||
@ -351,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'
|
||||
@ -360,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
|
||||
@ -380,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:
|
||||
@ -427,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.
|
||||
@ -490,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
|
||||
@ -743,18 +783,3 @@ 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.
|
||||
func convertMapToStruct(data map[string]interface{}, pointer interface{}) error {
|
||||
tagNameMap, err := structs.TagMapName(pointer, []string{OrmTagForStruct})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// It retrieves and returns the mapping between orm tag and the struct attribute name.
|
||||
mapping := make(map[string]string)
|
||||
for tag, attr := range tagNameMap {
|
||||
mapping[strings.Split(tag, ",")[0]] = attr
|
||||
}
|
||||
return gconv.Struct(data, pointer, mapping)
|
||||
}
|
||||
|
||||
@ -25,6 +25,8 @@ type Model struct {
|
||||
tables string // Operation table names, which can be more than one table names and aliases, like: "user", "user u", "user u, user_detail ud".
|
||||
fields string // Operation fields, multiple fields joined using char ','.
|
||||
fieldsEx string // Excluded operation fields, multiple fields joined using char ','.
|
||||
withArray []interface{} // Arguments for With feature.
|
||||
withAll bool // Enable model association operations on all objects that have "with" tag in the struct.
|
||||
extraArgs []interface{} // Extra custom arguments for sql.
|
||||
whereHolder []*whereHolder // Condition strings for where operation.
|
||||
groupBy string // Used for "group by" statement.
|
||||
@ -53,65 +55,76 @@ type whereHolder struct {
|
||||
}
|
||||
|
||||
const (
|
||||
OPTION_OMITEMPTY = 1
|
||||
OPTION_ALLOWEMPTY = 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 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. Table names:
|
||||
// Table("user")
|
||||
// Table("user u")
|
||||
// Table("user, user_detail")
|
||||
// Table("user u, user_detail ud")
|
||||
// 2. Table name with alias: Table("user", "u")
|
||||
// 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...)
|
||||
}
|
||||
|
||||
// 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")
|
||||
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])
|
||||
} else {
|
||||
panic("table cannot be empty")
|
||||
tables = c.db.QuotePrefixTableName(table[0])
|
||||
}
|
||||
return &Model{
|
||||
db: c.DB,
|
||||
db: c.db,
|
||||
tablesInit: tables,
|
||||
tables: tables,
|
||||
fields: "*",
|
||||
start: -1,
|
||||
offset: -1,
|
||||
option: OPTION_ALLOWEMPTY,
|
||||
option: OptionAllowEmpty,
|
||||
}
|
||||
}
|
||||
|
||||
// Model is alias of Core.Table.
|
||||
// See Core.Table.
|
||||
func (c *Core) Model(table ...string) *Model {
|
||||
return c.DB.Table(table...)
|
||||
// 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 acts like Core.Table except it operates on transaction.
|
||||
// See Core.Table.
|
||||
// Table is alias of tx.Model.
|
||||
// Deprecated, use Model instead.
|
||||
func (tx *TX) Table(table ...string) *Model {
|
||||
model := tx.db.Table(table...)
|
||||
return tx.Model(table...)
|
||||
}
|
||||
|
||||
// Model acts like Core.Model except it operates on transaction.
|
||||
// See Core.Model.
|
||||
func (tx *TX) Model(table ...string) *Model {
|
||||
model := tx.db.Model(table...)
|
||||
model.db = tx.db
|
||||
model.tx = tx
|
||||
return model
|
||||
}
|
||||
|
||||
// Model is alias of tx.Table.
|
||||
// See tx.Table.
|
||||
func (tx *TX) Model(table ...string) *Model {
|
||||
return tx.Table(table...)
|
||||
// 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.
|
||||
@ -170,12 +183,12 @@ func (m *Model) Schema(schema string) *Model {
|
||||
func (m *Model) Clone() *Model {
|
||||
newModel := (*Model)(nil)
|
||||
if m.tx != nil {
|
||||
newModel = m.tx.Table(m.tablesInit)
|
||||
newModel = m.tx.Model(m.tablesInit)
|
||||
} else {
|
||||
newModel = m.db.Table(m.tablesInit)
|
||||
newModel = m.db.Model(m.tablesInit)
|
||||
}
|
||||
*newModel = *m
|
||||
// Deep copy slice attributes.
|
||||
// Shallow copy slice attributes.
|
||||
if n := len(m.extraArgs); n > 0 {
|
||||
newModel.extraArgs = make([]interface{}, n)
|
||||
copy(newModel.extraArgs, m.extraArgs)
|
||||
@ -184,6 +197,10 @@ func (m *Model) Clone() *Model {
|
||||
newModel.whereHolder = make([]*whereHolder, n)
|
||||
copy(newModel.whereHolder, m.whereHolder)
|
||||
}
|
||||
if n := len(m.withArray); n > 0 {
|
||||
newModel.withArray = make([]interface{}, n)
|
||||
copy(newModel.withArray, m.withArray)
|
||||
}
|
||||
return newModel
|
||||
}
|
||||
|
||||
|
||||
@ -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")
|
||||
|
||||
@ -13,15 +13,15 @@ func (m *Model) Option(option int) *Model {
|
||||
return model
|
||||
}
|
||||
|
||||
// OptionOmitEmpty sets OPTION_OMITEMPTY option for the model, which automatically filers
|
||||
// OptionOmitEmpty sets OptionOmitEmpty option for the model, which automatically filers
|
||||
// the data and where attributes for empty values.
|
||||
// Deprecated, use OmitEmpty instead.
|
||||
func (m *Model) OptionOmitEmpty() *Model {
|
||||
return m.Option(OPTION_OMITEMPTY)
|
||||
return m.Option(OptionOmitEmpty)
|
||||
}
|
||||
|
||||
// OmitEmpty sets OPTION_OMITEMPTY option for the model, which automatically filers
|
||||
// OmitEmpty sets OptionOmitEmpty option for the model, which automatically filers
|
||||
// the data and where attributes for empty values.
|
||||
func (m *Model) OmitEmpty() *Model {
|
||||
return m.Option(OPTION_OMITEMPTY)
|
||||
return m.Option(OptionOmitEmpty)
|
||||
}
|
||||
|
||||
@ -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,26 +38,14 @@ 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 {
|
||||
return m.Where(where[0], where[1:]...).All()
|
||||
}
|
||||
var (
|
||||
softDeletingCondition = m.getConditionForSoftDeleting()
|
||||
conditionWhere, conditionExtra, conditionArgs = m.formatCondition(limit1, false)
|
||||
)
|
||||
if !m.unscoped && softDeletingCondition != "" {
|
||||
if conditionWhere == "" {
|
||||
conditionWhere = " WHERE "
|
||||
} else {
|
||||
conditionWhere += " AND "
|
||||
}
|
||||
conditionWhere += softDeletingCondition
|
||||
}
|
||||
|
||||
conditionWhere, conditionExtra, conditionArgs := m.formatCondition(limit1, false)
|
||||
// DO NOT quote the m.fields where, in case of fields like:
|
||||
// DISTINCT t.user_id uid
|
||||
return m.doGetAllBySql(
|
||||
@ -151,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 {
|
||||
@ -170,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) {
|
||||
@ -196,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) {
|
||||
@ -217,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)
|
||||
@ -237,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.doWithScanStruct(pointer)
|
||||
}
|
||||
|
||||
// Structs retrieves records from table and converts them into given struct slice.
|
||||
// The parameter <pointer> should be type of *[]struct/*[]*struct. It can create and fill the struct
|
||||
// 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)
|
||||
@ -261,14 +252,17 @@ func (m *Model) Structs(pointer interface{}, where ...interface{}) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return all.Structs(pointer)
|
||||
if err = all.Structs(pointer); err != nil {
|
||||
return err
|
||||
}
|
||||
return m.doWithScanStructs(pointer)
|
||||
}
|
||||
|
||||
// Scan automatically calls Struct or Structs function according to the type of parameter <pointer>.
|
||||
// It calls function Struct if <pointer> is type of *struct/**struct.
|
||||
// It calls function Structs if <pointer> is type of *[]struct/*[]*struct.
|
||||
// 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
|
||||
@ -287,21 +281,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 {
|
||||
@ -319,7 +312,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) {
|
||||
@ -331,7 +324,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 {
|
||||
@ -343,19 +336,7 @@ func (m *Model) Count(where ...interface{}) (int, error) {
|
||||
// DISTINCT t.user_id uid
|
||||
countFields = fmt.Sprintf(`COUNT(%s)`, m.fields)
|
||||
}
|
||||
var (
|
||||
softDeletingCondition = m.getConditionForSoftDeleting()
|
||||
conditionWhere, conditionExtra, conditionArgs = m.formatCondition(false, true)
|
||||
)
|
||||
if !m.unscoped && softDeletingCondition != "" {
|
||||
if conditionWhere == "" {
|
||||
conditionWhere = " WHERE "
|
||||
} else {
|
||||
conditionWhere += " AND "
|
||||
}
|
||||
conditionWhere += softDeletingCondition
|
||||
}
|
||||
|
||||
conditionWhere, conditionExtra, conditionArgs := m.formatCondition(false, true)
|
||||
s := fmt.Sprintf("SELECT %s FROM %s%s", countFields, m.tables, conditionWhere+conditionExtra)
|
||||
if len(m.groupBy) > 0 {
|
||||
s = fmt.Sprintf("SELECT COUNT(1) FROM (%s) count_alias", s)
|
||||
|
||||
@ -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 {
|
||||
@ -92,7 +92,7 @@ func (m *Model) doMappingAndFilterForInsertOrUpdateDataMap(data Map, allowOmitEm
|
||||
return nil, err
|
||||
}
|
||||
// Remove key-value pairs of which the value is empty.
|
||||
if allowOmitEmpty && m.option&OPTION_OMITEMPTY > 0 {
|
||||
if allowOmitEmpty && m.option&OptionOmitEmpty > 0 {
|
||||
tempMap := make(Map, len(data))
|
||||
for k, v := range data {
|
||||
if empty.IsEmpty(v) {
|
||||
@ -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 {
|
||||
@ -206,7 +206,7 @@ func (m *Model) formatCondition(limit1 bool, isCountStatement bool) (conditionWh
|
||||
case whereHolderWhere:
|
||||
if conditionWhere == "" {
|
||||
newWhere, newArgs := formatWhere(
|
||||
m.db, v.where, v.args, m.option&OPTION_OMITEMPTY > 0,
|
||||
m.db, v.where, v.args, m.option&OptionOmitEmpty > 0,
|
||||
)
|
||||
if len(newWhere) > 0 {
|
||||
conditionWhere = newWhere
|
||||
@ -218,7 +218,7 @@ func (m *Model) formatCondition(limit1 bool, isCountStatement bool) (conditionWh
|
||||
|
||||
case whereHolderAnd:
|
||||
newWhere, newArgs := formatWhere(
|
||||
m.db, v.where, v.args, m.option&OPTION_OMITEMPTY > 0,
|
||||
m.db, v.where, v.args, m.option&OptionOmitEmpty > 0,
|
||||
)
|
||||
if len(newWhere) > 0 {
|
||||
if len(conditionWhere) == 0 {
|
||||
@ -233,7 +233,7 @@ func (m *Model) formatCondition(limit1 bool, isCountStatement bool) (conditionWh
|
||||
|
||||
case whereHolderOr:
|
||||
newWhere, newArgs := formatWhere(
|
||||
m.db, v.where, v.args, m.option&OPTION_OMITEMPTY > 0,
|
||||
m.db, v.where, v.args, m.option&OptionOmitEmpty > 0,
|
||||
)
|
||||
if len(newWhere) > 0 {
|
||||
if len(conditionWhere) == 0 {
|
||||
@ -248,24 +248,38 @@ func (m *Model) formatCondition(limit1 bool, isCountStatement bool) (conditionWh
|
||||
}
|
||||
}
|
||||
}
|
||||
if conditionWhere != "" {
|
||||
conditionWhere = " WHERE " + conditionWhere
|
||||
// Soft deletion.
|
||||
softDeletingCondition := m.getConditionForSoftDeleting()
|
||||
if !m.unscoped && softDeletingCondition != "" {
|
||||
if conditionWhere == "" {
|
||||
conditionWhere = fmt.Sprintf(` WHERE %s`, softDeletingCondition)
|
||||
} else {
|
||||
conditionWhere = fmt.Sprintf(` WHERE (%s) AND %s`, conditionWhere, softDeletingCondition)
|
||||
}
|
||||
} else {
|
||||
if conditionWhere != "" {
|
||||
conditionWhere = " WHERE " + conditionWhere
|
||||
}
|
||||
}
|
||||
// GROUP BY.
|
||||
if m.groupBy != "" {
|
||||
conditionExtra += " GROUP BY " + m.groupBy
|
||||
}
|
||||
// HAVING.
|
||||
if len(m.having) > 0 {
|
||||
havingStr, havingArgs := formatWhere(
|
||||
m.db, m.having[0], gconv.Interfaces(m.having[1]), m.option&OPTION_OMITEMPTY > 0,
|
||||
m.db, m.having[0], gconv.Interfaces(m.having[1]), m.option&OptionOmitEmpty > 0,
|
||||
)
|
||||
if len(havingStr) > 0 {
|
||||
conditionExtra += " HAVING " + havingStr
|
||||
conditionArgs = append(conditionArgs, havingArgs...)
|
||||
}
|
||||
}
|
||||
// ORDER BY.
|
||||
if m.orderBy != "" {
|
||||
conditionExtra += " ORDER BY " + m.orderBy
|
||||
}
|
||||
// LIMIT.
|
||||
if !isCountStatement {
|
||||
if m.limit != 0 {
|
||||
if m.start >= 0 {
|
||||
@ -288,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))
|
||||
|
||||
256
database/gdb/gdb_model_with.go
Normal file
256
database/gdb/gdb_model_with.go
Normal file
@ -0,0 +1,256 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gdb
|
||||
|
||||
import (
|
||||
"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"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// With creates and returns an ORM model based on meta data of given object.
|
||||
// It also enables model association operations feature on given `object`.
|
||||
// It can be called multiple times to add one or more objects to model and enable
|
||||
// their mode association operations feature.
|
||||
// For example, if given struct definition:
|
||||
// type User struct {
|
||||
// gmeta.Meta `orm:"table:user"`
|
||||
// Id int `json:"id"`
|
||||
// Name string `json:"name"`
|
||||
// UserDetail *UserDetail `orm:"with:uid=id"`
|
||||
// UserScores []*UserScores `orm:"with:uid=id"`
|
||||
// }
|
||||
// We can enable model association operations on attribute `UserDetail` and `UserScores` by:
|
||||
// db.With(User{}.UserDetail).With(User{}.UserDetail).Scan(xxx)
|
||||
// Or:
|
||||
// db.With(UserDetail{}).With(UserDetail{}).Scan(xxx)
|
||||
func (m *Model) With(object interface{}) *Model {
|
||||
model := m.getModel()
|
||||
if m.tables == "" {
|
||||
m.tables = m.db.QuotePrefixTableName(getTableNameFromOrmTag(object))
|
||||
return model
|
||||
}
|
||||
model.withArray = append(model.withArray, object)
|
||||
return model
|
||||
}
|
||||
|
||||
// WithAll enables model association operations on all objects that have "with" tag in the struct.
|
||||
func (m *Model) WithAll() *Model {
|
||||
model := m.getModel()
|
||||
model.withAll = true
|
||||
return model
|
||||
}
|
||||
|
||||
// getWithTagObjectArrayFrom retrieves and returns object array that have "with" tag in the struct.
|
||||
func (m *Model) getWithTagObjectArrayFrom(pointer interface{}) ([]interface{}, error) {
|
||||
fieldMap, err := structs.FieldMap(pointer, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
withTagObjectArray := make([]interface{}, 0)
|
||||
for _, fieldValue := range fieldMap {
|
||||
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
|
||||
}
|
||||
withTagObjectArray = append(withTagObjectArray, fieldValue.Value.Interface())
|
||||
}
|
||||
return withTagObjectArray, nil
|
||||
}
|
||||
|
||||
// doWithScanStruct handles model association operations feature for single struct.
|
||||
func (m *Model) doWithScanStruct(pointer interface{}) error {
|
||||
var (
|
||||
err error
|
||||
withArray = m.withArray
|
||||
)
|
||||
if m.withAll {
|
||||
withArray, err = m.getWithTagObjectArrayFrom(pointer)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if len(withArray) == 0 {
|
||||
return nil
|
||||
}
|
||||
fieldMap, err := structs.FieldMap(pointer, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for withIndex, withItem := range 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,
|
||||
)
|
||||
}
|
||||
bindToReflectValue := fieldValue.Value
|
||||
switch bindToReflectValue.Kind() {
|
||||
case reflect.Array, reflect.Slice:
|
||||
if bindToReflectValue.CanAddr() {
|
||||
bindToReflectValue = bindToReflectValue.Addr()
|
||||
}
|
||||
}
|
||||
model := m.db.With(fieldValue.Value)
|
||||
for i, v := range withArray {
|
||||
if i == withIndex {
|
||||
continue
|
||||
}
|
||||
model = model.With(v)
|
||||
}
|
||||
err = model.Fields(withItemReflectValueType.FieldKeys()).
|
||||
Where(relatedFieldName, relatedFieldValue).
|
||||
Scan(bindToReflectValue)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// doWithScanStructs handles model association operations feature for struct slice.
|
||||
func (m *Model) doWithScanStructs(pointer interface{}) error {
|
||||
var (
|
||||
err error
|
||||
withArray = m.withArray
|
||||
)
|
||||
if m.withAll {
|
||||
withArray, err = m.getWithTagObjectArrayFrom(pointer)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if len(withArray) == 0 {
|
||||
return nil
|
||||
}
|
||||
fieldMap, err := structs.FieldMap(pointer, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for withIndex, withItem := range withArray {
|
||||
withItemReflectValueType, err := structs.StructType(withItem)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
withItemReflectValueTypeStr := gstr.TrimAll(withItemReflectValueType.String(), "*[]")
|
||||
for fieldName, 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 slice of related attribute from `pointer`.
|
||||
for attributeName, _ := range fieldMap {
|
||||
if utils.EqualFoldWithoutChars(attributeName, relatedAttrName) {
|
||||
relatedFieldValue = ListItemValuesUnique(pointer, attributeName)
|
||||
break
|
||||
}
|
||||
}
|
||||
if relatedFieldValue == nil {
|
||||
return gerror.Newf(
|
||||
`cannot find the related value for attribute name "%s" of with tag "%s"`,
|
||||
relatedAttrName, withTag,
|
||||
)
|
||||
}
|
||||
model := m.db.With(fieldValue.Value)
|
||||
for i, v := range withArray {
|
||||
if i == withIndex {
|
||||
continue
|
||||
}
|
||||
model = model.With(v)
|
||||
}
|
||||
err = model.Fields(withItemReflectValueType.FieldKeys()).
|
||||
Where(relatedFieldName, relatedFieldValue).
|
||||
ScanList(pointer, fieldName, withTag)
|
||||
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,14 +31,14 @@ 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
|
||||
if s.tx != nil {
|
||||
m = s.tx.Table(table)
|
||||
m = s.tx.Model(table)
|
||||
} else {
|
||||
m = s.db.Table(table)
|
||||
m = s.db.Model(table)
|
||||
}
|
||||
// Do not change the schema of the original db,
|
||||
// it here creates a new db and changes its schema.
|
||||
|
||||
@ -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
|
||||
|
||||
@ -10,25 +10,23 @@ import (
|
||||
"database/sql"
|
||||
"github.com/gogf/gf/container/gmap"
|
||||
"github.com/gogf/gf/encoding/gparser"
|
||||
"github.com/gogf/gf/errors/gerror"
|
||||
"github.com/gogf/gf/internal/empty"
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
"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 +35,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() {
|
||||
@ -54,29 +52,10 @@ func (r Record) Struct(pointer interface{}) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
// Special handling for parameter type: reflect.Value
|
||||
if _, ok := pointer.(reflect.Value); ok {
|
||||
return convertMapToStruct(r.Map(), pointer)
|
||||
}
|
||||
var (
|
||||
reflectValue = reflect.ValueOf(pointer)
|
||||
reflectKind = reflectValue.Kind()
|
||||
)
|
||||
if reflectKind != reflect.Ptr {
|
||||
return gerror.New("parameter should be type of *struct/**struct")
|
||||
}
|
||||
reflectValue = reflectValue.Elem()
|
||||
reflectKind = reflectValue.Kind()
|
||||
if reflectKind == reflect.Invalid {
|
||||
return gerror.New("parameter is an invalid pointer, maybe nil")
|
||||
}
|
||||
if reflectKind != reflect.Ptr && reflectKind != reflect.Struct {
|
||||
return gerror.New("parameter should be type of *struct/**struct")
|
||||
}
|
||||
return convertMapToStruct(r.Map(), pointer)
|
||||
return gconv.StructTag(r.Map(), pointer, OrmTagForStruct)
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
@ -7,16 +7,13 @@
|
||||
package gdb
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/container/gvar"
|
||||
"math"
|
||||
"reflect"
|
||||
|
||||
"github.com/gogf/gf/encoding/gparser"
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
"math"
|
||||
)
|
||||
|
||||
// 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 +29,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 +49,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 +71,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 +92,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 +120,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 +131,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 +142,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 +153,7 @@ func (r Result) MapKeyUint(key string) map[uint]Map {
|
||||
return m
|
||||
}
|
||||
|
||||
// RecordKeyInt converts <r> to a map[int]Record of which key is specified by <key>.
|
||||
// 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 +164,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 +175,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,52 +186,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)
|
||||
reflectKind = reflectValue.Kind()
|
||||
)
|
||||
if reflectKind != reflect.Ptr {
|
||||
return fmt.Errorf("parameter should be type of *[]struct/*[]*struct, but got: %v", reflectKind)
|
||||
}
|
||||
reflectValue = reflectValue.Elem()
|
||||
reflectKind = reflectValue.Kind()
|
||||
if reflectKind != reflect.Slice && reflectKind != reflect.Array {
|
||||
return fmt.Errorf("parameter should be type of *[]struct/*[]*struct, but got: %v", reflectKind)
|
||||
}
|
||||
length := len(r)
|
||||
if length == 0 {
|
||||
// The pointed slice is not empty.
|
||||
if reflectValue.Len() > 0 {
|
||||
// It here checks if it has struct item, which is already initialized.
|
||||
// It then returns error to warn the developer its empty and no conversion.
|
||||
if v := reflectValue.Index(0); v.Kind() != reflect.Ptr {
|
||||
return sql.ErrNoRows
|
||||
}
|
||||
}
|
||||
// Do nothing for empty struct slice.
|
||||
return nil
|
||||
}
|
||||
var (
|
||||
reflectType = reflect.TypeOf(pointer)
|
||||
array = reflect.MakeSlice(reflectType.Elem(), length, length)
|
||||
itemType = array.Index(0).Type()
|
||||
itemKind = itemType.Kind()
|
||||
)
|
||||
for i := 0; i < length; i++ {
|
||||
var elem reflect.Value
|
||||
if itemKind == reflect.Ptr {
|
||||
elem = reflect.New(itemType.Elem())
|
||||
} else {
|
||||
elem = reflect.New(itemType).Elem()
|
||||
}
|
||||
if err = r[i].Struct(elem); err != nil {
|
||||
return fmt.Errorf(`slice element conversion failed: %s`, err.Error())
|
||||
}
|
||||
array.Index(i).Set(elem)
|
||||
}
|
||||
reflect.ValueOf(pointer).Elem().Set(array)
|
||||
return nil
|
||||
return gconv.StructsTag(r, pointer, OrmTagForStruct)
|
||||
}
|
||||
|
||||
@ -11,11 +11,12 @@ import (
|
||||
"github.com/gogf/gf/errors/gerror"
|
||||
"github.com/gogf/gf/text/gstr"
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
"github.com/gogf/gf/util/gutil"
|
||||
"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 {
|
||||
@ -37,10 +38,13 @@ 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) {
|
||||
if r.IsEmpty() {
|
||||
return nil
|
||||
}
|
||||
// Necessary checks for parameters.
|
||||
if bindToAttrName == "" {
|
||||
return gerror.New(`bindToAttrName should not be empty`)
|
||||
@ -112,7 +116,17 @@ func (r Result) ScanList(listPointer interface{}, bindToAttrName string, relatio
|
||||
relationFromAttrName = relationKV[0]
|
||||
relationKVStr = relationKV[1]
|
||||
}
|
||||
array := gstr.SplitAndTrim(relationKVStr, ":")
|
||||
// The relation key string of table filed name and attribute name
|
||||
// can be joined with char '=' or ':'.
|
||||
array := gstr.SplitAndTrim(relationKVStr, "=")
|
||||
if len(array) == 1 {
|
||||
// Compatible with old splitting char ':'.
|
||||
array = gstr.SplitAndTrim(relationKVStr, ":")
|
||||
}
|
||||
if len(array) == 1 {
|
||||
// The relation names are the same.
|
||||
array = []string{relationKVStr, relationKVStr}
|
||||
}
|
||||
if len(array) == 2 {
|
||||
// Defined table field to relation attribute name.
|
||||
// Like:
|
||||
@ -120,6 +134,15 @@ func (r Result) ScanList(listPointer interface{}, bindToAttrName string, relatio
|
||||
// uid:UserId
|
||||
relationResultFieldName = array[0]
|
||||
relationBindToSubAttrName = array[1]
|
||||
if key, _ := gutil.MapPossibleItemByKey(r[0].Map(), relationResultFieldName); key == "" {
|
||||
return gerror.Newf(
|
||||
`cannot find possible related table field name "%s" from given relation key "%s"`,
|
||||
relationResultFieldName,
|
||||
relationKVStr,
|
||||
)
|
||||
} else {
|
||||
relationResultFieldName = key
|
||||
}
|
||||
} else {
|
||||
return gerror.New(`parameter relationKV should be format of "ResultFieldName:BindToAttrName"`)
|
||||
}
|
||||
@ -152,8 +175,9 @@ func (r Result) ScanList(listPointer interface{}, bindToAttrName string, relatio
|
||||
|
||||
// Bind to relation conditions.
|
||||
var (
|
||||
relationFromAttrValue reflect.Value
|
||||
relationFromAttrField reflect.Value
|
||||
relationFromAttrValue reflect.Value
|
||||
relationFromAttrField reflect.Value
|
||||
relationBindToSubAttrNameChecked bool
|
||||
)
|
||||
for i := 0; i < arrayValue.Len(); i++ {
|
||||
arrayElemValue := arrayValue.Index(i)
|
||||
@ -187,6 +211,29 @@ func (r Result) ScanList(listPointer interface{}, bindToAttrName string, relatio
|
||||
if len(relationDataMap) > 0 && !relationFromAttrValue.IsValid() {
|
||||
return gerror.Newf(`invalid relation specified: "%v"`, relationKV)
|
||||
}
|
||||
// Check and find possible bind to attribute name.
|
||||
if relationKVStr != "" && !relationBindToSubAttrNameChecked {
|
||||
relationFromAttrField = relationFromAttrValue.FieldByName(relationBindToSubAttrName)
|
||||
if !relationFromAttrField.IsValid() {
|
||||
var (
|
||||
relationFromAttrType = relationFromAttrValue.Type()
|
||||
filedMap = make(map[string]interface{})
|
||||
)
|
||||
for i := 0; i < relationFromAttrType.NumField(); i++ {
|
||||
filedMap[relationFromAttrType.Field(i).Name] = struct{}{}
|
||||
}
|
||||
if key, _ := gutil.MapPossibleItemByKey(filedMap, relationBindToSubAttrName); key == "" {
|
||||
return gerror.Newf(
|
||||
`cannot find possible related attribute name "%s" from given relation key "%s"`,
|
||||
relationBindToSubAttrName,
|
||||
relationKVStr,
|
||||
)
|
||||
} else {
|
||||
relationBindToSubAttrName = key
|
||||
}
|
||||
}
|
||||
relationBindToSubAttrNameChecked = true
|
||||
}
|
||||
switch bindToAttrKind {
|
||||
case reflect.Array, reflect.Slice:
|
||||
if len(relationDataMap) > 0 {
|
||||
@ -207,7 +254,12 @@ func (r Result) ScanList(listPointer interface{}, bindToAttrName string, relatio
|
||||
}
|
||||
|
||||
case reflect.Ptr:
|
||||
e := reflect.New(bindToAttrType.Elem()).Elem()
|
||||
var element reflect.Value
|
||||
if bindToAttrValue.IsNil() {
|
||||
element = reflect.New(bindToAttrType.Elem()).Elem()
|
||||
} else {
|
||||
element = bindToAttrValue.Elem()
|
||||
}
|
||||
if len(relationDataMap) > 0 {
|
||||
relationFromAttrField = relationFromAttrValue.FieldByName(relationBindToSubAttrName)
|
||||
if relationFromAttrField.IsValid() {
|
||||
@ -216,7 +268,7 @@ func (r Result) ScanList(listPointer interface{}, bindToAttrName string, relatio
|
||||
// There's no relational data.
|
||||
continue
|
||||
}
|
||||
if err = gconv.Struct(v, e); err != nil {
|
||||
if err = gconv.Struct(v, element); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
@ -224,19 +276,22 @@ func (r Result) ScanList(listPointer interface{}, bindToAttrName string, relatio
|
||||
return gerror.Newf(`invalid relation specified: "%v"`, relationKV)
|
||||
}
|
||||
} else {
|
||||
if i >= len(r) {
|
||||
// There's no relational data.
|
||||
continue
|
||||
}
|
||||
v := r[i]
|
||||
if v == nil {
|
||||
// There's no relational data.
|
||||
continue
|
||||
}
|
||||
if err = gconv.Struct(v, e); err != nil {
|
||||
if err = gconv.Struct(v, element); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
bindToAttrValue.Set(e.Addr())
|
||||
bindToAttrValue.Set(element.Addr())
|
||||
|
||||
case reflect.Struct:
|
||||
e := reflect.New(bindToAttrType).Elem()
|
||||
if len(relationDataMap) > 0 {
|
||||
relationFromAttrField = relationFromAttrValue.FieldByName(relationBindToSubAttrName)
|
||||
if relationFromAttrField.IsValid() {
|
||||
@ -245,7 +300,7 @@ func (r Result) ScanList(listPointer interface{}, bindToAttrName string, relatio
|
||||
// There's no relational data.
|
||||
continue
|
||||
}
|
||||
if err = gconv.Struct(relationDataItem, e); err != nil {
|
||||
if err = gconv.Struct(relationDataItem, bindToAttrValue); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
@ -253,16 +308,19 @@ func (r Result) ScanList(listPointer interface{}, bindToAttrName string, relatio
|
||||
return gerror.Newf(`invalid relation specified: "%v"`, relationKV)
|
||||
}
|
||||
} else {
|
||||
if i >= len(r) {
|
||||
// There's no relational data.
|
||||
continue
|
||||
}
|
||||
relationDataItem := r[i]
|
||||
if relationDataItem == nil {
|
||||
// There's no relational data.
|
||||
continue
|
||||
}
|
||||
if err = gconv.Struct(relationDataItem, e); err != nil {
|
||||
if err = gconv.Struct(relationDataItem, bindToAttrValue); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
bindToAttrValue.Set(e)
|
||||
|
||||
default:
|
||||
return gerror.Newf(`unsupported attribute type: %s`, bindToAttrKind.String())
|
||||
|
||||
@ -56,8 +56,8 @@ func Test_Custom_Driver(t *testing.T) {
|
||||
gdb.AddConfigNode("driver-test", gdb.ConfigNode{
|
||||
Host: "127.0.0.1",
|
||||
Port: "3306",
|
||||
User: USER,
|
||||
Pass: PASS,
|
||||
User: TestDbUser,
|
||||
Pass: TestDbPass,
|
||||
Name: "test",
|
||||
Type: customDriverName,
|
||||
Role: "master",
|
||||
@ -67,7 +67,7 @@ func Test_Custom_Driver(t *testing.T) {
|
||||
t.Assert(latestSqlString.Val(), "")
|
||||
sqlString := "select 10000"
|
||||
value, err := g.DB("driver-test").GetValue(sqlString)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
t.Assert(value, 10000)
|
||||
t.Assert(latestSqlString.Val(), sqlString)
|
||||
})
|
||||
|
||||
@ -18,13 +18,14 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
SIZE = 10
|
||||
TABLE = "user"
|
||||
SCHEMA1 = "test1"
|
||||
SCHEMA2 = "test2"
|
||||
PREFIX1 = "gf_"
|
||||
USER = "root"
|
||||
PASS = "12345678"
|
||||
TableSize = 10
|
||||
TableName = "user"
|
||||
TestSchema1 = "test1"
|
||||
TestSchema2 = "test2"
|
||||
TableNamePrefix1 = "gf_"
|
||||
TestDbUser = "root"
|
||||
TestDbPass = "12345678"
|
||||
CreateTime = "2018-10-24 10:00:00"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -38,12 +39,12 @@ func init() {
|
||||
"name": true,
|
||||
"type": true,
|
||||
}, false)
|
||||
gtest.Assert(err, nil)
|
||||
gtest.AssertNil(err)
|
||||
configNode = gdb.ConfigNode{
|
||||
Host: "127.0.0.1",
|
||||
Port: "3306",
|
||||
User: USER,
|
||||
Pass: PASS,
|
||||
User: TestDbUser,
|
||||
Pass: TestDbPass,
|
||||
Name: parser.GetOpt("name", ""),
|
||||
Type: parser.GetOpt("type", "mysql"),
|
||||
Role: "master",
|
||||
@ -54,7 +55,7 @@ func init() {
|
||||
MaxConnLifetime: 600,
|
||||
}
|
||||
nodePrefix := configNode
|
||||
nodePrefix.Prefix = PREFIX1
|
||||
nodePrefix.Prefix = TableNamePrefix1
|
||||
gdb.AddConfigNode("test", configNode)
|
||||
gdb.AddConfigNode("prefix", nodePrefix)
|
||||
gdb.AddConfigNode(gdb.DefaultGroupName, configNode)
|
||||
@ -65,13 +66,13 @@ func init() {
|
||||
db = r
|
||||
}
|
||||
schemaTemplate := "CREATE DATABASE IF NOT EXISTS `%s` CHARACTER SET UTF8"
|
||||
if _, err := db.Exec(fmt.Sprintf(schemaTemplate, SCHEMA1)); err != nil {
|
||||
if _, err := db.Exec(fmt.Sprintf(schemaTemplate, TestSchema1)); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
if _, err := db.Exec(fmt.Sprintf(schemaTemplate, SCHEMA2)); err != nil {
|
||||
if _, err := db.Exec(fmt.Sprintf(schemaTemplate, TestSchema2)); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
db.SetSchema(SCHEMA1)
|
||||
db.SetSchema(TestSchema1)
|
||||
|
||||
// Prefix db.
|
||||
if r, err := gdb.New("prefix"); err != nil {
|
||||
@ -79,13 +80,13 @@ func init() {
|
||||
} else {
|
||||
dbPrefix = r
|
||||
}
|
||||
if _, err := dbPrefix.Exec(fmt.Sprintf(schemaTemplate, SCHEMA1)); err != nil {
|
||||
if _, err := dbPrefix.Exec(fmt.Sprintf(schemaTemplate, TestSchema1)); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
if _, err := dbPrefix.Exec(fmt.Sprintf(schemaTemplate, SCHEMA2)); err != nil {
|
||||
if _, err := dbPrefix.Exec(fmt.Sprintf(schemaTemplate, TestSchema2)); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
dbPrefix.SetSchema(SCHEMA1)
|
||||
dbPrefix.SetSchema(TestSchema1)
|
||||
}
|
||||
|
||||
func createTable(table ...string) string {
|
||||
@ -104,7 +105,7 @@ func createTableWithDb(db gdb.DB, table ...string) (name string) {
|
||||
if len(table) > 0 {
|
||||
name = table[0]
|
||||
} else {
|
||||
name = fmt.Sprintf(`%s_%d`, TABLE, gtime.TimestampNano())
|
||||
name = fmt.Sprintf(`%s_%d`, TableName, gtime.TimestampNano())
|
||||
}
|
||||
dropTableWithDb(db, name)
|
||||
|
||||
@ -184,22 +185,22 @@ func createTableWithDb(db gdb.DB, table ...string) (name string) {
|
||||
func createInitTableWithDb(db gdb.DB, table ...string) (name string) {
|
||||
name = createTableWithDb(db, table...)
|
||||
array := garray.New(true)
|
||||
for i := 1; i <= SIZE; i++ {
|
||||
for i := 1; i <= TableSize; i++ {
|
||||
array.Append(g.Map{
|
||||
"id": i,
|
||||
"passport": fmt.Sprintf(`user_%d`, i),
|
||||
"password": fmt.Sprintf(`pass_%d`, i),
|
||||
"nickname": fmt.Sprintf(`name_%d`, i),
|
||||
"create_time": gtime.NewFromStr("2018-10-24 10:00:00").String(),
|
||||
"create_time": gtime.NewFromStr(CreateTime).String(),
|
||||
})
|
||||
}
|
||||
|
||||
result, err := db.BatchInsert(name, array.Slice())
|
||||
gtest.Assert(err, nil)
|
||||
gtest.AssertNil(err)
|
||||
|
||||
n, e := result.RowsAffected()
|
||||
gtest.Assert(e, nil)
|
||||
gtest.Assert(n, SIZE)
|
||||
gtest.Assert(n, TableSize)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
641
database/gdb/gdb_z_mysql_association_with_test.go
Normal file
641
database/gdb/gdb_z_mysql_association_with_test.go
Normal file
@ -0,0 +1,641 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gdb_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/test/gtest"
|
||||
"github.com/gogf/gf/util/gmeta"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_Table_Relation_With_Scan(t *testing.T) {
|
||||
var (
|
||||
tableUser = "user"
|
||||
tableUserDetail = "user_detail"
|
||||
tableUserScores = "user_scores"
|
||||
)
|
||||
if _, err := db.Exec(fmt.Sprintf(`
|
||||
CREATE TABLE IF NOT EXISTS %s (
|
||||
id int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||
name varchar(45) NOT NULL,
|
||||
PRIMARY KEY (id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
`, tableUser)); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
defer dropTable(tableUser)
|
||||
|
||||
if _, err := db.Exec(fmt.Sprintf(`
|
||||
CREATE TABLE IF NOT EXISTS %s (
|
||||
uid int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||
address varchar(45) NOT NULL,
|
||||
PRIMARY KEY (uid)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
`, tableUserDetail)); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
defer dropTable(tableUserDetail)
|
||||
|
||||
if _, err := db.Exec(fmt.Sprintf(`
|
||||
CREATE TABLE IF NOT EXISTS %s (
|
||||
id int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||
uid int(10) unsigned NOT NULL,
|
||||
score int(10) unsigned NOT NULL,
|
||||
PRIMARY KEY (id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
`, tableUserScores)); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
defer dropTable(tableUserScores)
|
||||
|
||||
type UserDetail struct {
|
||||
gmeta.Meta `orm:"table:user_detail"`
|
||||
Uid int `json:"uid"`
|
||||
Address string `json:"address"`
|
||||
}
|
||||
|
||||
type UserScores struct {
|
||||
gmeta.Meta `orm:"table:user_scores"`
|
||||
Id int `json:"id"`
|
||||
Uid int `json:"uid"`
|
||||
Score int `json:"score"`
|
||||
}
|
||||
|
||||
type User struct {
|
||||
gmeta.Meta `orm:"table:user"`
|
||||
Id int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
UserDetail *UserDetail `orm:"with:uid=id"`
|
||||
UserScores []*UserScores `orm:"with:uid=id"`
|
||||
}
|
||||
|
||||
// Initialize the data.
|
||||
var err error
|
||||
for i := 1; i <= 5; i++ {
|
||||
// User.
|
||||
_, err = db.Insert(tableUser, g.Map{
|
||||
"id": i,
|
||||
"name": fmt.Sprintf(`name_%d`, i),
|
||||
})
|
||||
gtest.Assert(err, nil)
|
||||
// Detail.
|
||||
_, err = db.Insert(tableUserDetail, g.Map{
|
||||
"uid": i,
|
||||
"address": fmt.Sprintf(`address_%d`, i),
|
||||
})
|
||||
gtest.Assert(err, nil)
|
||||
// Scores.
|
||||
for j := 1; j <= 5; j++ {
|
||||
_, err = db.Insert(tableUserScores, g.Map{
|
||||
"uid": i,
|
||||
"score": j,
|
||||
})
|
||||
gtest.Assert(err, nil)
|
||||
}
|
||||
}
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var user *User
|
||||
err := db.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`)
|
||||
t.Assert(len(user.UserScores), 5)
|
||||
t.Assert(user.UserScores[0].Uid, 3)
|
||||
t.Assert(user.UserScores[0].Score, 1)
|
||||
t.Assert(user.UserScores[4].Uid, 3)
|
||||
t.Assert(user.UserScores[4].Score, 5)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var user User
|
||||
err := db.With(user).
|
||||
With(user.UserDetail).
|
||||
With(user.UserScores).
|
||||
Where("id", 4).
|
||||
Scan(&user)
|
||||
t.AssertNil(err)
|
||||
t.Assert(user.Id, 4)
|
||||
t.AssertNE(user.UserDetail, nil)
|
||||
t.Assert(user.UserDetail.Uid, 4)
|
||||
t.Assert(user.UserDetail.Address, `address_4`)
|
||||
t.Assert(len(user.UserScores), 5)
|
||||
t.Assert(user.UserScores[0].Uid, 4)
|
||||
t.Assert(user.UserScores[0].Score, 1)
|
||||
t.Assert(user.UserScores[4].Uid, 4)
|
||||
t.Assert(user.UserScores[4].Score, 5)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var user *User
|
||||
err := db.With(User{}).
|
||||
With(UserDetail{}).
|
||||
With(UserScores{}).
|
||||
Where("id", 4).
|
||||
Scan(&user)
|
||||
t.AssertNil(err)
|
||||
t.Assert(user.Id, 4)
|
||||
t.AssertNE(user.UserDetail, nil)
|
||||
t.Assert(user.UserDetail.Uid, 4)
|
||||
t.Assert(user.UserDetail.Address, `address_4`)
|
||||
t.Assert(len(user.UserScores), 5)
|
||||
t.Assert(user.UserScores[0].Uid, 4)
|
||||
t.Assert(user.UserScores[0].Score, 1)
|
||||
t.Assert(user.UserScores[4].Uid, 4)
|
||||
t.Assert(user.UserScores[4].Score, 5)
|
||||
})
|
||||
// With part attribute: UserDetail.
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var user User
|
||||
err := db.With(user).
|
||||
With(user.UserDetail).
|
||||
Where("id", 4).
|
||||
Scan(&user)
|
||||
t.AssertNil(err)
|
||||
t.Assert(user.Id, 4)
|
||||
t.AssertNE(user.UserDetail, nil)
|
||||
t.Assert(user.UserDetail.Uid, 4)
|
||||
t.Assert(user.UserDetail.Address, `address_4`)
|
||||
t.Assert(len(user.UserScores), 0)
|
||||
})
|
||||
// With part attribute: UserScores.
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var user User
|
||||
err := db.With(user).
|
||||
With(user.UserScores).
|
||||
Where("id", 4).
|
||||
Scan(&user)
|
||||
t.AssertNil(err)
|
||||
t.Assert(user.Id, 4)
|
||||
t.Assert(user.UserDetail, nil)
|
||||
t.Assert(len(user.UserScores), 5)
|
||||
t.Assert(user.UserScores[0].Uid, 4)
|
||||
t.Assert(user.UserScores[0].Score, 1)
|
||||
t.Assert(user.UserScores[4].Uid, 4)
|
||||
t.Assert(user.UserScores[4].Score, 5)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Table_Relation_With_ScanList(t *testing.T) {
|
||||
var (
|
||||
tableUser = "user"
|
||||
tableUserDetail = "user_detail"
|
||||
tableUserScores = "user_scores"
|
||||
)
|
||||
if _, err := db.Exec(fmt.Sprintf(`
|
||||
CREATE TABLE IF NOT EXISTS %s (
|
||||
id int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||
name varchar(45) NOT NULL,
|
||||
PRIMARY KEY (id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
`, tableUser)); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
defer dropTable(tableUser)
|
||||
|
||||
if _, err := db.Exec(fmt.Sprintf(`
|
||||
CREATE TABLE IF NOT EXISTS %s (
|
||||
uid int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||
address varchar(45) NOT NULL,
|
||||
PRIMARY KEY (uid)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
`, tableUserDetail)); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
defer dropTable(tableUserDetail)
|
||||
|
||||
if _, err := db.Exec(fmt.Sprintf(`
|
||||
CREATE TABLE IF NOT EXISTS %s (
|
||||
id int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||
uid int(10) unsigned NOT NULL,
|
||||
score int(10) unsigned NOT NULL,
|
||||
PRIMARY KEY (id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
`, tableUserScores)); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
defer dropTable(tableUserScores)
|
||||
|
||||
type UserDetail struct {
|
||||
gmeta.Meta `orm:"table:user_detail"`
|
||||
Uid int `json:"uid"`
|
||||
Address string `json:"address"`
|
||||
}
|
||||
|
||||
type UserScores struct {
|
||||
gmeta.Meta `orm:"table:user_scores"`
|
||||
Id int `json:"id"`
|
||||
Uid int `json:"uid"`
|
||||
Score int `json:"score"`
|
||||
}
|
||||
|
||||
type User struct {
|
||||
gmeta.Meta `orm:"table:user"`
|
||||
Id int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
UserDetail *UserDetail `orm:"with:uid=id"`
|
||||
UserScores []*UserScores `orm:"with:uid=id"`
|
||||
}
|
||||
|
||||
// Initialize the data.
|
||||
var err error
|
||||
for i := 1; i <= 5; i++ {
|
||||
// User.
|
||||
_, err = db.Insert(tableUser, g.Map{
|
||||
"id": i,
|
||||
"name": fmt.Sprintf(`name_%d`, i),
|
||||
})
|
||||
gtest.Assert(err, nil)
|
||||
// Detail.
|
||||
_, err = db.Insert(tableUserDetail, g.Map{
|
||||
"uid": i,
|
||||
"address": fmt.Sprintf(`address_%d`, i),
|
||||
})
|
||||
gtest.Assert(err, nil)
|
||||
// Scores.
|
||||
for j := 1; j <= 5; j++ {
|
||||
_, err = db.Insert(tableUserScores, g.Map{
|
||||
"uid": i,
|
||||
"score": j,
|
||||
})
|
||||
gtest.Assert(err, nil)
|
||||
}
|
||||
}
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var users []*User
|
||||
err := db.With(User{}).
|
||||
With(User{}.UserDetail).
|
||||
With(User{}.UserScores).
|
||||
Where("id", []int{3, 4}).
|
||||
Scan(&users)
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(users), 2)
|
||||
t.Assert(users[0].Id, 3)
|
||||
t.Assert(users[0].Name, "name_3")
|
||||
t.AssertNE(users[0].UserDetail, nil)
|
||||
t.Assert(users[0].UserDetail.Uid, 3)
|
||||
t.Assert(users[0].UserDetail.Address, "address_3")
|
||||
t.Assert(len(users[0].UserScores), 5)
|
||||
t.Assert(users[0].UserScores[0].Uid, 3)
|
||||
t.Assert(users[0].UserScores[0].Score, 1)
|
||||
t.Assert(users[0].UserScores[4].Uid, 3)
|
||||
t.Assert(users[0].UserScores[4].Score, 5)
|
||||
|
||||
t.Assert(users[1].Id, 4)
|
||||
t.Assert(users[1].Name, "name_4")
|
||||
t.AssertNE(users[1].UserDetail, nil)
|
||||
t.Assert(users[1].UserDetail.Uid, 4)
|
||||
t.Assert(users[1].UserDetail.Address, "address_4")
|
||||
t.Assert(len(users[1].UserScores), 5)
|
||||
t.Assert(users[1].UserScores[0].Uid, 4)
|
||||
t.Assert(users[1].UserScores[0].Score, 1)
|
||||
t.Assert(users[1].UserScores[4].Uid, 4)
|
||||
t.Assert(users[1].UserScores[4].Score, 5)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var users []User
|
||||
err := db.With(User{}).
|
||||
With(User{}.UserDetail).
|
||||
With(User{}.UserScores).
|
||||
Where("id", []int{3, 4}).
|
||||
Scan(&users)
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(users), 2)
|
||||
t.Assert(users[0].Id, 3)
|
||||
t.Assert(users[0].Name, "name_3")
|
||||
t.AssertNE(users[0].UserDetail, nil)
|
||||
t.Assert(users[0].UserDetail.Uid, 3)
|
||||
t.Assert(users[0].UserDetail.Address, "address_3")
|
||||
t.Assert(len(users[0].UserScores), 5)
|
||||
t.Assert(users[0].UserScores[0].Uid, 3)
|
||||
t.Assert(users[0].UserScores[0].Score, 1)
|
||||
t.Assert(users[0].UserScores[4].Uid, 3)
|
||||
t.Assert(users[0].UserScores[4].Score, 5)
|
||||
|
||||
t.Assert(users[1].Id, 4)
|
||||
t.Assert(users[1].Name, "name_4")
|
||||
t.AssertNE(users[1].UserDetail, nil)
|
||||
t.Assert(users[1].UserDetail.Uid, 4)
|
||||
t.Assert(users[1].UserDetail.Address, "address_4")
|
||||
t.Assert(len(users[1].UserScores), 5)
|
||||
t.Assert(users[1].UserScores[0].Uid, 4)
|
||||
t.Assert(users[1].UserScores[0].Score, 1)
|
||||
t.Assert(users[1].UserScores[4].Uid, 4)
|
||||
t.Assert(users[1].UserScores[4].Score, 5)
|
||||
})
|
||||
// With part attribute: UserDetail.
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var users []*User
|
||||
err := db.With(User{}).
|
||||
With(User{}.UserDetail).
|
||||
Where("id", []int{3, 4}).
|
||||
Scan(&users)
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(users), 2)
|
||||
t.Assert(users[0].Id, 3)
|
||||
t.Assert(users[0].Name, "name_3")
|
||||
t.AssertNE(users[0].UserDetail, nil)
|
||||
t.Assert(users[0].UserDetail.Uid, 3)
|
||||
t.Assert(users[0].UserDetail.Address, "address_3")
|
||||
t.Assert(len(users[0].UserScores), 0)
|
||||
|
||||
t.Assert(users[1].Id, 4)
|
||||
t.Assert(users[1].Name, "name_4")
|
||||
t.AssertNE(users[1].UserDetail, nil)
|
||||
t.Assert(users[1].UserDetail.Uid, 4)
|
||||
t.Assert(users[1].UserDetail.Address, "address_4")
|
||||
t.Assert(len(users[1].UserScores), 0)
|
||||
})
|
||||
// With part attribute: UserScores.
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var users []*User
|
||||
err := db.With(User{}).
|
||||
With(User{}.UserScores).
|
||||
Where("id", []int{3, 4}).
|
||||
Scan(&users)
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(users), 2)
|
||||
t.Assert(users[0].Id, 3)
|
||||
t.Assert(users[0].Name, "name_3")
|
||||
t.Assert(users[0].UserDetail, nil)
|
||||
t.Assert(len(users[0].UserScores), 5)
|
||||
t.Assert(users[0].UserScores[0].Uid, 3)
|
||||
t.Assert(users[0].UserScores[0].Score, 1)
|
||||
t.Assert(users[0].UserScores[4].Uid, 3)
|
||||
t.Assert(users[0].UserScores[4].Score, 5)
|
||||
|
||||
t.Assert(users[1].Id, 4)
|
||||
t.Assert(users[1].Name, "name_4")
|
||||
t.Assert(users[1].UserDetail, nil)
|
||||
t.Assert(len(users[1].UserScores), 5)
|
||||
t.Assert(users[1].UserScores[0].Uid, 4)
|
||||
t.Assert(users[1].UserScores[0].Score, 1)
|
||||
t.Assert(users[1].UserScores[4].Uid, 4)
|
||||
t.Assert(users[1].UserScores[4].Score, 5)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Table_Relation_WithAll_Scan(t *testing.T) {
|
||||
var (
|
||||
tableUser = "user"
|
||||
tableUserDetail = "user_detail"
|
||||
tableUserScores = "user_scores"
|
||||
)
|
||||
if _, err := db.Exec(fmt.Sprintf(`
|
||||
CREATE TABLE IF NOT EXISTS %s (
|
||||
id int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||
name varchar(45) NOT NULL,
|
||||
PRIMARY KEY (id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
`, tableUser)); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
defer dropTable(tableUser)
|
||||
|
||||
if _, err := db.Exec(fmt.Sprintf(`
|
||||
CREATE TABLE IF NOT EXISTS %s (
|
||||
uid int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||
address varchar(45) NOT NULL,
|
||||
PRIMARY KEY (uid)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
`, tableUserDetail)); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
defer dropTable(tableUserDetail)
|
||||
|
||||
if _, err := db.Exec(fmt.Sprintf(`
|
||||
CREATE TABLE IF NOT EXISTS %s (
|
||||
id int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||
uid int(10) unsigned NOT NULL,
|
||||
score int(10) unsigned NOT NULL,
|
||||
PRIMARY KEY (id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
`, tableUserScores)); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
defer dropTable(tableUserScores)
|
||||
|
||||
type UserDetail struct {
|
||||
gmeta.Meta `orm:"table:user_detail"`
|
||||
Uid int `json:"uid"`
|
||||
Address string `json:"address"`
|
||||
}
|
||||
|
||||
type UserScores struct {
|
||||
gmeta.Meta `orm:"table:user_scores"`
|
||||
Id int `json:"id"`
|
||||
Uid int `json:"uid"`
|
||||
Score int `json:"score"`
|
||||
}
|
||||
|
||||
type User struct {
|
||||
gmeta.Meta `orm:"table:user"`
|
||||
Id int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
UserDetail *UserDetail `orm:"with:uid=id"`
|
||||
UserScores []*UserScores `orm:"with:uid=id"`
|
||||
}
|
||||
|
||||
// Initialize the data.
|
||||
var err error
|
||||
for i := 1; i <= 5; i++ {
|
||||
// User.
|
||||
_, err = db.Insert(tableUser, g.Map{
|
||||
"id": i,
|
||||
"name": fmt.Sprintf(`name_%d`, i),
|
||||
})
|
||||
gtest.Assert(err, nil)
|
||||
// Detail.
|
||||
_, err = db.Insert(tableUserDetail, g.Map{
|
||||
"uid": i,
|
||||
"address": fmt.Sprintf(`address_%d`, i),
|
||||
})
|
||||
gtest.Assert(err, nil)
|
||||
// Scores.
|
||||
for j := 1; j <= 5; j++ {
|
||||
_, err = db.Insert(tableUserScores, g.Map{
|
||||
"uid": i,
|
||||
"score": j,
|
||||
})
|
||||
gtest.Assert(err, nil)
|
||||
}
|
||||
}
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var user *User
|
||||
err := db.Model(tableUser).WithAll().Where("id", 3).Scan(&user)
|
||||
t.AssertNil(err)
|
||||
t.Assert(user.Id, 3)
|
||||
t.AssertNE(user.UserDetail, nil)
|
||||
t.Assert(user.UserDetail.Uid, 3)
|
||||
t.Assert(user.UserDetail.Address, `address_3`)
|
||||
t.Assert(len(user.UserScores), 5)
|
||||
t.Assert(user.UserScores[0].Uid, 3)
|
||||
t.Assert(user.UserScores[0].Score, 1)
|
||||
t.Assert(user.UserScores[4].Uid, 3)
|
||||
t.Assert(user.UserScores[4].Score, 5)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var user User
|
||||
err := db.Model(tableUser).WithAll().Where("id", 4).Scan(&user)
|
||||
t.AssertNil(err)
|
||||
t.Assert(user.Id, 4)
|
||||
t.AssertNE(user.UserDetail, nil)
|
||||
t.Assert(user.UserDetail.Uid, 4)
|
||||
t.Assert(user.UserDetail.Address, `address_4`)
|
||||
t.Assert(len(user.UserScores), 5)
|
||||
t.Assert(user.UserScores[0].Uid, 4)
|
||||
t.Assert(user.UserScores[0].Score, 1)
|
||||
t.Assert(user.UserScores[4].Uid, 4)
|
||||
t.Assert(user.UserScores[4].Score, 5)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Table_Relation_WithAll_ScanList(t *testing.T) {
|
||||
var (
|
||||
tableUser = "user"
|
||||
tableUserDetail = "user_detail"
|
||||
tableUserScores = "user_scores"
|
||||
)
|
||||
if _, err := db.Exec(fmt.Sprintf(`
|
||||
CREATE TABLE IF NOT EXISTS %s (
|
||||
id int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||
name varchar(45) NOT NULL,
|
||||
PRIMARY KEY (id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
`, tableUser)); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
defer dropTable(tableUser)
|
||||
|
||||
if _, err := db.Exec(fmt.Sprintf(`
|
||||
CREATE TABLE IF NOT EXISTS %s (
|
||||
uid int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||
address varchar(45) NOT NULL,
|
||||
PRIMARY KEY (uid)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
`, tableUserDetail)); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
defer dropTable(tableUserDetail)
|
||||
|
||||
if _, err := db.Exec(fmt.Sprintf(`
|
||||
CREATE TABLE IF NOT EXISTS %s (
|
||||
id int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||
uid int(10) unsigned NOT NULL,
|
||||
score int(10) unsigned NOT NULL,
|
||||
PRIMARY KEY (id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
`, tableUserScores)); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
defer dropTable(tableUserScores)
|
||||
|
||||
type UserDetail struct {
|
||||
gmeta.Meta `orm:"table:user_detail"`
|
||||
Uid int `json:"uid"`
|
||||
Address string `json:"address"`
|
||||
}
|
||||
|
||||
type UserScores struct {
|
||||
gmeta.Meta `orm:"table:user_scores"`
|
||||
Id int `json:"id"`
|
||||
Uid int `json:"uid"`
|
||||
Score int `json:"score"`
|
||||
}
|
||||
|
||||
type User struct {
|
||||
gmeta.Meta `orm:"table:user"`
|
||||
Id int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
UserDetail *UserDetail `orm:"with:uid=id"`
|
||||
UserScores []*UserScores `orm:"with:uid=id"`
|
||||
}
|
||||
|
||||
// Initialize the data.
|
||||
var err error
|
||||
for i := 1; i <= 5; i++ {
|
||||
// User.
|
||||
_, err = db.Insert(tableUser, g.Map{
|
||||
"id": i,
|
||||
"name": fmt.Sprintf(`name_%d`, i),
|
||||
})
|
||||
gtest.Assert(err, nil)
|
||||
// Detail.
|
||||
_, err = db.Insert(tableUserDetail, g.Map{
|
||||
"uid": i,
|
||||
"address": fmt.Sprintf(`address_%d`, i),
|
||||
})
|
||||
gtest.Assert(err, nil)
|
||||
// Scores.
|
||||
for j := 1; j <= 5; j++ {
|
||||
_, err = db.Insert(tableUserScores, g.Map{
|
||||
"uid": i,
|
||||
"score": j,
|
||||
})
|
||||
gtest.Assert(err, nil)
|
||||
}
|
||||
}
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var users []*User
|
||||
err := db.Model(tableUser).WithAll().Where("id", []int{3, 4}).Scan(&users)
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(users), 2)
|
||||
t.Assert(users[0].Id, 3)
|
||||
t.Assert(users[0].Name, "name_3")
|
||||
t.AssertNE(users[0].UserDetail, nil)
|
||||
t.Assert(users[0].UserDetail.Uid, 3)
|
||||
t.Assert(users[0].UserDetail.Address, "address_3")
|
||||
t.Assert(len(users[0].UserScores), 5)
|
||||
t.Assert(users[0].UserScores[0].Uid, 3)
|
||||
t.Assert(users[0].UserScores[0].Score, 1)
|
||||
t.Assert(users[0].UserScores[4].Uid, 3)
|
||||
t.Assert(users[0].UserScores[4].Score, 5)
|
||||
|
||||
t.Assert(users[1].Id, 4)
|
||||
t.Assert(users[1].Name, "name_4")
|
||||
t.AssertNE(users[1].UserDetail, nil)
|
||||
t.Assert(users[1].UserDetail.Uid, 4)
|
||||
t.Assert(users[1].UserDetail.Address, "address_4")
|
||||
t.Assert(len(users[1].UserScores), 5)
|
||||
t.Assert(users[1].UserScores[0].Uid, 4)
|
||||
t.Assert(users[1].UserScores[0].Score, 1)
|
||||
t.Assert(users[1].UserScores[4].Uid, 4)
|
||||
t.Assert(users[1].UserScores[4].Score, 5)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var users []User
|
||||
err := db.Model(tableUser).WithAll().Where("id", []int{3, 4}).Scan(&users)
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(users), 2)
|
||||
t.Assert(users[0].Id, 3)
|
||||
t.Assert(users[0].Name, "name_3")
|
||||
t.AssertNE(users[0].UserDetail, nil)
|
||||
t.Assert(users[0].UserDetail.Uid, 3)
|
||||
t.Assert(users[0].UserDetail.Address, "address_3")
|
||||
t.Assert(len(users[0].UserScores), 5)
|
||||
t.Assert(users[0].UserScores[0].Uid, 3)
|
||||
t.Assert(users[0].UserScores[0].Score, 1)
|
||||
t.Assert(users[0].UserScores[4].Uid, 3)
|
||||
t.Assert(users[0].UserScores[4].Score, 5)
|
||||
|
||||
t.Assert(users[1].Id, 4)
|
||||
t.Assert(users[1].Name, "name_4")
|
||||
t.AssertNE(users[1].UserDetail, nil)
|
||||
t.Assert(users[1].UserDetail.Uid, 4)
|
||||
t.Assert(users[1].UserDetail.Address, "address_4")
|
||||
t.Assert(len(users[1].UserScores), 5)
|
||||
t.Assert(users[1].UserScores[0].Uid, 4)
|
||||
t.Assert(users[1].UserScores[0].Score, 1)
|
||||
t.Assert(users[1].UserScores[4].Uid, 4)
|
||||
t.Assert(users[1].UserScores[4].Score, 5)
|
||||
})
|
||||
}
|
||||
@ -19,7 +19,7 @@ func Test_Instance(t *testing.T) {
|
||||
t.AssertNE(err, nil)
|
||||
|
||||
db, err := gdb.Instance()
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
|
||||
err1 := db.PingMaster()
|
||||
err2 := db.PingSlave()
|
||||
|
||||
@ -17,7 +17,7 @@ import (
|
||||
func Test_Ctx(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
db, err := gdb.Instance()
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
|
||||
err1 := db.PingMaster()
|
||||
err2 := db.PingSlave()
|
||||
|
||||
@ -17,9 +17,9 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
SCHEMA = "test_internal"
|
||||
USER = "root"
|
||||
PASS = "12345678"
|
||||
SCHEMA = "test_internal"
|
||||
TestDbUser = "root"
|
||||
TestDbPass = "12345678"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -32,12 +32,12 @@ func init() {
|
||||
"name": true,
|
||||
"type": true,
|
||||
}, false)
|
||||
gtest.Assert(err, nil)
|
||||
gtest.AssertNil(err)
|
||||
configNode = ConfigNode{
|
||||
Host: "127.0.0.1",
|
||||
Port: "3306",
|
||||
User: USER,
|
||||
Pass: PASS,
|
||||
User: TestDbUser,
|
||||
Pass: TestDbPass,
|
||||
Name: parser.GetOpt("name", ""),
|
||||
Type: parser.GetOpt("type", "mysql"),
|
||||
Role: "master",
|
||||
@ -204,7 +204,7 @@ CREATE TABLE %s (
|
||||
defer dropTable(table2)
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
model := db.Table(table1)
|
||||
model := db.Model(table1)
|
||||
gtest.Assert(model.getSoftFieldNameCreated(table2), "createat")
|
||||
gtest.Assert(model.getSoftFieldNameUpdated(table2), "updateat")
|
||||
gtest.Assert(model.getSoftFieldNameDeleted(table2), "deleteat")
|
||||
@ -243,48 +243,48 @@ CREATE TABLE %s (
|
||||
defer dropTable(table2)
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
model := db.Table(table1)
|
||||
model := db.Model(table1)
|
||||
t.Assert(model.getConditionForSoftDeleting(), "`delete_at` IS NULL")
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
model := db.Table(fmt.Sprintf(`%s as t`, table1))
|
||||
model := db.Model(fmt.Sprintf(`%s as t`, table1))
|
||||
t.Assert(model.getConditionForSoftDeleting(), "`delete_at` IS NULL")
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
model := db.Table(fmt.Sprintf(`%s, %s`, table1, table2))
|
||||
model := db.Model(fmt.Sprintf(`%s, %s`, table1, table2))
|
||||
t.Assert(model.getConditionForSoftDeleting(), fmt.Sprintf(
|
||||
"`%s`.`delete_at` IS NULL AND `%s`.`deleteat` IS NULL",
|
||||
table1, table2,
|
||||
))
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
model := db.Table(fmt.Sprintf(`%s t1, %s as t2`, table1, table2))
|
||||
model := db.Model(fmt.Sprintf(`%s t1, %s as t2`, table1, table2))
|
||||
t.Assert(model.getConditionForSoftDeleting(), "`t1`.`delete_at` IS NULL AND `t2`.`deleteat` IS NULL")
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
model := db.Table(fmt.Sprintf(`%s as t1, %s as t2`, table1, table2))
|
||||
model := db.Model(fmt.Sprintf(`%s as t1, %s as t2`, table1, table2))
|
||||
t.Assert(model.getConditionForSoftDeleting(), "`t1`.`delete_at` IS NULL AND `t2`.`deleteat` IS NULL")
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
model := db.Table(fmt.Sprintf(`%s as t1`, table1)).LeftJoin(table2+" t2", "t2.id2=t1.id1")
|
||||
model := db.Model(fmt.Sprintf(`%s as t1`, table1)).LeftJoin(table2+" t2", "t2.id2=t1.id1")
|
||||
t.Assert(model.getConditionForSoftDeleting(), "`t1`.`delete_at` IS NULL AND `t2`.`deleteat` IS NULL")
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
model := db.Table(fmt.Sprintf(`%s`, table1)).LeftJoin(table2, "t2.id2=t1.id1")
|
||||
model := db.Model(fmt.Sprintf(`%s`, table1)).LeftJoin(table2, "t2.id2=t1.id1")
|
||||
t.Assert(model.getConditionForSoftDeleting(), fmt.Sprintf(
|
||||
"`%s`.`delete_at` IS NULL AND `%s`.`deleteat` IS NULL",
|
||||
table1, table2,
|
||||
))
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
model := db.Table(fmt.Sprintf(`%s`, table1)).LeftJoin(table2, "t2.id2=t1.id1").RightJoin(table2, "t2.id2=t1.id1")
|
||||
model := db.Model(fmt.Sprintf(`%s`, table1)).LeftJoin(table2, "t2.id2=t1.id1").RightJoin(table2, "t2.id2=t1.id1")
|
||||
t.Assert(model.getConditionForSoftDeleting(), fmt.Sprintf(
|
||||
"`%s`.`delete_at` IS NULL AND `%s`.`deleteat` IS NULL AND `%s`.`deleteat` IS NULL",
|
||||
table1, table2, table2,
|
||||
))
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
model := db.Table(table1+" as t1").LeftJoin(table2+" as t2", "t2.id2=t1.id1").RightJoin(table2+" as t3 ", "t2.id2=t1.id1")
|
||||
model := db.Model(table1+" as t1").LeftJoin(table2+" as t2", "t2.id2=t1.id1").RightJoin(table2+" as t3 ", "t2.id2=t1.id1")
|
||||
t.Assert(
|
||||
model.getConditionForSoftDeleting(),
|
||||
"`t1`.`delete_at` IS NULL AND `t2`.`deleteat` IS NULL AND `t3`.`deleteat` IS NULL",
|
||||
@ -329,10 +329,330 @@ func TestResult_Structs1(t *testing.T) {
|
||||
}
|
||||
array := make([]*B, 2)
|
||||
err := r.Structs(&array)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
t.Assert(array[0].Id, 0)
|
||||
t.Assert(array[1].Id, 0)
|
||||
t.Assert(array[0].Name, "john")
|
||||
t.Assert(array[1].Name, "smith")
|
||||
})
|
||||
}
|
||||
|
||||
// https://github.com/gogf/gf/issues/1159
|
||||
func Test_ScanList_NoRecreate_PtrAttribute(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type S1 struct {
|
||||
Id int
|
||||
Name string
|
||||
Age int
|
||||
Score int
|
||||
}
|
||||
type S3 struct {
|
||||
One *S1
|
||||
}
|
||||
var (
|
||||
s []*S3
|
||||
err error
|
||||
)
|
||||
r1 := Result{
|
||||
Record{
|
||||
"id": gvar.New(1),
|
||||
"name": gvar.New("john"),
|
||||
"age": gvar.New(16),
|
||||
},
|
||||
Record{
|
||||
"id": gvar.New(2),
|
||||
"name": gvar.New("smith"),
|
||||
"age": gvar.New(18),
|
||||
},
|
||||
}
|
||||
err = r1.ScanList(&s, "One")
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(s), 2)
|
||||
t.Assert(s[0].One.Name, "john")
|
||||
t.Assert(s[0].One.Age, 16)
|
||||
t.Assert(s[1].One.Name, "smith")
|
||||
t.Assert(s[1].One.Age, 18)
|
||||
|
||||
r2 := Result{
|
||||
Record{
|
||||
"id": gvar.New(1),
|
||||
"age": gvar.New(20),
|
||||
},
|
||||
Record{
|
||||
"id": gvar.New(2),
|
||||
"age": gvar.New(21),
|
||||
},
|
||||
}
|
||||
err = r2.ScanList(&s, "One", "One", "id:Id")
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(s), 2)
|
||||
t.Assert(s[0].One.Name, "john")
|
||||
t.Assert(s[0].One.Age, 20)
|
||||
t.Assert(s[1].One.Name, "smith")
|
||||
t.Assert(s[1].One.Age, 21)
|
||||
})
|
||||
}
|
||||
|
||||
// https://github.com/gogf/gf/issues/1159
|
||||
func Test_ScanList_NoRecreate_StructAttribute(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type S1 struct {
|
||||
Id int
|
||||
Name string
|
||||
Age int
|
||||
Score int
|
||||
}
|
||||
type S3 struct {
|
||||
One S1
|
||||
}
|
||||
var (
|
||||
s []*S3
|
||||
err error
|
||||
)
|
||||
r1 := Result{
|
||||
Record{
|
||||
"id": gvar.New(1),
|
||||
"name": gvar.New("john"),
|
||||
"age": gvar.New(16),
|
||||
},
|
||||
Record{
|
||||
"id": gvar.New(2),
|
||||
"name": gvar.New("smith"),
|
||||
"age": gvar.New(18),
|
||||
},
|
||||
}
|
||||
err = r1.ScanList(&s, "One")
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(s), 2)
|
||||
t.Assert(s[0].One.Name, "john")
|
||||
t.Assert(s[0].One.Age, 16)
|
||||
t.Assert(s[1].One.Name, "smith")
|
||||
t.Assert(s[1].One.Age, 18)
|
||||
|
||||
r2 := Result{
|
||||
Record{
|
||||
"id": gvar.New(1),
|
||||
"age": gvar.New(20),
|
||||
},
|
||||
Record{
|
||||
"id": gvar.New(2),
|
||||
"age": gvar.New(21),
|
||||
},
|
||||
}
|
||||
err = r2.ScanList(&s, "One", "One", "id:Id")
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(s), 2)
|
||||
t.Assert(s[0].One.Name, "john")
|
||||
t.Assert(s[0].One.Age, 20)
|
||||
t.Assert(s[1].One.Name, "smith")
|
||||
t.Assert(s[1].One.Age, 21)
|
||||
})
|
||||
}
|
||||
|
||||
// https://github.com/gogf/gf/issues/1159
|
||||
func Test_ScanList_NoRecreate_SliceAttribute_Ptr(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type S1 struct {
|
||||
Id int
|
||||
Name string
|
||||
Age int
|
||||
Score int
|
||||
}
|
||||
type S2 struct {
|
||||
Id int
|
||||
Pid int
|
||||
Name string
|
||||
Age int
|
||||
Score int
|
||||
}
|
||||
type S3 struct {
|
||||
One *S1
|
||||
Many []*S2
|
||||
}
|
||||
var (
|
||||
s []*S3
|
||||
err error
|
||||
)
|
||||
r1 := Result{
|
||||
Record{
|
||||
"id": gvar.New(1),
|
||||
"name": gvar.New("john"),
|
||||
"age": gvar.New(16),
|
||||
},
|
||||
Record{
|
||||
"id": gvar.New(2),
|
||||
"name": gvar.New("smith"),
|
||||
"age": gvar.New(18),
|
||||
},
|
||||
}
|
||||
err = r1.ScanList(&s, "One")
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(s), 2)
|
||||
t.Assert(s[0].One.Name, "john")
|
||||
t.Assert(s[0].One.Age, 16)
|
||||
t.Assert(s[1].One.Name, "smith")
|
||||
t.Assert(s[1].One.Age, 18)
|
||||
|
||||
r2 := Result{
|
||||
Record{
|
||||
"id": gvar.New(100),
|
||||
"pid": gvar.New(1),
|
||||
"age": gvar.New(30),
|
||||
"name": gvar.New("john"),
|
||||
},
|
||||
Record{
|
||||
"id": gvar.New(200),
|
||||
"pid": gvar.New(1),
|
||||
"age": gvar.New(31),
|
||||
"name": gvar.New("smith"),
|
||||
},
|
||||
}
|
||||
err = r2.ScanList(&s, "Many", "One", "pid:Id")
|
||||
//fmt.Printf("%+v", err)
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(s), 2)
|
||||
t.Assert(s[0].One.Name, "john")
|
||||
t.Assert(s[0].One.Age, 16)
|
||||
t.Assert(len(s[0].Many), 2)
|
||||
t.Assert(s[0].Many[0].Name, "john")
|
||||
t.Assert(s[0].Many[0].Age, 30)
|
||||
t.Assert(s[0].Many[1].Name, "smith")
|
||||
t.Assert(s[0].Many[1].Age, 31)
|
||||
|
||||
t.Assert(s[1].One.Name, "smith")
|
||||
t.Assert(s[1].One.Age, 18)
|
||||
t.Assert(len(s[1].Many), 0)
|
||||
|
||||
r3 := Result{
|
||||
Record{
|
||||
"id": gvar.New(100),
|
||||
"pid": gvar.New(1),
|
||||
"age": gvar.New(40),
|
||||
},
|
||||
Record{
|
||||
"id": gvar.New(200),
|
||||
"pid": gvar.New(1),
|
||||
"age": gvar.New(41),
|
||||
},
|
||||
}
|
||||
err = r3.ScanList(&s, "Many", "One", "pid:Id")
|
||||
//fmt.Printf("%+v", err)
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(s), 2)
|
||||
t.Assert(s[0].One.Name, "john")
|
||||
t.Assert(s[0].One.Age, 16)
|
||||
t.Assert(len(s[0].Many), 2)
|
||||
t.Assert(s[0].Many[0].Name, "john")
|
||||
t.Assert(s[0].Many[0].Age, 40)
|
||||
t.Assert(s[0].Many[1].Name, "smith")
|
||||
t.Assert(s[0].Many[1].Age, 41)
|
||||
|
||||
t.Assert(s[1].One.Name, "smith")
|
||||
t.Assert(s[1].One.Age, 18)
|
||||
t.Assert(len(s[1].Many), 0)
|
||||
})
|
||||
}
|
||||
|
||||
// https://github.com/gogf/gf/issues/1159
|
||||
func Test_ScanList_NoRecreate_SliceAttribute_Struct(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type S1 struct {
|
||||
Id int
|
||||
Name string
|
||||
Age int
|
||||
Score int
|
||||
}
|
||||
type S2 struct {
|
||||
Id int
|
||||
Pid int
|
||||
Name string
|
||||
Age int
|
||||
Score int
|
||||
}
|
||||
type S3 struct {
|
||||
One S1
|
||||
Many []S2
|
||||
}
|
||||
var (
|
||||
s []S3
|
||||
err error
|
||||
)
|
||||
r1 := Result{
|
||||
Record{
|
||||
"id": gvar.New(1),
|
||||
"name": gvar.New("john"),
|
||||
"age": gvar.New(16),
|
||||
},
|
||||
Record{
|
||||
"id": gvar.New(2),
|
||||
"name": gvar.New("smith"),
|
||||
"age": gvar.New(18),
|
||||
},
|
||||
}
|
||||
err = r1.ScanList(&s, "One")
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(s), 2)
|
||||
t.Assert(s[0].One.Name, "john")
|
||||
t.Assert(s[0].One.Age, 16)
|
||||
t.Assert(s[1].One.Name, "smith")
|
||||
t.Assert(s[1].One.Age, 18)
|
||||
|
||||
r2 := Result{
|
||||
Record{
|
||||
"id": gvar.New(100),
|
||||
"pid": gvar.New(1),
|
||||
"age": gvar.New(30),
|
||||
"name": gvar.New("john"),
|
||||
},
|
||||
Record{
|
||||
"id": gvar.New(200),
|
||||
"pid": gvar.New(1),
|
||||
"age": gvar.New(31),
|
||||
"name": gvar.New("smith"),
|
||||
},
|
||||
}
|
||||
err = r2.ScanList(&s, "Many", "One", "pid:Id")
|
||||
//fmt.Printf("%+v", err)
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(s), 2)
|
||||
t.Assert(s[0].One.Name, "john")
|
||||
t.Assert(s[0].One.Age, 16)
|
||||
t.Assert(len(s[0].Many), 2)
|
||||
t.Assert(s[0].Many[0].Name, "john")
|
||||
t.Assert(s[0].Many[0].Age, 30)
|
||||
t.Assert(s[0].Many[1].Name, "smith")
|
||||
t.Assert(s[0].Many[1].Age, 31)
|
||||
|
||||
t.Assert(s[1].One.Name, "smith")
|
||||
t.Assert(s[1].One.Age, 18)
|
||||
t.Assert(len(s[1].Many), 0)
|
||||
|
||||
r3 := Result{
|
||||
Record{
|
||||
"id": gvar.New(100),
|
||||
"pid": gvar.New(1),
|
||||
"age": gvar.New(40),
|
||||
},
|
||||
Record{
|
||||
"id": gvar.New(200),
|
||||
"pid": gvar.New(1),
|
||||
"age": gvar.New(41),
|
||||
},
|
||||
}
|
||||
err = r3.ScanList(&s, "Many", "One", "pid:Id")
|
||||
//fmt.Printf("%+v", err)
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(s), 2)
|
||||
t.Assert(s[0].One.Name, "john")
|
||||
t.Assert(s[0].One.Age, 16)
|
||||
t.Assert(len(s[0].Many), 2)
|
||||
t.Assert(s[0].Many[0].Name, "john")
|
||||
t.Assert(s[0].Many[0].Age, 40)
|
||||
t.Assert(s[0].Many[1].Name, "smith")
|
||||
t.Assert(s[0].Many[1].Age, 41)
|
||||
|
||||
t.Assert(s[1].One.Name, "smith")
|
||||
t.Assert(s[1].One.Age, 18)
|
||||
t.Assert(len(s[1].Many), 0)
|
||||
})
|
||||
}
|
||||
|
||||
@ -36,13 +36,13 @@ func Test_DB_Ping(t *testing.T) {
|
||||
func Test_DB_Query(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
_, err := db.Query("SELECT ?", 1)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
|
||||
_, err = db.Query("SELECT ?+?", 1, 2)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
|
||||
_, err = db.Query("SELECT ?+?", g.Slice{1, 2})
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
|
||||
_, err = db.Query("ERROR")
|
||||
t.AssertNE(err, nil)
|
||||
@ -53,7 +53,7 @@ func Test_DB_Query(t *testing.T) {
|
||||
func Test_DB_Exec(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
_, err := db.Exec("SELECT ?", 1)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
|
||||
_, err = db.Exec("ERROR")
|
||||
t.AssertNE(err, nil)
|
||||
@ -64,17 +64,17 @@ func Test_DB_Exec(t *testing.T) {
|
||||
func Test_DB_Prepare(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
st, err := db.Prepare("SELECT 100")
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
|
||||
rows, err := st.Query()
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
|
||||
array, err := rows.Columns()
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
t.Assert(array[0], "100")
|
||||
|
||||
err = rows.Close()
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
})
|
||||
}
|
||||
|
||||
@ -90,7 +90,7 @@ func Test_DB_Insert(t *testing.T) {
|
||||
"nickname": "T1",
|
||||
"create_time": gtime.Now().String(),
|
||||
})
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
|
||||
// normal map
|
||||
result, err := db.Insert(table, g.Map{
|
||||
@ -100,7 +100,7 @@ func Test_DB_Insert(t *testing.T) {
|
||||
"nickname": "name_2",
|
||||
"create_time": gtime.Now().String(),
|
||||
})
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
n, _ := result.RowsAffected()
|
||||
t.Assert(n, 1)
|
||||
|
||||
@ -120,12 +120,12 @@ func Test_DB_Insert(t *testing.T) {
|
||||
Nickname: "name_3",
|
||||
CreateTime: timeStr,
|
||||
})
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
n, _ = result.RowsAffected()
|
||||
t.Assert(n, 1)
|
||||
|
||||
one, err := db.Table(table).Where("id", 3).One()
|
||||
t.Assert(err, nil)
|
||||
one, err := db.Model(table).Where("id", 3).One()
|
||||
t.AssertNil(err)
|
||||
|
||||
t.Assert(one["id"].Int(), 3)
|
||||
t.Assert(one["passport"].String(), "user_3")
|
||||
@ -142,12 +142,12 @@ func Test_DB_Insert(t *testing.T) {
|
||||
Nickname: "name_4",
|
||||
CreateTime: timeStr,
|
||||
})
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
n, _ = result.RowsAffected()
|
||||
t.Assert(n, 1)
|
||||
|
||||
one, err = db.Table(table).Where("id", 4).One()
|
||||
t.Assert(err, nil)
|
||||
one, err = db.Model(table).Where("id", 4).One()
|
||||
t.AssertNil(err)
|
||||
t.Assert(one["id"].Int(), 4)
|
||||
t.Assert(one["passport"].String(), "t4")
|
||||
t.Assert(one["password"].String(), "25d55ad283aa400af464c76d713c07ad")
|
||||
@ -172,12 +172,12 @@ func Test_DB_Insert(t *testing.T) {
|
||||
"create_time": timeStr,
|
||||
},
|
||||
})
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
n, _ = r.RowsAffected()
|
||||
t.Assert(n, 2)
|
||||
|
||||
one, err = db.Table(table).Where("id", 200).One()
|
||||
t.Assert(err, nil)
|
||||
one, err = db.Model(table).Where("id", 200).One()
|
||||
t.AssertNil(err)
|
||||
t.Assert(one["id"].Int(), 200)
|
||||
t.Assert(one["passport"].String(), "t200")
|
||||
t.Assert(one["password"].String(), "25d55ad283aa400af464c76d71qw07ad")
|
||||
@ -204,10 +204,10 @@ func Test_DB_Insert_WithStructAndSliceAttribute(t *testing.T) {
|
||||
"create_time": gtime.Now().String(),
|
||||
}
|
||||
_, err := db.Insert(table, data)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
|
||||
one, err := db.GetOne(fmt.Sprintf("SELECT * FROM %s WHERE id=?", table), 1)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
t.Assert(one["passport"], data["passport"])
|
||||
t.Assert(one["create_time"], data["create_time"])
|
||||
t.Assert(one["nickname"], gparser.MustToJson(data["nickname"]))
|
||||
@ -234,10 +234,10 @@ func Test_DB_Insert_KeyFieldNameMapping(t *testing.T) {
|
||||
CreateTime: "2020-10-10 12:00:01",
|
||||
}
|
||||
_, err := db.Insert(table, data)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
|
||||
one, err := db.GetOne(fmt.Sprintf("SELECT * FROM %s WHERE id=?", table), 1)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
t.Assert(one["passport"], data.Passport)
|
||||
t.Assert(one["create_time"], data.CreateTime)
|
||||
t.Assert(one["nickname"], data.Nickname)
|
||||
@ -264,10 +264,10 @@ func Test_DB_Upadte_KeyFieldNameMapping(t *testing.T) {
|
||||
CreateTime: "2020-10-10 12:00:01",
|
||||
}
|
||||
_, err := db.Update(table, data, "id=1")
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
|
||||
one, err := db.GetOne(fmt.Sprintf("SELECT * FROM %s WHERE id=?", table), 1)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
t.Assert(one["passport"], data.Passport)
|
||||
t.Assert(one["create_time"], data.CreateTime)
|
||||
t.Assert(one["nickname"], data.Nickname)
|
||||
@ -320,7 +320,7 @@ func Test_DB_InsertIgnore(t *testing.T) {
|
||||
"nickname": "T1",
|
||||
"create_time": gtime.Now().String(),
|
||||
})
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
})
|
||||
}
|
||||
|
||||
@ -344,7 +344,7 @@ func Test_DB_BatchInsert(t *testing.T) {
|
||||
"create_time": gtime.Now().String(),
|
||||
},
|
||||
}, 1)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
n, _ := r.RowsAffected()
|
||||
t.Assert(n, 2)
|
||||
|
||||
@ -372,7 +372,7 @@ func Test_DB_BatchInsert(t *testing.T) {
|
||||
"create_time": gtime.Now().String(),
|
||||
},
|
||||
}, 1)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
n, _ := r.RowsAffected()
|
||||
t.Assert(n, 2)
|
||||
})
|
||||
@ -388,7 +388,7 @@ func Test_DB_BatchInsert(t *testing.T) {
|
||||
"nickname": "T1",
|
||||
"create_time": gtime.Now().String(),
|
||||
})
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
n, _ := result.RowsAffected()
|
||||
t.Assert(n, 1)
|
||||
})
|
||||
@ -416,7 +416,7 @@ func Test_DB_BatchInsert_Struct(t *testing.T) {
|
||||
CreateTime: gtime.Now(),
|
||||
}
|
||||
result, err := db.BatchInsert(table, user)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
n, _ := result.RowsAffected()
|
||||
t.Assert(n, 1)
|
||||
})
|
||||
@ -435,10 +435,10 @@ func Test_DB_Save(t *testing.T) {
|
||||
"nickname": "T11",
|
||||
"create_time": timeStr,
|
||||
})
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
|
||||
one, err := db.Table(table).Where("id", 1).One()
|
||||
t.Assert(err, nil)
|
||||
one, err := db.Model(table).Where("id", 1).One()
|
||||
t.AssertNil(err)
|
||||
t.Assert(one["id"].Int(), 1)
|
||||
t.Assert(one["passport"].String(), "t1")
|
||||
t.Assert(one["password"].String(), "25d55ad283aa400af464c76d713c07ad")
|
||||
@ -460,10 +460,10 @@ func Test_DB_Replace(t *testing.T) {
|
||||
"nickname": "T11",
|
||||
"create_time": timeStr,
|
||||
})
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
|
||||
one, err := db.Table(table).Where("id", 1).One()
|
||||
t.Assert(err, nil)
|
||||
one, err := db.Model(table).Where("id", 1).One()
|
||||
t.AssertNil(err)
|
||||
t.Assert(one["id"].Int(), 1)
|
||||
t.Assert(one["passport"].String(), "t1")
|
||||
t.Assert(one["password"].String(), "25d55ad283aa400af464c76d713c07ad")
|
||||
@ -478,12 +478,12 @@ func Test_DB_Update(t *testing.T) {
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
result, err := db.Update(table, "password='987654321'", "id=3")
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
n, _ := result.RowsAffected()
|
||||
t.Assert(n, 1)
|
||||
|
||||
one, err := db.Table(table).Where("id", 3).One()
|
||||
t.Assert(err, nil)
|
||||
one, err := db.Model(table).Where("id", 3).One()
|
||||
t.AssertNil(err)
|
||||
t.Assert(one["id"].Int(), 3)
|
||||
t.Assert(one["passport"].String(), "user_3")
|
||||
t.Assert(one["password"].String(), "987654321")
|
||||
@ -497,19 +497,19 @@ func Test_DB_GetAll(t *testing.T) {
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
result, err := db.GetAll(fmt.Sprintf("SELECT * FROM %s WHERE id=?", table), 1)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(result), 1)
|
||||
t.Assert(result[0]["id"].Int(), 1)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
result, err := db.GetAll(fmt.Sprintf("SELECT * FROM %s WHERE id=?", table), g.Slice{1})
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(result), 1)
|
||||
t.Assert(result[0]["id"].Int(), 1)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
result, err := db.GetAll(fmt.Sprintf("SELECT * FROM %s WHERE id in(?)", table), g.Slice{1, 2, 3})
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(result), 3)
|
||||
t.Assert(result[0]["id"].Int(), 1)
|
||||
t.Assert(result[1]["id"].Int(), 2)
|
||||
@ -517,7 +517,7 @@ func Test_DB_GetAll(t *testing.T) {
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
result, err := db.GetAll(fmt.Sprintf("SELECT * FROM %s WHERE id in(?,?,?)", table), g.Slice{1, 2, 3})
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(result), 3)
|
||||
t.Assert(result[0]["id"].Int(), 1)
|
||||
t.Assert(result[1]["id"].Int(), 2)
|
||||
@ -525,7 +525,7 @@ func Test_DB_GetAll(t *testing.T) {
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
result, err := db.GetAll(fmt.Sprintf("SELECT * FROM %s WHERE id in(?,?,?)", table), g.Slice{1, 2, 3}...)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(result), 3)
|
||||
t.Assert(result[0]["id"].Int(), 1)
|
||||
t.Assert(result[1]["id"].Int(), 2)
|
||||
@ -533,7 +533,7 @@ func Test_DB_GetAll(t *testing.T) {
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
result, err := db.GetAll(fmt.Sprintf("SELECT * FROM %s WHERE id>=? AND id <=?", table), g.Slice{1, 3})
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(result), 3)
|
||||
t.Assert(result[0]["id"].Int(), 1)
|
||||
t.Assert(result[1]["id"].Int(), 2)
|
||||
@ -546,7 +546,7 @@ func Test_DB_GetOne(t *testing.T) {
|
||||
defer dropTable(table)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
record, err := db.GetOne(fmt.Sprintf("SELECT * FROM %s WHERE passport=?", table), "user_1")
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
t.Assert(record["nickname"].String(), "name_1")
|
||||
})
|
||||
}
|
||||
@ -556,7 +556,7 @@ func Test_DB_GetValue(t *testing.T) {
|
||||
defer dropTable(table)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
value, err := db.GetValue(fmt.Sprintf("SELECT id FROM %s WHERE passport=?", table), "user_3")
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
t.Assert(value.Int(), 3)
|
||||
})
|
||||
}
|
||||
@ -566,8 +566,8 @@ func Test_DB_GetCount(t *testing.T) {
|
||||
defer dropTable(table)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
count, err := db.GetCount(fmt.Sprintf("SELECT * FROM %s", table))
|
||||
t.Assert(err, nil)
|
||||
t.Assert(count, SIZE)
|
||||
t.AssertNil(err)
|
||||
t.Assert(count, TableSize)
|
||||
})
|
||||
}
|
||||
|
||||
@ -584,7 +584,7 @@ func Test_DB_GetStruct(t *testing.T) {
|
||||
}
|
||||
user := new(User)
|
||||
err := db.GetStruct(user, fmt.Sprintf("SELECT * FROM %s WHERE id=?", table), 3)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
t.Assert(user.NickName, "name_3")
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
@ -597,7 +597,7 @@ func Test_DB_GetStruct(t *testing.T) {
|
||||
}
|
||||
user := new(User)
|
||||
err := db.GetStruct(user, fmt.Sprintf("SELECT * FROM %s WHERE id=?", table), 3)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
t.Assert(user.NickName, "name_3")
|
||||
})
|
||||
}
|
||||
@ -615,8 +615,8 @@ func Test_DB_GetStructs(t *testing.T) {
|
||||
}
|
||||
var users []User
|
||||
err := db.GetStructs(&users, fmt.Sprintf("SELECT * FROM %s WHERE id>?", table), 1)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(len(users), SIZE-1)
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(users), TableSize-1)
|
||||
t.Assert(users[0].Id, 2)
|
||||
t.Assert(users[1].Id, 3)
|
||||
t.Assert(users[2].Id, 4)
|
||||
@ -635,8 +635,8 @@ func Test_DB_GetStructs(t *testing.T) {
|
||||
}
|
||||
var users []User
|
||||
err := db.GetStructs(&users, fmt.Sprintf("SELECT * FROM %s WHERE id>?", table), 1)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(len(users), SIZE-1)
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(users), TableSize-1)
|
||||
t.Assert(users[0].Id, 2)
|
||||
t.Assert(users[1].Id, 3)
|
||||
t.Assert(users[2].Id, 4)
|
||||
@ -659,7 +659,7 @@ func Test_DB_GetScan(t *testing.T) {
|
||||
}
|
||||
user := new(User)
|
||||
err := db.GetScan(user, fmt.Sprintf("SELECT * FROM %s WHERE id=?", table), 3)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
t.Assert(user.NickName, "name_3")
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
@ -672,7 +672,7 @@ func Test_DB_GetScan(t *testing.T) {
|
||||
}
|
||||
user := new(User)
|
||||
err := db.GetScan(user, fmt.Sprintf("SELECT * FROM %s WHERE id=?", table), 3)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
t.Assert(user.NickName, "name_3")
|
||||
})
|
||||
|
||||
@ -686,8 +686,8 @@ func Test_DB_GetScan(t *testing.T) {
|
||||
}
|
||||
var users []User
|
||||
err := db.GetScan(&users, fmt.Sprintf("SELECT * FROM %s WHERE id>?", table), 1)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(len(users), SIZE-1)
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(users), TableSize-1)
|
||||
t.Assert(users[0].Id, 2)
|
||||
t.Assert(users[1].Id, 3)
|
||||
t.Assert(users[2].Id, 4)
|
||||
@ -706,8 +706,8 @@ func Test_DB_GetScan(t *testing.T) {
|
||||
}
|
||||
var users []User
|
||||
err := db.GetScan(&users, fmt.Sprintf("SELECT * FROM %s WHERE id>?", table), 1)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(len(users), SIZE-1)
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(users), TableSize-1)
|
||||
t.Assert(users[0].Id, 2)
|
||||
t.Assert(users[1].Id, 3)
|
||||
t.Assert(users[2].Id, 4)
|
||||
@ -722,9 +722,9 @@ func Test_DB_Delete(t *testing.T) {
|
||||
defer dropTable(table)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
result, err := db.Delete(table, 1)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
n, _ := result.RowsAffected()
|
||||
t.Assert(n, SIZE)
|
||||
t.Assert(n, TableSize)
|
||||
})
|
||||
}
|
||||
|
||||
@ -746,7 +746,7 @@ func Test_DB_Time(t *testing.T) {
|
||||
n, _ := result.RowsAffected()
|
||||
t.Assert(n, 1)
|
||||
value, err := db.GetValue(fmt.Sprintf("select `passport` from `%s` where id=?", table), 200)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
t.Assert(value.String(), "t200")
|
||||
})
|
||||
|
||||
@ -765,13 +765,13 @@ func Test_DB_Time(t *testing.T) {
|
||||
n, _ := result.RowsAffected()
|
||||
t.Assert(n, 1)
|
||||
value, err := db.GetValue(fmt.Sprintf("select `passport` from `%s` where id=?", table), 300)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
t.Assert(value.String(), "t300")
|
||||
})
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
result, err := db.Delete(table, 1)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
n, _ := result.RowsAffected()
|
||||
t.Assert(n, 2)
|
||||
})
|
||||
@ -781,10 +781,10 @@ func Test_DB_ToJson(t *testing.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
_, err := db.Update(table, "create_time='2010-10-10 00:00:01'", "id=?", 1)
|
||||
gtest.Assert(err, nil)
|
||||
gtest.AssertNil(err)
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
result, err := db.Table(table).Fields("*").Where("id =? ", 1).Select()
|
||||
result, err := db.Model(table).Fields("*").Where("id =? ", 1).Select()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
@ -821,11 +821,11 @@ func Test_DB_ToJson(t *testing.T) {
|
||||
|
||||
result = nil
|
||||
err = result.Structs(&users)
|
||||
t.AssertNE(err, nil)
|
||||
t.AssertNil(err)
|
||||
})
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
result, err := db.Table(table).Fields("*").Where("id =? ", 1).One()
|
||||
result, err := db.Model(table).Fields("*").Where("id =? ", 1).One()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
@ -855,10 +855,10 @@ func Test_DB_ToXml(t *testing.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
_, err := db.Update(table, "create_time='2010-10-10 00:00:01'", "id=?", 1)
|
||||
gtest.Assert(err, nil)
|
||||
gtest.AssertNil(err)
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
record, err := db.Table(table).Fields("*").Where("id = ?", 1).One()
|
||||
record, err := db.Model(table).Fields("*").Where("id = ?", 1).One()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
@ -921,10 +921,10 @@ func Test_DB_ToStringMap(t *testing.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
_, err := db.Update(table, "create_time='2010-10-10 00:00:01'", "id=?", 1)
|
||||
gtest.Assert(err, nil)
|
||||
gtest.AssertNil(err)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
id := "1"
|
||||
result, err := db.Table(table).Fields("*").Where("id = ?", 1).Select()
|
||||
result, err := db.Model(table).Fields("*").Where("id = ?", 1).Select()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
@ -957,11 +957,11 @@ func Test_DB_ToIntMap(t *testing.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
_, err := db.Update(table, "create_time='2010-10-10 00:00:01'", "id=?", 1)
|
||||
gtest.Assert(err, nil)
|
||||
gtest.AssertNil(err)
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
id := 1
|
||||
result, err := db.Table(table).Fields("*").Where("id = ?", id).Select()
|
||||
result, err := db.Model(table).Fields("*").Where("id = ?", id).Select()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
@ -993,11 +993,11 @@ func Test_DB_ToUintMap(t *testing.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
_, err := db.Update(table, "create_time='2010-10-10 00:00:01'", "id=?", 1)
|
||||
gtest.Assert(err, nil)
|
||||
gtest.AssertNil(err)
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
id := 1
|
||||
result, err := db.Table(table).Fields("*").Where("id = ?", id).Select()
|
||||
result, err := db.Model(table).Fields("*").Where("id = ?", id).Select()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
@ -1030,12 +1030,12 @@ func Test_DB_ToStringRecord(t *testing.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
_, err := db.Update(table, "create_time='2010-10-10 00:00:01'", "id=?", 1)
|
||||
gtest.Assert(err, nil)
|
||||
gtest.AssertNil(err)
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
id := 1
|
||||
ids := "1"
|
||||
result, err := db.Table(table).Fields("*").Where("id = ?", id).Select()
|
||||
result, err := db.Model(table).Fields("*").Where("id = ?", id).Select()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
@ -1068,11 +1068,11 @@ func Test_DB_ToIntRecord(t *testing.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
_, err := db.Update(table, "create_time='2010-10-10 00:00:01'", "id=?", 1)
|
||||
gtest.Assert(err, nil)
|
||||
gtest.AssertNil(err)
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
id := 1
|
||||
result, err := db.Table(table).Fields("*").Where("id = ?", id).Select()
|
||||
result, err := db.Model(table).Fields("*").Where("id = ?", id).Select()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
@ -1105,11 +1105,11 @@ func Test_DB_ToUintRecord(t *testing.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
_, err := db.Update(table, "create_time='2010-10-10 00:00:01'", "id=?", 1)
|
||||
gtest.Assert(err, nil)
|
||||
gtest.AssertNil(err)
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
id := 1
|
||||
result, err := db.Table(table).Fields("*").Where("id = ?", id).Select()
|
||||
result, err := db.Model(table).Fields("*").Where("id = ?", id).Select()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
@ -1169,7 +1169,7 @@ func Test_DB_TableField(t *testing.T) {
|
||||
"field_varchar": "abc",
|
||||
"field_varbinary": "aaa",
|
||||
}
|
||||
res, err := db.Table(name).Data(data).Insert()
|
||||
res, err := db.Model(name).Data(data).Insert()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
@ -1181,7 +1181,7 @@ func Test_DB_TableField(t *testing.T) {
|
||||
gtest.Assert(n, 1)
|
||||
}
|
||||
|
||||
result, err := db.Table(name).Fields("*").Where("field_int = ?", 2).Select()
|
||||
result, err := db.Model(name).Fields("*").Where("field_int = ?", 2).Select()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
@ -1191,8 +1191,8 @@ func Test_DB_TableField(t *testing.T) {
|
||||
|
||||
func Test_DB_Prefix(t *testing.T) {
|
||||
db := dbPrefix
|
||||
name := fmt.Sprintf(`%s_%d`, TABLE, gtime.TimestampNano())
|
||||
table := PREFIX1 + name
|
||||
name := fmt.Sprintf(`%s_%d`, TableName, gtime.TimestampNano())
|
||||
table := TableNamePrefix1 + name
|
||||
createTableWithDb(db, table)
|
||||
defer dropTable(table)
|
||||
|
||||
@ -1205,7 +1205,7 @@ func Test_DB_Prefix(t *testing.T) {
|
||||
"nickname": fmt.Sprintf(`name_%d`, id),
|
||||
"create_time": gtime.NewFromStr("2018-10-24 10:00:00").String(),
|
||||
})
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
|
||||
n, e := result.RowsAffected()
|
||||
t.Assert(e, nil)
|
||||
@ -1221,7 +1221,7 @@ func Test_DB_Prefix(t *testing.T) {
|
||||
"nickname": fmt.Sprintf(`name_%d`, id),
|
||||
"create_time": gtime.NewFromStr("2018-10-24 10:00:01").String(),
|
||||
})
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
|
||||
n, e := result.RowsAffected()
|
||||
t.Assert(e, nil)
|
||||
@ -1237,7 +1237,7 @@ func Test_DB_Prefix(t *testing.T) {
|
||||
"nickname": fmt.Sprintf(`name_%d`, id),
|
||||
"create_time": gtime.NewFromStr("2018-10-24 10:00:02").String(),
|
||||
})
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
|
||||
n, e := result.RowsAffected()
|
||||
t.Assert(e, nil)
|
||||
@ -1253,7 +1253,7 @@ func Test_DB_Prefix(t *testing.T) {
|
||||
"nickname": fmt.Sprintf(`name_%d`, id),
|
||||
"create_time": gtime.NewFromStr("2018-10-24 10:00:03").String(),
|
||||
}, "id=?", id)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
|
||||
n, e := result.RowsAffected()
|
||||
t.Assert(e, nil)
|
||||
@ -1263,7 +1263,7 @@ func Test_DB_Prefix(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
id := 10000
|
||||
result, err := db.Delete(name, "id=?", id)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
|
||||
n, e := result.RowsAffected()
|
||||
t.Assert(e, nil)
|
||||
@ -1272,7 +1272,7 @@ func Test_DB_Prefix(t *testing.T) {
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.New(true)
|
||||
for i := 1; i <= SIZE; i++ {
|
||||
for i := 1; i <= TableSize; i++ {
|
||||
array.Append(g.Map{
|
||||
"id": i,
|
||||
"passport": fmt.Sprintf(`user_%d`, i),
|
||||
@ -1283,11 +1283,11 @@ func Test_DB_Prefix(t *testing.T) {
|
||||
}
|
||||
|
||||
result, err := db.BatchInsert(name, array.Slice())
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
|
||||
n, e := result.RowsAffected()
|
||||
t.Assert(e, nil)
|
||||
t.Assert(n, SIZE)
|
||||
t.Assert(n, TableSize)
|
||||
})
|
||||
|
||||
}
|
||||
@ -1300,7 +1300,7 @@ func Test_Model_InnerJoin(t *testing.T) {
|
||||
defer dropTable(table1)
|
||||
defer dropTable(table2)
|
||||
|
||||
res, err := db.Table(table1).Where("id > ?", 5).Delete()
|
||||
res, err := db.Model(table1).Where("id > ?", 5).Delete()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@ -1312,14 +1312,14 @@ func Test_Model_InnerJoin(t *testing.T) {
|
||||
|
||||
t.Assert(n, 5)
|
||||
|
||||
result, err := db.Table(table1+" u1").InnerJoin(table2+" u2", "u1.id = u2.id").OrderBy("u1.id").Select()
|
||||
result, err := db.Model(table1+" u1").InnerJoin(table2+" u2", "u1.id = u2.id").OrderBy("u1.id").Select()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
t.Assert(len(result), 5)
|
||||
|
||||
result, err = db.Table(table1+" u1").InnerJoin(table2+" u2", "u1.id = u2.id").Where("u1.id > ?", 1).OrderBy("u1.id").Select()
|
||||
result, err = db.Model(table1+" u1").InnerJoin(table2+" u2", "u1.id = u2.id").Where("u1.id > ?", 1).OrderBy("u1.id").Select()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@ -1336,7 +1336,7 @@ func Test_Model_LeftJoin(t *testing.T) {
|
||||
defer dropTable(table1)
|
||||
defer dropTable(table2)
|
||||
|
||||
res, err := db.Table(table2).Where("id > ?", 3).Delete()
|
||||
res, err := db.Model(table2).Where("id > ?", 3).Delete()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@ -1348,14 +1348,14 @@ func Test_Model_LeftJoin(t *testing.T) {
|
||||
t.Assert(n, 7)
|
||||
}
|
||||
|
||||
result, err := db.Table(table1+" u1").LeftJoin(table2+" u2", "u1.id = u2.id").Select()
|
||||
result, err := db.Model(table1+" u1").LeftJoin(table2+" u2", "u1.id = u2.id").Select()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
t.Assert(len(result), 10)
|
||||
|
||||
result, err = db.Table(table1+" u1").LeftJoin(table2+" u2", "u1.id = u2.id").Where("u1.id > ? ", 2).Select()
|
||||
result, err = db.Model(table1+" u1").LeftJoin(table2+" u2", "u1.id = u2.id").Where("u1.id > ? ", 2).Select()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@ -1372,7 +1372,7 @@ func Test_Model_RightJoin(t *testing.T) {
|
||||
defer dropTable(table1)
|
||||
defer dropTable(table2)
|
||||
|
||||
res, err := db.Table(table1).Where("id > ?", 3).Delete()
|
||||
res, err := db.Model(table1).Where("id > ?", 3).Delete()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@ -1384,13 +1384,13 @@ func Test_Model_RightJoin(t *testing.T) {
|
||||
|
||||
t.Assert(n, 7)
|
||||
|
||||
result, err := db.Table(table1+" u1").RightJoin(table2+" u2", "u1.id = u2.id").Select()
|
||||
result, err := db.Model(table1+" u1").RightJoin(table2+" u2", "u1.id = u2.id").Select()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Assert(len(result), 10)
|
||||
|
||||
result, err = db.Table(table1+" u1").RightJoin(table2+" u2", "u1.id = u2.id").Where("u1.id > 2").Select()
|
||||
result, err = db.Model(table1+" u1").RightJoin(table2+" u2", "u1.id = u2.id").Where("u1.id > 2").Select()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@ -1403,7 +1403,7 @@ func Test_Empty_Slice_Argument(t *testing.T) {
|
||||
defer dropTable(table)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
result, err := db.GetAll(fmt.Sprintf(`select * from %s where id in(?)`, table), g.Slice{})
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(result), 0)
|
||||
})
|
||||
}
|
||||
@ -1430,7 +1430,7 @@ func Test_DB_UpdateCounter(t *testing.T) {
|
||||
"updated_time": 0,
|
||||
}
|
||||
_, err = db.Insert(tableName, insertData)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
})
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
@ -1442,11 +1442,11 @@ func Test_DB_UpdateCounter(t *testing.T) {
|
||||
"views": gdbCounter,
|
||||
}
|
||||
result, err := db.Update(tableName, updateData, "id", 1)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
n, _ := result.RowsAffected()
|
||||
t.Assert(n, 1)
|
||||
one, err := db.Table(tableName).Where("id", 1).One()
|
||||
t.Assert(err, nil)
|
||||
one, err := db.Model(tableName).Where("id", 1).One()
|
||||
t.AssertNil(err)
|
||||
t.Assert(one["id"].Int(), 1)
|
||||
t.Assert(one["views"].Int(), 1)
|
||||
})
|
||||
@ -1461,11 +1461,11 @@ func Test_DB_UpdateCounter(t *testing.T) {
|
||||
"updated_time": gtime.Now().Unix(),
|
||||
}
|
||||
result, err := db.Update(tableName, updateData, "id", 1)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
n, _ := result.RowsAffected()
|
||||
t.Assert(n, 1)
|
||||
one, err := db.Table(tableName).Where("id", 1).One()
|
||||
t.Assert(err, nil)
|
||||
one, err := db.Model(tableName).Where("id", 1).One()
|
||||
t.AssertNil(err)
|
||||
t.Assert(one["id"].Int(), 1)
|
||||
t.Assert(one["views"].Int(), 0)
|
||||
})
|
||||
@ -1486,6 +1486,6 @@ func Test_DB_Ctx_Logger(t *testing.T) {
|
||||
db.SetDebug(true)
|
||||
ctx := context.WithValue(context.Background(), "Trace-Id", "123456789")
|
||||
_, err := db.Ctx(ctx).Query("SELECT 1")
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
})
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -19,7 +19,7 @@ func Test_Insert_Raw(t *testing.T) {
|
||||
defer dropTable(table)
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
user := db.Table(table)
|
||||
user := db.Model(table)
|
||||
result, err := user.Filter().Data(g.Map{
|
||||
"id": gdb.Raw("id+2"),
|
||||
"passport": "port_1",
|
||||
@ -27,7 +27,7 @@ func Test_Insert_Raw(t *testing.T) {
|
||||
"nickname": "name_1",
|
||||
"create_time": gdb.Raw("now()"),
|
||||
}).Insert()
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
n, _ := result.LastInsertId()
|
||||
t.Assert(n, 2)
|
||||
})
|
||||
@ -38,7 +38,7 @@ func Test_BatchInsert_Raw(t *testing.T) {
|
||||
defer dropTable(table)
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
user := db.Table(table)
|
||||
user := db.Model(table)
|
||||
result, err := user.Filter().Data(
|
||||
g.List{
|
||||
g.Map{
|
||||
@ -57,7 +57,7 @@ func Test_BatchInsert_Raw(t *testing.T) {
|
||||
},
|
||||
},
|
||||
).Insert()
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
n, _ := result.LastInsertId()
|
||||
t.Assert(n, 4)
|
||||
})
|
||||
@ -68,19 +68,19 @@ func Test_Update_Raw(t *testing.T) {
|
||||
defer dropTable(table)
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
user := db.Table(table)
|
||||
user := db.Model(table)
|
||||
result, err := user.Data(g.Map{
|
||||
"id": gdb.Raw("id+100"),
|
||||
"create_time": gdb.Raw("now()"),
|
||||
}).Where("id", 1).Update()
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
n, _ := result.RowsAffected()
|
||||
t.Assert(n, 1)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
user := db.Table(table)
|
||||
user := db.Model(table)
|
||||
n, err := user.Where("id", 101).Count()
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
t.Assert(n, 1)
|
||||
})
|
||||
}
|
||||
|
||||
@ -8,6 +8,7 @@ package gdb_test
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"github.com/gogf/gf/database/gdb"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/os/gtime"
|
||||
"github.com/gogf/gf/test/gtest"
|
||||
@ -31,7 +32,7 @@ func Test_Model_Inherit_Insert(t *testing.T) {
|
||||
Password string `json:"password"`
|
||||
Nickname string `json:"nickname"`
|
||||
}
|
||||
result, err := db.Table(table).Filter().Data(User{
|
||||
result, err := db.Model(table).Filter().Data(User{
|
||||
Passport: "john-test",
|
||||
Password: "123456",
|
||||
Nickname: "John",
|
||||
@ -41,11 +42,11 @@ func Test_Model_Inherit_Insert(t *testing.T) {
|
||||
CreateTime: gtime.Now().String(),
|
||||
},
|
||||
}).Insert()
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
n, _ := result.RowsAffected()
|
||||
t.Assert(n, 1)
|
||||
value, err := db.Table(table).Fields("passport").Where("id=100").Value()
|
||||
t.Assert(err, nil)
|
||||
value, err := db.Model(table).Fields("passport").Where("id=100").Value()
|
||||
t.AssertNil(err)
|
||||
t.Assert(value.String(), "john-test")
|
||||
})
|
||||
}
|
||||
@ -77,13 +78,13 @@ func Test_Model_Inherit_MapToStruct(t *testing.T) {
|
||||
"nickname": "T1",
|
||||
"create_time": gtime.Now().String(),
|
||||
}
|
||||
result, err := db.Table(table).Filter().Data(data).Insert()
|
||||
t.Assert(err, nil)
|
||||
result, err := db.Model(table).Filter().Data(data).Insert()
|
||||
t.AssertNil(err)
|
||||
n, _ := result.RowsAffected()
|
||||
t.Assert(n, 1)
|
||||
|
||||
one, err := db.Table(table).Where("id=100").One()
|
||||
t.Assert(err, nil)
|
||||
one, err := db.Model(table).Where("id=100").One()
|
||||
t.AssertNil(err)
|
||||
|
||||
user := new(User)
|
||||
|
||||
@ -109,11 +110,11 @@ func Test_Struct_Pointer_Attribute(t *testing.T) {
|
||||
}
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
one, err := db.Table(table).FindOne(1)
|
||||
t.Assert(err, nil)
|
||||
one, err := db.Model(table).FindOne(1)
|
||||
t.AssertNil(err)
|
||||
user := new(User)
|
||||
err = one.Struct(user)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
t.Assert(*user.Id, 1)
|
||||
t.Assert(*user.Passport, "user_1")
|
||||
t.Assert(*user.Password, "pass_1")
|
||||
@ -121,8 +122,8 @@ func Test_Struct_Pointer_Attribute(t *testing.T) {
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
user := new(User)
|
||||
err := db.Table(table).Struct(user, "id=1")
|
||||
t.Assert(err, nil)
|
||||
err := db.Model(table).Struct(user, "id=1")
|
||||
t.AssertNil(err)
|
||||
t.Assert(*user.Id, 1)
|
||||
t.Assert(*user.Passport, "user_1")
|
||||
t.Assert(*user.Password, "pass_1")
|
||||
@ -130,8 +131,8 @@ func Test_Struct_Pointer_Attribute(t *testing.T) {
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var user *User
|
||||
err := db.Table(table).Struct(&user, "id=1")
|
||||
t.Assert(err, nil)
|
||||
err := db.Model(table).Struct(&user, "id=1")
|
||||
t.AssertNil(err)
|
||||
t.Assert(*user.Id, 1)
|
||||
t.Assert(*user.Passport, "user_1")
|
||||
t.Assert(*user.Password, "pass_1")
|
||||
@ -151,11 +152,11 @@ func Test_Structs_Pointer_Attribute(t *testing.T) {
|
||||
}
|
||||
// All
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
one, err := db.Table(table).All("id < 3")
|
||||
t.Assert(err, nil)
|
||||
one, err := db.Model(table).All("id < 3")
|
||||
t.AssertNil(err)
|
||||
users := make([]User, 0)
|
||||
err = one.Structs(&users)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(users), 2)
|
||||
t.Assert(*users[0].Id, 1)
|
||||
t.Assert(*users[0].Passport, "user_1")
|
||||
@ -163,11 +164,11 @@ func Test_Structs_Pointer_Attribute(t *testing.T) {
|
||||
t.Assert(users[0].Nickname, "name_1")
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
one, err := db.Table(table).All("id < 3")
|
||||
t.Assert(err, nil)
|
||||
one, err := db.Model(table).All("id < 3")
|
||||
t.AssertNil(err)
|
||||
users := make([]*User, 0)
|
||||
err = one.Structs(&users)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(users), 2)
|
||||
t.Assert(*users[0].Id, 1)
|
||||
t.Assert(*users[0].Passport, "user_1")
|
||||
@ -176,10 +177,10 @@ func Test_Structs_Pointer_Attribute(t *testing.T) {
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var users []User
|
||||
one, err := db.Table(table).All("id < 3")
|
||||
t.Assert(err, nil)
|
||||
one, err := db.Model(table).All("id < 3")
|
||||
t.AssertNil(err)
|
||||
err = one.Structs(&users)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(users), 2)
|
||||
t.Assert(*users[0].Id, 1)
|
||||
t.Assert(*users[0].Passport, "user_1")
|
||||
@ -188,10 +189,10 @@ func Test_Structs_Pointer_Attribute(t *testing.T) {
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var users []*User
|
||||
one, err := db.Table(table).All("id < 3")
|
||||
t.Assert(err, nil)
|
||||
one, err := db.Model(table).All("id < 3")
|
||||
t.AssertNil(err)
|
||||
err = one.Structs(&users)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(users), 2)
|
||||
t.Assert(*users[0].Id, 1)
|
||||
t.Assert(*users[0].Passport, "user_1")
|
||||
@ -201,8 +202,8 @@ func Test_Structs_Pointer_Attribute(t *testing.T) {
|
||||
// Structs
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
users := make([]User, 0)
|
||||
err := db.Table(table).Structs(&users, "id < 3")
|
||||
t.Assert(err, nil)
|
||||
err := db.Model(table).Structs(&users, "id < 3")
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(users), 2)
|
||||
t.Assert(*users[0].Id, 1)
|
||||
t.Assert(*users[0].Passport, "user_1")
|
||||
@ -211,8 +212,8 @@ func Test_Structs_Pointer_Attribute(t *testing.T) {
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
users := make([]*User, 0)
|
||||
err := db.Table(table).Structs(&users, "id < 3")
|
||||
t.Assert(err, nil)
|
||||
err := db.Model(table).Structs(&users, "id < 3")
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(users), 2)
|
||||
t.Assert(*users[0].Id, 1)
|
||||
t.Assert(*users[0].Passport, "user_1")
|
||||
@ -221,8 +222,8 @@ func Test_Structs_Pointer_Attribute(t *testing.T) {
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var users []User
|
||||
err := db.Table(table).Structs(&users, "id < 3")
|
||||
t.Assert(err, nil)
|
||||
err := db.Model(table).Structs(&users, "id < 3")
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(users), 2)
|
||||
t.Assert(*users[0].Id, 1)
|
||||
t.Assert(*users[0].Passport, "user_1")
|
||||
@ -231,8 +232,8 @@ func Test_Structs_Pointer_Attribute(t *testing.T) {
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var users []*User
|
||||
err := db.Table(table).Structs(&users, "id < 3")
|
||||
t.Assert(err, nil)
|
||||
err := db.Model(table).Structs(&users, "id < 3")
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(users), 2)
|
||||
t.Assert(*users[0].Id, 1)
|
||||
t.Assert(*users[0].Passport, "user_1")
|
||||
@ -254,14 +255,14 @@ func Test_Struct_Empty(t *testing.T) {
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
user := new(User)
|
||||
err := db.Table(table).Where("id=100").Struct(user)
|
||||
err := db.Model(table).Where("id=100").Struct(user)
|
||||
t.Assert(err, sql.ErrNoRows)
|
||||
t.AssertNE(user, nil)
|
||||
})
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
one, err := db.Table(table).Where("id=100").One()
|
||||
t.Assert(err, nil)
|
||||
one, err := db.Model(table).Where("id=100").One()
|
||||
t.AssertNil(err)
|
||||
var user *User
|
||||
t.Assert(one.Struct(&user), nil)
|
||||
t.Assert(user, nil)
|
||||
@ -269,8 +270,8 @@ func Test_Struct_Empty(t *testing.T) {
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var user *User
|
||||
err := db.Table(table).Where("id=100").Struct(&user)
|
||||
t.Assert(err, nil)
|
||||
err := db.Model(table).Where("id=100").Struct(&user)
|
||||
t.AssertNil(err)
|
||||
t.Assert(user, nil)
|
||||
})
|
||||
}
|
||||
@ -287,39 +288,39 @@ func Test_Structs_Empty(t *testing.T) {
|
||||
}
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
all, err := db.Table(table).Where("id>100").All()
|
||||
t.Assert(err, nil)
|
||||
all, err := db.Model(table).Where("id>100").All()
|
||||
t.AssertNil(err)
|
||||
users := make([]User, 0)
|
||||
t.Assert(all.Structs(&users), nil)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
all, err := db.Table(table).Where("id>100").All()
|
||||
t.Assert(err, nil)
|
||||
all, err := db.Model(table).Where("id>100").All()
|
||||
t.AssertNil(err)
|
||||
users := make([]User, 10)
|
||||
t.AssertNE(all.Structs(&users), nil)
|
||||
t.Assert(all.Structs(&users), nil)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
all, err := db.Table(table).Where("id>100").All()
|
||||
t.Assert(err, nil)
|
||||
all, err := db.Model(table).Where("id>100").All()
|
||||
t.AssertNil(err)
|
||||
var users []User
|
||||
t.Assert(all.Structs(&users), nil)
|
||||
})
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
all, err := db.Table(table).Where("id>100").All()
|
||||
t.Assert(err, nil)
|
||||
all, err := db.Model(table).Where("id>100").All()
|
||||
t.AssertNil(err)
|
||||
users := make([]*User, 0)
|
||||
t.Assert(all.Structs(&users), nil)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
all, err := db.Table(table).Where("id>100").All()
|
||||
t.Assert(err, nil)
|
||||
all, err := db.Model(table).Where("id>100").All()
|
||||
t.AssertNil(err)
|
||||
users := make([]*User, 10)
|
||||
t.Assert(all.Structs(&users), nil)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
all, err := db.Table(table).Where("id>100").All()
|
||||
t.Assert(err, nil)
|
||||
all, err := db.Model(table).Where("id>100").All()
|
||||
t.AssertNil(err)
|
||||
var users []*User
|
||||
t.Assert(all.Structs(&users), nil)
|
||||
})
|
||||
@ -343,21 +344,65 @@ func (st *MyTimeSt) UnmarshalValue(v interface{}) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func Test_Model_Scan_CustomType(t *testing.T) {
|
||||
func Test_Model_Scan_CustomType_Time(t *testing.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
st := new(MyTimeSt)
|
||||
err := db.Table(table).Fields("create_time").Scan(st)
|
||||
t.Assert(err, nil)
|
||||
err := db.Model(table).Fields("create_time").Scan(st)
|
||||
t.AssertNil(err)
|
||||
t.Assert(st.CreateTime.String(), "2018-10-24 10:00:00")
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var stSlice []*MyTimeSt
|
||||
err := db.Table(table).Fields("create_time").Scan(&stSlice)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(len(stSlice), SIZE)
|
||||
err := db.Model(table).Fields("create_time").Scan(&stSlice)
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(stSlice), TableSize)
|
||||
t.Assert(stSlice[0].CreateTime.String(), "2018-10-24 10:00:00")
|
||||
t.Assert(stSlice[9].CreateTime.String(), "2018-10-24 10:00:00")
|
||||
})
|
||||
}
|
||||
|
||||
type User struct {
|
||||
Id int
|
||||
Passport string
|
||||
Password string
|
||||
Nickname string
|
||||
CreateTime *gtime.Time
|
||||
}
|
||||
|
||||
func (user *User) UnmarshalValue(value interface{}) error {
|
||||
switch result := value.(type) {
|
||||
case map[string]interface{}:
|
||||
user.Id = result["id"].(gdb.Value).Int()
|
||||
user.Passport = result["passport"].(gdb.Value).String()
|
||||
user.Password = ""
|
||||
user.Nickname = result["nickname"].(gdb.Value).String()
|
||||
user.CreateTime = result["create_time"].(gdb.Value).GTime()
|
||||
return nil
|
||||
default:
|
||||
return gconv.Struct(value, user)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_Model_Scan_UnmarshalValue(t *testing.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var users []*User
|
||||
err := db.Model(table).Order("id asc").Scan(&users)
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(users), TableSize)
|
||||
t.Assert(users[0].Id, 1)
|
||||
t.Assert(users[0].Passport, "user_1")
|
||||
t.Assert(users[0].Password, "")
|
||||
t.Assert(users[0].Nickname, "name_1")
|
||||
t.Assert(users[0].CreateTime.String(), CreateTime)
|
||||
|
||||
t.Assert(users[9].Id, 10)
|
||||
t.Assert(users[9].Passport, "user_10")
|
||||
t.Assert(users[9].Password, "")
|
||||
t.Assert(users[9].Nickname, "name_10")
|
||||
t.Assert(users[9].CreateTime.String(), CreateTime)
|
||||
})
|
||||
}
|
||||
|
||||
@ -40,13 +40,13 @@ CREATE TABLE %s (
|
||||
"id": 1,
|
||||
"name": "name_1",
|
||||
}
|
||||
r, err := db.Table(table).Data(dataInsert).Insert()
|
||||
t.Assert(err, nil)
|
||||
r, err := db.Model(table).Data(dataInsert).Insert()
|
||||
t.AssertNil(err)
|
||||
n, _ := r.RowsAffected()
|
||||
t.Assert(n, 1)
|
||||
|
||||
oneInsert, err := db.Table(table).FindOne(1)
|
||||
t.Assert(err, nil)
|
||||
oneInsert, err := db.Model(table).FindOne(1)
|
||||
t.AssertNil(err)
|
||||
t.Assert(oneInsert["id"].Int(), 1)
|
||||
t.Assert(oneInsert["name"].String(), "name_1")
|
||||
t.Assert(oneInsert["delete_at"].String(), "")
|
||||
@ -61,13 +61,13 @@ CREATE TABLE %s (
|
||||
"id": 1,
|
||||
"name": "name_10",
|
||||
}
|
||||
r, err = db.Table(table).Data(dataSave).Save()
|
||||
t.Assert(err, nil)
|
||||
r, err = db.Model(table).Data(dataSave).Save()
|
||||
t.AssertNil(err)
|
||||
n, _ = r.RowsAffected()
|
||||
t.Assert(n, 2)
|
||||
|
||||
oneSave, err := db.Table(table).FindOne(1)
|
||||
t.Assert(err, nil)
|
||||
oneSave, err := db.Model(table).FindOne(1)
|
||||
t.AssertNil(err)
|
||||
t.Assert(oneSave["id"].Int(), 1)
|
||||
t.Assert(oneSave["name"].String(), "name_10")
|
||||
t.Assert(oneSave["delete_at"].String(), "")
|
||||
@ -82,13 +82,13 @@ CREATE TABLE %s (
|
||||
dataUpdate := g.Map{
|
||||
"name": "name_1000",
|
||||
}
|
||||
r, err = db.Table(table).Data(dataUpdate).WherePri(1).Update()
|
||||
t.Assert(err, nil)
|
||||
r, err = db.Model(table).Data(dataUpdate).WherePri(1).Update()
|
||||
t.AssertNil(err)
|
||||
n, _ = r.RowsAffected()
|
||||
t.Assert(n, 1)
|
||||
|
||||
oneUpdate, err := db.Table(table).FindOne(1)
|
||||
t.Assert(err, nil)
|
||||
oneUpdate, err := db.Model(table).FindOne(1)
|
||||
t.AssertNil(err)
|
||||
t.Assert(oneUpdate["id"].Int(), 1)
|
||||
t.Assert(oneUpdate["name"].String(), "name_1000")
|
||||
t.Assert(oneUpdate["delete_at"].String(), "")
|
||||
@ -100,13 +100,13 @@ CREATE TABLE %s (
|
||||
"id": 1,
|
||||
"name": "name_100",
|
||||
}
|
||||
r, err = db.Table(table).Data(dataReplace).Replace()
|
||||
t.Assert(err, nil)
|
||||
r, err = db.Model(table).Data(dataReplace).Replace()
|
||||
t.AssertNil(err)
|
||||
n, _ = r.RowsAffected()
|
||||
t.Assert(n, 2)
|
||||
|
||||
oneReplace, err := db.Table(table).FindOne(1)
|
||||
t.Assert(err, nil)
|
||||
oneReplace, err := db.Model(table).FindOne(1)
|
||||
t.AssertNil(err)
|
||||
t.Assert(oneReplace["id"].Int(), 1)
|
||||
t.Assert(oneReplace["name"].String(), "name_100")
|
||||
t.Assert(oneReplace["delete_at"].String(), "")
|
||||
@ -117,36 +117,36 @@ CREATE TABLE %s (
|
||||
time.Sleep(2 * time.Second)
|
||||
|
||||
// Delete
|
||||
r, err = db.Table(table).Delete("id", 1)
|
||||
t.Assert(err, nil)
|
||||
r, err = db.Model(table).Delete("id", 1)
|
||||
t.AssertNil(err)
|
||||
n, _ = r.RowsAffected()
|
||||
t.Assert(n, 1)
|
||||
// Delete Select
|
||||
one4, err := db.Table(table).FindOne(1)
|
||||
t.Assert(err, nil)
|
||||
one4, err := db.Model(table).FindOne(1)
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(one4), 0)
|
||||
one5, err := db.Table(table).Unscoped().FindOne(1)
|
||||
t.Assert(err, nil)
|
||||
one5, err := db.Model(table).Unscoped().FindOne(1)
|
||||
t.AssertNil(err)
|
||||
t.Assert(one5["id"].Int(), 1)
|
||||
t.AssertGE(one5["delete_at"].GTime().Timestamp(), gtime.Timestamp()-2)
|
||||
// Delete Count
|
||||
i, err := db.Table(table).FindCount()
|
||||
t.Assert(err, nil)
|
||||
i, err := db.Model(table).FindCount()
|
||||
t.AssertNil(err)
|
||||
t.Assert(i, 0)
|
||||
i, err = db.Table(table).Unscoped().FindCount()
|
||||
t.Assert(err, nil)
|
||||
i, err = db.Model(table).Unscoped().FindCount()
|
||||
t.AssertNil(err)
|
||||
t.Assert(i, 1)
|
||||
|
||||
// Delete Unscoped
|
||||
r, err = db.Table(table).Unscoped().Delete("id", 1)
|
||||
t.Assert(err, nil)
|
||||
r, err = db.Model(table).Unscoped().Delete("id", 1)
|
||||
t.AssertNil(err)
|
||||
n, _ = r.RowsAffected()
|
||||
t.Assert(n, 1)
|
||||
one6, err := db.Table(table).Unscoped().FindOne(1)
|
||||
t.Assert(err, nil)
|
||||
one6, err := db.Model(table).Unscoped().FindOne(1)
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(one6), 0)
|
||||
i, err = db.Table(table).Unscoped().FindCount()
|
||||
t.Assert(err, nil)
|
||||
i, err = db.Model(table).Unscoped().FindCount()
|
||||
t.AssertNil(err)
|
||||
t.Assert(i, 0)
|
||||
})
|
||||
}
|
||||
@ -174,13 +174,13 @@ CREATE TABLE %s (
|
||||
"id": 1,
|
||||
"name": "name_1",
|
||||
}
|
||||
r, err := db.Table(table).Data(dataInsert).Insert()
|
||||
t.Assert(err, nil)
|
||||
r, err := db.Model(table).Data(dataInsert).Insert()
|
||||
t.AssertNil(err)
|
||||
n, _ := r.RowsAffected()
|
||||
t.Assert(n, 1)
|
||||
|
||||
oneInsert, err := db.Table(table).FindOne(1)
|
||||
t.Assert(err, nil)
|
||||
oneInsert, err := db.Model(table).FindOne(1)
|
||||
t.AssertNil(err)
|
||||
t.Assert(oneInsert["id"].Int(), 1)
|
||||
t.Assert(oneInsert["name"].String(), "name_1")
|
||||
t.Assert(oneInsert["deleted_at"].String(), "")
|
||||
@ -195,13 +195,13 @@ CREATE TABLE %s (
|
||||
"id": 1,
|
||||
"name": "name_10",
|
||||
}
|
||||
r, err = db.Table(table).Data(dataSave).Save()
|
||||
t.Assert(err, nil)
|
||||
r, err = db.Model(table).Data(dataSave).Save()
|
||||
t.AssertNil(err)
|
||||
n, _ = r.RowsAffected()
|
||||
t.Assert(n, 2)
|
||||
|
||||
oneSave, err := db.Table(table).FindOne(1)
|
||||
t.Assert(err, nil)
|
||||
oneSave, err := db.Model(table).FindOne(1)
|
||||
t.AssertNil(err)
|
||||
t.Assert(oneSave["id"].Int(), 1)
|
||||
t.Assert(oneSave["name"].String(), "name_10")
|
||||
t.Assert(oneSave["deleted_at"].String(), "")
|
||||
@ -216,13 +216,13 @@ CREATE TABLE %s (
|
||||
dataUpdate := g.Map{
|
||||
"name": "name_1000",
|
||||
}
|
||||
r, err = db.Table(table).Data(dataUpdate).WherePri(1).Update()
|
||||
t.Assert(err, nil)
|
||||
r, err = db.Model(table).Data(dataUpdate).WherePri(1).Update()
|
||||
t.AssertNil(err)
|
||||
n, _ = r.RowsAffected()
|
||||
t.Assert(n, 1)
|
||||
|
||||
oneUpdate, err := db.Table(table).FindOne(1)
|
||||
t.Assert(err, nil)
|
||||
oneUpdate, err := db.Model(table).FindOne(1)
|
||||
t.AssertNil(err)
|
||||
t.Assert(oneUpdate["id"].Int(), 1)
|
||||
t.Assert(oneUpdate["name"].String(), "name_1000")
|
||||
t.Assert(oneUpdate["deleted_at"].String(), "")
|
||||
@ -234,13 +234,13 @@ CREATE TABLE %s (
|
||||
"id": 1,
|
||||
"name": "name_100",
|
||||
}
|
||||
r, err = db.Table(table).Data(dataReplace).Replace()
|
||||
t.Assert(err, nil)
|
||||
r, err = db.Model(table).Data(dataReplace).Replace()
|
||||
t.AssertNil(err)
|
||||
n, _ = r.RowsAffected()
|
||||
t.Assert(n, 2)
|
||||
|
||||
oneReplace, err := db.Table(table).FindOne(1)
|
||||
t.Assert(err, nil)
|
||||
oneReplace, err := db.Model(table).FindOne(1)
|
||||
t.AssertNil(err)
|
||||
t.Assert(oneReplace["id"].Int(), 1)
|
||||
t.Assert(oneReplace["name"].String(), "name_100")
|
||||
t.Assert(oneReplace["deleted_at"].String(), "")
|
||||
@ -251,36 +251,36 @@ CREATE TABLE %s (
|
||||
time.Sleep(2 * time.Second)
|
||||
|
||||
// Delete
|
||||
r, err = db.Table(table).Delete("id", 1)
|
||||
t.Assert(err, nil)
|
||||
r, err = db.Model(table).Delete("id", 1)
|
||||
t.AssertNil(err)
|
||||
n, _ = r.RowsAffected()
|
||||
t.Assert(n, 1)
|
||||
// Delete Select
|
||||
one4, err := db.Table(table).FindOne(1)
|
||||
t.Assert(err, nil)
|
||||
one4, err := db.Model(table).FindOne(1)
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(one4), 0)
|
||||
one5, err := db.Table(table).Unscoped().FindOne(1)
|
||||
t.Assert(err, nil)
|
||||
one5, err := db.Model(table).Unscoped().FindOne(1)
|
||||
t.AssertNil(err)
|
||||
t.Assert(one5["id"].Int(), 1)
|
||||
t.AssertGE(one5["deleted_at"].GTime().Timestamp(), gtime.Timestamp()-2)
|
||||
// Delete Count
|
||||
i, err := db.Table(table).FindCount()
|
||||
t.Assert(err, nil)
|
||||
i, err := db.Model(table).FindCount()
|
||||
t.AssertNil(err)
|
||||
t.Assert(i, 0)
|
||||
i, err = db.Table(table).Unscoped().FindCount()
|
||||
t.Assert(err, nil)
|
||||
i, err = db.Model(table).Unscoped().FindCount()
|
||||
t.AssertNil(err)
|
||||
t.Assert(i, 1)
|
||||
|
||||
// Delete Unscoped
|
||||
r, err = db.Table(table).Unscoped().Delete("id", 1)
|
||||
t.Assert(err, nil)
|
||||
r, err = db.Model(table).Unscoped().Delete("id", 1)
|
||||
t.AssertNil(err)
|
||||
n, _ = r.RowsAffected()
|
||||
t.Assert(n, 1)
|
||||
one6, err := db.Table(table).Unscoped().FindOne(1)
|
||||
t.Assert(err, nil)
|
||||
one6, err := db.Model(table).Unscoped().FindOne(1)
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(one6), 0)
|
||||
i, err = db.Table(table).Unscoped().FindCount()
|
||||
t.Assert(err, nil)
|
||||
i, err = db.Model(table).Unscoped().FindCount()
|
||||
t.AssertNil(err)
|
||||
t.Assert(i, 0)
|
||||
})
|
||||
}
|
||||
@ -315,13 +315,13 @@ CREATE TABLE %s (
|
||||
Id: 1,
|
||||
Name: "name_1",
|
||||
}
|
||||
r, err := db.Table(table).Data(dataInsert).Insert()
|
||||
t.Assert(err, nil)
|
||||
r, err := db.Model(table).Data(dataInsert).Insert()
|
||||
t.AssertNil(err)
|
||||
n, _ := r.RowsAffected()
|
||||
t.Assert(n, 1)
|
||||
|
||||
oneInsert, err := db.Table(table).FindOne(1)
|
||||
t.Assert(err, nil)
|
||||
oneInsert, err := db.Model(table).FindOne(1)
|
||||
t.AssertNil(err)
|
||||
t.Assert(oneInsert["id"].Int(), 1)
|
||||
t.Assert(oneInsert["name"].String(), "name_1")
|
||||
t.Assert(oneInsert["deleted_at"].String(), "")
|
||||
@ -336,13 +336,13 @@ CREATE TABLE %s (
|
||||
Id: 1,
|
||||
Name: "name_10",
|
||||
}
|
||||
r, err = db.Table(table).Data(dataSave).OmitEmpty().Save()
|
||||
t.Assert(err, nil)
|
||||
r, err = db.Model(table).Data(dataSave).OmitEmpty().Save()
|
||||
t.AssertNil(err)
|
||||
n, _ = r.RowsAffected()
|
||||
t.Assert(n, 2)
|
||||
|
||||
oneSave, err := db.Table(table).FindOne(1)
|
||||
t.Assert(err, nil)
|
||||
oneSave, err := db.Model(table).FindOne(1)
|
||||
t.AssertNil(err)
|
||||
t.Assert(oneSave["id"].Int(), 1)
|
||||
t.Assert(oneSave["name"].String(), "name_10")
|
||||
t.Assert(oneSave["deleted_at"].String(), "")
|
||||
@ -357,13 +357,13 @@ CREATE TABLE %s (
|
||||
dataUpdate := User{
|
||||
Name: "name_1000",
|
||||
}
|
||||
r, err = db.Table(table).Data(dataUpdate).OmitEmpty().WherePri(1).Update()
|
||||
t.Assert(err, nil)
|
||||
r, err = db.Model(table).Data(dataUpdate).OmitEmpty().WherePri(1).Update()
|
||||
t.AssertNil(err)
|
||||
n, _ = r.RowsAffected()
|
||||
t.Assert(n, 1)
|
||||
|
||||
oneUpdate, err := db.Table(table).FindOne(1)
|
||||
t.Assert(err, nil)
|
||||
oneUpdate, err := db.Model(table).FindOne(1)
|
||||
t.AssertNil(err)
|
||||
t.Assert(oneUpdate["id"].Int(), 1)
|
||||
t.Assert(oneUpdate["name"].String(), "name_1000")
|
||||
t.Assert(oneUpdate["deleted_at"].String(), "")
|
||||
@ -375,13 +375,13 @@ CREATE TABLE %s (
|
||||
Id: 1,
|
||||
Name: "name_100",
|
||||
}
|
||||
r, err = db.Table(table).Data(dataReplace).OmitEmpty().Replace()
|
||||
t.Assert(err, nil)
|
||||
r, err = db.Model(table).Data(dataReplace).OmitEmpty().Replace()
|
||||
t.AssertNil(err)
|
||||
n, _ = r.RowsAffected()
|
||||
t.Assert(n, 2)
|
||||
|
||||
oneReplace, err := db.Table(table).FindOne(1)
|
||||
t.Assert(err, nil)
|
||||
oneReplace, err := db.Model(table).FindOne(1)
|
||||
t.AssertNil(err)
|
||||
t.Assert(oneReplace["id"].Int(), 1)
|
||||
t.Assert(oneReplace["name"].String(), "name_100")
|
||||
t.Assert(oneReplace["deleted_at"].String(), "")
|
||||
@ -392,36 +392,36 @@ CREATE TABLE %s (
|
||||
time.Sleep(2 * time.Second)
|
||||
|
||||
// Delete
|
||||
r, err = db.Table(table).Delete("id", 1)
|
||||
t.Assert(err, nil)
|
||||
r, err = db.Model(table).Delete("id", 1)
|
||||
t.AssertNil(err)
|
||||
n, _ = r.RowsAffected()
|
||||
t.Assert(n, 1)
|
||||
// Delete Select
|
||||
one4, err := db.Table(table).FindOne(1)
|
||||
t.Assert(err, nil)
|
||||
one4, err := db.Model(table).FindOne(1)
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(one4), 0)
|
||||
one5, err := db.Table(table).Unscoped().FindOne(1)
|
||||
t.Assert(err, nil)
|
||||
one5, err := db.Model(table).Unscoped().FindOne(1)
|
||||
t.AssertNil(err)
|
||||
t.Assert(one5["id"].Int(), 1)
|
||||
t.AssertGE(one5["deleted_at"].GTime().Timestamp(), gtime.Timestamp()-2)
|
||||
// Delete Count
|
||||
i, err := db.Table(table).FindCount()
|
||||
t.Assert(err, nil)
|
||||
i, err := db.Model(table).FindCount()
|
||||
t.AssertNil(err)
|
||||
t.Assert(i, 0)
|
||||
i, err = db.Table(table).Unscoped().FindCount()
|
||||
t.Assert(err, nil)
|
||||
i, err = db.Model(table).Unscoped().FindCount()
|
||||
t.AssertNil(err)
|
||||
t.Assert(i, 1)
|
||||
|
||||
// Delete Unscoped
|
||||
r, err = db.Table(table).Unscoped().Delete("id", 1)
|
||||
t.Assert(err, nil)
|
||||
r, err = db.Model(table).Unscoped().Delete("id", 1)
|
||||
t.AssertNil(err)
|
||||
n, _ = r.RowsAffected()
|
||||
t.Assert(n, 1)
|
||||
one6, err := db.Table(table).Unscoped().FindOne(1)
|
||||
t.Assert(err, nil)
|
||||
one6, err := db.Model(table).Unscoped().FindOne(1)
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(one6), 0)
|
||||
i, err = db.Table(table).Unscoped().FindCount()
|
||||
t.Assert(err, nil)
|
||||
i, err = db.Model(table).Unscoped().FindCount()
|
||||
t.AssertNil(err)
|
||||
t.Assert(i, 0)
|
||||
})
|
||||
}
|
||||
@ -448,26 +448,26 @@ CREATE TABLE %s (
|
||||
"id": 1,
|
||||
"num": 10,
|
||||
}
|
||||
r, err := db.Table(table).Data(dataInsert).Insert()
|
||||
t.Assert(err, nil)
|
||||
r, err := db.Model(table).Data(dataInsert).Insert()
|
||||
t.AssertNil(err)
|
||||
n, _ := r.RowsAffected()
|
||||
t.Assert(n, 1)
|
||||
|
||||
oneInsert, err := db.Table(table).FindOne(1)
|
||||
t.Assert(err, nil)
|
||||
oneInsert, err := db.Model(table).FindOne(1)
|
||||
t.AssertNil(err)
|
||||
t.Assert(oneInsert["id"].Int(), 1)
|
||||
t.Assert(oneInsert["num"].Int(), 10)
|
||||
|
||||
// Update.
|
||||
r, err = db.Table(table).Data("num=num+1").Where("id=?", 1).Update()
|
||||
t.Assert(err, nil)
|
||||
r, err = db.Model(table).Data("num=num+1").Where("id=?", 1).Update()
|
||||
t.AssertNil(err)
|
||||
n, _ = r.RowsAffected()
|
||||
t.Assert(n, 1)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_SoftDelete(t *testing.T) {
|
||||
table := "time_test_table"
|
||||
table := "time_test_table_" + gtime.TimestampNanoStr()
|
||||
if _, err := db.Exec(fmt.Sprintf(`
|
||||
CREATE TABLE %s (
|
||||
id int(11) NOT NULL,
|
||||
@ -488,39 +488,39 @@ CREATE TABLE %s (
|
||||
"id": i,
|
||||
"name": fmt.Sprintf("name_%d", i),
|
||||
}
|
||||
r, err := db.Table(table).Data(data).Insert()
|
||||
t.Assert(err, nil)
|
||||
r, err := db.Model(table).Data(data).Insert()
|
||||
t.AssertNil(err)
|
||||
n, _ := r.RowsAffected()
|
||||
t.Assert(n, 1)
|
||||
}
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
one, err := db.Table(table).FindOne(1)
|
||||
t.Assert(err, nil)
|
||||
one, err := db.Model(table).FindOne(1)
|
||||
t.AssertNil(err)
|
||||
t.AssertNE(one["create_at"].String(), "")
|
||||
t.AssertNE(one["update_at"].String(), "")
|
||||
t.Assert(one["delete_at"].String(), "")
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
one, err := db.Table(table).FindOne(10)
|
||||
t.Assert(err, nil)
|
||||
one, err := db.Model(table).FindOne(10)
|
||||
t.AssertNil(err)
|
||||
t.AssertNE(one["create_at"].String(), "")
|
||||
t.AssertNE(one["update_at"].String(), "")
|
||||
t.Assert(one["delete_at"].String(), "")
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
ids := g.SliceInt{1, 3, 5}
|
||||
r, err := db.Table(table).Where("id", ids).Delete()
|
||||
t.Assert(err, nil)
|
||||
r, err := db.Model(table).Where("id", ids).Delete()
|
||||
t.AssertNil(err)
|
||||
n, _ := r.RowsAffected()
|
||||
t.Assert(n, 3)
|
||||
|
||||
count, err := db.Table(table).FindCount(ids)
|
||||
t.Assert(err, nil)
|
||||
count, err := db.Model(table).FindCount(ids)
|
||||
t.AssertNil(err)
|
||||
t.Assert(count, 0)
|
||||
|
||||
all, err := db.Table(table).Unscoped().FindAll(ids)
|
||||
t.Assert(err, nil)
|
||||
all, err := db.Model(table).Unscoped().FindAll(ids)
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(all), 3)
|
||||
t.AssertNE(all[0]["create_at"].String(), "")
|
||||
t.AssertNE(all[0]["update_at"].String(), "")
|
||||
@ -571,8 +571,8 @@ CREATE TABLE %s (
|
||||
"id": 1,
|
||||
"name": "name_1",
|
||||
}
|
||||
r, err := db.Table(table1).Data(dataInsert1).Insert()
|
||||
t.Assert(err, nil)
|
||||
r, err := db.Model(table1).Data(dataInsert1).Insert()
|
||||
t.AssertNil(err)
|
||||
n, _ := r.RowsAffected()
|
||||
t.Assert(n, 1)
|
||||
|
||||
@ -580,33 +580,75 @@ CREATE TABLE %s (
|
||||
"id": 1,
|
||||
"name": "name_2",
|
||||
}
|
||||
r, err = db.Table(table2).Data(dataInsert2).Insert()
|
||||
t.Assert(err, nil)
|
||||
r, err = db.Model(table2).Data(dataInsert2).Insert()
|
||||
t.AssertNil(err)
|
||||
n, _ = r.RowsAffected()
|
||||
t.Assert(n, 1)
|
||||
|
||||
one, err := db.Table(table1, "t1").LeftJoin(table2, "t2", "t2.id=t1.id").Fields("t1.name").FindOne()
|
||||
t.Assert(err, nil)
|
||||
one, err := db.Model(table1, "t1").LeftJoin(table2, "t2", "t2.id=t1.id").Fields("t1.name").FindOne()
|
||||
t.AssertNil(err)
|
||||
t.Assert(one["name"], "name_1")
|
||||
|
||||
// Soft deleting.
|
||||
r, err = db.Table(table1).Delete()
|
||||
t.Assert(err, nil)
|
||||
r, err = db.Model(table1).Delete()
|
||||
t.AssertNil(err)
|
||||
n, _ = r.RowsAffected()
|
||||
t.Assert(n, 1)
|
||||
|
||||
one, err = db.Table(table1, "t1").LeftJoin(table2, "t2", "t2.id=t1.id").Fields("t1.name").FindOne()
|
||||
t.Assert(err, nil)
|
||||
one, err = db.Model(table1, "t1").LeftJoin(table2, "t2", "t2.id=t1.id").Fields("t1.name").FindOne()
|
||||
t.AssertNil(err)
|
||||
t.Assert(one.IsEmpty(), true)
|
||||
|
||||
one, err = db.Table(table2, "t2").LeftJoin(table1, "t1", "t2.id=t1.id").Fields("t2.name").FindOne()
|
||||
t.Assert(err, nil)
|
||||
one, err = db.Model(table2, "t2").LeftJoin(table1, "t1", "t2.id=t1.id").Fields("t2.name").FindOne()
|
||||
t.AssertNil(err)
|
||||
t.Assert(one.IsEmpty(), true)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_SoftDelete_WhereAndOr(t *testing.T) {
|
||||
table := "time_test_table_" + gtime.TimestampNanoStr()
|
||||
if _, err := db.Exec(fmt.Sprintf(`
|
||||
CREATE TABLE %s (
|
||||
id int(11) NOT NULL,
|
||||
name varchar(45) DEFAULT NULL,
|
||||
create_at datetime DEFAULT NULL,
|
||||
update_at datetime DEFAULT NULL,
|
||||
delete_at datetime DEFAULT NULL,
|
||||
PRIMARY KEY (id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
`, table)); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
defer dropTable(table)
|
||||
//db.SetDebug(true)
|
||||
// Add datas.
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
for i := 1; i <= 10; i++ {
|
||||
data := g.Map{
|
||||
"id": i,
|
||||
"name": fmt.Sprintf("name_%d", i),
|
||||
}
|
||||
r, err := db.Model(table).Data(data).Insert()
|
||||
t.AssertNil(err)
|
||||
n, _ := r.RowsAffected()
|
||||
t.Assert(n, 1)
|
||||
}
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
ids := g.SliceInt{1, 3, 5}
|
||||
r, err := db.Model(table).Where("id", ids).Delete()
|
||||
t.AssertNil(err)
|
||||
n, _ := r.RowsAffected()
|
||||
t.Assert(n, 3)
|
||||
|
||||
count, err := db.Model(table).Where("id", 1).Or("id", 3).Count()
|
||||
t.AssertNil(err)
|
||||
t.Assert(count, 0)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_CreateUpdateTime_Struct(t *testing.T) {
|
||||
table := "time_test_table"
|
||||
table := "time_test_table_" + gtime.TimestampNanoStr()
|
||||
if _, err := db.Exec(fmt.Sprintf(`
|
||||
CREATE TABLE %s (
|
||||
id int(11) NOT NULL,
|
||||
@ -637,13 +679,13 @@ CREATE TABLE %s (
|
||||
UpdateAt: nil,
|
||||
DeleteAt: nil,
|
||||
}
|
||||
r, err := db.Table(table).Data(dataInsert).Insert()
|
||||
t.Assert(err, nil)
|
||||
r, err := db.Model(table).Data(dataInsert).Insert()
|
||||
t.AssertNil(err)
|
||||
n, _ := r.RowsAffected()
|
||||
t.Assert(n, 1)
|
||||
|
||||
oneInsert, err := db.Table(table).FindOne(1)
|
||||
t.Assert(err, nil)
|
||||
oneInsert, err := db.Model(table).FindOne(1)
|
||||
t.AssertNil(err)
|
||||
t.Assert(oneInsert["id"].Int(), 1)
|
||||
t.Assert(oneInsert["name"].String(), "name_1")
|
||||
t.Assert(oneInsert["delete_at"].String(), "")
|
||||
@ -660,13 +702,13 @@ CREATE TABLE %s (
|
||||
UpdateAt: nil,
|
||||
DeleteAt: nil,
|
||||
}
|
||||
r, err = db.Table(table).Data(dataSave).Save()
|
||||
t.Assert(err, nil)
|
||||
r, err = db.Model(table).Data(dataSave).Save()
|
||||
t.AssertNil(err)
|
||||
n, _ = r.RowsAffected()
|
||||
t.Assert(n, 2)
|
||||
|
||||
oneSave, err := db.Table(table).FindOne(1)
|
||||
t.Assert(err, nil)
|
||||
oneSave, err := db.Model(table).FindOne(1)
|
||||
t.AssertNil(err)
|
||||
t.Assert(oneSave["id"].Int(), 1)
|
||||
t.Assert(oneSave["name"].String(), "name_10")
|
||||
t.Assert(oneSave["delete_at"].String(), "")
|
||||
@ -684,13 +726,13 @@ CREATE TABLE %s (
|
||||
UpdateAt: nil,
|
||||
DeleteAt: nil,
|
||||
}
|
||||
r, err = db.Table(table).Data(dataUpdate).WherePri(1).Update()
|
||||
t.Assert(err, nil)
|
||||
r, err = db.Model(table).Data(dataUpdate).WherePri(1).Update()
|
||||
t.AssertNil(err)
|
||||
n, _ = r.RowsAffected()
|
||||
t.Assert(n, 1)
|
||||
|
||||
oneUpdate, err := db.Table(table).FindOne(1)
|
||||
t.Assert(err, nil)
|
||||
oneUpdate, err := db.Model(table).FindOne(1)
|
||||
t.AssertNil(err)
|
||||
t.Assert(oneUpdate["id"].Int(), 1)
|
||||
t.Assert(oneUpdate["name"].String(), "name_1000")
|
||||
t.Assert(oneUpdate["delete_at"].String(), "")
|
||||
@ -705,13 +747,13 @@ CREATE TABLE %s (
|
||||
UpdateAt: nil,
|
||||
DeleteAt: nil,
|
||||
}
|
||||
r, err = db.Table(table).Data(dataReplace).Replace()
|
||||
t.Assert(err, nil)
|
||||
r, err = db.Model(table).Data(dataReplace).Replace()
|
||||
t.AssertNil(err)
|
||||
n, _ = r.RowsAffected()
|
||||
t.Assert(n, 2)
|
||||
|
||||
oneReplace, err := db.Table(table).FindOne(1)
|
||||
t.Assert(err, nil)
|
||||
oneReplace, err := db.Model(table).FindOne(1)
|
||||
t.AssertNil(err)
|
||||
t.Assert(oneReplace["id"].Int(), 1)
|
||||
t.Assert(oneReplace["name"].String(), "name_100")
|
||||
t.Assert(oneReplace["delete_at"].String(), "")
|
||||
@ -721,36 +763,36 @@ CREATE TABLE %s (
|
||||
time.Sleep(2 * time.Second)
|
||||
|
||||
// Delete
|
||||
r, err = db.Table(table).Delete("id", 1)
|
||||
t.Assert(err, nil)
|
||||
r, err = db.Model(table).Delete("id", 1)
|
||||
t.AssertNil(err)
|
||||
n, _ = r.RowsAffected()
|
||||
t.Assert(n, 1)
|
||||
// Delete Select
|
||||
one4, err := db.Table(table).FindOne(1)
|
||||
t.Assert(err, nil)
|
||||
one4, err := db.Model(table).FindOne(1)
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(one4), 0)
|
||||
one5, err := db.Table(table).Unscoped().FindOne(1)
|
||||
t.Assert(err, nil)
|
||||
one5, err := db.Model(table).Unscoped().FindOne(1)
|
||||
t.AssertNil(err)
|
||||
t.Assert(one5["id"].Int(), 1)
|
||||
t.AssertGE(one5["delete_at"].GTime().Timestamp(), gtime.Timestamp()-2)
|
||||
// Delete Count
|
||||
i, err := db.Table(table).FindCount()
|
||||
t.Assert(err, nil)
|
||||
i, err := db.Model(table).FindCount()
|
||||
t.AssertNil(err)
|
||||
t.Assert(i, 0)
|
||||
i, err = db.Table(table).Unscoped().FindCount()
|
||||
t.Assert(err, nil)
|
||||
i, err = db.Model(table).Unscoped().FindCount()
|
||||
t.AssertNil(err)
|
||||
t.Assert(i, 1)
|
||||
|
||||
// Delete Unscoped
|
||||
r, err = db.Table(table).Unscoped().Delete("id", 1)
|
||||
t.Assert(err, nil)
|
||||
r, err = db.Model(table).Unscoped().Delete("id", 1)
|
||||
t.AssertNil(err)
|
||||
n, _ = r.RowsAffected()
|
||||
t.Assert(n, 1)
|
||||
one6, err := db.Table(table).Unscoped().FindOne(1)
|
||||
t.Assert(err, nil)
|
||||
one6, err := db.Model(table).Unscoped().FindOne(1)
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(one6), 0)
|
||||
i, err = db.Table(table).Unscoped().FindCount()
|
||||
t.Assert(err, nil)
|
||||
i, err = db.Model(table).Unscoped().FindCount()
|
||||
t.AssertNil(err)
|
||||
t.Assert(i, 0)
|
||||
})
|
||||
}
|
||||
|
||||
@ -90,23 +90,23 @@ func Test_TX_Rollback(t *testing.T) {
|
||||
func Test_TX_Prepare(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
tx, err := db.Begin()
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
|
||||
st, err := tx.Prepare("SELECT 100")
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
|
||||
rows, err := st.Query()
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
|
||||
array, err := rows.Columns()
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
t.Assert(array[0], "100")
|
||||
|
||||
rows.Close()
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
|
||||
tx.Commit()
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
})
|
||||
}
|
||||
|
||||
@ -183,7 +183,7 @@ func Test_TX_BatchInsert(t *testing.T) {
|
||||
if err := tx.Commit(); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
if n, err := db.Table(table).Count(); err != nil {
|
||||
if n, err := db.Model(table).Count(); err != nil {
|
||||
gtest.Error(err)
|
||||
} else {
|
||||
t.Assert(n, 2)
|
||||
@ -221,12 +221,12 @@ func Test_TX_BatchReplace(t *testing.T) {
|
||||
if err := tx.Commit(); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
if n, err := db.Table(table).Count(); err != nil {
|
||||
if n, err := db.Model(table).Count(); err != nil {
|
||||
gtest.Error(err)
|
||||
} else {
|
||||
t.Assert(n, SIZE)
|
||||
t.Assert(n, TableSize)
|
||||
}
|
||||
if value, err := db.Table(table).Fields("password").Where("id", 2).Value(); err != nil {
|
||||
if value, err := db.Model(table).Fields("password").Where("id", 2).Value(); err != nil {
|
||||
gtest.Error(err)
|
||||
} else {
|
||||
t.Assert(value.String(), "PASS_2")
|
||||
@ -258,13 +258,13 @@ func Test_TX_BatchSave(t *testing.T) {
|
||||
gtest.Error(err)
|
||||
}
|
||||
|
||||
if n, err := db.Table(table).Count(); err != nil {
|
||||
if n, err := db.Model(table).Count(); err != nil {
|
||||
gtest.Error(err)
|
||||
} else {
|
||||
t.Assert(n, SIZE)
|
||||
t.Assert(n, TableSize)
|
||||
}
|
||||
|
||||
if value, err := db.Table(table).Fields("password").Where("id", 4).Value(); err != nil {
|
||||
if value, err := db.Model(table).Fields("password").Where("id", 4).Value(); err != nil {
|
||||
gtest.Error(err)
|
||||
} else {
|
||||
t.Assert(value.String(), "PASS_4")
|
||||
@ -293,7 +293,7 @@ func Test_TX_Replace(t *testing.T) {
|
||||
if err := tx.Rollback(); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
if value, err := db.Table(table).Fields("nickname").Where("id", 1).Value(); err != nil {
|
||||
if value, err := db.Model(table).Fields("nickname").Where("id", 1).Value(); err != nil {
|
||||
gtest.Error(err)
|
||||
} else {
|
||||
t.Assert(value.String(), "name_1")
|
||||
@ -322,7 +322,7 @@ func Test_TX_Save(t *testing.T) {
|
||||
if err := tx.Commit(); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
if value, err := db.Table(table).Fields("nickname").Where("id", 1).Value(); err != nil {
|
||||
if value, err := db.Model(table).Fields("nickname").Where("id", 1).Value(); err != nil {
|
||||
gtest.Error(err)
|
||||
} else {
|
||||
t.Assert(value.String(), "NAME_1")
|
||||
@ -351,7 +351,7 @@ func Test_TX_Update(t *testing.T) {
|
||||
_, err = tx.Table(table).Fields("create_time").Where("id", 3).Value()
|
||||
t.AssertNE(err, nil)
|
||||
|
||||
if value, err := db.Table(table).Fields("create_time").Where("id", 3).Value(); err != nil {
|
||||
if value, err := db.Model(table).Fields("create_time").Where("id", 3).Value(); err != nil {
|
||||
gtest.Error(err)
|
||||
} else {
|
||||
t.Assert(value.String(), "2019-10-24 10:00:00")
|
||||
@ -435,7 +435,7 @@ func Test_TX_GetCount(t *testing.T) {
|
||||
if count, err := tx.GetCount("SELECT * FROM " + table); err != nil {
|
||||
gtest.Error(err)
|
||||
} else {
|
||||
t.Assert(count, SIZE)
|
||||
t.Assert(count, TableSize)
|
||||
}
|
||||
if err := tx.Commit(); err != nil {
|
||||
gtest.Error(err)
|
||||
@ -513,7 +513,7 @@ func Test_TX_GetStructs(t *testing.T) {
|
||||
if err := tx.GetStructs(&users, fmt.Sprintf("SELECT * FROM %s WHERE id>=?", table), 1); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
t.Assert(len(users), SIZE)
|
||||
t.Assert(len(users), TableSize)
|
||||
t.Assert(users[0].Id, 1)
|
||||
t.Assert(users[1].Id, 2)
|
||||
t.Assert(users[2].Id, 3)
|
||||
@ -542,7 +542,7 @@ func Test_TX_GetStructs(t *testing.T) {
|
||||
if err := tx.GetStructs(&users, fmt.Sprintf("SELECT * FROM %s WHERE id>=?", table), 1); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
t.Assert(len(users), SIZE)
|
||||
t.Assert(len(users), TableSize)
|
||||
t.Assert(users[0].Id, 1)
|
||||
t.Assert(users[1].Id, 2)
|
||||
t.Assert(users[2].Id, 3)
|
||||
@ -621,7 +621,7 @@ func Test_TX_GetScan(t *testing.T) {
|
||||
if err := tx.GetScan(&users, fmt.Sprintf("SELECT * FROM %s WHERE id>=?", table), 1); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
t.Assert(len(users), SIZE)
|
||||
t.Assert(len(users), TableSize)
|
||||
t.Assert(users[0].Id, 1)
|
||||
t.Assert(users[1].Id, 2)
|
||||
t.Assert(users[2].Id, 3)
|
||||
@ -650,7 +650,7 @@ func Test_TX_GetScan(t *testing.T) {
|
||||
if err := tx.GetScan(&users, fmt.Sprintf("SELECT * FROM %s WHERE id>=?", table), 1); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
t.Assert(len(users), SIZE)
|
||||
t.Assert(len(users), TableSize)
|
||||
t.Assert(users[0].Id, 1)
|
||||
t.Assert(users[1].Id, 2)
|
||||
t.Assert(users[2].Id, 3)
|
||||
@ -679,7 +679,7 @@ func Test_TX_Delete(t *testing.T) {
|
||||
if err := tx.Commit(); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
if n, err := db.Table(table).Count(); err != nil {
|
||||
if n, err := db.Model(table).Count(); err != nil {
|
||||
gtest.Error(err)
|
||||
} else {
|
||||
t.Assert(n, 0)
|
||||
@ -704,10 +704,10 @@ func Test_TX_Delete(t *testing.T) {
|
||||
if err := tx.Rollback(); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
if n, err := db.Table(table).Count(); err != nil {
|
||||
if n, err := db.Model(table).Count(); err != nil {
|
||||
gtest.Error(err)
|
||||
} else {
|
||||
t.Assert(n, SIZE)
|
||||
t.Assert(n, TableSize)
|
||||
t.AssertNE(n, 0)
|
||||
}
|
||||
})
|
||||
@ -732,7 +732,7 @@ func Test_Transaction(t *testing.T) {
|
||||
})
|
||||
t.AssertNE(err, nil)
|
||||
|
||||
if value, err := db.Table(table).Fields("nickname").Where("id", 1).Value(); err != nil {
|
||||
if value, err := db.Model(table).Fields("nickname").Where("id", 1).Value(); err != nil {
|
||||
gtest.Error(err)
|
||||
} else {
|
||||
t.Assert(value.String(), "name_1")
|
||||
@ -752,9 +752,9 @@ func Test_Transaction(t *testing.T) {
|
||||
}
|
||||
return nil
|
||||
})
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
|
||||
if value, err := db.Table(table).Fields("nickname").Where("id", 1).Value(); err != nil {
|
||||
if value, err := db.Model(table).Fields("nickname").Where("id", 1).Value(); err != nil {
|
||||
gtest.Error(err)
|
||||
} else {
|
||||
t.Assert(value.String(), "NAME_1")
|
||||
@ -782,7 +782,7 @@ func Test_Transaction_Panic(t *testing.T) {
|
||||
})
|
||||
t.AssertNE(err, nil)
|
||||
|
||||
if value, err := db.Table(table).Fields("nickname").Where("id", 1).Value(); err != nil {
|
||||
if value, err := db.Model(table).Fields("nickname").Where("id", 1).Value(); err != nil {
|
||||
gtest.Error(err)
|
||||
} else {
|
||||
t.Assert(value.String(), "name_1")
|
||||
|
||||
@ -58,13 +58,13 @@ func Test_Types(t *testing.T) {
|
||||
"tinyint": true,
|
||||
"bool": false,
|
||||
}
|
||||
r, err := db.Table("types").Data(data).Insert()
|
||||
t.Assert(err, nil)
|
||||
r, err := db.Model("types").Data(data).Insert()
|
||||
t.AssertNil(err)
|
||||
n, _ := r.RowsAffected()
|
||||
t.Assert(n, 1)
|
||||
|
||||
one, err := db.Table("types").One()
|
||||
t.Assert(err, nil)
|
||||
one, err := db.Model("types").One()
|
||||
t.AssertNil(err)
|
||||
t.Assert(one["id"].Int(), 1)
|
||||
t.Assert(one["blob"].String(), data["blob"])
|
||||
t.Assert(one["binary"].String(), data["binary"])
|
||||
@ -87,8 +87,8 @@ func Test_Types(t *testing.T) {
|
||||
TinyInt bool
|
||||
}
|
||||
var obj *T
|
||||
err = db.Table("types").Struct(&obj)
|
||||
t.Assert(err, nil)
|
||||
err = db.Model("types").Struct(&obj)
|
||||
t.AssertNil(err)
|
||||
t.Assert(obj.Id, 1)
|
||||
t.Assert(obj.Blob, data["blob"])
|
||||
t.Assert(obj.Binary, data["binary"])
|
||||
|
||||
@ -11,7 +11,6 @@ import (
|
||||
"errors"
|
||||
"github.com/gogf/gf/container/gvar"
|
||||
"github.com/gogf/gf/internal/json"
|
||||
"github.com/gogf/gf/net/gtrace"
|
||||
"github.com/gogf/gf/os/gtime"
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
"github.com/gomodule/redigo/redis"
|
||||
@ -60,14 +59,12 @@ func (c *Conn) do(timeout time.Duration, commandName string, args ...interface{}
|
||||
timestampMilli2 := gtime.TimestampMilli()
|
||||
|
||||
// Tracing.
|
||||
if gtrace.IsActivated(c.ctx) {
|
||||
c.addTracingItem(&tracingItem{
|
||||
err: err,
|
||||
commandName: commandName,
|
||||
arguments: args,
|
||||
costMilli: timestampMilli2 - timestampMilli1,
|
||||
})
|
||||
}
|
||||
c.addTracingItem(&tracingItem{
|
||||
err: err,
|
||||
commandName: commandName,
|
||||
arguments: args,
|
||||
costMilli: timestampMilli2 - timestampMilli1,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@ -12,6 +12,7 @@ import (
|
||||
"github.com/gogf/gf"
|
||||
"github.com/gogf/gf/internal/json"
|
||||
"github.com/gogf/gf/net/gtrace"
|
||||
"github.com/gogf/gf/os/gcmd"
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/codes"
|
||||
"go.opentelemetry.io/otel/label"
|
||||
@ -27,6 +28,7 @@ type tracingItem struct {
|
||||
}
|
||||
|
||||
const (
|
||||
tracingInstrumentName = "github.com/gogf/gf/database/gredis"
|
||||
tracingAttrRedisHost = "redis.host"
|
||||
tracingAttrRedisPort = "redis.port"
|
||||
tracingAttrRedisDb = "redis.db"
|
||||
@ -36,11 +38,24 @@ const (
|
||||
tracingEventRedisExecutionArguments = "redis.execution.arguments"
|
||||
)
|
||||
|
||||
var (
|
||||
// tracingInternal enables tracing for internal type spans.
|
||||
// It's true in default.
|
||||
tracingInternal = true
|
||||
)
|
||||
|
||||
func init() {
|
||||
tracingInternal = gcmd.GetOptWithEnv("gf.tracing.internal", true).Bool()
|
||||
}
|
||||
|
||||
// addTracingItem checks and adds redis tracing information to OpenTelemetry.
|
||||
func (c *Conn) addTracingItem(item *tracingItem) {
|
||||
if !tracingInternal || !gtrace.IsActivated(c.ctx) {
|
||||
return
|
||||
}
|
||||
tr := otel.GetTracerProvider().Tracer(
|
||||
"github.com/gogf/gf/database/gredis",
|
||||
trace.WithInstrumentationVersion(fmt.Sprintf(`%s`, gf.VERSION)),
|
||||
tracingInstrumentName,
|
||||
trace.WithInstrumentationVersion(gf.VERSION),
|
||||
)
|
||||
ctx := c.ctx
|
||||
if ctx == nil {
|
||||
|
||||
@ -9,6 +9,7 @@ package gdebug
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/internal/utils"
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
@ -82,6 +83,11 @@ func StackWithFilters(filters []string, skip ...int) string {
|
||||
if strings.Contains(file, stackFilterKey) {
|
||||
continue
|
||||
}
|
||||
if !utils.IsDebugEnabled() {
|
||||
if strings.Contains(file, utils.StackFilterKeyForGoFrame) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
if fn := runtime.FuncForPC(pc); fn == nil {
|
||||
name = "unknown"
|
||||
} else {
|
||||
|
||||
@ -10,6 +10,7 @@ import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/internal/utils"
|
||||
"io"
|
||||
"runtime"
|
||||
"strings"
|
||||
@ -24,7 +25,8 @@ type Error struct {
|
||||
}
|
||||
|
||||
const (
|
||||
stackFilterKey = "/errors/gerror/gerror"
|
||||
// Filtering key for current error module paths.
|
||||
stackFilterKeyLocal = "/errors/gerror/gerror"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -182,20 +184,34 @@ func formatSubStack(st stack, buffer *bytes.Buffer) {
|
||||
for _, p := range st {
|
||||
if fn := runtime.FuncForPC(p - 1); fn != nil {
|
||||
file, line := fn.FileLine(p - 1)
|
||||
if strings.Contains(file, stackFilterKey) {
|
||||
continue
|
||||
// Custom filtering.
|
||||
if !utils.IsDebugEnabled() {
|
||||
if strings.Contains(file, utils.StackFilterKeyForGoFrame) {
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
if strings.Contains(file, stackFilterKeyLocal) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
// Avoid stack string like "<autogenerated>"
|
||||
if strings.Contains(file, "<") {
|
||||
continue
|
||||
}
|
||||
if goRootForFilter != "" && len(file) >= len(goRootForFilter) && file[0:len(goRootForFilter)] == goRootForFilter {
|
||||
// Ignore GO ROOT paths.
|
||||
if goRootForFilter != "" &&
|
||||
len(file) >= len(goRootForFilter) &&
|
||||
file[0:len(goRootForFilter)] == goRootForFilter {
|
||||
continue
|
||||
}
|
||||
// Graceful indent.
|
||||
if index > 9 {
|
||||
space = " "
|
||||
}
|
||||
buffer.WriteString(fmt.Sprintf(" %d).%s%s\n \t%s:%d\n", index, space, fn.Name(), file, line))
|
||||
buffer.WriteString(fmt.Sprintf(
|
||||
" %d).%s%s\n \t%s:%d\n",
|
||||
index, space, fn.Name(), file, line,
|
||||
))
|
||||
index++
|
||||
}
|
||||
}
|
||||
|
||||
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,27 +80,35 @@ 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...)
|
||||
}
|
||||
|
||||
// Table is alias of Model.
|
||||
func Table(tables string, db ...string) *gdb.Model {
|
||||
return DB(db...).Table(tables)
|
||||
// The database component is designed not only for
|
||||
// relational databases but also for NoSQL databases in the future. The name
|
||||
// "Table" is not proper for that purpose any more.
|
||||
// Deprecated, use Model instead.
|
||||
func Table(tables ...string) *gdb.Model {
|
||||
return DB().Model(tables...)
|
||||
}
|
||||
|
||||
// Model creates and returns a model from specified database or default database configuration.
|
||||
// The optional parameter <db> specifies the configuration group name of the database,
|
||||
// which is "default" in default.
|
||||
func Model(tables string, db ...string) *gdb.Model {
|
||||
return DB(db...).Model(tables)
|
||||
// Model creates and returns a model based on configuration of default database group.
|
||||
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.
|
||||
|
||||
1
go.mod
1
go.mod
@ -10,6 +10,7 @@ require (
|
||||
github.com/gomodule/redigo v2.0.0+incompatible
|
||||
github.com/gorilla/websocket v1.4.1
|
||||
github.com/grokify/html-strip-tags-go v0.0.0-20190921062105-daaa06bf1aaf
|
||||
github.com/mattn/go-runewidth v0.0.10 // indirect
|
||||
github.com/olekukonko/tablewriter v0.0.1
|
||||
go.opentelemetry.io/otel v0.16.0
|
||||
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102
|
||||
|
||||
127
internal/command/command.go
Normal file
127
internal/command/command.go
Normal file
@ -0,0 +1,127 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
//
|
||||
|
||||
// Package command provides console operations, like options/arguments reading.
|
||||
package command
|
||||
|
||||
import (
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
defaultParsedArgs = make([]string, 0)
|
||||
defaultParsedOptions = make(map[string]string)
|
||||
argumentRegex = regexp.MustCompile(`^\-{1,2}([\w\?\.\-]+)(=){0,1}(.*)$`)
|
||||
)
|
||||
|
||||
// Custom initialization.
|
||||
func Init(args ...string) {
|
||||
if len(args) == 0 {
|
||||
if len(defaultParsedArgs) == 0 && len(defaultParsedOptions) == 0 {
|
||||
args = os.Args
|
||||
} else {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
defaultParsedArgs = make([]string, 0)
|
||||
defaultParsedOptions = make(map[string]string)
|
||||
}
|
||||
// Parsing os.Args with default algorithm.
|
||||
for i := 0; i < len(args); {
|
||||
array := argumentRegex.FindStringSubmatch(args[i])
|
||||
if len(array) > 2 {
|
||||
if array[2] == "=" {
|
||||
defaultParsedOptions[array[1]] = array[3]
|
||||
} else if i < len(args)-1 {
|
||||
if len(args[i+1]) > 0 && args[i+1][0] == '-' {
|
||||
// Eg: gf gen -d -n 1
|
||||
defaultParsedOptions[array[1]] = array[3]
|
||||
} else {
|
||||
// Eg: gf gen -n 2
|
||||
defaultParsedOptions[array[1]] = args[i+1]
|
||||
i += 2
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
// Eg: gf gen -h
|
||||
defaultParsedOptions[array[1]] = array[3]
|
||||
}
|
||||
} else {
|
||||
defaultParsedArgs = append(defaultParsedArgs, args[i])
|
||||
}
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
// GetOpt returns the option value named `name`.
|
||||
func GetOpt(name string, def ...string) string {
|
||||
Init()
|
||||
if v, ok := defaultParsedOptions[name]; ok {
|
||||
return v
|
||||
}
|
||||
if len(def) > 0 {
|
||||
return def[0]
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// GetOptAll returns all parsed options.
|
||||
func GetOptAll() map[string]string {
|
||||
Init()
|
||||
return defaultParsedOptions
|
||||
}
|
||||
|
||||
// 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`.
|
||||
func GetArg(index int, def ...string) string {
|
||||
Init()
|
||||
if index < len(defaultParsedArgs) {
|
||||
return defaultParsedArgs[index]
|
||||
}
|
||||
if len(def) > 0 {
|
||||
return def[0]
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// GetArgAll returns all parsed arguments.
|
||||
func GetArgAll() []string {
|
||||
Init()
|
||||
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.
|
||||
//
|
||||
// Fetching Rules:
|
||||
// 1. Command line arguments are in lowercase format, eg: gf.<package name>.<variable name>;
|
||||
// 2. Environment arguments are in uppercase format, eg: GF_<package name>_<variable name>;
|
||||
func GetOptWithEnv(key string, def ...string) string {
|
||||
cmdKey := strings.ToLower(strings.Replace(key, "_", ".", -1))
|
||||
if ContainsOpt(cmdKey) {
|
||||
return GetOpt(cmdKey)
|
||||
} else {
|
||||
envKey := strings.ToUpper(strings.Replace(key, ".", "_", -1))
|
||||
if r, ok := os.LookupEnv(envKey); ok {
|
||||
return r
|
||||
} else {
|
||||
if len(def) > 0 {
|
||||
return def[0]
|
||||
}
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
@ -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 {
|
||||
|
||||
@ -10,7 +10,7 @@ package intlog
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/debug/gdebug"
|
||||
"github.com/gogf/gf/os/gcmd"
|
||||
"github.com/gogf/gf/internal/utils"
|
||||
"path/filepath"
|
||||
"time"
|
||||
)
|
||||
@ -25,29 +25,20 @@ var (
|
||||
)
|
||||
|
||||
func init() {
|
||||
// Debugging configured.
|
||||
if !gcmd.GetWithEnv("GF_DEBUG").IsEmpty() {
|
||||
isGFDebug = true
|
||||
return
|
||||
}
|
||||
isGFDebug = utils.IsDebugEnabled()
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
}
|
||||
|
||||
// IsEnabled checks and returns whether current process is in GF development.
|
||||
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
|
||||
@ -55,8 +46,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
|
||||
@ -64,8 +55,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
|
||||
@ -75,7 +66,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{}
|
||||
|
||||
@ -13,38 +13,14 @@ import (
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// Type wraps reflect.Type for additional features.
|
||||
type Type struct {
|
||||
reflect.Type
|
||||
}
|
||||
|
||||
// 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,13 +90,12 @@ 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.
|
||||
//
|
||||
// The parameter <pointer> should be type of struct/*struct.
|
||||
// TagMapField retrieves struct tags as map[tag]*Field from `pointer`, and returns it.
|
||||
// The parameter `object` should be either type of struct/*struct/[]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) {
|
||||
fields, err := TagFields(pointer, priority)
|
||||
func TagMapField(object interface{}, priority []string) (map[string]*Field, error) {
|
||||
fields, err := TagFields(object, priority)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -67,19 +119,31 @@ func getFieldValues(value interface{}) ([]*Field, error) {
|
||||
reflectValue = reflect.ValueOf(value)
|
||||
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, errors.New("given value should be type of struct/*struct")
|
||||
return nil, errors.New("given value should be either type of struct/*struct/[]struct/[]*struct")
|
||||
}
|
||||
var (
|
||||
structType = reflectValue.Type()
|
||||
@ -88,8 +152,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 +191,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...)
|
||||
|
||||
75
internal/structs/structs_type.go
Normal file
75
internal/structs/structs_type.go
Normal file
@ -0,0 +1,75 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package structs
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/errors/gerror"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// StructType retrieves and returns the struct Type of specified struct/*struct.
|
||||
// The parameter `object` should be either type of struct/*struct/[]struct/[]*struct.
|
||||
func StructType(object interface{}) (*Type, error) {
|
||||
var (
|
||||
reflectValue reflect.Value
|
||||
reflectKind reflect.Kind
|
||||
reflectType reflect.Type
|
||||
)
|
||||
if rv, ok := object.(reflect.Value); ok {
|
||||
reflectValue = rv
|
||||
} else {
|
||||
reflectValue = reflect.ValueOf(object)
|
||||
}
|
||||
reflectKind = reflectValue.Kind()
|
||||
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()
|
||||
default:
|
||||
goto exitLoop
|
||||
}
|
||||
}
|
||||
exitLoop:
|
||||
for reflectKind == reflect.Ptr {
|
||||
reflectValue = reflectValue.Elem()
|
||||
reflectKind = reflectValue.Kind()
|
||||
}
|
||||
if reflectKind != reflect.Struct {
|
||||
return nil, gerror.Newf(
|
||||
`invalid object kind "%s", kind of "struct" is required`,
|
||||
reflectKind,
|
||||
)
|
||||
}
|
||||
reflectType = reflectValue.Type()
|
||||
return &Type{
|
||||
Type: reflectType,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// 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,4 +123,123 @@ 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) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type B struct {
|
||||
Name string
|
||||
}
|
||||
type A struct {
|
||||
B
|
||||
}
|
||||
r, err := structs.StructType(new(A))
|
||||
t.AssertNil(err)
|
||||
t.Assert(r.Signature(), `github.com/gogf/gf/internal/structs_test/structs_test.A`)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type B struct {
|
||||
Name string
|
||||
}
|
||||
type A struct {
|
||||
B
|
||||
}
|
||||
r, err := structs.StructType(new(A).B)
|
||||
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 {
|
||||
*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"})
|
||||
})
|
||||
}
|
||||
|
||||
37
internal/utils/utils_debug.go
Normal file
37
internal/utils/utils_debug.go
Normal file
@ -0,0 +1,37 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package utils
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/internal/command"
|
||||
)
|
||||
|
||||
const (
|
||||
debugKey = "gf.debug" // Debug key for checking if in debug mode.
|
||||
StackFilterKeyForGoFrame = "/github.com/gogf/gf/" // Stack filtering key for all GoFrame module paths.
|
||||
)
|
||||
|
||||
var (
|
||||
// isDebugEnabled marks whether GoFrame debug mode is enabled.
|
||||
isDebugEnabled = false
|
||||
)
|
||||
|
||||
func init() {
|
||||
// Debugging configured.
|
||||
value := command.GetOptWithEnv(debugKey)
|
||||
if value == "" || value == "0" || value == "false" {
|
||||
isDebugEnabled = false
|
||||
} else {
|
||||
isDebugEnabled = true
|
||||
}
|
||||
}
|
||||
|
||||
// IsDebugEnabled checks and returns whether debug mode is enabled.
|
||||
// The debug mode is enabled when command argument "gf.debug" or environment "GF_DEBUG" is passed.
|
||||
func IsDebugEnabled() bool {
|
||||
return isDebugEnabled
|
||||
}
|
||||
@ -10,6 +10,21 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
// DefaultTrimChars are the characters which are stripped by Trim* functions in default.
|
||||
DefaultTrimChars = string([]byte{
|
||||
'\t', // Tab.
|
||||
'\v', // Vertical tab.
|
||||
'\n', // New line (line feed).
|
||||
'\r', // Carriage return.
|
||||
'\f', // New page.
|
||||
' ', // Ordinary space.
|
||||
0x00, // NUL-byte.
|
||||
0x85, // Delete.
|
||||
0xA0, // Non-breaking space.
|
||||
})
|
||||
)
|
||||
|
||||
// IsLetterUpper checks whether the given byte b is in upper case.
|
||||
func IsLetterUpper(b byte) bool {
|
||||
if b >= byte('A') && b <= byte('Z') {
|
||||
@ -67,7 +82,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,8 +102,32 @@ 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))
|
||||
}
|
||||
|
||||
// SplitAndTrim splits string <str> by a string <delimiter> to an array,
|
||||
// and calls Trim to every element of this array. It ignores the elements
|
||||
// which are empty after Trim.
|
||||
func SplitAndTrim(str, delimiter string, characterMask ...string) []string {
|
||||
array := make([]string, 0)
|
||||
for _, v := range strings.Split(str, delimiter) {
|
||||
v = Trim(v, characterMask...)
|
||||
if v != "" {
|
||||
array = append(array, v)
|
||||
}
|
||||
}
|
||||
return array
|
||||
}
|
||||
|
||||
// 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 {
|
||||
trimChars := DefaultTrimChars
|
||||
if len(characterMask) > 0 {
|
||||
trimChars += characterMask[0]
|
||||
}
|
||||
return strings.Trim(str, trimChars)
|
||||
}
|
||||
|
||||
@ -16,7 +16,6 @@ import (
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/codes"
|
||||
"go.opentelemetry.io/otel/label"
|
||||
"go.opentelemetry.io/otel/propagation"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
@ -24,8 +23,10 @@ import (
|
||||
|
||||
const (
|
||||
tracingMaxContentLogSize = 512 * 1024 // Max log size for request and response body.
|
||||
tracingInstrumentName = "github.com/gogf/gf/net/ghttp.Server"
|
||||
tracingEventHttpRequest = "http.request"
|
||||
tracingEventHttpRequestHeaders = "http.request.headers"
|
||||
tracingEventHttpRequestBaggage = "http.request.baggage"
|
||||
tracingEventHttpRequestBody = "http.request.body"
|
||||
tracingEventHttpResponse = "http.response"
|
||||
tracingEventHttpResponseHeaders = "http.response.headers"
|
||||
@ -39,16 +40,8 @@ func MiddlewareClientTracing(c *Client, r *http.Request) (*ClientResponse, error
|
||||
|
||||
// MiddlewareServerTracing is a serer middleware that enables tracing feature using standards of OpenTelemetry.
|
||||
func MiddlewareServerTracing(r *Request) {
|
||||
tr := otel.GetTracerProvider().Tracer(
|
||||
"github.com/gogf/gf/net/ghttp.Server",
|
||||
trace.WithInstrumentationVersion(fmt.Sprintf(`%s`, gf.VERSION)),
|
||||
)
|
||||
// Tracing content parsing, start root span.
|
||||
propagator := propagation.NewCompositeTextMapPropagator(
|
||||
propagation.TraceContext{},
|
||||
propagation.Baggage{},
|
||||
)
|
||||
ctx := propagator.Extract(r.Context(), r.Header)
|
||||
tr := otel.GetTracerProvider().Tracer(tracingInstrumentName, trace.WithInstrumentationVersion(gf.VERSION))
|
||||
ctx := otel.GetTextMapPropagator().Extract(r.Context(), r.Header)
|
||||
ctx, span := tr.Start(ctx, r.URL.String(), trace.WithSpanKind(trace.SpanKindServer))
|
||||
defer span.End()
|
||||
|
||||
@ -71,6 +64,7 @@ func MiddlewareServerTracing(r *Request) {
|
||||
}
|
||||
span.AddEvent(tracingEventHttpRequest, trace.WithAttributes(
|
||||
label.Any(tracingEventHttpRequestHeaders, httputil.HeaderToMap(r.Header)),
|
||||
label.Any(tracingEventHttpRequestBaggage, gtrace.GetBaggageMap(ctx)),
|
||||
label.String(tracingEventHttpRequestBody, reqBodyContent),
|
||||
))
|
||||
|
||||
|
||||
@ -58,7 +58,8 @@ func (s *Server) doBindObject(
|
||||
methodMap[strings.TrimSpace(v)] = true
|
||||
}
|
||||
}
|
||||
// 当pattern中的method为all时,去掉该method,以便于后续方法判断
|
||||
// If the `method` in `pattern` is `defaultMethod`,
|
||||
// it removes for convenience for next statement control.
|
||||
domain, method, path, err := s.parsePattern(pattern)
|
||||
if err != nil {
|
||||
s.Logger().Fatal(err)
|
||||
@ -67,11 +68,21 @@ func (s *Server) doBindObject(
|
||||
if strings.EqualFold(method, defaultMethod) {
|
||||
pattern = s.serveHandlerKey("", path, domain)
|
||||
}
|
||||
m := make(map[string]*handlerItem)
|
||||
v := reflect.ValueOf(object)
|
||||
t := v.Type()
|
||||
initFunc := (func(*Request))(nil)
|
||||
shutFunc := (func(*Request))(nil)
|
||||
var (
|
||||
m = make(map[string]*handlerItem)
|
||||
v = reflect.ValueOf(object)
|
||||
t = v.Type()
|
||||
initFunc func(*Request)
|
||||
shutFunc func(*Request)
|
||||
)
|
||||
// If given `object` is not pointer, it then creates a temporary one,
|
||||
// of which the value is `v`.
|
||||
if v.Kind() == reflect.Struct {
|
||||
newValue := reflect.New(t)
|
||||
newValue.Elem().Set(v)
|
||||
v = newValue
|
||||
t = v.Type()
|
||||
}
|
||||
structName := t.Elem().Name()
|
||||
if v.MethodByName("Init").IsValid() {
|
||||
initFunc = v.MethodByName("Init").Interface().(func(*Request))
|
||||
@ -149,9 +160,21 @@ func (s *Server) doBindObjectMethod(
|
||||
pattern string, object interface{}, method string,
|
||||
middleware []HandlerFunc, source string,
|
||||
) {
|
||||
m := make(map[string]*handlerItem)
|
||||
v := reflect.ValueOf(object)
|
||||
t := v.Type()
|
||||
var (
|
||||
m = make(map[string]*handlerItem)
|
||||
v = reflect.ValueOf(object)
|
||||
t = v.Type()
|
||||
initFunc func(*Request)
|
||||
shutFunc func(*Request)
|
||||
)
|
||||
// If given `object` is not pointer, it then creates a temporary one,
|
||||
// of which the value is `v`.
|
||||
if v.Kind() == reflect.Struct {
|
||||
newValue := reflect.New(t)
|
||||
newValue.Elem().Set(v)
|
||||
v = newValue
|
||||
t = v.Type()
|
||||
}
|
||||
structName := t.Elem().Name()
|
||||
methodName := strings.TrimSpace(method)
|
||||
methodValue := v.MethodByName(methodName)
|
||||
@ -159,8 +182,6 @@ func (s *Server) doBindObjectMethod(
|
||||
s.Logger().Fatal("invalid method name: " + methodName)
|
||||
return
|
||||
}
|
||||
initFunc := (func(*Request))(nil)
|
||||
shutFunc := (func(*Request))(nil)
|
||||
if v.MethodByName("Init").IsValid() {
|
||||
initFunc = v.MethodByName("Init").Interface().(func(*Request))
|
||||
}
|
||||
@ -199,11 +220,21 @@ func (s *Server) doBindObjectRest(
|
||||
pattern string, object interface{},
|
||||
middleware []HandlerFunc, source string,
|
||||
) {
|
||||
m := make(map[string]*handlerItem)
|
||||
v := reflect.ValueOf(object)
|
||||
t := v.Type()
|
||||
initFunc := (func(*Request))(nil)
|
||||
shutFunc := (func(*Request))(nil)
|
||||
var (
|
||||
m = make(map[string]*handlerItem)
|
||||
v = reflect.ValueOf(object)
|
||||
t = v.Type()
|
||||
initFunc func(*Request)
|
||||
shutFunc func(*Request)
|
||||
)
|
||||
// If given `object` is not pointer, it then creates a temporary one,
|
||||
// of which the value is `v`.
|
||||
if v.Kind() == reflect.Struct {
|
||||
newValue := reflect.New(t)
|
||||
newValue.Elem().Set(v)
|
||||
v = newValue
|
||||
t = v.Type()
|
||||
}
|
||||
structName := t.Elem().Name()
|
||||
if v.MethodByName("Init").IsValid() {
|
||||
initFunc = v.MethodByName("Init").Interface().(func(*Request))
|
||||
|
||||
@ -421,3 +421,23 @@ func Test_Client_Middleware(t *testing.T) {
|
||||
t.Assert(resp, nil)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Client_Agent(t *testing.T) {
|
||||
p, _ := ports.PopRand()
|
||||
s := g.Server(p)
|
||||
s.BindHandler("/", func(r *ghttp.Request) {
|
||||
r.Response.Write(r.UserAgent())
|
||||
})
|
||||
s.SetPort(p)
|
||||
s.SetDumpRouterMap(false)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
c := g.Client().SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
|
||||
c.SetAgent("test")
|
||||
t.Assert(c.GetContent("/"), "test")
|
||||
})
|
||||
}
|
||||
|
||||
@ -8,9 +8,12 @@ package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"github.com/gogf/gf"
|
||||
"github.com/gogf/gf/errors/gerror"
|
||||
"github.com/gogf/gf/os/gfile"
|
||||
"github.com/gogf/gf/text/gstr"
|
||||
"golang.org/x/net/proxy"
|
||||
"net"
|
||||
@ -27,7 +30,6 @@ type Client struct {
|
||||
http.Client // Underlying HTTP Client.
|
||||
ctx context.Context // Context for each request.
|
||||
dump bool // Mark this request will be dumped.
|
||||
agent string // Client agent.
|
||||
parent *Client // Parent http client, this is used for chaining operations.
|
||||
header map[string]string // Custom header map.
|
||||
cookies map[string]string // Custom cookie map.
|
||||
@ -46,7 +48,7 @@ var (
|
||||
|
||||
// New creates and returns a new HTTP client object.
|
||||
func New() *Client {
|
||||
return &Client{
|
||||
client := &Client{
|
||||
Client: http.Client{
|
||||
Transport: &http.Transport{
|
||||
// No validation for https certification of the server in default.
|
||||
@ -58,11 +60,12 @@ func New() *Client {
|
||||
},
|
||||
header: make(map[string]string),
|
||||
cookies: make(map[string]string),
|
||||
agent: defaultClientAgent,
|
||||
}
|
||||
client.header["User-Agent"] = defaultClientAgent
|
||||
return client
|
||||
}
|
||||
|
||||
// Clone clones current client and returns a new one.
|
||||
// Clone deeply clones current client and returns a new one.
|
||||
func (c *Client) Clone() *Client {
|
||||
newClient := New()
|
||||
*newClient = *c
|
||||
@ -198,8 +201,8 @@ func (c *Client) SetProxy(proxyURL string) {
|
||||
return
|
||||
}
|
||||
if _proxy.Scheme == "http" {
|
||||
if _, ok := c.Transport.(*http.Transport); ok {
|
||||
c.Transport.(*http.Transport).Proxy = http.ProxyURL(_proxy)
|
||||
if v, ok := c.Transport.(*http.Transport); ok {
|
||||
v.Proxy = http.ProxyURL(_proxy)
|
||||
}
|
||||
} else {
|
||||
var auth = &proxy.Auth{}
|
||||
@ -227,11 +230,55 @@ func (c *Client) SetProxy(proxyURL string) {
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if _, ok := c.Transport.(*http.Transport); ok {
|
||||
c.Transport.(*http.Transport).DialContext = func(ctx context.Context, network, addr string) (conn net.Conn, e error) {
|
||||
if v, ok := c.Transport.(*http.Transport); ok {
|
||||
v.DialContext = func(ctx context.Context, network, addr string) (conn net.Conn, e error) {
|
||||
return dialer.Dial(network, addr)
|
||||
}
|
||||
}
|
||||
//c.SetTimeout(10*time.Second)
|
||||
}
|
||||
}
|
||||
|
||||
// SetTlsKeyCrt sets the certificate and key file for TLS configuration of client.
|
||||
func (c *Client) SetTLSKeyCrt(crtFile, keyFile string) error {
|
||||
tlsConfig, err := LoadKeyCrt(crtFile, keyFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if v, ok := c.Transport.(*http.Transport); ok {
|
||||
tlsConfig.InsecureSkipVerify = true
|
||||
v.TLSClientConfig = tlsConfig
|
||||
return nil
|
||||
}
|
||||
return gerror.New(`cannot set TLSClientConfig for custom Transport of the client`)
|
||||
}
|
||||
|
||||
// SetTlsConfig sets the TLS configuration of client.
|
||||
func (c *Client) SetTLSConfig(tlsConfig *tls.Config) error {
|
||||
if v, ok := c.Transport.(*http.Transport); ok {
|
||||
v.TLSClientConfig = tlsConfig
|
||||
return nil
|
||||
}
|
||||
return gerror.New(`cannot set TLSClientConfig for custom Transport of the client`)
|
||||
}
|
||||
|
||||
// LoadKeyCrt creates and returns a TLS configuration object with given certificate and key files.
|
||||
func LoadKeyCrt(crtFile, keyFile string) (*tls.Config, error) {
|
||||
crtPath, err := gfile.Search(crtFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
keyPath, err := gfile.Search(keyFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
crt, err := tls.LoadX509KeyPair(crtPath, keyPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tlsConfig := &tls.Config{}
|
||||
tlsConfig.Certificates = []tls.Certificate{crt}
|
||||
tlsConfig.Time = time.Now
|
||||
tlsConfig.Rand = rand.Reader
|
||||
return tlsConfig, nil
|
||||
}
|
||||
|
||||
@ -58,7 +58,7 @@ func (r *Response) RawRequest() string {
|
||||
|
||||
// RawResponse returns the raw content of the response.
|
||||
func (r *Response) RawResponse() string {
|
||||
// Response can be nil.
|
||||
// Response might be nil.
|
||||
if r == nil || r.Response == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
@ -117,7 +117,7 @@ func (c *Client) DoRequest(method, url string, data ...interface{}) (resp *Respo
|
||||
}
|
||||
|
||||
// Auto saving cookie content.
|
||||
if c.browserMode && resp != nil {
|
||||
if c.browserMode && resp != nil && resp.Response != nil {
|
||||
now := time.Now()
|
||||
for _, v := range resp.Response.Cookies() {
|
||||
if !v.Expires.IsZero() && v.Expires.UnixNano() < now.UnixNano() {
|
||||
@ -258,8 +258,8 @@ func (c *Client) prepareRequest(method, url string, data ...interface{}) (req *h
|
||||
}
|
||||
}
|
||||
// It's necessary set the req.Host if you want to custom the host value of the request.
|
||||
// It uses the "Host" value from header if it's not set in the request.
|
||||
if host := req.Header.Get("Host"); host != "" && req.Host == "" {
|
||||
// It uses the "Host" value from header if it's not empty.
|
||||
if host := req.Header.Get("Host"); host != "" {
|
||||
req.Host = host
|
||||
}
|
||||
// Custom Cookie.
|
||||
@ -279,10 +279,6 @@ func (c *Client) prepareRequest(method, url string, data ...interface{}) (req *h
|
||||
if len(c.authUser) > 0 {
|
||||
req.SetBasicAuth(c.authUser, c.authPass)
|
||||
}
|
||||
// Client agent.
|
||||
if c.agent != "" {
|
||||
req.Header.Set("User-Agent", c.agent)
|
||||
}
|
||||
return req, nil
|
||||
}
|
||||
|
||||
|
||||
@ -25,8 +25,11 @@ type Response struct {
|
||||
func (r *Response) initCookie() {
|
||||
if r.cookies == nil {
|
||||
r.cookies = make(map[string]string)
|
||||
for _, v := range r.Cookies() {
|
||||
r.cookies[v.Name] = v.Value
|
||||
// Response might be nil.
|
||||
if r != nil && r.Response != nil {
|
||||
for _, v := range r.Cookies() {
|
||||
r.cookies[v.Name] = v.Value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -49,6 +52,10 @@ func (r *Response) GetCookieMap() map[string]string {
|
||||
|
||||
// ReadAll retrieves and returns the response content as []byte.
|
||||
func (r *Response) ReadAll() []byte {
|
||||
// Response might be nil.
|
||||
if r == nil || r.Response == nil {
|
||||
return []byte{}
|
||||
}
|
||||
body, err := ioutil.ReadAll(r.Response.Body)
|
||||
if err != nil {
|
||||
return nil
|
||||
|
||||
@ -15,7 +15,6 @@ import (
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/codes"
|
||||
"go.opentelemetry.io/otel/label"
|
||||
"go.opentelemetry.io/otel/propagation"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
@ -24,6 +23,7 @@ import (
|
||||
|
||||
const (
|
||||
tracingMaxContentLogSize = 512 * 1024 // Max log size for request and response body.
|
||||
tracingInstrumentName = "github.com/gogf/gf/net/ghttp.Client"
|
||||
tracingAttrHttpAddressRemote = "http.address.remote"
|
||||
tracingAttrHttpAddressLocal = "http.address.local"
|
||||
tracingAttrHttpDnsStart = "http.dns.start"
|
||||
@ -32,6 +32,7 @@ const (
|
||||
tracingAttrHttpConnectDone = "http.connect.done"
|
||||
tracingEventHttpRequest = "http.request"
|
||||
tracingEventHttpRequestHeaders = "http.request.headers"
|
||||
tracingEventHttpRequestBaggage = "http.request.baggage"
|
||||
tracingEventHttpRequestBody = "http.request.body"
|
||||
tracingEventHttpResponse = "http.response"
|
||||
tracingEventHttpResponseHeaders = "http.response.headers"
|
||||
@ -40,21 +41,14 @@ const (
|
||||
|
||||
// MiddlewareTracing is a client middleware that enables tracing feature using standards of OpenTelemetry.
|
||||
func MiddlewareTracing(c *Client, r *http.Request) (response *Response, err error) {
|
||||
tr := otel.GetTracerProvider().Tracer(
|
||||
"github.com/gogf/gf/net/ghttp.Client",
|
||||
trace.WithInstrumentationVersion(fmt.Sprintf(`%s`, gf.VERSION)),
|
||||
)
|
||||
tr := otel.GetTracerProvider().Tracer(tracingInstrumentName, trace.WithInstrumentationVersion(gf.VERSION))
|
||||
ctx, span := tr.Start(r.Context(), r.URL.String(), trace.WithSpanKind(trace.SpanKindClient))
|
||||
defer span.End()
|
||||
|
||||
span.SetAttributes(gtrace.CommonLabels()...)
|
||||
|
||||
// Inject tracing content into http header.
|
||||
propagator := propagation.NewCompositeTextMapPropagator(
|
||||
propagation.TraceContext{},
|
||||
propagation.Baggage{},
|
||||
)
|
||||
propagator.Inject(ctx, r.Header)
|
||||
otel.GetTextMapPropagator().Inject(ctx, r.Header)
|
||||
|
||||
// Continue client handler executing.
|
||||
response, err = c.Next(
|
||||
|
||||
@ -11,6 +11,7 @@ import (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/internal/utils"
|
||||
"github.com/gogf/gf/net/gtrace"
|
||||
"go.opentelemetry.io/otel/codes"
|
||||
"go.opentelemetry.io/otel/label"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
@ -154,6 +155,7 @@ func (ct *clientTracer) wroteRequest(info httptrace.WroteRequestInfo) {
|
||||
}
|
||||
ct.span.AddEvent(tracingEventHttpRequest, trace.WithAttributes(
|
||||
label.Any(tracingEventHttpRequestHeaders, ct.headers),
|
||||
label.Any(tracingEventHttpRequestBaggage, gtrace.GetBaggageMap(ct.Context)),
|
||||
label.String(tracingEventHttpRequestBody, bodyContent),
|
||||
))
|
||||
}
|
||||
|
||||
@ -9,42 +9,62 @@ package gtrace
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/gogf/gf/container/gmap"
|
||||
"github.com/gogf/gf/container/gvar"
|
||||
"github.com/gogf/gf/net/gipv4"
|
||||
"github.com/gogf/gf/text/gstr"
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/baggage"
|
||||
"go.opentelemetry.io/otel/label"
|
||||
"go.opentelemetry.io/otel/propagation"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
tracingCommonKeyIpIntranet = `ip.intranet`
|
||||
tracingCommonKeyIpHostname = `hostname`
|
||||
)
|
||||
|
||||
var (
|
||||
intranetIps, _ = gipv4.GetIntranetIpArray()
|
||||
intranetIpStr = strings.Join(intranetIps, ",")
|
||||
hostname, _ = os.Hostname()
|
||||
// defaultTextMapPropagator is the default propagator for context propagation between peers.
|
||||
defaultTextMapPropagator = propagation.NewCompositeTextMapPropagator(
|
||||
propagation.TraceContext{},
|
||||
propagation.Baggage{},
|
||||
)
|
||||
)
|
||||
|
||||
// IsActivated checks and returns if tracing feature is activated.
|
||||
func IsActivated(ctx context.Context) bool {
|
||||
return GetTraceId(ctx) != ""
|
||||
func init() {
|
||||
CheckSetDefaultTextMapPropagator()
|
||||
}
|
||||
|
||||
// CommonLabels returns common used attribute labels:
|
||||
// ip.intranet, hostname.
|
||||
func CommonLabels() []label.KeyValue {
|
||||
return []label.KeyValue{
|
||||
label.String(`ip.intranet`, gstr.Join(intranetIps, ",")),
|
||||
label.String(`hostname`, hostname),
|
||||
label.String(tracingCommonKeyIpHostname, hostname),
|
||||
label.String(tracingCommonKeyIpIntranet, intranetIpStr),
|
||||
}
|
||||
}
|
||||
|
||||
// Tracer is a short function for retrieve Tracer.
|
||||
func Tracer(name ...string) trace.Tracer {
|
||||
tracerName := ""
|
||||
if len(name) > 0 {
|
||||
tracerName = name[0]
|
||||
// IsActivated checks and returns if tracing feature is activated.
|
||||
func IsActivated(ctx context.Context) bool {
|
||||
return GetTraceId(ctx) != ""
|
||||
}
|
||||
|
||||
// CheckSetDefaultTextMapPropagator sets the default TextMapPropagator if it is not set previously.
|
||||
func CheckSetDefaultTextMapPropagator() {
|
||||
p := otel.GetTextMapPropagator()
|
||||
if len(p.Fields()) == 0 {
|
||||
otel.SetTextMapPropagator(GetDefaultTextMapPropagator())
|
||||
}
|
||||
return otel.Tracer(tracerName)
|
||||
}
|
||||
|
||||
// GetDefaultTextMapPropagator returns the default propagator for context propagation between peers.
|
||||
func GetDefaultTextMapPropagator() propagation.TextMapPropagator {
|
||||
return defaultTextMapPropagator
|
||||
}
|
||||
|
||||
// GetTraceId retrieves and returns TraceId from context.
|
||||
@ -76,30 +96,21 @@ func GetSpanId(ctx context.Context) string {
|
||||
// SetBaggageValue is a convenient function for adding one key-value pair to baggage.
|
||||
// Note that it uses label.Any to set the key-value pair.
|
||||
func SetBaggageValue(ctx context.Context, key string, value interface{}) context.Context {
|
||||
if ctx == nil {
|
||||
ctx = context.Background()
|
||||
}
|
||||
return baggage.ContextWithValues(ctx, label.Any(key, value))
|
||||
return NewBaggage(ctx).SetValue(key, value)
|
||||
}
|
||||
|
||||
// SetBaggageMap is a convenient function for adding map key-value pairs to baggage.
|
||||
// Note that it uses label.Any to set the key-value pair.
|
||||
func SetBaggageMap(ctx context.Context, data map[string]interface{}) context.Context {
|
||||
if ctx == nil {
|
||||
ctx = context.Background()
|
||||
}
|
||||
pairs := make([]label.KeyValue, 0)
|
||||
for k, v := range data {
|
||||
pairs = append(pairs, label.Any(k, v))
|
||||
}
|
||||
return baggage.ContextWithValues(ctx, pairs...)
|
||||
return NewBaggage(ctx).SetMap(data)
|
||||
}
|
||||
|
||||
// GetBaggageMap retrieves and returns the baggage values as map.
|
||||
func GetBaggageMap(ctx context.Context) *gmap.StrAnyMap {
|
||||
return NewBaggage(ctx).GetMap()
|
||||
}
|
||||
|
||||
// GetBaggageVar retrieves value and returns a *gvar.Var for specified key from baggage.
|
||||
func GetBaggageVar(ctx context.Context, key string) *gvar.Var {
|
||||
if ctx == nil {
|
||||
return gvar.New(nil)
|
||||
}
|
||||
value := baggage.Value(ctx, label.Key(key))
|
||||
return gvar.New(value.AsInterface())
|
||||
return NewBaggage(ctx).GetVar(key)
|
||||
}
|
||||
|
||||
75
net/gtrace/gtrace_baggage.go
Normal file
75
net/gtrace/gtrace_baggage.go
Normal file
@ -0,0 +1,75 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gtrace
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/gogf/gf/container/gmap"
|
||||
"github.com/gogf/gf/container/gvar"
|
||||
"go.opentelemetry.io/otel/baggage"
|
||||
"go.opentelemetry.io/otel/label"
|
||||
)
|
||||
|
||||
// Baggage holds the data through all tracing spans.
|
||||
type Baggage struct {
|
||||
ctx context.Context
|
||||
}
|
||||
|
||||
// NewBaggage creates and returns a new Baggage object from given tracing context.
|
||||
func NewBaggage(ctx context.Context) *Baggage {
|
||||
if ctx == nil {
|
||||
ctx = context.Background()
|
||||
}
|
||||
return &Baggage{
|
||||
ctx: ctx,
|
||||
}
|
||||
}
|
||||
|
||||
// Ctx returns the context that Baggage holds.
|
||||
func (b *Baggage) Ctx() context.Context {
|
||||
return b.ctx
|
||||
}
|
||||
|
||||
// SetValue is a convenient function for adding one key-value pair to baggage.
|
||||
// Note that it uses label.Any to set the key-value pair.
|
||||
func (b *Baggage) SetValue(key string, value interface{}) context.Context {
|
||||
b.ctx = baggage.ContextWithValues(b.ctx, label.Any(key, value))
|
||||
return b.ctx
|
||||
}
|
||||
|
||||
// SetMap is a convenient function for adding map key-value pairs to baggage.
|
||||
// Note that it uses label.Any to set the key-value pair.
|
||||
func (b *Baggage) SetMap(data map[string]interface{}) context.Context {
|
||||
pairs := make([]label.KeyValue, 0)
|
||||
for k, v := range data {
|
||||
pairs = append(pairs, label.Any(k, v))
|
||||
}
|
||||
b.ctx = baggage.ContextWithValues(b.ctx, pairs...)
|
||||
return b.ctx
|
||||
}
|
||||
|
||||
// GetMap retrieves and returns the baggage values as map.
|
||||
func (b *Baggage) GetMap() *gmap.StrAnyMap {
|
||||
m := gmap.NewStrAnyMap()
|
||||
set := baggage.Set(b.ctx)
|
||||
if length := set.Len(); length > 0 {
|
||||
if length == 0 {
|
||||
return m
|
||||
}
|
||||
inter := set.Iter()
|
||||
for inter.Next() {
|
||||
m.Set(string(inter.Label().Key), inter.Label().Value.AsInterface())
|
||||
}
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// GetVar retrieves value and returns a *gvar.Var for specified key from baggage.
|
||||
func (b *Baggage) GetVar(key string) *gvar.Var {
|
||||
value := baggage.Value(b.ctx, label.Key(key))
|
||||
return gvar.New(value.AsInterface())
|
||||
}
|
||||
51
net/gtrace/gtrace_carrier.go
Normal file
51
net/gtrace/gtrace_carrier.go
Normal file
@ -0,0 +1,51 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gtrace
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/internal/json"
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
)
|
||||
|
||||
type Carrier struct {
|
||||
data map[string]interface{}
|
||||
}
|
||||
|
||||
func NewCarrier(data ...map[string]interface{}) *Carrier {
|
||||
carrier := &Carrier{}
|
||||
if len(data) > 0 && data[0] != nil {
|
||||
carrier.data = data[0]
|
||||
} else {
|
||||
carrier.data = make(map[string]interface{})
|
||||
}
|
||||
return carrier
|
||||
}
|
||||
|
||||
func (c *Carrier) Get(k string) string {
|
||||
return gconv.String(c.data[k])
|
||||
}
|
||||
|
||||
func (c *Carrier) Set(k, v string) {
|
||||
c.data[k] = v
|
||||
}
|
||||
|
||||
func (c *Carrier) MustMarshal() []byte {
|
||||
b, err := json.Marshal(c.data)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func (c *Carrier) String() string {
|
||||
return string(c.MustMarshal())
|
||||
}
|
||||
|
||||
func (c *Carrier) UnmarshalJSON(b []byte) error {
|
||||
carrier := NewCarrier(nil)
|
||||
return json.Unmarshal(b, carrier.data)
|
||||
}
|
||||
24
net/gtrace/gtrace_span.go
Normal file
24
net/gtrace/gtrace_span.go
Normal file
@ -0,0 +1,24 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gtrace
|
||||
|
||||
import (
|
||||
"context"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
)
|
||||
|
||||
type Span struct {
|
||||
trace.Span
|
||||
}
|
||||
|
||||
// NewSpan creates a span using default tracer.
|
||||
func NewSpan(ctx context.Context, spanName string, opts ...trace.SpanOption) (context.Context, *Span) {
|
||||
ctx, span := NewTracer().Start(ctx, spanName, opts...)
|
||||
return ctx, &Span{
|
||||
Span: span,
|
||||
}
|
||||
}
|
||||
27
net/gtrace/gtrace_tracer.go
Normal file
27
net/gtrace/gtrace_tracer.go
Normal file
@ -0,0 +1,27 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gtrace
|
||||
|
||||
import (
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
)
|
||||
|
||||
type Tracer struct {
|
||||
trace.Tracer
|
||||
}
|
||||
|
||||
// Tracer is a short function for retrieving Tracer.
|
||||
func NewTracer(name ...string) *Tracer {
|
||||
tracerName := ""
|
||||
if len(name) > 0 {
|
||||
tracerName = name[0]
|
||||
}
|
||||
return &Tracer{
|
||||
Tracer: otel.Tracer(tracerName),
|
||||
}
|
||||
}
|
||||
64
net/gtrace/gtrace_unit_carrier_test.go
Normal file
64
net/gtrace/gtrace_unit_carrier_test.go
Normal file
@ -0,0 +1,64 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gtrace_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/gogf/gf/net/gtrace"
|
||||
"github.com/gogf/gf/test/gtest"
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/oteltest"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
"testing"
|
||||
)
|
||||
|
||||
const (
|
||||
traceIDStr = "4bf92f3577b34da6a3ce929d0e0e4736"
|
||||
spanIDStr = "00f067aa0ba902b7"
|
||||
)
|
||||
|
||||
var (
|
||||
traceID = mustTraceIDFromHex(traceIDStr)
|
||||
spanID = mustSpanIDFromHex(spanIDStr)
|
||||
)
|
||||
|
||||
func mustTraceIDFromHex(s string) (t trace.TraceID) {
|
||||
var err error
|
||||
t, err = trace.TraceIDFromHex(s)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func mustSpanIDFromHex(s string) (t trace.SpanID) {
|
||||
var err error
|
||||
t, err = trace.SpanIDFromHex(s)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func TestNewCarrier(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
ctx := trace.ContextWithRemoteSpanContext(context.Background(), trace.SpanContext{
|
||||
TraceID: traceID,
|
||||
SpanID: spanID,
|
||||
TraceFlags: trace.FlagsSampled,
|
||||
})
|
||||
ctx, _ = oteltest.DefaultTracer().Start(ctx, "inject")
|
||||
carrier1 := gtrace.NewCarrier()
|
||||
otel.GetTextMapPropagator().Inject(ctx, carrier1)
|
||||
t.Assert(carrier1.String(), `{"traceparent":"00-4bf92f3577b34da6a3ce929d0e0e4736-0000000000000002-01","tracestate":""}`)
|
||||
|
||||
ctx = otel.GetTextMapPropagator().Extract(ctx, carrier1)
|
||||
gotSc := trace.RemoteSpanContextFromContext(ctx)
|
||||
t.Assert(gotSc.TraceID.String(), traceID.String())
|
||||
t.Assert(gotSc.SpanID.String(), "0000000000000002")
|
||||
})
|
||||
}
|
||||
@ -59,7 +59,7 @@ func Get(name string, def ...interface{}) interface{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get retrieves and returns the build-in binary variable of given name as gvar.Var.
|
||||
// GetVar retrieves and returns the build-in binary variable of given name as gvar.Var.
|
||||
func GetVar(name string, def ...interface{}) *gvar.Var {
|
||||
return gvar.New(Get(name, def...))
|
||||
}
|
||||
|
||||
106
os/gcfg/gcfg.go
106
os/gcfg/gcfg.go
@ -40,7 +40,7 @@ type Config struct {
|
||||
}
|
||||
|
||||
var (
|
||||
supportedFileTypes = []string{"toml", "yaml", "json", "ini", "xml"}
|
||||
supportedFileTypes = []string{"toml", "yaml", "yml", "json", "ini", "xml"}
|
||||
resourceTryFiles = []string{"", "/", "config/", "config", "/config", "/config/"}
|
||||
)
|
||||
|
||||
@ -52,7 +52,7 @@ func New(file ...string) *Config {
|
||||
name = file[0]
|
||||
} else {
|
||||
// Custom default configuration file name from command line or environment.
|
||||
if customFile := gcmd.GetWithEnv(fmt.Sprintf("%s.file", cmdEnvKey)).String(); customFile != "" {
|
||||
if customFile := gcmd.GetOptWithEnv(fmt.Sprintf("%s.file", cmdEnvKey)).String(); customFile != "" {
|
||||
name = customFile
|
||||
}
|
||||
}
|
||||
@ -62,7 +62,7 @@ func New(file ...string) *Config {
|
||||
jsonMap: gmap.NewStrAnyMap(true),
|
||||
}
|
||||
// Customized dir path from env/cmd.
|
||||
if customPath := gcmd.GetWithEnv(fmt.Sprintf("%s.path", cmdEnvKey)).String(); customPath != "" {
|
||||
if customPath := gcmd.GetOptWithEnv(fmt.Sprintf("%s.path", cmdEnvKey)).String(); customPath != "" {
|
||||
if gfile.Exists(customPath) {
|
||||
_ = c.SetPath(customPath)
|
||||
} else {
|
||||
@ -71,33 +71,28 @@ func New(file ...string) *Config {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Dir path of working dir.
|
||||
if err := c.AddPath(gfile.Pwd()); err != nil {
|
||||
intlog.Error(err)
|
||||
}
|
||||
|
||||
// Dir path of main package.
|
||||
if mainPath := gfile.MainPkgPath(); mainPath != "" && gfile.Exists(mainPath) {
|
||||
_ = c.AddPath(mainPath)
|
||||
if err := c.AddPath(mainPath); err != nil {
|
||||
intlog.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Dir path of binary.
|
||||
if selfPath := gfile.SelfDir(); selfPath != "" && gfile.Exists(selfPath) {
|
||||
_ = c.AddPath(selfPath)
|
||||
if err := c.AddPath(selfPath); err != nil {
|
||||
intlog.Error(err)
|
||||
}
|
||||
}
|
||||
// Dir path of working dir.
|
||||
_ = c.AddPath(gfile.Pwd())
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *Config) getSearchPaths() []string {
|
||||
var (
|
||||
searchPaths = c.searchPaths.Slice()
|
||||
mainPkgPath = gfile.MainPkgPath()
|
||||
)
|
||||
if mainPkgPath != "" {
|
||||
if !gstr.InArray(searchPaths, mainPkgPath) {
|
||||
searchPaths = append([]string{mainPkgPath}, searchPaths...)
|
||||
}
|
||||
}
|
||||
return searchPaths
|
||||
}
|
||||
|
||||
// filePath returns the absolute configuration file path for the given filename by <file>.
|
||||
func (c *Config) filePath(file ...string) (path string) {
|
||||
name := c.defaultName
|
||||
@ -107,20 +102,21 @@ func (c *Config) filePath(file ...string) (path string) {
|
||||
path = c.FilePath(name)
|
||||
if path == "" {
|
||||
var (
|
||||
searchPaths = c.getSearchPaths()
|
||||
buffer = bytes.NewBuffer(nil)
|
||||
buffer = bytes.NewBuffer(nil)
|
||||
)
|
||||
|
||||
if len(searchPaths) > 0 {
|
||||
if c.searchPaths.Len() > 0 {
|
||||
buffer.WriteString(fmt.Sprintf("[gcfg] cannot find config file \"%s\" in following paths:", name))
|
||||
index := 1
|
||||
for _, v := range searchPaths {
|
||||
v = gstr.TrimRight(v, `\/`)
|
||||
buffer.WriteString(fmt.Sprintf("\n%d. %s", index, v))
|
||||
index++
|
||||
buffer.WriteString(fmt.Sprintf("\n%d. %s", index, v+gfile.Separator+"config"))
|
||||
index++
|
||||
}
|
||||
c.searchPaths.RLockFunc(func(array []string) {
|
||||
index := 1
|
||||
for _, v := range array {
|
||||
v = gstr.TrimRight(v, `\/`)
|
||||
buffer.WriteString(fmt.Sprintf("\n%d. %s", index, v))
|
||||
index++
|
||||
buffer.WriteString(fmt.Sprintf("\n%d. %s", index, v+gfile.Separator+"config"))
|
||||
index++
|
||||
}
|
||||
})
|
||||
|
||||
} else {
|
||||
buffer.WriteString(fmt.Sprintf("[gcfg] cannot find config file \"%s\" with no path set/add", name))
|
||||
}
|
||||
@ -281,7 +277,6 @@ func (c *Config) FilePath(file ...string) (path string) {
|
||||
if len(file) > 0 {
|
||||
name = file[0]
|
||||
}
|
||||
searchPaths := c.getSearchPaths()
|
||||
// Searching resource manager.
|
||||
if !gres.IsEmpty() {
|
||||
for _, v := range resourceTryFiles {
|
||||
@ -290,25 +285,29 @@ func (c *Config) FilePath(file ...string) (path string) {
|
||||
return
|
||||
}
|
||||
}
|
||||
for _, prefix := range searchPaths {
|
||||
for _, v := range resourceTryFiles {
|
||||
if file := gres.Get(prefix + v + name); file != nil {
|
||||
path = file.Name()
|
||||
return
|
||||
c.searchPaths.RLockFunc(func(array []string) {
|
||||
for _, prefix := range array {
|
||||
for _, v := range resourceTryFiles {
|
||||
if file := gres.Get(prefix + v + name); file != nil {
|
||||
path = file.Name()
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
// Searching the file system.
|
||||
for _, prefix := range searchPaths {
|
||||
prefix = gstr.TrimRight(prefix, `\/`)
|
||||
if path, _ = gspath.Search(prefix, name); path != "" {
|
||||
return
|
||||
c.searchPaths.RLockFunc(func(array []string) {
|
||||
for _, prefix := range array {
|
||||
prefix = gstr.TrimRight(prefix, `\/`)
|
||||
if path, _ = gspath.Search(prefix, name); path != "" {
|
||||
return
|
||||
}
|
||||
if path, _ = gspath.Search(prefix+gfile.Separator+"config", name); path != "" {
|
||||
return
|
||||
}
|
||||
}
|
||||
if path, _ = gspath.Search(prefix+gfile.Separator+"config", name); path != "" {
|
||||
return
|
||||
}
|
||||
}
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
@ -392,13 +391,12 @@ func (c *Config) getJson(file ...string) *gjson.Json {
|
||||
}
|
||||
}
|
||||
return j
|
||||
} else {
|
||||
if errorPrint() {
|
||||
if filePath != "" {
|
||||
glog.Criticalf(`[gcfg] Load config file "%s" failed: %s`, filePath, err.Error())
|
||||
} else {
|
||||
glog.Criticalf(`[gcfg] Load configuration failed: %s`, err.Error())
|
||||
}
|
||||
}
|
||||
if errorPrint() {
|
||||
if filePath != "" {
|
||||
glog.Criticalf(`[gcfg] Load config file "%s" failed: %s`, filePath, err.Error())
|
||||
} else {
|
||||
glog.Criticalf(`[gcfg] Load configuration failed: %s`, err.Error())
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
||||
@ -18,5 +18,5 @@ const (
|
||||
|
||||
// errorPrint checks whether printing error to stdout.
|
||||
func errorPrint() bool {
|
||||
return gcmd.GetWithEnv(errorPrintKey, true).Bool()
|
||||
return gcmd.GetOptWithEnv(errorPrintKey, true).Bool()
|
||||
}
|
||||
|
||||
@ -32,10 +32,15 @@ func Instance(name ...string) *Config {
|
||||
}
|
||||
return instances.GetOrSetFuncLock(key, func() interface{} {
|
||||
c := New()
|
||||
for _, fileType := range supportedFileTypes {
|
||||
if file := fmt.Sprintf(`%s.%s`, key, fileType); c.Available(file) {
|
||||
c.SetFileName(file)
|
||||
break
|
||||
// If it's not using default configuration or its configuration file is not available,
|
||||
// it searches the possible configuration file according to the name and all supported
|
||||
// file types.
|
||||
if key != DefaultName || !c.Available() {
|
||||
for _, fileType := range supportedFileTypes {
|
||||
if file := fmt.Sprintf(`%s.%s`, key, fileType); c.Available(file) {
|
||||
c.SetFileName(file)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return c
|
||||
|
||||
@ -78,7 +78,6 @@ v4 = "1.234"
|
||||
"cache": "127.0.0.1:6379,1",
|
||||
})
|
||||
t.AssertEQ(c.FilePath(), gfile.Pwd()+gfile.Separator+path)
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
@ -89,7 +88,7 @@ func Test_Instance_AutoLocateConfigFile(t *testing.T) {
|
||||
// Automatically locate the configuration file with supported file extensions.
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
pwd := gfile.Pwd()
|
||||
gfile.Chdir(gdebug.TestDataPath())
|
||||
t.AssertNil(gfile.Chdir(gdebug.TestDataPath()))
|
||||
defer gfile.Chdir(pwd)
|
||||
t.Assert(Instance("c1") != nil, true)
|
||||
t.Assert(Instance("c1").Get("my-config"), "1")
|
||||
@ -98,10 +97,23 @@ func Test_Instance_AutoLocateConfigFile(t *testing.T) {
|
||||
// Automatically locate the configuration file with supported file extensions.
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
pwd := gfile.Pwd()
|
||||
gfile.Chdir(gdebug.TestDataPath("folder1"))
|
||||
t.AssertNil(gfile.Chdir(gdebug.TestDataPath("folder1")))
|
||||
defer gfile.Chdir(pwd)
|
||||
t.Assert(Instance("c2").Get("my-config"), 2)
|
||||
})
|
||||
// Default configuration file.
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
instances.Clear()
|
||||
pwd := gfile.Pwd()
|
||||
t.AssertNil(gfile.Chdir(gdebug.TestDataPath("default")))
|
||||
defer gfile.Chdir(pwd)
|
||||
t.Assert(Instance().Get("my-config"), 1)
|
||||
|
||||
instances.Clear()
|
||||
t.AssertNil(genv.Set("GF_GCFG_FILE", "config.json"))
|
||||
defer genv.Set("GF_GCFG_FILE", "")
|
||||
t.Assert(Instance().Get("my-config"), 2)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Instance_EnvPath(t *testing.T) {
|
||||
|
||||
1
os/gcfg/testdata/default/config.json
vendored
Normal file
1
os/gcfg/testdata/default/config.json
vendored
Normal file
@ -0,0 +1 @@
|
||||
{"my-config": 2}
|
||||
1
os/gcfg/testdata/default/config.toml
vendored
Normal file
1
os/gcfg/testdata/default/config.toml
vendored
Normal file
@ -0,0 +1 @@
|
||||
my-config = "1"
|
||||
@ -9,69 +9,25 @@
|
||||
package gcmd
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/container/gvar"
|
||||
"github.com/gogf/gf/internal/command"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/gogf/gf/container/gvar"
|
||||
|
||||
"github.com/gogf/gf/text/gregex"
|
||||
)
|
||||
|
||||
var (
|
||||
defaultParsedArgs = make([]string, 0)
|
||||
defaultParsedOptions = make(map[string]string)
|
||||
defaultCommandFuncMap = make(map[string]func())
|
||||
)
|
||||
|
||||
// Custom initialization.
|
||||
func Init(args ...string) {
|
||||
if len(args) == 0 {
|
||||
if len(defaultParsedArgs) == 0 && len(defaultParsedOptions) == 0 {
|
||||
args = os.Args
|
||||
} else {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
defaultParsedArgs = make([]string, 0)
|
||||
defaultParsedOptions = make(map[string]string)
|
||||
}
|
||||
// Parsing os.Args with default algorithm.
|
||||
for i := 0; i < len(args); {
|
||||
array, _ := gregex.MatchString(`^\-{1,2}([\w\?\.\-]+)(=){0,1}(.*)$`, args[i])
|
||||
if len(array) > 2 {
|
||||
if array[2] == "=" {
|
||||
defaultParsedOptions[array[1]] = array[3]
|
||||
} else if i < len(args)-1 {
|
||||
if len(args[i+1]) > 0 && args[i+1][0] == '-' {
|
||||
// Eg: gf gen -d -n 1
|
||||
defaultParsedOptions[array[1]] = array[3]
|
||||
} else {
|
||||
// Eg: gf gen -n 2
|
||||
defaultParsedOptions[array[1]] = args[i+1]
|
||||
i += 2
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
// Eg: gf gen -h
|
||||
defaultParsedOptions[array[1]] = array[3]
|
||||
}
|
||||
} else {
|
||||
defaultParsedArgs = append(defaultParsedArgs, args[i])
|
||||
}
|
||||
i++
|
||||
}
|
||||
command.Init(args...)
|
||||
}
|
||||
|
||||
// GetOpt returns the option value named <name>.
|
||||
func GetOpt(name string, def ...string) string {
|
||||
Init()
|
||||
if v, ok := defaultParsedOptions[name]; ok {
|
||||
return v
|
||||
}
|
||||
if len(def) > 0 {
|
||||
return def[0]
|
||||
}
|
||||
return ""
|
||||
return command.GetOpt(name, def...)
|
||||
}
|
||||
|
||||
// GetOptVar returns the option value named <name> as gvar.Var.
|
||||
@ -83,26 +39,19 @@ func GetOptVar(name string, def ...string) *gvar.Var {
|
||||
// GetOptAll returns all parsed options.
|
||||
func GetOptAll() map[string]string {
|
||||
Init()
|
||||
return defaultParsedOptions
|
||||
return command.GetOptAll()
|
||||
}
|
||||
|
||||
// ContainsOpt checks whether option named <name> exist in the arguments.
|
||||
func ContainsOpt(name string, def ...string) bool {
|
||||
func ContainsOpt(name string) bool {
|
||||
Init()
|
||||
_, ok := defaultParsedOptions[name]
|
||||
return ok
|
||||
return command.ContainsOpt(name)
|
||||
}
|
||||
|
||||
// GetArg returns the argument at <index>.
|
||||
func GetArg(index int, def ...string) string {
|
||||
Init()
|
||||
if index < len(defaultParsedArgs) {
|
||||
return defaultParsedArgs[index]
|
||||
}
|
||||
if len(def) > 0 {
|
||||
return def[0]
|
||||
}
|
||||
return ""
|
||||
return command.GetArg(index, def...)
|
||||
}
|
||||
|
||||
// GetArgVar returns the argument at <index> as gvar.Var.
|
||||
@ -114,31 +63,37 @@ func GetArgVar(index int, def ...string) *gvar.Var {
|
||||
// GetArgAll returns all parsed arguments.
|
||||
func GetArgAll() []string {
|
||||
Init()
|
||||
return defaultParsedArgs
|
||||
return command.GetArgAll()
|
||||
}
|
||||
|
||||
// GetWithEnv returns the command line argument of the specified <key>.
|
||||
// GetWithEnv is alias of GetOptWithEnv.
|
||||
// Deprecated.
|
||||
func GetWithEnv(key string, def ...interface{}) *gvar.Var {
|
||||
return GetOptWithEnv(key, def...)
|
||||
}
|
||||
|
||||
// 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>;
|
||||
// 2. Environment arguments are in uppercase format, eg: GF_<package name>_<variable name>;
|
||||
func GetWithEnv(key string, def ...interface{}) *gvar.Var {
|
||||
value := interface{}(nil)
|
||||
if len(def) > 0 {
|
||||
value = def[0]
|
||||
}
|
||||
func GetOptWithEnv(key string, def ...interface{}) *gvar.Var {
|
||||
cmdKey := strings.ToLower(strings.Replace(key, "_", ".", -1))
|
||||
if v := GetOpt(cmdKey); v != "" {
|
||||
value = v
|
||||
if ContainsOpt(cmdKey) {
|
||||
return gvar.New(GetOpt(cmdKey))
|
||||
} else {
|
||||
envKey := strings.ToUpper(strings.Replace(key, ".", "_", -1))
|
||||
if v := os.Getenv(envKey); v != "" {
|
||||
value = v
|
||||
if r, ok := os.LookupEnv(envKey); ok {
|
||||
return gvar.New(r)
|
||||
} else {
|
||||
if len(def) > 0 {
|
||||
return gvar.New(def[0])
|
||||
}
|
||||
}
|
||||
}
|
||||
return gvar.New(value)
|
||||
return gvar.New(nil)
|
||||
}
|
||||
|
||||
// BuildOptions builds the options as string.
|
||||
|
||||
@ -59,12 +59,12 @@ func Test_GetWithEnv(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
genv.Set("TEST", "1")
|
||||
defer genv.Remove("TEST")
|
||||
t.Assert(gcmd.GetWithEnv("test"), 1)
|
||||
t.Assert(gcmd.GetOptWithEnv("test"), 1)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
genv.Set("TEST", "1")
|
||||
defer genv.Remove("TEST")
|
||||
gcmd.Init("-test", "2")
|
||||
t.Assert(gcmd.GetWithEnv("test"), 2)
|
||||
t.Assert(gcmd.GetOptWithEnv("test"), 2)
|
||||
})
|
||||
}
|
||||
|
||||
@ -11,8 +11,8 @@ import (
|
||||
"github.com/gogf/gf/container/gvar"
|
||||
"github.com/gogf/gf/os/gcmd"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
import "strings"
|
||||
|
||||
// All returns a copy of strings representing the environment,
|
||||
// in the form "key=value".
|
||||
|
||||
@ -20,7 +20,7 @@ const (
|
||||
|
||||
var (
|
||||
// Default expire time for file content caching.
|
||||
cacheExpire = gcmd.GetWithEnv("gf.gfile.cache", gDEFAULT_CACHE_EXPIRE).Duration()
|
||||
cacheExpire = gcmd.GetOptWithEnv("gf.gfile.cache", gDEFAULT_CACHE_EXPIRE).Duration()
|
||||
|
||||
// internalCache is the memory cache for internal usage.
|
||||
internalCache = gcache.New()
|
||||
|
||||
@ -26,7 +26,7 @@ var (
|
||||
)
|
||||
|
||||
func init() {
|
||||
defaultDebug = gcmd.GetWithEnv("gf.glog.debug", true).Bool()
|
||||
defaultDebug = gcmd.GetOptWithEnv("gf.glog.debug", true).Bool()
|
||||
SetDebug(defaultDebug)
|
||||
}
|
||||
|
||||
|
||||
@ -59,7 +59,7 @@ func New(path string, cache bool) *SPath {
|
||||
}
|
||||
|
||||
// Get creates and returns a instance of searching manager for given path.
|
||||
// The parameter <cache> specifies whether using cache feature for this manager.
|
||||
// The parameter `cache` specifies whether using cache feature for this manager.
|
||||
// If cache feature is enabled, it asynchronously and recursively scans the path
|
||||
// and updates all sub files/folders to the cache using package gfsnotify.
|
||||
func Get(root string, cache bool) *SPath {
|
||||
@ -71,24 +71,24 @@ func Get(root string, cache bool) *SPath {
|
||||
}).(*SPath)
|
||||
}
|
||||
|
||||
// Search searches file <name> under path <root>.
|
||||
// The parameter <root> should be a absolute path. It will not automatically
|
||||
// convert <root> to absolute path for performance reason.
|
||||
// The optional parameter <indexFiles> specifies the searching index files when the result is a directory.
|
||||
// For example, if the result <a> is a directory, and <indexFiles> is [index.html, main.html], it will also
|
||||
// search [index.html, main.html] under <a>. It returns the absolute file path if any of them found,
|
||||
// or else it returns <a>.
|
||||
// Search searches file `name` under path `root`.
|
||||
// The parameter `root` should be a absolute path. It will not automatically
|
||||
// convert `root` to absolute path for performance reason.
|
||||
// The optional parameter `indexFiles` specifies the searching index files when the result is a directory.
|
||||
// For example, if the result `filePath` is a directory, and `indexFiles` is [index.html, main.html], it will also
|
||||
// search [index.html, main.html] under `filePath`. It returns the absolute file path if any of them found,
|
||||
// or else it returns `filePath`.
|
||||
func Search(root string, name string, indexFiles ...string) (filePath string, isDir bool) {
|
||||
return Get(root, false).Search(name, indexFiles...)
|
||||
}
|
||||
|
||||
// SearchWithCache searches file <name> under path <root> with cache feature enabled.
|
||||
// The parameter <root> should be a absolute path. It will not automatically
|
||||
// convert <root> to absolute path for performance reason.
|
||||
// The optional parameter <indexFiles> specifies the searching index files when the result is a directory.
|
||||
// For example, if the result <a> is a directory, and <indexFiles> is [index.html, main.html], it will also
|
||||
// search [index.html, main.html] under <a>. It returns the absolute file path if any of them found,
|
||||
// or else it returns <a>.
|
||||
// SearchWithCache searches file `name` under path `root` with cache feature enabled.
|
||||
// The parameter `root` should be a absolute path. It will not automatically
|
||||
// convert `root` to absolute path for performance reason.
|
||||
// The optional parameter `indexFiles` specifies the searching index files when the result is a directory.
|
||||
// For example, if the result `filePath` is a directory, and `indexFiles` is [index.html, main.html], it will also
|
||||
// search [index.html, main.html] under `filePath`. It returns the absolute file path if any of them found,
|
||||
// or else it returns `filePath`.
|
||||
func SearchWithCache(root string, name string, indexFiles ...string) (filePath string, isDir bool) {
|
||||
return Get(root, true).Search(name, indexFiles...)
|
||||
}
|
||||
@ -156,11 +156,11 @@ func (sp *SPath) Add(path string) (realPath string, err error) {
|
||||
}
|
||||
}
|
||||
|
||||
// Search searches file <name> in the manager.
|
||||
// The optional parameter <indexFiles> specifies the searching index files when the result is a directory.
|
||||
// For example, if the result <a> is a directory, and <indexFiles> is [index.html, main.html], it will also
|
||||
// search [index.html, main.html] under <a>. It returns the absolute file path if any of them found,
|
||||
// or else it returns <a>.
|
||||
// Search searches file `name` in the manager.
|
||||
// The optional parameter `indexFiles` specifies the searching index files when the result is a directory.
|
||||
// For example, if the result `filePath` is a directory, and `indexFiles` is [index.html, main.html], it will also
|
||||
// search [index.html, main.html] under `filePath`. It returns the absolute file path if any of them found,
|
||||
// or else it returns `filePath`.
|
||||
func (sp *SPath) Search(name string, indexFiles ...string) (filePath string, isDir bool) {
|
||||
// No cache enabled.
|
||||
if sp.cache == nil {
|
||||
@ -213,8 +213,8 @@ func (sp *SPath) Search(name string, indexFiles ...string) (filePath string, isD
|
||||
return
|
||||
}
|
||||
|
||||
// Remove deletes the <path> from cache files of the manager.
|
||||
// The parameter <path> can be either a absolute path or just a relative file name.
|
||||
// Remove deletes the `path` from cache files of the manager.
|
||||
// The parameter `path` can be either a absolute path or just a relative file name.
|
||||
func (sp *SPath) Remove(path string) {
|
||||
if sp.cache == nil {
|
||||
return
|
||||
|
||||
@ -90,22 +90,27 @@ func TestSPath_Basic(t *testing.T) {
|
||||
realPath, err = gsp.Add("gf_tmp1")
|
||||
t.Assert(err != nil, false)
|
||||
t.Assert(realPath, gfile.Join(root, "gf_tmp1"))
|
||||
|
||||
realPath, err = gsp.Add("gf_tmp3")
|
||||
t.Assert(err != nil, true)
|
||||
t.Assert(realPath, "")
|
||||
|
||||
gsp.Remove(gfile.Join(root, "gf_tmp"))
|
||||
gsp.Remove(gfile.Join(root, "gf_tmp1"))
|
||||
gsp.Remove(gfile.Join(root, "gf_tmp3"))
|
||||
t.Assert(gsp.Size(), 3)
|
||||
t.Assert(len(gsp.Paths()), 3)
|
||||
|
||||
gsp.AllPaths()
|
||||
gsp.Set(root)
|
||||
fp, isDir = gsp.Search("gf_tmp")
|
||||
t.Assert(fp, gfile.Join(root, "gf_tmp"))
|
||||
t.Assert(isDir, true)
|
||||
|
||||
fp, isDir = gsp.Search("gf_tmp", "gf.txt")
|
||||
t.Assert(fp, gfile.Join(root, "gf_tmp", "gf.txt"))
|
||||
t.Assert(isDir, false)
|
||||
|
||||
fp, isDir = gsp.Search("/", "gf.txt")
|
||||
t.Assert(fp, pwd)
|
||||
t.Assert(isDir, true)
|
||||
|
||||
@ -45,7 +45,7 @@ const (
|
||||
// "2018/10/31 - 16:38:46"
|
||||
// "2018-02-09",
|
||||
// "2018.02.09",
|
||||
timeRegexPattern1 = `(\d{4}[-/\.]\d{2}[-/\.]\d{2})[:\sT-]*(\d{0,2}:{0,1}\d{0,2}:{0,1}\d{0,2}){0,1}\.{0,1}(\d{0,9})([\sZ]{0,1})([\+-]{0,1})([:\d]*)`
|
||||
timeRegexPattern1 = `(\d{4}[-/\.]\d{1,2}[-/\.]\d{1,2})[:\sT-]*(\d{0,2}:{0,1}\d{0,2}:{0,1}\d{0,2}){0,1}\.{0,1}(\d{0,9})([\sZ]{0,1})([\+-]{0,1})([:\d]*)`
|
||||
|
||||
// Regular expression2(datetime separator supports '-', '/', '.').
|
||||
// Eg:
|
||||
|
||||
@ -266,11 +266,11 @@ func formatToRegexPattern(format string) string {
|
||||
// formatMonthDaySuffixMap returns the short english word for current day.
|
||||
func formatMonthDaySuffixMap(day string) string {
|
||||
switch day {
|
||||
case "01":
|
||||
case "01", "21", "31":
|
||||
return "st"
|
||||
case "02":
|
||||
case "02", "22":
|
||||
return "nd"
|
||||
case "03":
|
||||
case "03", "23":
|
||||
return "rd"
|
||||
default:
|
||||
return "th"
|
||||
|
||||
@ -42,6 +42,12 @@ func Test_New(t *testing.T) {
|
||||
timeTemp := gtime.New(timeNow.TimestampMicro())
|
||||
t.Assert(timeTemp.Time.Format("2006-01-02 15:04:05"), timeNow.Time.Format("2006-01-02 15:04:05"))
|
||||
})
|
||||
// short datetime.
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
timeTemp := gtime.New("2021-2-9 08:01:21")
|
||||
t.Assert(timeTemp.Format("Y-m-d H:i:s"), "2021-02-09 08:01:21")
|
||||
t.Assert(timeTemp.Time.Format("2006-01-02 15:04:05"), "2021-02-09 08:01:21")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Nil(t *testing.T) {
|
||||
|
||||
@ -42,9 +42,9 @@ const (
|
||||
)
|
||||
|
||||
var (
|
||||
defaultSlots = gcmd.GetWithEnv(fmt.Sprintf("%s.slots", cmdEnvKey), defaultSlotNumber).Int()
|
||||
defaultLevel = gcmd.GetWithEnv(fmt.Sprintf("%s.level", cmdEnvKey), defaultWheelLevel).Int()
|
||||
defaultInterval = gcmd.GetWithEnv(fmt.Sprintf("%s.interval", cmdEnvKey), defaultWheelInterval).Duration() * time.Millisecond
|
||||
defaultSlots = gcmd.GetOptWithEnv(fmt.Sprintf("%s.slots", cmdEnvKey), defaultSlotNumber).Int()
|
||||
defaultLevel = gcmd.GetOptWithEnv(fmt.Sprintf("%s.level", cmdEnvKey), defaultWheelLevel).Int()
|
||||
defaultInterval = gcmd.GetOptWithEnv(fmt.Sprintf("%s.interval", cmdEnvKey), defaultWheelInterval).Duration() * time.Millisecond
|
||||
defaultTimer = New(defaultSlots, defaultInterval, defaultLevel)
|
||||
)
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user