Compare commits

..

69 Commits

Author SHA1 Message Date
2fa558b25f improve gjson 2019-07-18 20:06:42 +08:00
f465b478d6 improve gstr/gjson/gconv 2019-07-18 18:59:49 +08:00
a4abac4916 improve unit test cases for gdb; add more functions for gjson; improve ghttp.Response.WriteStatus function 2019-07-17 23:24:27 +08:00
5ab64e31fd improve benchmark test cases for gset/glist 2019-07-16 20:45:57 +08:00
ac6a8b9b74 improve benchmark test cases for gmap 2019-07-16 20:30:10 +08:00
f43d252d08 improve style for stack output for glog/gerror 2019-07-16 19:49:21 +08:00
185f9efb9c remove third/golang.org/x/text/encoding 2019-07-16 18:52:50 +08:00
47d423036f improve gjson 2019-07-16 17:27:21 +08:00
7fae21f255 improve gjson 2019-07-16 17:11:01 +08:00
3fce835da0 improve performance for gjson.Load* functions; add GetQueryVar/GetPostVar functions for ghttp.Request 2019-07-16 16:23:03 +08:00
0b62ccf941 improve gmutex 2019-07-16 10:25:10 +08:00
e484685282 improve debug feature for gdb; add skip support for file line print for glog 2019-07-15 19:54:13 +08:00
9c857705ff Merge branch 'feature_1.8.0' 2019-07-15 17:41:27 +08:00
76f680de33 fix issue in debug for gdb 2019-07-15 17:40:21 +08:00
1181b6ae3a add MarshalJSON interface implements for garray/gmap/gtime/gvar/gtree 2019-07-13 17:48:16 +08:00
8a7f4ab156 RELEASE updates 2019-07-13 17:18:48 +08:00
99d43a7ddc comment updates for gmutex 2019-07-13 16:56:20 +08:00
bd97d7d94e improve gmutex 2019-07-13 16:41:23 +08:00
edd93c39a3 improve gerror 2019-07-13 14:24:44 +08:00
28e5c33e81 comment updates for gdb 2019-07-13 11:35:22 +08:00
e6bb5e82a3 version updates 2019-07-13 09:57:40 +08:00
a24b97b004 Merge branch 'feature_1.8.0' of https://github.com/gogf/gf 2019-07-13 09:57:10 +08:00
08eeff7f1c RELEASE updates 2019-07-13 09:55:10 +08:00
769f31e69a RELEASE updates 2019-07-13 09:53:53 +08:00
6d1ca028e7 fix issue in unit test case for ghttp 2019-07-12 23:20:36 +08:00
a228356399 RELEASE updates 2019-07-12 22:57:40 +08:00
3521f1b641 add more unit test cases for gdb for slice parameter feature 2019-07-12 22:37:49 +08:00
1dedf3d83b RELEASE updates 2019-07-12 22:33:34 +08:00
da4c01c011 Merge branch 'master' of https://github.com/gogf/gf into feature_1.8.0 2019-07-12 22:02:16 +08:00
9cd445ad40 improve gvalid tag for gvalid package 2019-07-12 21:37:48 +08:00
ff4ef7e240 Merge pull request #247 from 2892931976/gf-chj 2019-07-12 21:02:19 +08:00
4de574ee86 auto create struct for ghttp.Request.GetToStruct 2019-07-12 20:56:45 +08:00
0481b4025b gvalid test 20190712 10:20 2019-07-12 10:23:28 +08:00
9495b858fd RELEASE updates 2019-07-11 21:28:06 +08:00
5576adbd0b update ghttp. 2019-07-11 19:47:15 +08:00
b8da86bce8 Merge pull request #243 from iReadX/dev 2019-07-11 19:45:49 +08:00
a926d3dadd merge master 2019-07-11 19:06:32 +08:00
yc
759be06ebc Function BuildParams add 'UnUrlEncode' param, default false. 2019-07-11 10:02:44 +08:00
aea37272ea merge master 2019-07-10 23:16:39 +08:00
a3d2e03425 merge master 2019-07-09 15:49:09 +08:00
125af33941 gvalid and gfcache test 20190709 15:06 2019-07-09 15:10:02 +08:00
f9c478f250 improve package feature for gtcp 2019-07-09 14:03:43 +08:00
5abf1b5742 Merge branch 'feature_1.8.0' of https://github.com/gogf/gf into feature_1.8.0 2019-07-09 13:27:34 +08:00
9ac3841342 add time.Duration parameter support for gfcache 2019-07-09 13:19:28 +08:00
1f315c5b8d add time.Duration parameter support for gcache 2019-07-09 13:15:53 +08:00
b9440587d0 add Offset function for gdb.Model; improve schema changing feature for gdb 2019-07-09 12:50:38 +08:00
835a971f89 add slice parameter support for method operations for gdb 2019-07-09 12:04:24 +08:00
0bba2092af add one slice passing all parameters support for gdb.Model.Where 2019-07-09 11:34:45 +08:00
c3240218f8 merge master 2019-07-09 11:11:49 +08:00
9e392985b8 Merge branch 'feature_1.8.0' of https://github.com/gogf/gf into feature_1.8.0 2019-07-09 11:11:03 +08:00
9ca9d6c4bd add Cause function for gerror 2019-07-09 10:40:26 +08:00
8ff21d6936 adding support of slice parameters for gdb.Model.Where 2019-07-09 08:40:28 +08:00
691baf5c16 comment updates 2019-07-07 09:47:33 +08:00
b439aedce5 RELEASE updates 2019-07-06 17:35:03 +08:00
2504e405a7 add sql.ErrNoRows feature for querying of gdb 2019-07-06 17:13:17 +08:00
6426409bf9 add debug configuration support for gdb 2019-07-06 16:51:50 +08:00
9cf4ea5c91 rename Priority to Weight for gdb 2019-07-06 16:14:45 +08:00
86343abcf8 merge master 2019-07-06 16:02:05 +08:00
1c71340719 gfcache test 20190704 15:00 2019-07-04 15:03:56 +08:00
363dede57c gvalid test 20190704 11:16 2019-07-04 11:19:59 +08:00
105cdf6fe6 20190703 14:25 2019-07-03 14:28:20 +08:00
5135264d56 Merge branch 'develop' of https://github.com/gogf/gf into develop 2019-07-03 14:25:49 +08:00
c3ff1a3db3 merge master 2019-07-03 14:25:29 +08:00
39bd2616c4 add go.sum to gitignore 2019-06-30 22:22:24 +08:00
6302789c41 improve package gerror; add support for gerror formating output in glog 2019-06-30 22:21:08 +08:00
10102f2db9 improving gerror 2019-06-30 12:54:06 +08:00
009ce9063e improve gerror/glog 2019-06-29 23:35:32 +08:00
58a405bfa8 add package gerror; improve internal/debug,glog package 2019-06-29 18:17:33 +08:00
d5f7ca634d add package internal/debug; add gutil.Stack/PrintStack; rename Backtrace to Stack for glog 2019-06-29 10:45:50 +08:00
140 changed files with 4922 additions and 3802 deletions

1
.gitignore vendored
View File

@ -15,4 +15,3 @@ cbuild
**/.DS_Store
.vscode/
go.sum

View File

@ -21,7 +21,7 @@ addons:
- local
before_install:
- pwd
- mysql -e 'CREATE DATABASE IF NOT EXISTS test;'
install:
- cat /etc/hosts

View File

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

View File

