Compare commits

..

56 Commits

Author SHA1 Message Date
19bfc48dca version updates 2021-03-04 22:49:11 +08:00
ae0cc5a4b6 add more unit testing case for ScanList feature 2021-03-04 20:45:05 +08:00
ba74e0beb2 improve ScanList of the same replation names for package gdb 2021-03-04 00:07:06 +08:00
e5ca4e788e fix issue #1190 2021-03-03 14:29:01 +08:00
1c7f034135 Merge branch 'master' of https://github.com/gogf/gf 2021-03-02 23:32:50 +08:00
4c78ce6e3f fix issue #1187 2021-03-02 23:27:50 +08:00
7803d557b3 Merge pull request #1183 from rc452860/master 2021-03-02 00:04:05 +08:00
204fea395c add struct object support in group router registering for package ghttp 2021-03-01 20:49:09 +08:00
bd13de2b39 improve package internal/intlog 2021-03-01 17:50:02 +08:00
d0f649b328 improve package internal/intlog 2021-03-01 17:39:13 +08:00
04e42d2175 improve package internal/intlog 2021-03-01 17:15:18 +08:00
ffc88eaaa7 improve package gcfg for main package path searching 2021-03-01 17:05:44 +08:00
4b4cc5ebb9 improve package gcfg for main package path searching 2021-03-01 17:02:07 +08:00
0e6cddb547 去除cancelFunc避免退出之前关闭mysqlConn导致数据获取不完整 2021-03-01 01:43:53 +08:00
a69aec9070 Merge branch 'master' of https://github.com/gogf/gf 2021-02-28 20:14:10 +08:00
d2bd37962e add unit testing case of UnmarshalValue for struct converting of querying result; improve package gdb 2021-02-27 23:58:36 +08:00
eb6763b0fd fix issue of failing configuration for default configuration file for package gcfg 2021-02-26 13:57:47 +08:00
d330afdd36 add required* rules checks for map/slice 2021-02-24 01:20:06 +08:00
5db4bbc186 fix issue 1162 2021-02-24 01:07:09 +08:00
e00f2666ff Merge pull request #1176 from develop1024/master 2021-02-24 00:39:35 +08:00
0238cdd5ec 修改注释错误-os/gbuild/GetVar 2021-02-23 23:07:28 +08:00
2c34d96b9d add tls configuration for ghttp.Client 2021-02-23 22:00:11 +08:00
65b3630f6d Merge pull request #1174 from aimingo/master
style:  code style
2021-02-22 11:41:45 +08:00
54b629561e style: code style 2021-02-22 11:20:10 +08:00
285ad36e7d add short datetime string parsing support for package gtime 2021-02-21 22:24:51 +08:00
5adde275fc add switch variable for internal type tracing components 2021-02-14 22:00:56 +08:00
a3fa10d820 great! completed 'with' feature for package gdb 2021-02-09 18:00:43 +08:00
acf47f3907 orm with feature 2021-02-08 17:57:21 +08:00
b4d5335e43 add package gmeta for embedded struct of meta data feature 2021-02-07 21:23:09 +08:00
39fb842e9b Merge branch 'master' into feature/ormwith 2021-02-07 15:34:50 +08:00
7a5d86b44d README updates 2021-02-07 15:34:36 +08:00
397e11e124 improve package gdb for association feature 2021-02-07 14:39:32 +08:00
f4314c318e up 2021-02-07 10:40:02 +08:00
9bb5536163 Merge branch 'master' of https://github.com/gogf/gf into develop 2021-02-07 10:32:18 +08:00
2344701f22 README updates 2021-02-07 10:31:59 +08:00
3bb909ba4f improve comment for package gspath 2021-02-06 11:42:58 +08:00
8ae0bd148b improve ScanList for package gdb; improve Structs for package gconv 2021-02-05 17:42:05 +08:00
72251b880a improve package gconv for slice converting 2021-02-05 14:44:20 +08:00
fefde4c290 improve stack feature for package gdebug/gerror 2021-02-04 00:10:13 +08:00
3b2bae6128 improve soft deletion for package gdb 2021-02-03 23:11:17 +08:00
ae559b57de fix dryrun configuration for package gdb 2021-02-03 22:46:59 +08:00
6135085d61 improve package gtrace 2021-02-03 15:27:41 +08:00
40bdc76af1 improve package gtrace 2021-02-03 15:14:07 +08:00
d4f982a9cf Merge branch 'master' of https://github.com/gogf/gf 2021-02-02 15:18:37 +08:00
813841bb68 rename GetWithEnv to GetOptWithEnv for packge gcmd 2021-02-02 15:16:09 +08:00
930e63e6b9 Merge pull request #1154 from gouguoyin/master
formatMonthDaySuffixMap() misjudged suffix
2021-02-02 13:16:17 +08:00
ce40d139e7 fix issue of nil response handling for ghttp.Client 2021-02-01 21:51:42 +08:00
8368e11827 merge develop 2021-02-01 17:14:08 +08:00
e6b4662ec2 improve tracing feature 2021-02-01 17:10:50 +08:00
28f83d3d32 formatMonthDaySuffixMap() misjudged suffix
when day is 21,abbreviated as 21st,suffix is st ,is not th
when day is 22,abbreviated as 22nd,suffix is nd ,is not th
when day is 23,abbreviated as 23rd,suffix is rd ,is not th
when day is 31,abbreviated as 31st,suffix is st ,is not th
2021-01-31 15:40:27 +08:00
3e33d66ab4 fix issue https://github.com/gogf/gf/issues/1148 2021-01-30 23:05:02 +08:00
13248d6736 Merge pull request #1149 from tangjoin/master
Update client_request.go
2021-01-30 22:57:14 +08:00
8bd24724e7 Update client_request.go 2021-01-30 22:10:06 +08:00
80248e9a6e improve tracing 2021-01-28 14:09:13 +08:00
2734903886 tracing baggage 2021-01-28 13:51:23 +08:00
2451b40d3e improve package gtrace 2021-01-28 13:11:09 +08:00
136 changed files with 6670 additions and 2739 deletions

