mirror of
https://gitee.com/johng/gf
synced 2026-06-10 19:31:44 +08:00
Compare commits
93 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 87067e1e8a | |||
| edf90799d3 | |||
| c8cfa33bc7 | |||
| 14ce35f570 | |||
| 56db028b21 | |||
| 561bc5bc33 | |||
| 79ba162156 | |||
| 03bcf51a95 | |||
| 308fdccf65 | |||
| fd92fd2409 | |||
| a2823a501e | |||
| cc60bc9dab | |||
| 425ca8aa3e | |||
| 73d2b7ed06 | |||
| 0048e2c8c4 | |||
| 549adf6487 | |||
| b5502c5580 | |||
| 46bf669cdd | |||
| fa7a3e987d | |||
| b316b9c073 | |||
| 5b57c35522 | |||
| e0a3233ea3 | |||
| 1b588b14d8 | |||
| 2c839db941 | |||
| f9eaa8f930 | |||
| 59397fd8a5 | |||
| 6be582355c | |||
| 257897763d | |||
| 29d50994ae | |||
| b129ee3f04 | |||
| a47699bf6e | |||
| 66b8b591df | |||
| 7b47fe96dd | |||
| abc8e62d58 | |||
| ef36cf8446 | |||
| c5345239fc | |||
| a229960218 | |||
| 43180ef239 | |||
| e10240bd7c | |||
| a0ef6fce81 | |||
| 314bee2b4e | |||
| 050c93ee8d | |||
| feefcc98ef | |||
| 8f4ce91361 | |||
| f03f56ba4e | |||
| 457d552fa0 | |||
| be2fcf7f57 | |||
| ad6c4f5245 | |||
| db621e38d9 | |||
| 170af1ab00 | |||
| 31ef4e7b5a | |||
| f56f689970 | |||
| 17b73e7cb3 | |||
| 903f29a824 | |||
| 12f2193963 | |||
| 13ecbc263e | |||
| 7b9888c004 | |||
| 91cd4f96f0 | |||
| 95a44122dd | |||
| 2bd76dfdde | |||
| 26895294f9 | |||
| 4cd4559784 | |||
| 3fc96f2bd0 | |||
| 91dd9e2bf9 | |||
| 9c5642f141 | |||
| 00f0a743fc | |||
| 232751e3db | |||
| 1c600d5b20 | |||
| 9bc3b44a61 | |||
| 83729f18ad | |||
| fdb6e70322 | |||
| efaf3d591c | |||
| ef50eb6d6b | |||
| fb5a1f2306 | |||
| 9d4382d12e | |||
| 9ee76ecc93 | |||
| 0d2ca48d16 | |||
| f1857df5e2 | |||
| fc1dfb7ff9 | |||
| dc407bf293 | |||
| 4eb057227c | |||
| 9cd944fd77 | |||
| cd3593182a | |||
| 0f6820df9e | |||
| 0e158903c2 | |||
| 214d0513e5 | |||
| 75ca866991 | |||
| f22aa1b5d6 | |||
| 00d7ee93b2 | |||
| 537cbf983e | |||
| 4c54b1cfbc | |||
| 248e6ff134 | |||
| 9be92cc3d4 |
18
.example/net/ghttp/server/redirect/redirect_to.go
Normal file
18
.example/net/ghttp/server/redirect/redirect_to.go
Normal 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()
|
||||
}
|
||||
15
.example/net/ghttp/server/servefile/servefile.go
Normal file
15
.example/net/ghttp/server/servefile/servefile.go
Normal 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()
|
||||
}
|
||||
15
.example/net/ghttp/server/servefile/servefiledownload.go
Normal file
15
.example/net/ghttp/server/servefile/servefiledownload.go
Normal 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()
|
||||
}
|
||||
1
.example/net/ghttp/server/servefile/test.txt
Normal file
1
.example/net/ghttp/server/servefile/test.txt
Normal file
@ -0,0 +1 @@
|
||||
test
|
||||
@ -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
|
||||
}
|
||||
|
||||
28
.example/os/gcache/getorset_func_lock.go
Normal file
28
.example/os/gcache/getorset_func_lock.go
Normal 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)
|
||||
}
|
||||
19
.example/os/gcache/note_interface_key.go
Normal file
19
.example/os/gcache/note_interface_key.go
Normal 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))
|
||||
}
|
||||
29
.example/os/gcache/note_interface_value.go
Normal file
29
.example/os/gcache/note_interface_value.go
Normal 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)
|
||||
}
|
||||
14
.example/os/glog/glog_CtxKeys.go
Normal file
14
.example/os/glog/glog_CtxKeys.go
Normal 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)
|
||||
}
|
||||
@ -6,7 +6,7 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
err := glog.SetConfigWithMap(g.Map{
|
||||
err := g.Log().SetConfigWithMap(g.Map{
|
||||
"prefix": "[TEST]",
|
||||
})
|
||||
if err != nil {
|
||||
@ -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)
|
||||
}
|
||||
@ -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)
|
||||
}
|
||||
@ -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)
|
||||
|
||||
@ -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)
|
||||
}
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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")
|
||||
}
|
||||
|
||||
@ -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() {
|
||||
|
||||
@ -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"})
|
||||
}
|
||||
|
||||
@ -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")
|
||||
}
|
||||
|
||||
@ -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")
|
||||
}
|
||||
|
||||
@ -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")
|
||||
}
|
||||
|
||||
@ -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() {
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@ -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")
|
||||
}
|
||||
|
||||
@ -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())
|
||||
}
|
||||
|
||||
@ -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()
|
||||
}
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
31
.example/os/glog/handler/glog_handler_greylog.go
Normal file
31
.example/os/glog/handler/glog_handler_greylog.go
Normal 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")
|
||||
//}
|
||||
42
.example/os/glog/handler/glog_handler_json.go
Normal file
42
.example/os/glog/handler/glog_handler_json.go
Normal 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")
|
||||
}
|
||||
@ -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) {
|
||||
@ -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() {
|
||||
@ -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()
|
||||
}
|
||||
@ -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!")
|
||||
}
|
||||
57
.github/workflows/go.yml
vendored
57
.github/workflows/go.yml
vendored
@ -3,10 +3,11 @@ name: GoFrame CI
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- develop
|
||||
- 'master'
|
||||
- 'develop'
|
||||
- 'fix/unsafe'
|
||||
pull_request:
|
||||
branches: [master, develop]
|
||||
branches: ['master', 'develop','fix/**']
|
||||
env:
|
||||
GF_DEBUG: 1
|
||||
|
||||
@ -16,7 +17,7 @@ jobs:
|
||||
# Service containers to run with `code-test`
|
||||
services:
|
||||
redis:
|
||||
image : redis
|
||||
image: redis
|
||||
options: >-
|
||||
--health-cmd "redis-cli ping"
|
||||
--health-interval 10s
|
||||
@ -28,7 +29,7 @@ jobs:
|
||||
mysql:
|
||||
image: mysql:5.7
|
||||
env:
|
||||
MYSQL_DATABASE : test
|
||||
MYSQL_DATABASE: test
|
||||
MYSQL_ROOT_PASSWORD: 12345678
|
||||
ports:
|
||||
# Maps tcp port 3306 on service container to the host
|
||||
@ -37,35 +38,35 @@ jobs:
|
||||
# strategy set
|
||||
strategy:
|
||||
matrix:
|
||||
go: ["1.14", "1.15", "1.16"]
|
||||
go: [ "1.15", "1.16", "1.17" ]
|
||||
|
||||
steps:
|
||||
- name: Set Up Timezone
|
||||
uses: szenius/set-timezone@v1.0
|
||||
with:
|
||||
timezoneLinux: "Asia/Shanghai"
|
||||
- name: Set Up Timezone
|
||||
uses: szenius/set-timezone@v1.0
|
||||
with:
|
||||
timezoneLinux: "Asia/Shanghai"
|
||||
|
||||
- name: Checkout Repositary
|
||||
uses: actions/checkout@v2
|
||||
- name: Checkout Repositary
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Set Up Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: ${{ matrix.go }}
|
||||
- name: Set Up Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: ${{ matrix.go }}
|
||||
|
||||
- name: Before Script
|
||||
run: |
|
||||
date
|
||||
find . -name "*.go" | xargs gofmt -w
|
||||
git diff --name-only --exit-code || exit 1
|
||||
sudo echo "127.0.0.1 local" | sudo tee -a /etc/hosts
|
||||
- name: Before Script
|
||||
run: |
|
||||
date
|
||||
find . -name "*.go" | xargs gofmt -w
|
||||
git diff --name-only --exit-code || exit 1
|
||||
sudo echo "127.0.0.1 local" | sudo tee -a /etc/hosts
|
||||
|
||||
- name: Run i386 Arch Test
|
||||
run: GOARCH=386 go test -v ./... || exit 1
|
||||
- name: Run i386 Arch Test
|
||||
run: GOARCH=386 go test -v ./... || exit 1
|
||||
|
||||
- name: Run amd64 Arch Test
|
||||
run: GOARCH=amd64 go test -v ./... -race -coverprofile=coverage.txt -covermode=atomic
|
||||
- name: Run amd64 Arch Test
|
||||
run: GOARCH=amd64 go test -v ./... -race -coverprofile=coverage.txt -covermode=atomic
|
||||
|
||||
- name: Report Coverage
|
||||
run: bash <(curl -s https://codecov.io/bash)
|
||||
- name: Report Coverage
|
||||
run: bash <(curl -s https://codecov.io/bash)
|
||||
|
||||
|
||||
6
.test/config.yaml
Normal file
6
.test/config.yaml
Normal file
@ -0,0 +1,6 @@
|
||||
database:
|
||||
default:
|
||||
link: "mysql:root:12345678@tcp(127.0.0.1:3306)/test"
|
||||
|
||||
server:
|
||||
address: 127.0.0.1:8199
|
||||
16
.test/main.go
Normal file
16
.test/main.go
Normal file
@ -0,0 +1,16 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
"github.com/gogf/gf/v2/text/gregex"
|
||||
)
|
||||
|
||||
func main() {
|
||||
fileContent := gfile.GetContents(`/Users/john/Workspace/Go/GOPATH/src/git.code.oa.com/Khaos/eros/app/khaos-oss/internal/logic/resource/resource_horizontal_downgrade.go`)
|
||||
matches, err := gregex.MatchAllString(`func \(\w+ (.+?)\) ([\s\S]+?) {`, fileContent)
|
||||
fmt.Println(err)
|
||||
g.Dump(matches)
|
||||
}
|
||||
@ -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 {
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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:
|
||||
|
||||
39
container/garray/garray_z_bench_any_test.go
Normal file
39
container/garray/garray_z_bench_any_test.go
Normal 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,
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -460,7 +460,7 @@ func (m *AnyAnyMap) Merge(other *AnyAnyMap) {
|
||||
// String returns the map as a string.
|
||||
func (m *AnyAnyMap) String() string {
|
||||
b, _ := m.MarshalJSON()
|
||||
return gconv.UnsafeBytesToStr(b)
|
||||
return string(b)
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
|
||||
@ -458,7 +458,7 @@ func (m *IntAnyMap) Merge(other *IntAnyMap) {
|
||||
// String returns the map as a string.
|
||||
func (m *IntAnyMap) String() string {
|
||||
b, _ := m.MarshalJSON()
|
||||
return gconv.UnsafeBytesToStr(b)
|
||||
return string(b)
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
|
||||
@ -429,7 +429,7 @@ func (m *IntIntMap) Merge(other *IntIntMap) {
|
||||
// String returns the map as a string.
|
||||
func (m *IntIntMap) String() string {
|
||||
b, _ := m.MarshalJSON()
|
||||
return gconv.UnsafeBytesToStr(b)
|
||||
return string(b)
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
|
||||
@ -429,7 +429,7 @@ func (m *IntStrMap) Merge(other *IntStrMap) {
|
||||
// String returns the map as a string.
|
||||
func (m *IntStrMap) String() string {
|
||||
b, _ := m.MarshalJSON()
|
||||
return gconv.UnsafeBytesToStr(b)
|
||||
return string(b)
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
|
||||
@ -454,7 +454,7 @@ func (m *StrAnyMap) Merge(other *StrAnyMap) {
|
||||
// String returns the map as a string.
|
||||
func (m *StrAnyMap) String() string {
|
||||
b, _ := m.MarshalJSON()
|
||||
return gconv.UnsafeBytesToStr(b)
|
||||
return string(b)
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
|
||||
@ -432,7 +432,7 @@ func (m *StrIntMap) Merge(other *StrIntMap) {
|
||||
// String returns the map as a string.
|
||||
func (m *StrIntMap) String() string {
|
||||
b, _ := m.MarshalJSON()
|
||||
return gconv.UnsafeBytesToStr(b)
|
||||
return string(b)
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
|
||||
@ -432,7 +432,7 @@ func (m *StrStrMap) Merge(other *StrStrMap) {
|
||||
// String returns the map as a string.
|
||||
func (m *StrStrMap) String() string {
|
||||
b, _ := m.MarshalJSON()
|
||||
return gconv.UnsafeBytesToStr(b)
|
||||
return string(b)
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
|
||||
@ -513,7 +513,7 @@ func (m *ListMap) Merge(other *ListMap) {
|
||||
// String returns the map as a string.
|
||||
func (m *ListMap) String() string {
|
||||
b, _ := m.MarshalJSON()
|
||||
return gconv.UnsafeBytesToStr(b)
|
||||
return string(b)
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -7,9 +7,10 @@
|
||||
package gtype
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
"strconv"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
)
|
||||
|
||||
// Byte is a struct for concurrent-safe operation for type byte.
|
||||
@ -60,12 +61,12 @@ func (v *Byte) String() string {
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
func (v *Byte) MarshalJSON() ([]byte, error) {
|
||||
return gconv.UnsafeStrToBytes(strconv.FormatUint(uint64(v.Val()), 10)), nil
|
||||
return []byte(strconv.FormatUint(uint64(v.Val()), 10)), nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
|
||||
func (v *Byte) UnmarshalJSON(b []byte) error {
|
||||
v.Set(gconv.Uint8(gconv.UnsafeBytesToStr(b)))
|
||||
v.Set(gconv.Uint8(string(b)))
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@ -9,8 +9,9 @@ package gtype
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
)
|
||||
|
||||
// Bytes is a struct for concurrent-safe operation for type []byte.
|
||||
@ -59,7 +60,7 @@ func (v *Bytes) MarshalJSON() ([]byte, error) {
|
||||
val := v.Val()
|
||||
dst := make([]byte, base64.StdEncoding.EncodedLen(len(val)))
|
||||
base64.StdEncoding.Encode(dst, val)
|
||||
return gconv.UnsafeStrToBytes(`"` + gconv.UnsafeBytesToStr(dst) + `"`), nil
|
||||
return []byte(`"` + string(dst) + `"`), nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
|
||||
|
||||
@ -7,11 +7,12 @@
|
||||
package gtype
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
"math"
|
||||
"strconv"
|
||||
"sync/atomic"
|
||||
"unsafe"
|
||||
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
)
|
||||
|
||||
// Float32 is a struct for concurrent-safe operation for type float32.
|
||||
@ -73,12 +74,12 @@ func (v *Float32) String() string {
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
func (v *Float32) MarshalJSON() ([]byte, error) {
|
||||
return gconv.UnsafeStrToBytes(strconv.FormatFloat(float64(v.Val()), 'g', -1, 32)), nil
|
||||
return []byte(strconv.FormatFloat(float64(v.Val()), 'g', -1, 32)), nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
|
||||
func (v *Float32) UnmarshalJSON(b []byte) error {
|
||||
v.Set(gconv.Float32(gconv.UnsafeBytesToStr(b)))
|
||||
v.Set(gconv.Float32(string(b)))
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@ -7,11 +7,12 @@
|
||||
package gtype
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
"math"
|
||||
"strconv"
|
||||
"sync/atomic"
|
||||
"unsafe"
|
||||
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
)
|
||||
|
||||
// Float64 is a struct for concurrent-safe operation for type float64.
|
||||
@ -73,12 +74,12 @@ func (v *Float64) String() string {
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
func (v *Float64) MarshalJSON() ([]byte, error) {
|
||||
return gconv.UnsafeStrToBytes(strconv.FormatFloat(v.Val(), 'g', -1, 64)), nil
|
||||
return []byte(strconv.FormatFloat(v.Val(), 'g', -1, 64)), nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
|
||||
func (v *Float64) UnmarshalJSON(b []byte) error {
|
||||
v.Set(gconv.Float64(gconv.UnsafeBytesToStr(b)))
|
||||
v.Set(gconv.Float64(string(b)))
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@ -7,9 +7,10 @@
|
||||
package gtype
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
"strconv"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
)
|
||||
|
||||
// Int is a struct for concurrent-safe operation for type int.
|
||||
@ -60,12 +61,12 @@ func (v *Int) String() string {
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
func (v *Int) MarshalJSON() ([]byte, error) {
|
||||
return gconv.UnsafeStrToBytes(strconv.Itoa(v.Val())), nil
|
||||
return []byte(strconv.Itoa(v.Val())), nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
|
||||
func (v *Int) UnmarshalJSON(b []byte) error {
|
||||
v.Set(gconv.Int(gconv.UnsafeBytesToStr(b)))
|
||||
v.Set(gconv.Int(string(b)))
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@ -7,9 +7,10 @@
|
||||
package gtype
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
"strconv"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
)
|
||||
|
||||
// Int32 is a struct for concurrent-safe operation for type int32.
|
||||
@ -60,12 +61,12 @@ func (v *Int32) String() string {
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
func (v *Int32) MarshalJSON() ([]byte, error) {
|
||||
return gconv.UnsafeStrToBytes(strconv.Itoa(int(v.Val()))), nil
|
||||
return []byte(strconv.Itoa(int(v.Val()))), nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
|
||||
func (v *Int32) UnmarshalJSON(b []byte) error {
|
||||
v.Set(gconv.Int32(gconv.UnsafeBytesToStr(b)))
|
||||
v.Set(gconv.Int32(string(b)))
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@ -7,9 +7,10 @@
|
||||
package gtype
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
"strconv"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
)
|
||||
|
||||
// Int64 is a struct for concurrent-safe operation for type int64.
|
||||
@ -60,12 +61,12 @@ func (v *Int64) String() string {
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
func (v *Int64) MarshalJSON() ([]byte, error) {
|
||||
return gconv.UnsafeStrToBytes(strconv.FormatInt(v.Val(), 10)), nil
|
||||
return []byte(strconv.FormatInt(v.Val(), 10)), nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
|
||||
func (v *Int64) UnmarshalJSON(b []byte) error {
|
||||
v.Set(gconv.Int64(gconv.UnsafeBytesToStr(b)))
|
||||
v.Set(gconv.Int64(string(b)))
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@ -8,8 +8,9 @@ package gtype
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
)
|
||||
|
||||
// String is a struct for concurrent-safe operation for type string.
|
||||
@ -55,12 +56,12 @@ func (v *String) String() string {
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
func (v *String) MarshalJSON() ([]byte, error) {
|
||||
return gconv.UnsafeStrToBytes(`"` + v.Val() + `"`), nil
|
||||
return []byte(`"` + v.Val() + `"`), nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
|
||||
func (v *String) UnmarshalJSON(b []byte) error {
|
||||
v.Set(gconv.UnsafeBytesToStr(bytes.Trim(b, `"`)))
|
||||
v.Set(string(bytes.Trim(b, `"`)))
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@ -7,9 +7,10 @@
|
||||
package gtype
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
"strconv"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
)
|
||||
|
||||
// Uint is a struct for concurrent-safe operation for type uint.
|
||||
@ -60,12 +61,12 @@ func (v *Uint) String() string {
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
func (v *Uint) MarshalJSON() ([]byte, error) {
|
||||
return gconv.UnsafeStrToBytes(strconv.FormatUint(uint64(v.Val()), 10)), nil
|
||||
return []byte(strconv.FormatUint(uint64(v.Val()), 10)), nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
|
||||
func (v *Uint) UnmarshalJSON(b []byte) error {
|
||||
v.Set(gconv.Uint(gconv.UnsafeBytesToStr(b)))
|
||||
v.Set(gconv.Uint(string(b)))
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@ -7,9 +7,10 @@
|
||||
package gtype
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
"strconv"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
)
|
||||
|
||||
// Uint32 is a struct for concurrent-safe operation for type uint32.
|
||||
@ -60,12 +61,12 @@ func (v *Uint32) String() string {
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
func (v *Uint32) MarshalJSON() ([]byte, error) {
|
||||
return gconv.UnsafeStrToBytes(strconv.FormatUint(uint64(v.Val()), 10)), nil
|
||||
return []byte(strconv.FormatUint(uint64(v.Val()), 10)), nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
|
||||
func (v *Uint32) UnmarshalJSON(b []byte) error {
|
||||
v.Set(gconv.Uint32(gconv.UnsafeBytesToStr(b)))
|
||||
v.Set(gconv.Uint32(string(b)))
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@ -7,9 +7,10 @@
|
||||
package gtype
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
"strconv"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
)
|
||||
|
||||
// Uint64 is a struct for concurrent-safe operation for type uint64.
|
||||
@ -60,12 +61,12 @@ func (v *Uint64) String() string {
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
func (v *Uint64) MarshalJSON() ([]byte, error) {
|
||||
return gconv.UnsafeStrToBytes(strconv.FormatUint(v.Val(), 10)), nil
|
||||
return []byte(strconv.FormatUint(v.Val(), 10)), nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
|
||||
func (v *Uint64) UnmarshalJSON(b []byte) error {
|
||||
v.Set(gconv.Uint64(gconv.UnsafeBytesToStr(b)))
|
||||
v.Set(gconv.Uint64(string(b)))
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@ -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...)
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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...)
|
||||
}
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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...)
|
||||
}
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
|
||||
@ -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)
|
||||
}
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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] != "" {
|
||||
|
||||
@ -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 (
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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...)
|
||||
|
||||
@ -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...)
|
||||
}
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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),
|
||||
)
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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`,
|
||||
)
|
||||
}
|
||||
|
||||
@ -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(),
|
||||
|
||||
@ -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))
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -8,6 +8,7 @@ package gdb
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
|
||||
"github.com/gogf/gf/container/gmap"
|
||||
"github.com/gogf/gf/encoding/gparser"
|
||||
"github.com/gogf/gf/internal/empty"
|
||||
@ -22,13 +23,13 @@ func (r Record) Interface() interface{} {
|
||||
// Json converts `r` to JSON format content.
|
||||
func (r Record) Json() string {
|
||||
content, _ := gparser.VarToJson(r.Map())
|
||||
return gconv.UnsafeBytesToStr(content)
|
||||
return string(content)
|
||||
}
|
||||
|
||||
// Xml converts `r` to XML format content.
|
||||
func (r Record) Xml(rootTag ...string) string {
|
||||
content, _ := gparser.VarToXml(r.Map(), rootTag...)
|
||||
return gconv.UnsafeBytesToStr(content)
|
||||
return string(content)
|
||||
}
|
||||
|
||||
// Map converts `r` to map[string]interface{}.
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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)
|
||||
})
|
||||
}
|
||||
|
||||
@ -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))
|
||||
})
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user