mirror of
https://gitee.com/johng/gf
synced 2026-06-08 18:47:45 +08:00
Compare commits
153 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9a6aa01115 | |||
| 4a88e0255d | |||
| edc56949b7 | |||
| 63bc06d0fe | |||
| 9b58b66172 | |||
| 3517295e96 | |||
| c685876e6f | |||
| cb2c9c43a8 | |||
| 751a567e84 | |||
| 0a99bb9a7d | |||
| a2e7aec37f | |||
| 102e2d07d9 | |||
| bb39ed136f | |||
| 363f6eba44 | |||
| 0ca305a1bf | |||
| 1d1e64b834 | |||
| 688e327f15 | |||
| 8c7ec0e7d9 | |||
| 84fef8dea3 | |||
| c1d2ad68b3 | |||
| a577605726 | |||
| 71444736ae | |||
| b7e41ec32c | |||
| 7fa09596b0 | |||
| 7316e6648f | |||
| e9d346ce4f | |||
| 4b91e709f7 | |||
| 7de89286da | |||
| 569a953b43 | |||
| 1e7f795c69 | |||
| d2ae383b83 | |||
| 8978112433 | |||
| 117eaea720 | |||
| 278e85357d | |||
| 60ec59fa4a | |||
| 0aa82ad020 | |||
| 7bd319ddc7 | |||
| 7ceb667486 | |||
| 80c4786afd | |||
| 2f741d3b24 | |||
| d2b65f0f91 | |||
| c226782f23 | |||
| 57a82ebcc0 | |||
| 416885a726 | |||
| 9b4d2d9172 | |||
| d4b2bf20bb | |||
| ce9a0555c5 | |||
| 5171250a9d | |||
| 80b629916a | |||
| ecaf0da228 | |||
| 8c0a905a9f | |||
| 5a0326f666 | |||
| 790a651ac1 | |||
| 6f93bd17f2 | |||
| 3419d66c4b | |||
| cabf684ec9 | |||
| 2b6e6ce28e | |||
| 32101189a2 | |||
| 600c081801 | |||
| 0899a9d49a | |||
| c56f4eabca | |||
| bfe89e0b12 | |||
| 6cb38cfa92 | |||
| 09bb0c9397 | |||
| fa47b0306d | |||
| 55429ad589 | |||
| 9592fb099f | |||
| 18ec6116ad | |||
| 9d0f306684 | |||
| 3eba8d690f | |||
| c0b59007ce | |||
| 3485ba2a5d | |||
| 43ecfc7484 | |||
| 5ba53e56c9 | |||
| af1d14ace6 | |||
| c02bf715c5 | |||
| 0c0e902b07 | |||
| 750b53d7aa | |||
| 1d807c095a | |||
| 69b5873bf9 | |||
| 60fc9b6417 | |||
| 0bc8944a08 | |||
| 33292f54e0 | |||
| fc215ef0b2 | |||
| 65c67427d4 | |||
| bc8142974f | |||
| fadb7a8f8f | |||
| e1bfe90833 | |||
| 042dc0b33f | |||
| 8ca535dbf0 | |||
| e20183e7a1 | |||
| df86ffb61e | |||
| bfcf133c91 | |||
| 24a377d3a8 | |||
| baf51bc68f | |||
| 5f4b585164 | |||
| 17a11187b0 | |||
| 7caf7976cf | |||
| 1a31792c14 | |||
| d56eb49e41 | |||
| a1236b5e16 | |||
| 8f278be0dc | |||
| f8ab71e7f0 | |||
| 9cca3a3ec1 | |||
| 97bcf2a438 | |||
| 85dd2e9f4f | |||
| 23d62da87f | |||
| b4d947fecd | |||
| 650c95af31 | |||
| 943116d495 | |||
| 644df7c16e | |||
| 638773b216 | |||
| 889e7914e2 | |||
| 68cc85f2b2 | |||
| c273ce576b | |||
| f8d57096a8 | |||
| d7542e87ae | |||
| b178210a31 | |||
| 3e6a23b0e1 | |||
| ee8d2afe58 | |||
| 11e102e137 | |||
| e06b62ecf2 | |||
| d178102f82 | |||
| e1dd5cce7d | |||
| 1edc1f35fb | |||
| 4df47be521 | |||
| 9cb88bca5a | |||
| fa8cc1d3f4 | |||
| 9ae8a7ca33 | |||
| f4da179140 | |||
| 13330658cb | |||
| 9f04e46166 | |||
| 1072ea3fb0 | |||
| ea9e8055a4 | |||
| 784abf2a30 | |||
| ed4a70deff | |||
| fef20d10a2 | |||
| 176dcdc7cc | |||
| 12ed05f846 | |||
| 5999f22f76 | |||
| c056fd2a06 | |||
| cb422f043e | |||
| 4ae89dc9f6 | |||
| 4f6f07db1d | |||
| cd981c7294 | |||
| 557d2967fa | |||
| a7a70636dd | |||
| 2215661f89 | |||
| a22b590b43 | |||
| 1b0b209662 | |||
| 2a2761c54f | |||
| 1c83d72f39 | |||
| fcea774b59 |
@ -9,7 +9,6 @@ package driver
|
||||
import (
|
||||
"database/sql"
|
||||
"github.com/gogf/gf/database/gdb"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/os/gtime"
|
||||
)
|
||||
|
||||
@ -51,14 +50,13 @@ func (d *MyDriver) New(core *gdb.Core, node *gdb.ConfigNode) (gdb.DB, error) {
|
||||
func (d *MyDriver) DoQuery(link gdb.Link, sql string, args ...interface{}) (rows *sql.Rows, err error) {
|
||||
tsMilli := gtime.TimestampMilli()
|
||||
rows, err = d.DriverMysql.DoQuery(link, sql, args...)
|
||||
if _, err := d.DriverMysql.InsertIgnore("monitor", g.Map{
|
||||
"sql": gdb.FormatSqlWithArgs(sql, args),
|
||||
"cost": gtime.TimestampMilli() - tsMilli,
|
||||
"time": gtime.Now(),
|
||||
"error": err.Error(),
|
||||
}); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
link.Exec(
|
||||
"INSERT INTO `monitor`(`sql`,`cost`,`time`,`error`) VALUES(?,?,?,?)",
|
||||
gdb.FormatSqlWithArgs(sql, args),
|
||||
gtime.TimestampMilli()-tsMilli,
|
||||
gtime.Now(),
|
||||
err,
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
@ -67,13 +65,12 @@ func (d *MyDriver) DoQuery(link gdb.Link, sql string, args ...interface{}) (rows
|
||||
func (d *MyDriver) DoExec(link gdb.Link, sql string, args ...interface{}) (result sql.Result, err error) {
|
||||
tsMilli := gtime.TimestampMilli()
|
||||
result, err = d.DriverMysql.DoExec(link, sql, args...)
|
||||
if _, err := d.DriverMysql.InsertIgnore("monitor", g.Map{
|
||||
"sql": gdb.FormatSqlWithArgs(sql, args),
|
||||
"cost": gtime.TimestampMilli() - tsMilli,
|
||||
"time": gtime.Now(),
|
||||
"error": err.Error(),
|
||||
}); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
link.Exec(
|
||||
"INSERT INTO `monitor`(`sql`,`cost`,`time`,`error`) VALUES(?,?,?,?)",
|
||||
gdb.FormatSqlWithArgs(sql, args),
|
||||
gtime.TimestampMilli()-tsMilli,
|
||||
gtime.Now(),
|
||||
err,
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
# MySQL.
|
||||
[database]
|
||||
debug = true
|
||||
link = "mysql:root:12345678@tcp(127.0.0.1:3306)/test?parseTime=true&loc=Local"
|
||||
link = "mysql:root:12345678@tcp(127.0.0.1:3306)/test?parseTime=true"
|
||||
MaxOpen = 100
|
||||
|
||||
# Redis.
|
||||
|
||||
@ -3,7 +3,7 @@ package main
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/database/gdb"
|
||||
"time"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
)
|
||||
|
||||
func main() {
|
||||
@ -18,10 +18,9 @@ func main() {
|
||||
|
||||
db.SetDebug(true)
|
||||
|
||||
type User struct {
|
||||
CreateTime time.Time `orm:"create_time"`
|
||||
}
|
||||
r, e := db.Table("user").Data(User{CreateTime: time.Now()}).Insert()
|
||||
r, e := db.Table("user").Data(g.Map{
|
||||
"create_at": "now()",
|
||||
}).Unscoped().Insert()
|
||||
if e != nil {
|
||||
panic(e)
|
||||
}
|
||||
|
||||
@ -1,12 +1,15 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
)
|
||||
|
||||
func main() {
|
||||
db := g.DB()
|
||||
db.SetDebug(true)
|
||||
|
||||
db.Table("user").Data("num=num+1").Where("id", 8).Update()
|
||||
one, err := g.DB().Table("carlist c").
|
||||
LeftJoin("cardetail d", "c.postid=d.carid").
|
||||
Where("c.postid", "142039140032006").
|
||||
Fields("c.*,d.*").One()
|
||||
fmt.Println(err)
|
||||
g.Dump(one)
|
||||
}
|
||||
|
||||
@ -15,10 +15,10 @@ func main() {
|
||||
r.Response.Writeln("end")
|
||||
})
|
||||
s.BindHookHandlerByMap(p, map[string]ghttp.HandlerFunc{
|
||||
ghttp.HOOK_BEFORE_SERVE: func(r *ghttp.Request) {
|
||||
ghttp.HookBeforeServe: func(r *ghttp.Request) {
|
||||
glog.To(r.Response.Writer).Println("BeforeServe")
|
||||
},
|
||||
ghttp.HOOK_AFTER_SERVE: func(r *ghttp.Request) {
|
||||
ghttp.HookAfterServe: func(r *ghttp.Request) {
|
||||
glog.To(r.Response.Writer).Println("AfterServe")
|
||||
},
|
||||
})
|
||||
|
||||
@ -12,7 +12,7 @@ func Order(r *ghttp.Request) {
|
||||
func main() {
|
||||
s := g.Server()
|
||||
s.Group("/api.v1", func(group *ghttp.RouterGroup) {
|
||||
group.Hook("/*any", ghttp.HOOK_BEFORE_SERVE, func(r *ghttp.Request) {
|
||||
group.Hook("/*any", ghttp.HookBeforeServe, func(r *ghttp.Request) {
|
||||
r.Response.CORSDefault()
|
||||
})
|
||||
g.GET("/order", Order)
|
||||
|
||||
@ -11,10 +11,10 @@ func main() {
|
||||
p := "/:name/info/{uid}"
|
||||
s := g.Server()
|
||||
s.BindHookHandlerByMap(p, map[string]ghttp.HandlerFunc{
|
||||
ghttp.HOOK_BEFORE_SERVE: func(r *ghttp.Request) { glog.Println(ghttp.HOOK_BEFORE_SERVE) },
|
||||
ghttp.HOOK_AFTER_SERVE: func(r *ghttp.Request) { glog.Println(ghttp.HOOK_AFTER_SERVE) },
|
||||
ghttp.HOOK_BEFORE_OUTPUT: func(r *ghttp.Request) { glog.Println(ghttp.HOOK_BEFORE_OUTPUT) },
|
||||
ghttp.HOOK_AFTER_OUTPUT: func(r *ghttp.Request) { glog.Println(ghttp.HOOK_AFTER_OUTPUT) },
|
||||
ghttp.HookBeforeServe: func(r *ghttp.Request) { glog.Println(ghttp.HookBeforeServe) },
|
||||
ghttp.HookAfterServe: func(r *ghttp.Request) { glog.Println(ghttp.HookAfterServe) },
|
||||
ghttp.HookBeforeOutput: func(r *ghttp.Request) { glog.Println(ghttp.HookBeforeOutput) },
|
||||
ghttp.HookAfterOutput: func(r *ghttp.Request) { glog.Println(ghttp.HookAfterOutput) },
|
||||
})
|
||||
s.BindHandler(p, func(r *ghttp.Request) {
|
||||
r.Response.Write("用户:", r.Get("name"), ", uid:", r.Get("uid"))
|
||||
|
||||
@ -11,7 +11,7 @@ func main() {
|
||||
// 多事件回调示例,事件1
|
||||
pattern1 := "/:name/info"
|
||||
s.BindHookHandlerByMap(pattern1, map[string]ghttp.HandlerFunc{
|
||||
ghttp.HOOK_BEFORE_SERVE: func(r *ghttp.Request) {
|
||||
ghttp.HookBeforeServe: func(r *ghttp.Request) {
|
||||
r.SetParam("uid", 1000)
|
||||
},
|
||||
})
|
||||
@ -22,7 +22,7 @@ func main() {
|
||||
// 多事件回调示例,事件2
|
||||
pattern2 := "/{object}/list/{page}.java"
|
||||
s.BindHookHandlerByMap(pattern2, map[string]ghttp.HandlerFunc{
|
||||
ghttp.HOOK_BEFORE_OUTPUT: func(r *ghttp.Request) {
|
||||
ghttp.HookBeforeOutput: func(r *ghttp.Request) {
|
||||
r.Response.SetBuffer([]byte(
|
||||
fmt.Sprintf("通过事件修改输出内容, object:%s, page:%s", r.Get("object"), r.GetRouterString("page"))),
|
||||
)
|
||||
|
||||
@ -7,10 +7,10 @@ import (
|
||||
|
||||
func main() {
|
||||
s := g.Server()
|
||||
s.BindHookHandler("/*any", ghttp.HOOK_BEFORE_SERVE, func(r *ghttp.Request) {
|
||||
s.BindHookHandler("/*any", ghttp.HookBeforeServe, func(r *ghttp.Request) {
|
||||
r.Response.Writeln("/*any")
|
||||
})
|
||||
s.BindHookHandler("/v1/*", ghttp.HOOK_BEFORE_SERVE, func(r *ghttp.Request) {
|
||||
s.BindHookHandler("/v1/*", ghttp.HookBeforeServe, func(r *ghttp.Request) {
|
||||
r.Response.Writeln("/v1/*")
|
||||
r.ExitHook()
|
||||
})
|
||||
|
||||
@ -11,7 +11,7 @@ func main() {
|
||||
r.Response.Writeln(r.Get("name"))
|
||||
})
|
||||
s.BindHookHandlerByMap("/", map[string]ghttp.HandlerFunc{
|
||||
ghttp.HOOK_BEFORE_SERVE: func(r *ghttp.Request) {
|
||||
ghttp.HookBeforeServe: func(r *ghttp.Request) {
|
||||
r.SetParam("name", "john")
|
||||
},
|
||||
})
|
||||
|
||||
@ -12,17 +12,17 @@ func main() {
|
||||
})
|
||||
|
||||
s.BindHookHandlerByMap("/priority/:name", map[string]ghttp.HandlerFunc{
|
||||
ghttp.HOOK_BEFORE_SERVE: func(r *ghttp.Request) {
|
||||
ghttp.HookBeforeServe: func(r *ghttp.Request) {
|
||||
r.Response.Writeln("/priority/:name")
|
||||
},
|
||||
})
|
||||
s.BindHookHandlerByMap("/priority/*any", map[string]ghttp.HandlerFunc{
|
||||
ghttp.HOOK_BEFORE_SERVE: func(r *ghttp.Request) {
|
||||
ghttp.HookBeforeServe: func(r *ghttp.Request) {
|
||||
r.Response.Writeln("/priority/*any")
|
||||
},
|
||||
})
|
||||
s.BindHookHandlerByMap("/priority/show", map[string]ghttp.HandlerFunc{
|
||||
ghttp.HOOK_BEFORE_SERVE: func(r *ghttp.Request) {
|
||||
ghttp.HookBeforeServe: func(r *ghttp.Request) {
|
||||
r.Response.Writeln("/priority/show")
|
||||
},
|
||||
})
|
||||
|
||||
@ -27,10 +27,10 @@ func main() {
|
||||
})
|
||||
})
|
||||
group.Group("/hook", func(group *ghttp.RouterGroup) {
|
||||
group.Hook("/*", ghttp.HOOK_BEFORE_SERVE, func(r *ghttp.Request) {
|
||||
group.Hook("/*", ghttp.HookBeforeServe, func(r *ghttp.Request) {
|
||||
r.Response.Write("hook any")
|
||||
})
|
||||
group.Hook("/:name", ghttp.HOOK_BEFORE_SERVE, func(r *ghttp.Request) {
|
||||
group.Hook("/:name", ghttp.HookBeforeServe, func(r *ghttp.Request) {
|
||||
r.Response.Write("hook name")
|
||||
})
|
||||
})
|
||||
|
||||
@ -18,7 +18,7 @@ func main() {
|
||||
s := g.Server()
|
||||
s.SetIndexFolder(true)
|
||||
s.SetServerRoot("root")
|
||||
s.BindHookHandler("/*", ghttp.HOOK_BEFORE_SERVE, func(r *ghttp.Request) {
|
||||
s.BindHookHandler("/*", ghttp.HookBeforeServe, func(r *ghttp.Request) {
|
||||
fmt.Println(r.URL.Path, r.IsFileRequest())
|
||||
})
|
||||
s.BindHandler("/template", func(r *ghttp.Request) {
|
||||
|
||||
@ -27,7 +27,7 @@ func main() {
|
||||
s := g.Server()
|
||||
obj := new(Object)
|
||||
s.Group("/api").Bind([]ghttp.GroupItem{
|
||||
{"ALL", "*", HookHandler, ghttp.HOOK_BEFORE_SERVE},
|
||||
{"ALL", "*", HookHandler, ghttp.HookBeforeServe},
|
||||
{"ALL", "/handler", Handler},
|
||||
{"ALL", "/obj", obj},
|
||||
{"GET", "/obj/show", obj, "Show"},
|
||||
|
||||
@ -54,10 +54,10 @@ func main() {
|
||||
})
|
||||
})
|
||||
group.Group("/hook", func(group *ghttp.RouterGroup) {
|
||||
group.Hook("/*", ghttp.HOOK_BEFORE_SERVE, func(r *ghttp.Request) {
|
||||
group.Hook("/*", ghttp.HookBeforeServe, func(r *ghttp.Request) {
|
||||
r.Response.Write("hook any")
|
||||
})
|
||||
group.Hook("/:name", ghttp.HOOK_BEFORE_SERVE, func(r *ghttp.Request) {
|
||||
group.Hook("/:name", ghttp.HookBeforeServe, func(r *ghttp.Request) {
|
||||
r.Response.Write("hook name")
|
||||
})
|
||||
})
|
||||
|
||||
@ -9,7 +9,7 @@ import (
|
||||
|
||||
func main() {
|
||||
s := g.Server()
|
||||
s.BindHookHandler("/*any", ghttp.HOOK_BEFORE_SERVE, func(r *ghttp.Request) {
|
||||
s.BindHookHandler("/*any", ghttp.HookBeforeServe, func(r *ghttp.Request) {
|
||||
fmt.Println(r.Router)
|
||||
fmt.Println(r.Get("customer_id"))
|
||||
})
|
||||
|
||||
@ -13,7 +13,7 @@ func main() {
|
||||
key := "key"
|
||||
|
||||
// 第一次锁带时间
|
||||
gmlock.Lock(key, 1000)
|
||||
gmlock.Lock(key)
|
||||
glog.Println("lock1")
|
||||
// 这个时候上一次的计时解锁已失效
|
||||
gmlock.Unlock(key)
|
||||
|
||||
22
DONATOR.MD
22
DONATOR.MD
@ -119,6 +119,28 @@ please note your github/gitee account in your payment bill. All the donations wi
|
||||
|金毛|wechat|¥100.00|
|
||||
|莫失莫忘|wechat|¥100.00|
|
||||
|**航|alipay|¥20.00|
|
||||
|阿康|wechat|¥100.00|
|
||||
|Tzp|wechat|¥10.00|
|
||||
|[hkxiaoyu118](https://github.com/hkxiaoyu118)|wechat|¥10.00|
|
||||
|辰|wechat|¥50.00|
|
||||
|LSJ|wechat|¥66.66|我想我是海:祝gf越来越好,统治后端
|
||||
|yu|wechat|¥100.00|感谢开源,加油!我是QQ群里的lah
|
||||
|雁字回时月满楼|wechat|¥20.00|感谢gf
|
||||
|Panda|wechat|¥20.00|支持一下!gf很棒👍
|
||||
|[Thunur](https://gitee.com/thunur)|wechat|¥100.00|
|
||||
|[Mr.奇淼](https://www.gin-vue-admin.com/)|wechat|¥18.88|强哥无敌,奇淼爱你
|
||||
|[SliverHorn](hhttps://github.com/sliverhorn)|wechat|¥17.77|强哥无敌,SliverHorn爱你
|
||||
|[fly的狐狸](https://github.com/zcool321)|wechat|¥50.00|
|
||||
|北漂生活|wechat|¥66.66|gf大展鸿图
|
||||
|YJ|wechat|¥10.00|YangJ-Eric祝愿越来越好
|
||||
|秋葵|wechat|¥20.00|之前强哥
|
||||
|陈诚|wechat|¥100.00|Loocor恭喜郭总发版🎉
|
||||
|**栋|alipay|¥100.00|
|
||||
|**浩|alipay|¥100.00|
|
||||
|RAGGA-TIME|alipay|¥50.00|
|
||||
|[ChArmy](https://gitee.com/charmy)|alipay|¥50.00|
|
||||
|[sanfenzui](https://gitee.com/sanfenzui)|alipay|¥88.00|
|
||||
|刘宇|wechat|¥30.00|请你喝咖啡
|
||||
|
||||
|
||||
|
||||
|
||||
@ -125,6 +125,7 @@ The concurrency starts from `100` to `10000`.
|
||||
- [LeYouJia](https://www.leyoujia.com/)
|
||||
- [IGG](https://igg.com)
|
||||
- [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://github.com/gogf/gf/issues/168).
|
||||
|
||||
|
||||
@ -145,6 +145,7 @@ ab -t 10 -c 100 http://127.0.0.1:3000/json
|
||||
- [乐有家](https://www.leyoujia.com/)
|
||||
- [IGG](https://igg.com)
|
||||
- [喜马拉雅](https://www.ximalaya.com)
|
||||
- [作业帮](https://www.zybang.com/)
|
||||
|
||||
> 在这里只列举了部分知名的用户,如果您的企业或者产品正在使用`GoFrame`,欢迎到 [这里](https://github.com/gogf/gf/issues/168) 留言。
|
||||
|
||||
|
||||
174
RELEASE.2.MD
174
RELEASE.2.MD
@ -1,3 +1,177 @@
|
||||
# `v1.14.2` (2020-10-27)
|
||||
|
||||
# GoFrame
|
||||
|
||||
`GF(Go Frame)`是一款模块化、高性能、生产级的Go基础开发框架。实现了比较完善的基础设施建设以及开发工具链,提供了常用的基础开发模块,如:缓存、日志、队列、数组、集合、容器、定时器、命令行、内存锁、对象池、配置管理、资源管理、数据校验、数据编码、定时任务、数据库ORM、TCP/UDP组件、进程管理/通信等等。并提供了Web服务开发的系列核心组件,如:`Router`、`Cookie`、`Session`、`Middleware`、服务注册、模板引擎等等,支持热重启、热更新、域名绑定、`TLS/HTTPS`、`Rewrite`等特性。
|
||||
|
||||
## 特点
|
||||
|
||||
* 模块化、松耦合设计;
|
||||
* 模块丰富、开箱即用;
|
||||
* 简便易用、易于维护;
|
||||
* 高代码质量、高单元测试覆盖率;
|
||||
* 社区活跃,大牛谦逊低调脾气好;
|
||||
* 详尽的开发文档及示例;
|
||||
* 完善的本地中文化支持;
|
||||
* 设计为团队及企业使用;
|
||||
|
||||
## 支持我们
|
||||
|
||||
OSC最佳开源项目评选开始了,如果您喜欢`GoFrame`,欢迎为`GoFrame`投上您宝贵的一票🙏 https://www.oschina.net/p/goframe
|
||||
|
||||
|
||||
# Change Log
|
||||
|
||||
由于`GoFrame`是模块化设计,因此每个版本的更新记录都会以模块的形式进行介绍。
|
||||
|
||||
重要更新:
|
||||
1. 将框架内所有的`json`操作从标准库替换为`json-iterator/go`,提高操作效率。
|
||||
1. 缓存模块重构底层设计,增加适配器设计模式,并增加内存及`Redis`适配器支持。其中内存适配器默认核心模块提供,`Redis`适配器由社区模块提供:https://goframe.org/os/gcache/adapter
|
||||
1. 增加可自定义的校验规则注册特性:https://goframe.org/util/gvalid/customrule
|
||||
1. `Web Server`增加所有配置项示例:https://goframe.org/net/ghttp/config/example
|
||||
1. `ORM`新增基于`Redis`的`SQL`缓存适配器:https://goframe.org/database/gdb/model/cache
|
||||
1. `ORM`新增模型关联实验特性:https://goframe.org/database/gdb/model/association
|
||||
1. `ORM`改进时间自动更新特性增加自定义时间字段:https://goframe.org/database/gdb/model/auto-time
|
||||
1. 错误处理模块新增`Current`及`Next`方法:https://goframe.org/errors/gerror/index
|
||||
|
||||
|
||||
|
||||
## `net`
|
||||
1. `ghttp`
|
||||
- `Client`
|
||||
- 增加`GetVar/PutVar/PostVar`等`*Var`请求方法,用于发起`HTTP`请求获取内容之后直接返回泛型对象,方便类型转换,特别是针对于返回`XML/JSON`的结果处理将会更加简便:https://goframe.org/net/ghttp/client/demo/index
|
||||
- 增加`SetProxy/Proxy`方法,用于设置客户端代理,支持`HTTP/Socket5`代理类型:https://goframe.org/net/ghttp/client/demo/proxy
|
||||
- 增加`SetRedirectLimit/RedirectLimit`方法,用于设置页面跳转数量限制。
|
||||
- `Request`
|
||||
- 增加`ParseQuery`, `ParseForm`方法,用于解析指定类型的参数,并绑定到给定的对象。
|
||||
- 增加`GetHeader`方法,用于获取指定`Header`参数。
|
||||
- 增加`GetRemoteIp`方法,用于获取请求客户端IP。在IP白名单限制时应当使用`GetRemoteIp`而不是`GetClientIp`进行判断,后者可以通过`Header`伪造。
|
||||
- 增加`ReloadParam`方法,往往用在中间件处理中,当中间件修改了请求参数,需要通过调用该方法重新解析一下请求参数。
|
||||
- 增加`GetRouterMap`方法,用于获得所有的路由参数返回为`map`。
|
||||
- `Response`
|
||||
- 将`Output`方法名称改为`Flush`,用于将缓冲区的数据写入到客户端数据流中。
|
||||
- `Server`
|
||||
- `Server`增加所有配置项示例:https://goframe.org/net/ghttp/config/example
|
||||
- 增加`SessionCookieOutput`配置,用于控制是否输出`SessionId`到`Cookie`中,默认为开启。
|
||||
- 改进路由解析,增加对`URI`带有重复的`/`符号的支持。
|
||||
- `Pprof`功能路由支持`Domain`绑定。
|
||||
- 其他一些细节改进。
|
||||
- `Cookie`
|
||||
- 增加`SetHttpCookie`方法,用于根据标准库`http.Cookie`对象设置`Cookie`。
|
||||
- 其他一些功能改进
|
||||
|
||||
|
||||
## `database`
|
||||
1. `gdb`
|
||||
- 新增模型关联实验特性:https://goframe.org/database/gdb/model/association
|
||||
- 改进时间自动更新特性增加自定义时间字段:https://goframe.org/database/gdb/model/auto-time
|
||||
- 新增基于`Redis`的`SQL`缓存适配器:https://goframe.org/database/gdb/model/cache
|
||||
- 新增对输入参数的键名-字段名自动识别映射特性:https://goframe.org/database/gdb/senior
|
||||
- 新增`DB.HasTable`方法,用于判断是否当前数据库存在指定数据表。
|
||||
- 新增`Model.HasField`方法,用于判断是否当前数据表存在指定字段。
|
||||
- 新增`Model.ScanList`方法,用于智能地将当前`struct`/`slice`绑定到指定的`list`对应属性上。
|
||||
- 新增`Result.MapKeyValue`方法,用于将当前`Result`转换为`map[string]Value`类型。
|
||||
- 新增`Result.IsEmpty/Len/Size/ScanList`方法。
|
||||
- 增加`ListItemValues`及`ListItemValuesUnique`方法,用于自动获取`list`中指定名称的键值或属性值,构成`slice`返回。
|
||||
- `SQL`日志内容增加分组名称打印。
|
||||
- 改进`DataToMapDeep`方法。
|
||||
- 其他一些细节改进工作。
|
||||
|
||||
1. `gredis`
|
||||
- 新增`TLS`特性支持,并支持配置文件配置,https://goframe.org/database/gredis/config
|
||||
|
||||
## `container`
|
||||
1. `gvar`
|
||||
- 增加`Scan`及`ScanDeep`方法,用于`struct`/`slice`自动识别转换。
|
||||
- 增加`ListItemValues`及`ListItemValuesUnique`方法,用于自动获取`list`中指定名称的键值或属性值,构成`slice`返回。
|
||||
- 增加`MapStrAny`接口实现方法。
|
||||
|
||||
## `os`
|
||||
1. `gcache`
|
||||
- 增加`GetVar`方法,用于获取缓存数据并返回为泛型对象。
|
||||
- 增加`Update`方法,用于仅修改缓存数值,不修改缓存过期时间。
|
||||
- 增加`UpdateExpire`方法,用于仅修改缓存过期时间,不修改缓存数值。
|
||||
- 重构底层设计,增加适配器设计模式,并增加内存及`Redis`适配器支持。其中内存适配器默认核心模块提供,`Redis`适配器由社区模块提供:https://goframe.org/os/gcache/adapter
|
||||
- 注意,本次模块的修改会有部分方法不兼容,部分方法增加了`error`参数返回,升级时请注意查看。编译时将不会通过。
|
||||
- 其他一些功能改进。
|
||||
1. `gfile`
|
||||
- 增加`ScanDirFileFunc`方法,用于自定义函数处理的递归目录文件遍历。
|
||||
- 改进`Scan*`方法,增加递归层级限制,默认层级限制为`100000`.
|
||||
|
||||
1. `gfsnotify`
|
||||
- 去掉模块初始化时的`Watcher`对象创建,调整为运行时按需创建,并且增加了并发安全控制。
|
||||
|
||||
1. `grpool`
|
||||
- 增加`AddWithRecover`方法,用于添加异步任务时给定一个`recover`处理方法,当任务`panic`时交由该`recover`方法处理,防止异步任务`panic`引起整个进程崩溃。
|
||||
> 这里解决的痛点是`recover`只能捕获到当前`goroutine`的`panic`,因此只能在创建异步任务的时候指定`recover`处理方法。
|
||||
|
||||
1. `gtime`
|
||||
- 增加`ParseDuration`方法,增加了对时间单位`d`的支持,表示天。
|
||||
- 改进`New`方法,支持通过字符串、时间戳、`time.Time`对象创建`gtime.Time`对象,https://goframe.org/os/gtime/time
|
||||
- 改进`Add/AddStr/ToLocation/ToZone/UTCLocal/AddDate/Truncate/Round`方法,这些方法调用时,不再修改当前对象本身,而是创建并返回一个新的`gtine.Time`对象,以便保证和标准库`time.Time`的逻辑一致,防止混淆。
|
||||
- 其他一些细节改进。
|
||||
1. `gtimer`
|
||||
- 增加`Reset`方法,用于重置定时任务的计时。
|
||||
1. `gfcache`
|
||||
- 去掉了该模块,该模块的功能作用不是特别大。
|
||||
|
||||
## `debug`
|
||||
1. `gdebug`
|
||||
- 新增`GoroutineId`方法,用于获取当前执行的`goroutine id`,仅作调试使用。
|
||||
|
||||
## `encoding`
|
||||
1. `gjson`
|
||||
- 新增`GetScan/GetScanDeep`方法。
|
||||
- 新增`ToScan/ToScanDeep`方法。
|
||||
- 新增`LoadContentType`方法,用于根据指定类型的内容创建`Json`操作对象。
|
||||
- 新增`IsValidDataType`方法,用于判断给定的数据类型是否支持解析。
|
||||
- 其他一些改进。
|
||||
- 单元测试完善。
|
||||
|
||||
1. `gcompress`
|
||||
- 新增`GzipFile/UnGzipFile`基于`gzip`压缩算法的文件压缩/解压。
|
||||
|
||||
## `i18n`
|
||||
1. `gi18n`
|
||||
- 新增`TranslateFormat/TranslateFormatLang/Tf/Tfl`方法: https://goframe.org/i18n/gi18n/index
|
||||
|
||||
## `text`
|
||||
1. `gstr`
|
||||
- 增加`SnakeFirstUpperCase`方法,用于在字母大写前增加连接符,并不会处理数字,例如:`SnakeFirstUpperCase("RGBCodeMd5")`将会返回`rgb_code_md5`。
|
||||
|
||||
## `util`
|
||||
1. `gconv`
|
||||
- 增加对指针基本类型的转换支持。
|
||||
- 增加`Scan/ScanDeep`方法,用于自动识别转换`Struct/[]Struct`。
|
||||
- 改进`MapDeep`方法的层级递归处理。
|
||||
- 其他一些细节改进,性能改进。
|
||||
|
||||
1. `gutil`
|
||||
- 增加`ListItemValues`及`ListItemValuesUnique`方法,用于自动获取`list`中指定名称的键值或属性值,构成`slice`返回。
|
||||
- 增加`ItemValue`方法,用于获取指定`map/*map/struct/*struct`类型的键值/属性值。
|
||||
- 增加`MapOmitEmpty`方法,用于过滤`map`中的空值。
|
||||
- 增加`SliceDelete`方法,用于数组项删除。
|
||||
- 增加`Try`方法,通过闭包执行给定的方法,如果方法产生`panic`则该方法返回`error`,否则返回`nil`。
|
||||
- 改进`TryCatch(try func(), catch ...func(exception interface{}))`为`TryCatch(try func(), catch ...func(exception error))`
|
||||
|
||||
1. `gvalid`
|
||||
- 增加自定义规则特性,开发者可注册自定义的校验规则:https://goframe.org/util/gvalid/customrule
|
||||
- 其他一些功能改进。
|
||||
|
||||
## `error`
|
||||
1. `gerror`
|
||||
- 新增`Current`方法,用于获取当前错误层级的`error`接口对象。
|
||||
- 新增`Next`方法,用于获取层级错误的下一级错误`error`接口对象。当下一层级不存在时,返回`nil`。
|
||||
- 文档更新:https://goframe.org/errors/gerror/index
|
||||
|
||||
## Bug Fix
|
||||
1. 修复`garray`模块的`Unique`方法问题。
|
||||
1. 修复`glog`中定时器懒初始化时的`goroutine`泄露问题。
|
||||
1. 修复`gstr`中名称`Case`转换相关方法在名称中带有数字+特殊字符时的名称转换问题。
|
||||
1. 修复`ghttp`模块中的`CORS`跨域设置`Header`细节问题。
|
||||
1. 其他BUG修复:https://github.com/gogf/gf/issues?q=is%3Aissue+label%3Abug+is%3Aclosed
|
||||
|
||||
|
||||
# `v1.13.1` (2020-06-10)
|
||||
|
||||
# GoFrame
|
||||
|
||||
@ -453,7 +453,7 @@ func (a *Array) Clone() (newArray *Array) {
|
||||
array := make([]interface{}, len(a.array))
|
||||
copy(array, a.array)
|
||||
a.mu.RUnlock()
|
||||
return NewArrayFrom(array, !a.mu.IsSafe())
|
||||
return NewArrayFrom(array, a.mu.IsSafe())
|
||||
}
|
||||
|
||||
// Clear deletes all items of current array.
|
||||
|
||||
@ -469,7 +469,7 @@ func (a *IntArray) Clone() (newArray *IntArray) {
|
||||
array := make([]int, len(a.array))
|
||||
copy(array, a.array)
|
||||
a.mu.RUnlock()
|
||||
return NewIntArrayFrom(array, !a.mu.IsSafe())
|
||||
return NewIntArrayFrom(array, a.mu.IsSafe())
|
||||
}
|
||||
|
||||
// Clear deletes all items of current array.
|
||||
|
||||
@ -457,7 +457,7 @@ func (a *StrArray) Clone() (newArray *StrArray) {
|
||||
array := make([]string, len(a.array))
|
||||
copy(array, a.array)
|
||||
a.mu.RUnlock()
|
||||
return NewStrArrayFrom(array, !a.mu.IsSafe())
|
||||
return NewStrArrayFrom(array, a.mu.IsSafe())
|
||||
}
|
||||
|
||||
// Clear deletes all items of current array.
|
||||
|
||||
@ -446,7 +446,7 @@ func (a *SortedArray) binSearch(value interface{}, lock bool) (index int, result
|
||||
mid := 0
|
||||
cmp := -2
|
||||
for min <= max {
|
||||
mid = (min + max) / 2
|
||||
mid = min + int((max-min)/2)
|
||||
cmp = a.getComparator()(value, a.array[mid])
|
||||
switch {
|
||||
case cmp < 0:
|
||||
@ -499,7 +499,7 @@ func (a *SortedArray) Clone() (newArray *SortedArray) {
|
||||
array := make([]interface{}, len(a.array))
|
||||
copy(array, a.array)
|
||||
a.mu.RUnlock()
|
||||
return NewSortedArrayFrom(array, a.comparator, !a.mu.IsSafe())
|
||||
return NewSortedArrayFrom(array, a.comparator, a.mu.IsSafe())
|
||||
}
|
||||
|
||||
// Clear deletes all items of current array.
|
||||
|
||||
@ -443,7 +443,7 @@ func (a *SortedIntArray) binSearch(value int, lock bool) (index int, result int)
|
||||
mid := 0
|
||||
cmp := -2
|
||||
for min <= max {
|
||||
mid = (min + max) / 2
|
||||
mid = min + int((max-min)/2)
|
||||
cmp = a.getComparator()(value, a.array[mid])
|
||||
switch {
|
||||
case cmp < 0:
|
||||
@ -496,7 +496,7 @@ func (a *SortedIntArray) Clone() (newArray *SortedIntArray) {
|
||||
array := make([]int, len(a.array))
|
||||
copy(array, a.array)
|
||||
a.mu.RUnlock()
|
||||
return NewSortedIntArrayFrom(array, !a.mu.IsSafe())
|
||||
return NewSortedIntArrayFrom(array, a.mu.IsSafe())
|
||||
}
|
||||
|
||||
// Clear deletes all items of current array.
|
||||
|
||||
@ -445,7 +445,7 @@ func (a *SortedStrArray) binSearch(value string, lock bool) (index int, result i
|
||||
mid := 0
|
||||
cmp := -2
|
||||
for min <= max {
|
||||
mid = (min + max) / 2
|
||||
mid = min + int((max-min)/2)
|
||||
cmp = a.getComparator()(value, a.array[mid])
|
||||
switch {
|
||||
case cmp < 0:
|
||||
@ -498,7 +498,7 @@ func (a *SortedStrArray) Clone() (newArray *SortedStrArray) {
|
||||
array := make([]string, len(a.array))
|
||||
copy(array, a.array)
|
||||
a.mu.RUnlock()
|
||||
return NewSortedStrArrayFrom(array, !a.mu.IsSafe())
|
||||
return NewSortedStrArrayFrom(array, a.mu.IsSafe())
|
||||
}
|
||||
|
||||
// Clear deletes all items of current array.
|
||||
|
||||
@ -56,7 +56,7 @@ func (m *IntAnyMap) Iterator(f func(k int, v interface{}) bool) {
|
||||
|
||||
// Clone returns a new hash map with copy of current map data.
|
||||
func (m *IntAnyMap) Clone() *IntAnyMap {
|
||||
return NewIntAnyMapFrom(m.MapCopy(), !m.mu.IsSafe())
|
||||
return NewIntAnyMapFrom(m.MapCopy(), m.mu.IsSafe())
|
||||
}
|
||||
|
||||
// Map returns the underlying data map.
|
||||
|
||||
@ -54,7 +54,7 @@ func (m *IntIntMap) Iterator(f func(k int, v int) bool) {
|
||||
|
||||
// Clone returns a new hash map with copy of current map data.
|
||||
func (m *IntIntMap) Clone() *IntIntMap {
|
||||
return NewIntIntMapFrom(m.MapCopy(), !m.mu.IsSafe())
|
||||
return NewIntIntMapFrom(m.MapCopy(), m.mu.IsSafe())
|
||||
}
|
||||
|
||||
// Map returns the underlying data map.
|
||||
|
||||
@ -54,7 +54,7 @@ func (m *IntStrMap) Iterator(f func(k int, v string) bool) {
|
||||
|
||||
// Clone returns a new hash map with copy of current map data.
|
||||
func (m *IntStrMap) Clone() *IntStrMap {
|
||||
return NewIntStrMapFrom(m.MapCopy(), !m.mu.IsSafe())
|
||||
return NewIntStrMapFrom(m.MapCopy(), m.mu.IsSafe())
|
||||
}
|
||||
|
||||
// Map returns the underlying data map.
|
||||
|
||||
@ -56,7 +56,7 @@ func (m *StrAnyMap) Iterator(f func(k string, v interface{}) bool) {
|
||||
|
||||
// Clone returns a new hash map with copy of current map data.
|
||||
func (m *StrAnyMap) Clone() *StrAnyMap {
|
||||
return NewStrAnyMapFrom(m.MapCopy(), !m.mu.IsSafe())
|
||||
return NewStrAnyMapFrom(m.MapCopy(), m.mu.IsSafe())
|
||||
}
|
||||
|
||||
// Map returns the underlying data map.
|
||||
|
||||
@ -54,7 +54,7 @@ func (m *StrIntMap) Iterator(f func(k string, v int) bool) {
|
||||
|
||||
// Clone returns a new hash map with copy of current map data.
|
||||
func (m *StrIntMap) Clone() *StrIntMap {
|
||||
return NewStrIntMapFrom(m.MapCopy(), !m.mu.IsSafe())
|
||||
return NewStrIntMapFrom(m.MapCopy(), m.mu.IsSafe())
|
||||
}
|
||||
|
||||
// Map returns the underlying data map.
|
||||
|
||||
@ -55,7 +55,7 @@ func (m *StrStrMap) Iterator(f func(k string, v string) bool) {
|
||||
|
||||
// Clone returns a new hash map with copy of current map data.
|
||||
func (m *StrStrMap) Clone() *StrStrMap {
|
||||
return NewStrStrMapFrom(m.MapCopy(), !m.mu.IsSafe())
|
||||
return NewStrStrMapFrom(m.MapCopy(), m.mu.IsSafe())
|
||||
}
|
||||
|
||||
// Map returns the underlying data map.
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
// Copyright GoFrame gf Author(https://github.com/gogf/gf). 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,
|
||||
@ -19,18 +19,10 @@ import (
|
||||
|
||||
// Pool is an Object-Reusable Pool.
|
||||
type Pool struct {
|
||||
// Available/idle items list.
|
||||
list *glist.List
|
||||
|
||||
// Whether the pool is closed.
|
||||
closed *gtype.Bool
|
||||
|
||||
// Time To Live for pool items.
|
||||
TTL time.Duration
|
||||
|
||||
// Callback function to create pool item.
|
||||
NewFunc func() (interface{}, error)
|
||||
|
||||
list *glist.List // Available/idle items list.
|
||||
closed *gtype.Bool // Whether the pool is closed.
|
||||
TTL time.Duration // Time To Live for pool items.
|
||||
NewFunc func() (interface{}, error) // Callback function to create pool item.
|
||||
// ExpireFunc is the for expired items destruction.
|
||||
// This function needs to be defined when the pool items
|
||||
// need to perform additional destruction operations.
|
||||
@ -40,8 +32,8 @@ type Pool struct {
|
||||
|
||||
// Pool item.
|
||||
type poolItem struct {
|
||||
expire int64 // Expire timestamp in milliseconds.
|
||||
value interface{} // Item value.
|
||||
value interface{} // Item value.
|
||||
expireAt int64 // Expire timestamp in milliseconds.
|
||||
}
|
||||
|
||||
// Creation function for object.
|
||||
@ -80,11 +72,11 @@ func (p *Pool) Put(value interface{}) error {
|
||||
value: value,
|
||||
}
|
||||
if p.TTL == 0 {
|
||||
item.expire = 0
|
||||
item.expireAt = 0
|
||||
} else {
|
||||
// As for Golang version < 1.13, there's no method Milliseconds for time.Duration.
|
||||
// So we need calculate the milliseconds using its nanoseconds value.
|
||||
item.expire = gtime.TimestampMilli() + p.TTL.Nanoseconds()/1000000
|
||||
item.expireAt = gtime.TimestampMilli() + p.TTL.Nanoseconds()/1000000
|
||||
}
|
||||
p.list.PushBack(item)
|
||||
return nil
|
||||
@ -112,8 +104,11 @@ func (p *Pool) Get() (interface{}, error) {
|
||||
for !p.closed.Val() {
|
||||
if r := p.list.PopFront(); r != nil {
|
||||
f := r.(*poolItem)
|
||||
if f.expire == 0 || f.expire > gtime.TimestampMilli() {
|
||||
if f.expireAt == 0 || f.expireAt > gtime.TimestampMilli() {
|
||||
return f.value, nil
|
||||
} else if p.ExpireFunc != nil {
|
||||
// TODO: move expire function calling asynchronously from `Get` operation.
|
||||
p.ExpireFunc(f.value)
|
||||
}
|
||||
} else {
|
||||
break
|
||||
@ -169,9 +164,9 @@ func (p *Pool) checkExpireItems() {
|
||||
}
|
||||
if r := p.list.PopFront(); r != nil {
|
||||
item := r.(*poolItem)
|
||||
latestExpire = item.expire
|
||||
latestExpire = item.expireAt
|
||||
// TODO improve the auto-expiration mechanism of the pool.
|
||||
if item.expire > timestampMilli {
|
||||
if item.expireAt > timestampMilli {
|
||||
p.list.PushFront(item)
|
||||
break
|
||||
}
|
||||
|
||||
@ -56,7 +56,7 @@ func NewAVLTreeFrom(comparator func(v1, v2 interface{}) int, data map[interface{
|
||||
|
||||
// Clone returns a new tree with a copy of current tree.
|
||||
func (tree *AVLTree) Clone() *AVLTree {
|
||||
newTree := NewAVLTree(tree.comparator, !tree.mu.IsSafe())
|
||||
newTree := NewAVLTree(tree.comparator, tree.mu.IsSafe())
|
||||
newTree.Sets(tree.Map())
|
||||
return newTree
|
||||
}
|
||||
@ -441,9 +441,9 @@ func (tree *AVLTree) MapStrAny() map[string]interface{} {
|
||||
func (tree *AVLTree) Flip(comparator ...func(v1, v2 interface{}) int) {
|
||||
t := (*AVLTree)(nil)
|
||||
if len(comparator) > 0 {
|
||||
t = NewAVLTree(comparator[0], !tree.mu.IsSafe())
|
||||
t = NewAVLTree(comparator[0], tree.mu.IsSafe())
|
||||
} else {
|
||||
t = NewAVLTree(tree.comparator, !tree.mu.IsSafe())
|
||||
t = NewAVLTree(tree.comparator, tree.mu.IsSafe())
|
||||
}
|
||||
tree.IteratorAsc(func(key, value interface{}) bool {
|
||||
t.put(value, key, nil, &t.root)
|
||||
|
||||
@ -68,7 +68,7 @@ func NewBTreeFrom(m int, comparator func(v1, v2 interface{}) int, data map[inter
|
||||
|
||||
// Clone returns a new tree with a copy of current tree.
|
||||
func (tree *BTree) Clone() *BTree {
|
||||
newTree := NewBTree(tree.m, tree.comparator, !tree.mu.IsSafe())
|
||||
newTree := NewBTree(tree.m, tree.comparator, tree.mu.IsSafe())
|
||||
newTree.Sets(tree.Map())
|
||||
return newTree
|
||||
}
|
||||
@ -620,7 +620,7 @@ func (tree *BTree) middle() int {
|
||||
func (tree *BTree) search(node *BTreeNode, key interface{}) (index int, found bool) {
|
||||
low, mid, high := 0, 0, len(node.Entries)-1
|
||||
for low <= high {
|
||||
mid = (high + low) / 2
|
||||
mid = low + int((high-low)/2)
|
||||
compare := tree.getComparator()(key, node.Entries[mid].Key)
|
||||
switch {
|
||||
case compare > 0:
|
||||
|
||||
@ -83,7 +83,7 @@ func (tree *RedBlackTree) SetComparator(comparator func(a, b interface{}) int) {
|
||||
|
||||
// Clone returns a new tree with a copy of current tree.
|
||||
func (tree *RedBlackTree) Clone() *RedBlackTree {
|
||||
newTree := NewRedBlackTree(tree.comparator, !tree.mu.IsSafe())
|
||||
newTree := NewRedBlackTree(tree.comparator, tree.mu.IsSafe())
|
||||
newTree.Sets(tree.Map())
|
||||
return newTree
|
||||
}
|
||||
@ -656,9 +656,9 @@ func (tree *RedBlackTree) Search(key interface{}) (value interface{}, found bool
|
||||
func (tree *RedBlackTree) Flip(comparator ...func(v1, v2 interface{}) int) {
|
||||
t := (*RedBlackTree)(nil)
|
||||
if len(comparator) > 0 {
|
||||
t = NewRedBlackTree(comparator[0], !tree.mu.IsSafe())
|
||||
t = NewRedBlackTree(comparator[0], tree.mu.IsSafe())
|
||||
} else {
|
||||
t = NewRedBlackTree(tree.comparator, !tree.mu.IsSafe())
|
||||
t = NewRedBlackTree(tree.comparator, tree.mu.IsSafe())
|
||||
}
|
||||
tree.IteratorAsc(func(key, value interface{}) bool {
|
||||
t.doSet(value, key)
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2018-2019 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
// Copyright GoFrame Author(https://github.com/gogf/gf). 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,
|
||||
@ -11,8 +11,6 @@ import (
|
||||
"github.com/gogf/gf/internal/json"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/internal/empty"
|
||||
|
||||
"github.com/gogf/gf/container/gtype"
|
||||
"github.com/gogf/gf/os/gtime"
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
@ -88,16 +86,6 @@ func (v *Var) Interface() interface{} {
|
||||
return v.Val()
|
||||
}
|
||||
|
||||
// IsNil checks whether <v> is nil.
|
||||
func (v *Var) IsNil() bool {
|
||||
return v.Val() == nil
|
||||
}
|
||||
|
||||
// IsEmpty checks whether <v> is empty.
|
||||
func (v *Var) IsEmpty() bool {
|
||||
return empty.IsEmpty(v.Val())
|
||||
}
|
||||
|
||||
// Bytes converts and returns <v> as []byte.
|
||||
func (v *Var) Bytes() []byte {
|
||||
return gconv.Bytes(v.Val())
|
||||
|
||||
98
container/gvar/gvar_is.go
Normal file
98
container/gvar/gvar_is.go
Normal file
@ -0,0 +1,98 @@
|
||||
// Copyright GoFrame Author(https://github.com/gogf/gf). 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 gvar
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/internal/empty"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// IsNil checks whether <v> is nil.
|
||||
func (v *Var) IsNil() bool {
|
||||
return v.Val() == nil
|
||||
}
|
||||
|
||||
// IsEmpty checks whether <v> is empty.
|
||||
func (v *Var) IsEmpty() bool {
|
||||
return empty.IsEmpty(v.Val())
|
||||
}
|
||||
|
||||
// IsInt checks whether <v> is type of int.
|
||||
func (v *Var) IsInt() bool {
|
||||
switch v.Val().(type) {
|
||||
case int, *int, int8, *int8, int16, *int16, int32, *int32, int64, *int64:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsUint checks whether <v> is type of uint.
|
||||
func (v *Var) IsUint() bool {
|
||||
switch v.Val().(type) {
|
||||
case uint, *uint, uint8, *uint8, uint16, *uint16, uint32, *uint32, uint64, *uint64:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsFloat checks whether <v> is type of float.
|
||||
func (v *Var) IsFloat() bool {
|
||||
switch v.Val().(type) {
|
||||
case float32, *float32, float64, *float64:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsSlice checks whether <v> is type of slice.
|
||||
func (v *Var) IsSlice() bool {
|
||||
var (
|
||||
reflectValue = reflect.ValueOf(v.Val())
|
||||
reflectKind = reflectValue.Kind()
|
||||
)
|
||||
for reflectKind == reflect.Ptr {
|
||||
reflectValue = reflectValue.Elem()
|
||||
}
|
||||
switch reflectKind {
|
||||
case reflect.Slice, reflect.Array:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsMap checks whether <v> is type of map.
|
||||
func (v *Var) IsMap() bool {
|
||||
var (
|
||||
reflectValue = reflect.ValueOf(v.Val())
|
||||
reflectKind = reflectValue.Kind()
|
||||
)
|
||||
for reflectKind == reflect.Ptr {
|
||||
reflectValue = reflectValue.Elem()
|
||||
}
|
||||
switch reflectKind {
|
||||
case reflect.Map:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsStruct checks whether <v> is type of struct.
|
||||
func (v *Var) IsStruct() bool {
|
||||
var (
|
||||
reflectValue = reflect.ValueOf(v.Val())
|
||||
reflectKind = reflectValue.Kind()
|
||||
)
|
||||
for reflectKind == reflect.Ptr {
|
||||
reflectValue = reflectValue.Elem()
|
||||
reflectKind = reflectValue.Kind()
|
||||
}
|
||||
switch reflectKind {
|
||||
case reflect.Struct:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
@ -20,6 +20,7 @@ func (v *Var) Struct(pointer interface{}, mapping ...map[string]string) error {
|
||||
// Struct maps value of <v> to <pointer> recursively.
|
||||
// The parameter <pointer> should be a pointer to a struct instance.
|
||||
// The parameter <mapping> is used to specify the key-to-attribute mapping rules.
|
||||
// Deprecated, use Struct instead.
|
||||
func (v *Var) StructDeep(pointer interface{}, mapping ...map[string]string) error {
|
||||
return gconv.StructDeep(v.Val(), pointer, mapping...)
|
||||
}
|
||||
@ -30,6 +31,7 @@ func (v *Var) Structs(pointer interface{}, mapping ...map[string]string) error {
|
||||
}
|
||||
|
||||
// StructsDeep converts and returns <v> as given struct slice recursively.
|
||||
// Deprecated, use Struct instead.
|
||||
func (v *Var) StructsDeep(pointer interface{}, mapping ...map[string]string) error {
|
||||
return gconv.StructsDeep(v.Val(), pointer, mapping...)
|
||||
}
|
||||
|
||||
170
container/gvar/gvar_z_unit_is_test.go
Normal file
170
container/gvar/gvar_z_unit_is_test.go
Normal file
@ -0,0 +1,170 @@
|
||||
// Copyright GoFrame Author(https://github.com/gogf/gf). 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 gvar_test
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/test/gtest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestVar_IsNil(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(g.NewVar(0).IsNil(), false)
|
||||
t.Assert(g.NewVar(nil).IsNil(), true)
|
||||
t.Assert(g.NewVar(g.Map{}).IsNil(), false)
|
||||
t.Assert(g.NewVar(g.Slice{}).IsNil(), false)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(g.NewVar(1).IsNil(), false)
|
||||
t.Assert(g.NewVar(0.1).IsNil(), false)
|
||||
t.Assert(g.NewVar(g.Map{"k": "v"}).IsNil(), false)
|
||||
t.Assert(g.NewVar(g.Slice{0}).IsNil(), false)
|
||||
})
|
||||
}
|
||||
|
||||
func TestVar_IsEmpty(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(g.NewVar(0).IsEmpty(), true)
|
||||
t.Assert(g.NewVar(nil).IsEmpty(), true)
|
||||
t.Assert(g.NewVar(g.Map{}).IsEmpty(), true)
|
||||
t.Assert(g.NewVar(g.Slice{}).IsEmpty(), true)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(g.NewVar(1).IsEmpty(), false)
|
||||
t.Assert(g.NewVar(0.1).IsEmpty(), false)
|
||||
t.Assert(g.NewVar(g.Map{"k": "v"}).IsEmpty(), false)
|
||||
t.Assert(g.NewVar(g.Slice{0}).IsEmpty(), false)
|
||||
})
|
||||
}
|
||||
|
||||
func TestVar_IsInt(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(g.NewVar(0).IsInt(), true)
|
||||
t.Assert(g.NewVar(nil).IsInt(), false)
|
||||
t.Assert(g.NewVar(g.Map{}).IsInt(), false)
|
||||
t.Assert(g.NewVar(g.Slice{}).IsInt(), false)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(g.NewVar(1).IsInt(), true)
|
||||
t.Assert(g.NewVar(-1).IsInt(), true)
|
||||
t.Assert(g.NewVar(0.1).IsInt(), false)
|
||||
t.Assert(g.NewVar(g.Map{"k": "v"}).IsInt(), false)
|
||||
t.Assert(g.NewVar(g.Slice{0}).IsInt(), false)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(g.NewVar(int8(1)).IsInt(), true)
|
||||
t.Assert(g.NewVar(uint8(1)).IsInt(), false)
|
||||
})
|
||||
}
|
||||
|
||||
func TestVar_IsUint(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(g.NewVar(0).IsUint(), false)
|
||||
t.Assert(g.NewVar(nil).IsUint(), false)
|
||||
t.Assert(g.NewVar(g.Map{}).IsUint(), false)
|
||||
t.Assert(g.NewVar(g.Slice{}).IsUint(), false)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(g.NewVar(1).IsUint(), false)
|
||||
t.Assert(g.NewVar(-1).IsUint(), false)
|
||||
t.Assert(g.NewVar(0.1).IsUint(), false)
|
||||
t.Assert(g.NewVar(g.Map{"k": "v"}).IsUint(), false)
|
||||
t.Assert(g.NewVar(g.Slice{0}).IsUint(), false)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(g.NewVar(int8(1)).IsUint(), false)
|
||||
t.Assert(g.NewVar(uint8(1)).IsUint(), true)
|
||||
})
|
||||
}
|
||||
|
||||
func TestVar_IsFloat(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(g.NewVar(0).IsFloat(), false)
|
||||
t.Assert(g.NewVar(nil).IsFloat(), false)
|
||||
t.Assert(g.NewVar(g.Map{}).IsFloat(), false)
|
||||
t.Assert(g.NewVar(g.Slice{}).IsFloat(), false)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(g.NewVar(1).IsFloat(), false)
|
||||
t.Assert(g.NewVar(-1).IsFloat(), false)
|
||||
t.Assert(g.NewVar(0.1).IsFloat(), true)
|
||||
t.Assert(g.NewVar(float64(1)).IsFloat(), true)
|
||||
t.Assert(g.NewVar(g.Map{"k": "v"}).IsFloat(), false)
|
||||
t.Assert(g.NewVar(g.Slice{0}).IsFloat(), false)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(g.NewVar(int8(1)).IsFloat(), false)
|
||||
t.Assert(g.NewVar(uint8(1)).IsFloat(), false)
|
||||
})
|
||||
}
|
||||
|
||||
func TestVar_IsSlice(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(g.NewVar(0).IsSlice(), false)
|
||||
t.Assert(g.NewVar(nil).IsSlice(), false)
|
||||
t.Assert(g.NewVar(g.Map{}).IsSlice(), false)
|
||||
t.Assert(g.NewVar(g.Slice{}).IsSlice(), true)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(g.NewVar(1).IsSlice(), false)
|
||||
t.Assert(g.NewVar(-1).IsSlice(), false)
|
||||
t.Assert(g.NewVar(0.1).IsSlice(), false)
|
||||
t.Assert(g.NewVar(float64(1)).IsSlice(), false)
|
||||
t.Assert(g.NewVar(g.Map{"k": "v"}).IsSlice(), false)
|
||||
t.Assert(g.NewVar(g.Slice{0}).IsSlice(), true)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(g.NewVar(int8(1)).IsSlice(), false)
|
||||
t.Assert(g.NewVar(uint8(1)).IsSlice(), false)
|
||||
})
|
||||
}
|
||||
|
||||
func TestVar_IsMap(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(g.NewVar(0).IsMap(), false)
|
||||
t.Assert(g.NewVar(nil).IsMap(), false)
|
||||
t.Assert(g.NewVar(g.Map{}).IsMap(), true)
|
||||
t.Assert(g.NewVar(g.Slice{}).IsMap(), false)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(g.NewVar(1).IsMap(), false)
|
||||
t.Assert(g.NewVar(-1).IsMap(), false)
|
||||
t.Assert(g.NewVar(0.1).IsMap(), false)
|
||||
t.Assert(g.NewVar(float64(1)).IsMap(), false)
|
||||
t.Assert(g.NewVar(g.Map{"k": "v"}).IsMap(), true)
|
||||
t.Assert(g.NewVar(g.Slice{0}).IsMap(), false)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(g.NewVar(int8(1)).IsMap(), false)
|
||||
t.Assert(g.NewVar(uint8(1)).IsMap(), false)
|
||||
})
|
||||
}
|
||||
|
||||
func TestVar_IsStruct(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(g.NewVar(0).IsStruct(), false)
|
||||
t.Assert(g.NewVar(nil).IsStruct(), false)
|
||||
t.Assert(g.NewVar(g.Map{}).IsStruct(), false)
|
||||
t.Assert(g.NewVar(g.Slice{}).IsStruct(), false)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(g.NewVar(1).IsStruct(), false)
|
||||
t.Assert(g.NewVar(-1).IsStruct(), false)
|
||||
t.Assert(g.NewVar(0.1).IsStruct(), false)
|
||||
t.Assert(g.NewVar(float64(1)).IsStruct(), false)
|
||||
t.Assert(g.NewVar(g.Map{"k": "v"}).IsStruct(), false)
|
||||
t.Assert(g.NewVar(g.Slice{0}).IsStruct(), false)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a := &struct {
|
||||
}{}
|
||||
t.Assert(g.NewVar(a).IsStruct(), true)
|
||||
t.Assert(g.NewVar(*a).IsStruct(), true)
|
||||
t.Assert(g.NewVar(&a).IsStruct(), true)
|
||||
})
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
// Copyright GoFrame Author(https://github.com/gogf/gf). 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,
|
||||
@ -8,10 +8,11 @@
|
||||
package gdb
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/internal/cmdenv"
|
||||
"github.com/gogf/gf/errors/gerror"
|
||||
"github.com/gogf/gf/os/gcmd"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/container/gvar"
|
||||
@ -42,6 +43,12 @@ type DB interface {
|
||||
// Note that it is not recommended using the this function manually.
|
||||
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.
|
||||
Ctx(ctx context.Context) DB
|
||||
|
||||
// ===========================================================================
|
||||
// Query APIs.
|
||||
// ===========================================================================
|
||||
@ -137,6 +144,7 @@ type DB interface {
|
||||
// Utility methods.
|
||||
// ===========================================================================
|
||||
|
||||
GetCtx() context.Context
|
||||
GetChars() (charLeft string, charRight string)
|
||||
GetMaster(schema ...string) (*sql.DB, error)
|
||||
GetSlave(schema ...string) (*sql.DB, error)
|
||||
@ -154,28 +162,24 @@ type DB interface {
|
||||
HandleSqlBeforeCommit(link Link, sql string, args []interface{}) (string, []interface{})
|
||||
|
||||
// ===========================================================================
|
||||
// Internal methods.
|
||||
// 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)
|
||||
convertValue(fieldValue interface{}, fieldType string) interface{}
|
||||
rowsToResult(rows *sql.Rows) (Result, error)
|
||||
convertFieldValueToLocalValue(fieldValue interface{}, fieldType string) interface{}
|
||||
convertRowsToResult(rows *sql.Rows) (Result, error)
|
||||
}
|
||||
|
||||
// Core is the base struct for database management.
|
||||
type Core struct {
|
||||
DB DB // DB interface object.
|
||||
group string // Configuration group name.
|
||||
debug *gtype.Bool // Enable debug mode for the database.
|
||||
cache *gcache.Cache // Cache manager, SQL result cache only.
|
||||
schema *gtype.String // Custom schema for this object.
|
||||
dryrun *gtype.Bool // Dry run.
|
||||
prefix string // Table prefix.
|
||||
logger *glog.Logger // Logger.
|
||||
config *ConfigNode // Current config node.
|
||||
maxIdleConnCount int // Max idle connection count.
|
||||
maxOpenConnCount int // Max open connection count.
|
||||
maxConnLifetime time.Duration // Max TTL for a connection.
|
||||
DB DB // DB interface object.
|
||||
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.
|
||||
@ -214,32 +218,30 @@ type Link interface {
|
||||
Prepare(sql string) (*sql.Stmt, error)
|
||||
}
|
||||
|
||||
// Counter is the type for update count.
|
||||
type Counter struct {
|
||||
Field string
|
||||
Value float64
|
||||
}
|
||||
|
||||
type (
|
||||
// Value is the field value type.
|
||||
Value = *gvar.Var
|
||||
|
||||
// Record is the row record of the table.
|
||||
Record map[string]Value
|
||||
|
||||
// Result is the row record array.
|
||||
Result []Record
|
||||
|
||||
// Map is alias of map[string]interface{},
|
||||
// which is the most common usage map type.
|
||||
Map = map[string]interface{}
|
||||
|
||||
// List is type of map array.
|
||||
List = []Map
|
||||
Raw string // Raw is a raw sql that will not be treated as argument but as a direct sql part.
|
||||
Value = *gvar.Var // Value is the field value type.
|
||||
Record map[string]Value // Record is the row record of the table.
|
||||
Result []Record // Result is the row record array.
|
||||
Map = map[string]interface{} // Map is alias of map[string]interface{}, which is the most common usage map type.
|
||||
List = []Map // List is type of map array.
|
||||
)
|
||||
|
||||
const (
|
||||
gINSERT_OPTION_DEFAULT = 0
|
||||
gINSERT_OPTION_REPLACE = 1
|
||||
gINSERT_OPTION_SAVE = 2
|
||||
gINSERT_OPTION_IGNORE = 3
|
||||
gDEFAULT_BATCH_NUM = 10 // Per count for batch insert/replace/save
|
||||
gDEFAULT_CONN_MAX_IDLE_COUNT = 10 // Max idle connection count in pool.
|
||||
gDEFAULT_CONN_MAX_LIFE_TIME = 30 // Max life time for per connection in pool in seconds.
|
||||
insertOptionDefault = 0
|
||||
insertOptionReplace = 1
|
||||
insertOptionSave = 2
|
||||
insertOptionIgnore = 3
|
||||
defaultBatchNumber = 10 // Per count for batch insert/replace/save.
|
||||
defaultMaxIdleConnCount = 10 // Max idle connection count in pool.
|
||||
defaultMaxOpenConnCount = 100 // Max open connection count in pool.
|
||||
defaultMaxConnLifeTime = 30 * time.Second // Max life time for per connection in pool in seconds.
|
||||
)
|
||||
|
||||
var (
|
||||
@ -264,7 +266,10 @@ var (
|
||||
|
||||
// regularFieldNameRegPattern is the regular expression pattern for a string
|
||||
// which is a regular field name of table.
|
||||
regularFieldNameRegPattern = `^[\w\.\-]+$`
|
||||
regularFieldNameRegPattern = `^[\w\.\-\_]+$`
|
||||
|
||||
// internalCache is the memory cache for internal usage.
|
||||
internalCache = gcache.New()
|
||||
|
||||
// allDryRun sets dry-run feature for all database connections.
|
||||
// It is commonly used for command options for convenience.
|
||||
@ -273,7 +278,7 @@ var (
|
||||
|
||||
func init() {
|
||||
// allDryRun is initialized from environment or command options.
|
||||
allDryRun = cmdenv.Get("gf.gdb.dryrun", false).Bool()
|
||||
allDryRun = gcmd.GetWithEnv("gf.gdb.dryrun", false).Bool()
|
||||
}
|
||||
|
||||
// Register registers custom database driver to gdb.
|
||||
@ -284,7 +289,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,
|
||||
// which is DEFAULT_GROUP_NAME in default.
|
||||
// which is DefaultGroupName in default.
|
||||
func New(group ...string) (db DB, err error) {
|
||||
groupName := configs.group
|
||||
if len(group) > 0 && group[0] != "" {
|
||||
@ -294,21 +299,17 @@ func New(group ...string) (db DB, err error) {
|
||||
defer configs.RUnlock()
|
||||
|
||||
if len(configs.config) < 1 {
|
||||
return nil, errors.New("empty database configuration")
|
||||
return nil, gerror.New("empty database configuration")
|
||||
}
|
||||
if _, ok := configs.config[groupName]; ok {
|
||||
if node, err := getConfigNodeByGroup(groupName, true); err == nil {
|
||||
c := &Core{
|
||||
group: groupName,
|
||||
debug: gtype.NewBool(),
|
||||
cache: gcache.New(),
|
||||
schema: gtype.NewString(),
|
||||
dryrun: gtype.NewBool(),
|
||||
logger: glog.New(),
|
||||
prefix: node.Prefix,
|
||||
config: node,
|
||||
maxIdleConnCount: gDEFAULT_CONN_MAX_IDLE_COUNT,
|
||||
maxConnLifetime: gDEFAULT_CONN_MAX_LIFE_TIME, // Default max connection life time if user does not configure.
|
||||
group: groupName,
|
||||
debug: gtype.NewBool(),
|
||||
cache: gcache.New(),
|
||||
schema: gtype.NewString(),
|
||||
logger: glog.New(),
|
||||
config: node,
|
||||
}
|
||||
if v, ok := driverMap[node.Type]; ok {
|
||||
c.DB, err = v.New(c, node)
|
||||
@ -317,19 +318,19 @@ func New(group ...string) (db DB, err error) {
|
||||
}
|
||||
return c.DB, nil
|
||||
} else {
|
||||
return nil, errors.New(fmt.Sprintf(`unsupported database type "%s"`, node.Type))
|
||||
return nil, gerror.New(fmt.Sprintf(`unsupported database type "%s"`, node.Type))
|
||||
}
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
return nil, errors.New(fmt.Sprintf(`database configuration node "%s" is not found`, groupName))
|
||||
return nil, gerror.New(fmt.Sprintf(`database configuration node "%s" is not found`, groupName))
|
||||
}
|
||||
}
|
||||
|
||||
// Instance returns an instance for DB operations.
|
||||
// The parameter <name> specifies the configuration group name,
|
||||
// which is DEFAULT_GROUP_NAME in default.
|
||||
// which is DefaultGroupName in default.
|
||||
func Instance(name ...string) (db DB, err error) {
|
||||
group := configs.group
|
||||
if len(name) > 0 && name[0] != "" {
|
||||
@ -363,7 +364,7 @@ func getConfigNodeByGroup(group string, master bool) (*ConfigNode, error) {
|
||||
}
|
||||
}
|
||||
if len(masterList) < 1 {
|
||||
return nil, errors.New("at least one master node configuration's need to make sense")
|
||||
return nil, gerror.New("at least one master node configuration's need to make sense")
|
||||
}
|
||||
if len(slaveList) < 1 {
|
||||
slaveList = masterList
|
||||
@ -374,7 +375,7 @@ func getConfigNodeByGroup(group string, master bool) (*ConfigNode, error) {
|
||||
return getConfigNodeByWeight(slaveList), nil
|
||||
}
|
||||
} else {
|
||||
return nil, errors.New(fmt.Sprintf("empty database configuration for item name '%s'", group))
|
||||
return nil, gerror.New(fmt.Sprintf("empty database configuration for item name '%s'", group))
|
||||
}
|
||||
}
|
||||
|
||||
@ -441,28 +442,32 @@ func (c *Core) getSqlDb(master bool, schema ...string) (sqlDb *sql.DB, err error
|
||||
node = &n
|
||||
}
|
||||
// Cache the underlying connection pool object by node.
|
||||
v, _ := gcache.GetOrSetFuncLock(node.String(), func() (interface{}, error) {
|
||||
v, _ := internalCache.GetOrSetFuncLock(node.String(), func() (interface{}, error) {
|
||||
sqlDb, err = c.DB.Open(node)
|
||||
if err != nil {
|
||||
intlog.Printf("DB open failed: %v, %+v", err, node)
|
||||
return nil, err
|
||||
}
|
||||
if c.maxIdleConnCount > 0 {
|
||||
sqlDb.SetMaxIdleConns(c.maxIdleConnCount)
|
||||
} else if node.MaxIdleConnCount > 0 {
|
||||
sqlDb.SetMaxIdleConns(node.MaxIdleConnCount)
|
||||
if c.config.MaxIdleConnCount > 0 {
|
||||
sqlDb.SetMaxIdleConns(c.config.MaxIdleConnCount)
|
||||
} else {
|
||||
sqlDb.SetMaxIdleConns(defaultMaxIdleConnCount)
|
||||
}
|
||||
|
||||
if c.maxOpenConnCount > 0 {
|
||||
sqlDb.SetMaxOpenConns(c.maxOpenConnCount)
|
||||
} else if node.MaxOpenConnCount > 0 {
|
||||
sqlDb.SetMaxOpenConns(node.MaxOpenConnCount)
|
||||
if c.config.MaxOpenConnCount > 0 {
|
||||
sqlDb.SetMaxOpenConns(c.config.MaxOpenConnCount)
|
||||
} else {
|
||||
sqlDb.SetMaxOpenConns(defaultMaxOpenConnCount)
|
||||
}
|
||||
|
||||
if c.maxConnLifetime > 0 {
|
||||
sqlDb.SetConnMaxLifetime(c.maxConnLifetime * time.Second)
|
||||
} else if node.MaxConnLifetime > 0 {
|
||||
sqlDb.SetConnMaxLifetime(node.MaxConnLifetime * time.Second)
|
||||
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)
|
||||
} else {
|
||||
sqlDb.SetConnMaxLifetime(c.config.MaxConnLifetime * time.Second)
|
||||
}
|
||||
} else {
|
||||
sqlDb.SetConnMaxLifetime(defaultMaxConnLifeTime)
|
||||
}
|
||||
return sqlDb, nil
|
||||
}, 0)
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
// Copyright GoFrame Author(https://github.com/gogf/gf). 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,
|
||||
@ -8,9 +8,10 @@
|
||||
package gdb
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/errors/gerror"
|
||||
"github.com/gogf/gf/text/gstr"
|
||||
"reflect"
|
||||
"strings"
|
||||
@ -23,6 +24,35 @@ import (
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
)
|
||||
|
||||
// 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.
|
||||
func (c *Core) Ctx(ctx context.Context) DB {
|
||||
if ctx == nil {
|
||||
return c.DB
|
||||
}
|
||||
var (
|
||||
err error
|
||||
newCore = &Core{}
|
||||
configNode = c.DB.GetConfig()
|
||||
)
|
||||
*newCore = *c
|
||||
newCore.ctx = ctx
|
||||
newCore.DB, err = driverMap[configNode.Type].New(newCore, configNode)
|
||||
// Seldom error, just log it.
|
||||
if err != nil {
|
||||
c.DB.GetLogger().Ctx(ctx).Error(err)
|
||||
}
|
||||
return newCore.DB
|
||||
}
|
||||
|
||||
// GetCtx returns the context for current DB.
|
||||
// Note that it might be nil.
|
||||
func (c *Core) GetCtx() context.Context {
|
||||
return c.ctx
|
||||
}
|
||||
|
||||
// Master creates and returns a connection from master node if master-slave configured.
|
||||
// It returns the default connection if master-slave not configured.
|
||||
func (c *Core) Master() (*sql.DB, error) {
|
||||
@ -164,7 +194,7 @@ func (c *Core) DoGetAll(link Link, sql string, args ...interface{}) (result Resu
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
return c.DB.rowsToResult(rows)
|
||||
return c.DB.convertRowsToResult(rows)
|
||||
}
|
||||
|
||||
// GetOne queries and returns one record from database.
|
||||
@ -437,10 +467,10 @@ func (c *Core) DoInsert(link Link, table string, data interface{}, option int, b
|
||||
case reflect.Map:
|
||||
dataMap = ConvertDataForTableRecord(data)
|
||||
default:
|
||||
return result, errors.New(fmt.Sprint("unsupported data type:", reflectKind))
|
||||
return result, gerror.New(fmt.Sprint("unsupported data type:", reflectKind))
|
||||
}
|
||||
if len(dataMap) == 0 {
|
||||
return nil, errors.New("data cannot be empty")
|
||||
return nil, gerror.New("data cannot be empty")
|
||||
}
|
||||
var (
|
||||
charL, charR = c.DB.GetChars()
|
||||
@ -449,10 +479,14 @@ func (c *Core) DoInsert(link Link, table string, data interface{}, option int, b
|
||||
)
|
||||
for k, v := range dataMap {
|
||||
fields = append(fields, charL+k+charR)
|
||||
values = append(values, "?")
|
||||
params = append(params, v)
|
||||
if s, ok := v.(Raw); ok {
|
||||
values = append(values, gconv.String(s))
|
||||
} else {
|
||||
values = append(values, "?")
|
||||
params = append(params, v)
|
||||
}
|
||||
}
|
||||
if option == gINSERT_OPTION_SAVE {
|
||||
if option == insertOptionSave {
|
||||
for k, _ := range dataMap {
|
||||
// If it's SAVE operation,
|
||||
// do not automatically update the creating time.
|
||||
@ -573,11 +607,11 @@ func (c *Core) DoBatchInsert(link Link, table string, list interface{}, option i
|
||||
listMap = List{ConvertDataForTableRecord(value)}
|
||||
}
|
||||
default:
|
||||
return result, errors.New(fmt.Sprint("unsupported list type:", kind))
|
||||
return result, gerror.New(fmt.Sprint("unsupported list type:", kind))
|
||||
}
|
||||
}
|
||||
if len(listMap) < 1 {
|
||||
return result, errors.New("data list cannot be empty")
|
||||
return result, gerror.New("data list cannot be empty")
|
||||
}
|
||||
if link == nil {
|
||||
if link, err = c.DB.Master(); err != nil {
|
||||
@ -585,21 +619,18 @@ func (c *Core) DoBatchInsert(link Link, table string, list interface{}, option i
|
||||
}
|
||||
}
|
||||
// Handle the field names and place holders.
|
||||
holders := []string(nil)
|
||||
for k, _ := range listMap[0] {
|
||||
keys = append(keys, k)
|
||||
holders = append(holders, "?")
|
||||
}
|
||||
// Prepare the batch result pointer.
|
||||
var (
|
||||
charL, charR = c.DB.GetChars()
|
||||
batchResult = new(SqlResult)
|
||||
keysStr = charL + strings.Join(keys, charR+","+charL) + charR
|
||||
valueHolderStr = "(" + strings.Join(holders, ",") + ")"
|
||||
operation = GetInsertOperationByOption(option)
|
||||
updateStr = ""
|
||||
charL, charR = c.DB.GetChars()
|
||||
batchResult = new(SqlResult)
|
||||
keysStr = charL + strings.Join(keys, charR+","+charL) + charR
|
||||
operation = GetInsertOperationByOption(option)
|
||||
updateStr = ""
|
||||
)
|
||||
if option == gINSERT_OPTION_SAVE {
|
||||
if option == insertOptionSave {
|
||||
for _, k := range keys {
|
||||
// If it's SAVE operation,
|
||||
// do not automatically update the creating time.
|
||||
@ -617,27 +648,34 @@ func (c *Core) DoBatchInsert(link Link, table string, list interface{}, option i
|
||||
}
|
||||
updateStr = fmt.Sprintf("ON DUPLICATE KEY UPDATE %s", updateStr)
|
||||
}
|
||||
batchNum := gDEFAULT_BATCH_NUM
|
||||
batchNum := defaultBatchNumber
|
||||
if len(batch) > 0 && batch[0] > 0 {
|
||||
batchNum = batch[0]
|
||||
}
|
||||
listMapLen := len(listMap)
|
||||
var (
|
||||
listMapLen = len(listMap)
|
||||
valueHolder = make([]string, 0)
|
||||
)
|
||||
for i := 0; i < listMapLen; i++ {
|
||||
values = values[:0]
|
||||
// Note that the map type is unordered,
|
||||
// so it should use slice+key to retrieve the value.
|
||||
for _, k := range keys {
|
||||
params = append(params, listMap[i][k])
|
||||
if s, ok := listMap[i][k].(Raw); ok {
|
||||
values = append(values, gconv.String(s))
|
||||
} else {
|
||||
values = append(values, "?")
|
||||
params = append(params, listMap[i][k])
|
||||
}
|
||||
}
|
||||
values = append(values, valueHolderStr)
|
||||
valueHolder = append(valueHolder, "("+gstr.Join(values, ",")+")")
|
||||
if len(values) == batchNum || (i == listMapLen-1 && len(values) > 0) {
|
||||
r, err := c.DB.DoExec(
|
||||
link,
|
||||
fmt.Sprintf(
|
||||
"%s INTO %s(%s) VALUES%s %s",
|
||||
operation,
|
||||
table,
|
||||
keysStr,
|
||||
strings.Join(values, ","),
|
||||
operation, table, keysStr,
|
||||
gstr.Join(valueHolder, ","),
|
||||
updateStr,
|
||||
),
|
||||
params...,
|
||||
@ -652,7 +690,7 @@ func (c *Core) DoBatchInsert(link Link, table string, list interface{}, option i
|
||||
batchResult.affected += n
|
||||
}
|
||||
params = params[:0]
|
||||
values = values[:0]
|
||||
valueHolder = valueHolder[:0]
|
||||
}
|
||||
}
|
||||
return batchResult, nil
|
||||
@ -699,15 +737,35 @@ func (c *Core) DoUpdate(link Link, table string, data interface{}, condition str
|
||||
dataMap = ConvertDataForTableRecord(data)
|
||||
)
|
||||
for k, v := range dataMap {
|
||||
fields = append(fields, c.DB.QuoteWord(k)+"=?")
|
||||
params = append(params, v)
|
||||
switch value := v.(type) {
|
||||
case *Counter:
|
||||
if value.Value != 0 {
|
||||
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)
|
||||
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))
|
||||
} else {
|
||||
fields = append(fields, c.DB.QuoteWord(k)+"=?")
|
||||
params = append(params, v)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
updates = strings.Join(fields, ",")
|
||||
default:
|
||||
updates = gconv.String(data)
|
||||
}
|
||||
if len(updates) == 0 {
|
||||
return nil, errors.New("data cannot be empty")
|
||||
return nil, gerror.New("data cannot be empty")
|
||||
}
|
||||
if len(params) > 0 {
|
||||
args = append(params, args...)
|
||||
@ -752,8 +810,8 @@ func (c *Core) DoDelete(link Link, table string, condition string, args ...inter
|
||||
return c.DB.DoExec(link, fmt.Sprintf("DELETE FROM %s%s", table, condition), args...)
|
||||
}
|
||||
|
||||
// rowsToResult converts underlying data record type sql.Rows to Result type.
|
||||
func (c *Core) rowsToResult(rows *sql.Rows) (Result, error) {
|
||||
// convertRowsToResult converts underlying data record type sql.Rows to Result type.
|
||||
func (c *Core) convertRowsToResult(rows *sql.Rows) (Result, error) {
|
||||
if !rows.Next() {
|
||||
return nil, nil
|
||||
}
|
||||
@ -785,7 +843,7 @@ func (c *Core) rowsToResult(rows *sql.Rows) (Result, error) {
|
||||
if value == nil {
|
||||
row[columnNames[i]] = gvar.New(nil)
|
||||
} else {
|
||||
row[columnNames[i]] = gvar.New(c.DB.convertValue(value, columnTypes[i]))
|
||||
row[columnNames[i]] = gvar.New(c.DB.convertFieldValueToLocalValue(value, columnTypes[i]))
|
||||
}
|
||||
}
|
||||
records = append(records, row)
|
||||
@ -806,14 +864,14 @@ func (c *Core) MarshalJSON() ([]byte, error) {
|
||||
}
|
||||
|
||||
// writeSqlToLogger outputs the sql object to logger.
|
||||
// It is enabled when configuration "debug" is true.
|
||||
// It is enabled only if configuration "debug" is true.
|
||||
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.Error(s)
|
||||
c.logger.Ctx(c.DB.GetCtx()).Error(s)
|
||||
} else {
|
||||
c.logger.Debug(s)
|
||||
c.logger.Ctx(c.DB.GetCtx()).Debug(s)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
// Copyright GoFrame Author(https://github.com/gogf/gf). 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,
|
||||
@ -16,7 +16,8 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
DEFAULT_GROUP_NAME = "default" // Default group name.
|
||||
DEFAULT_GROUP_NAME = "default" // Deprecated, use DefaultGroupName instead.
|
||||
DefaultGroupName = "default" // Default group name.
|
||||
)
|
||||
|
||||
// Config is the configuration management object.
|
||||
@ -27,25 +28,26 @@ type ConfigGroup []ConfigNode
|
||||
|
||||
// ConfigNode is configuration for one node.
|
||||
type ConfigNode struct {
|
||||
Host string // Host of server, ip or domain like: 127.0.0.1, localhost
|
||||
Port string // Port, it's commonly 3306.
|
||||
User string // Authentication username.
|
||||
Pass string // Authentication password.
|
||||
Name string // Default used database name.
|
||||
Type string // Database type: mysql, sqlite, mssql, pgsql, oracle.
|
||||
Role string // (Optional, "master" in default) Node role, used for master-slave mode: master, slave.
|
||||
Debug bool // (Optional) Debug mode enables debug information logging and output.
|
||||
Prefix string // (Optional) Table prefix.
|
||||
DryRun bool // (Optional) Dry run, which does SELECT but no INSERT/UPDATE/DELETE statements.
|
||||
Weight int // (Optional) Weight for load balance calculating, it's useless if there's just one node.
|
||||
Charset string // (Optional, "utf8mb4" in default) Custom charset when operating on database.
|
||||
CreatedAt string // (Optional) The filed name of table for automatic-filled created datetime.
|
||||
UpdatedAt string // (Optional) The filed name of table for automatic-filled updated datetime.
|
||||
DeletedAt string // (Optional) The filed name of table for automatic-filled updated datetime.
|
||||
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.
|
||||
Host string // Host of server, ip or domain like: 127.0.0.1, localhost
|
||||
Port string // Port, it's commonly 3306.
|
||||
User string // Authentication username.
|
||||
Pass string // Authentication password.
|
||||
Name string // Default used database name.
|
||||
Type string // Database type: mysql, sqlite, mssql, pgsql, oracle.
|
||||
Role string // (Optional, "master" in default) Node role, used for master-slave mode: master, slave.
|
||||
Debug bool // (Optional) Debug mode enables debug information logging and output.
|
||||
Prefix string // (Optional) Table prefix.
|
||||
DryRun bool // (Optional) Dry run, which does SELECT but no INSERT/UPDATE/DELETE statements.
|
||||
Weight int // (Optional) Weight for load balance calculating, it's useless if there's just one node.
|
||||
Charset string // (Optional, "utf8mb4" in default) Custom charset when operating on database.
|
||||
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.
|
||||
CreatedAt string // (Optional) The filed name of table for automatic-filled created datetime.
|
||||
UpdatedAt string // (Optional) The filed name of table for automatic-filled updated datetime.
|
||||
DeletedAt string // (Optional) The filed name of table for automatic-filled updated datetime.
|
||||
TimeMaintainDisabled bool // (Optional) Disable the automatic time maintaining feature.
|
||||
}
|
||||
|
||||
// configs is internal used configuration object.
|
||||
@ -57,7 +59,7 @@ var configs struct {
|
||||
|
||||
func init() {
|
||||
configs.config = make(Config)
|
||||
configs.group = DEFAULT_GROUP_NAME
|
||||
configs.group = DefaultGroupName
|
||||
}
|
||||
|
||||
// SetConfig sets the global configuration for package.
|
||||
@ -87,12 +89,12 @@ func AddConfigNode(group string, node ConfigNode) {
|
||||
|
||||
// AddDefaultConfigNode adds one node configuration to configuration of default group.
|
||||
func AddDefaultConfigNode(node ConfigNode) {
|
||||
AddConfigNode(DEFAULT_GROUP_NAME, node)
|
||||
AddConfigNode(DefaultGroupName, node)
|
||||
}
|
||||
|
||||
// AddDefaultConfigGroup adds multiple node configurations to configuration of default group.
|
||||
func AddDefaultConfigGroup(nodes ConfigGroup) {
|
||||
SetConfigGroup(DEFAULT_GROUP_NAME, nodes)
|
||||
SetConfigGroup(DefaultGroupName, nodes)
|
||||
}
|
||||
|
||||
// GetConfig retrieves and returns the configuration of given group.
|
||||
@ -138,18 +140,18 @@ func (c *Core) GetLogger() *glog.Logger {
|
||||
|
||||
// SetMaxIdleConnCount sets the max idle connection count for underlying connection pool.
|
||||
func (c *Core) SetMaxIdleConnCount(n int) {
|
||||
c.maxIdleConnCount = n
|
||||
c.config.MaxIdleConnCount = n
|
||||
}
|
||||
|
||||
// SetMaxOpenConnCount sets the max open connection count for underlying connection pool.
|
||||
func (c *Core) SetMaxOpenConnCount(n int) {
|
||||
c.maxOpenConnCount = n
|
||||
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.maxConnLifetime = d
|
||||
c.config.MaxConnLifetime = d
|
||||
}
|
||||
|
||||
// String returns the node as string.
|
||||
@ -165,6 +167,11 @@ func (node *ConfigNode) String() string {
|
||||
)
|
||||
}
|
||||
|
||||
// GetConfig returns the current used node configuration.
|
||||
func (c *Core) GetConfig() *ConfigNode {
|
||||
return c.config
|
||||
}
|
||||
|
||||
// SetDebug enables/disables the debug mode.
|
||||
func (c *Core) SetDebug(debug bool) {
|
||||
c.debug.Set(debug)
|
||||
@ -180,28 +187,27 @@ func (c *Core) GetCache() *gcache.Cache {
|
||||
return c.cache
|
||||
}
|
||||
|
||||
// GetPrefix returns the table prefix string configured.
|
||||
func (c *Core) GetPrefix() string {
|
||||
return c.prefix
|
||||
}
|
||||
|
||||
// GetGroup returns the group string configured.
|
||||
func (c *Core) GetGroup() string {
|
||||
return c.group
|
||||
}
|
||||
|
||||
// SetDryRun enables/disables the DryRun feature.
|
||||
func (c *Core) SetDryRun(dryrun bool) {
|
||||
c.dryrun.Set(dryrun)
|
||||
// Deprecated, use GetConfig instead.
|
||||
func (c *Core) SetDryRun(enabled bool) {
|
||||
c.config.DryRun = enabled
|
||||
}
|
||||
|
||||
// GetDryRun returns the DryRun value.
|
||||
// Deprecated, use GetConfig instead.
|
||||
func (c *Core) GetDryRun() bool {
|
||||
if allDryRun {
|
||||
// Globally set.
|
||||
return true
|
||||
}
|
||||
return c.dryrun.Val()
|
||||
return c.config.DryRun
|
||||
}
|
||||
|
||||
// GetPrefix returns the table prefix string configured.
|
||||
// Deprecated, use GetConfig instead.
|
||||
func (c *Core) GetPrefix() string {
|
||||
return c.config.Prefix
|
||||
}
|
||||
|
||||
// SetSchema changes the schema for this database connection object.
|
||||
@ -215,8 +221,3 @@ func (c *Core) SetSchema(schema string) {
|
||||
func (c *Core) GetSchema() string {
|
||||
return c.schema.Val()
|
||||
}
|
||||
|
||||
// GetConfig returns the current used node configuration.
|
||||
func (c *Core) GetConfig() *ConfigNode {
|
||||
return c.config
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
// Copyright GoFrame Author(https://github.com/gogf/gf). 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,
|
||||
@ -22,9 +22,9 @@ import (
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
)
|
||||
|
||||
// convertValue automatically checks and converts field value from database type
|
||||
// convertFieldValueToLocalValue automatically checks and converts field value from database type
|
||||
// to golang variable type.
|
||||
func (c *Core) convertValue(fieldValue interface{}, fieldType string) interface{} {
|
||||
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 fieldType == "" {
|
||||
@ -146,7 +146,8 @@ func (c *Core) convertValue(fieldValue interface{}, fieldType string) interface{
|
||||
}
|
||||
}
|
||||
|
||||
// filterFields removes all key-value pairs which are not the field of given table.
|
||||
// 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 {
|
||||
fieldsKeyMap := make(map[string]interface{}, len(fieldsMap))
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
// Copyright GoFrame Author(https://github.com/gogf/gf). 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,
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
// Copyright GoFrame Author(https://github.com/gogf/gf). 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,
|
||||
@ -13,9 +13,8 @@ package gdb
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/os/gcache"
|
||||
"github.com/gogf/gf/errors/gerror"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
@ -193,14 +192,14 @@ func (d *DriverMssql) TableFields(table string, schema ...string) (fields map[st
|
||||
charL, charR := d.GetChars()
|
||||
table = gstr.Trim(table, charL+charR)
|
||||
if gstr.Contains(table, " ") {
|
||||
return nil, errors.New("function TableFields supports only single table operations")
|
||||
return nil, gerror.New("function TableFields supports only single table operations")
|
||||
}
|
||||
checkSchema := d.DB.GetSchema()
|
||||
if len(schema) > 0 && schema[0] != "" {
|
||||
checkSchema = schema[0]
|
||||
}
|
||||
v, _ := gcache.GetOrSetFunc(
|
||||
fmt.Sprintf(`mssql_table_fields_%s_%s`, table, checkSchema),
|
||||
v, _ := internalCache.GetOrSetFunc(
|
||||
fmt.Sprintf(`mssql_table_fields_%s_%s@group:%s`, table, checkSchema, d.GetGroup()),
|
||||
func() (interface{}, error) {
|
||||
var (
|
||||
result Result
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
// Copyright GoFrame Author(https://github.com/gogf/gf). 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,
|
||||
@ -8,10 +8,9 @@ package gdb
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/errors/gerror"
|
||||
"github.com/gogf/gf/internal/intlog"
|
||||
"github.com/gogf/gf/os/gcache"
|
||||
"github.com/gogf/gf/text/gregex"
|
||||
"github.com/gogf/gf/text/gstr"
|
||||
|
||||
@ -32,6 +31,7 @@ func (d *DriverMysql) New(core *Core, node *ConfigNode) (DB, error) {
|
||||
}
|
||||
|
||||
// Open creates and returns a underlying sql.DB object for mysql.
|
||||
// Note that it converts time.Time argument to local timezone in default.
|
||||
func (d *DriverMysql) Open(config *ConfigNode) (*sql.DB, error) {
|
||||
var source string
|
||||
if config.LinkInfo != "" {
|
||||
@ -42,7 +42,7 @@ func (d *DriverMysql) Open(config *ConfigNode) (*sql.DB, error) {
|
||||
}
|
||||
} else {
|
||||
source = fmt.Sprintf(
|
||||
"%s:%s@tcp(%s:%s)/%s?charset=%s&multiStatements=true&parseTime=true&loc=Local",
|
||||
"%s:%s@tcp(%s:%s)/%s?charset=%s&multiStatements=true&parseTime=true",
|
||||
config.User, config.Pass, config.Host, config.Port, config.Name, config.Charset,
|
||||
)
|
||||
}
|
||||
@ -97,14 +97,14 @@ func (d *DriverMysql) TableFields(table string, schema ...string) (fields map[st
|
||||
charL, charR := d.GetChars()
|
||||
table = gstr.Trim(table, charL+charR)
|
||||
if gstr.Contains(table, " ") {
|
||||
return nil, errors.New("function TableFields supports only single table operations")
|
||||
return nil, gerror.New("function TableFields supports only single table operations")
|
||||
}
|
||||
checkSchema := d.schema.Val()
|
||||
if len(schema) > 0 && schema[0] != "" {
|
||||
checkSchema = schema[0]
|
||||
}
|
||||
v, _ := gcache.GetOrSetFunc(
|
||||
fmt.Sprintf(`mysql_table_fields_%s_%s`, table, checkSchema),
|
||||
v, _ := internalCache.GetOrSetFunc(
|
||||
fmt.Sprintf(`mysql_table_fields_%s_%s@group:%s`, table, checkSchema, d.GetGroup()),
|
||||
func() (interface{}, error) {
|
||||
var (
|
||||
result Result
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
// Copyright GoFrame Author(https://github.com/gogf/gf). 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,
|
||||
@ -13,11 +13,11 @@ package gdb
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/errors/gerror"
|
||||
"github.com/gogf/gf/internal/intlog"
|
||||
"github.com/gogf/gf/os/gcache"
|
||||
"github.com/gogf/gf/text/gstr"
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
@ -73,6 +73,15 @@ func (d *DriverOracle) HandleSqlBeforeCommit(link Link, sql string, args []inter
|
||||
return fmt.Sprintf(":v%d", index)
|
||||
})
|
||||
str, _ = gregex.ReplaceString("\"", "", str)
|
||||
// Change time string argument wrapping with TO_DATE function.
|
||||
for i, v := range args {
|
||||
if reflect.TypeOf(v).Kind() == reflect.String {
|
||||
valueStr := gconv.String(v)
|
||||
if gregex.IsMatchString(`^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$`, valueStr) {
|
||||
args[i] = fmt.Sprintf(`TO_DATE('%s','yyyy-MM-dd HH:MI:SS')`, valueStr)
|
||||
}
|
||||
}
|
||||
}
|
||||
return d.parseSql(str), args
|
||||
}
|
||||
|
||||
@ -153,14 +162,14 @@ func (d *DriverOracle) TableFields(table string, schema ...string) (fields map[s
|
||||
charL, charR := d.GetChars()
|
||||
table = gstr.Trim(table, charL+charR)
|
||||
if gstr.Contains(table, " ") {
|
||||
return nil, errors.New("function TableFields supports only single table operations")
|
||||
return nil, gerror.New("function TableFields supports only single table operations")
|
||||
}
|
||||
checkSchema := d.DB.GetSchema()
|
||||
if len(schema) > 0 && schema[0] != "" {
|
||||
checkSchema = schema[0]
|
||||
}
|
||||
v, _ := gcache.GetOrSetFunc(
|
||||
fmt.Sprintf(`oracle_table_fields_%s_%s`, table, checkSchema),
|
||||
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(`
|
||||
@ -196,7 +205,7 @@ FROM USER_TAB_COLUMNS WHERE TABLE_NAME = '%s' ORDER BY COLUMN_ID`,
|
||||
|
||||
func (d *DriverOracle) getTableUniqueIndex(table string) (fields map[string]map[string]string, err error) {
|
||||
table = strings.ToUpper(table)
|
||||
v, _ := gcache.GetOrSetFunc(
|
||||
v, _ := internalCache.GetOrSetFunc(
|
||||
"table_unique_index_"+table,
|
||||
func() (interface{}, error) {
|
||||
res := (Result)(nil)
|
||||
@ -243,13 +252,13 @@ func (d *DriverOracle) DoInsert(link Link, table string, data interface{}, optio
|
||||
case reflect.Struct:
|
||||
dataMap = ConvertDataForTableRecord(data)
|
||||
default:
|
||||
return result, errors.New(fmt.Sprint("unsupported data type:", kind))
|
||||
return result, gerror.New(fmt.Sprint("unsupported data type:", kind))
|
||||
}
|
||||
|
||||
indexs := make([]string, 0)
|
||||
indexMap := make(map[string]string)
|
||||
indexExists := false
|
||||
if option != gINSERT_OPTION_DEFAULT {
|
||||
if option != insertOptionDefault {
|
||||
index, err := d.getTableUniqueIndex(table)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -277,7 +286,7 @@ func (d *DriverOracle) DoInsert(link Link, table string, data interface{}, optio
|
||||
k = strings.ToUpper(k)
|
||||
|
||||
// 操作类型为REPLACE/SAVE时且存在唯一索引才使用merge,否则使用insert
|
||||
if (option == gINSERT_OPTION_REPLACE || option == gINSERT_OPTION_SAVE) && indexExists {
|
||||
if (option == insertOptionReplace || option == insertOptionSave) && indexExists {
|
||||
fields = append(fields, tableAlias1+"."+charL+k+charR)
|
||||
values = append(values, tableAlias2+"."+charL+k+charR)
|
||||
params = append(params, v)
|
||||
@ -303,18 +312,18 @@ func (d *DriverOracle) DoInsert(link Link, table string, data interface{}, optio
|
||||
}
|
||||
}
|
||||
|
||||
if indexExists && option != gINSERT_OPTION_DEFAULT {
|
||||
if indexExists && option != insertOptionDefault {
|
||||
switch option {
|
||||
case gINSERT_OPTION_REPLACE:
|
||||
case insertOptionReplace:
|
||||
fallthrough
|
||||
case gINSERT_OPTION_SAVE:
|
||||
case insertOptionSave:
|
||||
tmp := fmt.Sprintf(
|
||||
"MERGE INTO %s %s USING(SELECT %s FROM DUAL) %s ON(%s) WHEN MATCHED THEN UPDATE SET %s WHEN NOT MATCHED THEN INSERT (%s) VALUES(%s)",
|
||||
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...)
|
||||
case gINSERT_OPTION_IGNORE:
|
||||
case insertOptionIgnore:
|
||||
return d.DB.DoExec(link,
|
||||
fmt.Sprintf(
|
||||
"INSERT /*+ IGNORE_ROW_ON_DUPKEY_INDEX(%s(%s)) */ INTO %s(%s) VALUES(%s)",
|
||||
@ -368,12 +377,12 @@ func (d *DriverOracle) DoBatchInsert(link Link, table string, list interface{},
|
||||
case reflect.Struct:
|
||||
listMap = List{Map(ConvertDataForTableRecord(list))}
|
||||
default:
|
||||
return result, errors.New(fmt.Sprint("unsupported list type:", kind))
|
||||
return result, gerror.New(fmt.Sprint("unsupported list type:", kind))
|
||||
}
|
||||
}
|
||||
// 判断长度
|
||||
if len(listMap) < 1 {
|
||||
return result, errors.New("empty data list")
|
||||
return result, gerror.New("empty data list")
|
||||
}
|
||||
if link == nil {
|
||||
if link, err = d.DB.Master(); err != nil {
|
||||
@ -392,7 +401,7 @@ func (d *DriverOracle) DoBatchInsert(link Link, table string, list interface{},
|
||||
valueHolderStr := strings.Join(holders, ",")
|
||||
|
||||
// 当操作类型非insert时调用单笔的insert功能
|
||||
if option != gINSERT_OPTION_DEFAULT {
|
||||
if option != insertOptionDefault {
|
||||
for _, v := range listMap {
|
||||
r, err := d.DB.DoInsert(link, table, v, option, 1)
|
||||
if err != nil {
|
||||
@ -410,7 +419,7 @@ func (d *DriverOracle) DoBatchInsert(link Link, table string, list interface{},
|
||||
}
|
||||
|
||||
// 构造批量写入数据格式(注意map的遍历是无序的)
|
||||
batchNum := gDEFAULT_BATCH_NUM
|
||||
batchNum := defaultBatchNumber
|
||||
if len(batch) > 0 {
|
||||
batchNum = batch[0]
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
// Copyright GoFrame Author(https://github.com/gogf/gf). 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,
|
||||
@ -13,10 +13,9 @@ package gdb
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/errors/gerror"
|
||||
"github.com/gogf/gf/internal/intlog"
|
||||
"github.com/gogf/gf/os/gcache"
|
||||
"github.com/gogf/gf/text/gstr"
|
||||
"strings"
|
||||
|
||||
@ -101,15 +100,15 @@ func (d *DriverPgsql) TableFields(table string, schema ...string) (fields map[st
|
||||
charL, charR := d.GetChars()
|
||||
table = gstr.Trim(table, charL+charR)
|
||||
if gstr.Contains(table, " ") {
|
||||
return nil, errors.New("function TableFields supports only single table operations")
|
||||
return nil, gerror.New("function TableFields supports only single table operations")
|
||||
}
|
||||
table, _ = gregex.ReplaceString("\"", "", table)
|
||||
checkSchema := d.DB.GetSchema()
|
||||
if len(schema) > 0 && schema[0] != "" {
|
||||
checkSchema = schema[0]
|
||||
}
|
||||
v, _ := gcache.GetOrSetFunc(
|
||||
fmt.Sprintf(`pgsql_table_fields_%s_%s`, table, checkSchema),
|
||||
v, _ := internalCache.GetOrSetFunc(
|
||||
fmt.Sprintf(`pgsql_table_fields_%s_%s@group:%s`, table, checkSchema, d.GetGroup()),
|
||||
func() (interface{}, error) {
|
||||
var (
|
||||
result Result
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
// Copyright GoFrame Author(https://github.com/gogf/gf). 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,
|
||||
@ -12,10 +12,9 @@ package gdb
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/errors/gerror"
|
||||
"github.com/gogf/gf/internal/intlog"
|
||||
"github.com/gogf/gf/os/gcache"
|
||||
"github.com/gogf/gf/os/gfile"
|
||||
"github.com/gogf/gf/text/gstr"
|
||||
"strings"
|
||||
@ -37,15 +36,14 @@ func (d *DriverSqlite) New(core *Core, node *ConfigNode) (DB, error) {
|
||||
// Open creates and returns a underlying sql.DB object for sqlite.
|
||||
func (d *DriverSqlite) Open(config *ConfigNode) (*sql.DB, error) {
|
||||
var source string
|
||||
var err error
|
||||
if config.LinkInfo != "" {
|
||||
source = config.LinkInfo
|
||||
} else {
|
||||
source = config.Name
|
||||
}
|
||||
source, err = gfile.Search(source)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
// It searches the source file to locate its absolute path..
|
||||
if absolutePath, _ := gfile.Search(source); absolutePath != "" {
|
||||
source = absolutePath
|
||||
}
|
||||
intlog.Printf("Open: %s", source)
|
||||
if db, err := sql.Open("sqlite3", source); err == nil {
|
||||
@ -93,14 +91,14 @@ func (d *DriverSqlite) TableFields(table string, schema ...string) (fields map[s
|
||||
charL, charR := d.GetChars()
|
||||
table = gstr.Trim(table, charL+charR)
|
||||
if gstr.Contains(table, " ") {
|
||||
return nil, errors.New("function TableFields supports only single table operations")
|
||||
return nil, gerror.New("function TableFields supports only single table operations")
|
||||
}
|
||||
checkSchema := d.DB.GetSchema()
|
||||
if len(schema) > 0 && schema[0] != "" {
|
||||
checkSchema = schema[0]
|
||||
}
|
||||
v, _ := gcache.GetOrSetFunc(
|
||||
fmt.Sprintf(`sqlite_table_fields_%s_%s`, table, checkSchema),
|
||||
v, _ := internalCache.GetOrSetFunc(
|
||||
fmt.Sprintf(`sqlite_table_fields_%s_%s@group:%s`, table, checkSchema, d.GetGroup()),
|
||||
func() (interface{}, error) {
|
||||
var (
|
||||
result Result
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2017-2018 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
// Copyright GoFrame Author(https://github.com/gogf/gf). 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,
|
||||
@ -8,8 +8,8 @@ package gdb
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/errors/gerror"
|
||||
"github.com/gogf/gf/internal/empty"
|
||||
"github.com/gogf/gf/internal/json"
|
||||
"github.com/gogf/gf/internal/utils"
|
||||
@ -88,9 +88,9 @@ func ListItemValuesUnique(list interface{}, key string, subKey ...interface{}) [
|
||||
func GetInsertOperationByOption(option int) string {
|
||||
var operator string
|
||||
switch option {
|
||||
case gINSERT_OPTION_REPLACE:
|
||||
case insertOptionReplace:
|
||||
operator = "REPLACE"
|
||||
case gINSERT_OPTION_IGNORE:
|
||||
case insertOptionIgnore:
|
||||
operator = "INSERT IGNORE"
|
||||
default:
|
||||
operator = "INSERT"
|
||||
@ -127,6 +127,8 @@ func ConvertDataForTableRecord(value interface{}) map[string]interface{} {
|
||||
switch v.(type) {
|
||||
case time.Time, *time.Time, gtime.Time, *gtime.Time:
|
||||
continue
|
||||
case Counter, *Counter:
|
||||
continue
|
||||
default:
|
||||
// Use string conversion in default.
|
||||
if s, ok := v.(apiString); ok {
|
||||
@ -312,32 +314,40 @@ func doQuoteString(s, charLeft, charRight string) string {
|
||||
|
||||
// GetWhereConditionOfStruct returns the where condition sql and arguments by given struct pointer.
|
||||
// This function automatically retrieves primary or unique field and its attribute value as condition.
|
||||
func GetWhereConditionOfStruct(pointer interface{}) (where string, args []interface{}) {
|
||||
func GetWhereConditionOfStruct(pointer interface{}) (where string, args []interface{}, err error) {
|
||||
tagField, err := structs.TagFields(pointer, []string{ORM_TAG_FOR_STRUCT})
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
array := ([]string)(nil)
|
||||
for _, field := range structs.TagFields(pointer, []string{ORM_TAG_FOR_STRUCT}, true) {
|
||||
array = strings.Split(field.Tag, ",")
|
||||
for _, field := range tagField {
|
||||
array = strings.Split(field.TagValue, ",")
|
||||
if len(array) > 1 && gstr.InArray([]string{ORM_TAG_FOR_UNIQUE, ORM_TAG_FOR_PRIMARY}, array[1]) {
|
||||
return array[0], []interface{}{field.Value()}
|
||||
return array[0], []interface{}{field.Value()}, nil
|
||||
}
|
||||
if len(where) > 0 {
|
||||
where += " "
|
||||
}
|
||||
where += field.Tag + "=?"
|
||||
where += field.TagValue + "=?"
|
||||
args = append(args, field.Value())
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// GetPrimaryKey retrieves and returns primary key field name from given struct.
|
||||
func GetPrimaryKey(pointer interface{}) string {
|
||||
func GetPrimaryKey(pointer interface{}) (string, error) {
|
||||
tagField, err := structs.TagFields(pointer, []string{ORM_TAG_FOR_STRUCT})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
array := ([]string)(nil)
|
||||
for _, field := range structs.TagFields(pointer, []string{ORM_TAG_FOR_STRUCT}, true) {
|
||||
array = strings.Split(field.Tag, ",")
|
||||
for _, field := range tagField {
|
||||
array = strings.Split(field.TagValue, ",")
|
||||
if len(array) > 1 && array[1] == ORM_TAG_FOR_PRIMARY {
|
||||
return array[0]
|
||||
return array[0], nil
|
||||
}
|
||||
}
|
||||
return ""
|
||||
return "", nil
|
||||
}
|
||||
|
||||
// GetPrimaryKeyCondition returns a new where condition by primary field name.
|
||||
@ -394,7 +404,6 @@ func formatSql(sql string, args []interface{}) (newSql string, newArgs []interfa
|
||||
}
|
||||
|
||||
// formatWhere formats where statement and its arguments.
|
||||
// TODO []interface{} type support for parameter <where> does not completed yet.
|
||||
func formatWhere(db DB, where interface{}, args []interface{}, omitEmpty bool) (newWhere string, newArgs []interface{}) {
|
||||
var (
|
||||
buffer = bytes.NewBuffer(nil)
|
||||
@ -476,20 +485,26 @@ func formatWhere(db DB, where interface{}, args []interface{}, omitEmpty bool) (
|
||||
}
|
||||
|
||||
// formatWhereInterfaces formats <where> as []interface{}.
|
||||
// TODO supporting for parameter <where> with []interface{} type is not completed yet.
|
||||
func formatWhereInterfaces(db DB, where []interface{}, buffer *bytes.Buffer, newArgs []interface{}) []interface{} {
|
||||
if len(where) == 0 {
|
||||
return newArgs
|
||||
}
|
||||
if len(where)%2 != 0 {
|
||||
buffer.WriteString(gstr.Join(gconv.Strings(where), ""))
|
||||
return newArgs
|
||||
}
|
||||
var str string
|
||||
var array []interface{}
|
||||
var holderCount int
|
||||
for i := 0; i < len(where); {
|
||||
if holderCount > 0 {
|
||||
array = gconv.Interfaces(where[i])
|
||||
newArgs = append(newArgs, array...)
|
||||
holderCount -= len(array)
|
||||
for i := 0; i < len(where); i += 2 {
|
||||
str = gconv.String(where[i])
|
||||
if buffer.Len() > 0 {
|
||||
buffer.WriteString(" AND " + db.QuoteWord(str) + "=?")
|
||||
} else {
|
||||
str = gconv.String(where[i])
|
||||
holderCount = gstr.Count(str, "?")
|
||||
buffer.WriteString(str)
|
||||
buffer.WriteString(db.QuoteWord(str) + "=?")
|
||||
}
|
||||
if s, ok := where[i+1].(Raw); ok {
|
||||
buffer.WriteString(gconv.String(s))
|
||||
} else {
|
||||
newArgs = append(newArgs, where[i+1])
|
||||
}
|
||||
}
|
||||
return newArgs
|
||||
@ -558,14 +573,18 @@ func formatWhereKeyValue(db DB, buffer *bytes.Buffer, newArgs []interface{}, key
|
||||
} else {
|
||||
buffer.WriteString(quotedKey)
|
||||
}
|
||||
newArgs = append(newArgs, value)
|
||||
if s, ok := value.(Raw); ok {
|
||||
buffer.WriteString(gconv.String(s))
|
||||
} else {
|
||||
newArgs = append(newArgs, value)
|
||||
}
|
||||
}
|
||||
}
|
||||
return newArgs
|
||||
}
|
||||
|
||||
// handleArguments is a nice function which handles the query and its arguments before committing to
|
||||
// underlying driver.
|
||||
// handleArguments is an important function, which handles the sql and all its arguments
|
||||
// before committing them to underlying driver.
|
||||
func handleArguments(sql string, args []interface{}) (newSql string, newArgs []interface{}) {
|
||||
newSql = sql
|
||||
// insertHolderCount is used to calculate the inserting position for the '?' holder.
|
||||
@ -574,14 +593,14 @@ func handleArguments(sql string, args []interface{}) (newSql string, newArgs []i
|
||||
if len(args) > 0 {
|
||||
for index, arg := range args {
|
||||
var (
|
||||
rv = reflect.ValueOf(arg)
|
||||
kind = rv.Kind()
|
||||
reflectValue = reflect.ValueOf(arg)
|
||||
reflectKind = reflectValue.Kind()
|
||||
)
|
||||
if kind == reflect.Ptr {
|
||||
rv = rv.Elem()
|
||||
kind = rv.Kind()
|
||||
for reflectKind == reflect.Ptr {
|
||||
reflectValue = reflectValue.Elem()
|
||||
reflectKind = reflectValue.Kind()
|
||||
}
|
||||
switch kind {
|
||||
switch reflectKind {
|
||||
case reflect.Slice, reflect.Array:
|
||||
// It does not split the type of []byte.
|
||||
// Eg: table.Where("name = ?", []byte("john"))
|
||||
@ -590,7 +609,7 @@ func handleArguments(sql string, args []interface{}) (newSql string, newArgs []i
|
||||
continue
|
||||
}
|
||||
|
||||
if rv.Len() == 0 {
|
||||
if reflectValue.Len() == 0 {
|
||||
// Empty slice argument, it converts the sql to a false sql.
|
||||
// Eg:
|
||||
// Query("select * from xxx where id in(?)", g.Slice{}) -> select * from xxx where 0=1
|
||||
@ -604,15 +623,15 @@ func handleArguments(sql string, args []interface{}) (newSql string, newArgs []i
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for i := 0; i < rv.Len(); i++ {
|
||||
newArgs = append(newArgs, rv.Index(i).Interface())
|
||||
for i := 0; i < reflectValue.Len(); i++ {
|
||||
newArgs = append(newArgs, reflectValue.Index(i).Interface())
|
||||
}
|
||||
}
|
||||
|
||||
// If the '?' holder count equals the length of the slice,
|
||||
// it does not implement the arguments splitting logic.
|
||||
// Eg: db.Query("SELECT ?+?", g.Slice{1, 2})
|
||||
if len(args) == 1 && gstr.Count(newSql, "?") == rv.Len() {
|
||||
if len(args) == 1 && gstr.Count(newSql, "?") == reflectValue.Len() {
|
||||
break
|
||||
}
|
||||
// counter is used to finding the inserting position for the '?' holder.
|
||||
@ -627,36 +646,37 @@ func handleArguments(sql string, args []interface{}) (newSql string, newArgs []i
|
||||
counter++
|
||||
if counter == index+insertHolderCount+1 {
|
||||
replaced = true
|
||||
insertHolderCount += rv.Len() - 1
|
||||
return "?" + strings.Repeat(",?", rv.Len()-1)
|
||||
insertHolderCount += reflectValue.Len() - 1
|
||||
return "?" + strings.Repeat(",?", reflectValue.Len()-1)
|
||||
}
|
||||
return s
|
||||
})
|
||||
|
||||
// Special struct handling.
|
||||
case reflect.Struct:
|
||||
// The underlying driver supports time.Time/*time.Time types.
|
||||
if _, ok := arg.(time.Time); ok {
|
||||
newArgs = append(newArgs, arg)
|
||||
continue
|
||||
}
|
||||
if _, ok := arg.(*time.Time); ok {
|
||||
newArgs = append(newArgs, arg)
|
||||
continue
|
||||
}
|
||||
switch v := arg.(type) {
|
||||
// The underlying driver supports time.Time/*time.Time types.
|
||||
case time.Time, *time.Time:
|
||||
newArgs = append(newArgs, arg)
|
||||
continue
|
||||
|
||||
// Special handling for gtime.Time.
|
||||
// Special handling for gtime.Time/*gtime.Time.
|
||||
//
|
||||
// DO NOT use its underlying gtime.Time.Time as its argument,
|
||||
// because the std time.Time will be converted to certain timezone
|
||||
// according to underlying driver. And the underlying driver also
|
||||
// converts the time.Time to string automatically as the following does.
|
||||
case gtime.Time:
|
||||
newArgs = append(newArgs, v.String())
|
||||
continue
|
||||
|
||||
case *gtime.Time:
|
||||
newArgs = append(newArgs, v.String())
|
||||
continue
|
||||
|
||||
default:
|
||||
// It converts the struct to string in default
|
||||
// if it implements the String interface.
|
||||
// if it has implemented the String interface.
|
||||
if v, ok := arg.(apiString); ok {
|
||||
newArgs = append(newArgs, v.String())
|
||||
continue
|
||||
@ -675,7 +695,7 @@ func handleArguments(sql string, args []interface{}) (newSql string, newArgs []i
|
||||
// formatError customizes and returns the SQL error.
|
||||
func formatError(err error, sql string, args ...interface{}) error {
|
||||
if err != nil && err != ErrNoRows {
|
||||
return errors.New(fmt.Sprintf("%s, %s\n", err.Error(), FormatSqlWithArgs(sql, args)))
|
||||
return gerror.New(fmt.Sprintf("%s, %s\n", err.Error(), FormatSqlWithArgs(sql, args)))
|
||||
}
|
||||
return err
|
||||
}
|
||||
@ -707,7 +727,7 @@ func FormatSqlWithArgs(sql string, args []interface{}) string {
|
||||
return `'` + gstr.QuoteMeta(gconv.String(args[index]), `'`) + `'`
|
||||
case reflect.Struct:
|
||||
if t, ok := args[index].(time.Time); ok {
|
||||
return `'` + gtime.NewFromTime(t).String() + `'`
|
||||
return `'` + t.Format(`2006-01-02 15:04:05`) + `'`
|
||||
}
|
||||
return `'` + gstr.QuoteMeta(gconv.String(args[index]), `'`) + `'`
|
||||
}
|
||||
@ -718,13 +738,17 @@ func FormatSqlWithArgs(sql string, args []interface{}) string {
|
||||
return newQuery
|
||||
}
|
||||
|
||||
// mapToStruct maps the <data> to given struct.
|
||||
// convertMapToStruct maps the <data> to given struct.
|
||||
// Note that the given parameter <pointer> should be a pointer to s struct.
|
||||
func mapToStruct(data map[string]interface{}, pointer interface{}) error {
|
||||
func convertMapToStruct(data map[string]interface{}, pointer interface{}) error {
|
||||
tagNameMap, err := structs.TagMapName(pointer, []string{ORM_TAG_FOR_STRUCT})
|
||||
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 structs.TagMapName(pointer, []string{ORM_TAG_FOR_STRUCT}, true) {
|
||||
for tag, attr := range tagNameMap {
|
||||
mapping[strings.Split(tag, ",")[0]] = attr
|
||||
}
|
||||
return gconv.StructDeep(data, pointer, mapping)
|
||||
return gconv.Struct(data, pointer, mapping)
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
// Copyright GoFrame Author(https://github.com/gogf/gf). 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,
|
||||
@ -7,6 +7,7 @@
|
||||
package gdb
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/text/gregex"
|
||||
"time"
|
||||
@ -52,13 +53,14 @@ type whereHolder struct {
|
||||
}
|
||||
|
||||
const (
|
||||
gLINK_TYPE_MASTER = 1
|
||||
gLINK_TYPE_SLAVE = 2
|
||||
gWHERE_HOLDER_WHERE = 1
|
||||
gWHERE_HOLDER_AND = 2
|
||||
gWHERE_HOLDER_OR = 3
|
||||
OPTION_OMITEMPTY = 1 << iota
|
||||
OPTION_ALLOWEMPTY
|
||||
OPTION_OMITEMPTY = 1
|
||||
OPTION_ALLOWEMPTY = 2
|
||||
|
||||
linkTypeMaster = 1
|
||||
linkTypeSlave = 2
|
||||
whereHolderWhere = 1
|
||||
whereHolderAnd = 2
|
||||
whereHolderOr = 3
|
||||
)
|
||||
|
||||
// Table creates and returns a new ORM model from given schema.
|
||||
@ -112,6 +114,16 @@ func (tx *TX) Model(table ...string) *Model {
|
||||
return tx.Table(table...)
|
||||
}
|
||||
|
||||
// Ctx sets the context for current operation.
|
||||
func (m *Model) Ctx(ctx context.Context) *Model {
|
||||
if ctx == nil {
|
||||
return m
|
||||
}
|
||||
model := m.getModel()
|
||||
model.db = model.db.Ctx(ctx)
|
||||
return model
|
||||
}
|
||||
|
||||
// As sets an alias name for current table.
|
||||
func (m *Model) As(as string) *Model {
|
||||
if m.tables != "" {
|
||||
@ -141,6 +153,7 @@ func (m *Model) DB(db DB) *Model {
|
||||
// TX sets/changes the transaction for current operation.
|
||||
func (m *Model) TX(tx *TX) *Model {
|
||||
model := m.getModel()
|
||||
model.db = tx.db
|
||||
model.tx = tx
|
||||
return model
|
||||
}
|
||||
@ -177,7 +190,7 @@ func (m *Model) Clone() *Model {
|
||||
// Master marks the following operation on master node.
|
||||
func (m *Model) Master() *Model {
|
||||
model := m.getModel()
|
||||
model.linkType = gLINK_TYPE_MASTER
|
||||
model.linkType = linkTypeMaster
|
||||
return model
|
||||
}
|
||||
|
||||
@ -185,7 +198,7 @@ func (m *Model) Master() *Model {
|
||||
// Note that it makes sense only if there's any slave node configured.
|
||||
func (m *Model) Slave() *Model {
|
||||
model := m.getModel()
|
||||
model.linkType = gLINK_TYPE_SLAVE
|
||||
model.linkType = linkTypeSlave
|
||||
return model
|
||||
}
|
||||
|
||||
@ -199,3 +212,10 @@ func (m *Model) Safe(safe ...bool) *Model {
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// Args sets custom arguments for model operation.
|
||||
func (m *Model) Args(args ...interface{}) *Model {
|
||||
model := m.getModel()
|
||||
model.extraArgs = append(model.extraArgs, args)
|
||||
return model
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
// Copyright GoFrame Author(https://github.com/gogf/gf). 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,
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
// Copyright GoFrame Author(https://github.com/gogf/gf). 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,
|
||||
@ -27,7 +27,7 @@ func (m *Model) Where(where interface{}, args ...interface{}) *Model {
|
||||
model.whereHolder = make([]*whereHolder, 0)
|
||||
}
|
||||
model.whereHolder = append(model.whereHolder, &whereHolder{
|
||||
operator: gWHERE_HOLDER_WHERE,
|
||||
operator: whereHolderWhere,
|
||||
where: where,
|
||||
args: args,
|
||||
})
|
||||
@ -65,7 +65,7 @@ func (m *Model) And(where interface{}, args ...interface{}) *Model {
|
||||
model.whereHolder = make([]*whereHolder, 0)
|
||||
}
|
||||
model.whereHolder = append(model.whereHolder, &whereHolder{
|
||||
operator: gWHERE_HOLDER_AND,
|
||||
operator: whereHolderAnd,
|
||||
where: where,
|
||||
args: args,
|
||||
})
|
||||
@ -79,7 +79,7 @@ func (m *Model) Or(where interface{}, args ...interface{}) *Model {
|
||||
model.whereHolder = make([]*whereHolder, 0)
|
||||
}
|
||||
model.whereHolder = append(model.whereHolder, &whereHolder{
|
||||
operator: gWHERE_HOLDER_OR,
|
||||
operator: whereHolderOr,
|
||||
where: where,
|
||||
args: args,
|
||||
})
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
// Copyright GoFrame Author(https://github.com/gogf/gf). 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,
|
||||
@ -9,7 +9,9 @@ package gdb
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/errors/gerror"
|
||||
"github.com/gogf/gf/os/gtime"
|
||||
"github.com/gogf/gf/text/gstr"
|
||||
)
|
||||
|
||||
// Delete does "DELETE FROM ... " statement for the model.
|
||||
@ -26,7 +28,7 @@ func (m *Model) Delete(where ...interface{}) (result sql.Result, err error) {
|
||||
}()
|
||||
var (
|
||||
fieldNameDelete = m.getSoftFieldNameDeleted()
|
||||
conditionWhere, conditionExtra, conditionArgs = m.formatCondition(false)
|
||||
conditionWhere, conditionExtra, conditionArgs = m.formatCondition(false, false)
|
||||
)
|
||||
// Soft deleting.
|
||||
if !m.unscoped && fieldNameDelete != "" {
|
||||
@ -38,5 +40,9 @@ func (m *Model) Delete(where ...interface{}) (result sql.Result, err error) {
|
||||
append([]interface{}{gtime.Now().String()}, conditionArgs...),
|
||||
)
|
||||
}
|
||||
return m.db.DoDelete(m.getLink(true), m.tables, conditionWhere+conditionExtra, conditionArgs...)
|
||||
conditionStr := conditionWhere + conditionExtra
|
||||
if !gstr.ContainsI(conditionStr, " WHERE ") {
|
||||
return nil, gerror.New("there should be WHERE condition statement for DELETE operation")
|
||||
}
|
||||
return m.db.DoDelete(m.getLink(true), m.tables, conditionStr, conditionArgs...)
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
// Copyright GoFrame Author(https://github.com/gogf/gf). 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,
|
||||
@ -10,6 +10,8 @@ import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/container/gset"
|
||||
"github.com/gogf/gf/text/gstr"
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
"github.com/gogf/gf/util/gutil"
|
||||
)
|
||||
|
||||
// Filter marks filtering the fields which does not exist in the fields of the operated table.
|
||||
@ -24,10 +26,29 @@ func (m *Model) Filter() *Model {
|
||||
}
|
||||
|
||||
// Fields sets the operation fields of the model, multiple fields joined using char ','.
|
||||
func (m *Model) Fields(fields ...string) *Model {
|
||||
if len(fields) > 0 {
|
||||
// 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 {
|
||||
return m
|
||||
}
|
||||
switch {
|
||||
// String slice.
|
||||
case length >= 2:
|
||||
model := m.getModel()
|
||||
model.fields = gstr.Join(fields, ",")
|
||||
model.fields = gstr.Join(m.mappingAndFilterToTableFields(gconv.Strings(fieldNamesOrMapStruct)), ",")
|
||||
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}), ",")
|
||||
case []string:
|
||||
model.fields = gstr.Join(m.mappingAndFilterToTableFields(r), ",")
|
||||
default:
|
||||
model.fields = gstr.Join(m.mappingAndFilterToTableFields(gutil.Keys(r)), ",")
|
||||
}
|
||||
return model
|
||||
}
|
||||
return m
|
||||
@ -35,10 +56,26 @@ func (m *Model) Fields(fields ...string) *Model {
|
||||
|
||||
// FieldsEx sets the excluded operation fields of the model, multiple fields joined using char ','.
|
||||
// Note that this function supports only single table operations.
|
||||
func (m *Model) FieldsEx(fields ...string) *Model {
|
||||
if len(fields) > 0 {
|
||||
model := m.getModel()
|
||||
model.fieldsEx = gstr.Join(fields, ",")
|
||||
// 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 {
|
||||
return m
|
||||
}
|
||||
model := m.getModel()
|
||||
switch {
|
||||
case length >= 2:
|
||||
model.fieldsEx = gstr.Join(m.mappingAndFilterToTableFields(gconv.Strings(fieldNamesOrMapStruct)), ",")
|
||||
return model
|
||||
case length == 1:
|
||||
switch r := fieldNamesOrMapStruct[0].(type) {
|
||||
case string:
|
||||
model.fieldsEx = gstr.Join(m.mappingAndFilterToTableFields([]string{r}), ",")
|
||||
case []string:
|
||||
model.fieldsEx = gstr.Join(m.mappingAndFilterToTableFields(r), ",")
|
||||
default:
|
||||
model.fieldsEx = gstr.Join(m.mappingAndFilterToTableFields(gutil.Keys(r)), ",")
|
||||
}
|
||||
return model
|
||||
}
|
||||
return m
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
// Copyright GoFrame Author(https://github.com/gogf/gf). 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,
|
||||
@ -8,7 +8,7 @@ package gdb
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
"github.com/gogf/gf/errors/gerror"
|
||||
"github.com/gogf/gf/os/gtime"
|
||||
"github.com/gogf/gf/text/gstr"
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
@ -101,7 +101,7 @@ func (m *Model) Insert(data ...interface{}) (result sql.Result, err error) {
|
||||
if len(data) > 0 {
|
||||
return m.Data(data...).Insert()
|
||||
}
|
||||
return m.doInsertWithOption(gINSERT_OPTION_DEFAULT, data...)
|
||||
return m.doInsertWithOption(insertOptionDefault, data...)
|
||||
}
|
||||
|
||||
// InsertIgnore does "INSERT IGNORE INTO ..." statement for the model.
|
||||
@ -109,9 +109,9 @@ func (m *Model) Insert(data ...interface{}) (result sql.Result, err error) {
|
||||
// see Model.Data.
|
||||
func (m *Model) InsertIgnore(data ...interface{}) (result sql.Result, err error) {
|
||||
if len(data) > 0 {
|
||||
return m.Data(data...).Insert()
|
||||
return m.Data(data...).InsertIgnore()
|
||||
}
|
||||
return m.doInsertWithOption(gINSERT_OPTION_IGNORE, data...)
|
||||
return m.doInsertWithOption(insertOptionIgnore, data...)
|
||||
}
|
||||
|
||||
// Replace does "REPLACE INTO ..." statement for the model.
|
||||
@ -121,7 +121,7 @@ func (m *Model) Replace(data ...interface{}) (result sql.Result, err error) {
|
||||
if len(data) > 0 {
|
||||
return m.Data(data...).Replace()
|
||||
}
|
||||
return m.doInsertWithOption(gINSERT_OPTION_REPLACE, data...)
|
||||
return m.doInsertWithOption(insertOptionReplace, data...)
|
||||
}
|
||||
|
||||
// Save does "INSERT INTO ... ON DUPLICATE KEY UPDATE..." statement for the model.
|
||||
@ -134,7 +134,7 @@ func (m *Model) Save(data ...interface{}) (result sql.Result, err error) {
|
||||
if len(data) > 0 {
|
||||
return m.Data(data...).Save()
|
||||
}
|
||||
return m.doInsertWithOption(gINSERT_OPTION_SAVE, data...)
|
||||
return m.doInsertWithOption(insertOptionSave, data...)
|
||||
}
|
||||
|
||||
// doInsertWithOption inserts data with option parameter.
|
||||
@ -145,7 +145,7 @@ func (m *Model) doInsertWithOption(option int, data ...interface{}) (result sql.
|
||||
}
|
||||
}()
|
||||
if m.data == nil {
|
||||
return nil, errors.New("inserting into table with empty data")
|
||||
return nil, gerror.New("inserting into table with empty data")
|
||||
}
|
||||
var (
|
||||
nowString = gtime.Now().String()
|
||||
@ -155,7 +155,7 @@ func (m *Model) doInsertWithOption(option int, data ...interface{}) (result sql.
|
||||
)
|
||||
// Batch operation.
|
||||
if list, ok := m.data.(List); ok {
|
||||
batch := gDEFAULT_BATCH_NUM
|
||||
batch := defaultBatchNumber
|
||||
if m.batch > 0 {
|
||||
batch = m.batch
|
||||
}
|
||||
@ -207,5 +207,5 @@ func (m *Model) doInsertWithOption(option int, data ...interface{}) (result sql.
|
||||
option,
|
||||
)
|
||||
}
|
||||
return nil, errors.New("inserting into table with invalid data type")
|
||||
return nil, gerror.New("inserting into table with invalid data type")
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
// Copyright GoFrame Author(https://github.com/gogf/gf). 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,
|
||||
@ -13,7 +13,7 @@ import (
|
||||
|
||||
// isSubQuery checks and returns whether given string a sub-query sql string.
|
||||
func isSubQuery(s string) bool {
|
||||
s = gstr.TrimLeft(s)
|
||||
s = gstr.TrimLeft(s, "()")
|
||||
if p := gstr.Pos(s, " "); p != -1 {
|
||||
if gstr.Equal(s[:p], "select") {
|
||||
return true
|
||||
@ -27,35 +27,9 @@ func isSubQuery(s string) bool {
|
||||
// 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")
|
||||
// Table("user", "u").LeftJoin("SELECT xxx FROM xxx AS a", "a.uid=u.uid")
|
||||
func (m *Model) LeftJoin(table ...string) *Model {
|
||||
var (
|
||||
model = m.getModel()
|
||||
joinStr = ""
|
||||
)
|
||||
if len(table) > 0 {
|
||||
if isSubQuery(table[0]) {
|
||||
joinStr = "(" + table[0] + ")"
|
||||
} else {
|
||||
joinStr = m.db.QuotePrefixTableName(table[0])
|
||||
}
|
||||
}
|
||||
if len(table) > 2 {
|
||||
model.tables += fmt.Sprintf(
|
||||
" LEFT JOIN %s AS %s ON (%s)",
|
||||
joinStr, m.db.QuoteWord(table[1]), table[2],
|
||||
)
|
||||
} else if len(table) == 2 {
|
||||
model.tables += fmt.Sprintf(
|
||||
" LEFT JOIN %s ON (%s)",
|
||||
joinStr, table[1],
|
||||
)
|
||||
} else if len(table) == 1 {
|
||||
model.tables += fmt.Sprintf(
|
||||
" LEFT JOIN %s",
|
||||
joinStr,
|
||||
)
|
||||
}
|
||||
return model
|
||||
return m.doJoin("LEFT", table...)
|
||||
}
|
||||
|
||||
// RightJoin does "RIGHT JOIN ... ON ..." statement on the model.
|
||||
@ -63,35 +37,9 @@ func (m *Model) LeftJoin(table ...string) *Model {
|
||||
// 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")
|
||||
// Table("user", "u").RightJoin("SELECT xxx FROM xxx AS a", "a.uid=u.uid")
|
||||
func (m *Model) RightJoin(table ...string) *Model {
|
||||
var (
|
||||
model = m.getModel()
|
||||
joinStr = ""
|
||||
)
|
||||
if len(table) > 0 {
|
||||
if isSubQuery(table[0]) {
|
||||
joinStr = "(" + table[0] + ")"
|
||||
} else {
|
||||
joinStr = m.db.QuotePrefixTableName(table[0])
|
||||
}
|
||||
}
|
||||
if len(table) > 2 {
|
||||
model.tables += fmt.Sprintf(
|
||||
" RIGHT JOIN %s AS %s ON (%s)",
|
||||
joinStr, m.db.QuoteWord(table[1]), table[2],
|
||||
)
|
||||
} else if len(table) == 2 {
|
||||
model.tables += fmt.Sprintf(
|
||||
" RIGHT JOIN %s ON (%s)",
|
||||
joinStr, table[1],
|
||||
)
|
||||
} else if len(table) == 1 {
|
||||
model.tables += fmt.Sprintf(
|
||||
" RIGHT JOIN %s",
|
||||
joinStr,
|
||||
)
|
||||
}
|
||||
return model
|
||||
return m.doJoin("RIGHT", table...)
|
||||
}
|
||||
|
||||
// InnerJoin does "INNER JOIN ... ON ..." statement on the model.
|
||||
@ -99,32 +47,47 @@ func (m *Model) RightJoin(table ...string) *Model {
|
||||
// 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")
|
||||
// Table("user", "u").InnerJoin("SELECT xxx FROM xxx AS a", "a.uid=u.uid")
|
||||
func (m *Model) InnerJoin(table ...string) *Model {
|
||||
return m.doJoin("INNER", table...)
|
||||
}
|
||||
|
||||
// doJoin does "LEFT/RIGHT/INNER JOIN ... ON ..." statement on the model.
|
||||
// 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")
|
||||
// Table("user", "u").InnerJoin("SELECT xxx FROM xxx AS a", "a.uid=u.uid")
|
||||
// Related issues:
|
||||
// https://github.com/gogf/gf/issues/1024
|
||||
func (m *Model) doJoin(operator string, table ...string) *Model {
|
||||
var (
|
||||
model = m.getModel()
|
||||
joinStr = ""
|
||||
)
|
||||
if len(table) > 0 {
|
||||
if isSubQuery(table[0]) {
|
||||
joinStr = "(" + table[0] + ")"
|
||||
joinStr = gstr.Trim(table[0])
|
||||
if joinStr[0] != '(' {
|
||||
joinStr = "(" + joinStr + ")"
|
||||
}
|
||||
} else {
|
||||
joinStr = m.db.QuotePrefixTableName(table[0])
|
||||
}
|
||||
}
|
||||
if len(table) > 2 {
|
||||
model.tables += fmt.Sprintf(
|
||||
" INNER JOIN %s AS %s ON (%s)",
|
||||
joinStr, m.db.QuoteWord(table[1]), table[2],
|
||||
" %s JOIN %s AS %s ON (%s)",
|
||||
operator, joinStr, m.db.QuoteWord(table[1]), table[2],
|
||||
)
|
||||
} else if len(table) == 2 {
|
||||
model.tables += fmt.Sprintf(
|
||||
" INNER JOIN %s ON (%s)",
|
||||
joinStr, table[1],
|
||||
" %s JOIN %s ON (%s)",
|
||||
operator, joinStr, table[1],
|
||||
)
|
||||
} else if len(table) == 1 {
|
||||
model.tables += fmt.Sprintf(
|
||||
" INNER JOIN %s",
|
||||
joinStr,
|
||||
" %s JOIN %s", operator, joinStr,
|
||||
)
|
||||
}
|
||||
return model
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
// Copyright GoFrame Author(https://github.com/gogf/gf). 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,
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
// Copyright GoFrame Author(https://github.com/gogf/gf). 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,
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
// Copyright GoFrame Author(https://github.com/gogf/gf). 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,
|
||||
@ -46,7 +46,7 @@ func (m *Model) doGetAll(limit1 bool, where ...interface{}) (Result, error) {
|
||||
}
|
||||
var (
|
||||
softDeletingCondition = m.getConditionForSoftDeleting()
|
||||
conditionWhere, conditionExtra, conditionArgs = m.formatCondition(limit1)
|
||||
conditionWhere, conditionExtra, conditionArgs = m.formatCondition(limit1, false)
|
||||
)
|
||||
if !m.unscoped && softDeletingCondition != "" {
|
||||
if conditionWhere == "" {
|
||||
@ -75,6 +75,9 @@ func (m *Model) doGetAll(limit1 bool, where ...interface{}) (Result, error) {
|
||||
func (m *Model) getFieldsFiltered() string {
|
||||
if m.fieldsEx == "" {
|
||||
// No filtering.
|
||||
if !gstr.Contains(m.fields, ".") && !gstr.Contains(m.fields, " ") {
|
||||
return m.db.QuoteString(m.fields)
|
||||
}
|
||||
return m.fields
|
||||
}
|
||||
var (
|
||||
@ -112,7 +115,7 @@ func (m *Model) getFieldsFiltered() string {
|
||||
if len(newFields) > 0 {
|
||||
newFields += ","
|
||||
}
|
||||
newFields += k
|
||||
newFields += m.db.QuoteWord(k)
|
||||
}
|
||||
return newFields
|
||||
}
|
||||
@ -120,7 +123,7 @@ func (m *Model) getFieldsFiltered() string {
|
||||
// Chunk iterates the query result with given size and callback function.
|
||||
func (m *Model) Chunk(limit int, callback func(result Result, err error) bool) {
|
||||
page := m.start
|
||||
if page == 0 {
|
||||
if page <= 0 {
|
||||
page = 1
|
||||
}
|
||||
model := m
|
||||
@ -341,7 +344,7 @@ func (m *Model) Count(where ...interface{}) (int, error) {
|
||||
}
|
||||
var (
|
||||
softDeletingCondition = m.getConditionForSoftDeleting()
|
||||
conditionWhere, conditionExtra, conditionArgs = m.formatCondition(false)
|
||||
conditionWhere, conditionExtra, conditionArgs = m.formatCondition(false, true)
|
||||
)
|
||||
if !m.unscoped && softDeletingCondition != "" {
|
||||
if conditionWhere == "" {
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2020 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
// Copyright GoFrame Author(https://github.com/gogf/gf). 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,
|
||||
@ -32,6 +32,10 @@ func (m *Model) Unscoped() *Model {
|
||||
// If there's no field name for storing creating time, it returns an empty string.
|
||||
// It checks the key with or without cases or chars '-'/'_'/'.'/' '.
|
||||
func (m *Model) getSoftFieldNameCreated(table ...string) string {
|
||||
// It checks whether this feature disabled.
|
||||
if m.db.GetConfig().TimeMaintainDisabled {
|
||||
return ""
|
||||
}
|
||||
tableName := ""
|
||||
if len(table) > 0 {
|
||||
tableName = table[0]
|
||||
@ -40,7 +44,7 @@ func (m *Model) getSoftFieldNameCreated(table ...string) string {
|
||||
}
|
||||
config := m.db.GetConfig()
|
||||
if config.CreatedAt != "" {
|
||||
return m.getSoftFieldName(tableName, append([]string{config.CreatedAt}, createdFiledNames...))
|
||||
return m.getSoftFieldName(tableName, []string{config.CreatedAt})
|
||||
}
|
||||
return m.getSoftFieldName(tableName, createdFiledNames)
|
||||
}
|
||||
@ -49,6 +53,10 @@ func (m *Model) getSoftFieldNameCreated(table ...string) string {
|
||||
// If there's no field name for storing updating time, it returns an empty string.
|
||||
// It checks the key with or without cases or chars '-'/'_'/'.'/' '.
|
||||
func (m *Model) getSoftFieldNameUpdated(table ...string) (field string) {
|
||||
// It checks whether this feature disabled.
|
||||
if m.db.GetConfig().TimeMaintainDisabled {
|
||||
return ""
|
||||
}
|
||||
tableName := ""
|
||||
if len(table) > 0 {
|
||||
tableName = table[0]
|
||||
@ -57,7 +65,7 @@ func (m *Model) getSoftFieldNameUpdated(table ...string) (field string) {
|
||||
}
|
||||
config := m.db.GetConfig()
|
||||
if config.UpdatedAt != "" {
|
||||
return m.getSoftFieldName(tableName, append([]string{config.UpdatedAt}, updatedFiledNames...))
|
||||
return m.getSoftFieldName(tableName, []string{config.UpdatedAt})
|
||||
}
|
||||
return m.getSoftFieldName(tableName, updatedFiledNames)
|
||||
}
|
||||
@ -66,6 +74,10 @@ func (m *Model) getSoftFieldNameUpdated(table ...string) (field string) {
|
||||
// If there's no field name for storing deleting time, it returns an empty string.
|
||||
// It checks the key with or without cases or chars '-'/'_'/'.'/' '.
|
||||
func (m *Model) getSoftFieldNameDeleted(table ...string) (field string) {
|
||||
// It checks whether this feature disabled.
|
||||
if m.db.GetConfig().TimeMaintainDisabled {
|
||||
return ""
|
||||
}
|
||||
tableName := ""
|
||||
if len(table) > 0 {
|
||||
tableName = table[0]
|
||||
@ -74,7 +86,7 @@ func (m *Model) getSoftFieldNameDeleted(table ...string) (field string) {
|
||||
}
|
||||
config := m.db.GetConfig()
|
||||
if config.UpdatedAt != "" {
|
||||
return m.getSoftFieldName(tableName, append([]string{config.DeletedAt}, deletedFiledNames...))
|
||||
return m.getSoftFieldName(tableName, []string{config.DeletedAt})
|
||||
}
|
||||
return m.getSoftFieldName(tableName, deletedFiledNames)
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
// Copyright GoFrame Author(https://github.com/gogf/gf). 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,
|
||||
@ -8,8 +8,8 @@ package gdb
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/errors/gerror"
|
||||
"github.com/gogf/gf/os/gtime"
|
||||
"github.com/gogf/gf/text/gstr"
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
@ -38,14 +38,14 @@ func (m *Model) Update(dataAndWhere ...interface{}) (result sql.Result, err erro
|
||||
}
|
||||
}()
|
||||
if m.data == nil {
|
||||
return nil, errors.New("updating table with empty data")
|
||||
return nil, gerror.New("updating table with empty data")
|
||||
}
|
||||
var (
|
||||
updateData = m.data
|
||||
fieldNameCreate = m.getSoftFieldNameCreated()
|
||||
fieldNameUpdate = m.getSoftFieldNameUpdated()
|
||||
fieldNameDelete = m.getSoftFieldNameDeleted()
|
||||
conditionWhere, conditionExtra, conditionArgs = m.formatCondition(false)
|
||||
conditionWhere, conditionExtra, conditionArgs = m.formatCondition(false, false)
|
||||
)
|
||||
// Automatically update the record updating time.
|
||||
if !m.unscoped && fieldNameUpdate != "" {
|
||||
@ -77,11 +77,15 @@ func (m *Model) Update(dataAndWhere ...interface{}) (result sql.Result, err erro
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
conditionStr := conditionWhere + conditionExtra
|
||||
if !gstr.ContainsI(conditionStr, " WHERE ") {
|
||||
return nil, gerror.New("there should be WHERE condition statement for UPDATE operation")
|
||||
}
|
||||
return m.db.DoUpdate(
|
||||
m.getLink(true),
|
||||
m.tables,
|
||||
newData,
|
||||
conditionWhere+conditionExtra,
|
||||
conditionStr,
|
||||
m.mergeArguments(conditionArgs)...,
|
||||
)
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
// Copyright GoFrame Author(https://github.com/gogf/gf). 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,
|
||||
@ -11,8 +11,10 @@ import (
|
||||
"github.com/gogf/gf/container/gset"
|
||||
"github.com/gogf/gf/internal/empty"
|
||||
"github.com/gogf/gf/os/gtime"
|
||||
"github.com/gogf/gf/text/gregex"
|
||||
"github.com/gogf/gf/text/gstr"
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
"github.com/gogf/gf/util/gutil"
|
||||
"time"
|
||||
)
|
||||
|
||||
@ -26,6 +28,37 @@ 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)
|
||||
if err != nil || len(fieldsMap) == 0 {
|
||||
return fields
|
||||
}
|
||||
var (
|
||||
inputFieldsArray = gstr.SplitAndTrim(gstr.Join(fields, ","), ",")
|
||||
outputFieldsArray = make([]string, 0, len(inputFieldsArray))
|
||||
)
|
||||
fieldsKeyMap := make(map[string]interface{}, len(fieldsMap))
|
||||
for k, _ := range fieldsMap {
|
||||
fieldsKeyMap[k] = nil
|
||||
}
|
||||
for _, field := range inputFieldsArray {
|
||||
if _, ok := fieldsKeyMap[field]; !ok {
|
||||
if !gregex.IsMatchString(regularFieldNameRegPattern, field) {
|
||||
outputFieldsArray = append(outputFieldsArray, field)
|
||||
continue
|
||||
} else {
|
||||
if foundKey, _ := gutil.MapPossibleItemByKey(fieldsKeyMap, field); foundKey != "" {
|
||||
outputFieldsArray = append(outputFieldsArray, foundKey)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
outputFieldsArray = append(outputFieldsArray, field)
|
||||
}
|
||||
}
|
||||
return outputFieldsArray
|
||||
}
|
||||
|
||||
// filterDataForInsertOrUpdate does filter feature with data for inserting/updating operations.
|
||||
// Note that, it does not filter list item, which is also type of map, for "omit empty" feature.
|
||||
func (m *Model) filterDataForInsertOrUpdate(data interface{}) (interface{}, error) {
|
||||
@ -121,19 +154,19 @@ func (m *Model) getLink(master bool) Link {
|
||||
linkType := m.linkType
|
||||
if linkType == 0 {
|
||||
if master {
|
||||
linkType = gLINK_TYPE_MASTER
|
||||
linkType = linkTypeMaster
|
||||
} else {
|
||||
linkType = gLINK_TYPE_SLAVE
|
||||
linkType = linkTypeSlave
|
||||
}
|
||||
}
|
||||
switch linkType {
|
||||
case gLINK_TYPE_MASTER:
|
||||
case linkTypeMaster:
|
||||
link, err := m.db.GetMaster(m.schema)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return link
|
||||
case gLINK_TYPE_SLAVE:
|
||||
case linkTypeSlave:
|
||||
link, err := m.db.GetSlave(m.schema)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
@ -164,11 +197,11 @@ func (m *Model) getPrimaryKey() string {
|
||||
// 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.
|
||||
func (m *Model) formatCondition(limit1 bool) (conditionWhere string, conditionExtra string, conditionArgs []interface{}) {
|
||||
func (m *Model) formatCondition(limit1 bool, isCountStatement bool) (conditionWhere string, conditionExtra string, conditionArgs []interface{}) {
|
||||
if len(m.whereHolder) > 0 {
|
||||
for _, v := range m.whereHolder {
|
||||
switch v.operator {
|
||||
case gWHERE_HOLDER_WHERE:
|
||||
case whereHolderWhere:
|
||||
if conditionWhere == "" {
|
||||
newWhere, newArgs := formatWhere(
|
||||
m.db, v.where, v.args, m.option&OPTION_OMITEMPTY > 0,
|
||||
@ -181,7 +214,7 @@ func (m *Model) formatCondition(limit1 bool) (conditionWhere string, conditionEx
|
||||
}
|
||||
fallthrough
|
||||
|
||||
case gWHERE_HOLDER_AND:
|
||||
case whereHolderAnd:
|
||||
newWhere, newArgs := formatWhere(
|
||||
m.db, v.where, v.args, m.option&OPTION_OMITEMPTY > 0,
|
||||
)
|
||||
@ -196,7 +229,7 @@ func (m *Model) formatCondition(limit1 bool) (conditionWhere string, conditionEx
|
||||
conditionArgs = append(conditionArgs, newArgs...)
|
||||
}
|
||||
|
||||
case gWHERE_HOLDER_OR:
|
||||
case whereHolderOr:
|
||||
newWhere, newArgs := formatWhere(
|
||||
m.db, v.where, v.args, m.option&OPTION_OMITEMPTY > 0,
|
||||
)
|
||||
@ -219,9 +252,6 @@ func (m *Model) formatCondition(limit1 bool) (conditionWhere string, conditionEx
|
||||
if m.groupBy != "" {
|
||||
conditionExtra += " GROUP BY " + m.groupBy
|
||||
}
|
||||
if m.orderBy != "" {
|
||||
conditionExtra += " ORDER BY " + m.orderBy
|
||||
}
|
||||
if len(m.having) > 0 {
|
||||
havingStr, havingArgs := formatWhere(
|
||||
m.db, m.having[0], gconv.Interfaces(m.having[1]), m.option&OPTION_OMITEMPTY > 0,
|
||||
@ -231,18 +261,25 @@ func (m *Model) formatCondition(limit1 bool) (conditionWhere string, conditionEx
|
||||
conditionArgs = append(conditionArgs, havingArgs...)
|
||||
}
|
||||
}
|
||||
if m.limit != 0 {
|
||||
if m.start >= 0 {
|
||||
conditionExtra += fmt.Sprintf(" LIMIT %d,%d", m.start, m.limit)
|
||||
} else {
|
||||
conditionExtra += fmt.Sprintf(" LIMIT %d", m.limit)
|
||||
if m.orderBy != "" {
|
||||
conditionExtra += " ORDER BY " + m.orderBy
|
||||
}
|
||||
if !isCountStatement {
|
||||
if m.limit != 0 {
|
||||
if m.start >= 0 {
|
||||
conditionExtra += fmt.Sprintf(" LIMIT %d,%d", m.start, m.limit)
|
||||
} else {
|
||||
conditionExtra += fmt.Sprintf(" LIMIT %d", m.limit)
|
||||
}
|
||||
} else if limit1 {
|
||||
conditionExtra += " LIMIT 1"
|
||||
}
|
||||
|
||||
if m.offset >= 0 {
|
||||
conditionExtra += fmt.Sprintf(" OFFSET %d", m.offset)
|
||||
}
|
||||
} else if limit1 {
|
||||
conditionExtra += " LIMIT 1"
|
||||
}
|
||||
if m.offset >= 0 {
|
||||
conditionExtra += fmt.Sprintf(" OFFSET %d", m.offset)
|
||||
}
|
||||
|
||||
if m.lockInfo != "" {
|
||||
conditionExtra += " " + m.lockInfo
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
// Copyright GoFrame Author(https://github.com/gogf/gf). 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,
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
// Copyright GoFrame Author(https://github.com/gogf/gf). 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,
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
// Copyright GoFrame Author(https://github.com/gogf/gf). 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,
|
||||
@ -59,7 +59,7 @@ func (tx *TX) GetAll(sql string, args ...interface{}) (Result, error) {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
return tx.db.rowsToResult(rows)
|
||||
return tx.db.convertRowsToResult(rows)
|
||||
}
|
||||
|
||||
// GetOne queries and returns one record from database.
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
// Copyright GoFrame Author(https://github.com/gogf/gf). 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,
|
||||
@ -8,9 +8,9 @@ package gdb
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
"github.com/gogf/gf/container/gmap"
|
||||
"github.com/gogf/gf/encoding/gparser"
|
||||
"github.com/gogf/gf/errors/gerror"
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
"reflect"
|
||||
)
|
||||
@ -52,24 +52,24 @@ func (r Record) Struct(pointer interface{}) error {
|
||||
}
|
||||
// Special handling for parameter type: reflect.Value
|
||||
if _, ok := pointer.(reflect.Value); ok {
|
||||
return mapToStruct(r.Map(), pointer)
|
||||
return convertMapToStruct(r.Map(), pointer)
|
||||
}
|
||||
var (
|
||||
reflectValue = reflect.ValueOf(pointer)
|
||||
reflectKind = reflectValue.Kind()
|
||||
)
|
||||
if reflectKind != reflect.Ptr {
|
||||
return errors.New("parameter should be type of *struct/**struct")
|
||||
return gerror.New("parameter should be type of *struct/**struct")
|
||||
}
|
||||
reflectValue = reflectValue.Elem()
|
||||
reflectKind = reflectValue.Kind()
|
||||
if reflectKind == reflect.Invalid {
|
||||
return errors.New("parameter is an invalid pointer, maybe nil")
|
||||
return gerror.New("parameter is an invalid pointer, maybe nil")
|
||||
}
|
||||
if reflectKind != reflect.Ptr && reflectKind != reflect.Struct {
|
||||
return errors.New("parameter should be type of *struct/**struct")
|
||||
return gerror.New("parameter should be type of *struct/**struct")
|
||||
}
|
||||
return mapToStruct(r.Map(), pointer)
|
||||
return convertMapToStruct(r.Map(), pointer)
|
||||
}
|
||||
|
||||
// IsEmpty checks and returns whether <r> is empty.
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
// Copyright GoFrame Author(https://github.com/gogf/gf). 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,
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
// Copyright GoFrame Author(https://github.com/gogf/gf). 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,
|
||||
@ -224,19 +224,16 @@ func (r Result) Structs(pointer interface{}) (err error) {
|
||||
itemKind = itemType.Kind()
|
||||
)
|
||||
for i := 0; i < length; i++ {
|
||||
var elem reflect.Value
|
||||
if itemKind == reflect.Ptr {
|
||||
e := reflect.New(itemType.Elem()).Elem()
|
||||
if err = r[i].Struct(e); err != nil {
|
||||
return fmt.Errorf(`slice element conversion failed: %s`, err.Error())
|
||||
}
|
||||
array.Index(i).Set(e.Addr())
|
||||
elem = reflect.New(itemType.Elem())
|
||||
} else {
|
||||
e := reflect.New(itemType).Elem()
|
||||
if err = r[i].Struct(e); err != nil {
|
||||
return fmt.Errorf(`slice element conversion failed: %s`, err.Error())
|
||||
}
|
||||
array.Index(i).Set(e)
|
||||
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
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
// Copyright GoFrame Author(https://github.com/gogf/gf). 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,
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2020 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
// Copyright GoFrame Author(https://github.com/gogf/gf). 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,
|
||||
@ -8,8 +8,8 @@ package gdb
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/errors/gerror"
|
||||
"github.com/gogf/gf/text/gstr"
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
"github.com/gogf/gf/util/gutil"
|
||||
@ -45,14 +45,14 @@ import (
|
||||
func (r Result) ScanList(listPointer interface{}, attributeName string, relation ...string) (err error) {
|
||||
// Necessary checks for parameters.
|
||||
if attributeName == "" {
|
||||
return errors.New(`attributeName should not be empty`)
|
||||
return gerror.New(`attributeName should not be empty`)
|
||||
}
|
||||
if len(relation) > 0 {
|
||||
if len(relation) < 2 {
|
||||
return errors.New(`relation name and key should are both necessary`)
|
||||
return gerror.New(`relation name and key should are both necessary`)
|
||||
}
|
||||
if relation[0] == "" || relation[1] == "" {
|
||||
return errors.New(`relation name and key should not be empty`)
|
||||
return gerror.New(`relation name and key should not be empty`)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
// Copyright GoFrame Author(https://github.com/gogf/gf). 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,
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
// Copyright GoFrame Author(https://github.com/gogf/gf). 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,
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
// Copyright GoFrame Author(https://github.com/gogf/gf). 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,
|
||||
@ -57,7 +57,7 @@ func init() {
|
||||
nodePrefix.Prefix = PREFIX1
|
||||
gdb.AddConfigNode("test", configNode)
|
||||
gdb.AddConfigNode("prefix", nodePrefix)
|
||||
gdb.AddConfigNode(gdb.DEFAULT_GROUP_NAME, configNode)
|
||||
gdb.AddConfigNode(gdb.DefaultGroupName, configNode)
|
||||
// Default db.
|
||||
if r, err := gdb.New(); err != nil {
|
||||
gtest.Error(err)
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
// Copyright GoFrame Author(https://github.com/gogf/gf). 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,
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
// Copyright GoFrame Author(https://github.com/gogf/gf). 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,
|
||||
|
||||
64
database/gdb/gdb_z_mysql_ctx_test.go
Normal file
64
database/gdb/gdb_z_mysql_ctx_test.go
Normal file
@ -0,0 +1,64 @@
|
||||
// Copyright GoFrame Author(https://github.com/gogf/gf). 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 (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/gogf/gf/database/gdb"
|
||||
"github.com/gogf/gf/test/gtest"
|
||||
)
|
||||
|
||||
func Test_Ctx(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
db, err := gdb.Instance()
|
||||
t.Assert(err, nil)
|
||||
|
||||
err1 := db.PingMaster()
|
||||
err2 := db.PingSlave()
|
||||
t.Assert(err1, nil)
|
||||
t.Assert(err2, nil)
|
||||
|
||||
newDb := db.Ctx(context.Background())
|
||||
t.AssertNE(newDb, nil)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Ctx_Query(t *testing.T) {
|
||||
db.GetLogger().SetCtxKeys("SpanId", "TraceId")
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
db.SetDebug(true)
|
||||
defer db.SetDebug(false)
|
||||
ctx := context.WithValue(context.Background(), "TraceId", "12345678")
|
||||
ctx = context.WithValue(ctx, "SpanId", "0.1")
|
||||
db.Ctx(ctx).Query("select 1")
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
db.SetDebug(true)
|
||||
defer db.SetDebug(false)
|
||||
db.Query("select 2")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Ctx_Model(t *testing.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
db.GetLogger().SetCtxKeys("SpanId", "TraceId")
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
db.SetDebug(true)
|
||||
defer db.SetDebug(false)
|
||||
ctx := context.WithValue(context.Background(), "TraceId", "12345678")
|
||||
ctx = context.WithValue(ctx, "SpanId", "0.1")
|
||||
db.Model(table).Ctx(ctx).All()
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
db.SetDebug(true)
|
||||
defer db.SetDebug(false)
|
||||
db.Model(table).All()
|
||||
})
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
// Copyright GoFrame Author(https://github.com/gogf/gf). 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,
|
||||
@ -9,6 +9,7 @@ package gdb
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/go-sql-driver/mysql"
|
||||
"github.com/gogf/gf/container/gvar"
|
||||
"github.com/gogf/gf/os/gcmd"
|
||||
"github.com/gogf/gf/os/gtime"
|
||||
"github.com/gogf/gf/test/gtest"
|
||||
@ -46,7 +47,7 @@ func init() {
|
||||
MaxOpenConnCount: 10,
|
||||
MaxConnLifetime: 600,
|
||||
}
|
||||
AddConfigNode(DEFAULT_GROUP_NAME, configNode)
|
||||
AddConfigNode(DefaultGroupName, configNode)
|
||||
// Default db.
|
||||
if r, err := New(); err != nil {
|
||||
gtest.Error(err)
|
||||
@ -312,3 +313,26 @@ func Test_isSubQuery(t *testing.T) {
|
||||
t.Assert(isSubQuery("select 1"), true)
|
||||
})
|
||||
}
|
||||
|
||||
func TestResult_Structs1(t *testing.T) {
|
||||
type A struct {
|
||||
Id int `orm:"id"`
|
||||
}
|
||||
type B struct {
|
||||
*A
|
||||
Name string
|
||||
}
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
r := Result{
|
||||
Record{"id": gvar.New(nil), "name": gvar.New("john")},
|
||||
Record{"id": gvar.New(nil), "name": gvar.New("smith")},
|
||||
}
|
||||
array := make([]*B, 2)
|
||||
err := r.Structs(&array)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(array[0].Id, 0)
|
||||
t.Assert(array[1].Id, 0)
|
||||
t.Assert(array[0].Name, "john")
|
||||
t.Assert(array[1].Name, "smith")
|
||||
})
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
// Copyright GoFrame Author(https://github.com/gogf/gf). 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,
|
||||
@ -719,7 +719,7 @@ func Test_DB_Delete(t *testing.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
result, err := db.Delete(table, nil)
|
||||
result, err := db.Delete(table, 1)
|
||||
t.Assert(err, nil)
|
||||
n, _ := result.RowsAffected()
|
||||
t.Assert(n, SIZE)
|
||||
@ -768,7 +768,7 @@ func Test_DB_Time(t *testing.T) {
|
||||
})
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
result, err := db.Delete(table, nil)
|
||||
result, err := db.Delete(table, 1)
|
||||
t.Assert(err, nil)
|
||||
n, _ := result.RowsAffected()
|
||||
t.Assert(n, 2)
|
||||
@ -1405,3 +1405,67 @@ func Test_Empty_Slice_Argument(t *testing.T) {
|
||||
t.Assert(len(result), 0)
|
||||
})
|
||||
}
|
||||
|
||||
// update counter test
|
||||
func Test_DB_UpdateCounter(t *testing.T) {
|
||||
tableName := "gf_update_counter_test_" + gtime.TimestampNanoStr()
|
||||
_, err := db.Exec(fmt.Sprintf(`
|
||||
CREATE TABLE IF NOT EXISTS %s (
|
||||
id int(10) unsigned NOT NULL,
|
||||
views int(8) unsigned DEFAULT '0' NOT NULL ,
|
||||
updated_time int(10) unsigned DEFAULT '0' NOT NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
`, tableName))
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
defer dropTable(tableName)
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
insertData := g.Map{
|
||||
"id": 1,
|
||||
"views": 0,
|
||||
"updated_time": 0,
|
||||
}
|
||||
_, err = db.Insert(tableName, insertData)
|
||||
t.Assert(err, nil)
|
||||
})
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
gdbCounter := &gdb.Counter{
|
||||
Field: "views",
|
||||
Value: 1,
|
||||
}
|
||||
updateData := g.Map{
|
||||
"views": gdbCounter,
|
||||
"updated_time": gtime.Now().Unix(),
|
||||
}
|
||||
result, err := db.Update(tableName, updateData, "id", 1)
|
||||
t.Assert(err, nil)
|
||||
n, _ := result.RowsAffected()
|
||||
t.Assert(n, 1)
|
||||
one, err := db.Table(tableName).Where("id", 1).One()
|
||||
t.Assert(err, nil)
|
||||
t.Assert(one["id"].Int(), 1)
|
||||
t.Assert(one["views"].Int(), 1)
|
||||
})
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
gdbCounter := &gdb.Counter{
|
||||
Field: "views",
|
||||
Value: -1,
|
||||
}
|
||||
updateData := g.Map{
|
||||
"views": gdbCounter,
|
||||
"updated_time": gtime.Now().Unix(),
|
||||
}
|
||||
result, err := db.Update(tableName, updateData, "id", 1)
|
||||
t.Assert(err, nil)
|
||||
n, _ := result.RowsAffected()
|
||||
t.Assert(n, 1)
|
||||
one, err := db.Table(tableName).Where("id", 1).One()
|
||||
t.Assert(err, nil)
|
||||
t.Assert(one["id"].Int(), 1)
|
||||
t.Assert(one["views"].Int(), 0)
|
||||
})
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
// Copyright GoFrame Author(https://github.com/gogf/gf). 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,
|
||||
@ -11,7 +11,9 @@ import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/container/garray"
|
||||
"github.com/gogf/gf/container/gmap"
|
||||
"github.com/gogf/gf/debug/gdebug"
|
||||
"github.com/gogf/gf/encoding/gparser"
|
||||
"github.com/gogf/gf/os/gfile"
|
||||
"github.com/gogf/gf/util/gutil"
|
||||
"testing"
|
||||
"time"
|
||||
@ -387,7 +389,7 @@ func Test_Model_Update(t *testing.T) {
|
||||
defer dropTable(table)
|
||||
// UPDATE...LIMIT
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
result, err := db.Table(table).Data("nickname", "T100").Order("id desc").Limit(2).Update()
|
||||
result, err := db.Table(table).Data("nickname", "T100").Where(1).Order("id desc").Limit(2).Update()
|
||||
t.Assert(err, nil)
|
||||
n, _ := result.RowsAffected()
|
||||
t.Assert(n, 2)
|
||||
@ -763,6 +765,32 @@ func Test_Model_Count(t *testing.T) {
|
||||
t.Assert(err, nil)
|
||||
t.Assert(count, SIZE)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
count, err := db.Table(table).FieldsEx("id").Where("id>8").Count()
|
||||
t.Assert(err, nil)
|
||||
t.Assert(count, 2)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
count, err := db.Table(table).Fields("distinct id,nickname").Where("id>8").Count()
|
||||
t.Assert(err, nil)
|
||||
t.Assert(count, 2)
|
||||
})
|
||||
// COUNT...LIMIT...
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
count, err := db.Table(table).Page(1, 2).Count()
|
||||
t.Assert(err, nil)
|
||||
t.Assert(count, SIZE)
|
||||
})
|
||||
//gtest.C(t, func(t *gtest.T) {
|
||||
// count, err := db.Table(table).Fields("id myid").Where("id>8").Count()
|
||||
// t.Assert(err, nil)
|
||||
// t.Assert(count, 2)
|
||||
//})
|
||||
//gtest.C(t, func(t *gtest.T) {
|
||||
// count, err := db.Table(table).As("u1").LeftJoin(table, "u2", "u2.id=u1.id").Fields("u2.id u2id").Where("u1.id>8").Count()
|
||||
// t.Assert(err, nil)
|
||||
// t.Assert(count, 2)
|
||||
//})
|
||||
}
|
||||
|
||||
func Test_Model_FindCount(t *testing.T) {
|
||||
@ -1182,6 +1210,21 @@ func Test_Model_Where(t *testing.T) {
|
||||
t.AssertGT(len(result), 0)
|
||||
t.Assert(result["id"].Int(), 3)
|
||||
})
|
||||
|
||||
// slice
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
result, err := db.Table(table).Where(g.Slice{"id", 3}).One()
|
||||
t.Assert(err, nil)
|
||||
t.AssertGT(len(result), 0)
|
||||
t.Assert(result["id"].Int(), 3)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
result, err := db.Table(table).Where(g.Slice{"id", 3, "nickname", "name_3"}).One()
|
||||
t.Assert(err, nil)
|
||||
t.AssertGT(len(result), 0)
|
||||
t.Assert(result["id"].Int(), 3)
|
||||
})
|
||||
|
||||
// slice parameter
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
result, err := db.Table(table).Where("id=? and nickname=?", g.Slice{3, "name_3"}).One()
|
||||
@ -1747,14 +1790,14 @@ func Test_Model_Delete(t *testing.T) {
|
||||
|
||||
// DELETE...LIMIT
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
result, err := db.Table(table).Limit(2).Delete()
|
||||
result, err := db.Table(table).Where(1).Limit(2).Delete()
|
||||
t.Assert(err, nil)
|
||||
n, _ := result.RowsAffected()
|
||||
t.Assert(n, 2)
|
||||
})
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
result, err := db.Table(table).Delete()
|
||||
result, err := db.Table(table).Where(1).Delete()
|
||||
t.Assert(err, nil)
|
||||
n, _ := result.RowsAffected()
|
||||
t.Assert(n, SIZE-2)
|
||||
@ -2014,7 +2057,7 @@ func Test_Model_Option_Where(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
r, err := db.Table(table).OmitEmpty().Data("nickname", 1).Where(g.Map{"id": 0, "passport": ""}).Update()
|
||||
r, err := db.Table(table).OmitEmpty().Data("nickname", 1).Where(g.Map{"id": 0, "passport": ""}).And(1).Update()
|
||||
t.Assert(err, nil)
|
||||
n, _ := r.RowsAffected()
|
||||
t.Assert(n, SIZE)
|
||||
@ -2093,6 +2136,19 @@ func Test_Model_FieldsEx(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Model_FieldsEx_WithReservedWords(t *testing.T) {
|
||||
table := "fieldsex_test_table"
|
||||
sqlTpcPath := gdebug.TestDataPath("reservedwords_table_tpl.sql")
|
||||
if _, err := db.Exec(fmt.Sprintf(gfile.GetContents(sqlTpcPath), table)); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
defer dropTable(table)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
_, err := db.Table(table).FieldsEx("content").One()
|
||||
t.Assert(err, nil)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Model_FieldsStr(t *testing.T) {
|
||||
table := createTable()
|
||||
defer dropTable(table)
|
||||
@ -2566,6 +2622,144 @@ func Test_Model_Min_Max(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Model_Fields_AutoMapping(t *testing.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
value, err := db.Table(table).Fields("ID").Where("id", 2).Value()
|
||||
t.Assert(err, nil)
|
||||
t.Assert(value.Int(), 2)
|
||||
})
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
value, err := db.Table(table).Fields("NICK_NAME").Where("id", 2).Value()
|
||||
t.Assert(err, nil)
|
||||
t.Assert(value.String(), "name_2")
|
||||
})
|
||||
// Map
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
one, err := db.Table(table).Fields(g.Map{
|
||||
"ID": 1,
|
||||
"NICK_NAME": 1,
|
||||
}).Where("id", 2).One()
|
||||
t.Assert(err, nil)
|
||||
t.Assert(len(one), 2)
|
||||
t.Assert(one["id"], 2)
|
||||
t.Assert(one["nickname"], "name_2")
|
||||
})
|
||||
// Struct
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type T struct {
|
||||
ID int
|
||||
NICKNAME int
|
||||
}
|
||||
one, err := db.Table(table).Fields(&T{
|
||||
ID: 0,
|
||||
NICKNAME: 0,
|
||||
}).Where("id", 2).One()
|
||||
t.Assert(err, nil)
|
||||
t.Assert(len(one), 2)
|
||||
t.Assert(one["id"], 2)
|
||||
t.Assert(one["nickname"], "name_2")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Model_FieldsEx_AutoMapping(t *testing.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
|
||||
// "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(),
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
value, err := db.Table(table).FieldsEx("Passport, Password, NickName, CreateTime").Where("id", 2).Value()
|
||||
t.Assert(err, nil)
|
||||
t.Assert(value.Int(), 2)
|
||||
})
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
value, err := db.Table(table).FieldsEx("ID, Passport, Password, CreateTime").Where("id", 2).Value()
|
||||
t.Assert(err, nil)
|
||||
t.Assert(value.String(), "name_2")
|
||||
})
|
||||
// Map
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
one, err := db.Table(table).FieldsEx(g.Map{
|
||||
"Passport": 1,
|
||||
"Password": 1,
|
||||
"CreateTime": 1,
|
||||
}).Where("id", 2).One()
|
||||
t.Assert(err, nil)
|
||||
t.Assert(len(one), 2)
|
||||
t.Assert(one["id"], 2)
|
||||
t.Assert(one["nickname"], "name_2")
|
||||
})
|
||||
// Struct
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type T struct {
|
||||
Passport int
|
||||
Password int
|
||||
CreateTime int
|
||||
}
|
||||
one, err := db.Table(table).FieldsEx(&T{
|
||||
Passport: 0,
|
||||
Password: 0,
|
||||
CreateTime: 0,
|
||||
}).Where("id", 2).One()
|
||||
t.Assert(err, nil)
|
||||
t.Assert(len(one), 2)
|
||||
t.Assert(one["id"], 2)
|
||||
t.Assert(one["nickname"], "name_2")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Model_Fields_Struct(t *testing.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
|
||||
type A struct {
|
||||
Passport string
|
||||
Password string
|
||||
}
|
||||
type B struct {
|
||||
A
|
||||
NickName string
|
||||
}
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
one, err := db.Table(table).Fields(A{}).Where("id", 2).One()
|
||||
t.Assert(err, nil)
|
||||
t.Assert(len(one), 2)
|
||||
t.Assert(one["passport"], "user_2")
|
||||
t.Assert(one["password"], "pass_2")
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
one, err := db.Table(table).Fields(&A{}).Where("id", 2).One()
|
||||
t.Assert(err, nil)
|
||||
t.Assert(len(one), 2)
|
||||
t.Assert(one["passport"], "user_2")
|
||||
t.Assert(one["password"], "pass_2")
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
one, err := db.Table(table).Fields(B{}).Where("id", 2).One()
|
||||
t.Assert(err, nil)
|
||||
t.Assert(len(one), 3)
|
||||
t.Assert(one["passport"], "user_2")
|
||||
t.Assert(one["password"], "pass_2")
|
||||
t.Assert(one["nickname"], "name_2")
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
one, err := db.Table(table).Fields(&B{}).Where("id", 2).One()
|
||||
t.Assert(err, nil)
|
||||
t.Assert(len(one), 3)
|
||||
t.Assert(one["passport"], "user_2")
|
||||
t.Assert(one["password"], "pass_2")
|
||||
t.Assert(one["nickname"], "name_2")
|
||||
})
|
||||
}
|
||||
func Test_Model_NullField(t *testing.T) {
|
||||
table := createTable()
|
||||
defer dropTable(table)
|
||||
@ -2642,3 +2836,204 @@ func Test_Model_HasField(t *testing.T) {
|
||||
t.Assert(err, nil)
|
||||
})
|
||||
}
|
||||
|
||||
// Issue: https://github.com/gogf/gf/issues/1002
|
||||
func Test_Model_Issue1002(t *testing.T) {
|
||||
table := createTable()
|
||||
defer dropTable(table)
|
||||
|
||||
result, err := db.Table(table).Data(g.Map{
|
||||
"id": 1,
|
||||
"passport": "port_1",
|
||||
"password": "pass_1",
|
||||
"nickname": "name_2",
|
||||
"create_time": "2020-10-27 19:03:33",
|
||||
}).Insert()
|
||||
gtest.Assert(err, nil)
|
||||
n, _ := result.RowsAffected()
|
||||
gtest.Assert(n, 1)
|
||||
|
||||
// where + string.
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
v, err := db.Table(table).Fields("id").Where("create_time>'2020-10-27 19:03:32' and create_time<'2020-10-27 19:03:34'").Value()
|
||||
t.Assert(err, nil)
|
||||
t.Assert(v.Int(), 1)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
v, err := db.Table(table).Fields("id").Where("create_time>'2020-10-27 19:03:32' and create_time<'2020-10-27 19:03:34'").FindValue()
|
||||
t.Assert(err, nil)
|
||||
t.Assert(v.Int(), 1)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
v, err := db.Table(table).Where("create_time>'2020-10-27 19:03:32' and create_time<'2020-10-27 19:03:34'").FindValue("id")
|
||||
t.Assert(err, nil)
|
||||
t.Assert(v.Int(), 1)
|
||||
})
|
||||
// where + string arguments.
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
v, err := db.Table(table).Fields("id").Where("create_time>? and create_time<?", "2020-10-27 19:03:32", "2020-10-27 19:03:34").Value()
|
||||
t.Assert(err, nil)
|
||||
t.Assert(v.Int(), 1)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
v, err := db.Table(table).Fields("id").Where("create_time>? and create_time<?", "2020-10-27 19:03:32", "2020-10-27 19:03:34").FindValue()
|
||||
t.Assert(err, nil)
|
||||
t.Assert(v.Int(), 1)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
v, err := db.Table(table).Where("create_time>? and create_time<?", "2020-10-27 19:03:32", "2020-10-27 19:03:34").FindValue("id")
|
||||
t.Assert(err, nil)
|
||||
t.Assert(v.Int(), 1)
|
||||
})
|
||||
// where + gtime.Time arguments.
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
v, err := db.Table(table).Fields("id").Where("create_time>? and create_time<?", gtime.New("2020-10-27 19:03:32"), gtime.New("2020-10-27 19:03:34")).Value()
|
||||
t.Assert(err, nil)
|
||||
t.Assert(v.Int(), 1)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
v, err := db.Table(table).Fields("id").Where("create_time>? and create_time<?", gtime.New("2020-10-27 19:03:32"), gtime.New("2020-10-27 19:03:34")).FindValue()
|
||||
t.Assert(err, nil)
|
||||
t.Assert(v.Int(), 1)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
v, err := db.Table(table).Where("create_time>? and create_time<?", gtime.New("2020-10-27 19:03:32"), gtime.New("2020-10-27 19:03:34")).FindValue("id")
|
||||
t.Assert(err, nil)
|
||||
t.Assert(v.Int(), 1)
|
||||
})
|
||||
// where + time.Time arguments, UTC.
|
||||
t1, _ := time.Parse("2006-01-02 15:04:05", "2020-10-27 19:03:32")
|
||||
t2, _ := time.Parse("2006-01-02 15:04:05", "2020-10-27 19:03:34")
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
v, err := db.Table(table).Fields("id").Where("create_time>? and create_time<?", t1, t2).Value()
|
||||
t.Assert(err, nil)
|
||||
t.Assert(v.Int(), 1)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
v, err := db.Table(table).Fields("id").Where("create_time>? and create_time<?", t1, t2).FindValue()
|
||||
t.Assert(err, nil)
|
||||
t.Assert(v.Int(), 1)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
v, err := db.Table(table).Where("create_time>? and create_time<?", t1, t2).FindValue("id")
|
||||
t.Assert(err, nil)
|
||||
t.Assert(v.Int(), 1)
|
||||
})
|
||||
}
|
||||
|
||||
func createTableForTimeZoneTest() string {
|
||||
tableName := "user_" + gtime.Now().TimestampNanoStr()
|
||||
if _, err := db.Exec(fmt.Sprintf(`
|
||||
CREATE TABLE %s (
|
||||
id int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||
passport varchar(45) NULL,
|
||||
password char(32) NULL,
|
||||
nickname varchar(45) NULL,
|
||||
created_at timestamp NULL,
|
||||
updated_at timestamp NULL,
|
||||
deleted_at timestamp NULL,
|
||||
PRIMARY KEY (id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
`, tableName,
|
||||
)); err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
return tableName
|
||||
}
|
||||
|
||||
// https://github.com/gogf/gf/issues/1012
|
||||
func Test_TimeZoneInsert(t *testing.T) {
|
||||
tableName := createTableForTimeZoneTest()
|
||||
defer dropTable(tableName)
|
||||
|
||||
asiaLocal, err := time.LoadLocation("Asia/Shanghai")
|
||||
gtest.Assert(err, nil)
|
||||
|
||||
CreateTime := "2020-11-22 12:23:45"
|
||||
UpdateTime := "2020-11-22 13:23:45"
|
||||
DeleteTime := "2020-11-22 14:23:45"
|
||||
type User struct {
|
||||
Id int `json:"id"`
|
||||
CreatedAt *gtime.Time `json:"created_at"`
|
||||
UpdatedAt gtime.Time `json:"updated_at"`
|
||||
DeletedAt time.Time `json:"deleted_at"`
|
||||
}
|
||||
t1, _ := time.ParseInLocation("2006-01-02 15:04:05", CreateTime, asiaLocal)
|
||||
t2, _ := time.ParseInLocation("2006-01-02 15:04:05", UpdateTime, asiaLocal)
|
||||
t3, _ := time.ParseInLocation("2006-01-02 15:04:05", DeleteTime, asiaLocal)
|
||||
u := &User{
|
||||
Id: 1,
|
||||
CreatedAt: gtime.New(t1.UTC()),
|
||||
UpdatedAt: *gtime.New(t2.UTC()),
|
||||
DeletedAt: t3.UTC(),
|
||||
}
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
_, _ = db.Table(tableName).Unscoped().Insert(u)
|
||||
userEntity := &User{}
|
||||
err := db.Table(tableName).Where("id", 1).Unscoped().Struct(&userEntity)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(userEntity.CreatedAt.String(), "2020-11-22 04:23:45")
|
||||
t.Assert(userEntity.UpdatedAt.String(), "2020-11-22 05:23:45")
|
||||
t.Assert(gtime.NewFromTime(userEntity.DeletedAt).String(), "2020-11-22 06:23:45")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Model_Fields_Map_Struct(t *testing.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
// map
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
result, err := db.Table(table).Fields(g.Map{
|
||||
"ID": 1,
|
||||
"PASSPORT": 1,
|
||||
"NONE_EXIST": 1,
|
||||
}).Where("id", 1).One()
|
||||
t.Assert(err, nil)
|
||||
t.Assert(len(result), 2)
|
||||
t.Assert(result["id"], 1)
|
||||
t.Assert(result["passport"], "user_1")
|
||||
})
|
||||
// struct
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type A struct {
|
||||
ID int
|
||||
PASSPORT string
|
||||
XXX_TYPE int
|
||||
}
|
||||
var a = A{}
|
||||
err := db.Table(table).Fields(a).Where("id", 1).Struct(&a)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(a.ID, 1)
|
||||
t.Assert(a.PASSPORT, "user_1")
|
||||
t.Assert(a.XXX_TYPE, 0)
|
||||
})
|
||||
// *struct
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type A struct {
|
||||
ID int
|
||||
PASSPORT string
|
||||
XXX_TYPE int
|
||||
}
|
||||
var a *A
|
||||
err := db.Table(table).Fields(a).Where("id", 1).Struct(&a)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(a.ID, 1)
|
||||
t.Assert(a.PASSPORT, "user_1")
|
||||
t.Assert(a.XXX_TYPE, 0)
|
||||
})
|
||||
// **struct
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type A struct {
|
||||
ID int
|
||||
PASSPORT string
|
||||
XXX_TYPE int
|
||||
}
|
||||
var a *A
|
||||
err := db.Table(table).Fields(&a).Where("id", 1).Struct(&a)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(a.ID, 1)
|
||||
t.Assert(a.PASSPORT, "user_1")
|
||||
t.Assert(a.XXX_TYPE, 0)
|
||||
})
|
||||
}
|
||||
|
||||
86
database/gdb/gdb_z_mysql_raw_test.go
Normal file
86
database/gdb/gdb_z_mysql_raw_test.go
Normal file
@ -0,0 +1,86 @@
|
||||
// Copyright GoFrame Author(https://github.com/gogf/gf). 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 (
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"testing"
|
||||
|
||||
"github.com/gogf/gf/database/gdb"
|
||||
"github.com/gogf/gf/test/gtest"
|
||||
)
|
||||
|
||||
func Test_Insert_Raw(t *testing.T) {
|
||||
table := createTable()
|
||||
defer dropTable(table)
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
user := db.Table(table)
|
||||
result, err := user.Filter().Data(g.Map{
|
||||
"id": gdb.Raw("id+2"),
|
||||
"passport": "port_1",
|
||||
"password": "pass_1",
|
||||
"nickname": "name_1",
|
||||
"create_time": gdb.Raw("now()"),
|
||||
}).Insert()
|
||||
t.Assert(err, nil)
|
||||
n, _ := result.LastInsertId()
|
||||
t.Assert(n, 2)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_BatchInsert_Raw(t *testing.T) {
|
||||
table := createTable()
|
||||
defer dropTable(table)
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
user := db.Table(table)
|
||||
result, err := user.Filter().Data(
|
||||
g.List{
|
||||
g.Map{
|
||||
"id": gdb.Raw("id+2"),
|
||||
"passport": "port_2",
|
||||
"password": "pass_2",
|
||||
"nickname": "name_2",
|
||||
"create_time": gdb.Raw("now()"),
|
||||
},
|
||||
g.Map{
|
||||
"id": gdb.Raw("id+4"),
|
||||
"passport": "port_4",
|
||||
"password": "pass_4",
|
||||
"nickname": "name_4",
|
||||
"create_time": gdb.Raw("now()"),
|
||||
},
|
||||
},
|
||||
).Insert()
|
||||
t.Assert(err, nil)
|
||||
n, _ := result.LastInsertId()
|
||||
t.Assert(n, 4)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Update_Raw(t *testing.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
user := db.Table(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)
|
||||
n, _ := result.RowsAffected()
|
||||
t.Assert(n, 1)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
user := db.Table(table)
|
||||
n, err := user.Where("id", 101).Count()
|
||||
t.Assert(err, nil)
|
||||
t.Assert(n, 1)
|
||||
})
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
// Copyright GoFrame Author(https://github.com/gogf/gf). 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,
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
// Copyright GoFrame Author(https://github.com/gogf/gf). 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,
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
// Copyright GoFrame Author(https://github.com/gogf/gf). 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,
|
||||
@ -7,9 +7,9 @@
|
||||
package gdb_test
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/database/gdb"
|
||||
"github.com/gogf/gf/errors/gerror"
|
||||
"testing"
|
||||
|
||||
"github.com/gogf/gf/frame/g"
|
||||
@ -676,7 +676,7 @@ func Test_TX_Delete(t *testing.T) {
|
||||
if err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
if _, err := tx.Delete(table, nil); err != nil {
|
||||
if _, err := tx.Delete(table, 1); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
if err := tx.Commit(); err != nil {
|
||||
@ -696,7 +696,7 @@ func Test_TX_Delete(t *testing.T) {
|
||||
if err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
if _, err := tx.Delete(table, nil); err != nil {
|
||||
if _, err := tx.Delete(table, 1); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
if n, err := tx.Table(table).Count(); err != nil {
|
||||
@ -731,7 +731,7 @@ func Test_Transaction(t *testing.T) {
|
||||
}); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
return errors.New("error")
|
||||
return gerror.New("error")
|
||||
})
|
||||
t.AssertNE(err, nil)
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
// Copyright GoFrame Author(https://github.com/gogf/gf). 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,
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
// Copyright GoFrame Author(https://github.com/gogf/gf). 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,
|
||||
|
||||
20
database/gdb/testdata/reservedwords_table_tpl.sql
vendored
Normal file
20
database/gdb/testdata/reservedwords_table_tpl.sql
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
CREATE TABLE %s (
|
||||
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`key` varchar(45) DEFAULT NULL,
|
||||
`category_id` int(10) unsigned NOT NULL,
|
||||
`user_id` int(10) unsigned NOT NULL,
|
||||
`title` varchar(255) NOT NULL,
|
||||
`content` mediumtext NOT NULL,
|
||||
`sort` int(10) unsigned DEFAULT '0',
|
||||
`brief` varchar(255) DEFAULT NULL,
|
||||
`thumb` varchar(255) DEFAULT NULL,
|
||||
`tags` varchar(900) DEFAULT NULL,
|
||||
`referer` varchar(255) DEFAULT NULL,
|
||||
`status` smallint(5) unsigned DEFAULT '0',
|
||||
`view_count` int(10) unsigned DEFAULT '0',
|
||||
`zan_count` int(10) unsigned DEFAULT NULL,
|
||||
`cai_count` int(10) unsigned DEFAULT NULL,
|
||||
`created_at` datetime DEFAULT NULL,
|
||||
`updated_at` datetime DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
@ -19,8 +19,8 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
DEFAULT_GROUP_NAME = "default" // Default configuration group name.
|
||||
DEFAULT_REDIS_PORT = 6379 // Default redis port configuration if not passed.
|
||||
DefaultGroupName = "default" // Default configuration group name.
|
||||
DefaultRedisPort = 6379 // Default redis port configuration if not passed.
|
||||
)
|
||||
|
||||
var (
|
||||
@ -31,7 +31,7 @@ var (
|
||||
// SetConfig sets the global configuration for specified group.
|
||||
// If <name> is not passed, it sets configuration for the default group name.
|
||||
func SetConfig(config Config, name ...string) {
|
||||
group := DEFAULT_GROUP_NAME
|
||||
group := DefaultGroupName
|
||||
if len(name) > 0 {
|
||||
group = name[0]
|
||||
}
|
||||
@ -44,7 +44,7 @@ func SetConfig(config Config, name ...string) {
|
||||
// SetConfigByStr sets the global configuration for specified group with string.
|
||||
// If <name> is not passed, it sets configuration for the default group name.
|
||||
func SetConfigByStr(str string, name ...string) error {
|
||||
group := DEFAULT_GROUP_NAME
|
||||
group := DefaultGroupName
|
||||
if len(name) > 0 {
|
||||
group = name[0]
|
||||
}
|
||||
@ -60,7 +60,7 @@ func SetConfigByStr(str string, name ...string) error {
|
||||
// GetConfig returns the global configuration with specified group name.
|
||||
// If <name> is not passed, it returns configuration of the default group name.
|
||||
func GetConfig(name ...string) (config Config, ok bool) {
|
||||
group := DEFAULT_GROUP_NAME
|
||||
group := DefaultGroupName
|
||||
if len(name) > 0 {
|
||||
group = name[0]
|
||||
}
|
||||
@ -73,7 +73,7 @@ func GetConfig(name ...string) (config Config, ok bool) {
|
||||
// RemoveConfig removes the global configuration with specified group.
|
||||
// If <name> is not passed, it removes configuration of the default group name.
|
||||
func RemoveConfig(name ...string) {
|
||||
group := DEFAULT_GROUP_NAME
|
||||
group := DefaultGroupName
|
||||
if len(name) > 0 {
|
||||
group = name[0]
|
||||
}
|
||||
@ -96,7 +96,7 @@ func ConfigFromStr(str string) (config Config, err error) {
|
||||
Pass: array[4],
|
||||
}
|
||||
if config.Port == 0 {
|
||||
config.Port = DEFAULT_REDIS_PORT
|
||||
config.Port = DefaultRedisPort
|
||||
}
|
||||
if v, ok := parse["maxIdle"]; ok {
|
||||
config.MaxIdle = gconv.Int(v)
|
||||
@ -127,7 +127,7 @@ func ConfigFromStr(str string) (config Config, err error) {
|
||||
Pass: array[4],
|
||||
}
|
||||
if config.Port == 0 {
|
||||
config.Port = DEFAULT_REDIS_PORT
|
||||
config.Port = DefaultRedisPort
|
||||
}
|
||||
} else {
|
||||
err = gerror.Newf(`invalid redis configuration: "%s"`, str)
|
||||
|
||||
@ -17,7 +17,7 @@ var (
|
||||
// The <name> param is unnecessary, if <name> is not passed,
|
||||
// it returns a redis instance with default configuration group.
|
||||
func Instance(name ...string) *Redis {
|
||||
group := DEFAULT_GROUP_NAME
|
||||
group := DefaultGroupName
|
||||
if len(name) > 0 && name[0] != "" {
|
||||
group = name[0]
|
||||
}
|
||||
|
||||
@ -9,32 +9,33 @@ package ghtml
|
||||
|
||||
import (
|
||||
"html"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
strip "github.com/grokify/html-strip-tags-go"
|
||||
)
|
||||
|
||||
// 过滤掉HTML标签,只返回text内容
|
||||
// 参考:http://php.net/manual/zh/function.strip-tags.php
|
||||
// StripTags strips HTML tags from content, and returns only text.
|
||||
// Referer: http://php.net/manual/zh/function.strip-tags.php
|
||||
func StripTags(s string) string {
|
||||
return strip.StripTags(s)
|
||||
}
|
||||
|
||||
// 本函数各方面都和SpecialChars一样,
|
||||
// 除了Entities会转换所有具有 HTML 实体的字符。
|
||||
// 参考:http://php.net/manual/zh/function.htmlentities.php
|
||||
// Entities encodes all HTML chars for content.
|
||||
// Referer: http://php.net/manual/zh/function.htmlentities.php
|
||||
func Entities(s string) string {
|
||||
return html.EscapeString(s)
|
||||
}
|
||||
|
||||
// Entities 的相反操作
|
||||
// 参考:http://php.net/manual/zh/function.html-entity-decode.php
|
||||
// EntitiesDecode decodes all HTML chars for content.
|
||||
// Referer: http://php.net/manual/zh/function.html-entity-decode.php
|
||||
func EntitiesDecode(s string) string {
|
||||
return html.UnescapeString(s)
|
||||
}
|
||||
|
||||
// 将html中的部分特殊标签转换为html转义标签
|
||||
// 参考:http://php.net/manual/zh/function.htmlspecialchars.php
|
||||
// SpecialChars encodes some special chars for content, these special chars are:
|
||||
// "&", "<", ">", `"`, "'".
|
||||
// Referer: http://php.net/manual/zh/function.htmlspecialchars.php
|
||||
func SpecialChars(s string) string {
|
||||
return strings.NewReplacer(
|
||||
"&", "&",
|
||||
@ -45,8 +46,9 @@ func SpecialChars(s string) string {
|
||||
).Replace(s)
|
||||
}
|
||||
|
||||
// 将html部分转义标签还原为html特殊标签
|
||||
// 参考:http://php.net/manual/zh/function.htmlspecialchars-decode.php
|
||||
// SpecialCharsDecode decodes some special chars for content, these special chars are:
|
||||
// "&", "<", ">", `"`, "'".
|
||||
// Referer: http://php.net/manual/zh/function.htmlspecialchars-decode.php
|
||||
func SpecialCharsDecode(s string) string {
|
||||
return strings.NewReplacer(
|
||||
"&", "&",
|
||||
@ -56,3 +58,46 @@ func SpecialCharsDecode(s string) string {
|
||||
"'", "'",
|
||||
).Replace(s)
|
||||
}
|
||||
|
||||
// SpecialCharsMapOrStruct automatically encodes string values/attributes for map/struct.
|
||||
func SpecialCharsMapOrStruct(mapOrStruct interface{}) error {
|
||||
var (
|
||||
reflectValue = reflect.ValueOf(mapOrStruct)
|
||||
reflectKind = reflectValue.Kind()
|
||||
)
|
||||
for reflectValue.IsValid() && (reflectKind == reflect.Ptr || reflectKind == reflect.Interface) {
|
||||
reflectValue = reflectValue.Elem()
|
||||
reflectKind = reflectValue.Kind()
|
||||
}
|
||||
switch reflectKind {
|
||||
case reflect.Map:
|
||||
var (
|
||||
mapKeys = reflectValue.MapKeys()
|
||||
mapValue reflect.Value
|
||||
)
|
||||
for _, key := range mapKeys {
|
||||
mapValue = reflectValue.MapIndex(key)
|
||||
switch mapValue.Kind() {
|
||||
case reflect.String:
|
||||
reflectValue.SetMapIndex(key, reflect.ValueOf(SpecialChars(mapValue.String())))
|
||||
case reflect.Interface:
|
||||
if mapValue.Elem().Kind() == reflect.String {
|
||||
reflectValue.SetMapIndex(key, reflect.ValueOf(SpecialChars(mapValue.Elem().String())))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case reflect.Struct:
|
||||
var (
|
||||
fieldValue reflect.Value
|
||||
)
|
||||
for i := 0; i < reflectValue.NumField(); i++ {
|
||||
fieldValue = reflectValue.Field(i)
|
||||
switch fieldValue.Kind() {
|
||||
case reflect.String:
|
||||
fieldValue.Set(reflect.ValueOf(SpecialChars(fieldValue.String())))
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -3,16 +3,18 @@
|
||||
// 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 ghtml_test
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"testing"
|
||||
|
||||
"github.com/gogf/gf/encoding/ghtml"
|
||||
"github.com/gogf/gf/test/gtest"
|
||||
)
|
||||
|
||||
func TestStripTags(t *testing.T) {
|
||||
func Test_StripTags(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
src := `<p>Test paragraph.</p><!-- Comment --> <a href="#fragment">Other text</a>`
|
||||
dst := `Test paragraph. Other text`
|
||||
@ -20,7 +22,7 @@ func TestStripTags(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestEntities(t *testing.T) {
|
||||
func Test_Entities(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
src := `A 'quote' "is" <b>bold</b>`
|
||||
dst := `A 'quote' "is" <b>bold</b>`
|
||||
@ -29,7 +31,7 @@ func TestEntities(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestSpecialChars(t *testing.T) {
|
||||
func Test_SpecialChars(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
src := `A 'quote' "is" <b>bold</b>`
|
||||
dst := `A 'quote' "is" <b>bold</b>`
|
||||
@ -37,3 +39,43 @@ func TestSpecialChars(t *testing.T) {
|
||||
t.Assert(ghtml.SpecialCharsDecode(dst), src)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_SpecialCharsMapOrStruct_Map(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a := g.Map{
|
||||
"Title": "<h1>T</h1>",
|
||||
"Content": "<div>C</div>",
|
||||
}
|
||||
err := ghtml.SpecialCharsMapOrStruct(a)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(a["Title"], `<h1>T</h1>`)
|
||||
t.Assert(a["Content"], `<div>C</div>`)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a := g.MapStrStr{
|
||||
"Title": "<h1>T</h1>",
|
||||
"Content": "<div>C</div>",
|
||||
}
|
||||
err := ghtml.SpecialCharsMapOrStruct(a)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(a["Title"], `<h1>T</h1>`)
|
||||
t.Assert(a["Content"], `<div>C</div>`)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_SpecialCharsMapOrStruct_Struct(t *testing.T) {
|
||||
type A struct {
|
||||
Title string
|
||||
Content string
|
||||
}
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a := &A{
|
||||
Title: "<h1>T</h1>",
|
||||
Content: "<div>C</div>",
|
||||
}
|
||||
err := ghtml.SpecialCharsMapOrStruct(a)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(a.Title, `<h1>T</h1>`)
|
||||
t.Assert(a.Content, `<div>C</div>`)
|
||||
})
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
// Copyright GoFrame Author(https://github.com/gogf/gf). 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,
|
||||
@ -315,6 +315,7 @@ func (j *Json) GetStruct(pattern string, pointer interface{}, mapping ...map[str
|
||||
}
|
||||
|
||||
// GetStructDeep does GetStruct recursively.
|
||||
// Deprecated, use GetStruct instead.
|
||||
func (j *Json) GetStructDeep(pattern string, pointer interface{}, mapping ...map[string]string) error {
|
||||
return gconv.StructDeep(j.Get(pattern), pointer, mapping...)
|
||||
}
|
||||
@ -325,6 +326,7 @@ func (j *Json) GetStructs(pattern string, pointer interface{}, mapping ...map[st
|
||||
}
|
||||
|
||||
// GetStructsDeep converts any slice to given struct slice recursively.
|
||||
// Deprecated, use GetStructs instead.
|
||||
func (j *Json) GetStructsDeep(pattern string, pointer interface{}, mapping ...map[string]string) error {
|
||||
return gconv.StructsDeep(j.Get(pattern), pointer, mapping...)
|
||||
}
|
||||
@ -368,98 +370,60 @@ func (j *Json) GetMapToMapsDeep(pattern string, pointer interface{}, mapping ...
|
||||
return gconv.MapToMapsDeep(j.Get(pattern), pointer, mapping...)
|
||||
}
|
||||
|
||||
// ToMap converts current Json object to map[string]interface{}.
|
||||
// Map converts current Json object to map[string]interface{}.
|
||||
// It returns nil if fails.
|
||||
func (j *Json) ToMap() map[string]interface{} {
|
||||
func (j *Json) Map() map[string]interface{} {
|
||||
j.mu.RLock()
|
||||
defer j.mu.RUnlock()
|
||||
return gconv.Map(*(j.p))
|
||||
}
|
||||
|
||||
// ToArray converts current Json object to []interface{}.
|
||||
// Array converts current Json object to []interface{}.
|
||||
// It returns nil if fails.
|
||||
func (j *Json) ToArray() []interface{} {
|
||||
func (j *Json) Array() []interface{} {
|
||||
j.mu.RLock()
|
||||
defer j.mu.RUnlock()
|
||||
return gconv.Interfaces(*(j.p))
|
||||
}
|
||||
|
||||
// ToStruct converts current Json object to specified object.
|
||||
// Struct converts current Json object to specified object.
|
||||
// The <pointer> should be a pointer type of *struct.
|
||||
func (j *Json) ToStruct(pointer interface{}, mapping ...map[string]string) error {
|
||||
func (j *Json) Struct(pointer interface{}, mapping ...map[string]string) error {
|
||||
j.mu.RLock()
|
||||
defer j.mu.RUnlock()
|
||||
return gconv.Struct(*(j.p), pointer, mapping...)
|
||||
}
|
||||
|
||||
// ToStructDeep converts current Json object to specified object recursively.
|
||||
// The <pointer> should be a pointer type of *struct.
|
||||
func (j *Json) ToStructDeep(pointer interface{}, mapping ...map[string]string) error {
|
||||
j.mu.RLock()
|
||||
defer j.mu.RUnlock()
|
||||
return gconv.StructDeep(*(j.p), pointer, mapping...)
|
||||
}
|
||||
|
||||
// ToStructs converts current Json object to specified object slice.
|
||||
// Structs converts current Json object to specified object slice.
|
||||
// The <pointer> should be a pointer type of []struct/*struct.
|
||||
func (j *Json) ToStructs(pointer interface{}, mapping ...map[string]string) error {
|
||||
func (j *Json) Structs(pointer interface{}, mapping ...map[string]string) error {
|
||||
j.mu.RLock()
|
||||
defer j.mu.RUnlock()
|
||||
return gconv.Structs(*(j.p), pointer, mapping...)
|
||||
}
|
||||
|
||||
// ToStructsDeep converts current Json object to specified object slice recursively.
|
||||
// The <pointer> should be a pointer type of []struct/*struct.
|
||||
func (j *Json) ToStructsDeep(pointer interface{}, mapping ...map[string]string) error {
|
||||
j.mu.RLock()
|
||||
defer j.mu.RUnlock()
|
||||
return gconv.StructsDeep(*(j.p), pointer, mapping...)
|
||||
}
|
||||
|
||||
// ToScan automatically calls Struct or Structs function according to the type of parameter
|
||||
// Scan automatically calls Struct or Structs function according to the type of parameter
|
||||
// <pointer> to implement the converting..
|
||||
func (j *Json) ToScan(pointer interface{}, mapping ...map[string]string) error {
|
||||
func (j *Json) Scan(pointer interface{}, mapping ...map[string]string) error {
|
||||
return gconv.Scan(*(j.p), pointer, mapping...)
|
||||
}
|
||||
|
||||
// ToScanDeep automatically calls StructDeep or StructsDeep function according to the type of
|
||||
// parameter <pointer> to implement the converting..
|
||||
func (j *Json) ToScanDeep(pointer interface{}, mapping ...map[string]string) error {
|
||||
return gconv.ScanDeep(*(j.p), pointer, mapping...)
|
||||
}
|
||||
|
||||
// ToMapToMap converts current Json object to specified map variable.
|
||||
// MapToMap converts current Json object to specified map variable.
|
||||
// The parameter of <pointer> should be type of *map.
|
||||
func (j *Json) ToMapToMap(pointer interface{}, mapping ...map[string]string) error {
|
||||
func (j *Json) MapToMap(pointer interface{}, mapping ...map[string]string) error {
|
||||
j.mu.RLock()
|
||||
defer j.mu.RUnlock()
|
||||
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.
|
||||
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.
|
||||
// MapToMaps converts current Json object to specified map variable slice.
|
||||
// The parameter of <pointer> should be type of []map/*map.
|
||||
func (j *Json) ToMapToMaps(pointer interface{}, mapping ...map[string]string) error {
|
||||
func (j *Json) MapToMaps(pointer interface{}, mapping ...map[string]string) error {
|
||||
j.mu.RLock()
|
||||
defer j.mu.RUnlock()
|
||||
return gconv.MapToMaps(*(j.p), pointer, mapping...)
|
||||
}
|
||||
|
||||
// ToMapToMapsDeep converts current Json object to specified map variable slice recursively.
|
||||
// The parameter of <pointer> should be type of []map/*map.
|
||||
func (j *Json) ToMapToMapsDeep(pointer interface{}, mapping ...map[string]string) error {
|
||||
j.mu.RLock()
|
||||
defer j.mu.RUnlock()
|
||||
return gconv.MapToMapsDeep(*(j.p), pointer, mapping...)
|
||||
}
|
||||
|
||||
// Dump prints current Json object with more manually readable.
|
||||
func (j *Json) Dump() {
|
||||
j.mu.RLock()
|
||||
|
||||
@ -228,7 +228,7 @@ func IsValidDataType(dataType string) bool {
|
||||
func checkDataType(content []byte) string {
|
||||
if json.Valid(content) {
|
||||
return "json"
|
||||
} else if gregex.IsMatch(`^<.+>[\S\s]+<.+>$`, content) {
|
||||
} else if gregex.IsMatch(`^<.+>[\S\s]+<.+>\s*$`, content) {
|
||||
return "xml"
|
||||
} else if !gregex.IsMatch(`[\n\r]*[\s\t\w\-\."]+\s*=\s*"""[\s\S]+"""`, content) && !gregex.IsMatch(`[\n\r]*[\s\t\w\-\."]+\s*=\s*'''[\s\S]+'''`, content) &&
|
||||
((gregex.IsMatch(`^[\n\r]*[\w\-\s\t]+\s*:\s*".+"`, content) || gregex.IsMatch(`^[\n\r]*[\w\-\s\t]+\s*:\s*\w+`, content)) ||
|
||||
|
||||
113
encoding/gjson/gjson_deprecated.go
Normal file
113
encoding/gjson/gjson_deprecated.go
Normal file
@ -0,0 +1,113 @@
|
||||
// Copyright GoFrame Author(https://github.com/gogf/gf). 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 gjson
|
||||
|
||||
import "github.com/gogf/gf/util/gconv"
|
||||
|
||||
// ToMap converts current Json object to map[string]interface{}.
|
||||
// It returns nil if fails.
|
||||
// Deprecated, use Map instead.
|
||||
func (j *Json) ToMap() map[string]interface{} {
|
||||
j.mu.RLock()
|
||||
defer j.mu.RUnlock()
|
||||
return gconv.Map(*(j.p))
|
||||
}
|
||||
|
||||
// ToArray converts current Json object to []interface{}.
|
||||
// It returns nil if fails.
|
||||
// Deprecated, use Array instead.
|
||||
func (j *Json) ToArray() []interface{} {
|
||||
j.mu.RLock()
|
||||
defer j.mu.RUnlock()
|
||||
return gconv.Interfaces(*(j.p))
|
||||
}
|
||||
|
||||
// ToStruct converts current Json object to specified object.
|
||||
// The <pointer> should be a pointer type of *struct.
|
||||
// Deprecated, use Struct instead.
|
||||
func (j *Json) ToStruct(pointer interface{}, mapping ...map[string]string) error {
|
||||
j.mu.RLock()
|
||||
defer j.mu.RUnlock()
|
||||
return gconv.Struct(*(j.p), pointer, mapping...)
|
||||
}
|
||||
|
||||
// ToStructDeep converts current Json object to specified object recursively.
|
||||
// The <pointer> should be a pointer type of *struct.
|
||||
// Deprecated, use Struct instead.
|
||||
func (j *Json) ToStructDeep(pointer interface{}, mapping ...map[string]string) error {
|
||||
j.mu.RLock()
|
||||
defer j.mu.RUnlock()
|
||||
return gconv.StructDeep(*(j.p), pointer, mapping...)
|
||||
}
|
||||
|
||||
// ToStructs converts current Json object to specified object slice.
|
||||
// The <pointer> should be a pointer type of []struct/*struct.
|
||||
// Deprecated, use Structs instead.
|
||||
func (j *Json) ToStructs(pointer interface{}, mapping ...map[string]string) error {
|
||||
j.mu.RLock()
|
||||
defer j.mu.RUnlock()
|
||||
return gconv.Structs(*(j.p), pointer, mapping...)
|
||||
}
|
||||
|
||||
// ToStructsDeep converts current Json object to specified object slice recursively.
|
||||
// The <pointer> should be a pointer type of []struct/*struct.
|
||||
// Deprecated, use Structs instead.
|
||||
func (j *Json) ToStructsDeep(pointer interface{}, mapping ...map[string]string) error {
|
||||
j.mu.RLock()
|
||||
defer j.mu.RUnlock()
|
||||
return gconv.StructsDeep(*(j.p), pointer, mapping...)
|
||||
}
|
||||
|
||||
// ToScan automatically calls Struct or Structs function according to the type of parameter
|
||||
// <pointer> to implement the converting..
|
||||
// Deprecated, use Scan instead.
|
||||
func (j *Json) ToScan(pointer interface{}, mapping ...map[string]string) error {
|
||||
return gconv.Scan(*(j.p), pointer, mapping...)
|
||||
}
|
||||
|
||||
// ToScanDeep automatically calls StructDeep or StructsDeep function according to the type of
|
||||
// parameter <pointer> to implement the converting..
|
||||
// Deprecated, use Scan instead.
|
||||
func (j *Json) ToScanDeep(pointer interface{}, mapping ...map[string]string) error {
|
||||
return gconv.ScanDeep(*(j.p), pointer, mapping...)
|
||||
}
|
||||
|
||||
// ToMapToMap converts current Json object to specified map variable.
|
||||
// The parameter of <pointer> should be type of *map.
|
||||
// Deprecated, use MapToMap instead.
|
||||
func (j *Json) ToMapToMap(pointer interface{}, mapping ...map[string]string) error {
|
||||
j.mu.RLock()
|
||||
defer j.mu.RUnlock()
|
||||
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.
|
||||
func (j *Json) ToMapToMaps(pointer interface{}, mapping ...map[string]string) error {
|
||||
j.mu.RLock()
|
||||
defer j.mu.RUnlock()
|
||||
return gconv.MapToMaps(*(j.p), pointer, mapping...)
|
||||
}
|
||||
|
||||
// ToMapToMapsDeep converts current Json object to specified map variable slice recursively.
|
||||
// The parameter of <pointer> should be type of []map/*map.
|
||||
// Deprecated, use MapToMaps instead.
|
||||
func (j *Json) ToMapToMapsDeep(pointer interface{}, mapping ...map[string]string) error {
|
||||
j.mu.RLock()
|
||||
defer j.mu.RUnlock()
|
||||
return gconv.MapToMapsDeep(*(j.p), pointer, mapping...)
|
||||
}
|
||||
@ -7,41 +7,91 @@
|
||||
package gjson_test
|
||||
|
||||
import (
|
||||
json2 "encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/gogf/gf/encoding/gjson"
|
||||
)
|
||||
|
||||
var (
|
||||
jsonStr1 = `[1,2,3]`
|
||||
jsonStr1 = `{"name":"john","slice":[1,2,3]}`
|
||||
jsonStr2 = `{"CallbackCommand":"Group.CallbackAfterSendMsg","From_Account":"61934946","GroupId":"@TGS#2FLGX67FD","MsgBody":[{"MsgContent":{"Text":"是的"},"MsgType":"TIMTextElem"}],"MsgSeq":23,"MsgTime":1567032819,"Operator_Account":"61934946","Random":2804799576,"Type":"Public"}`
|
||||
jsonObj1 = gjson.New(jsonStr1)
|
||||
jsonObj2 = gjson.New(jsonStr2)
|
||||
)
|
||||
|
||||
func Benchmark_Validate1(b *testing.B) {
|
||||
func Benchmark_Validate_Simple_Json(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
gjson.Valid(jsonStr1)
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_Validate2(b *testing.B) {
|
||||
func Benchmark_Validate_Complicated_Json(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
gjson.Valid(jsonStr2)
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_Set1(b *testing.B) {
|
||||
func Benchmark_Get_Simple_Json(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
p := gjson.New(map[string]string{
|
||||
"k1": "v1",
|
||||
"k2": "v2",
|
||||
})
|
||||
p.Set("k1.k11", []int{1, 2, 3})
|
||||
jsonObj1.Get("name")
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_Set2(b *testing.B) {
|
||||
func Benchmark_Get_Complicated_Json(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
p := gjson.New([]string{"a"})
|
||||
jsonObj2.Get("GroupId")
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_Stdlib_Json_Unmarshal_Simple_Json(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
var m map[string]interface{}
|
||||
json2.Unmarshal([]byte(jsonStr1), &m)
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_Stdlib_Json_Unmarshal_Complicated_Json(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
var m map[string]interface{}
|
||||
json2.Unmarshal([]byte(jsonStr2), &m)
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_New_Simple_Json(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
gjson.New(jsonStr1)
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_New_Complicated_Json(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
gjson.New(jsonStr2)
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_Remove_Simple_Json(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
jsonObj1.Remove("name")
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_Remove_Complicated_Json(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
jsonObj2.Remove("GroupId")
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_New_Nil_And_Set_Simple(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
p := gjson.New(nil)
|
||||
p.Set("k", "v")
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_New_Nil_And_Set_Multiple_Level(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
p := gjson.New(nil)
|
||||
p.Set("0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0", []int{1, 2, 3})
|
||||
}
|
||||
}
|
||||
|
||||
@ -203,7 +203,7 @@ func Test_ToStruct1(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func Test_ToStructDeep(t *testing.T) {
|
||||
func Test_ToStruct(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type Item struct {
|
||||
Title string `json:"title"`
|
||||
@ -231,10 +231,73 @@ func Test_ToStructDeep(t *testing.T) {
|
||||
t.Assert(j.GetBool("items"), false)
|
||||
t.Assert(j.GetArray("items"), nil)
|
||||
m := new(M)
|
||||
err = j.ToStructDeep(m)
|
||||
err = j.ToStruct(m)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNE(m.Me, nil)
|
||||
t.Assert(m.Me["day"], "20009")
|
||||
t.Assert(m.Items, nil)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_ToStruct_Complicated(t *testing.T) {
|
||||
type CertInfo struct {
|
||||
UserRealName string `json:"userRealname,omitempty"`
|
||||
IdentType string `json:"identType,omitempty"`
|
||||
IdentNo string `json:"identNo,omitempty"`
|
||||
CompanyName string `json:"companyName,omitempty"`
|
||||
Website string `json:"website,omitempty"`
|
||||
RegisterNo string `json:"registerNo,omitempty"`
|
||||
AreaCode string `json:"areaCode,omitempty"`
|
||||
Address string `json:"address,omitempty"`
|
||||
CommunityCreditCode string `json:"communityCreditCode,omitempty"`
|
||||
PhoneNumber string `json:"phoneNumber,omitempty"`
|
||||
AreaName string `json:"areaName,omitempty"`
|
||||
PhoneAreaCode string `json:"phoneAreaCode,omitempty"`
|
||||
OperateRange string `json:"operateRange,omitempty"`
|
||||
Email string `json:"email,omitempty"`
|
||||
LegalPersonName string `json:"legalPersonName,omitempty"`
|
||||
OrgCode string `json:"orgCode,omitempty"`
|
||||
BusinessLicense string `json:"businessLicense,omitempty"`
|
||||
FilePath1 string `json:"filePath1,omitempty"`
|
||||
MobileNo string `json:"mobileNo,omitempty"`
|
||||
CardName string `json:"cardName,omitempty"`
|
||||
BankMobileNo string `json:"bankMobileNo,omitempty"`
|
||||
BankCode string `json:"bankCode,omitempty"`
|
||||
BankCard string `json:"bankCard,omitempty"`
|
||||
}
|
||||
|
||||
type CertList struct {
|
||||
StatusCode uint `json:"statusCode,string"`
|
||||
SrcType uint `json:"srcType,string"`
|
||||
CertID string `json:"certId"`
|
||||
CardType string `json:"cardType,omitempty"`
|
||||
CertInfo CertInfo `json:"certInfo"`
|
||||
}
|
||||
|
||||
type Response struct {
|
||||
UserLevel uint `json:"userLevel,string,omitempty"`
|
||||
CertList []CertList `json:"certList"`
|
||||
}
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
jsonContent := `{
|
||||
"certList":[
|
||||
{"certId":"2023313","certInfo":"{\"address\":\"xxxxxxx\",\"phoneNumber\":\"15084890\",\"companyName\":\"dddd\",\"communityCreditCode\":\"91110111MBE1G2B\",\"operateRange\":\"fff\",\"registerNo\":\"91110111MA00G2B\",\"legalPersonName\":\"rrr\"}","srcType":"1","statusCode":"2"},
|
||||
{"certId":"2023314","certInfo":"{\"identNo\":\"342224196507051\",\"userRealname\":\"xxxx\",\"identType\":\"01\"}","srcType":"8","statusCode":"0"},
|
||||
{"certId":"2023322","certInfo":"{\"businessLicense\":\"91110111MA00BE1G\",\"companyName\":\"sssss\",\"communityCreditCode\":\"91110111MA00BE1\"}","srcType":"2","statusCode":"0"}
|
||||
]
|
||||
}`
|
||||
j, err := gjson.LoadContent(jsonContent)
|
||||
t.Assert(err, nil)
|
||||
var response = new(Response)
|
||||
err = j.ToStruct(response)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(len(response.CertList), 3)
|
||||
t.Assert(response.CertList[0].CertID, 2023313)
|
||||
t.Assert(response.CertList[1].CertID, 2023314)
|
||||
t.Assert(response.CertList[2].CertID, 2023322)
|
||||
t.Assert(response.CertList[0].CertInfo.PhoneNumber, "15084890")
|
||||
t.Assert(response.CertList[1].CertInfo.IdentNo, "342224196507051")
|
||||
t.Assert(response.CertList[2].CertInfo.BusinessLicense, "91110111MA00BE1G")
|
||||
})
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
// Copyright GoFrame gf Author(https://github.com/gogf/gf). 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,
|
||||
@ -14,67 +14,71 @@ import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// ApiStack is the interface for Stack feature.
|
||||
type ApiStack interface {
|
||||
Error() string // It should be en error.
|
||||
// apiCode is the interface for Code feature.
|
||||
type apiCode interface {
|
||||
Error() string // It should be an error.
|
||||
Code() int
|
||||
}
|
||||
|
||||
// apiStack is the interface for Stack feature.
|
||||
type apiStack interface {
|
||||
Error() string // It should be an error.
|
||||
Stack() string
|
||||
}
|
||||
|
||||
// ApiCause is the interface for Cause feature.
|
||||
type ApiCause interface {
|
||||
Error() string // It should be en error.
|
||||
// apiCause is the interface for Cause feature.
|
||||
type apiCause interface {
|
||||
Error() string // It should be an error.
|
||||
Cause() error
|
||||
}
|
||||
|
||||
// ApiLevel is the interface for Current/Next feature.
|
||||
type ApiLevel interface {
|
||||
// apiCurrent is the interface for Current feature.
|
||||
type apiCurrent interface {
|
||||
Error() string // It should be an error.
|
||||
Current() error
|
||||
}
|
||||
|
||||
// apiNext is the interface for Next feature.
|
||||
type apiNext interface {
|
||||
Error() string // It should be an error.
|
||||
Next() error
|
||||
}
|
||||
|
||||
// New creates and returns an error which is formatted from given text.
|
||||
func New(text string) error {
|
||||
if text == "" {
|
||||
return nil
|
||||
}
|
||||
return &Error{
|
||||
stack: callers(),
|
||||
text: text,
|
||||
code: -1,
|
||||
}
|
||||
}
|
||||
|
||||
// Newf returns an error that formats as the given format and args.
|
||||
func Newf(format string, args ...interface{}) error {
|
||||
return &Error{
|
||||
stack: callers(),
|
||||
text: fmt.Sprintf(format, args...),
|
||||
code: -1,
|
||||
}
|
||||
}
|
||||
|
||||
// NewSkip creates and returns an error which is formatted from given text.
|
||||
// The parameter <skip> specifies the stack callers skipped amount.
|
||||
func NewSkip(skip int, text string) error {
|
||||
if text == "" {
|
||||
return nil
|
||||
}
|
||||
return &Error{
|
||||
stack: callers(skip),
|
||||
text: text,
|
||||
code: -1,
|
||||
}
|
||||
}
|
||||
|
||||
// Newf returns an error that formats as the given format and args.
|
||||
func Newf(format string, args ...interface{}) error {
|
||||
if format == "" {
|
||||
return nil
|
||||
}
|
||||
return &Error{
|
||||
stack: callers(),
|
||||
text: fmt.Sprintf(format, args...),
|
||||
}
|
||||
}
|
||||
|
||||
// NewfSkip returns an error that formats as the given format and args.
|
||||
// NewSkipf returns an error that formats as the given format and args.
|
||||
// The parameter <skip> specifies the stack callers skipped amount.
|
||||
func NewfSkip(skip int, format string, args ...interface{}) error {
|
||||
if format == "" {
|
||||
return nil
|
||||
}
|
||||
func NewSkipf(skip int, format string, args ...interface{}) error {
|
||||
return &Error{
|
||||
stack: callers(skip),
|
||||
text: fmt.Sprintf(format, args...),
|
||||
code: -1,
|
||||
}
|
||||
}
|
||||
|
||||
@ -88,6 +92,7 @@ func Wrap(err error, text string) error {
|
||||
error: err,
|
||||
stack: callers(),
|
||||
text: text,
|
||||
code: -1,
|
||||
}
|
||||
}
|
||||
|
||||
@ -102,13 +107,91 @@ func Wrapf(err error, format string, args ...interface{}) error {
|
||||
error: err,
|
||||
stack: callers(),
|
||||
text: fmt.Sprintf(format, args...),
|
||||
code: -1,
|
||||
}
|
||||
}
|
||||
|
||||
// NewCode creates and returns an error that has error code and given text.
|
||||
func NewCode(code int, text string) error {
|
||||
return &Error{
|
||||
stack: callers(),
|
||||
text: text,
|
||||
code: code,
|
||||
}
|
||||
}
|
||||
|
||||
// NewCodef returns an error that has error code and formats as the given format and args.
|
||||
func NewCodef(code int, format string, args ...interface{}) error {
|
||||
return &Error{
|
||||
stack: callers(),
|
||||
text: fmt.Sprintf(format, args...),
|
||||
code: code,
|
||||
}
|
||||
}
|
||||
|
||||
// NewCodeSkip creates and returns an error which has error code and is formatted from given text.
|
||||
// The parameter <skip> specifies the stack callers skipped amount.
|
||||
func NewCodeSkip(code, skip int, text string) error {
|
||||
return &Error{
|
||||
stack: callers(skip),
|
||||
text: text,
|
||||
code: code,
|
||||
}
|
||||
}
|
||||
|
||||
// NewCodeSkipf returns an error that has error code and formats as the given format and args.
|
||||
// The parameter <skip> specifies the stack callers skipped amount.
|
||||
func NewCodeSkipf(code, skip int, format string, args ...interface{}) error {
|
||||
return &Error{
|
||||
stack: callers(skip),
|
||||
text: fmt.Sprintf(format, args...),
|
||||
code: code,
|
||||
}
|
||||
}
|
||||
|
||||
// WrapCode wraps error with code and text.
|
||||
// It returns nil if given err is nil.
|
||||
func WrapCode(code int, err error, text string) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
return &Error{
|
||||
error: err,
|
||||
stack: callers(),
|
||||
text: text,
|
||||
code: code,
|
||||
}
|
||||
}
|
||||
|
||||
// WrapCodef wraps error with code and format specifier.
|
||||
// It returns nil if given <err> is nil.
|
||||
func WrapCodef(code int, err error, format string, args ...interface{}) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
return &Error{
|
||||
error: err,
|
||||
stack: callers(),
|
||||
text: fmt.Sprintf(format, args...),
|
||||
code: code,
|
||||
}
|
||||
}
|
||||
|
||||
// Cause returns the error code of current error.
|
||||
// It returns -1 if it has no error code or it does not implements interface Code.
|
||||
func Code(err error) int {
|
||||
if err != nil {
|
||||
if e, ok := err.(apiCode); ok {
|
||||
return e.Code()
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// Cause returns the root cause error of <err>.
|
||||
func Cause(err error) error {
|
||||
if err != nil {
|
||||
if e, ok := err.(ApiCause); ok {
|
||||
if e, ok := err.(apiCause); ok {
|
||||
return e.Cause()
|
||||
}
|
||||
}
|
||||
@ -121,7 +204,7 @@ func Stack(err error) string {
|
||||
if err == nil {
|
||||
return ""
|
||||
}
|
||||
if e, ok := err.(ApiStack); ok {
|
||||
if e, ok := err.(apiStack); ok {
|
||||
return e.Stack()
|
||||
}
|
||||
return ""
|
||||
@ -133,7 +216,7 @@ func Current(err error) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
if e, ok := err.(ApiLevel); ok {
|
||||
if e, ok := err.(apiCurrent); ok {
|
||||
return e.Current()
|
||||
}
|
||||
return err
|
||||
@ -145,7 +228,7 @@ func Next(err error) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
if e, ok := err.(ApiLevel); ok {
|
||||
if e, ok := err.(apiNext); ok {
|
||||
return e.Next()
|
||||
}
|
||||
return nil
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
// Copyright GoFrame Author(https://github.com/gogf/gf). 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,
|
||||
@ -8,6 +8,7 @@ package gerror
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"runtime"
|
||||
@ -19,10 +20,11 @@ type Error struct {
|
||||
error error // Wrapped error.
|
||||
stack stack // Stack array, which records the stack information when this error is created or wrapped.
|
||||
text string // Error text, which is created by New* functions.
|
||||
code int // Error code if necessary.
|
||||
}
|
||||
|
||||
const (
|
||||
gFILTER_KEY = "/errors/gerror/gerror"
|
||||
stackFilterKey = "/errors/gerror/gerror"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -42,13 +44,23 @@ func (err *Error) Error() string {
|
||||
if err == nil {
|
||||
return ""
|
||||
}
|
||||
if err.text != "" {
|
||||
if err.error != nil {
|
||||
return err.text + ": " + err.error.Error()
|
||||
errStr := err.text
|
||||
if err.error != nil {
|
||||
if err.text != "" {
|
||||
errStr += ": "
|
||||
}
|
||||
return err.text
|
||||
errStr += err.error.Error()
|
||||
}
|
||||
return err.error.Error()
|
||||
return errStr
|
||||
}
|
||||
|
||||
// Code returns the error code.
|
||||
// It returns -1 if it has no error code.
|
||||
func (err *Error) Code() int {
|
||||
if err == nil {
|
||||
return -1
|
||||
}
|
||||
return err.code
|
||||
}
|
||||
|
||||
// Cause returns the root cause error.
|
||||
@ -60,12 +72,18 @@ func (err *Error) Cause() error {
|
||||
for loop != nil {
|
||||
if loop.error != nil {
|
||||
if e, ok := loop.error.(*Error); ok {
|
||||
// Internal Error struct.
|
||||
loop = e
|
||||
} else if e, ok := loop.error.(apiCause); ok {
|
||||
// Other Error that implements ApiCause interface.
|
||||
return e.Cause()
|
||||
} else {
|
||||
return loop.error
|
||||
}
|
||||
} else {
|
||||
return loop
|
||||
// return loop
|
||||
// To be compatible with Case of https://github.com/pkg/errors.
|
||||
return errors.New(loop.text)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
@ -105,9 +123,11 @@ func (err *Error) Stack() string {
|
||||
if err == nil {
|
||||
return ""
|
||||
}
|
||||
loop := err
|
||||
index := 1
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
var (
|
||||
loop = err
|
||||
index = 1
|
||||
buffer = bytes.NewBuffer(nil)
|
||||
)
|
||||
for loop != nil {
|
||||
buffer.WriteString(fmt.Sprintf("%d. %-v\n", index, loop))
|
||||
index++
|
||||
@ -156,7 +176,7 @@ 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, gFILTER_KEY) {
|
||||
if strings.Contains(file, stackFilterKey) {
|
||||
continue
|
||||
}
|
||||
// Avoid stack string like "<autogenerated>"
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user