View File

@ -1,7 +1,7 @@
# GoFrame
[![Go Doc](https://godoc.org/github.com/gogf/gf?status.svg)](https://godoc.org/github.com/gogf/gf)
[![Build Status](https://travis-ci.org/gogf/gf.svg?branch=master)](https://travis-ci.org/gogf/gf)
[![Build Status](https://travis-ci.com/gogf/gf.svg?branch=master)](https://travis-ci.org/gogf/gf)
[![Go Report](https://goreportcard.com/badge/github.com/gogf/gf?v=1)](https://goreportcard.com/report/github.com/gogf/gf)
[![Code Coverage](https://codecov.io/gh/gogf/gf/branch/master/graph/badge.svg)](https://codecov.io/gh/gogf/gf/branch/master)
[![Production Ready](https://img.shields.io/badge/production-ready-blue.svg)](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>

View File

@ -1,6 +1,6 @@
# GoFrame
[![Go Doc](https://godoc.org/github.com/gogf/gf?status.svg)](https://godoc.org/github.com/gogf/gf)
[![Build Status](https://travis-ci.org/gogf/gf.svg?branch=master)](https://travis-ci.org/gogf/gf)
[![Build Status](https://travis-ci.com/gogf/gf.svg?branch=master)](https://travis-ci.org/gogf/gf)
[![Go Report](https://goreportcard.com/badge/github.com/gogf/gf?v=1)](https://goreportcard.com/report/github.com/gogf/gf)
[![Code Coverage](https://codecov.io/gh/gogf/gf/branch/master/graph/badge.svg)](https://codecov.io/gh/gogf/gf/branch/master)
[![Production Ready](https://img.shields.io/badge/production-ready-blue.svg)](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>

View File

@ -7,5 +7,4 @@
// Package gtree provides concurrent-safe/unsafe tree containers.
//
// Some implements are from: https://github.com/emirpasic/gods
// Thanks!
package gtree

View File

@ -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",

View File

@ -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
}

View File

@ -30,21 +30,21 @@ import (
// a global or package variable for long using.
func (c *Core) Ctx(ctx context.Context) DB {
if ctx == nil {
return c.DB
return c.db
}
var (
err error
newCore = &Core{}
configNode = c.DB.GetConfig()
configNode = c.db.GetConfig()
)
*newCore = *c
newCore.ctx = ctx
newCore.DB, err = driverMap[configNode.Type].New(newCore, configNode)
newCore.db, err = driverMap[configNode.Type].New(newCore, configNode)
// Seldom error, just log it.
if err != nil {
c.DB.GetLogger().Ctx(ctx).Error(err)
c.db.GetLogger().Ctx(ctx).Error(err)
}
return newCore.DB
return newCore.db
}
// GetCtx returns the context for current DB.
@ -59,22 +59,22 @@ func (c *Core) GetCtx() context.Context {
// GetCtxTimeout returns the context and cancel function for specified timeout type.
func (c *Core) GetCtxTimeout(timeoutType int, ctx context.Context) (context.Context, context.CancelFunc) {
if ctx == nil {
ctx = c.DB.GetCtx()
ctx = c.db.GetCtx()
} else {
ctx = context.WithValue(ctx, "WrappedByGetCtxTimeout", nil)
}
switch timeoutType {
case ctxTimeoutTypeExec:
if c.DB.GetConfig().ExecTimeout > 0 {
return context.WithTimeout(ctx, c.DB.GetConfig().ExecTimeout)
if c.db.GetConfig().ExecTimeout > 0 {
return context.WithTimeout(ctx, c.db.GetConfig().ExecTimeout)
}
case ctxTimeoutTypeQuery:
if c.DB.GetConfig().QueryTimeout > 0 {
return context.WithTimeout(ctx, c.DB.GetConfig().QueryTimeout)
if c.db.GetConfig().QueryTimeout > 0 {
return context.WithTimeout(ctx, c.db.GetConfig().QueryTimeout)
}
case ctxTimeoutTypePrepare:
if c.DB.GetConfig().PrepareTimeout > 0 {
return context.WithTimeout(ctx, c.DB.GetConfig().PrepareTimeout)
if c.db.GetConfig().PrepareTimeout > 0 {
return context.WithTimeout(ctx, c.db.GetConfig().PrepareTimeout)
}
default:
panic(gerror.Newf("invalid context timeout type: %d", timeoutType))
@ -97,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
}

View File

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

View File

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

View File

@ -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...)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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)
}

View File

@ -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
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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)
}

View File

@ -28,7 +28,7 @@ func (m *Model) Select(where ...interface{}) (Result, error) {
// It retrieves the records from table and returns the result as slice type.
// It returns nil if there's no record retrieved with the given conditions from table.
//
// The optional parameter <where> is the same as the parameter of Model.Where function,
// The optional parameter `where` is the same as the parameter of Model.Where function,
// see Model.Where.
func (m *Model) All(where ...interface{}) (Result, error) {
return m.doGetAll(false, where...)
@ -38,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)

View File

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

View File

@ -18,7 +18,7 @@ import (
"time"
)
// getModel creates and returns a cloned model of current model if <safe> is true, or else it returns
// getModel creates and returns a cloned model of current model if `safe` is true, or else it returns
// the current model.
func (m *Model) getModel() *Model {
if !m.safe {
@ -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))

View 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
}

View File

@ -16,7 +16,7 @@ type Schema struct {
// Schema creates and returns a schema.
func (c *Core) Schema(schema string) *Schema {
return &Schema{
db: c.DB,
db: c.db,
schema: schema,
}
}
@ -31,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.

View File

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

View File

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

View File

@ -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
}

View File

@ -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)
}

View File

@ -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())

View File

@ -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)
})

View File

@ -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

View 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)
})
}

View File

@ -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()

View File

@ -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()

View File

@ -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)
})
}

View File

@ -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

View File

@ -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)
})
}

View File

@ -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)
})
}

View File

@ -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)
})
}

