mirror of
https://gitee.com/johng/gf
synced 2026-06-08 18:47:45 +08:00
Compare commits
179 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2636f8acf1 | |||
| 9042a885fa | |||
| ef837bd9c6 | |||
| 6a76725d64 | |||
| 697dbdc604 | |||
| 86f98f3710 | |||
| 470c696976 | |||
| e61bd174c8 | |||
| c71b9bc122 | |||
| c5339cbbe7 | |||
| 29020b0ac0 | |||
| 0a0530af0a | |||
| a5783ab860 | |||
| ae2de91b4c | |||
| ceecbef586 | |||
| 2fa558b25f | |||
| f465b478d6 | |||
| a4abac4916 | |||
| 5ab64e31fd | |||
| ac6a8b9b74 | |||
| f43d252d08 | |||
| 185f9efb9c | |||
| 47d423036f | |||
| 7fae21f255 | |||
| 3fce835da0 | |||
| 0b62ccf941 | |||
| e484685282 | |||
| 9c857705ff | |||
| 76f680de33 | |||
| 1181b6ae3a | |||
| 8a7f4ab156 | |||
| 99d43a7ddc | |||
| bd97d7d94e | |||
| edd93c39a3 | |||
| 28e5c33e81 | |||
| e6bb5e82a3 | |||
| a24b97b004 | |||
| 08eeff7f1c | |||
| 769f31e69a | |||
| 6d1ca028e7 | |||
| a228356399 | |||
| 3521f1b641 | |||
| 1dedf3d83b | |||
| da4c01c011 | |||
| 9cd445ad40 | |||
| ff4ef7e240 | |||
| 4de574ee86 | |||
| 0481b4025b | |||
| 9495b858fd | |||
| 5576adbd0b | |||
| b8da86bce8 | |||
| a926d3dadd | |||
| a95624ab19 | |||
| 10e042454c | |||
| 759be06ebc | |||
| aea37272ea | |||
| 2f17d37f7b | |||
| 5c9766fb1b | |||
| a3d2e03425 | |||
| 717dae8f5d | |||
| 373dbde42c | |||
| 125af33941 | |||
| b43e36a79d | |||
| 145fccdf6e | |||
| f9c478f250 | |||
| 5da822fdc4 | |||
| 5abf1b5742 | |||
| 9ac3841342 | |||
| 1f315c5b8d | |||
| b9440587d0 | |||
| 835a971f89 | |||
| 0bba2092af | |||
| c3240218f8 | |||
| 9e392985b8 | |||
| a7d30dd1d5 | |||
| fa96d881e1 | |||
| 9ca9d6c4bd | |||
| 7ad66db491 | |||
| 1dbda3872b | |||
| 3619a46f52 | |||
| 6a93d166c5 | |||
| c84e62febe | |||
| 8ff21d6936 | |||
| 3b0012ec30 | |||
| 62a3f1693d | |||
| ed6796dde0 | |||
| 46721d7552 | |||
| a9a5a78e02 | |||
| 7c4431ceeb | |||
| 8cfdc8f27f | |||
| 691baf5c16 | |||
| 8669057681 | |||
| b439aedce5 | |||
| 2504e405a7 | |||
| 6426409bf9 | |||
| 9cf4ea5c91 | |||
| 86343abcf8 | |||
| d0fe2d2f75 | |||
| 49ef4fd266 | |||
| 929a57ceb8 | |||
| c6f94ed95a | |||
| 949ac459fc | |||
| 82394cd70e | |||
| 4670a5d2e2 | |||
| 273d992493 | |||
| 1c71340719 | |||
| ed61c2ee22 | |||
| 363dede57c | |||
| b29c6add47 | |||
| 8d01e565c5 | |||
| 47e0fb95d5 | |||
| 23d346b291 | |||
| 105cdf6fe6 | |||
| 5135264d56 | |||
| c3ff1a3db3 | |||
| bd105a188f | |||
| 09affd3981 | |||
| 8e7e18e22d | |||
| e03fd80fd4 | |||
| adc3201dcf | |||
| 3a681e5b1a | |||
| c90ed0d424 | |||
| 285b45d19d | |||
| 783f1c8457 | |||
| daa7f12994 | |||
| 5d464494b6 | |||
| 4ca2513d00 | |||
| 15d69ed950 | |||
| 39bd2616c4 | |||
| 6302789c41 | |||
| 5c7b25c960 | |||
| 10102f2db9 | |||
| 6f5b5e4dc2 | |||
| 007c8a7b64 | |||
| 009ce9063e | |||
| 58a405bfa8 | |||
| 42214f17fc | |||
| b5f117e932 | |||
| b0f158047c | |||
| d5f7ca634d | |||
| 0801245871 | |||
| 278fd3515f | |||
| 56588f3f7f | |||
| a5d01cb547 | |||
| fabf5d1ad5 | |||
| ebae3a4929 | |||
| e4c42bde89 | |||
| f7515edde9 | |||
| 04c422c3af | |||
| 339ca74ff4 | |||
| 741a13379a | |||
| 055c6a668e | |||
| 735c5fc7ed | |||
| 3bfff2347f | |||
| 7d83604540 | |||
| adf2ddb510 | |||
| f30c9020fa | |||
| 1b3073f3f9 | |||
| 615161ac9d | |||
| 02e06e7c6e | |||
| d15268eb22 | |||
| e48415d932 | |||
| e5fa341f39 | |||
| 234d734981 | |||
| d771ed9209 | |||
| 37d72cb8b3 | |||
| 1154f9601b | |||
| 322513f99b | |||
| 6e7ac60d4d | |||
| 405840607f | |||
| 8417e7ef65 | |||
| ac2fe44d8e | |||
| 3030d7f032 | |||
| d74034e08e | |||
| d41cc7c3b6 | |||
| 3120d0bd7a | |||
| 7bebf062be | |||
| 6c657dff37 | |||
| 5896dadaad |
1
.gitignore
vendored
1
.gitignore
vendored
@ -15,4 +15,3 @@ cbuild
|
||||
**/.DS_Store
|
||||
.vscode/
|
||||
go.sum
|
||||
|
||||
|
||||
@ -21,7 +21,7 @@ addons:
|
||||
- local
|
||||
|
||||
before_install:
|
||||
- pwd
|
||||
- mysql -e 'CREATE DATABASE IF NOT EXISTS test;'
|
||||
|
||||
install:
|
||||
- cat /etc/hosts
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
| Name | Channel | Amount
|
||||
|---|---|---
|
||||
|[hailaz](https://gitee.com/hailaz)|gitee|¥20.00
|
||||
|[ireadx](https://github.com/ireadx)|alipay|¥201.00
|
||||
|[ireadx](https://github.com/ireadx)|alipay|¥301.00
|
||||
|[mg91](https://gitee.com/mg91)|gitee|¥10.00
|
||||
|[pibigstar](https://github.com/pibigstar)|alipay|¥10.00
|
||||
|[tiangenglan](https://gitee.com/tiangenglan)|gitee|¥30.00
|
||||
@ -15,6 +15,7 @@
|
||||
|x*z|wechat|¥20.00
|
||||
|潘兄|wechat|¥100.00
|
||||
|Fly的狐狸|wechat|¥100.00
|
||||
|全|alipay|¥100.00
|
||||
|土豆相公|alipay|¥66.60
|
||||
|Hades|alipay|¥66.66
|
||||
|蔡蔡|wechat|¥666.00
|
||||
|
||||
226
README.MD
226
README.MD
@ -40,6 +40,7 @@ golang version >= 1.10
|
||||
|
||||
# Quick Start
|
||||
|
||||
## Hello World
|
||||
```go
|
||||
package main
|
||||
|
||||
@ -56,8 +57,231 @@ func main() {
|
||||
s.Run()
|
||||
}
|
||||
```
|
||||
## Rich Router
|
||||
```go
|
||||
package main
|
||||
|
||||
[View More..](https://goframe.org/start/index)
|
||||
import (
|
||||
"github.com/gogf/gf/g/net/ghttp"
|
||||
"github.com/gogf/gf/g"
|
||||
)
|
||||
|
||||
func main() {
|
||||
s := g.Server()
|
||||
s.BindHandler("/{class}-{course}/:name/*act", func(r *ghttp.Request) {
|
||||
r.Response.Writeln(r.Get("class"))
|
||||
r.Response.Writeln(r.Get("course"))
|
||||
r.Response.Writeln(r.Get("name"))
|
||||
r.Response.Writeln(r.Get("act"))
|
||||
})
|
||||
s.SetPort(8199)
|
||||
s.Run()
|
||||
}
|
||||
```
|
||||
## Group Routers
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g"
|
||||
"github.com/gogf/gf/g/net/ghttp"
|
||||
)
|
||||
|
||||
type Object struct {}
|
||||
|
||||
func (o *Object) Show(r *ghttp.Request) {
|
||||
r.Response.Writeln("Object Show")
|
||||
}
|
||||
|
||||
func (o *Object) Delete(r *ghttp.Request) {
|
||||
r.Response.Writeln("Object REST Delete")
|
||||
}
|
||||
|
||||
func Handler(r *ghttp.Request) {
|
||||
r.Response.Writeln("Handler")
|
||||
}
|
||||
|
||||
func HookHandler(r *ghttp.Request) {
|
||||
r.Response.Writeln("Hook Handler")
|
||||
}
|
||||
|
||||
func main() {
|
||||
s := g.Server()
|
||||
obj := new(Object)
|
||||
group := s.Group("/api")
|
||||
group.ALL ("*", HookHandler, ghttp.HOOK_BEFORE_SERVE)
|
||||
group.ALL ("/handler", Handler)
|
||||
group.ALL ("/obj", obj)
|
||||
group.GET ("/obj/showit", obj, "Show")
|
||||
group.REST("/obj/rest", obj)
|
||||
s.SetPort(8199)
|
||||
s.Run()
|
||||
}
|
||||
```
|
||||
or
|
||||
```go
|
||||
func main() {
|
||||
s := g.Server()
|
||||
obj := new(Object)
|
||||
s.Group("/api").Bind([]ghttp.GroupItem{
|
||||
{"ALL", "*", HookHandler, ghttp.HOOK_BEFORE_SERVE},
|
||||
{"ALL", "/handler", Handler},
|
||||
{"ALL", "/obj", obj},
|
||||
{"GET", "/obj/showit", obj, "Show"},
|
||||
{"REST", "/obj/rest", obj},
|
||||
})
|
||||
s.SetPort(8199)
|
||||
s.Run()
|
||||
}
|
||||
```
|
||||
## Multi ports & domains
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g"
|
||||
"github.com/gogf/gf/g/net/ghttp"
|
||||
)
|
||||
|
||||
func Hello1(r *ghttp.Request) {
|
||||
r.Response.Write("127.0.0.1: Hello1!")
|
||||
}
|
||||
|
||||
func Hello2(r *ghttp.Request) {
|
||||
r.Response.Write("localhost: Hello2!")
|
||||
}
|
||||
|
||||
func main() {
|
||||
s := g.Server()
|
||||
s.Domain("127.0.0.1").BindHandler("/", Hello1)
|
||||
s.Domain("localhost").BindHandler("/", Hello2)
|
||||
s.SetPort(8100, 8200, 8300)
|
||||
s.Run()
|
||||
}
|
||||
```
|
||||
## Template Engine
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g"
|
||||
"github.com/gogf/gf/g/net/ghttp"
|
||||
)
|
||||
|
||||
func main() {
|
||||
s := g.Server()
|
||||
s.BindHandler("/template", func(r *ghttp.Request) {
|
||||
r.Response.WriteTpl("index.tpl", g.Map{
|
||||
"id": 123,
|
||||
"name": "john",
|
||||
})
|
||||
})
|
||||
s.SetPort(8199)
|
||||
s.Run()
|
||||
}
|
||||
```
|
||||
## File Uploading
|
||||
```go
|
||||
func Upload(r *ghttp.Request) {
|
||||
if f, h, e := r.FormFile("upload-file"); e == nil {
|
||||
defer f.Close()
|
||||
name := gfile.Basename(h.Filename)
|
||||
buffer := make([]byte, h.Size)
|
||||
f.Read(buffer)
|
||||
gfile.PutBinContents("/tmp/" + name, buffer)
|
||||
r.Response.Write(name + " uploaded successly")
|
||||
} else {
|
||||
r.Response.Write(e.Error())
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## ORM Operations
|
||||
|
||||
### 1. Retrieving instance
|
||||
```go
|
||||
db := g.DB()
|
||||
db := g.DB("user-center")
|
||||
```
|
||||
### 2. Chaining Operations
|
||||
|
||||
`Where + string`
|
||||
```go
|
||||
// SELECT * FROM user WHERE uid>1 LIMIT 0,10
|
||||
r, err := db.Table("user").Where("uid > ?", 1).Limit(0, 10).Select()
|
||||
|
||||
// SELECT uid,name FROM user WHERE uid>1 LIMIT 0,10
|
||||
r, err := db.Table("user").Fileds("uid,name").Where("uid > ?", 1).Limit(0, 10).Select()
|
||||
|
||||
// SELECT * FROM user WHERE uid=1
|
||||
r, err := db.Table("user").Where("u.uid=1",).One()
|
||||
r, err := db.Table("user").Where("u.uid", 1).One()
|
||||
r, err := db.Table("user").Where("u.uid=?", 1).One()
|
||||
// SELECT * FROM user WHERE (uid=1) AND (name='john')
|
||||
r, err := db.Table("user").Where("uid", 1).Where("name", "john").One()
|
||||
r, err := db.Table("user").Where("uid=?", 1).And("name=?", "john").One()
|
||||
// SELECT * FROM user WHERE (uid=1) OR (name='john')
|
||||
r, err := db.Table("user").Where("uid=?", 1).Or("name=?", "john").One()
|
||||
```
|
||||
`Where + map`
|
||||
```go
|
||||
// SELECT * FROM user WHERE uid=1 AND name='john'
|
||||
r, err := db.Table("user").Where(g.Map{"uid" : 1, "name" : "john"}).One()
|
||||
// SELECT * FROM user WHERE uid=1 AND age>18
|
||||
r, err := db.Table("user").Where(g.Map{"uid" : 1, "age>" : 18}).One()
|
||||
```
|
||||
`Where + struct/*struct`
|
||||
```go
|
||||
type User struct {
|
||||
Id int `json:"uid"`
|
||||
UserName string `gconv:"name"`
|
||||
}
|
||||
// SELECT * FROM user WHERE uid =1 AND name='john'
|
||||
r, err := db.Table("user").Where(User{ Id : 1, UserName : "john"}).One()
|
||||
// SELECT * FROM user WHERE uid =1
|
||||
r, err := db.Table("user").Where(&User{ Id : 1}).One()
|
||||
```
|
||||
### 3. Update & Delete
|
||||
```go
|
||||
// UPDATE user SET name='john guo' WHERE name='john'
|
||||
r, err := db.Table("user").Data(gdb.Map{"name" : "john guo"}).Where("name=?", "john").Update()
|
||||
r, err := db.Table("user").Data("name='john guo'").Where("name=?", "john").Update()
|
||||
// UPDATE user SET status=1 ORDER BY login_time asc LIMIT 10
|
||||
r, err := db.Table("user").Data("status", 1).OrderBy("login_time asc").Limit(10).Update
|
||||
|
||||
// DELETE FROM user WHERE uid=10
|
||||
r, err := db.Table("user").Where("uid=?", 10).Delete()
|
||||
// DELETE FROM user ORDER BY login_time asc LIMIT 10
|
||||
r, err := db.Table("user").OrderBy("login_time asc").Limit(10).Delete()
|
||||
```
|
||||
### 4. Insert & Replace & Save
|
||||
```go
|
||||
r, err := db.Table("user").Data(g.Map{"name": "john"}).Insert()
|
||||
r, err := db.Table("user").Data(g.Map{"uid": 10000, "name": "john"}).Replace()
|
||||
r, err := db.Table("user").Data(g.Map{"uid": 10001, "name": "john"}).Save()
|
||||
```
|
||||
### 5. Transaction
|
||||
```go
|
||||
if tx, err := db.Begin(); err == nil {
|
||||
r, err := tx.Save("user", g.Map{
|
||||
"uid" : 1,
|
||||
"name" : "john",
|
||||
})
|
||||
tx.Commit()
|
||||
}
|
||||
```
|
||||
### 6. Error Handling
|
||||
```go
|
||||
func GetOrderInfo(id int) (order *Order, err error) {
|
||||
err = g.DB().Table("order").Where("id", id).Struct(&order)
|
||||
if err != nil && err == sql.ErrNoRows {
|
||||
err = nil
|
||||
}
|
||||
return
|
||||
}
|
||||
```
|
||||
|
||||
[More Features...](https://goframe.org/start/index)
|
||||
|
||||
|
||||
# License
|
||||
|
||||
74
RELEASE.MD
74
RELEASE.MD
@ -1,4 +1,76 @@
|
||||
# `v1.7.0`
|
||||
# `v1.8.0` (2019-07-15)
|
||||
|
||||
## 新功能改进
|
||||
1. 框架目前 `69` 个开发模块(不包括内部模块),原生代码 `65302` 行(不包含第三包依赖包),单元测试覆盖率达到`77%`;
|
||||
1. 新增`gerror`错误处理模块:https://goframe.org/errors/gerror/index
|
||||
1. 改进`gcharset`字符编码转换模块,支持更多的字符集:https://goframe.org/encoding/gcharset/index
|
||||
1. 新增`gmutex`模块,基于`channel`实现的高级互斥锁模块,支持更丰富的互斥锁特性:https://goframe.org/os/gmutex/index
|
||||
1. 改进`glog`日志模块:
|
||||
- 新增日志异步输出特性:https://goframe.org/os/glog/async
|
||||
- 新增`Flags`额外功能特性:https://goframe.org/os/glog/flags
|
||||
- 新增`Json`数据格式输出:https://goframe.org/os/glog/json
|
||||
- 新增自定义`Writer`接口特性:https://goframe.org/os/glog/writer
|
||||
- **修改`Backtrace`名称为`Stack`,并改进调用堆栈输出格式;**
|
||||
- 新增`Expose`方法暴露内部默认`Logger`对象;
|
||||
1. 改进`gdb`数据库ORM模块:
|
||||
- **改进错误处理,当数据库操作没有查询到数据时,`error`返回`sql.ErrNoRows`**:https://goframe.org/database/gdb/error
|
||||
- 改进`Update`/`Delete`方法支持`Order BY`及`LIMIT`特性;
|
||||
- 数据库链式操作及方法操作中,预处理变量参数支持`slice`参数:https://goframe.org/database/gdb/chaining/model
|
||||
- **修改`Priority`权重配置名称为`Weight`;**
|
||||
- 新增`Debug`配置,可配置开启/关闭调试特性:https://goframe.org/database/gdb/config
|
||||
- 新增`Offset`方法,该方法为可选链式操作方法,`pgsql`数据库可直接通过`Limit`方法第二个参数自动识别为`Offset`语法;
|
||||
- 改进数据库动态切换特性,支持不同数据库类型的当前操作数据库切换;
|
||||
- 改进简化配置文件结构:https://goframe.org/database/gdb/config
|
||||
1. 改进`gconv`数据转换模块:
|
||||
- 对结构体对象转换时支持更多的标签:`gconv/c/json`;
|
||||
- 支持`*struct/[]struct/[]*struct`自动初始化创建对象/数组:https://goframe.org/util/gconv/struct
|
||||
- 新增`Strusts/StrctsDeep`方法,用于结构体数组的递归转换;
|
||||
- 新增`StructDeep`方法,用于对结构体对象的递归转换;
|
||||
- 新增`MapDeep`方法,用于对结构体属性的递归转换;
|
||||
1. 改进`ghttp`模块:
|
||||
- 改进`ghttp`模块的分组路由功能,完善逻辑处理细节,程序更加稳健;
|
||||
- 改进`ghttp.Request.Get*ToStruct`方法,支持`params/param/p`标签,支持结构体递归转换,并且支持`**struct`参数的对象自动初始化;
|
||||
- 改进`ghttp.CORSDefault`的跨域设置参数,`AllowOrigin`参数调整为`*`;
|
||||
1. 改进`gvalid`数据校验模块:
|
||||
- 增加对校验标签`gvalid/valid/v`的支持;
|
||||
- 改进`CheckStruct`支持对结构体对象的递归校验:https://goframe.org/util/gvalid/checkstruct
|
||||
1. 改进`gtcp`TCP通信模块:
|
||||
- 改进通信包协议设计,更加轻量级高效:https://goframe.org/net/gtcp/conn/pkg
|
||||
- 改进`TCP Server`增加对`TLS`的支持:https://goframe.org/net/gtcp/tls
|
||||
- 增加`Server.Cloce`服务端关闭方法;
|
||||
1. 改进`gproc`模块的通信数据结构,并使用`gtcp`的轻量级包协议重构消息发送逻辑;
|
||||
1. 改进`gqueue`模块增加数据同步缓冲机制,解决大数据量下的内存占用及延迟问题;
|
||||
1. 改进`gmlock`模块,使用`gmutex`模块替换内部的互斥锁,增加更多的操作方法;
|
||||
1. 改进`gaes`加密模块,增加`CBC`模式的加密/解密方法:
|
||||
1. 改进`garray.Range/SubSlice`方法,改进设计,提高性能;
|
||||
1. 改进`gjson`/`gparser`模块实现`MarshalJSON`接口以实现自定义的`JSON`数据格式转换;
|
||||
1. **改进`crypto`分类下模块的方法返回值,增加`error`错误变量返回,以保证更严谨的接口设计风格;**
|
||||
1. **改进`gbase64`模块,输入输出类型发生改变,并增加多个相关方法;**
|
||||
1. 改进`gflock`修改方法名`UnLock`为`Unlock`,新增`IsRLocked`方法;
|
||||
1. 新增`gfile.CopyFile/CopyDir`方法,用于文件及目录的复制;
|
||||
1. 改进`gjson/gparser/gvar/gcfg`模块增加更多的类型转换方法;
|
||||
1. 改进`gcache`模块,过期时间参数支持`time.Duration`类型;
|
||||
1. 新增`internal/structs`包,强大且便捷的结构体解析,并改进框架中所有涉及到结构体反射处理的模块;
|
||||
1. 改进`gbinary`增加封装方法对`BigEndian`的支持;
|
||||
|
||||
## Bug Fix
|
||||
1. 修复`garray.Search`返回值问题;
|
||||
1. 修复`garray.Contains`, `garray.New*ArrayFromCopy`方法逻辑问题;
|
||||
1. 修复`gjson.Remove`删除`slice`参数问题;
|
||||
1. 修复`gtree.AVLTree.Remove`方法返回值问题;
|
||||
1. 修复`gqueue.Size`不准确的大小问题;
|
||||
1. 修复`queue.Close`问题;
|
||||
1. 修复`gcache.GetOrSetLockFunc`当回调函数返回`nil`结果时的死锁问题;
|
||||
1. 修复`gfsnotify.Add`方法默认递归监控添加失效问题;
|
||||
1. 修复`gdb.Model.Scan`在某些参数类型下的失效问题;
|
||||
|
||||
## 注意事项
|
||||
|
||||
请注意以上粗体文字部分,如有使用,在您升级时可能会出现不兼容性。
|
||||
|
||||
|
||||
|
||||
# `v1.7.0` (2019-06-10)
|
||||
## 新功能/改进
|
||||
1. 重构改进`glog`模块:
|
||||
- 去掉日志模块所有的锁机制,改为无锁设计,执行性能更加高效
|
||||
|
||||
2
TODO.MD
2
TODO.MD
@ -49,7 +49,7 @@
|
||||
1. gdb的Cache缓存功能增加可自定义缓存接口,以便支持外部缓存功能,缓存接口可以通过io.ReadWriter接口实现;
|
||||
1. grpool增加支持阻塞添加任务接口;
|
||||
1. gdb.Model在链式安全的对象创建中增加sync.Pool的使用;
|
||||
|
||||
1. 增加g.Table快捷方法以方便操作数据表,但是得考虑后续模型操作设计,特别是脚手架的模型管理;
|
||||
|
||||
# DONE
|
||||
1. gconv完善针对不同类型的判断,例如:尽量减少sprintf("%v", xxx)来执行string类型的转换;
|
||||
|
||||
@ -8,7 +8,7 @@ package garray
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"encoding/json"
|
||||
"math"
|
||||
"sort"
|
||||
|
||||
@ -614,5 +614,13 @@ func (a *IntArray) CountValues() map[int]int {
|
||||
func (a *IntArray) String() string {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
return fmt.Sprint(a.array)
|
||||
jsonContent, _ := json.Marshal(a.array)
|
||||
return string(jsonContent)
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
func (a *IntArray) MarshalJSON() ([]byte, error) {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
return json.Marshal(a.array)
|
||||
}
|
||||
|
||||
@ -8,7 +8,7 @@ package garray
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"encoding/json"
|
||||
"math"
|
||||
"sort"
|
||||
|
||||
@ -608,5 +608,13 @@ func (a *Array) CountValues() map[interface{}]int {
|
||||
func (a *Array) String() string {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
return fmt.Sprint(a.array)
|
||||
jsonContent, _ := json.Marshal(a.array)
|
||||
return string(jsonContent)
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
func (a *Array) MarshalJSON() ([]byte, error) {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
return json.Marshal(a.array)
|
||||
}
|
||||
|
||||
@ -8,7 +8,7 @@ package garray
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"encoding/json"
|
||||
"math"
|
||||
"sort"
|
||||
"strings"
|
||||
@ -614,5 +614,13 @@ func (a *StringArray) CountValues() map[string]int {
|
||||
func (a *StringArray) String() string {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
return fmt.Sprint(a.array)
|
||||
jsonContent, _ := json.Marshal(a.array)
|
||||
return string(jsonContent)
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
func (a *StringArray) MarshalJSON() ([]byte, error) {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
return json.Marshal(a.array)
|
||||
}
|
||||
|
||||
@ -8,7 +8,7 @@ package garray
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"encoding/json"
|
||||
"math"
|
||||
"sort"
|
||||
|
||||
@ -539,5 +539,13 @@ func (a *SortedIntArray) CountValues() map[int]int {
|
||||
func (a *SortedIntArray) String() string {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
return fmt.Sprint(a.array)
|
||||
jsonContent, _ := json.Marshal(a.array)
|
||||
return string(jsonContent)
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
func (a *SortedIntArray) MarshalJSON() ([]byte, error) {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
return json.Marshal(a.array)
|
||||
}
|
||||
|
||||
@ -8,7 +8,7 @@ package garray
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"encoding/json"
|
||||
"math"
|
||||
"sort"
|
||||
|
||||
@ -540,5 +540,13 @@ func (a *SortedArray) CountValues() map[interface{}]int {
|
||||
func (a *SortedArray) String() string {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
return fmt.Sprint(a.array)
|
||||
jsonContent, _ := json.Marshal(a.array)
|
||||
return string(jsonContent)
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
func (a *SortedArray) MarshalJSON() ([]byte, error) {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
return json.Marshal(a.array)
|
||||
}
|
||||
|
||||
@ -8,7 +8,7 @@ package garray
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"encoding/json"
|
||||
"math"
|
||||
"sort"
|
||||
"strings"
|
||||
@ -534,5 +534,13 @@ func (a *SortedStringArray) CountValues() map[string]int {
|
||||
func (a *SortedStringArray) String() string {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
return fmt.Sprint(a.array)
|
||||
jsonContent, _ := json.Marshal(a.array)
|
||||
return string(jsonContent)
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
func (a *SortedStringArray) MarshalJSON() ([]byte, error) {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
return json.Marshal(a.array)
|
||||
}
|
||||
|
||||
@ -9,8 +9,9 @@
|
||||
package garray_test
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g/container/garray"
|
||||
"testing"
|
||||
|
||||
"github.com/gogf/gf/g/container/garray"
|
||||
)
|
||||
|
||||
var (
|
||||
|
||||
@ -31,6 +31,7 @@ func Test_SortedIntArray1(t *testing.T) {
|
||||
array.Add(i)
|
||||
}
|
||||
gtest.Assert(array.Slice(), expect)
|
||||
gtest.Assert(array.Add().Slice(), expect)
|
||||
}
|
||||
|
||||
func Test_SortedIntArray2(t *testing.T) {
|
||||
@ -44,11 +45,15 @@ func Test_SortedIntArray2(t *testing.T) {
|
||||
|
||||
func Test_SortedStringArray1(t *testing.T) {
|
||||
expect := []string{"0", "1", "10", "2", "3", "4", "5", "6", "7", "8", "9"}
|
||||
array := garray.NewSortedStringArray()
|
||||
array1 := garray.NewSortedStringArray()
|
||||
array2 := garray.NewSortedStringArray(true)
|
||||
for i := 10; i > -1; i-- {
|
||||
array.Add(gconv.String(i))
|
||||
array1.Add(gconv.String(i))
|
||||
array2.Add(gconv.String(i))
|
||||
}
|
||||
gtest.Assert(array.Slice(), expect)
|
||||
gtest.Assert(array1.Slice(), expect)
|
||||
gtest.Assert(array2.Slice(), expect)
|
||||
|
||||
}
|
||||
|
||||
func Test_SortedStringArray2(t *testing.T) {
|
||||
@ -58,6 +63,8 @@ func Test_SortedStringArray2(t *testing.T) {
|
||||
array.Add(gconv.String(i))
|
||||
}
|
||||
gtest.Assert(array.Slice(), expect)
|
||||
array.Add()
|
||||
gtest.Assert(array.Slice(), expect)
|
||||
}
|
||||
|
||||
func Test_SortedArray1(t *testing.T) {
|
||||
@ -73,13 +80,18 @@ func Test_SortedArray1(t *testing.T) {
|
||||
|
||||
func Test_SortedArray2(t *testing.T) {
|
||||
expect := []string{"0", "1", "10", "2", "3", "4", "5", "6", "7", "8", "9"}
|
||||
array := garray.NewSortedArray(func(v1, v2 interface{}) int {
|
||||
func1 := func(v1, v2 interface{}) int {
|
||||
return strings.Compare(gconv.String(v1), gconv.String(v2))
|
||||
})
|
||||
}
|
||||
array := garray.NewSortedArray(func1)
|
||||
array2 := garray.NewSortedArray(func1, true)
|
||||
for i := 0; i <= 10; i++ {
|
||||
array.Add(gconv.String(i))
|
||||
array2.Add(gconv.String(i))
|
||||
}
|
||||
gtest.Assert(array.Slice(), expect)
|
||||
gtest.Assert(array.Add().Slice(), expect)
|
||||
gtest.Assert(array2.Slice(), expect)
|
||||
}
|
||||
|
||||
func TestNewFromCopy(t *testing.T) {
|
||||
@ -89,6 +101,5 @@ func TestNewFromCopy(t *testing.T) {
|
||||
gtest.AssertIN(array1.PopRands(2), a1)
|
||||
gtest.Assert(len(array1.PopRands(1)), 1)
|
||||
gtest.Assert(len(array1.PopRands(9)), 3)
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
@ -9,7 +9,9 @@
|
||||
package garray_test
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g/util/gconv"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/g/container/garray"
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
@ -18,12 +20,15 @@ import (
|
||||
func Test_IntArray_Basic(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
expect := []int{0, 1, 2, 3}
|
||||
expect2 := []int{}
|
||||
array := garray.NewIntArrayFrom(expect)
|
||||
array2 := garray.NewIntArrayFrom(expect2)
|
||||
gtest.Assert(array.Slice(), expect)
|
||||
array.Set(0, 100)
|
||||
gtest.Assert(array.Get(0), 100)
|
||||
gtest.Assert(array.Get(1), 1)
|
||||
gtest.Assert(array.Search(100), 0)
|
||||
gtest.Assert(array2.Search(100), -1)
|
||||
gtest.Assert(array.Contains(100), true)
|
||||
gtest.Assert(array.Remove(0), 100)
|
||||
gtest.Assert(array.Contains(100), false)
|
||||
@ -44,13 +49,16 @@ func TestIntArray_Sort(t *testing.T) {
|
||||
expect1 := []int{0, 1, 2, 3}
|
||||
expect2 := []int{3, 2, 1, 0}
|
||||
array := garray.NewIntArray()
|
||||
array2 := garray.NewIntArray(true)
|
||||
for i := 3; i >= 0; i-- {
|
||||
array.Append(i)
|
||||
array2.Append(i)
|
||||
}
|
||||
array.Sort()
|
||||
gtest.Assert(array.Slice(), expect1)
|
||||
array.Sort(true)
|
||||
gtest.Assert(array.Slice(), expect2)
|
||||
gtest.Assert(array2.Slice(), expect2)
|
||||
})
|
||||
}
|
||||
|
||||
@ -98,21 +106,48 @@ func TestIntArray_Range(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
value1 := []int{0, 1, 2, 3, 4, 5, 6}
|
||||
array1 := garray.NewIntArrayFrom(value1)
|
||||
array2 := garray.NewIntArrayFrom(value1, true)
|
||||
gtest.Assert(array1.Range(0, 1), []int{0})
|
||||
gtest.Assert(array1.Range(1, 2), []int{1})
|
||||
gtest.Assert(array1.Range(0, 2), []int{0, 1})
|
||||
gtest.Assert(array1.Range(10, 2), nil)
|
||||
gtest.Assert(array1.Range(-1, 10), value1)
|
||||
gtest.Assert(array2.Range(1, 2), []int{1})
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntArray_Merge(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
a1 := []int{0, 1, 2, 3}
|
||||
a2 := []int{4, 5, 6, 7}
|
||||
array1 := garray.NewIntArrayFrom(a1)
|
||||
array2 := garray.NewIntArrayFrom(a2)
|
||||
gtest.Assert(array1.Merge(array2).Slice(), []int{0, 1, 2, 3, 4, 5, 6, 7})
|
||||
func1 := func(v1, v2 interface{}) int {
|
||||
if gconv.Int(v1) < gconv.Int(v2) {
|
||||
return 0
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
n1 := []int{0, 1, 2, 3}
|
||||
n2 := []int{4, 5, 6, 7}
|
||||
i1 := []interface{}{"1", "2"}
|
||||
s1 := []string{"a", "b", "c"}
|
||||
s2 := []string{"e", "f"}
|
||||
a1 := garray.NewIntArrayFrom(n1)
|
||||
a2 := garray.NewIntArrayFrom(n2)
|
||||
a3 := garray.NewArrayFrom(i1)
|
||||
a4 := garray.NewStringArrayFrom(s1)
|
||||
|
||||
a5 := garray.NewSortedStringArrayFrom(s2)
|
||||
a6 := garray.NewSortedIntArrayFrom([]int{1, 2, 3})
|
||||
|
||||
a7 := garray.NewSortedStringArrayFrom(s1)
|
||||
a8 := garray.NewSortedArrayFrom([]interface{}{4, 5}, func1)
|
||||
|
||||
gtest.Assert(a1.Merge(a2).Slice(), []int{0, 1, 2, 3, 4, 5, 6, 7})
|
||||
gtest.Assert(a1.Merge(a3).Len(), 10)
|
||||
gtest.Assert(a1.Merge(a4).Len(), 13)
|
||||
gtest.Assert(a1.Merge(a5).Len(), 15)
|
||||
gtest.Assert(a1.Merge(a6).Len(), 18)
|
||||
gtest.Assert(a1.Merge(a7).Len(), 21)
|
||||
gtest.Assert(a1.Merge(a8).Len(), 23)
|
||||
})
|
||||
}
|
||||
|
||||
@ -124,6 +159,7 @@ func TestIntArray_Fill(t *testing.T) {
|
||||
array2 := garray.NewIntArrayFrom(a2)
|
||||
gtest.Assert(array1.Fill(1, 2, 100).Slice(), []int{0, 100, 100})
|
||||
gtest.Assert(array2.Fill(0, 2, 100).Slice(), []int{100, 100})
|
||||
gtest.Assert(array2.Fill(-1, 2, 100).Slice(), []int{100, 100})
|
||||
})
|
||||
}
|
||||
|
||||
@ -136,6 +172,7 @@ func TestIntArray_Chunk(t *testing.T) {
|
||||
gtest.Assert(chunks[0], []int{1, 2})
|
||||
gtest.Assert(chunks[1], []int{3, 4})
|
||||
gtest.Assert(chunks[2], []int{5})
|
||||
gtest.Assert(array1.Chunk(0), nil)
|
||||
})
|
||||
}
|
||||
|
||||
@ -153,6 +190,7 @@ func TestIntArray_SubSlice(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
a1 := []int{0, 1, 2, 3, 4, 5, 6}
|
||||
array1 := garray.NewIntArrayFrom(a1)
|
||||
array2 := garray.NewIntArrayFrom(a1, true)
|
||||
gtest.Assert(array1.SubSlice(6), []int{6})
|
||||
gtest.Assert(array1.SubSlice(5), []int{5, 6})
|
||||
gtest.Assert(array1.SubSlice(8), nil)
|
||||
@ -168,6 +206,7 @@ func TestIntArray_SubSlice(t *testing.T) {
|
||||
gtest.Assert(array1.SubSlice(-9, 3), nil)
|
||||
gtest.Assert(array1.SubSlice(1, -1), []int{0})
|
||||
gtest.Assert(array1.SubSlice(1, -3), nil)
|
||||
gtest.Assert(array2.SubSlice(0, 2), []int{0, 1})
|
||||
})
|
||||
}
|
||||
|
||||
@ -193,7 +232,6 @@ func TestIntArray_PopRands(t *testing.T) {
|
||||
ns2 := array.PopRands(7)
|
||||
gtest.AssertIN(len(ns2), 6)
|
||||
gtest.AssertIN(ns2, []int{100, 200, 300, 400, 500, 600})
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
@ -226,6 +264,7 @@ func TestNewSortedIntArrayFrom(t *testing.T) {
|
||||
a1 := []int{0, 3, 2, 1, 4, 5, 6}
|
||||
array1 := garray.NewSortedIntArrayFrom(a1, true)
|
||||
gtest.Assert(array1.Join("."), "0.1.2.3.4.5.6")
|
||||
gtest.Assert(array1.Slice(), a1)
|
||||
})
|
||||
}
|
||||
|
||||
@ -260,7 +299,6 @@ func TestSortedIntArray_Sort(t *testing.T) {
|
||||
|
||||
gtest.Assert(array2.Len(), 4)
|
||||
gtest.Assert(array2, []int{0, 1, 2, 3})
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
@ -271,7 +309,6 @@ func TestSortedIntArray_Get(t *testing.T) {
|
||||
gtest.Assert(array1.Get(0), 0)
|
||||
gtest.Assert(array1.Get(1), 1)
|
||||
gtest.Assert(array1.Get(3), 5)
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
@ -296,7 +333,6 @@ func TestSortedIntArray_Remove(t *testing.T) {
|
||||
i3 = array2.Remove(1)
|
||||
gtest.Assert(array2.Search(4), -1)
|
||||
gtest.Assert(i3, 4)
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
@ -308,7 +344,6 @@ func TestSortedIntArray_PopLeft(t *testing.T) {
|
||||
gtest.Assert(i1, 1)
|
||||
gtest.Assert(array1.Len(), 3)
|
||||
gtest.Assert(array1.Search(1), -1)
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
@ -348,7 +383,6 @@ func TestSortedIntArray_PopRands(t *testing.T) {
|
||||
gtest.Assert(array2.Len(), 0)
|
||||
gtest.Assert(len(ns2), 4)
|
||||
gtest.AssertIN(ns2, []int{1, 3, 5, 2})
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
@ -365,7 +399,6 @@ func TestSortedIntArray_PopLefts(t *testing.T) {
|
||||
ns2 := array2.PopLefts(5)
|
||||
gtest.Assert(array2.Len(), 0)
|
||||
gtest.AssertIN(ns2, []int{1, 3, 5, 2})
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
@ -389,6 +422,7 @@ func TestSortedIntArray_Range(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
a1 := []int{1, 3, 5, 2, 6, 7}
|
||||
array1 := garray.NewSortedIntArrayFrom(a1)
|
||||
array2 := garray.NewSortedIntArrayFrom(a1, true)
|
||||
ns1 := array1.Range(1, 4)
|
||||
gtest.Assert(len(ns1), 3)
|
||||
gtest.Assert(ns1, []int{2, 3, 5})
|
||||
@ -401,7 +435,7 @@ func TestSortedIntArray_Range(t *testing.T) {
|
||||
|
||||
nsl := array1.Range(5, 8)
|
||||
gtest.Assert(len(nsl), 1)
|
||||
|
||||
gtest.Assert(array2.Range(1, 2), []int{2})
|
||||
})
|
||||
}
|
||||
|
||||
@ -418,7 +452,6 @@ func TestSortedIntArray_Contains(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
a1 := []int{1, 3, 5}
|
||||
array1 := garray.NewSortedIntArrayFrom(a1)
|
||||
//gtest.Assert(array1.Contains(3),true) //todo 这一行应该返回true
|
||||
gtest.Assert(array1.Contains(4), false)
|
||||
})
|
||||
}
|
||||
@ -439,7 +472,6 @@ func TestSortedIntArray_Clear(t *testing.T) {
|
||||
array1 := garray.NewSortedIntArrayFrom(a1)
|
||||
array1.Clear()
|
||||
gtest.Assert(array1.Len(), 0)
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
@ -453,7 +485,6 @@ func TestSortedIntArray_Chunk(t *testing.T) {
|
||||
gtest.Assert(ns1[0], []int{1, 2})
|
||||
gtest.Assert(ns1[2], []int{5})
|
||||
gtest.Assert(len(ns2), 0)
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
@ -461,6 +492,7 @@ func TestSortedIntArray_SubSlice(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
a1 := []int{1, 2, 3, 4, 5}
|
||||
array1 := garray.NewSortedIntArrayFrom(a1)
|
||||
array2 := garray.NewSortedIntArrayFrom(a1, true)
|
||||
ns1 := array1.SubSlice(1, 2)
|
||||
gtest.Assert(len(ns1), 2)
|
||||
gtest.Assert(ns1, []int{2, 3})
|
||||
@ -475,6 +507,10 @@ func TestSortedIntArray_SubSlice(t *testing.T) {
|
||||
ns4 := array1.SubSlice(3, 1)
|
||||
gtest.Assert(len(ns4), 1)
|
||||
gtest.Assert(ns4, []int{4})
|
||||
gtest.Assert(array1.SubSlice(-1, 1), []int{5})
|
||||
gtest.Assert(array1.SubSlice(-9, 1), nil)
|
||||
gtest.Assert(array1.SubSlice(1, -9), nil)
|
||||
gtest.Assert(array2.SubSlice(1, 2), []int{2, 3})
|
||||
})
|
||||
}
|
||||
|
||||
@ -519,7 +555,6 @@ func TestSortedIntArray_SetUnique(t *testing.T) {
|
||||
array1.SetUnique(true)
|
||||
gtest.Assert(array1.Len(), 5)
|
||||
gtest.Assert(array1, []int{1, 2, 3, 4, 5})
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
@ -531,7 +566,6 @@ func TestIntArray_SetArray(t *testing.T) {
|
||||
array1.SetArray(a2)
|
||||
gtest.Assert(array1.Len(), 2)
|
||||
gtest.Assert(array1, []int{6, 7})
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
@ -621,3 +655,171 @@ func TestIntArray_Remove(t *testing.T) {
|
||||
gtest.Assert(array1.Len(), 2)
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntArray_LockFunc(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
s1 := []int{1, 2, 3, 4}
|
||||
a1 := garray.NewIntArrayFrom(s1)
|
||||
|
||||
ch1 := make(chan int64, 3)
|
||||
ch2 := make(chan int64, 3)
|
||||
//go1
|
||||
go a1.LockFunc(func(n1 []int) { //读写锁
|
||||
time.Sleep(2 * time.Second) //暂停2秒
|
||||
n1[2] = 6
|
||||
ch2 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000)
|
||||
})
|
||||
|
||||
//go2
|
||||
go func() {
|
||||
time.Sleep(100 * time.Millisecond) //故意暂停0.01秒,等go1执行锁后,再开始执行.
|
||||
ch1 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000)
|
||||
a1.Len()
|
||||
ch1 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000)
|
||||
}()
|
||||
|
||||
t1 := <-ch1
|
||||
t2 := <-ch1
|
||||
<-ch2 //等待go1完成
|
||||
|
||||
// 防止ci抖动,以豪秒为单位
|
||||
gtest.AssertGT(t2-t1, 20) //go1加的读写互斥锁,所go2读的时候被阻塞。
|
||||
gtest.Assert(a1.Contains(6), true)
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntArray_SortFunc(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
s1 := []int{1, 4, 3, 2}
|
||||
a1 := garray.NewIntArrayFrom(s1)
|
||||
func1 := func(v1, v2 int) bool {
|
||||
return v1 < v2
|
||||
}
|
||||
a11 := a1.SortFunc(func1)
|
||||
gtest.Assert(a11, []int{1, 2, 3, 4})
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntArray_RLockFunc(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
s1 := []int{1, 2, 3, 4}
|
||||
a1 := garray.NewIntArrayFrom(s1)
|
||||
|
||||
ch1 := make(chan int64, 3)
|
||||
ch2 := make(chan int64, 1)
|
||||
//go1
|
||||
go a1.RLockFunc(func(n1 []int) { //读锁
|
||||
time.Sleep(2 * time.Second) //暂停1秒
|
||||
n1[2] = 6
|
||||
ch2 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000)
|
||||
})
|
||||
|
||||
//go2
|
||||
go func() {
|
||||
time.Sleep(100 * time.Millisecond) //故意暂停0.01秒,等go1执行锁后,再开始执行.
|
||||
ch1 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000)
|
||||
a1.Len()
|
||||
ch1 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000)
|
||||
}()
|
||||
|
||||
t1 := <-ch1
|
||||
t2 := <-ch1
|
||||
<-ch2 //等待go1完成
|
||||
|
||||
// 防止ci抖动,以豪秒为单位
|
||||
gtest.AssertLT(t2-t1, 20) //go1加的读锁,所go2读的时候,并没有阻塞。
|
||||
gtest.Assert(a1.Contains(6), true)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedIntArray_LockFunc(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
s1 := []int{1, 2, 3, 4}
|
||||
a1 := garray.NewSortedIntArrayFrom(s1)
|
||||
ch1 := make(chan int64, 3)
|
||||
ch2 := make(chan int64, 3)
|
||||
//go1
|
||||
go a1.LockFunc(func(n1 []int) { //读写锁
|
||||
time.Sleep(2 * time.Second) //暂停2秒
|
||||
n1[2] = 6
|
||||
ch2 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000)
|
||||
})
|
||||
|
||||
//go2
|
||||
go func() {
|
||||
time.Sleep(100 * time.Millisecond) //故意暂停0.01秒,等go1执行锁后,再开始执行.
|
||||
ch1 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000)
|
||||
a1.Len()
|
||||
ch1 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000)
|
||||
}()
|
||||
|
||||
t1 := <-ch1
|
||||
t2 := <-ch1
|
||||
<-ch2 //等待go1完成
|
||||
|
||||
// 防止ci抖动,以豪秒为单位
|
||||
gtest.AssertGT(t2-t1, 20) //go1加的读写互斥锁,所go2读的时候被阻塞。
|
||||
gtest.Assert(a1.Contains(6), true)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedIntArray_RLockFunc(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
s1 := []int{1, 2, 3, 4}
|
||||
a1 := garray.NewSortedIntArrayFrom(s1)
|
||||
|
||||
ch1 := make(chan int64, 3)
|
||||
ch2 := make(chan int64, 1)
|
||||
//go1
|
||||
go a1.RLockFunc(func(n1 []int) { //读锁
|
||||
time.Sleep(2 * time.Second) //暂停1秒
|
||||
n1[2] = 6
|
||||
ch2 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000)
|
||||
})
|
||||
|
||||
//go2
|
||||
go func() {
|
||||
time.Sleep(100 * time.Millisecond) //故意暂停0.01秒,等go1执行锁后,再开始执行.
|
||||
ch1 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000)
|
||||
a1.Len()
|
||||
ch1 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000)
|
||||
}()
|
||||
|
||||
t1 := <-ch1
|
||||
t2 := <-ch1
|
||||
<-ch2 //等待go1完成
|
||||
|
||||
// 防止ci抖动,以豪秒为单位
|
||||
gtest.AssertLT(t2-t1, 20) //go1加的读锁,所go2读的时候,并没有阻塞。
|
||||
gtest.Assert(a1.Contains(6), true)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedIntArray_Merge(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
func1 := func(v1, v2 interface{}) int {
|
||||
if gconv.Int(v1) < gconv.Int(v2) {
|
||||
return 0
|
||||
}
|
||||
return 1
|
||||
}
|
||||
i0 := []int{1, 2, 3, 4}
|
||||
s2 := []string{"e", "f"}
|
||||
i1 := garray.NewIntArrayFrom([]int{1, 2, 3})
|
||||
i2 := garray.NewArrayFrom([]interface{}{3})
|
||||
s3 := garray.NewStringArrayFrom([]string{"g", "h"})
|
||||
s4 := garray.NewSortedArrayFrom([]interface{}{4, 5}, func1)
|
||||
s5 := garray.NewSortedStringArrayFrom(s2)
|
||||
s6 := garray.NewSortedIntArrayFrom([]int{1, 2, 3})
|
||||
a1 := garray.NewSortedIntArrayFrom(i0)
|
||||
|
||||
gtest.Assert(a1.Merge(s2).Len(), 6)
|
||||
gtest.Assert(a1.Merge(i1).Len(), 9)
|
||||
gtest.Assert(a1.Merge(i2).Len(), 10)
|
||||
gtest.Assert(a1.Merge(s3).Len(), 12)
|
||||
gtest.Assert(a1.Merge(s4).Len(), 14)
|
||||
gtest.Assert(a1.Merge(s5).Len(), 16)
|
||||
gtest.Assert(a1.Merge(s6).Len(), 19)
|
||||
})
|
||||
}
|
||||
|
||||
@ -14,28 +14,36 @@ import (
|
||||
"github.com/gogf/gf/g/util/gconv"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func Test_Array_Basic(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
expect := []interface{}{0, 1, 2, 3}
|
||||
array := garray.NewArrayFrom(expect)
|
||||
array2 := garray.NewArrayFrom(expect)
|
||||
array3 := garray.NewArrayFrom([]interface{}{})
|
||||
gtest.Assert(array.Slice(), expect)
|
||||
array.Set(0, 100)
|
||||
gtest.Assert(array.Get(0), 100)
|
||||
gtest.Assert(array.Get(1), 1)
|
||||
gtest.Assert(array.Search(100), 0)
|
||||
gtest.Assert(array3.Search(100), -1)
|
||||
gtest.Assert(array.Contains(100), true)
|
||||
gtest.Assert(array.Remove(0), 100)
|
||||
|
||||
gtest.Assert(array2.Remove(3), 3)
|
||||
gtest.Assert(array2.Remove(1), 1)
|
||||
|
||||
gtest.Assert(array.Contains(100), false)
|
||||
array.Append(4)
|
||||
gtest.Assert(array.Len(), 4)
|
||||
array.InsertBefore(0, 100)
|
||||
array.InsertAfter(0, 200)
|
||||
gtest.Assert(array.Slice(), []interface{}{100, 200, 1, 2, 3, 4})
|
||||
gtest.Assert(array.Slice(), []interface{}{100, 200, 2, 2, 3, 4})
|
||||
array.InsertBefore(5, 300)
|
||||
array.InsertAfter(6, 400)
|
||||
gtest.Assert(array.Slice(), []interface{}{100, 200, 1, 2, 3, 300, 4, 400})
|
||||
gtest.Assert(array.Slice(), []interface{}{100, 200, 2, 2, 3, 300, 4, 400})
|
||||
gtest.Assert(array.Clear().Len(), 0)
|
||||
})
|
||||
}
|
||||
@ -111,20 +119,48 @@ func TestArray_Range(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
value1 := []interface{}{0, 1, 2, 3, 4, 5, 6}
|
||||
array1 := garray.NewArrayFrom(value1)
|
||||
array2 := garray.NewArrayFrom(value1, true)
|
||||
gtest.Assert(array1.Range(0, 1), []interface{}{0})
|
||||
gtest.Assert(array1.Range(1, 2), []interface{}{1})
|
||||
gtest.Assert(array1.Range(0, 2), []interface{}{0, 1})
|
||||
gtest.Assert(array1.Range(-1, 10), value1)
|
||||
gtest.Assert(array1.Range(10, 2), nil)
|
||||
gtest.Assert(array2.Range(1, 3), []interface{}{1, 2})
|
||||
})
|
||||
}
|
||||
|
||||
func TestArray_Merge(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
a1 := []interface{}{0, 1, 2, 3}
|
||||
a2 := []interface{}{4, 5, 6, 7}
|
||||
array1 := garray.NewArrayFrom(a1)
|
||||
array2 := garray.NewArrayFrom(a2)
|
||||
func1 := func(v1, v2 interface{}) int {
|
||||
if gconv.Int(v1) < gconv.Int(v2) {
|
||||
return 0
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
i1 := []interface{}{0, 1, 2, 3}
|
||||
i2 := []interface{}{4, 5, 6, 7}
|
||||
array1 := garray.NewArrayFrom(i1)
|
||||
array2 := garray.NewArrayFrom(i2)
|
||||
gtest.Assert(array1.Merge(array2).Slice(), []interface{}{0, 1, 2, 3, 4, 5, 6, 7})
|
||||
|
||||
//s1 := []string{"a", "b", "c", "d"}
|
||||
s2 := []string{"e", "f"}
|
||||
i3 := garray.NewIntArrayFrom([]int{1, 2, 3})
|
||||
i4 := garray.NewArrayFrom([]interface{}{3})
|
||||
s3 := garray.NewStringArrayFrom([]string{"g", "h"})
|
||||
s4 := garray.NewSortedArrayFrom([]interface{}{4, 5}, func1)
|
||||
s5 := garray.NewSortedStringArrayFrom(s2)
|
||||
s6 := garray.NewSortedIntArrayFrom([]int{1, 2, 3})
|
||||
a1 := garray.NewArrayFrom(i1)
|
||||
|
||||
gtest.Assert(a1.Merge(s2).Len(), 6)
|
||||
gtest.Assert(a1.Merge(i3).Len(), 9)
|
||||
gtest.Assert(a1.Merge(i4).Len(), 10)
|
||||
gtest.Assert(a1.Merge(s3).Len(), 12)
|
||||
gtest.Assert(a1.Merge(s4).Len(), 14)
|
||||
gtest.Assert(a1.Merge(s5).Len(), 16)
|
||||
gtest.Assert(a1.Merge(s6).Len(), 19)
|
||||
})
|
||||
}
|
||||
|
||||
@ -133,9 +169,10 @@ func TestArray_Fill(t *testing.T) {
|
||||
a1 := []interface{}{0}
|
||||
a2 := []interface{}{0}
|
||||
array1 := garray.NewArrayFrom(a1)
|
||||
array2 := garray.NewArrayFrom(a2)
|
||||
array2 := garray.NewArrayFrom(a2, true)
|
||||
gtest.Assert(array1.Fill(1, 2, 100).Slice(), []interface{}{0, 100, 100})
|
||||
gtest.Assert(array2.Fill(0, 2, 100).Slice(), []interface{}{100, 100})
|
||||
gtest.Assert(array2.Fill(-1, 2, 100).Slice(), []interface{}{100, 100})
|
||||
})
|
||||
}
|
||||
|
||||
@ -148,6 +185,7 @@ func TestArray_Chunk(t *testing.T) {
|
||||
gtest.Assert(chunks[0], []interface{}{1, 2})
|
||||
gtest.Assert(chunks[1], []interface{}{3, 4})
|
||||
gtest.Assert(chunks[2], []interface{}{5})
|
||||
gtest.Assert(array1.Chunk(0), nil)
|
||||
})
|
||||
}
|
||||
|
||||
@ -165,9 +203,15 @@ func TestArray_SubSlice(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
a1 := []interface{}{0, 1, 2, 3, 4, 5, 6}
|
||||
array1 := garray.NewArrayFrom(a1)
|
||||
array2 := garray.NewArrayFrom(a1, true)
|
||||
gtest.Assert(array1.SubSlice(0, 2), []interface{}{0, 1})
|
||||
gtest.Assert(array1.SubSlice(2, 2), []interface{}{2, 3})
|
||||
gtest.Assert(array1.SubSlice(5, 8), []interface{}{5, 6})
|
||||
gtest.Assert(array1.SubSlice(9, 1), nil)
|
||||
gtest.Assert(array1.SubSlice(-2, 2), []interface{}{5, 6})
|
||||
gtest.Assert(array1.SubSlice(-9, 2), nil)
|
||||
gtest.Assert(array1.SubSlice(1, -2), nil)
|
||||
gtest.Assert(array2.SubSlice(0, 2), []interface{}{0, 1})
|
||||
})
|
||||
}
|
||||
|
||||
@ -179,6 +223,14 @@ func TestArray_Rand(t *testing.T) {
|
||||
gtest.Assert(len(array1.Rands(10)), 7)
|
||||
gtest.AssertIN(array1.Rands(1)[0], a1)
|
||||
})
|
||||
|
||||
gtest.Case(t, func() {
|
||||
s1 := []interface{}{"a", "b", "c", "d"}
|
||||
a1 := garray.NewArrayFrom(s1)
|
||||
i1 := a1.Rand()
|
||||
gtest.Assert(a1.Contains(i1), true)
|
||||
gtest.Assert(a1.Len(), 4)
|
||||
})
|
||||
}
|
||||
|
||||
func TestArray_Shuffle(t *testing.T) {
|
||||
@ -496,6 +548,7 @@ func TestSortedArray_Range(t *testing.T) {
|
||||
return strings.Compare(gconv.String(v1), gconv.String(v2))
|
||||
}
|
||||
array1 := garray.NewSortedArrayFrom(a1, func1)
|
||||
array2 := garray.NewSortedArrayFrom(a1, func1, true)
|
||||
i1 := array1.Range(2, 5)
|
||||
gtest.Assert(i1, []interface{}{"c", "d", "e"})
|
||||
gtest.Assert(array1.Len(), 6)
|
||||
@ -509,6 +562,8 @@ func TestSortedArray_Range(t *testing.T) {
|
||||
gtest.Assert(len(i2), 2)
|
||||
gtest.Assert(i2, []interface{}{"e", "f"})
|
||||
|
||||
gtest.Assert(array2.Range(1, 3), []interface{}{"b", "c"})
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
@ -587,6 +642,7 @@ func TestSortedArray_SubSlice(t *testing.T) {
|
||||
return strings.Compare(gconv.String(v1), gconv.String(v2))
|
||||
}
|
||||
array1 := garray.NewSortedArrayFrom(a1, func1)
|
||||
array2 := garray.NewSortedArrayFrom(a1, func1, true)
|
||||
i1 := array1.SubSlice(2, 3)
|
||||
gtest.Assert(len(i1), 3)
|
||||
gtest.Assert(i1, []interface{}{"c", "d", "e"})
|
||||
@ -598,6 +654,13 @@ func TestSortedArray_SubSlice(t *testing.T) {
|
||||
i1 = array1.SubSlice(7, 2)
|
||||
gtest.Assert(len(i1), 0)
|
||||
|
||||
s1 := array1.SubSlice(1, -2)
|
||||
gtest.Assert(s1, nil)
|
||||
|
||||
s1 = array1.SubSlice(-9, 2)
|
||||
gtest.Assert(s1, nil)
|
||||
gtest.Assert(array2.SubSlice(1, 3), []interface{}{"b", "c", "d"})
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
@ -676,3 +739,168 @@ func TestSortedArray_SetUnique(t *testing.T) {
|
||||
gtest.Assert(array1, []interface{}{"a", "c", "d"})
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedArray_LockFunc(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
func1 := func(v1, v2 interface{}) int {
|
||||
return strings.Compare(gconv.String(v1), gconv.String(v2))
|
||||
}
|
||||
s1 := []interface{}{"a", "b", "c", "d"}
|
||||
a1 := garray.NewSortedArrayFrom(s1, func1)
|
||||
|
||||
ch1 := make(chan int64, 3)
|
||||
ch2 := make(chan int64, 3)
|
||||
//go1
|
||||
go a1.LockFunc(func(n1 []interface{}) { //读写锁
|
||||
time.Sleep(2 * time.Second) //暂停2秒
|
||||
n1[2] = "g"
|
||||
ch2 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000)
|
||||
})
|
||||
|
||||
//go2
|
||||
go func() {
|
||||
time.Sleep(100 * time.Millisecond) //故意暂停0.01秒,等go1执行锁后,再开始执行.
|
||||
ch1 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000)
|
||||
a1.Len()
|
||||
ch1 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000)
|
||||
}()
|
||||
|
||||
t1 := <-ch1
|
||||
t2 := <-ch1
|
||||
<-ch2 //等待go1完成
|
||||
|
||||
// 防止ci抖动,以豪秒为单位
|
||||
gtest.AssertGT(t2-t1, 20) //go1加的读写互斥锁,所go2读的时候被阻塞。
|
||||
gtest.Assert(a1.Contains("g"), true)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedArray_RLockFunc(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
func1 := func(v1, v2 interface{}) int {
|
||||
return strings.Compare(gconv.String(v1), gconv.String(v2))
|
||||
}
|
||||
s1 := []interface{}{"a", "b", "c", "d"}
|
||||
a1 := garray.NewSortedArrayFrom(s1, func1)
|
||||
|
||||
ch1 := make(chan int64, 3)
|
||||
ch2 := make(chan int64, 3)
|
||||
//go1
|
||||
go a1.RLockFunc(func(n1 []interface{}) { //读写锁
|
||||
time.Sleep(2 * time.Second) //暂停2秒
|
||||
n1[2] = "g"
|
||||
ch2 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000)
|
||||
})
|
||||
|
||||
//go2
|
||||
go func() {
|
||||
time.Sleep(100 * time.Millisecond) //故意暂停0.01秒,等go1执行锁后,再开始执行.
|
||||
ch1 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000)
|
||||
a1.Len()
|
||||
ch1 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000)
|
||||
}()
|
||||
|
||||
t1 := <-ch1
|
||||
t2 := <-ch1
|
||||
<-ch2 //等待go1完成
|
||||
|
||||
// 防止ci抖动,以豪秒为单位
|
||||
gtest.AssertLT(t2-t1, 20) //go1加的读锁,所go2读的时候不会被阻塞。
|
||||
gtest.Assert(a1.Contains("g"), true)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedArray_Merge(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
func1 := func(v1, v2 interface{}) int {
|
||||
if gconv.Int(v1) < gconv.Int(v2) {
|
||||
return 0
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
s1 := []interface{}{"a", "b", "c", "d"}
|
||||
s2 := []string{"e", "f"}
|
||||
i1 := garray.NewIntArrayFrom([]int{1, 2, 3})
|
||||
i2 := garray.NewArrayFrom([]interface{}{3})
|
||||
s3 := garray.NewStringArrayFrom([]string{"g", "h"})
|
||||
s4 := garray.NewSortedArrayFrom([]interface{}{4, 5}, func1)
|
||||
s5 := garray.NewSortedStringArrayFrom(s2)
|
||||
s6 := garray.NewSortedIntArrayFrom([]int{1, 2, 3})
|
||||
|
||||
a1 := garray.NewSortedArrayFrom(s1, func1)
|
||||
|
||||
gtest.Assert(a1.Merge(s2).Len(), 6)
|
||||
gtest.Assert(a1.Merge(i1).Len(), 9)
|
||||
gtest.Assert(a1.Merge(i2).Len(), 10)
|
||||
gtest.Assert(a1.Merge(s3).Len(), 12)
|
||||
gtest.Assert(a1.Merge(s4).Len(), 14)
|
||||
gtest.Assert(a1.Merge(s5).Len(), 16)
|
||||
gtest.Assert(a1.Merge(s6).Len(), 19)
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
func TestArray_LockFunc(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
s1 := []interface{}{"a", "b", "c", "d"}
|
||||
a1 := garray.NewArrayFrom(s1)
|
||||
|
||||
ch1 := make(chan int64, 3)
|
||||
ch2 := make(chan int64, 3)
|
||||
//go1
|
||||
go a1.LockFunc(func(n1 []interface{}) { //读写锁
|
||||
time.Sleep(2 * time.Second) //暂停2秒
|
||||
n1[2] = "g"
|
||||
ch2 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000)
|
||||
})
|
||||
|
||||
//go2
|
||||
go func() {
|
||||
time.Sleep(100 * time.Millisecond) //故意暂停0.01秒,等go1执行锁后,再开始执行.
|
||||
ch1 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000)
|
||||
a1.Len()
|
||||
ch1 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000)
|
||||
}()
|
||||
|
||||
t1 := <-ch1
|
||||
t2 := <-ch1
|
||||
<-ch2 //等待go1完成
|
||||
|
||||
// 防止ci抖动,以豪秒为单位
|
||||
gtest.AssertGT(t2-t1, 20) //go1加的读写互斥锁,所go2读的时候被阻塞。
|
||||
gtest.Assert(a1.Contains("g"), true)
|
||||
})
|
||||
}
|
||||
|
||||
func TestArray_RLockFunc(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
s1 := []interface{}{"a", "b", "c", "d"}
|
||||
a1 := garray.NewArrayFrom(s1)
|
||||
|
||||
ch1 := make(chan int64, 3)
|
||||
ch2 := make(chan int64, 1)
|
||||
//go1
|
||||
go a1.RLockFunc(func(n1 []interface{}) { //读锁
|
||||
time.Sleep(2 * time.Second) //暂停1秒
|
||||
n1[2] = "g"
|
||||
ch2 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000)
|
||||
})
|
||||
|
||||
//go2
|
||||
go func() {
|
||||
time.Sleep(100 * time.Millisecond) //故意暂停0.01秒,等go1执行锁后,再开始执行.
|
||||
ch1 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000)
|
||||
a1.Len()
|
||||
ch1 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000)
|
||||
}()
|
||||
|
||||
t1 := <-ch1
|
||||
t2 := <-ch1
|
||||
<-ch2 //等待go1完成
|
||||
|
||||
// 防止ci抖动,以豪秒为单位
|
||||
gtest.AssertLT(t2-t1, 20) //go1加的读锁,所go2读的时候,并没有阻塞。
|
||||
gtest.Assert(a1.Contains("g"), true)
|
||||
})
|
||||
}
|
||||
|
||||
@ -14,12 +14,15 @@ import (
|
||||
"github.com/gogf/gf/g/util/gconv"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func Test_StringArray_Basic(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
expect := []string{"0", "1", "2", "3"}
|
||||
array := garray.NewStringArrayFrom(expect)
|
||||
array2 := garray.NewStringArrayFrom(expect, true)
|
||||
array3 := garray.NewStringArrayFrom([]string{})
|
||||
gtest.Assert(array.Slice(), expect)
|
||||
array.Set(0, "100")
|
||||
gtest.Assert(array.Get(0), 100)
|
||||
@ -37,6 +40,8 @@ func Test_StringArray_Basic(t *testing.T) {
|
||||
array.InsertAfter(6, "400")
|
||||
gtest.Assert(array.Slice(), []string{"100", "200", "1", "2", "3", "300", "4", "400"})
|
||||
gtest.Assert(array.Clear().Len(), 0)
|
||||
gtest.Assert(array2.Slice(), expect)
|
||||
gtest.Assert(array3.Search("100"), -1)
|
||||
})
|
||||
}
|
||||
|
||||
@ -99,20 +104,48 @@ func TestString_Range(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
value1 := []string{"0", "1", "2", "3", "4", "5", "6"}
|
||||
array1 := garray.NewStringArrayFrom(value1)
|
||||
array2 := garray.NewStringArrayFrom(value1, true)
|
||||
gtest.Assert(array1.Range(0, 1), []interface{}{"0"})
|
||||
gtest.Assert(array1.Range(1, 2), []interface{}{"1"})
|
||||
gtest.Assert(array1.Range(0, 2), []interface{}{"0", "1"})
|
||||
gtest.Assert(array1.Range(-1, 10), value1)
|
||||
gtest.Assert(array1.Range(10, 1), nil)
|
||||
gtest.Assert(array2.Range(0, 1), []interface{}{"0"})
|
||||
})
|
||||
}
|
||||
|
||||
func TestStringArray_Merge(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
a1 := []string{"0", "1", "2", "3"}
|
||||
a2 := []string{"4", "5", "6", "7"}
|
||||
array1 := garray.NewStringArrayFrom(a1)
|
||||
array2 := garray.NewStringArrayFrom(a2)
|
||||
a11 := []string{"0", "1", "2", "3"}
|
||||
a21 := []string{"4", "5", "6", "7"}
|
||||
array1 := garray.NewStringArrayFrom(a11)
|
||||
array2 := garray.NewStringArrayFrom(a21)
|
||||
gtest.Assert(array1.Merge(array2).Slice(), []string{"0", "1", "2", "3", "4", "5", "6", "7"})
|
||||
|
||||
func1 := func(v1, v2 interface{}) int {
|
||||
if gconv.Int(v1) < gconv.Int(v2) {
|
||||
return 0
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
s1 := []string{"a", "b", "c", "d"}
|
||||
s2 := []string{"e", "f"}
|
||||
i1 := garray.NewIntArrayFrom([]int{1, 2, 3})
|
||||
i2 := garray.NewArrayFrom([]interface{}{3})
|
||||
s3 := garray.NewStringArrayFrom([]string{"g", "h"})
|
||||
s4 := garray.NewSortedArrayFrom([]interface{}{4, 5}, func1)
|
||||
s5 := garray.NewSortedStringArrayFrom(s2)
|
||||
s6 := garray.NewSortedIntArrayFrom([]int{1, 2, 3})
|
||||
a1 := garray.NewStringArrayFrom(s1)
|
||||
|
||||
gtest.Assert(a1.Merge(s2).Len(), 6)
|
||||
gtest.Assert(a1.Merge(i1).Len(), 9)
|
||||
gtest.Assert(a1.Merge(i2).Len(), 10)
|
||||
gtest.Assert(a1.Merge(s3).Len(), 12)
|
||||
gtest.Assert(a1.Merge(s4).Len(), 14)
|
||||
gtest.Assert(a1.Merge(s5).Len(), 16)
|
||||
gtest.Assert(a1.Merge(s6).Len(), 19)
|
||||
})
|
||||
}
|
||||
|
||||
@ -139,7 +172,6 @@ func TestStringArray_Chunk(t *testing.T) {
|
||||
gtest.Assert(chunks[1], []string{"3", "4"})
|
||||
gtest.Assert(chunks[2], []string{"5"})
|
||||
gtest.Assert(len(array1.Chunk(0)), 0)
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
@ -157,9 +189,15 @@ func TestStringArray_SubSlice(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
a1 := []string{"0", "1", "2", "3", "4", "5", "6"}
|
||||
array1 := garray.NewStringArrayFrom(a1)
|
||||
array2 := garray.NewStringArrayFrom(a1, true)
|
||||
gtest.Assert(array1.SubSlice(0, 2), []string{"0", "1"})
|
||||
gtest.Assert(array1.SubSlice(2, 2), []string{"2", "3"})
|
||||
gtest.Assert(array1.SubSlice(5, 8), []string{"5", "6"})
|
||||
gtest.Assert(array1.SubSlice(8, 2), nil)
|
||||
gtest.Assert(array1.SubSlice(1, -2), nil)
|
||||
gtest.Assert(array1.SubSlice(-5, 2), []string{"2", "3"})
|
||||
gtest.Assert(array1.SubSlice(-10, 1), nil)
|
||||
gtest.Assert(array2.SubSlice(0, 2), []string{"0", "1"})
|
||||
})
|
||||
}
|
||||
|
||||
@ -172,7 +210,6 @@ func TestStringArray_Rand(t *testing.T) {
|
||||
gtest.AssertIN(array1.Rands(1)[0], a1)
|
||||
gtest.Assert(len(array1.Rand()), 1)
|
||||
gtest.AssertIN(array1.Rand(), a1)
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
@ -181,10 +218,9 @@ func TestStringArray_PopRands(t *testing.T) {
|
||||
a1 := []string{"a", "b", "c", "d", "e", "f", "g"}
|
||||
a2 := []string{"1", "2", "3", "4", "5", "6", "7"}
|
||||
array1 := garray.NewStringArrayFrom(a1)
|
||||
//todo gtest.AssertIN(array1.PopRands(1),a1)
|
||||
gtest.AssertIN(array1.PopRands(1), strings.Join(a1, ","))
|
||||
gtest.AssertNI(array1.PopRands(1), strings.Join(a2, ","))
|
||||
|
||||
gtest.Assert(len(array1.PopRands(10)), 5)
|
||||
})
|
||||
}
|
||||
|
||||
@ -275,26 +311,6 @@ func TestStringArray_Sum(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
//func TestStringArray_SortFunc(t *testing.T) {
|
||||
// gtest.Case(t, func() {
|
||||
// a1 := []string{"0","1","2","3","4","5","6"}
|
||||
// //a2 := []string{"0","a","3","4","5","6"}
|
||||
// array1 := garray.NewStringArrayFrom(a1)
|
||||
//
|
||||
// lesss:=func(v1,v2 string)bool{
|
||||
// if v1>v2{
|
||||
// return true
|
||||
// }
|
||||
// return false
|
||||
// }
|
||||
// gtest.Assert(array1.Len(),7)
|
||||
// gtest.Assert(lesss("1","2"),false)
|
||||
// gtest.Assert(array1.SortFunc(lesss("1","2")) ,false)
|
||||
//
|
||||
//
|
||||
// })
|
||||
//}
|
||||
|
||||
func TestStringArray_PopRand(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
a1 := []string{"0", "1", "2", "3", "4", "5", "6"}
|
||||
@ -324,7 +340,6 @@ func TestStringArray_CountValues(t *testing.T) {
|
||||
gtest.Assert(len(m1), 6)
|
||||
gtest.Assert(m1["2"], 1)
|
||||
gtest.Assert(m1["4"], 2)
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
@ -357,7 +372,6 @@ func TestSortedStringArray_SetArray(t *testing.T) {
|
||||
gtest.Assert(array1.Contains("d"), false)
|
||||
gtest.Assert(array1.Contains("b"), false)
|
||||
gtest.Assert(array1.Contains("g"), true)
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
@ -367,7 +381,7 @@ func TestSortedStringArray_Sort(t *testing.T) {
|
||||
array1 := garray.NewSortedStringArrayFrom(a1)
|
||||
|
||||
gtest.Assert(array1, []string{"a", "b", "c", "d"})
|
||||
array1.Sort() //todo 这个SortedStringArray.sort这个方法没有必要,
|
||||
array1.Sort()
|
||||
gtest.Assert(array1.Len(), 4)
|
||||
gtest.Assert(array1.Contains("c"), true)
|
||||
gtest.Assert(array1, []string{"a", "b", "c", "d"})
|
||||
@ -380,7 +394,6 @@ func TestSortedStringArray_Get(t *testing.T) {
|
||||
array1 := garray.NewSortedStringArrayFrom(a1)
|
||||
gtest.Assert(array1.Get(2), "c")
|
||||
gtest.Assert(array1.Get(0), "a")
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
@ -400,7 +413,6 @@ func TestSortedStringArray_Remove(t *testing.T) {
|
||||
// 此时array1里的元素只剩下2个
|
||||
gtest.Assert(array1.Remove(1), "d")
|
||||
gtest.Assert(array1.Len(), 1)
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
@ -486,6 +498,7 @@ func TestSortedStringArray_Range(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
a1 := []string{"e", "a", "d", "c", "b", "f", "g"}
|
||||
array1 := garray.NewSortedStringArrayFrom(a1)
|
||||
array2 := garray.NewSortedStringArrayFrom(a1, true)
|
||||
s1 := array1.Range(2, 4)
|
||||
gtest.Assert(len(s1), 2)
|
||||
gtest.Assert(s1, []string{"c", "d"})
|
||||
@ -497,6 +510,11 @@ func TestSortedStringArray_Range(t *testing.T) {
|
||||
s1 = array1.Range(4, 8)
|
||||
gtest.Assert(len(s1), 3)
|
||||
gtest.Assert(s1, []string{"e", "f", "g"})
|
||||
gtest.Assert(array1.Range(10, 2), nil)
|
||||
|
||||
s2 := array2.Range(2, 4)
|
||||
gtest.Assert(s2, []string{"c", "d"})
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
@ -528,7 +546,6 @@ func TestSortedStringArray_Clear(t *testing.T) {
|
||||
array1 := garray.NewSortedStringArrayFrom(a1)
|
||||
array1.Clear()
|
||||
gtest.Assert(array1.Len(), 0)
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
@ -536,6 +553,7 @@ func TestSortedStringArray_SubSlice(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
a1 := []string{"e", "a", "d", "c", "b", "f", "g"}
|
||||
array1 := garray.NewSortedStringArrayFrom(a1)
|
||||
array2 := garray.NewSortedStringArrayFrom(a1, true)
|
||||
s1 := array1.SubSlice(1, 3)
|
||||
gtest.Assert(len(s1), 3)
|
||||
gtest.Assert(s1, []string{"b", "c", "d"})
|
||||
@ -547,6 +565,16 @@ func TestSortedStringArray_SubSlice(t *testing.T) {
|
||||
s3 := array1.SubSlice(10, 2)
|
||||
gtest.Assert(len(s3), 0)
|
||||
|
||||
s3 = array1.SubSlice(-5, 2)
|
||||
gtest.Assert(s3, []string{"c", "d"})
|
||||
|
||||
s3 = array1.SubSlice(-10, 2)
|
||||
gtest.Assert(s3, nil)
|
||||
|
||||
s3 = array1.SubSlice(1, -2)
|
||||
gtest.Assert(s3, nil)
|
||||
|
||||
gtest.Assert(array2.SubSlice(1, 3), []string{"b", "c", "d"})
|
||||
})
|
||||
}
|
||||
|
||||
@ -564,7 +592,6 @@ func TestSortedStringArray_Rand(t *testing.T) {
|
||||
a1 := []string{"e", "a", "d"}
|
||||
array1 := garray.NewSortedStringArrayFrom(a1)
|
||||
gtest.AssertIN(array1.Rand(), []string{"e", "a", "d"})
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
@ -611,6 +638,7 @@ func TestSortedStringArray_Chunk(t *testing.T) {
|
||||
gtest.Assert(len(array2), 3)
|
||||
gtest.Assert(len(array2[0]), 2)
|
||||
gtest.Assert(array2[1], []string{"c", "d"})
|
||||
gtest.Assert(array1.Chunk(0), nil)
|
||||
})
|
||||
}
|
||||
|
||||
@ -634,6 +662,174 @@ func TestStringArray_Remove(t *testing.T) {
|
||||
s1 = array1.Remove(3)
|
||||
gtest.Assert(s1, "c")
|
||||
gtest.Assert(array1.Len(), 3)
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
func TestStringArray_RLockFunc(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
s1 := []string{"a", "b", "c", "d"}
|
||||
a1 := garray.NewStringArrayFrom(s1, true)
|
||||
|
||||
ch1 := make(chan int64, 3)
|
||||
ch2 := make(chan int64, 1)
|
||||
//go1
|
||||
go a1.RLockFunc(func(n1 []string) { //读锁
|
||||
time.Sleep(2 * time.Second) //暂停1秒
|
||||
n1[2] = "g"
|
||||
ch2 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000)
|
||||
})
|
||||
|
||||
//go2
|
||||
go func() {
|
||||
time.Sleep(100 * time.Millisecond) //故意暂停0.01秒,等go1执行锁后,再开始执行.
|
||||
ch1 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000)
|
||||
a1.Len()
|
||||
ch1 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000)
|
||||
}()
|
||||
|
||||
t1 := <-ch1
|
||||
t2 := <-ch1
|
||||
<-ch2 //等待go1完成
|
||||
|
||||
// 防止ci抖动,以豪秒为单位
|
||||
gtest.AssertLT(t2-t1, 20) //go1加的读锁,所go2读的时候,并没有阻塞。
|
||||
gtest.Assert(a1.Contains("g"), true)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedStringArray_LockFunc(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
s1 := []string{"a", "b", "c", "d"}
|
||||
a1 := garray.NewSortedStringArrayFrom(s1)
|
||||
|
||||
ch1 := make(chan int64, 3)
|
||||
ch2 := make(chan int64, 3)
|
||||
//go1
|
||||
go a1.LockFunc(func(n1 []string) { //读写锁
|
||||
time.Sleep(2 * time.Second) //暂停2秒
|
||||
n1[2] = "g"
|
||||
ch2 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000)
|
||||
})
|
||||
|
||||
//go2
|
||||
go func() {
|
||||
time.Sleep(100 * time.Millisecond) //故意暂停0.01秒,等go1执行锁后,再开始执行.
|
||||
ch1 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000)
|
||||
a1.Len()
|
||||
ch1 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000)
|
||||
}()
|
||||
|
||||
t1 := <-ch1
|
||||
t2 := <-ch1
|
||||
<-ch2 //等待go1完成
|
||||
|
||||
// 防止ci抖动,以豪秒为单位
|
||||
gtest.AssertGT(t2-t1, 20) //go1加的读写互斥锁,所go2读的时候被阻塞。
|
||||
gtest.Assert(a1.Contains("g"), true)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedStringArray_RLockFunc(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
s1 := []string{"a", "b", "c", "d"}
|
||||
a1 := garray.NewSortedStringArrayFrom(s1)
|
||||
|
||||
ch1 := make(chan int64, 3)
|
||||
ch2 := make(chan int64, 1)
|
||||
//go1
|
||||
go a1.RLockFunc(func(n1 []string) { //读锁
|
||||
time.Sleep(2 * time.Second) //暂停1秒
|
||||
n1[2] = "g"
|
||||
ch2 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000)
|
||||
})
|
||||
|
||||
//go2
|
||||
go func() {
|
||||
time.Sleep(100 * time.Millisecond) //故意暂停0.01秒,等go1执行锁后,再开始执行.
|
||||
ch1 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000)
|
||||
a1.Len()
|
||||
ch1 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000)
|
||||
}()
|
||||
|
||||
t1 := <-ch1
|
||||
t2 := <-ch1
|
||||
<-ch2 //等待go1完成
|
||||
|
||||
// 防止ci抖动,以豪秒为单位
|
||||
gtest.AssertLT(t2-t1, 20) //go1加的读锁,所go2读的时候,并没有阻塞。
|
||||
gtest.Assert(a1.Contains("g"), true)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedStringArray_Merge(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
func1 := func(v1, v2 interface{}) int {
|
||||
if gconv.Int(v1) < gconv.Int(v2) {
|
||||
return 0
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
s1 := []string{"a", "b", "c", "d"}
|
||||
s2 := []string{"e", "f"}
|
||||
i1 := garray.NewIntArrayFrom([]int{1, 2, 3})
|
||||
i2 := garray.NewArrayFrom([]interface{}{3})
|
||||
s3 := garray.NewStringArrayFrom([]string{"g", "h"})
|
||||
s4 := garray.NewSortedArrayFrom([]interface{}{4, 5}, func1)
|
||||
s5 := garray.NewSortedStringArrayFrom(s2)
|
||||
s6 := garray.NewSortedIntArrayFrom([]int{1, 2, 3})
|
||||
a1 := garray.NewSortedStringArrayFrom(s1)
|
||||
|
||||
gtest.Assert(a1.Merge(s2).Len(), 6)
|
||||
gtest.Assert(a1.Merge(i1).Len(), 9)
|
||||
gtest.Assert(a1.Merge(i2).Len(), 10)
|
||||
gtest.Assert(a1.Merge(s3).Len(), 12)
|
||||
gtest.Assert(a1.Merge(s4).Len(), 14)
|
||||
gtest.Assert(a1.Merge(s5).Len(), 16)
|
||||
gtest.Assert(a1.Merge(s6).Len(), 19)
|
||||
})
|
||||
}
|
||||
|
||||
func TestStringArray_SortFunc(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
s1 := []string{"a", "d", "c", "b"}
|
||||
a1 := garray.NewStringArrayFrom(s1)
|
||||
func1 := func(v1, v2 string) bool {
|
||||
return v1 < v2
|
||||
}
|
||||
a11 := a1.SortFunc(func1)
|
||||
gtest.Assert(a11, []string{"a", "b", "c", "d"})
|
||||
})
|
||||
}
|
||||
|
||||
func TestStringArray_LockFunc(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
s1 := []string{"a", "b", "c", "d"}
|
||||
a1 := garray.NewStringArrayFrom(s1)
|
||||
|
||||
ch1 := make(chan int64, 3)
|
||||
ch2 := make(chan int64, 3)
|
||||
//go1
|
||||
go a1.LockFunc(func(n1 []string) { //读写锁
|
||||
time.Sleep(2 * time.Second) //暂停2秒
|
||||
n1[2] = "g"
|
||||
ch2 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000)
|
||||
})
|
||||
|
||||
//go2
|
||||
go func() {
|
||||
time.Sleep(100 * time.Millisecond) //故意暂停0.01秒,等go1执行锁后,再开始执行.
|
||||
ch1 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000)
|
||||
a1.Len()
|
||||
ch1 <- gconv.Int64(time.Now().UnixNano() / 1000 / 1000)
|
||||
}()
|
||||
|
||||
t1 := <-ch1
|
||||
t2 := <-ch1
|
||||
<-ch2 //等待go1完成
|
||||
|
||||
// 防止ci抖动,以豪秒为单位
|
||||
gtest.AssertGT(t2-t1, 20) //go1加的读写互斥锁,所go2读的时候被阻塞。
|
||||
gtest.Assert(a1.Contains("g"), true)
|
||||
})
|
||||
}
|
||||
|
||||
@ -13,41 +13,49 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
l = New()
|
||||
bn = 20000000
|
||||
l = New()
|
||||
)
|
||||
|
||||
func Benchmark_PushBack(b *testing.B) {
|
||||
b.N = bn
|
||||
for i := 0; i < b.N; i++ {
|
||||
l.PushBack(i)
|
||||
}
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
i := 0
|
||||
for pb.Next() {
|
||||
l.PushBack(i)
|
||||
i++
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func Benchmark_PushFront(b *testing.B) {
|
||||
b.N = bn
|
||||
for i := 0; i < b.N; i++ {
|
||||
l.PushFront(i)
|
||||
}
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
i := 0
|
||||
for pb.Next() {
|
||||
l.PushFront(i)
|
||||
i++
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func Benchmark_Len(b *testing.B) {
|
||||
b.N = bn
|
||||
for i := 0; i < b.N; i++ {
|
||||
l.Len()
|
||||
}
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
for pb.Next() {
|
||||
l.Len()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func Benchmark_PopFront(b *testing.B) {
|
||||
b.N = bn
|
||||
for i := 0; i < b.N; i++ {
|
||||
l.PopFront()
|
||||
}
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
for pb.Next() {
|
||||
l.PopFront()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func Benchmark_PopBack(b *testing.B) {
|
||||
b.N = bn
|
||||
for i := 0; i < b.N; i++ {
|
||||
l.PopBack()
|
||||
}
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
for pb.Next() {
|
||||
l.PopBack()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@ -8,6 +8,7 @@ package glist
|
||||
|
||||
import (
|
||||
"container/list"
|
||||
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
"github.com/gogf/gf/g/util/gconv"
|
||||
|
||||
@ -407,17 +408,18 @@ func TestList_PushBacks(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestList_PopBacks(t *testing.T) {
|
||||
l := New()
|
||||
a1 := []interface{}{1, 2, 3, 4}
|
||||
a2 := []interface{}{"a", "c", "b", "e"}
|
||||
l.PushFronts(a1)
|
||||
i1 := l.PopBacks(2)
|
||||
gtest.Assert(i1, []interface{}{"1", "2"})
|
||||
|
||||
l.PushBacks(a2) //4.3,a,c,b,e
|
||||
i1 = l.PopBacks(3)
|
||||
gtest.Assert(i1, []interface{}{"e", "b", "c"})
|
||||
gtest.Case(t, func() {
|
||||
l := New()
|
||||
a1 := []interface{}{1, 2, 3, 4}
|
||||
a2 := []interface{}{"a", "c", "b", "e"}
|
||||
l.PushFronts(a1)
|
||||
i1 := l.PopBacks(2)
|
||||
gtest.Assert(i1, []interface{}{1, 2})
|
||||
|
||||
l.PushBacks(a2) //4.3,a,c,b,e
|
||||
i1 = l.PopBacks(3)
|
||||
gtest.Assert(i1, []interface{}{"e", "b", "c"})
|
||||
})
|
||||
}
|
||||
|
||||
func TestList_PopFronts(t *testing.T) {
|
||||
@ -425,7 +427,7 @@ func TestList_PopFronts(t *testing.T) {
|
||||
a1 := []interface{}{1, 2, 3, 4}
|
||||
l.PushFronts(a1)
|
||||
i1 := l.PopFronts(2)
|
||||
gtest.Assert(i1, []interface{}{"4", "3"})
|
||||
gtest.Assert(i1, []interface{}{4, 3})
|
||||
gtest.Assert(l.Len(), 2)
|
||||
}
|
||||
|
||||
|
||||
@ -7,6 +7,10 @@
|
||||
package gmap
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/gogf/gf/g/util/gconv"
|
||||
|
||||
"github.com/gogf/gf/g/container/gvar"
|
||||
"github.com/gogf/gf/g/internal/rwmutex"
|
||||
)
|
||||
@ -328,3 +332,8 @@ func (m *AnyAnyMap) Merge(other *AnyAnyMap) {
|
||||
m.data[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
func (m *AnyAnyMap) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(gconv.Map(m.Map()))
|
||||
}
|
||||
|
||||
@ -8,6 +8,8 @@
|
||||
package gmap
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/gogf/gf/g/container/gvar"
|
||||
"github.com/gogf/gf/g/internal/rwmutex"
|
||||
"github.com/gogf/gf/g/util/gconv"
|
||||
@ -330,3 +332,10 @@ func (m *IntAnyMap) Merge(other *IntAnyMap) {
|
||||
m.data[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
func (m *IntAnyMap) MarshalJSON() ([]byte, error) {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
return json.Marshal(m.data)
|
||||
}
|
||||
|
||||
@ -7,6 +7,8 @@
|
||||
package gmap
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/gogf/gf/g/internal/rwmutex"
|
||||
)
|
||||
|
||||
@ -306,3 +308,10 @@ func (m *IntIntMap) Merge(other *IntIntMap) {
|
||||
m.data[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
func (m *IntIntMap) MarshalJSON() ([]byte, error) {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
return json.Marshal(m.data)
|
||||
}
|
||||
|
||||
@ -7,6 +7,8 @@
|
||||
package gmap
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/gogf/gf/g/internal/rwmutex"
|
||||
"github.com/gogf/gf/g/util/gconv"
|
||||
)
|
||||
@ -307,3 +309,10 @@ func (m *IntStrMap) Merge(other *IntStrMap) {
|
||||
m.data[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
func (m *IntStrMap) MarshalJSON() ([]byte, error) {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
return json.Marshal(m.data)
|
||||
}
|
||||
|
||||
@ -8,6 +8,8 @@
|
||||
package gmap
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/gogf/gf/g/container/gvar"
|
||||
"github.com/gogf/gf/g/internal/rwmutex"
|
||||
"github.com/gogf/gf/g/util/gconv"
|
||||
@ -332,3 +334,10 @@ func (m *StrAnyMap) Merge(other *StrAnyMap) {
|
||||
m.data[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
func (m *StrAnyMap) MarshalJSON() ([]byte, error) {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
return json.Marshal(m.data)
|
||||
}
|
||||
|
||||
@ -8,6 +8,8 @@
|
||||
package gmap
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/gogf/gf/g/internal/rwmutex"
|
||||
"github.com/gogf/gf/g/util/gconv"
|
||||
)
|
||||
@ -310,3 +312,10 @@ func (m *StrIntMap) Merge(other *StrIntMap) {
|
||||
m.data[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
func (m *StrIntMap) MarshalJSON() ([]byte, error) {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
return json.Marshal(m.data)
|
||||
}
|
||||
|
||||
@ -8,6 +8,8 @@
|
||||
package gmap
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/gogf/gf/g/internal/rwmutex"
|
||||
)
|
||||
|
||||
@ -309,3 +311,10 @@ func (m *StrStrMap) Merge(other *StrStrMap) {
|
||||
m.data[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
func (m *StrStrMap) MarshalJSON() ([]byte, error) {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
return json.Marshal(m.data)
|
||||
}
|
||||
|
||||
@ -7,6 +7,10 @@
|
||||
package gmap
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/gogf/gf/g/util/gconv"
|
||||
|
||||
"github.com/gogf/gf/g/container/glist"
|
||||
"github.com/gogf/gf/g/container/gvar"
|
||||
"github.com/gogf/gf/g/internal/rwmutex"
|
||||
@ -364,3 +368,8 @@ func (m *ListMap) Merge(other *ListMap) {
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
func (m *ListMap) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(gconv.Map(m.Map()))
|
||||
}
|
||||
|
||||
@ -9,9 +9,10 @@
|
||||
package gmap_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/gogf/gf/g/container/gmap"
|
||||
"github.com/gogf/gf/g/util/gutil"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var hashMap = gmap.New()
|
||||
@ -19,37 +20,61 @@ var listMap = gmap.NewListMap()
|
||||
var treeMap = gmap.NewTreeMap(gutil.ComparatorInt)
|
||||
|
||||
func Benchmark_HashMap_Set(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
hashMap.Set(i, i)
|
||||
}
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
i := 0
|
||||
for pb.Next() {
|
||||
hashMap.Set(i, i)
|
||||
i++
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func Benchmark_ListMap_Set(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
listMap.Set(i, i)
|
||||
}
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
i := 0
|
||||
for pb.Next() {
|
||||
listMap.Set(i, i)
|
||||
i++
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func Benchmark_TreeMap_Set(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
treeMap.Set(i, i)
|
||||
}
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
i := 0
|
||||
for pb.Next() {
|
||||
treeMap.Set(i, i)
|
||||
i++
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func Benchmark_HashMap_Get(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
hashMap.Get(i)
|
||||
}
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
i := 0
|
||||
for pb.Next() {
|
||||
hashMap.Get(i)
|
||||
i++
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func Benchmark_ListMap_Get(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
listMap.Get(i)
|
||||
}
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
i := 0
|
||||
for pb.Next() {
|
||||
listMap.Get(i)
|
||||
i++
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func Benchmark_TreeMap_Get(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
treeMap.Get(i)
|
||||
}
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
i := 0
|
||||
for pb.Next() {
|
||||
treeMap.Get(i)
|
||||
i++
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@ -9,99 +9,162 @@
|
||||
package gmap_test
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g/container/gmap"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"github.com/gogf/gf/g/container/gmap"
|
||||
)
|
||||
|
||||
var ififm = gmap.New()
|
||||
var iim = gmap.NewIntIntMap()
|
||||
var iifm = gmap.NewIntAnyMap()
|
||||
var ism = gmap.NewIntStrMap()
|
||||
var sim = gmap.NewStrIntMap()
|
||||
var sifm = gmap.NewStrAnyMap()
|
||||
var ssm = gmap.NewStrStrMap()
|
||||
var anyAnyMap = gmap.NewAnyAnyMap()
|
||||
var intIntMap = gmap.NewIntIntMap()
|
||||
var intAnyMap = gmap.NewIntAnyMap()
|
||||
var intStrMap = gmap.NewIntStrMap()
|
||||
var strIntMap = gmap.NewStrIntMap()
|
||||
var strAnyMap = gmap.NewStrAnyMap()
|
||||
var strStrMap = gmap.NewStrStrMap()
|
||||
|
||||
func Benchmark_IntIntMap_Set(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
iim.Set(i, i)
|
||||
}
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
i := 0
|
||||
for pb.Next() {
|
||||
intIntMap.Set(i, i)
|
||||
i++
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func Benchmark_IntAnyMap_Set(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
iifm.Set(i, i)
|
||||
}
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
i := 0
|
||||
for pb.Next() {
|
||||
intAnyMap.Set(i, i)
|
||||
i++
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func Benchmark_IntStrMap_Set(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
ism.Set(i, strconv.Itoa(i))
|
||||
}
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
i := 0
|
||||
for pb.Next() {
|
||||
intStrMap.Set(i, "123456789")
|
||||
i++
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func Benchmark_AnyAnyMap_Set(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
ififm.Set(i, i)
|
||||
}
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
i := 0
|
||||
for pb.Next() {
|
||||
anyAnyMap.Set(i, i)
|
||||
i++
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Note that there's additional performance cost for string conversion.
|
||||
func Benchmark_StrIntMap_Set(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
sim.Set(strconv.Itoa(i), i)
|
||||
}
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
i := 0
|
||||
for pb.Next() {
|
||||
strIntMap.Set(strconv.Itoa(i), i)
|
||||
i++
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Note that there's additional performance cost for string conversion.
|
||||
func Benchmark_StrAnyMap_Set(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
sifm.Set(strconv.Itoa(i), i)
|
||||
}
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
i := 0
|
||||
for pb.Next() {
|
||||
strAnyMap.Set(strconv.Itoa(i), i)
|
||||
i++
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Note that there's additional performance cost for string conversion.
|
||||
func Benchmark_StrStrMap_Set(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
ssm.Set(strconv.Itoa(i), strconv.Itoa(i))
|
||||
}
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
i := 0
|
||||
for pb.Next() {
|
||||
strStrMap.Set(strconv.Itoa(i), "123456789")
|
||||
i++
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func Benchmark_IntIntMap_Get(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
iim.Get(i)
|
||||
}
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
i := 0
|
||||
for pb.Next() {
|
||||
intIntMap.Get(i)
|
||||
i++
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func Benchmark_IntAnyMap_Get(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
iifm.Get(i)
|
||||
}
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
i := 0
|
||||
for pb.Next() {
|
||||
intAnyMap.Get(i)
|
||||
i++
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func Benchmark_IntStrMap_Get(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
ism.Get(i)
|
||||
}
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
i := 0
|
||||
for pb.Next() {
|
||||
intStrMap.Get(i)
|
||||
i++
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func Benchmark_AnyAnyMap_Get(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
ififm.Get(i)
|
||||
}
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
i := 0
|
||||
for pb.Next() {
|
||||
anyAnyMap.Get(i)
|
||||
i++
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Note that there's additional performance cost for string conversion.
|
||||
func Benchmark_StrIntMap_Get(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
sim.Get(strconv.Itoa(i))
|
||||
}
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
i := 0
|
||||
for pb.Next() {
|
||||
strIntMap.Get(strconv.Itoa(i))
|
||||
i++
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Note that there's additional performance cost for string conversion.
|
||||
func Benchmark_StrAnyMap_Get(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
sifm.Get(strconv.Itoa(i))
|
||||
}
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
i := 0
|
||||
for pb.Next() {
|
||||
strAnyMap.Get(strconv.Itoa(i))
|
||||
i++
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Note that there's additional performance cost for string conversion.
|
||||
func Benchmark_StrStrMap_Get(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
ssm.Get(strconv.Itoa(i))
|
||||
}
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
i := 0
|
||||
for pb.Next() {
|
||||
strStrMap.Get(strconv.Itoa(i))
|
||||
i++
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@ -9,46 +9,71 @@
|
||||
package gmap_test
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g/container/gmap"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"github.com/gogf/gf/g/container/gmap"
|
||||
)
|
||||
|
||||
var m1 = gmap.NewIntIntMap()
|
||||
var m2 = sync.Map{}
|
||||
var gm = gmap.NewIntIntMap()
|
||||
var sm = sync.Map{}
|
||||
|
||||
func BenchmarkGmapSet(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
m1.Set(i, i)
|
||||
}
|
||||
func Benchmark_GMapSet(b *testing.B) {
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
i := 0
|
||||
for pb.Next() {
|
||||
gm.Set(i, i)
|
||||
i++
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func BenchmarkSyncmapSet(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
m2.Store(i, i)
|
||||
}
|
||||
func Benchmark_SyncMapSet(b *testing.B) {
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
i := 0
|
||||
for pb.Next() {
|
||||
sm.Store(i, i)
|
||||
i++
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func BenchmarkGmapGet(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
m1.Get(i)
|
||||
}
|
||||
func Benchmark_GMapGet(b *testing.B) {
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
i := 0
|
||||
for pb.Next() {
|
||||
gm.Get(i)
|
||||
i++
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func BenchmarkSyncmapGet(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
m2.Load(i)
|
||||
}
|
||||
func Benchmark_SyncMapGet(b *testing.B) {
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
i := 0
|
||||
for pb.Next() {
|
||||
sm.Load(i)
|
||||
i++
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func BenchmarkGmapRemove(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
m1.Remove(i)
|
||||
}
|
||||
func Benchmark_GMapRemove(b *testing.B) {
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
i := 0
|
||||
for pb.Next() {
|
||||
gm.Remove(i)
|
||||
i++
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func BenchmarkSyncmapRmove(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
m2.Delete(i)
|
||||
}
|
||||
func Benchmark_SyncMapRmove(b *testing.B) {
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
i := 0
|
||||
for pb.Next() {
|
||||
sm.Delete(i)
|
||||
i++
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@ -9,9 +9,10 @@
|
||||
package gmap_test
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g/container/gmap"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"github.com/gogf/gf/g/container/gmap"
|
||||
)
|
||||
|
||||
var ififmUnsafe = gmap.New(true)
|
||||
@ -22,7 +23,7 @@ var simUnsafe = gmap.NewStrIntMap(true)
|
||||
var sifmUnsafe = gmap.NewStrAnyMap(true)
|
||||
var ssmUnsafe = gmap.NewStrStrMap(true)
|
||||
|
||||
// 写入性能测试
|
||||
// Writing benchmarks.
|
||||
|
||||
func Benchmark_Unsafe_IntIntMap_Set(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
@ -66,7 +67,7 @@ func Benchmark_Unsafe_StrStrMap_Set(b *testing.B) {
|
||||
}
|
||||
}
|
||||
|
||||
// 读取性能测试
|
||||
// Reading benchmarks.
|
||||
|
||||
func Benchmark_Unsafe_IntIntMap_Get(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
|
||||
@ -1,7 +1,14 @@
|
||||
// Copyright 2019 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,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gpool_test
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/gogf/gf/g"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@ -30,7 +37,7 @@ func Test_Gpool(t *testing.T) {
|
||||
//test won't be timeout
|
||||
v1, err1 := p1.Get()
|
||||
gtest.Assert(err1, nil)
|
||||
gtest.Assert(v1, 1)
|
||||
gtest.AssertIN(v1, g.Slice{1, 2})
|
||||
//test clear
|
||||
p1.Clear()
|
||||
gtest.Assert(p1.Size(), 0)
|
||||
@ -43,13 +50,12 @@ func Test_Gpool(t *testing.T) {
|
||||
p1.Put(4)
|
||||
v1, err1 = p1.Get()
|
||||
gtest.Assert(err1, nil)
|
||||
gtest.Assert(v1, 3)
|
||||
gtest.AssertIN(v1, g.Slice{3, 4})
|
||||
//test close
|
||||
p1.Close()
|
||||
v1, err1 = p1.Get()
|
||||
gtest.Assert(err1, nil)
|
||||
gtest.Assert(v1, "hello")
|
||||
|
||||
})
|
||||
|
||||
gtest.Case(t, func() {
|
||||
|
||||
@ -46,7 +46,7 @@ func New(limit ...int) *Queue {
|
||||
q := &Queue{
|
||||
closed: gtype.NewBool(),
|
||||
}
|
||||
if len(limit) > 0 {
|
||||
if len(limit) > 0 && limit[0] > 0 {
|
||||
q.limit = limit[0]
|
||||
q.C = make(chan interface{}, limit[0])
|
||||
} else {
|
||||
@ -87,7 +87,7 @@ func (q *Queue) startAsyncLoop() {
|
||||
<-q.events
|
||||
}
|
||||
}
|
||||
// It should be here to close q.C.
|
||||
// It should be here to close q.C if <q> is unlimited size.
|
||||
// It's the sender's responsibility to close channel when it should be closed.
|
||||
close(q.C)
|
||||
}
|
||||
@ -119,6 +119,9 @@ func (q *Queue) Close() {
|
||||
if q.events != nil {
|
||||
close(q.events)
|
||||
}
|
||||
if q.limit > 0 {
|
||||
close(q.C)
|
||||
}
|
||||
for i := 0; i < gDEFAULT_MAX_BATCH_SIZE; i++ {
|
||||
q.Pop()
|
||||
}
|
||||
|
||||
@ -4,127 +4,170 @@
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
// go test *.go -bench=".*"
|
||||
// go test *.go -bench=".*" -benchmem
|
||||
|
||||
package gset_test
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g/container/gset"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"github.com/gogf/gf/g/container/gset"
|
||||
)
|
||||
|
||||
var ints = gset.NewIntSet()
|
||||
var itfs = gset.NewSet()
|
||||
var strs = gset.NewStringSet()
|
||||
var intsUnsafe = gset.NewIntSet(true)
|
||||
var itfsUnsafe = gset.NewSet(true)
|
||||
var strsUnsafe = gset.NewStringSet(true)
|
||||
var intSet = gset.NewIntSet()
|
||||
var anySet = gset.NewSet()
|
||||
var strSet = gset.NewStringSet()
|
||||
var intSetUnsafe = gset.NewIntSet(true)
|
||||
var anySetUnsafe = gset.NewSet(true)
|
||||
var strSetUnsafe = gset.NewStringSet(true)
|
||||
|
||||
func Benchmark_IntSet_Add(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
ints.Add(i)
|
||||
}
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
i := 0
|
||||
for pb.Next() {
|
||||
intSet.Add(i)
|
||||
i++
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func Benchmark_IntSet_Contains(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
ints.Contains(i)
|
||||
}
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
i := 0
|
||||
for pb.Next() {
|
||||
intSet.Contains(i)
|
||||
i++
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func Benchmark_IntSet_Remove(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
ints.Remove(i)
|
||||
}
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
i := 0
|
||||
for pb.Next() {
|
||||
intSet.Remove(i)
|
||||
i++
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func Benchmark_Set_Add(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
itfs.Add(i)
|
||||
}
|
||||
func Benchmark_AnySet_Add(b *testing.B) {
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
i := 0
|
||||
for pb.Next() {
|
||||
anySet.Add(i)
|
||||
i++
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func Benchmark_Set_Contains(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
itfs.Contains(i)
|
||||
}
|
||||
func Benchmark_AnySet_Contains(b *testing.B) {
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
i := 0
|
||||
for pb.Next() {
|
||||
anySet.Contains(i)
|
||||
i++
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func Benchmark_Set_Remove(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
itfs.Remove(i)
|
||||
}
|
||||
func Benchmark_AnySet_Remove(b *testing.B) {
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
i := 0
|
||||
for pb.Next() {
|
||||
anySet.Remove(i)
|
||||
i++
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func Benchmark_StringSet_Add(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
strs.Add(strconv.Itoa(i))
|
||||
}
|
||||
// Note that there's additional performance cost for string conversion.
|
||||
func Benchmark_StrSet_Add(b *testing.B) {
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
i := 0
|
||||
for pb.Next() {
|
||||
strSet.Add(strconv.Itoa(i))
|
||||
i++
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func Benchmark_StringSet_Contains(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
strs.Contains(strconv.Itoa(i))
|
||||
}
|
||||
// Note that there's additional performance cost for string conversion.
|
||||
func Benchmark_StrSet_Contains(b *testing.B) {
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
i := 0
|
||||
for pb.Next() {
|
||||
strSet.Contains(strconv.Itoa(i))
|
||||
i++
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func Benchmark_StringSet_Remove(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
strs.Remove(strconv.Itoa(i))
|
||||
}
|
||||
// Note that there's additional performance cost for string conversion.
|
||||
func Benchmark_StrSet_Remove(b *testing.B) {
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
i := 0
|
||||
for pb.Next() {
|
||||
strSet.Remove(strconv.Itoa(i))
|
||||
i++
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func Benchmark_Unsafe_IntSet_Add(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
intsUnsafe.Add(i)
|
||||
intSetUnsafe.Add(i)
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_Unsafe_IntSet_Contains(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
intsUnsafe.Contains(i)
|
||||
intSetUnsafe.Contains(i)
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_Unsafe_IntSet_Remove(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
intsUnsafe.Remove(i)
|
||||
intSetUnsafe.Remove(i)
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_Unsafe_Set_Add(b *testing.B) {
|
||||
func Benchmark_Unsafe_AnySet_Add(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
itfsUnsafe.Add(i)
|
||||
anySetUnsafe.Add(i)
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_Unsafe_Set_Contains(b *testing.B) {
|
||||
func Benchmark_Unsafe_AnySet_Contains(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
itfsUnsafe.Contains(i)
|
||||
anySetUnsafe.Contains(i)
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_Unsafe_Set_Remove(b *testing.B) {
|
||||
func Benchmark_Unsafe_AnySet_Remove(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
itfsUnsafe.Remove(i)
|
||||
anySetUnsafe.Remove(i)
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_Unsafe_StringSet_Add(b *testing.B) {
|
||||
// Note that there's additional performance cost for string conversion.
|
||||
func Benchmark_Unsafe_StrSet_Add(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
strsUnsafe.Add(strconv.Itoa(i))
|
||||
strSetUnsafe.Add(strconv.Itoa(i))
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_Unsafe_StringSet_Contains(b *testing.B) {
|
||||
// Note that there's additional performance cost for string conversion.
|
||||
func Benchmark_Unsafe_StrSet_Contains(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
strsUnsafe.Contains(strconv.Itoa(i))
|
||||
strSetUnsafe.Contains(strconv.Itoa(i))
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_Unsafe_StringSet_Remove(b *testing.B) {
|
||||
// Note that there's additional performance cost for string conversion.
|
||||
func Benchmark_Unsafe_StrSet_Remove(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
strsUnsafe.Remove(strconv.Itoa(i))
|
||||
strSetUnsafe.Remove(strconv.Itoa(i))
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,7 +7,9 @@
|
||||
package gtree
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/gogf/gf/g/container/gvar"
|
||||
"github.com/gogf/gf/g/internal/rwmutex"
|
||||
)
|
||||
@ -704,3 +706,8 @@ func output(node *AVLTreeNode, prefix string, isTail bool, str *string) {
|
||||
output(node.children[0], newPrefix, true, str)
|
||||
}
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
func (tree *AVLTree) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(tree.Map())
|
||||
}
|
||||
|
||||
@ -8,10 +8,12 @@ package gtree
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/gogf/gf/g/container/gvar"
|
||||
"github.com/gogf/gf/g/internal/rwmutex"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// BTree holds elements of the B-tree.
|
||||
@ -845,3 +847,8 @@ func (tree *BTree) deleteChild(node *BTreeNode, index int) {
|
||||
node.Children[len(node.Children)-1] = nil
|
||||
node.Children = node.Children[:len(node.Children)-1]
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
func (tree *BTree) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(tree.Map())
|
||||
}
|
||||
|
||||
@ -7,7 +7,9 @@
|
||||
package gtree
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/gogf/gf/g/container/gvar"
|
||||
"github.com/gogf/gf/g/internal/rwmutex"
|
||||
)
|
||||
@ -833,3 +835,8 @@ func (tree *RedBlackTree) nodeColor(node *RedBlackTreeNode) color {
|
||||
}
|
||||
return node.color
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
func (tree *RedBlackTree) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(tree.Map())
|
||||
}
|
||||
|
||||
@ -8,10 +8,12 @@
|
||||
package gvar
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/g/container/gtype"
|
||||
"github.com/gogf/gf/g/os/gtime"
|
||||
"github.com/gogf/gf/g/util/gconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Var is an universal variable type.
|
||||
@ -34,6 +36,11 @@ func New(value interface{}, unsafe ...bool) *Var {
|
||||
return v
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
func (v *Var) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(v.Val())
|
||||
}
|
||||
|
||||
// Set sets <value> to <v>, and returns the old value.
|
||||
func (v *Var) Set(value interface{}) (old interface{}) {
|
||||
if v.safe {
|
||||
@ -58,33 +65,6 @@ func (v *Var) Interface() interface{} {
|
||||
return v.Val()
|
||||
}
|
||||
|
||||
// Time converts and returns <v> as time.Time.
|
||||
// The parameter <format> specifies the format of the time string using gtime,
|
||||
// eg: Y-m-d H:i:s.
|
||||
func (v *Var) Time(format ...string) time.Time {
|
||||
return gconv.Time(v.Val(), format...)
|
||||
}
|
||||
|
||||
// Duration converts and returns <v> as time.Duration.
|
||||
// If value of <v> is string, then it uses time.ParseDuration for conversion.
|
||||
func (v *Var) Duration() time.Duration {
|
||||
return gconv.Duration(v.Val())
|
||||
}
|
||||
|
||||
// GTime converts and returns <v> as *gtime.Time.
|
||||
// The parameter <format> specifies the format of the time string using gtime,
|
||||
// eg: Y-m-d H:i:s.
|
||||
func (v *Var) GTime(format ...string) *gtime.Time {
|
||||
return gconv.GTime(v.Val(), format...)
|
||||
}
|
||||
|
||||
// Struct maps value of <v> to <objPointer>.
|
||||
// The parameter <objPointer> should be a pointer to a struct instance.
|
||||
// The parameter <mapping> is used to specify the key-to-attribute mapping rules.
|
||||
func (v *Var) Struct(pointer interface{}, mapping ...map[string]string) error {
|
||||
return gconv.Struct(v.Val(), pointer, mapping...)
|
||||
}
|
||||
|
||||
// IsNil checks whether <v> is nil.
|
||||
func (v *Var) IsNil() bool {
|
||||
return v.Val() == nil
|
||||
@ -184,3 +164,57 @@ func (v *Var) Strings() []string {
|
||||
func (v *Var) Interfaces() []interface{} {
|
||||
return gconv.Interfaces(v.Val())
|
||||
}
|
||||
|
||||
// Time converts and returns <v> as time.Time.
|
||||
// The parameter <format> specifies the format of the time string using gtime,
|
||||
// eg: Y-m-d H:i:s.
|
||||
func (v *Var) Time(format ...string) time.Time {
|
||||
return gconv.Time(v.Val(), format...)
|
||||
}
|
||||
|
||||
// Duration converts and returns <v> as time.Duration.
|
||||
// If value of <v> is string, then it uses time.ParseDuration for conversion.
|
||||
func (v *Var) Duration() time.Duration {
|
||||
return gconv.Duration(v.Val())
|
||||
}
|
||||
|
||||
// GTime converts and returns <v> as *gtime.Time.
|
||||
// The parameter <format> specifies the format of the time string using gtime,
|
||||
// eg: Y-m-d H:i:s.
|
||||
func (v *Var) GTime(format ...string) *gtime.Time {
|
||||
return gconv.GTime(v.Val(), format...)
|
||||
}
|
||||
|
||||
// Map converts <v> to map[string]interface{}.
|
||||
func (v *Var) Map(tags ...string) map[string]interface{} {
|
||||
return gconv.Map(v.Val(), tags...)
|
||||
}
|
||||
|
||||
// MapDeep converts <v> to map[string]interface{} recursively.
|
||||
func (v *Var) MapDeep(tags ...string) map[string]interface{} {
|
||||
return gconv.MapDeep(v.Val(), tags...)
|
||||
}
|
||||
|
||||
// Struct maps value of <v> to <pointer>.
|
||||
// The parameter <pointer> should be a pointer to a struct instance.
|
||||
// The parameter <mapping> is used to specify the key-to-attribute mapping rules.
|
||||
func (v *Var) Struct(pointer interface{}, mapping ...map[string]string) error {
|
||||
return gconv.Struct(v.Val(), pointer, mapping...)
|
||||
}
|
||||
|
||||
// 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.
|
||||
func (v *Var) StructDeep(pointer interface{}, mapping ...map[string]string) error {
|
||||
return gconv.StructDeep(v.Val(), pointer, mapping...)
|
||||
}
|
||||
|
||||
// Structs converts <v> to given struct slice.
|
||||
func (v *Var) Structs(pointer interface{}, mapping ...map[string]string) (err error) {
|
||||
return gconv.Structs(v.Val(), pointer, mapping...)
|
||||
}
|
||||
|
||||
// StructsDeep converts <v> to given struct slice recursively.
|
||||
func (v *Var) StructsDeep(pointer interface{}, mapping ...map[string]string) (err error) {
|
||||
return gconv.StructsDeep(v.Val(), pointer, mapping...)
|
||||
}
|
||||
|
||||
@ -13,7 +13,7 @@ import (
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"github.com/gogf/gf/g/internal/errors"
|
||||
"github.com/gogf/gf/g/errors/gerror"
|
||||
"github.com/gogf/gf/g/util/gconv"
|
||||
)
|
||||
|
||||
@ -40,7 +40,7 @@ func EncryptFile(path string) (encrypt string, err error) {
|
||||
return "", err
|
||||
}
|
||||
defer func() {
|
||||
err = errors.Wrap(f.Close(), "file closing error")
|
||||
err = gerror.Wrap(f.Close(), "file closing error")
|
||||
}()
|
||||
h := md5.New()
|
||||
_, err = io.Copy(h, f)
|
||||
|
||||
@ -13,7 +13,7 @@ import (
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"github.com/gogf/gf/g/internal/errors"
|
||||
"github.com/gogf/gf/g/errors/gerror"
|
||||
"github.com/gogf/gf/g/util/gconv"
|
||||
)
|
||||
|
||||
@ -37,7 +37,7 @@ func EncryptFile(path string) (encrypt string, err error) {
|
||||
return "", err
|
||||
}
|
||||
defer func() {
|
||||
err = errors.Wrap(f.Close(), "file closing error")
|
||||
err = gerror.Wrap(f.Close(), "file closing error")
|
||||
}()
|
||||
h := sha1.New()
|
||||
_, err = io.Copy(h, f)
|
||||
|
||||
@ -77,9 +77,9 @@ type DB interface {
|
||||
Update(table string, data interface{}, condition interface{}, args ...interface{}) (sql.Result, error)
|
||||
Delete(table string, condition interface{}, args ...interface{}) (sql.Result, error)
|
||||
|
||||
// 创建链式操作对象(Table为From的别名)
|
||||
Table(tables string) *Model
|
||||
// 创建链式操作对象
|
||||
From(tables string) *Model
|
||||
Table(tables string) *Model
|
||||
|
||||
// 设置管理
|
||||
SetDebug(debug bool)
|
||||
@ -87,16 +87,19 @@ type DB interface {
|
||||
GetQueriedSqls() []*Sql
|
||||
GetLastSql() *Sql
|
||||
PrintQueriedSqls()
|
||||
SetMaxIdleConns(n int)
|
||||
SetMaxOpenConns(n int)
|
||||
SetConnMaxLifetime(n int)
|
||||
SetMaxIdleConnCount(n int)
|
||||
SetMaxOpenConnCount(n int)
|
||||
SetMaxConnLifetime(n int)
|
||||
|
||||
// 内部方法接口
|
||||
getCache() *gcache.Cache
|
||||
getChars() (charLeft string, charRight string)
|
||||
getDebug() bool
|
||||
quoteWord(s string) string
|
||||
setSchema(sqlDb *sql.DB, schema string) error
|
||||
filterFields(table string, data map[string]interface{}) map[string]interface{}
|
||||
convertValue(fieldValue interface{}, fieldType string) interface{}
|
||||
formatWhere(where interface{}, args []interface{}) (newWhere string, newArgs []interface{})
|
||||
convertValue(fieldValue []byte, fieldType string) interface{}
|
||||
getTableFields(table string) (map[string]string, error)
|
||||
rowsToResult(rows *sql.Rows) (Result, error)
|
||||
handleSqlBeforeExec(sql string) string
|
||||
@ -248,9 +251,9 @@ func getConfigNodeByGroup(group string, master bool) (*ConfigNode, error) {
|
||||
slaveList = masterList
|
||||
}
|
||||
if master {
|
||||
return getConfigNodeByPriority(masterList), nil
|
||||
return getConfigNodeByWeight(masterList), nil
|
||||
} else {
|
||||
return getConfigNodeByPriority(slaveList), nil
|
||||
return getConfigNodeByWeight(slaveList), nil
|
||||
}
|
||||
} else {
|
||||
return nil, errors.New(fmt.Sprintf("empty database configuration for item name '%s'", group))
|
||||
@ -263,19 +266,19 @@ func getConfigNodeByGroup(group string, master bool) (*ConfigNode, error) {
|
||||
// 2、那么节点1的权重范围为[0, 99],节点2的权重范围为[100, 199],比例为1:1;
|
||||
// 3、假如计算出的随机数为99;
|
||||
// 4、那么选择的配置为节点1;
|
||||
func getConfigNodeByPriority(cg ConfigGroup) *ConfigNode {
|
||||
func getConfigNodeByWeight(cg ConfigGroup) *ConfigNode {
|
||||
if len(cg) < 2 {
|
||||
return &cg[0]
|
||||
}
|
||||
var total int
|
||||
for i := 0; i < len(cg); i++ {
|
||||
total += cg[i].Priority * 100
|
||||
total += cg[i].Weight * 100
|
||||
}
|
||||
// 如果total为0表示所有连接都没有配置priority属性,那么默认都是1
|
||||
if total == 0 {
|
||||
for i := 0; i < len(cg); i++ {
|
||||
cg[i].Priority = 1
|
||||
total += cg[i].Priority * 100
|
||||
cg[i].Weight = 1
|
||||
total += cg[i].Weight * 100
|
||||
}
|
||||
}
|
||||
// 不能取到末尾的边界点
|
||||
@ -286,7 +289,7 @@ func getConfigNodeByPriority(cg ConfigGroup) *ConfigNode {
|
||||
min := 0
|
||||
max := 0
|
||||
for i := 0; i < len(cg); i++ {
|
||||
max = min + cg[i].Priority*100
|
||||
max = min + cg[i].Weight*100
|
||||
//fmt.Printf("r: %d, min: %d, max: %d\n", r, min, max)
|
||||
if r >= min && r < max {
|
||||
return &cg[i]
|
||||
@ -308,12 +311,14 @@ func (bs *dbBase) getSqlDb(master bool) (sqlDb *sql.DB, err error) {
|
||||
if node.Charset == "" {
|
||||
node.Charset = "utf8"
|
||||
}
|
||||
// 缓存连接对象(该对象其实是一个连接池对象)
|
||||
v := bs.cache.GetOrSetFuncLock(node.String(), func() interface{} {
|
||||
sqlDb, err = bs.db.Open(node)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 接口对象可能会覆盖这些连接参数,所以这里优先判断有误设置连接池属性。
|
||||
// 若无设置则使用配置节点的连接池参数
|
||||
if n := bs.maxIdleConnCount.Val(); n > 0 {
|
||||
sqlDb.SetMaxIdleConns(n)
|
||||
} else if node.MaxIdleConnCount > 0 {
|
||||
@ -336,9 +341,15 @@ func (bs *dbBase) getSqlDb(master bool) (sqlDb *sql.DB, err error) {
|
||||
if v != nil && sqlDb == nil {
|
||||
sqlDb = v.(*sql.DB)
|
||||
}
|
||||
// 是否开启调试模式
|
||||
if node.Debug {
|
||||
bs.db.SetDebug(node.Debug)
|
||||
}
|
||||
// 是否手动选择数据库
|
||||
if v := bs.schema.Val(); v != "" {
|
||||
sqlDb.Exec("USE " + v)
|
||||
if e := bs.db.setSchema(sqlDb, v); e != nil {
|
||||
err = e
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@ -8,10 +8,13 @@
|
||||
package gdb
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/g/text/gstr"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/gogf/gf/g/container/gvar"
|
||||
@ -22,7 +25,13 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
gDEFAULT_DEBUG_SQL_LENGTH = 1000 // 默认调试模式下记录的SQL条数
|
||||
// 默认调试模式下记录的SQL条数
|
||||
gDEFAULT_DEBUG_SQL_LENGTH = 1000
|
||||
)
|
||||
|
||||
var (
|
||||
// 用于可转义的单词的识别正则对象
|
||||
wordReg = regexp.MustCompile(`^[a-zA-Z0-9\-_]+$`)
|
||||
)
|
||||
|
||||
// 获取最近一条执行的sql
|
||||
@ -78,6 +87,7 @@ func (bs *dbBase) Query(query string, args ...interface{}) (rows *sql.Rows, err
|
||||
|
||||
// 数据库sql查询操作,主要执行查询
|
||||
func (bs *dbBase) doQuery(link dbLink, query string, args ...interface{}) (rows *sql.Rows, err error) {
|
||||
query, args = formatQuery(query, args)
|
||||
query = bs.db.handleSqlBeforeExec(query)
|
||||
if bs.db.getDebug() {
|
||||
mTime1 := gtime.Millisecond()
|
||||
@ -114,6 +124,7 @@ func (bs *dbBase) Exec(query string, args ...interface{}) (result sql.Result, er
|
||||
|
||||
// 执行一条sql,并返回执行情况,主要用于非查询操作
|
||||
func (bs *dbBase) doExec(link dbLink, query string, args ...interface{}) (result sql.Result, err error) {
|
||||
query, args = formatQuery(query, args)
|
||||
query = bs.db.handleSqlBeforeExec(query)
|
||||
if bs.db.getDebug() {
|
||||
mTime1 := gtime.Millisecond()
|
||||
@ -178,43 +189,40 @@ func (bs *dbBase) GetOne(query string, args ...interface{}) (Record, error) {
|
||||
}
|
||||
|
||||
// 数据库查询,查询单条记录,自动映射数据到给定的struct对象中
|
||||
func (bs *dbBase) GetStruct(objPointer interface{}, query string, args ...interface{}) error {
|
||||
func (bs *dbBase) GetStruct(pointer interface{}, query string, args ...interface{}) error {
|
||||
one, err := bs.GetOne(query, args...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return one.ToStruct(objPointer)
|
||||
return one.ToStruct(pointer)
|
||||
}
|
||||
|
||||
// 数据库查询,查询多条记录,并自动转换为指定的slice对象, 如: []struct/[]*struct。
|
||||
func (bs *dbBase) GetStructs(objPointerSlice interface{}, query string, args ...interface{}) error {
|
||||
func (bs *dbBase) GetStructs(pointer interface{}, query string, args ...interface{}) error {
|
||||
all, err := bs.GetAll(query, args...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return all.ToStructs(objPointerSlice)
|
||||
return all.ToStructs(pointer)
|
||||
}
|
||||
|
||||
// 将结果转换为指定的struct/*struct/[]struct/[]*struct,
|
||||
// 参数应该为指针类型,否则返回失败。
|
||||
// 该方法自动识别参数类型,调用Struct/Structs方法。
|
||||
func (bs *dbBase) GetScan(objPointer interface{}, query string, args ...interface{}) error {
|
||||
t := reflect.TypeOf(objPointer)
|
||||
func (bs *dbBase) GetScan(pointer interface{}, query string, args ...interface{}) error {
|
||||
t := reflect.TypeOf(pointer)
|
||||
k := t.Kind()
|
||||
if k != reflect.Ptr {
|
||||
return fmt.Errorf("params should be type of pointer, but got: %v", k)
|
||||
}
|
||||
k = t.Elem().Kind()
|
||||
switch k {
|
||||
case reflect.Array:
|
||||
case reflect.Slice:
|
||||
return bs.db.GetStructs(objPointer, query, args...)
|
||||
case reflect.Array, reflect.Slice:
|
||||
return bs.db.GetStructs(pointer, query, args...)
|
||||
case reflect.Struct:
|
||||
return bs.db.GetStruct(objPointer, query, args...)
|
||||
default:
|
||||
return fmt.Errorf("element type should be type of struct/slice, unsupported: %v", k)
|
||||
return bs.db.GetStruct(pointer, query, args...)
|
||||
}
|
||||
return nil
|
||||
return fmt.Errorf("element type should be type of struct/slice, unsupported: %v", k)
|
||||
}
|
||||
|
||||
// 数据库查询,获取查询字段值
|
||||
@ -311,6 +319,7 @@ func (bs *dbBase) doInsert(link dbLink, table string, data interface{}, option i
|
||||
var values []string
|
||||
var params []interface{}
|
||||
var dataMap Map
|
||||
table = bs.db.quoteWord(table)
|
||||
// 使用反射判断data数据类型,如果为slice类型,那么自动转为批量操作
|
||||
rv := reflect.ValueOf(data)
|
||||
kind := rv.Kind()
|
||||
@ -339,16 +348,16 @@ func (bs *dbBase) doInsert(link dbLink, table string, data interface{}, option i
|
||||
operation := getInsertOperationByOption(option)
|
||||
updateStr := ""
|
||||
if option == OPTION_SAVE {
|
||||
var updates []string
|
||||
for k, _ := range dataMap {
|
||||
updates = append(updates,
|
||||
fmt.Sprintf("%s%s%s=VALUES(%s%s%s)",
|
||||
charL, k, charR,
|
||||
charL, k, charR,
|
||||
),
|
||||
if len(updateStr) > 0 {
|
||||
updateStr += ","
|
||||
}
|
||||
updateStr += fmt.Sprintf("%s%s%s=VALUES(%s%s%s)",
|
||||
charL, k, charR,
|
||||
charL, k, charR,
|
||||
)
|
||||
}
|
||||
updateStr = fmt.Sprintf("ON DUPLICATE KEY UPDATE %s", strings.Join(updates, ","))
|
||||
updateStr = fmt.Sprintf(" ON DUPLICATE KEY UPDATE %s", updateStr)
|
||||
}
|
||||
if link == nil {
|
||||
if link, err = bs.db.Master(); err != nil {
|
||||
@ -381,6 +390,7 @@ func (bs *dbBase) doBatchInsert(link dbLink, table string, list interface{}, opt
|
||||
var keys []string
|
||||
var values []string
|
||||
var params []interface{}
|
||||
table = bs.db.quoteWord(table)
|
||||
listMap := (List)(nil)
|
||||
switch v := list.(type) {
|
||||
case Result:
|
||||
@ -432,22 +442,22 @@ func (bs *dbBase) doBatchInsert(link dbLink, table string, list interface{}, opt
|
||||
}
|
||||
batchResult := new(batchSqlResult)
|
||||
charL, charR := bs.db.getChars()
|
||||
keyStr := charL + strings.Join(keys, charL+","+charR) + charR
|
||||
keyStr := charL + strings.Join(keys, charR+","+charL) + charR
|
||||
valueHolderStr := "(" + strings.Join(holders, ",") + ")"
|
||||
// 操作判断
|
||||
operation := getInsertOperationByOption(option)
|
||||
updateStr := ""
|
||||
if option == OPTION_SAVE {
|
||||
var updates []string
|
||||
for _, k := range keys {
|
||||
updates = append(updates,
|
||||
fmt.Sprintf("%s%s%s=VALUES(%s%s%s)",
|
||||
charL, k, charR,
|
||||
charL, k, charR,
|
||||
),
|
||||
if len(updateStr) > 0 {
|
||||
updateStr += ","
|
||||
}
|
||||
updateStr += fmt.Sprintf("%s%s%s=VALUES(%s%s%s)",
|
||||
charL, k, charR,
|
||||
charL, k, charR,
|
||||
)
|
||||
}
|
||||
updateStr = fmt.Sprintf(" ON DUPLICATE KEY UPDATE %s", strings.Join(updates, ","))
|
||||
updateStr = fmt.Sprintf(" ON DUPLICATE KEY UPDATE %s", updateStr)
|
||||
}
|
||||
// 构造批量写入数据格式(注意map的遍历是无序的)
|
||||
batchNum := gDEFAULT_BATCH_NUM
|
||||
@ -499,7 +509,7 @@ func (bs *dbBase) doBatchInsert(link dbLink, table string, list interface{}, opt
|
||||
// CURD操作:数据更新,统一采用sql预处理。
|
||||
// data参数支持string/map/struct/*struct类型。
|
||||
func (bs *dbBase) Update(table string, data interface{}, condition interface{}, args ...interface{}) (sql.Result, error) {
|
||||
newWhere, newArgs := formatWhere(condition, args)
|
||||
newWhere, newArgs := bs.db.formatWhere(condition, args)
|
||||
if newWhere != "" {
|
||||
newWhere = " WHERE " + newWhere
|
||||
}
|
||||
@ -509,8 +519,8 @@ func (bs *dbBase) Update(table string, data interface{}, condition interface{},
|
||||
// CURD操作:数据更新,统一采用sql预处理。
|
||||
// data参数支持string/map/struct/*struct类型类型。
|
||||
func (bs *dbBase) doUpdate(link dbLink, table string, data interface{}, condition string, args ...interface{}) (result sql.Result, err error) {
|
||||
table = bs.db.quoteWord(table)
|
||||
updates := ""
|
||||
charL, charR := bs.db.getChars()
|
||||
// 使用反射进行类型判断
|
||||
rv := reflect.ValueOf(data)
|
||||
kind := rv.Kind()
|
||||
@ -525,7 +535,7 @@ func (bs *dbBase) doUpdate(link dbLink, table string, data interface{}, conditio
|
||||
case reflect.Struct:
|
||||
var fields []string
|
||||
for k, v := range structToMap(data) {
|
||||
fields = append(fields, fmt.Sprintf("%s%s%s=?", charL, k, charR))
|
||||
fields = append(fields, bs.db.quoteWord(k)+"=?")
|
||||
params = append(params, convertParam(v))
|
||||
}
|
||||
updates = strings.Join(fields, ",")
|
||||
@ -546,7 +556,7 @@ func (bs *dbBase) doUpdate(link dbLink, table string, data interface{}, conditio
|
||||
|
||||
// CURD操作:删除数据
|
||||
func (bs *dbBase) Delete(table string, condition interface{}, args ...interface{}) (result sql.Result, err error) {
|
||||
newWhere, newArgs := formatWhere(condition, args)
|
||||
newWhere, newArgs := bs.db.formatWhere(condition, args)
|
||||
if newWhere != "" {
|
||||
newWhere = " WHERE " + newWhere
|
||||
}
|
||||
@ -560,6 +570,7 @@ func (bs *dbBase) doDelete(link dbLink, table string, condition string, args ...
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
table = bs.db.quoteWord(table)
|
||||
return bs.db.doExec(link, fmt.Sprintf("DELETE FROM %s%s", table, condition), args...)
|
||||
}
|
||||
|
||||
@ -570,6 +581,9 @@ func (bs *dbBase) getCache() *gcache.Cache {
|
||||
|
||||
// 将数据查询的列表数据*sql.Rows转换为Result类型
|
||||
func (bs *dbBase) rowsToResult(rows *sql.Rows) (Result, error) {
|
||||
if !rows.Next() {
|
||||
return nil, sql.ErrNoRows
|
||||
}
|
||||
// 列信息列表, 名称与类型
|
||||
columnTypes, err := rows.ColumnTypes()
|
||||
if err != nil {
|
||||
@ -588,7 +602,7 @@ func (bs *dbBase) rowsToResult(rows *sql.Rows) (Result, error) {
|
||||
for i := range values {
|
||||
scanArgs[i] = &values[i]
|
||||
}
|
||||
for rows.Next() {
|
||||
for {
|
||||
if err := rows.Scan(scanArgs...); err != nil {
|
||||
return records, err
|
||||
}
|
||||
@ -602,10 +616,112 @@ func (bs *dbBase) rowsToResult(rows *sql.Rows) (Result, error) {
|
||||
// 由于 sql.RawBytes 是slice类型, 这里必须使用值复制
|
||||
v := make([]byte, len(column))
|
||||
copy(v, column)
|
||||
//fmt.Println(columns[i], types[i], string(v), v, bs.db.convertValue(v, types[i]))
|
||||
row[columns[i]] = gvar.New(bs.db.convertValue(v, types[i]), true)
|
||||
}
|
||||
}
|
||||
records = append(records, row)
|
||||
if !rows.Next() {
|
||||
break
|
||||
}
|
||||
}
|
||||
return records, nil
|
||||
}
|
||||
|
||||
// 格式化Where查询条件。
|
||||
func (bs *dbBase) formatWhere(where interface{}, args []interface{}) (newWhere string, newArgs []interface{}) {
|
||||
// 条件字符串处理
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
// 使用反射进行类型判断
|
||||
rv := reflect.ValueOf(where)
|
||||
kind := rv.Kind()
|
||||
if kind == reflect.Ptr {
|
||||
rv = rv.Elem()
|
||||
kind = rv.Kind()
|
||||
}
|
||||
switch kind {
|
||||
// map/struct类型
|
||||
case reflect.Map:
|
||||
fallthrough
|
||||
case reflect.Struct:
|
||||
for key, value := range structToMap(where) {
|
||||
// 字段安全符号判断
|
||||
key = bs.db.quoteWord(key)
|
||||
if buffer.Len() > 0 {
|
||||
buffer.WriteString(" AND ")
|
||||
}
|
||||
// 支持slice键值/属性,如果只有一个?占位符号,那么作为IN查询,否则打散作为多个查询参数
|
||||
rv := reflect.ValueOf(value)
|
||||
switch rv.Kind() {
|
||||
case reflect.Slice:
|
||||
fallthrough
|
||||
case reflect.Array:
|
||||
count := gstr.Count(key, "?")
|
||||
if count == 0 {
|
||||
buffer.WriteString(key + " IN(?)")
|
||||
newArgs = append(newArgs, value)
|
||||
} else if count != rv.Len() {
|
||||
buffer.WriteString(key)
|
||||
newArgs = append(newArgs, value)
|
||||
} else {
|
||||
buffer.WriteString(key)
|
||||
// 如果键名/属性名称中带有多个?占位符号,那么将参数打散
|
||||
newArgs = append(newArgs, gconv.Interfaces(value)...)
|
||||
}
|
||||
default:
|
||||
if value == nil {
|
||||
buffer.WriteString(key)
|
||||
} else {
|
||||
// 支持key带操作符号
|
||||
if gstr.Pos(key, "?") == -1 {
|
||||
if gstr.Pos(key, "<") == -1 && gstr.Pos(key, ">") == -1 && gstr.Pos(key, "=") == -1 {
|
||||
buffer.WriteString(key + "=?")
|
||||
} else {
|
||||
buffer.WriteString(key + "?")
|
||||
}
|
||||
} else {
|
||||
buffer.WriteString(key)
|
||||
}
|
||||
newArgs = append(newArgs, value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
buffer.WriteString(gconv.String(where))
|
||||
}
|
||||
// 没有任何条件查询参数,直接返回
|
||||
if buffer.Len() == 0 {
|
||||
return "", args
|
||||
}
|
||||
newArgs = append(newArgs, args...)
|
||||
newWhere = buffer.String()
|
||||
// 查询条件参数处理,主要处理slice参数类型
|
||||
if len(newArgs) > 0 {
|
||||
// 支持例如 Where/And/Or("uid", 1) 这种格式
|
||||
if gstr.Pos(newWhere, "?") == -1 {
|
||||
if gstr.Pos(newWhere, "<") == -1 && gstr.Pos(newWhere, ">") == -1 && gstr.Pos(newWhere, "=") == -1 {
|
||||
newWhere += "=?"
|
||||
} else {
|
||||
newWhere += "?"
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// 使用关键字操作符转义给定字符串。
|
||||
// 如果给定的字符串不为单词,那么不转义,直接返回该字符串。
|
||||
func (bs *dbBase) quoteWord(s string) string {
|
||||
charLeft, charRight := bs.db.getChars()
|
||||
if wordReg.MatchString(s) && !gstr.ContainsAny(s, charLeft+charRight) {
|
||||
return charLeft + s + charRight
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// 动态切换数据库
|
||||
func (bs *dbBase) setSchema(sqlDb *sql.DB, schema string) error {
|
||||
_, err := sqlDb.Exec("USE " + schema)
|
||||
return err
|
||||
}
|
||||
|
||||
@ -3,14 +3,14 @@
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
// 对常用关系数据库的封装管理包
|
||||
|
||||
package gdb
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/g/container/gring"
|
||||
"sync"
|
||||
|
||||
"github.com/gogf/gf/g/container/gring"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -30,10 +30,11 @@ type ConfigNode struct {
|
||||
User string // 账号
|
||||
Pass string // 密码
|
||||
Name string // 数据库名称
|
||||
Type string // 数据库类型:mysql, sqlite, mssql, pgsql, oracle(目前仅支持mysql)
|
||||
Type string // 数据库类型:mysql, sqlite, mssql, pgsql, oracle
|
||||
Role string // (可选,默认为master)数据库的角色,用于主从操作分离,至少需要有一个master,参数值:master, slave
|
||||
Debug bool // (可选)开启调试模式
|
||||
Weight int // (可选)用于负载均衡的权重计算,当集群中只有一个节点时,权重没有任何意义
|
||||
Charset string // (可选,默认为 utf8)编码,默认为 utf8
|
||||
Priority int // (可选)用于负载均衡的权重计算,当集群中只有一个节点时,权重没有任何意义
|
||||
LinkInfo string // (可选)自定义链接信息,当该字段被设置值时,以上链接字段(Host,Port,User,Pass,Name)将失效(该字段是一个扩展功能)
|
||||
MaxIdleConnCount int // (可选)连接池最大限制的连接数
|
||||
MaxOpenConnCount int // (可选)连接池最大打开的连接数
|
||||
@ -47,37 +48,6 @@ var configs struct {
|
||||
defaultGroup string // 默认数据库分组名称
|
||||
}
|
||||
|
||||
// 数据库集群配置示例,支持主从处理,多数据库集群支持
|
||||
/*
|
||||
var DatabaseConfiguration = Config {
|
||||
// 数据库集群配置名称
|
||||
"default" : ConfigGroup {
|
||||
{
|
||||
Host : "192.168.1.100",
|
||||
Port : "3306",
|
||||
User : "root",
|
||||
Pass : "123456",
|
||||
Name : "test",
|
||||
Type : "mysql",
|
||||
Role : "master",
|
||||
Charset : "utf8",
|
||||
Priority : 100,
|
||||
},
|
||||
{
|
||||
Host : "192.168.1.101",
|
||||
Port : "3306",
|
||||
User : "root",
|
||||
Pass : "123456",
|
||||
Name : "test",
|
||||
Type : "mysql",
|
||||
Role : "slave",
|
||||
Charset : "utf8",
|
||||
Priority : 100,
|
||||
},
|
||||
},
|
||||
}
|
||||
*/
|
||||
|
||||
// 包初始化
|
||||
func init() {
|
||||
configs.config = make(Config)
|
||||
@ -136,24 +106,24 @@ func SetDefaultGroup(name string) {
|
||||
// 获取默认链接的数据库链接配置项(默认是 default)
|
||||
func GetDefaultGroup() string {
|
||||
defer instances.Clear()
|
||||
configs.Lock()
|
||||
defer configs.Unlock()
|
||||
configs.RLock()
|
||||
defer configs.RUnlock()
|
||||
return configs.defaultGroup
|
||||
}
|
||||
|
||||
// 设置数据库连接池中空闲链接的大小
|
||||
func (bs *dbBase) SetMaxIdleConns(n int) {
|
||||
func (bs *dbBase) SetMaxIdleConnCount(n int) {
|
||||
bs.maxIdleConnCount.Set(n)
|
||||
}
|
||||
|
||||
// 设置数据库连接池最大打开的链接数量
|
||||
func (bs *dbBase) SetMaxOpenConns(n int) {
|
||||
func (bs *dbBase) SetMaxOpenConnCount(n int) {
|
||||
bs.maxOpenConnCount.Set(n)
|
||||
}
|
||||
|
||||
// 设置数据库连接可重复利用的时间,超过该时间则被关闭废弃
|
||||
// 如果 d <= 0 表示该链接会一直重复利用
|
||||
func (bs *dbBase) SetConnMaxLifetime(n int) {
|
||||
func (bs *dbBase) SetMaxConnLifetime(n int) {
|
||||
bs.maxConnLifetime.Set(n)
|
||||
}
|
||||
|
||||
@ -162,14 +132,17 @@ func (node *ConfigNode) String() string {
|
||||
if node.LinkInfo != "" {
|
||||
return node.LinkInfo
|
||||
}
|
||||
return fmt.Sprintf(`%s@%s:%s,%s,%s,%s,%s,%d-%d-%d`, node.User, node.Host, node.Port,
|
||||
node.Name, node.Type, node.Role, node.Charset,
|
||||
return fmt.Sprintf(`%s@%s:%s,%s,%s,%s,%s,%v,%d-%d-%d`, node.User, node.Host, node.Port,
|
||||
node.Name, node.Type, node.Role, node.Charset, node.Debug,
|
||||
node.MaxIdleConnCount, node.MaxOpenConnCount, node.MaxConnLifetime,
|
||||
)
|
||||
}
|
||||
|
||||
// 是否开启调试服务
|
||||
func (bs *dbBase) SetDebug(debug bool) {
|
||||
if bs.debug.Val() == debug {
|
||||
return
|
||||
}
|
||||
bs.debug.Set(debug)
|
||||
if debug && bs.sqls == nil {
|
||||
bs.sqls = gring.New(gDEFAULT_DEBUG_SQL_LENGTH)
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
package gdb
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
@ -26,75 +26,14 @@ type apiString interface {
|
||||
String() string
|
||||
}
|
||||
|
||||
// 格式化Where查询条件
|
||||
func formatWhere(where interface{}, args []interface{}) (newWhere string, newArgs []interface{}) {
|
||||
// 条件字符串处理
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
// 使用反射进行类型判断
|
||||
rv := reflect.ValueOf(where)
|
||||
kind := rv.Kind()
|
||||
if kind == reflect.Ptr {
|
||||
rv = rv.Elem()
|
||||
kind = rv.Kind()
|
||||
}
|
||||
tmpArgs := []interface{}(nil)
|
||||
switch kind {
|
||||
// map/struct类型
|
||||
case reflect.Map:
|
||||
fallthrough
|
||||
case reflect.Struct:
|
||||
for key, value := range structToMap(where) {
|
||||
if buffer.Len() > 0 {
|
||||
buffer.WriteString(" AND ")
|
||||
}
|
||||
// 支持slice键值/属性,如果只有一个?占位符号,那么作为IN查询,否则打散作为多个查询参数
|
||||
rv := reflect.ValueOf(value)
|
||||
switch rv.Kind() {
|
||||
case reflect.Slice:
|
||||
fallthrough
|
||||
case reflect.Array:
|
||||
count := gstr.Count(key, "?")
|
||||
if count == 0 {
|
||||
buffer.WriteString(key + " IN(?)")
|
||||
tmpArgs = append(tmpArgs, value)
|
||||
} else if count != rv.Len() {
|
||||
buffer.WriteString(key)
|
||||
tmpArgs = append(tmpArgs, value)
|
||||
} else {
|
||||
buffer.WriteString(key)
|
||||
// 如果键名/属性名称中带有多个?占位符号,那么将参数打散
|
||||
tmpArgs = append(tmpArgs, gconv.Interfaces(value)...)
|
||||
}
|
||||
default:
|
||||
if value == nil {
|
||||
buffer.WriteString(key)
|
||||
} else {
|
||||
if gstr.Pos(key, "?") == -1 {
|
||||
if gstr.Pos(key, "<") == -1 && gstr.Pos(key, ">") == -1 && gstr.Pos(key, "=") == -1 {
|
||||
buffer.WriteString(key + "=?")
|
||||
} else {
|
||||
buffer.WriteString(key + "?")
|
||||
}
|
||||
} else {
|
||||
buffer.WriteString(key)
|
||||
}
|
||||
tmpArgs = append(tmpArgs, value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
buffer.WriteString(gconv.String(where))
|
||||
}
|
||||
// 没有任何条件查询参数,直接返回
|
||||
if buffer.Len() == 0 {
|
||||
return "", args
|
||||
}
|
||||
newWhere = buffer.String()
|
||||
tmpArgs = append(tmpArgs, args...)
|
||||
// 格式化SQL语句。
|
||||
// 1. 支持参数只传一个slice;
|
||||
// 2. 支持占位符号数量自动扩展;
|
||||
func formatQuery(query string, args []interface{}) (newQuery string, newArgs []interface{}) {
|
||||
newQuery = query
|
||||
// 查询条件参数处理,主要处理slice参数类型
|
||||
if len(tmpArgs) > 0 {
|
||||
for index, arg := range tmpArgs {
|
||||
if len(args) > 0 {
|
||||
for index, arg := range args {
|
||||
rv := reflect.ValueOf(arg)
|
||||
kind := rv.Kind()
|
||||
if kind == reflect.Ptr {
|
||||
@ -102,17 +41,24 @@ func formatWhere(where interface{}, args []interface{}) (newWhere string, newArg
|
||||
kind = rv.Kind()
|
||||
}
|
||||
switch kind {
|
||||
// '?'占位符支持slice类型,
|
||||
// 这里会将slice参数拆散,并更新原有占位符'?'为多个'?',使用','符号连接。
|
||||
case reflect.Slice:
|
||||
fallthrough
|
||||
case reflect.Array:
|
||||
// '?'占位符支持slice类型, 这里会将slice参数拆散,并更新原有占位符'?'为多个'?',使用','符号连接。
|
||||
case reflect.Slice, reflect.Array:
|
||||
// 不拆分[]byte类型
|
||||
if _, ok := arg.([]byte); ok {
|
||||
newArgs = append(newArgs, arg)
|
||||
continue
|
||||
}
|
||||
for i := 0; i < rv.Len(); i++ {
|
||||
newArgs = append(newArgs, rv.Index(i).Interface())
|
||||
}
|
||||
// 如果参数直接传递slice,并且占位符数量与slice长度相等,
|
||||
// 那么不用替换扩展占位符数量,直接使用该slice作为查询参数
|
||||
if len(args) == 1 && gstr.Count(newQuery, "?") == rv.Len() {
|
||||
break
|
||||
}
|
||||
// counter用于匹配该参数的位置(与index对应)
|
||||
counter := 0
|
||||
newWhere, _ = gregex.ReplaceStringFunc(`\?`, newWhere, func(s string) string {
|
||||
newQuery, _ = gregex.ReplaceStringFunc(`\?`, newQuery, func(s string) string {
|
||||
counter++
|
||||
if counter == index+1 {
|
||||
return "?" + strings.Repeat(",?", rv.Len()-1)
|
||||
@ -120,14 +66,6 @@ func formatWhere(where interface{}, args []interface{}) (newWhere string, newArg
|
||||
return s
|
||||
})
|
||||
default:
|
||||
// 支持例如 Where/And/Or("uid", 1) 这种格式
|
||||
if gstr.Pos(newWhere, "?") == -1 {
|
||||
if gstr.Pos(newWhere, "<") == -1 && gstr.Pos(newWhere, ">") == -1 && gstr.Pos(newWhere, "=") == -1 {
|
||||
newWhere += "=?"
|
||||
} else {
|
||||
newWhere += "?"
|
||||
}
|
||||
}
|
||||
newArgs = append(newArgs, arg)
|
||||
}
|
||||
}
|
||||
@ -174,7 +112,7 @@ func printSql(v *Sql) {
|
||||
)
|
||||
if v.Error != nil {
|
||||
s += "\nError: " + v.Error.Error()
|
||||
glog.Backtrace(true, 2).Error(s)
|
||||
glog.Stack(true, 2).Error(s)
|
||||
} else {
|
||||
glog.Debug(s)
|
||||
}
|
||||
@ -182,7 +120,7 @@ func printSql(v *Sql) {
|
||||
|
||||
// 格式化错误信息
|
||||
func formatError(err error, query string, args ...interface{}) error {
|
||||
if err != nil {
|
||||
if err != nil && err != sql.ErrNoRows {
|
||||
errStr := fmt.Sprintf("DB ERROR: %s\n", err.Error())
|
||||
errStr += fmt.Sprintf("DB QUERY: %s\n", query)
|
||||
if len(args) > 0 {
|
||||
|
||||
@ -3,8 +3,6 @@
|
||||
// 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.
|
||||
//
|
||||
// @author john, ymrjqyy
|
||||
|
||||
package gdb
|
||||
|
||||
@ -30,6 +28,7 @@ type Model struct {
|
||||
orderBy string // 排序语句
|
||||
start int // 分页开始
|
||||
limit int // 分页条数
|
||||
offset int // 查询偏移量(OFFSET语法)
|
||||
data interface{} // 操作数据(注意仅支持Map/List/string类型)
|
||||
batch int // 批量操作条数
|
||||
filter bool // 是否按照表字段过滤data参数
|
||||
@ -44,9 +43,10 @@ func (bs *dbBase) Table(tables string) *Model {
|
||||
return &Model{
|
||||
db: bs.db,
|
||||
tablesInit: tables,
|
||||
tables: tables,
|
||||
tables: bs.db.quoteWord(tables),
|
||||
fields: "*",
|
||||
start: -1,
|
||||
offset: -1,
|
||||
safe: false,
|
||||
}
|
||||
}
|
||||
@ -62,7 +62,10 @@ func (tx *TX) Table(tables string) *Model {
|
||||
db: tx.db,
|
||||
tx: tx,
|
||||
tablesInit: tables,
|
||||
tables: tables,
|
||||
tables: tx.db.quoteWord(tables),
|
||||
fields: "*",
|
||||
start: -1,
|
||||
offset: -1,
|
||||
safe: false,
|
||||
}
|
||||
}
|
||||
@ -151,7 +154,7 @@ func (md *Model) Where(where interface{}, args ...interface{}) *Model {
|
||||
if model.where != "" {
|
||||
return md.And(where, args...)
|
||||
}
|
||||
newWhere, newArgs := formatWhere(where, args)
|
||||
newWhere, newArgs := md.db.formatWhere(where, args)
|
||||
model.where = newWhere
|
||||
model.whereArgs = newArgs
|
||||
return model
|
||||
@ -160,7 +163,7 @@ func (md *Model) Where(where interface{}, args ...interface{}) *Model {
|
||||
// 链式操作,添加AND条件到Where中
|
||||
func (md *Model) And(where interface{}, args ...interface{}) *Model {
|
||||
model := md.getModel()
|
||||
newWhere, newArgs := formatWhere(where, args)
|
||||
newWhere, newArgs := md.db.formatWhere(where, args)
|
||||
if len(model.where) > 0 && model.where[0] == '(' {
|
||||
model.where = fmt.Sprintf(`%s AND (%s)`, model.where, newWhere)
|
||||
} else {
|
||||
@ -173,7 +176,7 @@ func (md *Model) And(where interface{}, args ...interface{}) *Model {
|
||||
// 链式操作,添加OR条件到Where中
|
||||
func (md *Model) Or(where interface{}, args ...interface{}) *Model {
|
||||
model := md.getModel()
|
||||
newWhere, newArgs := formatWhere(where, args)
|
||||
newWhere, newArgs := md.db.formatWhere(where, args)
|
||||
if len(model.where) > 0 && model.where[0] == '(' {
|
||||
model.where = fmt.Sprintf(`%s OR (%s)`, model.where, newWhere)
|
||||
} else {
|
||||
@ -214,6 +217,14 @@ func (md *Model) Limit(limit ...int) *Model {
|
||||
return model
|
||||
}
|
||||
|
||||
// 链式操作,OFFSET语法(部分数据库支持)。
|
||||
// 注意:可以使用Limit方法调用替换该方法特性,底层不同数据库将会自动替换LIMIT语法为OFFSET语法。
|
||||
func (md *Model) Offset(offset int) *Model {
|
||||
model := md.getModel()
|
||||
model.offset = offset
|
||||
return model
|
||||
}
|
||||
|
||||
// 链式操作,翻页,注意分页页码从1开始,而Limit方法从0开始。
|
||||
func (md *Model) ForPage(page, limit int) *Model {
|
||||
model := md.getModel()
|
||||
@ -463,7 +474,7 @@ func (md *Model) Select() (Result, error) {
|
||||
|
||||
// 链式操作,查询所有记录
|
||||
func (md *Model) All() (Result, error) {
|
||||
return md.getAll(fmt.Sprintf("SELECT %s FROM %s %s", md.fields, md.tables, md.getConditionSql()), md.whereArgs...)
|
||||
return md.getAll(fmt.Sprintf("SELECT %s FROM %s%s", md.fields, md.tables, md.getConditionSql()), md.whereArgs...)
|
||||
}
|
||||
|
||||
// 链式操作,查询单条记录
|
||||
@ -511,21 +522,18 @@ func (md *Model) Structs(objPointerSlice interface{}) error {
|
||||
// 链式操作,将结果转换为指定的struct/*struct/[]struct/[]*struct,
|
||||
// 参数应该为指针类型,否则返回失败。
|
||||
// 该方法自动识别参数类型,调用Struct/Structs方法。
|
||||
func (md *Model) Scan(objPointer interface{}) error {
|
||||
t := reflect.TypeOf(objPointer)
|
||||
func (md *Model) Scan(pointer interface{}) error {
|
||||
t := reflect.TypeOf(pointer)
|
||||
k := t.Kind()
|
||||
if k != reflect.Ptr {
|
||||
return fmt.Errorf("params should be type of pointer, but got: %v", k)
|
||||
}
|
||||
k = t.Elem().Kind()
|
||||
switch k {
|
||||
switch t.Elem().Kind() {
|
||||
case reflect.Array:
|
||||
case reflect.Slice:
|
||||
return md.Structs(objPointer)
|
||||
case reflect.Struct:
|
||||
return md.Struct(objPointer)
|
||||
return md.Structs(pointer)
|
||||
default:
|
||||
return fmt.Errorf("element type should be type of struct/slice, unsupported: %v", k)
|
||||
return md.Struct(pointer)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@ -608,11 +616,13 @@ func (md *Model) getConditionSql() string {
|
||||
}
|
||||
if md.limit != 0 {
|
||||
if md.start >= 0 {
|
||||
s += fmt.Sprintf(" LIMIT %d, %d", md.start, md.limit)
|
||||
s += fmt.Sprintf(" LIMIT %d,%d", md.start, md.limit)
|
||||
} else {
|
||||
s += fmt.Sprintf(" LIMIT %d", md.limit)
|
||||
}
|
||||
|
||||
}
|
||||
if md.offset >= 0 {
|
||||
s += fmt.Sprintf(" OFFSET %d", md.offset)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
@ -3,23 +3,21 @@
|
||||
// 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.
|
||||
/*
|
||||
@author wenzi1<liyz23@qq.com>
|
||||
@date 20181109
|
||||
说明:
|
||||
1.需要导入sqlserver驱动: github.com/denisenkom/go-mssqldb
|
||||
2.不支持save/replace方法
|
||||
3.不支持LastInsertId方法
|
||||
*/
|
||||
// 说明:
|
||||
// 1.需要导入sqlserver驱动: github.com/denisenkom/go-mssqldb
|
||||
// 2.不支持save/replace方法
|
||||
// 3.不支持LastInsertId方法
|
||||
//
|
||||
|
||||
package gdb
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/g/text/gregex"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/gogf/gf/g/text/gregex"
|
||||
)
|
||||
|
||||
// 数据库链接对象
|
||||
|
||||
@ -3,23 +3,20 @@
|
||||
// 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.
|
||||
/*
|
||||
@author wenzi1<liyz23@qq.com>
|
||||
@date 20181026
|
||||
说明:
|
||||
1.需要导入oracle驱动: github.com/mattn/go-oci8
|
||||
2.不支持save/replace方法,可以调用这2个方法估计会报错,还没测试过,(应该是可以通过oracle的merge来实现这2个功能的,还没仔细研究)
|
||||
3.不支持LastInsertId方法
|
||||
*/
|
||||
// 说明:
|
||||
// 1.需要导入oracle驱动: github.com/mattn/go-oci8
|
||||
// 2.不支持save/replace方法,可以调用这2个方法估计会报错,还没测试过,(应该是可以通过oracle的merge来实现这2个功能的,还没仔细研究)
|
||||
// 3.不支持LastInsertId方法
|
||||
|
||||
package gdb
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/g/text/gregex"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/gogf/gf/g/text/gregex"
|
||||
)
|
||||
|
||||
// 数据库链接对象
|
||||
|
||||
@ -9,12 +9,16 @@ package gdb
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"regexp"
|
||||
|
||||
"github.com/gogf/gf/g/text/gregex"
|
||||
)
|
||||
|
||||
// PostgreSQL的适配.
|
||||
//
|
||||
// 使用时需要import:
|
||||
//
|
||||
// _ "github.com/gogf/gf/third/github.com/lib/pq"
|
||||
//
|
||||
// @todo 需要完善replace和save的操作覆盖
|
||||
|
||||
// 数据库链接对象
|
||||
@ -37,6 +41,12 @@ func (db *dbPgsql) Open(config *ConfigNode) (*sql.DB, error) {
|
||||
}
|
||||
}
|
||||
|
||||
// 动态切换数据库
|
||||
func (db *dbPgsql) setSchema(sqlDb *sql.DB, schema string) error {
|
||||
_, err := sqlDb.Exec("SET search_path TO " + schema)
|
||||
return err
|
||||
}
|
||||
|
||||
// 获得关键字操作符
|
||||
func (db *dbPgsql) getChars() (charLeft string, charRight string) {
|
||||
return "\"", "\""
|
||||
@ -44,11 +54,12 @@ func (db *dbPgsql) getChars() (charLeft string, charRight string) {
|
||||
|
||||
// 在执行sql之前对sql进行进一步处理
|
||||
func (db *dbPgsql) handleSqlBeforeExec(query string) string {
|
||||
reg := regexp.MustCompile("\\?")
|
||||
index := 0
|
||||
str := reg.ReplaceAllStringFunc(query, func(s string) string {
|
||||
query, _ = gregex.ReplaceStringFunc("\\?", query, func(s string) string {
|
||||
index++
|
||||
return fmt.Sprintf("$%d", index)
|
||||
})
|
||||
return str
|
||||
// 分页语法替换
|
||||
query, _ = gregex.ReplaceString(` LIMIT (\d+),\s*(\d+)`, ` LIMIT $1 OFFSET $2`, query)
|
||||
return query
|
||||
}
|
||||
|
||||
@ -3,7 +3,6 @@
|
||||
// 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.
|
||||
// @author wxkj<wxscz@qq.com>
|
||||
|
||||
package gdb
|
||||
|
||||
@ -41,7 +40,7 @@ func (db *dbSqlite) getChars() (charLeft string, charRight string) {
|
||||
return "`", "`"
|
||||
}
|
||||
|
||||
// 在执行sql之前对sql进行进一步处理
|
||||
// 在执行sql之前对sql进行进一步处理。
|
||||
// @todo 需要增加对Save方法的支持,可使用正则来实现替换,
|
||||
// @todo 将ON DUPLICATE KEY UPDATE触发器修改为两条SQL语句(INSERT OR IGNORE & UPDATE)
|
||||
func (db *dbSqlite) handleSqlBeforeExec(query string) string {
|
||||
|
||||
@ -8,37 +8,49 @@ package gdb
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/gogf/gf/g/encoding/gbinary"
|
||||
|
||||
"github.com/gogf/gf/g/text/gregex"
|
||||
"github.com/gogf/gf/g/util/gconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
/*
|
||||
// 同步数据库表结构到内存中
|
||||
func (bs *dbBase) syncTableStructure() {
|
||||
bs.tables = make(map[string]map[string]string)
|
||||
for _, table := range bs.db.getTables() {
|
||||
bs.tables[table], _ = bs.db.getTableFields(table)
|
||||
}
|
||||
}
|
||||
*/
|
||||
//// 同步数据库表结构到内存中
|
||||
//func (bs *dbBase) syncTableStructure() {
|
||||
// bs.tables = make(map[string]map[string]string)
|
||||
// for _, table := range bs.db.getTables() {
|
||||
// bs.tables[table], _ = bs.db.getTableFields(table)
|
||||
// }
|
||||
//}
|
||||
|
||||
// 字段类型转换,将数据库字段类型转换为golang变量类型
|
||||
func (bs *dbBase) convertValue(fieldValue interface{}, fieldType string) interface{} {
|
||||
func (bs *dbBase) convertValue(fieldValue []byte, fieldType string) interface{} {
|
||||
t, _ := gregex.ReplaceString(`\(.+\)`, "", fieldType)
|
||||
t = strings.ToLower(t)
|
||||
switch t {
|
||||
case "binary", "varbinary", "blob", "tinyblob", "mediumblob", "longblob":
|
||||
return gconv.Bytes(fieldValue)
|
||||
return fieldValue
|
||||
|
||||
case "bit", "int", "tinyint", "small_int", "medium_int":
|
||||
return gconv.Int(fieldValue)
|
||||
case "int", "tinyint", "small_int", "medium_int":
|
||||
return gconv.Int(string(fieldValue))
|
||||
|
||||
case "big_int":
|
||||
return gconv.Int64(fieldValue)
|
||||
return gconv.Int64(string(fieldValue))
|
||||
|
||||
case "float", "double", "decimal":
|
||||
return gconv.Float64(fieldValue)
|
||||
return gconv.Float64(string(fieldValue))
|
||||
|
||||
case "bit":
|
||||
s := string(fieldValue)
|
||||
// 这里的字符串判断是为兼容不同的数据库类型,如: mssql
|
||||
if strings.EqualFold(s, "true") {
|
||||
return 1
|
||||
}
|
||||
if strings.EqualFold(s, "false") {
|
||||
return 0
|
||||
}
|
||||
return gbinary.BeDecodeToInt64(fieldValue)
|
||||
|
||||
case "bool":
|
||||
return gconv.Bool(fieldValue)
|
||||
@ -47,22 +59,22 @@ func (bs *dbBase) convertValue(fieldValue interface{}, fieldType string) interfa
|
||||
// 自动识别类型, 以便默认支持更多数据库类型
|
||||
switch {
|
||||
case strings.Contains(t, "int"):
|
||||
return gconv.Int(fieldValue)
|
||||
return gconv.Int(string(fieldValue))
|
||||
|
||||
case strings.Contains(t, "text") || strings.Contains(t, "char"):
|
||||
return gconv.String(fieldValue)
|
||||
return string(fieldValue)
|
||||
|
||||
case strings.Contains(t, "float") || strings.Contains(t, "double"):
|
||||
return gconv.Float64(fieldValue)
|
||||
return gconv.Float64(string(fieldValue))
|
||||
|
||||
case strings.Contains(t, "bool"):
|
||||
return gconv.Bool(fieldValue)
|
||||
return gconv.Bool(string(fieldValue))
|
||||
|
||||
case strings.Contains(t, "binary") || strings.Contains(t, "blob"):
|
||||
return gconv.Bytes(fieldValue)
|
||||
return fieldValue
|
||||
|
||||
default:
|
||||
return gconv.String(fieldValue)
|
||||
return string(fieldValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -84,8 +96,7 @@ func (bs *dbBase) getTableFields(table string) (fields map[string]string, err er
|
||||
// 缓存不存在时会查询数据表结构,缓存后不过期,直至程序重启(重新部署)
|
||||
v := bs.cache.GetOrSetFunc("table_fields_"+table, func() interface{} {
|
||||
result := (Result)(nil)
|
||||
charL, charR := bs.db.getChars()
|
||||
result, err = bs.GetAll(fmt.Sprintf(`SHOW COLUMNS FROM %s%s%s`, charL, table, charR))
|
||||
result, err = bs.GetAll(fmt.Sprintf(`SHOW COLUMNS FROM %s`, bs.db.quoteWord(table)))
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -165,7 +165,7 @@ func (tx *TX) BatchSave(table string, list interface{}, batch ...int) (sql.Resul
|
||||
// CURD操作:数据更新,统一采用sql预处理,
|
||||
// data参数支持字符串或者关联数组类型,内部会自行做判断处理.
|
||||
func (tx *TX) Update(table string, data interface{}, condition interface{}, args ...interface{}) (sql.Result, error) {
|
||||
newWhere, newArgs := formatWhere(condition, args)
|
||||
newWhere, newArgs := tx.db.formatWhere(condition, args)
|
||||
if newWhere != "" {
|
||||
newWhere = " WHERE " + newWhere
|
||||
}
|
||||
@ -179,7 +179,7 @@ func (tx *TX) doUpdate(table string, data interface{}, condition string, args ..
|
||||
|
||||
// CURD操作:删除数据
|
||||
func (tx *TX) Delete(table string, condition interface{}, args ...interface{}) (sql.Result, error) {
|
||||
newWhere, newArgs := formatWhere(condition, args)
|
||||
newWhere, newArgs := tx.db.formatWhere(condition, args)
|
||||
if newWhere != "" {
|
||||
newWhere = " WHERE " + newWhere
|
||||
}
|
||||
|
||||
@ -7,6 +7,8 @@
|
||||
package gdb
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
|
||||
"github.com/gogf/gf/g/encoding/gparser"
|
||||
)
|
||||
|
||||
@ -33,5 +35,8 @@ func (r Record) ToMap() Map {
|
||||
|
||||
// 将Map变量映射到指定的struct对象中,注意参数应当是一个对象的指针
|
||||
func (r Record) ToStruct(pointer interface{}) error {
|
||||
if r == nil {
|
||||
return sql.ErrNoRows
|
||||
}
|
||||
return mapToStruct(r.ToMap(), pointer)
|
||||
}
|
||||
|
||||
@ -7,9 +7,11 @@
|
||||
package gdb
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/g/encoding/gparser"
|
||||
"reflect"
|
||||
|
||||
"github.com/gogf/gf/g/encoding/gparser"
|
||||
)
|
||||
|
||||
// 将结果集转换为JSON字符串
|
||||
@ -100,28 +102,32 @@ func (r Result) ToUintRecord(key string) map[uint]Record {
|
||||
}
|
||||
|
||||
// 将结果列表转换为指定对象的slice。
|
||||
func (r Result) ToStructs(objPointerSlice interface{}) error {
|
||||
func (r Result) ToStructs(pointer interface{}) (err error) {
|
||||
l := len(r)
|
||||
if l == 0 {
|
||||
return nil
|
||||
return sql.ErrNoRows
|
||||
}
|
||||
t := reflect.TypeOf(objPointerSlice)
|
||||
t := reflect.TypeOf(pointer)
|
||||
if t.Kind() != reflect.Ptr {
|
||||
return fmt.Errorf("params should be type of pointer, but got: %v", t.Kind())
|
||||
return fmt.Errorf("pointer should be type of pointer, but got: %v", t.Kind())
|
||||
}
|
||||
a := reflect.MakeSlice(t.Elem(), l, l)
|
||||
itemType := a.Index(0).Type()
|
||||
array := reflect.MakeSlice(t.Elem(), l, l)
|
||||
itemType := array.Index(0).Type()
|
||||
for i := 0; i < l; i++ {
|
||||
if itemType.Kind() == reflect.Ptr {
|
||||
e := reflect.New(itemType.Elem()).Elem()
|
||||
r[i].ToStruct(e)
|
||||
a.Index(i).Set(e.Addr())
|
||||
if err = r[i].ToStruct(e); err != nil {
|
||||
return err
|
||||
}
|
||||
array.Index(i).Set(e.Addr())
|
||||
} else {
|
||||
e := reflect.New(itemType).Elem()
|
||||
r[i].ToStruct(e)
|
||||
a.Index(i).Set(e)
|
||||
if err = r[i].ToStruct(e); err != nil {
|
||||
return err
|
||||
}
|
||||
array.Index(i).Set(e)
|
||||
}
|
||||
}
|
||||
reflect.ValueOf(objPointerSlice).Elem().Set(a)
|
||||
reflect.ValueOf(pointer).Elem().Set(array)
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -19,12 +19,14 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
// 初始化表数据量
|
||||
INIT_DATA_SIZE = 10
|
||||
INIT_DATA_SIZE = 10 // 初始化表数据量
|
||||
TABLE = "user" // 测试数据表
|
||||
SCHEMA1 = "test1" // 测试数据库1
|
||||
SCHEMA2 = "test2" // 测试数据库2
|
||||
)
|
||||
|
||||
var (
|
||||
// 数据库对象/接口
|
||||
// 测试包变量,ORM对象
|
||||
db gdb.DB
|
||||
)
|
||||
|
||||
@ -32,60 +34,63 @@ var (
|
||||
// 测试前需要修改连接参数。
|
||||
func init() {
|
||||
node := gdb.ConfigNode{
|
||||
Host: "127.0.0.1",
|
||||
Port: "3306",
|
||||
User: "root",
|
||||
Pass: "",
|
||||
Name: "",
|
||||
Type: "mysql",
|
||||
Role: "master",
|
||||
Charset: "utf8",
|
||||
Priority: 1,
|
||||
Host: "127.0.0.1",
|
||||
Port: "3306",
|
||||
User: "root",
|
||||
Pass: "",
|
||||
Name: "",
|
||||
Type: "mysql",
|
||||
Role: "master",
|
||||
Charset: "utf8",
|
||||
Weight: 1,
|
||||
}
|
||||
hostname, _ := os.Hostname()
|
||||
// 本地测试hack
|
||||
if hostname == "ijohn" {
|
||||
// 作者本地测试hack
|
||||
if hostname, _ := os.Hostname(); hostname == "ijohn" {
|
||||
node.Pass = "12345678"
|
||||
}
|
||||
gdb.AddConfigNode("test", node)
|
||||
gdb.AddConfigNode(gdb.DEFAULT_GROUP_NAME, node)
|
||||
if r, err := gdb.New(); err != nil {
|
||||
gtest.Fatal(err)
|
||||
gtest.Error(err)
|
||||
} else {
|
||||
db = r
|
||||
}
|
||||
// 准备测试数据结构
|
||||
if _, err := db.Exec("CREATE DATABASE IF NOT EXISTS `test` CHARACTER SET UTF8"); err != nil {
|
||||
gtest.Fatal(err)
|
||||
// 准备测试数据结构:数据库
|
||||
schemaTemplate := "CREATE DATABASE IF NOT EXISTS `%s` CHARACTER SET UTF8"
|
||||
if _, err := db.Exec(fmt.Sprintf(schemaTemplate, SCHEMA1)); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
// 选择操作数据库
|
||||
db.SetSchema("test")
|
||||
// 多个数据库,用于测试数据库切换
|
||||
if _, err := db.Exec(fmt.Sprintf(schemaTemplate, SCHEMA2)); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
// 设置默认操作数据库
|
||||
db.SetSchema(SCHEMA1)
|
||||
// 创建默认用户表
|
||||
createTable("user")
|
||||
createTable(TABLE)
|
||||
}
|
||||
|
||||
// 创建指定名称的user测试表,当table为空时,创建随机的表名。
|
||||
// 创建的测试表默认没有任何数据。
|
||||
// 执行完成后返回该表名。
|
||||
// TODO 支持更多数据库
|
||||
func createTable(table ...string) (name string) {
|
||||
if len(table) > 0 {
|
||||
name = table[0]
|
||||
} else {
|
||||
name = fmt.Sprintf(`user_%d`, gtime.Nanosecond())
|
||||
name = fmt.Sprintf(`%s_%d`, TABLE, gtime.Nanosecond())
|
||||
}
|
||||
dropTable(name)
|
||||
if _, err := db.Exec(fmt.Sprintf(`
|
||||
CREATE TABLE %s (
|
||||
id int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '用户ID',
|
||||
passport varchar(45) NOT NULL COMMENT '账号',
|
||||
password char(32) NOT NULL COMMENT '密码',
|
||||
nickname varchar(45) NOT NULL COMMENT '昵称',
|
||||
id int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '用户ID',
|
||||
passport varchar(45) NOT NULL COMMENT '账号',
|
||||
password char(32) NOT NULL COMMENT '密码',
|
||||
nickname varchar(45) NOT NULL COMMENT '昵称',
|
||||
create_time timestamp NOT NULL COMMENT '创建时间/注册时间',
|
||||
PRIMARY KEY (id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
`, name)); err != nil {
|
||||
gtest.Fatal(err)
|
||||
gtest.Error(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
@ -97,10 +102,10 @@ func createInitTable(table ...string) (name string) {
|
||||
for i := 1; i <= INIT_DATA_SIZE; i++ {
|
||||
array.Append(g.Map{
|
||||
"id": i,
|
||||
"passport": fmt.Sprintf(`t%d`, i),
|
||||
"password": fmt.Sprintf(`p%d`, i),
|
||||
"nickname": fmt.Sprintf(`T%d`, i),
|
||||
"create_time": gtime.Now().String(),
|
||||
"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(),
|
||||
})
|
||||
}
|
||||
result, err := db.Table(name).Data(array.Slice()).Insert()
|
||||
@ -115,6 +120,6 @@ func createInitTable(table ...string) (name string) {
|
||||
// 删除指定表.
|
||||
func dropTable(table string) {
|
||||
if _, err := db.Exec(fmt.Sprintf("DROP TABLE IF EXISTS `%s`", table)); err != nil {
|
||||
gtest.Fatal(err)
|
||||
gtest.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,536 +0,0 @@
|
||||
// Copyright 2019 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,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gdb_test
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g"
|
||||
"github.com/gogf/gf/g/os/gtime"
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestDbBase_Ping(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
err1 := db.PingMaster()
|
||||
err2 := db.PingSlave()
|
||||
gtest.Assert(err1, nil)
|
||||
gtest.Assert(err2, nil)
|
||||
})
|
||||
}
|
||||
|
||||
func TestDbBase_Query(t *testing.T) {
|
||||
if _, err := db.Query("SELECT ?", 1); err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
if _, err := db.Query("ERROR"); err == nil {
|
||||
gtest.Fatal("FAIL")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDbBase_Exec(t *testing.T) {
|
||||
if _, err := db.Exec("SELECT ?", 1); err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
if _, err := db.Exec("ERROR"); err == nil {
|
||||
gtest.Fatal("FAIL")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDbBase_Prepare(t *testing.T) {
|
||||
st, err := db.Prepare("SELECT 100")
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
rows, err := st.Query()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
array, err := rows.Columns()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
gtest.Assert(array[0], "100")
|
||||
if err := rows.Close(); err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDbBase_Insert(t *testing.T) {
|
||||
if _, err := db.Insert("user", g.Map{
|
||||
"id": 1,
|
||||
"passport": "t1",
|
||||
"password": "25d55ad283aa400af464c76d713c07ad",
|
||||
"nickname": "T1",
|
||||
"create_time": gtime.Now().String(),
|
||||
}); err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
// normal map
|
||||
result, err := db.Insert("user", map[interface{}]interface{}{
|
||||
"id": "2",
|
||||
"passport": "t2",
|
||||
"password": "25d55ad283aa400af464c76d713c07ad",
|
||||
"nickname": "T2",
|
||||
"create_time": gtime.Now().String(),
|
||||
})
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
n, _ := result.RowsAffected()
|
||||
gtest.Assert(n, 1)
|
||||
|
||||
// struct
|
||||
type User struct {
|
||||
Id int `gconv:"id"`
|
||||
Passport string `json:"passport"`
|
||||
Password string `gconv:"password"`
|
||||
Nickname string `gconv:"nickname"`
|
||||
CreateTime string `json:"create_time"`
|
||||
}
|
||||
result, err = db.Insert("user", User{
|
||||
Id: 3,
|
||||
Passport: "t3",
|
||||
Password: "25d55ad283aa400af464c76d713c07ad",
|
||||
Nickname: "T3",
|
||||
CreateTime: gtime.Now().String(),
|
||||
})
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
n, _ = result.RowsAffected()
|
||||
gtest.Assert(n, 1)
|
||||
value, err := db.GetValue("select `passport` from `user` where id=?", 3)
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(value.String(), "t3")
|
||||
|
||||
// *struct
|
||||
result, err = db.Insert("user", &User{
|
||||
Id: 4,
|
||||
Passport: "t4",
|
||||
Password: "25d55ad283aa400af464c76d713c07ad",
|
||||
Nickname: "T4",
|
||||
CreateTime: gtime.Now().String(),
|
||||
})
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
n, _ = result.RowsAffected()
|
||||
gtest.Assert(n, 1)
|
||||
value, err = db.GetValue("select `passport` from `user` where id=?", 4)
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(value.String(), "t4")
|
||||
|
||||
// batch with Insert
|
||||
if r, err := db.Insert("user", []interface{}{
|
||||
map[interface{}]interface{}{
|
||||
"id": 200,
|
||||
"passport": "t200",
|
||||
"password": "25d55ad283aa400af464c76d713c07ad",
|
||||
"nickname": "T200",
|
||||
"create_time": gtime.Now().String(),
|
||||
},
|
||||
map[interface{}]interface{}{
|
||||
"id": 300,
|
||||
"passport": "t300",
|
||||
"password": "25d55ad283aa400af464c76d713c07ad",
|
||||
"nickname": "T300",
|
||||
"create_time": gtime.Now().String(),
|
||||
},
|
||||
}); err != nil {
|
||||
gtest.Fatal(err)
|
||||
} else {
|
||||
n, _ := r.RowsAffected()
|
||||
gtest.Assert(n, 2)
|
||||
}
|
||||
|
||||
// clear unnecessary data
|
||||
result, err = db.Delete("user", "id>?", 1)
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
n, _ = result.RowsAffected()
|
||||
gtest.Assert(n, 5)
|
||||
}
|
||||
|
||||
func TestDbBase_BatchInsert(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
if r, err := db.BatchInsert("user", g.List{
|
||||
{
|
||||
"id": 2,
|
||||
"passport": "t2",
|
||||
"password": "25d55ad283aa400af464c76d713c07ad",
|
||||
"nickname": "T2",
|
||||
"create_time": gtime.Now().String(),
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"passport": "t3",
|
||||
"password": "25d55ad283aa400af464c76d713c07ad",
|
||||
"nickname": "T3",
|
||||
"create_time": gtime.Now().String(),
|
||||
},
|
||||
}, 1); err != nil {
|
||||
gtest.Fatal(err)
|
||||
} else {
|
||||
n, _ := r.RowsAffected()
|
||||
gtest.Assert(n, 2)
|
||||
}
|
||||
|
||||
result, err := db.Delete("user", "id>?", 1)
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
n, _ := result.RowsAffected()
|
||||
gtest.Assert(n, 2)
|
||||
|
||||
// []interface{}
|
||||
if r, err := db.BatchInsert("user", []interface{}{
|
||||
map[interface{}]interface{}{
|
||||
"id": 2,
|
||||
"passport": "t2",
|
||||
"password": "25d55ad283aa400af464c76d713c07ad",
|
||||
"nickname": "T2",
|
||||
"create_time": gtime.Now().String(),
|
||||
},
|
||||
map[interface{}]interface{}{
|
||||
"id": 3,
|
||||
"passport": "t3",
|
||||
"password": "25d55ad283aa400af464c76d713c07ad",
|
||||
"nickname": "T3",
|
||||
"create_time": gtime.Now().String(),
|
||||
},
|
||||
}, 1); err != nil {
|
||||
gtest.Fatal(err)
|
||||
} else {
|
||||
n, _ := r.RowsAffected()
|
||||
gtest.Assert(n, 2)
|
||||
}
|
||||
})
|
||||
// batch insert map
|
||||
gtest.Case(t, func() {
|
||||
table := createTable()
|
||||
defer dropTable(table)
|
||||
result, err := db.BatchInsert(table, g.Map{
|
||||
"id": 1,
|
||||
"passport": "t1",
|
||||
"password": "p1",
|
||||
"nickname": "T1",
|
||||
"create_time": gtime.Now().String(),
|
||||
})
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
n, _ := result.RowsAffected()
|
||||
gtest.Assert(n, 1)
|
||||
})
|
||||
// batch insert struct
|
||||
gtest.Case(t, func() {
|
||||
table := createTable()
|
||||
defer dropTable(table)
|
||||
|
||||
type User struct {
|
||||
Id int `gconv:"id"`
|
||||
Passport string `gconv:"passport"`
|
||||
Password string `gconv:"password"`
|
||||
NickName string `gconv:"nickname"`
|
||||
CreateTime *gtime.Time `gconv:"create_time"`
|
||||
}
|
||||
user := &User{
|
||||
Id: 1,
|
||||
Passport: "t1",
|
||||
Password: "p1",
|
||||
NickName: "T1",
|
||||
CreateTime: gtime.Now(),
|
||||
}
|
||||
result, err := db.BatchInsert(table, user)
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
n, _ := result.RowsAffected()
|
||||
gtest.Assert(n, 1)
|
||||
})
|
||||
}
|
||||
|
||||
func TestDbBase_Save(t *testing.T) {
|
||||
if _, err := db.Save("user", g.Map{
|
||||
"id": 1,
|
||||
"passport": "t1",
|
||||
"password": "25d55ad283aa400af464c76d713c07ad",
|
||||
"nickname": "T11",
|
||||
"create_time": gtime.Now().String(),
|
||||
}); err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDbBase_Replace(t *testing.T) {
|
||||
if _, err := db.Save("user", g.Map{
|
||||
"id": 1,
|
||||
"passport": "t1",
|
||||
"password": "25d55ad283aa400af464c76d713c07ad",
|
||||
"nickname": "T111",
|
||||
"create_time": gtime.Now().String(),
|
||||
}); err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDbBase_Update(t *testing.T) {
|
||||
if result, err := db.Update("user", "create_time='2010-10-10 00:00:01'", "id=3"); err != nil {
|
||||
gtest.Fatal(err)
|
||||
} else {
|
||||
n, _ := result.RowsAffected()
|
||||
gtest.Assert(n, 1)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDbBase_GetAll(t *testing.T) {
|
||||
if result, err := db.GetAll("SELECT * FROM user WHERE id=?", 1); err != nil {
|
||||
gtest.Fatal(err)
|
||||
} else {
|
||||
gtest.Assert(len(result), 1)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDbBase_GetOne(t *testing.T) {
|
||||
if record, err := db.GetOne("SELECT * FROM user WHERE passport=?", "t1"); err != nil {
|
||||
gtest.Fatal(err)
|
||||
} else {
|
||||
if record == nil {
|
||||
gtest.Fatal("FAIL")
|
||||
}
|
||||
gtest.Assert(record["nickname"].String(), "T111")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDbBase_GetValue(t *testing.T) {
|
||||
if value, err := db.GetValue("SELECT id FROM user WHERE passport=?", "t3"); err != nil {
|
||||
gtest.Fatal(err)
|
||||
} else {
|
||||
gtest.Assert(value.Int(), 3)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDbBase_GetCount(t *testing.T) {
|
||||
if count, err := db.GetCount("SELECT * FROM user"); err != nil {
|
||||
gtest.Fatal(err)
|
||||
} else {
|
||||
gtest.Assert(count, 3)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDbBase_GetStruct(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
type User struct {
|
||||
Id int
|
||||
Passport string
|
||||
Password string
|
||||
NickName string
|
||||
CreateTime gtime.Time
|
||||
}
|
||||
user := new(User)
|
||||
if err := db.GetStruct(user, "SELECT * FROM user WHERE id=?", 3); err != nil {
|
||||
gtest.Fatal(err)
|
||||
} else {
|
||||
gtest.Assert(user.CreateTime.String(), "2010-10-10 00:00:01")
|
||||
}
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
type User struct {
|
||||
Id int
|
||||
Passport string
|
||||
Password string
|
||||
NickName string
|
||||
CreateTime *gtime.Time
|
||||
}
|
||||
user := new(User)
|
||||
if err := db.GetStruct(user, "SELECT * FROM user WHERE id=?", 3); err != nil {
|
||||
gtest.Fatal(err)
|
||||
} else {
|
||||
gtest.Assert(user.CreateTime.String(), "2010-10-10 00:00:01")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestDbBase_GetStructs(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
type User struct {
|
||||
Id int
|
||||
Passport string
|
||||
Password string
|
||||
NickName string
|
||||
CreateTime gtime.Time
|
||||
}
|
||||
var users []User
|
||||
if err := db.GetStructs(&users, "SELECT * FROM user WHERE id>=?", 1); err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
gtest.Assert(len(users), 3)
|
||||
gtest.Assert(users[0].Id, 1)
|
||||
gtest.Assert(users[1].Id, 2)
|
||||
gtest.Assert(users[2].Id, 3)
|
||||
gtest.Assert(users[0].NickName, "T111")
|
||||
gtest.Assert(users[1].NickName, "T2")
|
||||
gtest.Assert(users[2].NickName, "T3")
|
||||
gtest.Assert(users[2].CreateTime.String(), "2010-10-10 00:00:01")
|
||||
})
|
||||
|
||||
gtest.Case(t, func() {
|
||||
type User struct {
|
||||
Id int
|
||||
Passport string
|
||||
Password string
|
||||
NickName string
|
||||
CreateTime *gtime.Time
|
||||
}
|
||||
var users []User
|
||||
if err := db.GetStructs(&users, "SELECT * FROM user WHERE id>=?", 1); err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
gtest.Assert(len(users), 3)
|
||||
gtest.Assert(users[0].Id, 1)
|
||||
gtest.Assert(users[1].Id, 2)
|
||||
gtest.Assert(users[2].Id, 3)
|
||||
gtest.Assert(users[0].NickName, "T111")
|
||||
gtest.Assert(users[1].NickName, "T2")
|
||||
gtest.Assert(users[2].NickName, "T3")
|
||||
gtest.Assert(users[2].CreateTime.String(), "2010-10-10 00:00:01")
|
||||
})
|
||||
}
|
||||
|
||||
func TestDbBase_GetScan(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
type User struct {
|
||||
Id int
|
||||
Passport string
|
||||
Password string
|
||||
NickName string
|
||||
CreateTime gtime.Time
|
||||
}
|
||||
user := new(User)
|
||||
if err := db.GetScan(user, "SELECT * FROM user WHERE id=?", 3); err != nil {
|
||||
gtest.Fatal(err)
|
||||
} else {
|
||||
gtest.Assert(user.CreateTime.String(), "2010-10-10 00:00:01")
|
||||
}
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
type User struct {
|
||||
Id int
|
||||
Passport string
|
||||
Password string
|
||||
NickName string
|
||||
CreateTime *gtime.Time
|
||||
}
|
||||
user := new(User)
|
||||
if err := db.GetScan(user, "SELECT * FROM user WHERE id=?", 3); err != nil {
|
||||
gtest.Fatal(err)
|
||||
} else {
|
||||
gtest.Assert(user.CreateTime.String(), "2010-10-10 00:00:01")
|
||||
}
|
||||
})
|
||||
|
||||
gtest.Case(t, func() {
|
||||
type User struct {
|
||||
Id int
|
||||
Passport string
|
||||
Password string
|
||||
NickName string
|
||||
CreateTime gtime.Time
|
||||
}
|
||||
var users []User
|
||||
if err := db.GetScan(&users, "SELECT * FROM user WHERE id>=?", 1); err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
gtest.Assert(len(users), 3)
|
||||
gtest.Assert(users[0].Id, 1)
|
||||
gtest.Assert(users[1].Id, 2)
|
||||
gtest.Assert(users[2].Id, 3)
|
||||
gtest.Assert(users[0].NickName, "T111")
|
||||
gtest.Assert(users[1].NickName, "T2")
|
||||
gtest.Assert(users[2].NickName, "T3")
|
||||
gtest.Assert(users[2].CreateTime.String(), "2010-10-10 00:00:01")
|
||||
})
|
||||
|
||||
gtest.Case(t, func() {
|
||||
type User struct {
|
||||
Id int
|
||||
Passport string
|
||||
Password string
|
||||
NickName string
|
||||
CreateTime *gtime.Time
|
||||
}
|
||||
var users []User
|
||||
if err := db.GetScan(&users, "SELECT * FROM user WHERE id>=?", 1); err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
gtest.Assert(len(users), 3)
|
||||
gtest.Assert(users[0].Id, 1)
|
||||
gtest.Assert(users[1].Id, 2)
|
||||
gtest.Assert(users[2].Id, 3)
|
||||
gtest.Assert(users[0].NickName, "T111")
|
||||
gtest.Assert(users[1].NickName, "T2")
|
||||
gtest.Assert(users[2].NickName, "T3")
|
||||
gtest.Assert(users[2].CreateTime.String(), "2010-10-10 00:00:01")
|
||||
})
|
||||
}
|
||||
|
||||
func TestDbBase_Delete(t *testing.T) {
|
||||
if result, err := db.Delete("user", nil); err != nil {
|
||||
gtest.Fatal(err)
|
||||
} else {
|
||||
n, _ := result.RowsAffected()
|
||||
gtest.Assert(n, 3)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDbBase_Time(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
result, err := db.Insert("user", g.Map{
|
||||
"id": 200,
|
||||
"passport": "t200",
|
||||
"password": "123456",
|
||||
"nickname": "T200",
|
||||
"create_time": time.Now(),
|
||||
})
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
n, _ := result.RowsAffected()
|
||||
gtest.Assert(n, 1)
|
||||
value, err := db.GetValue("select `passport` from `user` where id=?", 200)
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(value.String(), "t200")
|
||||
})
|
||||
|
||||
gtest.Case(t, func() {
|
||||
t := time.Now()
|
||||
result, err := db.Insert("user", g.Map{
|
||||
"id": 300,
|
||||
"passport": "t300",
|
||||
"password": "123456",
|
||||
"nickname": "T300",
|
||||
"create_time": &t,
|
||||
})
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
n, _ := result.RowsAffected()
|
||||
gtest.Assert(n, 1)
|
||||
value, err := db.GetValue("select `passport` from `user` where id=?", 300)
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(value.String(), "t300")
|
||||
})
|
||||
|
||||
if result, err := db.Delete("user", nil); err != nil {
|
||||
gtest.Fatal(err)
|
||||
} else {
|
||||
n, _ := result.RowsAffected()
|
||||
gtest.Assert(n, 2)
|
||||
}
|
||||
}
|
||||
@ -1,685 +0,0 @@
|
||||
// Copyright 2019 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,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gdb_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/gogf/gf/g"
|
||||
"github.com/gogf/gf/g/os/gtime"
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
)
|
||||
|
||||
// 基本测试
|
||||
func TestModel_Insert(t *testing.T) {
|
||||
result, err := db.Table("user").Filter().Data(g.Map{
|
||||
"id": 1,
|
||||
"uid": 1,
|
||||
"passport": "t1",
|
||||
"password": "25d55ad283aa400af464c76d713c07ad",
|
||||
"nickname": "T1",
|
||||
"create_time": gtime.Now().String(),
|
||||
}).Insert()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
n, _ := result.LastInsertId()
|
||||
gtest.Assert(n, 1)
|
||||
|
||||
result, err = db.Table("user").Filter().Data(map[interface{}]interface{}{
|
||||
"id": "2",
|
||||
"uid": "2",
|
||||
"passport": "t2",
|
||||
"password": "25d55ad283aa400af464c76d713c07ad",
|
||||
"nickname": "T2",
|
||||
"create_time": gtime.Now().String(),
|
||||
}).Insert()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
n, _ = result.RowsAffected()
|
||||
gtest.Assert(n, 1)
|
||||
|
||||
type User struct {
|
||||
Id int `gconv:"id"`
|
||||
Uid int `gconv:"uid"`
|
||||
Passport string `json:"passport"`
|
||||
Password string `gconv:"password"`
|
||||
Nickname string `gconv:"nickname"`
|
||||
CreateTime string `json:"create_time"`
|
||||
}
|
||||
result, err = db.Table("user").Filter().Data(User{
|
||||
Id: 3,
|
||||
Uid: 3,
|
||||
Passport: "t3",
|
||||
Password: "25d55ad283aa400af464c76d713c07ad",
|
||||
Nickname: "T3",
|
||||
CreateTime: gtime.Now().String(),
|
||||
}).Insert()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
n, _ = result.RowsAffected()
|
||||
gtest.Assert(n, 1)
|
||||
value, err := db.Table("user").Fields("passport").Where("id=3").Value()
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(value.String(), "t3")
|
||||
|
||||
result, err = db.Table("user").Filter().Data(&User{
|
||||
Id: 4,
|
||||
Uid: 4,
|
||||
Passport: "t4",
|
||||
Password: "25d55ad283aa400af464c76d713c07ad",
|
||||
Nickname: "T4",
|
||||
CreateTime: gtime.Now().String(),
|
||||
}).Insert()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
n, _ = result.RowsAffected()
|
||||
gtest.Assert(n, 1)
|
||||
value, err = db.Table("user").Fields("passport").Where("id=4").Value()
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(value.String(), "t4")
|
||||
|
||||
result, err = db.Table("user").Where("id>?", 1).Delete()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
n, _ = result.RowsAffected()
|
||||
gtest.Assert(n, 3)
|
||||
}
|
||||
|
||||
func TestModel_Batch(t *testing.T) {
|
||||
// batch insert
|
||||
gtest.Case(t, func() {
|
||||
result, err := db.Table("user").Filter().Data(g.List{
|
||||
{
|
||||
"id": 2,
|
||||
"uid": 2,
|
||||
"passport": "t2",
|
||||
"password": "25d55ad283aa400af464c76d713c07ad",
|
||||
"nickname": "T2",
|
||||
"create_time": gtime.Now().String(),
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"uid": 3,
|
||||
"passport": "t3",
|
||||
"password": "25d55ad283aa400af464c76d713c07ad",
|
||||
"nickname": "T3",
|
||||
"create_time": gtime.Now().String(),
|
||||
},
|
||||
}).Batch(1).Insert()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
n, _ := result.RowsAffected()
|
||||
gtest.Assert(n, 2)
|
||||
})
|
||||
|
||||
// batch save
|
||||
gtest.Case(t, func() {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
result, err := db.Table(table).All()
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(len(result), INIT_DATA_SIZE)
|
||||
for _, v := range result {
|
||||
v["nickname"].Set(v["nickname"].String() + v["id"].String())
|
||||
}
|
||||
r, e := db.Table(table).Data(result).Save()
|
||||
gtest.Assert(e, nil)
|
||||
n, e := r.RowsAffected()
|
||||
gtest.Assert(e, nil)
|
||||
gtest.Assert(n, INIT_DATA_SIZE*2)
|
||||
})
|
||||
|
||||
// batch replace
|
||||
gtest.Case(t, func() {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
result, err := db.Table(table).All()
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(len(result), INIT_DATA_SIZE)
|
||||
for _, v := range result {
|
||||
v["nickname"].Set(v["nickname"].String() + v["id"].String())
|
||||
}
|
||||
r, e := db.Table(table).Data(result).Replace()
|
||||
gtest.Assert(e, nil)
|
||||
n, e := r.RowsAffected()
|
||||
gtest.Assert(e, nil)
|
||||
gtest.Assert(n, INIT_DATA_SIZE*2)
|
||||
})
|
||||
}
|
||||
|
||||
func TestModel_Replace(t *testing.T) {
|
||||
result, err := db.Table("user").Data(g.Map{
|
||||
"id": 1,
|
||||
"passport": "t11",
|
||||
"password": "25d55ad283aa400af464c76d713c07ad",
|
||||
"nickname": "T11",
|
||||
"create_time": "2018-10-10 00:01:10",
|
||||
}).Replace()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
n, _ := result.RowsAffected()
|
||||
gtest.Assert(n, 2)
|
||||
}
|
||||
|
||||
func TestModel_Save(t *testing.T) {
|
||||
result, err := db.Table("user").Data(g.Map{
|
||||
"id": 1,
|
||||
"passport": "t111",
|
||||
"password": "25d55ad283aa400af464c76d713c07ad",
|
||||
"nickname": "T111",
|
||||
"create_time": "2018-10-10 00:01:10",
|
||||
}).Save()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
n, _ := result.RowsAffected()
|
||||
gtest.Assert(n, 2)
|
||||
}
|
||||
|
||||
func TestModel_Update(t *testing.T) {
|
||||
table := createInitTable()
|
||||
// UPDATE...LIMIT
|
||||
gtest.Case(t, func() {
|
||||
result, err := db.Table(table).Data("nickname", "T100").OrderBy("id desc").Limit(2).Update()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
n, _ := result.RowsAffected()
|
||||
gtest.Assert(n, 2)
|
||||
|
||||
v1, err := db.Table(table).Fields("nickname").Where("id", 10).Value()
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(v1.String(), "T100")
|
||||
|
||||
v2, err := db.Table(table).Fields("nickname").Where("id", 8).Value()
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(v2.String(), "T8")
|
||||
})
|
||||
|
||||
gtest.Case(t, func() {
|
||||
result, err := db.Table("user").Data("passport", "t22").Where("passport=?", "t2").Update()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
n, _ := result.RowsAffected()
|
||||
gtest.Assert(n, 1)
|
||||
})
|
||||
|
||||
gtest.Case(t, func() {
|
||||
result, err := db.Table("user").Data("passport", "t2").Where("passport='t22'").Update()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
n, _ := result.RowsAffected()
|
||||
gtest.Assert(n, 1)
|
||||
})
|
||||
}
|
||||
|
||||
func TestModel_Clone(t *testing.T) {
|
||||
md := db.Table("user").Where("id IN(?)", g.Slice{1, 3})
|
||||
count, err := md.Count()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
record, err := md.OrderBy("id DESC").One()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
result, err := md.OrderBy("id ASC").All()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
gtest.Assert(count, 2)
|
||||
gtest.Assert(record["id"].Int(), 3)
|
||||
gtest.Assert(len(result), 2)
|
||||
gtest.Assert(result[0]["id"].Int(), 1)
|
||||
gtest.Assert(result[1]["id"].Int(), 3)
|
||||
}
|
||||
|
||||
func TestModel_Safe(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
md := db.Table("user").Safe(false).Where("id IN(?)", g.Slice{1, 3})
|
||||
count, err := md.Count()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
gtest.Assert(count, 2)
|
||||
md.And("id = ?", 1)
|
||||
count, err = md.Count()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
gtest.Assert(count, 1)
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
md := db.Table("user").Safe(true).Where("id IN(?)", g.Slice{1, 3})
|
||||
count, err := md.Count()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
gtest.Assert(count, 2)
|
||||
md.And("id = ?", 1)
|
||||
count, err = md.Count()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
gtest.Assert(count, 2)
|
||||
})
|
||||
}
|
||||
|
||||
func TestModel_All(t *testing.T) {
|
||||
result, err := db.Table("user").All()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
gtest.Assert(len(result), 3)
|
||||
}
|
||||
|
||||
func TestModel_One(t *testing.T) {
|
||||
record, err := db.Table("user").Where("id", 1).One()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
if record == nil {
|
||||
gtest.Fatal("FAIL")
|
||||
}
|
||||
gtest.Assert(record["nickname"].String(), "T111")
|
||||
}
|
||||
|
||||
func TestModel_Value(t *testing.T) {
|
||||
value, err := db.Table("user").Fields("nickname").Where("id", 1).Value()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
if value == nil {
|
||||
gtest.Fatal("FAIL")
|
||||
}
|
||||
gtest.Assert(value.String(), "T111")
|
||||
}
|
||||
|
||||
func TestModel_Count(t *testing.T) {
|
||||
count, err := db.Table("user").Count()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
gtest.Assert(count, 3)
|
||||
}
|
||||
|
||||
func TestModel_Select(t *testing.T) {
|
||||
result, err := db.Table("user").Select()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
gtest.Assert(len(result), 3)
|
||||
}
|
||||
|
||||
func TestModel_Struct(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
type User struct {
|
||||
Id int
|
||||
Passport string
|
||||
Password string
|
||||
NickName string
|
||||
CreateTime gtime.Time
|
||||
}
|
||||
user := new(User)
|
||||
err := db.Table("user").Where("id=1").Struct(user)
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
gtest.Assert(user.NickName, "T111")
|
||||
gtest.Assert(user.CreateTime.String(), "2018-10-10 00:01:10")
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
type User struct {
|
||||
Id int
|
||||
Passport string
|
||||
Password string
|
||||
NickName string
|
||||
CreateTime *gtime.Time
|
||||
}
|
||||
user := new(User)
|
||||
err := db.Table("user").Where("id=1").Struct(user)
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
gtest.Assert(user.NickName, "T111")
|
||||
gtest.Assert(user.CreateTime.String(), "2018-10-10 00:01:10")
|
||||
})
|
||||
}
|
||||
|
||||
func TestModel_Structs(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
type User struct {
|
||||
Id int
|
||||
Passport string
|
||||
Password string
|
||||
NickName string
|
||||
CreateTime gtime.Time
|
||||
}
|
||||
var users []User
|
||||
err := db.Table("user").OrderBy("id asc").Structs(&users)
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
gtest.Assert(len(users), 3)
|
||||
gtest.Assert(users[0].Id, 1)
|
||||
gtest.Assert(users[1].Id, 2)
|
||||
gtest.Assert(users[2].Id, 3)
|
||||
gtest.Assert(users[0].NickName, "T111")
|
||||
gtest.Assert(users[1].NickName, "T2")
|
||||
gtest.Assert(users[2].NickName, "T3")
|
||||
gtest.Assert(users[0].CreateTime.String(), "2018-10-10 00:01:10")
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
type User struct {
|
||||
Id int
|
||||
Passport string
|
||||
Password string
|
||||
NickName string
|
||||
CreateTime *gtime.Time
|
||||
}
|
||||
var users []*User
|
||||
err := db.Table("user").OrderBy("id asc").Structs(&users)
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
gtest.Assert(len(users), 3)
|
||||
gtest.Assert(users[0].Id, 1)
|
||||
gtest.Assert(users[1].Id, 2)
|
||||
gtest.Assert(users[2].Id, 3)
|
||||
gtest.Assert(users[0].NickName, "T111")
|
||||
gtest.Assert(users[1].NickName, "T2")
|
||||
gtest.Assert(users[2].NickName, "T3")
|
||||
gtest.Assert(users[0].CreateTime.String(), "2018-10-10 00:01:10")
|
||||
})
|
||||
}
|
||||
|
||||
func TestModel_Scan(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
type User struct {
|
||||
Id int
|
||||
Passport string
|
||||
Password string
|
||||
NickName string
|
||||
CreateTime gtime.Time
|
||||
}
|
||||
user := new(User)
|
||||
err := db.Table("user").Where("id=1").Scan(user)
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
gtest.Assert(user.NickName, "T111")
|
||||
gtest.Assert(user.CreateTime.String(), "2018-10-10 00:01:10")
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
type User struct {
|
||||
Id int
|
||||
Passport string
|
||||
Password string
|
||||
NickName string
|
||||
CreateTime *gtime.Time
|
||||
}
|
||||
user := new(User)
|
||||
err := db.Table("user").Where("id=1").Scan(user)
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
gtest.Assert(user.NickName, "T111")
|
||||
gtest.Assert(user.CreateTime.String(), "2018-10-10 00:01:10")
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
type User struct {
|
||||
Id int
|
||||
Passport string
|
||||
Password string
|
||||
NickName string
|
||||
CreateTime gtime.Time
|
||||
}
|
||||
var users []User
|
||||
err := db.Table("user").OrderBy("id asc").Scan(&users)
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
gtest.Assert(len(users), 3)
|
||||
gtest.Assert(users[0].Id, 1)
|
||||
gtest.Assert(users[1].Id, 2)
|
||||
gtest.Assert(users[2].Id, 3)
|
||||
gtest.Assert(users[0].NickName, "T111")
|
||||
gtest.Assert(users[1].NickName, "T2")
|
||||
gtest.Assert(users[2].NickName, "T3")
|
||||
gtest.Assert(users[0].CreateTime.String(), "2018-10-10 00:01:10")
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
type User struct {
|
||||
Id int
|
||||
Passport string
|
||||
Password string
|
||||
NickName string
|
||||
CreateTime *gtime.Time
|
||||
}
|
||||
var users []*User
|
||||
err := db.Table("user").OrderBy("id asc").Scan(&users)
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
gtest.Assert(len(users), 3)
|
||||
gtest.Assert(users[0].Id, 1)
|
||||
gtest.Assert(users[1].Id, 2)
|
||||
gtest.Assert(users[2].Id, 3)
|
||||
gtest.Assert(users[0].NickName, "T111")
|
||||
gtest.Assert(users[1].NickName, "T2")
|
||||
gtest.Assert(users[2].NickName, "T3")
|
||||
gtest.Assert(users[0].CreateTime.String(), "2018-10-10 00:01:10")
|
||||
})
|
||||
}
|
||||
|
||||
func TestModel_OrderBy(t *testing.T) {
|
||||
result, err := db.Table("user").OrderBy("id DESC").Select()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
gtest.Assert(len(result), 3)
|
||||
gtest.Assert(result[0]["nickname"].String(), "T3")
|
||||
}
|
||||
|
||||
func TestModel_GroupBy(t *testing.T) {
|
||||
result, err := db.Table("user").GroupBy("id").Select()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
gtest.Assert(len(result), 3)
|
||||
gtest.Assert(result[0]["nickname"].String(), "T111")
|
||||
}
|
||||
|
||||
func TestModel_Where(t *testing.T) {
|
||||
// string
|
||||
gtest.Case(t, func() {
|
||||
result, err := db.Table("user").Where("id=? and nickname=?", 3, "T3").One()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
gtest.AssertGT(len(result), 0)
|
||||
gtest.Assert(result["id"].Int(), 3)
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
result, err := db.Table("user").Where("id", 3).One()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
gtest.AssertGT(len(result), 0)
|
||||
gtest.Assert(result["id"].Int(), 3)
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
result, err := db.Table("user").Where("id", 3).Where("nickname", "T3").One()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
gtest.Assert(result["id"].Int(), 3)
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
result, err := db.Table("user").Where("id", 3).And("nickname", "T3").One()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
gtest.Assert(result["id"].Int(), 3)
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
result, err := db.Table("user").Where("id", 30).Or("nickname", "T3").One()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
gtest.Assert(result["id"].Int(), 3)
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
result, err := db.Table("user").Where("id", 30).Or("nickname", "T3").And("id>?", 1).One()
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(result["id"].Int(), 3)
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
result, err := db.Table("user").Where("id", 30).Or("nickname", "T3").And("id>", 1).One()
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(result["id"].Int(), 3)
|
||||
})
|
||||
// map
|
||||
gtest.Case(t, func() {
|
||||
result, err := db.Table("user").Where(g.Map{"id": 3, "nickname": "T3"}).One()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
gtest.Assert(result["id"].Int(), 3)
|
||||
})
|
||||
// map key operator
|
||||
gtest.Case(t, func() {
|
||||
result, err := db.Table("user").Where(g.Map{"id>": 1, "id<": 3}).One()
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(result["id"].Int(), 2)
|
||||
})
|
||||
// complicated where 1
|
||||
gtest.Case(t, func() {
|
||||
//db.SetDebug(true)
|
||||
conditions := g.Map{
|
||||
"nickname like ?": "%T%",
|
||||
"id between ? and ?": g.Slice{1, 3},
|
||||
"id > 0": nil,
|
||||
"create_time > 0": nil,
|
||||
"id": g.Slice{1, 2, 3},
|
||||
}
|
||||
result, err := db.Table("user").Where(conditions).OrderBy("id asc").All()
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(len(result), 3)
|
||||
gtest.Assert(result[0]["id"].Int(), 1)
|
||||
})
|
||||
// complicated where 2
|
||||
gtest.Case(t, func() {
|
||||
//db.SetDebug(true)
|
||||
conditions := g.Map{
|
||||
"nickname like ?": "%T%",
|
||||
"id between ? and ?": g.Slice{1, 3},
|
||||
"id >= ?": 1,
|
||||
"create_time > ?": 0,
|
||||
"id in(?)": g.Slice{1, 2, 3},
|
||||
}
|
||||
result, err := db.Table("user").Where(conditions).OrderBy("id asc").All()
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(len(result), 3)
|
||||
gtest.Assert(result[0]["id"].Int(), 1)
|
||||
})
|
||||
// struct
|
||||
gtest.Case(t, func() {
|
||||
type User struct {
|
||||
Id int `json:"id"`
|
||||
Nickname string `gconv:"nickname"`
|
||||
}
|
||||
result, err := db.Table("user").Where(User{3, "T3"}).One()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
gtest.Assert(result["id"].Int(), 3)
|
||||
|
||||
result, err = db.Table("user").Where(&User{3, "T3"}).One()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
gtest.Assert(result["id"].Int(), 3)
|
||||
})
|
||||
// slice single
|
||||
gtest.Case(t, func() {
|
||||
result, err := db.Table("user").Where("id IN(?)", g.Slice{1, 3}).OrderBy("id ASC").All()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
gtest.Assert(len(result), 2)
|
||||
gtest.Assert(result[0]["id"].Int(), 1)
|
||||
gtest.Assert(result[1]["id"].Int(), 3)
|
||||
})
|
||||
// slice + string
|
||||
gtest.Case(t, func() {
|
||||
result, err := db.Table("user").Where("nickname=? AND id IN(?)", "T3", g.Slice{1, 3}).OrderBy("id ASC").All()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
gtest.Assert(len(result), 1)
|
||||
gtest.Assert(result[0]["id"].Int(), 3)
|
||||
})
|
||||
// slice + map
|
||||
gtest.Case(t, func() {
|
||||
result, err := db.Table("user").Where(g.Map{
|
||||
"id": g.Slice{1, 3},
|
||||
"nickname": "T3",
|
||||
}).OrderBy("id ASC").All()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
gtest.Assert(len(result), 1)
|
||||
gtest.Assert(result[0]["id"].Int(), 3)
|
||||
})
|
||||
// slice + struct
|
||||
gtest.Case(t, func() {
|
||||
type User struct {
|
||||
Ids []int `json:"id"`
|
||||
Nickname string `gconv:"nickname"`
|
||||
}
|
||||
result, err := db.Table("user").Where(User{
|
||||
Ids: []int{1, 3},
|
||||
Nickname: "T3",
|
||||
}).OrderBy("id ASC").All()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
gtest.Assert(len(result), 1)
|
||||
gtest.Assert(result[0]["id"].Int(), 3)
|
||||
})
|
||||
}
|
||||
|
||||
func TestModel_Delete(t *testing.T) {
|
||||
// DELETE...LIMIT
|
||||
gtest.Case(t, func() {
|
||||
result, err := db.Table("user").Limit(2).Delete()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
n, _ := result.RowsAffected()
|
||||
gtest.Assert(n, 2)
|
||||
})
|
||||
|
||||
gtest.Case(t, func() {
|
||||
result, err := db.Table("user").Delete()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
n, _ := result.RowsAffected()
|
||||
gtest.Assert(n, 1)
|
||||
})
|
||||
}
|
||||
@ -1,588 +0,0 @@
|
||||
// Copyright 2019 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,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gdb_test
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g"
|
||||
"github.com/gogf/gf/g/os/gtime"
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestTX_Query(t *testing.T) {
|
||||
tx, err := db.Begin()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
if rows, err := tx.Query("SELECT ?", 1); err != nil {
|
||||
gtest.Fatal(err)
|
||||
} else {
|
||||
rows.Close()
|
||||
}
|
||||
if _, err := tx.Query("ERROR"); err == nil {
|
||||
gtest.Fatal("FAIL")
|
||||
}
|
||||
if err := tx.Commit(); err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTX_Exec(t *testing.T) {
|
||||
tx, err := db.Begin()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
if _, err := tx.Exec("SELECT ?", 1); err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
if _, err := tx.Exec("ERROR"); err == nil {
|
||||
gtest.Fatal("FAIL")
|
||||
}
|
||||
if err := tx.Commit(); err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTX_Commit(t *testing.T) {
|
||||
tx, err := db.Begin()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
if err := tx.Commit(); err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTX_Rollback(t *testing.T) {
|
||||
tx, err := db.Begin()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
if err := tx.Rollback(); err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTX_Prepare(t *testing.T) {
|
||||
tx, err := db.Begin()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
st, err := tx.Prepare("SELECT 100")
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
rows, err := st.Query()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
array, err := rows.Columns()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
gtest.Assert(array[0], "100")
|
||||
if err := rows.Close(); err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
if err := tx.Commit(); err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTX_Insert(t *testing.T) {
|
||||
tx, err := db.Begin()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
if _, err := tx.Insert("user", g.Map{
|
||||
"id": 1,
|
||||
"passport": "t1",
|
||||
"password": "25d55ad283aa400af464c76d713c07ad",
|
||||
"nickname": "T1",
|
||||
"create_time": gtime.Now().String(),
|
||||
}); err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
if err := tx.Commit(); err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
if n, err := db.Table("user").Count(); err != nil {
|
||||
gtest.Fatal(err)
|
||||
} else {
|
||||
gtest.Assert(n, 1)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTX_BatchInsert(t *testing.T) {
|
||||
tx, err := db.Begin()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
if _, err := tx.BatchInsert("user", g.List{
|
||||
{
|
||||
"id": 2,
|
||||
"passport": "t",
|
||||
"password": "25d55ad283aa400af464c76d713c07ad",
|
||||
"nickname": "T2",
|
||||
"create_time": gtime.Now().String(),
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"passport": "t3",
|
||||
"password": "25d55ad283aa400af464c76d713c07ad",
|
||||
"nickname": "T3",
|
||||
"create_time": gtime.Now().String(),
|
||||
},
|
||||
}, 10); err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
if err := tx.Commit(); err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
if n, err := db.Table("user").Count(); err != nil {
|
||||
gtest.Fatal(err)
|
||||
} else {
|
||||
gtest.Assert(n, 3)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTX_BatchReplace(t *testing.T) {
|
||||
tx, err := db.Begin()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
if _, err := tx.BatchReplace("user", g.List{
|
||||
{
|
||||
"id": 2,
|
||||
"passport": "t2",
|
||||
"password": "p2",
|
||||
"nickname": "T2",
|
||||
"create_time": gtime.Now().String(),
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"passport": "t4",
|
||||
"password": "25d55ad283aa400af464c76d713c07ad",
|
||||
"nickname": "T4",
|
||||
"create_time": gtime.Now().String(),
|
||||
},
|
||||
}, 10); err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
if err := tx.Commit(); err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
// 数据数量
|
||||
if n, err := db.Table("user").Count(); err != nil {
|
||||
gtest.Fatal(err)
|
||||
} else {
|
||||
gtest.Assert(n, 4)
|
||||
}
|
||||
// 检查replace后的数值
|
||||
if value, err := db.Table("user").Fields("password").Where("id", 2).Value(); err != nil {
|
||||
gtest.Fatal(err)
|
||||
} else {
|
||||
gtest.Assert(value.String(), "p2")
|
||||
}
|
||||
}
|
||||
|
||||
func TestTX_BatchSave(t *testing.T) {
|
||||
tx, err := db.Begin()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
if _, err := tx.BatchSave("user", g.List{
|
||||
{
|
||||
"id": 4,
|
||||
"passport": "t4",
|
||||
"password": "p4",
|
||||
"nickname": "T4",
|
||||
"create_time": gtime.Now().String(),
|
||||
},
|
||||
}, 10); err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
if err := tx.Commit(); err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
// 数据数量
|
||||
if n, err := db.Table("user").Count(); err != nil {
|
||||
gtest.Fatal(err)
|
||||
} else {
|
||||
gtest.Assert(n, 4)
|
||||
}
|
||||
// 检查replace后的数值
|
||||
if value, err := db.Table("user").Fields("password").Where("id", 4).Value(); err != nil {
|
||||
gtest.Fatal(err)
|
||||
} else {
|
||||
gtest.Assert(value.String(), "p4")
|
||||
}
|
||||
}
|
||||
|
||||
func TestTX_Replace(t *testing.T) {
|
||||
tx, err := db.Begin()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
if _, err := tx.Replace("user", g.Map{
|
||||
"id": 1,
|
||||
"passport": "t11",
|
||||
"password": "25d55ad283aa400af464c76d713c07ad",
|
||||
"nickname": "T11",
|
||||
"create_time": gtime.Now().String(),
|
||||
}); err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
if err := tx.Rollback(); err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
if value, err := db.Table("user").Fields("nickname").Where("id", 1).Value(); err != nil {
|
||||
gtest.Fatal(err)
|
||||
} else {
|
||||
gtest.Assert(value.String(), "T1")
|
||||
}
|
||||
}
|
||||
|
||||
func TestTX_Save(t *testing.T) {
|
||||
tx, err := db.Begin()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
if _, err := tx.Save("user", g.Map{
|
||||
"id": 1,
|
||||
"passport": "t11",
|
||||
"password": "25d55ad283aa400af464c76d713c07ad",
|
||||
"nickname": "T11",
|
||||
"create_time": gtime.Now().String(),
|
||||
}); err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
if err := tx.Commit(); err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
if value, err := db.Table("user").Fields("nickname").Where("id", 1).Value(); err != nil {
|
||||
gtest.Fatal(err)
|
||||
} else {
|
||||
gtest.Assert(value.String(), "T11")
|
||||
}
|
||||
}
|
||||
|
||||
func TestTX_Update(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
tx, err := db.Begin()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
if result, err := db.Update("user", "create_time='2010-10-10 00:00:01'", "id=3"); err != nil {
|
||||
gtest.Fatal(err)
|
||||
} else {
|
||||
n, _ := result.RowsAffected()
|
||||
gtest.Assert(n, 1)
|
||||
}
|
||||
if err := tx.Commit(); err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
if value, err := db.Table("user").Fields("create_time").Where("id", 3).Value(); err != nil {
|
||||
gtest.Fatal(err)
|
||||
} else {
|
||||
gtest.Assert(value.String(), "2010-10-10 00:00:01")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestTX_GetAll(t *testing.T) {
|
||||
tx, err := db.Begin()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
if result, err := tx.GetAll("SELECT * FROM user WHERE id=?", 1); err != nil {
|
||||
gtest.Fatal(err)
|
||||
} else {
|
||||
gtest.Assert(len(result), 1)
|
||||
}
|
||||
if err := tx.Commit(); err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTX_GetOne(t *testing.T) {
|
||||
tx, err := db.Begin()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
if record, err := tx.GetOne("SELECT * FROM user WHERE passport=?", "t2"); err != nil {
|
||||
gtest.Fatal(err)
|
||||
} else {
|
||||
if record == nil {
|
||||
gtest.Fatal("FAIL")
|
||||
}
|
||||
gtest.Assert(record["nickname"].String(), "T2")
|
||||
}
|
||||
if err := tx.Commit(); err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTX_GetValue(t *testing.T) {
|
||||
tx, err := db.Begin()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
if value, err := tx.GetValue("SELECT id FROM user WHERE passport=?", "t3"); err != nil {
|
||||
gtest.Fatal(err)
|
||||
} else {
|
||||
gtest.Assert(value.Int(), 3)
|
||||
}
|
||||
if err := tx.Commit(); err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTX_GetCount(t *testing.T) {
|
||||
tx, err := db.Begin()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
if count, err := tx.GetCount("SELECT * FROM user"); err != nil {
|
||||
gtest.Fatal(err)
|
||||
} else {
|
||||
gtest.Assert(count, 4)
|
||||
}
|
||||
if err := tx.Commit(); err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTX_GetStruct(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
tx, err := db.Begin()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
type User struct {
|
||||
Id int
|
||||
Passport string
|
||||
Password string
|
||||
NickName string
|
||||
CreateTime gtime.Time
|
||||
}
|
||||
user := new(User)
|
||||
if err := tx.GetStruct(user, "SELECT * FROM user WHERE id=?", 3); err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
gtest.Assert(user.NickName, "T3")
|
||||
gtest.Assert(user.CreateTime.String(), "2010-10-10 00:00:01")
|
||||
if err := tx.Commit(); err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
tx, err := db.Begin()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
type User struct {
|
||||
Id int
|
||||
Passport string
|
||||
Password string
|
||||
NickName string
|
||||
CreateTime *gtime.Time
|
||||
}
|
||||
user := new(User)
|
||||
if err := tx.GetStruct(user, "SELECT * FROM user WHERE id=?", 3); err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
gtest.Assert(user.NickName, "T3")
|
||||
gtest.Assert(user.CreateTime.String(), "2010-10-10 00:00:01")
|
||||
if err := tx.Commit(); err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestTX_GetStructs(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
tx, err := db.Begin()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
type User struct {
|
||||
Id int
|
||||
Passport string
|
||||
Password string
|
||||
NickName string
|
||||
CreateTime gtime.Time
|
||||
}
|
||||
var users []User
|
||||
if err := tx.GetStructs(&users, "SELECT * FROM user WHERE id>=?", 1); err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
gtest.Assert(len(users), 4)
|
||||
gtest.Assert(users[0].Id, 1)
|
||||
gtest.Assert(users[1].Id, 2)
|
||||
gtest.Assert(users[2].Id, 3)
|
||||
gtest.Assert(users[0].NickName, "T11")
|
||||
gtest.Assert(users[1].NickName, "T2")
|
||||
gtest.Assert(users[2].NickName, "T3")
|
||||
gtest.Assert(users[2].CreateTime.String(), "2010-10-10 00:00:01")
|
||||
if err := tx.Commit(); err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
})
|
||||
|
||||
gtest.Case(t, func() {
|
||||
tx, err := db.Begin()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
type User struct {
|
||||
Id int
|
||||
Passport string
|
||||
Password string
|
||||
NickName string
|
||||
CreateTime *gtime.Time
|
||||
}
|
||||
var users []User
|
||||
if err := tx.GetStructs(&users, "SELECT * FROM user WHERE id>=?", 1); err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
gtest.Assert(len(users), 4)
|
||||
gtest.Assert(users[0].Id, 1)
|
||||
gtest.Assert(users[1].Id, 2)
|
||||
gtest.Assert(users[2].Id, 3)
|
||||
gtest.Assert(users[0].NickName, "T11")
|
||||
gtest.Assert(users[1].NickName, "T2")
|
||||
gtest.Assert(users[2].NickName, "T3")
|
||||
gtest.Assert(users[2].CreateTime.String(), "2010-10-10 00:00:01")
|
||||
if err := tx.Commit(); err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestTX_GetScan(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
tx, err := db.Begin()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
type User struct {
|
||||
Id int
|
||||
Passport string
|
||||
Password string
|
||||
NickName string
|
||||
CreateTime gtime.Time
|
||||
}
|
||||
user := new(User)
|
||||
if err := tx.GetScan(user, "SELECT * FROM user WHERE id=?", 3); err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
gtest.Assert(user.NickName, "T3")
|
||||
gtest.Assert(user.CreateTime.String(), "2010-10-10 00:00:01")
|
||||
if err := tx.Commit(); err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
tx, err := db.Begin()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
type User struct {
|
||||
Id int
|
||||
Passport string
|
||||
Password string
|
||||
NickName string
|
||||
CreateTime *gtime.Time
|
||||
}
|
||||
user := new(User)
|
||||
if err := tx.GetScan(user, "SELECT * FROM user WHERE id=?", 3); err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
gtest.Assert(user.NickName, "T3")
|
||||
gtest.Assert(user.CreateTime.String(), "2010-10-10 00:00:01")
|
||||
if err := tx.Commit(); err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
})
|
||||
|
||||
gtest.Case(t, func() {
|
||||
tx, err := db.Begin()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
type User struct {
|
||||
Id int
|
||||
Passport string
|
||||
Password string
|
||||
NickName string
|
||||
CreateTime gtime.Time
|
||||
}
|
||||
var users []User
|
||||
if err := tx.GetScan(&users, "SELECT * FROM user WHERE id>=?", 1); err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
gtest.Assert(len(users), 4)
|
||||
gtest.Assert(users[0].Id, 1)
|
||||
gtest.Assert(users[1].Id, 2)
|
||||
gtest.Assert(users[2].Id, 3)
|
||||
gtest.Assert(users[0].NickName, "T11")
|
||||
gtest.Assert(users[1].NickName, "T2")
|
||||
gtest.Assert(users[2].NickName, "T3")
|
||||
gtest.Assert(users[2].CreateTime.String(), "2010-10-10 00:00:01")
|
||||
if err := tx.Commit(); err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
})
|
||||
|
||||
gtest.Case(t, func() {
|
||||
tx, err := db.Begin()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
type User struct {
|
||||
Id int
|
||||
Passport string
|
||||
Password string
|
||||
NickName string
|
||||
CreateTime *gtime.Time
|
||||
}
|
||||
var users []User
|
||||
if err := tx.GetScan(&users, "SELECT * FROM user WHERE id>=?", 1); err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
gtest.Assert(len(users), 4)
|
||||
gtest.Assert(users[0].Id, 1)
|
||||
gtest.Assert(users[1].Id, 2)
|
||||
gtest.Assert(users[2].Id, 3)
|
||||
gtest.Assert(users[0].NickName, "T11")
|
||||
gtest.Assert(users[1].NickName, "T2")
|
||||
gtest.Assert(users[2].NickName, "T3")
|
||||
gtest.Assert(users[2].CreateTime.String(), "2010-10-10 00:00:01")
|
||||
if err := tx.Commit(); err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestTX_Delete(t *testing.T) {
|
||||
tx, err := db.Begin()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
if _, err := tx.Delete("user", nil); err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
if err := tx.Commit(); err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
if n, err := db.Table("user").Count(); err != nil {
|
||||
gtest.Fatal(err)
|
||||
} else {
|
||||
gtest.Assert(n, 0)
|
||||
}
|
||||
}
|
||||
60
g/database/gdb/gdb_unit_types_test.go
Normal file
60
g/database/gdb/gdb_unit_types_test.go
Normal file
@ -0,0 +1,60 @@
|
||||
// Copyright 2019 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,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gdb_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/gogf/gf/g"
|
||||
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
)
|
||||
|
||||
func Test_Types(t *testing.T) {
|
||||
|
||||
gtest.Case(t, func() {
|
||||
if _, err := db.Exec(fmt.Sprintf(`
|
||||
CREATE TABLE IF NOT EXISTS types (
|
||||
id int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||
%s blob NOT NULL,
|
||||
%s binary(8) NOT NULL,
|
||||
%s date NOT NULL,
|
||||
%s decimal(5,2) NOT NULL,
|
||||
%s double NOT NULL,
|
||||
%s bit(2) NOT NULL,
|
||||
PRIMARY KEY (id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
`, "`blob`", "`binary`", "`date`", "`decimal`", "`double`", "`bit`")); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
defer dropTable("types")
|
||||
data := g.Map{
|
||||
"id": 1,
|
||||
"blob": "i love gf",
|
||||
"binary": []byte("abcdefgh"),
|
||||
"date": "2018-10-24",
|
||||
"decimal": 123.456,
|
||||
"double": 123.456,
|
||||
"bit": 2,
|
||||
}
|
||||
r, err := db.Table("types").Data(data).Insert()
|
||||
gtest.Assert(err, nil)
|
||||
n, _ := r.RowsAffected()
|
||||
gtest.Assert(n, 1)
|
||||
|
||||
one, err := db.Table("types").One()
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(one["id"].Int(), 1)
|
||||
gtest.Assert(one["blob"].String(), data["blob"])
|
||||
gtest.Assert(one["binary"].String(), data["binary"])
|
||||
gtest.Assert(one["date"].String(), data["date"])
|
||||
gtest.Assert(one["decimal"].String(), 123.46)
|
||||
gtest.Assert(one["double"].String(), data["double"])
|
||||
gtest.Assert(one["bit"].Int(), data["bit"])
|
||||
})
|
||||
}
|
||||
627
g/database/gdb/gdb_unit_z_method_test.go
Normal file
627
g/database/gdb/gdb_unit_z_method_test.go
Normal file
@ -0,0 +1,627 @@
|
||||
// Copyright 2019 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,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gdb_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/g"
|
||||
"github.com/gogf/gf/g/os/gtime"
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
)
|
||||
|
||||
func Test_DB_Ping(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
err1 := db.PingMaster()
|
||||
err2 := db.PingSlave()
|
||||
gtest.Assert(err1, nil)
|
||||
gtest.Assert(err2, nil)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_DB_Query(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
_, err := db.Query("SELECT ?", 1)
|
||||
gtest.Assert(err, nil)
|
||||
|
||||
_, err = db.Query("SELECT ?+?", 1, 2)
|
||||
gtest.Assert(err, nil)
|
||||
|
||||
_, err = db.Query("SELECT ?+?", g.Slice{1, 2})
|
||||
gtest.Assert(err, nil)
|
||||
|
||||
_, err = db.Query("ERROR")
|
||||
gtest.AssertNE(err, nil)
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func Test_DB_Exec(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
_, err := db.Exec("SELECT ?", 1)
|
||||
gtest.Assert(err, nil)
|
||||
|
||||
_, err = db.Exec("ERROR")
|
||||
gtest.AssertNE(err, nil)
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func Test_DB_Prepare(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
st, err := db.Prepare("SELECT 100")
|
||||
gtest.Assert(err, nil)
|
||||
|
||||
rows, err := st.Query()
|
||||
gtest.Assert(err, nil)
|
||||
|
||||
array, err := rows.Columns()
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(array[0], "100")
|
||||
|
||||
err = rows.Close()
|
||||
gtest.Assert(err, nil)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_DB_Insert(t *testing.T) {
|
||||
table := createTable()
|
||||
defer dropTable(table)
|
||||
|
||||
gtest.Case(t, func() {
|
||||
_, err := db.Insert(table, g.Map{
|
||||
"id": 1,
|
||||
"passport": "t1",
|
||||
"password": "25d55ad283aa400af464c76d713c07ad",
|
||||
"nickname": "T1",
|
||||
"create_time": gtime.Now().String(),
|
||||
})
|
||||
gtest.Assert(err, nil)
|
||||
|
||||
// normal map
|
||||
result, err := db.Insert(table, g.Map{
|
||||
"id": "2",
|
||||
"passport": "t2",
|
||||
"password": "25d55ad283aa400af464c76d713c07ad",
|
||||
"nickname": "name_2",
|
||||
"create_time": gtime.Now().String(),
|
||||
})
|
||||
gtest.Assert(err, nil)
|
||||
n, _ := result.RowsAffected()
|
||||
gtest.Assert(n, 1)
|
||||
|
||||
// struct
|
||||
type User struct {
|
||||
Id int `gconv:"id"`
|
||||
Passport string `json:"passport"`
|
||||
Password string `gconv:"password"`
|
||||
Nickname string `gconv:"nickname"`
|
||||
CreateTime string `json:"create_time"`
|
||||
}
|
||||
timeStr := gtime.Now().String()
|
||||
result, err = db.Insert(table, User{
|
||||
Id: 3,
|
||||
Passport: "user_3",
|
||||
Password: "25d55ad283aa400af464c76d713c07ad",
|
||||
Nickname: "name_3",
|
||||
CreateTime: timeStr,
|
||||
})
|
||||
gtest.Assert(err, nil)
|
||||
n, _ = result.RowsAffected()
|
||||
gtest.Assert(n, 1)
|
||||
|
||||
one, err := db.Table(table).Where("id", 3).One()
|
||||
gtest.Assert(err, nil)
|
||||
|
||||
gtest.Assert(one["id"].Int(), 3)
|
||||
gtest.Assert(one["passport"].String(), "user_3")
|
||||
gtest.Assert(one["password"].String(), "25d55ad283aa400af464c76d713c07ad")
|
||||
gtest.Assert(one["nickname"].String(), "name_3")
|
||||
gtest.Assert(one["create_time"].String(), timeStr)
|
||||
|
||||
// *struct
|
||||
timeStr = gtime.Now().String()
|
||||
result, err = db.Insert(table, &User{
|
||||
Id: 4,
|
||||
Passport: "t4",
|
||||
Password: "25d55ad283aa400af464c76d713c07ad",
|
||||
Nickname: "name_4",
|
||||
CreateTime: timeStr,
|
||||
})
|
||||
gtest.Assert(err, nil)
|
||||
n, _ = result.RowsAffected()
|
||||
gtest.Assert(n, 1)
|
||||
|
||||
one, err = db.Table(table).Where("id", 4).One()
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(one["id"].Int(), 4)
|
||||
gtest.Assert(one["passport"].String(), "t4")
|
||||
gtest.Assert(one["password"].String(), "25d55ad283aa400af464c76d713c07ad")
|
||||
gtest.Assert(one["nickname"].String(), "name_4")
|
||||
gtest.Assert(one["create_time"].String(), timeStr)
|
||||
|
||||
// batch with Insert
|
||||
timeStr = gtime.Now().String()
|
||||
r, err := db.Insert(table, g.Slice{
|
||||
g.Map{
|
||||
"id": 200,
|
||||
"passport": "t200",
|
||||
"password": "25d55ad283aa400af464c76d71qw07ad",
|
||||
"nickname": "T200",
|
||||
"create_time": timeStr,
|
||||
},
|
||||
g.Map{
|
||||
"id": 300,
|
||||
"passport": "t300",
|
||||
"password": "25d55ad283aa400af464c76d713c07ad",
|
||||
"nickname": "T300",
|
||||
"create_time": timeStr,
|
||||
},
|
||||
})
|
||||
gtest.Assert(err, nil)
|
||||
n, _ = r.RowsAffected()
|
||||
gtest.Assert(n, 2)
|
||||
|
||||
one, err = db.Table(table).Where("id", 200).One()
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(one["id"].Int(), 200)
|
||||
gtest.Assert(one["passport"].String(), "t200")
|
||||
gtest.Assert(one["password"].String(), "25d55ad283aa400af464c76d71qw07ad")
|
||||
gtest.Assert(one["nickname"].String(), "T200")
|
||||
gtest.Assert(one["create_time"].String(), timeStr)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_DB_BatchInsert(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
table := createTable()
|
||||
defer dropTable(table)
|
||||
r, err := db.BatchInsert(table, g.List{
|
||||
{
|
||||
"id": 2,
|
||||
"passport": "t2",
|
||||
"password": "25d55ad283aa400af464c76d713c07ad",
|
||||
"nickname": "name_2",
|
||||
"create_time": gtime.Now().String(),
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"passport": "user_3",
|
||||
"password": "25d55ad283aa400af464c76d713c07ad",
|
||||
"nickname": "name_3",
|
||||
"create_time": gtime.Now().String(),
|
||||
},
|
||||
}, 1)
|
||||
gtest.Assert(err, nil)
|
||||
n, _ := r.RowsAffected()
|
||||
gtest.Assert(n, 2)
|
||||
})
|
||||
|
||||
gtest.Case(t, func() {
|
||||
table := createTable()
|
||||
defer dropTable(table)
|
||||
// []interface{}
|
||||
r, err := db.BatchInsert(table, g.Slice{
|
||||
g.Map{
|
||||
"id": 2,
|
||||
"passport": "t2",
|
||||
"password": "25d55ad283aa400af464c76d713c07ad",
|
||||
"nickname": "name_2",
|
||||
"create_time": gtime.Now().String(),
|
||||
},
|
||||
g.Map{
|
||||
"id": 3,
|
||||
"passport": "user_3",
|
||||
"password": "25d55ad283aa400af464c76d713c07ad",
|
||||
"nickname": "name_3",
|
||||
"create_time": gtime.Now().String(),
|
||||
},
|
||||
}, 1)
|
||||
gtest.Assert(err, nil)
|
||||
n, _ := r.RowsAffected()
|
||||
gtest.Assert(n, 2)
|
||||
})
|
||||
|
||||
// batch insert map
|
||||
gtest.Case(t, func() {
|
||||
table := createTable()
|
||||
defer dropTable(table)
|
||||
result, err := db.BatchInsert(table, g.Map{
|
||||
"id": 1,
|
||||
"passport": "t1",
|
||||
"password": "p1",
|
||||
"nickname": "T1",
|
||||
"create_time": gtime.Now().String(),
|
||||
})
|
||||
gtest.Assert(err, nil)
|
||||
n, _ := result.RowsAffected()
|
||||
gtest.Assert(n, 1)
|
||||
})
|
||||
|
||||
// batch insert struct
|
||||
gtest.Case(t, func() {
|
||||
table := createTable()
|
||||
defer dropTable(table)
|
||||
|
||||
type User struct {
|
||||
Id int `gconv:"id"`
|
||||
Passport string `gconv:"passport"`
|
||||
Password string `gconv:"password"`
|
||||
NickName string `gconv:"nickname"`
|
||||
CreateTime *gtime.Time `gconv:"create_time"`
|
||||
}
|
||||
user := &User{
|
||||
Id: 1,
|
||||
Passport: "t1",
|
||||
Password: "p1",
|
||||
NickName: "T1",
|
||||
CreateTime: gtime.Now(),
|
||||
}
|
||||
result, err := db.BatchInsert(table, user)
|
||||
gtest.Assert(err, nil)
|
||||
n, _ := result.RowsAffected()
|
||||
gtest.Assert(n, 1)
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func Test_DB_Save(t *testing.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
|
||||
gtest.Case(t, func() {
|
||||
timeStr := gtime.Now().String()
|
||||
_, err := db.Save(table, g.Map{
|
||||
"id": 1,
|
||||
"passport": "t1",
|
||||
"password": "25d55ad283aa400af464c76d713c07ad",
|
||||
"nickname": "T11",
|
||||
"create_time": timeStr,
|
||||
})
|
||||
gtest.Assert(err, nil)
|
||||
|
||||
one, err := db.Table(table).Where("id", 1).One()
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(one["id"].Int(), 1)
|
||||
gtest.Assert(one["passport"].String(), "t1")
|
||||
gtest.Assert(one["password"].String(), "25d55ad283aa400af464c76d713c07ad")
|
||||
gtest.Assert(one["nickname"].String(), "T11")
|
||||
gtest.Assert(one["create_time"].String(), timeStr)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_DB_Replace(t *testing.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
|
||||
gtest.Case(t, func() {
|
||||
timeStr := gtime.Now().String()
|
||||
_, err := db.Replace(table, g.Map{
|
||||
"id": 1,
|
||||
"passport": "t1",
|
||||
"password": "25d55ad283aa400af464c76d713c07ad",
|
||||
"nickname": "T11",
|
||||
"create_time": timeStr,
|
||||
})
|
||||
gtest.Assert(err, nil)
|
||||
|
||||
one, err := db.Table(table).Where("id", 1).One()
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(one["id"].Int(), 1)
|
||||
gtest.Assert(one["passport"].String(), "t1")
|
||||
gtest.Assert(one["password"].String(), "25d55ad283aa400af464c76d713c07ad")
|
||||
gtest.Assert(one["nickname"].String(), "T11")
|
||||
gtest.Assert(one["create_time"].String(), timeStr)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_DB_Update(t *testing.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
|
||||
gtest.Case(t, func() {
|
||||
result, err := db.Update(table, "password='987654321'", "id=3")
|
||||
gtest.Assert(err, nil)
|
||||
n, _ := result.RowsAffected()
|
||||
gtest.Assert(n, 1)
|
||||
|
||||
one, err := db.Table(table).Where("id", 3).One()
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(one["id"].Int(), 3)
|
||||
gtest.Assert(one["passport"].String(), "user_3")
|
||||
gtest.Assert(one["password"].String(), "987654321")
|
||||
gtest.Assert(one["nickname"].String(), "name_3")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_DB_GetAll(t *testing.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
|
||||
gtest.Case(t, func() {
|
||||
result, err := db.GetAll(fmt.Sprintf("SELECT * FROM %s WHERE id=?", table), 1)
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(len(result), 1)
|
||||
gtest.Assert(result[0]["id"].Int(), 1)
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
result, err := db.GetAll(fmt.Sprintf("SELECT * FROM %s WHERE id=?", table), g.Slice{1})
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(len(result), 1)
|
||||
gtest.Assert(result[0]["id"].Int(), 1)
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
result, err := db.GetAll(fmt.Sprintf("SELECT * FROM %s WHERE id in(?)", table), g.Slice{1, 2, 3})
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(len(result), 3)
|
||||
gtest.Assert(result[0]["id"].Int(), 1)
|
||||
gtest.Assert(result[1]["id"].Int(), 2)
|
||||
gtest.Assert(result[2]["id"].Int(), 3)
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
result, err := db.GetAll(fmt.Sprintf("SELECT * FROM %s WHERE id in(?,?,?)", table), g.Slice{1, 2, 3})
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(len(result), 3)
|
||||
gtest.Assert(result[0]["id"].Int(), 1)
|
||||
gtest.Assert(result[1]["id"].Int(), 2)
|
||||
gtest.Assert(result[2]["id"].Int(), 3)
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
result, err := db.GetAll(fmt.Sprintf("SELECT * FROM %s WHERE id in(?,?,?)", table), g.Slice{1, 2, 3}...)
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(len(result), 3)
|
||||
gtest.Assert(result[0]["id"].Int(), 1)
|
||||
gtest.Assert(result[1]["id"].Int(), 2)
|
||||
gtest.Assert(result[2]["id"].Int(), 3)
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
result, err := db.GetAll(fmt.Sprintf("SELECT * FROM %s WHERE id>=? AND id <=?", table), g.Slice{1, 3})
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(len(result), 3)
|
||||
gtest.Assert(result[0]["id"].Int(), 1)
|
||||
gtest.Assert(result[1]["id"].Int(), 2)
|
||||
gtest.Assert(result[2]["id"].Int(), 3)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_DB_GetOne(t *testing.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
gtest.Case(t, func() {
|
||||
record, err := db.GetOne(fmt.Sprintf("SELECT * FROM %s WHERE passport=?", table), "user_1")
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(record["nickname"].String(), "name_1")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_DB_GetValue(t *testing.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
gtest.Case(t, func() {
|
||||
value, err := db.GetValue(fmt.Sprintf("SELECT id FROM %s WHERE passport=?", table), "user_3")
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(value.Int(), 3)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_DB_GetCount(t *testing.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
gtest.Case(t, func() {
|
||||
count, err := db.GetCount(fmt.Sprintf("SELECT * FROM %s", table))
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(count, INIT_DATA_SIZE)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_DB_GetStruct(t *testing.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
gtest.Case(t, func() {
|
||||
type User struct {
|
||||
Id int
|
||||
Passport string
|
||||
Password string
|
||||
NickName string
|
||||
CreateTime gtime.Time
|
||||
}
|
||||
user := new(User)
|
||||
err := db.GetStruct(user, fmt.Sprintf("SELECT * FROM %s WHERE id=?", table), 3)
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(user.NickName, "name_3")
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
type User struct {
|
||||
Id int
|
||||
Passport string
|
||||
Password string
|
||||
NickName string
|
||||
CreateTime *gtime.Time
|
||||
}
|
||||
user := new(User)
|
||||
err := db.GetStruct(user, fmt.Sprintf("SELECT * FROM %s WHERE id=?", table), 3)
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(user.NickName, "name_3")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_DB_GetStructs(t *testing.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
gtest.Case(t, func() {
|
||||
type User struct {
|
||||
Id int
|
||||
Passport string
|
||||
Password string
|
||||
NickName string
|
||||
CreateTime gtime.Time
|
||||
}
|
||||
var users []User
|
||||
err := db.GetStructs(&users, fmt.Sprintf("SELECT * FROM %s WHERE id>?", table), 1)
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(len(users), INIT_DATA_SIZE-1)
|
||||
gtest.Assert(users[0].Id, 2)
|
||||
gtest.Assert(users[1].Id, 3)
|
||||
gtest.Assert(users[2].Id, 4)
|
||||
gtest.Assert(users[0].NickName, "name_2")
|
||||
gtest.Assert(users[1].NickName, "name_3")
|
||||
gtest.Assert(users[2].NickName, "name_4")
|
||||
})
|
||||
|
||||
gtest.Case(t, func() {
|
||||
type User struct {
|
||||
Id int
|
||||
Passport string
|
||||
Password string
|
||||
NickName string
|
||||
CreateTime *gtime.Time
|
||||
}
|
||||
var users []User
|
||||
err := db.GetStructs(&users, fmt.Sprintf("SELECT * FROM %s WHERE id>?", table), 1)
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(len(users), INIT_DATA_SIZE-1)
|
||||
gtest.Assert(users[0].Id, 2)
|
||||
gtest.Assert(users[1].Id, 3)
|
||||
gtest.Assert(users[2].Id, 4)
|
||||
gtest.Assert(users[0].NickName, "name_2")
|
||||
gtest.Assert(users[1].NickName, "name_3")
|
||||
gtest.Assert(users[2].NickName, "name_4")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_DB_GetScan(t *testing.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
gtest.Case(t, func() {
|
||||
type User struct {
|
||||
Id int
|
||||
Passport string
|
||||
Password string
|
||||
NickName string
|
||||
CreateTime gtime.Time
|
||||
}
|
||||
user := new(User)
|
||||
err := db.GetScan(user, fmt.Sprintf("SELECT * FROM %s WHERE id=?", table), 3)
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(user.NickName, "name_3")
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
type User struct {
|
||||
Id int
|
||||
Passport string
|
||||
Password string
|
||||
NickName string
|
||||
CreateTime *gtime.Time
|
||||
}
|
||||
user := new(User)
|
||||
err := db.GetScan(user, fmt.Sprintf("SELECT * FROM %s WHERE id=?", table), 3)
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(user.NickName, "name_3")
|
||||
})
|
||||
|
||||
gtest.Case(t, func() {
|
||||
type User struct {
|
||||
Id int
|
||||
Passport string
|
||||
Password string
|
||||
NickName string
|
||||
CreateTime gtime.Time
|
||||
}
|
||||
var users []User
|
||||
err := db.GetScan(&users, fmt.Sprintf("SELECT * FROM %s WHERE id>?", table), 1)
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(len(users), INIT_DATA_SIZE-1)
|
||||
gtest.Assert(users[0].Id, 2)
|
||||
gtest.Assert(users[1].Id, 3)
|
||||
gtest.Assert(users[2].Id, 4)
|
||||
gtest.Assert(users[0].NickName, "name_2")
|
||||
gtest.Assert(users[1].NickName, "name_3")
|
||||
gtest.Assert(users[2].NickName, "name_4")
|
||||
})
|
||||
|
||||
gtest.Case(t, func() {
|
||||
type User struct {
|
||||
Id int
|
||||
Passport string
|
||||
Password string
|
||||
NickName string
|
||||
CreateTime *gtime.Time
|
||||
}
|
||||
var users []User
|
||||
err := db.GetScan(&users, fmt.Sprintf("SELECT * FROM %s WHERE id>?", table), 1)
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(len(users), INIT_DATA_SIZE-1)
|
||||
gtest.Assert(users[0].Id, 2)
|
||||
gtest.Assert(users[1].Id, 3)
|
||||
gtest.Assert(users[2].Id, 4)
|
||||
gtest.Assert(users[0].NickName, "name_2")
|
||||
gtest.Assert(users[1].NickName, "name_3")
|
||||
gtest.Assert(users[2].NickName, "name_4")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_DB_Delete(t *testing.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
gtest.Case(t, func() {
|
||||
result, err := db.Delete(table, nil)
|
||||
gtest.Assert(err, nil)
|
||||
n, _ := result.RowsAffected()
|
||||
gtest.Assert(n, INIT_DATA_SIZE)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_DB_Time(t *testing.T) {
|
||||
table := createTable()
|
||||
defer dropTable(table)
|
||||
|
||||
gtest.Case(t, func() {
|
||||
result, err := db.Insert(table, g.Map{
|
||||
"id": 200,
|
||||
"passport": "t200",
|
||||
"password": "123456",
|
||||
"nickname": "T200",
|
||||
"create_time": time.Now(),
|
||||
})
|
||||
if err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
n, _ := result.RowsAffected()
|
||||
gtest.Assert(n, 1)
|
||||
value, err := db.GetValue(fmt.Sprintf("select `passport` from `%s` where id=?", table), 200)
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(value.String(), "t200")
|
||||
})
|
||||
|
||||
gtest.Case(t, func() {
|
||||
t := time.Now()
|
||||
result, err := db.Insert(table, g.Map{
|
||||
"id": 300,
|
||||
"passport": "t300",
|
||||
"password": "123456",
|
||||
"nickname": "T300",
|
||||
"create_time": &t,
|
||||
})
|
||||
if err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
n, _ := result.RowsAffected()
|
||||
gtest.Assert(n, 1)
|
||||
value, err := db.GetValue(fmt.Sprintf("select `passport` from `%s` where id=?", table), 300)
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(value.String(), "t300")
|
||||
})
|
||||
|
||||
gtest.Case(t, func() {
|
||||
result, err := db.Delete(table, nil)
|
||||
gtest.Assert(err, nil)
|
||||
n, _ := result.RowsAffected()
|
||||
gtest.Assert(n, 2)
|
||||
})
|
||||
}
|
||||
812
g/database/gdb/gdb_unit_z_model_test.go
Normal file
812
g/database/gdb/gdb_unit_z_model_test.go
Normal file
@ -0,0 +1,812 @@
|
||||
// Copyright 2019 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,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gdb_test
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/gogf/gf/g"
|
||||
"github.com/gogf/gf/g/os/gtime"
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
)
|
||||
|
||||
func Test_Model_Insert(t *testing.T) {
|
||||
table := createTable()
|
||||
defer dropTable(table)
|
||||
gtest.Case(t, func() {
|
||||
result, err := db.Table(table).Filter().Data(g.Map{
|
||||
"id": 1,
|
||||
"uid": 1,
|
||||
"passport": "t1",
|
||||
"password": "25d55ad283aa400af464c76d713c07ad",
|
||||
"nickname": "name_1",
|
||||
"create_time": gtime.Now().String(),
|
||||
}).Insert()
|
||||
gtest.Assert(err, nil)
|
||||
n, _ := result.LastInsertId()
|
||||
gtest.Assert(n, 1)
|
||||
|
||||
result, err = db.Table(table).Filter().Data(g.Map{
|
||||
"id": "2",
|
||||
"uid": "2",
|
||||
"passport": "t2",
|
||||
"password": "25d55ad283aa400af464c76d713c07ad",
|
||||
"nickname": "name_2",
|
||||
"create_time": gtime.Now().String(),
|
||||
}).Insert()
|
||||
gtest.Assert(err, nil)
|
||||
n, _ = result.RowsAffected()
|
||||
gtest.Assert(n, 1)
|
||||
|
||||
type User struct {
|
||||
Id int `gconv:"id"`
|
||||
Uid int `gconv:"uid"`
|
||||
Passport string `json:"passport"`
|
||||
Password string `gconv:"password"`
|
||||
Nickname string `gconv:"nickname"`
|
||||
CreateTime string `json:"create_time"`
|
||||
}
|
||||
result, err = db.Table(table).Filter().Data(User{
|
||||
Id: 3,
|
||||
Uid: 3,
|
||||
Passport: "t3",
|
||||
Password: "25d55ad283aa400af464c76d713c07ad",
|
||||
Nickname: "name_3",
|
||||
CreateTime: gtime.Now().String(),
|
||||
}).Insert()
|
||||
gtest.Assert(err, nil)
|
||||
n, _ = result.RowsAffected()
|
||||
gtest.Assert(n, 1)
|
||||
value, err := db.Table(table).Fields("passport").Where("id=3").Value()
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(value.String(), "t3")
|
||||
|
||||
result, err = db.Table(table).Filter().Data(&User{
|
||||
Id: 4,
|
||||
Uid: 4,
|
||||
Passport: "t4",
|
||||
Password: "25d55ad283aa400af464c76d713c07ad",
|
||||
Nickname: "T4",
|
||||
CreateTime: gtime.Now().String(),
|
||||
}).Insert()
|
||||
gtest.Assert(err, nil)
|
||||
n, _ = result.RowsAffected()
|
||||
gtest.Assert(n, 1)
|
||||
value, err = db.Table(table).Fields("passport").Where("id=4").Value()
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(value.String(), "t4")
|
||||
|
||||
result, err = db.Table(table).Where("id>?", 1).Delete()
|
||||
gtest.Assert(err, nil)
|
||||
n, _ = result.RowsAffected()
|
||||
gtest.Assert(n, 3)
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func Test_Model_Batch(t *testing.T) {
|
||||
// bacth insert
|
||||
gtest.Case(t, func() {
|
||||
table := createTable()
|
||||
defer dropTable(table)
|
||||
result, err := db.Table(table).Filter().Data(g.List{
|
||||
{
|
||||
"id": 2,
|
||||
"uid": 2,
|
||||
"passport": "t2",
|
||||
"password": "25d55ad283aa400af464c76d713c07ad",
|
||||
"nickname": "name_2",
|
||||
"create_time": gtime.Now().String(),
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"uid": 3,
|
||||
"passport": "t3",
|
||||
"password": "25d55ad283aa400af464c76d713c07ad",
|
||||
"nickname": "name_3",
|
||||
"create_time": gtime.Now().String(),
|
||||
},
|
||||
}).Batch(1).Insert()
|
||||
if err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
n, _ := result.RowsAffected()
|
||||
gtest.Assert(n, 2)
|
||||
})
|
||||
|
||||
// batch save
|
||||
gtest.Case(t, func() {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
result, err := db.Table(table).All()
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(len(result), INIT_DATA_SIZE)
|
||||
for _, v := range result {
|
||||
v["nickname"].Set(v["nickname"].String() + v["id"].String())
|
||||
}
|
||||
r, e := db.Table(table).Data(result).Save()
|
||||
gtest.Assert(e, nil)
|
||||
n, e := r.RowsAffected()
|
||||
gtest.Assert(e, nil)
|
||||
gtest.Assert(n, INIT_DATA_SIZE*2)
|
||||
})
|
||||
|
||||
// batch replace
|
||||
gtest.Case(t, func() {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
result, err := db.Table(table).All()
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(len(result), INIT_DATA_SIZE)
|
||||
for _, v := range result {
|
||||
v["nickname"].Set(v["nickname"].String() + v["id"].String())
|
||||
}
|
||||
r, e := db.Table(table).Data(result).Replace()
|
||||
gtest.Assert(e, nil)
|
||||
n, e := r.RowsAffected()
|
||||
gtest.Assert(e, nil)
|
||||
gtest.Assert(n, INIT_DATA_SIZE*2)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Model_Replace(t *testing.T) {
|
||||
table := createTable()
|
||||
defer dropTable(table)
|
||||
|
||||
gtest.Case(t, func() {
|
||||
result, err := db.Table(table).Data(g.Map{
|
||||
"id": 1,
|
||||
"passport": "t11",
|
||||
"password": "25d55ad283aa400af464c76d713c07ad",
|
||||
"nickname": "T11",
|
||||
"create_time": "2018-10-24 10:00:00",
|
||||
}).Replace()
|
||||
gtest.Assert(err, nil)
|
||||
n, _ := result.RowsAffected()
|
||||
gtest.Assert(n, 1)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Model_Save(t *testing.T) {
|
||||
table := createTable()
|
||||
defer dropTable(table)
|
||||
gtest.Case(t, func() {
|
||||
result, err := db.Table(table).Data(g.Map{
|
||||
"id": 1,
|
||||
"passport": "t111",
|
||||
"password": "25d55ad283aa400af464c76d713c07ad",
|
||||
"nickname": "T111",
|
||||
"create_time": "2018-10-24 10:00:00",
|
||||
}).Save()
|
||||
gtest.Assert(err, nil)
|
||||
n, _ := result.RowsAffected()
|
||||
gtest.Assert(n, 1)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Model_Update(t *testing.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
// UPDATE...LIMIT
|
||||
gtest.Case(t, func() {
|
||||
result, err := db.Table(table).Data("nickname", "T100").OrderBy("id desc").Limit(2).Update()
|
||||
gtest.Assert(err, nil)
|
||||
n, _ := result.RowsAffected()
|
||||
gtest.Assert(n, 2)
|
||||
|
||||
v1, err := db.Table(table).Fields("nickname").Where("id", 10).Value()
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(v1.String(), "T100")
|
||||
|
||||
v2, err := db.Table(table).Fields("nickname").Where("id", 8).Value()
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(v2.String(), "name_8")
|
||||
})
|
||||
|
||||
gtest.Case(t, func() {
|
||||
result, err := db.Table(table).Data("passport", "user_22").Where("passport=?", "user_2").Update()
|
||||
gtest.Assert(err, nil)
|
||||
n, _ := result.RowsAffected()
|
||||
gtest.Assert(n, 1)
|
||||
})
|
||||
|
||||
gtest.Case(t, func() {
|
||||
result, err := db.Table(table).Data("passport", "user_2").Where("passport='user_22'").Update()
|
||||
gtest.Assert(err, nil)
|
||||
n, _ := result.RowsAffected()
|
||||
gtest.Assert(n, 1)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Model_Clone(t *testing.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
|
||||
gtest.Case(t, func() {
|
||||
md := db.Table(table).Where("id IN(?)", g.Slice{1, 3})
|
||||
count, err := md.Count()
|
||||
gtest.Assert(err, nil)
|
||||
|
||||
record, err := md.OrderBy("id DESC").One()
|
||||
gtest.Assert(err, nil)
|
||||
|
||||
result, err := md.OrderBy("id ASC").All()
|
||||
gtest.Assert(err, nil)
|
||||
|
||||
gtest.Assert(count, 2)
|
||||
gtest.Assert(record["id"].Int(), 3)
|
||||
gtest.Assert(len(result), 2)
|
||||
gtest.Assert(result[0]["id"].Int(), 1)
|
||||
gtest.Assert(result[1]["id"].Int(), 3)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Model_Safe(t *testing.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
|
||||
gtest.Case(t, func() {
|
||||
md := db.Table(table).Safe(false).Where("id IN(?)", g.Slice{1, 3})
|
||||
count, err := md.Count()
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(count, 2)
|
||||
|
||||
md.And("id = ?", 1)
|
||||
count, err = md.Count()
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(count, 1)
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
md := db.Table(table).Safe(true).Where("id IN(?)", g.Slice{1, 3})
|
||||
count, err := md.Count()
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(count, 2)
|
||||
|
||||
md.And("id = ?", 1)
|
||||
count, err = md.Count()
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(count, 2)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Model_All(t *testing.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
|
||||
gtest.Case(t, func() {
|
||||
result, err := db.Table(table).All()
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(len(result), INIT_DATA_SIZE)
|
||||
})
|
||||
// sql.ErrNoRows
|
||||
gtest.Case(t, func() {
|
||||
result, err := db.Table(table).Where("id<0").All()
|
||||
gtest.Assert(result, nil)
|
||||
gtest.Assert(err, sql.ErrNoRows)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Model_One(t *testing.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
gtest.Case(t, func() {
|
||||
record, err := db.Table(table).Where("id", 1).One()
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(record["nickname"].String(), "name_1")
|
||||
})
|
||||
// sql.ErrNoRows
|
||||
gtest.Case(t, func() {
|
||||
record, err := db.Table(table).Where("id", 0).One()
|
||||
gtest.Assert(err, sql.ErrNoRows)
|
||||
gtest.Assert(record, nil)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Model_Value(t *testing.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
gtest.Case(t, func() {
|
||||
value, err := db.Table(table).Fields("nickname").Where("id", 1).Value()
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(value.String(), "name_1")
|
||||
})
|
||||
// sql.ErrNoRows
|
||||
gtest.Case(t, func() {
|
||||
value, err := db.Table(table).Fields("nickname").Where("id", 0).Value()
|
||||
gtest.Assert(err, sql.ErrNoRows)
|
||||
gtest.Assert(value, nil)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Model_Count(t *testing.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
gtest.Case(t, func() {
|
||||
count, err := db.Table(table).Count()
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(count, INIT_DATA_SIZE)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Model_Select(t *testing.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
gtest.Case(t, func() {
|
||||
result, err := db.Table(table).Select()
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(len(result), INIT_DATA_SIZE)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Model_Struct(t *testing.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
gtest.Case(t, func() {
|
||||
type User struct {
|
||||
Id int
|
||||
Passport string
|
||||
Password string
|
||||
NickName string
|
||||
CreateTime gtime.Time
|
||||
}
|
||||
user := new(User)
|
||||
err := db.Table(table).Where("id=1").Struct(user)
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(user.NickName, "name_1")
|
||||
gtest.Assert(user.CreateTime.String(), "2018-10-24 10:00:00")
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
type User struct {
|
||||
Id int
|
||||
Passport string
|
||||
Password string
|
||||
NickName string
|
||||
CreateTime *gtime.Time
|
||||
}
|
||||
user := new(User)
|
||||
err := db.Table(table).Where("id=1").Struct(user)
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(user.NickName, "name_1")
|
||||
gtest.Assert(user.CreateTime.String(), "2018-10-24 10:00:00")
|
||||
})
|
||||
// Auto creating struct object.
|
||||
gtest.Case(t, func() {
|
||||
type User struct {
|
||||
Id int
|
||||
Passport string
|
||||
Password string
|
||||
NickName string
|
||||
CreateTime *gtime.Time
|
||||
}
|
||||
user := (*User)(nil)
|
||||
err := db.Table(table).Where("id=1").Struct(&user)
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(user.NickName, "name_1")
|
||||
gtest.Assert(user.CreateTime.String(), "2018-10-24 10:00:00")
|
||||
})
|
||||
// Just using Scan.
|
||||
gtest.Case(t, func() {
|
||||
type User struct {
|
||||
Id int
|
||||
Passport string
|
||||
Password string
|
||||
NickName string
|
||||
CreateTime *gtime.Time
|
||||
}
|
||||
user := (*User)(nil)
|
||||
err := db.Table(table).Where("id=1").Scan(&user)
|
||||
if err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
gtest.Assert(user.NickName, "name_1")
|
||||
gtest.Assert(user.CreateTime.String(), "2018-10-24 10:00:00")
|
||||
})
|
||||
// sql.ErrNoRows
|
||||
gtest.Case(t, func() {
|
||||
type User struct {
|
||||
Id int
|
||||
Passport string
|
||||
Password string
|
||||
NickName string
|
||||
CreateTime *gtime.Time
|
||||
}
|
||||
user := new(User)
|
||||
err := db.Table(table).Where("id=-1").Struct(user)
|
||||
gtest.Assert(err, sql.ErrNoRows)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Model_Structs(t *testing.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
|
||||
gtest.Case(t, func() {
|
||||
type User struct {
|
||||
Id int
|
||||
Passport string
|
||||
Password string
|
||||
NickName string
|
||||
CreateTime gtime.Time
|
||||
}
|
||||
var users []User
|
||||
err := db.Table(table).OrderBy("id asc").Structs(&users)
|
||||
if err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
gtest.Assert(len(users), INIT_DATA_SIZE)
|
||||
gtest.Assert(users[0].Id, 1)
|
||||
gtest.Assert(users[1].Id, 2)
|
||||
gtest.Assert(users[2].Id, 3)
|
||||
gtest.Assert(users[0].NickName, "name_1")
|
||||
gtest.Assert(users[1].NickName, "name_2")
|
||||
gtest.Assert(users[2].NickName, "name_3")
|
||||
gtest.Assert(users[0].CreateTime.String(), "2018-10-24 10:00:00")
|
||||
})
|
||||
// Auto create struct slice.
|
||||
gtest.Case(t, func() {
|
||||
type User struct {
|
||||
Id int
|
||||
Passport string
|
||||
Password string
|
||||
NickName string
|
||||
CreateTime *gtime.Time
|
||||
}
|
||||
var users []*User
|
||||
err := db.Table(table).OrderBy("id asc").Structs(&users)
|
||||
if err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
gtest.Assert(len(users), INIT_DATA_SIZE)
|
||||
gtest.Assert(users[0].Id, 1)
|
||||
gtest.Assert(users[1].Id, 2)
|
||||
gtest.Assert(users[2].Id, 3)
|
||||
gtest.Assert(users[0].NickName, "name_1")
|
||||
gtest.Assert(users[1].NickName, "name_2")
|
||||
gtest.Assert(users[2].NickName, "name_3")
|
||||
gtest.Assert(users[0].CreateTime.String(), "2018-10-24 10:00:00")
|
||||
})
|
||||
// Just using Scan.
|
||||
gtest.Case(t, func() {
|
||||
type User struct {
|
||||
Id int
|
||||
Passport string
|
||||
Password string
|
||||
NickName string
|
||||
CreateTime *gtime.Time
|
||||
}
|
||||
var users []*User
|
||||
err := db.Table(table).OrderBy("id asc").Scan(&users)
|
||||
if err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
gtest.Assert(len(users), INIT_DATA_SIZE)
|
||||
gtest.Assert(users[0].Id, 1)
|
||||
gtest.Assert(users[1].Id, 2)
|
||||
gtest.Assert(users[2].Id, 3)
|
||||
gtest.Assert(users[0].NickName, "name_1")
|
||||
gtest.Assert(users[1].NickName, "name_2")
|
||||
gtest.Assert(users[2].NickName, "name_3")
|
||||
gtest.Assert(users[0].CreateTime.String(), "2018-10-24 10:00:00")
|
||||
})
|
||||
// sql.ErrNoRows
|
||||
gtest.Case(t, func() {
|
||||
type User struct {
|
||||
Id int
|
||||
Passport string
|
||||
Password string
|
||||
NickName string
|
||||
CreateTime *gtime.Time
|
||||
}
|
||||
var users []*User
|
||||
err := db.Table(table).Where("id<0").Structs(&users)
|
||||
gtest.Assert(err, sql.ErrNoRows)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Model_Scan(t *testing.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
|
||||
gtest.Case(t, func() {
|
||||
type User struct {
|
||||
Id int
|
||||
Passport string
|
||||
Password string
|
||||
NickName string
|
||||
CreateTime gtime.Time
|
||||
}
|
||||
user := new(User)
|
||||
err := db.Table(table).Where("id=1").Scan(user)
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(user.NickName, "name_1")
|
||||
gtest.Assert(user.CreateTime.String(), "2018-10-24 10:00:00")
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
type User struct {
|
||||
Id int
|
||||
Passport string
|
||||
Password string
|
||||
NickName string
|
||||
CreateTime *gtime.Time
|
||||
}
|
||||
user := new(User)
|
||||
err := db.Table(table).Where("id=1").Scan(user)
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(user.NickName, "name_1")
|
||||
gtest.Assert(user.CreateTime.String(), "2018-10-24 10:00:00")
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
type User struct {
|
||||
Id int
|
||||
Passport string
|
||||
Password string
|
||||
NickName string
|
||||
CreateTime gtime.Time
|
||||
}
|
||||
var users []User
|
||||
err := db.Table(table).OrderBy("id asc").Scan(&users)
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(len(users), INIT_DATA_SIZE)
|
||||
gtest.Assert(users[0].Id, 1)
|
||||
gtest.Assert(users[1].Id, 2)
|
||||
gtest.Assert(users[2].Id, 3)
|
||||
gtest.Assert(users[0].NickName, "name_1")
|
||||
gtest.Assert(users[1].NickName, "name_2")
|
||||
gtest.Assert(users[2].NickName, "name_3")
|
||||
gtest.Assert(users[0].CreateTime.String(), "2018-10-24 10:00:00")
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
type User struct {
|
||||
Id int
|
||||
Passport string
|
||||
Password string
|
||||
NickName string
|
||||
CreateTime *gtime.Time
|
||||
}
|
||||
var users []*User
|
||||
err := db.Table(table).OrderBy("id asc").Scan(&users)
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(len(users), INIT_DATA_SIZE)
|
||||
gtest.Assert(users[0].Id, 1)
|
||||
gtest.Assert(users[1].Id, 2)
|
||||
gtest.Assert(users[2].Id, 3)
|
||||
gtest.Assert(users[0].NickName, "name_1")
|
||||
gtest.Assert(users[1].NickName, "name_2")
|
||||
gtest.Assert(users[2].NickName, "name_3")
|
||||
gtest.Assert(users[0].CreateTime.String(), "2018-10-24 10:00:00")
|
||||
})
|
||||
// sql.ErrNoRows
|
||||
gtest.Case(t, func() {
|
||||
type User struct {
|
||||
Id int
|
||||
Passport string
|
||||
Password string
|
||||
NickName string
|
||||
CreateTime *gtime.Time
|
||||
}
|
||||
user := new(User)
|
||||
users := new([]*User)
|
||||
err1 := db.Table(table).Where("id < 0").Scan(user)
|
||||
err2 := db.Table(table).Where("id < 0").Scan(users)
|
||||
gtest.Assert(err1, sql.ErrNoRows)
|
||||
gtest.Assert(err2, sql.ErrNoRows)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Model_OrderBy(t *testing.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
|
||||
gtest.Case(t, func() {
|
||||
result, err := db.Table(table).OrderBy("id DESC").Select()
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(len(result), INIT_DATA_SIZE)
|
||||
gtest.Assert(result[0]["nickname"].String(), fmt.Sprintf("name_%d", INIT_DATA_SIZE))
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Model_GroupBy(t *testing.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
|
||||
gtest.Case(t, func() {
|
||||
result, err := db.Table(table).GroupBy("id").Select()
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(len(result), INIT_DATA_SIZE)
|
||||
gtest.Assert(result[0]["nickname"].String(), "name_1")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Model_Where(t *testing.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
|
||||
// string
|
||||
gtest.Case(t, func() {
|
||||
result, err := db.Table(table).Where("id=? and nickname=?", 3, "name_3").One()
|
||||
gtest.Assert(err, nil)
|
||||
gtest.AssertGT(len(result), 0)
|
||||
gtest.Assert(result["id"].Int(), 3)
|
||||
})
|
||||
// slice parameter
|
||||
gtest.Case(t, func() {
|
||||
result, err := db.Table(table).Where("id=? and nickname=?", g.Slice{3, "name_3"}).One()
|
||||
gtest.Assert(err, nil)
|
||||
gtest.AssertGT(len(result), 0)
|
||||
gtest.Assert(result["id"].Int(), 3)
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
result, err := db.Table(table).Where("id=?", g.Slice{3}).One()
|
||||
gtest.Assert(err, nil)
|
||||
gtest.AssertGT(len(result), 0)
|
||||
gtest.Assert(result["id"].Int(), 3)
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
result, err := db.Table(table).Where("id", 3).One()
|
||||
gtest.Assert(err, nil)
|
||||
gtest.AssertGT(len(result), 0)
|
||||
gtest.Assert(result["id"].Int(), 3)
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
result, err := db.Table(table).Where("id", 3).Where("nickname", "name_3").One()
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(result["id"].Int(), 3)
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
result, err := db.Table(table).Where("id", 3).And("nickname", "name_3").One()
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(result["id"].Int(), 3)
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
result, err := db.Table(table).Where("id", 30).Or("nickname", "name_3").One()
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(result["id"].Int(), 3)
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
result, err := db.Table(table).Where("id", 30).Or("nickname", "name_3").And("id>?", 1).One()
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(result["id"].Int(), 3)
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
result, err := db.Table(table).Where("id", 30).Or("nickname", "name_3").And("id>", 1).One()
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(result["id"].Int(), 3)
|
||||
})
|
||||
// slice
|
||||
gtest.Case(t, func() {
|
||||
result, err := db.Table(table).Where("id=? AND nickname=?", g.Slice{3, "name_3"}...).One()
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(result["id"].Int(), 3)
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
result, err := db.Table(table).Where("id=? AND nickname=?", g.Slice{3, "name_3"}).One()
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(result["id"].Int(), 3)
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
result, err := db.Table(table).Where("passport like ? and nickname like ?", g.Slice{"user_3", "name_3"}).One()
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(result["id"].Int(), 3)
|
||||
})
|
||||
// map
|
||||
gtest.Case(t, func() {
|
||||
result, err := db.Table(table).Where(g.Map{"id": 3, "nickname": "name_3"}).One()
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(result["id"].Int(), 3)
|
||||
})
|
||||
// map key operator
|
||||
gtest.Case(t, func() {
|
||||
result, err := db.Table(table).Where(g.Map{"id>": 1, "id<": 3}).One()
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(result["id"].Int(), 2)
|
||||
})
|
||||
// complicated where 1
|
||||
gtest.Case(t, func() {
|
||||
//db.SetDebug(true)
|
||||
conditions := g.Map{
|
||||
"nickname like ?": "%name%",
|
||||
"id between ? and ?": g.Slice{1, 3},
|
||||
"id > 0": nil,
|
||||
"create_time > 0": nil,
|
||||
"id": g.Slice{1, 2, 3},
|
||||
}
|
||||
result, err := db.Table(table).Where(conditions).OrderBy("id asc").All()
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(len(result), 3)
|
||||
gtest.Assert(result[0]["id"].Int(), 1)
|
||||
})
|
||||
// complicated where 2
|
||||
gtest.Case(t, func() {
|
||||
//db.SetDebug(true)
|
||||
conditions := g.Map{
|
||||
"nickname like ?": "%name%",
|
||||
"id between ? and ?": g.Slice{1, 3},
|
||||
"id >= ?": 1,
|
||||
"create_time > ?": 0,
|
||||
"id in(?)": g.Slice{1, 2, 3},
|
||||
}
|
||||
result, err := db.Table(table).Where(conditions).OrderBy("id asc").All()
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(len(result), 3)
|
||||
gtest.Assert(result[0]["id"].Int(), 1)
|
||||
})
|
||||
// struct
|
||||
gtest.Case(t, func() {
|
||||
type User struct {
|
||||
Id int `json:"id"`
|
||||
Nickname string `gconv:"nickname"`
|
||||
}
|
||||
result, err := db.Table(table).Where(User{3, "name_3"}).One()
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(result["id"].Int(), 3)
|
||||
|
||||
result, err = db.Table(table).Where(&User{3, "name_3"}).One()
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(result["id"].Int(), 3)
|
||||
})
|
||||
// slice single
|
||||
gtest.Case(t, func() {
|
||||
result, err := db.Table(table).Where("id IN(?)", g.Slice{1, 3}).OrderBy("id ASC").All()
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(len(result), 2)
|
||||
gtest.Assert(result[0]["id"].Int(), 1)
|
||||
gtest.Assert(result[1]["id"].Int(), 3)
|
||||
})
|
||||
// slice + string
|
||||
gtest.Case(t, func() {
|
||||
result, err := db.Table(table).Where("nickname=? AND id IN(?)", "name_3", g.Slice{1, 3}).OrderBy("id ASC").All()
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(len(result), 1)
|
||||
gtest.Assert(result[0]["id"].Int(), 3)
|
||||
})
|
||||
// slice + map
|
||||
gtest.Case(t, func() {
|
||||
result, err := db.Table(table).Where(g.Map{
|
||||
"id": g.Slice{1, 3},
|
||||
"nickname": "name_3",
|
||||
}).OrderBy("id ASC").All()
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(len(result), 1)
|
||||
gtest.Assert(result[0]["id"].Int(), 3)
|
||||
})
|
||||
// slice + struct
|
||||
gtest.Case(t, func() {
|
||||
type User struct {
|
||||
Ids []int `json:"id"`
|
||||
Nickname string `gconv:"nickname"`
|
||||
}
|
||||
result, err := db.Table(table).Where(User{
|
||||
Ids: []int{1, 3},
|
||||
Nickname: "name_3",
|
||||
}).OrderBy("id ASC").All()
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(len(result), 1)
|
||||
gtest.Assert(result[0]["id"].Int(), 3)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Model_Delete(t *testing.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
|
||||
// DELETE...LIMIT
|
||||
gtest.Case(t, func() {
|
||||
result, err := db.Table(table).Limit(2).Delete()
|
||||
gtest.Assert(err, nil)
|
||||
n, _ := result.RowsAffected()
|
||||
gtest.Assert(n, 2)
|
||||
})
|
||||
|
||||
gtest.Case(t, func() {
|
||||
result, err := db.Table(table).Delete()
|
||||
gtest.Assert(err, nil)
|
||||
n, _ := result.RowsAffected()
|
||||
gtest.Assert(n, INIT_DATA_SIZE-2)
|
||||
})
|
||||
}
|
||||
@ -7,13 +7,17 @@
|
||||
package gdb_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/gogf/gf/g"
|
||||
"github.com/gogf/gf/g/os/gtime"
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestModel_Inherit_Insert(t *testing.T) {
|
||||
func Test_Model_Inherit_Insert(t *testing.T) {
|
||||
table := createTable()
|
||||
defer dropTable(table)
|
||||
|
||||
gtest.Case(t, func() {
|
||||
type Base struct {
|
||||
Id int `json:"id"`
|
||||
@ -26,7 +30,7 @@ func TestModel_Inherit_Insert(t *testing.T) {
|
||||
Password string `json:"password"`
|
||||
Nickname string `json:"nickname"`
|
||||
}
|
||||
result, err := db.Table("user").Filter().Data(User{
|
||||
result, err := db.Table(table).Filter().Data(User{
|
||||
Passport: "john-test",
|
||||
Password: "123456",
|
||||
Nickname: "John",
|
||||
@ -39,16 +43,16 @@ func TestModel_Inherit_Insert(t *testing.T) {
|
||||
gtest.Assert(err, nil)
|
||||
n, _ := result.RowsAffected()
|
||||
gtest.Assert(n, 1)
|
||||
value, err := db.Table("user").Fields("passport").Where("id=100").Value()
|
||||
value, err := db.Table(table).Fields("passport").Where("id=100").Value()
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(value.String(), "john-test")
|
||||
// Delete this test data.
|
||||
_, err = db.Table("user").Where("id", 100).Delete()
|
||||
gtest.Assert(err, nil)
|
||||
})
|
||||
}
|
||||
|
||||
func TestModel_Inherit_MapToStruct(t *testing.T) {
|
||||
func Test_Model_Inherit_MapToStruct(t *testing.T) {
|
||||
table := createTable()
|
||||
defer dropTable(table)
|
||||
|
||||
gtest.Case(t, func() {
|
||||
type Ids struct {
|
||||
Id int `json:"id"`
|
||||
@ -72,12 +76,12 @@ func TestModel_Inherit_MapToStruct(t *testing.T) {
|
||||
"nickname": "T1",
|
||||
"create_time": gtime.Now().String(),
|
||||
}
|
||||
result, err := db.Table("user").Filter().Data(data).Insert()
|
||||
result, err := db.Table(table).Filter().Data(data).Insert()
|
||||
gtest.Assert(err, nil)
|
||||
n, _ := result.RowsAffected()
|
||||
gtest.Assert(n, 1)
|
||||
|
||||
one, err := db.Table("user").Where("id=100").One()
|
||||
one, err := db.Table(table).Where("id=100").One()
|
||||
gtest.Assert(err, nil)
|
||||
|
||||
user := new(User)
|
||||
@ -89,9 +93,6 @@ func TestModel_Inherit_MapToStruct(t *testing.T) {
|
||||
gtest.Assert(user.Nickname, data["nickname"])
|
||||
gtest.Assert(user.CreateTime, data["create_time"])
|
||||
|
||||
// Delete this test data.
|
||||
_, err = db.Table("user").Where("id", 100).Delete()
|
||||
gtest.Assert(err, nil)
|
||||
})
|
||||
|
||||
}
|
||||
677
g/database/gdb/gdb_unit_z_transaction_test.go
Normal file
677
g/database/gdb/gdb_unit_z_transaction_test.go
Normal file
@ -0,0 +1,677 @@
|
||||
// Copyright 2019 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,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gdb_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/gogf/gf/g"
|
||||
"github.com/gogf/gf/g/os/gtime"
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
)
|
||||
|
||||
func Test_TX_Query(t *testing.T) {
|
||||
tx, err := db.Begin()
|
||||
if err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
if rows, err := tx.Query("SELECT ?", 1); err != nil {
|
||||
gtest.Error(err)
|
||||
} else {
|
||||
rows.Close()
|
||||
}
|
||||
if rows, err := tx.Query("SELECT ?+?", 1, 2); err != nil {
|
||||
gtest.Error(err)
|
||||
} else {
|
||||
rows.Close()
|
||||
}
|
||||
if rows, err := tx.Query("SELECT ?+?", g.Slice{1, 2}); err != nil {
|
||||
gtest.Error(err)
|
||||
} else {
|
||||
rows.Close()
|
||||
}
|
||||
if _, err := tx.Query("ERROR"); err == nil {
|
||||
gtest.Error("FAIL")
|
||||
}
|
||||
if err := tx.Commit(); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_TX_Exec(t *testing.T) {
|
||||
tx, err := db.Begin()
|
||||
if err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
if _, err := tx.Exec("SELECT ?", 1); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
if _, err := tx.Exec("SELECT ?+?", 1, 2); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
if _, err := tx.Exec("SELECT ?+?", g.Slice{1, 2}); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
if _, err := tx.Exec("ERROR"); err == nil {
|
||||
gtest.Error("FAIL")
|
||||
}
|
||||
if err := tx.Commit(); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_TX_Commit(t *testing.T) {
|
||||
tx, err := db.Begin()
|
||||
if err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
if err := tx.Commit(); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_TX_Rollback(t *testing.T) {
|
||||
tx, err := db.Begin()
|
||||
if err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
if err := tx.Rollback(); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_TX_Prepare(t *testing.T) {
|
||||
tx, err := db.Begin()
|
||||
if err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
st, err := tx.Prepare("SELECT 100")
|
||||
if err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
rows, err := st.Query()
|
||||
if err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
array, err := rows.Columns()
|
||||
if err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
gtest.Assert(array[0], "100")
|
||||
if err := rows.Close(); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
if err := tx.Commit(); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_TX_Insert(t *testing.T) {
|
||||
table := createTable()
|
||||
defer dropTable(table)
|
||||
|
||||
gtest.Case(t, func() {
|
||||
tx, err := db.Begin()
|
||||
if err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
if _, err := tx.Insert(table, g.Map{
|
||||
"id": 1,
|
||||
"passport": "t1",
|
||||
"password": "25d55ad283aa400af464c76d713c07ad",
|
||||
"nickname": "T1",
|
||||
"create_time": gtime.Now().String(),
|
||||
}); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
if err := tx.Commit(); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
if n, err := db.Table(table).Count(); err != nil {
|
||||
gtest.Error(err)
|
||||
} else {
|
||||
gtest.Assert(n, 1)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func Test_TX_BatchInsert(t *testing.T) {
|
||||
table := createTable()
|
||||
defer dropTable(table)
|
||||
|
||||
gtest.Case(t, func() {
|
||||
tx, err := db.Begin()
|
||||
if err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
if _, err := tx.BatchInsert(table, g.List{
|
||||
{
|
||||
"id": 2,
|
||||
"passport": "t",
|
||||
"password": "25d55ad283aa400af464c76d713c07ad",
|
||||
"nickname": "T2",
|
||||
"create_time": gtime.Now().String(),
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"passport": "t3",
|
||||
"password": "25d55ad283aa400af464c76d713c07ad",
|
||||
"nickname": "T3",
|
||||
"create_time": gtime.Now().String(),
|
||||
},
|
||||
}, 10); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
if err := tx.Commit(); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
if n, err := db.Table(table).Count(); err != nil {
|
||||
gtest.Error(err)
|
||||
} else {
|
||||
gtest.Assert(n, 2)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func Test_TX_BatchReplace(t *testing.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
|
||||
gtest.Case(t, func() {
|
||||
tx, err := db.Begin()
|
||||
if err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
if _, err := tx.BatchReplace(table, g.List{
|
||||
{
|
||||
"id": 2,
|
||||
"passport": "USER_2",
|
||||
"password": "PASS_2",
|
||||
"nickname": "NAME_2",
|
||||
"create_time": gtime.Now().String(),
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"passport": "USER_4",
|
||||
"password": "PASS_4",
|
||||
"nickname": "NAME_4",
|
||||
"create_time": gtime.Now().String(),
|
||||
},
|
||||
}, 10); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
if err := tx.Commit(); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
if n, err := db.Table(table).Count(); err != nil {
|
||||
gtest.Error(err)
|
||||
} else {
|
||||
gtest.Assert(n, INIT_DATA_SIZE)
|
||||
}
|
||||
if value, err := db.Table(table).Fields("password").Where("id", 2).Value(); err != nil {
|
||||
gtest.Error(err)
|
||||
} else {
|
||||
gtest.Assert(value.String(), "PASS_2")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func Test_TX_BatchSave(t *testing.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
|
||||
gtest.Case(t, func() {
|
||||
tx, err := db.Begin()
|
||||
if err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
if _, err := tx.BatchSave(table, g.List{
|
||||
{
|
||||
"id": 4,
|
||||
"passport": "USER_4",
|
||||
"password": "PASS_4",
|
||||
"nickname": "NAME_4",
|
||||
"create_time": gtime.Now().String(),
|
||||
},
|
||||
}, 10); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
if err := tx.Commit(); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
|
||||
if n, err := db.Table(table).Count(); err != nil {
|
||||
gtest.Error(err)
|
||||
} else {
|
||||
gtest.Assert(n, INIT_DATA_SIZE)
|
||||
}
|
||||
|
||||
if value, err := db.Table(table).Fields("password").Where("id", 4).Value(); err != nil {
|
||||
gtest.Error(err)
|
||||
} else {
|
||||
gtest.Assert(value.String(), "PASS_4")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func Test_TX_Replace(t *testing.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
|
||||
gtest.Case(t, func() {
|
||||
tx, err := db.Begin()
|
||||
if err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
if _, err := tx.Replace(table, g.Map{
|
||||
"id": 1,
|
||||
"passport": "USER_1",
|
||||
"password": "PASS_1",
|
||||
"nickname": "NAME_1",
|
||||
"create_time": gtime.Now().String(),
|
||||
}); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
if err := tx.Rollback(); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
if value, err := db.Table(table).Fields("nickname").Where("id", 1).Value(); err != nil {
|
||||
gtest.Error(err)
|
||||
} else {
|
||||
gtest.Assert(value.String(), "name_1")
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func Test_TX_Save(t *testing.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
|
||||
gtest.Case(t, func() {
|
||||
tx, err := db.Begin()
|
||||
if err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
if _, err := tx.Save(table, g.Map{
|
||||
"id": 1,
|
||||
"passport": "USER_1",
|
||||
"password": "PASS_1",
|
||||
"nickname": "NAME_1",
|
||||
"create_time": gtime.Now().String(),
|
||||
}); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
if err := tx.Commit(); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
if value, err := db.Table(table).Fields("nickname").Where("id", 1).Value(); err != nil {
|
||||
gtest.Error(err)
|
||||
} else {
|
||||
gtest.Assert(value.String(), "NAME_1")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func Test_TX_Update(t *testing.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
|
||||
gtest.Case(t, func() {
|
||||
tx, err := db.Begin()
|
||||
if err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
if result, err := tx.Update(table, "create_time='2019-10-24 10:00:00'", "id=3"); err != nil {
|
||||
gtest.Error(err)
|
||||
} else {
|
||||
n, _ := result.RowsAffected()
|
||||
gtest.Assert(n, 1)
|
||||
}
|
||||
if err := tx.Commit(); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
_, err = tx.Table(table).Fields("create_time").Where("id", 3).Value()
|
||||
gtest.AssertNE(err, nil)
|
||||
|
||||
if value, err := db.Table(table).Fields("create_time").Where("id", 3).Value(); err != nil {
|
||||
gtest.Error(err)
|
||||
} else {
|
||||
gtest.Assert(value.String(), "2019-10-24 10:00:00")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func Test_TX_GetAll(t *testing.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
|
||||
gtest.Case(t, func() {
|
||||
tx, err := db.Begin()
|
||||
if err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
if result, err := tx.GetAll(fmt.Sprintf("SELECT * FROM %s WHERE id=?", table), 1); err != nil {
|
||||
gtest.Error(err)
|
||||
} else {
|
||||
gtest.Assert(len(result), 1)
|
||||
}
|
||||
if err := tx.Commit(); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func Test_TX_GetOne(t *testing.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
|
||||
gtest.Case(t, func() {
|
||||
tx, err := db.Begin()
|
||||
if err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
if record, err := tx.GetOne(fmt.Sprintf("SELECT * FROM %s WHERE passport=?", table), "user_2"); err != nil {
|
||||
gtest.Error(err)
|
||||
} else {
|
||||
if record == nil {
|
||||
gtest.Error("FAIL")
|
||||
}
|
||||
gtest.Assert(record["nickname"].String(), "name_2")
|
||||
}
|
||||
if err := tx.Commit(); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func Test_TX_GetValue(t *testing.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
|
||||
gtest.Case(t, func() {
|
||||
tx, err := db.Begin()
|
||||
if err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
if value, err := tx.GetValue(fmt.Sprintf("SELECT id FROM %s WHERE passport=?", table), "user_3"); err != nil {
|
||||
gtest.Error(err)
|
||||
} else {
|
||||
gtest.Assert(value.Int(), 3)
|
||||
}
|
||||
if err := tx.Commit(); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func Test_TX_GetCount(t *testing.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
|
||||
gtest.Case(t, func() {
|
||||
tx, err := db.Begin()
|
||||
if err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
if count, err := tx.GetCount("SELECT * FROM " + table); err != nil {
|
||||
gtest.Error(err)
|
||||
} else {
|
||||
gtest.Assert(count, INIT_DATA_SIZE)
|
||||
}
|
||||
if err := tx.Commit(); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func Test_TX_GetStruct(t *testing.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
|
||||
gtest.Case(t, func() {
|
||||
tx, err := db.Begin()
|
||||
if err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
type User struct {
|
||||
Id int
|
||||
Passport string
|
||||
Password string
|
||||
NickName string
|
||||
CreateTime gtime.Time
|
||||
}
|
||||
user := new(User)
|
||||
if err := tx.GetStruct(user, fmt.Sprintf("SELECT * FROM %s WHERE id=?", table), 3); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
gtest.Assert(user.NickName, "name_3")
|
||||
gtest.Assert(user.CreateTime.String(), "2018-10-24 10:00:00")
|
||||
if err := tx.Commit(); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
tx, err := db.Begin()
|
||||
if err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
type User struct {
|
||||
Id int
|
||||
Passport string
|
||||
Password string
|
||||
NickName string
|
||||
CreateTime *gtime.Time
|
||||
}
|
||||
user := new(User)
|
||||
if err := tx.GetStruct(user, fmt.Sprintf("SELECT * FROM %s WHERE id=?", table), 3); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
gtest.Assert(user.NickName, "name_3")
|
||||
gtest.Assert(user.CreateTime.String(), "2018-10-24 10:00:00")
|
||||
if err := tx.Commit(); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func Test_TX_GetStructs(t *testing.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
|
||||
gtest.Case(t, func() {
|
||||
tx, err := db.Begin()
|
||||
if err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
type User struct {
|
||||
Id int
|
||||
Passport string
|
||||
Password string
|
||||
NickName string
|
||||
CreateTime gtime.Time
|
||||
}
|
||||
var users []User
|
||||
if err := tx.GetStructs(&users, fmt.Sprintf("SELECT * FROM %s WHERE id>=?", table), 1); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
gtest.Assert(len(users), INIT_DATA_SIZE)
|
||||
gtest.Assert(users[0].Id, 1)
|
||||
gtest.Assert(users[1].Id, 2)
|
||||
gtest.Assert(users[2].Id, 3)
|
||||
gtest.Assert(users[0].NickName, "name_1")
|
||||
gtest.Assert(users[1].NickName, "name_2")
|
||||
gtest.Assert(users[2].NickName, "name_3")
|
||||
gtest.Assert(users[2].CreateTime.String(), "2018-10-24 10:00:00")
|
||||
if err := tx.Commit(); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
})
|
||||
|
||||
gtest.Case(t, func() {
|
||||
tx, err := db.Begin()
|
||||
if err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
type User struct {
|
||||
Id int
|
||||
Passport string
|
||||
Password string
|
||||
NickName string
|
||||
CreateTime *gtime.Time
|
||||
}
|
||||
var users []User
|
||||
if err := tx.GetStructs(&users, fmt.Sprintf("SELECT * FROM %s WHERE id>=?", table), 1); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
gtest.Assert(len(users), INIT_DATA_SIZE)
|
||||
gtest.Assert(users[0].Id, 1)
|
||||
gtest.Assert(users[1].Id, 2)
|
||||
gtest.Assert(users[2].Id, 3)
|
||||
gtest.Assert(users[0].NickName, "name_1")
|
||||
gtest.Assert(users[1].NickName, "name_2")
|
||||
gtest.Assert(users[2].NickName, "name_3")
|
||||
gtest.Assert(users[2].CreateTime.String(), "2018-10-24 10:00:00")
|
||||
if err := tx.Commit(); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func Test_TX_GetScan(t *testing.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
|
||||
gtest.Case(t, func() {
|
||||
tx, err := db.Begin()
|
||||
if err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
type User struct {
|
||||
Id int
|
||||
Passport string
|
||||
Password string
|
||||
NickName string
|
||||
CreateTime gtime.Time
|
||||
}
|
||||
user := new(User)
|
||||
if err := tx.GetScan(user, fmt.Sprintf("SELECT * FROM %s WHERE id=?", table), 3); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
gtest.Assert(user.NickName, "name_3")
|
||||
gtest.Assert(user.CreateTime.String(), "2018-10-24 10:00:00")
|
||||
if err := tx.Commit(); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
})
|
||||
gtest.Case(t, func() {
|
||||
tx, err := db.Begin()
|
||||
if err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
type User struct {
|
||||
Id int
|
||||
Passport string
|
||||
Password string
|
||||
NickName string
|
||||
CreateTime *gtime.Time
|
||||
}
|
||||
user := new(User)
|
||||
if err := tx.GetScan(user, fmt.Sprintf("SELECT * FROM %s WHERE id=?", table), 3); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
gtest.Assert(user.NickName, "name_3")
|
||||
gtest.Assert(user.CreateTime.String(), "2018-10-24 10:00:00")
|
||||
if err := tx.Commit(); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
})
|
||||
|
||||
gtest.Case(t, func() {
|
||||
tx, err := db.Begin()
|
||||
if err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
type User struct {
|
||||
Id int
|
||||
Passport string
|
||||
Password string
|
||||
NickName string
|
||||
CreateTime gtime.Time
|
||||
}
|
||||
var users []User
|
||||
if err := tx.GetScan(&users, fmt.Sprintf("SELECT * FROM %s WHERE id>=?", table), 1); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
gtest.Assert(len(users), INIT_DATA_SIZE)
|
||||
gtest.Assert(users[0].Id, 1)
|
||||
gtest.Assert(users[1].Id, 2)
|
||||
gtest.Assert(users[2].Id, 3)
|
||||
gtest.Assert(users[0].NickName, "name_1")
|
||||
gtest.Assert(users[1].NickName, "name_2")
|
||||
gtest.Assert(users[2].NickName, "name_3")
|
||||
gtest.Assert(users[2].CreateTime.String(), "2018-10-24 10:00:00")
|
||||
if err := tx.Commit(); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
})
|
||||
|
||||
gtest.Case(t, func() {
|
||||
tx, err := db.Begin()
|
||||
if err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
type User struct {
|
||||
Id int
|
||||
Passport string
|
||||
Password string
|
||||
NickName string
|
||||
CreateTime *gtime.Time
|
||||
}
|
||||
var users []User
|
||||
if err := tx.GetScan(&users, fmt.Sprintf("SELECT * FROM %s WHERE id>=?", table), 1); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
gtest.Assert(len(users), INIT_DATA_SIZE)
|
||||
gtest.Assert(users[0].Id, 1)
|
||||
gtest.Assert(users[1].Id, 2)
|
||||
gtest.Assert(users[2].Id, 3)
|
||||
gtest.Assert(users[0].NickName, "name_1")
|
||||
gtest.Assert(users[1].NickName, "name_2")
|
||||
gtest.Assert(users[2].NickName, "name_3")
|
||||
gtest.Assert(users[2].CreateTime.String(), "2018-10-24 10:00:00")
|
||||
if err := tx.Commit(); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func Test_TX_Delete(t *testing.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
|
||||
gtest.Case(t, func() {
|
||||
tx, err := db.Begin()
|
||||
if err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
if _, err := tx.Delete(table, nil); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
if err := tx.Commit(); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
if n, err := db.Table(table).Count(); err != nil {
|
||||
gtest.Error(err)
|
||||
} else {
|
||||
gtest.Assert(n, 0)
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
@ -7,11 +7,12 @@
|
||||
package gredis_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/g/database/gredis"
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
redis2 "github.com/gogf/gf/third/github.com/gomodule/redigo/redis"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
|
||||
@ -22,10 +22,7 @@ func Encode(src []byte) []byte {
|
||||
func Decode(dst []byte) ([]byte, error) {
|
||||
src := make([]byte, base64.StdEncoding.DecodedLen(len(dst)))
|
||||
n, err := base64.StdEncoding.Decode(src, dst)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return src[:n], nil
|
||||
return src[:n], err
|
||||
}
|
||||
|
||||
// EncodeString encodes bytes with BASE64 algorithm.
|
||||
|
||||
@ -6,340 +6,129 @@
|
||||
|
||||
// Package gbinary provides useful API for handling binary/bytes data.
|
||||
//
|
||||
// 注意gbinary模块统一使用LittleEndian进行编码。
|
||||
// 注意gbinary模块默认使用LittleEndian进行编码。
|
||||
package gbinary
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"math"
|
||||
)
|
||||
|
||||
// 二进制位(0|1)
|
||||
type Bit int8
|
||||
|
||||
// 针对基本类型进行二进制打包,支持的基本数据类型包括:int/8/16/32/64、uint/8/16/32/64、float32/64、bool、string、[]byte
|
||||
// 其他未知类型使用 fmt.Sprintf("%v", value) 转换为字符串之后处理
|
||||
func Encode(vs ...interface{}) []byte {
|
||||
buf := new(bytes.Buffer)
|
||||
for i := 0; i < len(vs); i++ {
|
||||
if vs[i] == nil {
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
switch value := vs[i].(type) {
|
||||
case int:
|
||||
buf.Write(EncodeInt(value))
|
||||
case int8:
|
||||
buf.Write(EncodeInt8(value))
|
||||
case int16:
|
||||
buf.Write(EncodeInt16(value))
|
||||
case int32:
|
||||
buf.Write(EncodeInt32(value))
|
||||
case int64:
|
||||
buf.Write(EncodeInt64(value))
|
||||
case uint:
|
||||
buf.Write(EncodeUint(value))
|
||||
case uint8:
|
||||
buf.Write(EncodeUint8(value))
|
||||
case uint16:
|
||||
buf.Write(EncodeUint16(value))
|
||||
case uint32:
|
||||
buf.Write(EncodeUint32(value))
|
||||
case uint64:
|
||||
buf.Write(EncodeUint64(value))
|
||||
case bool:
|
||||
buf.Write(EncodeBool(value))
|
||||
case string:
|
||||
buf.Write(EncodeString(value))
|
||||
case []byte:
|
||||
buf.Write(value)
|
||||
case float32:
|
||||
buf.Write(EncodeFloat32(value))
|
||||
case float64:
|
||||
buf.Write(EncodeFloat64(value))
|
||||
default:
|
||||
if err := binary.Write(buf, binary.LittleEndian, value); err != nil {
|
||||
buf.Write(EncodeString(fmt.Sprintf("%v", value)))
|
||||
}
|
||||
}
|
||||
}
|
||||
return buf.Bytes()
|
||||
func Encode(values ...interface{}) []byte {
|
||||
return LeEncode(values...)
|
||||
}
|
||||
|
||||
// 将变量转换为二进制[]byte,并指定固定的[]byte长度返回,长度单位为字节(byte);
|
||||
// 如果转换的二进制长度超过指定长度,那么进行截断处理
|
||||
func EncodeByLength(length int, vs ...interface{}) []byte {
|
||||
b := Encode(vs...)
|
||||
if len(b) < length {
|
||||
b = append(b, make([]byte, length-len(b))...)
|
||||
} else if len(b) > length {
|
||||
b = b[0:length]
|
||||
}
|
||||
return b
|
||||
func EncodeByLength(length int, values ...interface{}) []byte {
|
||||
return LeEncodeByLength(length, values...)
|
||||
}
|
||||
|
||||
// 整形二进制解包,注意第二个及其后参数为字长确定的整形变量的指针地址,以便确定解析的[]byte长度,
|
||||
// 例如:int8/16/32/64、uint8/16/32/64、float32/64等等
|
||||
func Decode(b []byte, vs ...interface{}) error {
|
||||
buf := bytes.NewBuffer(b)
|
||||
for i := 0; i < len(vs); i++ {
|
||||
err := binary.Read(buf, binary.LittleEndian, vs[i])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
func Decode(b []byte, values ...interface{}) error {
|
||||
return LeDecode(b, values...)
|
||||
}
|
||||
|
||||
func EncodeString(s string) []byte {
|
||||
return []byte(s)
|
||||
return LeEncodeString(s)
|
||||
}
|
||||
|
||||
func DecodeToString(b []byte) string {
|
||||
return string(b)
|
||||
return LeDecodeToString(b)
|
||||
}
|
||||
|
||||
func EncodeBool(b bool) []byte {
|
||||
if b == true {
|
||||
return []byte{1}
|
||||
} else {
|
||||
return []byte{0}
|
||||
}
|
||||
return LeEncodeBool(b)
|
||||
}
|
||||
|
||||
// 自动识别int类型长度,转换为[]byte
|
||||
func EncodeInt(i int) []byte {
|
||||
if i <= math.MaxInt8 {
|
||||
return EncodeInt8(int8(i))
|
||||
} else if i <= math.MaxInt16 {
|
||||
return EncodeInt16(int16(i))
|
||||
} else if i <= math.MaxInt32 {
|
||||
return EncodeInt32(int32(i))
|
||||
} else {
|
||||
return EncodeInt64(int64(i))
|
||||
}
|
||||
return LeEncodeInt(i)
|
||||
}
|
||||
|
||||
// 自动识别uint类型长度,转换为[]byte
|
||||
func EncodeUint(i uint) []byte {
|
||||
if i <= math.MaxUint8 {
|
||||
return EncodeUint8(uint8(i))
|
||||
} else if i <= math.MaxUint16 {
|
||||
return EncodeUint16(uint16(i))
|
||||
} else if i <= math.MaxUint32 {
|
||||
return EncodeUint32(uint32(i))
|
||||
} else {
|
||||
return EncodeUint64(uint64(i))
|
||||
}
|
||||
return LeEncodeUint(i)
|
||||
}
|
||||
|
||||
func EncodeInt8(i int8) []byte {
|
||||
return []byte{byte(i)}
|
||||
return LeEncodeInt8(i)
|
||||
}
|
||||
|
||||
func EncodeUint8(i uint8) []byte {
|
||||
return []byte{byte(i)}
|
||||
return LeEncodeUint8(i)
|
||||
}
|
||||
|
||||
func EncodeInt16(i int16) []byte {
|
||||
bytes := make([]byte, 2)
|
||||
binary.LittleEndian.PutUint16(bytes, uint16(i))
|
||||
return bytes
|
||||
return LeEncodeInt16(i)
|
||||
}
|
||||
|
||||
func EncodeUint16(i uint16) []byte {
|
||||
bytes := make([]byte, 2)
|
||||
binary.LittleEndian.PutUint16(bytes, i)
|
||||
return bytes
|
||||
return LeEncodeUint16(i)
|
||||
}
|
||||
|
||||
func EncodeInt32(i int32) []byte {
|
||||
bytes := make([]byte, 4)
|
||||
binary.LittleEndian.PutUint32(bytes, uint32(i))
|
||||
return bytes
|
||||
return LeEncodeInt32(i)
|
||||
}
|
||||
|
||||
func EncodeUint32(i uint32) []byte {
|
||||
bytes := make([]byte, 4)
|
||||
binary.LittleEndian.PutUint32(bytes, i)
|
||||
return bytes
|
||||
return LeEncodeUint32(i)
|
||||
}
|
||||
|
||||
func EncodeInt64(i int64) []byte {
|
||||
bytes := make([]byte, 8)
|
||||
binary.LittleEndian.PutUint64(bytes, uint64(i))
|
||||
return bytes
|
||||
return LeEncodeInt64(i)
|
||||
}
|
||||
|
||||
func EncodeUint64(i uint64) []byte {
|
||||
bytes := make([]byte, 8)
|
||||
binary.LittleEndian.PutUint64(bytes, i)
|
||||
return bytes
|
||||
return LeEncodeUint64(i)
|
||||
}
|
||||
|
||||
func EncodeFloat32(f float32) []byte {
|
||||
bits := math.Float32bits(f)
|
||||
bytes := make([]byte, 4)
|
||||
binary.LittleEndian.PutUint32(bytes, bits)
|
||||
return bytes
|
||||
return LeEncodeFloat32(f)
|
||||
}
|
||||
|
||||
func EncodeFloat64(f float64) []byte {
|
||||
bits := math.Float64bits(f)
|
||||
bytes := make([]byte, 8)
|
||||
binary.LittleEndian.PutUint64(bytes, bits)
|
||||
return bytes
|
||||
return LeEncodeFloat64(f)
|
||||
}
|
||||
|
||||
// 当b位数不够时,进行高位补0
|
||||
func fillUpSize(b []byte, l int) []byte {
|
||||
if len(b) >= l {
|
||||
return b
|
||||
}
|
||||
c := make([]byte, 0)
|
||||
c = append(c, b...)
|
||||
for i := 0; i < l-len(b); i++ {
|
||||
c = append(c, 0x00)
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
// 将二进制解析为int类型,根据[]byte的长度进行自动转换.
|
||||
// 注意内部使用的是uint*,使用int会造成位丢失。
|
||||
func DecodeToInt(b []byte) int {
|
||||
if len(b) < 2 {
|
||||
return int(DecodeToUint8(b))
|
||||
} else if len(b) < 3 {
|
||||
return int(DecodeToUint16(b))
|
||||
} else if len(b) < 5 {
|
||||
return int(DecodeToUint32(b))
|
||||
} else {
|
||||
return int(DecodeToUint64(b))
|
||||
}
|
||||
return LeDecodeToInt(b)
|
||||
}
|
||||
|
||||
// 将二进制解析为uint类型,根据[]byte的长度进行自动转换
|
||||
func DecodeToUint(b []byte) uint {
|
||||
if len(b) < 2 {
|
||||
return uint(DecodeToUint8(b))
|
||||
} else if len(b) < 3 {
|
||||
return uint(DecodeToUint16(b))
|
||||
} else if len(b) < 5 {
|
||||
return uint(DecodeToUint32(b))
|
||||
} else {
|
||||
return uint(DecodeToUint64(b))
|
||||
}
|
||||
return LeDecodeToUint(b)
|
||||
}
|
||||
|
||||
// 将二进制解析为bool类型,识别标准是判断二进制中数值是否都为0,或者为空
|
||||
func DecodeToBool(b []byte) bool {
|
||||
if len(b) == 0 {
|
||||
return false
|
||||
}
|
||||
if bytes.Compare(b, make([]byte, len(b))) == 0 {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
return LeDecodeToBool(b)
|
||||
}
|
||||
|
||||
func DecodeToInt8(b []byte) int8 {
|
||||
return int8(b[0])
|
||||
return LeDecodeToInt8(b)
|
||||
}
|
||||
|
||||
func DecodeToUint8(b []byte) uint8 {
|
||||
return uint8(b[0])
|
||||
return LeDecodeToUint8(b)
|
||||
}
|
||||
|
||||
func DecodeToInt16(b []byte) int16 {
|
||||
return int16(binary.LittleEndian.Uint16(fillUpSize(b, 2)))
|
||||
return LeDecodeToInt16(b)
|
||||
}
|
||||
|
||||
func DecodeToUint16(b []byte) uint16 {
|
||||
return binary.LittleEndian.Uint16(fillUpSize(b, 2))
|
||||
return LeDecodeToUint16(b)
|
||||
}
|
||||
|
||||
func DecodeToInt32(b []byte) int32 {
|
||||
return int32(binary.LittleEndian.Uint32(fillUpSize(b, 4)))
|
||||
return LeDecodeToInt32(b)
|
||||
}
|
||||
|
||||
func DecodeToUint32(b []byte) uint32 {
|
||||
return binary.LittleEndian.Uint32(fillUpSize(b, 4))
|
||||
return LeDecodeToUint32(b)
|
||||
}
|
||||
|
||||
func DecodeToInt64(b []byte) int64 {
|
||||
return int64(binary.LittleEndian.Uint64(fillUpSize(b, 8)))
|
||||
return LeDecodeToInt64(b)
|
||||
}
|
||||
|
||||
func DecodeToUint64(b []byte) uint64 {
|
||||
return binary.LittleEndian.Uint64(fillUpSize(b, 8))
|
||||
return LeDecodeToUint64(b)
|
||||
}
|
||||
|
||||
func DecodeToFloat32(b []byte) float32 {
|
||||
return math.Float32frombits(binary.LittleEndian.Uint32(fillUpSize(b, 4)))
|
||||
return LeDecodeToFloat32(b)
|
||||
}
|
||||
|
||||
func DecodeToFloat64(b []byte) float64 {
|
||||
return math.Float64frombits(binary.LittleEndian.Uint64(fillUpSize(b, 8)))
|
||||
}
|
||||
|
||||
// 默认编码
|
||||
func EncodeBits(bits []Bit, i int, l int) []Bit {
|
||||
return EncodeBitsWithUint(bits, uint(i), l)
|
||||
}
|
||||
|
||||
// 将ui按位合并到bits数组中,并占length长度位(注意:uis数组中存放的是二进制的0|1数字)
|
||||
func EncodeBitsWithUint(bits []Bit, ui uint, l int) []Bit {
|
||||
a := make([]Bit, l)
|
||||
for i := l - 1; i >= 0; i-- {
|
||||
a[i] = Bit(ui & 1)
|
||||
ui >>= 1
|
||||
}
|
||||
if bits != nil {
|
||||
return append(bits, a...)
|
||||
} else {
|
||||
return a
|
||||
}
|
||||
}
|
||||
|
||||
// 将bits转换为[]byte,从左至右进行编码,不足1 byte按0往末尾补充
|
||||
func EncodeBitsToBytes(bits []Bit) []byte {
|
||||
if len(bits)%8 != 0 {
|
||||
for i := 0; i < len(bits)%8; i++ {
|
||||
bits = append(bits, 0)
|
||||
}
|
||||
}
|
||||
b := make([]byte, 0)
|
||||
for i := 0; i < len(bits); i += 8 {
|
||||
b = append(b, byte(DecodeBitsToUint(bits[i:i+8])))
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// 解析为int
|
||||
func DecodeBits(bits []Bit) int {
|
||||
v := int(0)
|
||||
for _, i := range bits {
|
||||
v = v<<1 | int(i)
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// 解析为uint
|
||||
func DecodeBitsToUint(bits []Bit) uint {
|
||||
v := uint(0)
|
||||
for _, i := range bits {
|
||||
v = v<<1 | uint(i)
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// 解析[]byte为字位数组[]uint8
|
||||
func DecodeBytesToBits(bs []byte) []Bit {
|
||||
bits := make([]Bit, 0)
|
||||
for _, b := range bs {
|
||||
bits = EncodeBitsWithUint(bits, uint(b), 8)
|
||||
}
|
||||
return bits
|
||||
return LeDecodeToFloat64(b)
|
||||
}
|
||||
|
||||
278
g/encoding/gbinary/gbinary_be.go
Normal file
278
g/encoding/gbinary/gbinary_be.go
Normal file
@ -0,0 +1,278 @@
|
||||
// Copyright 2017 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,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gbinary
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"math"
|
||||
)
|
||||
|
||||
// 针对基本类型进行二进制打包,支持的基本数据类型包括:
|
||||
// int/8/16/32/64、uint/8/16/32/64、float32/64、bool、string、[]byte。
|
||||
// 其他未知类型使用 fmt.Sprintf("%v", value) 转换为字符串之后处理。
|
||||
func BeEncode(values ...interface{}) []byte {
|
||||
buf := new(bytes.Buffer)
|
||||
for i := 0; i < len(values); i++ {
|
||||
if values[i] == nil {
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
switch value := values[i].(type) {
|
||||
case int:
|
||||
buf.Write(BeEncodeInt(value))
|
||||
case int8:
|
||||
buf.Write(BeEncodeInt8(value))
|
||||
case int16:
|
||||
buf.Write(BeEncodeInt16(value))
|
||||
case int32:
|
||||
buf.Write(BeEncodeInt32(value))
|
||||
case int64:
|
||||
buf.Write(BeEncodeInt64(value))
|
||||
case uint:
|
||||
buf.Write(BeEncodeUint(value))
|
||||
case uint8:
|
||||
buf.Write(BeEncodeUint8(value))
|
||||
case uint16:
|
||||
buf.Write(BeEncodeUint16(value))
|
||||
case uint32:
|
||||
buf.Write(BeEncodeUint32(value))
|
||||
case uint64:
|
||||
buf.Write(BeEncodeUint64(value))
|
||||
case bool:
|
||||
buf.Write(BeEncodeBool(value))
|
||||
case string:
|
||||
buf.Write(BeEncodeString(value))
|
||||
case []byte:
|
||||
buf.Write(value)
|
||||
case float32:
|
||||
buf.Write(BeEncodeFloat32(value))
|
||||
case float64:
|
||||
buf.Write(BeEncodeFloat64(value))
|
||||
default:
|
||||
if err := binary.Write(buf, binary.BigEndian, value); err != nil {
|
||||
buf.Write(BeEncodeString(fmt.Sprintf("%v", value)))
|
||||
}
|
||||
}
|
||||
}
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
// 将变量转换为二进制[]byte,并指定固定的[]byte长度返回,长度单位为字节(byte);
|
||||
// 如果转换的二进制长度超过指定长度,那么进行截断处理
|
||||
func BeEncodeByLength(length int, values ...interface{}) []byte {
|
||||
b := BeEncode(values...)
|
||||
if len(b) < length {
|
||||
b = append(b, make([]byte, length-len(b))...)
|
||||
} else if len(b) > length {
|
||||
b = b[0:length]
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// 整形二进制解包,注意第二个及其后参数为字长确定的整形变量的指针地址,以便确定解析的[]byte长度,
|
||||
// 例如:int8/16/32/64、uint8/16/32/64、float32/64等等
|
||||
func BeDecode(b []byte, values ...interface{}) error {
|
||||
buf := bytes.NewBuffer(b)
|
||||
for i := 0; i < len(values); i++ {
|
||||
err := binary.Read(buf, binary.BigEndian, values[i])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func BeEncodeString(s string) []byte {
|
||||
return []byte(s)
|
||||
}
|
||||
|
||||
func BeDecodeToString(b []byte) string {
|
||||
return string(b)
|
||||
}
|
||||
|
||||
func BeEncodeBool(b bool) []byte {
|
||||
if b == true {
|
||||
return []byte{1}
|
||||
} else {
|
||||
return []byte{0}
|
||||
}
|
||||
}
|
||||
|
||||
// 自动识别int类型长度,转换为[]byte
|
||||
func BeEncodeInt(i int) []byte {
|
||||
if i <= math.MaxInt8 {
|
||||
return BeEncodeInt8(int8(i))
|
||||
} else if i <= math.MaxInt16 {
|
||||
return BeEncodeInt16(int16(i))
|
||||
} else if i <= math.MaxInt32 {
|
||||
return BeEncodeInt32(int32(i))
|
||||
} else {
|
||||
return BeEncodeInt64(int64(i))
|
||||
}
|
||||
}
|
||||
|
||||
// 自动识别uint类型长度,转换为[]byte
|
||||
func BeEncodeUint(i uint) []byte {
|
||||
if i <= math.MaxUint8 {
|
||||
return BeEncodeUint8(uint8(i))
|
||||
} else if i <= math.MaxUint16 {
|
||||
return BeEncodeUint16(uint16(i))
|
||||
} else if i <= math.MaxUint32 {
|
||||
return BeEncodeUint32(uint32(i))
|
||||
} else {
|
||||
return BeEncodeUint64(uint64(i))
|
||||
}
|
||||
}
|
||||
|
||||
func BeEncodeInt8(i int8) []byte {
|
||||
return []byte{byte(i)}
|
||||
}
|
||||
|
||||
func BeEncodeUint8(i uint8) []byte {
|
||||
return []byte{byte(i)}
|
||||
}
|
||||
|
||||
func BeEncodeInt16(i int16) []byte {
|
||||
b := make([]byte, 2)
|
||||
binary.BigEndian.PutUint16(b, uint16(i))
|
||||
return b
|
||||
}
|
||||
|
||||
func BeEncodeUint16(i uint16) []byte {
|
||||
b := make([]byte, 2)
|
||||
binary.BigEndian.PutUint16(b, i)
|
||||
return b
|
||||
}
|
||||
|
||||
func BeEncodeInt32(i int32) []byte {
|
||||
b := make([]byte, 4)
|
||||
binary.BigEndian.PutUint32(b, uint32(i))
|
||||
return b
|
||||
}
|
||||
|
||||
func BeEncodeUint32(i uint32) []byte {
|
||||
b := make([]byte, 4)
|
||||
binary.BigEndian.PutUint32(b, i)
|
||||
return b
|
||||
}
|
||||
|
||||
func BeEncodeInt64(i int64) []byte {
|
||||
b := make([]byte, 8)
|
||||
binary.BigEndian.PutUint64(b, uint64(i))
|
||||
return b
|
||||
}
|
||||
|
||||
func BeEncodeUint64(i uint64) []byte {
|
||||
b := make([]byte, 8)
|
||||
binary.BigEndian.PutUint64(b, i)
|
||||
return b
|
||||
}
|
||||
|
||||
func BeEncodeFloat32(f float32) []byte {
|
||||
bits := math.Float32bits(f)
|
||||
b := make([]byte, 4)
|
||||
binary.BigEndian.PutUint32(b, bits)
|
||||
return b
|
||||
}
|
||||
|
||||
func BeEncodeFloat64(f float64) []byte {
|
||||
bits := math.Float64bits(f)
|
||||
b := make([]byte, 8)
|
||||
binary.BigEndian.PutUint64(b, bits)
|
||||
return b
|
||||
}
|
||||
|
||||
// 将二进制解析为int类型,根据[]byte的长度进行自动转换.
|
||||
// 注意内部使用的是uint*,使用int会造成位丢失。
|
||||
func BeDecodeToInt(b []byte) int {
|
||||
if len(b) < 2 {
|
||||
return int(BeDecodeToUint8(b))
|
||||
} else if len(b) < 3 {
|
||||
return int(BeDecodeToUint16(b))
|
||||
} else if len(b) < 5 {
|
||||
return int(BeDecodeToUint32(b))
|
||||
} else {
|
||||
return int(BeDecodeToUint64(b))
|
||||
}
|
||||
}
|
||||
|
||||
// 将二进制解析为uint类型,根据[]byte的长度进行自动转换
|
||||
func BeDecodeToUint(b []byte) uint {
|
||||
if len(b) < 2 {
|
||||
return uint(BeDecodeToUint8(b))
|
||||
} else if len(b) < 3 {
|
||||
return uint(BeDecodeToUint16(b))
|
||||
} else if len(b) < 5 {
|
||||
return uint(BeDecodeToUint32(b))
|
||||
} else {
|
||||
return uint(BeDecodeToUint64(b))
|
||||
}
|
||||
}
|
||||
|
||||
// 将二进制解析为bool类型,识别标准是判断二进制中数值是否都为0,或者为空。
|
||||
func BeDecodeToBool(b []byte) bool {
|
||||
if len(b) == 0 {
|
||||
return false
|
||||
}
|
||||
if bytes.Compare(b, make([]byte, len(b))) == 0 {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func BeDecodeToInt8(b []byte) int8 {
|
||||
return int8(b[0])
|
||||
}
|
||||
|
||||
func BeDecodeToUint8(b []byte) uint8 {
|
||||
return uint8(b[0])
|
||||
}
|
||||
|
||||
func BeDecodeToInt16(b []byte) int16 {
|
||||
return int16(binary.BigEndian.Uint16(BeFillUpSize(b, 2)))
|
||||
}
|
||||
|
||||
func BeDecodeToUint16(b []byte) uint16 {
|
||||
return binary.BigEndian.Uint16(BeFillUpSize(b, 2))
|
||||
}
|
||||
|
||||
func BeDecodeToInt32(b []byte) int32 {
|
||||
return int32(binary.BigEndian.Uint32(BeFillUpSize(b, 4)))
|
||||
}
|
||||
|
||||
func BeDecodeToUint32(b []byte) uint32 {
|
||||
return binary.BigEndian.Uint32(BeFillUpSize(b, 4))
|
||||
}
|
||||
|
||||
func BeDecodeToInt64(b []byte) int64 {
|
||||
return int64(binary.BigEndian.Uint64(BeFillUpSize(b, 8)))
|
||||
}
|
||||
|
||||
func BeDecodeToUint64(b []byte) uint64 {
|
||||
return binary.BigEndian.Uint64(BeFillUpSize(b, 8))
|
||||
}
|
||||
|
||||
func BeDecodeToFloat32(b []byte) float32 {
|
||||
return math.Float32frombits(binary.BigEndian.Uint32(BeFillUpSize(b, 4)))
|
||||
}
|
||||
|
||||
func BeDecodeToFloat64(b []byte) float64 {
|
||||
return math.Float64frombits(binary.BigEndian.Uint64(BeFillUpSize(b, 8)))
|
||||
}
|
||||
|
||||
// 当b位数不够时,进行低位补0。
|
||||
// 注意这里为了不影响原有输入参数,是采用的值复制设计。
|
||||
func BeFillUpSize(b []byte, l int) []byte {
|
||||
if len(b) >= l {
|
||||
return b[:l]
|
||||
}
|
||||
c := make([]byte, l)
|
||||
copy(c[l-len(b):], b)
|
||||
return c
|
||||
}
|
||||
72
g/encoding/gbinary/gbinary_bits.go
Normal file
72
g/encoding/gbinary/gbinary_bits.go
Normal file
@ -0,0 +1,72 @@
|
||||
// Copyright 2017 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,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gbinary
|
||||
|
||||
// 实验特性
|
||||
|
||||
// 二进制位(0|1)
|
||||
type Bit int8
|
||||
|
||||
// 默认编码
|
||||
func EncodeBits(bits []Bit, i int, l int) []Bit {
|
||||
return EncodeBitsWithUint(bits, uint(i), l)
|
||||
}
|
||||
|
||||
// 将ui按位合并到bits数组中,并占length长度位(注意:uis数组中存放的是二进制的0|1数字)
|
||||
func EncodeBitsWithUint(bits []Bit, ui uint, l int) []Bit {
|
||||
a := make([]Bit, l)
|
||||
for i := l - 1; i >= 0; i-- {
|
||||
a[i] = Bit(ui & 1)
|
||||
ui >>= 1
|
||||
}
|
||||
if bits != nil {
|
||||
return append(bits, a...)
|
||||
} else {
|
||||
return a
|
||||
}
|
||||
}
|
||||
|
||||
// 将bits转换为[]byte,从左至右进行编码,不足1 byte按0往末尾补充
|
||||
func EncodeBitsToBytes(bits []Bit) []byte {
|
||||
if len(bits)%8 != 0 {
|
||||
for i := 0; i < len(bits)%8; i++ {
|
||||
bits = append(bits, 0)
|
||||
}
|
||||
}
|
||||
b := make([]byte, 0)
|
||||
for i := 0; i < len(bits); i += 8 {
|
||||
b = append(b, byte(DecodeBitsToUint(bits[i:i+8])))
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// 解析为int
|
||||
func DecodeBits(bits []Bit) int {
|
||||
v := int(0)
|
||||
for _, i := range bits {
|
||||
v = v<<1 | int(i)
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// 解析为uint
|
||||
func DecodeBitsToUint(bits []Bit) uint {
|
||||
v := uint(0)
|
||||
for _, i := range bits {
|
||||
v = v<<1 | uint(i)
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// 解析[]byte为字位数组[]uint8
|
||||
func DecodeBytesToBits(bs []byte) []Bit {
|
||||
bits := make([]Bit, 0)
|
||||
for _, b := range bs {
|
||||
bits = EncodeBitsWithUint(bits, uint(b), 8)
|
||||
}
|
||||
return bits
|
||||
}
|
||||
7
g/encoding/gbinary/gbinary_func.go
Normal file
7
g/encoding/gbinary/gbinary_func.go
Normal file
@ -0,0 +1,7 @@
|
||||
// Copyright 2019 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,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gbinary
|
||||
278
g/encoding/gbinary/gbinary_le.go
Normal file
278
g/encoding/gbinary/gbinary_le.go
Normal file
@ -0,0 +1,278 @@
|
||||
// Copyright 2017 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,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gbinary
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"math"
|
||||
)
|
||||
|
||||
// 针对基本类型进行二进制打包,支持的基本数据类型包括:
|
||||
// int/8/16/32/64、uint/8/16/32/64、float32/64、bool、string、[]byte。
|
||||
// 其他未知类型使用 fmt.Sprintf("%v", value) 转换为字符串之后处理。
|
||||
func LeEncode(values ...interface{}) []byte {
|
||||
buf := new(bytes.Buffer)
|
||||
for i := 0; i < len(values); i++ {
|
||||
if values[i] == nil {
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
switch value := values[i].(type) {
|
||||
case int:
|
||||
buf.Write(LeEncodeInt(value))
|
||||
case int8:
|
||||
buf.Write(LeEncodeInt8(value))
|
||||
case int16:
|
||||
buf.Write(LeEncodeInt16(value))
|
||||
case int32:
|
||||
buf.Write(LeEncodeInt32(value))
|
||||
case int64:
|
||||
buf.Write(LeEncodeInt64(value))
|
||||
case uint:
|
||||
buf.Write(LeEncodeUint(value))
|
||||
case uint8:
|
||||
buf.Write(LeEncodeUint8(value))
|
||||
case uint16:
|
||||
buf.Write(LeEncodeUint16(value))
|
||||
case uint32:
|
||||
buf.Write(LeEncodeUint32(value))
|
||||
case uint64:
|
||||
buf.Write(LeEncodeUint64(value))
|
||||
case bool:
|
||||
buf.Write(LeEncodeBool(value))
|
||||
case string:
|
||||
buf.Write(LeEncodeString(value))
|
||||
case []byte:
|
||||
buf.Write(value)
|
||||
case float32:
|
||||
buf.Write(LeEncodeFloat32(value))
|
||||
case float64:
|
||||
buf.Write(LeEncodeFloat64(value))
|
||||
default:
|
||||
if err := binary.Write(buf, binary.LittleEndian, value); err != nil {
|
||||
buf.Write(LeEncodeString(fmt.Sprintf("%v", value)))
|
||||
}
|
||||
}
|
||||
}
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
// 将变量转换为二进制[]byte,并指定固定的[]byte长度返回,长度单位为字节(byte);
|
||||
// 如果转换的二进制长度超过指定长度,那么进行截断处理
|
||||
func LeEncodeByLength(length int, values ...interface{}) []byte {
|
||||
b := LeEncode(values...)
|
||||
if len(b) < length {
|
||||
b = append(b, make([]byte, length-len(b))...)
|
||||
} else if len(b) > length {
|
||||
b = b[0:length]
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// 整形二进制解包,注意第二个及其后参数为字长确定的整形变量的指针地址,以便确定解析的[]byte长度,
|
||||
// 例如:int8/16/32/64、uint8/16/32/64、float32/64等等
|
||||
func LeDecode(b []byte, values ...interface{}) error {
|
||||
buf := bytes.NewBuffer(b)
|
||||
for i := 0; i < len(values); i++ {
|
||||
err := binary.Read(buf, binary.LittleEndian, values[i])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func LeEncodeString(s string) []byte {
|
||||
return []byte(s)
|
||||
}
|
||||
|
||||
func LeDecodeToString(b []byte) string {
|
||||
return string(b)
|
||||
}
|
||||
|
||||
func LeEncodeBool(b bool) []byte {
|
||||
if b == true {
|
||||
return []byte{1}
|
||||
} else {
|
||||
return []byte{0}
|
||||
}
|
||||
}
|
||||
|
||||
// 自动识别int类型长度,转换为[]byte
|
||||
func LeEncodeInt(i int) []byte {
|
||||
if i <= math.MaxInt8 {
|
||||
return EncodeInt8(int8(i))
|
||||
} else if i <= math.MaxInt16 {
|
||||
return EncodeInt16(int16(i))
|
||||
} else if i <= math.MaxInt32 {
|
||||
return EncodeInt32(int32(i))
|
||||
} else {
|
||||
return EncodeInt64(int64(i))
|
||||
}
|
||||
}
|
||||
|
||||
// 自动识别uint类型长度,转换为[]byte
|
||||
func LeEncodeUint(i uint) []byte {
|
||||
if i <= math.MaxUint8 {
|
||||
return EncodeUint8(uint8(i))
|
||||
} else if i <= math.MaxUint16 {
|
||||
return EncodeUint16(uint16(i))
|
||||
} else if i <= math.MaxUint32 {
|
||||
return EncodeUint32(uint32(i))
|
||||
} else {
|
||||
return EncodeUint64(uint64(i))
|
||||
}
|
||||
}
|
||||
|
||||
func LeEncodeInt8(i int8) []byte {
|
||||
return []byte{byte(i)}
|
||||
}
|
||||
|
||||
func LeEncodeUint8(i uint8) []byte {
|
||||
return []byte{byte(i)}
|
||||
}
|
||||
|
||||
func LeEncodeInt16(i int16) []byte {
|
||||
b := make([]byte, 2)
|
||||
binary.LittleEndian.PutUint16(b, uint16(i))
|
||||
return b
|
||||
}
|
||||
|
||||
func LeEncodeUint16(i uint16) []byte {
|
||||
b := make([]byte, 2)
|
||||
binary.LittleEndian.PutUint16(b, i)
|
||||
return b
|
||||
}
|
||||
|
||||
func LeEncodeInt32(i int32) []byte {
|
||||
b := make([]byte, 4)
|
||||
binary.LittleEndian.PutUint32(b, uint32(i))
|
||||
return b
|
||||
}
|
||||
|
||||
func LeEncodeUint32(i uint32) []byte {
|
||||
b := make([]byte, 4)
|
||||
binary.LittleEndian.PutUint32(b, i)
|
||||
return b
|
||||
}
|
||||
|
||||
func LeEncodeInt64(i int64) []byte {
|
||||
b := make([]byte, 8)
|
||||
binary.LittleEndian.PutUint64(b, uint64(i))
|
||||
return b
|
||||
}
|
||||
|
||||
func LeEncodeUint64(i uint64) []byte {
|
||||
b := make([]byte, 8)
|
||||
binary.LittleEndian.PutUint64(b, i)
|
||||
return b
|
||||
}
|
||||
|
||||
func LeEncodeFloat32(f float32) []byte {
|
||||
bits := math.Float32bits(f)
|
||||
b := make([]byte, 4)
|
||||
binary.LittleEndian.PutUint32(b, bits)
|
||||
return b
|
||||
}
|
||||
|
||||
func LeEncodeFloat64(f float64) []byte {
|
||||
bits := math.Float64bits(f)
|
||||
b := make([]byte, 8)
|
||||
binary.LittleEndian.PutUint64(b, bits)
|
||||
return b
|
||||
}
|
||||
|
||||
// 将二进制解析为int类型,根据[]byte的长度进行自动转换.
|
||||
// 注意内部使用的是uint*,使用int会造成位丢失。
|
||||
func LeDecodeToInt(b []byte) int {
|
||||
if len(b) < 2 {
|
||||
return int(LeDecodeToUint8(b))
|
||||
} else if len(b) < 3 {
|
||||
return int(LeDecodeToUint16(b))
|
||||
} else if len(b) < 5 {
|
||||
return int(LeDecodeToUint32(b))
|
||||
} else {
|
||||
return int(LeDecodeToUint64(b))
|
||||
}
|
||||
}
|
||||
|
||||
// 将二进制解析为uint类型,根据[]byte的长度进行自动转换
|
||||
func LeDecodeToUint(b []byte) uint {
|
||||
if len(b) < 2 {
|
||||
return uint(LeDecodeToUint8(b))
|
||||
} else if len(b) < 3 {
|
||||
return uint(LeDecodeToUint16(b))
|
||||
} else if len(b) < 5 {
|
||||
return uint(LeDecodeToUint32(b))
|
||||
} else {
|
||||
return uint(LeDecodeToUint64(b))
|
||||
}
|
||||
}
|
||||
|
||||
// 将二进制解析为bool类型,识别标准是判断二进制中数值是否都为0,或者为空。
|
||||
func LeDecodeToBool(b []byte) bool {
|
||||
if len(b) == 0 {
|
||||
return false
|
||||
}
|
||||
if bytes.Compare(b, make([]byte, len(b))) == 0 {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func LeDecodeToInt8(b []byte) int8 {
|
||||
return int8(b[0])
|
||||
}
|
||||
|
||||
func LeDecodeToUint8(b []byte) uint8 {
|
||||
return uint8(b[0])
|
||||
}
|
||||
|
||||
func LeDecodeToInt16(b []byte) int16 {
|
||||
return int16(binary.LittleEndian.Uint16(LeFillUpSize(b, 2)))
|
||||
}
|
||||
|
||||
func LeDecodeToUint16(b []byte) uint16 {
|
||||
return binary.LittleEndian.Uint16(LeFillUpSize(b, 2))
|
||||
}
|
||||
|
||||
func LeDecodeToInt32(b []byte) int32 {
|
||||
return int32(binary.LittleEndian.Uint32(LeFillUpSize(b, 4)))
|
||||
}
|
||||
|
||||
func LeDecodeToUint32(b []byte) uint32 {
|
||||
return binary.LittleEndian.Uint32(LeFillUpSize(b, 4))
|
||||
}
|
||||
|
||||
func LeDecodeToInt64(b []byte) int64 {
|
||||
return int64(binary.LittleEndian.Uint64(LeFillUpSize(b, 8)))
|
||||
}
|
||||
|
||||
func LeDecodeToUint64(b []byte) uint64 {
|
||||
return binary.LittleEndian.Uint64(LeFillUpSize(b, 8))
|
||||
}
|
||||
|
||||
func LeDecodeToFloat32(b []byte) float32 {
|
||||
return math.Float32frombits(binary.LittleEndian.Uint32(LeFillUpSize(b, 4)))
|
||||
}
|
||||
|
||||
func LeDecodeToFloat64(b []byte) float64 {
|
||||
return math.Float64frombits(binary.LittleEndian.Uint64(LeFillUpSize(b, 8)))
|
||||
}
|
||||
|
||||
// 当b位数不够时,进行高位补0。
|
||||
// 注意这里为了不影响原有输入参数,是采用的值复制设计。
|
||||
func LeFillUpSize(b []byte, l int) []byte {
|
||||
if len(b) >= l {
|
||||
return b[:l]
|
||||
}
|
||||
c := make([]byte, l)
|
||||
copy(c, b)
|
||||
return c
|
||||
}
|
||||
84
g/encoding/gbinary/gbinary_z_be_test.go
Normal file
84
g/encoding/gbinary/gbinary_z_be_test.go
Normal file
@ -0,0 +1,84 @@
|
||||
// Copyright 2017 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,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gbinary_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/gogf/gf/g/encoding/gbinary"
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
)
|
||||
|
||||
func Test_BeEncodeAndBeDecode(t *testing.T) {
|
||||
for k, v := range testData {
|
||||
ve := gbinary.BeEncode(v)
|
||||
ve1 := gbinary.BeEncodeByLength(len(ve), v)
|
||||
|
||||
//t.Logf("%s:%v, encoded:%v\n", k, v, ve)
|
||||
switch v.(type) {
|
||||
case int:
|
||||
gtest.Assert(gbinary.BeDecodeToInt(ve), v)
|
||||
gtest.Assert(gbinary.BeDecodeToInt(ve1), v)
|
||||
case int8:
|
||||
gtest.Assert(gbinary.BeDecodeToInt8(ve), v)
|
||||
gtest.Assert(gbinary.BeDecodeToInt8(ve1), v)
|
||||
case int16:
|
||||
gtest.Assert(gbinary.BeDecodeToInt16(ve), v)
|
||||
gtest.Assert(gbinary.BeDecodeToInt16(ve1), v)
|
||||
case int32:
|
||||
gtest.Assert(gbinary.BeDecodeToInt32(ve), v)
|
||||
gtest.Assert(gbinary.BeDecodeToInt32(ve1), v)
|
||||
case int64:
|
||||
gtest.Assert(gbinary.BeDecodeToInt64(ve), v)
|
||||
gtest.Assert(gbinary.BeDecodeToInt64(ve1), v)
|
||||
case uint:
|
||||
gtest.Assert(gbinary.BeDecodeToUint(ve), v)
|
||||
gtest.Assert(gbinary.BeDecodeToUint(ve1), v)
|
||||
case uint8:
|
||||
gtest.Assert(gbinary.BeDecodeToUint8(ve), v)
|
||||
gtest.Assert(gbinary.BeDecodeToUint8(ve1), v)
|
||||
case uint16:
|
||||
gtest.Assert(gbinary.BeDecodeToUint16(ve1), v)
|
||||
gtest.Assert(gbinary.BeDecodeToUint16(ve), v)
|
||||
case uint32:
|
||||
gtest.Assert(gbinary.BeDecodeToUint32(ve1), v)
|
||||
gtest.Assert(gbinary.BeDecodeToUint32(ve), v)
|
||||
case uint64:
|
||||
gtest.Assert(gbinary.BeDecodeToUint64(ve), v)
|
||||
gtest.Assert(gbinary.BeDecodeToUint64(ve1), v)
|
||||
case bool:
|
||||
gtest.Assert(gbinary.BeDecodeToBool(ve), v)
|
||||
gtest.Assert(gbinary.BeDecodeToBool(ve1), v)
|
||||
case string:
|
||||
gtest.Assert(gbinary.BeDecodeToString(ve), v)
|
||||
gtest.Assert(gbinary.BeDecodeToString(ve1), v)
|
||||
case float32:
|
||||
gtest.Assert(gbinary.BeDecodeToFloat32(ve), v)
|
||||
gtest.Assert(gbinary.BeDecodeToFloat32(ve1), v)
|
||||
case float64:
|
||||
gtest.Assert(gbinary.BeDecodeToFloat64(ve), v)
|
||||
gtest.Assert(gbinary.BeDecodeToFloat64(ve1), v)
|
||||
default:
|
||||
if v == nil {
|
||||
continue
|
||||
}
|
||||
res := make([]byte, len(ve))
|
||||
err := gbinary.BeDecode(ve, res)
|
||||
if err != nil {
|
||||
t.Errorf("test data: %s, %v, error:%v", k, v, err)
|
||||
}
|
||||
gtest.Assert(res, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Test_BeEncodeStruct(t *testing.T) {
|
||||
user := User{"wenzi1", 999, "www.baidu.com"}
|
||||
ve := gbinary.BeEncode(user)
|
||||
s := gbinary.BeDecodeToString(ve)
|
||||
gtest.Assert(string(s), s)
|
||||
}
|
||||
84
g/encoding/gbinary/gbinary_z_le_test.go
Normal file
84
g/encoding/gbinary/gbinary_z_le_test.go
Normal file
@ -0,0 +1,84 @@
|
||||
// Copyright 2017 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,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gbinary_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/gogf/gf/g/encoding/gbinary"
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
)
|
||||
|
||||
func Test_LeEncodeAndLeDecode(t *testing.T) {
|
||||
for k, v := range testData {
|
||||
ve := gbinary.LeEncode(v)
|
||||
ve1 := gbinary.LeEncodeByLength(len(ve), v)
|
||||
|
||||
//t.Logf("%s:%v, encoded:%v\n", k, v, ve)
|
||||
switch v.(type) {
|
||||
case int:
|
||||
gtest.Assert(gbinary.LeDecodeToInt(ve), v)
|
||||
gtest.Assert(gbinary.LeDecodeToInt(ve1), v)
|
||||
case int8:
|
||||
gtest.Assert(gbinary.LeDecodeToInt8(ve), v)
|
||||
gtest.Assert(gbinary.LeDecodeToInt8(ve1), v)
|
||||
case int16:
|
||||
gtest.Assert(gbinary.LeDecodeToInt16(ve), v)
|
||||
gtest.Assert(gbinary.LeDecodeToInt16(ve1), v)
|
||||
case int32:
|
||||
gtest.Assert(gbinary.LeDecodeToInt32(ve), v)
|
||||
gtest.Assert(gbinary.LeDecodeToInt32(ve1), v)
|
||||
case int64:
|
||||
gtest.Assert(gbinary.LeDecodeToInt64(ve), v)
|
||||
gtest.Assert(gbinary.LeDecodeToInt64(ve1), v)
|
||||
case uint:
|
||||
gtest.Assert(gbinary.LeDecodeToUint(ve), v)
|
||||
gtest.Assert(gbinary.LeDecodeToUint(ve1), v)
|
||||
case uint8:
|
||||
gtest.Assert(gbinary.LeDecodeToUint8(ve), v)
|
||||
gtest.Assert(gbinary.LeDecodeToUint8(ve1), v)
|
||||
case uint16:
|
||||
gtest.Assert(gbinary.LeDecodeToUint16(ve1), v)
|
||||
gtest.Assert(gbinary.LeDecodeToUint16(ve), v)
|
||||
case uint32:
|
||||
gtest.Assert(gbinary.LeDecodeToUint32(ve1), v)
|
||||
gtest.Assert(gbinary.LeDecodeToUint32(ve), v)
|
||||
case uint64:
|
||||
gtest.Assert(gbinary.LeDecodeToUint64(ve), v)
|
||||
gtest.Assert(gbinary.LeDecodeToUint64(ve1), v)
|
||||
case bool:
|
||||
gtest.Assert(gbinary.LeDecodeToBool(ve), v)
|
||||
gtest.Assert(gbinary.LeDecodeToBool(ve1), v)
|
||||
case string:
|
||||
gtest.Assert(gbinary.LeDecodeToString(ve), v)
|
||||
gtest.Assert(gbinary.LeDecodeToString(ve1), v)
|
||||
case float32:
|
||||
gtest.Assert(gbinary.LeDecodeToFloat32(ve), v)
|
||||
gtest.Assert(gbinary.LeDecodeToFloat32(ve1), v)
|
||||
case float64:
|
||||
gtest.Assert(gbinary.LeDecodeToFloat64(ve), v)
|
||||
gtest.Assert(gbinary.LeDecodeToFloat64(ve1), v)
|
||||
default:
|
||||
if v == nil {
|
||||
continue
|
||||
}
|
||||
res := make([]byte, len(ve))
|
||||
err := gbinary.LeDecode(ve, res)
|
||||
if err != nil {
|
||||
t.Errorf("test data: %s, %v, error:%v", k, v, err)
|
||||
}
|
||||
gtest.Assert(res, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Test_LeEncodeStruct(t *testing.T) {
|
||||
user := User{"wenzi1", 999, "www.baidu.com"}
|
||||
ve := gbinary.LeEncode(user)
|
||||
s := gbinary.LeDecodeToString(ve)
|
||||
gtest.Assert(string(s), s)
|
||||
}
|
||||
@ -7,12 +7,19 @@
|
||||
package gbinary_test
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g/encoding/gbinary"
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
"math"
|
||||
"testing"
|
||||
|
||||
"github.com/gogf/gf/g/encoding/gbinary"
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
)
|
||||
|
||||
type User struct {
|
||||
Name string
|
||||
Age int
|
||||
Url string
|
||||
}
|
||||
|
||||
var testData = map[string]interface{}{
|
||||
//"nil": nil,
|
||||
"int": int(123),
|
||||
@ -39,7 +46,9 @@ var testData = map[string]interface{}{
|
||||
"float64": float64(123.456),
|
||||
}
|
||||
|
||||
func TestEncodeAndDecode(t *testing.T) {
|
||||
var testBitData = []int{0, 99, 122, 129, 222, 999, 22322}
|
||||
|
||||
func Test_EncodeAndDecode(t *testing.T) {
|
||||
for k, v := range testData {
|
||||
ve := gbinary.Encode(v)
|
||||
ve1 := gbinary.EncodeByLength(len(ve), v)
|
||||
@ -102,22 +111,14 @@ func TestEncodeAndDecode(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
type User struct {
|
||||
Name string
|
||||
Age int
|
||||
Url string
|
||||
}
|
||||
|
||||
func TestEncodeStruct(t *testing.T) {
|
||||
func Test_EncodeStruct(t *testing.T) {
|
||||
user := User{"wenzi1", 999, "www.baidu.com"}
|
||||
ve := gbinary.Encode(user)
|
||||
s := gbinary.DecodeToString(ve)
|
||||
gtest.Assert(string(s), s)
|
||||
}
|
||||
|
||||
var testBitData = []int{0, 99, 122, 129, 222, 999, 22322}
|
||||
|
||||
func TestBits(t *testing.T) {
|
||||
func Test_Bits(t *testing.T) {
|
||||
for i := range testBitData {
|
||||
bits := make([]gbinary.Bit, 0)
|
||||
res := gbinary.EncodeBits(bits, testBitData[i], 64)
|
||||
@ -27,9 +27,7 @@ type Json struct {
|
||||
mu *rwmutex.RWMutex
|
||||
p *interface{} // Pointer for hierarchical data access, it's the root of data in default.
|
||||
c byte // Char separator('.' in default).
|
||||
// Violence Check(false in default), which is used to access data
|
||||
// when the hierarchical data key contains separator char.
|
||||
vc bool
|
||||
vc bool // Violence Check(false in default), which is used to access data when the hierarchical data key contains separator char.
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
@ -37,8 +35,8 @@ func (j *Json) MarshalJSON() ([]byte, error) {
|
||||
return j.ToJson()
|
||||
}
|
||||
|
||||
// Set <value> by <pattern>.
|
||||
// Notice:
|
||||
// setValue sets <value> to <j> by <pattern>.
|
||||
// Note:
|
||||
// 1. If value is nil and removed is true, means deleting this value;
|
||||
// 2. It's quite complicated in hierarchical data search, node creating and data assignment;
|
||||
func (j *Json) setValue(pattern string, value interface{}, removed bool) error {
|
||||
@ -53,8 +51,8 @@ func (j *Json) setValue(pattern string, value interface{}, removed bool) error {
|
||||
*j.p = make(map[string]interface{})
|
||||
}
|
||||
}
|
||||
var pparent *interface{} = nil // 父级元素项(设置时需要根据子级的内容确定数据类型,所以必须记录父级)
|
||||
var pointer *interface{} = j.p // 当前操作层级项
|
||||
var pparent *interface{} = nil // Parent pointer.
|
||||
var pointer *interface{} = j.p // Current pointer.
|
||||
j.mu.Lock()
|
||||
defer j.mu.Unlock()
|
||||
for i := 0; i < length; i++ {
|
||||
@ -62,26 +60,26 @@ func (j *Json) setValue(pattern string, value interface{}, removed bool) error {
|
||||
case map[string]interface{}:
|
||||
if i == length-1 {
|
||||
if removed && value == nil {
|
||||
// 删除map元素
|
||||
// Delete item from map.
|
||||
delete((*pointer).(map[string]interface{}), array[i])
|
||||
} else {
|
||||
(*pointer).(map[string]interface{})[array[i]] = value
|
||||
}
|
||||
} else {
|
||||
// 当键名不存在的情况这里会进行处理
|
||||
// If the key does not exit in the map.
|
||||
if v, ok := (*pointer).(map[string]interface{})[array[i]]; !ok {
|
||||
if removed && value == nil {
|
||||
goto done
|
||||
}
|
||||
// 创建新节点
|
||||
// Creating new node.
|
||||
if gstr.IsNumeric(array[i+1]) {
|
||||
// 创建array节点
|
||||
// Creating array node.
|
||||
n, _ := strconv.Atoi(array[i+1])
|
||||
var v interface{} = make([]interface{}, n+1)
|
||||
pparent = j.setPointerWithValue(pointer, array[i], v)
|
||||
pointer = &v
|
||||
} else {
|
||||
// 创建map节点
|
||||
// Creating map node.
|
||||
var v interface{} = make(map[string]interface{})
|
||||
pparent = j.setPointerWithValue(pointer, array[i], v)
|
||||
pointer = &v
|
||||
@ -93,7 +91,6 @@ func (j *Json) setValue(pattern string, value interface{}, removed bool) error {
|
||||
}
|
||||
|
||||
case []interface{}:
|
||||
// 键名与当前指针类型不符合,需要执行**覆盖操作**
|
||||
if !gstr.IsNumeric(array[i]) {
|
||||
if i == length-1 {
|
||||
*pointer = map[string]interface{}{array[i]: value}
|
||||
@ -110,11 +107,11 @@ func (j *Json) setValue(pattern string, value interface{}, removed bool) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// 叶子节点
|
||||
// Leaf node.
|
||||
if i == length-1 {
|
||||
if len((*pointer).([]interface{})) > valn {
|
||||
if removed && value == nil {
|
||||
// 删除数据元素
|
||||
// Deleting element.
|
||||
if pparent == nil {
|
||||
*pointer = append((*pointer).([]interface{})[:valn], (*pointer).([]interface{})[valn+1:]...)
|
||||
} else {
|
||||
@ -128,10 +125,10 @@ func (j *Json) setValue(pattern string, value interface{}, removed bool) error {
|
||||
goto done
|
||||
}
|
||||
if pparent == nil {
|
||||
// 表示根节点
|
||||
// It is the root node.
|
||||
j.setPointerWithValue(pointer, array[i], value)
|
||||
} else {
|
||||
// 非根节点
|
||||
// It is not the root node.
|
||||
s := make([]interface{}, valn+1)
|
||||
copy(s, (*pointer).([]interface{}))
|
||||
s[valn] = value
|
||||
@ -160,8 +157,8 @@ func (j *Json) setValue(pattern string, value interface{}, removed bool) error {
|
||||
}
|
||||
}
|
||||
|
||||
// 如果当前指针指向的变量不是引用类型的,
|
||||
// 那么修改变量必须通过父级进行修改,即 pparent
|
||||
// If the variable pointed to by the <pointer> is not of a reference type,
|
||||
// then it modifies the variable via its the parent, ie: pparent.
|
||||
default:
|
||||
if removed && value == nil {
|
||||
goto done
|
||||
@ -199,7 +196,7 @@ done:
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert <value> to map[string]interface{} or []interface{},
|
||||
// convertValue converts <value> to map[string]interface{} or []interface{},
|
||||
// which can be supported for hierarchical data access.
|
||||
func (j *Json) convertValue(value interface{}) interface{} {
|
||||
switch value.(type) {
|
||||
@ -232,7 +229,7 @@ func (j *Json) convertValue(value interface{}) interface{} {
|
||||
}
|
||||
}
|
||||
|
||||
// Set <key>:<value> to <pointer>, the <key> may be a map key or slice index.
|
||||
// setPointerWithValue sets <key>:<value> to <pointer>, the <key> may be a map key or slice index.
|
||||
// It returns the pointer to the new value set.
|
||||
func (j *Json) setPointerWithValue(pointer *interface{}, key string, value interface{}) *interface{} {
|
||||
switch (*pointer).(type) {
|
||||
@ -257,7 +254,7 @@ func (j *Json) setPointerWithValue(pointer *interface{}, key string, value inter
|
||||
return pointer
|
||||
}
|
||||
|
||||
// Get a pointer to the value by specified <pattern>.
|
||||
// getPointerByPattern returns a pointer to the value by specified <pattern>.
|
||||
func (j *Json) getPointerByPattern(pattern string) *interface{} {
|
||||
if j.vc {
|
||||
return j.getPointerByPatternWithViolenceCheck(pattern)
|
||||
@ -266,7 +263,7 @@ func (j *Json) getPointerByPattern(pattern string) *interface{} {
|
||||
}
|
||||
}
|
||||
|
||||
// Get a pointer to the value of specified <pattern> with violence check.
|
||||
// getPointerByPatternWithViolenceCheck returns a pointer to the value of specified <pattern> with violence check.
|
||||
func (j *Json) getPointerByPatternWithViolenceCheck(pattern string) *interface{} {
|
||||
if !j.vc {
|
||||
return j.getPointerByPatternWithoutViolenceCheck(pattern)
|
||||
@ -305,7 +302,7 @@ func (j *Json) getPointerByPatternWithViolenceCheck(pattern string) *interface{}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get a pointer to the value of specified <pattern>, with no violence check.
|
||||
// getPointerByPatternWithoutViolenceCheck returns a pointer to the value of specified <pattern>, with no violence check.
|
||||
func (j *Json) getPointerByPatternWithoutViolenceCheck(pattern string) *interface{} {
|
||||
if j.vc {
|
||||
return j.getPointerByPatternWithViolenceCheck(pattern)
|
||||
@ -329,7 +326,7 @@ func (j *Json) getPointerByPatternWithoutViolenceCheck(pattern string) *interfac
|
||||
return nil
|
||||
}
|
||||
|
||||
// Check whether there's value by <key> in specified <pointer>.
|
||||
// checkPatternByPointer checks whether there's value by <key> in specified <pointer>.
|
||||
// It returns a pointer to the value.
|
||||
func (j *Json) checkPatternByPointer(key string, pointer *interface{}) *interface{} {
|
||||
switch (*pointer).(type) {
|
||||
|
||||
@ -8,10 +8,13 @@ package gjson
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/g/util/gutil"
|
||||
|
||||
"github.com/gogf/gf/g/container/gvar"
|
||||
"github.com/gogf/gf/g/os/gtime"
|
||||
"github.com/gogf/gf/g/util/gconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Val returns the json value.
|
||||
@ -21,6 +24,13 @@ func (j *Json) Value() interface{} {
|
||||
return *(j.p)
|
||||
}
|
||||
|
||||
// IsNil checks whether the value pointed by <j> is nil.
|
||||
func (j *Json) IsNil() bool {
|
||||
j.mu.RLock()
|
||||
defer j.mu.RUnlock()
|
||||
return j.p == nil || *(j.p) == nil
|
||||
}
|
||||
|
||||
// Get returns value by specified <pattern>.
|
||||
// It returns all values of current Json object, if <pattern> is empty or not specified.
|
||||
// It returns nil if no value found by <pattern>.
|
||||
@ -66,11 +76,7 @@ func (j *Json) GetMap(pattern string, def ...interface{}) map[string]interface{}
|
||||
// GetJson gets the value by specified <pattern>,
|
||||
// and converts it to a un-concurrent-safe Json object.
|
||||
func (j *Json) GetJson(pattern string, def ...interface{}) *Json {
|
||||
result := j.Get(pattern, def...)
|
||||
if result != nil {
|
||||
return New(result, true)
|
||||
}
|
||||
return nil
|
||||
return New(j.Get(pattern, def...), true)
|
||||
}
|
||||
|
||||
// GetJsons gets the value by specified <pattern>,
|
||||
@ -113,6 +119,10 @@ func (j *Json) GetString(pattern string, def ...interface{}) string {
|
||||
return gconv.String(j.Get(pattern, def...))
|
||||
}
|
||||
|
||||
func (j *Json) GetBytes(pattern string, def ...interface{}) []byte {
|
||||
return gconv.Bytes(j.Get(pattern, def...))
|
||||
}
|
||||
|
||||
// GetBool gets the value by specified <pattern>,
|
||||
// and converts it to bool.
|
||||
// It returns false when value is: "", 0, false, off, nil;
|
||||
@ -252,6 +262,7 @@ func (j *Json) Append(pattern string, value interface{}) error {
|
||||
// GetToVar gets the value by specified <pattern>,
|
||||
// and converts it to specified golang variable <v>.
|
||||
// The <pointer> should be a pointer type.
|
||||
// Deprecated.
|
||||
func (j *Json) GetToVar(pattern string, pointer interface{}) error {
|
||||
r := j.Get(pattern)
|
||||
if r != nil {
|
||||
@ -266,11 +277,32 @@ func (j *Json) GetToVar(pattern string, pointer interface{}) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetToStruct gets the value by specified <pattern>,
|
||||
// and converts it to specified object <objPointer>.
|
||||
// The <objPointer> should be the pointer to an object.
|
||||
func (j *Json) GetToStruct(pattern string, pointer interface{}) error {
|
||||
return gconv.Struct(j.Get(pattern), pointer)
|
||||
// GetStruct gets the value by specified <pattern>,
|
||||
// and converts it to specified object <pointer>.
|
||||
// The <pointer> should be the pointer to an object.
|
||||
func (j *Json) GetStruct(pattern string, pointer interface{}, mapping ...map[string]string) error {
|
||||
return gconv.Struct(j.Get(pattern), pointer, mapping...)
|
||||
}
|
||||
|
||||
// GetStructDeep does GetStruct recursively.
|
||||
func (j *Json) GetStructDeep(pattern string, pointer interface{}, mapping ...map[string]string) error {
|
||||
return gconv.StructDeep(j.Get(pattern), pointer, mapping...)
|
||||
}
|
||||
|
||||
// GetStructs converts any slice to given struct slice.
|
||||
func (j *Json) GetStructs(pattern string, pointer interface{}, mapping ...map[string]string) error {
|
||||
return gconv.Structs(j.Get(pattern), pointer, mapping...)
|
||||
}
|
||||
|
||||
// GetStructsDeep converts any slice to given struct slice recursively.
|
||||
func (j *Json) GetStructsDeep(pattern string, pointer interface{}, mapping ...map[string]string) error {
|
||||
return gconv.StructsDeep(j.Get(pattern), pointer, mapping...)
|
||||
}
|
||||
|
||||
// GetToStruct is alias of GetStruct.
|
||||
// Deprecated.
|
||||
func (j *Json) GetToStruct(pattern string, pointer interface{}, mapping ...map[string]string) error {
|
||||
return j.GetStruct(pattern, pointer, mapping...)
|
||||
}
|
||||
|
||||
// ToMap converts current Json object to map[string]interface{}.
|
||||
@ -290,21 +322,41 @@ func (j *Json) ToArray() []interface{} {
|
||||
}
|
||||
|
||||
// ToStruct converts current Json object to specified object.
|
||||
// The <objPointer> should be a pointer type.
|
||||
// The <pointer> should be a pointer type.
|
||||
func (j *Json) ToStruct(pointer interface{}) error {
|
||||
j.mu.RLock()
|
||||
defer j.mu.RUnlock()
|
||||
return gconv.Struct(*(j.p), pointer)
|
||||
}
|
||||
|
||||
// Dump prints current Json object with more manually readable.
|
||||
func (j *Json) Dump() error {
|
||||
func (j *Json) ToStructDeep(pointer interface{}) error {
|
||||
j.mu.RLock()
|
||||
defer j.mu.RUnlock()
|
||||
if b, err := j.ToJsonIndent(); err != nil {
|
||||
return err
|
||||
} else {
|
||||
fmt.Println(string(b))
|
||||
}
|
||||
return nil
|
||||
return gconv.StructDeep(*(j.p), pointer)
|
||||
}
|
||||
|
||||
func (j *Json) ToStructs(pointer interface{}) error {
|
||||
j.mu.RLock()
|
||||
defer j.mu.RUnlock()
|
||||
return gconv.Structs(*(j.p), pointer)
|
||||
}
|
||||
|
||||
func (j *Json) ToStructsDeep(pointer interface{}) error {
|
||||
j.mu.RLock()
|
||||
defer j.mu.RUnlock()
|
||||
return gconv.StructsDeep(*(j.p), pointer)
|
||||
}
|
||||
|
||||
// Dump prints current Json object with more manually readable.
|
||||
func (j *Json) Dump() {
|
||||
j.mu.RLock()
|
||||
defer j.mu.RUnlock()
|
||||
gutil.Dump(*j.p)
|
||||
}
|
||||
|
||||
// Export returns <j> as a string with more manually readable.
|
||||
func (j *Json) Export() string {
|
||||
j.mu.RLock()
|
||||
defer j.mu.RUnlock()
|
||||
return gutil.Export(*j.p)
|
||||
}
|
||||
|
||||
@ -12,6 +12,10 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"github.com/gogf/gf/g/os/gfile"
|
||||
|
||||
"github.com/gogf/gf/g/encoding/gtoml"
|
||||
"github.com/gogf/gf/g/encoding/gxml"
|
||||
"github.com/gogf/gf/g/encoding/gyaml"
|
||||
@ -19,7 +23,6 @@ import (
|
||||
"github.com/gogf/gf/g/os/gfcache"
|
||||
"github.com/gogf/gf/g/text/gregex"
|
||||
"github.com/gogf/gf/g/util/gconv"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// New creates a Json object with any variable type of <data>,
|
||||
@ -128,64 +131,92 @@ func DecodeToJson(data interface{}, unsafe ...bool) (*Json, error) {
|
||||
// Load loads content from specified file <path>,
|
||||
// and creates a Json object from its content.
|
||||
func Load(path string, unsafe ...bool) (*Json, error) {
|
||||
return LoadContent(gfcache.GetBinContents(path), unsafe...)
|
||||
return doLoadContent(gfile.Ext(path), gfcache.GetBinContents(path), unsafe...)
|
||||
}
|
||||
|
||||
// LoadContent creates a Json object from given content,
|
||||
// it checks the data type of <content> automatically,
|
||||
// supporting JSON, XML, YAML and TOML types of data.
|
||||
func LoadContent(data interface{}, unsafe ...bool) (*Json, error) {
|
||||
func LoadJson(data interface{}, unsafe ...bool) (*Json, error) {
|
||||
return doLoadContent("json", gconv.Bytes(data), unsafe...)
|
||||
}
|
||||
|
||||
func LoadXml(data interface{}, unsafe ...bool) (*Json, error) {
|
||||
return doLoadContent("xml", gconv.Bytes(data), unsafe...)
|
||||
}
|
||||
|
||||
func LoadYaml(data interface{}, unsafe ...bool) (*Json, error) {
|
||||
return doLoadContent("yaml", gconv.Bytes(data), unsafe...)
|
||||
}
|
||||
|
||||
func LoadToml(data interface{}, unsafe ...bool) (*Json, error) {
|
||||
return doLoadContent("toml", gconv.Bytes(data), unsafe...)
|
||||
}
|
||||
|
||||
func doLoadContent(dataType string, data []byte, unsafe ...bool) (*Json, error) {
|
||||
var err error
|
||||
var result interface{}
|
||||
b := gconv.Bytes(data)
|
||||
t := ""
|
||||
if len(b) == 0 {
|
||||
if len(data) == 0 {
|
||||
return New(nil, unsafe...), nil
|
||||
}
|
||||
// auto check data type
|
||||
if json.Valid(b) {
|
||||
t = "json"
|
||||
} else if gregex.IsMatch(`^<.+>[\S\s]+<.+>$`, b) {
|
||||
t = "xml"
|
||||
} else if gregex.IsMatch(`^[\s\t]*\w+\s*:\s*.+`, b) || gregex.IsMatch(`\n[\s\t]*\w+\s*:\s*.+`, b) {
|
||||
t = "yml"
|
||||
} else if gregex.IsMatch(`^[\s\t]*\w+\s*=\s*.+`, b) || gregex.IsMatch(`\n[\s\t]*\w+\s*=\s*.+`, b) {
|
||||
t = "toml"
|
||||
} else {
|
||||
return nil, errors.New("unsupported data type")
|
||||
if dataType == "" {
|
||||
dataType = checkDataType(data)
|
||||
}
|
||||
// convert to json type data
|
||||
switch t {
|
||||
switch dataType {
|
||||
case "json", ".json":
|
||||
// ok
|
||||
|
||||
case "xml", ".xml":
|
||||
// TODO UseNumber
|
||||
b, err = gxml.ToJson(b)
|
||||
if data, err = gxml.ToJson(data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
case "yml", "yaml", ".yml", ".yaml":
|
||||
// TODO UseNumber
|
||||
b, err = gyaml.ToJson(b)
|
||||
if data, err = gyaml.ToJson(data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
case "toml", ".toml":
|
||||
// TODO UseNumber
|
||||
b, err = gtoml.ToJson(b)
|
||||
if data, err = gtoml.ToJson(data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
default:
|
||||
err = errors.New("nonsupport type " + t)
|
||||
err = errors.New("unsupported type for loading")
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if result == nil {
|
||||
decoder := json.NewDecoder(bytes.NewReader(b))
|
||||
decoder := json.NewDecoder(bytes.NewReader(data))
|
||||
decoder.UseNumber()
|
||||
if err := decoder.Decode(&result); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch result.(type) {
|
||||
case string, []byte:
|
||||
return nil, fmt.Errorf(`json decoding failed for content: %s`, string(b))
|
||||
return nil, fmt.Errorf(`json decoding failed for content: %s`, string(data))
|
||||
}
|
||||
}
|
||||
return New(result, unsafe...), nil
|
||||
}
|
||||
|
||||
func LoadContent(data interface{}, unsafe ...bool) (*Json, error) {
|
||||
content := gconv.Bytes(data)
|
||||
if len(content) == 0 {
|
||||
return New(nil, unsafe...), nil
|
||||
}
|
||||
return doLoadContent(checkDataType(content), content, unsafe...)
|
||||
|
||||
}
|
||||
|
||||
// checkDataType automatically checks and returns the data type for <content>.
|
||||
func checkDataType(content []byte) string {
|
||||
if json.Valid(content) {
|
||||
return "json"
|
||||
} else if gregex.IsMatch(`^<.+>[\S\s]+<.+>$`, content) {
|
||||
return "xml"
|
||||
} else if gregex.IsMatch(`^[\s\t]*[\w\-]+\s*:\s*.+`, content) || gregex.IsMatch(`\n[\s\t]*[\w\-]+\s*:\s*.+`, content) {
|
||||
return "yml"
|
||||
} else if gregex.IsMatch(`^[\s\t]*[\w\-]+\s*=\s*.+`, content) || gregex.IsMatch(`\n[\s\t]*[\w\-]+\s*=\s*.+`, content) {
|
||||
return "toml"
|
||||
} else {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,10 +7,11 @@
|
||||
package gjson_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/gogf/gf/g"
|
||||
"github.com/gogf/gf/g/encoding/gjson"
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_New(t *testing.T) {
|
||||
@ -347,7 +348,7 @@ func Test_Convert2(t *testing.T) {
|
||||
j := gjson.New(`{"name":"gf","time":"2019-06-12"}`)
|
||||
gtest.Assert(j.Value().(g.Map)["name"], "gf")
|
||||
gtest.Assert(j.GetMap("name1"), nil)
|
||||
gtest.Assert(j.GetJson("name1"), nil)
|
||||
gtest.AssertNE(j.GetJson("name1"), nil)
|
||||
gtest.Assert(j.GetJsons("name1"), nil)
|
||||
gtest.Assert(j.GetJsonMap("name1"), nil)
|
||||
gtest.Assert(j.Contains("name1"), false)
|
||||
@ -361,7 +362,7 @@ func Test_Convert2(t *testing.T) {
|
||||
err := j.ToStruct(&name)
|
||||
gtest.Assert(err, nil)
|
||||
gtest.Assert(name.Name, "gf")
|
||||
err = j.Dump()
|
||||
j.Dump()
|
||||
gtest.Assert(err, nil)
|
||||
|
||||
j = gjson.New(`{"person":{"name":"gf"}}`)
|
||||
@ -370,7 +371,7 @@ func Test_Convert2(t *testing.T) {
|
||||
gtest.Assert(name.Name, "gf")
|
||||
|
||||
j = gjson.New(`{"name":"gf""}`)
|
||||
err = j.Dump()
|
||||
j.Dump()
|
||||
gtest.Assert(err, nil)
|
||||
|
||||
j = gjson.New(`[1,2,3]`)
|
||||
@ -459,3 +460,10 @@ func Test_Basic(t *testing.T) {
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
func Test_IsNil(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
j := gjson.New(nil)
|
||||
gtest.Assert(j.IsNil(), true)
|
||||
})
|
||||
}
|
||||
|
||||
@ -7,11 +7,12 @@
|
||||
package gjson_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/gogf/gf/g"
|
||||
"github.com/gogf/gf/g/encoding/gjson"
|
||||
"github.com/gogf/gf/g/os/gfile"
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_Load_JSON(t *testing.T) {
|
||||
@ -50,7 +51,7 @@ func Test_Load_XML(t *testing.T) {
|
||||
gtest.Assert(j.Get("doc.n"), "123456789")
|
||||
gtest.Assert(j.Get("doc.m"), g.Map{"k": "v"})
|
||||
gtest.Assert(j.Get("doc.m.k"), "v")
|
||||
gtest.Assert(j.Get("doc.a"), g.Slice{1, 2, 3})
|
||||
gtest.Assert(j.Get("doc.a"), g.Slice{"1", "2", "3"})
|
||||
gtest.Assert(j.Get("doc.a.1"), 2)
|
||||
})
|
||||
// XML
|
||||
@ -63,7 +64,7 @@ func Test_Load_XML(t *testing.T) {
|
||||
gtest.Assert(j.Get("doc.n"), "123456789")
|
||||
gtest.Assert(j.Get("doc.m"), g.Map{"k": "v"})
|
||||
gtest.Assert(j.Get("doc.m.k"), "v")
|
||||
gtest.Assert(j.Get("doc.a"), g.Slice{1, 2, 3})
|
||||
gtest.Assert(j.Get("doc.a"), g.Slice{"1", "2", "3"})
|
||||
gtest.Assert(j.Get("doc.a.1"), 2)
|
||||
})
|
||||
|
||||
@ -133,7 +134,7 @@ func Test_Load_YAML2(t *testing.T) {
|
||||
func Test_Load_TOML1(t *testing.T) {
|
||||
data := []byte(`
|
||||
a = ["1", "2", "3"]
|
||||
n = "123456789"
|
||||
n = 123456789
|
||||
|
||||
[m]
|
||||
k = "v"
|
||||
@ -145,7 +146,7 @@ n = "123456789"
|
||||
gtest.Assert(j.Get("n"), "123456789")
|
||||
gtest.Assert(j.Get("m"), g.Map{"k": "v"})
|
||||
gtest.Assert(j.Get("m.k"), "v")
|
||||
gtest.Assert(j.Get("a"), g.Slice{1, 2, 3})
|
||||
gtest.Assert(j.Get("a"), g.Slice{"1", "2", "3"})
|
||||
gtest.Assert(j.Get("a.1"), 2)
|
||||
})
|
||||
// TOML
|
||||
@ -158,7 +159,7 @@ n = "123456789"
|
||||
gtest.Assert(j.Get("n"), "123456789")
|
||||
gtest.Assert(j.Get("m"), g.Map{"k": "v"})
|
||||
gtest.Assert(j.Get("m.k"), "v")
|
||||
gtest.Assert(j.Get("a"), g.Slice{1, 2, 3})
|
||||
gtest.Assert(j.Get("a"), g.Slice{"1", "2", "3"})
|
||||
gtest.Assert(j.Get("a.1"), 2)
|
||||
})
|
||||
}
|
||||
|
||||
@ -7,9 +7,10 @@
|
||||
package gparser
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/g/container/gvar"
|
||||
"github.com/gogf/gf/g/os/gtime"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Val returns the value.
|
||||
@ -141,15 +142,37 @@ func (p *Parser) GetGTime(pattern string, format ...string) *gtime.Time {
|
||||
// GetToVar gets the value by specified <pattern>,
|
||||
// and converts it to specified golang variable <v>.
|
||||
// The <v> should be a pointer type.
|
||||
// Deprecated.
|
||||
func (p *Parser) GetToVar(pattern string, pointer interface{}) error {
|
||||
return p.json.GetToVar(pattern, pointer)
|
||||
}
|
||||
|
||||
// GetToStruct gets the value by specified <pattern>,
|
||||
// GetStruct gets the value by specified <pattern>,
|
||||
// and converts it to specified object <pointer>.
|
||||
// The <pointer> should be the pointer to a struct.
|
||||
func (p *Parser) GetToStruct(pattern string, pointer interface{}) error {
|
||||
return p.json.GetToStruct(pattern, pointer)
|
||||
// The <pointer> should be the pointer to an object.
|
||||
func (p *Parser) GetStruct(pattern string, pointer interface{}, mapping ...map[string]string) error {
|
||||
return p.json.GetStruct(pattern, pointer, mapping...)
|
||||
}
|
||||
|
||||
// GetStructDeep does GetStruct recursively.
|
||||
func (p *Parser) GetStructDeep(pattern string, pointer interface{}, mapping ...map[string]string) error {
|
||||
return p.json.GetStructDeep(pattern, pointer, mapping...)
|
||||
}
|
||||
|
||||
// GetStructs converts any slice to given struct slice.
|
||||
func (p *Parser) GetStructs(pattern string, pointer interface{}, mapping ...map[string]string) error {
|
||||
return p.json.GetStructs(pattern, pointer, mapping...)
|
||||
}
|
||||
|
||||
// GetStructsDeep converts any slice to given struct slice recursively.
|
||||
func (p *Parser) GetStructsDeep(pattern string, pointer interface{}, mapping ...map[string]string) error {
|
||||
return p.json.GetStructsDeep(pattern, pointer, mapping...)
|
||||
}
|
||||
|
||||
// GetToStruct is alias of GetStruct.
|
||||
// Deprecated.
|
||||
func (p *Parser) GetToStruct(pattern string, pointer interface{}, mapping ...map[string]string) error {
|
||||
return p.json.GetStruct(pattern, pointer, mapping...)
|
||||
}
|
||||
|
||||
// Set sets value with specified <pattern>.
|
||||
@ -195,7 +218,23 @@ func (p *Parser) ToStruct(pointer interface{}) error {
|
||||
return p.json.ToStruct(pointer)
|
||||
}
|
||||
|
||||
// Dump prints current Json object with more manually readable.
|
||||
func (p *Parser) Dump() error {
|
||||
return p.json.Dump()
|
||||
func (p *Parser) ToStructDeep(pointer interface{}) error {
|
||||
return p.json.ToStructDeep(pointer)
|
||||
}
|
||||
|
||||
func (p *Parser) ToStructs(pointer interface{}) error {
|
||||
return p.json.ToStructs(pointer)
|
||||
}
|
||||
|
||||
func (p *Parser) ToStructsDeep(pointer interface{}) error {
|
||||
return p.json.ToStructsDeep(pointer)
|
||||
}
|
||||
|
||||
// Dump prints current Json object with more manually readable.
|
||||
func (p *Parser) Dump() {
|
||||
p.json.Dump()
|
||||
}
|
||||
|
||||
func (p *Parser) Export() string {
|
||||
return p.json.Export()
|
||||
}
|
||||
|
||||
@ -7,12 +7,13 @@
|
||||
package gparser_test
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
|
||||
"github.com/gogf/gf/g"
|
||||
"github.com/gogf/gf/g/encoding/gparser"
|
||||
"github.com/gogf/gf/g/os/gfile"
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_Load_JSON(t *testing.T) {
|
||||
@ -51,7 +52,7 @@ func Test_Load_XML(t *testing.T) {
|
||||
gtest.Assert(j.Get("doc.n"), "123456789")
|
||||
gtest.Assert(j.Get("doc.m"), g.Map{"k": "v"})
|
||||
gtest.Assert(j.Get("doc.m.k"), "v")
|
||||
gtest.Assert(j.Get("doc.a"), g.Slice{1, 2, 3})
|
||||
gtest.Assert(j.Get("doc.a"), g.Slice{"1", "2", "3"})
|
||||
gtest.Assert(j.Get("doc.a.1"), 2)
|
||||
})
|
||||
// XML
|
||||
@ -64,7 +65,7 @@ func Test_Load_XML(t *testing.T) {
|
||||
gtest.Assert(j.Get("doc.n"), "123456789")
|
||||
gtest.Assert(j.Get("doc.m"), g.Map{"k": "v"})
|
||||
gtest.Assert(j.Get("doc.m.k"), "v")
|
||||
gtest.Assert(j.Get("doc.a"), g.Slice{1, 2, 3})
|
||||
gtest.Assert(j.Get("doc.a"), g.Slice{"1", "2", "3"})
|
||||
gtest.Assert(j.Get("doc.a.1"), 2)
|
||||
})
|
||||
|
||||
@ -146,7 +147,7 @@ n = "123456789"
|
||||
gtest.Assert(j.Get("n"), "123456789")
|
||||
gtest.Assert(j.Get("m"), g.Map{"k": "v"})
|
||||
gtest.Assert(j.Get("m.k"), "v")
|
||||
gtest.Assert(j.Get("a"), g.Slice{1, 2, 3})
|
||||
gtest.Assert(j.Get("a"), g.Slice{"1", "2", "3"})
|
||||
gtest.Assert(j.Get("a.1"), 2)
|
||||
})
|
||||
// TOML
|
||||
@ -159,7 +160,7 @@ n = "123456789"
|
||||
gtest.Assert(j.Get("n"), "123456789")
|
||||
gtest.Assert(j.Get("m"), g.Map{"k": "v"})
|
||||
gtest.Assert(j.Get("m.k"), "v")
|
||||
gtest.Assert(j.Get("a"), g.Slice{1, 2, 3})
|
||||
gtest.Assert(j.Get("a"), g.Slice{"1", "2", "3"})
|
||||
gtest.Assert(j.Get("a.1"), 2)
|
||||
})
|
||||
}
|
||||
|
||||
90
g/errors/gerror/gerror.go
Normal file
90
g/errors/gerror/gerror.go
Normal file
@ -0,0 +1,90 @@
|
||||
// Copyright 2019 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,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
// Package errors provides simple functions to manipulate errors.
|
||||
package gerror
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/gogf/gf/g/util/gconv"
|
||||
)
|
||||
|
||||
type ApiStack interface {
|
||||
Stack() string
|
||||
}
|
||||
|
||||
type ApiCause interface {
|
||||
Cause() error
|
||||
}
|
||||
|
||||
// New returns an error that formats as the given value.
|
||||
func New(value interface{}) error {
|
||||
if value == nil {
|
||||
return nil
|
||||
}
|
||||
return NewText(gconv.String(value))
|
||||
}
|
||||
|
||||
// NewText returns an error that formats as the given text.
|
||||
func NewText(text string) error {
|
||||
if text == "" {
|
||||
return nil
|
||||
}
|
||||
return &Error{
|
||||
stack: callers(),
|
||||
text: text,
|
||||
}
|
||||
}
|
||||
|
||||
// Wrap wraps error with text.
|
||||
// It returns nil if given err is nil.
|
||||
func Wrap(err error, text string) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
return &Error{
|
||||
error: err,
|
||||
stack: callers(),
|
||||
text: text,
|
||||
}
|
||||
}
|
||||
|
||||
// Wrapf returns an error annotating err with a stack trace
|
||||
// at the point Wrapf is called, and the format specifier.
|
||||
// It returns nil if given err is nil.
|
||||
func Wrapf(err error, format string, args ...interface{}) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
return &Error{
|
||||
error: err,
|
||||
stack: callers(),
|
||||
text: fmt.Sprintf(format, args...),
|
||||
}
|
||||
}
|
||||
|
||||
// Cause returns the root cause error.
|
||||
func Cause(err error) error {
|
||||
if err != nil {
|
||||
if e, ok := err.(ApiCause); ok {
|
||||
return e.Cause()
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Stack returns the stack callers as string.
|
||||
// It returns an empty string id the <err> does not support stacks.
|
||||
func Stack(err error) string {
|
||||
if err == nil {
|
||||
return ""
|
||||
}
|
||||
if e, ok := err.(ApiStack); ok {
|
||||
return e.Stack()
|
||||
}
|
||||
return ""
|
||||
}
|
||||
143
g/errors/gerror/gerror_error.go
Normal file
143
g/errors/gerror/gerror_error.go
Normal file
@ -0,0 +1,143 @@
|
||||
// Copyright 2019 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,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gerror
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Error is custom error for additional features.
|
||||
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.
|
||||
}
|
||||
|
||||
const (
|
||||
gFILTER_KEY = "/g/errors/gerror/gerror"
|
||||
)
|
||||
|
||||
var (
|
||||
// goRootForFilter is used for stack filtering purpose.
|
||||
goRootForFilter = runtime.GOROOT()
|
||||
)
|
||||
|
||||
func init() {
|
||||
if goRootForFilter != "" {
|
||||
goRootForFilter = strings.Replace(goRootForFilter, "\\", "/", -1)
|
||||
}
|
||||
}
|
||||
|
||||
// Error implements the interface of Error, it returns the error as string.
|
||||
func (err *Error) Error() string {
|
||||
if err.text != "" {
|
||||
if err.error != nil {
|
||||
return err.text + ": " + err.error.Error()
|
||||
}
|
||||
return err.text
|
||||
}
|
||||
return err.error.Error()
|
||||
}
|
||||
|
||||
// Cause returns the root cause error.
|
||||
func (err *Error) Cause() error {
|
||||
loop := err
|
||||
for loop != nil {
|
||||
if loop.error != nil {
|
||||
if e, ok := loop.error.(*Error); ok {
|
||||
loop = e
|
||||
} else {
|
||||
return loop.error
|
||||
}
|
||||
} else {
|
||||
return loop
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Format formats the frame according to the fmt.Formatter interface.
|
||||
//
|
||||
// %v, %s : Print the error string;
|
||||
// %-v, %-s : Print current error string;
|
||||
// %+s : Print full stack error list;
|
||||
// %+v : Print the error string and full stack error list;
|
||||
func (err *Error) Format(s fmt.State, verb rune) {
|
||||
switch verb {
|
||||
case 's', 'v':
|
||||
switch {
|
||||
case s.Flag('-'):
|
||||
if err.text != "" {
|
||||
io.WriteString(s, err.text)
|
||||
} else {
|
||||
io.WriteString(s, err.Error())
|
||||
}
|
||||
case s.Flag('+'):
|
||||
if verb == 's' {
|
||||
io.WriteString(s, err.Stack())
|
||||
} else {
|
||||
io.WriteString(s, err.Error()+"\n"+err.Stack())
|
||||
}
|
||||
default:
|
||||
io.WriteString(s, err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Stack returns the stack callers as string.
|
||||
// It returns an empty string id the <err> does not support stacks.
|
||||
func (err *Error) Stack() string {
|
||||
if err == nil {
|
||||
return ""
|
||||
}
|
||||
loop := err
|
||||
index := 1
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
for loop != nil {
|
||||
buffer.WriteString(fmt.Sprintf("%d. %-v\n", index, loop))
|
||||
index++
|
||||
formatSubStack(loop.stack, buffer)
|
||||
if loop.error != nil {
|
||||
if e, ok := loop.error.(*Error); ok {
|
||||
loop = e
|
||||
} else {
|
||||
buffer.WriteString(fmt.Sprintf("%d. %s\n", index, loop.error.Error()))
|
||||
index++
|
||||
break
|
||||
}
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
return buffer.String()
|
||||
}
|
||||
|
||||
// formatSubStack formats the stack for error.
|
||||
func formatSubStack(st stack, buffer *bytes.Buffer) {
|
||||
index := 1
|
||||
space := " "
|
||||
for _, p := range st {
|
||||
if fn := runtime.FuncForPC(p - 1); fn != nil {
|
||||
file, line := fn.FileLine(p - 1)
|
||||
if strings.Contains(file, gFILTER_KEY) {
|
||||
continue
|
||||
}
|
||||
if goRootForFilter != "" && len(file) >= len(goRootForFilter) && file[0:len(goRootForFilter)] == goRootForFilter {
|
||||
continue
|
||||
}
|
||||
if index > 9 {
|
||||
space = " "
|
||||
}
|
||||
buffer.WriteString(fmt.Sprintf(" %d).%s%s\n \t%s:%d\n", index, space, fn.Name(), file, line))
|
||||
index++
|
||||
}
|
||||
}
|
||||
}
|
||||
22
g/errors/gerror/gerror_stack.go
Normal file
22
g/errors/gerror/gerror_stack.go
Normal file
@ -0,0 +1,22 @@
|
||||
// Copyright 2019 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,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gerror
|
||||
|
||||
import "runtime"
|
||||
|
||||
// stack represents a stack of program counters.
|
||||
type stack []uintptr
|
||||
|
||||
const (
|
||||
gMAX_STACK_DEPTH = 32
|
||||
)
|
||||
|
||||
func callers() stack {
|
||||
var pcs [gMAX_STACK_DEPTH]uintptr
|
||||
n := runtime.Callers(3, pcs[:])
|
||||
return pcs[0:n]
|
||||
}
|
||||
133
g/errors/gerror/gerror_test.go
Normal file
133
g/errors/gerror/gerror_test.go
Normal file
@ -0,0 +1,133 @@
|
||||
// Copyright 2019 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,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gerror_test
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/gogf/gf/g/errors/gerror"
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
)
|
||||
|
||||
func interfaceNil() interface{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
func nilError() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func Test_Nil(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.Assert(gerror.New(interfaceNil()), nil)
|
||||
gtest.Assert(gerror.Wrap(nilError(), "test"), nil)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Wrap(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
err := errors.New("1")
|
||||
err = gerror.Wrap(err, "2")
|
||||
err = gerror.Wrap(err, "3")
|
||||
gtest.AssertNE(err, nil)
|
||||
gtest.Assert(err.Error(), "3: 2: 1")
|
||||
})
|
||||
|
||||
gtest.Case(t, func() {
|
||||
err := gerror.New("1")
|
||||
err = gerror.Wrap(err, "2")
|
||||
err = gerror.Wrap(err, "3")
|
||||
gtest.AssertNE(err, nil)
|
||||
gtest.Assert(err.Error(), "3: 2: 1")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Cause(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
err := errors.New("1")
|
||||
gtest.Assert(gerror.Cause(err), err)
|
||||
})
|
||||
|
||||
gtest.Case(t, func() {
|
||||
err := errors.New("1")
|
||||
err = gerror.Wrap(err, "2")
|
||||
err = gerror.Wrap(err, "3")
|
||||
gtest.Assert(gerror.Cause(err), "1")
|
||||
})
|
||||
|
||||
gtest.Case(t, func() {
|
||||
err := gerror.New("1")
|
||||
gtest.Assert(gerror.Cause(err), "1")
|
||||
})
|
||||
|
||||
gtest.Case(t, func() {
|
||||
err := gerror.New("1")
|
||||
err = gerror.Wrap(err, "2")
|
||||
err = gerror.Wrap(err, "3")
|
||||
gtest.Assert(gerror.Cause(err), "1")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Format(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
err := errors.New("1")
|
||||
err = gerror.Wrap(err, "2")
|
||||
err = gerror.Wrap(err, "3")
|
||||
gtest.AssertNE(err, nil)
|
||||
gtest.Assert(fmt.Sprintf("%s", err), "3: 2: 1")
|
||||
gtest.Assert(fmt.Sprintf("%v", err), "3: 2: 1")
|
||||
})
|
||||
|
||||
gtest.Case(t, func() {
|
||||
err := gerror.New("1")
|
||||
err = gerror.Wrap(err, "2")
|
||||
err = gerror.Wrap(err, "3")
|
||||
gtest.AssertNE(err, nil)
|
||||
gtest.Assert(fmt.Sprintf("%s", err), "3: 2: 1")
|
||||
gtest.Assert(fmt.Sprintf("%v", err), "3: 2: 1")
|
||||
})
|
||||
|
||||
gtest.Case(t, func() {
|
||||
err := gerror.New("1")
|
||||
err = gerror.Wrap(err, "2")
|
||||
err = gerror.Wrap(err, "3")
|
||||
gtest.AssertNE(err, nil)
|
||||
gtest.Assert(fmt.Sprintf("%-s", err), "3")
|
||||
gtest.Assert(fmt.Sprintf("%-v", err), "3")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Stack(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
err := errors.New("1")
|
||||
gtest.Assert(fmt.Sprintf("%+v", err), "1")
|
||||
})
|
||||
|
||||
gtest.Case(t, func() {
|
||||
err := errors.New("1")
|
||||
err = gerror.Wrap(err, "2")
|
||||
err = gerror.Wrap(err, "3")
|
||||
gtest.AssertNE(err, nil)
|
||||
//fmt.Printf("%+v", err)
|
||||
})
|
||||
|
||||
gtest.Case(t, func() {
|
||||
err := gerror.New("1")
|
||||
gtest.AssertNE(fmt.Sprintf("%+v", err), "1")
|
||||
//fmt.Printf("%+v", err)
|
||||
})
|
||||
|
||||
gtest.Case(t, func() {
|
||||
err := gerror.New("1")
|
||||
err = gerror.Wrap(err, "2")
|
||||
err = gerror.Wrap(err, "3")
|
||||
gtest.AssertNE(err, nil)
|
||||
//fmt.Printf("%+v", err)
|
||||
})
|
||||
}
|
||||
@ -9,6 +9,8 @@ package gins
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/g/container/gmap"
|
||||
"github.com/gogf/gf/g/database/gdb"
|
||||
"github.com/gogf/gf/g/database/gredis"
|
||||
@ -19,7 +21,6 @@ import (
|
||||
"github.com/gogf/gf/g/text/gregex"
|
||||
"github.com/gogf/gf/g/text/gstr"
|
||||
"github.com/gogf/gf/g/util/gconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -87,75 +88,34 @@ func Database(name ...string) gdb.DB {
|
||||
glog.Error(`database init failed: "database" node not found, is config file or configuration missing?`)
|
||||
return nil
|
||||
}
|
||||
for group, v := range m {
|
||||
// Parse <m> as map-slice.
|
||||
for group, groupConfig := range m {
|
||||
cg := gdb.ConfigGroup{}
|
||||
if list, ok := v.([]interface{}); ok {
|
||||
for _, nodeValue := range list {
|
||||
node := gdb.ConfigNode{}
|
||||
nodeMap := nodeValue.(map[string]interface{})
|
||||
if value, ok := nodeMap["host"]; ok {
|
||||
node.Host = gconv.String(value)
|
||||
switch value := groupConfig.(type) {
|
||||
case []interface{}:
|
||||
for _, v := range value {
|
||||
if node := parseDBConfigNode(v); node != nil {
|
||||
cg = append(cg, *node)
|
||||
}
|
||||
if value, ok := nodeMap["port"]; ok {
|
||||
node.Port = gconv.String(value)
|
||||
}
|
||||
if value, ok := nodeMap["user"]; ok {
|
||||
node.User = gconv.String(value)
|
||||
}
|
||||
if value, ok := nodeMap["pass"]; ok {
|
||||
node.Pass = gconv.String(value)
|
||||
}
|
||||
if value, ok := nodeMap["name"]; ok {
|
||||
node.Name = gconv.String(value)
|
||||
}
|
||||
if value, ok := nodeMap["type"]; ok {
|
||||
node.Type = gconv.String(value)
|
||||
}
|
||||
if value, ok := nodeMap["role"]; ok {
|
||||
node.Role = gconv.String(value)
|
||||
}
|
||||
if value, ok := nodeMap["charset"]; ok {
|
||||
node.Charset = gconv.String(value)
|
||||
}
|
||||
if value, ok := nodeMap["priority"]; ok {
|
||||
node.Priority = gconv.Int(value)
|
||||
}
|
||||
// Deprecated
|
||||
if value, ok := nodeMap["linkinfo"]; ok {
|
||||
node.LinkInfo = gconv.String(value)
|
||||
}
|
||||
// Deprecated
|
||||
if value, ok := nodeMap["link-info"]; ok {
|
||||
node.LinkInfo = gconv.String(value)
|
||||
}
|
||||
if value, ok := nodeMap["linkInfo"]; ok {
|
||||
node.LinkInfo = gconv.String(value)
|
||||
}
|
||||
// Deprecated
|
||||
if value, ok := nodeMap["max-idle"]; ok {
|
||||
node.MaxIdleConnCount = gconv.Int(value)
|
||||
}
|
||||
if value, ok := nodeMap["maxIdle"]; ok {
|
||||
node.MaxIdleConnCount = gconv.Int(value)
|
||||
}
|
||||
// Deprecated
|
||||
if value, ok := nodeMap["max-open"]; ok {
|
||||
node.MaxOpenConnCount = gconv.Int(value)
|
||||
}
|
||||
if value, ok := nodeMap["maxOpen"]; ok {
|
||||
node.MaxOpenConnCount = gconv.Int(value)
|
||||
}
|
||||
// Deprecated
|
||||
if value, ok := nodeMap["max-lifetime"]; ok {
|
||||
node.MaxConnLifetime = gconv.Int(value)
|
||||
}
|
||||
if value, ok := nodeMap["maxLifetime"]; ok {
|
||||
node.MaxConnLifetime = gconv.Int(value)
|
||||
}
|
||||
cg = append(cg, node)
|
||||
}
|
||||
case map[string]interface{}:
|
||||
if node := parseDBConfigNode(value); node != nil {
|
||||
cg = append(cg, *node)
|
||||
}
|
||||
}
|
||||
gdb.AddConfigGroup(group, cg)
|
||||
if len(cg) > 0 {
|
||||
gdb.AddConfigGroup(group, cg)
|
||||
}
|
||||
}
|
||||
// Parse <m> as a single node configuration.
|
||||
if node := parseDBConfigNode(m); node != nil {
|
||||
cg := gdb.ConfigGroup{}
|
||||
if node.LinkInfo != "" || node.Host != "" {
|
||||
cg = append(cg, *node)
|
||||
}
|
||||
if len(cg) > 0 {
|
||||
gdb.AddConfigGroup(group, cg)
|
||||
}
|
||||
}
|
||||
addConfigMonitor(key, config)
|
||||
}
|
||||
@ -172,6 +132,84 @@ func Database(name ...string) gdb.DB {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 解析数据库配置节点项
|
||||
func parseDBConfigNode(value interface{}) *gdb.ConfigNode {
|
||||
nodeMap, ok := value.(map[string]interface{})
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
node := &gdb.ConfigNode{}
|
||||
if value, ok := nodeMap["host"]; ok {
|
||||
node.Host = gconv.String(value)
|
||||
}
|
||||
if value, ok := nodeMap["port"]; ok {
|
||||
node.Port = gconv.String(value)
|
||||
}
|
||||
if value, ok := nodeMap["user"]; ok {
|
||||
node.User = gconv.String(value)
|
||||
}
|
||||
if value, ok := nodeMap["pass"]; ok {
|
||||
node.Pass = gconv.String(value)
|
||||
}
|
||||
if value, ok := nodeMap["name"]; ok {
|
||||
node.Name = gconv.String(value)
|
||||
}
|
||||
if value, ok := nodeMap["type"]; ok {
|
||||
node.Type = gconv.String(value)
|
||||
}
|
||||
if value, ok := nodeMap["role"]; ok {
|
||||
node.Role = gconv.String(value)
|
||||
}
|
||||
if value, ok := nodeMap["debug"]; ok {
|
||||
node.Debug = gconv.Bool(value)
|
||||
}
|
||||
if value, ok := nodeMap["charset"]; ok {
|
||||
node.Charset = gconv.String(value)
|
||||
}
|
||||
if value, ok := nodeMap["weight"]; ok {
|
||||
node.Weight = gconv.Int(value)
|
||||
}
|
||||
if value, ok := nodeMap["linkinfo"]; ok {
|
||||
node.LinkInfo = gconv.String(value)
|
||||
}
|
||||
if value, ok := nodeMap["link-info"]; ok {
|
||||
node.LinkInfo = gconv.String(value)
|
||||
}
|
||||
if value, ok := nodeMap["linkInfo"]; ok {
|
||||
node.LinkInfo = gconv.String(value)
|
||||
}
|
||||
if value, ok := nodeMap["link"]; ok {
|
||||
node.LinkInfo = gconv.String(value)
|
||||
}
|
||||
if value, ok := nodeMap["max-idle"]; ok {
|
||||
node.MaxIdleConnCount = gconv.Int(value)
|
||||
}
|
||||
if value, ok := nodeMap["maxIdle"]; ok {
|
||||
node.MaxIdleConnCount = gconv.Int(value)
|
||||
}
|
||||
if value, ok := nodeMap["max-open"]; ok {
|
||||
node.MaxOpenConnCount = gconv.Int(value)
|
||||
}
|
||||
if value, ok := nodeMap["maxOpen"]; ok {
|
||||
node.MaxOpenConnCount = gconv.Int(value)
|
||||
}
|
||||
if value, ok := nodeMap["max-lifetime"]; ok {
|
||||
node.MaxConnLifetime = gconv.Int(value)
|
||||
}
|
||||
if value, ok := nodeMap["maxLifetime"]; ok {
|
||||
node.MaxConnLifetime = gconv.Int(value)
|
||||
}
|
||||
// Parse link syntax.
|
||||
if node.LinkInfo != "" && node.Type == "" {
|
||||
match, _ := gregex.MatchString(`([a-z]+):(.+)`, node.LinkInfo)
|
||||
if len(match) == 3 {
|
||||
node.Type = match[1]
|
||||
node.LinkInfo = match[2]
|
||||
}
|
||||
}
|
||||
return node
|
||||
}
|
||||
|
||||
// Redis操作对象,使用了连接池
|
||||
func Redis(name ...string) *gredis.Redis {
|
||||
config := Config()
|
||||
|
||||
@ -8,12 +8,14 @@ package gins_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/g/os/gcfg"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/g/frame/gins"
|
||||
"github.com/gogf/gf/g/os/gfile"
|
||||
"github.com/gogf/gf/g/os/gtime"
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func Test_Config(t *testing.T) {
|
||||
@ -166,3 +168,17 @@ test = "v=1"
|
||||
gtest.Assert(gins.Config("test").Get("redis.disk"), "127.0.0.1:6379,0")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Basic2(t *testing.T) {
|
||||
config := `log-path = "logs"`
|
||||
gtest.Case(t, func() {
|
||||
path := gcfg.DEFAULT_CONFIG_FILE
|
||||
err := gfile.PutContents(path, config)
|
||||
gtest.Assert(err, nil)
|
||||
defer func() {
|
||||
_ = gfile.Remove(path)
|
||||
}()
|
||||
|
||||
gtest.Assert(gins.Config().Get("log-path"), "logs")
|
||||
})
|
||||
}
|
||||
|
||||
@ -7,12 +7,12 @@
|
||||
package gins_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/g/frame/gins"
|
||||
"github.com/gogf/gf/g/os/gfile"
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func Test_Database(t *testing.T) {
|
||||
@ -31,8 +31,8 @@ test = "v=2"
|
||||
name = "test"
|
||||
type = "mysql"
|
||||
role = "master"
|
||||
weight = "1"
|
||||
charset = "utf8"
|
||||
priority = "1"
|
||||
[[database.test]]
|
||||
host = "127.0.0.1"
|
||||
port = "3306"
|
||||
@ -42,8 +42,8 @@ test = "v=2"
|
||||
name = "test"
|
||||
type = "mysql"
|
||||
role = "master"
|
||||
weight = "1"
|
||||
charset = "utf8"
|
||||
priority = "1"
|
||||
# Redis数据库配置
|
||||
[redis]
|
||||
default = "127.0.0.1:6379,0"
|
||||
@ -59,7 +59,7 @@ test = "v=2"
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
|
||||
gtest.Case(t, func() {
|
||||
fmt.Println("gins Test_Database", gins.Config().Get("test"))
|
||||
//fmt.Println("gins Test_Database", gins.Config().Get("test"))
|
||||
|
||||
dbDefault := gins.Database()
|
||||
dbTest := gins.Database("test")
|
||||
|
||||
118
g/internal/debug/stack.go
Normal file
118
g/internal/debug/stack.go
Normal file
@ -0,0 +1,118 @@
|
||||
// Copyright 2019 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,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
// Package debug contains facilities for programs to debug themselves while
|
||||
// they are running.
|
||||
package debug
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
gMAX_DEPTH = 1000
|
||||
gFILTER_KEY = "/g/internal/debug/stack.go"
|
||||
)
|
||||
|
||||
var (
|
||||
// goRootForFilter is used for stack filtering purpose.
|
||||
goRootForFilter = runtime.GOROOT()
|
||||
)
|
||||
|
||||
func init() {
|
||||
if goRootForFilter != "" {
|
||||
goRootForFilter = strings.Replace(goRootForFilter, "\\", "/", -1)
|
||||
}
|
||||
}
|
||||
|
||||
// PrintStack prints to standard error the stack trace returned by runtime.Stack.
|
||||
func PrintStack(skip ...int) {
|
||||
fmt.Print(Stack(skip...))
|
||||
}
|
||||
|
||||
// Stack returns a formatted stack trace of the goroutine that calls it.
|
||||
// It calls runtime.Stack with a large enough buffer to capture the entire trace.
|
||||
func Stack(skip ...int) string {
|
||||
return StackWithFilter("", skip...)
|
||||
}
|
||||
|
||||
// StackWithFilter returns a formatted stack trace of the goroutine that calls it.
|
||||
// It calls runtime.Stack with a large enough buffer to capture the entire trace.
|
||||
//
|
||||
// The parameter <filter> is used to filter the path of the caller.
|
||||
func StackWithFilter(filter string, skip ...int) string {
|
||||
number := 0
|
||||
if len(skip) > 0 {
|
||||
number = skip[0]
|
||||
}
|
||||
name := ""
|
||||
space := " "
|
||||
index := 1
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
for i := callerFromIndex(filter) + number; i < gMAX_DEPTH; i++ {
|
||||
if pc, file, line, ok := runtime.Caller(i); ok {
|
||||
if goRootForFilter != "" && len(file) >= len(goRootForFilter) && file[0:len(goRootForFilter)] == goRootForFilter {
|
||||
continue
|
||||
}
|
||||
if fn := runtime.FuncForPC(pc); fn == nil {
|
||||
name = "unknown"
|
||||
} else {
|
||||
name = fn.Name()
|
||||
}
|
||||
if index > 9 {
|
||||
space = " "
|
||||
}
|
||||
buffer.WriteString(fmt.Sprintf("%d.%s%s\n %s:%d\n", index, space, name, file, line))
|
||||
index++
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
return buffer.String()
|
||||
}
|
||||
|
||||
// CallerPath returns the absolute file path along with its line number of the caller.
|
||||
func Caller(skip ...int) string {
|
||||
return CallerWithFilter("", skip...)
|
||||
}
|
||||
|
||||
// CallerPathWithFilter returns the absolute file path along with its line number of the caller.
|
||||
//
|
||||
// The parameter <filter> is used to filter the path of the caller.
|
||||
func CallerWithFilter(filter string, skip ...int) string {
|
||||
number := 0
|
||||
if len(skip) > 0 {
|
||||
number = skip[0]
|
||||
}
|
||||
for i := callerFromIndex(filter) + number; i < gMAX_DEPTH; i++ {
|
||||
if _, file, line, ok := runtime.Caller(i); ok {
|
||||
return fmt.Sprintf(`%s:%d`, file, line)
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// callerFromIndex returns the caller position exclusive of the debug package.
|
||||
func callerFromIndex(filter string) int {
|
||||
for i := 0; i < gMAX_DEPTH; i++ {
|
||||
if _, file, _, ok := runtime.Caller(i); ok {
|
||||
if filter != "" && strings.Contains(file, filter) {
|
||||
continue
|
||||
}
|
||||
if strings.Contains(file, gFILTER_KEY) {
|
||||
continue
|
||||
}
|
||||
// exclude the depth from the function of current package.
|
||||
return i - 1
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
@ -18,7 +18,6 @@ func IsEmpty(value interface{}) bool {
|
||||
if value == nil {
|
||||
return true
|
||||
}
|
||||
// 优先通过断言来进行常用类型判断
|
||||
switch value := value.(type) {
|
||||
case int:
|
||||
return value == 0
|
||||
|
||||
@ -1,48 +0,0 @@
|
||||
// Copyright 2019 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,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
// Package errors provides simple functions to manipulate errors.
|
||||
//
|
||||
// This package can be scalable due to https://go.googlesource.com/proposal/+/master/design/go2draft.md.
|
||||
package errors
|
||||
|
||||
import "github.com/gogf/gf/g/util/gconv"
|
||||
|
||||
// errorWrapper is a simple wrapper for errors.
|
||||
type errorWrapper struct {
|
||||
s string
|
||||
}
|
||||
|
||||
// New returns an error that formats as the given value.
|
||||
func New(value interface{}) error {
|
||||
if value == nil {
|
||||
return nil
|
||||
}
|
||||
return NewText(gconv.String(value))
|
||||
}
|
||||
|
||||
// NewText returns an error that formats as the given text.
|
||||
func NewText(text string) error {
|
||||
if text == "" {
|
||||
return nil
|
||||
}
|
||||
return &errorWrapper{
|
||||
s: text,
|
||||
}
|
||||
}
|
||||
|
||||
// Wrap wraps error with text.
|
||||
func Wrap(err error, text string) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
return NewText(text + ": " + err.Error())
|
||||
}
|
||||
|
||||
// Error implements interface Error.
|
||||
func (e *errorWrapper) Error() string {
|
||||
return e.s
|
||||
}
|
||||
@ -1,39 +0,0 @@
|
||||
// Copyright 2019 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,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package errors_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/gogf/gf/g/internal/errors"
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
)
|
||||
|
||||
func interfaceNil() interface{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
func nilError() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func Test_Nil(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
gtest.Assert(errors.New(interfaceNil()), nil)
|
||||
gtest.Assert(errors.Wrap(nilError(), "test"), nil)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Wrap(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
err := errors.New("1")
|
||||
err = errors.Wrap(err, "func2 error")
|
||||
err = errors.Wrap(err, "func3 error")
|
||||
gtest.AssertNE(err, nil)
|
||||
gtest.Assert(err.Error(), "func3 error: func2 error: 1")
|
||||
})
|
||||
}
|
||||
15
g/internal/structs/structs.go
Normal file
15
g/internal/structs/structs.go
Normal file
@ -0,0 +1,15 @@
|
||||
// Copyright 2019 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,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
// Package structs provides functions for struct conversion.
|
||||
package structs
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/third/github.com/fatih/structs"
|
||||
)
|
||||
|
||||
// Field is alias of structs.Field.
|
||||
type Field = structs.Field
|
||||
64
g/internal/structs/structs_map.go
Normal file
64
g/internal/structs/structs_map.go
Normal file
@ -0,0 +1,64 @@
|
||||
// Copyright 2019 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,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package structs
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"github.com/gogf/gf/third/github.com/fatih/structs"
|
||||
)
|
||||
|
||||
// MapField retrieves struct field as map[name/tag]*Field from <pointer>, and returns it.
|
||||
//
|
||||
// The parameter <recursive> specifies whether retrieving the struct field recursively.
|
||||
//
|
||||
// Note that it only retrieves the exported attributes with first letter up-case from struct.
|
||||
func MapField(pointer interface{}, priority []string, recursive bool) map[string]*Field {
|
||||
fieldMap := make(map[string]*Field)
|
||||
fields := ([]*structs.Field)(nil)
|
||||
if v, ok := pointer.(reflect.Value); ok {
|
||||
fields = structs.Fields(v.Interface())
|
||||
} else {
|
||||
fields = structs.Fields(pointer)
|
||||
}
|
||||
tag := ""
|
||||
name := ""
|
||||
for _, field := range fields {
|
||||
name = field.Name()
|
||||
// Only retrieve exported attributes.
|
||||
if name[0] < byte('A') || name[0] > byte('Z') {
|
||||
continue
|
||||
}
|
||||
fieldMap[name] = field
|
||||
tag = ""
|
||||
for _, p := range priority {
|
||||
tag = field.Tag(p)
|
||||
if tag != "" {
|
||||
break
|
||||
}
|
||||
}
|
||||
if tag != "" {
|
||||
fieldMap[tag] = field
|
||||
}
|
||||
if recursive {
|
||||
rv := reflect.ValueOf(field.Value())
|
||||
kind := rv.Kind()
|
||||
if kind == reflect.Ptr {
|
||||
rv = rv.Elem()
|
||||
kind = rv.Kind()
|
||||
}
|
||||
if kind == reflect.Struct {
|
||||
for k, v := range MapField(rv, priority, true) {
|
||||
if _, ok := fieldMap[k]; !ok {
|
||||
fieldMap[k] = v
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return fieldMap
|
||||
}
|
||||
93
g/internal/structs/structs_tag.go
Normal file
93
g/internal/structs/structs_tag.go
Normal file
@ -0,0 +1,93 @@
|
||||
// Copyright 2019 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,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package structs
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"github.com/gogf/gf/third/github.com/fatih/structs"
|
||||
)
|
||||
|
||||
// TagMapName retrieves struct tags as map[tag]attribute from <pointer>, and returns it.
|
||||
//
|
||||
// The parameter <recursive> specifies whether retrieving the struct field recursively.
|
||||
//
|
||||
// Note that it only retrieves the exported attributes with first letter up-case from struct.
|
||||
func TagMapName(pointer interface{}, priority []string, recursive bool) map[string]string {
|
||||
tagMap := TagMapField(pointer, priority, recursive)
|
||||
if len(tagMap) > 0 {
|
||||
m := make(map[string]string, len(tagMap))
|
||||
for k, v := range tagMap {
|
||||
m[k] = v.Name()
|
||||
}
|
||||
return m
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// TagMapField retrieves struct tags as map[tag]*Field from <pointer>, and returns it.
|
||||
//
|
||||
// The parameter <recursive> specifies whether retrieving the struct field recursively.
|
||||
//
|
||||
// Note that it only retrieves the exported attributes with first letter up-case from struct.
|
||||
func TagMapField(pointer interface{}, priority []string, recursive bool) map[string]*Field {
|
||||
tagMap := make(map[string]*Field)
|
||||
fields := ([]*structs.Field)(nil)
|
||||
if v, ok := pointer.(reflect.Value); ok {
|
||||
fields = structs.Fields(v.Interface())
|
||||
} else {
|
||||
rv := reflect.ValueOf(pointer)
|
||||
kind := rv.Kind()
|
||||
if kind == reflect.Ptr {
|
||||
rv = rv.Elem()
|
||||
kind = rv.Kind()
|
||||
}
|
||||
// If pointer is type of **struct and nil, then automatically create a temporary struct,
|
||||
// which is used for structs.Fields.
|
||||
if kind == reflect.Ptr && (!rv.IsValid() || rv.IsNil()) {
|
||||
fields = structs.Fields(reflect.New(rv.Type().Elem()).Elem().Interface())
|
||||
} else {
|
||||
fields = structs.Fields(pointer)
|
||||
}
|
||||
}
|
||||
tag := ""
|
||||
name := ""
|
||||
for _, field := range fields {
|
||||
name = field.Name()
|
||||
// Only retrieve exported attributes.
|
||||
if name[0] < byte('A') || name[0] > byte('Z') {
|
||||
continue
|
||||
}
|
||||
|
||||
tag = ""
|
||||
for _, p := range priority {
|
||||
tag = field.Tag(p)
|
||||
if tag != "" {
|
||||
break
|
||||
}
|
||||
}
|
||||
if tag != "" {
|
||||
tagMap[tag] = field
|
||||
}
|
||||
if recursive {
|
||||
rv := reflect.ValueOf(field.Value())
|
||||
kind := rv.Kind()
|
||||
if kind == reflect.Ptr {
|
||||
rv = rv.Elem()
|
||||
kind = rv.Kind()
|
||||
}
|
||||
if kind == reflect.Struct {
|
||||
for k, v := range TagMapField(rv, priority, true) {
|
||||
if _, ok := tagMap[k]; !ok {
|
||||
tagMap[k] = v
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return tagMap
|
||||
}
|
||||
73
g/internal/structs/structs_test.go
Normal file
73
g/internal/structs/structs_test.go
Normal file
@ -0,0 +1,73 @@
|
||||
// Copyright 2019 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,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package structs_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/gogf/gf/g/internal/structs"
|
||||
|
||||
"github.com/gogf/gf/g"
|
||||
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
)
|
||||
|
||||
func Test_Basic(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
type User struct {
|
||||
Id int
|
||||
Name string `params:"name"`
|
||||
Pass string `my-tag1:"pass1" my-tag2:"pass2" params:"pass"`
|
||||
}
|
||||
var user User
|
||||
gtest.Assert(structs.TagMapName(user, []string{"params"}, true), g.Map{"name": "Name", "pass": "Pass"})
|
||||
gtest.Assert(structs.TagMapName(&user, []string{"params"}, true), g.Map{"name": "Name", "pass": "Pass"})
|
||||
|
||||
gtest.Assert(structs.TagMapName(&user, []string{"params", "my-tag1"}, true), g.Map{"name": "Name", "pass": "Pass"})
|
||||
gtest.Assert(structs.TagMapName(&user, []string{"my-tag1", "params"}, true), g.Map{"name": "Name", "pass1": "Pass"})
|
||||
gtest.Assert(structs.TagMapName(&user, []string{"my-tag2", "params"}, true), g.Map{"name": "Name", "pass2": "Pass"})
|
||||
})
|
||||
|
||||
gtest.Case(t, func() {
|
||||
type Base struct {
|
||||
Pass1 string `params:"password1"`
|
||||
Pass2 string `params:"password2"`
|
||||
}
|
||||
type UserWithBase struct {
|
||||
Id int
|
||||
Name string
|
||||
Base `params:"base"`
|
||||
}
|
||||
user := new(UserWithBase)
|
||||
gtest.Assert(structs.TagMapName(user, []string{"params"}, true), g.Map{
|
||||
"base": "Base",
|
||||
"password1": "Pass1",
|
||||
"password2": "Pass2",
|
||||
})
|
||||
})
|
||||
|
||||
gtest.Case(t, func() {
|
||||
type Base struct {
|
||||
Pass1 string `params:"password1"`
|
||||
Pass2 string `params:"password2"`
|
||||
}
|
||||
type UserWithBase1 struct {
|
||||
Id int
|
||||
Name string
|
||||
Base
|
||||
}
|
||||
type UserWithBase2 struct {
|
||||
Id int
|
||||
Name string
|
||||
Pass Base
|
||||
}
|
||||
user1 := new(UserWithBase1)
|
||||
user2 := new(UserWithBase2)
|
||||
gtest.Assert(structs.TagMapName(user1, []string{"params"}, true), g.Map{"password1": "Pass1", "password2": "Pass2"})
|
||||
gtest.Assert(structs.TagMapName(user2, []string{"params"}, true), g.Map{"password1": "Pass1", "password2": "Pass2"})
|
||||
})
|
||||
}
|
||||
60
g/internal/strutils/strutils.go
Normal file
60
g/internal/strutils/strutils.go
Normal file
@ -0,0 +1,60 @@
|
||||
// Copyright 2019 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,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
// Package strutils provides some string functions for internal usage.
|
||||
package strutils
|
||||
|
||||
import "strings"
|
||||
|
||||
// IsLetterUpper tests whether the given byte b is in upper case.
|
||||
func IsLetterUpper(b byte) bool {
|
||||
if b >= byte('A') && b <= byte('Z') {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsLetterLower tests whether the given byte b is in lower case.
|
||||
func IsLetterLower(b byte) bool {
|
||||
if b >= byte('a') && b <= byte('z') {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsNumeric tests whether the given string s is numeric.
|
||||
func IsNumeric(s string) bool {
|
||||
length := len(s)
|
||||
if length == 0 {
|
||||
return false
|
||||
}
|
||||
for i := 0; i < len(s); i++ {
|
||||
if s[i] < byte('0') || s[i] > byte('9') {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// UcFirst returns a copy of the string s with the first letter mapped to its upper case.
|
||||
func UcFirst(s string) string {
|
||||
if len(s) == 0 {
|
||||
return s
|
||||
}
|
||||
if IsLetterLower(s[0]) {
|
||||
return string(s[0]-32) + s[1:]
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// ReplaceByMap returns a copy of <origin>,
|
||||
// which is replaced by a map in unordered way, case-sensitively.
|
||||
func ReplaceByMap(origin string, replaces map[string]string) string {
|
||||
for k, v := range replaces {
|
||||
origin = strings.Replace(origin, k, v, -1)
|
||||
}
|
||||
return origin
|
||||
}
|
||||
@ -6,3 +6,7 @@
|
||||
|
||||
// Package ghttp provides powerful http server and simple client implements.
|
||||
package ghttp
|
||||
|
||||
var (
|
||||
paramTagPriority = []string{"param", "params", "p"}
|
||||
)
|
||||
|
||||
@ -7,29 +7,32 @@
|
||||
package ghttp
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/gogf/gf/g/encoding/gurl"
|
||||
"github.com/gogf/gf/g/util/gconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// 构建请求参数,参数支持任意数据类型,常见参数类型为string/map。
|
||||
// 如果参数为map类型,参数值将会进行urlencode编码。
|
||||
func BuildParams(params interface{}) (encodedParamStr string) {
|
||||
m := gconv.Map(params)
|
||||
// 如果参数为map类型,参数值将会进行urlencode编码;可以通过 noUrlEncode:true 参数取消编码。
|
||||
func BuildParams(params interface{}, noUrlEncode ...bool) (encodedParamStr string) {
|
||||
m, urlEncode := gconv.Map(params), true
|
||||
if len(m) == 0 {
|
||||
return gconv.String(params)
|
||||
}
|
||||
if len(noUrlEncode) == 1 {
|
||||
urlEncode = !noUrlEncode[0]
|
||||
}
|
||||
s := ""
|
||||
for k, v := range m {
|
||||
if len(encodedParamStr) > 0 {
|
||||
encodedParamStr += "&"
|
||||
}
|
||||
s = gconv.String(v)
|
||||
if len(s) > 6 && strings.Compare(s[0:6], "@file:") == 0 {
|
||||
encodedParamStr += k + "=" + s
|
||||
} else {
|
||||
encodedParamStr += k + "=" + gurl.Encode(s)
|
||||
if urlEncode && len(s) > 6 && strings.Compare(s[0:6], "@file:") != 0 {
|
||||
s = gurl.Encode(s)
|
||||
}
|
||||
encodedParamStr += k + "=" + s
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@ -8,14 +8,14 @@ package ghttp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/gogf/gf/g/container/gvar"
|
||||
"github.com/gogf/gf/g/encoding/gjson"
|
||||
"github.com/gogf/gf/g/os/gtime"
|
||||
"github.com/gogf/gf/g/text/gregex"
|
||||
"github.com/gogf/gf/third/github.com/fatih/structs"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// 请求对象
|
||||
@ -232,17 +232,3 @@ func (r *Request) GetUrl() string {
|
||||
func (r *Request) GetReferer() string {
|
||||
return r.Header.Get("Referer")
|
||||
}
|
||||
|
||||
// 获得结构体对象的参数名称标签,构成map返回
|
||||
func (r *Request) getStructParamsTagMap(pointer interface{}) map[string]string {
|
||||
tagMap := make(map[string]string)
|
||||
fields := structs.Fields(pointer)
|
||||
for _, field := range fields {
|
||||
if tag := field.Tag("params"); tag != "" {
|
||||
for _, v := range strings.Split(tag, ",") {
|
||||
tagMap[strings.TrimSpace(v)] = field.Name()
|
||||
}
|
||||
}
|
||||
}
|
||||
return tagMap
|
||||
}
|
||||
|
||||
@ -7,6 +7,8 @@
|
||||
package ghttp
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g/container/gvar"
|
||||
"github.com/gogf/gf/g/internal/structs"
|
||||
"github.com/gogf/gf/g/util/gconv"
|
||||
)
|
||||
|
||||
@ -31,7 +33,6 @@ func (r *Request) AddPost(key string, value string) {
|
||||
r.PostForm[key] = append(r.PostForm[key], value)
|
||||
}
|
||||
|
||||
// 获得post参数
|
||||
func (r *Request) GetPost(key string, def ...interface{}) []string {
|
||||
r.initPost()
|
||||
if v, ok := r.PostForm[key]; ok {
|
||||
@ -43,6 +44,10 @@ func (r *Request) GetPost(key string, def ...interface{}) []string {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Request) GetPostVar(key string, def ...interface{}) *gvar.Var {
|
||||
return gvar.New(r.GetPostString(key, def...), true)
|
||||
}
|
||||
|
||||
func (r *Request) GetPostString(key string, def ...interface{}) string {
|
||||
value := r.GetPost(key, def...)
|
||||
if value != nil && value[0] != "" {
|
||||
@ -143,7 +148,7 @@ func (r *Request) GetPostMap(def ...map[string]string) map[string]string {
|
||||
|
||||
// 将所有的request参数映射到struct属性上,参数object应当为一个struct对象的指针, mapping为非必需参数,自定义参数与属性的映射关系
|
||||
func (r *Request) GetPostToStruct(pointer interface{}, mapping ...map[string]string) error {
|
||||
tagMap := r.getStructParamsTagMap(pointer)
|
||||
tagMap := structs.TagMapName(pointer, paramTagPriority, true)
|
||||
if len(mapping) > 0 {
|
||||
for k, v := range mapping[0] {
|
||||
tagMap[k] = v
|
||||
@ -153,5 +158,5 @@ func (r *Request) GetPostToStruct(pointer interface{}, mapping ...map[string]str
|
||||
for k, v := range r.GetPostMap() {
|
||||
params[k] = v
|
||||
}
|
||||
return gconv.Struct(params, pointer, tagMap)
|
||||
return gconv.StructDeep(params, pointer, tagMap)
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user