mirror of
https://gitee.com/johng/gf
synced 2026-06-09 19:13:58 +08:00
Compare commits
131 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6a80091fef | |||
| 742653ce75 | |||
| a8c3d07d9f | |||
| df1ef5db78 | |||
| 4b15ab5e99 | |||
| cdc97e9b2b | |||
| bd84b97614 | |||
| d7eb1cca07 | |||
| 5856f74d83 | |||
| 563509c4a6 | |||
| d0753fa527 | |||
| 524e3bedc7 | |||
| 30b60754e3 | |||
| 88c49dc2cb | |||
| bb3dcc2622 | |||
| 5cca0640d9 | |||
| fb48ceeeee | |||
| 90f4bba8fd | |||
| 802187abc4 | |||
| 49dd17c047 | |||
| a3b94c24de | |||
| ac4485dc84 | |||
| 4d1e25cdab | |||
| ec00e99477 | |||
| a73dca3e50 | |||
| ed71d2b2ac | |||
| 3911c2e0a2 | |||
| 1b6765db50 | |||
| 345fb69521 | |||
| 83a6abc70f | |||
| cfef726205 | |||
| de0f9e728c | |||
| d9754a532b | |||
| dc51023c61 | |||
| 87ce5c5419 | |||
| c4fc9f9618 | |||
| 8d7aa5db83 | |||
| 9c70fe3be8 | |||
| 78027d2ec6 | |||
| d4e4b9addf | |||
| b2acd22f58 | |||
| ae5ecb5bfa | |||
| 6f1340ce36 | |||
| 473fdba14f | |||
| 93a01a1aaf | |||
| 7c51ff4707 | |||
| bb6883213f | |||
| 54395b39a6 | |||
| 6deb817fd0 | |||
| 482e093331 | |||
| 042f903157 | |||
| c423ad4830 | |||
| 958b109a12 | |||
| 113fffdd69 | |||
| 4aaf09fded | |||
| 2f3df76f37 | |||
| cf7706b16d | |||
| 36963c05a2 | |||
| 48d840a1b8 | |||
| e3ebc908bb | |||
| 9ed8d8c113 | |||
| 9eab5daf19 | |||
| 6a24b595f0 | |||
| 150f237f13 | |||
| 17233084f1 | |||
| 41f2138b39 | |||
| 6376b8aaa6 | |||
| 58362ad143 | |||
| d72d23c2eb | |||
| 7702c5bfde | |||
| 20f2a6c003 | |||
| 0d4c1c47d5 | |||
| 4d32733790 | |||
| 0e58b6e95b | |||
| 19bfc48dca | |||
| ae0cc5a4b6 | |||
| ba74e0beb2 | |||
| e5ca4e788e | |||
| 1c7f034135 | |||
| 4c78ce6e3f | |||
| 7803d557b3 | |||
| 204fea395c | |||
| bd13de2b39 | |||
| d0f649b328 | |||
| 04e42d2175 | |||
| ffc88eaaa7 | |||
| 4b4cc5ebb9 | |||
| 0e6cddb547 | |||
| a69aec9070 | |||
| d2bd37962e | |||
| eb6763b0fd | |||
| d330afdd36 | |||
| 5db4bbc186 | |||
| e00f2666ff | |||
| 0238cdd5ec | |||
| 2c34d96b9d | |||
| 65b3630f6d | |||
| 54b629561e | |||
| 285ad36e7d | |||
| 5adde275fc | |||
| a3fa10d820 | |||
| acf47f3907 | |||
| b4d5335e43 | |||
| 39fb842e9b | |||
| 7a5d86b44d | |||
| 397e11e124 | |||
| f4314c318e | |||
| 9bb5536163 | |||
| 2344701f22 | |||
| 3bb909ba4f | |||
| 8ae0bd148b | |||
| 72251b880a | |||
| fefde4c290 | |||
| 3b2bae6128 | |||
| ae559b57de | |||
| 6135085d61 | |||
| 40bdc76af1 | |||
| d4f982a9cf | |||
| 813841bb68 | |||
| 930e63e6b9 | |||
| ce40d139e7 | |||
| 8368e11827 | |||
| e6b4662ec2 | |||
| 28f83d3d32 | |||
| 3e33d66ab4 | |||
| 13248d6736 | |||
| 8bd24724e7 | |||
| 80248e9a6e | |||
| 2734903886 | |||
| 2451b40d3e | |||
| 9fb6227461 |
9
.example/database/gdb/mysql/gdb_distinct.go
Normal file
9
.example/database/gdb/mysql/gdb_distinct.go
Normal file
@ -0,0 +1,9 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/frame/g"
|
||||
)
|
||||
|
||||
func main() {
|
||||
g.DB().Model("user").Distinct().CountColumn("uid,name")
|
||||
}
|
||||
27
.example/database/gdb/mysql/gdb_transaction.go
Normal file
27
.example/database/gdb/mysql/gdb_transaction.go
Normal 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)
|
||||
}
|
||||
}
|
||||
34
.example/database/gdb/mysql/gdb_transaction_closure.go
Normal file
34
.example/database/gdb/mysql/gdb_transaction_closure.go
Normal 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)
|
||||
}
|
||||
}
|
||||
40
.example/database/gdb/mysql/gdb_transaction_savepoint.go
Normal file
40
.example/database/gdb/mysql/gdb_transaction_savepoint.go
Normal 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)
|
||||
}
|
||||
}
|
||||
66
.example/database/gdb/mysql/gdb_with_insert.go
Normal file
66
.example/database/gdb/mysql/gdb_with_insert.go
Normal 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
|
||||
})
|
||||
}
|
||||
37
.example/database/gdb/mysql/gdb_with_slect.go
Normal file
37
.example/database/gdb/mysql/gdb_with_slect.go
Normal 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)
|
||||
}
|
||||
@ -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")
|
||||
|
||||
@ -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
1
.gitignore
vendored
@ -14,5 +14,4 @@ bin/
|
||||
cbuild
|
||||
**/.DS_Store
|
||||
.vscode/
|
||||
go.sum
|
||||
.example/other/
|
||||
|
||||
@ -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:
|
||||
|
||||
30
README.MD
30
README.MD
@ -1,7 +1,7 @@
|
||||
# GoFrame
|
||||
|
||||
[](https://godoc.org/github.com/gogf/gf)
|
||||
[](https://travis-ci.org/gogf/gf)
|
||||
[](https://travis-ci.org/gogf/gf)
|
||||
[](https://goreportcard.com/report/github.com/gogf/gf)
|
||||
[](https://codecov.io/gh/gogf/gf/branch/master)
|
||||
[](https://github.com/gogf/gf)
|
||||
@ -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 Group:Add friend`389961817` in WeChat, commenting `GF`
|
||||
- Issues:https://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>
|
||||
|
||||
|
||||
|
||||
|
||||
50
README_ZH.MD
50
README_ZH.MD
@ -1,6 +1,6 @@
|
||||
# GoFrame
|
||||
[](https://godoc.org/github.com/gogf/gf)
|
||||
[](https://travis-ci.org/gogf/gf)
|
||||
[](https://travis-ci.org/gogf/gf)
|
||||
[](https://goreportcard.com/report/github.com/gogf/gf)
|
||||
[](https://codecov.io/gh/gogf/gf/branch/master)
|
||||
[](https://github.com/gogf/gf)
|
||||
@ -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`
|
||||
- 主库ISSUE:https://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>
|
||||
|
||||
|
||||
@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,5 +7,4 @@
|
||||
// Package gtree provides concurrent-safe/unsafe tree containers.
|
||||
//
|
||||
// Some implements are from: https://github.com/emirpasic/gods
|
||||
// Thanks!
|
||||
package gtree
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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,
|
||||
)
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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),
|
||||
))
|
||||
}
|
||||
|
||||
@ -11,31 +11,31 @@ import (
|
||||
"database/sql"
|
||||
)
|
||||
|
||||
// GetMaster acts like function Master but with additional <schema> parameter specifying
|
||||
// GetMaster acts like function Master but with additional `schema` parameter specifying
|
||||
// the schema for the connection. It is defined for internal usage.
|
||||
// Also see Master.
|
||||
func (c *Core) GetMaster(schema ...string) (*sql.DB, error) {
|
||||
return c.getSqlDb(true, schema...)
|
||||
}
|
||||
|
||||
// GetSlave acts like function Slave but with additional <schema> parameter specifying
|
||||
// GetSlave acts like function Slave but with additional `schema` parameter specifying
|
||||
// the schema for the connection. It is defined for internal usage.
|
||||
// Also see Slave.
|
||||
func (c *Core) GetSlave(schema ...string) (*sql.DB, error) {
|
||||
return c.getSqlDb(false, schema...)
|
||||
}
|
||||
|
||||
// QuoteWord checks given string <s> a word, if true quotes it with security chars of the database
|
||||
// and returns the quoted string; or else return <s> without any change.
|
||||
// QuoteWord checks given string `s` a word, if true quotes it with security chars of the database
|
||||
// and returns the quoted string; or else return `s` without any change.
|
||||
func (c *Core) QuoteWord(s string) string {
|
||||
charLeft, charRight := c.DB.GetChars()
|
||||
charLeft, charRight := c.db.GetChars()
|
||||
return doQuoteWord(s, charLeft, charRight)
|
||||
}
|
||||
|
||||
// QuoteString quotes string with quote chars. Strings like:
|
||||
// "user", "user u", "user,user_detail", "user u, user_detail ut", "u.id asc".
|
||||
func (c *Core) QuoteString(s string) string {
|
||||
charLeft, charRight := c.DB.GetChars()
|
||||
charLeft, charRight := c.db.GetChars()
|
||||
return doQuoteString(s, charLeft, charRight)
|
||||
}
|
||||
|
||||
@ -49,8 +49,8 @@ func (c *Core) QuoteString(s string) string {
|
||||
// Note that, this will automatically checks the table prefix whether already added,
|
||||
// if true it does nothing to the table name, or else adds the prefix to the table name.
|
||||
func (c *Core) QuotePrefixTableName(table string) string {
|
||||
charLeft, charRight := c.DB.GetChars()
|
||||
return doHandleTableName(table, c.DB.GetPrefix(), charLeft, charRight)
|
||||
charLeft, charRight := c.db.GetChars()
|
||||
return doHandleTableName(table, c.db.GetPrefix(), charLeft, charRight)
|
||||
}
|
||||
|
||||
// GetChars returns the security char for current database.
|
||||
|
||||
@ -185,12 +185,12 @@ func (d *DriverMssql) parseSql(sql string) string {
|
||||
// It's mainly used in cli tool chain for automatically generating the models.
|
||||
func (d *DriverMssql) Tables(schema ...string) (tables []string, err error) {
|
||||
var result Result
|
||||
link, err := d.DB.GetSlave(schema...)
|
||||
link, err := d.db.GetSlave(schema...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result, err = d.DB.DoGetAll(link, `SELECT NAME FROM SYSOBJECTS WHERE XTYPE='U' AND STATUS >= 0 ORDER BY NAME`)
|
||||
result, err = d.db.DoGetAll(link, `SELECT NAME FROM SYSOBJECTS WHERE XTYPE='U' AND STATUS >= 0 ORDER BY NAME`)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -23,7 +23,7 @@ func isSubQuery(s string) bool {
|
||||
}
|
||||
|
||||
// LeftJoin does "LEFT JOIN ... ON ..." statement on the model.
|
||||
// The parameter <table> can be joined table and its joined condition,
|
||||
// The parameter `table` can be joined table and its joined condition,
|
||||
// and also with its alias name, like:
|
||||
// Table("user").LeftJoin("user_detail", "user_detail.uid=user.uid")
|
||||
// Table("user", "u").LeftJoin("user_detail", "ud", "ud.uid=u.uid")
|
||||
@ -33,7 +33,7 @@ func (m *Model) LeftJoin(table ...string) *Model {
|
||||
}
|
||||
|
||||
// RightJoin does "RIGHT JOIN ... ON ..." statement on the model.
|
||||
// The parameter <table> can be joined table and its joined condition,
|
||||
// The parameter `table` can be joined table and its joined condition,
|
||||
// and also with its alias name, like:
|
||||
// Table("user").RightJoin("user_detail", "user_detail.uid=user.uid")
|
||||
// Table("user", "u").RightJoin("user_detail", "ud", "ud.uid=u.uid")
|
||||
@ -43,7 +43,7 @@ func (m *Model) RightJoin(table ...string) *Model {
|
||||
}
|
||||
|
||||
// InnerJoin does "INNER JOIN ... ON ..." statement on the model.
|
||||
// The parameter <table> can be joined table and its joined condition,
|
||||
// The parameter `table` can be joined table and its joined condition,
|
||||
// and also with its alias name, like:
|
||||
// Table("user").InnerJoin("user_detail", "user_detail.uid=user.uid")
|
||||
// Table("user", "u").InnerJoin("user_detail", "ud", "ud.uid=u.uid")
|
||||
@ -53,7 +53,7 @@ func (m *Model) InnerJoin(table ...string) *Model {
|
||||
}
|
||||
|
||||
// doJoin does "LEFT/RIGHT/INNER JOIN ... ON ..." statement on the model.
|
||||
// The parameter <table> can be joined table and its joined condition,
|
||||
// The parameter `table` can be joined table and its joined condition,
|
||||
// and also with its alias name, like:
|
||||
// Table("user").InnerJoin("user_detail", "user_detail.uid=user.uid")
|
||||
// Table("user", "u").InnerJoin("user_detail", "ud", "ud.uid=u.uid")
|
||||
|
||||
@ -13,15 +13,15 @@ func (m *Model) Option(option int) *Model {
|
||||
return model
|
||||
}
|
||||
|
||||
// OptionOmitEmpty sets OPTION_OMITEMPTY option for the model, which automatically filers
|
||||
// OptionOmitEmpty sets OptionOmitEmpty option for the model, which automatically filers
|
||||
// the data and where attributes for empty values.
|
||||
// Deprecated, use OmitEmpty instead.
|
||||
func (m *Model) OptionOmitEmpty() *Model {
|
||||
return m.Option(OPTION_OMITEMPTY)
|
||||
return m.Option(OptionOmitEmpty)
|
||||
}
|
||||
|
||||
// OmitEmpty sets OPTION_OMITEMPTY option for the model, which automatically filers
|
||||
// OmitEmpty sets OptionOmitEmpty option for the model, which automatically filers
|
||||
// the data and where attributes for empty values.
|
||||
func (m *Model) OmitEmpty() *Model {
|
||||
return m.Option(OPTION_OMITEMPTY)
|
||||
return m.Option(OptionOmitEmpty)
|
||||
}
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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(
|
||||
|
||||
@ -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()
|
||||
}
|
||||
|
||||
@ -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))
|
||||
|
||||
260
database/gdb/gdb_model_with.go
Normal file
260
database/gdb/gdb_model_with.go
Normal 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
|
||||
}
|
||||
@ -16,7 +16,7 @@ type Schema struct {
|
||||
// Schema creates and returns a schema.
|
||||
func (c *Core) Schema(schema string) *Schema {
|
||||
return &Schema{
|
||||
db: c.DB,
|
||||
db: c.db,
|
||||
schema: schema,
|
||||
}
|
||||
}
|
||||
@ -31,14 +31,14 @@ func (tx *TX) Schema(schema string) *Schema {
|
||||
}
|
||||
|
||||
// Table creates and returns a new ORM model.
|
||||
// The parameter <tables> can be more than one table names, like :
|
||||
// The parameter `tables` can be more than one table names, like :
|
||||
// "user", "user u", "user, user_detail", "user u, user_detail ud"
|
||||
func (s *Schema) Table(table string) *Model {
|
||||
var m *Model
|
||||
if s.tx != nil {
|
||||
m = s.tx.Table(table)
|
||||
m = s.tx.Model(table)
|
||||
} else {
|
||||
m = s.db.Table(table)
|
||||
m = s.db.Model(table)
|
||||
}
|
||||
// Do not change the schema of the original db,
|
||||
// it here creates a new db and changes its schema.
|
||||
|
||||
@ -69,11 +69,11 @@ func (s *Stmt) doStmtCommit(stmtType string, ctx context.Context, args ...interf
|
||||
Error: err,
|
||||
Start: timestampMilli1,
|
||||
End: timestampMilli2,
|
||||
Group: s.core.DB.GetGroup(),
|
||||
Group: s.core.db.GetGroup(),
|
||||
}
|
||||
)
|
||||
s.core.addSqlToTracing(ctx, sqlObj)
|
||||
if s.core.DB.GetDebug() {
|
||||
if s.core.db.GetDebug() {
|
||||
s.core.writeSqlToLogger(sqlObj)
|
||||
}
|
||||
return result, err
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
@ -11,11 +11,12 @@ import (
|
||||
"github.com/gogf/gf/errors/gerror"
|
||||
"github.com/gogf/gf/text/gstr"
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
"github.com/gogf/gf/util/gutil"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// ScanList converts <r> to struct slice which contains other complex struct attributes.
|
||||
// Note that the parameter <listPointer> should be type of *[]struct/*[]*struct.
|
||||
// ScanList converts `r` to struct slice which contains other complex struct attributes.
|
||||
// Note that the parameter `listPointer` should be type of *[]struct/*[]*struct.
|
||||
// Usage example:
|
||||
//
|
||||
// type Entity struct {
|
||||
@ -37,10 +38,13 @@ import (
|
||||
// The "uid" in the example codes is the table field name of the result, and the "Uid" is the relational
|
||||
// struct attribute name - not the attribute name of the bound to target. In the example codes, it's attribute
|
||||
// name "Uid" of "User" of entity "Entity". It automatically calculates the HasOne/HasMany relationship with
|
||||
// given <relation> parameter.
|
||||
// given `relation` parameter.
|
||||
//
|
||||
// See the example or unit testing cases for clear understanding for this function.
|
||||
func (r Result) ScanList(listPointer interface{}, bindToAttrName string, relationKV ...string) (err error) {
|
||||
if r.IsEmpty() {
|
||||
return nil
|
||||
}
|
||||
// Necessary checks for parameters.
|
||||
if bindToAttrName == "" {
|
||||
return gerror.New(`bindToAttrName should not be empty`)
|
||||
@ -112,7 +116,17 @@ func (r Result) ScanList(listPointer interface{}, bindToAttrName string, relatio
|
||||
relationFromAttrName = relationKV[0]
|
||||
relationKVStr = relationKV[1]
|
||||
}
|
||||
array := gstr.SplitAndTrim(relationKVStr, ":")
|
||||
// The relation key string of table filed name and attribute name
|
||||
// can be joined with char '=' or ':'.
|
||||
array := gstr.SplitAndTrim(relationKVStr, "=")
|
||||
if len(array) == 1 {
|
||||
// Compatible with old splitting char ':'.
|
||||
array = gstr.SplitAndTrim(relationKVStr, ":")
|
||||
}
|
||||
if len(array) == 1 {
|
||||
// The relation names are the same.
|
||||
array = []string{relationKVStr, relationKVStr}
|
||||
}
|
||||
if len(array) == 2 {
|
||||
// Defined table field to relation attribute name.
|
||||
// Like:
|
||||
@ -120,6 +134,15 @@ func (r Result) ScanList(listPointer interface{}, bindToAttrName string, relatio
|
||||
// uid:UserId
|
||||
relationResultFieldName = array[0]
|
||||
relationBindToSubAttrName = array[1]
|
||||
if key, _ := gutil.MapPossibleItemByKey(r[0].Map(), relationResultFieldName); key == "" {
|
||||
return gerror.Newf(
|
||||
`cannot find possible related table field name "%s" from given relation key "%s"`,
|
||||
relationResultFieldName,
|
||||
relationKVStr,
|
||||
)
|
||||
} else {
|
||||
relationResultFieldName = key
|
||||
}
|
||||
} else {
|
||||
return gerror.New(`parameter relationKV should be format of "ResultFieldName:BindToAttrName"`)
|
||||
}
|
||||
@ -152,8 +175,9 @@ func (r Result) ScanList(listPointer interface{}, bindToAttrName string, relatio
|
||||
|
||||
// Bind to relation conditions.
|
||||
var (
|
||||
relationFromAttrValue reflect.Value
|
||||
relationFromAttrField reflect.Value
|
||||
relationFromAttrValue reflect.Value
|
||||
relationFromAttrField reflect.Value
|
||||
relationBindToSubAttrNameChecked bool
|
||||
)
|
||||
for i := 0; i < arrayValue.Len(); i++ {
|
||||
arrayElemValue := arrayValue.Index(i)
|
||||
@ -187,6 +211,29 @@ func (r Result) ScanList(listPointer interface{}, bindToAttrName string, relatio
|
||||
if len(relationDataMap) > 0 && !relationFromAttrValue.IsValid() {
|
||||
return gerror.Newf(`invalid relation specified: "%v"`, relationKV)
|
||||
}
|
||||
// Check and find possible bind to attribute name.
|
||||
if relationKVStr != "" && !relationBindToSubAttrNameChecked {
|
||||
relationFromAttrField = relationFromAttrValue.FieldByName(relationBindToSubAttrName)
|
||||
if !relationFromAttrField.IsValid() {
|
||||
var (
|
||||
relationFromAttrType = relationFromAttrValue.Type()
|
||||
filedMap = make(map[string]interface{})
|
||||
)
|
||||
for i := 0; i < relationFromAttrType.NumField(); i++ {
|
||||
filedMap[relationFromAttrType.Field(i).Name] = struct{}{}
|
||||
}
|
||||
if key, _ := gutil.MapPossibleItemByKey(filedMap, relationBindToSubAttrName); key == "" {
|
||||
return gerror.Newf(
|
||||
`cannot find possible related attribute name "%s" from given relation key "%s"`,
|
||||
relationBindToSubAttrName,
|
||||
relationKVStr,
|
||||
)
|
||||
} else {
|
||||
relationBindToSubAttrName = key
|
||||
}
|
||||
}
|
||||
relationBindToSubAttrNameChecked = true
|
||||
}
|
||||
switch bindToAttrKind {
|
||||
case reflect.Array, reflect.Slice:
|
||||
if len(relationDataMap) > 0 {
|
||||
@ -207,7 +254,12 @@ func (r Result) ScanList(listPointer interface{}, bindToAttrName string, relatio
|
||||
}
|
||||
|
||||
case reflect.Ptr:
|
||||
e := reflect.New(bindToAttrType.Elem()).Elem()
|
||||
var element reflect.Value
|
||||
if bindToAttrValue.IsNil() {
|
||||
element = reflect.New(bindToAttrType.Elem()).Elem()
|
||||
} else {
|
||||
element = bindToAttrValue.Elem()
|
||||
}
|
||||
if len(relationDataMap) > 0 {
|
||||
relationFromAttrField = relationFromAttrValue.FieldByName(relationBindToSubAttrName)
|
||||
if relationFromAttrField.IsValid() {
|
||||
@ -216,7 +268,7 @@ func (r Result) ScanList(listPointer interface{}, bindToAttrName string, relatio
|
||||
// There's no relational data.
|
||||
continue
|
||||
}
|
||||
if err = gconv.Struct(v, e); err != nil {
|
||||
if err = gconv.Struct(v, element); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
@ -224,19 +276,22 @@ func (r Result) ScanList(listPointer interface{}, bindToAttrName string, relatio
|
||||
return gerror.Newf(`invalid relation specified: "%v"`, relationKV)
|
||||
}
|
||||
} else {
|
||||
if i >= len(r) {
|
||||
// There's no relational data.
|
||||
continue
|
||||
}
|
||||
v := r[i]
|
||||
if v == nil {
|
||||
// There's no relational data.
|
||||
continue
|
||||
}
|
||||
if err = gconv.Struct(v, e); err != nil {
|
||||
if err = gconv.Struct(v, element); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
bindToAttrValue.Set(e.Addr())
|
||||
bindToAttrValue.Set(element.Addr())
|
||||
|
||||
case reflect.Struct:
|
||||
e := reflect.New(bindToAttrType).Elem()
|
||||
if len(relationDataMap) > 0 {
|
||||
relationFromAttrField = relationFromAttrValue.FieldByName(relationBindToSubAttrName)
|
||||
if relationFromAttrField.IsValid() {
|
||||
@ -245,7 +300,7 @@ func (r Result) ScanList(listPointer interface{}, bindToAttrName string, relatio
|
||||
// There's no relational data.
|
||||
continue
|
||||
}
|
||||
if err = gconv.Struct(relationDataItem, e); err != nil {
|
||||
if err = gconv.Struct(relationDataItem, bindToAttrValue); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
@ -253,16 +308,19 @@ func (r Result) ScanList(listPointer interface{}, bindToAttrName string, relatio
|
||||
return gerror.Newf(`invalid relation specified: "%v"`, relationKV)
|
||||
}
|
||||
} else {
|
||||
if i >= len(r) {
|
||||
// There's no relational data.
|
||||
continue
|
||||
}
|
||||
relationDataItem := r[i]
|
||||
if relationDataItem == nil {
|
||||
// There's no relational data.
|
||||
continue
|
||||
}
|
||||
if err = gconv.Struct(relationDataItem, e); err != nil {
|
||||
if err = gconv.Struct(relationDataItem, bindToAttrValue); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
bindToAttrValue.Set(e)
|
||||
|
||||
default:
|
||||
return gerror.Newf(`unsupported attribute type: %s`, bindToAttrKind.String())
|
||||
|
||||
@ -56,8 +56,8 @@ func Test_Custom_Driver(t *testing.T) {
|
||||
gdb.AddConfigNode("driver-test", gdb.ConfigNode{
|
||||
Host: "127.0.0.1",
|
||||
Port: "3306",
|
||||
User: USER,
|
||||
Pass: PASS,
|
||||
User: TestDbUser,
|
||||
Pass: TestDbPass,
|
||||
Name: "test",
|
||||
Type: customDriverName,
|
||||
Role: "master",
|
||||
@ -67,7 +67,7 @@ func Test_Custom_Driver(t *testing.T) {
|
||||
t.Assert(latestSqlString.Val(), "")
|
||||
sqlString := "select 10000"
|
||||
value, err := g.DB("driver-test").GetValue(sqlString)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
t.Assert(value, 10000)
|
||||
t.Assert(latestSqlString.Val(), sqlString)
|
||||
})
|
||||
|
||||
@ -18,13 +18,14 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
SIZE = 10
|
||||
TABLE = "user"
|
||||
SCHEMA1 = "test1"
|
||||
SCHEMA2 = "test2"
|
||||
PREFIX1 = "gf_"
|
||||
USER = "root"
|
||||
PASS = "12345678"
|
||||
TableSize = 10
|
||||
TableName = "user"
|
||||
TestSchema1 = "test1"
|
||||
TestSchema2 = "test2"
|
||||
TableNamePrefix1 = "gf_"
|
||||
TestDbUser = "root"
|
||||
TestDbPass = "12345678"
|
||||
CreateTime = "2018-10-24 10:00:00"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -38,12 +39,12 @@ func init() {
|
||||
"name": true,
|
||||
"type": true,
|
||||
}, false)
|
||||
gtest.Assert(err, nil)
|
||||
gtest.AssertNil(err)
|
||||
configNode = gdb.ConfigNode{
|
||||
Host: "127.0.0.1",
|
||||
Port: "3306",
|
||||
User: USER,
|
||||
Pass: PASS,
|
||||
User: TestDbUser,
|
||||
Pass: TestDbPass,
|
||||
Name: parser.GetOpt("name", ""),
|
||||
Type: parser.GetOpt("type", "mysql"),
|
||||
Role: "master",
|
||||
@ -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
668
database/gdb/gdb_z_mysql_association_with_test.go
Normal file
668
database/gdb/gdb_z_mysql_association_with_test.go
Normal 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)
|
||||
})
|
||||
}
|
||||
@ -19,7 +19,7 @@ func Test_Instance(t *testing.T) {
|
||||
t.AssertNE(err, nil)
|
||||
|
||||
db, err := gdb.Instance()
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
|
||||
err1 := db.PingMaster()
|
||||
err2 := db.PingSlave()
|
||||
|
||||
@ -17,7 +17,7 @@ import (
|
||||
func Test_Ctx(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
db, err := gdb.Instance()
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
|
||||
err1 := db.PingMaster()
|
||||
err2 := db.PingSlave()
|
||||
|
||||
@ -17,9 +17,9 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
SCHEMA = "test_internal"
|
||||
USER = "root"
|
||||
PASS = "12345678"
|
||||
SCHEMA = "test_internal"
|
||||
TestDbUser = "root"
|
||||
TestDbPass = "12345678"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -32,12 +32,12 @@ func init() {
|
||||
"name": true,
|
||||
"type": true,
|
||||
}, false)
|
||||
gtest.Assert(err, nil)
|
||||
gtest.AssertNil(err)
|
||||
configNode = ConfigNode{
|
||||
Host: "127.0.0.1",
|
||||
Port: "3306",
|
||||
User: USER,
|
||||
Pass: PASS,
|
||||
User: TestDbUser,
|
||||
Pass: TestDbPass,
|
||||
Name: parser.GetOpt("name", ""),
|
||||
Type: parser.GetOpt("type", "mysql"),
|
||||
Role: "master",
|
||||
@ -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)
|
||||
})
|
||||
}
|
||||
|
||||
@ -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
@ -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)
|
||||
})
|
||||
}
|
||||
|
||||
@ -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)
|
||||
})
|
||||
}
|
||||
|
||||
@ -40,13 +40,13 @@ CREATE TABLE %s (
|
||||
"id": 1,
|
||||
"name": "name_1",
|
||||
}
|
||||
r, err := db.Table(table).Data(dataInsert).Insert()
|
||||
t.Assert(err, nil)
|
||||
r, err := db.Model(table).Data(dataInsert).Insert()
|
||||
t.AssertNil(err)
|
||||
n, _ := r.RowsAffected()
|
||||
t.Assert(n, 1)
|
||||
|
||||
oneInsert, err := db.Table(table).FindOne(1)
|
||||
t.Assert(err, nil)
|
||||
oneInsert, err := db.Model(table).FindOne(1)
|
||||
t.AssertNil(err)
|
||||
t.Assert(oneInsert["id"].Int(), 1)
|
||||
t.Assert(oneInsert["name"].String(), "name_1")
|
||||
t.Assert(oneInsert["delete_at"].String(), "")
|
||||
@ -61,13 +61,13 @@ CREATE TABLE %s (
|
||||
"id": 1,
|
||||
"name": "name_10",
|
||||
}
|
||||
r, err = db.Table(table).Data(dataSave).Save()
|
||||
t.Assert(err, nil)
|
||||
r, err = db.Model(table).Data(dataSave).Save()
|
||||
t.AssertNil(err)
|
||||
n, _ = r.RowsAffected()
|
||||
t.Assert(n, 2)
|
||||
|
||||
oneSave, err := db.Table(table).FindOne(1)
|
||||
t.Assert(err, nil)
|
||||
oneSave, err := db.Model(table).FindOne(1)
|
||||
t.AssertNil(err)
|
||||
t.Assert(oneSave["id"].Int(), 1)
|
||||
t.Assert(oneSave["name"].String(), "name_10")
|
||||
t.Assert(oneSave["delete_at"].String(), "")
|
||||
@ -82,13 +82,13 @@ CREATE TABLE %s (
|
||||
dataUpdate := g.Map{
|
||||
"name": "name_1000",
|
||||
}
|
||||
r, err = db.Table(table).Data(dataUpdate).WherePri(1).Update()
|
||||
t.Assert(err, nil)
|
||||
r, err = db.Model(table).Data(dataUpdate).WherePri(1).Update()
|
||||
t.AssertNil(err)
|
||||
n, _ = r.RowsAffected()
|
||||
t.Assert(n, 1)
|
||||
|
||||
oneUpdate, err := db.Table(table).FindOne(1)
|
||||
t.Assert(err, nil)
|
||||
oneUpdate, err := db.Model(table).FindOne(1)
|
||||
t.AssertNil(err)
|
||||
t.Assert(oneUpdate["id"].Int(), 1)
|
||||
t.Assert(oneUpdate["name"].String(), "name_1000")
|
||||
t.Assert(oneUpdate["delete_at"].String(), "")
|
||||
@ -100,13 +100,13 @@ CREATE TABLE %s (
|
||||
"id": 1,
|
||||
"name": "name_100",
|
||||
}
|
||||
r, err = db.Table(table).Data(dataReplace).Replace()
|
||||
t.Assert(err, nil)
|
||||
r, err = db.Model(table).Data(dataReplace).Replace()
|
||||
t.AssertNil(err)
|
||||
n, _ = r.RowsAffected()
|
||||
t.Assert(n, 2)
|
||||
|
||||
oneReplace, err := db.Table(table).FindOne(1)
|
||||
t.Assert(err, nil)
|
||||
oneReplace, err := db.Model(table).FindOne(1)
|
||||
t.AssertNil(err)
|
||||
t.Assert(oneReplace["id"].Int(), 1)
|
||||
t.Assert(oneReplace["name"].String(), "name_100")
|
||||
t.Assert(oneReplace["delete_at"].String(), "")
|
||||
@ -117,36 +117,36 @@ CREATE TABLE %s (
|
||||
time.Sleep(2 * time.Second)
|
||||
|
||||
// Delete
|
||||
r, err = db.Table(table).Delete("id", 1)
|
||||
t.Assert(err, nil)
|
||||
r, err = db.Model(table).Delete("id", 1)
|
||||
t.AssertNil(err)
|
||||
n, _ = r.RowsAffected()
|
||||
t.Assert(n, 1)
|
||||
// Delete Select
|
||||
one4, err := db.Table(table).FindOne(1)
|
||||
t.Assert(err, nil)
|
||||
one4, err := db.Model(table).FindOne(1)
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(one4), 0)
|
||||
one5, err := db.Table(table).Unscoped().FindOne(1)
|
||||
t.Assert(err, nil)
|
||||
one5, err := db.Model(table).Unscoped().FindOne(1)
|
||||
t.AssertNil(err)
|
||||
t.Assert(one5["id"].Int(), 1)
|
||||
t.AssertGE(one5["delete_at"].GTime().Timestamp(), gtime.Timestamp()-2)
|
||||
// Delete Count
|
||||
i, err := db.Table(table).FindCount()
|
||||
t.Assert(err, nil)
|
||||
i, err := db.Model(table).FindCount()
|
||||
t.AssertNil(err)
|
||||
t.Assert(i, 0)
|
||||
i, err = db.Table(table).Unscoped().FindCount()
|
||||
t.Assert(err, nil)
|
||||
i, err = db.Model(table).Unscoped().FindCount()
|
||||
t.AssertNil(err)
|
||||
t.Assert(i, 1)
|
||||
|
||||
// Delete Unscoped
|
||||
r, err = db.Table(table).Unscoped().Delete("id", 1)
|
||||
t.Assert(err, nil)
|
||||
r, err = db.Model(table).Unscoped().Delete("id", 1)
|
||||
t.AssertNil(err)
|
||||
n, _ = r.RowsAffected()
|
||||
t.Assert(n, 1)
|
||||
one6, err := db.Table(table).Unscoped().FindOne(1)
|
||||
t.Assert(err, nil)
|
||||
one6, err := db.Model(table).Unscoped().FindOne(1)
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(one6), 0)
|
||||
i, err = db.Table(table).Unscoped().FindCount()
|
||||
t.Assert(err, nil)
|
||||
i, err = db.Model(table).Unscoped().FindCount()
|
||||
t.AssertNil(err)
|
||||
t.Assert(i, 0)
|
||||
})
|
||||
}
|
||||
@ -174,13 +174,13 @@ CREATE TABLE %s (
|
||||
"id": 1,
|
||||
"name": "name_1",
|
||||
}
|
||||
r, err := db.Table(table).Data(dataInsert).Insert()
|
||||
t.Assert(err, nil)
|
||||
r, err := db.Model(table).Data(dataInsert).Insert()
|
||||
t.AssertNil(err)
|
||||
n, _ := r.RowsAffected()
|
||||
t.Assert(n, 1)
|
||||
|
||||
oneInsert, err := db.Table(table).FindOne(1)
|
||||
t.Assert(err, nil)
|
||||
oneInsert, err := db.Model(table).FindOne(1)
|
||||
t.AssertNil(err)
|
||||
t.Assert(oneInsert["id"].Int(), 1)
|
||||
t.Assert(oneInsert["name"].String(), "name_1")
|
||||
t.Assert(oneInsert["deleted_at"].String(), "")
|
||||
@ -195,13 +195,13 @@ CREATE TABLE %s (
|
||||
"id": 1,
|
||||
"name": "name_10",
|
||||
}
|
||||
r, err = db.Table(table).Data(dataSave).Save()
|
||||
t.Assert(err, nil)
|
||||
r, err = db.Model(table).Data(dataSave).Save()
|
||||
t.AssertNil(err)
|
||||
n, _ = r.RowsAffected()
|
||||
t.Assert(n, 2)
|
||||
|
||||
oneSave, err := db.Table(table).FindOne(1)
|
||||
t.Assert(err, nil)
|
||||
oneSave, err := db.Model(table).FindOne(1)
|
||||
t.AssertNil(err)
|
||||
t.Assert(oneSave["id"].Int(), 1)
|
||||
t.Assert(oneSave["name"].String(), "name_10")
|
||||
t.Assert(oneSave["deleted_at"].String(), "")
|
||||
@ -216,13 +216,13 @@ CREATE TABLE %s (
|
||||
dataUpdate := g.Map{
|
||||
"name": "name_1000",
|
||||
}
|
||||
r, err = db.Table(table).Data(dataUpdate).WherePri(1).Update()
|
||||
t.Assert(err, nil)
|
||||
r, err = db.Model(table).Data(dataUpdate).WherePri(1).Update()
|
||||
t.AssertNil(err)
|
||||
n, _ = r.RowsAffected()
|
||||
t.Assert(n, 1)
|
||||
|
||||
oneUpdate, err := db.Table(table).FindOne(1)
|
||||
t.Assert(err, nil)
|
||||
oneUpdate, err := db.Model(table).FindOne(1)
|
||||
t.AssertNil(err)
|
||||
t.Assert(oneUpdate["id"].Int(), 1)
|
||||
t.Assert(oneUpdate["name"].String(), "name_1000")
|
||||
t.Assert(oneUpdate["deleted_at"].String(), "")
|
||||
@ -234,13 +234,13 @@ CREATE TABLE %s (
|
||||
"id": 1,
|
||||
"name": "name_100",
|
||||
}
|
||||
r, err = db.Table(table).Data(dataReplace).Replace()
|
||||
t.Assert(err, nil)
|
||||
r, err = db.Model(table).Data(dataReplace).Replace()
|
||||
t.AssertNil(err)
|
||||
n, _ = r.RowsAffected()
|
||||
t.Assert(n, 2)
|
||||
|
||||
oneReplace, err := db.Table(table).FindOne(1)
|
||||
t.Assert(err, nil)
|
||||
oneReplace, err := db.Model(table).FindOne(1)
|
||||
t.AssertNil(err)
|
||||
t.Assert(oneReplace["id"].Int(), 1)
|
||||
t.Assert(oneReplace["name"].String(), "name_100")
|
||||
t.Assert(oneReplace["deleted_at"].String(), "")
|
||||
@ -251,36 +251,36 @@ CREATE TABLE %s (
|
||||
time.Sleep(2 * time.Second)
|
||||
|
||||
// Delete
|
||||
r, err = db.Table(table).Delete("id", 1)
|
||||
t.Assert(err, nil)
|
||||
r, err = db.Model(table).Delete("id", 1)
|
||||
t.AssertNil(err)
|
||||
n, _ = r.RowsAffected()
|
||||
t.Assert(n, 1)
|
||||
// Delete Select
|
||||
one4, err := db.Table(table).FindOne(1)
|
||||
t.Assert(err, nil)
|
||||
one4, err := db.Model(table).FindOne(1)
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(one4), 0)
|
||||
one5, err := db.Table(table).Unscoped().FindOne(1)
|
||||
t.Assert(err, nil)
|
||||
one5, err := db.Model(table).Unscoped().FindOne(1)
|
||||
t.AssertNil(err)
|
||||
t.Assert(one5["id"].Int(), 1)
|
||||
t.AssertGE(one5["deleted_at"].GTime().Timestamp(), gtime.Timestamp()-2)
|
||||
// Delete Count
|
||||
i, err := db.Table(table).FindCount()
|
||||
t.Assert(err, nil)
|
||||
i, err := db.Model(table).FindCount()
|
||||
t.AssertNil(err)
|
||||
t.Assert(i, 0)
|
||||
i, err = db.Table(table).Unscoped().FindCount()
|
||||
t.Assert(err, nil)
|
||||
i, err = db.Model(table).Unscoped().FindCount()
|
||||
t.AssertNil(err)
|
||||
t.Assert(i, 1)
|
||||
|
||||
// Delete Unscoped
|
||||
r, err = db.Table(table).Unscoped().Delete("id", 1)
|
||||
t.Assert(err, nil)
|
||||
r, err = db.Model(table).Unscoped().Delete("id", 1)
|
||||
t.AssertNil(err)
|
||||
n, _ = r.RowsAffected()
|
||||
t.Assert(n, 1)
|
||||
one6, err := db.Table(table).Unscoped().FindOne(1)
|
||||
t.Assert(err, nil)
|
||||
one6, err := db.Model(table).Unscoped().FindOne(1)
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(one6), 0)
|
||||
i, err = db.Table(table).Unscoped().FindCount()
|
||||
t.Assert(err, nil)
|
||||
i, err = db.Model(table).Unscoped().FindCount()
|
||||
t.AssertNil(err)
|
||||
t.Assert(i, 0)
|
||||
})
|
||||
}
|
||||
@ -315,13 +315,13 @@ CREATE TABLE %s (
|
||||
Id: 1,
|
||||
Name: "name_1",
|
||||
}
|
||||
r, err := db.Table(table).Data(dataInsert).Insert()
|
||||
t.Assert(err, nil)
|
||||
r, err := db.Model(table).Data(dataInsert).Insert()
|
||||
t.AssertNil(err)
|
||||
n, _ := r.RowsAffected()
|
||||
t.Assert(n, 1)
|
||||
|
||||
oneInsert, err := db.Table(table).FindOne(1)
|
||||
t.Assert(err, nil)
|
||||
oneInsert, err := db.Model(table).FindOne(1)
|
||||
t.AssertNil(err)
|
||||
t.Assert(oneInsert["id"].Int(), 1)
|
||||
t.Assert(oneInsert["name"].String(), "name_1")
|
||||
t.Assert(oneInsert["deleted_at"].String(), "")
|
||||
@ -336,13 +336,13 @@ CREATE TABLE %s (
|
||||
Id: 1,
|
||||
Name: "name_10",
|
||||
}
|
||||
r, err = db.Table(table).Data(dataSave).OmitEmpty().Save()
|
||||
t.Assert(err, nil)
|
||||
r, err = db.Model(table).Data(dataSave).OmitEmpty().Save()
|
||||
t.AssertNil(err)
|
||||
n, _ = r.RowsAffected()
|
||||
t.Assert(n, 2)
|
||||
|
||||
oneSave, err := db.Table(table).FindOne(1)
|
||||
t.Assert(err, nil)
|
||||
oneSave, err := db.Model(table).FindOne(1)
|
||||
t.AssertNil(err)
|
||||
t.Assert(oneSave["id"].Int(), 1)
|
||||
t.Assert(oneSave["name"].String(), "name_10")
|
||||
t.Assert(oneSave["deleted_at"].String(), "")
|
||||
@ -357,13 +357,13 @@ CREATE TABLE %s (
|
||||
dataUpdate := User{
|
||||
Name: "name_1000",
|
||||
}
|
||||
r, err = db.Table(table).Data(dataUpdate).OmitEmpty().WherePri(1).Update()
|
||||
t.Assert(err, nil)
|
||||
r, err = db.Model(table).Data(dataUpdate).OmitEmpty().WherePri(1).Update()
|
||||
t.AssertNil(err)
|
||||
n, _ = r.RowsAffected()
|
||||
t.Assert(n, 1)
|
||||
|
||||
oneUpdate, err := db.Table(table).FindOne(1)
|
||||
t.Assert(err, nil)
|
||||
oneUpdate, err := db.Model(table).FindOne(1)
|
||||
t.AssertNil(err)
|
||||
t.Assert(oneUpdate["id"].Int(), 1)
|
||||
t.Assert(oneUpdate["name"].String(), "name_1000")
|
||||
t.Assert(oneUpdate["deleted_at"].String(), "")
|
||||
@ -375,13 +375,13 @@ CREATE TABLE %s (
|
||||
Id: 1,
|
||||
Name: "name_100",
|
||||
}
|
||||
r, err = db.Table(table).Data(dataReplace).OmitEmpty().Replace()
|
||||
t.Assert(err, nil)
|
||||
r, err = db.Model(table).Data(dataReplace).OmitEmpty().Replace()
|
||||
t.AssertNil(err)
|
||||
n, _ = r.RowsAffected()
|
||||
t.Assert(n, 2)
|
||||
|
||||
oneReplace, err := db.Table(table).FindOne(1)
|
||||
t.Assert(err, nil)
|
||||
oneReplace, err := db.Model(table).FindOne(1)
|
||||
t.AssertNil(err)
|
||||
t.Assert(oneReplace["id"].Int(), 1)
|
||||
t.Assert(oneReplace["name"].String(), "name_100")
|
||||
t.Assert(oneReplace["deleted_at"].String(), "")
|
||||
@ -392,36 +392,36 @@ CREATE TABLE %s (
|
||||
time.Sleep(2 * time.Second)
|
||||
|
||||
// Delete
|
||||
r, err = db.Table(table).Delete("id", 1)
|
||||
t.Assert(err, nil)
|
||||
r, err = db.Model(table).Delete("id", 1)
|
||||
t.AssertNil(err)
|
||||
n, _ = r.RowsAffected()
|
||||
t.Assert(n, 1)
|
||||
// Delete Select
|
||||
one4, err := db.Table(table).FindOne(1)
|
||||
t.Assert(err, nil)
|
||||
one4, err := db.Model(table).FindOne(1)
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(one4), 0)
|
||||
one5, err := db.Table(table).Unscoped().FindOne(1)
|
||||
t.Assert(err, nil)
|
||||
one5, err := db.Model(table).Unscoped().FindOne(1)
|
||||
t.AssertNil(err)
|
||||
t.Assert(one5["id"].Int(), 1)
|
||||
t.AssertGE(one5["deleted_at"].GTime().Timestamp(), gtime.Timestamp()-2)
|
||||
// Delete Count
|
||||
i, err := db.Table(table).FindCount()
|
||||
t.Assert(err, nil)
|
||||
i, err := db.Model(table).FindCount()
|
||||
t.AssertNil(err)
|
||||
t.Assert(i, 0)
|
||||
i, err = db.Table(table).Unscoped().FindCount()
|
||||
t.Assert(err, nil)
|
||||
i, err = db.Model(table).Unscoped().FindCount()
|
||||
t.AssertNil(err)
|
||||
t.Assert(i, 1)
|
||||
|
||||
// Delete Unscoped
|
||||
r, err = db.Table(table).Unscoped().Delete("id", 1)
|
||||
t.Assert(err, nil)
|
||||
r, err = db.Model(table).Unscoped().Delete("id", 1)
|
||||
t.AssertNil(err)
|
||||
n, _ = r.RowsAffected()
|
||||
t.Assert(n, 1)
|
||||
one6, err := db.Table(table).Unscoped().FindOne(1)
|
||||
t.Assert(err, nil)
|
||||
one6, err := db.Model(table).Unscoped().FindOne(1)
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(one6), 0)
|
||||
i, err = db.Table(table).Unscoped().FindCount()
|
||||
t.Assert(err, nil)
|
||||
i, err = db.Model(table).Unscoped().FindCount()
|
||||
t.AssertNil(err)
|
||||
t.Assert(i, 0)
|
||||
})
|
||||
}
|
||||
@ -448,26 +448,26 @@ CREATE TABLE %s (
|
||||
"id": 1,
|
||||
"num": 10,
|
||||
}
|
||||
r, err := db.Table(table).Data(dataInsert).Insert()
|
||||
t.Assert(err, nil)
|
||||
r, err := db.Model(table).Data(dataInsert).Insert()
|
||||
t.AssertNil(err)
|
||||
n, _ := r.RowsAffected()
|
||||
t.Assert(n, 1)
|
||||
|
||||
oneInsert, err := db.Table(table).FindOne(1)
|
||||
t.Assert(err, nil)
|
||||
oneInsert, err := db.Model(table).FindOne(1)
|
||||
t.AssertNil(err)
|
||||
t.Assert(oneInsert["id"].Int(), 1)
|
||||
t.Assert(oneInsert["num"].Int(), 10)
|
||||
|
||||
// Update.
|
||||
r, err = db.Table(table).Data("num=num+1").Where("id=?", 1).Update()
|
||||
t.Assert(err, nil)
|
||||
r, err = db.Model(table).Data("num=num+1").Where("id=?", 1).Update()
|
||||
t.AssertNil(err)
|
||||
n, _ = r.RowsAffected()
|
||||
t.Assert(n, 1)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_SoftDelete(t *testing.T) {
|
||||
table := "time_test_table"
|
||||
table := "time_test_table_" + gtime.TimestampNanoStr()
|
||||
if _, err := db.Exec(fmt.Sprintf(`
|
||||
CREATE TABLE %s (
|
||||
id int(11) NOT NULL,
|
||||
@ -488,39 +488,39 @@ CREATE TABLE %s (
|
||||
"id": i,
|
||||
"name": fmt.Sprintf("name_%d", i),
|
||||
}
|
||||
r, err := db.Table(table).Data(data).Insert()
|
||||
t.Assert(err, nil)
|
||||
r, err := db.Model(table).Data(data).Insert()
|
||||
t.AssertNil(err)
|
||||
n, _ := r.RowsAffected()
|
||||
t.Assert(n, 1)
|
||||
}
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
one, err := db.Table(table).FindOne(1)
|
||||
t.Assert(err, nil)
|
||||
one, err := db.Model(table).FindOne(1)
|
||||
t.AssertNil(err)
|
||||
t.AssertNE(one["create_at"].String(), "")
|
||||
t.AssertNE(one["update_at"].String(), "")
|
||||
t.Assert(one["delete_at"].String(), "")
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
one, err := db.Table(table).FindOne(10)
|
||||
t.Assert(err, nil)
|
||||
one, err := db.Model(table).FindOne(10)
|
||||
t.AssertNil(err)
|
||||
t.AssertNE(one["create_at"].String(), "")
|
||||
t.AssertNE(one["update_at"].String(), "")
|
||||
t.Assert(one["delete_at"].String(), "")
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
ids := g.SliceInt{1, 3, 5}
|
||||
r, err := db.Table(table).Where("id", ids).Delete()
|
||||
t.Assert(err, nil)
|
||||
r, err := db.Model(table).Where("id", ids).Delete()
|
||||
t.AssertNil(err)
|
||||
n, _ := r.RowsAffected()
|
||||
t.Assert(n, 3)
|
||||
|
||||
count, err := db.Table(table).FindCount(ids)
|
||||
t.Assert(err, nil)
|
||||
count, err := db.Model(table).FindCount(ids)
|
||||
t.AssertNil(err)
|
||||
t.Assert(count, 0)
|
||||
|
||||
all, err := db.Table(table).Unscoped().FindAll(ids)
|
||||
t.Assert(err, nil)
|
||||
all, err := db.Model(table).Unscoped().FindAll(ids)
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(all), 3)
|
||||
t.AssertNE(all[0]["create_at"].String(), "")
|
||||
t.AssertNE(all[0]["update_at"].String(), "")
|
||||
@ -571,8 +571,8 @@ CREATE TABLE %s (
|
||||
"id": 1,
|
||||
"name": "name_1",
|
||||
}
|
||||
r, err := db.Table(table1).Data(dataInsert1).Insert()
|
||||
t.Assert(err, nil)
|
||||
r, err := db.Model(table1).Data(dataInsert1).Insert()
|
||||
t.AssertNil(err)
|
||||
n, _ := r.RowsAffected()
|
||||
t.Assert(n, 1)
|
||||
|
||||
@ -580,33 +580,75 @@ CREATE TABLE %s (
|
||||
"id": 1,
|
||||
"name": "name_2",
|
||||
}
|
||||
r, err = db.Table(table2).Data(dataInsert2).Insert()
|
||||
t.Assert(err, nil)
|
||||
r, err = db.Model(table2).Data(dataInsert2).Insert()
|
||||
t.AssertNil(err)
|
||||
n, _ = r.RowsAffected()
|
||||
t.Assert(n, 1)
|
||||
|
||||
one, err := db.Table(table1, "t1").LeftJoin(table2, "t2", "t2.id=t1.id").Fields("t1.name").FindOne()
|
||||
t.Assert(err, nil)
|
||||
one, err := db.Model(table1, "t1").LeftJoin(table2, "t2", "t2.id=t1.id").Fields("t1.name").FindOne()
|
||||
t.AssertNil(err)
|
||||
t.Assert(one["name"], "name_1")
|
||||
|
||||
// Soft deleting.
|
||||
r, err = db.Table(table1).Delete()
|
||||
t.Assert(err, nil)
|
||||
r, err = db.Model(table1).Delete()
|
||||
t.AssertNil(err)
|
||||
n, _ = r.RowsAffected()
|
||||
t.Assert(n, 1)
|
||||
|
||||
one, err = db.Table(table1, "t1").LeftJoin(table2, "t2", "t2.id=t1.id").Fields("t1.name").FindOne()
|
||||
t.Assert(err, nil)
|
||||
one, err = db.Model(table1, "t1").LeftJoin(table2, "t2", "t2.id=t1.id").Fields("t1.name").FindOne()
|
||||
t.AssertNil(err)
|
||||
t.Assert(one.IsEmpty(), true)
|
||||
|
||||
one, err = db.Table(table2, "t2").LeftJoin(table1, "t1", "t2.id=t1.id").Fields("t2.name").FindOne()
|
||||
t.Assert(err, nil)
|
||||
one, err = db.Model(table2, "t2").LeftJoin(table1, "t1", "t2.id=t1.id").Fields("t2.name").FindOne()
|
||||
t.AssertNil(err)
|
||||
t.Assert(one.IsEmpty(), true)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_SoftDelete_WhereAndOr(t *testing.T) {
|
||||
table := "time_test_table_" + gtime.TimestampNanoStr()
|
||||
if _, err := db.Exec(fmt.Sprintf(`
|
||||
CREATE TABLE %s (
|
||||
id int(11) NOT NULL,
|
||||
name varchar(45) DEFAULT NULL,
|
||||
create_at datetime DEFAULT NULL,
|
||||
update_at datetime DEFAULT NULL,
|
||||
delete_at datetime DEFAULT NULL,
|
||||
PRIMARY KEY (id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
`, table)); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
defer dropTable(table)
|
||||
//db.SetDebug(true)
|
||||
// Add datas.
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
for i := 1; i <= 10; i++ {
|
||||
data := g.Map{
|
||||
"id": i,
|
||||
"name": fmt.Sprintf("name_%d", i),
|
||||
}
|
||||
r, err := db.Model(table).Data(data).Insert()
|
||||
t.AssertNil(err)
|
||||
n, _ := r.RowsAffected()
|
||||
t.Assert(n, 1)
|
||||
}
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
ids := g.SliceInt{1, 3, 5}
|
||||
r, err := db.Model(table).Where("id", ids).Delete()
|
||||
t.AssertNil(err)
|
||||
n, _ := r.RowsAffected()
|
||||
t.Assert(n, 3)
|
||||
|
||||
count, err := db.Model(table).Where("id", 1).Or("id", 3).Count()
|
||||
t.AssertNil(err)
|
||||
t.Assert(count, 0)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_CreateUpdateTime_Struct(t *testing.T) {
|
||||
table := "time_test_table"
|
||||
table := "time_test_table_" + gtime.TimestampNanoStr()
|
||||
if _, err := db.Exec(fmt.Sprintf(`
|
||||
CREATE TABLE %s (
|
||||
id int(11) NOT NULL,
|
||||
@ -637,13 +679,13 @@ CREATE TABLE %s (
|
||||
UpdateAt: nil,
|
||||
DeleteAt: nil,
|
||||
}
|
||||
r, err := db.Table(table).Data(dataInsert).Insert()
|
||||
t.Assert(err, nil)
|
||||
r, err := db.Model(table).Data(dataInsert).Insert()
|
||||
t.AssertNil(err)
|
||||
n, _ := r.RowsAffected()
|
||||
t.Assert(n, 1)
|
||||
|
||||
oneInsert, err := db.Table(table).FindOne(1)
|
||||
t.Assert(err, nil)
|
||||
oneInsert, err := db.Model(table).FindOne(1)
|
||||
t.AssertNil(err)
|
||||
t.Assert(oneInsert["id"].Int(), 1)
|
||||
t.Assert(oneInsert["name"].String(), "name_1")
|
||||
t.Assert(oneInsert["delete_at"].String(), "")
|
||||
@ -660,13 +702,13 @@ CREATE TABLE %s (
|
||||
UpdateAt: nil,
|
||||
DeleteAt: nil,
|
||||
}
|
||||
r, err = db.Table(table).Data(dataSave).Save()
|
||||
t.Assert(err, nil)
|
||||
r, err = db.Model(table).Data(dataSave).Save()
|
||||
t.AssertNil(err)
|
||||
n, _ = r.RowsAffected()
|
||||
t.Assert(n, 2)
|
||||
|
||||
oneSave, err := db.Table(table).FindOne(1)
|
||||
t.Assert(err, nil)
|
||||
oneSave, err := db.Model(table).FindOne(1)
|
||||
t.AssertNil(err)
|
||||
t.Assert(oneSave["id"].Int(), 1)
|
||||
t.Assert(oneSave["name"].String(), "name_10")
|
||||
t.Assert(oneSave["delete_at"].String(), "")
|
||||
@ -684,13 +726,13 @@ CREATE TABLE %s (
|
||||
UpdateAt: nil,
|
||||
DeleteAt: nil,
|
||||
}
|
||||
r, err = db.Table(table).Data(dataUpdate).WherePri(1).Update()
|
||||
t.Assert(err, nil)
|
||||
r, err = db.Model(table).Data(dataUpdate).WherePri(1).Update()
|
||||
t.AssertNil(err)
|
||||
n, _ = r.RowsAffected()
|
||||
t.Assert(n, 1)
|
||||
|
||||
oneUpdate, err := db.Table(table).FindOne(1)
|
||||
t.Assert(err, nil)
|
||||
oneUpdate, err := db.Model(table).FindOne(1)
|
||||
t.AssertNil(err)
|
||||
t.Assert(oneUpdate["id"].Int(), 1)
|
||||
t.Assert(oneUpdate["name"].String(), "name_1000")
|
||||
t.Assert(oneUpdate["delete_at"].String(), "")
|
||||
@ -705,13 +747,13 @@ CREATE TABLE %s (
|
||||
UpdateAt: nil,
|
||||
DeleteAt: nil,
|
||||
}
|
||||
r, err = db.Table(table).Data(dataReplace).Replace()
|
||||
t.Assert(err, nil)
|
||||
r, err = db.Model(table).Data(dataReplace).Replace()
|
||||
t.AssertNil(err)
|
||||
n, _ = r.RowsAffected()
|
||||
t.Assert(n, 2)
|
||||
|
||||
oneReplace, err := db.Table(table).FindOne(1)
|
||||
t.Assert(err, nil)
|
||||
oneReplace, err := db.Model(table).FindOne(1)
|
||||
t.AssertNil(err)
|
||||
t.Assert(oneReplace["id"].Int(), 1)
|
||||
t.Assert(oneReplace["name"].String(), "name_100")
|
||||
t.Assert(oneReplace["delete_at"].String(), "")
|
||||
@ -721,36 +763,36 @@ CREATE TABLE %s (
|
||||
time.Sleep(2 * time.Second)
|
||||
|
||||
// Delete
|
||||
r, err = db.Table(table).Delete("id", 1)
|
||||
t.Assert(err, nil)
|
||||
r, err = db.Model(table).Delete("id", 1)
|
||||
t.AssertNil(err)
|
||||
n, _ = r.RowsAffected()
|
||||
t.Assert(n, 1)
|
||||
// Delete Select
|
||||
one4, err := db.Table(table).FindOne(1)
|
||||
t.Assert(err, nil)
|
||||
one4, err := db.Model(table).FindOne(1)
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(one4), 0)
|
||||
one5, err := db.Table(table).Unscoped().FindOne(1)
|
||||
t.Assert(err, nil)
|
||||
one5, err := db.Model(table).Unscoped().FindOne(1)
|
||||
t.AssertNil(err)
|
||||
t.Assert(one5["id"].Int(), 1)
|
||||
t.AssertGE(one5["delete_at"].GTime().Timestamp(), gtime.Timestamp()-2)
|
||||
// Delete Count
|
||||
i, err := db.Table(table).FindCount()
|
||||
t.Assert(err, nil)
|
||||
i, err := db.Model(table).FindCount()
|
||||
t.AssertNil(err)
|
||||
t.Assert(i, 0)
|
||||
i, err = db.Table(table).Unscoped().FindCount()
|
||||
t.Assert(err, nil)
|
||||
i, err = db.Model(table).Unscoped().FindCount()
|
||||
t.AssertNil(err)
|
||||
t.Assert(i, 1)
|
||||
|
||||
// Delete Unscoped
|
||||
r, err = db.Table(table).Unscoped().Delete("id", 1)
|
||||
t.Assert(err, nil)
|
||||
r, err = db.Model(table).Unscoped().Delete("id", 1)
|
||||
t.AssertNil(err)
|
||||
n, _ = r.RowsAffected()
|
||||
t.Assert(n, 1)
|
||||
one6, err := db.Table(table).Unscoped().FindOne(1)
|
||||
t.Assert(err, nil)
|
||||
one6, err := db.Model(table).Unscoped().FindOne(1)
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(one6), 0)
|
||||
i, err = db.Table(table).Unscoped().FindCount()
|
||||
t.Assert(err, nil)
|
||||
i, err = db.Model(table).Unscoped().FindCount()
|
||||
t.AssertNil(err)
|
||||
t.Assert(i, 0)
|
||||
})
|
||||
}
|
||||
|
||||
@ -90,23 +90,23 @@ func Test_TX_Rollback(t *testing.T) {
|
||||
func Test_TX_Prepare(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
tx, err := db.Begin()
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
|
||||
st, err := tx.Prepare("SELECT 100")
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
|
||||
rows, err := st.Query()
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
|
||||
array, err := rows.Columns()
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
t.Assert(array[0], "100")
|
||||
|
||||
rows.Close()
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
|
||||
tx.Commit()
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
})
|
||||
}
|
||||
|
||||
@ -183,7 +183,7 @@ func Test_TX_BatchInsert(t *testing.T) {
|
||||
if err := tx.Commit(); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
if n, err := db.Table(table).Count(); err != nil {
|
||||
if n, err := db.Model(table).Count(); err != nil {
|
||||
gtest.Error(err)
|
||||
} else {
|
||||
t.Assert(n, 2)
|
||||
@ -221,12 +221,12 @@ func Test_TX_BatchReplace(t *testing.T) {
|
||||
if err := tx.Commit(); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
if n, err := db.Table(table).Count(); err != nil {
|
||||
if n, err := db.Model(table).Count(); err != nil {
|
||||
gtest.Error(err)
|
||||
} else {
|
||||
t.Assert(n, SIZE)
|
||||
t.Assert(n, TableSize)
|
||||
}
|
||||
if value, err := db.Table(table).Fields("password").Where("id", 2).Value(); err != nil {
|
||||
if value, err := db.Model(table).Fields("password").Where("id", 2).Value(); err != nil {
|
||||
gtest.Error(err)
|
||||
} else {
|
||||
t.Assert(value.String(), "PASS_2")
|
||||
@ -258,13 +258,13 @@ func Test_TX_BatchSave(t *testing.T) {
|
||||
gtest.Error(err)
|
||||
}
|
||||
|
||||
if n, err := db.Table(table).Count(); err != nil {
|
||||
if n, err := db.Model(table).Count(); err != nil {
|
||||
gtest.Error(err)
|
||||
} else {
|
||||
t.Assert(n, SIZE)
|
||||
t.Assert(n, TableSize)
|
||||
}
|
||||
|
||||
if value, err := db.Table(table).Fields("password").Where("id", 4).Value(); err != nil {
|
||||
if value, err := db.Model(table).Fields("password").Where("id", 4).Value(); err != nil {
|
||||
gtest.Error(err)
|
||||
} else {
|
||||
t.Assert(value.String(), "PASS_4")
|
||||
@ -293,7 +293,7 @@ func Test_TX_Replace(t *testing.T) {
|
||||
if err := tx.Rollback(); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
if value, err := db.Table(table).Fields("nickname").Where("id", 1).Value(); err != nil {
|
||||
if value, err := db.Model(table).Fields("nickname").Where("id", 1).Value(); err != nil {
|
||||
gtest.Error(err)
|
||||
} else {
|
||||
t.Assert(value.String(), "name_1")
|
||||
@ -322,7 +322,7 @@ func Test_TX_Save(t *testing.T) {
|
||||
if err := tx.Commit(); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
if value, err := db.Table(table).Fields("nickname").Where("id", 1).Value(); err != nil {
|
||||
if value, err := db.Model(table).Fields("nickname").Where("id", 1).Value(); err != nil {
|
||||
gtest.Error(err)
|
||||
} else {
|
||||
t.Assert(value.String(), "NAME_1")
|
||||
@ -351,7 +351,7 @@ func Test_TX_Update(t *testing.T) {
|
||||
_, err = tx.Table(table).Fields("create_time").Where("id", 3).Value()
|
||||
t.AssertNE(err, nil)
|
||||
|
||||
if value, err := db.Table(table).Fields("create_time").Where("id", 3).Value(); err != nil {
|
||||
if value, err := db.Model(table).Fields("create_time").Where("id", 3).Value(); err != nil {
|
||||
gtest.Error(err)
|
||||
} else {
|
||||
t.Assert(value.String(), "2019-10-24 10:00:00")
|
||||
@ -435,7 +435,7 @@ func Test_TX_GetCount(t *testing.T) {
|
||||
if count, err := tx.GetCount("SELECT * FROM " + table); err != nil {
|
||||
gtest.Error(err)
|
||||
} else {
|
||||
t.Assert(count, SIZE)
|
||||
t.Assert(count, TableSize)
|
||||
}
|
||||
if err := tx.Commit(); err != nil {
|
||||
gtest.Error(err)
|
||||
@ -513,7 +513,7 @@ func Test_TX_GetStructs(t *testing.T) {
|
||||
if err := tx.GetStructs(&users, fmt.Sprintf("SELECT * FROM %s WHERE id>=?", table), 1); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
t.Assert(len(users), SIZE)
|
||||
t.Assert(len(users), TableSize)
|
||||
t.Assert(users[0].Id, 1)
|
||||
t.Assert(users[1].Id, 2)
|
||||
t.Assert(users[2].Id, 3)
|
||||
@ -542,7 +542,7 @@ func Test_TX_GetStructs(t *testing.T) {
|
||||
if err := tx.GetStructs(&users, fmt.Sprintf("SELECT * FROM %s WHERE id>=?", table), 1); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
t.Assert(len(users), SIZE)
|
||||
t.Assert(len(users), TableSize)
|
||||
t.Assert(users[0].Id, 1)
|
||||
t.Assert(users[1].Id, 2)
|
||||
t.Assert(users[2].Id, 3)
|
||||
@ -621,7 +621,7 @@ func Test_TX_GetScan(t *testing.T) {
|
||||
if err := tx.GetScan(&users, fmt.Sprintf("SELECT * FROM %s WHERE id>=?", table), 1); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
t.Assert(len(users), SIZE)
|
||||
t.Assert(len(users), TableSize)
|
||||
t.Assert(users[0].Id, 1)
|
||||
t.Assert(users[1].Id, 2)
|
||||
t.Assert(users[2].Id, 3)
|
||||
@ -650,7 +650,7 @@ func Test_TX_GetScan(t *testing.T) {
|
||||
if err := tx.GetScan(&users, fmt.Sprintf("SELECT * FROM %s WHERE id>=?", table), 1); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
t.Assert(len(users), SIZE)
|
||||
t.Assert(len(users), TableSize)
|
||||
t.Assert(users[0].Id, 1)
|
||||
t.Assert(users[1].Id, 2)
|
||||
t.Assert(users[2].Id, 3)
|
||||
@ -679,7 +679,7 @@ func Test_TX_Delete(t *testing.T) {
|
||||
if err := tx.Commit(); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
if n, err := db.Table(table).Count(); err != nil {
|
||||
if n, err := db.Model(table).Count(); err != nil {
|
||||
gtest.Error(err)
|
||||
} else {
|
||||
t.Assert(n, 0)
|
||||
@ -704,10 +704,10 @@ func Test_TX_Delete(t *testing.T) {
|
||||
if err := tx.Rollback(); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
if n, err := db.Table(table).Count(); err != nil {
|
||||
if n, err := db.Model(table).Count(); err != nil {
|
||||
gtest.Error(err)
|
||||
} else {
|
||||
t.Assert(n, SIZE)
|
||||
t.Assert(n, TableSize)
|
||||
t.AssertNE(n, 0)
|
||||
}
|
||||
})
|
||||
@ -732,7 +732,7 @@ func Test_Transaction(t *testing.T) {
|
||||
})
|
||||
t.AssertNE(err, nil)
|
||||
|
||||
if value, err := db.Table(table).Fields("nickname").Where("id", 1).Value(); err != nil {
|
||||
if value, err := db.Model(table).Fields("nickname").Where("id", 1).Value(); err != nil {
|
||||
gtest.Error(err)
|
||||
} else {
|
||||
t.Assert(value.String(), "name_1")
|
||||
@ -752,9 +752,9 @@ func Test_Transaction(t *testing.T) {
|
||||
}
|
||||
return nil
|
||||
})
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
|
||||
if value, err := db.Table(table).Fields("nickname").Where("id", 1).Value(); err != nil {
|
||||
if value, err := db.Model(table).Fields("nickname").Where("id", 1).Value(); err != nil {
|
||||
gtest.Error(err)
|
||||
} else {
|
||||
t.Assert(value.String(), "NAME_1")
|
||||
@ -782,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)
|
||||
})
|
||||
}
|
||||
|
||||
@ -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"])
|
||||
|
||||
@ -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()
|
||||
}
|
||||
|
||||
@ -11,7 +11,6 @@ import (
|
||||
"errors"
|
||||
"github.com/gogf/gf/container/gvar"
|
||||
"github.com/gogf/gf/internal/json"
|
||||
"github.com/gogf/gf/net/gtrace"
|
||||
"github.com/gogf/gf/os/gtime"
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
"github.com/gomodule/redigo/redis"
|
||||
@ -60,14 +59,12 @@ func (c *Conn) do(timeout time.Duration, commandName string, args ...interface{}
|
||||
timestampMilli2 := gtime.TimestampMilli()
|
||||
|
||||
// Tracing.
|
||||
if gtrace.IsActivated(c.ctx) {
|
||||
c.addTracingItem(&tracingItem{
|
||||
err: err,
|
||||
commandName: commandName,
|
||||
arguments: args,
|
||||
costMilli: timestampMilli2 - timestampMilli1,
|
||||
})
|
||||
}
|
||||
c.addTracingItem(&tracingItem{
|
||||
err: err,
|
||||
commandName: commandName,
|
||||
arguments: args,
|
||||
costMilli: timestampMilli2 - timestampMilli1,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@ -12,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)),
|
||||
))
|
||||
}
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -10,6 +10,7 @@ import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/internal/utils"
|
||||
"io"
|
||||
"runtime"
|
||||
"strings"
|
||||
@ -24,7 +25,8 @@ type Error struct {
|
||||
}
|
||||
|
||||
const (
|
||||
stackFilterKey = "/errors/gerror/gerror"
|
||||
// Filtering key for current error module paths.
|
||||
stackFilterKeyLocal = "/errors/gerror/gerror"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -182,20 +184,34 @@ func formatSubStack(st stack, buffer *bytes.Buffer) {
|
||||
for _, p := range st {
|
||||
if fn := runtime.FuncForPC(p - 1); fn != nil {
|
||||
file, line := fn.FileLine(p - 1)
|
||||
if strings.Contains(file, stackFilterKey) {
|
||||
continue
|
||||
// Custom filtering.
|
||||
if !utils.IsDebugEnabled() {
|
||||
if strings.Contains(file, utils.StackFilterKeyForGoFrame) {
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
if strings.Contains(file, stackFilterKeyLocal) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
// Avoid stack string like "<autogenerated>"
|
||||
if strings.Contains(file, "<") {
|
||||
continue
|
||||
}
|
||||
if goRootForFilter != "" && len(file) >= len(goRootForFilter) && file[0:len(goRootForFilter)] == goRootForFilter {
|
||||
// Ignore GO ROOT paths.
|
||||
if goRootForFilter != "" &&
|
||||
len(file) >= len(goRootForFilter) &&
|
||||
file[0:len(goRootForFilter)] == goRootForFilter {
|
||||
continue
|
||||
}
|
||||
// Graceful indent.
|
||||
if index > 9 {
|
||||
space = " "
|
||||
}
|
||||
buffer.WriteString(fmt.Sprintf(" %d).%s%s\n \t%s:%d\n", index, space, fn.Name(), file, line))
|
||||
buffer.WriteString(fmt.Sprintf(
|
||||
" %d).%s%s\n \t%s:%d\n",
|
||||
index, space, fn.Name(), file, line,
|
||||
))
|
||||
index++
|
||||
}
|
||||
}
|
||||
|
||||
86
frame/g/g.go
86
frame/g/g.go
@ -6,51 +6,57 @@
|
||||
|
||||
package g
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/container/gvar"
|
||||
)
|
||||
import "github.com/gogf/gf/container/gvar"
|
||||
|
||||
// Var is a universal variable interface, like generics.
|
||||
type Var = gvar.Var
|
||||
|
||||
// Frequently-used map type alias.
|
||||
type Map = map[string]interface{}
|
||||
type MapAnyAny = map[interface{}]interface{}
|
||||
type MapAnyStr = map[interface{}]string
|
||||
type MapAnyInt = map[interface{}]int
|
||||
type MapStrAny = map[string]interface{}
|
||||
type MapStrStr = map[string]string
|
||||
type MapStrInt = map[string]int
|
||||
type MapIntAny = map[int]interface{}
|
||||
type MapIntStr = map[int]string
|
||||
type MapIntInt = map[int]int
|
||||
type MapAnyBool = map[interface{}]bool
|
||||
type MapStrBool = map[string]bool
|
||||
type MapIntBool = map[int]bool
|
||||
// Frequently-used map alias.
|
||||
type (
|
||||
Map = map[string]interface{}
|
||||
MapAnyAny = map[interface{}]interface{}
|
||||
MapAnyStr = map[interface{}]string
|
||||
MapAnyInt = map[interface{}]int
|
||||
MapStrAny = map[string]interface{}
|
||||
MapStrStr = map[string]string
|
||||
MapStrInt = map[string]int
|
||||
MapIntAny = map[int]interface{}
|
||||
MapIntStr = map[int]string
|
||||
MapIntInt = map[int]int
|
||||
MapAnyBool = map[interface{}]bool
|
||||
MapStrBool = map[string]bool
|
||||
MapIntBool = map[int]bool
|
||||
)
|
||||
|
||||
// Frequently-used slice type alias.
|
||||
type List = []Map
|
||||
type ListAnyAny = []MapAnyAny
|
||||
type ListAnyStr = []MapAnyStr
|
||||
type ListAnyInt = []MapAnyInt
|
||||
type ListStrAny = []MapStrAny
|
||||
type ListStrStr = []MapStrStr
|
||||
type ListStrInt = []MapStrInt
|
||||
type ListIntAny = []MapIntAny
|
||||
type ListIntStr = []MapIntStr
|
||||
type ListIntInt = []MapIntInt
|
||||
type ListAnyBool = []MapAnyBool
|
||||
type ListStrBool = []MapStrBool
|
||||
type ListIntBool = []MapIntBool
|
||||
// Frequently-used slice alias.
|
||||
type (
|
||||
List = []Map
|
||||
ListAnyAny = []MapAnyAny
|
||||
ListAnyStr = []MapAnyStr
|
||||
ListAnyInt = []MapAnyInt
|
||||
ListStrAny = []MapStrAny
|
||||
ListStrStr = []MapStrStr
|
||||
ListStrInt = []MapStrInt
|
||||
ListIntAny = []MapIntAny
|
||||
ListIntStr = []MapIntStr
|
||||
ListIntInt = []MapIntInt
|
||||
ListAnyBool = []MapAnyBool
|
||||
ListStrBool = []MapStrBool
|
||||
ListIntBool = []MapIntBool
|
||||
)
|
||||
|
||||
// Frequently-used slice type alias.
|
||||
type Slice = []interface{}
|
||||
type SliceAny = []interface{}
|
||||
type SliceStr = []string
|
||||
type SliceInt = []int
|
||||
// Frequently-used slice alias.
|
||||
type (
|
||||
Slice = []interface{}
|
||||
SliceAny = []interface{}
|
||||
SliceStr = []string
|
||||
SliceInt = []int
|
||||
)
|
||||
|
||||
// Array is alias of Slice.
|
||||
type Array = []interface{}
|
||||
type ArrayAny = []interface{}
|
||||
type ArrayStr = []string
|
||||
type ArrayInt = []int
|
||||
type (
|
||||
Array = []interface{}
|
||||
ArrayAny = []interface{}
|
||||
ArrayStr = []string
|
||||
ArrayInt = []int
|
||||
)
|
||||
|
||||
@ -80,27 +80,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.
|
||||
|
||||
@ -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,
|
||||
))
|
||||
|
||||
@ -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
9
go.mod
@ -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
59
go.sum
Normal 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=
|
||||
@ -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
127
internal/command/command.go
Normal file
@ -0,0 +1,127 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
//
|
||||
|
||||
// Package command provides console operations, like options/arguments reading.
|
||||
package command
|
||||
|
||||
import (
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
defaultParsedArgs = make([]string, 0)
|
||||
defaultParsedOptions = make(map[string]string)
|
||||
argumentRegex = regexp.MustCompile(`^\-{1,2}([\w\?\.\-]+)(=){0,1}(.*)$`)
|
||||
)
|
||||
|
||||
// Custom initialization.
|
||||
func Init(args ...string) {
|
||||
if len(args) == 0 {
|
||||
if len(defaultParsedArgs) == 0 && len(defaultParsedOptions) == 0 {
|
||||
args = os.Args
|
||||
} else {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
defaultParsedArgs = make([]string, 0)
|
||||
defaultParsedOptions = make(map[string]string)
|
||||
}
|
||||
// Parsing os.Args with default algorithm.
|
||||
for i := 0; i < len(args); {
|
||||
array := argumentRegex.FindStringSubmatch(args[i])
|
||||
if len(array) > 2 {
|
||||
if array[2] == "=" {
|
||||
defaultParsedOptions[array[1]] = array[3]
|
||||
} else if i < len(args)-1 {
|
||||
if len(args[i+1]) > 0 && args[i+1][0] == '-' {
|
||||
// Eg: gf gen -d -n 1
|
||||
defaultParsedOptions[array[1]] = array[3]
|
||||
} else {
|
||||
// Eg: gf gen -n 2
|
||||
defaultParsedOptions[array[1]] = args[i+1]
|
||||
i += 2
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
// Eg: gf gen -h
|
||||
defaultParsedOptions[array[1]] = array[3]
|
||||
}
|
||||
} else {
|
||||
defaultParsedArgs = append(defaultParsedArgs, args[i])
|
||||
}
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
// GetOpt returns the option value named `name`.
|
||||
func GetOpt(name string, def ...string) string {
|
||||
Init()
|
||||
if v, ok := defaultParsedOptions[name]; ok {
|
||||
return v
|
||||
}
|
||||
if len(def) > 0 {
|
||||
return def[0]
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// GetOptAll returns all parsed options.
|
||||
func GetOptAll() map[string]string {
|
||||
Init()
|
||||
return defaultParsedOptions
|
||||
}
|
||||
|
||||
// ContainsOpt checks whether option named `name` exist in the arguments.
|
||||
func ContainsOpt(name string) bool {
|
||||
Init()
|
||||
_, ok := defaultParsedOptions[name]
|
||||
return ok
|
||||
}
|
||||
|
||||
// GetArg returns the argument at `index`.
|
||||
func GetArg(index int, def ...string) string {
|
||||
Init()
|
||||
if index < len(defaultParsedArgs) {
|
||||
return defaultParsedArgs[index]
|
||||
}
|
||||
if len(def) > 0 {
|
||||
return def[0]
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// GetArgAll returns all parsed arguments.
|
||||
func GetArgAll() []string {
|
||||
Init()
|
||||
return defaultParsedArgs
|
||||
}
|
||||
|
||||
// GetOptWithEnv returns the command line argument of the specified `key`.
|
||||
// If the argument does not exist, then it returns the environment variable with specified `key`.
|
||||
// It returns the default value `def` if none of them exists.
|
||||
//
|
||||
// Fetching Rules:
|
||||
// 1. Command line arguments are in lowercase format, eg: gf.<package name>.<variable name>;
|
||||
// 2. Environment arguments are in uppercase format, eg: GF_<package name>_<variable name>;
|
||||
func GetOptWithEnv(key string, def ...string) string {
|
||||
cmdKey := strings.ToLower(strings.Replace(key, "_", ".", -1))
|
||||
if ContainsOpt(cmdKey) {
|
||||
return GetOpt(cmdKey)
|
||||
} else {
|
||||
envKey := strings.ToUpper(strings.Replace(key, ".", "_", -1))
|
||||
if r, ok := os.LookupEnv(envKey); ok {
|
||||
return r
|
||||
} else {
|
||||
if len(def) > 0 {
|
||||
return def[0]
|
||||
}
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
@ -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 {
|
||||
|
||||
@ -10,7 +10,7 @@ package intlog
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/debug/gdebug"
|
||||
"github.com/gogf/gf/os/gcmd"
|
||||
"github.com/gogf/gf/internal/utils"
|
||||
"path/filepath"
|
||||
"time"
|
||||
)
|
||||
@ -25,29 +25,20 @@ var (
|
||||
)
|
||||
|
||||
func init() {
|
||||
// Debugging configured.
|
||||
if !gcmd.GetWithEnv("GF_DEBUG").IsEmpty() {
|
||||
isGFDebug = true
|
||||
return
|
||||
}
|
||||
isGFDebug = utils.IsDebugEnabled()
|
||||
}
|
||||
|
||||
// SetEnabled enables/disables the internal logging manually.
|
||||
// Note that this function is not concurrent safe, be aware of the DATA RACE.
|
||||
func SetEnabled(enabled bool) {
|
||||
// If they're the same, it does not write the <isGFDebug> but only reading operation.
|
||||
// If they're the same, it does not write the `isGFDebug` but only reading operation.
|
||||
if isGFDebug != enabled {
|
||||
isGFDebug = enabled
|
||||
}
|
||||
}
|
||||
|
||||
// IsEnabled checks and returns whether current process is in GF development.
|
||||
func IsEnabled() bool {
|
||||
return isGFDebug
|
||||
}
|
||||
|
||||
// Print prints <v> with newline using fmt.Println.
|
||||
// The parameter <v> can be multiple variables.
|
||||
// Print prints `v` with newline using fmt.Println.
|
||||
// The parameter `v` can be multiple variables.
|
||||
func Print(v ...interface{}) {
|
||||
if !isGFDebug {
|
||||
return
|
||||
@ -55,8 +46,8 @@ func Print(v ...interface{}) {
|
||||
fmt.Println(append([]interface{}{now(), "[INTE]", file()}, v...)...)
|
||||
}
|
||||
|
||||
// Printf prints <v> with format <format> using fmt.Printf.
|
||||
// The parameter <v> can be multiple variables.
|
||||
// Printf prints `v` with format `format` using fmt.Printf.
|
||||
// The parameter `v` can be multiple variables.
|
||||
func Printf(format string, v ...interface{}) {
|
||||
if !isGFDebug {
|
||||
return
|
||||
@ -64,8 +55,8 @@ func Printf(format string, v ...interface{}) {
|
||||
fmt.Printf(now()+" [INTE] "+file()+" "+format+"\n", v...)
|
||||
}
|
||||
|
||||
// Error prints <v> with newline using fmt.Println.
|
||||
// The parameter <v> can be multiple variables.
|
||||
// Error prints `v` with newline using fmt.Println.
|
||||
// The parameter `v` can be multiple variables.
|
||||
func Error(v ...interface{}) {
|
||||
if !isGFDebug {
|
||||
return
|
||||
@ -75,7 +66,7 @@ func Error(v ...interface{}) {
|
||||
fmt.Println(array...)
|
||||
}
|
||||
|
||||
// Errorf prints <v> with format <format> using fmt.Printf.
|
||||
// Errorf prints `v` with format `format` using fmt.Printf.
|
||||
func Errorf(format string, v ...interface{}) {
|
||||
if !isGFDebug {
|
||||
return
|
||||
|
||||
@ -16,7 +16,7 @@ type Mutex struct {
|
||||
}
|
||||
|
||||
// New creates and returns a new *Mutex.
|
||||
// The parameter <safe> is used to specify whether using this mutex in concurrent-safety,
|
||||
// The parameter `safe` is used to specify whether using this mutex in concurrent-safety,
|
||||
// which is false in default.
|
||||
func New(safe ...bool) *Mutex {
|
||||
mu := new(Mutex)
|
||||
|
||||
@ -17,7 +17,7 @@ type RWMutex struct {
|
||||
}
|
||||
|
||||
// New creates and returns a new *RWMutex.
|
||||
// The parameter <safe> is used to specify whether using this mutex in concurrent safety,
|
||||
// The parameter `safe` is used to specify whether using this mutex in concurrent safety,
|
||||
// which is false in default.
|
||||
func New(safe ...bool) *RWMutex {
|
||||
mu := Create(safe...)
|
||||
@ -25,7 +25,7 @@ func New(safe ...bool) *RWMutex {
|
||||
}
|
||||
|
||||
// Create creates and returns a new RWMutex object.
|
||||
// The parameter <safe> is used to specify whether using this mutex in concurrent safety,
|
||||
// The parameter `safe` is used to specify whether using this mutex in concurrent safety,
|
||||
// which is false in default.
|
||||
func Create(safe ...bool) RWMutex {
|
||||
mu := RWMutex{}
|
||||
|
||||
@ -13,38 +13,14 @@ import (
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// Type wraps reflect.Type for additional features.
|
||||
type Type struct {
|
||||
reflect.Type
|
||||
}
|
||||
|
||||
// Field contains information of a struct field .
|
||||
type Field struct {
|
||||
value reflect.Value
|
||||
field reflect.StructField
|
||||
// Retrieved tag value. There might be more than one tags in the field,
|
||||
// but only one can be retrieved according to calling function rules.
|
||||
TagValue string
|
||||
}
|
||||
|
||||
// Tag returns the value associated with key in the tag string. If there is no
|
||||
// such key in the tag, Tag returns the empty string.
|
||||
func (f *Field) Tag(key string) string {
|
||||
return f.field.Tag.Get(key)
|
||||
}
|
||||
|
||||
// Value returns the underlying value of the field. It panics if the field
|
||||
// is not exported.
|
||||
func (f *Field) Value() interface{} {
|
||||
return f.value.Interface()
|
||||
}
|
||||
|
||||
// IsEmbedded returns true if the given field is an anonymous field (embedded)
|
||||
func (f *Field) IsEmbedded() bool {
|
||||
return f.field.Anonymous
|
||||
}
|
||||
|
||||
// IsExported returns true if the given field is exported.
|
||||
func (f *Field) IsExported() bool {
|
||||
return f.field.PkgPath == ""
|
||||
}
|
||||
|
||||
// Name returns the name of the given field
|
||||
func (f *Field) Name() string {
|
||||
return f.field.Name
|
||||
Value reflect.Value // The underlying value of the field.
|
||||
Field reflect.StructField // The underlying field of the field.
|
||||
TagValue string // Retrieved tag value. There might be more than one tags in the field, but only one can be retrieved according to calling function rules.
|
||||
}
|
||||
|
||||
108
internal/structs/structs_field.go
Normal file
108
internal/structs/structs_field.go
Normal 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
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
@ -9,20 +9,73 @@ package structs
|
||||
import (
|
||||
"errors"
|
||||
"reflect"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// TagFields retrieves struct tags as []*Field from <pointer>, and returns it.
|
||||
// ParseTag parses tag string into map.
|
||||
func ParseTag(tag string) map[string]string {
|
||||
var (
|
||||
key string
|
||||
data = make(map[string]string)
|
||||
)
|
||||
for tag != "" {
|
||||
// Skip leading space.
|
||||
i := 0
|
||||
for i < len(tag) && tag[i] == ' ' {
|
||||
i++
|
||||
}
|
||||
tag = tag[i:]
|
||||
if tag == "" {
|
||||
break
|
||||
}
|
||||
// Scan to colon. A space, a quote or a control character is a syntax error.
|
||||
// Strictly speaking, control chars include the range [0x7f, 0x9f], not just
|
||||
// [0x00, 0x1f], but in practice, we ignore the multi-byte control characters
|
||||
// as it is simpler to inspect the tag's bytes than the tag's runes.
|
||||
i = 0
|
||||
for i < len(tag) && tag[i] > ' ' && tag[i] != ':' && tag[i] != '"' && tag[i] != 0x7f {
|
||||
i++
|
||||
}
|
||||
if i == 0 || i+1 >= len(tag) || tag[i] != ':' || tag[i+1] != '"' {
|
||||
break
|
||||
}
|
||||
key = tag[:i]
|
||||
tag = tag[i+1:]
|
||||
|
||||
// Scan quoted string to find value.
|
||||
i = 1
|
||||
for i < len(tag) && tag[i] != '"' {
|
||||
if tag[i] == '\\' {
|
||||
i++
|
||||
}
|
||||
i++
|
||||
}
|
||||
if i >= len(tag) {
|
||||
break
|
||||
}
|
||||
quotedValue := string(tag[:i+1])
|
||||
tag = tag[i+1:]
|
||||
value, err := strconv.Unquote(quotedValue)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
data[key] = value
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
// TagFields retrieves and returns struct tags as []*Field from `pointer`.
|
||||
//
|
||||
// The parameter <pointer> should be type of struct/*struct.
|
||||
// The parameter `pointer` should be type of struct/*struct.
|
||||
//
|
||||
// Note that it only retrieves the exported attributes with first letter up-case from struct.
|
||||
func TagFields(pointer interface{}, priority []string) ([]*Field, error) {
|
||||
return getFieldValuesByTagPriority(pointer, priority, map[string]struct{}{})
|
||||
}
|
||||
|
||||
// TagMapName retrieves struct tags as map[tag]attribute from <pointer>, and returns it.
|
||||
// TagMapName retrieves and returns struct tags as map[tag]attribute from `pointer`.
|
||||
//
|
||||
// The parameter <pointer> should be type of struct/*struct.
|
||||
// The parameter `pointer` should be type of struct/*struct.
|
||||
//
|
||||
// Note that it only retrieves the exported attributes with first letter up-case from struct.
|
||||
func TagMapName(pointer interface{}, priority []string) (map[string]string, error) {
|
||||
@ -37,13 +90,12 @@ func TagMapName(pointer interface{}, priority []string) (map[string]string, erro
|
||||
return tagMap, nil
|
||||
}
|
||||
|
||||
// TagMapField retrieves struct tags as map[tag]*Field from <pointer>, and returns it.
|
||||
//
|
||||
// The parameter <pointer> should be type of struct/*struct.
|
||||
// TagMapField retrieves struct tags as map[tag]*Field from `pointer`, and returns it.
|
||||
// The parameter `object` should be either type of struct/*struct/[]struct/[]*struct.
|
||||
//
|
||||
// Note that it only retrieves the exported attributes with first letter up-case from struct.
|
||||
func TagMapField(pointer interface{}, priority []string) (map[string]*Field, error) {
|
||||
fields, err := TagFields(pointer, priority)
|
||||
func TagMapField(object interface{}, priority []string) (map[string]*Field, error) {
|
||||
fields, err := TagFields(object, priority)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -67,19 +119,31 @@ func getFieldValues(value interface{}) ([]*Field, error) {
|
||||
reflectValue = reflect.ValueOf(value)
|
||||
reflectKind = reflectValue.Kind()
|
||||
}
|
||||
|
||||
for reflectKind == reflect.Ptr {
|
||||
if !reflectValue.IsValid() || reflectValue.IsNil() {
|
||||
// If pointer is type of *struct and nil, then automatically create a temporary struct.
|
||||
for {
|
||||
switch reflectKind {
|
||||
case reflect.Ptr:
|
||||
if !reflectValue.IsValid() || reflectValue.IsNil() {
|
||||
// If pointer is type of *struct and nil, then automatically create a temporary struct.
|
||||
reflectValue = reflect.New(reflectValue.Type().Elem()).Elem()
|
||||
reflectKind = reflectValue.Kind()
|
||||
} else {
|
||||
reflectValue = reflectValue.Elem()
|
||||
reflectKind = reflectValue.Kind()
|
||||
}
|
||||
case reflect.Array, reflect.Slice:
|
||||
reflectValue = reflect.New(reflectValue.Type().Elem()).Elem()
|
||||
reflectKind = reflectValue.Kind()
|
||||
} else {
|
||||
reflectValue = reflectValue.Elem()
|
||||
reflectKind = reflectValue.Kind()
|
||||
default:
|
||||
goto exitLoop
|
||||
}
|
||||
}
|
||||
exitLoop:
|
||||
for reflectKind == reflect.Ptr {
|
||||
reflectValue = reflectValue.Elem()
|
||||
reflectKind = reflectValue.Kind()
|
||||
}
|
||||
if reflectKind != reflect.Struct {
|
||||
return nil, errors.New("given value should be type of struct/*struct")
|
||||
return nil, errors.New("given value should be either type of struct/*struct/[]struct/[]*struct")
|
||||
}
|
||||
var (
|
||||
structType = reflectValue.Type()
|
||||
@ -88,8 +152,8 @@ func getFieldValues(value interface{}) ([]*Field, error) {
|
||||
)
|
||||
for i := 0; i < length; i++ {
|
||||
fields[i] = &Field{
|
||||
value: reflectValue.Field(i),
|
||||
field: structType.Field(i),
|
||||
Value: reflectValue.Field(i),
|
||||
Field: structType.Field(i),
|
||||
}
|
||||
}
|
||||
return fields, nil
|
||||
@ -127,7 +191,7 @@ func getFieldValuesByTagPriority(pointer interface{}, priority []string, tagMap
|
||||
}
|
||||
// If this is an embedded attribute, it retrieves the tags recursively.
|
||||
if field.IsEmbedded() {
|
||||
if subTagFields, err := getFieldValuesByTagPriority(field.value, priority, tagMap); err != nil {
|
||||
if subTagFields, err := getFieldValuesByTagPriority(field.Value, priority, tagMap); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
tagFields = append(tagFields, subTagFields...)
|
||||
|
||||
75
internal/structs/structs_type.go
Normal file
75
internal/structs/structs_type.go
Normal file
@ -0,0 +1,75 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package structs
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/errors/gerror"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// StructType retrieves and returns the struct Type of specified struct/*struct.
|
||||
// The parameter `object` should be either type of struct/*struct/[]struct/[]*struct.
|
||||
func StructType(object interface{}) (*Type, error) {
|
||||
var (
|
||||
reflectValue reflect.Value
|
||||
reflectKind reflect.Kind
|
||||
reflectType reflect.Type
|
||||
)
|
||||
if rv, ok := object.(reflect.Value); ok {
|
||||
reflectValue = rv
|
||||
} else {
|
||||
reflectValue = reflect.ValueOf(object)
|
||||
}
|
||||
reflectKind = reflectValue.Kind()
|
||||
for {
|
||||
switch reflectKind {
|
||||
case reflect.Ptr:
|
||||
if !reflectValue.IsValid() || reflectValue.IsNil() {
|
||||
// If pointer is type of *struct and nil, then automatically create a temporary struct.
|
||||
reflectValue = reflect.New(reflectValue.Type().Elem()).Elem()
|
||||
reflectKind = reflectValue.Kind()
|
||||
} else {
|
||||
reflectValue = reflectValue.Elem()
|
||||
reflectKind = reflectValue.Kind()
|
||||
}
|
||||
case reflect.Array, reflect.Slice:
|
||||
reflectValue = reflect.New(reflectValue.Type().Elem()).Elem()
|
||||
reflectKind = reflectValue.Kind()
|
||||
default:
|
||||
goto exitLoop
|
||||
}
|
||||
}
|
||||
exitLoop:
|
||||
for reflectKind == reflect.Ptr {
|
||||
reflectValue = reflectValue.Elem()
|
||||
reflectKind = reflectValue.Kind()
|
||||
}
|
||||
if reflectKind != reflect.Struct {
|
||||
return nil, gerror.Newf(
|
||||
`invalid object kind "%s", kind of "struct" is required`,
|
||||
reflectKind,
|
||||
)
|
||||
}
|
||||
reflectType = reflectValue.Type()
|
||||
return &Type{
|
||||
Type: reflectType,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Signature returns an unique string as this type.
|
||||
func (t Type) Signature() string {
|
||||
return t.PkgPath() + "/" + t.String()
|
||||
}
|
||||
|
||||
// FieldKeys returns the keys of current struct/map.
|
||||
func (t Type) FieldKeys() []string {
|
||||
keys := make([]string, t.NumField())
|
||||
for i := 0; i < t.NumField(); i++ {
|
||||
keys[i] = t.Field(i).Name
|
||||
}
|
||||
return keys
|
||||
}
|
||||
@ -102,7 +102,7 @@ func Test_StructOfNilPointer(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func Test_MapField(t *testing.T) {
|
||||
func Test_FieldMap(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type User struct {
|
||||
Id int
|
||||
@ -110,7 +110,7 @@ func Test_MapField(t *testing.T) {
|
||||
Pass string `my-tag1:"pass1" my-tag2:"pass2" params:"pass"`
|
||||
}
|
||||
var user *User
|
||||
m, _ := structs.MapField(user, []string{"params"})
|
||||
m, _ := structs.FieldMap(user, []string{"params"})
|
||||
t.Assert(len(m), 3)
|
||||
_, ok := m["Id"]
|
||||
t.Assert(ok, true)
|
||||
@ -123,4 +123,123 @@ func Test_MapField(t *testing.T) {
|
||||
_, ok = m["pass"]
|
||||
t.Assert(ok, true)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type User struct {
|
||||
Id int
|
||||
Name string `params:"name"`
|
||||
Pass string `my-tag1:"pass1" my-tag2:"pass2" params:"pass"`
|
||||
}
|
||||
var user *User
|
||||
m, _ := structs.FieldMap(user, nil)
|
||||
t.Assert(len(m), 3)
|
||||
_, ok := m["Id"]
|
||||
t.Assert(ok, true)
|
||||
_, ok = m["Name"]
|
||||
t.Assert(ok, true)
|
||||
_, ok = m["name"]
|
||||
t.Assert(ok, false)
|
||||
_, ok = m["Pass"]
|
||||
t.Assert(ok, true)
|
||||
_, ok = m["pass"]
|
||||
t.Assert(ok, false)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_StructType(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type B struct {
|
||||
Name string
|
||||
}
|
||||
type A struct {
|
||||
B
|
||||
}
|
||||
r, err := structs.StructType(new(A))
|
||||
t.AssertNil(err)
|
||||
t.Assert(r.Signature(), `github.com/gogf/gf/internal/structs_test/structs_test.A`)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type B struct {
|
||||
Name string
|
||||
}
|
||||
type A struct {
|
||||
B
|
||||
}
|
||||
r, err := structs.StructType(new(A).B)
|
||||
t.AssertNil(err)
|
||||
t.Assert(r.Signature(), `github.com/gogf/gf/internal/structs_test/structs_test.B`)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type B struct {
|
||||
Name string
|
||||
}
|
||||
type A struct {
|
||||
*B
|
||||
}
|
||||
r, err := structs.StructType(new(A).B)
|
||||
t.AssertNil(err)
|
||||
t.Assert(r.String(), `structs_test.B`)
|
||||
})
|
||||
// Error.
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type B struct {
|
||||
Name string
|
||||
}
|
||||
type A struct {
|
||||
*B
|
||||
Id int
|
||||
}
|
||||
_, err := structs.StructType(new(A).Id)
|
||||
t.AssertNE(err, nil)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_StructTypeBySlice(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type B struct {
|
||||
Name string
|
||||
}
|
||||
type A struct {
|
||||
Array []*B
|
||||
}
|
||||
r, err := structs.StructType(new(A).Array)
|
||||
t.AssertNil(err)
|
||||
t.Assert(r.Signature(), `github.com/gogf/gf/internal/structs_test/structs_test.B`)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type B struct {
|
||||
Name string
|
||||
}
|
||||
type A struct {
|
||||
Array []B
|
||||
}
|
||||
r, err := structs.StructType(new(A).Array)
|
||||
t.AssertNil(err)
|
||||
t.Assert(r.Signature(), `github.com/gogf/gf/internal/structs_test/structs_test.B`)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type B struct {
|
||||
Name string
|
||||
}
|
||||
type A struct {
|
||||
Array *[]B
|
||||
}
|
||||
r, err := structs.StructType(new(A).Array)
|
||||
t.AssertNil(err)
|
||||
t.Assert(r.Signature(), `github.com/gogf/gf/internal/structs_test/structs_test.B`)
|
||||
})
|
||||
}
|
||||
|
||||
func TestType_FieldKeys(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type B struct {
|
||||
Id int
|
||||
Name string
|
||||
}
|
||||
type A struct {
|
||||
Array []*B
|
||||
}
|
||||
r, err := structs.StructType(new(A).Array)
|
||||
t.AssertNil(err)
|
||||
t.Assert(r.FieldKeys(), g.Slice{"Id", "Name"})
|
||||
})
|
||||
}
|
||||
|
||||
37
internal/utils/utils_debug.go
Normal file
37
internal/utils/utils_debug.go
Normal file
@ -0,0 +1,37 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package utils
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/internal/command"
|
||||
)
|
||||
|
||||
const (
|
||||
debugKey = "gf.debug" // Debug key for checking if in debug mode.
|
||||
StackFilterKeyForGoFrame = "/github.com/gogf/gf/" // Stack filtering key for all GoFrame module paths.
|
||||
)
|
||||
|
||||
var (
|
||||
// isDebugEnabled marks whether GoFrame debug mode is enabled.
|
||||
isDebugEnabled = false
|
||||
)
|
||||
|
||||
func init() {
|
||||
// Debugging configured.
|
||||
value := command.GetOptWithEnv(debugKey)
|
||||
if value == "" || value == "0" || value == "false" {
|
||||
isDebugEnabled = false
|
||||
} else {
|
||||
isDebugEnabled = true
|
||||
}
|
||||
}
|
||||
|
||||
// IsDebugEnabled checks and returns whether debug mode is enabled.
|
||||
// The debug mode is enabled when command argument "gf.debug" or environment "GF_DEBUG" is passed.
|
||||
func IsDebugEnabled() bool {
|
||||
return isDebugEnabled
|
||||
}
|
||||
@ -10,6 +10,21 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
// DefaultTrimChars are the characters which are stripped by Trim* functions in default.
|
||||
DefaultTrimChars = string([]byte{
|
||||
'\t', // Tab.
|
||||
'\v', // Vertical tab.
|
||||
'\n', // New line (line feed).
|
||||
'\r', // Carriage return.
|
||||
'\f', // New page.
|
||||
' ', // Ordinary space.
|
||||
0x00, // NUL-byte.
|
||||
0x85, // Delete.
|
||||
0xA0, // Non-breaking space.
|
||||
})
|
||||
)
|
||||
|
||||
// IsLetterUpper checks whether the given byte b is in upper case.
|
||||
func IsLetterUpper(b byte) bool {
|
||||
if b >= byte('A') && b <= byte('Z') {
|
||||
@ -67,7 +82,7 @@ func UcFirst(s string) string {
|
||||
return s
|
||||
}
|
||||
|
||||
// ReplaceByMap returns a copy of <origin>,
|
||||
// ReplaceByMap returns a copy of `origin`,
|
||||
// which is replaced by a map in unordered way, case-sensitively.
|
||||
func ReplaceByMap(origin string, replaces map[string]string) string {
|
||||
for k, v := range replaces {
|
||||
@ -87,8 +102,32 @@ func RemoveSymbols(s string) string {
|
||||
return string(b)
|
||||
}
|
||||
|
||||
// EqualFoldWithoutChars checks string <s1> and <s2> equal case-insensitively,
|
||||
// EqualFoldWithoutChars checks string `s1` and `s2` equal case-insensitively,
|
||||
// with/without chars '-'/'_'/'.'/' '.
|
||||
func EqualFoldWithoutChars(s1, s2 string) bool {
|
||||
return strings.EqualFold(RemoveSymbols(s1), RemoveSymbols(s2))
|
||||
}
|
||||
|
||||
// SplitAndTrim splits string <str> by a string <delimiter> to an array,
|
||||
// and calls Trim to every element of this array. It ignores the elements
|
||||
// which are empty after Trim.
|
||||
func SplitAndTrim(str, delimiter string, characterMask ...string) []string {
|
||||
array := make([]string, 0)
|
||||
for _, v := range strings.Split(str, delimiter) {
|
||||
v = Trim(v, characterMask...)
|
||||
if v != "" {
|
||||
array = append(array, v)
|
||||
}
|
||||
}
|
||||
return array
|
||||
}
|
||||
|
||||
// Trim strips whitespace (or other characters) from the beginning and end of a string.
|
||||
// The optional parameter <characterMask> specifies the additional stripped characters.
|
||||
func Trim(str string, characterMask ...string) string {
|
||||
trimChars := DefaultTrimChars
|
||||
if len(characterMask) > 0 {
|
||||
trimChars += characterMask[0]
|
||||
}
|
||||
return strings.Trim(str, trimChars)
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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()
|
||||
}
|
||||
|
||||
@ -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()
|
||||
}
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -9,6 +9,6 @@
|
||||
package ghttp
|
||||
|
||||
// registerSignalHandler does nothing on windows platform.
|
||||
func registerSignalHandler() {
|
||||
func handleProcessSignal() {
|
||||
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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() {
|
||||
|
||||
@ -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))
|
||||
|
||||
@ -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
Reference in New Issue
Block a user