View File

@ -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")

View File

@ -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"])

View File

@ -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
}

View File

@ -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 {

View File

@ -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 {

View File

@ -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++
}
}

View File

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

View File

@ -80,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
View File

@ -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
View 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 ""
}

View File

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

View File

@ -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

View File

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

View File

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

View File

@ -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.
}

View File

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

View File

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

View 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
}

View File

@ -102,7 +102,7 @@ func Test_StructOfNilPointer(t *testing.T) {
})
}
func Test_MapField(t *testing.T) {
func Test_FieldMap(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
type User struct {
Id int
@ -110,7 +110,7 @@ func Test_MapField(t *testing.T) {
Pass string `my-tag1:"pass1" my-tag2:"pass2" params:"pass"`
}
var user *User
m, _ := structs.MapField(user, []string{"params"})
m, _ := structs.FieldMap(user, []string{"params"})
t.Assert(len(m), 3)
_, ok := m["Id"]
t.Assert(ok, true)
@ -123,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"})
})
}

View 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
}

View File

@ -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)
}

View File

@ -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),
))

View File

@ -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))

View File

@ -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")
})
}

View File

@ -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
}

View File

@ -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 ""
}

View File

@ -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
}

View File

@ -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

View File

@ -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(

View File

@ -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),
))
}

View File

@ -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)
}

View 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())
}

View 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
View 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,
}
}

View 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),
}
}

View 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")
})
}

View File

@ -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...))
}

View File

@ -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

View File

@ -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()
}

View File

@ -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

View File

@ -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
View File

@ -0,0 +1 @@
{"my-config": 2}

1
os/gcfg/testdata/default/config.toml vendored Normal file
View File

@ -0,0 +1 @@
my-config = "1"

View File

@ -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.

View File

@ -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)
})
}

View File

@ -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".

View File

@ -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()

View File

@ -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)
}

View File

@ -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

View File

@ -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)

View File

@ -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:

View File

@ -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"

View File

@ -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) {

View File

@ -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