Compare commits

...

131 Commits

Author SHA1 Message Date
6a80091fef version updates 2021-05-03 00:00:29 +08:00
742653ce75 improve Model function for struct parameter that can retrieve table name tag from 2021-05-02 23:28:24 +08:00
a8c3d07d9f improve with feature for package gdb 2021-05-02 22:35:47 +08:00
df1ef5db78 add example for package gdb 2021-05-02 17:57:00 +08:00
4b15ab5e99 improve OrderRandom function for package gdb 2021-05-02 16:42:34 +08:00
cdc97e9b2b improve logging for transaction feature for package gdb 2021-05-02 15:58:28 +08:00
bd84b97614 add more Where*/Min/Max/Avg/Sum/CountColumn/Increment/Decrement/OrderAsc/OrderDesc/OrderRandom functions and associated unit testing cases for package gdb 2021-05-02 12:17:06 +08:00
d7eb1cca07 add nested transaction feature for package gdb 2021-05-02 09:35:54 +08:00
5856f74d83 up 2021-05-02 08:10:35 +08:00
563509c4a6 route map dumping updates 2021-05-01 09:04:16 +08:00
d0753fa527 Merge branch 'develop' 2021-04-26 20:38:16 +08:00
524e3bedc7 improve empty value validation for required-with* patterns for package gvalid 2021-04-26 20:37:36 +08:00
30b60754e3 README update 2021-04-16 10:32:55 +08:00
88c49dc2cb README update 2021-04-16 10:27:39 +08:00
bb3dcc2622 Merge branch 'master' of https://github.com/gogf/gf 2021-04-16 10:22:02 +08:00
5cca0640d9 README update 2021-04-16 10:21:42 +08:00
fb48ceeeee Merge pull request #1214 from ansionfor/add-graceful-timeout
add graceful reload timeout config
2021-04-10 08:23:54 +08:00
90f4bba8fd gofmt格式化 2021-04-07 20:52:38 +08:00
802187abc4 add graceful reload timeout 2021-04-07 16:06:37 +08:00
49dd17c047 remove maxIdleTime feature due to be compatible with old Golang version <= v1.14 2021-04-04 12:13:51 +08:00
a3b94c24de fix issue #854; add maxIdleTime configuration and comments update for package gdb; version updates 2021-04-04 12:01:22 +08:00
ac4485dc84 comment update for package gsession 2021-04-02 18:00:50 +08:00
4d1e25cdab change internal constant varibale names for package gqueue 2021-04-02 16:16:54 +08:00
ec00e99477 comments update for package gtime 2021-04-01 09:41:14 +08:00
a73dca3e50 Merge pull request #1196 from yanllllk/yys 2021-04-01 09:27:59 +08:00
yys
ed71d2b2ac add test 2021-03-31 14:07:28 +08:00
3911c2e0a2 README updates 2021-03-30 18:00:44 +08:00
1b6765db50 README updates 2021-03-30 17:44:57 +08:00
345fb69521 README updates 2021-03-30 17:43:44 +08:00
83a6abc70f README updates 2021-03-30 17:42:00 +08:00
cfef726205 README updates 2021-03-30 17:41:10 +08:00
de0f9e728c README updates 2021-03-30 17:39:56 +08:00
d9754a532b version updates 2021-03-30 17:20:13 +08:00
dc51023c61 upgrade github.com/olekukonko/tablewriter from 0.0.1 to 0.0.5 2021-03-30 16:54:52 +08:00
87ce5c5419 Merge branch 'master' of https://github.com/gogf/gf 2021-03-30 16:48:19 +08:00
c4fc9f9618 fix issue #1204: improve validation support for time value, eg: gtime.Time/*gtime.Time, time.Time/*time.Time. 2021-03-30 16:06:20 +08:00
8d7aa5db83 Merge pull request #1205 from Zh1Cheung/master
bug:The parameter of the New method is a pointer
2021-03-30 15:39:31 +08:00
9c70fe3be8 add more unit testing case for package gvalid 2021-03-30 15:25:26 +08:00
78027d2ec6 deprecated comments update 2021-03-30 13:43:08 +08:00
d4e4b9addf improve package gcfg for automatically checking and adding path of package main 2021-03-29 18:05:47 +08:00
b2acd22f58 improve package gi18n 2021-03-29 17:36:51 +08:00
ae5ecb5bfa update unit testing case for package gtrace 2021-03-29 16:12:14 +08:00
6f1340ce36 upgrade otel from v0.18.0 to v0.19.0 2021-03-29 16:00:56 +08:00
473fdba14f add replace in go.mod for otel 2021-03-29 15:51:03 +08:00
93a01a1aaf bug:The parameter of the New method is a pointer 2021-03-27 16:21:28 +08:00
yys
7c51ff4707 gtime 修改 2021-03-25 13:38:59 +08:00
bb6883213f fix issue #1202 2021-03-24 22:04:04 +08:00
54395b39a6 fix issue #1202 2021-03-24 21:19:23 +08:00
6deb817fd0 fix issue in unit testing case for package gpage 2021-03-23 17:58:09 +08:00
482e093331 add map/[]map converting support for gconv.Scan;improve gconv.MaptoMaps 2021-03-23 17:53:20 +08:00
042f903157 Merge branch 'master' of https://github.com/gogf/gf 2021-03-23 14:23:47 +08:00
c423ad4830 fix issue in gfile.MainPkgPath;improve fields filter for package gdb 2021-03-23 14:21:54 +08:00
958b109a12 fix removing cookie issue 2021-03-19 19:16:21 +08:00
113fffdd69 Merge pull request #1199 from yangyanqing/yangyanqing/fix-zte-chinese 2021-03-19 17:08:31 +08:00
4aaf09fded improve gfile.MainPkgPath 2021-03-19 16:35:55 +08:00
2f3df76f37 fix issue #1190 2021-03-19 15:37:33 +08:00
cf7706b16d ZTE 的中文名应该是“中兴通讯”,不是“中兴科技” 2021-03-19 11:45:17 +08:00
36963c05a2 fix issue #1190 2021-03-19 11:35:12 +08:00
48d840a1b8 ad go.sum 2021-03-18 15:29:42 +08:00
e3ebc908bb improve package gconv 2021-03-18 15:21:05 +08:00
9ed8d8c113 rename max content log size name for gtrace from maxlogsize to maxcontentlogsize 2021-03-18 11:22:44 +08:00
9eab5daf19 update required minimum go version from 1.11 to 1.14 2021-03-18 11:19:36 +08:00
6a24b595f0 upgrade otel from 0.16.0 to 0.18.0 2021-03-18 10:39:23 +08:00
150f237f13 fix issue in incorrect parameter sequence in package gi18n;improve package gcfg for detailed error printing 2021-03-16 14:39:01 +08:00
yys
17233084f1 为gtime添加scanner和valuer 2021-03-13 14:07:22 +08:00
41f2138b39 fix issue of overflow in grand.D 2021-03-11 23:29:39 +08:00
6376b8aaa6 remove session storage file removing feature for package gsession 2021-03-11 21:21:47 +08:00
58362ad143 add grand.D for random time.Duration;add checking and removing session files for package gsession 2021-03-11 20:05:08 +08:00
d72d23c2eb change browser mode from boolean markable variable to cookiejar for ghttp.Client 2021-03-11 18:58:13 +08:00
7702c5bfde add internal logging for package gdb/gredis 2021-03-11 11:39:49 +08:00
20f2a6c003 add recursive validation feature of struct attribute for package gvalid for #1165 2021-03-10 23:28:34 +08:00
0d4c1c47d5 improve package grand 2021-03-10 21:19:11 +08:00
4d32733790 improve ghttp.Client for #1179 2021-03-09 22:54:38 +08:00
0e58b6e95b README updates 2021-03-08 23:27:48 +08:00
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
9fb6227461 fix issue in signal handler for windows 2021-01-28 10:08:52 +08:00
203 changed files with 9783 additions and 4224 deletions

View File

@ -0,0 +1,9 @@
package main
import (
"github.com/gogf/gf/frame/g"
)
func main() {
g.DB().Model("user").Distinct().CountColumn("uid,name")
}

View File

@ -0,0 +1,27 @@
package main
import (
"github.com/gogf/gf/frame/g"
)
func main() {
var (
db = g.DB()
table = "user"
)
tx, err := db.Begin()
if err != nil {
panic(err)
}
if err = tx.Begin(); err != nil {
panic(err)
}
_, err = tx.Model(table).Data(g.Map{"id": 1, "name": "john"}).Insert()
if err = tx.Rollback(); err != nil {
panic(err)
}
_, err = tx.Model(table).Data(g.Map{"id": 2, "name": "smith"}).Insert()
if err = tx.Commit(); err != nil {
panic(err)
}
}

View File

@ -0,0 +1,34 @@
package main
import (
"github.com/gogf/gf/database/gdb"
"github.com/gogf/gf/frame/g"
)
func main() {
var (
err error
db = g.DB()
table = "user"
)
if err = db.Transaction(func(tx *gdb.TX) error {
// Nested transaction 1.
if err = tx.Transaction(func(tx *gdb.TX) error {
_, err = tx.Model(table).Data(g.Map{"id": 1, "name": "john"}).Insert()
return err
}); err != nil {
return err
}
// Nested transaction 2, panic.
if err = tx.Transaction(func(tx *gdb.TX) error {
_, err = tx.Model(table).Data(g.Map{"id": 2, "name": "smith"}).Insert()
// Create a panic that can make this transaction rollback automatically.
panic("error")
}); err != nil {
return err
}
return nil
}); err != nil {
panic(err)
}
}

View File

@ -0,0 +1,40 @@
package main
import (
"github.com/gogf/gf/frame/g"
)
func main() {
var (
err error
db = g.DB()
table = "user"
)
tx, err := db.Begin()
if err != nil {
panic(err)
}
defer func() {
if err := recover(); err != nil {
_ = tx.Rollback()
}
}()
if _, err = tx.Model(table).Data(g.Map{"id": 1, "name": "john"}).Insert(); err != nil {
panic(err)
}
if err = tx.SavePoint("MyPoint"); err != nil {
panic(err)
}
if _, err = tx.Model(table).Data(g.Map{"id": 2, "name": "smith"}).Insert(); err != nil {
panic(err)
}
if _, err = tx.Model(table).Data(g.Map{"id": 3, "name": "green"}).Insert(); err != nil {
panic(err)
}
if err = tx.RollbackTo("MyPoint"); err != nil {
panic(err)
}
if err = tx.Commit(); err != nil {
panic(err)
}
}

View File

@ -0,0 +1,66 @@
package main
import (
"fmt"
"github.com/gogf/gf/database/gdb"
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/util/gmeta"
)
func main() {
type UserDetail struct {
gmeta.Meta `orm:"table:user_detail"`
Uid int `json:"uid"`
Address string `json:"address"`
}
type UserScore struct {
gmeta.Meta `orm:"table:user_score"`
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 []*UserScore `orm:"with:uid=id"`
}
db := g.DB()
db.Transaction(func(tx *gdb.TX) error {
for i := 1; i <= 5; i++ {
// User.
user := User{
Name: fmt.Sprintf(`name_%d`, i),
}
lastInsertId, err := db.Model(user).Data(user).OmitEmpty().InsertAndGetId()
if err != nil {
return err
}
// Detail.
userDetail := UserDetail{
Uid: int(lastInsertId),
Address: fmt.Sprintf(`address_%d`, lastInsertId),
}
_, err = db.Model(userDetail).Data(userDetail).OmitEmpty().Insert()
if err != nil {
return err
}
// Scores.
for j := 1; j <= 5; j++ {
userScore := UserScore{
Uid: int(lastInsertId),
Score: j,
}
_, err = db.Model(userScore).Data(userScore).OmitEmpty().Insert()
if err != nil {
return err
}
}
}
return nil
})
}

View File

@ -0,0 +1,37 @@
package main
import (
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/util/gmeta"
)
func main() {
type UserDetail struct {
gmeta.Meta `orm:"table:user_detail"`
Uid int `json:"uid"`
Address string `json:"address"`
}
type UserScore struct {
gmeta.Meta `orm:"table:user_score"`
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 []*UserScore `orm:"with:uid=id"`
}
db := g.DB()
var user *User
err := db.Model(user).WithAll().Where("id", 3).Scan(&user)
if err != nil {
panic(err)
}
g.Dump(user)
}

View File

