Compare commits

..

84 Commits

Author SHA1 Message Date
fd92fd2409 version updates 2022-03-25 14:13:49 +08:00
a2823a501e upgrade otel to v1.0.0 for compatibility 2022-03-25 11:06:23 +08:00
cc60bc9dab fix issue printing to stdout using fmt.Fprintf for package glog 2021-09-01 16:14:17 +08:00
425ca8aa3e improve process comminucation folder creating for package gproc 2021-09-01 15:02:58 +08:00
73d2b7ed06 fix issue #1387 2021-08-30 22:26:02 +08:00
0048e2c8c4 improve package gstr 2021-08-30 14:29:04 +08:00
549adf6487 improve unit testing case for package gdb 2021-08-27 20:35:35 +08:00
b5502c5580 revert and use 2021-08-27 20:26:07 +08:00
46bf669cdd Merge branch 'master' of https://github.com/gogf/gf 2021-08-27 20:16:41 +08:00
fa7a3e987d improve With feature for package gdb 2021-08-27 20:16:29 +08:00
b316b9c073 example updates for package gcache 2021-08-27 01:01:41 +08:00
5b57c35522 comment updates for package gcache 2021-08-27 00:01:15 +08:00
e0a3233ea3 improve package gcron 2021-08-26 00:11:27 +08:00
1b588b14d8 Merge branch 'master' of https://github.com/gogf/gf 2021-08-25 23:46:16 +08:00
2c839db941 Merge pull request #1372 from gzhmt-developer/master
fix tcp http demo error content-length
2021-08-25 23:42:30 +08:00
f9eaa8f930 fix issue of nested transaction missing in function operations for package gdb 2021-08-25 22:31:59 +08:00
59397fd8a5 add buildin function add/minus/times/divide for package gview 2021-08-25 20:00:53 +08:00
6be582355c improve DB.Close for package gdb 2021-08-25 10:58:29 +08:00
257897763d Merge branch 'master' of https://github.com/gogf/gf 2021-08-25 00:54:47 +08:00
29d50994ae add function Close for DB for package gdb 2021-08-24 22:59:25 +08:00
b129ee3f04 fix issue in unit testing case for package ghttp 2021-08-24 21:35:49 +08:00
a47699bf6e fix issue in unit testing case for package gjson 2021-08-24 21:34:14 +08:00
66b8b591df fix issue in missing import for package gcode 2021-08-24 21:31:15 +08:00
7b47fe96dd update version of third-party redigo from v2.0.0+incompatible to v1.8.5 2021-08-24 21:22:58 +08:00
abc8e62d58 add package gcode and improve error implements for package gerror 2021-08-24 21:18:59 +08:00
ef36cf8446 fix issue in example of package gjson 2021-08-20 23:15:48 +08:00
c5345239fc improve logging content for package gcron 2021-08-20 15:49:45 +08:00
a229960218 add context id feature for package gctx/glog 2021-08-20 15:44:08 +08:00
43180ef239 improve package gcron 2021-08-20 14:09:15 +08:00
e10240bd7c improve package gcron 2021-08-20 11:52:22 +08:00
a0ef6fce81 add bacth registering function Map for package ghttp 2021-08-19 20:59:08 +08:00
314bee2b4e remove context key printing into logging content for context feature of package glog 2021-08-19 15:35:41 +08:00
050c93ee8d Merge branch 'develop' of https://github.com/gogf/gf 2021-08-19 14:12:04 +08:00
feefcc98ef improve condition parameter of struct by sequence for package gdb 2021-08-19 14:11:28 +08:00
8f4ce91361 improve condition parameter of struct by sequence for package gdb 2021-08-19 14:09:31 +08:00
f03f56ba4e improve gconv.Struct for map attribute converting 2021-08-19 11:28:25 +08:00
457d552fa0 add UnmarshalJSON interface support for package gconv 2021-08-17 16:51:29 +08:00
be2fcf7f57 add UnmarshalJSON interface support for package gconv 2021-08-17 16:30:47 +08:00
ad6c4f5245 improve session storage for package gsession 2021-08-16 21:47:45 +08:00
db621e38d9 improve ServeFile/ServeFileDownload for package ghttp 2021-08-16 20:30:58 +08:00
170af1ab00 Merge pull request #1374 from wangle201210/develop
add func SetWriterColorEnable for file log color
2021-08-15 23:52:10 +08:00
31ef4e7b5a fmt 2021-08-13 13:27:43 +08:00
f56f689970 fmt 2021-08-13 13:24:28 +08:00
17b73e7cb3 add func SetWriterColorEnable for file log color 2021-08-13 13:20:33 +08:00
903f29a824 add OmitNil feature for package gdb 2021-08-13 11:28:47 +08:00
12f2193963 improve handler feature for package glog 2021-08-12 22:14:31 +08:00
13ecbc263e improve handler feature for package glog 2021-08-12 21:58:06 +08:00
7b9888c004 add FieldCount/FieldSum/FieldMax/FieldMin/FieldAvg functions for gdb.Model 2021-08-12 19:42:44 +08:00
91cd4f96f0 Update gtcp_conn.go 2021-08-12 10:50:46 +08:00
95a44122dd comment update for package glog 2021-08-12 10:47:05 +08:00
2bd76dfdde go fmt code 2021-08-12 09:35:57 +08:00
26895294f9 Merge pull request #1 from gzhmt-developer/ansionfor-patch-1
fix tcp demo error content-length
2021-08-12 00:23:04 +08:00
4cd4559784 fix tcp demo error content-length 2021-08-12 00:21:36 +08:00
3fc96f2bd0 add package gctx 2021-08-11 13:20:00 +08:00
91dd9e2bf9 auto update value of time field if given time value is zero for package gdb 2021-08-10 20:30:32 +08:00
9c5642f141 fix issue in border value checks for gstr.CompareVersion/CompareVersionGo 2021-08-10 19:29:54 +08:00
00f0a743fc add function TestDataContent for package gdebug 2021-08-10 17:20:15 +08:00
232751e3db improve context key and caller path&func outputs for logger of package glog 2021-08-10 15:46:40 +08:00
1c600d5b20 revert ORM logger from interface to glog.Logger 2021-08-10 15:36:15 +08:00
9bc3b44a61 version updates 2021-08-09 19:30:16 +08:00
83729f18ad Merge branch 'develop' 2021-08-09 19:29:46 +08:00
fdb6e70322 comment update for package glog 2021-08-09 19:29:11 +08:00
efaf3d591c comment update for package glog 2021-08-09 19:25:23 +08:00
ef50eb6d6b comment update for package ghttp 2021-08-09 19:16:37 +08:00
fb5a1f2306 Merge pull request #1369 from wangle201210/develop
fix print log on windows
2021-08-09 19:16:24 +08:00
9d4382d12e fmt 2021-08-08 16:58:15 +08:00
9ee76ecc93 fix print log on windows 2021-08-08 16:23:25 +08:00
0d2ca48d16 update comment and standardize const naming for package gproc 2021-08-08 14:05:27 +08:00
f1857df5e2 comment update for package glog 2021-08-08 13:56:26 +08:00
fc1dfb7ff9 comment update;standardize const naming for package gtcp/gudp 2021-08-08 13:10:21 +08:00
dc407bf293 merge develop 2021-08-07 12:04:31 +08:00
4eb057227c add SizeFormat function for package gfile; add more internal logging for rotation feature for pacakge glog 2021-08-07 11:59:30 +08:00
9cd944fd77 improve time maintaining feature for package gdb 2021-08-07 11:01:15 +08:00
cd3593182a improve package gerror/ghttp for error code handling 2021-08-07 10:44:57 +08:00
0f6820df9e add more unit testing case for package gtimer 2021-08-06 14:13:37 +08:00
0e158903c2 improve package gtimer for priority checks 2021-08-06 12:08:49 +08:00
214d0513e5 Merge branch 'develop' of https://github.com/gogf/gf into develop 2021-08-05 19:15:31 +08:00
75ca866991 fix issue in ghttp.Server 2021-08-05 19:15:18 +08:00
f22aa1b5d6 Merge pull request #1361 from jroam/fixbug-garray 2021-08-05 13:09:31 +08:00
00d7ee93b2 improve package gerror 2021-08-05 11:40:31 +08:00
537cbf983e edit some function annotations 2021-08-04 23:35:29 +08:00
4c54b1cfbc improve errcode feature for package gerror 2021-08-04 20:50:45 +08:00
248e6ff134 bugfix garray Sort 2021-08-01 15:19:48 +08:00
9be92cc3d4 garray代码规范化 2021-07-28 22:18:06 +08:00
256 changed files with 3831 additions and 2407 deletions

View File

@ -0,0 +1,18 @@
package main
import (
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/net/ghttp"
)
func main() {
s := g.Server()
s.BindHandler("/", func(r *ghttp.Request) {
r.Response.RedirectTo("/login")
})
s.BindHandler("/login", func(r *ghttp.Request) {
r.Response.Writeln("Login First")
})
s.SetPort(8199)
s.Run()
}

View File

@ -0,0 +1,15 @@
package main
import (
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/net/ghttp"
)
func main() {
s := g.Server()
s.BindHandler("/", func(r *ghttp.Request) {
r.Response.ServeFile("test.txt")
})
s.SetPort(8999)
s.Run()
}

View File

@ -0,0 +1,15 @@
package main
import (
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/net/ghttp"
)
func main() {
s := g.Server()
s.BindHandler("/", func(r *ghttp.Request) {
r.Response.ServeFileDownload("test.txt")
})
s.SetPort(8999)
s.Run()
}

View File

@ -0,0 +1 @@
test

View File