@ -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`模块:
- 去掉日志模块所有的锁机制,改为无锁设计,执行性能更加高效

View File

@ -8,6 +8,7 @@ package garray
import (
"bytes"
"encoding/json"
"fmt"
"math"
"sort"
@ -616,3 +617,10 @@ func (a *IntArray) String() string {
defer a.mu.RUnlock()
return fmt.Sprint(a.array)
}
// 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)
}

View File

@ -8,6 +8,7 @@ package garray
import (
"bytes"
"encoding/json"
"fmt"
"math"
"sort"
@ -610,3 +611,10 @@ func (a *Array) String() string {
defer a.mu.RUnlock()
return fmt.Sprint(a.array)
}
// 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)
}

View File

@ -8,6 +8,7 @@ package garray
import (
"bytes"
"encoding/json"
"fmt"
"math"
"sort"
@ -616,3 +617,10 @@ func (a *StringArray) String() string {
defer a.mu.RUnlock()
return fmt.Sprint(a.array)
}
// 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)
}

View File

@ -8,6 +8,7 @@ package garray
import (
"bytes"
"encoding/json"
"fmt"
"math"
"sort"
@ -541,3 +542,10 @@ func (a *SortedIntArray) String() string {
defer a.mu.RUnlock()
return fmt.Sprint(a.array)
}
// 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)
}

View File

@ -8,6 +8,7 @@ package garray
import (
"bytes"
"encoding/json"
"fmt"
"math"
"sort"
@ -542,3 +543,10 @@ func (a *SortedArray) String() string {
defer a.mu.RUnlock()
return fmt.Sprint(a.array)
}
// 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)
}

View File

@ -8,6 +8,7 @@ package garray
import (
"bytes"
"encoding/json"
"fmt"
"math"
"sort"
@ -536,3 +537,10 @@ func (a *SortedStringArray) String() string {
defer a.mu.RUnlock()
return fmt.Sprint(a.array)
}
// 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)
}

View File

@ -9,8 +9,9 @@
package garray_test
import (
"github.com/gogf/gf/g/container/garray"
"testing"
"github.com/gogf/gf/g/container/garray"
)
var (

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -87,14 +87,15 @@ 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
setSchema(sqlDb *sql.DB, schema string) error
filterFields(table string, data map[string]interface{}) map[string]interface{}
convertValue(fieldValue interface{}, fieldType string) interface{}
getTableFields(table string) (map[string]string, error)
@ -248,9 +249,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 +264,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 +287,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 +309,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 +339,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
}

View File

@ -22,7 +22,8 @@ import (
)
const (
gDEFAULT_DEBUG_SQL_LENGTH = 1000 // 默认调试模式下记录的SQL条数
// 默认调试模式下记录的SQL条数
gDEFAULT_DEBUG_SQL_LENGTH = 1000
)
// 获取最近一条执行的sql
@ -78,6 +79,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 +116,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,28 +181,28 @@ 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)
@ -207,9 +210,9 @@ func (bs *dbBase) GetScan(objPointer interface{}, query string, args ...interfac
k = t.Elem().Kind()
switch k {
case reflect.Array, reflect.Slice:
return bs.db.GetStructs(objPointer, query, args...)
return bs.db.GetStructs(pointer, query, args...)
case reflect.Struct:
return bs.db.GetStruct(objPointer, query, args...)
return bs.db.GetStruct(pointer, query, args...)
}
return fmt.Errorf("element type should be type of struct/slice, unsupported: %v", k)
}
@ -567,6 +570,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 {
@ -585,7 +591,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
}
@ -603,6 +609,15 @@ func (bs *dbBase) rowsToResult(rows *sql.Rows) (Result, error) {
}
}
records = append(records, row)
if !rows.Next() {
break
}
}
return records, nil
}
// 动态切换数据库
func (bs *dbBase) setSchema(sqlDb *sql.DB, schema string) error {
_, err := sqlDb.Exec("USE " + schema)
return err
}

View File

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

View File

@ -27,7 +27,49 @@ type apiString interface {
String() string
}
// 格式化Where查询条件
// 格式化SQL语句。
// 1. 支持参数只传一个slice
// 2. 支持占位符号数量自动扩展;
func formatQuery(query string, args []interface{}) (newQuery string, newArgs []interface{}) {
newQuery = query
// 查询条件参数处理主要处理slice参数类型
if len(args) > 0 {
for index, arg := range args {
rv := reflect.ValueOf(arg)
kind := rv.Kind()
if kind == reflect.Ptr {
rv = rv.Elem()
kind = rv.Kind()
}
switch kind {
// '?'占位符支持slice类型, 这里会将slice参数拆散并更新原有占位符'?'为多个'?',使用','符号连接。
case reflect.Slice, reflect.Array:
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
newQuery, _ = gregex.ReplaceStringFunc(`\?`, newQuery, func(s string) string {
counter++
if counter == index+1 {
return "?" + strings.Repeat(",?", rv.Len()-1)
}
return s
})
default:
newArgs = append(newArgs, arg)
}
}
}
return
}
// 格式化Where查询条件。
func formatWhere(where interface{}, args []interface{}) (newWhere string, newArgs []interface{}) {
// 条件字符串处理
buffer := bytes.NewBuffer(nil)
@ -38,7 +80,6 @@ func formatWhere(where interface{}, args []interface{}) (newWhere string, newArg
rv = rv.Elem()
kind = rv.Kind()
}
tmpArgs := []interface{}(nil)
switch kind {
// map/struct类型
case reflect.Map:
@ -57,19 +98,20 @@ func formatWhere(where interface{}, args []interface{}) (newWhere string, newArg
count := gstr.Count(key, "?")
if count == 0 {
buffer.WriteString(key + " IN(?)")
tmpArgs = append(tmpArgs, value)
newArgs = append(newArgs, value)
} else if count != rv.Len() {
buffer.WriteString(key)
tmpArgs = append(tmpArgs, value)
newArgs = append(newArgs, value)
} else {
buffer.WriteString(key)
// 如果键名/属性名称中带有多个?占位符号,那么将参数打散
tmpArgs = append(tmpArgs, gconv.Interfaces(value)...)
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 + "=?")
@ -79,7 +121,7 @@ func formatWhere(where interface{}, args []interface{}) (newWhere string, newArg
} else {
buffer.WriteString(key)
}
tmpArgs = append(tmpArgs, value)
newArgs = append(newArgs, value)
}
}
}
@ -91,45 +133,16 @@ func formatWhere(where interface{}, args []interface{}) (newWhere string, newArg
if buffer.Len() == 0 {
return "", args
}
newArgs = append(newArgs, args...)
newWhere = buffer.String()
tmpArgs = append(tmpArgs, args...)
// 查询条件参数处理主要处理slice参数类型
if len(tmpArgs) > 0 {
for index, arg := range tmpArgs {
rv := reflect.ValueOf(arg)
kind := rv.Kind()
if kind == reflect.Ptr {
rv = rv.Elem()
kind = rv.Kind()
}
switch kind {
// '?'占位符支持slice类型,
// 这里会将slice参数拆散并更新原有占位符'?'为多个'?',使用','符号连接。
case reflect.Slice:
fallthrough
case reflect.Array:
for i := 0; i < rv.Len(); i++ {
newArgs = append(newArgs, rv.Index(i).Interface())
}
// counter用于匹配该参数的位置(与index对应)
counter := 0
newWhere, _ = gregex.ReplaceStringFunc(`\?`, newWhere, func(s string) string {
counter++
if counter == index+1 {
return "?" + strings.Repeat(",?", rv.Len()-1)
}
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)
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 += "?"
}
}
}
@ -175,7 +188,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)
}

View File

@ -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参数
@ -47,6 +46,7 @@ func (bs *dbBase) Table(tables string) *Model {
tables: tables,
fields: "*",
start: -1,
offset: -1,
safe: false,
}
}
@ -63,6 +63,9 @@ func (tx *TX) Table(tables string) *Model {
tx: tx,
tablesInit: tables,
tables: tables,
fields: "*",
start: -1,
offset: -1,
safe: false,
}
}
@ -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()
@ -605,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
}

View File

@ -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"
)
// 数据库链接对象

View File

@ -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"
)
// 数据库链接对象

View File

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

View File

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

View File

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

View File

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

View File

@ -1,800 +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 (
"database/sql"
"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")
})
// 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("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")
})
// 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("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").Struct(user)
gtest.Assert(err, sql.ErrNoRows)
})
}
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")
})
// 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("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")
})
// 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("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").Where("id<0").Structs(&users)
gtest.Assert(err, sql.ErrNoRows)
})
}
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")
})
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("user").Where("id < 0").Scan(user)
err2 := db.Table("user").Where("id < 0").Scan(users)
gtest.Assert(err1, sql.ErrNoRows)
gtest.Assert(err2, sql.ErrNoRows)
})
}
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)
})
// slice
gtest.Case(t, func() {
result, err := db.Table("user").Where("id=? AND nickname=?", g.Slice{3, "T3"}...).One()
if err != nil {
gtest.Fatal(err)
}
gtest.Assert(result["id"].Int(), 3)
})
gtest.Case(t, func() {
result, err := db.Table("user").Where("passport like ? and nickname like ?", g.Slice{"t3", "T3"}...).One()
if err != nil {
gtest.Fatal(err)
}
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)
})
}

View File

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

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

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

View File

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

View File

@ -0,0 +1,674 @@
// 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 := db.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)
}
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)
}
})
}

View File

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

View File

@ -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 {
@ -318,14 +329,34 @@ func (j *Json) ToStruct(pointer interface{}) error {
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)
}

View File

@ -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,88 @@ 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")
}
// 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...)
}
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 ""
}
}

View File

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

View File

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

View File

@ -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,6 +142,7 @@ 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)
}
@ -216,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()
}

90
g/errors/gerror/gerror.go Normal file
View 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 ""
}

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

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

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

View File

@ -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 (
@ -159,11 +160,14 @@ func parseDBConfigNode(value interface{}) *gdb.ConfigNode {
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["priority"]; ok {
node.Priority = gconv.Int(value)
if value, ok := nodeMap["weight"]; ok {
node.Weight = gconv.Int(value)
}
if value, ok := nodeMap["linkinfo"]; ok {
node.LinkInfo = gconv.String(value)

View File

@ -8,12 +8,13 @@ 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/os/gtime"
"github.com/gogf/gf/g/test/gtest"
"testing"
"time"
)
func Test_Config(t *testing.T) {

View File

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

121
g/internal/debug/stack.go Normal file
View File

@ -0,0 +1,121 @@
// 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() + number; i < gMAX_DEPTH; i++ {
if pc, file, line, ok := runtime.Caller(i); ok {
if filter != "" && strings.Contains(file, filter) {
continue
}
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() + number; i < gMAX_DEPTH; i++ {
if _, file, line, ok := runtime.Caller(i); ok {
if filter != "" && strings.Contains(file, filter) {
continue
}
return fmt.Sprintf(`%s:%d`, file, line)
} else {
break
}
}
return ""
}
// callerFromIndex returns the caller position exclusive of the debug package.
func callerFromIndex() int {
for i := 0; i < gMAX_DEPTH; i++ {
if _, file, _, ok := runtime.Caller(i); ok {
if strings.Contains(file, gFILTER_KEY) {
continue
}
// exclude the depth from the function of current package.
return i - 1
}
}
return 0
}

View File

@ -18,7 +18,6 @@ func IsEmpty(value interface{}) bool {
if value == nil {
return true
}
// 优先通过断言来进行常用类型判断
switch value := value.(type) {
case int:
return value == 0

View File

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

View File

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

View File

@ -40,7 +40,19 @@ func TagMapField(pointer interface{}, priority []string, recursive bool) map[str
if v, ok := pointer.(reflect.Value); ok {
fields = structs.Fields(v.Interface())
} else {
fields = structs.Fields(pointer)
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 := ""

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

View File

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

View File

@ -7,6 +7,7 @@
package ghttp
import (
"github.com/gogf/gf/g/container/gvar"
"github.com/gogf/gf/g/internal/structs"
"github.com/gogf/gf/g/util/gconv"
)
@ -32,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 {
@ -44,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] != "" {

View File

@ -9,6 +9,8 @@ package ghttp
import (
"strings"
"github.com/gogf/gf/g/container/gvar"
"github.com/gogf/gf/g/internal/structs"
"github.com/gogf/gf/g/util/gconv"
)
@ -54,6 +56,10 @@ func (r *Request) GetQuery(key string, def ...interface{}) []string {
return nil
}
func (r *Request) GetQueryVar(key string, def ...interface{}) *gvar.Var {
return gvar.New(r.GetQueryString(key, def...), true)
}
func (r *Request) GetQueryString(key string, def ...interface{}) string {
value := r.GetQuery(key, def...)
if value != nil && value[0] != "" {

View File

@ -10,11 +10,12 @@ package ghttp
import (
"bytes"
"fmt"
"net/http"
"strconv"
"github.com/gogf/gf/g/encoding/gparser"
"github.com/gogf/gf/g/os/gfile"
"github.com/gogf/gf/g/util/gconv"
"net/http"
"strconv"
)
// 服务端请求返回对象。
@ -131,7 +132,7 @@ func (r *Response) SetAllowCrossDomainRequest(allowOrigin string, allowMethods s
}
// 返回HTTP Code状态码
func (r *Response) WriteStatus(status int, content ...string) {
func (r *Response) WriteStatus(status int, content ...interface{}) {
if r.buffer.Len() == 0 {
// 状态码注册回调函数处理
if status != http.StatusOK {
@ -146,10 +147,12 @@ func (r *Response) WriteStatus(status int, content ...string) {
return
}
}
r.Header().Set("Content-Type", "text/plain; charset=utf-8")
r.Header().Set("X-Content-Type-Options", "nosniff")
if r.Header().Get("Content-Type") == "" {
r.Header().Set("Content-Type", "text/plain; charset=utf-8")
//r.Header().Set("X-Content-Type-Options", "nosniff")
}
if len(content) > 0 {
r.Write(content[0])
r.Write(content...)
} else {
r.Write(http.StatusText(status))
}

View File

@ -9,6 +9,7 @@ package ghttp
import (
"fmt"
"github.com/gogf/gf/g/os/gtime"
)
@ -32,7 +33,7 @@ func (s *Server) handleAccessLog(r *Request) {
)
content += fmt.Sprintf(` %.3f`, float64(r.LeaveTime-r.EnterTime)/1000)
content += fmt.Sprintf(`, %s, "%s", "%s"`, r.GetClientIp(), r.Referer(), r.UserAgent())
s.logger.Cat("access").Backtrace(false, 2).Stdout(s.config.LogStdout).Println(content)
s.logger.Cat("access").Stack(false, 2).Stdout(s.config.LogStdout).Println(content)
}
// 处理服务错误信息主要是panichttp请求的status由access log进行管理
@ -60,5 +61,5 @@ func (s *Server) handleErrorLog(error interface{}, r *Request) {
content += fmt.Sprintf(` %.3f`, float64(gtime.Microsecond()-r.EnterTime)/1000)
}
content += fmt.Sprintf(`, %s, "%s", "%s"`, r.GetClientIp(), r.Referer(), r.UserAgent())
s.logger.Cat("error").Backtrace(true, 2).Stdout(s.config.LogStdout).Error(content)
s.logger.Cat("error").Stack(true, 2).Stdout(s.config.LogStdout).Error(content)
}

View File

@ -0,0 +1,67 @@
// Copyright 2018 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 ghttp_test
import (
"fmt"
"testing"
"time"
"github.com/gogf/gf/g/util/gvalid"
"github.com/gogf/gf/g"
"github.com/gogf/gf/g/net/ghttp"
"github.com/gogf/gf/g/test/gtest"
)
func Test_Params_Struct(t *testing.T) {
type User struct {
Id int
Name string
Pass1 string `params:"password1"`
Pass2 string `params:"password2" gvalid:"passwd1 @required|length:2,20|password3#||密码强度不足"`
}
p := ports.PopRand()
s := g.Server(p)
s.BindHandler("/struct1", func(r *ghttp.Request) {
if m := r.GetMap(); len(m) > 0 {
user := new(User)
r.GetToStruct(user)
r.Response.Write(user.Id, user.Name, user.Pass1, user.Pass2)
}
})
s.BindHandler("/struct2", func(r *ghttp.Request) {
if m := r.GetMap(); len(m) > 0 {
user := (*User)(nil)
r.GetToStruct(&user)
r.Response.Write(user.Id, user.Name, user.Pass1, user.Pass2)
}
})
s.BindHandler("/struct-valid", func(r *ghttp.Request) {
if m := r.GetMap(); len(m) > 0 {
user := new(User)
r.GetToStruct(user)
err := gvalid.CheckStruct(user, nil)
r.Response.Write(err.Maps())
}
})
s.SetPort(p)
s.SetDumpRouteMap(false)
s.Start()
defer s.Shutdown()
// 等待启动完成
time.Sleep(time.Second)
gtest.Case(t, func() {
client := ghttp.NewClient()
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", p))
gtest.Assert(client.GetContent("/struct1", `id=1&name=john&password1=123&password2=456`), `1john123456`)
gtest.Assert(client.PostContent("/struct1", `id=1&name=john&password1=123&password2=456`), `1john123456`)
gtest.Assert(client.PostContent("/struct2", `id=1&name=john&password1=123&password2=456`), `1john123456`)
gtest.Assert(client.PostContent("/struct-valid", `id=1&name=john&password1=123&password2=0`), `{"passwd1":{"length":"字段长度为2到20个字符","password3":"密码强度不足"}}`)
})
}

View File

@ -14,7 +14,7 @@ import (
"net"
"time"
"github.com/gogf/gf/g/internal/errors"
"github.com/gogf/gf/g/errors/gerror"
)
// 封装的链接对象
@ -209,7 +209,7 @@ func (c *Conn) RecvWithTimeout(length int, timeout time.Duration, retry ...Retry
return nil, err
}
defer func() {
err = errors.Wrap(c.SetRecvDeadline(time.Time{}), "SetRecvDeadline error")
err = gerror.Wrap(c.SetRecvDeadline(time.Time{}), "SetRecvDeadline error")
}()
data, err = c.Recv(length, retry...)
return
@ -221,7 +221,7 @@ func (c *Conn) SendWithTimeout(data []byte, timeout time.Duration, retry ...Retr
return err
}
defer func() {
err = errors.Wrap(c.SetSendDeadline(time.Time{}), "SetSendDeadline error")
err = gerror.Wrap(c.SetSendDeadline(time.Time{}), "SetSendDeadline error")
}()
err = c.Send(data, retry...)
return

View File

@ -11,20 +11,23 @@ import (
"fmt"
"time"
"github.com/gogf/gf/g/internal/errors"
"github.com/gogf/gf/g/errors/gerror"
)
const (
// 默认允许最大的简单协议包大小(byte), 65535 byte
gPKG_MAX_DATA_SIZE = 65535
// 简单协议包头大小
gPKG_HEADER_SIZE = 3
gPKG_DEFAULT_MAX_DATA_SIZE = 65535
// 默认简单协议包头大小
gPKG_DEFAULT_HEADER_SIZE = 2
// 协议头最大大小
gPKG_MAX_HEADER_SIZE = 4
)
// 数据读取选项
type PkgOption struct {
MaxSize int // (byte)数据读取的最大包大小,最大不能超过3字节(0xFFFFFF,15MB)默认为65535byte
Retry Retry // 失败重试
HeaderSize int // 自定义头大小(默认为2字节,最大不能超过4字节)
MaxDataSize int // (byte)数据读取的最大包大小默认最大不能超过2字节(65535 byte)
Retry Retry // 失败重试
}
// getPkgOption wraps and returns the PkgOption.
@ -34,10 +37,13 @@ func getPkgOption(option ...PkgOption) (*PkgOption, error) {
if len(option) > 0 {
pkgOption = option[0]
}
if pkgOption.MaxSize == 0 {
pkgOption.MaxSize = gPKG_MAX_DATA_SIZE
} else if pkgOption.MaxSize > 0xFFFFFF {
return nil, fmt.Errorf(`package size %d exceeds allowed max size %d`, pkgOption.MaxSize, 0xFFFFFF)
if pkgOption.HeaderSize == 0 {
pkgOption.HeaderSize = gPKG_DEFAULT_HEADER_SIZE
}
if pkgOption.MaxDataSize == 0 {
pkgOption.MaxDataSize = gPKG_DEFAULT_MAX_DATA_SIZE
} else if pkgOption.MaxDataSize > 0xFFFFFF {
return nil, fmt.Errorf(`package size %d exceeds allowed max size %d`, pkgOption.MaxDataSize, 0xFFFFFF)
}
return &pkgOption, nil
}
@ -55,17 +61,18 @@ func (c *Conn) SendPkg(data []byte, option ...PkgOption) error {
return err
}
length := len(data)
if length > pkgOption.MaxSize {
return fmt.Errorf(`data size %d exceeds max pkg size %d`, length, gPKG_MAX_DATA_SIZE)
if length > pkgOption.MaxDataSize {
return fmt.Errorf(`data size %d exceeds max pkg size %d`, length, pkgOption.MaxDataSize)
}
buffer := make([]byte, gPKG_HEADER_SIZE+1+len(data))
offset := gPKG_MAX_HEADER_SIZE - pkgOption.HeaderSize
buffer := make([]byte, gPKG_MAX_HEADER_SIZE+len(data))
binary.BigEndian.PutUint32(buffer[0:], uint32(length))
copy(buffer[gPKG_HEADER_SIZE+1:], data)
copy(buffer[gPKG_MAX_HEADER_SIZE:], data)
if pkgOption.Retry.Count > 0 {
return c.Send(buffer[1:], pkgOption.Retry)
return c.Send(buffer[offset:], pkgOption.Retry)
}
//fmt.Println("SendPkg:", buffer[1:])
return c.Send(buffer[1:])
//fmt.Println("SendPkg:", buffer[offset:])
return c.Send(buffer[offset:])
}
// 简单协议: 带超时时间的数据发送
@ -74,7 +81,7 @@ func (c *Conn) SendPkgWithTimeout(data []byte, timeout time.Duration, option ...
return err
}
defer func() {
err = errors.Wrap(c.SetSendDeadline(time.Time{}), "SetSendDeadline error")
err = gerror.Wrap(c.SetSendDeadline(time.Time{}), "SetSendDeadline error")
}()
err = c.SendPkg(data, option...)
return
@ -109,26 +116,38 @@ func (c *Conn) RecvPkg(option ...PkgOption) (result []byte, err error) {
for {
// 先根据对象的缓冲区数据进行计算
for {
if len(c.buffer) >= gPKG_HEADER_SIZE {
// 注意"数据长度"为3个字节不满足4个字节的uint32类型因此这里"低位"补0
length = int(binary.BigEndian.Uint32([]byte{0, c.buffer[0], c.buffer[1], c.buffer[2]}))
if len(c.buffer) >= pkgOption.HeaderSize {
// 不满足4个字节的uint32类型因此这里"低位"补0
if length <= 0 {
switch pkgOption.HeaderSize {
case 1:
length = int(binary.BigEndian.Uint32([]byte{0, 0, 0, c.buffer[0]}))
case 2:
length = int(binary.BigEndian.Uint32([]byte{0, 0, c.buffer[0], c.buffer[1]}))
case 3:
length = int(binary.BigEndian.Uint32([]byte{0, c.buffer[0], c.buffer[1], c.buffer[2]}))
default:
length = int(binary.BigEndian.Uint32([]byte{c.buffer[0], c.buffer[1], c.buffer[2], c.buffer[3]}))
}
}
// 解析的大小是否符合规范,清空从该连接接收到的所有数据包
if length < 0 || length > pkgOption.MaxSize {
if length < 0 || length > pkgOption.MaxDataSize {
c.buffer = c.buffer[:0]
return nil, fmt.Errorf(`invalid package size %d`, length)
}
// 不满足包大小,需要继续读取
if len(c.buffer) < length+gPKG_HEADER_SIZE {
if len(c.buffer) < length+pkgOption.HeaderSize {
break
}
result = c.buffer[gPKG_HEADER_SIZE : gPKG_HEADER_SIZE+length]
c.buffer = c.buffer[gPKG_HEADER_SIZE+length:]
result = c.buffer[pkgOption.HeaderSize : pkgOption.HeaderSize+length]
c.buffer = c.buffer[pkgOption.HeaderSize+length:]
length = 0
return
} else {
break
}
}
// 读取系统socket缓冲区的完整数据
// 读取系统socket当前缓冲区的数据
temp, err = c.Recv(0, pkgOption.Retry)
if err != nil {
break
@ -147,7 +166,7 @@ func (c *Conn) RecvPkgWithTimeout(timeout time.Duration, option ...PkgOption) (d
return nil, err
}
defer func() {
err = errors.Wrap(c.SetRecvDeadline(time.Time{}), "SetRecvDeadline error")
err = gerror.Wrap(c.SetRecvDeadline(time.Time{}), "SetRecvDeadline error")
}()
data, err = c.RecvPkg(option...)
return

View File

@ -9,10 +9,9 @@ package gtcp
import (
"time"
"github.com/gogf/gf/g/internal/errors"
"github.com/gogf/gf/g/container/gmap"
"github.com/gogf/gf/g/container/gpool"
"github.com/gogf/gf/g/errors/gerror"
)
// 链接池链接对象
@ -122,7 +121,7 @@ func (c *PoolConn) RecvWithTimeout(length int, timeout time.Duration, retry ...R
return nil, err
}
defer func() {
err = errors.Wrap(c.SetRecvDeadline(time.Time{}), "SetRecvDeadline error")
err = gerror.Wrap(c.SetRecvDeadline(time.Time{}), "SetRecvDeadline error")
}()
data, err = c.Recv(length, retry...)
return
@ -134,7 +133,7 @@ func (c *PoolConn) SendWithTimeout(data []byte, timeout time.Duration, retry ...
return err
}
defer func() {
err = errors.Wrap(c.SetSendDeadline(time.Time{}), "SetSendDeadline error")
err = gerror.Wrap(c.SetSendDeadline(time.Time{}), "SetSendDeadline error")
}()
err = c.Send(data, retry...)
return

View File

@ -9,7 +9,7 @@ package gtcp
import (
"time"
"github.com/gogf/gf/g/internal/errors"
"github.com/gogf/gf/g/errors/gerror"
)
// 简单协议: (方法覆盖)发送数据
@ -47,7 +47,7 @@ func (c *PoolConn) RecvPkgWithTimeout(timeout time.Duration, option ...PkgOption
return nil, err
}
defer func() {
err = errors.Wrap(c.SetRecvDeadline(time.Time{}), "SetRecvDeadline error")
err = gerror.Wrap(c.SetRecvDeadline(time.Time{}), "SetRecvDeadline error")
}()
data, err = c.RecvPkg(option...)
return
@ -59,7 +59,7 @@ func (c *PoolConn) SendPkgWithTimeout(data []byte, timeout time.Duration, option
return err
}
defer func() {
err = errors.Wrap(c.SetSendDeadline(time.Time{}), "SetSendDeadline error")
err = gerror.Wrap(c.SetSendDeadline(time.Time{}), "SetSendDeadline error")
}()
err = c.SendPkg(data, option...)
return

View File

@ -11,7 +11,7 @@ import (
"net"
"time"
"github.com/gogf/gf/g/internal/errors"
"github.com/gogf/gf/g/errors/gerror"
)
// 封装的UDP链接对象
@ -182,7 +182,7 @@ func (c *Conn) RecvWithTimeout(length int, timeout time.Duration, retry ...Retry
return nil, err
}
defer func() {
err = errors.Wrap(c.SetRecvDeadline(time.Time{}), "SetRecvDeadline error")
err = gerror.Wrap(c.SetRecvDeadline(time.Time{}), "SetRecvDeadline error")
}()
data, err = c.Recv(length, retry...)
return
@ -194,7 +194,7 @@ func (c *Conn) SendWithTimeout(data []byte, timeout time.Duration, retry ...Retr
return err
}
defer func() {
err = errors.Wrap(c.SetSendDeadline(time.Time{}), "SetSendDeadline error")
err = gerror.Wrap(c.SetSendDeadline(time.Time{}), "SetSendDeadline error")
}()
err = c.Send(data, retry...)
return

View File

@ -12,21 +12,21 @@ var cache = New()
// Set sets cache with <key>-<value> pair, which is expired after <expire> milliseconds.
// If <expire> <=0 means it does not expire.
func Set(key interface{}, value interface{}, expire int) {
cache.Set(key, value, expire)
func Set(key interface{}, value interface{}, duration interface{}) {
cache.Set(key, value, duration)
}
// SetIfNotExist sets cache with <key>-<value> pair if <key> does not exist in the cache,
// which is expired after <expire> milliseconds.
// If <expire> <=0 means it does not expire.
func SetIfNotExist(key interface{}, value interface{}, expire int) bool {
return cache.SetIfNotExist(key, value, expire)
func SetIfNotExist(key interface{}, value interface{}, duration interface{}) bool {
return cache.SetIfNotExist(key, value, duration)
}
// Sets batch sets cache with key-value pairs by <data>, which is expired after <expire> milliseconds.
// If <expire> <=0 means it does not expire.
func Sets(data map[interface{}]interface{}, expire int) {
cache.Sets(data, expire)
func Sets(data map[interface{}]interface{}, duration interface{}) {
cache.Sets(data, duration)
}
// Get returns the value of <key>.
@ -39,8 +39,8 @@ func Get(key interface{}) interface{} {
// or sets <key>-<value> pair and returns <value> if <key> does not exist in the cache.
// The key-value pair expires after <expire> milliseconds.
// If <expire> <=0 means it does not expire.
func GetOrSet(key interface{}, value interface{}, expire int) interface{} {
return cache.GetOrSet(key, value, expire)
func GetOrSet(key interface{}, value interface{}, duration interface{}) interface{} {
return cache.GetOrSet(key, value, duration)
}
// GetOrSetFunc returns the value of <key>,
@ -48,8 +48,8 @@ func GetOrSet(key interface{}, value interface{}, expire int) interface{} {
// if <key> does not exist in the cache.
// The key-value pair expires after <expire> milliseconds.
// If <expire> <=0 means it does not expire.
func GetOrSetFunc(key interface{}, f func() interface{}, expire int) interface{} {
return cache.GetOrSetFunc(key, f, expire)
func GetOrSetFunc(key interface{}, f func() interface{}, duration interface{}) interface{} {
return cache.GetOrSetFunc(key, f, duration)
}
// GetOrSetFuncLock returns the value of <key>,
@ -59,8 +59,8 @@ func GetOrSetFunc(key interface{}, f func() interface{}, expire int) interface{}
// If <expire> <=0 means it does not expire.
//
// Note that the function <f> is executed within writing mutex lock.
func GetOrSetFuncLock(key interface{}, f func() interface{}, expire int) interface{} {
return cache.GetOrSetFuncLock(key, f, expire)
func GetOrSetFuncLock(key interface{}, f func() interface{}, duration interface{}) interface{} {
return cache.GetOrSetFuncLock(key, f, duration)
}
// Contains returns true if <key> exists in the cache, or else returns false.

View File

@ -9,6 +9,7 @@ package gcache
import (
"math"
"sync"
"time"
"github.com/gogf/gf/g/container/glist"
"github.com/gogf/gf/g/container/gset"
@ -103,9 +104,21 @@ func (c *memCache) getOrNewExpireSet(expire int64) (expireSet *gset.Set) {
return
}
// getMilliExpire converts parameter <duration> to int type in milliseconds.
//
// Note that there's some performance cost in type assertion here, but it's valuable.
func (c *memCache) getMilliExpire(duration interface{}) int {
if d, ok := duration.(time.Duration); ok {
return int(d.Nanoseconds() / 1000000)
} else {
return duration.(int)
}
}
// Set sets cache with <key>-<value> pair, which is expired after <expire> milliseconds.
// If <expire> <=0 means it does not expire.
func (c *memCache) Set(key interface{}, value interface{}, expire int) {
func (c *memCache) Set(key interface{}, value interface{}, duration interface{}) {
expire := c.getMilliExpire(duration)
expireTime := c.getInternalExpire(expire)
c.dataMu.Lock()
c.data[key] = memCacheItem{v: value, e: expireTime}
@ -119,7 +132,8 @@ func (c *memCache) Set(key interface{}, value interface{}, expire int) {
//
// It doubly checks the <key> whether exists in the cache using mutex writing lock
// before setting it to the cache.
func (c *memCache) doSetWithLockCheck(key interface{}, value interface{}, expire int) interface{} {
func (c *memCache) doSetWithLockCheck(key interface{}, value interface{}, duration interface{}) interface{} {
expire := c.getMilliExpire(duration)
expireTimestamp := c.getInternalExpire(expire)
c.dataMu.Lock()
defer c.dataMu.Unlock()
@ -149,7 +163,8 @@ func (c *memCache) getInternalExpire(expire int) int64 {
// SetIfNotExist sets cache with <key>-<value> pair if <key> does not exist in the cache,
// which is expired after <expire> milliseconds.
// If <expire> <=0 means it does not expire.
func (c *memCache) SetIfNotExist(key interface{}, value interface{}, expire int) bool {
func (c *memCache) SetIfNotExist(key interface{}, value interface{}, duration interface{}) bool {
expire := c.getMilliExpire(duration)
if !c.Contains(key) {
c.doSetWithLockCheck(key, value, expire)
return true
@ -159,7 +174,8 @@ func (c *memCache) SetIfNotExist(key interface{}, value interface{}, expire int)
// Sets batch sets cache with key-value pairs by <data>, which is expired after <expire> milliseconds.
// If <expire> <=0 means it does not expire.
func (c *memCache) Sets(data map[interface{}]interface{}, expire int) {
func (c *memCache) Sets(data map[interface{}]interface{}, duration interface{}) {
expire := c.getMilliExpire(duration)
expireTime := c.getInternalExpire(expire)
for k, v := range data {
c.dataMu.Lock()
@ -189,9 +205,9 @@ func (c *memCache) Get(key interface{}) interface{} {
// or sets <key>-<value> pair and returns <value> if <key> does not exist in the cache.
// The key-value pair expires after <expire> milliseconds.
// If <expire> <=0 means it does not expire.
func (c *memCache) GetOrSet(key interface{}, value interface{}, expire int) interface{} {
func (c *memCache) GetOrSet(key interface{}, value interface{}, duration interface{}) interface{} {
if v := c.Get(key); v == nil {
return c.doSetWithLockCheck(key, value, expire)
return c.doSetWithLockCheck(key, value, duration)
} else {
return v
}
@ -202,9 +218,9 @@ func (c *memCache) GetOrSet(key interface{}, value interface{}, expire int) inte
// if <key> does not exist in the cache.
// The key-value pair expires after <expire> milliseconds.
// If <expire> <=0 means it does not expire.
func (c *memCache) GetOrSetFunc(key interface{}, f func() interface{}, expire int) interface{} {
func (c *memCache) GetOrSetFunc(key interface{}, f func() interface{}, duration interface{}) interface{} {
if v := c.Get(key); v == nil {
return c.doSetWithLockCheck(key, f(), expire)
return c.doSetWithLockCheck(key, f(), duration)
} else {
return v
}
@ -217,9 +233,9 @@ func (c *memCache) GetOrSetFunc(key interface{}, f func() interface{}, expire in
// If <expire> <=0 means it does not expire.
//
// Note that the function <f> is executed within writing mutex lock.
func (c *memCache) GetOrSetFuncLock(key interface{}, f func() interface{}, expire int) interface{} {
func (c *memCache) GetOrSetFuncLock(key interface{}, f func() interface{}, duration interface{}) interface{} {
if v := c.Get(key); v == nil {
return c.doSetWithLockCheck(key, f, expire)
return c.doSetWithLockCheck(key, f, duration)
} else {
return v
}

View File

@ -9,13 +9,14 @@
package gcache_test
import (
"testing"
"time"
"github.com/gogf/gf/g"
"github.com/gogf/gf/g/container/gset"
"github.com/gogf/gf/g/os/gcache"
"github.com/gogf/gf/g/os/grpool"
"github.com/gogf/gf/g/test/gtest"
"testing"
"time"
)
//clear 用于清除全局缓存因gcache api 暂未暴露 Clear 方法
@ -49,6 +50,14 @@ func TestCache_Set_Expire(t *testing.T) {
gtest.Assert(cache.Size(), 0)
cache.Close()
})
gtest.Case(t, func() {
cache := gcache.New()
cache.Set(1, 11, 100*time.Millisecond)
gtest.Assert(cache.Get(1), 11)
time.Sleep(200 * time.Millisecond)
gtest.Assert(cache.Get(1), nil)
})
}
func TestCache_Keys_Values(t *testing.T) {
@ -205,7 +214,7 @@ func TestCache_SetConcurrency(t *testing.T) {
}()
select {
case <-time.After(2 * time.Second):
t.Log("first part end")
//t.Log("first part end")
}
go func() {
@ -217,7 +226,7 @@ func TestCache_SetConcurrency(t *testing.T) {
}()
select {
case <-time.After(2 * time.Second):
t.Log("second part end")
//t.Log("second part end")
}
})
}

View File

@ -8,6 +8,8 @@
package gfcache
import (
"time"
"github.com/gogf/gf/g/internal/cmdenv"
"github.com/gogf/gf/g/os/gcache"
"github.com/gogf/gf/g/os/gfile"
@ -27,18 +29,18 @@ var (
// GetContents returns string content of given file by <path> from cache.
// If there's no content in the cache, it will read it from disk file specified by <path>.
// The parameter <expire> specifies the caching time for this file content in seconds.
func GetContents(path string, expire ...int) string {
return string(GetBinContents(path, expire...))
func GetContents(path string, duration ...interface{}) string {
return string(GetBinContents(path, duration...))
}
// GetBinContents returns []byte content of given file by <path> from cache.
// If there's no content in the cache, it will read it from disk file specified by <path>.
// The parameter <expire> specifies the caching time for this file content in seconds.
func GetBinContents(path string, expire ...int) []byte {
func GetBinContents(path string, duration ...interface{}) []byte {
k := cacheKey(path)
e := cacheExpire
if len(expire) > 0 {
e = expire[0]
if len(duration) > 0 {
e = getSecondExpire(duration[0])
}
r := gcache.GetOrSetFuncLock(k, func() interface{} {
b := gfile.GetBinContents(path)
@ -58,7 +60,18 @@ func GetBinContents(path string, expire ...int) []byte {
return nil
}
// 生成缓存键名
// getSecondExpire converts parameter <duration> to int type in seconds.
//
// Note that there's some performance cost in type assertion here, but it's valuable.
func getSecondExpire(duration interface{}) int {
if d, ok := duration.(time.Duration); ok {
return int(d.Nanoseconds() / 1000000000)
} else {
return duration.(int)
}
}
// cacheKey produces the cache key for gcache.
func cacheKey(path string) string {
return "gf.gfcache:" + path
}

View File

@ -0,0 +1,92 @@
// 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.
// go test *.go -bench=".*" -benchmem
package gfcache_test
import (
"io/ioutil"
"os"
"testing"
"time"
"github.com/gogf/gf/g/os/gfcache"
"github.com/gogf/gf/g/os/gfile"
"github.com/gogf/gf/g/test/gtest"
)
func TestGetContents(t *testing.T) {
gtest.Case(t, func() {
var f *os.File
var err error
fileName := "test"
strTest := "123"
if !gfile.Exists(fileName) {
f, err = ioutil.TempFile("", fileName)
if err != nil {
t.Error("create file fail")
}
}
defer f.Close()
defer os.Remove(f.Name())
if gfile.Exists(f.Name()) {
f, err = gfile.OpenFile(f.Name(), os.O_APPEND|os.O_WRONLY, os.ModeAppend)
if err != nil {
t.Error("file open fail", err)
}
err = gfile.PutContents(f.Name(), strTest)
if err != nil {
t.Error("write error", err)
}
cache := gfcache.GetContents(f.Name(), 1)
gtest.Assert(cache, strTest)
}
})
gtest.Case(t, func() {
var f *os.File
var err error
fileName := "test2"
strTest := "123"
if !gfile.Exists(fileName) {
f, err = ioutil.TempFile("", fileName)
if err != nil {
t.Error("create file fail")
}
}
defer f.Close()
defer os.Remove(f.Name())
if gfile.Exists(f.Name()) {
cache := gfcache.GetContents(f.Name())
f, err = gfile.OpenFile(f.Name(), os.O_APPEND|os.O_WRONLY, os.ModeAppend)
if err != nil {
t.Error("file open fail", err)
}
err = gfile.PutContents(f.Name(), strTest)
if err != nil {
t.Error("write error", err)
}
gtest.Assert(cache, "")
time.Sleep(100 * time.Millisecond)
}
})
}

View File

@ -8,9 +8,10 @@
package glog
import (
"io"
"github.com/gogf/gf/g/internal/cmdenv"
"github.com/gogf/gf/g/os/grpool"
"io"
)
const (
@ -115,19 +116,19 @@ func GetFlags() int {
return logger.GetFlags()
}
// PrintBacktrace prints the caller backtrace,
// the optional parameter <skip> specify the skipped backtrace offset from the end point.
func PrintBacktrace(skip ...int) {
logger.PrintBacktrace(skip...)
// PrintStack prints the caller stack,
// the optional parameter <skip> specify the skipped stack offset from the end point.
func PrintStack(skip ...int) {
logger.PrintStack(skip...)
}
// GetBacktrace returns the caller backtrace content,
// the optional parameter <skip> specify the skipped backtrace offset from the end point.
func GetBacktrace(skip ...int) string {
return logger.GetBacktrace(skip...)
// GetStack returns the caller stack content,
// the optional parameter <skip> specify the skipped stack offset from the end point.
func GetStack(skip ...int) string {
return logger.GetStack(skip...)
}
// SetBacktrace enables/disables the backtrace feature in failure logging outputs.
func SetBacktrace(enabled bool) {
logger.SetBacktrace(enabled)
// SetStack enables/disables the stack feature in failure logging outputs.
func SetStack(enabled bool) {
logger.SetStack(enabled)
}

View File

@ -94,13 +94,13 @@ func Debugfln(format string, v ...interface{}) {
}
// Notice prints the logging content with [NOTI] header and newline.
// It also prints caller backtrace info if backtrace feature is enabled.
// It also prints caller stack info if stack feature is enabled.
func Notice(v ...interface{}) {
logger.Notice(v...)
}
// Noticef prints the logging content with [NOTI] header, custom format and newline.
// It also prints caller backtrace info if backtrace feature is enabled.
// It also prints caller stack info if stack feature is enabled.
func Noticef(format string, v ...interface{}) {
logger.Noticef(format, v...)
}
@ -112,13 +112,13 @@ func Noticefln(format string, v ...interface{}) {
}
// Warning prints the logging content with [WARN] header and newline.
// It also prints caller backtrace info if backtrace feature is enabled.
// It also prints caller stack info if stack feature is enabled.
func Warning(v ...interface{}) {
logger.Warning(v...)
}
// Warningf prints the logging content with [WARN] header, custom format and newline.
// It also prints caller backtrace info if backtrace feature is enabled.
// It also prints caller stack info if stack feature is enabled.
func Warningf(format string, v ...interface{}) {
logger.Warningf(format, v...)
}
@ -130,13 +130,13 @@ func Warningfln(format string, v ...interface{}) {
}
// Error prints the logging content with [ERRO] header and newline.
// It also prints caller backtrace info if backtrace feature is enabled.
// It also prints caller stack info if stack feature is enabled.
func Error(v ...interface{}) {
logger.Error(v...)
}
// Errorf prints the logging content with [ERRO] header, custom format and newline.
// It also prints caller backtrace info if backtrace feature is enabled.
// It also prints caller stack info if stack feature is enabled.
func Errorf(format string, v ...interface{}) {
logger.Errorf(format, v...)
}
@ -148,13 +148,13 @@ func Errorfln(format string, v ...interface{}) {
}
// Critical prints the logging content with [CRIT] header and newline.
// It also prints caller backtrace info if backtrace feature is enabled.
// It also prints caller stack info if stack feature is enabled.
func Critical(v ...interface{}) {
logger.Critical(v...)
}
// Criticalf prints the logging content with [CRIT] header, custom format and newline.
// It also prints caller backtrace info if backtrace feature is enabled.
// It also prints caller stack info if stack feature is enabled.
func Criticalf(format string, v ...interface{}) {
logger.Criticalf(format, v...)
}

View File

@ -46,16 +46,16 @@ func Level(level int) *Logger {
}
// Skip is a chaining function,
// which sets backtrace skip for the current logging content output.
// which sets stack skip for the current logging content output.
// It also affects the caller file path checks when line number printing enabled.
func Skip(skip int) *Logger {
return logger.Skip(skip)
}
// Backtrace is a chaining function,
// which sets backtrace options for the current logging content output .
func Backtrace(enabled bool, skip ...int) *Logger {
return logger.Backtrace(enabled, skip...)
// Stack is a chaining function,
// which sets stack options for the current logging content output .
func Stack(enabled bool, skip ...int) *Logger {
return logger.Stack(enabled, skip...)
}
// StdPrint is a chaining function,

View File

@ -10,17 +10,18 @@ import (
"bytes"
"errors"
"fmt"
"io"
"os"
"strings"
"time"
"github.com/gogf/gf/g/internal/debug"
"github.com/gogf/gf/g/os/gfile"
"github.com/gogf/gf/g/os/gfpool"
"github.com/gogf/gf/g/os/gtime"
"github.com/gogf/gf/g/text/gregex"
"github.com/gogf/gf/g/util/gconv"
"io"
"os"
"regexp"
"runtime"
"strings"
"time"
)
type Logger struct {
@ -31,8 +32,8 @@ type Logger struct {
file string // Format for logging file.
level int // Output level.
prefix string // Prefix string for every logging content.
btSkip int // Skip count for backtrace.
btStatus int // Backtrace status(1: enabled - default; 0: disabled)
stSkip int // Skip count for stack.
stStatus int // Stack status(1: enabled - default; 0: disabled)
headerPrint bool // Print header or not(true in default).
stdoutPrint bool // Output to stdout or not(true in default).
}
@ -42,6 +43,7 @@ const (
gDEFAULT_FILE_POOL_FLAGS = os.O_CREATE | os.O_WRONLY | os.O_APPEND
gDEFAULT_FPOOL_PERM = os.FileMode(0666)
gDEFAULT_FPOOL_EXPIRE = 60000
gPATH_FILTER_KEY = "/g/os/glog/glog"
)
const (
@ -54,25 +56,13 @@ const (
F_TIME_STD = F_TIME_DATE | F_TIME_MILLI
)
var (
// Default line break.
ln = "\n"
)
func init() {
// Initialize log line breaks depending on underlying os.
if runtime.GOOS == "windows" {
ln = "\r\n"
}
}
// New creates and returns a custom logger.
func New() *Logger {
logger := &Logger{
file: gDEFAULT_FILE_FORMAT,
flags: F_TIME_STD,
level: LEVEL_ALL,
btStatus: 1,
stStatus: 1,
headerPrint: true,
stdoutPrint: true,
}
@ -126,18 +116,18 @@ func (l *Logger) GetFlags() int {
return l.flags
}
// SetBacktrace enables/disables the backtrace feature in failure logging outputs.
func (l *Logger) SetBacktrace(enabled bool) {
// SetStack enables/disables the stack feature in failure logging outputs.
func (l *Logger) SetStack(enabled bool) {
if enabled {
l.btStatus = 1
l.stStatus = 1
} else {
l.btStatus = 0
l.stStatus = 0
}
}
// SetBacktraceSkip sets the backtrace offset from the end point.
func (l *Logger) SetBacktraceSkip(skip int) {
l.btSkip = skip
// SetStackSkip sets the stack offset from the end point.
func (l *Logger) SetStackSkip(skip int) {
l.stSkip = skip
}
// SetWriter sets the customized logging <writer> for logging.
@ -254,10 +244,10 @@ func (l *Logger) print(std io.Writer, lead string, value ...interface{}) {
// Caller path.
callerPath := ""
if l.flags&F_FILE_LONG > 0 {
callerPath = l.getLongFile() + ": "
callerPath = debug.CallerWithFilter(gPATH_FILTER_KEY, l.stSkip) + ": "
}
if l.flags&F_FILE_SHORT > 0 {
callerPath = gfile.Basename(l.getLongFile()) + ": "
callerPath = gfile.Basename(debug.CallerWithFilter(gPATH_FILTER_KEY, l.stSkip)) + ": "
}
if len(callerPath) > 0 {
buffer.WriteString(callerPath)
@ -267,13 +257,31 @@ func (l *Logger) print(std io.Writer, lead string, value ...interface{}) {
buffer.WriteString(l.prefix + " ")
}
}
for k, v := range value {
if k > 0 {
buffer.WriteByte(' ')
// Convert value to string.
tempStr := ""
valueStr := ""
for _, v := range value {
if err, ok := v.(error); ok {
tempStr = fmt.Sprintf("%+v", err)
} else {
tempStr = gconv.String(v)
}
if len(valueStr) > 0 {
if valueStr[len(valueStr)-1] == '\n' {
// Remove one blank line(\n\n).
if tempStr[0] == '\n' {
valueStr += tempStr[1:]
} else {
valueStr += tempStr
}
} else {
valueStr += " " + tempStr
}
} else {
valueStr = tempStr
}
buffer.WriteString(gconv.String(v))
}
buffer.WriteString(ln)
buffer.WriteString(valueStr + "\n")
if l.flags&F_ASYNC > 0 {
asyncPool.Add(func() {
l.printToWriter(std, buffer)
@ -305,16 +313,16 @@ func (l *Logger) printToWriter(std io.Writer, buffer *bytes.Buffer) {
}
}
// printStd prints content <s> without backtrace.
// printStd prints content <s> without stack.
func (l *Logger) printStd(lead string, value ...interface{}) {
l.print(os.Stdout, lead, value...)
}
// printStd prints content <s> with backtrace check.
// printStd prints content <s> with stack check.
func (l *Logger) printErr(lead string, value ...interface{}) {
if l.btStatus == 1 {
if s := l.GetBacktrace(); s != "" {
value = append(value, ln+"Backtrace:"+ln+s)
if l.stStatus == 1 {
if s := l.GetStack(); s != "" {
value = append(value, "\nStack:\n"+s)
}
}
// In matter of sequence, do not use stderr here, but use the same stdout.
@ -326,76 +334,22 @@ func (l *Logger) format(format string, value ...interface{}) string {
return fmt.Sprintf(format, value...)
}
// PrintBacktrace prints the caller backtrace,
// the optional parameter <skip> specify the skipped backtrace offset from the end point.
func (l *Logger) PrintBacktrace(skip ...int) {
if s := l.GetBacktrace(skip...); s != "" {
l.Println("Backtrace:" + ln + s)
// PrintStack prints the caller stack,
// the optional parameter <skip> specify the skipped stack offset from the end point.
func (l *Logger) PrintStack(skip ...int) {
if s := l.GetStack(skip...); s != "" {
l.Println("Stack:\n" + s)
} else {
l.Println()
}
}
// GetBacktrace returns the caller backtrace content,
// the optional parameter <skip> specify the skipped backtrace offset from the end point.
func (l *Logger) GetBacktrace(skip ...int) string {
customSkip := 0
// GetStack returns the caller stack content,
// the optional parameter <skip> specify the skipped stack offset from the end point.
func (l *Logger) GetStack(skip ...int) string {
number := 1
if len(skip) > 0 {
customSkip = skip[0]
number = skip[0] + 1
}
backtrace := ""
from := 0
// Find the caller position exclusive of the glog file.
for i := 0; i < 1000; i++ {
if _, file, _, ok := runtime.Caller(i); ok {
if !gregex.IsMatchString("/g/os/glog/glog.+$", file) {
from = i
break
}
}
}
// Find the true caller file path using custom skip.
index := 1
goRoot := runtime.GOROOT()
if goRoot != "" {
goRoot = strings.Replace(goRoot, "\\", "/", -1)
goRoot = regexp.QuoteMeta(goRoot)
}
for i := from + customSkip + l.btSkip; i < 1000; i++ {
if _, file, cline, ok := runtime.Caller(i); ok && len(file) > 2 {
if (goRoot == "" || !gregex.IsMatchString("^"+goRoot, file)) && !gregex.IsMatchString(`<autogenerated>`, file) {
backtrace += fmt.Sprintf(`%d. %s:%d%s`, index, file, cline, ln)
index++
}
} else {
break
}
}
return backtrace
}
// getLongFile returns the absolute file path along with its line number of the caller.
func (l *Logger) getLongFile() string {
from := 0
// Find the caller position exclusive of the glog file.
for i := 0; i < 1000; i++ {
if _, file, _, ok := runtime.Caller(i); ok {
if !gregex.IsMatchString("/g/os/glog/glog.+$", file) {
from = i
break
}
}
}
// Find the true caller file path using custom skip.
goRoot := runtime.GOROOT()
for i := from + l.btSkip; i < 1000; i++ {
if _, file, line, ok := runtime.Caller(i); ok && len(file) > 2 {
if (goRoot == "" || !gregex.IsMatchString("^"+goRoot, file)) && !gregex.IsMatchString(`<autogenerated>`, file) {
return fmt.Sprintf(`%s:%d`, file, line)
}
} else {
break
}
}
return ""
return debug.StackWithFilter(gPATH_FILTER_KEY, number)
}

View File

@ -116,7 +116,7 @@ func (l *Logger) Debugfln(format string, v ...interface{}) {
}
// Notice prints the logging content with [NOTI] header and newline.
// It also prints caller backtrace info if backtrace feature is enabled.
// It also prints caller stack info if stack feature is enabled.
func (l *Logger) Notice(v ...interface{}) {
if l.checkLevel(LEVEL_NOTI) {
l.printErr("[NOTI]", v...)
@ -124,7 +124,7 @@ func (l *Logger) Notice(v ...interface{}) {
}
// Noticef prints the logging content with [NOTI] header, custom format and newline.
// It also prints caller backtrace info if backtrace feature is enabled.
// It also prints caller stack info if stack feature is enabled.
func (l *Logger) Noticef(format string, v ...interface{}) {
if l.checkLevel(LEVEL_NOTI) {
l.printErr("[NOTI]", l.format(format, v...))
@ -140,7 +140,7 @@ func (l *Logger) Noticefln(format string, v ...interface{}) {
}
// Warning prints the logging content with [WARN] header and newline.
// It also prints caller backtrace info if backtrace feature is enabled.
// It also prints caller stack info if stack feature is enabled.
func (l *Logger) Warning(v ...interface{}) {
if l.checkLevel(LEVEL_WARN) {
l.printErr("[WARN]", v...)
@ -148,7 +148,7 @@ func (l *Logger) Warning(v ...interface{}) {
}
// Warningf prints the logging content with [WARN] header, custom format and newline.
// It also prints caller backtrace info if backtrace feature is enabled.
// It also prints caller stack info if stack feature is enabled.
func (l *Logger) Warningf(format string, v ...interface{}) {
if l.checkLevel(LEVEL_WARN) {
l.printErr("[WARN]", l.format(format, v...))
@ -164,7 +164,7 @@ func (l *Logger) Warningfln(format string, v ...interface{}) {
}
// Error prints the logging content with [ERRO] header and newline.
// It also prints caller backtrace info if backtrace feature is enabled.
// It also prints caller stack info if stack feature is enabled.
func (l *Logger) Error(v ...interface{}) {
if l.checkLevel(LEVEL_ERRO) {
l.printErr("[ERRO]", v...)
@ -172,7 +172,7 @@ func (l *Logger) Error(v ...interface{}) {
}
// Errorf prints the logging content with [ERRO] header, custom format and newline.
// It also prints caller backtrace info if backtrace feature is enabled.
// It also prints caller stack info if stack feature is enabled.
func (l *Logger) Errorf(format string, v ...interface{}) {
if l.checkLevel(LEVEL_ERRO) {
l.printErr("[ERRO]", l.format(format, v...))
@ -188,7 +188,7 @@ func (l *Logger) Errorfln(format string, v ...interface{}) {
}
// Critical prints the logging content with [CRIT] header and newline.
// It also prints caller backtrace info if backtrace feature is enabled.
// It also prints caller stack info if stack feature is enabled.
func (l *Logger) Critical(v ...interface{}) {
if l.checkLevel(LEVEL_CRIT) {
l.printErr("[CRIT]", v...)
@ -196,7 +196,7 @@ func (l *Logger) Critical(v ...interface{}) {
}
// Criticalf prints the logging content with [CRIT] header, custom format and newline.
// It also prints caller backtrace info if backtrace feature is enabled.
// It also prints caller stack info if stack feature is enabled.
func (l *Logger) Criticalf(format string, v ...interface{}) {
if l.checkLevel(LEVEL_CRIT) {
l.printErr("[CRIT]", l.format(format, v...))

View File

@ -7,8 +7,9 @@
package glog
import (
"github.com/gogf/gf/g/os/gfile"
"io"
"github.com/gogf/gf/g/os/gfile"
)
// To is a chaining function,
@ -82,7 +83,7 @@ func (l *Logger) Level(level int) *Logger {
}
// Skip is a chaining function,
// which sets backtrace skip for the current logging content output.
// which sets stack skip for the current logging content output.
// It also affects the caller file path checks when line number printing enabled.
func (l *Logger) Skip(skip int) *Logger {
logger := (*Logger)(nil)
@ -91,22 +92,22 @@ func (l *Logger) Skip(skip int) *Logger {
} else {
logger = l
}
logger.SetBacktraceSkip(skip)
logger.SetStackSkip(skip)
return logger
}
// Backtrace is a chaining function,
// which sets backtrace options for the current logging content output .
func (l *Logger) Backtrace(enabled bool, skip ...int) *Logger {
// Stack is a chaining function,
// which sets stack options for the current logging content output .
func (l *Logger) Stack(enabled bool, skip ...int) *Logger {
logger := (*Logger)(nil)
if l.parent == nil {
logger = l.Clone()
} else {
logger = l
}
logger.SetBacktrace(enabled)
logger.SetStack(enabled)
if len(skip) > 0 {
logger.SetBacktraceSkip(skip[0])
logger.SetStackSkip(skip[0])
}
return logger
}

View File

@ -1,4 +1,4 @@
// Copyright 2018-2019 gf Author(https://github.com/gogf/gf). All Rights Reserved.
// 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,
@ -19,8 +19,8 @@ type Mutex struct {
state *gtype.Int32 // Indicates the state of mutex.
writer *gtype.Int32 // Pending writer count.
reader *gtype.Int32 // Pending reader count.
writing chan struct{} // Channel used for writer blocking.
reading chan struct{} // Channel used for reader blocking.
writing chan struct{} // Channel for writer blocking.
reading chan struct{} // Channel for reader blocking.
}
// New creates and returns a new mutex.
@ -34,74 +34,85 @@ func New() *Mutex {
}
}
// Lock locks the mutex for writing.
// If the lock is already locked for reading or writing,
// Lock blocks until the lock is available.
// Lock locks the mutex for writing purpose.
// If the mutex is already locked by another goroutine for reading or writing,
// it blocks until the lock is available.
func (m *Mutex) Lock() {
for {
// If there're no readers pending and writing lock currently,
// then do the writing lock checks.
if m.reader.Val() == 0 && m.state.Cas(0, -1) {
// Using CAS operation to get the writing lock atomically.
if m.state.Cas(0, -1) {
return
}
// It or else blocks to wait for the next chance.
m.writer.Add(1)
<-m.writing
m.writer.Add(-1)
}
}
// Unlock unlocks the writing lock.
// Unlock unlocks writing lock on the mutex.
// It is safe to be called multiple times if there's any locks or not.
func (m *Mutex) Unlock() {
if m.state.Cas(-1, 0) {
// Note that there might be more than one goroutines can enter this block.
var n int32
// Writing lock unlocks, then first check the blocked readers.
if n := m.reader.Val(); n > 0 {
// If there're readers blocked, unlock them with preemption.
for ; n > 0; n-- {
m.reading <- struct{}{}
// If there're readers blocked, it unlocks them with preemption.
for {
if n = m.reader.Val(); n > 0 {
if m.reader.Cas(n, 0) {
for ; n > 0; n-- {
m.reading <- struct{}{}
}
break
} else {
runtime.Gosched()
}
} else {
break
}
return
}
if m.writer.Val() > 0 {
m.writing <- struct{}{}
// It then also kindly feeds the pending writers with one chance.
if n = m.writer.Val(); n > 0 {
if m.writer.Cas(n, n-1) {
m.writing <- struct{}{}
}
}
}
}
// TryLock tries locking the mutex for writing.
// It returns true if success, or if there's a write/reading lock on the mutex,
// it returns false.
// TryLock tries locking the mutex for writing purpose.
// It returns true immediately if success, or if there's a write/reading lock on the mutex,
// it returns false immediately.
func (m *Mutex) TryLock() bool {
if m.reader.Val() == 0 && m.state.Cas(0, -1) {
if m.state.Cas(0, -1) {
return true
}
return false
}
// RLock locks mutex for reading.
// RLock locks mutex for reading purpose purpose.
// If the mutex is already locked for writing,
// It blocks until the lock is available.
// it blocks until the lock is available.
func (m *Mutex) RLock() {
var n int32
for {
// If there're no writing lock and pending writers currently,
// then do the reading lock checks.
if n = m.state.Val(); n >= 0 && m.writer.Val() == 0 {
if n = m.state.Val(); n >= 0 {
// If there's no writing lock currently, then do the reading lock checks.
if m.state.Cas(n, n+1) {
return
} else {
runtime.Gosched()
continue
}
} else {
// It or else pends the reader.
m.reader.Add(1)
<-m.reading
}
// Or else pending the reader.
m.reader.Add(1)
<-m.reading
m.reader.Add(-1)
}
}
// RUnlock unlocks the reading lock.
// RUnlock unlocks the reading lock on the mutex.
// It is safe to be called multiple times if there's any locks or not.
func (m *Mutex) RUnlock() {
var n int32
@ -116,19 +127,25 @@ func (m *Mutex) RUnlock() {
break
}
}
// Reading lock unlocks, then first check the blocked writers.
if n == 1 && m.writer.Val() > 0 {
// No readers blocked, then the writers can take place.
m.writing <- struct{}{}
// Reading lock unlocks, it then only check the blocked writers.
// Note that it is not necessary to check the pending readers here.
// <n == 1> means the state of mutex comes down to zero.
if n == 1 {
if n = m.writer.Val(); n > 0 {
if m.writer.Cas(n, n-1) {
m.writing <- struct{}{}
}
}
}
}
// TryRLock tries locking the mutex for reading.
// It returns true if success, or if there's a writing lock on the mutex, it returns false.
// TryRLock tries locking the mutex for reading purpose.
// It returns true immediately if success, or if there's a writing lock on the mutex,
// it returns false immediately.
func (m *Mutex) TryRLock() bool {
var n int32
for {
if n = m.state.Val(); n >= 0 && m.writer.Val() == 0 {
if n = m.state.Val(); n >= 0 {
if m.state.Cas(n, n+1) {
return true
} else {
@ -182,28 +199,29 @@ func (m *Mutex) RLockFunc(f func()) {
}
// TryLockFunc tries locking the mutex for writing with given callback function <f>.
// it returns true if success, or if there's a write/reading lock on the mutex,
// it returns false.
// it returns true immediately if success, or if there's a write/reading lock on the mutex,
// it returns false immediately.
//
// It releases the lock after <f> is executed.
func (m *Mutex) TryLockFunc(f func()) bool {
func (m *Mutex) TryLockFunc(f func()) (result bool) {
if m.TryLock() {
result = true
defer m.Unlock()
f()
return true
}
return false
return
}
// TryRLockFunc tries locking the mutex for reading with given callback function <f>.
// It returns true if success, or if there's a writing lock on the mutex, it returns false.
// It returns true immediately if success, or if there's a writing lock on the mutex,
// it returns false immediately.
//
// It releases the lock after <f> is executed.
func (m *Mutex) TryRLockFunc(f func()) bool {
func (m *Mutex) TryRLockFunc(f func()) (result bool) {
if m.TryRLock() {
result = true
defer m.RUnlock()
f()
return true
}
return false
return
}

View File

@ -20,48 +20,64 @@ var (
)
func Benchmark_Mutex_LockUnlock(b *testing.B) {
for i := 0; i < b.N; i++ {
mu.Lock()
mu.Unlock()
}
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
mu.Lock()
mu.Unlock()
}
})
}
func Benchmark_RWMutex_LockUnlock(b *testing.B) {
for i := 0; i < b.N; i++ {
rwmu.Lock()
rwmu.Unlock()
}
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
rwmu.Lock()
rwmu.Unlock()
}
})
}
func Benchmark_RWMutex_RLockRUnlock(b *testing.B) {
for i := 0; i < b.N; i++ {
rwmu.RLock()
rwmu.RUnlock()
}
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
rwmu.RLock()
rwmu.RUnlock()
}
})
}
func Benchmark_GMutex_LockUnlock(b *testing.B) {
for i := 0; i < b.N; i++ {
gmu.Lock()
gmu.Unlock()
}
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
gmu.Lock()
gmu.Unlock()
}
})
}
func Benchmark_GMutex_TryLock(b *testing.B) {
for i := 0; i < b.N; i++ {
gmu.TryLock()
}
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
if gmu.TryLock() {
defer gmu.Unlock()
}
}
})
}
func Benchmark_GMutex_RLockRUnlock(b *testing.B) {
for i := 0; i < b.N; i++ {
gmu.RLock()
gmu.RUnlock()
}
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
gmu.RLock()
gmu.RUnlock()
}
})
}
func Benchmark_GMutex_TryRLock(b *testing.B) {
for i := 0; i < b.N; i++ {
gmu.TryRLock()
}
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
gmu.TryRLock()
}
})
}

View File

@ -8,10 +8,10 @@ package gtime
import (
"bytes"
"github.com/gogf/gf/g/text/gregex"
"github.com/gogf/gf/g/text/gstr"
"strconv"
"strings"
"github.com/gogf/gf/g/text/gregex"
)
var (
@ -110,9 +110,15 @@ func (t *Time) Format(format string) string {
// 有几个转换的符号需要特殊处理
switch runes[i] {
case 'j':
buffer.WriteString(gstr.ReplaceByArray(result, []string{"=j=0", "", "=j=", ""}))
for _, s := range []string{"=j=0", "=j="} {
result = strings.Replace(result, s, "", -1)
}
buffer.WriteString(result)
case 'G':
buffer.WriteString(gstr.ReplaceByArray(result, []string{"=G=0", "", "=G=", ""}))
for _, s := range []string{"=G=0", "=G="} {
result = strings.Replace(result, s, "", -1)
}
buffer.WriteString(result)
case 'u':
buffer.WriteString(strings.Replace(result, "=u=.", "", -1))
case 'w':

View File

@ -6,7 +6,9 @@
package gtime
import "time"
import (
"time"
)
type Time struct {
time.Time
@ -115,6 +117,16 @@ func (t *Time) Add(d time.Duration) *Time {
return t
}
// 当前时间加上指定时间段(使用字符串格式)
func (t *Time) AddStr(duration string) error {
if d, err := time.ParseDuration(duration); err != nil {
return err
} else {
t.Time = t.Time.Add(d)
}
return nil
}
// 时区转换为指定的时区(通过time.Location)
func (t *Time) ToLocation(location *time.Location) *Time {
t.Time = t.Time.In(location)
@ -165,3 +177,8 @@ func (t *Time) Truncate(d time.Duration) *Time {
t.Time = t.Time.Truncate(d)
return t
}
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
func (t *Time) MarshalJSON() ([]byte, error) {
return []byte(`"` + t.String() + `"`), nil
}

View File

@ -25,7 +25,7 @@ import (
func Case(t *testing.T, f func()) {
defer func() {
if err := recover(); err != nil {
fmt.Fprintf(os.Stderr, "%v\n%s", err, getBacktrace())
fmt.Fprintf(os.Stderr, "%v\n%s", err, getStack())
t.Fail()
}
}()
@ -256,7 +256,7 @@ func Error(message ...interface{}) {
// Fatal prints <message> to stderr and exit the process.
func Fatal(message ...interface{}) {
fmt.Fprintf(os.Stderr, "[FATAL] %s\n%s", fmt.Sprint(message...), getBacktrace())
fmt.Fprintf(os.Stderr, "[FATAL] %s\n%s", fmt.Sprint(message...), getStack())
os.Exit(1)
}
@ -299,14 +299,14 @@ func compareMap(value, expect interface{}) error {
return nil
}
// getBacktrace returns the caller backtrace content from getBacktrace.
// The parameter <skip> indicates the skip count of the caller backtrace from getBacktrace.
func getBacktrace(skip ...int) string {
// getStack returns the caller stack content from getStack.
// The parameter <skip> indicates the skip count of the caller stack from getStack.
func getStack(skip ...int) string {
customSkip := 0
if len(skip) > 0 {
customSkip = skip[0]
}
backtrace := ""
stack := ""
index := 1
from := 0
// Ignore current gtest lines and find the beginning index of caller file.
@ -337,13 +337,13 @@ func getBacktrace(skip ...int) string {
continue
}
}
backtrace += fmt.Sprintf(`%d. %s:%d%s`, index, file, cline, "\n")
stack += fmt.Sprintf(`%d. %s:%d%s`, index, file, cline, "\n")
index++
} else {
break
}
}
return backtrace
return stack
}
// isNil checks whether <value> is nil.

View File

@ -10,12 +10,17 @@ package gstr
import (
"bytes"
"fmt"
"github.com/gogf/gf/g/util/grand"
"math"
"strconv"
"strings"
"unicode"
"unicode/utf8"
"github.com/gogf/gf/g/internal/strutils"
"github.com/gogf/gf/g/util/gconv"
"github.com/gogf/gf/g/util/grand"
)
// Replace returns a copy of the string <origin>
@ -93,10 +98,7 @@ func ReplaceIByArray(origin string, array []string) string {
// 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 = Replace(origin, k, v)
}
return origin
return strutils.ReplaceByMap(origin, replaces)
}
// ReplaceIByMap returns a copy of <origin>,
@ -120,13 +122,7 @@ func ToUpper(s string) string {
// 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
return strutils.UcFirst(s)
}
// LcFirst returns a copy of the string s with the first letter mapped to its lower case.
@ -147,40 +143,25 @@ func UcWords(str string) string {
// 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
return strutils.IsLetterLower(b)
}
// 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
return strutils.IsLetterUpper(b)
}
// 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
return strutils.IsNumeric(s)
}
// SubStr returns a portion of string <str> specified by the <start> and <length> parameters.
func SubStr(str string, start int, length ...int) (substr string) {
// 将字符串的转换成[]rune
// Converting to []rune to support unicode.
rs := []rune(str)
lth := len(rs)
// 简单的越界判断
// Simple border checks.
if start < 0 {
start = 0
}
@ -197,7 +178,6 @@ func SubStr(str string, start int, length ...int) (substr string) {
if end > lth {
end = lth
}
// 返回子串
return string(rs[start:end])
}
@ -468,6 +448,14 @@ func Join(array []string, sep string) string {
return strings.Join(array, sep)
}
// JoinAny concatenates the elements of a to create a single string. The separator string
// sep is placed between elements in the resulting string.
//
// The parameter <array> can be any type of slice.
func JoinAny(array interface{}, sep string) string {
return strings.Join(gconv.Strings(array), sep)
}
// Explode splits string <str> by a string <delimiter>, to an array.
// See http://php.net/manual/en/function.explode.php.
func Explode(delimiter, str string) []string {

View File

@ -11,7 +11,7 @@ import (
"strings"
"github.com/gogf/gf/g/internal/empty"
"github.com/gogf/gf/g/text/gstr"
"github.com/gogf/gf/g/internal/strutils"
)
// Map converts any variable <value> to map[string]interface{}.
@ -116,7 +116,7 @@ func Map(value interface{}, tags ...string) map[string]interface{} {
for i := 0; i < rv.NumField(); i++ {
// Only convert the public attributes.
fieldName := rt.Field(i).Name
if !gstr.IsLetterUpper(fieldName[0]) {
if !strutils.IsLetterUpper(fieldName[0]) {
continue
}
name = ""

View File

@ -11,7 +11,7 @@ import (
"fmt"
"reflect"
"github.com/gogf/gf/g/text/gstr"
"github.com/gogf/gf/g/internal/strutils"
)
// SliceInt is alias of Ints.
@ -357,7 +357,7 @@ func Interfaces(i interface{}) []interface{} {
rt := rv.Type()
for i := 0; i < rv.NumField(); i++ {
// Only public attributes.
if !gstr.IsLetterUpper(rt.Field(i).Name[0]) {
if !strutils.IsLetterUpper(rt.Field(i).Name[0]) {
continue
}
array = append(array, rv.Field(i).Interface())
@ -447,6 +447,10 @@ func doStructs(params interface{}, pointer interface{}, deep bool, mapping ...ma
}
switch kind {
case reflect.Slice, reflect.Array:
// If <params> is an empty slice, no conversion.
if rv.Len() == 0 {
return nil
}
array := reflect.MakeSlice(pointerRt.Elem(), rv.Len(), rv.Len())
itemType := array.Index(0).Type()
for i := 0; i < rv.Len(); i++ {

View File

@ -13,7 +13,7 @@ import (
"strings"
"github.com/gogf/gf/g/internal/structs"
"github.com/gogf/gf/g/text/gstr"
"github.com/gogf/gf/g/internal/strutils"
)
// Struct maps the params key-value pairs to the corresponding struct object's properties.
@ -92,7 +92,7 @@ func Struct(params interface{}, pointer interface{}, mapping ...map[string]strin
elemType := elem.Type()
for i := 0; i < elem.NumField(); i++ {
// Only do converting to public attributes.
if !gstr.IsLetterUpper(elemType.Field(i).Name[0]) {
if !strutils.IsLetterUpper(elemType.Field(i).Name[0]) {
continue
}
attrMap[elemType.Field(i).Name] = struct{}{}
@ -100,8 +100,8 @@ func Struct(params interface{}, pointer interface{}, mapping ...map[string]strin
for mapK, mapV := range paramsMap {
name := ""
for _, checkName := range []string{
gstr.UcFirst(mapK),
gstr.ReplaceByMap(mapK, map[string]string{
strutils.UcFirst(mapK),
strutils.ReplaceByMap(mapK, map[string]string{
"_": "",
"-": "",
" ": "",
@ -118,7 +118,7 @@ func Struct(params interface{}, pointer interface{}, mapping ...map[string]strin
name = value
break
}
if strings.EqualFold(checkName, gstr.Replace(value, "_", "")) {
if strings.EqualFold(checkName, strings.Replace(value, "_", "", -1)) {
name = value
break
}
@ -159,7 +159,7 @@ func StructDeep(params interface{}, pointer interface{}, mapping ...map[string]s
rt := rv.Type()
for i := 0; i < rv.NumField(); i++ {
// Only do converting to public attributes.
if !gstr.IsLetterUpper(rt.Field(i).Name[0]) {
if !strutils.IsLetterUpper(rt.Field(i).Name[0]) {
continue
}
trv := rv.Field(i)

View File

@ -7,9 +7,10 @@
package gconv
import (
"github.com/gogf/gf/g/os/gtime"
"github.com/gogf/gf/g/text/gstr"
"time"
"github.com/gogf/gf/g/internal/strutils"
"github.com/gogf/gf/g/os/gtime"
)
// Time converts <i> to time.Time.
@ -22,7 +23,7 @@ func Time(i interface{}, format ...string) time.Time {
// If <i> is numeric, then it converts <i> as nanoseconds.
func Duration(i interface{}) time.Duration {
s := String(i)
if !gstr.IsNumeric(s) {
if !strutils.IsNumeric(s) {
d, _ := time.ParseDuration(s)
return d
}
@ -43,7 +44,7 @@ func GTime(i interface{}, format ...string) *gtime.Time {
t, _ := gtime.StrToTimeFormat(s, format[0])
return t
}
if gstr.IsNumeric(s) {
if strutils.IsNumeric(s) {
return gtime.NewFromTimeStamp(Int64(s))
} else {
t, _ := gtime.StrToTime(s)

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