@ -9,10 +9,11 @@ import (
// 使用原生gredis.New操作redis但是注意需要自己调用Close方法关闭redis链接池
func main() {
redis := gredis.New(gredis.Config{
config := &gredis.Config{
Host: "127.0.0.1",
Port: 6379,
})
}
redis := gredis.New(config)
defer redis.Close()
redis.Do("SET", "k", "v")
v, _ := redis.Do("GET", "k")

View File

@ -8,7 +8,6 @@ import (
func main() {
s := g.Server()
s.SetSessionCookieMaxAge(0)
s.Group("/", func(group *ghttp.RouterGroup) {
group.GET("/set", func(r *ghttp.Request) {
r.Session.Set("time", gtime.Timestamp())

1
.gitignore vendored
View File

@ -14,5 +14,4 @@ bin/
cbuild
**/.DS_Store
.vscode/
go.sum
.example/other/

View File

@ -4,11 +4,9 @@ arch: arm64-graviton2
language: go
go:
- "1.11.x"
- "1.12.x"
- "1.13.x"
- "1.14.x"
- "1.15.x"
- "1.16.x"
branches:
only:

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)
@ -9,11 +9,8 @@
English | [简体中文](README_ZH.MD)
`GF(GoFrame)` is a modular, powerful, high-performance and enterprise-class application development framework
of Golang. Providing a series of core components and dozens of practical modules, such as:
cache, logging, containers, timer, resource, validator, database orm, etc.
Supporting web server integrated with router, cookie, session, middleware, logger, configure,
template, https, hooks, rewrites and many more features.
`GoFrame` is a modular, powerful, high-performance and enterprise-class application development framework
of Golang.
> If you're a newbie to `Go`, you may consider `GoFrame` easy and great as `Laravel` in `PHP`, `SpringBoot` in `Java` or `Django` in `Python`.
@ -33,7 +30,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
@ -53,17 +50,10 @@ The `Web` component performance of `GoFrame`, please refer to third-party projec
# Documentation
* 中文官网: https://goframe.org
* GoDoc API: https://godoc.org/github.com/gogf/gf
* 中文官网: [https://goframe.org](https://goframe.org/display/gf)
* GoDoc API: [https://pkg.go.dev/github.com/gogf/gf](https://pkg.go.dev/github.com/gogf/gf)
# Discussion
- QQ Group[116707870](//shang.qq.com/wpa/qunwpa?idkey=195f91eceeb5d7fa76009b7cd5a4641f70bf4897b7f5a520635eb26ff17adfe7)
- WX GroupAdd friend`389961817` in WeChat, commenting `GF`
- Issueshttps://github.com/gogf/gf/issues
> It's recommended learning `GoFrame` through its awesome source codes and API reference.
# License
`GF` is licensed under the [MIT License](LICENSE), 100% free and open-source, forever.
@ -80,7 +70,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 +80,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 +88,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)
@ -8,24 +8,21 @@
[English](README.MD) | 简体中文
`GF(Go Frame)`是一款模块化、高性能、企业级的Go基础开发框架。
实现了比较完善的基础设施建设以及开发工具链,提供了常用的基础开发模块,
如:缓存、日志、队列、数组、集合、容器、定时器、命令行、内存锁、对象池、
配置管理、资源管理、数据校验、数据编码、定时任务、数据库ORM、TCP/UDP组件、进程管理/通信等等。
并提供了Web服务开发的系列核心组件Router、Cookie、Session、Middleware、服务注册、模板引擎等等
支持热重启、热更新、域名绑定、TLS/HTTPS、Rewrite等特性。
`GoFrame`是一款模块化、高性能、企业级的Go基础开发框架。
> 如果您初识`Go`语言,您可以将`GoFrame`类似于`PHP`中的`Laravel`, `Java`中的`SpringBoot`或者`Python`中的`Django`。
# 特点
* 模块化、松耦合设计;
* 模块丰富、开箱即用
* 简便易用、易于维护;
* 高代码质量、高单元测试覆盖率;
* 社区活跃,大牛谦逊低调脾气好;
* 详尽的开发文档及示例;
* 完善的本地中文化支持;
* 设计为团队及企业使用;
* 模块化、松耦合
* 模块丰富、开箱即用
* 简洁易用、快速接入
* 文档详尽、易于维护
* 自顶向下、体系化设计
* 统一框架、统一组件、降低选择成本
* 开发规范、设计模式、代码分层模型
* 强大便捷的开发工具链
* 完善的本地中文化支持
* 设计为团队及企业使用
# 地址
- **主库**https://github.com/gogf/gf
@ -49,7 +46,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>
# 模块
@ -68,16 +65,10 @@ golang版本 >= 1.11
# 文档
开发文档:https://goframe.org
官方站点:[https://goframe.org](https://goframe.org/display/gf)
接口文档https://godoc.org/github.com/gogf/gf
接口文档:[https://pkg.go.dev/github.com/gogf/gf](https://pkg.go.dev/github.com/gogf/gf)
# 帮助
- QQ交流群[116707870](//shang.qq.com/wpa/qunwpa?idkey=195f91eceeb5d7fa76009b7cd5a4641f70bf4897b7f5a520635eb26ff17adfe7)
- WX交流群微信添加`389961817`备注`GF`
- 主库ISSUEhttps://github.com/gogf/gf/issues
> 建议通过阅读`GoFrame`的源码以及API文档深度学习`GoFrame`,了解更多的精妙设计。
# 协议
@ -86,7 +77,7 @@ golang版本 >= 1.11
# 用户
- [腾讯科技](https://www.tencent.com/)
- [中兴科技](https://www.zte.com.cn/china/)
- [中兴通讯](https://www.zte.com.cn/china/)
- [蚂蚁金服](https://www.antfin.com/)
- [医联科技](https://www.medlinker.com/)
- [库币科技](https://www.kucoin.io/)
@ -95,7 +86,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 +96,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 +104,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

@ -35,10 +35,8 @@ type Queue struct {
}
const (
// Size for queue buffer.
gDEFAULT_QUEUE_SIZE = 10000
// Max batch size per-fetching from list.
gDEFAULT_MAX_BATCH_SIZE = 10
defaultQueueSize = 10000 // Size for queue buffer.
defaultBatchSize = 10 // Max batch size per-fetching from list.
)
// New returns an empty queue object.
@ -54,7 +52,7 @@ func New(limit ...int) *Queue {
} else {
q.list = glist.New(true)
q.events = make(chan struct{}, math.MaxInt32)
q.C = make(chan interface{}, gDEFAULT_QUEUE_SIZE)
q.C = make(chan interface{}, defaultQueueSize)
go q.asyncLoopFromListToChannel()
}
return q
@ -72,8 +70,8 @@ func (q *Queue) asyncLoopFromListToChannel() {
<-q.events
for !q.closed.Val() {
if length := q.list.Len(); length > 0 {
if length > gDEFAULT_MAX_BATCH_SIZE {
length = gDEFAULT_MAX_BATCH_SIZE
if length > defaultBatchSize {
length = defaultBatchSize
}
for _, v := range q.list.PopFronts(length) {
// When q.C is closed, it will panic here, especially q.C is being blocked for writing.
@ -101,7 +99,7 @@ func (q *Queue) Push(v interface{}) {
q.C <- v
} else {
q.list.PushBack(v)
if len(q.events) < gDEFAULT_QUEUE_SIZE {
if len(q.events) < defaultQueueSize {
q.events <- struct{}{}
}
}
@ -124,7 +122,7 @@ func (q *Queue) Close() {
if q.limit > 0 {
close(q.C)
}
for i := 0; i < gDEFAULT_MAX_BATCH_SIZE; i++ {
for i := 0; i < defaultBatchSize; i++ {
q.Pop()
}
}

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

@ -71,13 +71,6 @@ func (v *Var) MapToMap(pointer interface{}, mapping ...map[string]string) (err e
return gconv.MapToMap(v.Val(), pointer, mapping...)
}
// MapToMapDeep converts any map type variable <params> to another map type variable
// <pointer> recursively.
// See gconv.MapToMapDeep.
func (v *Var) MapToMapDeep(pointer interface{}, mapping ...map[string]string) (err error) {
return gconv.MapToMapDeep(v.Val(), pointer, mapping...)
}
// MapToMaps converts any map type variable <params> to another map type variable <pointer>.
// See gconv.MapToMaps.
func (v *Var) MapToMaps(pointer interface{}, mapping ...map[string]string) (err error) {

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

@ -32,155 +32,179 @@ type DB interface {
// Model creation.
// ===========================================================================
// Table function is deprecated, use Model instead.
// 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.
Table(table ...string) *Model
Model(table ...string) *Model
// Also see Core.Table.
Table(tableNameOrStruct ...interface{}) *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")
// Also see Core.Model.
Model(tableNameOrStruct ...interface{}) *Model
// Schema creates and returns a schema.
// Also see Core.Schema.
Schema(schema string) *Schema
// With creates and returns an ORM model based on meta data of given object.
// Also see Core.With.
With(objects ...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.
// Also see DriverMysql.Open.
Open(config *ConfigNode) (*sql.DB, error)
// Ctx is a chaining function, which creates and returns a new DB that is a shallow copy
// of current DB object and with given context in it.
// Note that this returned DB object can be used only once, so do not assign it to
// a global or package variable for long using.
// Also see Core.Ctx.
Ctx(ctx context.Context) DB
// ===========================================================================
// Query APIs.
// ===========================================================================
Query(sql string, args ...interface{}) (*sql.Rows, error)
Exec(sql string, args ...interface{}) (sql.Result, error)
Prepare(sql string, execOnMaster ...bool) (*Stmt, error)
Query(sql string, args ...interface{}) (*sql.Rows, error) // See Core.Query.
Exec(sql string, args ...interface{}) (sql.Result, error) // See Core.Exec.
Prepare(sql string, execOnMaster ...bool) (*Stmt, error) // See Core.Prepare.
// ===========================================================================
// Common APIs for CURD.
// ===========================================================================
Insert(table string, data interface{}, batch ...int) (sql.Result, error)
InsertIgnore(table string, data interface{}, batch ...int) (sql.Result, error)
Replace(table string, data interface{}, batch ...int) (sql.Result, error)
Save(table string, data interface{}, batch ...int) (sql.Result, error)
Insert(table string, data interface{}, batch ...int) (sql.Result, error) // See Core.Insert.
InsertIgnore(table string, data interface{}, batch ...int) (sql.Result, error) // See Core.InsertIgnore.
Replace(table string, data interface{}, batch ...int) (sql.Result, error) // See Core.Replace.
Save(table string, data interface{}, batch ...int) (sql.Result, error) // See Core.Save.
BatchInsert(table string, list interface{}, batch ...int) (sql.Result, error)
BatchReplace(table string, list interface{}, batch ...int) (sql.Result, error)
BatchSave(table string, list interface{}, batch ...int) (sql.Result, error)
BatchInsert(table string, list interface{}, batch ...int) (sql.Result, error) // See Core.BatchInsert.
BatchReplace(table string, list interface{}, batch ...int) (sql.Result, error) // See Core.BatchReplace.
BatchSave(table string, list interface{}, batch ...int) (sql.Result, error) // See Core.BatchSave.
Update(table string, data interface{}, condition interface{}, args ...interface{}) (sql.Result, error)
Delete(table string, condition interface{}, args ...interface{}) (sql.Result, error)
Update(table string, data interface{}, condition interface{}, args ...interface{}) (sql.Result, error) // See Core.Update.
Delete(table string, condition interface{}, args ...interface{}) (sql.Result, error) // See Core.Delete.
// ===========================================================================
// Internal APIs for CURD, which can be overwrote for custom CURD implements.
// ===========================================================================
DoQuery(link Link, sql string, args ...interface{}) (rows *sql.Rows, err error)
DoGetAll(link Link, sql string, args ...interface{}) (result Result, err error)
DoExec(link Link, sql string, args ...interface{}) (result sql.Result, err error)
DoPrepare(link Link, sql string) (*Stmt, error)
DoInsert(link Link, table string, data interface{}, option int, batch ...int) (result sql.Result, err error)
DoBatchInsert(link Link, table string, list interface{}, option int, batch ...int) (result sql.Result, err error)
DoUpdate(link Link, table string, data interface{}, condition string, args ...interface{}) (result sql.Result, err error)
DoDelete(link Link, table string, condition string, args ...interface{}) (result sql.Result, err error)
DoQuery(link Link, sql string, args ...interface{}) (rows *sql.Rows, err error) // See Core.DoQuery.
DoGetAll(link Link, sql string, args ...interface{}) (result Result, err error) // See Core.DoGetAll.
DoExec(link Link, sql string, args ...interface{}) (result sql.Result, err error) // See Core.DoExec.
DoPrepare(link Link, sql string) (*Stmt, error) // See Core.DoPrepare.
DoInsert(link Link, table string, data interface{}, option int, batch ...int) (result sql.Result, err error) // See Core.DoInsert.
DoBatchInsert(link Link, table string, list interface{}, option int, batch ...int) (result sql.Result, err error) // See Core.DoBatchInsert.
DoUpdate(link Link, table string, data interface{}, condition string, args ...interface{}) (result sql.Result, err error) // See Core.DoUpdate.
DoDelete(link Link, table string, condition string, args ...interface{}) (result sql.Result, err error) // See Core.DoDelete.
// ===========================================================================
// Query APIs for convenience purpose.
// ===========================================================================
GetAll(sql string, args ...interface{}) (Result, error)
GetOne(sql string, args ...interface{}) (Record, error)
GetValue(sql string, args ...interface{}) (Value, error)
GetArray(sql string, args ...interface{}) ([]Value, error)
GetCount(sql string, args ...interface{}) (int, error)
GetStruct(objPointer interface{}, sql string, args ...interface{}) error
GetStructs(objPointerSlice interface{}, sql string, args ...interface{}) error
GetScan(objPointer interface{}, sql string, args ...interface{}) error
GetAll(sql string, args ...interface{}) (Result, error) // See Core.GetAll.
GetOne(sql string, args ...interface{}) (Record, error) // See Core.GetOne.
GetValue(sql string, args ...interface{}) (Value, error) // See Core.GetValue.
GetArray(sql string, args ...interface{}) ([]Value, error) // See Core.GetArray.
GetCount(sql string, args ...interface{}) (int, error) // See Core.GetCount.
GetStruct(objPointer interface{}, sql string, args ...interface{}) error // See Core.GetStruct.
GetStructs(objPointerSlice interface{}, sql string, args ...interface{}) error // See Core.GetStructs.
GetScan(objPointer interface{}, sql string, args ...interface{}) error // See Core.GetScan.
// ===========================================================================
// Master/Slave specification support.
// ===========================================================================
Master() (*sql.DB, error)
Slave() (*sql.DB, error)
Master() (*sql.DB, error) // See Core.Master.
Slave() (*sql.DB, error) // See Core.Slave.
// ===========================================================================
// Ping-Pong.
// ===========================================================================
PingMaster() error
PingSlave() error
PingMaster() error // See Core.PingMaster.
PingSlave() error // See Core.PingSlave.
// ===========================================================================
// Transaction.
// ===========================================================================
Begin() (*TX, error)
Transaction(f func(tx *TX) error) (err error)
Begin() (*TX, error) // See Core.Begin.
Transaction(f func(tx *TX) error) (err error) // See Core.Transaction.
// ===========================================================================
// Configuration methods.
// ===========================================================================
GetCache() *gcache.Cache
SetDebug(debug bool)
GetDebug() bool
SetSchema(schema string)
GetSchema() string
GetPrefix() string
GetGroup() string
SetDryRun(dryrun bool)
GetDryRun() bool
SetLogger(logger *glog.Logger)
GetLogger() *glog.Logger
GetConfig() *ConfigNode
SetMaxIdleConnCount(n int)
SetMaxOpenConnCount(n int)
SetMaxConnLifetime(d time.Duration)
GetCache() *gcache.Cache // See Core.GetCache.
SetDebug(debug bool) // See Core.SetDebug.
GetDebug() bool // See Core.GetDebug.
SetSchema(schema string) // See Core.SetSchema.
GetSchema() string // See Core.GetSchema.
GetPrefix() string // See Core.GetPrefix.
GetGroup() string // See Core.GetGroup.
SetDryRun(enabled bool) // See Core.SetDryRun.
GetDryRun() bool // See Core.GetDryRun.
SetLogger(logger *glog.Logger) // See Core.SetLogger.
GetLogger() *glog.Logger // See Core.GetLogger.
GetConfig() *ConfigNode // See Core.GetConfig.
SetMaxIdleConnCount(n int) // See Core.SetMaxIdleConnCount.
SetMaxOpenConnCount(n int) // See Core.SetMaxOpenConnCount.
SetMaxConnLifeTime(d time.Duration) // See Core.SetMaxConnLifeTime.
// ===========================================================================
// Utility methods.
// ===========================================================================
GetCtx() context.Context
GetChars() (charLeft string, charRight string)
GetMaster(schema ...string) (*sql.DB, error)
GetSlave(schema ...string) (*sql.DB, error)
QuoteWord(s string) string
QuoteString(s string) string
QuotePrefixTableName(table string) string
Tables(schema ...string) (tables []string, err error)
TableFields(table string, schema ...string) (map[string]*TableField, error)
HasTable(name string) (bool, error)
FilteredLinkInfo() string
GetCtx() context.Context // See Core.GetCtx.
GetChars() (charLeft string, charRight string) // See Core.GetChars.
GetMaster(schema ...string) (*sql.DB, error) // See Core.GetMaster.
GetSlave(schema ...string) (*sql.DB, error) // See Core.GetSlave.
QuoteWord(s string) string // See Core.QuoteWord.
QuoteString(s string) string // See Core.QuoteString.
QuotePrefixTableName(table string) string // See Core.QuotePrefixTableName.
Tables(schema ...string) (tables []string, err error) // See Core.Tables.
TableFields(link Link, table string, schema ...string) (map[string]*TableField, error) // See Core.TableFields.
HasTable(name string) (bool, error) // See Core.HasTable.
FilteredLinkInfo() string // See Core.FilteredLinkInfo.
// 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.
// Also see Core.HandleSqlBeforeCommit.
HandleSqlBeforeCommit(link Link, sql string, args []interface{}) (string, []interface{})
// ===========================================================================
// Internal methods, for internal usage purpose, you do not need consider it.
// ===========================================================================
mappingAndFilterData(schema, table string, data map[string]interface{}, filter bool) (map[string]interface{}, error)
convertFieldValueToLocalValue(fieldValue interface{}, fieldType string) interface{}
convertRowsToResult(rows *sql.Rows) (Result, error)
mappingAndFilterData(schema, table string, data map[string]interface{}, filter bool) (map[string]interface{}, error) // See Core.mappingAndFilterData.
convertFieldValueToLocalValue(fieldValue interface{}, fieldType string) interface{} // See Core.convertFieldValueToLocalValue.
convertRowsToResult(rows *sql.Rows) (Result, error) // See Core.convertRowsToResult.
addSqlToTracing(ctx context.Context, sql *Sql) // See Core.addSqlToTracing.
writeSqlToLogger(v *Sql) // See Core.writeSqlToLogger.
}
// 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.
@ -284,6 +308,9 @@ var (
// internalCache is the memory cache for internal usage.
internalCache = gcache.New()
// tableFieldsMap caches the table information retrived from database.
tableFieldsMap = gmap.New(true)
// allDryRun sets dry-run feature for all database connections.
// It is commonly used for command options for convenience.
allDryRun = false
@ -291,7 +318,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 +328,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 +352,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 +369,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 +389,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 +458,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,11 +483,26 @@ 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)
intlog.Printf(
`open new connection, master:%#v, config:%#v, node:%#v`,
master, c.config, node,
)
defer func() {
if err != nil {
intlog.Printf(`open new connection failed: %v, %#v`, err, node)
} else {
intlog.Printf(
`open new connection success, master:%#v, config:%#v, node:%#v`,
master, c.config, node,
)
}
}()
sqlDb, err = c.db.Open(node)
if err != nil {
intlog.Printf("DB open failed: %v, %+v", err, node)
return nil, err
}
if c.config.MaxIdleConnCount > 0 {
sqlDb.SetMaxIdleConns(c.config.MaxIdleConnCount)
} else {
@ -471,13 +513,13 @@ func (c *Core) getSqlDb(master bool, schema ...string) (sqlDb *sql.DB, err error
} else {
sqlDb.SetMaxOpenConns(defaultMaxOpenConnCount)
}
if c.config.MaxConnLifetime > 0 {
if c.config.MaxConnLifeTime > 0 {
// Automatically checks whether MaxConnLifetime is configured using string like: "30s", "60s", etc.
// Or else it is configured just using number, which means value in seconds.
if c.config.MaxConnLifetime > time.Second {
sqlDb.SetConnMaxLifetime(c.config.MaxConnLifetime)
if c.config.MaxConnLifeTime > time.Second {
sqlDb.SetConnMaxLifetime(c.config.MaxConnLifeTime)
} else {
sqlDb.SetConnMaxLifetime(c.config.MaxConnLifetime * time.Second)
sqlDb.SetConnMaxLifetime(c.config.MaxConnLifeTime * time.Second)
}
} else {
sqlDb.SetConnMaxLifetime(defaultMaxConnLifeTime)
@ -488,10 +530,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,25 +97,22 @@ 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()
rows, err = link.QueryContext(ctx, sql, args...)
mTime2 := gtime.TimestampMilli()
@ -127,10 +124,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 +141,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 +161,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 +175,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 +190,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 +198,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 +228,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 +244,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
}
@ -279,9 +276,9 @@ 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.
// Note that if there are 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 +286,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 +296,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 +308,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 +320,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 +331,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 +348,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 +357,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 +366,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,18 +378,18 @@ 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()
if c.GetConfig().TranTimeout > 0 {
var cancelFunc context.CancelFunc
ctx, cancelFunc = context.WithTimeout(ctx, c.GetConfig().TranTimeout)
defer cancelFunc()
}
if tx, err := master.BeginTx(ctx, nil); err == nil {
//ctx := c.db.GetCtx()
//if c.GetConfig().TranTimeout > 0 {
// var cancelFunc context.CancelFunc
// ctx, cancelFunc = context.WithTimeout(ctx, c.GetConfig().TranTimeout)
// defer cancelFunc()
//}
if tx, err := master.Begin(); err == nil {
return &TX{
db: c.DB,
db: c.db,
tx: tx,
master: master,
}, nil
@ -402,16 +399,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 +435,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 +451,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 +468,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 +487,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 +503,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 +529,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 +545,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 +577,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 +593,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 +602,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 +611,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 +620,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 +631,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 +686,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 +696,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 +742,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 +770,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 +789,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,24 +812,29 @@ 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 := k
if 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 := k
if 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)
}
}
}
updates = strings.Join(fields, ",")
@ -847,11 +849,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 +862,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 +879,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 +920,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 +946,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 +971,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

@ -42,7 +42,7 @@ type ConfigNode struct {
LinkInfo string `json:"link"` // (Optional) Custom link information, when it is used, configuration Host/Port/User/Pass/Name are ignored.
MaxIdleConnCount int `json:"maxIdle"` // (Optional) Max idle connection configuration for underlying connection pool.
MaxOpenConnCount int `json:"maxOpen"` // (Optional) Max open connection configuration for underlying connection pool.
MaxConnLifetime time.Duration `json:"maxLifetime"` // (Optional) Max connection TTL configuration for underlying connection pool.
MaxConnLifeTime time.Duration `json:"maxLifeTime"` // (Optional) Max amount of time a connection may be idle before being closed.
QueryTimeout time.Duration `json:"queryTimeout"` // (Optional) Max query time for per dql.
ExecTimeout time.Duration `json:"execTimeout"` // (Optional) Max exec time for dml.
TranTimeout time.Duration `json:"tranTimeout"` // (Optional) Max exec time time for a transaction.
@ -141,20 +141,39 @@ func (c *Core) GetLogger() *glog.Logger {
return c.logger
}
// SetMaxIdleConnCount sets the max idle connection count for underlying connection pool.
// SetMaxIdleConnCount sets the maximum number of connections in the idle
// connection pool.
//
// If MaxOpenConns is greater than 0 but less than the new MaxIdleConns,
// then the new MaxIdleConns will be reduced to match the MaxOpenConns limit.
//
// If n <= 0, no idle connections are retained.
//
// The default max idle connections is currently 2. This may change in
// a future release.
func (c *Core) SetMaxIdleConnCount(n int) {
c.config.MaxIdleConnCount = n
}
// SetMaxOpenConnCount sets the max open connection count for underlying connection pool.
// SetMaxOpenConnCount sets the maximum number of open connections to the database.
//
// If MaxIdleConns is greater than 0 and the new MaxOpenConns is less than
// MaxIdleConns, then MaxIdleConns will be reduced to match the new
// MaxOpenConns limit.
//
// If n <= 0, then there is no limit on the number of open connections.
// The default is 0 (unlimited).
func (c *Core) SetMaxOpenConnCount(n int) {
c.config.MaxOpenConnCount = n
}
// SetMaxConnLifetime sets the connection TTL for underlying connection pool.
// If parameter <d> <= 0, it means the connection never expires.
func (c *Core) SetMaxConnLifetime(d time.Duration) {
c.config.MaxConnLifetime = d
// SetMaxConnLifeTime sets the maximum amount of time a connection may be reused.
//
// Expired connections may be closed lazily before reuse.
//
// If d <= 0, connections are not closed due to a connection's age.
func (c *Core) SetMaxConnLifeTime(d time.Duration) {
c.config.MaxConnLifeTime = d
}
// String returns the node as string.
@ -165,7 +184,7 @@ func (node *ConfigNode) String() string {
node.Name, node.Type, node.Role, node.Charset, node.Debug,
node.MaxIdleConnCount,
node.MaxOpenConnCount,
node.MaxConnLifetime,
node.MaxConnLifeTime,
node.LinkInfo,
)
}

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(nil, 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,13 +12,15 @@ 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/attribute"
"go.opentelemetry.io/otel/codes"
"go.opentelemetry.io/otel/label"
"go.opentelemetry.io/otel/trace"
)
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()
@ -47,33 +59,33 @@ func (c *Core) addSqlToTracing(ctx context.Context, sql *Sql) {
if sql.Error != nil {
span.SetStatus(codes.Error, fmt.Sprintf(`%+v`, sql.Error))
}
labels := make([]label.KeyValue, 0)
labels := make([]attribute.KeyValue, 0)
labels = append(labels, gtrace.CommonLabels()...)
labels = append(labels,
label.String(tracingAttrDbType, c.DB.GetConfig().Type),
attribute.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, attribute.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, attribute.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, attribute.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, attribute.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, attribute.String(tracingAttrDbLink, c.db.FilteredLinkInfo()))
}
if group := c.DB.GetGroup(); group != "" {
labels = append(labels, label.String(tracingAttrDbGroup, group))
if group := c.db.GetGroup(); group != "" {
labels = append(labels, attribute.String(tracingAttrDbGroup, group))
}
span.SetAttributes(labels...)
span.AddEvent(tracingEventDbExecution, trace.WithAttributes(
label.String(tracingEventDbExecutionSql, sql.Format),
label.String(tracingEventDbExecutionCost, fmt.Sprintf(`%d ms`, sql.End-sql.Start)),
label.String(tracingEventDbExecutionType, sql.Type),
attribute.String(tracingEventDbExecutionSql, sql.Format),
attribute.String(tracingEventDbExecutionCost, fmt.Sprintf(`%d ms`, sql.End-sql.Start)),
attribute.String(tracingEventDbExecutionType, sql.Type),
))
}

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
}
@ -203,28 +203,33 @@ func (d *DriverMssql) Tables(schema ...string) (tables []string, err error) {
}
// TableFields retrieves and returns the fields information of specified table of current schema.
func (d *DriverMssql) TableFields(table string, schema ...string) (fields map[string]*TableField, err error) {
//
// Also see DriverMysql.TableFields.
func (d *DriverMssql) TableFields(link Link, table string, schema ...string) (fields map[string]*TableField, err error) {
charL, charR := d.GetChars()
table = gstr.Trim(table, charL+charR)
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]
}
v, _ := internalCache.GetOrSetFunc(
fmt.Sprintf(`mssql_table_fields_%s_%s@group:%s`, table, checkSchema, d.GetGroup()),
func() (interface{}, error) {
var (
result Result
link *sql.DB
)
link, err = d.DB.GetSlave(checkSchema)
tableFieldsCacheKey := fmt.Sprintf(
`mssql_table_fields_%s_%s@group:%s`,
table, checkSchema, d.GetGroup(),
)
v := tableFieldsMap.GetOrSetFuncLock(tableFieldsCacheKey, func() interface{} {
var (
result Result
)
if link == nil {
link, err = d.db.GetSlave(checkSchema)
if err != nil {
return nil, err
return nil
}
structureSql := fmt.Sprintf(`
}
structureSql := fmt.Sprintf(`
SELECT
a.name Field,
CASE b.name
@ -252,29 +257,29 @@ LEFT JOIN sys.extended_properties g ON a.id=g.major_id AND a.colid=g.minor_id
LEFT JOIN sys.extended_properties f ON d.id=f.major_id AND f.minor_id =0
WHERE d.name='%s'
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)
if err != nil {
return nil, err
strings.ToUpper(table),
)
structureSql, _ = gregex.ReplaceString(`[\n\r\s]+`, " ", gstr.Trim(structureSql))
result, err = d.db.DoGetAll(link, structureSql)
if err != nil {
return nil
}
fields = make(map[string]*TableField)
for i, m := range result {
fields[strings.ToLower(m["Field"].String())] = &TableField{
Index: i,
Name: strings.ToLower(m["Field"].String()),
Type: strings.ToLower(m["Type"].String()),
Null: m["Null"].Bool(),
Key: m["Key"].String(),
Default: m["Default"].Val(),
Extra: m["Extra"].String(),
Comment: m["Comment"].String(),
}
fields = make(map[string]*TableField)
for i, m := range result {
fields[strings.ToLower(m["Field"].String())] = &TableField{
Index: i,
Name: strings.ToLower(m["Field"].String()),
Type: strings.ToLower(m["Type"].String()),
Null: m["Null"].Bool(),
Key: m["Key"].String(),
Default: m["Default"].Val(),
Extra: m["Extra"].String(),
Comment: m["Comment"].String(),
}
}
return fields, nil
}, 0)
if err == nil {
}
return fields
})
if v != nil {
fields = v.(map[string]*TableField)
}
return

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
}
@ -102,13 +102,16 @@ func (d *DriverMysql) Tables(schema ...string) (tables []string, err error) {
// TableFields retrieves and returns the fields information of specified table of current
// schema.
//
// The parameter `link` is optional, if given nil it automatically retrieves a raw sql connection
// as its link to proceed necessary sql query.
//
// Note that it returns a map containing the field name and its corresponding fields.
// As a map is unsorted, the TableField struct has a "Index" field marks its sequence in
// the fields.
//
// It's using cache feature to enhance the performance, which is never expired util the
// process restarts.
func (d *DriverMysql) TableFields(table string, schema ...string) (fields map[string]*TableField, err error) {
func (d *DriverMysql) TableFields(link Link, table string, schema ...string) (fields map[string]*TableField, err error) {
charL, charR := d.GetChars()
table = gstr.Trim(table, charL+charR)
if gstr.Contains(table, " ") {
@ -118,40 +121,43 @@ func (d *DriverMysql) TableFields(table string, schema ...string) (fields map[st
if len(schema) > 0 && schema[0] != "" {
checkSchema = schema[0]
}
v, _ := internalCache.GetOrSetFunc(
fmt.Sprintf(`mysql_table_fields_%s_%s@group:%s`, table, checkSchema, d.GetGroup()),
func() (interface{}, error) {
var (
result Result
link *sql.DB
)
link, err = d.DB.GetSlave(checkSchema)
tableFieldsCacheKey := fmt.Sprintf(
`mysql_table_fields_%s_%s@group:%s`,
table, checkSchema, d.GetGroup(),
)
v := tableFieldsMap.GetOrSetFuncLock(tableFieldsCacheKey, func() interface{} {
var (
result Result
)
if link == nil {
link, err = d.db.GetSlave(checkSchema)
if err != nil {
return nil, err
return nil
}
result, err = d.DB.DoGetAll(
link,
fmt.Sprintf(`SHOW FULL COLUMNS FROM %s`, d.DB.QuoteWord(table)),
)
if err != nil {
return nil, err
}
result, err = d.db.DoGetAll(
link,
fmt.Sprintf(`SHOW FULL COLUMNS FROM %s`, d.db.QuoteWord(table)),
)
if err != nil {
return nil
}
fields = make(map[string]*TableField)
for i, m := range result {
fields[m["Field"].String()] = &TableField{
Index: i,
Name: m["Field"].String(),
Type: m["Type"].String(),
Null: m["Null"].Bool(),
Key: m["Key"].String(),
Default: m["Default"].Val(),
Extra: m["Extra"].String(),
Comment: m["Comment"].String(),
}
fields = make(map[string]*TableField)
for i, m := range result {
fields[m["Field"].String()] = &TableField{
Index: i,
Name: m["Field"].String(),
Type: m["Type"].String(),
Null: m["Null"].Bool(),
Key: m["Key"].String(),
Default: m["Default"].Val(),
Extra: m["Extra"].String(),
Comment: m["Comment"].String(),
}
}
return fields, nil
}, 0)
if err == nil {
}
return fields
})
if v != nil {
fields = v.(map[string]*TableField)
}
return

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
}
@ -180,21 +179,26 @@ func (d *DriverOracle) Tables(schema ...string) (tables []string, err error) {
}
// TableFields retrieves and returns the fields information of specified table of current schema.
func (d *DriverOracle) TableFields(table string, schema ...string) (fields map[string]*TableField, err error) {
//
// Also see DriverMysql.TableFields.
func (d *DriverOracle) TableFields(link Link, table string, schema ...string) (fields map[string]*TableField, err error) {
charL, charR := d.GetChars()
table = gstr.Trim(table, charL+charR)
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]
}
v, _ := internalCache.GetOrSetFunc(
fmt.Sprintf(`oracle_table_fields_%s_%s@group:%s`, table, checkSchema, d.GetGroup()),
func() (interface{}, error) {
result := (Result)(nil)
structureSql := fmt.Sprintf(`
tableFieldsCacheKey := fmt.Sprintf(
`oracle_table_fields_%s_%s@group:%s`,
table, checkSchema, d.GetGroup(),
)
v := tableFieldsMap.GetOrSetFuncLock(tableFieldsCacheKey, func() interface{} {
var (
result Result
structureSql = fmt.Sprintf(`
SELECT
COLUMN_NAME AS FIELD,
CASE DATA_TYPE
@ -204,22 +208,29 @@ SELECT
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)
)
structureSql, _ = gregex.ReplaceString(`[\n\r\s]+`, " ", gstr.Trim(structureSql))
if link == nil {
link, err = d.db.GetSlave(checkSchema)
if err != nil {
return nil, err
return nil
}
fields = make(map[string]*TableField)
for i, m := range result {
fields[strings.ToLower(m["FIELD"].String())] = &TableField{
Index: i,
Name: strings.ToLower(m["FIELD"].String()),
Type: strings.ToLower(m["TYPE"].String()),
}
}
result, err = d.db.DoGetAll(link, structureSql)
if err != nil {
return nil
}
fields = make(map[string]*TableField)
for i, m := range result {
fields[strings.ToLower(m["FIELD"].String())] = &TableField{
Index: i,
Name: strings.ToLower(m["FIELD"].String()),
Type: strings.ToLower(m["TYPE"].String()),
}
return fields, nil
}, 0)
if err == nil {
}
return fields
})
if v != nil {
fields = v.(map[string]*TableField)
}
return
@ -231,7 +242,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 +279,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 +314,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 +338,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 +353,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 +365,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 +417,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 +429,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 +463,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 +479,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
}
@ -111,52 +111,56 @@ func (d *DriverPgsql) Tables(schema ...string) (tables []string, err error) {
}
// TableFields retrieves and returns the fields information of specified table of current schema.
func (d *DriverPgsql) TableFields(table string, schema ...string) (fields map[string]*TableField, err error) {
//
// Also see DriverMysql.TableFields.
func (d *DriverPgsql) TableFields(link Link, table string, schema ...string) (fields map[string]*TableField, err error) {
charL, charR := d.GetChars()
table = gstr.Trim(table, charL+charR)
if gstr.Contains(table, " ") {
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]
}
v, _ := internalCache.GetOrSetFunc(
fmt.Sprintf(`pgsql_table_fields_%s_%s@group:%s`, table, checkSchema, d.GetGroup()),
func() (interface{}, error) {
var (
result Result
link *sql.DB
)
link, err = d.DB.GetSlave(checkSchema)
if err != nil {
return nil, err
}
structureSql := fmt.Sprintf(`
tableFieldsCacheKey := fmt.Sprintf(
`pgsql_table_fields_%s_%s@group:%s`,
table, checkSchema, d.GetGroup(),
)
v := tableFieldsMap.GetOrSetFuncLock(tableFieldsCacheKey, func() interface{} {
var (
result Result
structureSql = fmt.Sprintf(`
SELECT a.attname AS field, t.typname AS type FROM pg_class c, pg_attribute a
LEFT OUTER JOIN pg_description b ON a.attrelid=b.objoid AND a.attnum = b.objsubid,pg_type t
WHERE c.relname = '%s' and a.attnum > 0 and a.attrelid = c.oid and a.atttypid = t.oid
ORDER BY a.attnum`,
strings.ToLower(table),
)
structureSql, _ = gregex.ReplaceString(`[\n\r\s]+`, " ", gstr.Trim(structureSql))
result, err = d.DB.DoGetAll(link, structureSql)
)
structureSql, _ = gregex.ReplaceString(`[\n\r\s]+`, " ", gstr.Trim(structureSql))
if link == nil {
link, err = d.db.GetSlave(checkSchema)
if err != nil {
return nil, err
return nil
}
fields = make(map[string]*TableField)
for i, m := range result {
fields[m["field"].String()] = &TableField{
Index: i,
Name: m["field"].String(),
Type: m["type"].String(),
}
}
result, err = d.db.DoGetAll(link, structureSql)
if err != nil {
return nil
}
fields = make(map[string]*TableField)
for i, m := range result {
fields[m["field"].String()] = &TableField{
Index: i,
Name: m["field"].String(),
Type: m["type"].String(),
}
return fields, nil
}, 0)
if err == nil {
}
return fields
})
if v != nil {
fields = v.(map[string]*TableField)
}
return

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
}
@ -93,42 +93,47 @@ func (d *DriverSqlite) Tables(schema ...string) (tables []string, err error) {
}
// TableFields retrieves and returns the fields information of specified table of current schema.
func (d *DriverSqlite) TableFields(table string, schema ...string) (fields map[string]*TableField, err error) {
//
// Also see DriverMysql.TableFields.
func (d *DriverSqlite) TableFields(link Link, table string, schema ...string) (fields map[string]*TableField, err error) {
charL, charR := d.GetChars()
table = gstr.Trim(table, charL+charR)
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]
}
v, _ := internalCache.GetOrSetFunc(
fmt.Sprintf(`sqlite_table_fields_%s_%s@group:%s`, table, checkSchema, d.GetGroup()),
func() (interface{}, error) {
var (
result Result
link *sql.DB
)
link, err = d.DB.GetSlave(checkSchema)
tableFieldsCacheKey := fmt.Sprintf(
`sqlite_table_fields_%s_%s@group:%s`,
table, checkSchema, d.GetGroup(),
)
v := tableFieldsMap.GetOrSetFuncLock(tableFieldsCacheKey, func() interface{} {
var (
result Result
)
if link == nil {
link, err = d.db.GetSlave(checkSchema)
if err != nil {
return nil, err
return nil
}
result, err = d.DB.DoGetAll(link, fmt.Sprintf(`PRAGMA TABLE_INFO(%s)`, table))
if err != nil {
return nil, err
}
result, err = d.db.DoGetAll(link, fmt.Sprintf(`PRAGMA TABLE_INFO(%s)`, table))
if err != nil {
return nil
}
fields = make(map[string]*TableField)
for i, m := range result {
fields[strings.ToLower(m["name"].String())] = &TableField{
Index: i,
Name: strings.ToLower(m["name"].String()),
Type: strings.ToLower(m["type"].String()),
}
fields = make(map[string]*TableField)
for i, m := range result {
fields[strings.ToLower(m["name"].String())] = &TableField{
Index: i,
Name: strings.ToLower(m["name"].String()),
Type: strings.ToLower(m["type"].String()),
}
}
return fields, nil
}, 0)
if err == nil {
}
return fields
})
if v != nil {
fields = v.(map[string]*TableField)
}
return

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...)
}
@ -195,7 +235,7 @@ func DataToMapDeep(value interface{}) map[string]interface{} {
name = ""
fieldTag = rtField.Tag
for _, tag := range structTagPriority {
if s := fieldTag.Get(tag); s != "" {
if s := fieldTag.Get(tag); s != "" && gregex.IsMatchString(regularFieldNameWithoutDotRegPattern, s) {
name = s
break
}
@ -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.
@ -37,6 +39,7 @@ type Model struct {
data interface{} // Data for operation, which can be type of map/[]map/struct/*struct/string, etc.
batch int // Batch number for batch Insert/Replace/Save operations.
filter bool // Filter data and where key-value pairs according to the fields of the table.
distinct string // Force the query to only return distinct results.
lockInfo string // Lock for update or in shared lock.
cacheEnabled bool // Enable sql result cache feature.
cacheDuration time.Duration // Cache TTL duration.
@ -53,65 +56,89 @@ 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")
func (c *Core) Table(table ...string) *Model {
tables := ""
if len(table) > 1 {
tables = fmt.Sprintf(
`%s AS %s`, c.DB.QuotePrefixTableName(table[0]), c.DB.QuoteWord(table[1]),
// Table is alias of Core.Model.
// See Core.Model.
// Deprecated, use Model instead.
func (c *Core) Table(tableNameOrStruct ...interface{}) *Model {
return c.db.Model(tableNameOrStruct...)
}
// Model creates and returns a new ORM model from given schema.
// The parameter `tableNameOrStruct` 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(tableNameOrStruct ...interface{}) *Model {
var (
tableStr = ""
tableName = ""
tableNames = make([]string, len(tableNameOrStruct))
)
for k, v := range tableNameOrStruct {
if s, ok := v.(string); ok {
tableNames[k] = s
} else if tableName = getTableNameFromOrmTag(v); tableName != "" {
tableNames[k] = tableName
}
}
if len(tableNames) > 1 {
tableStr = fmt.Sprintf(
`%s AS %s`, c.db.QuotePrefixTableName(tableNames[0]), c.db.QuoteWord(tableNames[1]),
)
} else if len(table) == 1 {
tables = c.DB.QuotePrefixTableName(table[0])
} else {
panic("table cannot be empty")
} else if len(tableNames) == 1 {
tableStr = c.db.QuotePrefixTableName(tableNames[0])
}
return &Model{
db: c.DB,
tablesInit: tables,
tables: tables,
db: c.db,
tablesInit: tableStr,
tables: tableStr,
fields: "*",
start: -1,
offset: -1,
option: OPTION_ALLOWEMPTY,
option: OptionAllowEmpty,
filter: true,
}
}
// 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(objects ...interface{}) *Model {
return c.db.Model().With(objects...)
}
// Table acts like Core.Table except it operates on transaction.
// See Core.Table.
func (tx *TX) Table(table ...string) *Model {
model := tx.db.Table(table...)
// Table is alias of tx.Model.
// Deprecated, use Model instead.
func (tx *TX) Table(tableNameOrStruct ...interface{}) *Model {
return tx.Model(tableNameOrStruct...)
}
// Model acts like Core.Model except it operates on transaction.
// See Core.Model.
func (tx *TX) Model(tableNameOrStruct ...interface{}) *Model {
model := tx.db.Model(tableNameOrStruct...)
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 +197,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 +211,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

@ -7,10 +7,11 @@
package gdb
import (
"fmt"
"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 +46,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 {
@ -58,7 +59,125 @@ func (m *Model) WherePri(where interface{}, args ...interface{}) *Model {
return m.Where(newWhere[0], newWhere[1:]...)
}
// WhereBetween builds `xxx BETWEEN x AND y` statement.
func (m *Model) WhereBetween(column string, min, max interface{}) *Model {
return m.Where(fmt.Sprintf(`%s BETWEEN ? AND ?`, m.db.QuoteWord(column)), min, max)
}
// WhereLike builds `xxx LIKE x` statement.
func (m *Model) WhereLike(column string, like interface{}) *Model {
return m.Where(fmt.Sprintf(`%s LIKE ?`, m.db.QuoteWord(column)), like)
}
// WhereIn builds `xxx IN (x)` statement.
func (m *Model) WhereIn(column string, in interface{}) *Model {
return m.Where(fmt.Sprintf(`%s IN (?)`, m.db.QuoteWord(column)), in)
}
// WhereNull builds `xxx IS NULL` statement.
func (m *Model) WhereNull(columns ...string) *Model {
model := m
for _, column := range columns {
model = m.Where(fmt.Sprintf(`%s IS NULL`, m.db.QuoteWord(column)))
}
return model
}
// WhereNotBetween builds `xxx NOT BETWEEN x AND y` statement.
func (m *Model) WhereNotBetween(column string, min, max interface{}) *Model {
return m.Where(fmt.Sprintf(`%s NOT BETWEEN ? AND ?`, m.db.QuoteWord(column)), min, max)
}
// WhereNotLike builds `xxx NOT LIKE x` statement.
func (m *Model) WhereNotLike(column string, like interface{}) *Model {
return m.Where(fmt.Sprintf(`%s NOT LIKE ?`, m.db.QuoteWord(column)), like)
}
// WhereNotIn builds `xxx NOT IN (x)` statement.
func (m *Model) WhereNotIn(column string, in interface{}) *Model {
return m.Where(fmt.Sprintf(`%s NOT IN (?)`, m.db.QuoteWord(column)), in)
}
// WhereNotNull builds `xxx IS NOT NULL` statement.
func (m *Model) WhereNotNull(columns ...string) *Model {
model := m
for _, column := range columns {
model = m.Where(fmt.Sprintf(`%s IS NOT NULL`, m.db.QuoteWord(column)))
}
return model
}
// WhereOr adds "OR" condition to the where statement.
func (m *Model) WhereOr(where interface{}, args ...interface{}) *Model {
model := m.getModel()
if model.whereHolder == nil {
model.whereHolder = make([]*whereHolder, 0)
}
model.whereHolder = append(model.whereHolder, &whereHolder{
operator: whereHolderOr,
where: where,
args: args,
})
return model
}
// WhereOrBetween builds `xxx BETWEEN x AND y` statement in `OR` conditions.
func (m *Model) WhereOrBetween(column string, min, max interface{}) *Model {
return m.WhereOr(fmt.Sprintf(`%s BETWEEN ? AND ?`, m.db.QuoteWord(column)), min, max)
}
// WhereOrLike builds `xxx LIKE x` statement in `OR` conditions.
func (m *Model) WhereOrLike(column string, like interface{}) *Model {
return m.WhereOr(fmt.Sprintf(`%s LIKE ?`, m.db.QuoteWord(column)), like)
}
// WhereOrIn builds `xxx IN (x)` statement in `OR` conditions.
func (m *Model) WhereOrIn(column string, in interface{}) *Model {
return m.WhereOr(fmt.Sprintf(`%s IN (?)`, m.db.QuoteWord(column)), in)
}
// WhereOrNull builds `xxx IS NULL` statement in `OR` conditions.
func (m *Model) WhereOrNull(columns ...string) *Model {
model := m
for _, column := range columns {
model = m.WhereOr(fmt.Sprintf(`%s IS NULL`, m.db.QuoteWord(column)))
}
return model
}
// WhereOrNotBetween builds `xxx NOT BETWEEN x AND y` statement in `OR` conditions.
func (m *Model) WhereOrNotBetween(column string, min, max interface{}) *Model {
return m.WhereOr(fmt.Sprintf(`%s NOT BETWEEN ? AND ?`, m.db.QuoteWord(column)), min, max)
}
// WhereOrNotLike builds `xxx NOT LIKE x` statement in `OR` conditions.
func (m *Model) WhereOrNotLike(column string, like interface{}) *Model {
return m.WhereOr(fmt.Sprintf(`%s NOT LIKE ?`, m.db.QuoteWord(column)), like)
}
// WhereOrNotIn builds `xxx NOT IN (x)` statement.
func (m *Model) WhereOrNotIn(column string, in interface{}) *Model {
return m.WhereOr(fmt.Sprintf(`%s NOT IN (?)`, m.db.QuoteWord(column)), in)
}
// WhereOrNotNull builds `xxx IS NOT NULL` statement in `OR` conditions.
func (m *Model) WhereOrNotNull(columns ...string) *Model {
model := m
for _, column := range columns {
model = m.WhereOr(fmt.Sprintf(`%s IS NOT NULL`, m.db.QuoteWord(column)))
}
return model
}
// Group sets the "GROUP BY" statement for the model.
func (m *Model) Group(groupBy string) *Model {
model := m.getModel()
model.groupBy = m.db.QuoteString(groupBy)
return model
}
// And adds "AND" condition to the where statement.
// Deprecated, use Where instead.
func (m *Model) And(where interface{}, args ...interface{}) *Model {
model := m.getModel()
if model.whereHolder == nil {
@ -73,49 +192,64 @@ func (m *Model) And(where interface{}, args ...interface{}) *Model {
}
// Or adds "OR" condition to the where statement.
// Deprecated, use WhereOr instead.
func (m *Model) Or(where interface{}, args ...interface{}) *Model {
model := m.getModel()
if model.whereHolder == nil {
model.whereHolder = make([]*whereHolder, 0)
}
model.whereHolder = append(model.whereHolder, &whereHolder{
operator: whereHolderOr,
where: where,
args: args,
})
return model
}
// Group sets the "GROUP BY" statement for the model.
func (m *Model) Group(groupBy string) *Model {
model := m.getModel()
model.groupBy = m.db.QuoteString(groupBy)
return model
return m.WhereOr(where, args...)
}
// GroupBy is alias of Model.Group.
// See Model.Group.
// Deprecated.
// Deprecated, use Group instead.
func (m *Model) GroupBy(groupBy string) *Model {
return m.Group(groupBy)
}
// Order sets the "ORDER BY" statement for the model.
func (m *Model) Order(orderBy ...string) *Model {
if len(orderBy) == 0 {
return m
}
model := m.getModel()
model.orderBy = m.db.QuoteString(strings.Join(orderBy, " "))
return model
}
// OrderAsc sets the "ORDER BY xxx ASC" statement for the model.
func (m *Model) OrderAsc(column string) *Model {
if len(column) == 0 {
return m
}
model := m.getModel()
model.orderBy = m.db.QuoteWord(column) + " ASC"
return model
}
// OrderDesc sets the "ORDER BY xxx DESC" statement for the model.
func (m *Model) OrderDesc(column string) *Model {
if len(column) == 0 {
return m
}
model := m.getModel()
model.orderBy = m.db.QuoteWord(column) + " DESC"
return model
}
// OrderRandom sets the "ORDER BY RANDOM()" statement for the model.
func (m *Model) OrderRandom() *Model {
model := m.getModel()
model.orderBy = "RAND()"
return model
}
// OrderBy is alias of Model.Order.
// See Model.Order.
// Deprecated.
// Deprecated, use Order instead.
func (m *Model) OrderBy(orderBy string) *Model {
return m.Order(orderBy)
}
// 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 {
@ -138,8 +272,15 @@ func (m *Model) Offset(offset int) *Model {
return model
}
// Distinct forces the query to only return distinct results.
func (m *Model) Distinct() *Model {
model := m.getModel()
model.distinct = "DISTINCT "
return 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()
@ -153,7 +294,7 @@ func (m *Model) Page(page, limit int) *Model {
// ForPage is alias of Model.Page.
// See Model.Page.
// Deprecated.
// Deprecated, use Page instead.
func (m *Model) ForPage(page, limit int) *Model {
return m.Page(page, limit)
}

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

@ -14,19 +14,8 @@ import (
"github.com/gogf/gf/util/gutil"
)
// Filter marks filtering the fields which does not exist in the fields of the operated table.
// Note that this function supports only single table operations.
func (m *Model) Filter() *Model {
if gstr.Contains(m.tables, " ") {
panic("function Filter supports only single table operations")
}
model := m.getModel()
model.filter = true
return 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 {
@ -36,18 +25,18 @@ func (m *Model) Fields(fieldNamesOrMapStruct ...interface{}) *Model {
// String slice.
case length >= 2:
model := m.getModel()
model.fields = gstr.Join(m.mappingAndFilterToTableFields(gconv.Strings(fieldNamesOrMapStruct)), ",")
model.fields = gstr.Join(m.mappingAndFilterToTableFields(gconv.Strings(fieldNamesOrMapStruct), true), ",")
return model
// It need type asserting.
case length == 1:
model := m.getModel()
switch r := fieldNamesOrMapStruct[0].(type) {
case string:
model.fields = gstr.Join(m.mappingAndFilterToTableFields([]string{r}), ",")
model.fields = gstr.Join(m.mappingAndFilterToTableFields([]string{r}, false), ",")
case []string:
model.fields = gstr.Join(m.mappingAndFilterToTableFields(r), ",")
model.fields = gstr.Join(m.mappingAndFilterToTableFields(r, true), ",")
default:
model.fields = gstr.Join(m.mappingAndFilterToTableFields(gutil.Keys(r)), ",")
model.fields = gstr.Join(m.mappingAndFilterToTableFields(gutil.Keys(r), true), ",")
}
return model
}
@ -56,7 +45,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 {
@ -65,36 +54,49 @@ func (m *Model) FieldsEx(fieldNamesOrMapStruct ...interface{}) *Model {
model := m.getModel()
switch {
case length >= 2:
model.fieldsEx = gstr.Join(m.mappingAndFilterToTableFields(gconv.Strings(fieldNamesOrMapStruct)), ",")
model.fieldsEx = gstr.Join(m.mappingAndFilterToTableFields(gconv.Strings(fieldNamesOrMapStruct), true), ",")
return model
case length == 1:
switch r := fieldNamesOrMapStruct[0].(type) {
case string:
model.fieldsEx = gstr.Join(m.mappingAndFilterToTableFields([]string{r}), ",")
model.fieldsEx = gstr.Join(m.mappingAndFilterToTableFields([]string{r}, false), ",")
case []string:
model.fieldsEx = gstr.Join(m.mappingAndFilterToTableFields(r), ",")
model.fieldsEx = gstr.Join(m.mappingAndFilterToTableFields(r, true), ",")
default:
model.fieldsEx = gstr.Join(m.mappingAndFilterToTableFields(gutil.Keys(r)), ",")
model.fieldsEx = gstr.Join(m.mappingAndFilterToTableFields(gutil.Keys(r), true), ",")
}
return model
}
return m
}
// Filter marks filtering the fields which does not exist in the fields of the operated table.
// Note that this function supports only single table operations.
// Deprecated, filter feature is automatically enabled from GoFrame v1.16.0, it is so no longer used.
func (m *Model) Filter() *Model {
if gstr.Contains(m.tables, " ") {
panic("function Filter supports only single table operations")
}
model := m.getModel()
model.filter = true
return model
}
// 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.").
// Deprecated, use GetFieldsStr instead.
// This function name confuses the user that it was a chaining function.
func (m *Model) FieldsStr(prefix ...string) string {
return m.GetFieldsStr(prefix...)
}
// 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.").
// GetFieldsStr retrieves and returns all fields from the table, joined with char ','.
// The optional parameter `prefix` specifies the prefix for each field, eg: FieldsStr("u.").
func (m *Model) GetFieldsStr(prefix ...string) string {
prefixStr := ""
if len(prefix) > 0 {
prefixStr = prefix[0]
}
tableFields, err := m.db.TableFields(m.tables)
tableFields, err := m.TableFields(m.tables)
if err != nil {
panic(err)
}
@ -116,22 +118,25 @@ func (m *Model) GetFieldsStr(prefix ...string) string {
return newFields
}
// 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.").
// Deprecated, use GetFieldsExStr instead.
// This function name confuses the user that it was a chaining function.
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,
// GetFieldsExStr 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 {
prefixStr = prefix[0]
}
tableFields, err := m.db.TableFields(m.tables)
tableFields, err := m.TableFields(m.tables)
if err != nil {
panic(err)
}
@ -159,7 +164,7 @@ func (m *Model) GetFieldsExStr(fields string, prefix ...string) string {
// HasField determine whether the field exists in the table.
func (m *Model) HasField(field string) (bool, error) {
tableFields, err := m.db.TableFields(m.tables)
tableFields, err := m.TableFields(m.tables)
if err != nil {
return false, err
}

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 {
@ -109,8 +109,20 @@ func (m *Model) Insert(data ...interface{}) (result sql.Result, err error) {
return m.doInsertWithOption(insertOptionDefault)
}
// InsertAndGetId performs action Insert and returns the last insert id that automatically generated.
func (m *Model) InsertAndGetId(data ...interface{}) (lastInsertId int64, err error) {
if len(data) > 0 {
return m.Data(data...).InsertAndGetId()
}
result, err := m.doInsertWithOption(insertOptionDefault)
if err != nil {
return 0, err
}
return result.LastInsertId()
}
// 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 +132,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 +142,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

@ -19,7 +19,7 @@ import (
// Select is alias of Model.All.
// See Model.All.
// Deprecated.
// Deprecated, use All instead.
func (m *Model) Select(where ...interface{}) (Result, error) {
return m.All(where...)
}
@ -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,31 +38,20 @@ 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(
fmt.Sprintf(
"SELECT %s FROM %s%s",
"SELECT %s%s FROM %s%s",
m.distinct,
m.getFieldsFiltered(),
m.tables,
conditionWhere+conditionExtra,
@ -96,7 +85,7 @@ func (m *Model) getFieldsFiltered() string {
panic("function FieldsEx supports only single table operations")
}
// Filter table fields with fieldEx.
tableFields, err := m.db.TableFields(m.tables)
tableFields, err := m.TableFields(m.tables)
if err != nil {
panic(err)
}
@ -151,7 +140,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 +159,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) {
@ -194,9 +183,9 @@ 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.
// Note that if there are 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 +206,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 +226,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 +253,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 +282,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 +313,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 +325,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 {
@ -341,21 +335,9 @@ func (m *Model) Count(where ...interface{}) (int, error) {
if m.fields != "" && m.fields != "*" {
// DO NOT quote the m.fields here, in case of fields like:
// DISTINCT t.user_id uid
countFields = fmt.Sprintf(`COUNT(%s)`, m.fields)
countFields = fmt.Sprintf(`COUNT(%s%s)`, m.distinct, 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)
@ -372,6 +354,62 @@ func (m *Model) Count(where ...interface{}) (int, error) {
return 0, nil
}
// CountColumn does "SELECT COUNT(x) FROM ..." statement for the model.
func (m *Model) CountColumn(column string) (int, error) {
if len(column) == 0 {
return 0, nil
}
return m.Fields(column).Count()
}
// Min does "SELECT MIN(x) FROM ..." statement for the model.
func (m *Model) Min(column string) (float64, error) {
if len(column) == 0 {
return 0, nil
}
value, err := m.Fields(fmt.Sprintf(`MIN(%s)`, m.db.QuoteWord(column))).Value()
if err != nil {
return 0, err
}
return value.Float64(), err
}
// Max does "SELECT MAX(x) FROM ..." statement for the model.
func (m *Model) Max(column string) (float64, error) {
if len(column) == 0 {
return 0, nil
}
value, err := m.Fields(fmt.Sprintf(`MAX(%s)`, m.db.QuoteWord(column))).Value()
if err != nil {
return 0, err
}
return value.Float64(), err
}
// Avg does "SELECT AVG(x) FROM ..." statement for the model.
func (m *Model) Avg(column string) (float64, error) {
if len(column) == 0 {
return 0, nil
}
value, err := m.Fields(fmt.Sprintf(`AVG(%s)`, m.db.QuoteWord(column))).Value()
if err != nil {
return 0, err
}
return value.Float64(), err
}
// Sum does "SELECT SUM(x) FROM ..." statement for the model.
func (m *Model) Sum(column string) (float64, error) {
if len(column) == 0 {
return 0, nil
}
value, err := m.Fields(fmt.Sprintf(`SUM(%s)`, m.db.QuoteWord(column))).Value()
if err != nil {
return 0, err
}
return value.Float64(), err
}
// FindOne retrieves and returns a single Record by Model.WherePri and Model.One.
// Also see Model.WherePri and Model.One.
func (m *Model) FindOne(where ...interface{}) (Record, error) {

View File

@ -93,7 +93,7 @@ func (m *Model) getSoftFieldNameDeleted(table ...string) (field string) {
// getSoftFieldName retrieves and returns the field name of the table for possible key.
func (m *Model) getSoftFieldName(table string, keys []string) (field string) {
fieldsMap, _ := m.db.TableFields(table)
fieldsMap, _ := m.TableFields(table)
if len(fieldsMap) > 0 {
for _, key := range keys {
field, _ = gutil.MapPossibleItemByKey(

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) {
@ -89,3 +89,19 @@ func (m *Model) Update(dataAndWhere ...interface{}) (result sql.Result, err erro
m.mergeArguments(conditionArgs)...,
)
}
// Increment increments a column's value by a given amount.
func (m *Model) Increment(column string, amount float64) (sql.Result, error) {
return m.getModel().Data(column, &Counter{
Field: column,
Value: amount,
}).Update()
}
// Decrement decrements a column's value by a given amount.
func (m *Model) Decrement(column string, amount float64) (sql.Result, error) {
return m.getModel().Data(column, &Counter{
Field: column,
Value: -amount,
}).Update()
}

View File

@ -18,7 +18,26 @@ import (
"time"
)
// getModel creates and returns a cloned model of current model if <safe> is true, or else it returns
// TableFields retrieves and returns the fields information of specified table of current
// schema.
//
// Also see DriverMysql.TableFields.
func (m *Model) TableFields(table string, schema ...string) (fields map[string]*TableField, err error) {
var (
link Link
)
if m.tx != nil {
link = m.tx.tx
} else {
link, err = m.db.GetSlave(schema...)
if err != nil {
return
}
}
return m.db.TableFields(link, table, schema...)
}
// 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 {
@ -29,8 +48,11 @@ func (m *Model) getModel() *Model {
}
// mappingAndFilterToTableFields mappings and changes given field name to really table field name.
func (m *Model) mappingAndFilterToTableFields(fields []string) []string {
fieldsMap, err := m.db.TableFields(m.tables)
// Eg:
// ID -> id
// NICK_Name -> nickname
func (m *Model) mappingAndFilterToTableFields(fields []string, filter bool) []string {
fieldsMap, err := m.TableFields(m.tables)
if err != nil || len(fieldsMap) == 0 {
return fields
}
@ -52,6 +74,8 @@ func (m *Model) mappingAndFilterToTableFields(fields []string) []string {
// Eg: id, name
if foundKey, _ := gutil.MapPossibleItemByKey(fieldsKeyMap, field); foundKey != "" {
outputFieldsArray = append(outputFieldsArray, foundKey)
} else if !filter {
outputFieldsArray = append(outputFieldsArray, field)
}
}
} else {
@ -92,7 +116,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 +171,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
@ -183,7 +207,7 @@ func (m *Model) getLink(master bool) Link {
// "user", "user u", "user as u, user_detail as ud".
func (m *Model) getPrimaryKey() string {
table := gstr.SplitAndTrim(m.tables, " ")[0]
tableFields, err := m.db.TableFields(table)
tableFields, err := m.TableFields(table)
if err != nil {
return ""
}
@ -196,9 +220,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 +230,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 +242,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 +257,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 +272,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 +326,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,260 @@
// 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)
// Or:
// db.With(UserDetail{}, UserDetail{}).Scan(xxx)
func (m *Model) With(objects ...interface{}) *Model {
model := m.getModel()
for _, object := range objects {
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

@ -9,6 +9,8 @@ package gdb
import (
"database/sql"
"fmt"
"github.com/gogf/gf/os/gtime"
"github.com/gogf/gf/util/gconv"
"reflect"
"github.com/gogf/gf/text/gregex"
@ -16,19 +18,139 @@ import (
// TX is the struct for transaction management.
type TX struct {
db DB
tx *sql.Tx
master *sql.DB
db DB // db is the current gdb database manager.
tx *sql.Tx // tx is the raw and underlying transaction manager.
master *sql.DB // master is the raw and underlying database manager.
transactionCount int // transactionCount marks the times that Begins.
}
// Commit commits the transaction.
const (
transactionPointerPrefix = "transaction"
)
// Commit commits current transaction.
// Note that it releases previous saved transaction point if it's in a nested transaction procedure,
// or else it commits the hole transaction.
func (tx *TX) Commit() error {
return tx.tx.Commit()
if tx.transactionCount > 0 {
tx.transactionCount--
_, err := tx.Exec("RELEASE SAVEPOINT " + tx.transactionKey())
return err
}
var (
sqlStr = "COMMIT"
mTime1 = gtime.TimestampMilli()
err = tx.tx.Commit()
mTime2 = gtime.TimestampMilli()
sqlObj = &Sql{
Sql: sqlStr,
Type: "TX.Commit",
Args: nil,
Format: sqlStr,
Error: err,
Start: mTime1,
End: mTime2,
Group: tx.db.GetGroup(),
}
)
tx.db.addSqlToTracing(tx.db.GetCtx(), sqlObj)
if tx.db.GetDebug() {
tx.db.writeSqlToLogger(sqlObj)
}
return err
}
// Rollback aborts the transaction.
// Rollback aborts current transaction.
// Note that it aborts current transaction if it's in a nested transaction procedure,
// or else it aborts the hole transaction.
func (tx *TX) Rollback() error {
return tx.tx.Rollback()
if tx.transactionCount > 0 {
tx.transactionCount--
_, err := tx.Exec("ROLLBACK TO SAVEPOINT " + tx.transactionKey())
return err
}
var (
sqlStr = "ROLLBACK"
mTime1 = gtime.TimestampMilli()
err = tx.tx.Rollback()
mTime2 = gtime.TimestampMilli()
sqlObj = &Sql{
Sql: sqlStr,
Type: "TX.Rollback",
Args: nil,
Format: sqlStr,
Error: err,
Start: mTime1,
End: mTime2,
Group: tx.db.GetGroup(),
}
)
tx.db.addSqlToTracing(tx.db.GetCtx(), sqlObj)
if tx.db.GetDebug() {
tx.db.writeSqlToLogger(sqlObj)
}
return err
}
// Begin starts a nested transaction procedure.
func (tx *TX) Begin() error {
_, err := tx.Exec("SAVEPOINT " + tx.transactionKey())
if err != nil {
return err
}
tx.transactionCount++
return nil
}
// SavePoint performs `SAVEPOINT xxx` SQL statement that saves transaction at current point.
// The parameter `point` specifies the point name that will be saved to server.
func (tx *TX) SavePoint(point string) error {
_, err := tx.Exec("SAVEPOINT " + tx.db.QuoteWord(point))
return err
}
// RollbackTo performs `ROLLBACK TO SAVEPOINT xxx` SQL statement that rollbacks to specified saved transaction.
// The parameter `point` specifies the point name that was saved previously.
func (tx *TX) RollbackTo(point string) error {
_, err := tx.Exec("ROLLBACK TO SAVEPOINT " + tx.db.QuoteWord(point))
return err
}
// transactionKey forms and returns the transaction key at current save point.
func (tx *TX) transactionKey() string {
return tx.db.QuoteWord(transactionPointerPrefix + gconv.String(tx.transactionCount))
}
// 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.
//
// Note that, you should not Commit or Rollback the transaction in function `f`
// as it is automatically handled by this function.
func (tx *TX) Transaction(f func(tx *TX) error) (err error) {
err = tx.Begin()
if err != nil {
return err
}
defer func() {
if err == nil {
if e := recover(); e != nil {
err = fmt.Errorf("%v", e)
}
}
if err != nil {
if e := tx.Rollback(); e != nil {
err = e
}
} else {
if e := tx.Commit(); e != nil {
err = e
}
}
}()
err = f(tx)
return
}
// Query does query operation on transaction.
@ -75,7 +197,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 +207,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 +219,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 +268,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 +284,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 +301,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 +320,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 +335,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()
@ -221,8 +343,8 @@ func (tx *TX) BatchInsert(table string, list interface{}, batch ...int) (sql.Res
return tx.Model(table).Data(list).Insert()
}
// BatchInsert batch inserts data with ignore option.
// The parameter <list> must be type of slice of map or struct.
// BatchInsertIgnore batch inserts data with ignore option.
// 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 +353,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 +362,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 +372,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 +390,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

@ -6,22 +6,22 @@
package gdb
// Deprecated.
// Deprecated, use Json instead.
func (r Record) ToJson() string {
return r.Json()
}
// Deprecated.
// Deprecated, use Xml instead.
func (r Record) ToXml(rootTag ...string) string {
return r.Xml(rootTag...)
}
// Deprecated.
// Deprecated, use Map instead.
func (r Record) ToMap() Map {
return r.Map()
}
// Deprecated.
// Deprecated, use Struct instead.
func (r Record) ToStruct(pointer interface{}) error {
return r.Struct(pointer)
}

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.List(), pointer, OrmTagForStruct)
}

View File

@ -6,52 +6,52 @@
package gdb
// Deprecated.
// Deprecated, use Json instead.
func (r Result) ToJson() string {
return r.Json()
}
// Deprecated.
// Deprecated, use Xml instead.
func (r Result) ToXml(rootTag ...string) string {
return r.Xml(rootTag...)
}
// Deprecated.
// Deprecated, use List instead.
func (r Result) ToList() List {
return r.List()
}
// Deprecated.
// Deprecated, use MapKeyStr instead.
func (r Result) ToStringMap(key string) map[string]Map {
return r.MapKeyStr(key)
}
// Deprecated.
// Deprecated, use MapKetInt instead.
func (r Result) ToIntMap(key string) map[int]Map {
return r.MapKeyInt(key)
}
// Deprecated.
// Deprecated, use MapKeyUint instead.
func (r Result) ToUintMap(key string) map[uint]Map {
return r.MapKeyUint(key)
}
// Deprecated.
// Deprecated, use RecordKeyStr instead.
func (r Result) ToStringRecord(key string) map[string]Record {
return r.RecordKeyStr(key)
}
// Deprecated.
// Deprecated, use RecordKetInt instead.
func (r Result) ToIntRecord(key string) map[int]Record {
return r.RecordKeyInt(key)
}
// Deprecated.
// Deprecated, use RecordKetUint instead.
func (r Result) ToUintRecord(key string) map[uint]Record {
return r.RecordKeyUint(key)
}
// Deprecated.
// Deprecated, use Structs instead.
func (r Result) ToStructs(pointer interface{}) (err error) {
return r.Structs(pointer)
}

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",
@ -51,10 +52,10 @@ func init() {
Weight: 1,
MaxIdleConnCount: 10,
MaxOpenConnCount: 10,
MaxConnLifetime: 600,
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,668 @@
// 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_score"
)
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 UserScore struct {
gmeta.Meta `orm:"table:user_score"`
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 []*UserScore `orm:"with:uid=id"`
}
// Initialize the data.
gtest.C(t, func(t *gtest.T) {
for i := 1; i <= 5; i++ {
// User.
user := User{
Name: fmt.Sprintf(`name_%d`, i),
}
lastInsertId, err := db.Model(user).Data(user).OmitEmpty().InsertAndGetId()
t.AssertNil(err)
// Detail.
userDetail := UserDetail{
Uid: int(lastInsertId),
Address: fmt.Sprintf(`address_%d`, lastInsertId),
}
_, err = db.Model(userDetail).Data(userDetail).OmitEmpty().Insert()
t.AssertNil(err)
// Scores.
for j := 1; j <= 5; j++ {
userScore := UserScore{
Uid: int(lastInsertId),
Score: j,
}
_, err = db.Model(userScore).Data(userScore).OmitEmpty().Insert()
t.AssertNil(err)
}
}
})
for i := 1; i <= 5; i++ {
// User.
user := User{
Name: fmt.Sprintf(`name_%d`, i),
}
lastInsertId, err := db.Model(user).Data(user).OmitEmpty().InsertAndGetId()
gtest.AssertNil(err)
// Detail.
userDetail := UserDetail{
Uid: int(lastInsertId),
Address: fmt.Sprintf(`address_%d`, lastInsertId),
}
_, err = db.Model(userDetail).Data(userDetail).Insert()
gtest.AssertNil(err)
// Scores.
for j := 1; j <= 5; j++ {
userScore := UserScore{
Uid: int(lastInsertId),
Score: j,
}
_, err = db.Model(userScore).Data(userScore).Insert()
gtest.AssertNil(err)
}
}
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(UserScore{}).
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",
@ -45,7 +45,7 @@ func init() {
Weight: 1,
MaxIdleConnCount: 10,
MaxOpenConnCount: 10,
MaxConnLifetime: 600,
MaxConnLifeTime: 600,
}
AddConfigNode(DefaultGroupName, configNode)
// Default db.
@ -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,40 +264,41 @@ 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)
})
}
func Test_DB_Insert_KeyFieldNameMapping_Error(t *testing.T) {
table := createTable()
defer dropTable(table)
gtest.C(t, func(t *gtest.T) {
type User struct {
Id int
Passport string
Password string
Nickname string
CreateTime string
NoneExistField string
}
data := User{
Id: 1,
Passport: "user_1",
Password: "pass_1",
Nickname: "name_1",
CreateTime: "2020-10-10 12:00:01",
}
_, err := db.Insert(table, data)
t.AssertNE(err, nil)
})
}
// This is no longer used as the filter feature is automatically enabled from GoFrame v1.16.0.
//func Test_DB_Insert_KeyFieldNameMapping_Error(t *testing.T) {
// table := createTable()
// defer dropTable(table)
//
// gtest.C(t, func(t *gtest.T) {
// type User struct {
// Id int
// Passport string
// Password string
// Nickname string
// CreateTime string
// NoneExistField string
// }
// data := User{
// Id: 1,
// Passport: "user_1",
// Password: "pass_1",
// Nickname: "name_1",
// CreateTime: "2020-10-10 12:00:01",
// }
// _, err := db.Insert(table, data)
// t.AssertNE(err, nil)
// })
//}
func Test_DB_InsertIgnore(t *testing.T) {
table := createInitTable()
@ -320,7 +321,7 @@ func Test_DB_InsertIgnore(t *testing.T) {
"nickname": "T1",
"create_time": gtime.Now().String(),
})
t.Assert(err, nil)
t.AssertNil(err)
})
}
@ -344,7 +345,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 +373,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 +389,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 +417,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 +436,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 +461,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 +479,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 +498,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 +518,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 +526,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 +534,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 +547,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 +557,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 +567,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 +585,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 +598,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 +616,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 +636,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 +660,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 +673,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 +687,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 +707,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 +723,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 +747,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 +766,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 +782,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 +822,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 +856,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 +922,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 +958,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 +994,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 +1031,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 +1069,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 +1106,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 +1170,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 +1182,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 +1192,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 +1206,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 +1222,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 +1238,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 +1254,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 +1264,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 +1273,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 +1284,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 +1301,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 +1313,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 +1337,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 +1349,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 +1373,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 +1385,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 +1404,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 +1431,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 +1443,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 +1462,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 +1487,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

@ -31,7 +31,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 +41,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 +77,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 +109,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 +121,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 +130,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 +151,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 +163,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 +176,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 +188,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 +201,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 +211,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 +221,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 +231,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 +254,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 +269,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 +287,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 +343,89 @@ 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")
})
}
func Test_Model_Scan_CustomType_String(t *testing.T) {
type MyString string
type MyStringSt struct {
Passport MyString
}
table := createInitTable()
defer dropTable(table)
gtest.C(t, func(t *gtest.T) {
st := new(MyStringSt)
err := db.Model(table).Fields("Passport").WherePri(1).Scan(st)
t.AssertNil(err)
t.Assert(st.Passport, "user_1")
})
gtest.C(t, func(t *gtest.T) {
var sts []MyStringSt
err := db.Model(table).Fields("Passport").Order("id asc").Scan(&sts)
t.AssertNil(err)
t.Assert(len(sts), TableSize)
t.Assert(sts[0].Passport, "user_1")
})
}
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"].(int)
user.Passport = result["passport"].(string)
user.Password = ""
user.Nickname = result["nickname"].(string)
user.CreateTime = gtime.New(result["create_time"])
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,10 +782,144 @@ 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")
}
})
}
func Test_Transaction_Nested_Begin_Rollback_Commit(t *testing.T) {
table := createTable()
defer dropTable(table)
gtest.C(t, func(t *gtest.T) {
tx, err := db.Begin()
t.AssertNil(err)
// tx begin.
err = tx.Begin()
t.AssertNil(err)
// tx rollback.
_, err = tx.Model(table).Data(g.Map{
"id": 1,
"passport": "user_1",
"password": "pass_1",
"nickname": "name_1",
}).Insert()
err = tx.Rollback()
t.AssertNil(err)
// tx commit.
_, err = tx.Model(table).Data(g.Map{
"id": 2,
"passport": "user_2",
"password": "pass_2",
"nickname": "name_2",
}).Insert()
err = tx.Commit()
t.AssertNil(err)
// check data.
all, err := db.Model(table).All()
t.AssertNil(err)
t.Assert(len(all), 1)
t.Assert(all[0]["id"], 2)
})
}
func Test_Transaction_Nested_TX_Transaction(t *testing.T) {
table := createTable()
defer dropTable(table)
gtest.C(t, func(t *gtest.T) {
var err error
err = db.Transaction(func(tx *gdb.TX) error {
// commit
err = tx.Transaction(func(tx *gdb.TX) error {
err = tx.Transaction(func(tx *gdb.TX) error {
err = tx.Transaction(func(tx *gdb.TX) error {
err = tx.Transaction(func(tx *gdb.TX) error {
err = tx.Transaction(func(tx *gdb.TX) error {
_, err = tx.Model(table).Data(g.Map{
"id": 1,
"passport": "USER_1",
"password": "PASS_1",
"nickname": "NAME_1",
"create_time": gtime.Now().String(),
}).Insert()
t.AssertNil(err)
return err
})
t.AssertNil(err)
return err
})
t.AssertNil(err)
return err
})
t.AssertNil(err)
return err
})
t.AssertNil(err)
return err
})
t.AssertNil(err)
// rollback
err = tx.Transaction(func(tx *gdb.TX) error {
_, err = tx.Model(table).Data(g.Map{
"id": 2,
"passport": "USER_2",
"password": "PASS_2",
"nickname": "NAME_2",
"create_time": gtime.Now().String(),
}).Insert()
t.AssertNil(err)
panic("error")
return err
})
t.AssertNE(err, nil)
return nil
})
t.AssertNil(err)
all, err := db.Model(table).All()
t.AssertNil(err)
t.Assert(len(all), 1)
t.Assert(all[0]["id"], 1)
})
}
func Test_Transaction_Nested_SavePoint_RollbackTo(t *testing.T) {
table := createTable()
defer dropTable(table)
gtest.C(t, func(t *gtest.T) {
tx, err := db.Begin()
t.AssertNil(err)
// tx save point.
_, err = tx.Model(table).Data(g.Map{
"id": 1,
"passport": "user_1",
"password": "pass_1",
"nickname": "name_1",
}).Insert()
err = tx.SavePoint("MyPoint")
t.AssertNil(err)
_, err = tx.Model(table).Data(g.Map{
"id": 2,
"passport": "user_2",
"password": "pass_2",
"nickname": "name_2",
}).Insert()
// tx rollback to.
err = tx.RollbackTo("MyPoint")
t.AssertNil(err)
// tx commit.
err = tx.Commit()
t.AssertNil(err)
// check data.
all, err := db.Model(table).All()
t.AssertNil(err)
t.Assert(len(all), 1)
t.Assert(all[0]["id"], 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

@ -16,6 +16,7 @@ package gredis
import (
"context"
"fmt"
"github.com/gogf/gf/internal/intlog"
"time"
"github.com/gogf/gf/container/gmap"
@ -113,6 +114,7 @@ func New(config *Config) *Redis {
if err != nil {
return nil, err
}
intlog.Printf(`open new connection, config:%+v`, config)
// AUTH
if len(config.Pass) > 0 {
if _, err := c.Do("AUTH", config.Pass); err != nil {
@ -188,7 +190,7 @@ func (r *Redis) Conn() *Conn {
}
// Alias of Conn, see Conn.
// Deprecated.
// Deprecated, use Conn instead.
func (r *Redis) GetConn() *Conn {
return r.Conn()
}

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,9 +12,10 @@ 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/attribute"
"go.opentelemetry.io/otel/codes"
"go.opentelemetry.io/otel/label"
"go.opentelemetry.io/otel/trace"
)
@ -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 {
@ -53,14 +68,14 @@ func (c *Conn) addTracingItem(item *tracingItem) {
}
span.SetAttributes(gtrace.CommonLabels()...)
span.SetAttributes(
label.String(tracingAttrRedisHost, c.redis.config.Host),
label.Int(tracingAttrRedisPort, c.redis.config.Port),
label.Int(tracingAttrRedisDb, c.redis.config.Db),
attribute.String(tracingAttrRedisHost, c.redis.config.Host),
attribute.Int(tracingAttrRedisPort, c.redis.config.Port),
attribute.Int(tracingAttrRedisDb, c.redis.config.Db),
)
jsonBytes, _ := json.Marshal(item.arguments)
span.AddEvent(tracingEventRedisExecution, trace.WithAttributes(
label.String(tracingEventRedisExecutionCommand, item.commandName),
label.String(tracingEventRedisExecutionCost, fmt.Sprintf(`%d ms`, item.costMilli)),
label.String(tracingEventRedisExecutionArguments, string(jsonBytes)),
attribute.String(tracingEventRedisExecutionCommand, item.commandName),
attribute.String(tracingEventRedisExecutionCost, fmt.Sprintf(`%d ms`, item.costMilli)),
attribute.String(tracingEventRedisExecutionArguments, string(jsonBytes)),
))
}

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

@ -354,13 +354,6 @@ func (j *Json) GetMapToMap(pattern string, pointer interface{}, mapping ...map[s
return gconv.MapToMap(j.Get(pattern), pointer, mapping...)
}
// GetMapToMapDeep retrieves the value by specified <pattern> and converts it to specified map
// variable recursively.
// See gconv.MapToMapDeep.
func (j *Json) GetMapToMapDeep(pattern string, pointer interface{}, mapping ...map[string]string) error {
return gconv.MapToMapDeep(j.Get(pattern), pointer, mapping...)
}
// GetMapToMaps retrieves the value by specified <pattern> and converts it to specified map slice
// variable.
// See gconv.MapToMaps.

View File

@ -85,15 +85,6 @@ func (j *Json) ToMapToMap(pointer interface{}, mapping ...map[string]string) err
return gconv.MapToMap(*(j.p), pointer, mapping...)
}
// ToMapToMapDeep converts current Json object to specified map variable recursively.
// The parameter of <pointer> should be type of *map.
// Deprecated, use MapToMap instead.
func (j *Json) ToMapToMapDeep(pointer interface{}, mapping ...map[string]string) error {
j.mu.RLock()
defer j.mu.RUnlock()
return gconv.MapToMapDeep(*(j.p), pointer, mapping...)
}
// ToMapToMaps converts current Json object to specified map variable slice.
// The parameter of <pointer> should be type of []map/*map.
// Deprecated, use MapToMaps instead.

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,30 @@ 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(tableNameOrStruct ...interface{}) *gdb.Model {
return DB().Model(tableNameOrStruct...)
}
// 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(tableNameOrStruct ...interface{}) *gdb.Model {
return DB().Model(tableNameOrStruct...)
}
// Redis returns an instance of redis client with specified configuration group name.

View File

@ -48,22 +48,26 @@ func Database(name ...string) gdb.DB {
configMap = Config().GetMap(configNodeKey)
}
if len(configMap) == 0 && !gdb.IsConfigured() {
if !Config().Available() {
configFilePath, err := Config().GetFilePath()
if configFilePath == "" {
exampleFileName := "config.example.toml"
if Config().Available(exampleFileName) {
panic(gerror.Newf(
if exampleConfigFilePath, _ := Config().GetFilePath(exampleFileName); exampleConfigFilePath != "" {
panic(gerror.Wrapf(
err,
`configuration file "%s" not found, but found "%s", did you miss renaming the configuration example file?`,
Config().GetFileName(),
exampleFileName,
))
} else {
panic(gerror.Newf(
panic(gerror.Wrapf(
err,
`configuration file "%s" not found, did you miss the configuration file or the file name setting?`,
Config().GetFileName(),
))
}
}
panic(gerror.Newf(
panic(gerror.Wrapf(
err,
`database initialization failed: "%s" node not found, is configuration file or configuration node missing?`,
configNodeNameDatabase,
))

View File

@ -47,7 +47,14 @@ func Redis(name ...string) *gredis.Redis {
panic(fmt.Sprintf(`configuration for redis not found for group "%s"`, group))
}
} else {
panic(fmt.Sprintf(`incomplete configuration for redis: "redis" node not found in config file "%s"`, config.FilePath()))
filepath, err := config.GetFilePath()
if err != nil {
panic(err)
}
panic(fmt.Sprintf(
`incomplete configuration for redis: "redis" node not found in config file "%s"`,
filepath,
))
}
return nil
})

9
go.mod
View File

@ -1,6 +1,6 @@
module github.com/gogf/gf
go 1.11
go 1.14
require (
github.com/BurntSushi/toml v0.3.1
@ -10,8 +10,11 @@ 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/olekukonko/tablewriter v0.0.1
go.opentelemetry.io/otel v0.16.0
github.com/mattn/go-runewidth v0.0.10 // indirect
github.com/olekukonko/tablewriter v0.0.5
go.opentelemetry.io/otel v0.19.0
go.opentelemetry.io/otel/oteltest v0.19.0
go.opentelemetry.io/otel/trace v0.19.0
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102
golang.org/x/text v0.3.4
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c

59
go.sum Normal file
View File

@ -0,0 +1,59 @@
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/clbanning/mxj v1.8.5-0.20200714211355-ff02cfb8ea28 h1:LdXxtjzvZYhhUaonAaAKArG3pyC67kGL3YY+6hGG8G4=
github.com/clbanning/mxj v1.8.5-0.20200714211355-ff02cfb8ea28/go.mod h1:BVjHeAH+rl9rs6f+QIpeRl0tfu10SXn1pUSa5PVGJng=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/gomodule/redigo v2.0.0+incompatible h1:K/R+8tc58AaqLkqG2Ol3Qk+DR/TlNuhuh457pBFPtt0=
github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM=
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/grokify/html-strip-tags-go v0.0.0-20190921062105-daaa06bf1aaf h1:wIOAyJMMen0ELGiFzlmqxdcV1yGbkyHBAB6PolcNbLA=
github.com/grokify/html-strip-tags-go v0.0.0-20190921062105-daaa06bf1aaf/go.mod h1:2Su6romC5/1VXOQMaWL2yb618ARB8iVo6/DR99A6d78=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.10 h1:CoZ3S2P7pvtP45xOtBw+/mDL2z0RKI576gSkzRRpdGg=
github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rivo/uniseg v0.1.0 h1:+2KBaVoUmb9XzDsrx/Ct0W/EYOSFf/nWTauy++DprtY=
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
go.opentelemetry.io/otel v0.19.0 h1:Lenfy7QHRXPZVsw/12CWpxX6d/JkrX8wrx2vO8G80Ng=
go.opentelemetry.io/otel v0.19.0/go.mod h1:j9bF567N9EfomkSidSfmMwIwIBuP37AMAIzVW85OxSg=
go.opentelemetry.io/otel/metric v0.19.0 h1:dtZ1Ju44gkJkYvo+3qGqVXmf88tc+a42edOywypengg=
go.opentelemetry.io/otel/metric v0.19.0/go.mod h1:8f9fglJPRnXuskQmKpnad31lcLJ2VmNNqIsx/uIwBSc=
go.opentelemetry.io/otel/oteltest v0.19.0 h1:YVfA0ByROYqTwOxqHVZYZExzEpfZor+MU1rU+ip2v9Q=
go.opentelemetry.io/otel/oteltest v0.19.0/go.mod h1:tI4yxwh8U21v7JD6R3BcA/2+RBoTKFexE/PJ/nSO7IA=
go.opentelemetry.io/otel/trace v0.19.0 h1:1ucYlenXIDA1OlHVLDZKX0ObXV5RLaq06DtUKz5e5zc=
go.opentelemetry.io/otel/trace v0.19.0/go.mod h1:4IXiNextNOpPnRlI4ryK69mn5iC84bjBWZQA5DXz/qg=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102 h1:42cLlJJdEh+ySyeUUbEQ5bsTiq8voBeTuweGVkY6Puw=
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.4 h1:0YWbFKbhXG/wIiuHDSKpS0Iy7FSA+u45VtBMfQcFTTc=
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@ -7,45 +7,40 @@
// Package gi18n implements internationalization and localization.
package gi18n
var (
// defaultManager is the default i18n instance for package functions.
defaultManager = Instance()
)
// SetPath sets the directory path storing i18n files.
func SetPath(path string) error {
return defaultManager.SetPath(path)
return Instance().SetPath(path)
}
// SetLanguage sets the language for translator.
func SetLanguage(language string) {
defaultManager.SetLanguage(language)
Instance().SetLanguage(language)
}
// SetDelimiters sets the delimiters for translator.
func SetDelimiters(left, right string) {
defaultManager.SetDelimiters(left, right)
Instance().SetDelimiters(left, right)
}
// T is alias of Translate for convenience.
func T(content string, language ...string) string {
return defaultManager.T(content, language...)
return Instance().T(content, language...)
}
// Tf is alias of TranslateFormat for convenience.
func Tf(format string, values ...interface{}) string {
return defaultManager.TranslateFormat(format, values...)
return Instance().TranslateFormat(format, values...)
}
// Tfl is alias of TranslateFormatLang for convenience.
func Tfl(language string, format string, values ...interface{}) string {
return defaultManager.TranslateFormatLang(language, format, values...)
return Instance().TranslateFormatLang(language, format, values...)
}
// TranslateFormat translates, formats and returns the <format> with configured language
// and given <values>.
func TranslateFormat(format string, values ...interface{}) string {
return defaultManager.TranslateFormat(format, values...)
return Instance().TranslateFormat(format, values...)
}
// TranslateFormatLang translates, formats and returns the <format> with configured language
@ -53,17 +48,17 @@ func TranslateFormat(format string, values ...interface{}) string {
// configured language. If <language> is given empty string, it uses the default configured
// language for the translation.
func TranslateFormatLang(language string, format string, values ...interface{}) string {
return defaultManager.TranslateFormatLang(format, language, values...)
return Instance().TranslateFormatLang(language, format, values...)
}
// Translate translates <content> with configured language and returns the translated content.
// The parameter <language> specifies custom translation language ignoring configured language.
func Translate(content string, language ...string) string {
return defaultManager.Translate(content, language...)
return Instance().Translate(content, language...)
}
// GetValue retrieves and returns the configured content for given key and specified language.
// It returns an empty string if not found.
func GetContent(key string, language ...string) string {
return defaultManager.GetContent(key, language...)
return Instance().GetContent(key, language...)
}

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

@ -9,6 +9,7 @@ package empty
import (
"reflect"
"time"
)
// apiString is used for type assert api for String().
@ -26,8 +27,13 @@ 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,
type apiTime interface {
Date() (year int, month time.Month, day int)
IsZero() bool
}
// 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 {
@ -80,6 +86,12 @@ func IsEmpty(value interface{}) bool {
return len(value) == 0
default:
// Common interfaces checks.
if f, ok := value.(apiTime); ok {
if f == nil {
return true
}
return f.IsZero()
}
if f, ok := value.(apiString); ok {
if f == nil {
return true
@ -151,9 +163,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

@ -0,0 +1,108 @@
// 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 "reflect"
// 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,
}
}
// Kind returns the reflect.Kind for Value of Field `f`.
func (f *Field) Kind() reflect.Kind {
return f.Value.Kind()
}
// OriginalKind retrieves and returns the original reflect.Kind for Value of Field `f`.
func (f *Field) OriginalKind() reflect.Kind {
var (
kind = f.Value.Kind()
value = f.Value
)
for kind == reflect.Ptr {
value = value.Elem()
kind = value.Kind()
}
return kind
}
// FieldMap retrieves and returns struct field as map[name/tag]*Field from `pointer`.
//
// The parameter `pointer` should be type of struct/*struct.
//
// 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 FieldMap(pointer interface{}, priority []string) (map[string]*Field, error) {
fields, err := getFieldValues(pointer)
if err != nil {
return nil, err
}
var (
tagValue = ""
mapField = make(map[string]*Field)
)
for _, field := range fields {
// Only retrieve exported attributes.
if !field.IsExported() {
continue
}
tagValue = ""
for _, p := range priority {
tagValue = field.Tag(p)
if tagValue != "" && tagValue != "-" {
break
}
}
tempField := field
tempField.TagValue = tagValue
if tagValue != "" {
mapField[tagValue] = tempField
} else {
if field.IsEmbedded() {
m, err := FieldMap(field.Value, priority)
if err != nil {
return nil, err
}
for k, v := range m {
if _, ok := mapField[k]; !ok {
tempV := v
mapField[k] = tempV
}
}
} else {
mapField[field.Name()] = tempField
}
}
}
return mapField, nil
}

View File

@ -1,59 +0,0 @@
// 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
// MapField retrieves struct field as map[name/tag]*Field from <pointer>, and returns the map.
//
// The parameter <pointer> should be type of struct/*struct.
//
// The parameter <priority> specifies the priority tag array for retrieving from high to low.
//
// 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) {
fields, err := getFieldValues(pointer)
if err != nil {
return nil, err
}
var (
tagValue = ""
mapField = make(map[string]*Field)
)
for _, field := range fields {
// Only retrieve exported attributes.
if !field.IsExported() {
continue
}
tagValue = ""
for _, p := range priority {
tagValue = field.Tag(p)
if tagValue != "" && tagValue != "-" {
break
}
}
tempField := field
tempField.TagValue = tagValue
if tagValue != "" {
mapField[tagValue] = tempField
} else {
if field.IsEmbedded() {
m, err := MapField(field.value, priority)
if err != nil {
return nil, err
}
for k, v := range m {
if _, ok := mapField[k]; !ok {
tempV := v
mapField[k] = tempV
}
}
} else {
mapField[field.Name()] = tempField
}
}
}
return mapField, nil
}

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

@ -13,9 +13,10 @@ import (
"github.com/gogf/gf/net/ghttp/internal/client"
"github.com/gogf/gf/net/ghttp/internal/httputil"
"github.com/gogf/gf/net/gtrace"
"github.com/gogf/gf/text/gstr"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/codes"
"go.opentelemetry.io/otel/label"
"go.opentelemetry.io/otel/propagation"
"go.opentelemetry.io/otel/trace"
"io/ioutil"
@ -23,9 +24,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 +41,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(), propagation.HeaderCarrier(r.Header))
ctx, span := tr.Start(ctx, r.URL.String(), trace.WithSpanKind(trace.SpanKindServer))
defer span.End()
@ -58,20 +52,17 @@ func MiddlewareServerTracing(r *Request) {
r.SetCtx(ctx)
// Request content logging.
var reqBodyContent string
if r.ContentLength <= tracingMaxContentLogSize {
reqBodyContentBytes, _ := ioutil.ReadAll(r.Body)
r.Body = utils.NewReadCloser(reqBodyContentBytes, false)
reqBodyContent = string(reqBodyContentBytes)
} else {
reqBodyContent = fmt.Sprintf(
"[Request Body Too Large For Tracing, Max: %d bytes]",
tracingMaxContentLogSize,
)
}
reqBodyContentBytes, _ := ioutil.ReadAll(r.Body)
r.Body = utils.NewReadCloser(reqBodyContentBytes, false)
span.AddEvent(tracingEventHttpRequest, trace.WithAttributes(
label.Any(tracingEventHttpRequestHeaders, httputil.HeaderToMap(r.Header)),
label.String(tracingEventHttpRequestBody, reqBodyContent),
attribute.Any(tracingEventHttpRequestHeaders, httputil.HeaderToMap(r.Header)),
attribute.Any(tracingEventHttpRequestBaggage, gtrace.GetBaggageMap(ctx)),
attribute.String(tracingEventHttpRequestBody, gstr.StrLimit(
string(reqBodyContentBytes),
gtrace.MaxContentLogSize(),
"...",
)),
))
// Continue executing.
@ -83,17 +74,16 @@ func MiddlewareServerTracing(r *Request) {
}
// Response content logging.
var resBodyContent string
if r.Response.BufferLength() <= tracingMaxContentLogSize {
resBodyContent = r.Response.BufferString()
} else {
resBodyContent = fmt.Sprintf(
"[Response Body Too Large For Tracing, Max: %d bytes]",
tracingMaxContentLogSize,
)
}
resBodyContent = r.Response.BufferString()
resBodyContent = gstr.StrLimit(
r.Response.BufferString(),
gtrace.MaxContentLogSize(),
"...",
)
span.AddEvent(tracingEventHttpResponse, trace.WithAttributes(
label.Any(tracingEventHttpResponseHeaders, httputil.HeaderToMap(r.Response.Header())),
label.String(tracingEventHttpResponseBody, resBodyContent),
attribute.Any(tracingEventHttpResponseHeaders, httputil.HeaderToMap(r.Response.Header())),
attribute.String(tracingEventHttpResponseBody, resBodyContent),
))
return
}

View File

@ -139,14 +139,14 @@ func (r *Request) GetVar(key string, def ...interface{}) *gvar.Var {
// GetRaw is alias of GetBody.
// See GetBody.
// Deprecated.
// Deprecated, use GetBody instead.
func (r *Request) GetRaw() []byte {
return r.GetBody()
}
// GetRawString is alias of GetBodyString.
// See GetBodyString.
// Deprecated.
// Deprecated, use GetBodyString instead.
func (r *Request) GetRawString() string {
return r.GetBodyString()
}

View File

@ -18,7 +18,7 @@ import (
// Note that if there're multiple parameters with the same name, the parameters are retrieved
// and overwrote in order of priority: form > body.
//
// Deprecated.
// Deprecated, use GetForm instead.
func (r *Request) GetPost(key string, def ...interface{}) interface{} {
r.parseForm()
if len(r.formMap) > 0 {
@ -38,82 +38,82 @@ func (r *Request) GetPost(key string, def ...interface{}) interface{} {
return nil
}
// Deprecated.
// Deprecated, use GetFormVar instead.
func (r *Request) GetPostVar(key string, def ...interface{}) *gvar.Var {
return gvar.New(r.GetPost(key, def...))
}
// Deprecated.
// Deprecated, use GetFormString instead.
func (r *Request) GetPostString(key string, def ...interface{}) string {
return r.GetPostVar(key, def...).String()
}
// Deprecated.
// Deprecated, use GetFormBool instead.
func (r *Request) GetPostBool(key string, def ...interface{}) bool {
return r.GetPostVar(key, def...).Bool()
}
// Deprecated.
// Deprecated, use GetFormInt instead.
func (r *Request) GetPostInt(key string, def ...interface{}) int {
return r.GetPostVar(key, def...).Int()
}
// Deprecated.
// Deprecated, use GetFormInt32 instead.
func (r *Request) GetPostInt32(key string, def ...interface{}) int32 {
return r.GetPostVar(key, def...).Int32()
}
// Deprecated.
// Deprecated, use GetFormInt64 instead.
func (r *Request) GetPostInt64(key string, def ...interface{}) int64 {
return r.GetPostVar(key, def...).Int64()
}
// Deprecated.
// Deprecated, use GetFormInts instead.
func (r *Request) GetPostInts(key string, def ...interface{}) []int {
return r.GetPostVar(key, def...).Ints()
}
// Deprecated.
// Deprecated, use GetFormUint instead.
func (r *Request) GetPostUint(key string, def ...interface{}) uint {
return r.GetPostVar(key, def...).Uint()
}
// Deprecated.
// Deprecated, use GetFormUint32 instead.
func (r *Request) GetPostUint32(key string, def ...interface{}) uint32 {
return r.GetPostVar(key, def...).Uint32()
}
// Deprecated.
// Deprecated, use GetFormUint64 instead.
func (r *Request) GetPostUint64(key string, def ...interface{}) uint64 {
return r.GetPostVar(key, def...).Uint64()
}
// Deprecated.
// Deprecated, use GetFormFloat32 instead.
func (r *Request) GetPostFloat32(key string, def ...interface{}) float32 {
return r.GetPostVar(key, def...).Float32()
}
// Deprecated.
// Deprecated, use GetFormFloat64 instead.
func (r *Request) GetPostFloat64(key string, def ...interface{}) float64 {
return r.GetPostVar(key, def...).Float64()
}
// Deprecated.
// Deprecated, use GetFormFloats instead.
func (r *Request) GetPostFloats(key string, def ...interface{}) []float64 {
return r.GetPostVar(key, def...).Floats()
}
// Deprecated.
// Deprecated, use GetFormArray instead.
func (r *Request) GetPostArray(key string, def ...interface{}) []string {
return r.GetPostVar(key, def...).Strings()
}
// Deprecated.
// Deprecated, use GetFormStrings instead.
func (r *Request) GetPostStrings(key string, def ...interface{}) []string {
return r.GetPostVar(key, def...).Strings()
}
// Deprecated.
// Deprecated, use GetFormInterfaces instead.
func (r *Request) GetPostInterfaces(key string, def ...interface{}) []interface{} {
return r.GetPostVar(key, def...).Interfaces()
}

View File

@ -193,7 +193,7 @@ func (s *Server) Start() error {
// If this is a child process, it then notifies its parent exit.
if gproc.IsChild() {
gtimer.SetTimeout(2*time.Second, func() {
gtimer.SetTimeout(time.Duration(s.config.GracefulTimeout)*time.Second, func() {
if err := gproc.Send(gproc.PPid(), []byte("exit"), adminGProcCommGroup); err != nil {
//glog.Error("server error in process communication:", err)
}
@ -268,7 +268,7 @@ func (s *Server) GetRouterArray() []RouterItem {
item.Middleware += gdebug.FuncName(v)
}
}
// If the domain does not exist in the dump map, it create the map.
// If the domain does not exist in the dump map, it creates the map.
// The value of the map is a custom sorted array.
if _, ok := m[item.Domain]; !ok {
// Sort in ASC order.

View File

@ -9,6 +9,6 @@
package ghttp
// registerSignalHandler does nothing on windows platform.
func registerSignalHandler() {
func handleProcessSignal() {
}

View File

@ -218,6 +218,9 @@ type ServerConfig struct {
// Graceful enables graceful reload feature for all servers of the process.
Graceful bool `json:"graceful"`
// GracefulTimeout set the maximum survival time (seconds) of the parent process.
GracefulTimeout uint8 `json:"gracefulTimeout"`
}
// Deprecated. Use NewConfig instead.
@ -265,6 +268,7 @@ func NewConfig() ServerConfig {
FormParsingMemory: 1024 * 1024, // 1MB
Rewrites: make(map[string]string),
Graceful: false,
GracefulTimeout: 2, // seconds
}
}

View File

@ -160,14 +160,14 @@ func (c *Cookie) Remove(key string) {
"",
c.request.Server.GetCookieDomain(),
c.request.Server.GetCookiePath(),
-86400,
-24*time.Hour,
)
}
// RemoveCookie deletes specified key and its value from cookie using given domain and path.
// It actually tells the http client that the cookie is expired, do not send it to server next time.
func (c *Cookie) RemoveCookie(key, domain, path string) {
c.SetCookie(key, "", domain, path, -86400)
c.SetCookie(key, "", domain, path, -24*time.Hour)
}
// Flush outputs the cookie items to client.

View File

@ -80,9 +80,9 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// Close the request and response body
// to release the file descriptor in time.
request.Request.Body.Close()
_ = request.Request.Body.Close()
if request.Request.Response != nil {
request.Request.Response.Body.Close()
_ = request.Request.Response.Body.Close()
}
}()
@ -168,7 +168,8 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}
// Automatically set the session id to cookie
// if it creates a new session id in this request.
// if it creates a new session id in this request
// and SessionCookieOutput is enabled.
if s.config.SessionCookieOutput &&
request.Session.IsDirty() &&
request.Session.Id() != request.GetSessionId() {

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

@ -52,7 +52,5 @@ func Test_Client_Request_13_Dump(t *testing.T) {
t.Assert(gstr.Contains(dumpedText3, "test_for_request_body"), true)
dumpedText4 := r2.RawResponse()
t.Assert(gstr.Contains(dumpedText4, "test_for_request_body"), false)
})
}

Some files were not shown because too many files have changed in this diff Show More