@ -16,7 +16,7 @@ func main() {
}
defer conn.Close()
if err := conn.Send([]byte("GET / HTTP/1.1\n\n")); err != nil {
if err := conn.Send([]byte("GET / HTTP/1.1\r\n\r\n")); err != nil {
panic(err)
}
@ -30,13 +30,14 @@ func main() {
array := bytes.Split(data, []byte(": "))
// 获得页面内容长度
if contentLength == 0 && len(array) == 2 && bytes.EqualFold([]byte("Content-Length"), array[0]) {
contentLength = gconv.Int(array[1])
// http 以\r\n换行需要把\r也去掉
contentLength = gconv.Int(string(array[1][:len(array[1])-1]))
}
header = append(header, data...)
header = append(header, '\n')
}
// header读取完毕读取文本内容
if contentLength > 0 && len(data) == 0 {
// header读取完毕读取文本内容, 1为\r
if contentLength > 0 && len(data) == 1 {
content, _ = conn.Recv(contentLength)
break
}

View File

@ -0,0 +1,28 @@
package main
import (
"fmt"
"github.com/gogf/gf/os/gcache"
"github.com/gogf/gf/os/gctx"
"time"
)
func main() {
var (
ch = make(chan struct{}, 0)
ctx = gctx.New()
key = `key`
value = `value`
)
for i := 0; i < 10; i++ {
go func(index int) {
<-ch
_, _ = gcache.Ctx(ctx).GetOrSetFuncLock(key, func() (interface{}, error) {
fmt.Println(index, "entered")
return value, nil
}, 0)
}(i)
}
close(ch)
time.Sleep(time.Second)
}

View File

@ -0,0 +1,19 @@
package main
import (
"fmt"
"github.com/gogf/gf/os/gcache"
"github.com/gogf/gf/os/gctx"
)
func main() {
var (
ctx = gctx.New()
key1 int32 = 1
key2 float64 = 1
value = `value`
)
_ = gcache.Ctx(ctx).Set(key1, value, 0)
fmt.Println(gcache.Ctx(ctx).Get(key1))
fmt.Println(gcache.Ctx(ctx).Get(key2))
}

View File

@ -0,0 +1,29 @@
package main
import (
"fmt"
"github.com/gogf/gf/os/gcache"
"github.com/gogf/gf/os/gctx"
)
func main() {
type User struct {
Id int
Name string
Site string
}
var (
ctx = gctx.New()
user *User
key = `UserKey`
value = &User{
Id: 1,
Name: "GoFrame",
Site: "https://goframe.org",
}
)
_ = gcache.Ctx(ctx).Set(key, value, 0)
v, _ := gcache.Ctx(ctx).GetVar(key)
_ = v.Scan(&user)
fmt.Printf(`%#v`, user)
}

View File

@ -0,0 +1,14 @@
package main
import (
"context"
"github.com/gogf/gf/frame/g"
)
func main() {
g.Log().SetCtxKeys("TraceId", "SpanId", "Test")
ctx := context.WithValue(context.Background(), "TraceId", "1234567890")
ctx = context.WithValue(ctx, "SpanId", "abcdefg")
g.Log().Ctx(ctx).Print(1, 2, 3)
}

View File

@ -6,7 +6,7 @@ import (
)
func main() {
err := glog.SetConfigWithMap(g.Map{
err := g.Log().SetConfigWithMap(g.Map{
"prefix": "[TEST]",
})
if err != nil {

View File

@ -1,14 +1,13 @@
package main
import (
"github.com/gogf/gf/frame/g"
"time"
"github.com/gogf/gf/os/glog"
)
func main() {
for i := 0; i < 10; i++ {
glog.Async().Print("async log", i)
g.Log().Async().Print("async log", i)
}
time.Sleep(time.Second)
}

View File

@ -1,15 +1,14 @@
package main
import (
"github.com/gogf/gf/frame/g"
"time"
"github.com/gogf/gf/os/glog"
)
func main() {
glog.SetAsync(true)
g.Log().SetAsync(true)
for i := 0; i < 10; i++ {
glog.Async().Print("async log", i)
g.Log().Print("async log", i)
}
time.Sleep(time.Second)
}

View File

@ -3,13 +3,12 @@ package main
import (
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/os/gfile"
"github.com/gogf/gf/os/glog"
)
func main() {
path := "/tmp/glog-cat"
glog.SetPath(path)
glog.Stdout(false).Cat("cat1").Cat("cat2").Println("test")
g.Log().SetPath(path)
g.Log().Stdout(false).Cat("cat1").Cat("cat2").Println("test")
list, err := gfile.ScanDir(path, "*", true)
g.Dump(err)
g.Dump(list)

View File

@ -1,14 +0,0 @@
package main
import (
"context"
"github.com/gogf/gf/os/glog"
)
func main() {
glog.SetCtxKeys("Trace-Id", "Span-Id", "Test")
ctx := context.WithValue(context.Background(), "Trace-Id", "1234567890")
ctx = context.WithValue(ctx, "Span-Id", "abcdefg")
glog.Ctx(ctx).Print(1, 2, 3)
}

View File

@ -1,19 +1,19 @@
package main
import (
"github.com/gogf/gf/frame/g"
"time"
"github.com/gogf/gf/os/glog"
"github.com/gogf/gf/os/gtime"
"github.com/gogf/gf/os/gtimer"
)
func main() {
gtimer.SetTimeout(3*time.Second, func() {
glog.SetDebug(false)
g.Log().SetDebug(false)
})
for {
glog.Debug(gtime.Datetime())
g.Log().Debug(gtime.Datetime())
time.Sleep(time.Second)
}
}

View File

@ -3,24 +3,25 @@ package main
import (
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/os/gfile"
"github.com/gogf/gf/os/glog"
)
// 设置日志等级
func main() {
l := glog.New()
path := "/tmp/glog"
l.SetPath(path)
l.SetStdoutPrint(false)
g.Log().SetPath(path)
g.Log().SetStdoutPrint(false)
// 使用默认文件名称格式
l.Println("标准文件名称格式,使用当前时间时期")
g.Log().Println("标准文件名称格式,使用当前时间时期")
// 通过SetFile设置文件名称格式
l.SetFile("stdout.log")
l.Println("设置日志输出文件名称格式为同一个文件")
g.Log().SetFile("stdout.log")
g.Log().Println("设置日志输出文件名称格式为同一个文件")
// 链式操作设置文件名称格式
l.File("stderr.log").Println("支持链式操作")
l.File("error-{Ymd}.log").Println("文件名称支持带gtime日期格式")
l.File("access-{Ymd}.log").Println("文件名称支持带gtime日期格式")
g.Log().File("stderr.log").Println("支持链式操作")
g.Log().File("error-{Ymd}.log").Println("文件名称支持带gtime日期格式")
g.Log().File("access-{Ymd}.log").Println("文件名称支持带gtime日期格式")
list, err := gfile.ScanDir(path, "*")
g.Dump(err)

View File

@ -1,15 +1,15 @@
package main
import (
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/os/glog"
)
func main() {
l := glog.New()
l.SetFlags(glog.F_TIME_TIME | glog.F_FILE_SHORT)
l.Println("time and short line number")
l.SetFlags(glog.F_TIME_MILLI | glog.F_FILE_LONG)
l.Println("time with millisecond and long line number")
l.SetFlags(glog.F_TIME_STD | glog.F_FILE_LONG)
l.Println("standard time format and long line number")
g.Log().SetFlags(glog.F_TIME_TIME | glog.F_FILE_SHORT)
g.Log().Println("time and short line number")
g.Log().SetFlags(glog.F_TIME_MILLI | glog.F_FILE_LONG)
g.Log().Println("time with millisecond and long line number")
g.Log().SetFlags(glog.F_TIME_STD | glog.F_FILE_LONG)
g.Log().Println("standard time format and long line number")
}

View File

@ -2,9 +2,9 @@ package main
import (
"errors"
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/errors/gerror"
"github.com/gogf/gf/os/glog"
)
func MakeError() error {
@ -18,8 +18,8 @@ func MakeGError() error {
func TestGError() {
err1 := MakeError()
err2 := MakeGError()
glog.Error(err1)
glog.Error(err2)
g.Log().Error(err1)
g.Log().Error(err2)
}
func main() {

View File

@ -2,15 +2,14 @@ package main
import (
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/os/glog"
)
func main() {
glog.Debug(g.Map{"uid": 100, "name": "john"})
g.Log().Debug(g.Map{"uid": 100, "name": "john"})
type User struct {
Uid int `json:"uid"`
Name string `json:"name"`
}
glog.Debug(User{100, "john"})
g.Log().Debug(User{100, "john"})
}

View File

@ -1,13 +1,13 @@
package main
import (
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/os/glog"
)
// 设置日志等级过滤掉Info日志信息
func main() {
l := glog.New()
l.Info("info1")
l.SetLevel(glog.LEVEL_ALL ^ glog.LEVEL_INFO)
l.Info("info2")
g.Log().Info("info1")
g.Log().SetLevel(glog.LEVEL_ALL ^ glog.LEVEL_INFO)
g.Log().Info("info2")
}

View File

@ -1,11 +1,11 @@
package main
import (
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/os/glog"
)
func main() {
l := glog.New()
l.SetLevelPrefix(glog.LEVEL_DEBU, "debug")
l.Debug("test")
g.Log().SetLevelPrefix(glog.LEVEL_DEBU, "debug")
g.Log().Debug("test")
}

View File

@ -1,10 +1,10 @@
package main
import (
"github.com/gogf/gf/os/glog"
"github.com/gogf/gf/frame/g"
)
func main() {
glog.Line().Debug("this is the short file name with its line number")
glog.Line(true).Debug("lone file name with line number")
g.Log().Line().Debug("this is the short file name with its line number")
g.Log().Line(true).Debug("lone file name with line number")
}

View File

@ -1,12 +1,12 @@
package main
import (
"github.com/gogf/gf/os/glog"
"github.com/gogf/gf/frame/g"
)
func PrintLog(content string) {
glog.Skip(0).Line().Println("line number with skip:", content)
glog.Line(true).Println("line number without skip:", content)
g.Log().Skip(0).Line().Println("line number with skip:", content)
g.Log().Line(true).Println("line number without skip:", content)
}
func main() {

View File

@ -3,14 +3,13 @@ package main
import (
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/os/gfile"
"github.com/gogf/gf/os/glog"
)
// 设置日志输出路径
func main() {
path := "/tmp/glog"
glog.SetPath(path)
glog.Println("日志内容")
g.Log().SetPath(path)
g.Log().Println("日志内容")
list, err := gfile.ScanDir(path, "*")
g.Dump(err)
g.Dump(list)

View File

@ -1,18 +1,18 @@
package main
import (
"github.com/gogf/gf/frame/g"
"time"
"github.com/gogf/gf/os/glog"
"github.com/gogf/gf/os/gtime"
)
// 测试删除日志文件是否会重建日志文件
func main() {
path := "/Users/john/Temp/test"
glog.SetPath(path)
g.Log().SetPath(path)
for {
glog.Println(gtime.Now().String())
g.Log().Println(gtime.Now().String())
time.Sleep(time.Second)
}
}

View File

@ -1,12 +1,11 @@
package main
import (
"github.com/gogf/gf/os/glog"
"github.com/gogf/gf/frame/g"
)
func main() {
l := glog.New()
l.SetPrefix("[API]")
l.Println("hello world")
l.Error("error occurred")
g.Log().SetPrefix("[API]")
g.Log().Println("hello world")
g.Log().Error("error occurred")
}

View File

@ -2,15 +2,11 @@ package main
import (
"fmt"
"github.com/gogf/gf/os/glog"
"github.com/gogf/gf/frame/g"
)
func main() {
g.Log().PrintStack()
glog.PrintStack()
glog.New().PrintStack()
fmt.Println(glog.GetStack())
fmt.Println(glog.New().GetStack())
fmt.Println(g.Log().GetStack())
}

View File

@ -1,22 +1,23 @@
package main
import (
"github.com/gogf/gf/frame/g"
"sync"
"github.com/gogf/gf/os/glog"
)
func main() {
wg := sync.WaitGroup{}
c := make(chan struct{})
var (
wg = sync.WaitGroup{}
ch = make(chan struct{})
)
wg.Add(3000)
for i := 0; i < 3000; i++ {
go func() {
<-c
glog.Println("abcdefghijklmnopqrstuvwxyz1234567890")
<-ch
g.Log().Println("abcdefghijklmnopqrstuvwxyz1234567890")
wg.Done()
}()
}
close(c)
close(ch)
wg.Wait()
}

View File

@ -2,8 +2,8 @@ package main
import (
"fmt"
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/net/ghttp"
"github.com/gogf/gf/os/glog"
"github.com/gogf/gf/text/gregex"
)
@ -16,7 +16,7 @@ func (w *MyWriter) Write(p []byte) (n int, err error) {
s := string(p)
if gregex.IsMatchString(`\[(PANI|FATA)\]`, s) {
fmt.Println("SERIOUS ISSUE OCCURRED!! I'd better tell monitor in first time!")
ghttp.PostContent("http://monitor.mydomain.com", s)
g.Client().PostContent("http://monitor.mydomain.com", s)
}
return w.logger.Write(p)
}

View File

@ -0,0 +1,31 @@
package main
//import (
// "context"
// "github.com/gogf/gf/frame/g"
// "github.com/gogf/gf/os/glog"
// "github.com/robertkowalski/graylog-golang"
//)
//
//var greyLogClient = gelf.New(gelf.Config{
// GraylogPort: 80,
// GraylogHostname: "graylog-host.com",
// Connection: "wan",
// MaxChunkSizeWan: 42,
// MaxChunkSizeLan: 1337,
//})
//
//// LoggingGreyLogHandler is an example handler for logging content to remote GreyLog service.
//var LoggingGreyLogHandler glog.Handler = func(ctx context.Context, in *glog.HandlerInput) {
// in.Next()
// greyLogClient.Log(in.Buffer.String())
//}
//
//func main() {
// g.Log().SetHandlers(LoggingGreyLogHandler)
//
// g.Log().Debug("Debugging...")
// g.Log().Warning("It is warning info")
// g.Log().Error("Error occurs, please have a check")
// glog.Println("test log")
//}

View File

@ -0,0 +1,42 @@
package main
import (
"context"
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/internal/json"
"github.com/gogf/gf/os/glog"
"github.com/gogf/gf/text/gstr"
"os"
)
// JsonOutputsForLogger is for JSON marshaling in sequence.
type JsonOutputsForLogger struct {
Time string `json:"time"`
Level string `json:"level"`
Content string `json:"content"`
}
// LoggingJsonHandler is an example handler for logging JSON format content.
var LoggingJsonHandler glog.Handler = func(ctx context.Context, in *glog.HandlerInput) {
jsonForLogger := JsonOutputsForLogger{
Time: in.TimeFormat,
Level: in.LevelFormat,
Content: gstr.Trim(in.String()),
}
jsonBytes, err := json.Marshal(jsonForLogger)
if err != nil {
_, _ = os.Stderr.WriteString(err.Error())
return
}
in.Buffer.Write(jsonBytes)
in.Buffer.WriteString("\n")
in.Next()
}
func main() {
g.Log().SetHandlers(LoggingJsonHandler)
g.Log().Debug("Debugging...")
g.Log().Warning("It is warning info")
g.Log().Error("Error occurs, please have a check")
}

View File

@ -10,8 +10,10 @@ import (
// 内存锁基本使用
func main() {
key := "lock"
wg := sync.WaitGroup{}
var (
key = "lock"
wg = sync.WaitGroup{}
)
for i := 0; i < 10; i++ {
wg.Add(1)
go func(i int) {

View File

@ -11,12 +11,13 @@ import (
// 测试Locker是否会产生死锁
func main() {
l := gmlock.New()
wg := sync.WaitGroup{}
key := "test"
event := make(chan int)
number := 100000
var (
l = gmlock.New()
wg = sync.WaitGroup{}
key = "test"
event = make(chan int)
number = 100000
)
for i := 0; i < number; i++ {
wg.Add(1)
go func() {

View File

@ -1,23 +0,0 @@
package main
import (
"sync"
"github.com/gogf/gf/os/glog"
"github.com/gogf/gf/os/gmlock"
)
// 内存锁 - 给定过期时间
func main() {
key := "lock"
wg := sync.WaitGroup{}
for i := 0; i < 10; i++ {
wg.Add(1)
go func(i int) {
gmlock.Lock(key, 1000)
glog.Println(i)
wg.Done()
}(i)
}
wg.Wait()
}

View File

@ -1,95 +0,0 @@
package main
import (
"fmt"
"math/rand"
"sync"
"time"
"github.com/gogf/gf/os/gmlock"
)
// 测试是否会产生死锁
func main() {
mu := gmlock.NewMutex()
wg := sync.WaitGroup{}
event := make(chan int)
number := 100000
for i := 0; i < number; i++ {
wg.Add(1)
go func() {
<-event
mu.Lock()
//fmt.Println("get lock")
mu.Unlock()
wg.Done()
}()
}
for i := 0; i < number; i++ {
wg.Add(1)
go func() {
<-event
mu.RLock()
//fmt.Println("get rlock")
mu.RUnlock()
wg.Done()
}()
}
for i := 0; i < number; i++ {
wg.Add(1)
go func() {
<-event
if mu.TryLock() {
//fmt.Println("get lock")
mu.Unlock()
}
wg.Done()
}()
}
for i := 0; i < number; i++ {
wg.Add(1)
go func() {
<-event
if mu.TryRLock() {
//fmt.Println("get rlock")
mu.RUnlock()
}
wg.Done()
}()
}
for i := 0; i < number; i++ {
wg.Add(1)
go func() {
<-event
if mu.TryLock() {
// 模拟业务逻辑的随机处理间隔
time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond)
mu.Unlock()
}
wg.Done()
}()
}
for i := 0; i < number; i++ {
wg.Add(1)
go func() {
<-event
if mu.TryRLock() {
// 模拟业务逻辑的随机处理间隔
time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond)
mu.RUnlock()
}
wg.Done()
}()
}
// 使用chan作为事件发送测试指令让所有的goroutine同时执行
close(event)
wg.Wait()
fmt.Println("done!")
}

View File

@ -9,6 +9,7 @@ package garray
import (
"bytes"
"fmt"
"github.com/gogf/gf/errors/gcode"
"github.com/gogf/gf/errors/gerror"
"github.com/gogf/gf/internal/empty"
"github.com/gogf/gf/internal/json"
@ -36,7 +37,7 @@ func New(safe ...bool) *Array {
return NewArraySize(0, 0, safe...)
}
// See New.
// NewArray is alias of New, please see New.
func NewArray(safe ...bool) *Array {
return NewArraySize(0, 0, safe...)
}
@ -123,7 +124,7 @@ func (a *Array) Set(index int, value interface{}) error {
a.mu.Lock()
defer a.mu.Unlock()
if index < 0 || index >= len(a.array) {
return gerror.NewCodef(gerror.CodeInvalidParameter, "index %d out of array range %d", index, len(a.array))
return gerror.NewCodef(gcode.CodeInvalidParameter, "index %d out of array range %d", index, len(a.array))
}
a.array[index] = value
return nil
@ -176,7 +177,7 @@ func (a *Array) InsertBefore(index int, value interface{}) error {
a.mu.Lock()
defer a.mu.Unlock()
if index < 0 || index >= len(a.array) {
return gerror.NewCodef(gerror.CodeInvalidParameter, "index %d out of array range %d", index, len(a.array))
return gerror.NewCodef(gcode.CodeInvalidParameter, "index %d out of array range %d", index, len(a.array))
}
rear := append([]interface{}{}, a.array[index:]...)
a.array = append(a.array[0:index], value)
@ -189,7 +190,7 @@ func (a *Array) InsertAfter(index int, value interface{}) error {
a.mu.Lock()
defer a.mu.Unlock()
if index < 0 || index >= len(a.array) {
return gerror.NewCodef(gerror.CodeInvalidParameter, "index %d out of array range %d", index, len(a.array))
return gerror.NewCodef(gcode.CodeInvalidParameter, "index %d out of array range %d", index, len(a.array))
}
rear := append([]interface{}{}, a.array[index+1:]...)
a.array = append(a.array[0:index+1], value)
@ -422,7 +423,7 @@ func (a *Array) SubSlice(offset int, length ...int) []interface{} {
}
}
// See PushRight.
// Append is alias of PushRight, please See PushRight.
func (a *Array) Append(value ...interface{}) *Array {
a.PushRight(value...)
return a
@ -545,7 +546,7 @@ func (a *Array) Fill(startIndex int, num int, value interface{}) error {
a.mu.Lock()
defer a.mu.Unlock()
if startIndex < 0 || startIndex > len(a.array) {
return gerror.NewCodef(gerror.CodeInvalidParameter, "index %d out of array range %d", startIndex, len(a.array))
return gerror.NewCodef(gcode.CodeInvalidParameter, "index %d out of array range %d", startIndex, len(a.array))
}
for i := startIndex; i < startIndex+num; i++ {
if i > len(a.array)-1 {

View File

@ -9,6 +9,7 @@ package garray
import (
"bytes"
"fmt"
"github.com/gogf/gf/errors/gcode"
"github.com/gogf/gf/errors/gerror"
"github.com/gogf/gf/internal/json"
"math"
@ -104,7 +105,7 @@ func (a *IntArray) Set(index int, value int) error {
a.mu.Lock()
defer a.mu.Unlock()
if index < 0 || index >= len(a.array) {
return gerror.NewCodef(gerror.CodeInvalidParameter, "index %d out of array range %d", index, len(a.array))
return gerror.NewCodef(gcode.CodeInvalidParameter, "index %d out of array range %d", index, len(a.array))
}
a.array[index] = value
return nil
@ -149,10 +150,7 @@ func (a *IntArray) Sort(reverse ...bool) *IntArray {
defer a.mu.Unlock()
if len(reverse) > 0 && reverse[0] {
sort.Slice(a.array, func(i, j int) bool {
if a.array[i] < a.array[j] {
return false
}
return true
return a.array[i] >= a.array[j]
})
} else {
sort.Ints(a.array)
@ -175,7 +173,7 @@ func (a *IntArray) InsertBefore(index int, value int) error {
a.mu.Lock()
defer a.mu.Unlock()
if index < 0 || index >= len(a.array) {
return gerror.NewCodef(gerror.CodeInvalidParameter, "index %d out of array range %d", index, len(a.array))
return gerror.NewCodef(gcode.CodeInvalidParameter, "index %d out of array range %d", index, len(a.array))
}
rear := append([]int{}, a.array[index:]...)
a.array = append(a.array[0:index], value)
@ -188,7 +186,7 @@ func (a *IntArray) InsertAfter(index int, value int) error {
a.mu.Lock()
defer a.mu.Unlock()
if index < 0 || index >= len(a.array) {
return gerror.NewCodef(gerror.CodeInvalidParameter, "index %d out of array range %d", index, len(a.array))
return gerror.NewCodef(gcode.CodeInvalidParameter, "index %d out of array range %d", index, len(a.array))
}
rear := append([]int{}, a.array[index+1:]...)
a.array = append(a.array[0:index+1], value)
@ -427,7 +425,7 @@ func (a *IntArray) SubSlice(offset int, length ...int) []int {
}
}
// See PushRight.
// Append is alias of PushRight,please See PushRight.
func (a *IntArray) Append(value ...int) *IntArray {
a.mu.Lock()
a.array = append(a.array, value...)
@ -559,7 +557,7 @@ func (a *IntArray) Fill(startIndex int, num int, value int) error {
a.mu.Lock()
defer a.mu.Unlock()
if startIndex < 0 || startIndex > len(a.array) {
return gerror.NewCodef(gerror.CodeInvalidParameter, "index %d out of array range %d", startIndex, len(a.array))
return gerror.NewCodef(gcode.CodeInvalidParameter, "index %d out of array range %d", startIndex, len(a.array))
}
for i := startIndex; i < startIndex+num; i++ {
if i > len(a.array)-1 {

View File

@ -8,6 +8,7 @@ package garray
import (
"bytes"
"github.com/gogf/gf/errors/gcode"
"github.com/gogf/gf/errors/gerror"
"github.com/gogf/gf/internal/json"
"github.com/gogf/gf/text/gstr"
@ -90,7 +91,7 @@ func (a *StrArray) Set(index int, value string) error {
a.mu.Lock()
defer a.mu.Unlock()
if index < 0 || index >= len(a.array) {
return gerror.NewCodef(gerror.CodeInvalidParameter, "index %d out of array range %d", index, len(a.array))
return gerror.NewCodef(gcode.CodeInvalidParameter, "index %d out of array range %d", index, len(a.array))
}
a.array[index] = value
return nil
@ -136,10 +137,7 @@ func (a *StrArray) Sort(reverse ...bool) *StrArray {
defer a.mu.Unlock()
if len(reverse) > 0 && reverse[0] {
sort.Slice(a.array, func(i, j int) bool {
if strings.Compare(a.array[i], a.array[j]) < 0 {
return false
}
return true
return strings.Compare(a.array[i], a.array[j]) >= 0
})
} else {
sort.Strings(a.array)
@ -162,7 +160,7 @@ func (a *StrArray) InsertBefore(index int, value string) error {
a.mu.Lock()
defer a.mu.Unlock()
if index < 0 || index >= len(a.array) {
return gerror.NewCodef(gerror.CodeInvalidParameter, "index %d out of array range %d", index, len(a.array))
return gerror.NewCodef(gcode.CodeInvalidParameter, "index %d out of array range %d", index, len(a.array))
}
rear := append([]string{}, a.array[index:]...)
a.array = append(a.array[0:index], value)
@ -175,7 +173,7 @@ func (a *StrArray) InsertAfter(index int, value string) error {
a.mu.Lock()
defer a.mu.Unlock()
if index < 0 || index >= len(a.array) {
return gerror.NewCodef(gerror.CodeInvalidParameter, "index %d out of array range %d", index, len(a.array))
return gerror.NewCodef(gcode.CodeInvalidParameter, "index %d out of array range %d", index, len(a.array))
}
rear := append([]string{}, a.array[index+1:]...)
a.array = append(a.array[0:index+1], value)
@ -414,7 +412,7 @@ func (a *StrArray) SubSlice(offset int, length ...int) []string {
}
}
// See PushRight.
// Append is alias of PushRight,please See PushRight.
func (a *StrArray) Append(value ...string) *StrArray {
a.mu.Lock()
a.array = append(a.array, value...)
@ -562,7 +560,7 @@ func (a *StrArray) Fill(startIndex int, num int, value string) error {
a.mu.Lock()
defer a.mu.Unlock()
if startIndex < 0 || startIndex > len(a.array) {
return gerror.NewCodef(gerror.CodeInvalidParameter, "index %d out of array range %d", startIndex, len(a.array))
return gerror.NewCodef(gcode.CodeInvalidParameter, "index %d out of array range %d", startIndex, len(a.array))
}
for i := startIndex; i < startIndex+num; i++ {
if i > len(a.array)-1 {

View File

@ -156,9 +156,7 @@ func (a *SortedArray) Append(values ...interface{}) *SortedArray {
if cmp > 0 {
index++
}
rear := append([]interface{}{}, a.array[index:]...)
a.array = append(a.array[0:index], value)
a.array = append(a.array, rear...)
a.array = append(a.array[:index], append([]interface{}{value}, a.array[index:]...)...)
}
return a
}
@ -453,7 +451,7 @@ func (a *SortedArray) binSearch(value interface{}, lock bool) (index int, result
mid := 0
cmp := -2
for min <= max {
mid = min + int((max-min)/2)
mid = min + (max-min)/2
cmp = a.getComparator()(value, a.array[mid])
switch {
case cmp < 0:

View File

@ -0,0 +1,39 @@
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
package garray_test
import (
"github.com/gogf/gf/container/garray"
"testing"
)
type anySortedArrayItem struct {
priority int64
value interface{}
}
var (
anyArray = garray.NewArray()
anySortedArray = garray.NewSortedArray(func(a, b interface{}) int {
return int(a.(anySortedArrayItem).priority - b.(anySortedArrayItem).priority)
})
)
func Benchmark_AnyArray_Add(b *testing.B) {
for i := 0; i < b.N; i++ {
anyArray.Append(i)
}
}
func Benchmark_AnySortedArray_Add(b *testing.B) {
for i := 0; i < b.N; i++ {
anySortedArray.Add(anySortedArrayItem{
priority: int64(i),
value: i,
})
}
}

View File

@ -8,6 +8,7 @@
package gpool
import (
"github.com/gogf/gf/errors/gcode"
"github.com/gogf/gf/errors/gerror"
"time"
@ -66,7 +67,7 @@ func New(ttl time.Duration, newFunc NewFunc, expireFunc ...ExpireFunc) *Pool {
// Put puts an item to pool.
func (p *Pool) Put(value interface{}) error {
if p.closed.Val() {
return gerror.NewCode(gerror.CodeInvalidOperation, "pool is closed")
return gerror.NewCode(gcode.CodeInvalidOperation, "pool is closed")
}
item := &poolItem{
value: value,
@ -117,7 +118,7 @@ func (p *Pool) Get() (interface{}, error) {
if p.NewFunc != nil {
return p.NewFunc()
}
return nil, gerror.NewCode(gerror.CodeInvalidOperation, "pool is empty")
return nil, gerror.NewCode(gcode.CodeInvalidOperation, "pool is empty")
}
// Size returns the count of available items of pool.

View File

@ -22,8 +22,8 @@ type Var struct {
safe bool // Concurrent safe or not.
}
// New creates and returns a new Var with given <value>.
// The optional parameter <safe> specifies whether Var is used in concurrent-safety,
// New creates and returns a new Var with given `value`.
// The optional parameter `safe` specifies whether Var is used in concurrent-safety,
// which is false in default.
func New(value interface{}, safe ...bool) *Var {
v := Var{}
@ -36,8 +36,8 @@ func New(value interface{}, safe ...bool) *Var {
return &v
}
// Create creates and returns a new Var with given <value>.
// The optional parameter <safe> specifies whether Var is used in concurrent-safety,
// Create creates and returns a new Var with given `value`.
// The optional parameter `safe` specifies whether Var is used in concurrent-safety,
// which is false in default.
func Create(value interface{}, safe ...bool) Var {
v := Var{}
@ -55,7 +55,7 @@ func (v *Var) Clone() *Var {
return New(v.Val(), v.safe)
}
// Set sets <value> to <v>, and returns the old value.
// Set sets `value` to `v`, and returns the old value.
func (v *Var) Set(value interface{}) (old interface{}) {
if v.safe {
if t, ok := v.value.(*gtype.Interface); ok {
@ -68,7 +68,7 @@ func (v *Var) Set(value interface{}) (old interface{}) {
return
}
// Val returns the current value of <v>.
// Val returns the current value of `v`.
func (v *Var) Val() interface{} {
if v == nil {
return nil
@ -86,96 +86,96 @@ func (v *Var) Interface() interface{} {
return v.Val()
}
// Bytes converts and returns <v> as []byte.
// Bytes converts and returns `v` as []byte.
func (v *Var) Bytes() []byte {
return gconv.Bytes(v.Val())
}
// String converts and returns <v> as string.
// String converts and returns `v` as string.
func (v *Var) String() string {
return gconv.String(v.Val())
}
// Bool converts and returns <v> as bool.
// Bool converts and returns `v` as bool.
func (v *Var) Bool() bool {
return gconv.Bool(v.Val())
}
// Int converts and returns <v> as int.
// Int converts and returns `v` as int.
func (v *Var) Int() int {
return gconv.Int(v.Val())
}
// Int8 converts and returns <v> as int8.
// Int8 converts and returns `v` as int8.
func (v *Var) Int8() int8 {
return gconv.Int8(v.Val())
}
// Int16 converts and returns <v> as int16.
// Int16 converts and returns `v` as int16.
func (v *Var) Int16() int16 {
return gconv.Int16(v.Val())
}
// Int32 converts and returns <v> as int32.
// Int32 converts and returns `v` as int32.
func (v *Var) Int32() int32 {
return gconv.Int32(v.Val())
}
// Int64 converts and returns <v> as int64.
// Int64 converts and returns `v` as int64.
func (v *Var) Int64() int64 {
return gconv.Int64(v.Val())
}
// Uint converts and returns <v> as uint.
// Uint converts and returns `v` as uint.
func (v *Var) Uint() uint {
return gconv.Uint(v.Val())
}
// Uint8 converts and returns <v> as uint8.
// Uint8 converts and returns `v` as uint8.
func (v *Var) Uint8() uint8 {
return gconv.Uint8(v.Val())
}
// Uint16 converts and returns <v> as uint16.
// Uint16 converts and returns `v` as uint16.
func (v *Var) Uint16() uint16 {
return gconv.Uint16(v.Val())
}
// Uint32 converts and returns <v> as uint32.
// Uint32 converts and returns `v` as uint32.
func (v *Var) Uint32() uint32 {
return gconv.Uint32(v.Val())
}
// Uint64 converts and returns <v> as uint64.
// Uint64 converts and returns `v` as uint64.
func (v *Var) Uint64() uint64 {
return gconv.Uint64(v.Val())
}
// Float32 converts and returns <v> as float32.
// Float32 converts and returns `v` as float32.
func (v *Var) Float32() float32 {
return gconv.Float32(v.Val())
}
// Float64 converts and returns <v> as float64.
// Float64 converts and returns `v` as float64.
func (v *Var) Float64() float64 {
return gconv.Float64(v.Val())
}
// Time converts and returns <v> as time.Time.
// The parameter <format> specifies the format of the time string using gtime,
// Time converts and returns `v` as time.Time.
// The parameter `format` specifies the format of the time string using gtime,
// eg: Y-m-d H:i:s.
func (v *Var) Time(format ...string) time.Time {
return gconv.Time(v.Val(), format...)
}
// Duration converts and returns <v> as time.Duration.
// If value of <v> is string, then it uses time.ParseDuration for conversion.
// Duration converts and returns `v` as time.Duration.
// If value of `v` is string, then it uses time.ParseDuration for conversion.
func (v *Var) Duration() time.Duration {
return gconv.Duration(v.Val())
}
// GTime converts and returns <v> as *gtime.Time.
// The parameter <format> specifies the format of the time string using gtime,
// GTime converts and returns `v` as *gtime.Time.
// The parameter `format` specifies the format of the time string using gtime,
// eg: Y-m-d H:i:s.
func (v *Var) GTime(format ...string) *gtime.Time {
return gconv.GTime(v.Val(), format...)

View File

@ -10,15 +10,15 @@ import (
"github.com/gogf/gf/util/gutil"
)
// ListItemValues retrieves and returns the elements of all item struct/map with key <key>.
// Note that the parameter <list> should be type of slice which contains elements of map or struct,
// ListItemValues retrieves and returns the elements of all item struct/map with key `key`.
// Note that the parameter `list` should be type of slice which contains elements of map or struct,
// or else it returns an empty slice.
func (v *Var) ListItemValues(key interface{}) (values []interface{}) {
return gutil.ListItemValues(v.Val(), key)
}
// ListItemValuesUnique retrieves and returns the unique elements of all struct/map with key <key>.
// Note that the parameter <list> should be type of slice which contains elements of map or struct,
// ListItemValuesUnique retrieves and returns the unique elements of all struct/map with key `key`.
// Note that the parameter `list` should be type of slice which contains elements of map or struct,
// or else it returns an empty slice.
func (v *Var) ListItemValuesUnique(key string) []interface{} {
return gutil.ListItemValuesUnique(v.Val(), key)

View File

@ -8,7 +8,7 @@ package gvar
import "github.com/gogf/gf/util/gconv"
// Map converts and returns <v> as map[string]interface{}.
// Map converts and returns `v` as map[string]interface{}.
func (v *Var) Map(tags ...string) map[string]interface{} {
return gconv.Map(v.Val(), tags...)
}
@ -18,12 +18,12 @@ func (v *Var) MapStrAny() map[string]interface{} {
return v.Map()
}
// MapStrStr converts and returns <v> as map[string]string.
// MapStrStr converts and returns `v` as map[string]string.
func (v *Var) MapStrStr(tags ...string) map[string]string {
return gconv.MapStrStr(v.Val(), tags...)
}
// MapStrVar converts and returns <v> as map[string]Var.
// MapStrVar converts and returns `v` as map[string]Var.
func (v *Var) MapStrVar(tags ...string) map[string]*Var {
m := v.Map(tags...)
if len(m) > 0 {
@ -36,17 +36,17 @@ func (v *Var) MapStrVar(tags ...string) map[string]*Var {
return nil
}
// MapDeep converts and returns <v> as map[string]interface{} recursively.
// MapDeep converts and returns `v` as map[string]interface{} recursively.
func (v *Var) MapDeep(tags ...string) map[string]interface{} {
return gconv.MapDeep(v.Val(), tags...)
}
// MapStrStrDeep converts and returns <v> as map[string]string recursively.
// MapStrStrDeep converts and returns `v` as map[string]string recursively.
func (v *Var) MapStrStrDeep(tags ...string) map[string]string {
return gconv.MapStrStrDeep(v.Val(), tags...)
}
// MapStrVarDeep converts and returns <v> as map[string]*Var recursively.
// MapStrVarDeep converts and returns `v` as map[string]*Var recursively.
func (v *Var) MapStrVarDeep(tags ...string) map[string]*Var {
m := v.MapDeep(tags...)
if len(m) > 0 {
@ -59,27 +59,33 @@ func (v *Var) MapStrVarDeep(tags ...string) map[string]*Var {
return nil
}
// Maps converts and returns <v> as map[string]string.
// Maps converts and returns `v` as map[string]string.
// See gconv.Maps.
func (v *Var) Maps(tags ...string) []map[string]interface{} {
return gconv.Maps(v.Val(), tags...)
}
// MapToMap converts any map type variable <params> to another map type variable <pointer>.
// MapsDeep converts `value` to []map[string]interface{} recursively.
// See gconv.MapsDeep.
func (v *Var) MapsDeep(tags ...string) []map[string]interface{} {
return gconv.MapsDeep(v.Val(), tags...)
}
// MapToMap converts any map type variable `params` to another map type variable `pointer`.
// See gconv.MapToMap.
func (v *Var) MapToMap(pointer interface{}, mapping ...map[string]string) (err error) {
return gconv.MapToMap(v.Val(), pointer, mapping...)
}
// MapToMaps converts any map type variable <params> to another map type variable <pointer>.
// MapToMaps converts any map type variable `params` to another map type variable `pointer`.
// See gconv.MapToMaps.
func (v *Var) MapToMaps(pointer interface{}, mapping ...map[string]string) (err error) {
return gconv.MapToMaps(v.Val(), pointer, mapping...)
}
// MapToMapsDeep converts any map type variable <params> to another map type variable
// <pointer> recursively.
// MapToMapsDeep converts any map type variable `params` to another map type variable
// `pointer` recursively.
// See gconv.MapToMapsDeep.
func (v *Var) MapToMapsDeep(pointer interface{}, mapping ...map[string]string) (err error) {
return gconv.MapToMapsDeep(v.Val(), pointer, mapping...)
return gconv.MapToMaps(v.Val(), pointer, mapping...)
}

View File

@ -8,22 +8,22 @@ package gvar
import "github.com/gogf/gf/util/gconv"
// Ints converts and returns <v> as []int.
// Ints converts and returns `v` as []int.
func (v *Var) Ints() []int {
return gconv.Ints(v.Val())
}
// Int64s converts and returns <v> as []int64.
// Int64s converts and returns `v` as []int64.
func (v *Var) Int64s() []int64 {
return gconv.Int64s(v.Val())
}
// Uints converts and returns <v> as []uint.
// Uints converts and returns `v` as []uint.
func (v *Var) Uints() []uint {
return gconv.Uints(v.Val())
}
// Uint64s converts and returns <v> as []uint64.
// Uint64s converts and returns `v` as []uint64.
func (v *Var) Uint64s() []uint64 {
return gconv.Uint64s(v.Val())
}
@ -33,22 +33,22 @@ func (v *Var) Floats() []float64 {
return gconv.Floats(v.Val())
}
// Float32s converts and returns <v> as []float32.
// Float32s converts and returns `v` as []float32.
func (v *Var) Float32s() []float32 {
return gconv.Float32s(v.Val())
}
// Float64s converts and returns <v> as []float64.
// Float64s converts and returns `v` as []float64.
func (v *Var) Float64s() []float64 {
return gconv.Float64s(v.Val())
}
// Strings converts and returns <v> as []string.
// Strings converts and returns `v` as []string.
func (v *Var) Strings() []string {
return gconv.Strings(v.Val())
}
// Interfaces converts and returns <v> as []interfaces{}.
// Interfaces converts and returns `v` as []interfaces{}.
func (v *Var) Interfaces() []interface{} {
return gconv.Interfaces(v.Val())
}
@ -63,7 +63,7 @@ func (v *Var) Array() []interface{} {
return v.Interfaces()
}
// Vars converts and returns <v> as []Var.
// Vars converts and returns `v` as []Var.
func (v *Var) Vars() []*Var {
array := gconv.Interfaces(v.Val())
if len(array) == 0 {

View File

@ -10,44 +10,48 @@ import (
"github.com/gogf/gf/util/gconv"
)
// Struct maps value of <v> to <pointer>.
// The parameter <pointer> should be a pointer to a struct instance.
// The parameter <mapping> is used to specify the key-to-attribute mapping rules.
// Struct maps value of `v` to `pointer`.
// The parameter `pointer` should be a pointer to a struct instance.
// The parameter `mapping` is used to specify the key-to-attribute mapping rules.
func (v *Var) Struct(pointer interface{}, mapping ...map[string]string) error {
return gconv.Struct(v.Val(), pointer, mapping...)
}
// Struct maps value of <v> to <pointer> recursively.
// The parameter <pointer> should be a pointer to a struct instance.
// The parameter <mapping> is used to specify the key-to-attribute mapping rules.
// StructDeep maps value of `v` to `pointer` recursively.
// The parameter `pointer` should be a pointer to a struct instance.
// The parameter `mapping` is used to specify the key-to-attribute mapping rules.
// Deprecated, use Struct instead.
func (v *Var) StructDeep(pointer interface{}, mapping ...map[string]string) error {
return gconv.StructDeep(v.Val(), pointer, mapping...)
}
// Structs converts and returns <v> as given struct slice.
// Structs converts and returns `v` as given struct slice.
func (v *Var) Structs(pointer interface{}, mapping ...map[string]string) error {
return gconv.Structs(v.Val(), pointer, mapping...)
}
// StructsDeep converts and returns <v> as given struct slice recursively.
// StructsDeep converts and returns `v` as given struct slice recursively.
// Deprecated, use Struct instead.
func (v *Var) StructsDeep(pointer interface{}, mapping ...map[string]string) error {
return gconv.StructsDeep(v.Val(), pointer, mapping...)
}
// Scan automatically calls Struct or Structs function according to the type of parameter
// <pointer> to implement the converting.
// It calls function Struct if <pointer> is type of *struct/**struct to do the converting.
// It calls function Structs if <pointer> is type of *[]struct/*[]*struct to do the converting.
// `pointer` to implement the converting.
//
// It calls function Struct if `pointer` is type of *struct/**struct to do the converting.
// It calls function Structs if `pointer` is type of *[]struct/*[]*struct to do the converting.
func (v *Var) Scan(pointer interface{}, mapping ...map[string]string) error {
return gconv.Scan(v.Val(), pointer, mapping...)
}
// ScanDeep automatically calls StructDeep or StructsDeep function according to the type of
// parameter <pointer> to implement the converting.
// It calls function StructDeep if <pointer> is type of *struct/**struct to do the converting.
// It calls function StructsDeep if <pointer> is type of *[]struct/*[]*struct to do the converting.
// parameter `pointer` to implement the converting.
//
// It calls function StructDeep if `pointer` is type of *struct/**struct to do the converting.
// It calls function StructsDeep if `pointer` is type of *[]struct/*[]*struct to do the converting.
//
// Deprecated, use Scan instead.
func (v *Var) ScanDeep(pointer interface{}, mapping ...map[string]string) error {
return gconv.ScanDeep(v.Val(), pointer, mapping...)
}

View File

@ -11,6 +11,7 @@ import (
"bytes"
"crypto/aes"
"crypto/cipher"
"github.com/gogf/gf/errors/gcode"
"github.com/gogf/gf/errors/gerror"
)
@ -63,7 +64,7 @@ func DecryptCBC(cipherText []byte, key []byte, iv ...[]byte) ([]byte, error) {
}
blockSize := block.BlockSize()
if len(cipherText) < blockSize {
return nil, gerror.NewCode(gerror.CodeInvalidParameter, "cipherText too short")
return nil, gerror.NewCode(gcode.CodeInvalidParameter, "cipherText too short")
}
ivValue := ([]byte)(nil)
if len(iv) > 0 {
@ -72,7 +73,7 @@ func DecryptCBC(cipherText []byte, key []byte, iv ...[]byte) ([]byte, error) {
ivValue = []byte(IVDefaultValue)
}
if len(cipherText)%blockSize != 0 {
return nil, gerror.NewCode(gerror.CodeInvalidParameter, "cipherText is not a multiple of the block size")
return nil, gerror.NewCode(gcode.CodeInvalidParameter, "cipherText is not a multiple of the block size")
}
blockModel := cipher.NewCBCDecrypter(block, ivValue)
plainText := make([]byte, len(cipherText))
@ -93,22 +94,22 @@ func PKCS5Padding(src []byte, blockSize int) []byte {
func PKCS5UnPadding(src []byte, blockSize int) ([]byte, error) {
length := len(src)
if blockSize <= 0 {
return nil, gerror.NewCode(gerror.CodeInvalidParameter, "invalid blocklen")
return nil, gerror.NewCode(gcode.CodeInvalidParameter, "invalid blocklen")
}
if length%blockSize != 0 || length == 0 {
return nil, gerror.NewCode(gerror.CodeInvalidParameter, "invalid data len")
return nil, gerror.NewCode(gcode.CodeInvalidParameter, "invalid data len")
}
unpadding := int(src[length-1])
if unpadding > blockSize || unpadding == 0 {
return nil, gerror.NewCode(gerror.CodeInvalidParameter, "invalid padding")
return nil, gerror.NewCode(gcode.CodeInvalidParameter, "invalid padding")
}
padding := src[length-unpadding:]
for i := 0; i < unpadding; i++ {
if padding[i] != byte(unpadding) {
return nil, gerror.NewCode(gerror.CodeInvalidParameter, "invalid padding")
return nil, gerror.NewCode(gcode.CodeInvalidParameter, "invalid padding")
}
}
@ -146,7 +147,7 @@ func DecryptCFB(cipherText []byte, key []byte, unPadding int, iv ...[]byte) ([]b
return nil, err
}
if len(cipherText) < aes.BlockSize {
return nil, gerror.NewCode(gerror.CodeInvalidParameter, "cipherText too short")
return nil, gerror.NewCode(gcode.CodeInvalidParameter, "cipherText too short")
}
ivValue := ([]byte)(nil)
if len(iv) > 0 {

View File

@ -11,6 +11,7 @@ import (
"bytes"
"crypto/cipher"
"crypto/des"
"github.com/gogf/gf/errors/gcode"
"github.com/gogf/gf/errors/gerror"
)
@ -66,7 +67,7 @@ func DecryptECB(cipherText []byte, key []byte, padding int) ([]byte, error) {
// The length of the <key> should be either 16 or 24 bytes.
func EncryptECBTriple(plainText []byte, key []byte, padding int) ([]byte, error) {
if len(key) != 16 && len(key) != 24 {
return nil, gerror.NewCode(gerror.CodeInvalidParameter, "key length error")
return nil, gerror.NewCode(gcode.CodeInvalidParameter, "key length error")
}
text, err := Padding(plainText, padding)
@ -100,7 +101,7 @@ func EncryptECBTriple(plainText []byte, key []byte, padding int) ([]byte, error)
// The length of the <key> should be either 16 or 24 bytes.
func DecryptECBTriple(cipherText []byte, key []byte, padding int) ([]byte, error) {
if len(key) != 16 && len(key) != 24 {
return nil, gerror.NewCode(gerror.CodeInvalidParameter, "key length error")
return nil, gerror.NewCode(gcode.CodeInvalidParameter, "key length error")
}
var newKey []byte
@ -138,7 +139,7 @@ func EncryptCBC(plainText []byte, key []byte, iv []byte, padding int) ([]byte, e
}
if len(iv) != block.BlockSize() {
return nil, gerror.NewCode(gerror.CodeInvalidParameter, "iv length invalid")
return nil, gerror.NewCode(gcode.CodeInvalidParameter, "iv length invalid")
}
text, err := Padding(plainText, padding)
@ -161,7 +162,7 @@ func DecryptCBC(cipherText []byte, key []byte, iv []byte, padding int) ([]byte,
}
if len(iv) != block.BlockSize() {
return nil, gerror.NewCode(gerror.CodeInvalidParameter, "iv length invalid")
return nil, gerror.NewCode(gcode.CodeInvalidParameter, "iv length invalid")
}
text := make([]byte, len(cipherText))
@ -179,7 +180,7 @@ func DecryptCBC(cipherText []byte, key []byte, iv []byte, padding int) ([]byte,
// EncryptCBCTriple encrypts <plainText> using TripleDES and CBC mode.
func EncryptCBCTriple(plainText []byte, key []byte, iv []byte, padding int) ([]byte, error) {
if len(key) != 16 && len(key) != 24 {
return nil, gerror.NewCode(gerror.CodeInvalidParameter, "key length invalid")
return nil, gerror.NewCode(gcode.CodeInvalidParameter, "key length invalid")
}
var newKey []byte
@ -196,7 +197,7 @@ func EncryptCBCTriple(plainText []byte, key []byte, iv []byte, padding int) ([]b
}
if len(iv) != block.BlockSize() {
return nil, gerror.NewCode(gerror.CodeInvalidParameter, "iv length invalid")
return nil, gerror.NewCode(gcode.CodeInvalidParameter, "iv length invalid")
}
text, err := Padding(plainText, padding)
@ -214,7 +215,7 @@ func EncryptCBCTriple(plainText []byte, key []byte, iv []byte, padding int) ([]b
// DecryptCBCTriple decrypts <cipherText> using TripleDES and CBC mode.
func DecryptCBCTriple(cipherText []byte, key []byte, iv []byte, padding int) ([]byte, error) {
if len(key) != 16 && len(key) != 24 {
return nil, gerror.NewCode(gerror.CodeInvalidParameter, "key length invalid")
return nil, gerror.NewCode(gcode.CodeInvalidParameter, "key length invalid")
}
var newKey []byte
@ -231,7 +232,7 @@ func DecryptCBCTriple(cipherText []byte, key []byte, iv []byte, padding int) ([]
}
if len(iv) != block.BlockSize() {
return nil, gerror.NewCode(gerror.CodeInvalidParameter, "iv length invalid")
return nil, gerror.NewCode(gcode.CodeInvalidParameter, "iv length invalid")
}
text := make([]byte, len(cipherText))
@ -262,12 +263,12 @@ func Padding(text []byte, padding int) ([]byte, error) {
switch padding {
case NOPADDING:
if len(text)%8 != 0 {
return nil, gerror.NewCode(gerror.CodeInvalidParameter, "text length invalid")
return nil, gerror.NewCode(gcode.CodeInvalidParameter, "text length invalid")
}
case PKCS5PADDING:
return PaddingPKCS5(text, 8), nil
default:
return nil, gerror.NewCode(gerror.CodeInvalidParameter, "padding type error")
return nil, gerror.NewCode(gcode.CodeInvalidParameter, "padding type error")
}
return text, nil
@ -277,12 +278,12 @@ func UnPadding(text []byte, padding int) ([]byte, error) {
switch padding {
case NOPADDING:
if len(text)%8 != 0 {
return nil, gerror.NewCode(gerror.CodeInvalidParameter, "text length invalid")
return nil, gerror.NewCode(gcode.CodeInvalidParameter, "text length invalid")
}
case PKCS5PADDING:
return UnPaddingPKCS5(text), nil
default:
return nil, gerror.NewCode(gerror.CodeInvalidParameter, "padding type error")
return nil, gerror.NewCode(gcode.CodeInvalidParameter, "padding type error")
}
return text, nil
}

View File

@ -10,6 +10,7 @@ package gdb
import (
"context"
"database/sql"
"github.com/gogf/gf/errors/gcode"
"time"
"github.com/gogf/gf/errors/gerror"
@ -72,6 +73,14 @@ type DB interface {
// Also see Core.Ctx.
Ctx(ctx context.Context) DB
// Close closes the database and prevents new queries from starting.
// Close then waits for all queries that have started processing on the server
// to finish.
//
// It is rare to Close a DB, as the DB handle is meant to be
// long-lived and shared between many goroutines.
Close(ctx context.Context) error
// ===========================================================================
// Query APIs.
// ===========================================================================
@ -93,7 +102,7 @@ type DB interface {
Delete(table string, condition interface{}, args ...interface{}) (sql.Result, error) // See Core.Delete.
// ===========================================================================
// Internal APIs for CURD, which can be overwrote for custom CURD implements.
// Internal APIs for CURD, which can be overwritten by custom CURD implements.
// ===========================================================================
DoGetAll(ctx context.Context, link Link, sql string, args ...interface{}) (result Result, err error) // See Core.DoGetAll.
@ -152,8 +161,8 @@ type DB interface {
GetGroup() string // See Core.GetGroup.
SetDryRun(enabled bool) // See Core.SetDryRun.
GetDryRun() bool // See Core.GetDryRun.
SetLogger(logger Logger) // See Core.SetLogger.
GetLogger() Logger // See Core.GetLogger.
SetLogger(logger *glog.Logger) // See Core.SetLogger.
GetLogger() *glog.Logger // See Core.GetLogger.
GetConfig() *ConfigNode // See Core.GetConfig.
SetMaxIdleConnCount(n int) // See Core.SetMaxIdleConnCount.
SetMaxOpenConnCount(n int) // See Core.SetMaxOpenConnCount.
@ -178,8 +187,9 @@ type Core struct {
group string // Configuration group name.
debug *gtype.Bool // Enable debug mode for the database, which can be changed in runtime.
cache *gcache.Cache // Cache manager, SQL result cache only.
links *gmap.StrAnyMap // links caches all created links by node.
schema *gtype.String // Custom schema for this object.
logger Logger // Logger for logging functionality.
logger *glog.Logger // Logger for logging functionality.
config *ConfigNode // Current config node.
}
@ -301,9 +311,6 @@ var (
// in the field name as it conflicts with "db.table.field" pattern in SOME situations.
regularFieldNameWithoutDotRegPattern = `^[\w\-]+$`
// internalCache is the memory cache for internal usage.
internalCache = gcache.New()
// tableFieldsMap caches the table information retrived from database.
tableFieldsMap = gmap.New(true)
@ -336,7 +343,7 @@ func New(group ...string) (db DB, err error) {
if len(configs.config) < 1 {
return nil, gerror.NewCode(
gerror.CodeInvalidConfiguration,
gcode.CodeInvalidConfiguration,
"database configuration is empty, please set the database configuration before using",
)
}
@ -346,8 +353,9 @@ func New(group ...string) (db DB, err error) {
group: groupName,
debug: gtype.NewBool(),
cache: gcache.New(),
links: gmap.NewStrAnyMap(true),
schema: gtype.NewString(),
logger: LoggerImp{glog.New()},
logger: glog.New(),
config: node,
}
if v, ok := driverMap[node.Type]; ok {
@ -358,7 +366,7 @@ func New(group ...string) (db DB, err error) {
return c.db, nil
} else {
return nil, gerror.NewCodef(
gerror.CodeInvalidConfiguration,
gcode.CodeInvalidConfiguration,
`cannot find database driver for specified database type "%s", did you misspell type name "%s" or forget importing the database driver?`,
node.Type, node.Type,
)
@ -368,7 +376,7 @@ func New(group ...string) (db DB, err error) {
}
} else {
return nil, gerror.NewCodef(
gerror.CodeInvalidConfiguration,
gcode.CodeInvalidConfiguration,
`database configuration node "%s" is not found, did you misspell group name "%s" or miss the database configuration?`,
groupName, groupName,
)
@ -411,7 +419,7 @@ func getConfigNodeByGroup(group string, master bool) (*ConfigNode, error) {
}
}
if len(masterList) < 1 {
return nil, gerror.NewCode(gerror.CodeInvalidConfiguration, "at least one master node configuration's need to make sense")
return nil, gerror.NewCode(gcode.CodeInvalidConfiguration, "at least one master node configuration's need to make sense")
}
if len(slaveList) < 1 {
slaveList = masterList
@ -422,7 +430,7 @@ func getConfigNodeByGroup(group string, master bool) (*ConfigNode, error) {
return getConfigNodeByWeight(slaveList), nil
}
} else {
return nil, gerror.NewCodef(gerror.CodeInvalidConfiguration, "empty database configuration for item name '%s'", group)
return nil, gerror.NewCodef(gcode.CodeInvalidConfiguration, "empty database configuration for item name '%s'", group)
}
}
@ -440,7 +448,7 @@ func getConfigNodeByWeight(cg ConfigGroup) *ConfigNode {
for i := 0; i < len(cg); i++ {
total += cg[i].Weight * 100
}
// If total is 0 means all of the nodes have no weight attribute configured.
// If total is 0 means all the nodes have no weight attribute configured.
// It then defaults each node's weight attribute to 1.
if total == 0 {
for i := 0; i < len(cg); i++ {
@ -489,7 +497,7 @@ func (c *Core) getSqlDb(master bool, schema ...string) (sqlDb *sql.DB, err error
node = &n
}
// Cache the underlying connection pool object by node.
v, _ := internalCache.GetOrSetFuncLock(node.String(), func() (interface{}, error) {
v := c.links.GetOrSetFuncLock(node.String(), func() interface{} {
intlog.Printf(
c.db.GetCtx(),
`open new connection, master:%#v, config:%#v, node:%#v`,
@ -509,7 +517,7 @@ func (c *Core) getSqlDb(master bool, schema ...string) (sqlDb *sql.DB, err error
sqlDb, err = c.db.Open(node)
if err != nil {
return nil, err
return nil
}
if c.config.MaxIdleConnCount > 0 {
@ -533,8 +541,8 @@ func (c *Core) getSqlDb(master bool, schema ...string) (sqlDb *sql.DB, err error
} else {
sqlDb.SetConnMaxLifetime(defaultMaxConnLifeTime)
}
return sqlDb, nil
}, 0)
return sqlDb
})
if v != nil && sqlDb == nil {
sqlDb = v.(*sql.DB)
}

View File

@ -11,6 +11,8 @@ import (
"context"
"database/sql"
"fmt"
"github.com/gogf/gf/errors/gcode"
"github.com/gogf/gf/internal/intlog"
"reflect"
"strings"
@ -36,10 +38,6 @@ func (c *Core) Ctx(ctx context.Context) DB {
if ctx == nil {
return c.db
}
// It is already set context in previous chaining operation.
if c.ctx != nil {
return c.db
}
ctx = context.WithValue(ctx, ctxStrictKeyName, 1)
// It makes a shallow copy of current db and changes its context for next chaining operation.
var (
@ -89,11 +87,33 @@ func (c *Core) GetCtxTimeout(timeoutType int, ctx context.Context) (context.Cont
return context.WithTimeout(ctx, c.db.GetConfig().PrepareTimeout)
}
default:
panic(gerror.NewCodef(gerror.CodeInvalidParameter, "invalid context timeout type: %d", timeoutType))
panic(gerror.NewCodef(gcode.CodeInvalidParameter, "invalid context timeout type: %d", timeoutType))
}
return ctx, func() {}
}
// Close closes the database and prevents new queries from starting.
// Close then waits for all queries that have started processing on the server
// to finish.
//
// It is rare to Close a DB, as the DB handle is meant to be
// long-lived and shared between many goroutines.
func (c *Core) Close(ctx context.Context) (err error) {
c.links.LockFunc(func(m map[string]interface{}) {
for k, v := range m {
if db, ok := v.(*sql.DB); ok {
err = db.Close()
intlog.Printf(ctx, `close link: %s, err: %v`, k, err)
if err != nil {
return
}
delete(m, k)
}
}
})
return
}
// Master creates and returns a connection from master node if master-slave configured.
// It returns the default connection if master-slave not configured.
func (c *Core) Master(schema ...string) (*sql.DB, error) {
@ -455,9 +475,8 @@ func (c *Core) formatOnDuplicate(columns []string, option DoInsertOption) string
}
} else {
for _, column := range columns {
// If it's SAVE operation,
// do not automatically update the creating time.
if c.isSoftCreatedFilledName(column) {
// If it's SAVE operation, do not automatically update the creating time.
if c.isSoftCreatedFieldName(column) {
continue
}
if len(onDuplicateStr) > 0 {
@ -553,7 +572,7 @@ func (c *Core) DoUpdate(ctx context.Context, link Link, table string, data inter
updates = gconv.String(data)
}
if len(updates) == 0 {
return nil, gerror.NewCode(gerror.CodeMissingParameter, "data cannot be empty")
return nil, gerror.NewCode(gcode.CodeMissingParameter, "data cannot be empty")
}
if len(params) > 0 {
args = append(params, args...)
@ -659,9 +678,9 @@ func (c *Core) writeSqlToLogger(ctx context.Context, sql *Sql) {
s := fmt.Sprintf("[%3d ms] [%s] %s%s", sql.End-sql.Start, sql.Group, transactionIdStr, sql.Format)
if sql.Error != nil {
s += "\nError: " + sql.Error.Error()
c.logger.Error(ctx, s)
c.logger.Ctx(ctx).Error(s)
} else {
c.logger.Debug(ctx, s)
c.logger.Ctx(ctx).Debug(s)
}
}
@ -679,8 +698,8 @@ func (c *Core) HasTable(name string) (bool, error) {
return false, nil
}
// isSoftCreatedFilledName checks and returns whether given filed name is an automatic-filled created time.
func (c *Core) isSoftCreatedFilledName(fieldName string) bool {
// isSoftCreatedFieldName checks and returns whether given filed name is an automatic-filled created time.
func (c *Core) isSoftCreatedFieldName(fieldName string) bool {
if fieldName == "" {
return false
}

View File

@ -8,6 +8,7 @@ package gdb
import (
"fmt"
"github.com/gogf/gf/os/glog"
"sync"
"time"
@ -133,12 +134,12 @@ func IsConfigured() bool {
}
// SetLogger sets the logger for orm.
func (c *Core) SetLogger(logger Logger) {
func (c *Core) SetLogger(logger *glog.Logger) {
c.logger = logger
}
// GetLogger returns the logger of the orm.
func (c *Core) GetLogger() Logger {
// GetLogger returns the (logger) of the orm.
func (c *Core) GetLogger() *glog.Logger {
return c.logger
}

View File

@ -1,27 +0,0 @@
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
package gdb
import (
"context"
"github.com/gogf/gf/os/glog"
)
// LoggerImp is the default implementation of interface Logger for DB.
type LoggerImp struct {
*glog.Logger
}
// Error implements function Error for interface Logger.
func (l LoggerImp) Error(ctx context.Context, s string) {
l.Ctx(ctx).Error(s)
}
// Debug implements function Debug for interface Logger.
func (l LoggerImp) Debug(ctx context.Context, s string) {
l.Ctx(ctx).Debug(s)
}

View File

@ -10,6 +10,7 @@ package gdb
import (
"context"
"database/sql"
"github.com/gogf/gf/errors/gcode"
"github.com/gogf/gf/errors/gerror"
"github.com/gogf/gf/os/gtime"
@ -26,10 +27,15 @@ func (c *Core) Query(sql string, args ...interface{}) (rows *sql.Rows, err error
func (c *Core) DoQuery(ctx context.Context, link Link, sql string, args ...interface{}) (rows *sql.Rows, err error) {
// Transaction checks.
if link == nil {
if link, err = c.SlaveLink(); err != nil {
if tx := TXFromCtx(ctx, c.db.GetGroup()); tx != nil {
// Firstly, check and retrieve transaction link from context.
link = &txLink{tx.tx}
} else if link, err = c.SlaveLink(); err != nil {
// Or else it creates one from master node.
return nil, err
}
} else if !link.IsTransaction() {
// If current link is not transaction link, it checks and retrieves transaction from context.
if tx := TXFromCtx(ctx, c.db.GetGroup()); tx != nil {
link = &txLink{tx.tx}
}
@ -83,10 +89,15 @@ func (c *Core) Exec(sql string, args ...interface{}) (result sql.Result, err err
func (c *Core) DoExec(ctx context.Context, link Link, sql string, args ...interface{}) (result sql.Result, err error) {
// Transaction checks.
if link == nil {
if link, err = c.MasterLink(); err != nil {
if tx := TXFromCtx(ctx, c.db.GetGroup()); tx != nil {
// Firstly, check and retrieve transaction link from context.
link = &txLink{tx.tx}
} else if link, err = c.MasterLink(); err != nil {
// Or else it creates one from master node.
return nil, err
}
} else if !link.IsTransaction() {
// If current link is not transaction link, it checks and retrieves transaction from context.
if tx := TXFromCtx(ctx, c.db.GetGroup()); tx != nil {
link = &txLink{tx.tx}
}
@ -136,7 +147,7 @@ func (c *Core) DoExec(ctx context.Context, link Link, sql string, args ...interf
func (c *Core) DoCommit(ctx context.Context, link Link, sql string, args []interface{}) (newSql string, newArgs []interface{}, err error) {
if c.db.GetConfig().CtxStrict {
if v := ctx.Value(ctxStrictKeyName); v == nil {
return sql, args, gerror.NewCode(gerror.CodeMissingParameter, ctxStrictErrorStr)
return sql, args, gerror.NewCode(gcode.CodeMissingParameter, ctxStrictErrorStr)
}
}
return sql, args, nil
@ -169,11 +180,25 @@ func (c *Core) Prepare(sql string, execOnMaster ...bool) (*Stmt, error) {
// DoPrepare calls prepare function on given link object and returns the statement object.
func (c *Core) DoPrepare(ctx context.Context, link Link, sql string) (*Stmt, error) {
if link != nil && !link.IsTransaction() {
// Transaction checks.
if link == nil {
if tx := TXFromCtx(ctx, c.db.GetGroup()); tx != nil {
// Firstly, check and retrieve transaction link from context.
link = &txLink{tx.tx}
} else {
// Or else it creates one from master node.
var err error
if link, err = c.MasterLink(); err != nil {
return nil, err
}
}
} else if !link.IsTransaction() {
// If current link is not transaction link, it checks and retrieves transaction from context.
if tx := TXFromCtx(ctx, c.db.GetGroup()); tx != nil {
link = &txLink{tx.tx}
}
}
if c.GetConfig().PrepareTimeout > 0 {
// DO NOT USE cancel function in prepare statement.
ctx, _ = context.WithTimeout(ctx, c.GetConfig().PrepareTimeout)
@ -181,7 +206,7 @@ func (c *Core) DoPrepare(ctx context.Context, link Link, sql string) (*Stmt, err
if c.db.GetConfig().CtxStrict {
if v := ctx.Value(ctxStrictKeyName); v == nil {
return nil, gerror.NewCode(gerror.CodeMissingParameter, ctxStrictErrorStr)
return nil, gerror.NewCode(gcode.CodeMissingParameter, ctxStrictErrorStr)
}
}

View File

@ -31,6 +31,7 @@ func (c *Core) SlaveLink(schema ...string) (Link, error) {
// QuoteWord checks given string `s` a word, if true quotes it with security chars of the database
// and returns the quoted string; or else return `s` without any change.
// The meaning of a `word` can be considered as a column name.
func (c *Core) QuoteWord(s string) string {
charLeft, charRight := c.db.GetChars()
return doQuoteWord(s, charLeft, charRight)
@ -38,6 +39,7 @@ func (c *Core) QuoteWord(s string) string {
// QuoteString quotes string with quote chars. Strings like:
// "user", "user u", "user,user_detail", "user u, user_detail ut", "u.id asc".
// The meaning of a `string` can be considered as part of a statement string including columns.
func (c *Core) QuoteString(s string) string {
charLeft, charRight := c.db.GetChars()
return doQuoteString(s, charLeft, charRight)

View File

@ -15,6 +15,7 @@ import (
"context"
"database/sql"
"fmt"
"github.com/gogf/gf/errors/gcode"
"strconv"
"strings"
@ -214,7 +215,7 @@ func (d *DriverMssql) TableFields(ctx context.Context, table string, schema ...s
charL, charR := d.GetChars()
table = gstr.Trim(table, charL+charR)
if gstr.Contains(table, " ") {
return nil, gerror.NewCode(gerror.CodeInvalidParameter, "function TableFields supports only single table operations")
return nil, gerror.NewCode(gcode.CodeInvalidParameter, "function TableFields supports only single table operations")
}
useSchema := d.db.GetSchema()
if len(schema) > 0 && schema[0] != "" {
@ -292,10 +293,10 @@ ORDER BY a.id,a.colorder`,
func (d *DriverMssql) DoInsert(ctx context.Context, link Link, table string, list List, option DoInsertOption) (result sql.Result, err error) {
switch option.InsertOption {
case insertOptionSave:
return nil, gerror.NewCode(gerror.CodeNotSupported, `Save operation is not supported by mssql driver`)
return nil, gerror.NewCode(gcode.CodeNotSupported, `Save operation is not supported by mssql driver`)
case insertOptionReplace:
return nil, gerror.NewCode(gerror.CodeNotSupported, `Replace operation is not supported by mssql driver`)
return nil, gerror.NewCode(gcode.CodeNotSupported, `Replace operation is not supported by mssql driver`)
default:
return d.Core.DoInsert(ctx, link, table, list, option)

View File

@ -10,14 +10,14 @@ import (
"context"
"database/sql"
"fmt"
"net/url"
"github.com/gogf/gf/errors/gcode"
"github.com/gogf/gf/errors/gerror"
"github.com/gogf/gf/internal/intlog"
"github.com/gogf/gf/text/gregex"
"github.com/gogf/gf/text/gstr"
"net/url"
_ "github.com/gogf/mysql"
_ "github.com/go-sql-driver/mysql"
)
// DriverMysql is the driver for mysql database.
@ -33,7 +33,7 @@ func (d *DriverMysql) New(core *Core, node *ConfigNode) (DB, error) {
}, nil
}
// Open creates and returns a underlying sql.DB object for mysql.
// Open creates and returns an underlying sql.DB object for mysql.
// Note that it converts time.Time argument to local timezone in default.
func (d *DriverMysql) Open(config *ConfigNode) (*sql.DB, error) {
var source string
@ -121,7 +121,7 @@ func (d *DriverMysql) TableFields(ctx context.Context, table string, schema ...s
charL, charR := d.GetChars()
table = gstr.Trim(table, charL+charR)
if gstr.Contains(table, " ") {
return nil, gerror.NewCode(gerror.CodeInvalidParameter, "function TableFields supports only single table operations")
return nil, gerror.NewCode(gcode.CodeInvalidParameter, "function TableFields supports only single table operations")
}
useSchema := d.schema.Val()
if len(schema) > 0 && schema[0] != "" {

View File

@ -15,6 +15,7 @@ import (
"context"
"database/sql"
"fmt"
"github.com/gogf/gf/errors/gcode"
"reflect"
"strconv"
"strings"
@ -186,7 +187,7 @@ func (d *DriverOracle) TableFields(ctx context.Context, table string, schema ...
charL, charR := d.GetChars()
table = gstr.Trim(table, charL+charR)
if gstr.Contains(table, " ") {
return nil, gerror.NewCode(gerror.CodeInvalidParameter, "function TableFields supports only single table operations")
return nil, gerror.NewCode(gcode.CodeInvalidParameter, "function TableFields supports only single table operations")
}
useSchema := d.db.GetSchema()
if len(schema) > 0 && schema[0] != "" {
@ -235,34 +236,6 @@ FROM USER_TAB_COLUMNS WHERE TABLE_NAME = '%s' ORDER BY COLUMN_ID`,
return
}
func (d *DriverOracle) getTableUniqueIndex(table string) (fields map[string]map[string]string, err error) {
table = strings.ToUpper(table)
v, _ := internalCache.GetOrSetFunc(
"table_unique_index_"+table,
func() (interface{}, error) {
res := (Result)(nil)
res, err = d.db.GetAll(fmt.Sprintf(`
SELECT INDEX_NAME,COLUMN_NAME,CHAR_LENGTH FROM USER_IND_COLUMNS
WHERE TABLE_NAME = '%s'
AND INDEX_NAME IN(SELECT INDEX_NAME FROM USER_INDEXES WHERE TABLE_NAME='%s' AND UNIQUENESS='UNIQUE')
ORDER BY INDEX_NAME,COLUMN_POSITION`, table, table))
if err != nil {
return nil, err
}
fields := make(map[string]map[string]string)
for _, v := range res {
mm := make(map[string]string)
mm[v["COLUMN_NAME"].String()] = v["CHAR_LENGTH"].String()
fields[v["INDEX_NAME"].String()] = mm
}
return fields, nil
}, 0)
if err == nil {
fields = v.(map[string]map[string]string)
}
return
}
// DoInsert inserts or updates data for given table.
// This function is usually used for custom interface definition, you do not need call it manually.
// The parameter `data` can be type of map/gmap/struct/*struct/[]map/[]struct, etc.
@ -278,10 +251,10 @@ func (d *DriverOracle) getTableUniqueIndex(table string) (fields map[string]map[
func (d *DriverOracle) DoInsert(ctx context.Context, link Link, table string, list List, option DoInsertOption) (result sql.Result, err error) {
switch option.InsertOption {
case insertOptionSave:
return nil, gerror.NewCode(gerror.CodeNotSupported, `Save operation is not supported by mssql driver`)
return nil, gerror.NewCode(gcode.CodeNotSupported, `Save operation is not supported by mssql driver`)
case insertOptionReplace:
return nil, gerror.NewCode(gerror.CodeNotSupported, `Replace operation is not supported by mssql driver`)
return nil, gerror.NewCode(gcode.CodeNotSupported, `Replace operation is not supported by mssql driver`)
}
var (

View File

@ -15,6 +15,7 @@ import (
"context"
"database/sql"
"fmt"
"github.com/gogf/gf/errors/gcode"
"strings"
"github.com/gogf/gf/errors/gerror"
@ -126,7 +127,7 @@ func (d *DriverPgsql) TableFields(ctx context.Context, table string, schema ...s
charL, charR := d.GetChars()
table = gstr.Trim(table, charL+charR)
if gstr.Contains(table, " ") {
return nil, gerror.NewCode(gerror.CodeInvalidParameter, "function TableFields supports only single table operations")
return nil, gerror.NewCode(gcode.CodeInvalidParameter, "function TableFields supports only single table operations")
}
table, _ = gregex.ReplaceString("\"", "", table)
useSchema := d.db.GetSchema()
@ -190,10 +191,10 @@ ORDER BY a.attnum`,
func (d *DriverPgsql) DoInsert(ctx context.Context, link Link, table string, list List, option DoInsertOption) (result sql.Result, err error) {
switch option.InsertOption {
case insertOptionSave:
return nil, gerror.NewCode(gerror.CodeNotSupported, `Save operation is not supported by pgsql driver`)
return nil, gerror.NewCode(gcode.CodeNotSupported, `Save operation is not supported by pgsql driver`)
case insertOptionReplace:
return nil, gerror.NewCode(gerror.CodeNotSupported, `Replace operation is not supported by pgsql driver`)
return nil, gerror.NewCode(gcode.CodeNotSupported, `Replace operation is not supported by pgsql driver`)
default:
return d.Core.DoInsert(ctx, link, table, list, option)

View File

@ -14,6 +14,7 @@ import (
"context"
"database/sql"
"fmt"
"github.com/gogf/gf/errors/gcode"
"strings"
"github.com/gogf/gf/errors/gerror"
@ -99,7 +100,7 @@ func (d *DriverSqlite) TableFields(ctx context.Context, table string, schema ...
charL, charR := d.GetChars()
table = gstr.Trim(table, charL+charR)
if gstr.Contains(table, " ") {
return nil, gerror.NewCode(gerror.CodeInvalidParameter, "function TableFields supports only single table operations")
return nil, gerror.NewCode(gcode.CodeInvalidParameter, "function TableFields supports only single table operations")
}
useSchema := d.db.GetSchema()
if len(schema) > 0 && schema[0] != "" {
@ -141,10 +142,10 @@ func (d *DriverSqlite) TableFields(ctx context.Context, table string, schema ...
func (d *DriverSqlite) DoInsert(ctx context.Context, link Link, table string, list List, option DoInsertOption) (result sql.Result, err error) {
switch option.InsertOption {
case insertOptionSave:
return nil, gerror.NewCode(gerror.CodeNotSupported, `Save operation is not supported by sqlite driver`)
return nil, gerror.NewCode(gcode.CodeNotSupported, `Save operation is not supported by sqlite driver`)
case insertOptionReplace:
return nil, gerror.NewCode(gerror.CodeNotSupported, `Replace operation is not supported by sqlite driver`)
return nil, gerror.NewCode(gcode.CodeNotSupported, `Replace operation is not supported by sqlite driver`)
default:
return d.Core.DoInsert(ctx, link, table, list, option)

View File

@ -10,6 +10,7 @@ import (
"bytes"
"database/sql"
"fmt"
"github.com/gogf/gf/errors/gcode"
"reflect"
"regexp"
"strings"
@ -195,8 +196,25 @@ func ConvertDataForTableRecord(value interface{}) map[string]interface{} {
}
case reflect.Struct:
switch v.(type) {
case time.Time, *time.Time, gtime.Time, *gtime.Time:
switch r := v.(type) {
// If the time is zero, it then updates it to nil,
// which will insert/update the value to database as "null".
case time.Time:
if r.IsZero() {
data[k] = nil
}
case gtime.Time:
if r.IsZero() {
data[k] = nil
}
case *gtime.Time:
if r.IsZero() {
data[k] = nil
}
case *time.Time:
continue
case Counter, *Counter:
@ -216,102 +234,26 @@ func ConvertDataForTableRecord(value interface{}) map[string]interface{} {
return data
}
// DataToMapDeep converts `value` to map type recursively.
// DataToMapDeep converts `value` to map type recursively(if attribute struct is embedded).
// The parameter `value` should be type of *map/map/*struct/struct.
// It supports embedded struct definition for struct.
func DataToMapDeep(value interface{}) map[string]interface{} {
if v, ok := value.(apiMapStrAny); ok {
return v.MapStrAny()
}
var (
rvValue reflect.Value
rvField reflect.Value
rvKind reflect.Kind
rtField reflect.StructField
)
if v, ok := value.(reflect.Value); ok {
rvValue = v
} else {
rvValue = reflect.ValueOf(value)
}
rvKind = rvValue.Kind()
if rvKind == reflect.Ptr {
rvValue = rvValue.Elem()
rvKind = rvValue.Kind()
}
// If given `value` is not a struct, it uses gconv.Map for converting.
if rvKind != reflect.Struct {
return gconv.Map(value, structTagPriority...)
}
// Struct handling.
var (
fieldTag reflect.StructTag
rvType = rvValue.Type()
name = ""
data = make(map[string]interface{})
)
for i := 0; i < rvValue.NumField(); i++ {
rtField = rvType.Field(i)
rvField = rvValue.Field(i)
fieldName := rtField.Name
if !utils.IsLetterUpper(fieldName[0]) {
continue
}
// Struct attribute inherit
if rtField.Anonymous {
for k, v := range DataToMapDeep(rvField) {
data[k] = v
}
continue
}
// Other attributes.
name = ""
fieldTag = rtField.Tag
for _, tag := range structTagPriority {
if s := fieldTag.Get(tag); s != "" {
name = s
break
}
}
if name == "" {
name = fieldName
} else {
// The "orm" tag supports json tag feature: -, omitempty
// The "orm" tag would be like: "id,priority", so it should use splitting handling.
name = gstr.Trim(name)
if name == "-" {
continue
}
array := gstr.SplitAndTrim(name, ",")
if len(array) > 1 {
switch array[1] {
case "omitempty":
if empty.IsEmpty(rvField.Interface()) {
continue
} else {
name = array[0]
}
default:
name = array[0]
}
}
}
// The underlying driver supports time.Time/*time.Time types.
fieldValue := rvField.Interface()
switch fieldValue.(type) {
m := gconv.Map(value, structTagPriority...)
for k, v := range m {
switch v.(type) {
case time.Time, *time.Time, gtime.Time, *gtime.Time:
data[name] = fieldValue
m[k] = v
default:
// Use string conversion in default.
if s, ok := fieldValue.(apiString); ok {
data[name] = s.String()
if s, ok := v.(apiString); ok {
m[k] = s.String()
} else {
data[name] = fieldValue
m[k] = v
}
}
}
return data
return m
}
// doHandleTableName adds prefix string and quote chars for the table. It handles table string like:
@ -477,39 +419,58 @@ func formatSql(sql string, args []interface{}) (newSql string, newArgs []interfa
return handleArguments(sql, args)
}
type formatWhereInput struct {
Where interface{}
Args []interface{}
OmitNil bool
OmitEmpty bool
Schema string
Table string
}
// formatWhere formats where statement and its arguments for `Where` and `Having` statements.
func formatWhere(db DB, where interface{}, args []interface{}, omitEmpty bool, schema, table string) (newWhere string, newArgs []interface{}) {
func formatWhere(db DB, in formatWhereInput) (newWhere string, newArgs []interface{}) {
var (
buffer = bytes.NewBuffer(nil)
rv = reflect.ValueOf(where)
kind = rv.Kind()
buffer = bytes.NewBuffer(nil)
reflectValue = reflect.ValueOf(in.Where)
reflectKind = reflectValue.Kind()
)
for kind == reflect.Ptr {
rv = rv.Elem()
kind = rv.Kind()
for reflectKind == reflect.Ptr {
reflectValue = reflectValue.Elem()
reflectKind = reflectValue.Kind()
}
switch kind {
switch reflectKind {
case reflect.Array, reflect.Slice:
newArgs = formatWhereInterfaces(db, gconv.Interfaces(where), buffer, newArgs)
newArgs = formatWhereInterfaces(db, gconv.Interfaces(in.Where), buffer, newArgs)
case reflect.Map:
for key, value := range DataToMapDeep(where) {
if gregex.IsMatchString(regularFieldNameRegPattern, key) && omitEmpty && empty.IsEmpty(value) {
continue
for key, value := range DataToMapDeep(in.Where) {
if gregex.IsMatchString(regularFieldNameRegPattern, key) {
if in.OmitNil && empty.IsNil(value) {
continue
}
if in.OmitEmpty && empty.IsEmpty(value) {
continue
}
}
newArgs = formatWhereKeyValue(db, buffer, newArgs, key, value)
}
case reflect.Struct:
// If `where` struct implements apiIterator interface,
// it then uses its Iterate function to iterates its key-value pairs.
// it then uses its Iterate function to iterate its key-value pairs.
// For example, ListMap and TreeMap are ordered map,
// which implement apiIterator interface and are index-friendly for where conditions.
if iterator, ok := where.(apiIterator); ok {
if iterator, ok := in.Where.(apiIterator); ok {
iterator.Iterator(func(key, value interface{}) bool {
ketStr := gconv.String(key)
if gregex.IsMatchString(regularFieldNameRegPattern, ketStr) && omitEmpty && empty.IsEmpty(value) {
return true
if gregex.IsMatchString(regularFieldNameRegPattern, ketStr) {
if in.OmitNil && empty.IsNil(value) {
return true
}
if in.OmitEmpty && empty.IsEmpty(value) {
return true
}
}
newArgs = formatWhereKeyValue(db, buffer, newArgs, ketStr, value)
return true
@ -517,29 +478,41 @@ func formatWhere(db DB, where interface{}, args []interface{}, omitEmpty bool, s
break
}
// Automatically mapping and filtering the struct attribute.
data := DataToMapDeep(where)
if table != "" {
data, _ = db.GetCore().mappingAndFilterData(schema, table, data, true)
var (
reflectType = reflectValue.Type()
structField reflect.StructField
)
data := DataToMapDeep(in.Where)
if in.Table != "" {
data, _ = db.GetCore().mappingAndFilterData(in.Schema, in.Table, data, true)
}
for key, value := range data {
if omitEmpty && empty.IsEmpty(value) {
continue
// Put the struct attributes in sequence in Where statement.
for i := 0; i < reflectType.NumField(); i++ {
structField = reflectType.Field(i)
foundKey, foundValue := gutil.MapPossibleItemByKey(data, structField.Name)
if foundKey != "" {
if in.OmitNil && empty.IsNil(foundValue) {
continue
}
if in.OmitEmpty && empty.IsEmpty(foundValue) {
continue
}
newArgs = formatWhereKeyValue(db, buffer, newArgs, foundKey, foundValue)
}
newArgs = formatWhereKeyValue(db, buffer, newArgs, key, value)
}
default:
// Usually a string.
var (
i = 0
whereStr = gconv.String(where)
whereStr = gconv.String(in.Where)
)
for {
if i >= len(args) {
if i >= len(in.Args) {
break
}
// Sub query, which is always used along with a string condition.
if model, ok := args[i].(*Model); ok {
if model, ok := in.Args[i].(*Model); ok {
var (
index = -1
)
@ -553,7 +526,7 @@ func formatWhere(db DB, where interface{}, args []interface{}, omitEmpty bool, s
}
return s
})
args = gutil.SliceDelete(args, i)
in.Args = gutil.SliceDelete(in.Args, i)
continue
}
i++
@ -562,9 +535,9 @@ func formatWhere(db DB, where interface{}, args []interface{}, omitEmpty bool, s
}
if buffer.Len() == 0 {
return "", args
return "", in.Args
}
newArgs = append(newArgs, args...)
newArgs = append(newArgs, in.Args...)
newWhere = buffer.String()
if len(newArgs) > 0 {
if gstr.Pos(newWhere, "?") == -1 {
@ -809,7 +782,7 @@ func handleArguments(sql string, args []interface{}) (newSql string, newArgs []i
// formatError customizes and returns the SQL error.
func formatError(err error, s string, args ...interface{}) error {
if err != nil && err != sql.ErrNoRows {
return gerror.NewCodef(gerror.CodeDbOperationError, "%s, %s\n", err.Error(), FormatSqlWithArgs(s, args))
return gerror.NewCodef(gcode.CodeDbOperationError, "%s, %s\n", err.Error(), FormatSqlWithArgs(s, args))
}
return err
}

View File

@ -73,6 +73,7 @@ const (
whereHolderOperatorWhere = 1
whereHolderOperatorAnd = 2
whereHolderOperatorOr = 3
defaultFields = "*"
)
// Table is alias of Core.Model.
@ -103,9 +104,14 @@ func (c *Core) Model(tableNameQueryOrStruct ...interface{}) *Model {
if len(tableNameQueryOrStruct) > 1 {
conditionStr := gconv.String(tableNameQueryOrStruct[0])
if gstr.Contains(conditionStr, "?") {
tableStr, extraArgs = formatWhere(
c.db, conditionStr, tableNameQueryOrStruct[1:], false, "", "",
)
tableStr, extraArgs = formatWhere(c.db, formatWhereInput{
Where: conditionStr,
Args: tableNameQueryOrStruct[1:],
OmitNil: false,
OmitEmpty: false,
Schema: "",
Table: "",
})
}
}
// Normal model creation.
@ -130,7 +136,7 @@ func (c *Core) Model(tableNameQueryOrStruct ...interface{}) *Model {
db: c.db,
tablesInit: tableStr,
tables: tableStr,
fields: "*",
fields: defaultFields,
start: -1,
offset: -1,
filter: true,

View File

@ -237,10 +237,13 @@ func (m *Model) WhereOrNotNull(columns ...string) *Model {
}
// Group sets the "GROUP BY" statement for the model.
func (m *Model) Group(groupBy string) *Model {
model := m.getModel()
model.groupBy = m.db.GetCore().QuoteString(groupBy)
return model
func (m *Model) Group(groupBy ...string) *Model {
if len(groupBy) > 0 {
model := m.getModel()
model.groupBy = m.db.GetCore().QuoteString(gstr.Join(groupBy, ","))
return model
}
return m
}
// And adds "AND" condition to the where statement.
@ -385,9 +388,14 @@ func (m *Model) formatCondition(limit1 bool, isCountStatement bool) (conditionWh
switch v.Operator {
case whereHolderOperatorWhere:
if conditionWhere == "" {
newWhere, newArgs := formatWhere(
m.db, v.Where, v.Args, m.option&optionOmitEmptyWhere > 0, m.schema, m.tables,
)
newWhere, newArgs := formatWhere(m.db, formatWhereInput{
Where: v.Where,
Args: v.Args,
OmitNil: m.option&optionOmitNilWhere > 0,
OmitEmpty: m.option&optionOmitEmptyWhere > 0,
Schema: m.schema,
Table: m.tables,
})
if len(newWhere) > 0 {
conditionWhere = newWhere
conditionArgs = newArgs
@ -397,9 +405,14 @@ func (m *Model) formatCondition(limit1 bool, isCountStatement bool) (conditionWh
fallthrough
case whereHolderOperatorAnd:
newWhere, newArgs := formatWhere(
m.db, v.Where, v.Args, m.option&optionOmitEmptyWhere > 0, m.schema, m.tables,
)
newWhere, newArgs := formatWhere(m.db, formatWhereInput{
Where: v.Where,
Args: v.Args,
OmitNil: m.option&optionOmitNilWhere > 0,
OmitEmpty: m.option&optionOmitEmptyWhere > 0,
Schema: m.schema,
Table: m.tables,
})
if len(newWhere) > 0 {
if len(conditionWhere) == 0 {
conditionWhere = newWhere
@ -412,9 +425,14 @@ func (m *Model) formatCondition(limit1 bool, isCountStatement bool) (conditionWh
}
case whereHolderOperatorOr:
newWhere, newArgs := formatWhere(
m.db, v.Where, v.Args, m.option&optionOmitEmptyWhere > 0, m.schema, m.tables,
)
newWhere, newArgs := formatWhere(m.db, formatWhereInput{
Where: v.Where,
Args: v.Args,
OmitNil: m.option&optionOmitNilWhere > 0,
OmitEmpty: m.option&optionOmitEmptyWhere > 0,
Schema: m.schema,
Table: m.tables,
})
if len(newWhere) > 0 {
if len(conditionWhere) == 0 {
conditionWhere = newWhere
@ -454,9 +472,14 @@ func (m *Model) formatCondition(limit1 bool, isCountStatement bool) (conditionWh
}
// HAVING.
if len(m.having) > 0 {
havingStr, havingArgs := formatWhere(
m.db, m.having[0], gconv.Interfaces(m.having[1]), m.option&optionOmitEmptyWhere > 0, m.schema, m.tables,
)
havingStr, havingArgs := formatWhere(m.db, formatWhereInput{
Where: m.having[0],
Args: gconv.Interfaces(m.having[1]),
OmitNil: m.option&optionOmitNilWhere > 0,
OmitEmpty: m.option&optionOmitEmptyWhere > 0,
Schema: m.schema,
Table: m.tables,
})
if len(havingStr) > 0 {
conditionExtra += " HAVING " + havingStr
conditionArgs = append(conditionArgs, havingArgs...)

View File

@ -9,6 +9,7 @@ package gdb
import (
"database/sql"
"fmt"
"github.com/gogf/gf/errors/gcode"
"github.com/gogf/gf/errors/gerror"
"github.com/gogf/gf/os/gtime"
@ -44,7 +45,7 @@ func (m *Model) Delete(where ...interface{}) (result sql.Result, err error) {
}
conditionStr := conditionWhere + conditionExtra
if !gstr.ContainsI(conditionStr, " WHERE ") {
return nil, gerror.NewCode(gerror.CodeMissingParameter, "there should be WHERE condition statement for DELETE operation")
return nil, gerror.NewCode(gcode.CodeMissingParameter, "there should be WHERE condition statement for DELETE operation")
}
return m.db.DoDelete(m.GetCtx(), m.getLink(true), m.tables, conditionStr, conditionArgs...)
}

View File

@ -14,7 +14,7 @@ import (
"github.com/gogf/gf/util/gutil"
)
// Fields sets the operation fields of the model, multiple fields joined using char ','.
// Fields appends `fieldNamesOrMapStruct` to the operation fields of the model, multiple fields joined using char ','.
// The parameter `fieldNamesOrMapStruct` can be type of string/map/*map/struct/*struct.
func (m *Model) Fields(fieldNamesOrMapStruct ...interface{}) *Model {
length := len(fieldNamesOrMapStruct)
@ -24,26 +24,32 @@ func (m *Model) Fields(fieldNamesOrMapStruct ...interface{}) *Model {
switch {
// String slice.
case length >= 2:
model := m.getModel()
model.fields = gstr.Join(m.mappingAndFilterToTableFields(gconv.Strings(fieldNamesOrMapStruct), true), ",")
return model
// It need type asserting.
return m.appendFieldsByStr(gstr.Join(
m.mappingAndFilterToTableFields(gconv.Strings(fieldNamesOrMapStruct), true),
",",
))
// It needs type asserting.
case length == 1:
model := m.getModel()
switch r := fieldNamesOrMapStruct[0].(type) {
case string:
model.fields = gstr.Join(m.mappingAndFilterToTableFields([]string{r}, false), ",")
return m.appendFieldsByStr(gstr.Join(
m.mappingAndFilterToTableFields([]string{r}, false), ",",
))
case []string:
model.fields = gstr.Join(m.mappingAndFilterToTableFields(r, true), ",")
return m.appendFieldsByStr(gstr.Join(
m.mappingAndFilterToTableFields(r, true), ",",
))
default:
model.fields = gstr.Join(m.mappingAndFilterToTableFields(gutil.Keys(r), true), ",")
return m.appendFieldsByStr(gstr.Join(
m.mappingAndFilterToTableFields(gutil.Keys(r), true), ",",
))
}
return model
}
return m
}
// FieldsEx sets the excluded operation fields of the model, multiple fields joined using char ','.
// FieldsEx appends `fieldNamesOrMapStruct` to the excluded operation fields of the model,
// multiple fields joined using char ','.
// Note that this function supports only single table operations.
// The parameter `fieldNamesOrMapStruct` can be type of string/map/*map/struct/*struct.
func (m *Model) FieldsEx(fieldNamesOrMapStruct ...interface{}) *Model {
@ -70,6 +76,78 @@ func (m *Model) FieldsEx(fieldNamesOrMapStruct ...interface{}) *Model {
return m
}
// FieldCount formats and appends commonly used field `COUNT(column)` to the select fields of model.
func (m *Model) FieldCount(column string, as ...string) *Model {
asStr := ""
if len(as) > 0 && as[0] != "" {
asStr = fmt.Sprintf(` AS %s`, m.db.GetCore().QuoteWord(as[0]))
}
return m.appendFieldsByStr(fmt.Sprintf(`COUNT(%s)%s`, m.db.GetCore().QuoteWord(column), asStr))
}
// FieldSum formats and appends commonly used field `SUM(column)` to the select fields of model.
func (m *Model) FieldSum(column string, as ...string) *Model {
asStr := ""
if len(as) > 0 && as[0] != "" {
asStr = fmt.Sprintf(` AS %s`, m.db.GetCore().QuoteWord(as[0]))
}
return m.appendFieldsByStr(fmt.Sprintf(`SUM(%s)%s`, m.db.GetCore().QuoteWord(column), asStr))
}
// FieldMin formats and appends commonly used field `MIN(column)` to the select fields of model.
func (m *Model) FieldMin(column string, as ...string) *Model {
asStr := ""
if len(as) > 0 && as[0] != "" {
asStr = fmt.Sprintf(` AS %s`, m.db.GetCore().QuoteWord(as[0]))
}
return m.appendFieldsByStr(fmt.Sprintf(`MIN(%s)%s`, m.db.GetCore().QuoteWord(column), asStr))
}
// FieldMax formats and appends commonly used field `MAX(column)` to the select fields of model.
func (m *Model) FieldMax(column string, as ...string) *Model {
asStr := ""
if len(as) > 0 && as[0] != "" {
asStr = fmt.Sprintf(` AS %s`, m.db.GetCore().QuoteWord(as[0]))
}
return m.appendFieldsByStr(fmt.Sprintf(`MAX(%s)%s`, m.db.GetCore().QuoteWord(column), asStr))
}
// FieldAvg formats and appends commonly used field `AVG(column)` to the select fields of model.
func (m *Model) FieldAvg(column string, as ...string) *Model {
asStr := ""
if len(as) > 0 && as[0] != "" {
asStr = fmt.Sprintf(` AS %s`, m.db.GetCore().QuoteWord(as[0]))
}
return m.appendFieldsByStr(fmt.Sprintf(`AVG(%s)%s`, m.db.GetCore().QuoteWord(column), asStr))
}
func (m *Model) appendFieldsByStr(fields string) *Model {
if fields != "" {
model := m.getModel()
if model.fields == defaultFields {
model.fields = ""
}
if model.fields != "" {
model.fields += ","
}
model.fields += fields
return model
}
return m
}
func (m *Model) appendFieldsExByStr(fieldsEx string) *Model {
if fieldsEx != "" {
model := m.getModel()
if model.fieldsEx != "" {
model.fieldsEx += ","
}
model.fieldsEx += fieldsEx
return model
}
return m
}
// Filter marks filtering the fields which does not exist in the fields of the operated table.
// Note that this function supports only single table operations.
// Deprecated, filter feature is automatically enabled from GoFrame v1.16.0, it is so no longer used.

View File

@ -9,6 +9,7 @@ package gdb
import (
"database/sql"
"github.com/gogf/gf/container/gset"
"github.com/gogf/gf/errors/gcode"
"reflect"
"github.com/gogf/gf/errors/gerror"
@ -82,8 +83,10 @@ func (m *Model) Data(data ...interface{}) *Model {
list[i] = ConvertDataForTableRecord(rv.Index(i).Interface())
}
model.data = list
case reflect.Map:
model.data = ConvertDataForTableRecord(data[0])
case reflect.Struct:
if v, ok := data[0].(apiInterfaces); ok {
var (
@ -97,6 +100,7 @@ func (m *Model) Data(data ...interface{}) *Model {
} else {
model.data = ConvertDataForTableRecord(data[0])
}
default:
model.data = data[0]
}
@ -210,14 +214,13 @@ func (m *Model) doInsertWithOption(insertOption int) (result sql.Result, err err
}
}()
if m.data == nil {
return nil, gerror.NewCode(gerror.CodeMissingParameter, "inserting into table with empty data")
return nil, gerror.NewCode(gcode.CodeMissingParameter, "inserting into table with empty data")
}
var (
list List
nowString = gtime.Now().String()
fieldNameCreate = m.getSoftFieldNameCreated()
fieldNameUpdate = m.getSoftFieldNameUpdated()
fieldNameDelete = m.getSoftFieldNameDeleted()
)
newData, err := m.filterDataForInsertOrUpdate(m.data)
if err != nil {
@ -275,18 +278,17 @@ func (m *Model) doInsertWithOption(insertOption int) (result sql.Result, err err
}
default:
return result, gerror.NewCodef(gerror.CodeInvalidParameter, "unsupported list type:%v", kind)
return result, gerror.NewCodef(gcode.CodeInvalidParameter, "unsupported list type:%v", kind)
}
}
if len(list) < 1 {
return result, gerror.NewCode(gerror.CodeMissingParameter, "data list cannot be empty")
return result, gerror.NewCode(gcode.CodeMissingParameter, "data list cannot be empty")
}
// Automatic handling for creating/updating time.
if !m.unscoped && (fieldNameCreate != "" || fieldNameUpdate != "") {
for k, v := range list {
gutil.MapDelete(v, fieldNameCreate, fieldNameUpdate, fieldNameDelete)
if fieldNameCreate != "" {
v[fieldNameCreate] = nowString
}
@ -366,7 +368,7 @@ func (m *Model) formatDoInsertOption(insertOption int, columnNames []string) (op
default:
return option, gerror.NewCodef(
gerror.CodeInvalidParameter,
gcode.CodeInvalidParameter,
`unsupported OnDuplicate parameter type "%s"`,
reflect.TypeOf(m.onDuplicate),
)
@ -410,7 +412,7 @@ func (m *Model) formatOnDuplicateExKeys(onDuplicateEx interface{}) ([]string, er
default:
return nil, gerror.NewCodef(
gerror.CodeInvalidParameter,
gcode.CodeInvalidParameter,
`unsupported OnDuplicateEx parameter type "%s"`,
reflect.TypeOf(onDuplicateEx),
)

View File

@ -7,9 +7,12 @@
package gdb
const (
optionOmitNil = optionOmitNilWhere | optionOmitNilData
optionOmitEmpty = optionOmitEmptyWhere | optionOmitEmptyData
optionOmitEmptyWhere = 1 << iota // 8
optionOmitEmptyData // 16
optionOmitNilWhere // 32
optionOmitNilData // 64
)
// Option adds extra operation option for the model.
@ -20,7 +23,7 @@ func (m *Model) Option(option int) *Model {
return model
}
// OmitEmpty sets OmitEmpty option for the model, which automatically filers
// OmitEmpty sets optionOmitEmpty option for the model, which automatically filers
// the data and where parameters for `empty` values.
func (m *Model) OmitEmpty() *Model {
model := m.getModel()
@ -28,7 +31,7 @@ func (m *Model) OmitEmpty() *Model {
return model
}
// OmitEmptyWhere sets OmitEmptyWhere option for the model, which automatically filers
// OmitEmptyWhere sets optionOmitEmptyWhere option for the model, which automatically filers
// the Where/Having parameters for `empty` values.
func (m *Model) OmitEmptyWhere() *Model {
model := m.getModel()
@ -36,10 +39,34 @@ func (m *Model) OmitEmptyWhere() *Model {
return model
}
// OmitEmptyData sets OmitEmptyData option for the model, which automatically filers
// OmitEmptyData sets optionOmitEmptyData option for the model, which automatically filers
// the Data parameters for `empty` values.
func (m *Model) OmitEmptyData() *Model {
model := m.getModel()
model.option = model.option | optionOmitEmptyData
return model
}
// OmitNil sets optionOmitNil option for the model, which automatically filers
// the data and where parameters for `nil` values.
func (m *Model) OmitNil() *Model {
model := m.getModel()
model.option = model.option | optionOmitNil
return model
}
// OmitNilWhere sets optionOmitNilWhere option for the model, which automatically filers
// the Where/Having parameters for `nil` values.
func (m *Model) OmitNilWhere() *Model {
model := m.getModel()
model.option = model.option | optionOmitNilWhere
return model
}
// OmitNilData sets optionOmitNilData option for the model, which automatically filers
// the Data parameters for `nil` values.
func (m *Model) OmitNilData() *Model {
model := m.getModel()
model.option = model.option | optionOmitNilData
return model
}

View File

@ -8,6 +8,7 @@ package gdb
import (
"fmt"
"github.com/gogf/gf/errors/gcode"
"github.com/gogf/gf/errors/gerror"
"reflect"
@ -316,7 +317,7 @@ func (m *Model) Scan(pointer interface{}, where ...interface{}) error {
reflectKind = reflectValue.Kind()
if reflectKind != reflect.Ptr {
return gerror.NewCode(gerror.CodeInvalidParameter, `the parameter "pointer" for function Scan should type of pointer`)
return gerror.NewCode(gcode.CodeInvalidParameter, `the parameter "pointer" for function Scan should type of pointer`)
}
for reflectKind == reflect.Ptr {
reflectValue = reflectValue.Elem()
@ -332,7 +333,7 @@ func (m *Model) Scan(pointer interface{}, where ...interface{}) error {
default:
return gerror.NewCode(
gerror.CodeInvalidParameter,
gcode.CodeInvalidParameter,
`element of parameter "pointer" for function Scan should type of struct/*struct/[]struct/[]*struct`,
)
}

View File

@ -9,13 +9,13 @@ package gdb
import (
"database/sql"
"fmt"
"github.com/gogf/gf/errors/gcode"
"reflect"
"github.com/gogf/gf/errors/gerror"
"github.com/gogf/gf/os/gtime"
"github.com/gogf/gf/text/gstr"
"github.com/gogf/gf/util/gconv"
"github.com/gogf/gf/util/gutil"
)
// Update does "UPDATE ... " statement for the model.
@ -39,13 +39,11 @@ func (m *Model) Update(dataAndWhere ...interface{}) (result sql.Result, err erro
}
}()
if m.data == nil {
return nil, gerror.NewCode(gerror.CodeMissingParameter, "updating table with empty data")
return nil, gerror.NewCode(gcode.CodeMissingParameter, "updating table with empty data")
}
var (
updateData = m.data
fieldNameCreate = m.getSoftFieldNameCreated()
fieldNameUpdate = m.getSoftFieldNameUpdated()
fieldNameDelete = m.getSoftFieldNameDeleted()
conditionWhere, conditionExtra, conditionArgs = m.formatCondition(false, false)
)
// Automatically update the record updating time.
@ -61,7 +59,6 @@ func (m *Model) Update(dataAndWhere ...interface{}) (result sql.Result, err erro
switch refKind {
case reflect.Map, reflect.Struct:
dataMap := ConvertDataForTableRecord(m.data)
gutil.MapDelete(dataMap, fieldNameCreate, fieldNameUpdate, fieldNameDelete)
if fieldNameUpdate != "" {
dataMap[fieldNameUpdate] = gtime.Now().String()
}
@ -80,7 +77,7 @@ func (m *Model) Update(dataAndWhere ...interface{}) (result sql.Result, err erro
}
conditionStr := conditionWhere + conditionExtra
if !gstr.ContainsI(conditionStr, " WHERE ") {
return nil, gerror.NewCode(gerror.CodeMissingParameter, "there should be WHERE condition statement for UPDATE operation")
return nil, gerror.NewCode(gcode.CodeMissingParameter, "there should be WHERE condition statement for UPDATE operation")
}
return m.db.DoUpdate(
m.GetCtx(),

View File

@ -109,6 +109,18 @@ func (m *Model) doMappingAndFilterForInsertOrUpdateDataMap(data Map, allowOmitEm
if err != nil {
return nil, err
}
// Remove key-value pairs of which the value is nil.
if allowOmitEmpty && m.option&optionOmitNilData > 0 {
tempMap := make(Map, len(data))
for k, v := range data {
if empty.IsNil(v) {
continue
}
tempMap[k] = v
}
data = tempMap
}
// Remove key-value pairs of which the value is empty.
if allowOmitEmpty && m.option&optionOmitEmptyData > 0 {
tempMap := make(Map, len(data))

View File

@ -8,6 +8,7 @@ package gdb
import (
"fmt"
"github.com/gogf/gf/errors/gcode"
"reflect"
"github.com/gogf/gf/errors/gerror"
@ -17,7 +18,7 @@ import (
"github.com/gogf/gf/text/gstr"
)
// With creates and returns an ORM model based on meta data of given object.
// With creates and returns an ORM model based on metadata of given object.
// It also enables model association operations feature on given `object`.
// It can be called multiple times to add one or more objects to model and enable
// their mode association operations feature.
@ -63,7 +64,7 @@ func (m *Model) doWithScanStruct(pointer interface{}) error {
err error
allowedTypeStrArray = make([]string, 0)
)
fieldMap, err := structs.FieldMap(structs.FieldMapInput{
currentStructFieldMap, err := structs.FieldMap(structs.FieldMapInput{
Pointer: pointer,
PriorityTagArray: nil,
RecursiveOption: structs.RecursiveOptionEmbeddedNoTag,
@ -73,7 +74,7 @@ func (m *Model) doWithScanStruct(pointer interface{}) error {
}
// It checks the with array and automatically calls the ScanList to complete association querying.
if !m.withAll {
for _, field := range fieldMap {
for _, field := range currentStructFieldMap {
for _, withItem := range m.withArray {
withItemReflectValueType, err := structs.StructType(withItem)
if err != nil {
@ -90,7 +91,7 @@ func (m *Model) doWithScanStruct(pointer interface{}) error {
}
}
}
for _, field := range fieldMap {
for _, field := range currentStructFieldMap {
var (
fieldTypeStr = gstr.TrimAll(field.Type().String(), "*[]")
parsedTagOutput = m.parseWithTagInFieldStruct(field)
@ -98,43 +99,40 @@ func (m *Model) doWithScanStruct(pointer interface{}) error {
if parsedTagOutput.With == "" {
continue
}
// Just handler "with" type attribute struct.
// It just handlers "with" type attribute struct, so it ignores other struct types.
if !m.withAll && !gstr.InArray(allowedTypeStrArray, fieldTypeStr) {
continue
}
array := gstr.SplitAndTrim(parsedTagOutput.With, "=")
if len(array) == 1 {
// It supports using only one column name
// It also supports using only one column name
// if both tables associates using the same column name.
array = append(array, parsedTagOutput.With)
}
var (
model *Model
fieldKeys []string
relatedFieldName = array[0]
relatedAttrName = array[1]
relatedFieldValue interface{}
model *Model
fieldKeys []string
relatedSourceName = array[0]
relatedTargetName = array[1]
relatedTargetValue interface{}
)
// Find the value of related attribute from `pointer`.
for attributeName, attributeValue := range fieldMap {
if utils.EqualFoldWithoutChars(attributeName, relatedAttrName) {
relatedFieldValue = attributeValue.Value.Interface()
for attributeName, attributeValue := range currentStructFieldMap {
if utils.EqualFoldWithoutChars(attributeName, relatedTargetName) {
relatedTargetValue = attributeValue.Value.Interface()
break
}
}
if relatedFieldValue == nil {
if relatedTargetValue == nil {
return gerror.NewCodef(
gerror.CodeInvalidParameter,
`cannot find the related value of attribute name "%s" in with tag "%s" for attribute "%s.%s"`,
relatedAttrName, parsedTagOutput.With, reflect.TypeOf(pointer).Elem(), field.Name(),
gcode.CodeInvalidParameter,
`cannot find the target related value of name "%s" in with tag "%s" for attribute "%s.%s"`,
relatedTargetName, parsedTagOutput.With, reflect.TypeOf(pointer).Elem(), field.Name(),
)
}
bindToReflectValue := field.Value
switch bindToReflectValue.Kind() {
case reflect.Array, reflect.Slice:
if bindToReflectValue.CanAddr() {
bindToReflectValue = bindToReflectValue.Addr()
}
if bindToReflectValue.Kind() != reflect.Ptr && bindToReflectValue.CanAddr() {
bindToReflectValue = bindToReflectValue.Addr()
}
// It automatically retrieves struct field names from current attribute struct/slice.
@ -158,7 +156,7 @@ func (m *Model) doWithScanStruct(pointer interface{}) error {
model = model.Order(parsedTagOutput.Order)
}
err = model.Fields(fieldKeys).Where(relatedFieldName, relatedFieldValue).Scan(bindToReflectValue)
err = model.Fields(fieldKeys).Where(relatedSourceName, relatedTargetValue).Scan(bindToReflectValue)
if err != nil {
return err
}
@ -178,7 +176,7 @@ func (m *Model) doWithScanStructs(pointer interface{}) error {
err error
allowedTypeStrArray = make([]string, 0)
)
fieldMap, err := structs.FieldMap(structs.FieldMapInput{
currentStructFieldMap, err := structs.FieldMap(structs.FieldMapInput{
Pointer: pointer,
PriorityTagArray: nil,
RecursiveOption: structs.RecursiveOptionEmbeddedNoTag,
@ -188,7 +186,7 @@ func (m *Model) doWithScanStructs(pointer interface{}) error {
}
// It checks the with array and automatically calls the ScanList to complete association querying.
if !m.withAll {
for _, field := range fieldMap {
for _, field := range currentStructFieldMap {
for _, withItem := range m.withArray {
withItemReflectValueType, err := structs.StructType(withItem)
if err != nil {
@ -206,7 +204,7 @@ func (m *Model) doWithScanStructs(pointer interface{}) error {
}
}
for fieldName, field := range fieldMap {
for fieldName, field := range currentStructFieldMap {
var (
fieldTypeStr = gstr.TrimAll(field.Type().String(), "*[]")
parsedTagOutput = m.parseWithTagInFieldStruct(field)
@ -224,24 +222,24 @@ func (m *Model) doWithScanStructs(pointer interface{}) error {
array = append(array, parsedTagOutput.With)
}
var (
model *Model
fieldKeys []string
relatedFieldName = array[0]
relatedAttrName = array[1]
relatedFieldValue interface{}
model *Model
fieldKeys []string
relatedSourceName = array[0]
relatedTargetName = array[1]
relatedTargetValue interface{}
)
// Find the value slice of related attribute from `pointer`.
for attributeName, _ := range fieldMap {
if utils.EqualFoldWithoutChars(attributeName, relatedAttrName) {
relatedFieldValue = ListItemValuesUnique(pointer, attributeName)
for attributeName, _ := range currentStructFieldMap {
if utils.EqualFoldWithoutChars(attributeName, relatedTargetName) {
relatedTargetValue = ListItemValuesUnique(pointer, attributeName)
break
}
}
if relatedFieldValue == nil {
if relatedTargetValue == nil {
return gerror.NewCodef(
gerror.CodeInvalidParameter,
gcode.CodeInvalidParameter,
`cannot find the related value for attribute name "%s" of with tag "%s"`,
relatedAttrName, parsedTagOutput.With,
relatedTargetName, parsedTagOutput.With,
)
}
@ -266,7 +264,9 @@ func (m *Model) doWithScanStructs(pointer interface{}) error {
model = model.Order(parsedTagOutput.Order)
}
err = model.Fields(fieldKeys).Where(relatedFieldName, relatedFieldValue).ScanList(pointer, fieldName, parsedTagOutput.With)
err = model.Fields(fieldKeys).
Where(relatedSourceName, relatedTargetValue).
ScanList(pointer, fieldName, parsedTagOutput.With)
if err != nil {
return err
}

View File

@ -9,6 +9,7 @@ package gdb
import (
"context"
"database/sql"
"github.com/gogf/gf/errors/gcode"
"github.com/gogf/gf/errors/gerror"
"github.com/gogf/gf/os/gtime"
@ -59,7 +60,7 @@ func (s *Stmt) doStmtCommit(ctx context.Context, stmtType string, args ...interf
result = s.Stmt.QueryRowContext(ctx, args...)
default:
panic(gerror.NewCodef(gerror.CodeInvalidParameter, `invalid stmtType: %s`, stmtType))
panic(gerror.NewCodef(gcode.CodeInvalidParameter, `invalid stmtType: %s`, stmtType))
}
var (
timestampMilli2 = gtime.TimestampMilli()

View File

@ -8,6 +8,7 @@ package gdb
import (
"database/sql"
"github.com/gogf/gf/errors/gcode"
"github.com/gogf/gf/errors/gerror"
"github.com/gogf/gf/text/gstr"
"github.com/gogf/gf/util/gconv"
@ -55,7 +56,7 @@ func doScanList(model *Model, result Result, listPointer interface{}, bindToAttr
}
// Necessary checks for parameters.
if bindToAttrName == "" {
return gerror.NewCode(gerror.CodeInvalidParameter, `bindToAttrName should not be empty`)
return gerror.NewCode(gcode.CodeInvalidParameter, `bindToAttrName should not be empty`)
}
var (
@ -67,12 +68,12 @@ func doScanList(model *Model, result Result, listPointer interface{}, bindToAttr
reflectKind = reflectValue.Kind()
}
if reflectKind != reflect.Ptr {
return gerror.NewCodef(gerror.CodeInvalidParameter, "listPointer should be type of *[]struct/*[]*struct, but got: %v", reflectKind)
return gerror.NewCodef(gcode.CodeInvalidParameter, "listPointer should be type of *[]struct/*[]*struct, but got: %v", reflectKind)
}
reflectValue = reflectValue.Elem()
reflectKind = reflectValue.Kind()
if reflectKind != reflect.Slice && reflectKind != reflect.Array {
return gerror.NewCodef(gerror.CodeInvalidParameter, "listPointer should be type of *[]struct/*[]*struct, but got: %v", reflectKind)
return gerror.NewCodef(gcode.CodeInvalidParameter, "listPointer should be type of *[]struct/*[]*struct, but got: %v", reflectKind)
}
length := len(result)
if length == 0 {
@ -136,7 +137,7 @@ func doScanList(model *Model, result Result, listPointer interface{}, bindToAttr
relationBindToSubAttrName = array[1]
if key, _ := gutil.MapPossibleItemByKey(result[0].Map(), relationResultFieldName); key == "" {
return gerror.NewCodef(
gerror.CodeInvalidParameter,
gcode.CodeInvalidParameter,
`cannot find possible related table field name "%s" from given relation key "%s"`,
relationResultFieldName,
relationKVStr,
@ -145,14 +146,14 @@ func doScanList(model *Model, result Result, listPointer interface{}, bindToAttr
relationResultFieldName = key
}
} else {
return gerror.NewCode(gerror.CodeInvalidParameter, `parameter relationKV should be format of "ResultFieldName:BindToAttrName"`)
return gerror.NewCode(gcode.CodeInvalidParameter, `parameter relationKV should be format of "ResultFieldName:BindToAttrName"`)
}
if relationResultFieldName != "" {
// Note that the value might be type of slice.
relationDataMap = result.MapKeyValue(relationResultFieldName)
}
if len(relationDataMap) == 0 {
return gerror.NewCodef(gerror.CodeInvalidParameter, `cannot find the relation data map, maybe invalid relation given "%v"`, relationKV)
return gerror.NewCodef(gcode.CodeInvalidParameter, `cannot find the relation data map, maybe invalid relation given "%v"`, relationKV)
}
}
// Bind to target attribute.
@ -166,7 +167,7 @@ func doScanList(model *Model, result Result, listPointer interface{}, bindToAttr
if arrayItemType.Kind() == reflect.Ptr {
if bindToAttrField, ok = arrayItemType.Elem().FieldByName(bindToAttrName); !ok {
return gerror.NewCodef(
gerror.CodeInvalidParameter,
gcode.CodeInvalidParameter,
`invalid parameter bindToAttrName: cannot find attribute with name "%s" from slice element`,
bindToAttrName,
)
@ -174,7 +175,7 @@ func doScanList(model *Model, result Result, listPointer interface{}, bindToAttr
} else {
if bindToAttrField, ok = arrayItemType.FieldByName(bindToAttrName); !ok {
return gerror.NewCodef(
gerror.CodeInvalidParameter,
gcode.CodeInvalidParameter,
`invalid parameter bindToAttrName: cannot find attribute with name "%s" from slice element`,
bindToAttrName,
)
@ -219,7 +220,7 @@ func doScanList(model *Model, result Result, listPointer interface{}, bindToAttr
relationFromAttrValue = arrayElemValue
}
if len(relationDataMap) > 0 && !relationFromAttrValue.IsValid() {
return gerror.NewCodef(gerror.CodeInvalidParameter, `invalid relation specified: "%v"`, relationKV)
return gerror.NewCodef(gcode.CodeInvalidParameter, `invalid relation specified: "%v"`, relationKV)
}
// Check and find possible bind to attribute name.
if relationKVStr != "" && !relationBindToSubAttrNameChecked {
@ -234,7 +235,7 @@ func doScanList(model *Model, result Result, listPointer interface{}, bindToAttr
}
if key, _ := gutil.MapPossibleItemByKey(filedMap, relationBindToSubAttrName); key == "" {
return gerror.NewCodef(
gerror.CodeInvalidParameter,
gcode.CodeInvalidParameter,
`cannot find possible related attribute name "%s" from given relation key "%s"`,
relationBindToSubAttrName,
relationKVStr,
@ -265,11 +266,11 @@ func doScanList(model *Model, result Result, listPointer interface{}, bindToAttr
}
} else {
// May be the attribute does not exist yet.
return gerror.NewCodef(gerror.CodeInvalidParameter, `invalid relation specified: "%v"`, relationKV)
return gerror.NewCodef(gcode.CodeInvalidParameter, `invalid relation specified: "%v"`, relationKV)
}
} else {
return gerror.NewCodef(
gerror.CodeInvalidParameter,
gcode.CodeInvalidParameter,
`relationKey should not be empty as field "%s" is slice`,
bindToAttrName,
)
@ -301,7 +302,7 @@ func doScanList(model *Model, result Result, listPointer interface{}, bindToAttr
}
} else {
// May be the attribute does not exist yet.
return gerror.NewCodef(gerror.CodeInvalidParameter, `invalid relation specified: "%v"`, relationKV)
return gerror.NewCodef(gcode.CodeInvalidParameter, `invalid relation specified: "%v"`, relationKV)
}
} else {
if i >= len(result) {
@ -345,7 +346,7 @@ func doScanList(model *Model, result Result, listPointer interface{}, bindToAttr
}
} else {
// May be the attribute does not exist yet.
return gerror.NewCodef(gerror.CodeInvalidParameter, `invalid relation specified: "%v"`, relationKV)
return gerror.NewCodef(gcode.CodeInvalidParameter, `invalid relation specified: "%v"`, relationKV)
}
} else {
if i >= len(result) {
@ -369,7 +370,7 @@ func doScanList(model *Model, result Result, listPointer interface{}, bindToAttr
}
default:
return gerror.NewCodef(gerror.CodeInvalidParameter, `unsupported attribute type: %s`, bindToAttrKind.String())
return gerror.NewCodef(gcode.CodeInvalidParameter, `unsupported attribute type: %s`, bindToAttrKind.String())
}
}
reflect.ValueOf(listPointer).Elem().Set(arrayValue)

View File

@ -368,6 +368,7 @@ PRIMARY KEY (id)
gtest.Assert(err, nil)
}
}
gtest.C(t, func(t *gtest.T) {
var users []*User
err := db.With(User{}).
@ -1866,3 +1867,127 @@ func Test_Table_Relation_With_MultipleDepends_Embedded(t *testing.T) {
t.Assert(tableA[1].TableB.TableC.Id, 300)
})
}
func Test_Table_Relation_WithAll_Embedded_Meta_NameMatchingRule(t *testing.T) {
var (
tableUser = "user1"
tableUserDetail = "user_detail1"
tableUserScores = "user_scores1"
)
if _, err := db.Exec(fmt.Sprintf(`
CREATE TABLE IF NOT EXISTS %s (
id int(10) unsigned NOT NULL AUTO_INCREMENT,
name varchar(45) NOT NULL,
PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
`, tableUser)); err != nil {
gtest.Error(err)
}
defer dropTable(tableUser)
if _, err := db.Exec(fmt.Sprintf(`
CREATE TABLE IF NOT EXISTS %s (
user_id int(10) unsigned NOT NULL,
address varchar(45) NOT NULL,
PRIMARY KEY (user_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
`, tableUserDetail)); err != nil {
gtest.Error(err)
}
defer dropTable(tableUserDetail)
if _, err := db.Exec(fmt.Sprintf(`
CREATE TABLE IF NOT EXISTS %s (
id int(10) unsigned NOT NULL AUTO_INCREMENT,
user_id int(10) unsigned NOT NULL,
score int(10) unsigned NOT NULL,
PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
`, tableUserScores)); err != nil {
gtest.Error(err)
}
defer dropTable(tableUserScores)
type UserDetail struct {
gmeta.Meta `orm:"table:user_detail1"`
UserID int `json:"user_id"`
Address string `json:"address"`
}
type UserScores struct {
gmeta.Meta `orm:"table:user_scores1"`
ID int `json:"id"`
UserID int `json:"user_id"`
Score int `json:"score"`
}
// For Test Only
type UserEmbedded struct {
ID int `json:"id"`
Name string `json:"name"`
}
type User struct {
gmeta.Meta `orm:"table:user1"`
UserEmbedded
UserDetail UserDetail `orm:"with:user_id=id"`
UserScores []*UserScores `orm:"with:user_id=id"`
}
// Initialize the data.
var err error
for i := 1; i <= 5; i++ {
// User.
_, err = db.Insert(tableUser, g.Map{
"id": i,
"name": fmt.Sprintf(`name_%d`, i),
})
gtest.AssertNil(err)
// Detail.
_, err = db.Insert(tableUserDetail, g.Map{
"user_id": i,
"address": fmt.Sprintf(`address_%d`, i),
})
gtest.AssertNil(err)
// Scores.
for j := 1; j <= 5; j++ {
_, err = db.Insert(tableUserScores, g.Map{
"user_id": i,
"score": j,
})
gtest.AssertNil(err)
}
}
db.SetDebug(true)
defer db.SetDebug(false)
gtest.C(t, func(t *gtest.T) {
var user *User
err := db.Model(tableUser).WithAll().Where("id", 3).Scan(&user)
t.AssertNil(err)
t.Assert(user.ID, 3)
t.AssertNE(user.UserDetail, nil)
t.Assert(user.UserDetail.UserID, 3)
t.Assert(user.UserDetail.Address, `address_3`)
t.Assert(len(user.UserScores), 5)
t.Assert(user.UserScores[0].UserID, 3)
t.Assert(user.UserScores[0].Score, 1)
t.Assert(user.UserScores[4].UserID, 3)
t.Assert(user.UserScores[4].Score, 5)
})
gtest.C(t, func(t *gtest.T) {
var user User
err := db.Model(tableUser).WithAll().Where("id", 4).Scan(&user)
t.AssertNil(err)
t.Assert(user.ID, 4)
t.AssertNE(user.UserDetail, nil)
t.Assert(user.UserDetail.UserID, 4)
t.Assert(user.UserDetail.Address, `address_4`)
t.Assert(len(user.UserScores), 5)
t.Assert(user.UserScores[0].UserID, 4)
t.Assert(user.UserScores[0].Score, 1)
t.Assert(user.UserScores[4].UserID, 4)
t.Assert(user.UserScores[4].Score, 5)
})
}

View File

@ -7,10 +7,10 @@
package gdb_test
import (
"testing"
"github.com/go-sql-driver/mysql"
"github.com/gogf/gf/database/gdb"
"github.com/gogf/gf/test/gtest"
"testing"
)
func Test_Instance(t *testing.T) {
@ -27,3 +27,16 @@ func Test_Instance(t *testing.T) {
t.Assert(err2, nil)
})
}
// Fix issue: https://github.com/gogf/gf/issues/819
func Test_Func_ConvertDataForTableRecord(t *testing.T) {
type Test struct {
ResetPasswordTokenAt mysql.NullTime `orm:"reset_password_token_at"`
}
gtest.C(t, func(t *gtest.T) {
m := gdb.ConvertDataForTableRecord(new(Test))
t.Assert(len(m), 1)
t.AssertNE(m["reset_password_token_at"], nil)
t.Assert(m["reset_password_token_at"], new(mysql.NullTime))
})
}

View File

@ -30,7 +30,7 @@ func Test_Ctx(t *testing.T) {
}
func Test_Ctx_Query(t *testing.T) {
db.GetLogger().(gdb.LoggerImp).SetCtxKeys("SpanId", "TraceId")
db.GetLogger().SetCtxKeys("SpanId", "TraceId")
gtest.C(t, func(t *gtest.T) {
db.SetDebug(true)
defer db.SetDebug(false)
@ -48,7 +48,7 @@ func Test_Ctx_Query(t *testing.T) {
func Test_Ctx_Model(t *testing.T) {
table := createInitTable()
defer dropTable(table)
db.GetLogger().(gdb.LoggerImp).SetCtxKeys("SpanId", "TraceId")
db.GetLogger().SetCtxKeys("SpanId", "TraceId")
gtest.C(t, func(t *gtest.T) {
db.SetDebug(true)
defer db.SetDebug(false)

View File

@ -12,7 +12,6 @@ import (
"github.com/gogf/gf/os/gcmd"
"github.com/gogf/gf/os/gtime"
"github.com/gogf/gf/test/gtest"
"github.com/gogf/mysql"
"testing"
)
@ -292,19 +291,6 @@ CREATE TABLE %s (
})
}
// Fix issue: https://github.com/gogf/gf/issues/819
func Test_Func_ConvertDataForTableRecord(t *testing.T) {
type Test struct {
ResetPasswordTokenAt mysql.NullTime `orm:"reset_password_token_at"`
}
gtest.C(t, func(t *gtest.T) {
m := ConvertDataForTableRecord(new(Test))
t.Assert(len(m), 1)
t.AssertNE(m["reset_password_token_at"], nil)
t.Assert(m["reset_password_token_at"], new(mysql.NullTime))
})
}
func Test_isSubQuery(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
t.Assert(isSubQuery("user"), false)

View File

@ -2132,24 +2132,75 @@ func Test_Model_Option_List(t *testing.T) {
}
func Test_Model_OmitEmpty(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
table := fmt.Sprintf(`table_%s`, gtime.TimestampNanoStr())
if _, err := db.Exec(fmt.Sprintf(`
table := fmt.Sprintf(`table_%s`, gtime.TimestampNanoStr())
if _, err := db.Exec(fmt.Sprintf(`
CREATE TABLE IF NOT EXISTS %s (
id int(10) unsigned NOT NULL AUTO_INCREMENT,
name varchar(45) NOT NULL,
PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
`, table)); err != nil {
gtest.Error(err)
}
defer dropTable(table)
gtest.Error(err)
}
defer dropTable(table)
gtest.C(t, func(t *gtest.T) {
_, err := db.Model(table).OmitEmpty().Data(g.Map{
"id": 1,
"name": "",
}).Save()
t.AssertNE(err, nil)
})
gtest.C(t, func(t *gtest.T) {
_, err := db.Model(table).OmitEmptyData().Data(g.Map{
"id": 1,
"name": "",
}).Save()
t.AssertNE(err, nil)
})
gtest.C(t, func(t *gtest.T) {
_, err := db.Model(table).OmitEmptyWhere().Data(g.Map{
"id": 1,
"name": "",
}).Save()
t.Assert(err, nil)
})
}
func Test_Model_OmitNil(t *testing.T) {
table := fmt.Sprintf(`table_%s`, gtime.TimestampNanoStr())
if _, err := db.Exec(fmt.Sprintf(`
CREATE TABLE IF NOT EXISTS %s (
id int(10) unsigned NOT NULL AUTO_INCREMENT,
name varchar(45) NOT NULL,
PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
`, table)); err != nil {
gtest.Error(err)
}
defer dropTable(table)
gtest.C(t, func(t *gtest.T) {
_, err := db.Model(table).OmitNil().Data(g.Map{
"id": 1,
"name": nil,
}).Save()
t.AssertNE(err, nil)
})
gtest.C(t, func(t *gtest.T) {
_, err := db.Model(table).OmitNil().Data(g.Map{
"id": 1,
"name": "",
}).Save()
t.Assert(err, nil)
})
gtest.C(t, func(t *gtest.T) {
_, err := db.Model(table).OmitNilWhere().Data(g.Map{
"id": 1,
"name": "",
}).Save()
t.Assert(err, nil)
})
}
func Test_Model_Option_Where(t *testing.T) {
@ -3768,3 +3819,96 @@ func Test_Model_Handler(t *testing.T) {
t.Assert(all[2]["id"], 4)
})
}
func Test_Model_FieldCount(t *testing.T) {
table := createInitTable()
defer dropTable(table)
gtest.C(t, func(t *gtest.T) {
all, err := db.Model(table).Fields("id").FieldCount("id", "total").Group("id").OrderAsc("id").All()
t.AssertNil(err)
t.Assert(len(all), TableSize)
t.Assert(all[0]["id"], 1)
t.Assert(all[0]["total"], 1)
})
}
func Test_Model_FieldMax(t *testing.T) {
table := createInitTable()
defer dropTable(table)
gtest.C(t, func(t *gtest.T) {
all, err := db.Model(table).Fields("id").FieldMax("id", "total").Group("id").OrderAsc("id").All()
t.AssertNil(err)
t.Assert(len(all), TableSize)
t.Assert(all[0]["id"], 1)
t.Assert(all[0]["total"], 1)
})
}
func Test_Model_FieldMin(t *testing.T) {
table := createInitTable()
defer dropTable(table)
gtest.C(t, func(t *gtest.T) {
all, err := db.Model(table).Fields("id").FieldMin("id", "total").Group("id").OrderAsc("id").All()
t.AssertNil(err)
t.Assert(len(all), TableSize)
t.Assert(all[0]["id"], 1)
t.Assert(all[0]["total"], 1)
})
}
func Test_Model_FieldAvg(t *testing.T) {
table := createInitTable()
defer dropTable(table)
gtest.C(t, func(t *gtest.T) {
all, err := db.Model(table).Fields("id").FieldAvg("id", "total").Group("id").OrderAsc("id").All()
t.AssertNil(err)
t.Assert(len(all), TableSize)
t.Assert(all[0]["id"], 1)
t.Assert(all[0]["total"], 1)
})
}
// https://github.com/gogf/gf/issues/1387
func Test_Model_GTime_DefaultValue(t *testing.T) {
table := createTable()
defer dropTable(table)
gtest.C(t, func(t *gtest.T) {
type User struct {
Id int
Passport string
Password string
Nickname string
CreateTime *gtime.Time
}
data := User{
Id: 1,
Passport: "user_1",
Password: "pass_1",
Nickname: "name_1",
}
// Insert
_, err := db.Model(table).Data(data).Insert()
t.AssertNil(err)
// Select
var (
user *User
)
err = db.Model(table).Scan(&user)
t.AssertNil(err)
t.Assert(user.Passport, data.Passport)
t.Assert(user.Password, data.Password)
t.Assert(user.CreateTime, data.CreateTime)
t.Assert(user.Nickname, data.Nickname)
// Insert
user.Id = 2
_, err = db.Model(table).Data(user).Insert()
t.AssertNil(err)
})
}

View File

@ -9,6 +9,7 @@ package gdb_test
import (
"database/sql"
"github.com/gogf/gf/database/gdb"
"github.com/gogf/gf/errors/gcode"
"github.com/gogf/gf/errors/gerror"
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/os/gtime"
@ -408,7 +409,7 @@ func (user *User) UnmarshalValue(value interface{}) error {
}
return nil
}
return gerror.NewCodef(gerror.CodeInvalidParameter, `unsupported value type for UnmarshalValue: %v`, reflect.TypeOf(value))
return gerror.NewCodef(gcode.CodeInvalidParameter, `unsupported value type for UnmarshalValue: %v`, reflect.TypeOf(value))
}
func Test_Model_Scan_UnmarshalValue(t *testing.T) {

View File

@ -663,6 +663,9 @@ CREATE TABLE %s (
}
defer dropTable(table)
//db.SetDebug(true)
//defer db.SetDebug(false)
type Entity struct {
Id uint64 `orm:"id,primary" json:"id"`
Name string `orm:"name" json:"name"`
@ -679,7 +682,7 @@ CREATE TABLE %s (
UpdateAt: nil,
DeleteAt: nil,
}
r, err := db.Model(table).Data(dataInsert).Insert()
r, err := db.Model(table).Data(dataInsert).OmitEmpty().Insert()
t.AssertNil(err)
n, _ := r.RowsAffected()
t.Assert(n, 1)
@ -702,7 +705,7 @@ CREATE TABLE %s (
UpdateAt: nil,
DeleteAt: nil,
}
r, err = db.Model(table).Data(dataSave).Save()
r, err = db.Model(table).Data(dataSave).OmitEmpty().Save()
t.AssertNil(err)
n, _ = r.RowsAffected()
t.Assert(n, 2)
@ -726,7 +729,7 @@ CREATE TABLE %s (
UpdateAt: nil,
DeleteAt: nil,
}
r, err = db.Model(table).Data(dataUpdate).WherePri(1).Update()
r, err = db.Model(table).Data(dataUpdate).WherePri(1).OmitEmpty().Update()
t.AssertNil(err)
n, _ = r.RowsAffected()
t.Assert(n, 1)
@ -747,7 +750,7 @@ CREATE TABLE %s (
UpdateAt: nil,
DeleteAt: nil,
}
r, err = db.Model(table).Data(dataReplace).Replace()
r, err = db.Model(table).Data(dataReplace).OmitEmpty().Replace()
t.AssertNil(err)
n, _ = r.RowsAffected()
t.Assert(n, 2)

View File

@ -9,6 +9,7 @@ package gdb_test
import (
"context"
"fmt"
"github.com/gogf/gf/os/gctx"
"testing"
"github.com/gogf/gf/database/gdb"
@ -1116,3 +1117,34 @@ func Test_Transaction_Nested_SavePoint_RollbackTo(t *testing.T) {
t.Assert(all[0]["id"], 1)
})
}
func Test_Transaction_Method(t *testing.T) {
table := createTable()
defer dropTable(table)
gtest.C(t, func(t *gtest.T) {
var err error
err = db.Transaction(gctx.New(), func(ctx context.Context, tx *gdb.TX) error {
_, err = db.Model(table).Ctx(ctx).Data(g.Map{
"id": 1,
"passport": "t1",
"password": "25d55ad283aa400af464c76d713c07ad",
"nickname": "T1",
"create_time": gtime.Now().String(),
}).Insert()
t.AssertNil(err)
_, err = db.Ctx(ctx).Exec(fmt.Sprintf(
"insert into %s(`passport`,`password`,`nickname`,`create_time`,`id`) "+
"VALUES('t2','25d55ad283aa400af464c76d713c07ad','T2','2021-08-25 21:53:00',2) ",
table))
t.AssertNil(err)
return gerror.New("rollback")
})
t.AssertNE(err, nil)
count, err := db.Model(table).Count()
t.AssertNil(err)
t.Assert(count, 0)
})
}

View File

@ -8,6 +8,7 @@ package gredis
import (
"context"
"github.com/gogf/gf/errors/gcode"
"github.com/gogf/gf/errors/gerror"
"github.com/gogf/gf/internal/intlog"
@ -114,7 +115,7 @@ func ConfigFromStr(str string) (config *Config, err error) {
config.Port = DefaultRedisPort
}
} else {
err = gerror.NewCodef(gerror.CodeInvalidConfiguration, `invalid redis configuration: "%s"`, str)
err = gerror.NewCodef(gcode.CodeInvalidConfiguration, `invalid redis configuration: "%s"`, str)
}
return
}

View File

@ -9,6 +9,7 @@ package gredis
import (
"context"
"github.com/gogf/gf/container/gvar"
"github.com/gogf/gf/errors/gcode"
"github.com/gogf/gf/errors/gerror"
"github.com/gogf/gf/internal/json"
"github.com/gogf/gf/os/gtime"
@ -50,7 +51,7 @@ func (c *Conn) do(timeout time.Duration, commandName string, args ...interface{}
if timeout > 0 {
conn, ok := c.Conn.(redis.ConnWithTimeout)
if !ok {
return gvar.New(nil), gerror.NewCode(gerror.CodeNotSupported, `current connection does not support "ConnWithTimeout"`)
return gvar.New(nil), gerror.NewCode(gcode.CodeNotSupported, `current connection does not support "ConnWithTimeout"`)
}
return conn.DoWithTimeout(timeout, commandName, args...)
}
@ -107,7 +108,7 @@ func (c *Conn) ReceiveVar() (*gvar.Var, error) {
func (c *Conn) ReceiveVarWithTimeout(timeout time.Duration) (*gvar.Var, error) {
conn, ok := c.Conn.(redis.ConnWithTimeout)
if !ok {
return gvar.New(nil), gerror.NewCode(gerror.CodeNotSupported, `current connection does not support "ConnWithTimeout"`)
return gvar.New(nil), gerror.NewCode(gcode.CodeNotSupported, `current connection does not support "ConnWithTimeout"`)
}
return resultToVar(conn.ReceiveWithTimeout(timeout))
}

View File

@ -8,7 +8,6 @@ package gdebug
import (
"fmt"
"github.com/gogf/gf/internal/utils"
"os"
"os/exec"
"path/filepath"
@ -34,7 +33,7 @@ func init() {
goRootForFilter = strings.Replace(goRootForFilter, "\\", "/", -1)
}
// Initialize internal package variable: selfPath.
selfPath, _ := exec.LookPath(os.Args[0])
selfPath, _ = exec.LookPath(os.Args[0])
if selfPath != "" {
selfPath, _ = filepath.Abs(selfPath)
}
@ -46,14 +45,14 @@ func init() {
// Caller returns the function name and the absolute file path along with its line
// number of the caller.
func Caller(skip ...int) (function string, path string, line int) {
return CallerWithFilter("", skip...)
return CallerWithFilter(nil, skip...)
}
// CallerWithFilter returns the function name and 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) (function string, path string, line int) {
// The parameter `filters` is used to filter the path of the caller.
func CallerWithFilter(filters []string, skip ...int) (function string, path string, line int) {
var (
number = 0
ok = true
@ -61,14 +60,17 @@ func CallerWithFilter(filter string, skip ...int) (function string, path string,
if len(skip) > 0 {
number = skip[0]
}
pc, file, line, start := callerFromIndex([]string{filter})
pc, file, line, start := callerFromIndex(filters)
if start != -1 {
for i := start + number; i < maxCallerDepth; i++ {
if i != start {
pc, file, line, ok = runtime.Caller(i)
}
if ok {
function := ""
if filterFileByFilters(file, filters) {
continue
}
function = ""
if fn := runtime.FuncForPC(pc); fn == nil {
function = "unknown"
} else {
@ -86,30 +88,14 @@ func CallerWithFilter(filter string, skip ...int) (function string, path string,
// callerFromIndex returns the caller position and according information exclusive of the
// debug package.
//
// VERY NOTE THAT, the returned index value should be <index - 1> as the caller's start point.
// VERY NOTE THAT, the returned index value should be `index - 1` as the caller's start point.
func callerFromIndex(filters []string) (pc uintptr, file string, line int, index int) {
var filtered, ok bool
var ok bool
for index = 0; index < maxCallerDepth; index++ {
if pc, file, line, ok = runtime.Caller(index); ok {
filtered = false
for _, filter := range filters {
if filter != "" && strings.Contains(file, filter) {
filtered = true
break
}
}
if filtered {
if filterFileByFilters(file, filters) {
continue
}
if !utils.IsDebugEnabled() {
if strings.Contains(file, utils.StackFilterKeyForGoFrame) {
continue
}
} else {
if strings.Contains(file, stackFilterKey) {
continue
}
}
if index > 0 {
index--
}
@ -119,6 +105,27 @@ func callerFromIndex(filters []string) (pc uintptr, file string, line int, index
return 0, "", -1, -1
}
func filterFileByFilters(file string, filters []string) (filtered bool) {
// Filter empty file.
if file == "" {
return true
}
// Filter gdebug package callings.
if strings.Contains(file, stackFilterKey) {
return true
}
for _, filter := range filters {
if filter != "" && strings.Contains(file, filter) {
return true
}
}
// GOROOT filter.
if goRootForFilter != "" && len(file) >= len(goRootForFilter) && file[0:len(goRootForFilter)] == goRootForFilter {
return true
}
return false
}
// CallerPackage returns the package name of the caller.
func CallerPackage() string {
function, _, _ := Caller()
@ -166,12 +173,12 @@ func CallerFileLineShort() string {
return fmt.Sprintf(`%s:%d`, filepath.Base(path), line)
}
// FuncPath returns the complete function path of given <f>.
// FuncPath returns the complete function path of given `f`.
func FuncPath(f interface{}) string {
return runtime.FuncForPC(reflect.ValueOf(f).Pointer()).Name()
}
// FuncName returns the function name of given <f>.
// FuncName returns the function name of given `f`.
func FuncName(f interface{}) string {
path := FuncPath(f)
if path == "" {

View File

@ -9,9 +9,7 @@ package gdebug
import (
"bytes"
"fmt"
"github.com/gogf/gf/internal/utils"
"runtime"
"strings"
)
// PrintStack prints to standard error the stack trace returned by runtime.Stack.
@ -22,21 +20,21 @@ func PrintStack(skip ...int) {
// 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...)
return StackWithFilter(nil, 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 {
return StackWithFilters([]string{filter}, skip...)
// The parameter `filter` is used to filter the path of the caller.
func StackWithFilter(filters []string, skip ...int) string {
return StackWithFilters(filters, skip...)
}
// StackWithFilters 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 <filters> is a slice of strings, which are used to filter the path of the
// The parameter `filters` is a slice of strings, which are used to filter the path of the
// caller.
//
// TODO Improve the performance using debug.Stack.
@ -50,7 +48,6 @@ func StackWithFilters(filters []string, skip ...int) string {
space = " "
index = 1
buffer = bytes.NewBuffer(nil)
filtered = false
ok = true
pc, file, line, start = callerFromIndex(filters)
)
@ -59,38 +56,9 @@ func StackWithFilters(filters []string, skip ...int) string {
pc, file, line, ok = runtime.Caller(i)
}
if ok {
// Filter empty file.
if file == "" {
if filterFileByFilters(file, filters) {
continue
}
// GOROOT filter.
if goRootForFilter != "" &&
len(file) >= len(goRootForFilter) &&
file[0:len(goRootForFilter)] == goRootForFilter {
continue
}
// Custom filtering.
filtered = false
for _, filter := range filters {
if filter != "" && strings.Contains(file, filter) {
filtered = true
break
}
}
if filtered {
continue
}
if !utils.IsDebugEnabled() {
if strings.Contains(file, utils.StackFilterKeyForGoFrame) {
continue
}
} else {
if strings.Contains(file, stackFilterKey) {
continue
}
}
if fn := runtime.FuncForPC(pc); fn == nil {
name = "unknown"
} else {

View File

@ -7,12 +7,13 @@
package gdebug
import (
"io/ioutil"
"path/filepath"
)
// TestDataPath retrieves and returns the testdata path of current package,
// which is used for unit testing cases only.
// The optional parameter <names> specifies the its sub-folders/sub-files,
// The optional parameter `names` specifies the sub-folders/sub-files,
// which will be joined with current system separator and returned with the path.
func TestDataPath(names ...string) string {
path := CallerDirectory() + string(filepath.Separator) + "testdata"
@ -21,3 +22,15 @@ func TestDataPath(names ...string) string {
}
return path
}
// TestDataContent retrieves and returns the file content for specified testdata path of current package
func TestDataContent(names ...string) string {
path := TestDataPath(names...)
if path != "" {
data, err := ioutil.ReadFile(path)
if err == nil {
return string(data)
}
}
return ""
}

View File

@ -21,6 +21,7 @@ package gcharset
import (
"bytes"
"github.com/gogf/gf/errors/gcode"
"github.com/gogf/gf/errors/gerror"
"io/ioutil"
@ -59,11 +60,11 @@ func Convert(dstCharset string, srcCharset string, src string) (dst string, err
transform.NewReader(bytes.NewReader([]byte(src)), e.NewDecoder()),
)
if err != nil {
return "", gerror.WrapCodef(gerror.CodeInternalError, err, "%s to utf8 failed", srcCharset)
return "", gerror.WrapCodef(gcode.CodeInternalError, err, "%s to utf8 failed", srcCharset)
}
src = string(tmp)
} else {
return dst, gerror.NewCodef(gerror.CodeInvalidParameter, "unsupported srcCharset: %s", srcCharset)
return dst, gerror.NewCodef(gcode.CodeInvalidParameter, "unsupported srcCharset: %s", srcCharset)
}
}
// Do the converting from UTF-8 to <dstCharset>.
@ -73,11 +74,11 @@ func Convert(dstCharset string, srcCharset string, src string) (dst string, err
transform.NewReader(bytes.NewReader([]byte(src)), e.NewEncoder()),
)
if err != nil {
return "", gerror.WrapCodef(gerror.CodeInternalError, err, "utf to %s failed", dstCharset)
return "", gerror.WrapCodef(gcode.CodeInternalError, err, "utf to %s failed", dstCharset)
}
dst = string(tmp)
} else {
return dst, gerror.NewCodef(gerror.CodeInvalidParameter, "unsupported dstCharset: %s", dstCharset)
return dst, gerror.NewCodef(gcode.CodeInvalidParameter, "unsupported dstCharset: %s", dstCharset)
}
} else {
dst = src

View File

@ -11,6 +11,7 @@ import (
"bufio"
"bytes"
"fmt"
"github.com/gogf/gf/errors/gcode"
"github.com/gogf/gf/errors/gerror"
"github.com/gogf/gf/internal/json"
"io"
@ -70,7 +71,7 @@ func Decode(data []byte) (res map[string]interface{}, err error) {
}
if haveSection == false {
return nil, gerror.NewCode(gerror.CodeInvalidParameter, "failed to parse INI file, section not found")
return nil, gerror.NewCode(gcode.CodeInvalidParameter, "failed to parse INI file, section not found")
}
return res, nil
}

View File

@ -9,6 +9,7 @@ package gjson
import (
"bytes"
"fmt"
"github.com/gogf/gf/errors/gcode"
"github.com/gogf/gf/errors/gerror"
"reflect"
@ -264,7 +265,7 @@ func doLoadContentWithOptions(dataType string, data []byte, options Options) (*J
return nil, err
}
default:
err = gerror.NewCode(gerror.CodeInvalidParameter, "unsupported type for loading")
err = gerror.NewCode(gcode.CodeInvalidParameter, "unsupported type for loading")
}
if err != nil {
return nil, err

View File

@ -19,6 +19,7 @@ func Example_conversionNormalFormats() {
"array" : ["John", "Ming"]
}
}`
if j, err := gjson.DecodeToJson(data); err != nil {
panic(err)
} else {
@ -48,8 +49,8 @@ func Example_conversionNormalFormats() {
// YAML:
// users:
// array:
// - John
// - Ming
// - John
// - Ming
// count: 1
//
// ======================

59
errors/gcode/gcode.go Normal file
View File

@ -0,0 +1,59 @@
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
// Package gcode provides universal error code definition and common error codes implements.
package gcode
// Code is universal error code interface definition.
type Code interface {
// Code returns the integer number of current error code.
Code() int
// Message returns the brief message for current error code.
Message() string
// Detail returns the detailed information of current error code,
// which is mainly designed as an extension field for error code.
Detail() interface{}
}
// ================================================================================================================
// Common error code definition.
// There are reserved internal error code by framework: code < 1000.
// ================================================================================================================
var (
CodeNil = localCode{-1, "", nil} // No error code specified.
CodeOK = localCode{0, "OK", nil} // It is OK.
CodeInternalError = localCode{50, "Internal Error", nil} // An error occurred internally.
CodeValidationFailed = localCode{51, "Validation Failed", nil} // Data validation failed.
CodeDbOperationError = localCode{52, "Database Operation Error", nil} // Database operation error.
CodeInvalidParameter = localCode{53, "Invalid Parameter", nil} // The given parameter for current operation is invalid.
CodeMissingParameter = localCode{54, "Missing Parameter", nil} // Parameter for current operation is missing.
CodeInvalidOperation = localCode{55, "Invalid Operation", nil} // The function cannot be used like this.
CodeInvalidConfiguration = localCode{56, "Invalid Configuration", nil} // The configuration is invalid for current operation.
CodeMissingConfiguration = localCode{57, "Missing Configuration", nil} // The configuration is missing for current operation.
CodeNotImplemented = localCode{58, "Not Implemented", nil} // The operation is not implemented yet.
CodeNotSupported = localCode{59, "Not Supported", nil} // The operation is not supported yet.
CodeOperationFailed = localCode{60, "Operation Failed", nil} // I tried, but I cannot give you what you want.
CodeNotAuthorized = localCode{61, "Not Authorized", nil} // Not Authorized.
CodeSecurityReason = localCode{62, "Security Reason", nil} // Security Reason.
CodeServerBusy = localCode{63, "Server Is Busy", nil} // Server is busy, please try again later.
CodeUnknown = localCode{64, "Unknown Error", nil} // Unknown error.
CodeNotFound = localCode{65, "Not Found", nil} // Resource does not exist.
CodeInvalidRequest = localCode{66, "Invalid Request", nil} // Invalid request.
CodeBusinessValidationFailed = localCode{300, "Business Validation Failed", nil} // Business validation failed.
)
// New creates and returns an error code.
// Note that it returns an interface object of Code.
func New(code int, message string, detail interface{}) Code {
return localCode{
code: code,
message: message,
detail: detail,
}
}

View File

@ -0,0 +1,43 @@
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
package gcode
import "fmt"
// localCode is an implementer for interface Code for internal usage only.
type localCode struct {
code int // Error code, usually an integer.
message string // Brief message for this error code.
detail interface{} // As type of interface, it is mainly designed as an extension field for error code.
}
// Code returns the integer number of current error code.
func (c localCode) Code() int {
return c.code
}
// Message returns the brief message for current error code.
func (c localCode) Message() string {
return c.message
}
// Detail returns the detailed information of current error code,
// which is mainly designed as an extension field for error code.
func (c localCode) Detail() interface{} {
return c.detail
}
// String returns current error code as a string.
func (c localCode) String() string {
if c.detail != nil {
return fmt.Sprintf(`%d:%s %v`, c.code, c.message, c.detail)
}
if c.message != "" {
return fmt.Sprintf(`%d:%s`, c.code, c.message)
}
return fmt.Sprintf(`%d`, c.code)
}

View File

@ -0,0 +1,23 @@
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
package gcode_test
import (
"github.com/gogf/gf/errors/gcode"
"testing"
"github.com/gogf/gf/test/gtest"
)
func Test_Nil(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
c := gcode.New(1, "custom error", "detailed description")
t.Assert(c.Code(), 1)
t.Assert(c.Message(), "custom error")
t.Assert(c.Detail(), "detailed description")
})
}

View File

@ -12,35 +12,36 @@ package gerror
import (
"fmt"
"github.com/gogf/gf/errors/gcode"
)
// apiCode is the interface for Code feature.
type apiCode interface {
Error() string // It should be an error.
Code() int
Error() string
Code() gcode.Code
}
// apiStack is the interface for Stack feature.
type apiStack interface {
Error() string // It should be an error.
Error() string
Stack() string
}
// apiCause is the interface for Cause feature.
type apiCause interface {
Error() string // It should be an error.
Error() string
Cause() error
}
// apiCurrent is the interface for Current feature.
type apiCurrent interface {
Error() string // It should be an error.
Error() string
Current() error
}
// apiNext is the interface for Next feature.
type apiNext interface {
Error() string // It should be an error.
Error() string
Next() error
}
@ -49,7 +50,7 @@ func New(text string) error {
return &Error{
stack: callers(),
text: text,
code: CodeNil,
code: gcode.CodeNil,
}
}
@ -58,7 +59,7 @@ func Newf(format string, args ...interface{}) error {
return &Error{
stack: callers(),
text: fmt.Sprintf(format, args...),
code: CodeNil,
code: gcode.CodeNil,
}
}
@ -68,7 +69,7 @@ func NewSkip(skip int, text string) error {
return &Error{
stack: callers(skip),
text: text,
code: CodeNil,
code: gcode.CodeNil,
}
}
@ -78,7 +79,7 @@ func NewSkipf(skip int, format string, args ...interface{}) error {
return &Error{
stack: callers(skip),
text: fmt.Sprintf(format, args...),
code: CodeNil,
code: gcode.CodeNil,
}
}
@ -142,16 +143,20 @@ func WrapSkipf(skip int, err error, format string, args ...interface{}) error {
}
// NewCode creates and returns an error that has error code and given text.
func NewCode(code int, text string) error {
func NewCode(code gcode.Code, text ...string) error {
errText := ""
if len(text) > 0 {
errText = text[0]
}
return &Error{
stack: callers(),
text: text,
text: errText,
code: code,
}
}
// NewCodef returns an error that has error code and formats as the given format and args.
func NewCodef(code int, format string, args ...interface{}) error {
func NewCodef(code gcode.Code, format string, args ...interface{}) error {
return &Error{
stack: callers(),
text: fmt.Sprintf(format, args...),
@ -161,17 +166,21 @@ func NewCodef(code int, format string, args ...interface{}) error {
// NewCodeSkip creates and returns an error which has error code and is formatted from given text.
// The parameter <skip> specifies the stack callers skipped amount.
func NewCodeSkip(code, skip int, text string) error {
func NewCodeSkip(code gcode.Code, skip int, text ...string) error {
errText := ""
if len(text) > 0 {
errText = text[0]
}
return &Error{
stack: callers(skip),
text: text,
text: errText,
code: code,
}
}
// NewCodeSkipf returns an error that has error code and formats as the given format and args.
// The parameter <skip> specifies the stack callers skipped amount.
func NewCodeSkipf(code, skip int, format string, args ...interface{}) error {
func NewCodeSkipf(code gcode.Code, skip int, format string, args ...interface{}) error {
return &Error{
stack: callers(skip),
text: fmt.Sprintf(format, args...),
@ -181,21 +190,25 @@ func NewCodeSkipf(code, skip int, format string, args ...interface{}) error {
// WrapCode wraps error with code and text.
// It returns nil if given err is nil.
func WrapCode(code int, err error, text string) error {
func WrapCode(code gcode.Code, err error, text ...string) error {
if err == nil {
return nil
}
errText := ""
if len(text) > 0 {
errText = text[0]
}
return &Error{
error: err,
stack: callers(),
text: text,
text: errText,
code: code,
}
}
// WrapCodef wraps error with code and format specifier.
// It returns nil if given <err> is nil.
func WrapCodef(code int, err error, format string, args ...interface{}) error {
func WrapCodef(code gcode.Code, err error, format string, args ...interface{}) error {
if err == nil {
return nil
}
@ -210,14 +223,18 @@ func WrapCodef(code int, err error, format string, args ...interface{}) error {
// WrapCodeSkip wraps error with code and text.
// It returns nil if given err is nil.
// The parameter <skip> specifies the stack callers skipped amount.
func WrapCodeSkip(code, skip int, err error, text string) error {
func WrapCodeSkip(code gcode.Code, skip int, err error, text ...string) error {
if err == nil {
return nil
}
errText := ""
if len(text) > 0 {
errText = text[0]
}
return &Error{
error: err,
stack: callers(skip),
text: text,
text: errText,
code: code,
}
}
@ -225,7 +242,7 @@ func WrapCodeSkip(code, skip int, err error, text string) error {
// WrapCodeSkipf wraps error with code and text that is formatted with given format and args.
// It returns nil if given err is nil.
// The parameter <skip> specifies the stack callers skipped amount.
func WrapCodeSkipf(code, skip int, err error, format string, args ...interface{}) error {
func WrapCodeSkipf(code gcode.Code, skip int, err error, format string, args ...interface{}) error {
if err == nil {
return nil
}
@ -238,14 +255,14 @@ func WrapCodeSkipf(code, skip int, err error, format string, args ...interface{}
}
// Code returns the error code of current error.
// It returns -1 if it has no error code or it does not implements interface Code.
func Code(err error) int {
// It returns CodeNil if it has no error code or it does not implements interface Code.
func Code(err error) gcode.Code {
if err != nil {
if e, ok := err.(apiCode); ok {
return e.Code()
}
}
return -1
return gcode.CodeNil
}
// Cause returns the root cause error of <err>.

View File

@ -1,40 +0,0 @@
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
package gerror
// Reserved internal error code of framework: code < 1000.
const (
// ===============================================================================
// Common system codes.
// ===============================================================================
CodeNil = -1 // No error code specified.
CodeOk = 0 // It is OK.
CodeInternalError = 50 + iota // An error occurred internally.
CodeValidationFailed // Data validation failed.
CodeDbOperationError // Database operation error.
CodeInvalidParameter // The given parameter for current operation is invalid.
CodeMissingParameter // Parameter for current operation is missing.
CodeInvalidOperation // The function cannot be used like this.
CodeInvalidConfiguration // The configuration is invalid for current operation.
CodeMissingConfiguration // The configuration is missing for current operation.
CodeNotImplemented // The operation is not implemented yet.
CodeNotSupported // The operation is not supported yet.
CodeOperationFailed // I tried, but I cannot give you what you want.
CodeNotAuthorized // Not Authorized.
CodeSecurityReason // Security Reason.
CodeServerBusy // Server is busy, please try again later.
CodeUnknown // Unknown error.
CodeResourceNotExist // Resource does not exist.
// ===============================================================================
// Common business codes.
// ===============================================================================
CodeBusinessValidationFailed = 300 + iota // Business validation failed.
)

View File

@ -10,6 +10,7 @@ import (
"bytes"
"errors"
"fmt"
"github.com/gogf/gf/errors/gcode"
"github.com/gogf/gf/internal/utils"
"io"
"runtime"
@ -18,10 +19,10 @@ import (
// 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.
code int // Error code if necessary.
error error // Wrapped error.
stack stack // Stack array, which records the stack information when this error is created or wrapped.
text string // Error text, which is created by New* functions.
code gcode.Code // Error code if necessary.
}
const (
@ -47,8 +48,11 @@ func (err *Error) Error() string {
return ""
}
errStr := err.text
if errStr == "" && err.code != nil {
errStr = err.code.Message()
}
if err.error != nil {
if err.text != "" {
if errStr != "" {
errStr += ": "
}
errStr += err.error.Error()
@ -57,10 +61,10 @@ func (err *Error) Error() string {
}
// Code returns the error code.
// It returns -1 if it has no error code.
func (err *Error) Code() int {
// It returns CodeNil if it has no error code.
func (err *Error) Code() gcode.Code {
if err == nil {
return -1
return gcode.CodeNil
}
return err.code
}
@ -159,6 +163,7 @@ func (err *Error) Current() error {
error: nil,
stack: err.stack,
text: err.text,
code: err.code,
}
}

View File

@ -6,12 +6,14 @@
package gerror
import "github.com/gogf/gf/errors/gcode"
// Option is option for creating error.
type Option struct {
Error error // Wrapped error if any.
Stack bool // Whether recording stack information into error.
Text string // Error text, which is created by New* functions.
Code int // Error code if necessary.
Error error // Wrapped error if any.
Stack bool // Whether recording stack information into error.
Text string // Error text, which is created by New* functions.
Code gcode.Code // Error code if necessary.
}
// NewOption creates and returns an error with Option.

View File

@ -8,6 +8,7 @@ package gerror_test
import (
"errors"
"github.com/gogf/gf/errors/gcode"
"github.com/gogf/gf/errors/gerror"
"testing"
)
@ -55,36 +56,36 @@ func Benchmark_NewSkipf(b *testing.B) {
func Benchmark_NewCode(b *testing.B) {
for i := 0; i < b.N; i++ {
gerror.NewCode(500, "test")
gerror.NewCode(gcode.New(500, "", nil), "test")
}
}
func Benchmark_NewCodef(b *testing.B) {
for i := 0; i < b.N; i++ {
gerror.NewCodef(500, "%s", "test")
gerror.NewCodef(gcode.New(500, "", nil), "%s", "test")
}
}
func Benchmark_NewCodeSkip(b *testing.B) {
for i := 0; i < b.N; i++ {
gerror.NewCodeSkip(1, 500, "test")
gerror.NewCodeSkip(gcode.New(1, "", nil), 500, "test")
}
}
func Benchmark_NewCodeSkipf(b *testing.B) {
for i := 0; i < b.N; i++ {
gerror.NewCodeSkipf(1, 500, "%s", "test")
gerror.NewCodeSkipf(gcode.New(1, "", nil), 500, "%s", "test")
}
}
func Benchmark_WrapCode(b *testing.B) {
for i := 0; i < b.N; i++ {
gerror.WrapCode(500, baseError, "test")
gerror.WrapCode(gcode.New(500, "", nil), baseError, "test")
}
}
func Benchmark_WrapCodef(b *testing.B) {
for i := 0; i < b.N; i++ {
gerror.WrapCodef(500, baseError, "test")
gerror.WrapCodef(gcode.New(500, "", nil), baseError, "test")
}
}

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