mirror of
https://gitee.com/johng/gf
synced 2026-06-10 11:27:17 +08:00
Compare commits
102 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7c8bbcb3af | |||
| ba18e2bf6b | |||
| 5fefe97b87 | |||
| 5fba250a14 | |||
| 2606ad83ac | |||
| d450de8e0d | |||
| e4b0de0d4f | |||
| 2af4fd86cc | |||
| ddd171bc18 | |||
| 2679f92aa8 | |||
| cca438d77f | |||
| f2bc29e5c1 | |||
| fe7209e76d | |||
| 7c4a0453b7 | |||
| 97879834bc | |||
| 332535901f | |||
| e68e7a3224 | |||
| 65befd5ac4 | |||
| 78bdb5ef71 | |||
| 6eb7261dfd | |||
| 4f82be5bc0 | |||
| 3ac5772059 | |||
| eb723e47c2 | |||
| 8aa7f08350 | |||
| a54559d016 | |||
| 8e1f6abac5 | |||
| 7f3a2207a3 | |||
| 742c7913ea | |||
| 2d8ab726e2 | |||
| 702a296258 | |||
| c3c5414ce2 | |||
| ee3d375532 | |||
| e3f5c9175c | |||
| 7c24449a24 | |||
| 1c09846d3e | |||
| db94346863 | |||
| 3e6b9864d5 | |||
| 392c81ad69 | |||
| fe142c93fd | |||
| 578e7d634b | |||
| 6659a49869 | |||
| d9bc8b05e1 | |||
| 571405597b | |||
| 1441ce7f5f | |||
| 071a587755 | |||
| c1db01425e | |||
| 7bfd48e2ab | |||
| e80309af32 | |||
| fa1814ff54 | |||
| d7b8a2684a | |||
| 8559cd299d | |||
| b2d3c7d1fd | |||
| 5100e0e8b7 | |||
| 8acb921ee3 | |||
| fc88001a8c | |||
| fab6c4048d | |||
| 7b32791006 | |||
| aa04948319 | |||
| 5903eb8ceb | |||
| 211e62a8e7 | |||
| 64a9b06e64 | |||
| 6d81aa4462 | |||
| 406b6bf410 | |||
| 4e41d8aff8 | |||
| 017c6e4e1f | |||
| 7d3233c7ad | |||
| 3e2662582c | |||
| c94dee8191 | |||
| ab2ef13d99 | |||
| fac9ab5c01 | |||
| 420e0b9ca4 | |||
| ea0340db8e | |||
| 0bd1ea07a7 | |||
| c8c28770fb | |||
| 0dfd968824 | |||
| b84ca9cc13 | |||
| e5734425ba | |||
| 2274a10cfd | |||
| 302e234bfe | |||
| 2b942bf06b | |||
| 522f6cb455 | |||
| a757fbd37d | |||
| 33567ef338 | |||
| b2a15c259e | |||
| d12409b118 | |||
| 8aed1eca13 | |||
| f389688caa | |||
| 7003c284d0 | |||
| facb2949c3 | |||
| 1b1355a595 | |||
| d76e4c8aed | |||
| cc1224e032 | |||
| 09de115670 | |||
| 23110b5d19 | |||
| a326f4a989 | |||
| d21b9d58e1 | |||
| 9d362c3738 | |||
| b06580d343 | |||
| a4240bdfb7 | |||
| 1eab1cb367 | |||
| 2e38416e12 | |||
| 034a3f1808 |
@ -1,12 +1,7 @@
|
||||
// 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 driver
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"github.com/gogf/gf/database/gdb"
|
||||
"github.com/gogf/gf/os/gtime"
|
||||
@ -47,9 +42,9 @@ func (d *MyDriver) New(core *gdb.Core, node *gdb.ConfigNode) (gdb.DB, error) {
|
||||
|
||||
// DoQuery commits the sql string and its arguments to underlying driver
|
||||
// through given link object and returns the execution result.
|
||||
func (d *MyDriver) DoQuery(link gdb.Link, sql string, args ...interface{}) (rows *sql.Rows, err error) {
|
||||
func (d *MyDriver) DoQuery(ctx context.Context, link gdb.Link, sql string, args ...interface{}) (rows *sql.Rows, err error) {
|
||||
tsMilli := gtime.TimestampMilli()
|
||||
rows, err = d.DriverMysql.DoQuery(link, sql, args...)
|
||||
rows, err = d.DriverMysql.DoQuery(ctx, link, sql, args...)
|
||||
link.Exec(
|
||||
"INSERT INTO `monitor`(`sql`,`cost`,`time`,`error`) VALUES(?,?,?,?)",
|
||||
gdb.FormatSqlWithArgs(sql, args),
|
||||
@ -62,9 +57,9 @@ func (d *MyDriver) DoQuery(link gdb.Link, sql string, args ...interface{}) (rows
|
||||
|
||||
// DoExec commits the query string and its arguments to underlying driver
|
||||
// through given link object and returns the execution result.
|
||||
func (d *MyDriver) DoExec(link gdb.Link, sql string, args ...interface{}) (result sql.Result, err error) {
|
||||
func (d *MyDriver) DoExec(ctx context.Context, link gdb.Link, sql string, args ...interface{}) (result sql.Result, err error) {
|
||||
tsMilli := gtime.TimestampMilli()
|
||||
result, err = d.DriverMysql.DoExec(link, sql, args...)
|
||||
result, err = d.DriverMysql.DoExec(ctx, link, sql, args...)
|
||||
link.Exec(
|
||||
"INSERT INTO `monitor`(`sql`,`cost`,`time`,`error`) VALUES(?,?,?,?)",
|
||||
gdb.FormatSqlWithArgs(sql, args),
|
||||
|
||||
@ -10,9 +10,9 @@ func main() {
|
||||
//db := g.DB()
|
||||
|
||||
gdb.AddDefaultConfigNode(gdb.ConfigNode{
|
||||
LinkInfo: "root:12345678@tcp(127.0.0.1:3306)/test?parseTime=true&loc=Local",
|
||||
Type: "mysql",
|
||||
Charset: "utf8",
|
||||
Link: "root:12345678@tcp(127.0.0.1:3306)/test?parseTime=true&loc=Local",
|
||||
Type: "mysql",
|
||||
Charset: "utf8",
|
||||
})
|
||||
db, _ := gdb.New()
|
||||
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/gogf/gf/i18n/gi18n"
|
||||
@ -13,6 +14,6 @@ func main() {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println(t.Translate(`hello`))
|
||||
fmt.Println(t.Translate(`{#hello}{#world}!`))
|
||||
fmt.Println(t.Translate(context.TODO(), `hello`))
|
||||
fmt.Println(t.Translate(context.TODO(), `{#hello}{#world}!`))
|
||||
}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/gogf/gf/i18n/gi18n"
|
||||
@ -13,6 +14,6 @@ func main() {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println(t.Translate(`hello`))
|
||||
fmt.Println(t.Translate(`{#hello}{#world}!`))
|
||||
fmt.Println(t.Translate(context.TODO(), `hello`))
|
||||
fmt.Println(t.Translate(context.TODO(), `{#hello}{#world}!`))
|
||||
}
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/i18n/gi18n"
|
||||
)
|
||||
|
||||
func main() {
|
||||
@ -10,6 +12,12 @@ func main() {
|
||||
orderId = 865271654
|
||||
orderAmount = 99.8
|
||||
)
|
||||
fmt.Println(g.I18n().Tfl(`en`, `{#OrderPaid}`, orderId, orderAmount))
|
||||
fmt.Println(g.I18n().Tfl(`zh-CN`, `{#OrderPaid}`, orderId, orderAmount))
|
||||
fmt.Println(g.I18n().Tf(
|
||||
gi18n.WithLanguage(context.TODO(), `en`),
|
||||
`{#OrderPaid}`, orderId, orderAmount,
|
||||
))
|
||||
fmt.Println(g.I18n().Tf(
|
||||
gi18n.WithLanguage(context.TODO(), `zh-CN`),
|
||||
`{#OrderPaid}`, orderId, orderAmount,
|
||||
))
|
||||
}
|
||||
|
||||
@ -2,15 +2,19 @@ package main
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/i18n/gi18n"
|
||||
"github.com/gogf/gf/net/ghttp"
|
||||
)
|
||||
|
||||
func main() {
|
||||
g.I18n().SetPath("/Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/.example/i18n/gi18n/i18n")
|
||||
s := g.Server()
|
||||
s.BindHandler("/", func(r *ghttp.Request) {
|
||||
r.Response.WriteTplContent(`{#hello}{#world}!`, g.Map{
|
||||
"I18nLanguage": r.Get("lang", "zh-CN"),
|
||||
s.Group("/", func(group *ghttp.RouterGroup) {
|
||||
group.Middleware(func(r *ghttp.Request) {
|
||||
r.SetCtx(gi18n.WithLanguage(r.Context(), r.GetString("lang", "zh-CN")))
|
||||
r.Middleware.Next()
|
||||
})
|
||||
group.ALL("/", func(r *ghttp.Request) {
|
||||
r.Response.WriteTplContent(`{#hello}{#world}!`)
|
||||
})
|
||||
})
|
||||
s.SetPort(8199)
|
||||
|
||||
@ -1 +1,3 @@
|
||||
OrderPaid = "您已成功完成订单号 #%d 支付,支付金额¥%.2f。"
|
||||
OrderPaid = "您已成功完成订单号 #%d 支付,支付金额¥%.2f。"
|
||||
hello = "你好"
|
||||
world = "世界"
|
||||
64
.example/net/ghttp/client/middleware/client.go
Normal file
64
.example/net/ghttp/client/middleware/client.go
Normal file
@ -0,0 +1,64 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/container/garray"
|
||||
"github.com/gogf/gf/crypto/gmd5"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/internal/json"
|
||||
"github.com/gogf/gf/net/ghttp"
|
||||
"github.com/gogf/gf/os/gtime"
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
"github.com/gogf/gf/util/guid"
|
||||
"github.com/gogf/gf/util/gutil"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
const (
|
||||
appId = "123"
|
||||
appSecret = "456"
|
||||
)
|
||||
|
||||
// 注入统一的接口签名参数
|
||||
func injectSignature(jsonContent []byte) []byte {
|
||||
var m map[string]interface{}
|
||||
_ = json.Unmarshal(jsonContent, &m)
|
||||
if len(m) > 0 {
|
||||
m["appid"] = appId
|
||||
m["nonce"] = guid.S()
|
||||
m["timestamp"] = gtime.Timestamp()
|
||||
var (
|
||||
keyArray = garray.NewSortedStrArrayFrom(gutil.Keys(m))
|
||||
sigContent string
|
||||
)
|
||||
keyArray.Iterator(func(k int, v string) bool {
|
||||
sigContent += v
|
||||
sigContent += gconv.String(m[v])
|
||||
return true
|
||||
})
|
||||
m["signature"] = gmd5.MustEncryptString(gmd5.MustEncryptString(sigContent) + appSecret)
|
||||
jsonContent, _ = json.Marshal(m)
|
||||
}
|
||||
return jsonContent
|
||||
}
|
||||
|
||||
func main() {
|
||||
c := g.Client()
|
||||
c.Use(func(c *ghttp.Client, r *http.Request) (resp *ghttp.ClientResponse, err error) {
|
||||
bodyBytes, _ := ioutil.ReadAll(r.Body)
|
||||
if len(bodyBytes) > 0 {
|
||||
// 注入签名相关参数,修改Request原有的提交参数
|
||||
bodyBytes = injectSignature(bodyBytes)
|
||||
r.Body = ioutil.NopCloser(bytes.NewBuffer(bodyBytes))
|
||||
r.ContentLength = int64(len(bodyBytes))
|
||||
}
|
||||
return c.Next(r)
|
||||
})
|
||||
content := c.ContentJson().PostContent("http://127.0.0.1:8199/", g.Map{
|
||||
"name": "goframe",
|
||||
"site": "https://goframe.org",
|
||||
})
|
||||
fmt.Println(content)
|
||||
}
|
||||
17
.example/net/ghttp/client/middleware/server.go
Normal file
17
.example/net/ghttp/client/middleware/server.go
Normal file
@ -0,0 +1,17 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/net/ghttp"
|
||||
)
|
||||
|
||||
func main() {
|
||||
s := g.Server()
|
||||
s.Group("/", func(group *ghttp.RouterGroup) {
|
||||
group.ALL("/", func(r *ghttp.Request) {
|
||||
r.Response.Write(r.GetMap())
|
||||
})
|
||||
})
|
||||
s.SetPort(8199)
|
||||
s.Run()
|
||||
}
|
||||
@ -25,7 +25,7 @@ func main() {
|
||||
//fmt.Println(r.GetBody())
|
||||
if err := r.Parse(&req); err != nil {
|
||||
// Validation error.
|
||||
if v, ok := err.(*gvalid.Error); ok {
|
||||
if v, ok := err.(gvalid.Error); ok {
|
||||
r.Response.WriteJsonExit(RegisterRes{
|
||||
Code: 1,
|
||||
Error: v.FirstString(),
|
||||
|
||||
@ -18,13 +18,13 @@ func main() {
|
||||
s.Group("/", func(rgroup *ghttp.RouterGroup) {
|
||||
rgroup.ALL("/user", func(r *ghttp.Request) {
|
||||
user := new(User)
|
||||
if err := r.GetToStruct(user); err != nil {
|
||||
if err := r.GetStruct(user); err != nil {
|
||||
r.Response.WriteJsonExit(g.Map{
|
||||
"message": err,
|
||||
"errcode": 1,
|
||||
})
|
||||
}
|
||||
if err := gvalid.CheckStruct(user, nil); err != nil {
|
||||
if err := gvalid.CheckStruct(r.Context(), user, nil); err != nil {
|
||||
r.Response.WriteJsonExit(g.Map{
|
||||
"message": err.Maps(),
|
||||
"errcode": 1,
|
||||
|
||||
@ -24,7 +24,7 @@ func main() {
|
||||
var req *RegisterReq
|
||||
if err := r.Parse(&req); err != nil {
|
||||
// Validation error.
|
||||
if v, ok := err.(*gvalid.Error); ok {
|
||||
if v, ok := err.(gvalid.Error); ok {
|
||||
r.Response.WriteJsonExit(RegisterRes{
|
||||
Code: 1,
|
||||
Error: v.FirstString(),
|
||||
36
.example/net/ghttp/server/session/redis/redis_bigint.go
Normal file
36
.example/net/ghttp/server/session/redis/redis_bigint.go
Normal file
@ -0,0 +1,36 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/net/ghttp"
|
||||
"github.com/gogf/gf/os/gsession"
|
||||
)
|
||||
|
||||
func main() {
|
||||
type User struct {
|
||||
Id int64
|
||||
Name string
|
||||
}
|
||||
s := g.Server()
|
||||
s.SetSessionStorage(gsession.NewStorageRedis(g.Redis()))
|
||||
s.Group("/", func(group *ghttp.RouterGroup) {
|
||||
group.GET("/set", func(r *ghttp.Request) {
|
||||
user := &User{
|
||||
Id: 1265476890672672808,
|
||||
Name: "john",
|
||||
}
|
||||
if err := r.Session.Set("user", user); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
r.Response.Write("ok")
|
||||
})
|
||||
group.GET("/get", func(r *ghttp.Request) {
|
||||
r.Response.WriteJson(r.Session.Get("user"))
|
||||
})
|
||||
group.GET("/clear", func(r *ghttp.Request) {
|
||||
r.Session.Clear()
|
||||
})
|
||||
})
|
||||
s.SetPort(8199)
|
||||
s.Run()
|
||||
}
|
||||
54
.example/os/gproc/signal/signal_handler.go
Normal file
54
.example/os/gproc/signal/signal_handler.go
Normal file
@ -0,0 +1,54 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
func signalHandlerForMQ() {
|
||||
var (
|
||||
sig os.Signal
|
||||
receivedChan = make(chan os.Signal)
|
||||
)
|
||||
signal.Notify(
|
||||
receivedChan,
|
||||
syscall.SIGINT,
|
||||
syscall.SIGQUIT,
|
||||
syscall.SIGKILL,
|
||||
syscall.SIGTERM,
|
||||
syscall.SIGABRT,
|
||||
)
|
||||
for {
|
||||
sig = <-receivedChan
|
||||
fmt.Println("MQ is shutting down due to signal:", sig.String())
|
||||
time.Sleep(time.Second)
|
||||
fmt.Println("MQ is shut down smoothly")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
fmt.Println("Process start, pid:", os.Getpid())
|
||||
go signalHandlerForMQ()
|
||||
|
||||
var (
|
||||
sig os.Signal
|
||||
receivedChan = make(chan os.Signal)
|
||||
)
|
||||
signal.Notify(
|
||||
receivedChan,
|
||||
syscall.SIGINT,
|
||||
syscall.SIGQUIT,
|
||||
syscall.SIGKILL,
|
||||
syscall.SIGTERM,
|
||||
syscall.SIGABRT,
|
||||
)
|
||||
for {
|
||||
sig = <-receivedChan
|
||||
fmt.Println("MainProcess is shutting down due to signal:", sig.String())
|
||||
return
|
||||
}
|
||||
}
|
||||
27
.example/os/gproc/signal/signal_handler_gproc.go
Normal file
27
.example/os/gproc/signal/signal_handler_gproc.go
Normal file
@ -0,0 +1,27 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/os/gproc"
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
||||
func signalHandlerForMQ(sig os.Signal) {
|
||||
fmt.Println("MQ is shutting down due to signal:", sig.String())
|
||||
time.Sleep(time.Second)
|
||||
fmt.Println("MQ is shut down smoothly")
|
||||
}
|
||||
|
||||
func signalHandlerForMain(sig os.Signal) {
|
||||
fmt.Println("MainProcess is shutting down due to signal:", sig.String())
|
||||
}
|
||||
|
||||
func main() {
|
||||
fmt.Println("Process start, pid:", os.Getpid())
|
||||
gproc.AddSigHandlerShutdown(
|
||||
signalHandlerForMQ,
|
||||
signalHandlerForMain,
|
||||
)
|
||||
gproc.Listen()
|
||||
}
|
||||
14
.example/util/gvalid/config.toml
Normal file
14
.example/util/gvalid/config.toml
Normal file
@ -0,0 +1,14 @@
|
||||
|
||||
|
||||
|
||||
# MySQL.
|
||||
[database]
|
||||
[database.default]
|
||||
link = "mysql:root:12345678@tcp(127.0.0.1:3306)/test"
|
||||
debug = true
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -1,31 +1,32 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/util/gvalid"
|
||||
)
|
||||
|
||||
func main() {
|
||||
//rule := "length:6,16"
|
||||
//if m := gvalid.Check("123456", rule, nil); m != nil {
|
||||
//if m := gvalid.Check(context.TODO(), "123456", rule, nil); m != nil {
|
||||
// fmt.Println(m)
|
||||
//}
|
||||
//if m := gvalid.Check("12345", rule, nil); m != nil {
|
||||
//if m := gvalid.Check(context.TODO(), "12345", rule, nil); m != nil {
|
||||
// fmt.Println(m)
|
||||
// // map[length:字段长度为6到16个字符]
|
||||
//}
|
||||
|
||||
//rule := "integer|between:6,16"
|
||||
//msgs := "请输入一个整数|参数大小不对啊老铁"
|
||||
//fmt.Println(gvalid.Check("5.66", rule, msgs))
|
||||
//fmt.Println(gvalid.Check(context.TODO(), "5.66", rule, msgs))
|
||||
//// map[integer:请输入一个整数 between:参数大小不对啊老铁]
|
||||
|
||||
//// 参数长度至少为6个数字或者6个字母,但是总长度不能超过16个字符
|
||||
//rule := `regex:\d{6,}|\D{6,}|max-length:16`
|
||||
//if m := gvalid.Check("123456", rule, nil); m != nil {
|
||||
//if m := gvalid.Check(context.TODO(), "123456", rule, nil); m != nil {
|
||||
// fmt.Println(m)
|
||||
//}
|
||||
//if m := gvalid.Check("abcde6", rule, nil); m != nil {
|
||||
//if m := gvalid.Check(context.TODO(), "abcde6", rule, nil); m != nil {
|
||||
// fmt.Println(m)
|
||||
// // map[regex:字段值不合法]
|
||||
//}
|
||||
@ -40,18 +41,20 @@ func main() {
|
||||
// "password" : "required|length:6,16|same:password2",
|
||||
// "password2" : "required|length:6,16",
|
||||
//}
|
||||
//fmt.Println(gvalid.CheckMap(params, rules))
|
||||
//fmt.Println(gvalid.CheckMap(context.TODO(), params, rules))
|
||||
//// map[passport:map[length:字段长度为6到16个字符] password:map[same:字段值不合法]]
|
||||
|
||||
params := map[string]interface{}{
|
||||
"passport": "john",
|
||||
"password": "123456",
|
||||
"password2": "1234567",
|
||||
"name": "gf",
|
||||
}
|
||||
rules := map[string]string{
|
||||
"passport": "required|length:6,16",
|
||||
"password": "required|length:6,16|same:password2",
|
||||
"password2": "required|length:6,16",
|
||||
"name": "size:5",
|
||||
}
|
||||
msgs := map[string]interface{}{
|
||||
"passport": "账号不能为空|账号长度应当在:min到:max之间",
|
||||
@ -59,8 +62,9 @@ func main() {
|
||||
"required": "密码不能为空",
|
||||
"same": "两次密码输入不相等",
|
||||
},
|
||||
"name": "名字长度必须为:size",
|
||||
}
|
||||
if e := gvalid.CheckMap(params, rules, msgs); e != nil {
|
||||
if e := gvalid.CheckMap(context.TODO(), params, rules, msgs); e != nil {
|
||||
g.Dump(e.Maps())
|
||||
}
|
||||
// map[passport:map[length:账号长度应当在6到16之间] password:map[same:两次密码输入不相等]]
|
||||
|
||||
28
.example/util/gvalid/gvalid_checkstructwithdata.go
Normal file
28
.example/util/gvalid/gvalid_checkstructwithdata.go
Normal file
@ -0,0 +1,28 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
"github.com/gogf/gf/util/gvalid"
|
||||
)
|
||||
|
||||
func main() {
|
||||
type User struct {
|
||||
Name string `v:"required#请输入用户姓名"`
|
||||
Type int `v:"required#请选择用户类型"`
|
||||
}
|
||||
data := g.Map{
|
||||
"name": "john",
|
||||
}
|
||||
user := User{}
|
||||
if err := gconv.Scan(data, &user); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
err := gvalid.CheckStructWithData(context.TODO(), user, data, nil)
|
||||
// 也可以使用
|
||||
// err := g.Validator().Data(data).CheckStruct(user)
|
||||
if err != nil {
|
||||
g.Dump(err.Items())
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/util/gvalid"
|
||||
@ -8,6 +9,6 @@ import (
|
||||
|
||||
func main() {
|
||||
g.I18n().SetLanguage("cn")
|
||||
err := gvalid.Check("", "required", nil)
|
||||
err := gvalid.Check(context.TODO(), "", "required", nil)
|
||||
fmt.Println(err.String())
|
||||
}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/util/gvalid"
|
||||
)
|
||||
@ -17,7 +18,7 @@ func main() {
|
||||
ConfiemPassword: "",
|
||||
}
|
||||
|
||||
e := gvalid.CheckStruct(user, nil)
|
||||
e := gvalid.CheckStruct(context.TODO(), user, nil)
|
||||
g.Dump(e.Map())
|
||||
g.Dump(e.Maps())
|
||||
g.Dump(e.String())
|
||||
|
||||
38
.example/util/gvalid/gvalid_i18n.go
Normal file
38
.example/util/gvalid/gvalid_i18n.go
Normal file
@ -0,0 +1,38 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/i18n/gi18n"
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
)
|
||||
|
||||
func main() {
|
||||
type User struct {
|
||||
Name string `v:"required#ReuiredUserName"`
|
||||
Type int `v:"required#ReuiredUserType"`
|
||||
Project string `v:"size:10#MustSize"`
|
||||
}
|
||||
var (
|
||||
data = g.Map{
|
||||
"name": "john",
|
||||
"project": "gf",
|
||||
}
|
||||
user = User{}
|
||||
ctxEn = gi18n.WithLanguage(context.TODO(), "en")
|
||||
ctxCh = gi18n.WithLanguage(context.TODO(), "zh-CN")
|
||||
)
|
||||
|
||||
if err := gconv.Scan(data, &user); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
// 英文
|
||||
if err := g.Validator().Ctx(ctxEn).Data(data).CheckStruct(user); err != nil {
|
||||
g.Dump(err.String())
|
||||
}
|
||||
// 中文
|
||||
if err := g.Validator().Ctx(ctxCh).Data(data).CheckStruct(user); err != nil {
|
||||
g.Dump(err.String())
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/util/gvalid"
|
||||
)
|
||||
@ -18,7 +19,7 @@ func main() {
|
||||
Pass2: "123",
|
||||
}
|
||||
|
||||
e := gvalid.CheckStruct(user, nil)
|
||||
e := gvalid.CheckStruct(context.TODO(), user, nil)
|
||||
g.Dump(e.String())
|
||||
g.Dump(e.FirstString())
|
||||
}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/gogf/gf/util/gvalid"
|
||||
@ -17,7 +18,7 @@ func main() {
|
||||
"password@required|length:6,16|same:password2#密码不能为空}|两次密码输入不相等",
|
||||
"password2@required|length:6,16#",
|
||||
}
|
||||
if e := gvalid.CheckMap(params, rules); e != nil {
|
||||
if e := gvalid.CheckMap(context.TODO(), params, rules); e != nil {
|
||||
fmt.Println(e.Map())
|
||||
fmt.Println(e.FirstItem())
|
||||
fmt.Println(e.FirstString())
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/util/gvalid"
|
||||
)
|
||||
@ -20,7 +21,7 @@ func main() {
|
||||
}
|
||||
|
||||
// 使用结构体定义的校验规则和错误提示进行校验
|
||||
g.Dump(gvalid.CheckStruct(user, nil).Map())
|
||||
g.Dump(gvalid.CheckStruct(context.TODO(), user, nil).Map())
|
||||
|
||||
// 自定义校验规则和错误提示,对定义的特定校验规则和错误提示进行覆盖
|
||||
rules := map[string]string{
|
||||
@ -31,5 +32,5 @@ func main() {
|
||||
"password3": "名称不能为空",
|
||||
},
|
||||
}
|
||||
g.Dump(gvalid.CheckStruct(user, rules, msgs).Map())
|
||||
g.Dump(gvalid.CheckStruct(context.TODO(), user, rules, msgs).Map())
|
||||
}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/util/gvalid"
|
||||
)
|
||||
@ -13,5 +14,5 @@ func main() {
|
||||
|
||||
user := &User{}
|
||||
|
||||
g.Dump(gvalid.CheckStruct(user, nil))
|
||||
g.Dump(gvalid.CheckStruct(context.TODO(), user, nil))
|
||||
}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/util/gvalid"
|
||||
)
|
||||
@ -15,5 +16,5 @@ func main() {
|
||||
Pass: "1",
|
||||
}
|
||||
|
||||
g.Dump(gvalid.CheckStruct(user, nil).Maps())
|
||||
g.Dump(gvalid.CheckStruct(context.TODO(), user, nil).Maps())
|
||||
}
|
||||
|
||||
40
.example/util/gvalid/gvalid_struct_meta.go
Normal file
40
.example/util/gvalid/gvalid_struct_meta.go
Normal file
@ -0,0 +1,40 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/errors/gerror"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/util/gmeta"
|
||||
)
|
||||
|
||||
type UserCreateReq struct {
|
||||
gmeta.Meta `v:"UserCreateReq"`
|
||||
Name string
|
||||
Pass string
|
||||
}
|
||||
|
||||
func UserCreateReqChecker(ctx context.Context, rule string, value interface{}, message string, data interface{}) error {
|
||||
user := &UserCreateReq{}
|
||||
if v, ok := data.(*UserCreateReq); ok {
|
||||
user = v
|
||||
}
|
||||
// SELECT COUNT(*) FROM `user` WHERE `name` = xxx
|
||||
count, err := g.Model("user").Ctx(ctx).Where("name", user.Name).Count()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if count > 0 {
|
||||
return gerror.Newf(`The name "%s" is already token`, user.Name)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
user := &UserCreateReq{
|
||||
Name: "john",
|
||||
Pass: "123456",
|
||||
}
|
||||
err := g.Validator().RuleFunc("UserCreateReq", UserCreateReqChecker).CheckStruct(user)
|
||||
fmt.Println(err)
|
||||
}
|
||||
@ -1,14 +0,0 @@
|
||||
|
||||
|
||||
"gf.gvalid.required" = "字段不能为空"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
7
.example/util/gvalid/i18n/en.toml
Normal file
7
.example/util/gvalid/i18n/en.toml
Normal file
@ -0,0 +1,7 @@
|
||||
"gf.gvalid.required" = "字段不能为空"
|
||||
|
||||
|
||||
"ReuiredUserName" = "Please input user name"
|
||||
"ReuiredUserType" = "Please select user type"
|
||||
"MustSize" = "Size of :attribute must be :size"
|
||||
|
||||
16
.example/util/gvalid/i18n/zh-CN.toml
Normal file
16
.example/util/gvalid/i18n/zh-CN.toml
Normal file
@ -0,0 +1,16 @@
|
||||
|
||||
|
||||
"gf.gvalid.required" = "字段不能为空"
|
||||
|
||||
"ReuiredUserName" = "请输入用户名称"
|
||||
"ReuiredUserType" = "请选择用户类型"
|
||||
"MustSize" = ":attribute长度必须为:size"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -1,4 +1,7 @@
|
||||
<!-- 为更高效率地交流并解决问题,请按照以下模板提交issue,感谢! -->
|
||||
<!-- 为高效处理您的疑问,如果觉得是BUG类问题,请您务必提供可复现该问题的最小可运行代码! -->
|
||||
<!-- 为高效处理您的疑问,如果觉得是BUG类问题,请您务必提供可复现该问题的最小可运行代码! -->
|
||||
<!-- 为高效处理您的疑问,如果觉得是BUG类问题,请您务必提供可复现该问题的最小可运行代码! -->
|
||||
<!-- 重要的事情说三遍! -->
|
||||
|
||||
### 1. 您当前使用的`Go`版本,及系统版本、系统架构?
|
||||
|
||||
|
||||
7
.github/ISSUE_TEMPLATE.MD
vendored
7
.github/ISSUE_TEMPLATE.MD
vendored
@ -1,5 +1,10 @@
|
||||
<!-- Please answer these questions before submitting your issue. Thanks! -->
|
||||
|
||||
<!-- 为高效处理您的疑问,如果觉得是BUG类问题,请您务必提供可复现该问题的最小可运行代码! -->
|
||||
<!-- 为高效处理您的疑问,如果觉得是BUG类问题,请您务必提供可复现该问题的最小可运行代码! -->
|
||||
<!-- 为高效处理您的疑问,如果觉得是BUG类问题,请您务必提供可复现该问题的最小可运行代码! -->
|
||||
<!-- 重要的事情说三遍! -->
|
||||
|
||||
### 1. What version of `Go` and system type/arch are you using?
|
||||
|
||||
<!--
|
||||
@ -13,7 +18,7 @@ What expect to see is like: `go 1.12, linux/amd64`
|
||||
<!-- You can find the GF version from your `go.mod`, or from the `version.go` in `GF` -->
|
||||
|
||||
|
||||
### 3. Can this issue be reproduced with the latest release?
|
||||
### 3. Can this issue be re-produced with the latest release?
|
||||
|
||||
|
||||
|
||||
|
||||
@ -30,7 +30,7 @@ type Array struct {
|
||||
}
|
||||
|
||||
// New creates and returns an empty array.
|
||||
// The parameter <safe> is used to specify whether using array in concurrent-safety,
|
||||
// The parameter `safe` is used to specify whether using array in concurrent-safety,
|
||||
// which is false in default.
|
||||
func New(safe ...bool) *Array {
|
||||
return NewArraySize(0, 0, safe...)
|
||||
@ -42,7 +42,7 @@ func NewArray(safe ...bool) *Array {
|
||||
}
|
||||
|
||||
// NewArraySize create and returns an array with given size and cap.
|
||||
// The parameter <safe> is used to specify whether using array in concurrent-safety,
|
||||
// The parameter `safe` is used to specify whether using array in concurrent-safety,
|
||||
// which is false in default.
|
||||
func NewArraySize(size int, cap int, safe ...bool) *Array {
|
||||
return &Array{
|
||||
@ -51,8 +51,8 @@ func NewArraySize(size int, cap int, safe ...bool) *Array {
|
||||
}
|
||||
}
|
||||
|
||||
// NewArrayRange creates and returns a array by a range from <start> to <end>
|
||||
// with step value <step>.
|
||||
// NewArrayRange creates and returns a array by a range from `start` to `end`
|
||||
// with step value `step`.
|
||||
func NewArrayRange(start, end, step int, safe ...bool) *Array {
|
||||
if step == 0 {
|
||||
panic(fmt.Sprintf(`invalid step value: %d`, step))
|
||||
@ -66,18 +66,20 @@ func NewArrayRange(start, end, step int, safe ...bool) *Array {
|
||||
return NewArrayFrom(slice, safe...)
|
||||
}
|
||||
|
||||
// NewFrom is alias of NewArrayFrom.
|
||||
// See NewArrayFrom.
|
||||
func NewFrom(array []interface{}, safe ...bool) *Array {
|
||||
return NewArrayFrom(array, safe...)
|
||||
}
|
||||
|
||||
// NewFromCopy is alias of NewArrayFromCopy.
|
||||
// See NewArrayFromCopy.
|
||||
func NewFromCopy(array []interface{}, safe ...bool) *Array {
|
||||
return NewArrayFromCopy(array, safe...)
|
||||
}
|
||||
|
||||
// NewArrayFrom creates and returns an array with given slice <array>.
|
||||
// The parameter <safe> is used to specify whether using array in concurrent-safety,
|
||||
// NewArrayFrom creates and returns an array with given slice `array`.
|
||||
// The parameter `safe` is used to specify whether using array in concurrent-safety,
|
||||
// which is false in default.
|
||||
func NewArrayFrom(array []interface{}, safe ...bool) *Array {
|
||||
return &Array{
|
||||
@ -86,8 +88,8 @@ func NewArrayFrom(array []interface{}, safe ...bool) *Array {
|
||||
}
|
||||
}
|
||||
|
||||
// NewArrayFromCopy creates and returns an array from a copy of given slice <array>.
|
||||
// The parameter <safe> is used to specify whether using array in concurrent-safety,
|
||||
// NewArrayFromCopy creates and returns an array from a copy of given slice `array`.
|
||||
// The parameter `safe` is used to specify whether using array in concurrent-safety,
|
||||
// which is false in default.
|
||||
func NewArrayFromCopy(array []interface{}, safe ...bool) *Array {
|
||||
newArray := make([]interface{}, len(array))
|
||||
@ -98,8 +100,15 @@ func NewArrayFromCopy(array []interface{}, safe ...bool) *Array {
|
||||
}
|
||||
}
|
||||
|
||||
// At returns the value by the specified index.
|
||||
// If the given `index` is out of range of the array, it returns `nil`.
|
||||
func (a *Array) At(index int) (value interface{}) {
|
||||
value, _ = a.Get(index)
|
||||
return
|
||||
}
|
||||
|
||||
// Get returns the value by the specified index.
|
||||
// If the given <index> is out of range of the array, the <found> is false.
|
||||
// If the given `index` is out of range of the array, the `found` is false.
|
||||
func (a *Array) Get(index int) (value interface{}, found bool) {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
@ -120,7 +129,7 @@ func (a *Array) Set(index int, value interface{}) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetArray sets the underlying slice array with the given <array>.
|
||||
// SetArray sets the underlying slice array with the given `array`.
|
||||
func (a *Array) SetArray(array []interface{}) *Array {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -128,7 +137,7 @@ func (a *Array) SetArray(array []interface{}) *Array {
|
||||
return a
|
||||
}
|
||||
|
||||
// Replace replaces the array items by given <array> from the beginning of array.
|
||||
// Replace replaces the array items by given `array` from the beginning of array.
|
||||
func (a *Array) Replace(array []interface{}) *Array {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -152,7 +161,7 @@ func (a *Array) Sum() (sum int) {
|
||||
return
|
||||
}
|
||||
|
||||
// SortFunc sorts the array by custom function <less>.
|
||||
// SortFunc sorts the array by custom function `less`.
|
||||
func (a *Array) SortFunc(less func(v1, v2 interface{}) bool) *Array {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -162,7 +171,7 @@ func (a *Array) SortFunc(less func(v1, v2 interface{}) bool) *Array {
|
||||
return a
|
||||
}
|
||||
|
||||
// InsertBefore inserts the <value> to the front of <index>.
|
||||
// InsertBefore inserts the `value` to the front of `index`.
|
||||
func (a *Array) InsertBefore(index int, value interface{}) error {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -175,7 +184,7 @@ func (a *Array) InsertBefore(index int, value interface{}) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// InsertAfter inserts the <value> to the back of <index>.
|
||||
// InsertAfter inserts the `value` to the back of `index`.
|
||||
func (a *Array) InsertAfter(index int, value interface{}) error {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -189,7 +198,7 @@ func (a *Array) InsertAfter(index int, value interface{}) error {
|
||||
}
|
||||
|
||||
// Remove removes an item by index.
|
||||
// If the given <index> is out of range of the array, the <found> is false.
|
||||
// If the given `index` is out of range of the array, the `found` is false.
|
||||
func (a *Array) Remove(index int) (value interface{}, found bool) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -247,14 +256,14 @@ func (a *Array) PushRight(value ...interface{}) *Array {
|
||||
}
|
||||
|
||||
// PopRand randomly pops and return an item out of array.
|
||||
// Note that if the array is empty, the <found> is false.
|
||||
// Note that if the array is empty, the `found` is false.
|
||||
func (a *Array) PopRand() (value interface{}, found bool) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
return a.doRemoveWithoutLock(grand.Intn(len(a.array)))
|
||||
}
|
||||
|
||||
// PopRands randomly pops and returns <size> items out of array.
|
||||
// PopRands randomly pops and returns `size` items out of array.
|
||||
func (a *Array) PopRands(size int) []interface{} {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -272,7 +281,7 @@ func (a *Array) PopRands(size int) []interface{} {
|
||||
}
|
||||
|
||||
// PopLeft pops and returns an item from the beginning of array.
|
||||
// Note that if the array is empty, the <found> is false.
|
||||
// Note that if the array is empty, the `found` is false.
|
||||
func (a *Array) PopLeft() (value interface{}, found bool) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -285,7 +294,7 @@ func (a *Array) PopLeft() (value interface{}, found bool) {
|
||||
}
|
||||
|
||||
// PopRight pops and returns an item from the end of array.
|
||||
// Note that if the array is empty, the <found> is false.
|
||||
// Note that if the array is empty, the `found` is false.
|
||||
func (a *Array) PopRight() (value interface{}, found bool) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -298,7 +307,7 @@ func (a *Array) PopRight() (value interface{}, found bool) {
|
||||
return value, true
|
||||
}
|
||||
|
||||
// PopLefts pops and returns <size> items from the beginning of array.
|
||||
// PopLefts pops and returns `size` items from the beginning of array.
|
||||
func (a *Array) PopLefts(size int) []interface{} {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -315,7 +324,7 @@ func (a *Array) PopLefts(size int) []interface{} {
|
||||
return value
|
||||
}
|
||||
|
||||
// PopRights pops and returns <size> items from the end of array.
|
||||
// PopRights pops and returns `size` items from the end of array.
|
||||
func (a *Array) PopRights(size int) []interface{} {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -337,8 +346,8 @@ func (a *Array) PopRights(size int) []interface{} {
|
||||
// Notice, if in concurrent-safe usage, it returns a copy of slice;
|
||||
// else a pointer to the underlying data.
|
||||
//
|
||||
// If <end> is negative, then the offset will start from the end of array.
|
||||
// If <end> is omitted, then the sequence will have everything from start up
|
||||
// If `end` is negative, then the offset will start from the end of array.
|
||||
// If `end` is omitted, then the sequence will have everything from start up
|
||||
// until the end of the array.
|
||||
func (a *Array) Range(start int, end ...int) []interface{} {
|
||||
a.mu.RLock()
|
||||
@ -364,7 +373,7 @@ func (a *Array) Range(start int, end ...int) []interface{} {
|
||||
}
|
||||
|
||||
// SubSlice returns a slice of elements from the array as specified
|
||||
// by the <offset> and <size> parameters.
|
||||
// by the `offset` and `size` parameters.
|
||||
// If in concurrent safe usage, it returns a copy of the slice; else a pointer.
|
||||
//
|
||||
// If offset is non-negative, the sequence will start at that offset in the array.
|
||||
@ -471,7 +480,7 @@ func (a *Array) Contains(value interface{}) bool {
|
||||
return a.Search(value) != -1
|
||||
}
|
||||
|
||||
// Search searches array by <value>, returns the index of <value>,
|
||||
// Search searches array by `value`, returns the index of `value`,
|
||||
// or returns -1 if not exists.
|
||||
func (a *Array) Search(value interface{}) int {
|
||||
a.mu.RLock()
|
||||
@ -506,7 +515,7 @@ func (a *Array) Unique() *Array {
|
||||
return a
|
||||
}
|
||||
|
||||
// LockFunc locks writing by callback function <f>.
|
||||
// LockFunc locks writing by callback function `f`.
|
||||
func (a *Array) LockFunc(f func(array []interface{})) *Array {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -514,7 +523,7 @@ func (a *Array) LockFunc(f func(array []interface{})) *Array {
|
||||
return a
|
||||
}
|
||||
|
||||
// RLockFunc locks reading by callback function <f>.
|
||||
// RLockFunc locks reading by callback function `f`.
|
||||
func (a *Array) RLockFunc(f func(array []interface{})) *Array {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
@ -522,16 +531,16 @@ func (a *Array) RLockFunc(f func(array []interface{})) *Array {
|
||||
return a
|
||||
}
|
||||
|
||||
// Merge merges <array> into current array.
|
||||
// The parameter <array> can be any garray or slice type.
|
||||
// Merge merges `array` into current array.
|
||||
// The parameter `array` can be any garray or slice type.
|
||||
// The difference between Merge and Append is Append supports only specified slice type,
|
||||
// but Merge supports more parameter types.
|
||||
func (a *Array) Merge(array interface{}) *Array {
|
||||
return a.Append(gconv.Interfaces(array)...)
|
||||
}
|
||||
|
||||
// Fill fills an array with num entries of the value <value>,
|
||||
// keys starting at the <startIndex> parameter.
|
||||
// Fill fills an array with num entries of the value `value`,
|
||||
// keys starting at the `startIndex` parameter.
|
||||
func (a *Array) Fill(startIndex int, num int, value interface{}) error {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -549,7 +558,7 @@ func (a *Array) Fill(startIndex int, num int, value interface{}) error {
|
||||
}
|
||||
|
||||
// Chunk splits an array into multiple arrays,
|
||||
// the size of each array is determined by <size>.
|
||||
// the size of each array is determined by `size`.
|
||||
// The last chunk may contain less than size elements.
|
||||
func (a *Array) Chunk(size int) [][]interface{} {
|
||||
if size < 1 {
|
||||
@ -571,9 +580,9 @@ func (a *Array) Chunk(size int) [][]interface{} {
|
||||
return n
|
||||
}
|
||||
|
||||
// Pad pads array to the specified length with <value>.
|
||||
// Pad pads array to the specified length with `value`.
|
||||
// If size is positive then the array is padded on the right, or negative on the left.
|
||||
// If the absolute value of <size> is less than or equal to the length of the array
|
||||
// If the absolute value of `size` is less than or equal to the length of the array
|
||||
// then no padding takes place.
|
||||
func (a *Array) Pad(size int, val interface{}) *Array {
|
||||
a.mu.Lock()
|
||||
@ -608,7 +617,7 @@ func (a *Array) Rand() (value interface{}, found bool) {
|
||||
return a.array[grand.Intn(len(a.array))], true
|
||||
}
|
||||
|
||||
// Rands randomly returns <size> items from array(no deleting).
|
||||
// Rands randomly returns `size` items from array(no deleting).
|
||||
func (a *Array) Rands(size int) []interface{} {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
@ -642,7 +651,7 @@ func (a *Array) Reverse() *Array {
|
||||
return a
|
||||
}
|
||||
|
||||
// Join joins array elements with a string <glue>.
|
||||
// Join joins array elements with a string `glue`.
|
||||
func (a *Array) Join(glue string) string {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
@ -675,8 +684,8 @@ func (a *Array) Iterator(f func(k int, v interface{}) bool) {
|
||||
a.IteratorAsc(f)
|
||||
}
|
||||
|
||||
// IteratorAsc iterates the array readonly in ascending order with given callback function <f>.
|
||||
// If <f> returns true, then it continues iterating; or false to stop.
|
||||
// IteratorAsc iterates the array readonly in ascending order with given callback function `f`.
|
||||
// If `f` returns true, then it continues iterating; or false to stop.
|
||||
func (a *Array) IteratorAsc(f func(k int, v interface{}) bool) {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
@ -687,8 +696,8 @@ func (a *Array) IteratorAsc(f func(k int, v interface{}) bool) {
|
||||
}
|
||||
}
|
||||
|
||||
// IteratorDesc iterates the array readonly in descending order with given callback function <f>.
|
||||
// If <f> returns true, then it continues iterating; or false to stop.
|
||||
// IteratorDesc iterates the array readonly in descending order with given callback function `f`.
|
||||
// If `f` returns true, then it continues iterating; or false to stop.
|
||||
func (a *Array) IteratorDesc(f func(k int, v interface{}) bool) {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
@ -736,7 +745,7 @@ func (a *Array) UnmarshalJSON(b []byte) error {
|
||||
}
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if err := json.Unmarshal(b, &a.array); err != nil {
|
||||
if err := json.UnmarshalUseNumber(b, &a.array); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
@ -748,7 +757,7 @@ func (a *Array) UnmarshalValue(value interface{}) error {
|
||||
defer a.mu.Unlock()
|
||||
switch value.(type) {
|
||||
case string, []byte:
|
||||
return json.Unmarshal(gconv.Bytes(value), &a.array)
|
||||
return json.UnmarshalUseNumber(gconv.Bytes(value), &a.array)
|
||||
default:
|
||||
a.array = gconv.SliceAny(value)
|
||||
}
|
||||
@ -784,7 +793,7 @@ func (a *Array) FilterEmpty() *Array {
|
||||
return a
|
||||
}
|
||||
|
||||
// Walk applies a user supplied function <f> to every item of array.
|
||||
// Walk applies a user supplied function `f` to every item of array.
|
||||
func (a *Array) Walk(f func(value interface{}) interface{}) *Array {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
|
||||
@ -28,14 +28,14 @@ type IntArray struct {
|
||||
}
|
||||
|
||||
// NewIntArray creates and returns an empty array.
|
||||
// The parameter <safe> is used to specify whether using array in concurrent-safety,
|
||||
// The parameter `safe` is used to specify whether using array in concurrent-safety,
|
||||
// which is false in default.
|
||||
func NewIntArray(safe ...bool) *IntArray {
|
||||
return NewIntArraySize(0, 0, safe...)
|
||||
}
|
||||
|
||||
// NewIntArraySize create and returns an array with given size and cap.
|
||||
// The parameter <safe> is used to specify whether using array in concurrent-safety,
|
||||
// The parameter `safe` is used to specify whether using array in concurrent-safety,
|
||||
// which is false in default.
|
||||
func NewIntArraySize(size int, cap int, safe ...bool) *IntArray {
|
||||
return &IntArray{
|
||||
@ -44,8 +44,8 @@ func NewIntArraySize(size int, cap int, safe ...bool) *IntArray {
|
||||
}
|
||||
}
|
||||
|
||||
// NewIntArrayRange creates and returns a array by a range from <start> to <end>
|
||||
// with step value <step>.
|
||||
// NewIntArrayRange creates and returns a array by a range from `start` to `end`
|
||||
// with step value `step`.
|
||||
func NewIntArrayRange(start, end, step int, safe ...bool) *IntArray {
|
||||
if step == 0 {
|
||||
panic(fmt.Sprintf(`invalid step value: %d`, step))
|
||||
@ -59,8 +59,8 @@ func NewIntArrayRange(start, end, step int, safe ...bool) *IntArray {
|
||||
return NewIntArrayFrom(slice, safe...)
|
||||
}
|
||||
|
||||
// NewIntArrayFrom creates and returns an array with given slice <array>.
|
||||
// The parameter <safe> is used to specify whether using array in concurrent-safety,
|
||||
// NewIntArrayFrom creates and returns an array with given slice `array`.
|
||||
// The parameter `safe` is used to specify whether using array in concurrent-safety,
|
||||
// which is false in default.
|
||||
func NewIntArrayFrom(array []int, safe ...bool) *IntArray {
|
||||
return &IntArray{
|
||||
@ -69,8 +69,8 @@ func NewIntArrayFrom(array []int, safe ...bool) *IntArray {
|
||||
}
|
||||
}
|
||||
|
||||
// NewIntArrayFromCopy creates and returns an array from a copy of given slice <array>.
|
||||
// The parameter <safe> is used to specify whether using array in concurrent-safety,
|
||||
// NewIntArrayFromCopy creates and returns an array from a copy of given slice `array`.
|
||||
// The parameter `safe` is used to specify whether using array in concurrent-safety,
|
||||
// which is false in default.
|
||||
func NewIntArrayFromCopy(array []int, safe ...bool) *IntArray {
|
||||
newArray := make([]int, len(array))
|
||||
@ -81,8 +81,15 @@ func NewIntArrayFromCopy(array []int, safe ...bool) *IntArray {
|
||||
}
|
||||
}
|
||||
|
||||
// At returns the value by the specified index.
|
||||
// If the given `index` is out of range of the array, it returns `0`.
|
||||
func (a *IntArray) At(index int) (value int) {
|
||||
value, _ = a.Get(index)
|
||||
return
|
||||
}
|
||||
|
||||
// Get returns the value by the specified index.
|
||||
// If the given <index> is out of range of the array, the <found> is false.
|
||||
// If the given `index` is out of range of the array, the `found` is false.
|
||||
func (a *IntArray) Get(index int) (value int, found bool) {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
@ -103,7 +110,7 @@ func (a *IntArray) Set(index int, value int) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetArray sets the underlying slice array with the given <array>.
|
||||
// SetArray sets the underlying slice array with the given `array`.
|
||||
func (a *IntArray) SetArray(array []int) *IntArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -111,7 +118,7 @@ func (a *IntArray) SetArray(array []int) *IntArray {
|
||||
return a
|
||||
}
|
||||
|
||||
// Replace replaces the array items by given <array> from the beginning of array.
|
||||
// Replace replaces the array items by given `array` from the beginning of array.
|
||||
func (a *IntArray) Replace(array []int) *IntArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -136,7 +143,7 @@ func (a *IntArray) Sum() (sum int) {
|
||||
}
|
||||
|
||||
// Sort sorts the array in increasing order.
|
||||
// The parameter <reverse> controls whether sort in increasing order(default) or decreasing order.
|
||||
// The parameter `reverse` controls whether sort in increasing order(default) or decreasing order.
|
||||
func (a *IntArray) Sort(reverse ...bool) *IntArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -153,7 +160,7 @@ func (a *IntArray) Sort(reverse ...bool) *IntArray {
|
||||
return a
|
||||
}
|
||||
|
||||
// SortFunc sorts the array by custom function <less>.
|
||||
// SortFunc sorts the array by custom function `less`.
|
||||
func (a *IntArray) SortFunc(less func(v1, v2 int) bool) *IntArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -163,7 +170,7 @@ func (a *IntArray) SortFunc(less func(v1, v2 int) bool) *IntArray {
|
||||
return a
|
||||
}
|
||||
|
||||
// InsertBefore inserts the <value> to the front of <index>.
|
||||
// InsertBefore inserts the `value` to the front of `index`.
|
||||
func (a *IntArray) InsertBefore(index int, value int) error {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -176,7 +183,7 @@ func (a *IntArray) InsertBefore(index int, value int) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// InsertAfter inserts the <value> to the back of <index>.
|
||||
// InsertAfter inserts the `value` to the back of `index`.
|
||||
func (a *IntArray) InsertAfter(index int, value int) error {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -190,7 +197,7 @@ func (a *IntArray) InsertAfter(index int, value int) error {
|
||||
}
|
||||
|
||||
// Remove removes an item by index.
|
||||
// If the given <index> is out of range of the array, the <found> is false.
|
||||
// If the given `index` is out of range of the array, the `found` is false.
|
||||
func (a *IntArray) Remove(index int) (value int, found bool) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -248,7 +255,7 @@ func (a *IntArray) PushRight(value ...int) *IntArray {
|
||||
}
|
||||
|
||||
// PopLeft pops and returns an item from the beginning of array.
|
||||
// Note that if the array is empty, the <found> is false.
|
||||
// Note that if the array is empty, the `found` is false.
|
||||
func (a *IntArray) PopLeft() (value int, found bool) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -261,7 +268,7 @@ func (a *IntArray) PopLeft() (value int, found bool) {
|
||||
}
|
||||
|
||||
// PopRight pops and returns an item from the end of array.
|
||||
// Note that if the array is empty, the <found> is false.
|
||||
// Note that if the array is empty, the `found` is false.
|
||||
func (a *IntArray) PopRight() (value int, found bool) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -275,16 +282,16 @@ func (a *IntArray) PopRight() (value int, found bool) {
|
||||
}
|
||||
|
||||
// PopRand randomly pops and return an item out of array.
|
||||
// Note that if the array is empty, the <found> is false.
|
||||
// Note that if the array is empty, the `found` is false.
|
||||
func (a *IntArray) PopRand() (value int, found bool) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
return a.doRemoveWithoutLock(grand.Intn(len(a.array)))
|
||||
}
|
||||
|
||||
// PopRands randomly pops and returns <size> items out of array.
|
||||
// If the given <size> is greater than size of the array, it returns all elements of the array.
|
||||
// Note that if given <size> <= 0 or the array is empty, it returns nil.
|
||||
// PopRands randomly pops and returns `size` items out of array.
|
||||
// If the given `size` is greater than size of the array, it returns all elements of the array.
|
||||
// Note that if given `size` <= 0 or the array is empty, it returns nil.
|
||||
func (a *IntArray) PopRands(size int) []int {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -301,9 +308,9 @@ func (a *IntArray) PopRands(size int) []int {
|
||||
return array
|
||||
}
|
||||
|
||||
// PopLefts pops and returns <size> items from the beginning of array.
|
||||
// If the given <size> is greater than size of the array, it returns all elements of the array.
|
||||
// Note that if given <size> <= 0 or the array is empty, it returns nil.
|
||||
// PopLefts pops and returns `size` items from the beginning of array.
|
||||
// If the given `size` is greater than size of the array, it returns all elements of the array.
|
||||
// Note that if given `size` <= 0 or the array is empty, it returns nil.
|
||||
func (a *IntArray) PopLefts(size int) []int {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -320,9 +327,9 @@ func (a *IntArray) PopLefts(size int) []int {
|
||||
return value
|
||||
}
|
||||
|
||||
// PopRights pops and returns <size> items from the end of array.
|
||||
// If the given <size> is greater than size of the array, it returns all elements of the array.
|
||||
// Note that if given <size> <= 0 or the array is empty, it returns nil.
|
||||
// PopRights pops and returns `size` items from the end of array.
|
||||
// If the given `size` is greater than size of the array, it returns all elements of the array.
|
||||
// Note that if given `size` <= 0 or the array is empty, it returns nil.
|
||||
func (a *IntArray) PopRights(size int) []int {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -344,8 +351,8 @@ func (a *IntArray) PopRights(size int) []int {
|
||||
// Notice, if in concurrent-safe usage, it returns a copy of slice;
|
||||
// else a pointer to the underlying data.
|
||||
//
|
||||
// If <end> is negative, then the offset will start from the end of array.
|
||||
// If <end> is omitted, then the sequence will have everything from start up
|
||||
// If `end` is negative, then the offset will start from the end of array.
|
||||
// If `end` is omitted, then the sequence will have everything from start up
|
||||
// until the end of the array.
|
||||
func (a *IntArray) Range(start int, end ...int) []int {
|
||||
a.mu.RLock()
|
||||
@ -371,7 +378,7 @@ func (a *IntArray) Range(start int, end ...int) []int {
|
||||
}
|
||||
|
||||
// SubSlice returns a slice of elements from the array as specified
|
||||
// by the <offset> and <size> parameters.
|
||||
// by the `offset` and `size` parameters.
|
||||
// If in concurrent safe usage, it returns a copy of the slice; else a pointer.
|
||||
//
|
||||
// If offset is non-negative, the sequence will start at that offset in the array.
|
||||
@ -487,7 +494,7 @@ func (a *IntArray) Contains(value int) bool {
|
||||
return a.Search(value) != -1
|
||||
}
|
||||
|
||||
// Search searches array by <value>, returns the index of <value>,
|
||||
// Search searches array by `value`, returns the index of `value`,
|
||||
// or returns -1 if not exists.
|
||||
func (a *IntArray) Search(value int) int {
|
||||
a.mu.RLock()
|
||||
@ -522,7 +529,7 @@ func (a *IntArray) Unique() *IntArray {
|
||||
return a
|
||||
}
|
||||
|
||||
// LockFunc locks writing by callback function <f>.
|
||||
// LockFunc locks writing by callback function `f`.
|
||||
func (a *IntArray) LockFunc(f func(array []int)) *IntArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -530,7 +537,7 @@ func (a *IntArray) LockFunc(f func(array []int)) *IntArray {
|
||||
return a
|
||||
}
|
||||
|
||||
// RLockFunc locks reading by callback function <f>.
|
||||
// RLockFunc locks reading by callback function `f`.
|
||||
func (a *IntArray) RLockFunc(f func(array []int)) *IntArray {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
@ -538,16 +545,16 @@ func (a *IntArray) RLockFunc(f func(array []int)) *IntArray {
|
||||
return a
|
||||
}
|
||||
|
||||
// Merge merges <array> into current array.
|
||||
// The parameter <array> can be any garray or slice type.
|
||||
// Merge merges `array` into current array.
|
||||
// The parameter `array` can be any garray or slice type.
|
||||
// The difference between Merge and Append is Append supports only specified slice type,
|
||||
// but Merge supports more parameter types.
|
||||
func (a *IntArray) Merge(array interface{}) *IntArray {
|
||||
return a.Append(gconv.Ints(array)...)
|
||||
}
|
||||
|
||||
// Fill fills an array with num entries of the value <value>,
|
||||
// keys starting at the <startIndex> parameter.
|
||||
// Fill fills an array with num entries of the value `value`,
|
||||
// keys starting at the `startIndex` parameter.
|
||||
func (a *IntArray) Fill(startIndex int, num int, value int) error {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -565,7 +572,7 @@ func (a *IntArray) Fill(startIndex int, num int, value int) error {
|
||||
}
|
||||
|
||||
// Chunk splits an array into multiple arrays,
|
||||
// the size of each array is determined by <size>.
|
||||
// the size of each array is determined by `size`.
|
||||
// The last chunk may contain less than size elements.
|
||||
func (a *IntArray) Chunk(size int) [][]int {
|
||||
if size < 1 {
|
||||
@ -587,9 +594,9 @@ func (a *IntArray) Chunk(size int) [][]int {
|
||||
return n
|
||||
}
|
||||
|
||||
// Pad pads array to the specified length with <value>.
|
||||
// Pad pads array to the specified length with `value`.
|
||||
// If size is positive then the array is padded on the right, or negative on the left.
|
||||
// If the absolute value of <size> is less than or equal to the length of the array
|
||||
// If the absolute value of `size` is less than or equal to the length of the array
|
||||
// then no padding takes place.
|
||||
func (a *IntArray) Pad(size int, value int) *IntArray {
|
||||
a.mu.Lock()
|
||||
@ -624,7 +631,7 @@ func (a *IntArray) Rand() (value int, found bool) {
|
||||
return a.array[grand.Intn(len(a.array))], true
|
||||
}
|
||||
|
||||
// Rands randomly returns <size> items from array(no deleting).
|
||||
// Rands randomly returns `size` items from array(no deleting).
|
||||
func (a *IntArray) Rands(size int) []int {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
@ -658,7 +665,7 @@ func (a *IntArray) Reverse() *IntArray {
|
||||
return a
|
||||
}
|
||||
|
||||
// Join joins array elements with a string <glue>.
|
||||
// Join joins array elements with a string `glue`.
|
||||
func (a *IntArray) Join(glue string) string {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
@ -691,8 +698,8 @@ func (a *IntArray) Iterator(f func(k int, v int) bool) {
|
||||
a.IteratorAsc(f)
|
||||
}
|
||||
|
||||
// IteratorAsc iterates the array readonly in ascending order with given callback function <f>.
|
||||
// If <f> returns true, then it continues iterating; or false to stop.
|
||||
// IteratorAsc iterates the array readonly in ascending order with given callback function `f`.
|
||||
// If `f` returns true, then it continues iterating; or false to stop.
|
||||
func (a *IntArray) IteratorAsc(f func(k int, v int) bool) {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
@ -703,8 +710,8 @@ func (a *IntArray) IteratorAsc(f func(k int, v int) bool) {
|
||||
}
|
||||
}
|
||||
|
||||
// IteratorDesc iterates the array readonly in descending order with given callback function <f>.
|
||||
// If <f> returns true, then it continues iterating; or false to stop.
|
||||
// IteratorDesc iterates the array readonly in descending order with given callback function `f`.
|
||||
// If `f` returns true, then it continues iterating; or false to stop.
|
||||
func (a *IntArray) IteratorDesc(f func(k int, v int) bool) {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
@ -735,7 +742,7 @@ func (a *IntArray) UnmarshalJSON(b []byte) error {
|
||||
}
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if err := json.Unmarshal(b, &a.array); err != nil {
|
||||
if err := json.UnmarshalUseNumber(b, &a.array); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
@ -747,7 +754,7 @@ func (a *IntArray) UnmarshalValue(value interface{}) error {
|
||||
defer a.mu.Unlock()
|
||||
switch value.(type) {
|
||||
case string, []byte:
|
||||
return json.Unmarshal(gconv.Bytes(value), &a.array)
|
||||
return json.UnmarshalUseNumber(gconv.Bytes(value), &a.array)
|
||||
default:
|
||||
a.array = gconv.SliceInt(value)
|
||||
}
|
||||
@ -768,7 +775,7 @@ func (a *IntArray) FilterEmpty() *IntArray {
|
||||
return a
|
||||
}
|
||||
|
||||
// Walk applies a user supplied function <f> to every item of array.
|
||||
// Walk applies a user supplied function `f` to every item of array.
|
||||
func (a *IntArray) Walk(f func(value int) int) *IntArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
|
||||
@ -30,14 +30,14 @@ type StrArray struct {
|
||||
}
|
||||
|
||||
// NewStrArray creates and returns an empty array.
|
||||
// The parameter <safe> is used to specify whether using array in concurrent-safety,
|
||||
// The parameter `safe` is used to specify whether using array in concurrent-safety,
|
||||
// which is false in default.
|
||||
func NewStrArray(safe ...bool) *StrArray {
|
||||
return NewStrArraySize(0, 0, safe...)
|
||||
}
|
||||
|
||||
// NewStrArraySize create and returns an array with given size and cap.
|
||||
// The parameter <safe> is used to specify whether using array in concurrent-safety,
|
||||
// The parameter `safe` is used to specify whether using array in concurrent-safety,
|
||||
// which is false in default.
|
||||
func NewStrArraySize(size int, cap int, safe ...bool) *StrArray {
|
||||
return &StrArray{
|
||||
@ -46,8 +46,8 @@ func NewStrArraySize(size int, cap int, safe ...bool) *StrArray {
|
||||
}
|
||||
}
|
||||
|
||||
// NewStrArrayFrom creates and returns an array with given slice <array>.
|
||||
// The parameter <safe> is used to specify whether using array in concurrent-safety,
|
||||
// NewStrArrayFrom creates and returns an array with given slice `array`.
|
||||
// The parameter `safe` is used to specify whether using array in concurrent-safety,
|
||||
// which is false in default.
|
||||
func NewStrArrayFrom(array []string, safe ...bool) *StrArray {
|
||||
return &StrArray{
|
||||
@ -56,8 +56,8 @@ func NewStrArrayFrom(array []string, safe ...bool) *StrArray {
|
||||
}
|
||||
}
|
||||
|
||||
// NewStrArrayFromCopy creates and returns an array from a copy of given slice <array>.
|
||||
// The parameter <safe> is used to specify whether using array in concurrent-safety,
|
||||
// NewStrArrayFromCopy creates and returns an array from a copy of given slice `array`.
|
||||
// The parameter `safe` is used to specify whether using array in concurrent-safety,
|
||||
// which is false in default.
|
||||
func NewStrArrayFromCopy(array []string, safe ...bool) *StrArray {
|
||||
newArray := make([]string, len(array))
|
||||
@ -68,8 +68,15 @@ func NewStrArrayFromCopy(array []string, safe ...bool) *StrArray {
|
||||
}
|
||||
}
|
||||
|
||||
// At returns the value by the specified index.
|
||||
// If the given `index` is out of range of the array, it returns an empty string.
|
||||
func (a *StrArray) At(index int) (value string) {
|
||||
value, _ = a.Get(index)
|
||||
return
|
||||
}
|
||||
|
||||
// Get returns the value by the specified index.
|
||||
// If the given <index> is out of range of the array, the <found> is false.
|
||||
// If the given `index` is out of range of the array, the `found` is false.
|
||||
func (a *StrArray) Get(index int) (value string, found bool) {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
@ -90,7 +97,7 @@ func (a *StrArray) Set(index int, value string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetArray sets the underlying slice array with the given <array>.
|
||||
// SetArray sets the underlying slice array with the given `array`.
|
||||
func (a *StrArray) SetArray(array []string) *StrArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -98,7 +105,7 @@ func (a *StrArray) SetArray(array []string) *StrArray {
|
||||
return a
|
||||
}
|
||||
|
||||
// Replace replaces the array items by given <array> from the beginning of array.
|
||||
// Replace replaces the array items by given `array` from the beginning of array.
|
||||
func (a *StrArray) Replace(array []string) *StrArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -123,7 +130,7 @@ func (a *StrArray) Sum() (sum int) {
|
||||
}
|
||||
|
||||
// Sort sorts the array in increasing order.
|
||||
// The parameter <reverse> controls whether sort
|
||||
// The parameter `reverse` controls whether sort
|
||||
// in increasing order(default) or decreasing order
|
||||
func (a *StrArray) Sort(reverse ...bool) *StrArray {
|
||||
a.mu.Lock()
|
||||
@ -141,7 +148,7 @@ func (a *StrArray) Sort(reverse ...bool) *StrArray {
|
||||
return a
|
||||
}
|
||||
|
||||
// SortFunc sorts the array by custom function <less>.
|
||||
// SortFunc sorts the array by custom function `less`.
|
||||
func (a *StrArray) SortFunc(less func(v1, v2 string) bool) *StrArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -151,7 +158,7 @@ func (a *StrArray) SortFunc(less func(v1, v2 string) bool) *StrArray {
|
||||
return a
|
||||
}
|
||||
|
||||
// InsertBefore inserts the <value> to the front of <index>.
|
||||
// InsertBefore inserts the `value` to the front of `index`.
|
||||
func (a *StrArray) InsertBefore(index int, value string) error {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -164,7 +171,7 @@ func (a *StrArray) InsertBefore(index int, value string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// InsertAfter inserts the <value> to the back of <index>.
|
||||
// InsertAfter inserts the `value` to the back of `index`.
|
||||
func (a *StrArray) InsertAfter(index int, value string) error {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -178,7 +185,7 @@ func (a *StrArray) InsertAfter(index int, value string) error {
|
||||
}
|
||||
|
||||
// Remove removes an item by index.
|
||||
// If the given <index> is out of range of the array, the <found> is false.
|
||||
// If the given `index` is out of range of the array, the `found` is false.
|
||||
func (a *StrArray) Remove(index int) (value string, found bool) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -236,7 +243,7 @@ func (a *StrArray) PushRight(value ...string) *StrArray {
|
||||
}
|
||||
|
||||
// PopLeft pops and returns an item from the beginning of array.
|
||||
// Note that if the array is empty, the <found> is false.
|
||||
// Note that if the array is empty, the `found` is false.
|
||||
func (a *StrArray) PopLeft() (value string, found bool) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -249,7 +256,7 @@ func (a *StrArray) PopLeft() (value string, found bool) {
|
||||
}
|
||||
|
||||
// PopRight pops and returns an item from the end of array.
|
||||
// Note that if the array is empty, the <found> is false.
|
||||
// Note that if the array is empty, the `found` is false.
|
||||
func (a *StrArray) PopRight() (value string, found bool) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -263,16 +270,16 @@ func (a *StrArray) PopRight() (value string, found bool) {
|
||||
}
|
||||
|
||||
// PopRand randomly pops and return an item out of array.
|
||||
// Note that if the array is empty, the <found> is false.
|
||||
// Note that if the array is empty, the `found` is false.
|
||||
func (a *StrArray) PopRand() (value string, found bool) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
return a.doRemoveWithoutLock(grand.Intn(len(a.array)))
|
||||
}
|
||||
|
||||
// PopRands randomly pops and returns <size> items out of array.
|
||||
// If the given <size> is greater than size of the array, it returns all elements of the array.
|
||||
// Note that if given <size> <= 0 or the array is empty, it returns nil.
|
||||
// PopRands randomly pops and returns `size` items out of array.
|
||||
// If the given `size` is greater than size of the array, it returns all elements of the array.
|
||||
// Note that if given `size` <= 0 or the array is empty, it returns nil.
|
||||
func (a *StrArray) PopRands(size int) []string {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -289,9 +296,9 @@ func (a *StrArray) PopRands(size int) []string {
|
||||
return array
|
||||
}
|
||||
|
||||
// PopLefts pops and returns <size> items from the beginning of array.
|
||||
// If the given <size> is greater than size of the array, it returns all elements of the array.
|
||||
// Note that if given <size> <= 0 or the array is empty, it returns nil.
|
||||
// PopLefts pops and returns `size` items from the beginning of array.
|
||||
// If the given `size` is greater than size of the array, it returns all elements of the array.
|
||||
// Note that if given `size` <= 0 or the array is empty, it returns nil.
|
||||
func (a *StrArray) PopLefts(size int) []string {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -308,9 +315,9 @@ func (a *StrArray) PopLefts(size int) []string {
|
||||
return value
|
||||
}
|
||||
|
||||
// PopRights pops and returns <size> items from the end of array.
|
||||
// If the given <size> is greater than size of the array, it returns all elements of the array.
|
||||
// Note that if given <size> <= 0 or the array is empty, it returns nil.
|
||||
// PopRights pops and returns `size` items from the end of array.
|
||||
// If the given `size` is greater than size of the array, it returns all elements of the array.
|
||||
// Note that if given `size` <= 0 or the array is empty, it returns nil.
|
||||
func (a *StrArray) PopRights(size int) []string {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -332,8 +339,8 @@ func (a *StrArray) PopRights(size int) []string {
|
||||
// Notice, if in concurrent-safe usage, it returns a copy of slice;
|
||||
// else a pointer to the underlying data.
|
||||
//
|
||||
// If <end> is negative, then the offset will start from the end of array.
|
||||
// If <end> is omitted, then the sequence will have everything from start up
|
||||
// If `end` is negative, then the offset will start from the end of array.
|
||||
// If `end` is omitted, then the sequence will have everything from start up
|
||||
// until the end of the array.
|
||||
func (a *StrArray) Range(start int, end ...int) []string {
|
||||
a.mu.RLock()
|
||||
@ -359,7 +366,7 @@ func (a *StrArray) Range(start int, end ...int) []string {
|
||||
}
|
||||
|
||||
// SubSlice returns a slice of elements from the array as specified
|
||||
// by the <offset> and <size> parameters.
|
||||
// by the `offset` and `size` parameters.
|
||||
// If in concurrent safe usage, it returns a copy of the slice; else a pointer.
|
||||
//
|
||||
// If offset is non-negative, the sequence will start at that offset in the array.
|
||||
@ -491,7 +498,7 @@ func (a *StrArray) ContainsI(value string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// Search searches array by <value>, returns the index of <value>,
|
||||
// Search searches array by `value`, returns the index of `value`,
|
||||
// or returns -1 if not exists.
|
||||
func (a *StrArray) Search(value string) int {
|
||||
a.mu.RLock()
|
||||
@ -526,7 +533,7 @@ func (a *StrArray) Unique() *StrArray {
|
||||
return a
|
||||
}
|
||||
|
||||
// LockFunc locks writing by callback function <f>.
|
||||
// LockFunc locks writing by callback function `f`.
|
||||
func (a *StrArray) LockFunc(f func(array []string)) *StrArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -534,7 +541,7 @@ func (a *StrArray) LockFunc(f func(array []string)) *StrArray {
|
||||
return a
|
||||
}
|
||||
|
||||
// RLockFunc locks reading by callback function <f>.
|
||||
// RLockFunc locks reading by callback function `f`.
|
||||
func (a *StrArray) RLockFunc(f func(array []string)) *StrArray {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
@ -542,16 +549,16 @@ func (a *StrArray) RLockFunc(f func(array []string)) *StrArray {
|
||||
return a
|
||||
}
|
||||
|
||||
// Merge merges <array> into current array.
|
||||
// The parameter <array> can be any garray or slice type.
|
||||
// Merge merges `array` into current array.
|
||||
// The parameter `array` can be any garray or slice type.
|
||||
// The difference between Merge and Append is Append supports only specified slice type,
|
||||
// but Merge supports more parameter types.
|
||||
func (a *StrArray) Merge(array interface{}) *StrArray {
|
||||
return a.Append(gconv.Strings(array)...)
|
||||
}
|
||||
|
||||
// Fill fills an array with num entries of the value <value>,
|
||||
// keys starting at the <startIndex> parameter.
|
||||
// Fill fills an array with num entries of the value `value`,
|
||||
// keys starting at the `startIndex` parameter.
|
||||
func (a *StrArray) Fill(startIndex int, num int, value string) error {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -569,7 +576,7 @@ func (a *StrArray) Fill(startIndex int, num int, value string) error {
|
||||
}
|
||||
|
||||
// Chunk splits an array into multiple arrays,
|
||||
// the size of each array is determined by <size>.
|
||||
// the size of each array is determined by `size`.
|
||||
// The last chunk may contain less than size elements.
|
||||
func (a *StrArray) Chunk(size int) [][]string {
|
||||
if size < 1 {
|
||||
@ -591,9 +598,9 @@ func (a *StrArray) Chunk(size int) [][]string {
|
||||
return n
|
||||
}
|
||||
|
||||
// Pad pads array to the specified length with <value>.
|
||||
// Pad pads array to the specified length with `value`.
|
||||
// If size is positive then the array is padded on the right, or negative on the left.
|
||||
// If the absolute value of <size> is less than or equal to the length of the array
|
||||
// If the absolute value of `size` is less than or equal to the length of the array
|
||||
// then no padding takes place.
|
||||
func (a *StrArray) Pad(size int, value string) *StrArray {
|
||||
a.mu.Lock()
|
||||
@ -628,7 +635,7 @@ func (a *StrArray) Rand() (value string, found bool) {
|
||||
return a.array[grand.Intn(len(a.array))], true
|
||||
}
|
||||
|
||||
// Rands randomly returns <size> items from array(no deleting).
|
||||
// Rands randomly returns `size` items from array(no deleting).
|
||||
func (a *StrArray) Rands(size int) []string {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
@ -662,7 +669,7 @@ func (a *StrArray) Reverse() *StrArray {
|
||||
return a
|
||||
}
|
||||
|
||||
// Join joins array elements with a string <glue>.
|
||||
// Join joins array elements with a string `glue`.
|
||||
func (a *StrArray) Join(glue string) string {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
@ -695,8 +702,8 @@ func (a *StrArray) Iterator(f func(k int, v string) bool) {
|
||||
a.IteratorAsc(f)
|
||||
}
|
||||
|
||||
// IteratorAsc iterates the array readonly in ascending order with given callback function <f>.
|
||||
// If <f> returns true, then it continues iterating; or false to stop.
|
||||
// IteratorAsc iterates the array readonly in ascending order with given callback function `f`.
|
||||
// If `f` returns true, then it continues iterating; or false to stop.
|
||||
func (a *StrArray) IteratorAsc(f func(k int, v string) bool) {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
@ -707,8 +714,8 @@ func (a *StrArray) IteratorAsc(f func(k int, v string) bool) {
|
||||
}
|
||||
}
|
||||
|
||||
// IteratorDesc iterates the array readonly in descending order with given callback function <f>.
|
||||
// If <f> returns true, then it continues iterating; or false to stop.
|
||||
// IteratorDesc iterates the array readonly in descending order with given callback function `f`.
|
||||
// If `f` returns true, then it continues iterating; or false to stop.
|
||||
func (a *StrArray) IteratorDesc(f func(k int, v string) bool) {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
@ -750,7 +757,7 @@ func (a *StrArray) UnmarshalJSON(b []byte) error {
|
||||
}
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if err := json.Unmarshal(b, &a.array); err != nil {
|
||||
if err := json.UnmarshalUseNumber(b, &a.array); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
@ -762,7 +769,7 @@ func (a *StrArray) UnmarshalValue(value interface{}) error {
|
||||
defer a.mu.Unlock()
|
||||
switch value.(type) {
|
||||
case string, []byte:
|
||||
return json.Unmarshal(gconv.Bytes(value), &a.array)
|
||||
return json.UnmarshalUseNumber(gconv.Bytes(value), &a.array)
|
||||
default:
|
||||
a.array = gconv.SliceStr(value)
|
||||
}
|
||||
@ -783,7 +790,7 @@ func (a *StrArray) FilterEmpty() *StrArray {
|
||||
return a
|
||||
}
|
||||
|
||||
// Walk applies a user supplied function <f> to every item of array.
|
||||
// Walk applies a user supplied function `f` to every item of array.
|
||||
func (a *StrArray) Walk(f func(value string) string) *StrArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
|
||||
@ -34,8 +34,8 @@ type SortedArray struct {
|
||||
}
|
||||
|
||||
// NewSortedArray creates and returns an empty sorted array.
|
||||
// The parameter <safe> is used to specify whether using array in concurrent-safety, which is false in default.
|
||||
// The parameter <comparator> used to compare values to sort in array,
|
||||
// The parameter `safe` is used to specify whether using array in concurrent-safety, which is false in default.
|
||||
// The parameter `comparator` used to compare values to sort in array,
|
||||
// if it returns value < 0, means v1 < v2; the v1 will be inserted before v2;
|
||||
// if it returns value = 0, means v1 = v2; the v1 will be replaced by v2;
|
||||
// if it returns value > 0, means v1 > v2; the v1 will be inserted after v2;
|
||||
@ -44,7 +44,7 @@ func NewSortedArray(comparator func(a, b interface{}) int, safe ...bool) *Sorted
|
||||
}
|
||||
|
||||
// NewSortedArraySize create and returns an sorted array with given size and cap.
|
||||
// The parameter <safe> is used to specify whether using array in concurrent-safety,
|
||||
// The parameter `safe` is used to specify whether using array in concurrent-safety,
|
||||
// which is false in default.
|
||||
func NewSortedArraySize(cap int, comparator func(a, b interface{}) int, safe ...bool) *SortedArray {
|
||||
return &SortedArray{
|
||||
@ -54,8 +54,8 @@ func NewSortedArraySize(cap int, comparator func(a, b interface{}) int, safe ...
|
||||
}
|
||||
}
|
||||
|
||||
// NewSortedArrayRange creates and returns a array by a range from <start> to <end>
|
||||
// with step value <step>.
|
||||
// NewSortedArrayRange creates and returns a array by a range from `start` to `end`
|
||||
// with step value `step`.
|
||||
func NewSortedArrayRange(start, end, step int, comparator func(a, b interface{}) int, safe ...bool) *SortedArray {
|
||||
if step == 0 {
|
||||
panic(fmt.Sprintf(`invalid step value: %d`, step))
|
||||
@ -69,8 +69,8 @@ func NewSortedArrayRange(start, end, step int, comparator func(a, b interface{})
|
||||
return NewSortedArrayFrom(slice, comparator, safe...)
|
||||
}
|
||||
|
||||
// NewSortedArrayFrom creates and returns an sorted array with given slice <array>.
|
||||
// The parameter <safe> is used to specify whether using array in concurrent-safety,
|
||||
// NewSortedArrayFrom creates and returns an sorted array with given slice `array`.
|
||||
// The parameter `safe` is used to specify whether using array in concurrent-safety,
|
||||
// which is false in default.
|
||||
func NewSortedArrayFrom(array []interface{}, comparator func(a, b interface{}) int, safe ...bool) *SortedArray {
|
||||
a := NewSortedArraySize(0, comparator, safe...)
|
||||
@ -81,8 +81,8 @@ func NewSortedArrayFrom(array []interface{}, comparator func(a, b interface{}) i
|
||||
return a
|
||||
}
|
||||
|
||||
// NewSortedArrayFromCopy creates and returns an sorted array from a copy of given slice <array>.
|
||||
// The parameter <safe> is used to specify whether using array in concurrent-safety,
|
||||
// NewSortedArrayFromCopy creates and returns an sorted array from a copy of given slice `array`.
|
||||
// The parameter `safe` is used to specify whether using array in concurrent-safety,
|
||||
// which is false in default.
|
||||
func NewSortedArrayFromCopy(array []interface{}, comparator func(a, b interface{}) int, safe ...bool) *SortedArray {
|
||||
newArray := make([]interface{}, len(array))
|
||||
@ -90,7 +90,14 @@ func NewSortedArrayFromCopy(array []interface{}, comparator func(a, b interface{
|
||||
return NewSortedArrayFrom(newArray, comparator, safe...)
|
||||
}
|
||||
|
||||
// SetArray sets the underlying slice array with the given <array>.
|
||||
// At returns the value by the specified index.
|
||||
// If the given `index` is out of range of the array, it returns `nil`.
|
||||
func (a *SortedArray) At(index int) (value interface{}) {
|
||||
value, _ = a.Get(index)
|
||||
return
|
||||
}
|
||||
|
||||
// SetArray sets the underlying slice array with the given `array`.
|
||||
func (a *SortedArray) SetArray(array []interface{}) *SortedArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -113,7 +120,7 @@ func (a *SortedArray) SetComparator(comparator func(a, b interface{}) int) {
|
||||
}
|
||||
|
||||
// Sort sorts the array in increasing order.
|
||||
// The parameter <reverse> controls whether sort
|
||||
// The parameter `reverse` controls whether sort
|
||||
// in increasing order(default) or decreasing order
|
||||
func (a *SortedArray) Sort() *SortedArray {
|
||||
a.mu.Lock()
|
||||
@ -157,7 +164,7 @@ func (a *SortedArray) Append(values ...interface{}) *SortedArray {
|
||||
}
|
||||
|
||||
// Get returns the value by the specified index.
|
||||
// If the given <index> is out of range of the array, the <found> is false.
|
||||
// If the given `index` is out of range of the array, the `found` is false.
|
||||
func (a *SortedArray) Get(index int) (value interface{}, found bool) {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
@ -168,7 +175,7 @@ func (a *SortedArray) Get(index int) (value interface{}, found bool) {
|
||||
}
|
||||
|
||||
// Remove removes an item by index.
|
||||
// If the given <index> is out of range of the array, the <found> is false.
|
||||
// If the given `index` is out of range of the array, the `found` is false.
|
||||
func (a *SortedArray) Remove(index int) (value interface{}, found bool) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -209,7 +216,7 @@ func (a *SortedArray) RemoveValue(value interface{}) bool {
|
||||
}
|
||||
|
||||
// PopLeft pops and returns an item from the beginning of array.
|
||||
// Note that if the array is empty, the <found> is false.
|
||||
// Note that if the array is empty, the `found` is false.
|
||||
func (a *SortedArray) PopLeft() (value interface{}, found bool) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -222,7 +229,7 @@ func (a *SortedArray) PopLeft() (value interface{}, found bool) {
|
||||
}
|
||||
|
||||
// PopRight pops and returns an item from the end of array.
|
||||
// Note that if the array is empty, the <found> is false.
|
||||
// Note that if the array is empty, the `found` is false.
|
||||
func (a *SortedArray) PopRight() (value interface{}, found bool) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -236,14 +243,14 @@ func (a *SortedArray) PopRight() (value interface{}, found bool) {
|
||||
}
|
||||
|
||||
// PopRand randomly pops and return an item out of array.
|
||||
// Note that if the array is empty, the <found> is false.
|
||||
// Note that if the array is empty, the `found` is false.
|
||||
func (a *SortedArray) PopRand() (value interface{}, found bool) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
return a.doRemoveWithoutLock(grand.Intn(len(a.array)))
|
||||
}
|
||||
|
||||
// PopRands randomly pops and returns <size> items out of array.
|
||||
// PopRands randomly pops and returns `size` items out of array.
|
||||
func (a *SortedArray) PopRands(size int) []interface{} {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -260,7 +267,7 @@ func (a *SortedArray) PopRands(size int) []interface{} {
|
||||
return array
|
||||
}
|
||||
|
||||
// PopLefts pops and returns <size> items from the beginning of array.
|
||||
// PopLefts pops and returns `size` items from the beginning of array.
|
||||
func (a *SortedArray) PopLefts(size int) []interface{} {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -277,7 +284,7 @@ func (a *SortedArray) PopLefts(size int) []interface{} {
|
||||
return value
|
||||
}
|
||||
|
||||
// PopRights pops and returns <size> items from the end of array.
|
||||
// PopRights pops and returns `size` items from the end of array.
|
||||
func (a *SortedArray) PopRights(size int) []interface{} {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -299,8 +306,8 @@ func (a *SortedArray) PopRights(size int) []interface{} {
|
||||
// Notice, if in concurrent-safe usage, it returns a copy of slice;
|
||||
// else a pointer to the underlying data.
|
||||
//
|
||||
// If <end> is negative, then the offset will start from the end of array.
|
||||
// If <end> is omitted, then the sequence will have everything from start up
|
||||
// If `end` is negative, then the offset will start from the end of array.
|
||||
// If `end` is omitted, then the sequence will have everything from start up
|
||||
// until the end of the array.
|
||||
func (a *SortedArray) Range(start int, end ...int) []interface{} {
|
||||
a.mu.RLock()
|
||||
@ -326,7 +333,7 @@ func (a *SortedArray) Range(start int, end ...int) []interface{} {
|
||||
}
|
||||
|
||||
// SubSlice returns a slice of elements from the array as specified
|
||||
// by the <offset> and <size> parameters.
|
||||
// by the `offset` and `size` parameters.
|
||||
// If in concurrent safe usage, it returns a copy of the slice; else a pointer.
|
||||
//
|
||||
// If offset is non-negative, the sequence will start at that offset in the array.
|
||||
@ -419,7 +426,7 @@ func (a *SortedArray) Contains(value interface{}) bool {
|
||||
return a.Search(value) != -1
|
||||
}
|
||||
|
||||
// Search searches array by <value>, returns the index of <value>,
|
||||
// Search searches array by `value`, returns the index of `value`,
|
||||
// or returns -1 if not exists.
|
||||
func (a *SortedArray) Search(value interface{}) (index int) {
|
||||
if i, r := a.binSearch(value, true); r == 0 {
|
||||
@ -430,9 +437,9 @@ func (a *SortedArray) Search(value interface{}) (index int) {
|
||||
|
||||
// Binary search.
|
||||
// It returns the last compared index and the result.
|
||||
// If <result> equals to 0, it means the value at <index> is equals to <value>.
|
||||
// If <result> lesser than 0, it means the value at <index> is lesser than <value>.
|
||||
// If <result> greater than 0, it means the value at <index> is greater than <value>.
|
||||
// If `result` equals to 0, it means the value at `index` is equals to `value`.
|
||||
// If `result` lesser than 0, it means the value at `index` is lesser than `value`.
|
||||
// If `result` greater than 0, it means the value at `index` is greater than `value`.
|
||||
func (a *SortedArray) binSearch(value interface{}, lock bool) (index int, result int) {
|
||||
if lock {
|
||||
a.mu.RLock()
|
||||
@ -512,7 +519,7 @@ func (a *SortedArray) Clear() *SortedArray {
|
||||
return a
|
||||
}
|
||||
|
||||
// LockFunc locks writing by callback function <f>.
|
||||
// LockFunc locks writing by callback function `f`.
|
||||
func (a *SortedArray) LockFunc(f func(array []interface{})) *SortedArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -526,7 +533,7 @@ func (a *SortedArray) LockFunc(f func(array []interface{})) *SortedArray {
|
||||
return a
|
||||
}
|
||||
|
||||
// RLockFunc locks reading by callback function <f>.
|
||||
// RLockFunc locks reading by callback function `f`.
|
||||
func (a *SortedArray) RLockFunc(f func(array []interface{})) *SortedArray {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
@ -534,8 +541,8 @@ func (a *SortedArray) RLockFunc(f func(array []interface{})) *SortedArray {
|
||||
return a
|
||||
}
|
||||
|
||||
// Merge merges <array> into current array.
|
||||
// The parameter <array> can be any garray or slice type.
|
||||
// Merge merges `array` into current array.
|
||||
// The parameter `array` can be any garray or slice type.
|
||||
// The difference between Merge and Append is Append supports only specified slice type,
|
||||
// but Merge supports more parameter types.
|
||||
func (a *SortedArray) Merge(array interface{}) *SortedArray {
|
||||
@ -543,7 +550,7 @@ func (a *SortedArray) Merge(array interface{}) *SortedArray {
|
||||
}
|
||||
|
||||
// Chunk splits an array into multiple arrays,
|
||||
// the size of each array is determined by <size>.
|
||||
// the size of each array is determined by `size`.
|
||||
// The last chunk may contain less than size elements.
|
||||
func (a *SortedArray) Chunk(size int) [][]interface{} {
|
||||
if size < 1 {
|
||||
@ -575,7 +582,7 @@ func (a *SortedArray) Rand() (value interface{}, found bool) {
|
||||
return a.array[grand.Intn(len(a.array))], true
|
||||
}
|
||||
|
||||
// Rands randomly returns <size> items from array(no deleting).
|
||||
// Rands randomly returns `size` items from array(no deleting).
|
||||
func (a *SortedArray) Rands(size int) []interface{} {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
@ -589,7 +596,7 @@ func (a *SortedArray) Rands(size int) []interface{} {
|
||||
return array
|
||||
}
|
||||
|
||||
// Join joins array elements with a string <glue>.
|
||||
// Join joins array elements with a string `glue`.
|
||||
func (a *SortedArray) Join(glue string) string {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
@ -622,8 +629,8 @@ func (a *SortedArray) Iterator(f func(k int, v interface{}) bool) {
|
||||
a.IteratorAsc(f)
|
||||
}
|
||||
|
||||
// IteratorAsc iterates the array readonly in ascending order with given callback function <f>.
|
||||
// If <f> returns true, then it continues iterating; or false to stop.
|
||||
// IteratorAsc iterates the array readonly in ascending order with given callback function `f`.
|
||||
// If `f` returns true, then it continues iterating; or false to stop.
|
||||
func (a *SortedArray) IteratorAsc(f func(k int, v interface{}) bool) {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
@ -634,8 +641,8 @@ func (a *SortedArray) IteratorAsc(f func(k int, v interface{}) bool) {
|
||||
}
|
||||
}
|
||||
|
||||
// IteratorDesc iterates the array readonly in descending order with given callback function <f>.
|
||||
// If <f> returns true, then it continues iterating; or false to stop.
|
||||
// IteratorDesc iterates the array readonly in descending order with given callback function `f`.
|
||||
// If `f` returns true, then it continues iterating; or false to stop.
|
||||
func (a *SortedArray) IteratorDesc(f func(k int, v interface{}) bool) {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
@ -685,7 +692,7 @@ func (a *SortedArray) UnmarshalJSON(b []byte) error {
|
||||
}
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if err := json.Unmarshal(b, &a.array); err != nil {
|
||||
if err := json.UnmarshalUseNumber(b, &a.array); err != nil {
|
||||
return err
|
||||
}
|
||||
if a.comparator != nil && a.array != nil {
|
||||
@ -706,7 +713,7 @@ func (a *SortedArray) UnmarshalValue(value interface{}) (err error) {
|
||||
defer a.mu.Unlock()
|
||||
switch value.(type) {
|
||||
case string, []byte:
|
||||
err = json.Unmarshal(gconv.Bytes(value), &a.array)
|
||||
err = json.UnmarshalUseNumber(gconv.Bytes(value), &a.array)
|
||||
default:
|
||||
a.array = gconv.SliceAny(value)
|
||||
}
|
||||
@ -761,7 +768,7 @@ func (a *SortedArray) FilterEmpty() *SortedArray {
|
||||
return a
|
||||
}
|
||||
|
||||
// Walk applies a user supplied function <f> to every item of array.
|
||||
// Walk applies a user supplied function `f` to every item of array.
|
||||
func (a *SortedArray) Walk(f func(value interface{}) interface{}) *SortedArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
|
||||
@ -31,14 +31,14 @@ type SortedIntArray struct {
|
||||
}
|
||||
|
||||
// NewSortedIntArray creates and returns an empty sorted array.
|
||||
// The parameter <safe> is used to specify whether using array in concurrent-safety,
|
||||
// The parameter `safe` is used to specify whether using array in concurrent-safety,
|
||||
// which is false in default.
|
||||
func NewSortedIntArray(safe ...bool) *SortedIntArray {
|
||||
return NewSortedIntArraySize(0, safe...)
|
||||
}
|
||||
|
||||
// NewSortedIntArrayComparator creates and returns an empty sorted array with specified comparator.
|
||||
// The parameter <safe> is used to specify whether using array in concurrent-safety which is false in default.
|
||||
// The parameter `safe` is used to specify whether using array in concurrent-safety which is false in default.
|
||||
func NewSortedIntArrayComparator(comparator func(a, b int) int, safe ...bool) *SortedIntArray {
|
||||
array := NewSortedIntArray(safe...)
|
||||
array.comparator = comparator
|
||||
@ -46,7 +46,7 @@ func NewSortedIntArrayComparator(comparator func(a, b int) int, safe ...bool) *S
|
||||
}
|
||||
|
||||
// NewSortedIntArraySize create and returns an sorted array with given size and cap.
|
||||
// The parameter <safe> is used to specify whether using array in concurrent-safety,
|
||||
// The parameter `safe` is used to specify whether using array in concurrent-safety,
|
||||
// which is false in default.
|
||||
func NewSortedIntArraySize(cap int, safe ...bool) *SortedIntArray {
|
||||
return &SortedIntArray{
|
||||
@ -56,8 +56,8 @@ func NewSortedIntArraySize(cap int, safe ...bool) *SortedIntArray {
|
||||
}
|
||||
}
|
||||
|
||||
// NewSortedIntArrayRange creates and returns a array by a range from <start> to <end>
|
||||
// with step value <step>.
|
||||
// NewSortedIntArrayRange creates and returns a array by a range from `start` to `end`
|
||||
// with step value `step`.
|
||||
func NewSortedIntArrayRange(start, end, step int, safe ...bool) *SortedIntArray {
|
||||
if step == 0 {
|
||||
panic(fmt.Sprintf(`invalid step value: %d`, step))
|
||||
@ -71,8 +71,8 @@ func NewSortedIntArrayRange(start, end, step int, safe ...bool) *SortedIntArray
|
||||
return NewSortedIntArrayFrom(slice, safe...)
|
||||
}
|
||||
|
||||
// NewIntArrayFrom creates and returns an sorted array with given slice <array>.
|
||||
// The parameter <safe> is used to specify whether using array in concurrent-safety,
|
||||
// NewSortedIntArrayFrom creates and returns an sorted array with given slice `array`.
|
||||
// The parameter `safe` is used to specify whether using array in concurrent-safety,
|
||||
// which is false in default.
|
||||
func NewSortedIntArrayFrom(array []int, safe ...bool) *SortedIntArray {
|
||||
a := NewSortedIntArraySize(0, safe...)
|
||||
@ -81,8 +81,8 @@ func NewSortedIntArrayFrom(array []int, safe ...bool) *SortedIntArray {
|
||||
return a
|
||||
}
|
||||
|
||||
// NewSortedIntArrayFromCopy creates and returns an sorted array from a copy of given slice <array>.
|
||||
// The parameter <safe> is used to specify whether using array in concurrent-safety,
|
||||
// NewSortedIntArrayFromCopy creates and returns an sorted array from a copy of given slice `array`.
|
||||
// The parameter `safe` is used to specify whether using array in concurrent-safety,
|
||||
// which is false in default.
|
||||
func NewSortedIntArrayFromCopy(array []int, safe ...bool) *SortedIntArray {
|
||||
newArray := make([]int, len(array))
|
||||
@ -90,7 +90,14 @@ func NewSortedIntArrayFromCopy(array []int, safe ...bool) *SortedIntArray {
|
||||
return NewSortedIntArrayFrom(newArray, safe...)
|
||||
}
|
||||
|
||||
// SetArray sets the underlying slice array with the given <array>.
|
||||
// At returns the value by the specified index.
|
||||
// If the given `index` is out of range of the array, it returns `0`.
|
||||
func (a *SortedIntArray) At(index int) (value int) {
|
||||
value, _ = a.Get(index)
|
||||
return
|
||||
}
|
||||
|
||||
// SetArray sets the underlying slice array with the given `array`.
|
||||
func (a *SortedIntArray) SetArray(array []int) *SortedIntArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -100,7 +107,7 @@ func (a *SortedIntArray) SetArray(array []int) *SortedIntArray {
|
||||
}
|
||||
|
||||
// Sort sorts the array in increasing order.
|
||||
// The parameter <reverse> controls whether sort
|
||||
// The parameter `reverse` controls whether sort
|
||||
// in increasing order(default) or decreasing order.
|
||||
func (a *SortedIntArray) Sort() *SortedIntArray {
|
||||
a.mu.Lock()
|
||||
@ -142,7 +149,7 @@ func (a *SortedIntArray) Append(values ...int) *SortedIntArray {
|
||||
}
|
||||
|
||||
// Get returns the value by the specified index.
|
||||
// If the given <index> is out of range of the array, the <found> is false.
|
||||
// If the given `index` is out of range of the array, the `found` is false.
|
||||
func (a *SortedIntArray) Get(index int) (value int, found bool) {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
@ -153,7 +160,7 @@ func (a *SortedIntArray) Get(index int) (value int, found bool) {
|
||||
}
|
||||
|
||||
// Remove removes an item by index.
|
||||
// If the given <index> is out of range of the array, the <found> is false.
|
||||
// If the given `index` is out of range of the array, the `found` is false.
|
||||
func (a *SortedIntArray) Remove(index int) (value int, found bool) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -194,7 +201,7 @@ func (a *SortedIntArray) RemoveValue(value int) bool {
|
||||
}
|
||||
|
||||
// PopLeft pops and returns an item from the beginning of array.
|
||||
// Note that if the array is empty, the <found> is false.
|
||||
// Note that if the array is empty, the `found` is false.
|
||||
func (a *SortedIntArray) PopLeft() (value int, found bool) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -207,7 +214,7 @@ func (a *SortedIntArray) PopLeft() (value int, found bool) {
|
||||
}
|
||||
|
||||
// PopRight pops and returns an item from the end of array.
|
||||
// Note that if the array is empty, the <found> is false.
|
||||
// Note that if the array is empty, the `found` is false.
|
||||
func (a *SortedIntArray) PopRight() (value int, found bool) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -221,16 +228,16 @@ func (a *SortedIntArray) PopRight() (value int, found bool) {
|
||||
}
|
||||
|
||||
// PopRand randomly pops and return an item out of array.
|
||||
// Note that if the array is empty, the <found> is false.
|
||||
// Note that if the array is empty, the `found` is false.
|
||||
func (a *SortedIntArray) PopRand() (value int, found bool) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
return a.doRemoveWithoutLock(grand.Intn(len(a.array)))
|
||||
}
|
||||
|
||||
// PopRands randomly pops and returns <size> items out of array.
|
||||
// If the given <size> is greater than size of the array, it returns all elements of the array.
|
||||
// Note that if given <size> <= 0 or the array is empty, it returns nil.
|
||||
// PopRands randomly pops and returns `size` items out of array.
|
||||
// If the given `size` is greater than size of the array, it returns all elements of the array.
|
||||
// Note that if given `size` <= 0 or the array is empty, it returns nil.
|
||||
func (a *SortedIntArray) PopRands(size int) []int {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -247,9 +254,9 @@ func (a *SortedIntArray) PopRands(size int) []int {
|
||||
return array
|
||||
}
|
||||
|
||||
// PopLefts pops and returns <size> items from the beginning of array.
|
||||
// If the given <size> is greater than size of the array, it returns all elements of the array.
|
||||
// Note that if given <size> <= 0 or the array is empty, it returns nil.
|
||||
// PopLefts pops and returns `size` items from the beginning of array.
|
||||
// If the given `size` is greater than size of the array, it returns all elements of the array.
|
||||
// Note that if given `size` <= 0 or the array is empty, it returns nil.
|
||||
func (a *SortedIntArray) PopLefts(size int) []int {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -266,9 +273,9 @@ func (a *SortedIntArray) PopLefts(size int) []int {
|
||||
return value
|
||||
}
|
||||
|
||||
// PopRights pops and returns <size> items from the end of array.
|
||||
// If the given <size> is greater than size of the array, it returns all elements of the array.
|
||||
// Note that if given <size> <= 0 or the array is empty, it returns nil.
|
||||
// PopRights pops and returns `size` items from the end of array.
|
||||
// If the given `size` is greater than size of the array, it returns all elements of the array.
|
||||
// Note that if given `size` <= 0 or the array is empty, it returns nil.
|
||||
func (a *SortedIntArray) PopRights(size int) []int {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -290,8 +297,8 @@ func (a *SortedIntArray) PopRights(size int) []int {
|
||||
// Notice, if in concurrent-safe usage, it returns a copy of slice;
|
||||
// else a pointer to the underlying data.
|
||||
//
|
||||
// If <end> is negative, then the offset will start from the end of array.
|
||||
// If <end> is omitted, then the sequence will have everything from start up
|
||||
// If `end` is negative, then the offset will start from the end of array.
|
||||
// If `end` is omitted, then the sequence will have everything from start up
|
||||
// until the end of the array.
|
||||
func (a *SortedIntArray) Range(start int, end ...int) []int {
|
||||
a.mu.RLock()
|
||||
@ -317,7 +324,7 @@ func (a *SortedIntArray) Range(start int, end ...int) []int {
|
||||
}
|
||||
|
||||
// SubSlice returns a slice of elements from the array as specified
|
||||
// by the <offset> and <size> parameters.
|
||||
// by the `offset` and `size` parameters.
|
||||
// If in concurrent safe usage, it returns a copy of the slice; else a pointer.
|
||||
//
|
||||
// If offset is non-negative, the sequence will start at that offset in the array.
|
||||
@ -416,7 +423,7 @@ func (a *SortedIntArray) Contains(value int) bool {
|
||||
return a.Search(value) != -1
|
||||
}
|
||||
|
||||
// Search searches array by <value>, returns the index of <value>,
|
||||
// Search searches array by `value`, returns the index of `value`,
|
||||
// or returns -1 if not exists.
|
||||
func (a *SortedIntArray) Search(value int) (index int) {
|
||||
if i, r := a.binSearch(value, true); r == 0 {
|
||||
@ -427,9 +434,9 @@ func (a *SortedIntArray) Search(value int) (index int) {
|
||||
|
||||
// Binary search.
|
||||
// It returns the last compared index and the result.
|
||||
// If <result> equals to 0, it means the value at <index> is equals to <value>.
|
||||
// If <result> lesser than 0, it means the value at <index> is lesser than <value>.
|
||||
// If <result> greater than 0, it means the value at <index> is greater than <value>.
|
||||
// If `result` equals to 0, it means the value at `index` is equals to `value`.
|
||||
// If `result` lesser than 0, it means the value at `index` is lesser than `value`.
|
||||
// If `result` greater than 0, it means the value at `index` is greater than `value`.
|
||||
func (a *SortedIntArray) binSearch(value int, lock bool) (index int, result int) {
|
||||
if lock {
|
||||
a.mu.RLock()
|
||||
@ -509,7 +516,7 @@ func (a *SortedIntArray) Clear() *SortedIntArray {
|
||||
return a
|
||||
}
|
||||
|
||||
// LockFunc locks writing by callback function <f>.
|
||||
// LockFunc locks writing by callback function `f`.
|
||||
func (a *SortedIntArray) LockFunc(f func(array []int)) *SortedIntArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -517,7 +524,7 @@ func (a *SortedIntArray) LockFunc(f func(array []int)) *SortedIntArray {
|
||||
return a
|
||||
}
|
||||
|
||||
// RLockFunc locks reading by callback function <f>.
|
||||
// RLockFunc locks reading by callback function `f`.
|
||||
func (a *SortedIntArray) RLockFunc(f func(array []int)) *SortedIntArray {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
@ -525,8 +532,8 @@ func (a *SortedIntArray) RLockFunc(f func(array []int)) *SortedIntArray {
|
||||
return a
|
||||
}
|
||||
|
||||
// Merge merges <array> into current array.
|
||||
// The parameter <array> can be any garray or slice type.
|
||||
// Merge merges `array` into current array.
|
||||
// The parameter `array` can be any garray or slice type.
|
||||
// The difference between Merge and Append is Append supports only specified slice type,
|
||||
// but Merge supports more parameter types.
|
||||
func (a *SortedIntArray) Merge(array interface{}) *SortedIntArray {
|
||||
@ -534,7 +541,7 @@ func (a *SortedIntArray) Merge(array interface{}) *SortedIntArray {
|
||||
}
|
||||
|
||||
// Chunk splits an array into multiple arrays,
|
||||
// the size of each array is determined by <size>.
|
||||
// the size of each array is determined by `size`.
|
||||
// The last chunk may contain less than size elements.
|
||||
func (a *SortedIntArray) Chunk(size int) [][]int {
|
||||
if size < 1 {
|
||||
@ -566,7 +573,7 @@ func (a *SortedIntArray) Rand() (value int, found bool) {
|
||||
return a.array[grand.Intn(len(a.array))], true
|
||||
}
|
||||
|
||||
// Rands randomly returns <size> items from array(no deleting).
|
||||
// Rands randomly returns `size` items from array(no deleting).
|
||||
func (a *SortedIntArray) Rands(size int) []int {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
@ -580,7 +587,7 @@ func (a *SortedIntArray) Rands(size int) []int {
|
||||
return array
|
||||
}
|
||||
|
||||
// Join joins array elements with a string <glue>.
|
||||
// Join joins array elements with a string `glue`.
|
||||
func (a *SortedIntArray) Join(glue string) string {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
@ -613,8 +620,8 @@ func (a *SortedIntArray) Iterator(f func(k int, v int) bool) {
|
||||
a.IteratorAsc(f)
|
||||
}
|
||||
|
||||
// IteratorAsc iterates the array readonly in ascending order with given callback function <f>.
|
||||
// If <f> returns true, then it continues iterating; or false to stop.
|
||||
// IteratorAsc iterates the array readonly in ascending order with given callback function `f`.
|
||||
// If `f` returns true, then it continues iterating; or false to stop.
|
||||
func (a *SortedIntArray) IteratorAsc(f func(k int, v int) bool) {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
@ -625,8 +632,8 @@ func (a *SortedIntArray) IteratorAsc(f func(k int, v int) bool) {
|
||||
}
|
||||
}
|
||||
|
||||
// IteratorDesc iterates the array readonly in descending order with given callback function <f>.
|
||||
// If <f> returns true, then it continues iterating; or false to stop.
|
||||
// IteratorDesc iterates the array readonly in descending order with given callback function `f`.
|
||||
// If `f` returns true, then it continues iterating; or false to stop.
|
||||
func (a *SortedIntArray) IteratorDesc(f func(k int, v int) bool) {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
@ -658,7 +665,7 @@ func (a *SortedIntArray) UnmarshalJSON(b []byte) error {
|
||||
}
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if err := json.Unmarshal(b, &a.array); err != nil {
|
||||
if err := json.UnmarshalUseNumber(b, &a.array); err != nil {
|
||||
return err
|
||||
}
|
||||
if a.array != nil {
|
||||
@ -676,7 +683,7 @@ func (a *SortedIntArray) UnmarshalValue(value interface{}) (err error) {
|
||||
defer a.mu.Unlock()
|
||||
switch value.(type) {
|
||||
case string, []byte:
|
||||
err = json.Unmarshal(gconv.Bytes(value), &a.array)
|
||||
err = json.UnmarshalUseNumber(gconv.Bytes(value), &a.array)
|
||||
default:
|
||||
a.array = gconv.SliceInt(value)
|
||||
}
|
||||
@ -707,7 +714,7 @@ func (a *SortedIntArray) FilterEmpty() *SortedIntArray {
|
||||
return a
|
||||
}
|
||||
|
||||
// Walk applies a user supplied function <f> to every item of array.
|
||||
// Walk applies a user supplied function `f` to every item of array.
|
||||
func (a *SortedIntArray) Walk(f func(value int) int) *SortedIntArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
|
||||
@ -32,14 +32,14 @@ type SortedStrArray struct {
|
||||
}
|
||||
|
||||
// NewSortedStrArray creates and returns an empty sorted array.
|
||||
// The parameter <safe> is used to specify whether using array in concurrent-safety,
|
||||
// The parameter `safe` is used to specify whether using array in concurrent-safety,
|
||||
// which is false in default.
|
||||
func NewSortedStrArray(safe ...bool) *SortedStrArray {
|
||||
return NewSortedStrArraySize(0, safe...)
|
||||
}
|
||||
|
||||
// NewSortedStrArrayComparator creates and returns an empty sorted array with specified comparator.
|
||||
// The parameter <safe> is used to specify whether using array in concurrent-safety which is false in default.
|
||||
// The parameter `safe` is used to specify whether using array in concurrent-safety which is false in default.
|
||||
func NewSortedStrArrayComparator(comparator func(a, b string) int, safe ...bool) *SortedStrArray {
|
||||
array := NewSortedStrArray(safe...)
|
||||
array.comparator = comparator
|
||||
@ -47,7 +47,7 @@ func NewSortedStrArrayComparator(comparator func(a, b string) int, safe ...bool)
|
||||
}
|
||||
|
||||
// NewSortedStrArraySize create and returns an sorted array with given size and cap.
|
||||
// The parameter <safe> is used to specify whether using array in concurrent-safety,
|
||||
// The parameter `safe` is used to specify whether using array in concurrent-safety,
|
||||
// which is false in default.
|
||||
func NewSortedStrArraySize(cap int, safe ...bool) *SortedStrArray {
|
||||
return &SortedStrArray{
|
||||
@ -57,8 +57,8 @@ func NewSortedStrArraySize(cap int, safe ...bool) *SortedStrArray {
|
||||
}
|
||||
}
|
||||
|
||||
// NewSortedStrArrayFrom creates and returns an sorted array with given slice <array>.
|
||||
// The parameter <safe> is used to specify whether using array in concurrent-safety,
|
||||
// NewSortedStrArrayFrom creates and returns an sorted array with given slice `array`.
|
||||
// The parameter `safe` is used to specify whether using array in concurrent-safety,
|
||||
// which is false in default.
|
||||
func NewSortedStrArrayFrom(array []string, safe ...bool) *SortedStrArray {
|
||||
a := NewSortedStrArraySize(0, safe...)
|
||||
@ -67,8 +67,8 @@ func NewSortedStrArrayFrom(array []string, safe ...bool) *SortedStrArray {
|
||||
return a
|
||||
}
|
||||
|
||||
// NewSortedStrArrayFromCopy creates and returns an sorted array from a copy of given slice <array>.
|
||||
// The parameter <safe> is used to specify whether using array in concurrent-safety,
|
||||
// NewSortedStrArrayFromCopy creates and returns an sorted array from a copy of given slice `array`.
|
||||
// The parameter `safe` is used to specify whether using array in concurrent-safety,
|
||||
// which is false in default.
|
||||
func NewSortedStrArrayFromCopy(array []string, safe ...bool) *SortedStrArray {
|
||||
newArray := make([]string, len(array))
|
||||
@ -76,7 +76,7 @@ func NewSortedStrArrayFromCopy(array []string, safe ...bool) *SortedStrArray {
|
||||
return NewSortedStrArrayFrom(newArray, safe...)
|
||||
}
|
||||
|
||||
// SetArray sets the underlying slice array with the given <array>.
|
||||
// SetArray sets the underlying slice array with the given `array`.
|
||||
func (a *SortedStrArray) SetArray(array []string) *SortedStrArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -85,8 +85,15 @@ func (a *SortedStrArray) SetArray(array []string) *SortedStrArray {
|
||||
return a
|
||||
}
|
||||
|
||||
// At returns the value by the specified index.
|
||||
// If the given `index` is out of range of the array, it returns an empty string.
|
||||
func (a *SortedStrArray) At(index int) (value string) {
|
||||
value, _ = a.Get(index)
|
||||
return
|
||||
}
|
||||
|
||||
// Sort sorts the array in increasing order.
|
||||
// The parameter <reverse> controls whether sort
|
||||
// The parameter `reverse` controls whether sort
|
||||
// in increasing order(default) or decreasing order.
|
||||
func (a *SortedStrArray) Sort() *SortedStrArray {
|
||||
a.mu.Lock()
|
||||
@ -128,7 +135,7 @@ func (a *SortedStrArray) Append(values ...string) *SortedStrArray {
|
||||
}
|
||||
|
||||
// Get returns the value by the specified index.
|
||||
// If the given <index> is out of range of the array, the <found> is false.
|
||||
// If the given `index` is out of range of the array, the `found` is false.
|
||||
func (a *SortedStrArray) Get(index int) (value string, found bool) {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
@ -139,7 +146,7 @@ func (a *SortedStrArray) Get(index int) (value string, found bool) {
|
||||
}
|
||||
|
||||
// Remove removes an item by index.
|
||||
// If the given <index> is out of range of the array, the <found> is false.
|
||||
// If the given `index` is out of range of the array, the `found` is false.
|
||||
func (a *SortedStrArray) Remove(index int) (value string, found bool) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -180,7 +187,7 @@ func (a *SortedStrArray) RemoveValue(value string) bool {
|
||||
}
|
||||
|
||||
// PopLeft pops and returns an item from the beginning of array.
|
||||
// Note that if the array is empty, the <found> is false.
|
||||
// Note that if the array is empty, the `found` is false.
|
||||
func (a *SortedStrArray) PopLeft() (value string, found bool) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -193,7 +200,7 @@ func (a *SortedStrArray) PopLeft() (value string, found bool) {
|
||||
}
|
||||
|
||||
// PopRight pops and returns an item from the end of array.
|
||||
// Note that if the array is empty, the <found> is false.
|
||||
// Note that if the array is empty, the `found` is false.
|
||||
func (a *SortedStrArray) PopRight() (value string, found bool) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -207,16 +214,16 @@ func (a *SortedStrArray) PopRight() (value string, found bool) {
|
||||
}
|
||||
|
||||
// PopRand randomly pops and return an item out of array.
|
||||
// Note that if the array is empty, the <found> is false.
|
||||
// Note that if the array is empty, the `found` is false.
|
||||
func (a *SortedStrArray) PopRand() (value string, found bool) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
return a.doRemoveWithoutLock(grand.Intn(len(a.array)))
|
||||
}
|
||||
|
||||
// PopRands randomly pops and returns <size> items out of array.
|
||||
// If the given <size> is greater than size of the array, it returns all elements of the array.
|
||||
// Note that if given <size> <= 0 or the array is empty, it returns nil.
|
||||
// PopRands randomly pops and returns `size` items out of array.
|
||||
// If the given `size` is greater than size of the array, it returns all elements of the array.
|
||||
// Note that if given `size` <= 0 or the array is empty, it returns nil.
|
||||
func (a *SortedStrArray) PopRands(size int) []string {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -233,9 +240,9 @@ func (a *SortedStrArray) PopRands(size int) []string {
|
||||
return array
|
||||
}
|
||||
|
||||
// PopLefts pops and returns <size> items from the beginning of array.
|
||||
// If the given <size> is greater than size of the array, it returns all elements of the array.
|
||||
// Note that if given <size> <= 0 or the array is empty, it returns nil.
|
||||
// PopLefts pops and returns `size` items from the beginning of array.
|
||||
// If the given `size` is greater than size of the array, it returns all elements of the array.
|
||||
// Note that if given `size` <= 0 or the array is empty, it returns nil.
|
||||
func (a *SortedStrArray) PopLefts(size int) []string {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -252,9 +259,9 @@ func (a *SortedStrArray) PopLefts(size int) []string {
|
||||
return value
|
||||
}
|
||||
|
||||
// PopRights pops and returns <size> items from the end of array.
|
||||
// If the given <size> is greater than size of the array, it returns all elements of the array.
|
||||
// Note that if given <size> <= 0 or the array is empty, it returns nil.
|
||||
// PopRights pops and returns `size` items from the end of array.
|
||||
// If the given `size` is greater than size of the array, it returns all elements of the array.
|
||||
// Note that if given `size` <= 0 or the array is empty, it returns nil.
|
||||
func (a *SortedStrArray) PopRights(size int) []string {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -276,8 +283,8 @@ func (a *SortedStrArray) PopRights(size int) []string {
|
||||
// Notice, if in concurrent-safe usage, it returns a copy of slice;
|
||||
// else a pointer to the underlying data.
|
||||
//
|
||||
// If <end> is negative, then the offset will start from the end of array.
|
||||
// If <end> is omitted, then the sequence will have everything from start up
|
||||
// If `end` is negative, then the offset will start from the end of array.
|
||||
// If `end` is omitted, then the sequence will have everything from start up
|
||||
// until the end of the array.
|
||||
func (a *SortedStrArray) Range(start int, end ...int) []string {
|
||||
a.mu.RLock()
|
||||
@ -303,7 +310,7 @@ func (a *SortedStrArray) Range(start int, end ...int) []string {
|
||||
}
|
||||
|
||||
// SubSlice returns a slice of elements from the array as specified
|
||||
// by the <offset> and <size> parameters.
|
||||
// by the `offset` and `size` parameters.
|
||||
// If in concurrent safe usage, it returns a copy of the slice; else a pointer.
|
||||
//
|
||||
// If offset is non-negative, the sequence will start at that offset in the array.
|
||||
@ -418,7 +425,7 @@ func (a *SortedStrArray) ContainsI(value string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// Search searches array by <value>, returns the index of <value>,
|
||||
// Search searches array by `value`, returns the index of `value`,
|
||||
// or returns -1 if not exists.
|
||||
func (a *SortedStrArray) Search(value string) (index int) {
|
||||
if i, r := a.binSearch(value, true); r == 0 {
|
||||
@ -429,9 +436,9 @@ func (a *SortedStrArray) Search(value string) (index int) {
|
||||
|
||||
// Binary search.
|
||||
// It returns the last compared index and the result.
|
||||
// If <result> equals to 0, it means the value at <index> is equals to <value>.
|
||||
// If <result> lesser than 0, it means the value at <index> is lesser than <value>.
|
||||
// If <result> greater than 0, it means the value at <index> is greater than <value>.
|
||||
// If `result` equals to 0, it means the value at `index` is equals to `value`.
|
||||
// If `result` lesser than 0, it means the value at `index` is lesser than `value`.
|
||||
// If `result` greater than 0, it means the value at `index` is greater than `value`.
|
||||
func (a *SortedStrArray) binSearch(value string, lock bool) (index int, result int) {
|
||||
if lock {
|
||||
a.mu.RLock()
|
||||
@ -511,7 +518,7 @@ func (a *SortedStrArray) Clear() *SortedStrArray {
|
||||
return a
|
||||
}
|
||||
|
||||
// LockFunc locks writing by callback function <f>.
|
||||
// LockFunc locks writing by callback function `f`.
|
||||
func (a *SortedStrArray) LockFunc(f func(array []string)) *SortedStrArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -519,7 +526,7 @@ func (a *SortedStrArray) LockFunc(f func(array []string)) *SortedStrArray {
|
||||
return a
|
||||
}
|
||||
|
||||
// RLockFunc locks reading by callback function <f>.
|
||||
// RLockFunc locks reading by callback function `f`.
|
||||
func (a *SortedStrArray) RLockFunc(f func(array []string)) *SortedStrArray {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
@ -527,8 +534,8 @@ func (a *SortedStrArray) RLockFunc(f func(array []string)) *SortedStrArray {
|
||||
return a
|
||||
}
|
||||
|
||||
// Merge merges <array> into current array.
|
||||
// The parameter <array> can be any garray or slice type.
|
||||
// Merge merges `array` into current array.
|
||||
// The parameter `array` can be any garray or slice type.
|
||||
// The difference between Merge and Append is Append supports only specified slice type,
|
||||
// but Merge supports more parameter types.
|
||||
func (a *SortedStrArray) Merge(array interface{}) *SortedStrArray {
|
||||
@ -536,7 +543,7 @@ func (a *SortedStrArray) Merge(array interface{}) *SortedStrArray {
|
||||
}
|
||||
|
||||
// Chunk splits an array into multiple arrays,
|
||||
// the size of each array is determined by <size>.
|
||||
// the size of each array is determined by `size`.
|
||||
// The last chunk may contain less than size elements.
|
||||
func (a *SortedStrArray) Chunk(size int) [][]string {
|
||||
if size < 1 {
|
||||
@ -568,7 +575,7 @@ func (a *SortedStrArray) Rand() (value string, found bool) {
|
||||
return a.array[grand.Intn(len(a.array))], true
|
||||
}
|
||||
|
||||
// Rands randomly returns <size> items from array(no deleting).
|
||||
// Rands randomly returns `size` items from array(no deleting).
|
||||
func (a *SortedStrArray) Rands(size int) []string {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
@ -582,7 +589,7 @@ func (a *SortedStrArray) Rands(size int) []string {
|
||||
return array
|
||||
}
|
||||
|
||||
// Join joins array elements with a string <glue>.
|
||||
// Join joins array elements with a string `glue`.
|
||||
func (a *SortedStrArray) Join(glue string) string {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
@ -615,8 +622,8 @@ func (a *SortedStrArray) Iterator(f func(k int, v string) bool) {
|
||||
a.IteratorAsc(f)
|
||||
}
|
||||
|
||||
// IteratorAsc iterates the array readonly in ascending order with given callback function <f>.
|
||||
// If <f> returns true, then it continues iterating; or false to stop.
|
||||
// IteratorAsc iterates the array readonly in ascending order with given callback function `f`.
|
||||
// If `f` returns true, then it continues iterating; or false to stop.
|
||||
func (a *SortedStrArray) IteratorAsc(f func(k int, v string) bool) {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
@ -627,8 +634,8 @@ func (a *SortedStrArray) IteratorAsc(f func(k int, v string) bool) {
|
||||
}
|
||||
}
|
||||
|
||||
// IteratorDesc iterates the array readonly in descending order with given callback function <f>.
|
||||
// If <f> returns true, then it continues iterating; or false to stop.
|
||||
// IteratorDesc iterates the array readonly in descending order with given callback function `f`.
|
||||
// If `f` returns true, then it continues iterating; or false to stop.
|
||||
func (a *SortedStrArray) IteratorDesc(f func(k int, v string) bool) {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
@ -671,7 +678,7 @@ func (a *SortedStrArray) UnmarshalJSON(b []byte) error {
|
||||
}
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if err := json.Unmarshal(b, &a.array); err != nil {
|
||||
if err := json.UnmarshalUseNumber(b, &a.array); err != nil {
|
||||
return err
|
||||
}
|
||||
if a.array != nil {
|
||||
@ -689,7 +696,7 @@ func (a *SortedStrArray) UnmarshalValue(value interface{}) (err error) {
|
||||
defer a.mu.Unlock()
|
||||
switch value.(type) {
|
||||
case string, []byte:
|
||||
err = json.Unmarshal(gconv.Bytes(value), &a.array)
|
||||
err = json.UnmarshalUseNumber(gconv.Bytes(value), &a.array)
|
||||
default:
|
||||
a.array = gconv.SliceStr(value)
|
||||
}
|
||||
@ -720,7 +727,7 @@ func (a *SortedStrArray) FilterEmpty() *SortedStrArray {
|
||||
return a
|
||||
}
|
||||
|
||||
// Walk applies a user supplied function <f> to every item of array.
|
||||
// Walk applies a user supplied function `f` to every item of array.
|
||||
func (a *SortedStrArray) Walk(f func(value string) string) *SortedStrArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
|
||||
@ -75,14 +75,14 @@ func ExampleNew() {
|
||||
func ExampleArray_Iterator() {
|
||||
array := garray.NewArrayFrom(g.Slice{"a", "b", "c"})
|
||||
// Iterator is alias of IteratorAsc, which iterates the array readonly in ascending order
|
||||
// with given callback function <f>.
|
||||
// If <f> returns true, then it continues iterating; or false to stop.
|
||||
// with given callback function `f`.
|
||||
// If `f` returns true, then it continues iterating; or false to stop.
|
||||
array.Iterator(func(k int, v interface{}) bool {
|
||||
fmt.Println(k, v)
|
||||
return true
|
||||
})
|
||||
// IteratorDesc iterates the array readonly in descending order with given callback function <f>.
|
||||
// If <f> returns true, then it continues iterating; or false to stop.
|
||||
// IteratorDesc iterates the array readonly in descending order with given callback function `f`.
|
||||
// If `f` returns true, then it continues iterating; or false to stop.
|
||||
array.IteratorDesc(func(k int, v interface{}) bool {
|
||||
fmt.Println(k, v)
|
||||
return true
|
||||
@ -150,7 +150,7 @@ func ExampleArray_Chunk() {
|
||||
array := garray.NewFrom(g.Slice{1, 2, 3, 4, 5, 6, 7, 8, 9})
|
||||
|
||||
// Chunk splits an array into multiple arrays,
|
||||
// the size of each array is determined by <size>.
|
||||
// the size of each array is determined by `size`.
|
||||
// The last chunk may contain less than size elements.
|
||||
fmt.Println(array.Chunk(2))
|
||||
|
||||
|
||||
@ -570,12 +570,12 @@ func TestArray_Json(t *testing.T) {
|
||||
t.Assert(err1, err2)
|
||||
|
||||
a2 := garray.New()
|
||||
err2 = json.Unmarshal(b2, &a2)
|
||||
err2 = json.UnmarshalUseNumber(b2, &a2)
|
||||
t.Assert(err2, nil)
|
||||
t.Assert(a2.Slice(), s1)
|
||||
|
||||
var a3 garray.Array
|
||||
err := json.Unmarshal(b2, &a3)
|
||||
err := json.UnmarshalUseNumber(b2, &a3)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(a3.Slice(), s1)
|
||||
})
|
||||
@ -589,12 +589,12 @@ func TestArray_Json(t *testing.T) {
|
||||
t.Assert(err1, err2)
|
||||
|
||||
a2 := garray.New()
|
||||
err2 = json.Unmarshal(b2, &a2)
|
||||
err2 = json.UnmarshalUseNumber(b2, &a2)
|
||||
t.Assert(err2, nil)
|
||||
t.Assert(a2.Slice(), s1)
|
||||
|
||||
var a3 garray.Array
|
||||
err := json.Unmarshal(b2, &a3)
|
||||
err := json.UnmarshalUseNumber(b2, &a3)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(a3.Slice(), s1)
|
||||
})
|
||||
@ -612,7 +612,7 @@ func TestArray_Json(t *testing.T) {
|
||||
t.Assert(err, nil)
|
||||
|
||||
user := new(User)
|
||||
err = json.Unmarshal(b, user)
|
||||
err = json.UnmarshalUseNumber(b, user)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(user.Name, data["Name"])
|
||||
t.Assert(user.Scores, data["Scores"])
|
||||
@ -631,7 +631,7 @@ func TestArray_Json(t *testing.T) {
|
||||
t.Assert(err, nil)
|
||||
|
||||
user := new(User)
|
||||
err = json.Unmarshal(b, user)
|
||||
err = json.UnmarshalUseNumber(b, user)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(user.Name, data["Name"])
|
||||
t.Assert(user.Scores, data["Scores"])
|
||||
|
||||
@ -615,11 +615,11 @@ func TestIntArray_Json(t *testing.T) {
|
||||
t.Assert(err1, err2)
|
||||
|
||||
a2 := garray.NewIntArray()
|
||||
err1 = json.Unmarshal(b2, &a2)
|
||||
err1 = json.UnmarshalUseNumber(b2, &a2)
|
||||
t.Assert(a2.Slice(), s1)
|
||||
|
||||
var a3 garray.IntArray
|
||||
err := json.Unmarshal(b2, &a3)
|
||||
err := json.UnmarshalUseNumber(b2, &a3)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(a3.Slice(), s1)
|
||||
})
|
||||
@ -633,11 +633,11 @@ func TestIntArray_Json(t *testing.T) {
|
||||
t.Assert(err1, err2)
|
||||
|
||||
a2 := garray.NewIntArray()
|
||||
err1 = json.Unmarshal(b2, &a2)
|
||||
err1 = json.UnmarshalUseNumber(b2, &a2)
|
||||
t.Assert(a2.Slice(), s1)
|
||||
|
||||
var a3 garray.IntArray
|
||||
err := json.Unmarshal(b2, &a3)
|
||||
err := json.UnmarshalUseNumber(b2, &a3)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(a3.Slice(), s1)
|
||||
})
|
||||
@ -655,7 +655,7 @@ func TestIntArray_Json(t *testing.T) {
|
||||
t.Assert(err, nil)
|
||||
|
||||
user := new(User)
|
||||
err = json.Unmarshal(b, user)
|
||||
err = json.UnmarshalUseNumber(b, user)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(user.Name, data["Name"])
|
||||
t.Assert(user.Scores, data["Scores"])
|
||||
@ -674,7 +674,7 @@ func TestIntArray_Json(t *testing.T) {
|
||||
t.Assert(err, nil)
|
||||
|
||||
user := new(User)
|
||||
err = json.Unmarshal(b, user)
|
||||
err = json.UnmarshalUseNumber(b, user)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(user.Name, data["Name"])
|
||||
t.Assert(user.Scores, data["Scores"])
|
||||
|
||||
@ -614,11 +614,11 @@ func TestStrArray_Json(t *testing.T) {
|
||||
t.Assert(err1, err2)
|
||||
|
||||
a2 := garray.NewStrArray()
|
||||
err1 = json.Unmarshal(b2, &a2)
|
||||
err1 = json.UnmarshalUseNumber(b2, &a2)
|
||||
t.Assert(a2.Slice(), s1)
|
||||
|
||||
var a3 garray.StrArray
|
||||
err := json.Unmarshal(b2, &a3)
|
||||
err := json.UnmarshalUseNumber(b2, &a3)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(a3.Slice(), s1)
|
||||
})
|
||||
@ -632,11 +632,11 @@ func TestStrArray_Json(t *testing.T) {
|
||||
t.Assert(err1, err2)
|
||||
|
||||
a2 := garray.NewStrArray()
|
||||
err1 = json.Unmarshal(b2, &a2)
|
||||
err1 = json.UnmarshalUseNumber(b2, &a2)
|
||||
t.Assert(a2.Slice(), s1)
|
||||
|
||||
var a3 garray.StrArray
|
||||
err := json.Unmarshal(b2, &a3)
|
||||
err := json.UnmarshalUseNumber(b2, &a3)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(a3.Slice(), s1)
|
||||
})
|
||||
@ -654,7 +654,7 @@ func TestStrArray_Json(t *testing.T) {
|
||||
t.Assert(err, nil)
|
||||
|
||||
user := new(User)
|
||||
err = json.Unmarshal(b, user)
|
||||
err = json.UnmarshalUseNumber(b, user)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(user.Name, data["Name"])
|
||||
t.Assert(user.Scores, data["Scores"])
|
||||
@ -673,7 +673,7 @@ func TestStrArray_Json(t *testing.T) {
|
||||
t.Assert(err, nil)
|
||||
|
||||
user := new(User)
|
||||
err = json.Unmarshal(b, user)
|
||||
err = json.UnmarshalUseNumber(b, user)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(user.Name, data["Name"])
|
||||
t.Assert(user.Scores, data["Scores"])
|
||||
|
||||
@ -656,11 +656,11 @@ func TestSortedArray_Json(t *testing.T) {
|
||||
t.Assert(err1, err2)
|
||||
|
||||
a2 := garray.NewSortedArray(gutil.ComparatorString)
|
||||
err1 = json.Unmarshal(b2, &a2)
|
||||
err1 = json.UnmarshalUseNumber(b2, &a2)
|
||||
t.Assert(a2.Slice(), s2)
|
||||
|
||||
var a3 garray.SortedArray
|
||||
err := json.Unmarshal(b2, &a3)
|
||||
err := json.UnmarshalUseNumber(b2, &a3)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(a3.Slice(), s1)
|
||||
t.Assert(a3.Interfaces(), s1)
|
||||
@ -676,11 +676,11 @@ func TestSortedArray_Json(t *testing.T) {
|
||||
t.Assert(err1, err2)
|
||||
|
||||
a2 := garray.NewSortedArray(gutil.ComparatorString)
|
||||
err1 = json.Unmarshal(b2, &a2)
|
||||
err1 = json.UnmarshalUseNumber(b2, &a2)
|
||||
t.Assert(a2.Slice(), s2)
|
||||
|
||||
var a3 garray.SortedArray
|
||||
err := json.Unmarshal(b2, &a3)
|
||||
err := json.UnmarshalUseNumber(b2, &a3)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(a3.Slice(), s1)
|
||||
t.Assert(a3.Interfaces(), s1)
|
||||
@ -699,7 +699,7 @@ func TestSortedArray_Json(t *testing.T) {
|
||||
t.Assert(err, nil)
|
||||
|
||||
user := new(User)
|
||||
err = json.Unmarshal(b, user)
|
||||
err = json.UnmarshalUseNumber(b, user)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(user.Name, data["Name"])
|
||||
t.AssertNE(user.Scores, nil)
|
||||
@ -735,7 +735,7 @@ func TestSortedArray_Json(t *testing.T) {
|
||||
t.Assert(err, nil)
|
||||
|
||||
user := new(User)
|
||||
err = json.Unmarshal(b, user)
|
||||
err = json.UnmarshalUseNumber(b, user)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(user.Name, data["Name"])
|
||||
t.AssertNE(user.Scores, nil)
|
||||
|
||||
@ -557,11 +557,11 @@ func TestSortedIntArray_Json(t *testing.T) {
|
||||
t.Assert(err1, err2)
|
||||
|
||||
a2 := garray.NewSortedIntArray()
|
||||
err1 = json.Unmarshal(b2, &a2)
|
||||
err1 = json.UnmarshalUseNumber(b2, &a2)
|
||||
t.Assert(a2.Slice(), s2)
|
||||
|
||||
var a3 garray.SortedIntArray
|
||||
err := json.Unmarshal(b2, &a3)
|
||||
err := json.UnmarshalUseNumber(b2, &a3)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(a3.Slice(), s1)
|
||||
})
|
||||
@ -576,11 +576,11 @@ func TestSortedIntArray_Json(t *testing.T) {
|
||||
t.Assert(err1, err2)
|
||||
|
||||
a2 := garray.NewSortedIntArray()
|
||||
err1 = json.Unmarshal(b2, &a2)
|
||||
err1 = json.UnmarshalUseNumber(b2, &a2)
|
||||
t.Assert(a2.Slice(), s2)
|
||||
|
||||
var a3 garray.SortedIntArray
|
||||
err := json.Unmarshal(b2, &a3)
|
||||
err := json.UnmarshalUseNumber(b2, &a3)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(a3.Slice(), s1)
|
||||
})
|
||||
@ -598,7 +598,7 @@ func TestSortedIntArray_Json(t *testing.T) {
|
||||
t.Assert(err, nil)
|
||||
|
||||
user := new(User)
|
||||
err = json.Unmarshal(b, user)
|
||||
err = json.UnmarshalUseNumber(b, user)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(user.Name, data["Name"])
|
||||
t.Assert(user.Scores, []int{98, 99, 100})
|
||||
@ -617,7 +617,7 @@ func TestSortedIntArray_Json(t *testing.T) {
|
||||
t.Assert(err, nil)
|
||||
|
||||
user := new(User)
|
||||
err = json.Unmarshal(b, user)
|
||||
err = json.UnmarshalUseNumber(b, user)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(user.Name, data["Name"])
|
||||
t.Assert(user.Scores, []int{98, 99, 100})
|
||||
|
||||
@ -577,12 +577,12 @@ func TestSortedStrArray_Json(t *testing.T) {
|
||||
t.Assert(err1, err2)
|
||||
|
||||
a2 := garray.NewSortedStrArray()
|
||||
err1 = json.Unmarshal(b2, &a2)
|
||||
err1 = json.UnmarshalUseNumber(b2, &a2)
|
||||
t.Assert(a2.Slice(), s2)
|
||||
t.Assert(a2.Interfaces(), s2)
|
||||
|
||||
var a3 garray.SortedStrArray
|
||||
err := json.Unmarshal(b2, &a3)
|
||||
err := json.UnmarshalUseNumber(b2, &a3)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(a3.Slice(), s1)
|
||||
t.Assert(a3.Interfaces(), s1)
|
||||
@ -598,12 +598,12 @@ func TestSortedStrArray_Json(t *testing.T) {
|
||||
t.Assert(err1, err2)
|
||||
|
||||
a2 := garray.NewSortedStrArray()
|
||||
err1 = json.Unmarshal(b2, &a2)
|
||||
err1 = json.UnmarshalUseNumber(b2, &a2)
|
||||
t.Assert(a2.Slice(), s2)
|
||||
t.Assert(a2.Interfaces(), s2)
|
||||
|
||||
var a3 garray.SortedStrArray
|
||||
err := json.Unmarshal(b2, &a3)
|
||||
err := json.UnmarshalUseNumber(b2, &a3)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(a3.Slice(), s1)
|
||||
t.Assert(a3.Interfaces(), s1)
|
||||
@ -622,7 +622,7 @@ func TestSortedStrArray_Json(t *testing.T) {
|
||||
t.Assert(err, nil)
|
||||
|
||||
user := new(User)
|
||||
err = json.Unmarshal(b, user)
|
||||
err = json.UnmarshalUseNumber(b, user)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(user.Name, data["Name"])
|
||||
t.Assert(user.Scores, []string{"A", "A", "A+"})
|
||||
@ -641,7 +641,7 @@ func TestSortedStrArray_Json(t *testing.T) {
|
||||
t.Assert(err, nil)
|
||||
|
||||
user := new(User)
|
||||
err = json.Unmarshal(b, user)
|
||||
err = json.UnmarshalUseNumber(b, user)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(user.Name, data["Name"])
|
||||
t.Assert(user.Scores, []string{"A", "A", "A+"})
|
||||
|
||||
@ -519,7 +519,7 @@ func (l *List) UnmarshalJSON(b []byte) error {
|
||||
l.list = list.New()
|
||||
}
|
||||
var array []interface{}
|
||||
if err := json.Unmarshal(b, &array); err != nil {
|
||||
if err := json.UnmarshalUseNumber(b, &array); err != nil {
|
||||
return err
|
||||
}
|
||||
l.PushBacks(array)
|
||||
@ -536,7 +536,7 @@ func (l *List) UnmarshalValue(value interface{}) (err error) {
|
||||
var array []interface{}
|
||||
switch value.(type) {
|
||||
case string, []byte:
|
||||
err = json.Unmarshal(gconv.Bytes(value), &array)
|
||||
err = json.UnmarshalUseNumber(gconv.Bytes(value), &array)
|
||||
default:
|
||||
array = gconv.SliceAny(value)
|
||||
}
|
||||
|
||||
@ -711,7 +711,7 @@ func TestList_Json(t *testing.T) {
|
||||
b, err := json.Marshal(a)
|
||||
t.Assert(err, nil)
|
||||
|
||||
err = json.Unmarshal(b, l)
|
||||
err = json.UnmarshalUseNumber(b, l)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(l.FrontAll(), a)
|
||||
})
|
||||
@ -721,7 +721,7 @@ func TestList_Json(t *testing.T) {
|
||||
b, err := json.Marshal(a)
|
||||
t.Assert(err, nil)
|
||||
|
||||
err = json.Unmarshal(b, &l)
|
||||
err = json.UnmarshalUseNumber(b, &l)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(l.FrontAll(), a)
|
||||
})
|
||||
|
||||
@ -476,7 +476,7 @@ func (m *AnyAnyMap) UnmarshalJSON(b []byte) error {
|
||||
m.data = make(map[interface{}]interface{})
|
||||
}
|
||||
var data map[string]interface{}
|
||||
if err := json.Unmarshal(b, &data); err != nil {
|
||||
if err := json.UnmarshalUseNumber(b, &data); err != nil {
|
||||
return err
|
||||
}
|
||||
for k, v := range data {
|
||||
|
||||
@ -475,7 +475,7 @@ func (m *IntAnyMap) UnmarshalJSON(b []byte) error {
|
||||
if m.data == nil {
|
||||
m.data = make(map[int]interface{})
|
||||
}
|
||||
if err := json.Unmarshal(b, &m.data); err != nil {
|
||||
if err := json.UnmarshalUseNumber(b, &m.data); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
@ -490,7 +490,7 @@ func (m *IntAnyMap) UnmarshalValue(value interface{}) (err error) {
|
||||
}
|
||||
switch value.(type) {
|
||||
case string, []byte:
|
||||
return json.Unmarshal(gconv.Bytes(value), &m.data)
|
||||
return json.UnmarshalUseNumber(gconv.Bytes(value), &m.data)
|
||||
default:
|
||||
for k, v := range gconv.Map(value) {
|
||||
m.data[gconv.Int(k)] = v
|
||||
|
||||
@ -446,7 +446,7 @@ func (m *IntIntMap) UnmarshalJSON(b []byte) error {
|
||||
if m.data == nil {
|
||||
m.data = make(map[int]int)
|
||||
}
|
||||
if err := json.Unmarshal(b, &m.data); err != nil {
|
||||
if err := json.UnmarshalUseNumber(b, &m.data); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
@ -461,7 +461,7 @@ func (m *IntIntMap) UnmarshalValue(value interface{}) (err error) {
|
||||
}
|
||||
switch value.(type) {
|
||||
case string, []byte:
|
||||
return json.Unmarshal(gconv.Bytes(value), &m.data)
|
||||
return json.UnmarshalUseNumber(gconv.Bytes(value), &m.data)
|
||||
default:
|
||||
for k, v := range gconv.Map(value) {
|
||||
m.data[gconv.Int(k)] = gconv.Int(v)
|
||||
|
||||
@ -446,7 +446,7 @@ func (m *IntStrMap) UnmarshalJSON(b []byte) error {
|
||||
if m.data == nil {
|
||||
m.data = make(map[int]string)
|
||||
}
|
||||
if err := json.Unmarshal(b, &m.data); err != nil {
|
||||
if err := json.UnmarshalUseNumber(b, &m.data); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
@ -461,7 +461,7 @@ func (m *IntStrMap) UnmarshalValue(value interface{}) (err error) {
|
||||
}
|
||||
switch value.(type) {
|
||||
case string, []byte:
|
||||
return json.Unmarshal(gconv.Bytes(value), &m.data)
|
||||
return json.UnmarshalUseNumber(gconv.Bytes(value), &m.data)
|
||||
default:
|
||||
for k, v := range gconv.Map(value) {
|
||||
m.data[gconv.Int(k)] = gconv.String(v)
|
||||
|
||||
@ -471,7 +471,7 @@ func (m *StrAnyMap) UnmarshalJSON(b []byte) error {
|
||||
if m.data == nil {
|
||||
m.data = make(map[string]interface{})
|
||||
}
|
||||
if err := json.Unmarshal(b, &m.data); err != nil {
|
||||
if err := json.UnmarshalUseNumber(b, &m.data); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
||||
@ -449,7 +449,7 @@ func (m *StrIntMap) UnmarshalJSON(b []byte) error {
|
||||
if m.data == nil {
|
||||
m.data = make(map[string]int)
|
||||
}
|
||||
if err := json.Unmarshal(b, &m.data); err != nil {
|
||||
if err := json.UnmarshalUseNumber(b, &m.data); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
@ -464,7 +464,7 @@ func (m *StrIntMap) UnmarshalValue(value interface{}) (err error) {
|
||||
}
|
||||
switch value.(type) {
|
||||
case string, []byte:
|
||||
return json.Unmarshal(gconv.Bytes(value), &m.data)
|
||||
return json.UnmarshalUseNumber(gconv.Bytes(value), &m.data)
|
||||
default:
|
||||
for k, v := range gconv.Map(value) {
|
||||
m.data[k] = gconv.Int(v)
|
||||
|
||||
@ -449,7 +449,7 @@ func (m *StrStrMap) UnmarshalJSON(b []byte) error {
|
||||
if m.data == nil {
|
||||
m.data = make(map[string]string)
|
||||
}
|
||||
if err := json.Unmarshal(b, &m.data); err != nil {
|
||||
if err := json.UnmarshalUseNumber(b, &m.data); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
||||
@ -530,7 +530,7 @@ func (m *ListMap) UnmarshalJSON(b []byte) error {
|
||||
m.list = glist.New()
|
||||
}
|
||||
var data map[string]interface{}
|
||||
if err := json.Unmarshal(b, &data); err != nil {
|
||||
if err := json.UnmarshalUseNumber(b, &data); err != nil {
|
||||
return err
|
||||
}
|
||||
for key, value := range data {
|
||||
|
||||
@ -255,7 +255,7 @@ func Test_AnyAnyMap_Json(t *testing.T) {
|
||||
t.Assert(err, nil)
|
||||
|
||||
m := gmap.New()
|
||||
err = json.Unmarshal(b, m)
|
||||
err = json.UnmarshalUseNumber(b, m)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(m.Get("k1"), data["k1"])
|
||||
t.Assert(m.Get("k2"), data["k2"])
|
||||
@ -269,7 +269,7 @@ func Test_AnyAnyMap_Json(t *testing.T) {
|
||||
t.Assert(err, nil)
|
||||
|
||||
var m gmap.Map
|
||||
err = json.Unmarshal(b, &m)
|
||||
err = json.UnmarshalUseNumber(b, &m)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(m.Get("k1"), data["k1"])
|
||||
t.Assert(m.Get("k2"), data["k2"])
|
||||
|
||||
@ -245,7 +245,7 @@ func Test_IntAnyMap_Json(t *testing.T) {
|
||||
t.Assert(err, nil)
|
||||
|
||||
m := gmap.NewIntAnyMap()
|
||||
err = json.Unmarshal(b, m)
|
||||
err = json.UnmarshalUseNumber(b, m)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(m.Get(1), data[1])
|
||||
t.Assert(m.Get(2), data[2])
|
||||
|
||||
@ -251,7 +251,7 @@ func Test_IntIntMap_Json(t *testing.T) {
|
||||
t.Assert(err, nil)
|
||||
|
||||
m := gmap.NewIntIntMap()
|
||||
err = json.Unmarshal(b, m)
|
||||
err = json.UnmarshalUseNumber(b, m)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(m.Get(1), data[1])
|
||||
t.Assert(m.Get(2), data[2])
|
||||
|
||||
@ -249,7 +249,7 @@ func Test_IntStrMap_Json(t *testing.T) {
|
||||
t.Assert(err, nil)
|
||||
|
||||
m := gmap.NewIntStrMap()
|
||||
err = json.Unmarshal(b, m)
|
||||
err = json.UnmarshalUseNumber(b, m)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(m.Get(1), data[1])
|
||||
t.Assert(m.Get(2), data[2])
|
||||
|
||||
@ -204,7 +204,7 @@ func Test_ListMap_Json(t *testing.T) {
|
||||
t.Assert(err, nil)
|
||||
|
||||
m := gmap.NewListMap()
|
||||
err = json.Unmarshal(b, m)
|
||||
err = json.UnmarshalUseNumber(b, m)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(m.Get("k1"), data["k1"])
|
||||
t.Assert(m.Get("k2"), data["k2"])
|
||||
@ -219,7 +219,7 @@ func Test_ListMap_Json(t *testing.T) {
|
||||
t.Assert(err, nil)
|
||||
|
||||
var m gmap.ListMap
|
||||
err = json.Unmarshal(b, &m)
|
||||
err = json.UnmarshalUseNumber(b, &m)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(m.Get("k1"), data["k1"])
|
||||
t.Assert(m.Get("k2"), data["k2"])
|
||||
|
||||
@ -243,7 +243,7 @@ func Test_StrAnyMap_Json(t *testing.T) {
|
||||
t.Assert(err, nil)
|
||||
|
||||
m := gmap.NewStrAnyMap()
|
||||
err = json.Unmarshal(b, m)
|
||||
err = json.UnmarshalUseNumber(b, m)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(m.Get("k1"), data["k1"])
|
||||
t.Assert(m.Get("k2"), data["k2"])
|
||||
@ -257,7 +257,7 @@ func Test_StrAnyMap_Json(t *testing.T) {
|
||||
t.Assert(err, nil)
|
||||
|
||||
var m gmap.StrAnyMap
|
||||
err = json.Unmarshal(b, &m)
|
||||
err = json.UnmarshalUseNumber(b, &m)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(m.Get("k1"), data["k1"])
|
||||
t.Assert(m.Get("k2"), data["k2"])
|
||||
|
||||
@ -247,7 +247,7 @@ func Test_StrIntMap_Json(t *testing.T) {
|
||||
t.Assert(err, nil)
|
||||
|
||||
m := gmap.NewStrIntMap()
|
||||
err = json.Unmarshal(b, m)
|
||||
err = json.UnmarshalUseNumber(b, m)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(m.Get("k1"), data["k1"])
|
||||
t.Assert(m.Get("k2"), data["k2"])
|
||||
@ -261,7 +261,7 @@ func Test_StrIntMap_Json(t *testing.T) {
|
||||
t.Assert(err, nil)
|
||||
|
||||
var m gmap.StrIntMap
|
||||
err = json.Unmarshal(b, &m)
|
||||
err = json.UnmarshalUseNumber(b, &m)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(m.Get("k1"), data["k1"])
|
||||
t.Assert(m.Get("k2"), data["k2"])
|
||||
|
||||
@ -244,7 +244,7 @@ func Test_StrStrMap_Json(t *testing.T) {
|
||||
t.Assert(err, nil)
|
||||
|
||||
m := gmap.NewStrStrMap()
|
||||
err = json.Unmarshal(b, m)
|
||||
err = json.UnmarshalUseNumber(b, m)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(m.Get("k1"), data["k1"])
|
||||
t.Assert(m.Get("k2"), data["k2"])
|
||||
@ -258,7 +258,7 @@ func Test_StrStrMap_Json(t *testing.T) {
|
||||
t.Assert(err, nil)
|
||||
|
||||
var m gmap.StrStrMap
|
||||
err = json.Unmarshal(b, &m)
|
||||
err = json.UnmarshalUseNumber(b, &m)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(m.Get("k1"), data["k1"])
|
||||
t.Assert(m.Get("k2"), data["k2"])
|
||||
|
||||
@ -188,7 +188,7 @@ func Test_TreeMap_Json(t *testing.T) {
|
||||
t.Assert(err, nil)
|
||||
|
||||
m := gmap.NewTreeMap(gutil.ComparatorString)
|
||||
err = json.Unmarshal(b, m)
|
||||
err = json.UnmarshalUseNumber(b, m)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(m.Get("k1"), data["k1"])
|
||||
t.Assert(m.Get("k2"), data["k2"])
|
||||
@ -202,7 +202,7 @@ func Test_TreeMap_Json(t *testing.T) {
|
||||
t.Assert(err, nil)
|
||||
|
||||
var m gmap.TreeMap
|
||||
err = json.Unmarshal(b, &m)
|
||||
err = json.UnmarshalUseNumber(b, &m)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(m.Get("k1"), data["k1"])
|
||||
t.Assert(m.Get("k2"), data["k2"])
|
||||
|
||||
@ -129,7 +129,7 @@ func (q *Queue) Close() {
|
||||
|
||||
// Len returns the length of the queue.
|
||||
// Note that the result might not be accurate as there's a
|
||||
// asynchronize channel reading the list constantly.
|
||||
// asynchronous channel reading the list constantly.
|
||||
func (q *Queue) Len() (length int) {
|
||||
if q.list != nil {
|
||||
length += q.list.Len()
|
||||
|
||||
@ -477,7 +477,7 @@ func (set *Set) UnmarshalJSON(b []byte) error {
|
||||
set.data = make(map[interface{}]struct{})
|
||||
}
|
||||
var array []interface{}
|
||||
if err := json.Unmarshal(b, &array); err != nil {
|
||||
if err := json.UnmarshalUseNumber(b, &array); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, v := range array {
|
||||
@ -496,7 +496,7 @@ func (set *Set) UnmarshalValue(value interface{}) (err error) {
|
||||
var array []interface{}
|
||||
switch value.(type) {
|
||||
case string, []byte:
|
||||
err = json.Unmarshal(gconv.Bytes(value), &array)
|
||||
err = json.UnmarshalUseNumber(gconv.Bytes(value), &array)
|
||||
default:
|
||||
array = gconv.SliceAny(value)
|
||||
}
|
||||
|
||||
@ -437,7 +437,7 @@ func (set *IntSet) UnmarshalJSON(b []byte) error {
|
||||
set.data = make(map[int]struct{})
|
||||
}
|
||||
var array []int
|
||||
if err := json.Unmarshal(b, &array); err != nil {
|
||||
if err := json.UnmarshalUseNumber(b, &array); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, v := range array {
|
||||
@ -456,7 +456,7 @@ func (set *IntSet) UnmarshalValue(value interface{}) (err error) {
|
||||
var array []int
|
||||
switch value.(type) {
|
||||
case string, []byte:
|
||||
err = json.Unmarshal(gconv.Bytes(value), &array)
|
||||
err = json.UnmarshalUseNumber(gconv.Bytes(value), &array)
|
||||
default:
|
||||
array = gconv.SliceInt(value)
|
||||
}
|
||||
|
||||
@ -21,7 +21,7 @@ type StrSet struct {
|
||||
data map[string]struct{}
|
||||
}
|
||||
|
||||
// New create and returns a new set, which contains un-repeated items.
|
||||
// NewStrSet create and returns a new set, which contains un-repeated items.
|
||||
// The parameter <safe> is used to specify whether using set in concurrent-safety,
|
||||
// which is false in default.
|
||||
func NewStrSet(safe ...bool) *StrSet {
|
||||
@ -465,7 +465,7 @@ func (set *StrSet) UnmarshalJSON(b []byte) error {
|
||||
set.data = make(map[string]struct{})
|
||||
}
|
||||
var array []string
|
||||
if err := json.Unmarshal(b, &array); err != nil {
|
||||
if err := json.UnmarshalUseNumber(b, &array); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, v := range array {
|
||||
@ -484,7 +484,7 @@ func (set *StrSet) UnmarshalValue(value interface{}) (err error) {
|
||||
var array []string
|
||||
switch value.(type) {
|
||||
case string, []byte:
|
||||
err = json.Unmarshal(gconv.Bytes(value), &array)
|
||||
err = json.UnmarshalUseNumber(gconv.Bytes(value), &array)
|
||||
default:
|
||||
array = gconv.SliceStr(value)
|
||||
}
|
||||
|
||||
@ -327,7 +327,7 @@ func TestSet_Json(t *testing.T) {
|
||||
t.Assert(err1, err2)
|
||||
|
||||
a2 := gset.New()
|
||||
err2 = json.Unmarshal(b2, &a2)
|
||||
err2 = json.UnmarshalUseNumber(b2, &a2)
|
||||
t.Assert(err2, nil)
|
||||
t.Assert(a2.Contains("a"), true)
|
||||
t.Assert(a2.Contains("b"), true)
|
||||
@ -336,7 +336,7 @@ func TestSet_Json(t *testing.T) {
|
||||
t.Assert(a2.Contains("e"), false)
|
||||
|
||||
var a3 gset.Set
|
||||
err := json.Unmarshal(b2, &a3)
|
||||
err := json.UnmarshalUseNumber(b2, &a3)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(a3.Contains("a"), true)
|
||||
t.Assert(a3.Contains("b"), true)
|
||||
|
||||
@ -358,7 +358,7 @@ func TestIntSet_Json(t *testing.T) {
|
||||
t.Assert(err1, err2)
|
||||
|
||||
a2 := gset.NewIntSet()
|
||||
err2 = json.Unmarshal(b2, &a2)
|
||||
err2 = json.UnmarshalUseNumber(b2, &a2)
|
||||
t.Assert(err2, nil)
|
||||
t.Assert(a2.Contains(1), true)
|
||||
t.Assert(a2.Contains(2), true)
|
||||
@ -367,7 +367,7 @@ func TestIntSet_Json(t *testing.T) {
|
||||
t.Assert(a2.Contains(5), false)
|
||||
|
||||
var a3 gset.IntSet
|
||||
err := json.Unmarshal(b2, &a3)
|
||||
err := json.UnmarshalUseNumber(b2, &a3)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(a2.Contains(1), true)
|
||||
t.Assert(a2.Contains(2), true)
|
||||
|
||||
@ -404,7 +404,7 @@ func TestStrSet_Json(t *testing.T) {
|
||||
t.Assert(err1, err2)
|
||||
|
||||
a2 := gset.NewStrSet()
|
||||
err2 = json.Unmarshal(b2, &a2)
|
||||
err2 = json.UnmarshalUseNumber(b2, &a2)
|
||||
t.Assert(err2, nil)
|
||||
t.Assert(a2.Contains("a"), true)
|
||||
t.Assert(a2.Contains("b"), true)
|
||||
@ -413,7 +413,7 @@ func TestStrSet_Json(t *testing.T) {
|
||||
t.Assert(a2.Contains("e"), false)
|
||||
|
||||
var a3 gset.StrSet
|
||||
err := json.Unmarshal(b2, &a3)
|
||||
err := json.UnmarshalUseNumber(b2, &a3)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(a3.Contains("a"), true)
|
||||
t.Assert(a3.Contains("b"), true)
|
||||
|
||||
@ -937,7 +937,7 @@ func (tree *RedBlackTree) UnmarshalJSON(b []byte) error {
|
||||
tree.comparator = gutil.ComparatorString
|
||||
}
|
||||
var data map[string]interface{}
|
||||
if err := json.Unmarshal(b, &data); err != nil {
|
||||
if err := json.UnmarshalUseNumber(b, &data); err != nil {
|
||||
return err
|
||||
}
|
||||
for k, v := range data {
|
||||
|
||||
@ -58,7 +58,7 @@ func (v *Interface) MarshalJSON() ([]byte, error) {
|
||||
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
|
||||
func (v *Interface) UnmarshalJSON(b []byte) error {
|
||||
var i interface{}
|
||||
err := json.Unmarshal(b, &i)
|
||||
err := json.UnmarshalUseNumber(b, &i)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -55,16 +55,16 @@ func Test_Bool_JSON(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var err error
|
||||
i := gtype.NewBool()
|
||||
err = json.Unmarshal([]byte("true"), &i)
|
||||
err = json.UnmarshalUseNumber([]byte("true"), &i)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(i.Val(), true)
|
||||
err = json.Unmarshal([]byte("false"), &i)
|
||||
err = json.UnmarshalUseNumber([]byte("false"), &i)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(i.Val(), false)
|
||||
err = json.Unmarshal([]byte("1"), &i)
|
||||
err = json.UnmarshalUseNumber([]byte("1"), &i)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(i.Val(), true)
|
||||
err = json.Unmarshal([]byte("0"), &i)
|
||||
err = json.UnmarshalUseNumber([]byte("0"), &i)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(i.Val(), false)
|
||||
})
|
||||
@ -78,7 +78,7 @@ func Test_Bool_JSON(t *testing.T) {
|
||||
t.Assert(b1, b2)
|
||||
|
||||
i2 := gtype.NewBool()
|
||||
err := json.Unmarshal(b2, &i2)
|
||||
err := json.UnmarshalUseNumber(b2, &i2)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(i2.Val(), i.Val())
|
||||
})
|
||||
@ -91,7 +91,7 @@ func Test_Bool_JSON(t *testing.T) {
|
||||
t.Assert(b1, b2)
|
||||
|
||||
i2 := gtype.NewBool()
|
||||
err := json.Unmarshal(b2, &i2)
|
||||
err := json.UnmarshalUseNumber(b2, &i2)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(i2.Val(), i.Val())
|
||||
})
|
||||
|
||||
@ -53,7 +53,7 @@ func Test_Byte_JSON(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var err error
|
||||
i := gtype.NewByte()
|
||||
err = json.Unmarshal([]byte("49"), &i)
|
||||
err = json.UnmarshalUseNumber([]byte("49"), &i)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(i.Val(), "49")
|
||||
})
|
||||
|
||||
@ -39,7 +39,7 @@ func Test_Bytes_JSON(t *testing.T) {
|
||||
t.Assert(b1, b2)
|
||||
|
||||
i2 := gtype.NewBytes()
|
||||
err := json.Unmarshal(b2, &i2)
|
||||
err := json.UnmarshalUseNumber(b2, &i2)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(i2.Val(), b)
|
||||
})
|
||||
|
||||
@ -40,7 +40,7 @@ func Test_Float32_JSON(t *testing.T) {
|
||||
t.Assert(b1, b2)
|
||||
|
||||
i2 := gtype.NewFloat32()
|
||||
err := json.Unmarshal(b2, &i2)
|
||||
err := json.UnmarshalUseNumber(b2, &i2)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(i2.Val(), v)
|
||||
})
|
||||
|
||||
@ -38,7 +38,7 @@ func Test_Float64_JSON(t *testing.T) {
|
||||
t.Assert(b1, b2)
|
||||
|
||||
i2 := gtype.NewFloat64()
|
||||
err := json.Unmarshal(b2, &i2)
|
||||
err := json.UnmarshalUseNumber(b2, &i2)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(i2.Val(), v)
|
||||
})
|
||||
|
||||
@ -51,7 +51,7 @@ func Test_Int32_JSON(t *testing.T) {
|
||||
t.Assert(b1, b2)
|
||||
|
||||
i2 := gtype.NewInt32()
|
||||
err := json.Unmarshal(b2, &i2)
|
||||
err := json.UnmarshalUseNumber(b2, &i2)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(i2.Val(), v)
|
||||
})
|
||||
|
||||
@ -50,7 +50,7 @@ func Test_Int64_JSON(t *testing.T) {
|
||||
t.Assert(b1, b2)
|
||||
|
||||
i2 := gtype.NewInt64()
|
||||
err := json.Unmarshal(b2, &i2)
|
||||
err := json.UnmarshalUseNumber(b2, &i2)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(i2.Val(), i)
|
||||
})
|
||||
|
||||
@ -50,7 +50,7 @@ func Test_Int_JSON(t *testing.T) {
|
||||
t.Assert(b1, b2)
|
||||
|
||||
i2 := gtype.NewInt()
|
||||
err := json.Unmarshal(b2, &i2)
|
||||
err := json.UnmarshalUseNumber(b2, &i2)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(i2.Val(), v)
|
||||
})
|
||||
|
||||
@ -40,7 +40,7 @@ func Test_Interface_JSON(t *testing.T) {
|
||||
t.Assert(b1, b2)
|
||||
|
||||
i2 := gtype.New()
|
||||
err := json.Unmarshal(b2, &i2)
|
||||
err := json.UnmarshalUseNumber(b2, &i2)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(i2.Val(), s)
|
||||
})
|
||||
|
||||
@ -38,7 +38,7 @@ func Test_String_JSON(t *testing.T) {
|
||||
t.Assert(b1, b2)
|
||||
|
||||
i2 := gtype.NewString()
|
||||
err := json.Unmarshal(b2, &i2)
|
||||
err := json.UnmarshalUseNumber(b2, &i2)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(i2.Val(), s)
|
||||
})
|
||||
|
||||
@ -50,7 +50,7 @@ func Test_Uint32_JSON(t *testing.T) {
|
||||
t.Assert(b1, b2)
|
||||
|
||||
i2 := gtype.NewUint32()
|
||||
err := json.Unmarshal(b2, &i2)
|
||||
err := json.UnmarshalUseNumber(b2, &i2)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(i2.Val(), i)
|
||||
})
|
||||
|
||||
@ -55,7 +55,7 @@ func Test_Uint64_JSON(t *testing.T) {
|
||||
t.Assert(b1, b2)
|
||||
|
||||
i2 := gtype.NewUint64()
|
||||
err := json.Unmarshal(b2, &i2)
|
||||
err := json.UnmarshalUseNumber(b2, &i2)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(i2.Val(), i)
|
||||
})
|
||||
|
||||
@ -49,7 +49,7 @@ func Test_Uint_JSON(t *testing.T) {
|
||||
t.Assert(b1, b2)
|
||||
|
||||
i2 := gtype.NewUint()
|
||||
err := json.Unmarshal(b2, &i2)
|
||||
err := json.UnmarshalUseNumber(b2, &i2)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(i2.Val(), i)
|
||||
})
|
||||
|
||||
@ -189,7 +189,7 @@ func (v *Var) MarshalJSON() ([]byte, error) {
|
||||
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
|
||||
func (v *Var) UnmarshalJSON(b []byte) error {
|
||||
var i interface{}
|
||||
err := json.Unmarshal(b, &i)
|
||||
err := json.UnmarshalUseNumber(b, &i)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -41,7 +41,7 @@ func TestVar_Json(t *testing.T) {
|
||||
b, err := json.Marshal(s)
|
||||
t.Assert(err, nil)
|
||||
|
||||
err = json.Unmarshal(b, v)
|
||||
err = json.UnmarshalUseNumber(b, v)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(v.String(), s)
|
||||
})
|
||||
@ -52,7 +52,7 @@ func TestVar_Json(t *testing.T) {
|
||||
b, err := json.Marshal(s)
|
||||
t.Assert(err, nil)
|
||||
|
||||
err = json.Unmarshal(b, &v)
|
||||
err = json.UnmarshalUseNumber(b, &v)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(v.String(), s)
|
||||
})
|
||||
|
||||
@ -11,9 +11,10 @@ import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/errors/gerror"
|
||||
"github.com/gogf/gf/os/gcmd"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/container/gvar"
|
||||
"github.com/gogf/gf/internal/intlog"
|
||||
@ -37,6 +38,7 @@ type DB interface {
|
||||
// relational databases but also for NoSQL databases in the future. The name
|
||||
// "Table" is not proper for that purpose any more.
|
||||
// Also see Core.Table.
|
||||
// Deprecated.
|
||||
Table(tableNameOrStruct ...interface{}) *Model
|
||||
|
||||
// Model creates and returns a new ORM model from given schema.
|
||||
@ -50,6 +52,9 @@ type DB interface {
|
||||
// Also see Core.Model.
|
||||
Model(tableNameOrStruct ...interface{}) *Model
|
||||
|
||||
// Raw creates and returns a model based on a raw sql not a table.
|
||||
Raw(rawSql string, args ...interface{}) *Model
|
||||
|
||||
// Schema creates and returns a schema.
|
||||
// Also see Core.Schema.
|
||||
Schema(schema string) *Schema
|
||||
@ -82,15 +87,11 @@ type DB interface {
|
||||
// Common APIs for CURD.
|
||||
// ===========================================================================
|
||||
|
||||
Insert(table string, data interface{}, batch ...int) (sql.Result, error) // See Core.Insert.
|
||||
InsertIgnore(table string, data interface{}, batch ...int) (sql.Result, error) // See Core.InsertIgnore.
|
||||
Replace(table string, data interface{}, batch ...int) (sql.Result, error) // See Core.Replace.
|
||||
Save(table string, data interface{}, batch ...int) (sql.Result, error) // See Core.Save.
|
||||
|
||||
BatchInsert(table string, list interface{}, batch ...int) (sql.Result, error) // See Core.BatchInsert.
|
||||
BatchReplace(table string, list interface{}, batch ...int) (sql.Result, error) // See Core.BatchReplace.
|
||||
BatchSave(table string, list interface{}, batch ...int) (sql.Result, error) // See Core.BatchSave.
|
||||
|
||||
Insert(table string, data interface{}, batch ...int) (sql.Result, error) // See Core.Insert.
|
||||
InsertIgnore(table string, data interface{}, batch ...int) (sql.Result, error) // See Core.InsertIgnore.
|
||||
InsertAndGetId(table string, data interface{}, batch ...int) (int64, error) // See Core.InsertAndGetId.
|
||||
Replace(table string, data interface{}, batch ...int) (sql.Result, error) // See Core.Replace.
|
||||
Save(table string, data interface{}, batch ...int) (sql.Result, error) // See Core.Save.
|
||||
Update(table string, data interface{}, condition interface{}, args ...interface{}) (sql.Result, error) // See Core.Update.
|
||||
Delete(table string, condition interface{}, args ...interface{}) (sql.Result, error) // See Core.Delete.
|
||||
|
||||
@ -98,14 +99,14 @@ type DB interface {
|
||||
// Internal APIs for CURD, which can be overwrote for custom CURD implements.
|
||||
// ===========================================================================
|
||||
|
||||
DoQuery(link Link, sql string, args ...interface{}) (rows *sql.Rows, err error) // See Core.DoQuery.
|
||||
DoGetAll(link Link, sql string, args ...interface{}) (result Result, err error) // See Core.DoGetAll.
|
||||
DoExec(link Link, sql string, args ...interface{}) (result sql.Result, err error) // See Core.DoExec.
|
||||
DoPrepare(link Link, sql string) (*Stmt, error) // See Core.DoPrepare.
|
||||
DoInsert(link Link, table string, data interface{}, option int, batch ...int) (result sql.Result, err error) // See Core.DoInsert.
|
||||
DoBatchInsert(link Link, table string, list interface{}, option int, batch ...int) (result sql.Result, err error) // See Core.DoBatchInsert.
|
||||
DoUpdate(link Link, table string, data interface{}, condition string, args ...interface{}) (result sql.Result, err error) // See Core.DoUpdate.
|
||||
DoDelete(link Link, table string, condition string, args ...interface{}) (result sql.Result, err error) // See Core.DoDelete.
|
||||
DoGetAll(ctx context.Context, link Link, sql string, args ...interface{}) (result Result, err error) // See Core.DoGetAll.
|
||||
DoInsert(ctx context.Context, link Link, table string, data List, option DoInsertOption) (result sql.Result, err error) // See Core.DoInsert.
|
||||
DoUpdate(ctx context.Context, link Link, table string, data interface{}, condition string, args ...interface{}) (result sql.Result, err error) // See Core.DoUpdate.
|
||||
DoDelete(ctx context.Context, link Link, table string, condition string, args ...interface{}) (result sql.Result, err error) // See Core.DoDelete.
|
||||
DoQuery(ctx context.Context, link Link, sql string, args ...interface{}) (rows *sql.Rows, err error) // See Core.DoQuery.
|
||||
DoExec(ctx context.Context, link Link, sql string, args ...interface{}) (result sql.Result, err error) // See Core.DoExec.
|
||||
DoCommit(ctx context.Context, link Link, sql string, args []interface{}) (newSql string, newArgs []interface{}) // See Core.DoCommit.
|
||||
DoPrepare(ctx context.Context, link Link, sql string) (*Stmt, error) // See Core.DoPrepare.
|
||||
|
||||
// ===========================================================================
|
||||
// Query APIs for convenience purpose.
|
||||
@ -119,13 +120,15 @@ type DB interface {
|
||||
GetStruct(objPointer interface{}, sql string, args ...interface{}) error // See Core.GetStruct.
|
||||
GetStructs(objPointerSlice interface{}, sql string, args ...interface{}) error // See Core.GetStructs.
|
||||
GetScan(objPointer interface{}, sql string, args ...interface{}) error // See Core.GetScan.
|
||||
Union(unions ...*Model) *Model // See Core.Union.
|
||||
UnionAll(unions ...*Model) *Model // See Core.UnionAll.
|
||||
|
||||
// ===========================================================================
|
||||
// Master/Slave specification support.
|
||||
// ===========================================================================
|
||||
|
||||
Master() (*sql.DB, error) // See Core.Master.
|
||||
Slave() (*sql.DB, error) // See Core.Slave.
|
||||
Master(schema ...string) (*sql.DB, error) // See Core.Master.
|
||||
Slave(schema ...string) (*sql.DB, error) // See Core.Slave.
|
||||
|
||||
// ===========================================================================
|
||||
// Ping-Pong.
|
||||
@ -138,8 +141,8 @@ type DB interface {
|
||||
// Transaction.
|
||||
// ===========================================================================
|
||||
|
||||
Begin() (*TX, error) // See Core.Begin.
|
||||
Transaction(f func(tx *TX) error) (err error) // See Core.Transaction.
|
||||
Begin() (*TX, error) // See Core.Begin.
|
||||
Transaction(ctx context.Context, f func(ctx context.Context, tx *TX) error) error // See Core.Transaction.
|
||||
|
||||
// ===========================================================================
|
||||
// Configuration methods.
|
||||
@ -165,40 +168,18 @@ type DB interface {
|
||||
// Utility methods.
|
||||
// ===========================================================================
|
||||
|
||||
GetCtx() context.Context // See Core.GetCtx.
|
||||
GetChars() (charLeft string, charRight string) // See Core.GetChars.
|
||||
GetMaster(schema ...string) (*sql.DB, error) // See Core.GetMaster.
|
||||
GetSlave(schema ...string) (*sql.DB, error) // See Core.GetSlave.
|
||||
QuoteWord(s string) string // See Core.QuoteWord.
|
||||
QuoteString(s string) string // See Core.QuoteString.
|
||||
QuotePrefixTableName(table string) string // See Core.QuotePrefixTableName.
|
||||
Tables(schema ...string) (tables []string, err error) // See Core.Tables.
|
||||
TableFields(link Link, table string, schema ...string) (map[string]*TableField, error) // See Core.TableFields.
|
||||
HasTable(name string) (bool, error) // See Core.HasTable.
|
||||
FilteredLinkInfo() string // See Core.FilteredLinkInfo.
|
||||
|
||||
// HandleSqlBeforeCommit is a hook function, which deals with the sql string before
|
||||
// it's committed to underlying driver. The parameter `link` specifies the current
|
||||
// database connection operation object. You can modify the sql string `sql` and its
|
||||
// arguments `args` as you wish before they're committed to driver.
|
||||
// Also see Core.HandleSqlBeforeCommit.
|
||||
HandleSqlBeforeCommit(link Link, sql string, args []interface{}) (string, []interface{})
|
||||
|
||||
// ===========================================================================
|
||||
// Internal methods, for internal usage purpose, you do not need consider it.
|
||||
// ===========================================================================
|
||||
|
||||
mappingAndFilterData(schema, table string, data map[string]interface{}, filter bool) (map[string]interface{}, error) // See Core.mappingAndFilterData.
|
||||
convertFieldValueToLocalValue(fieldValue interface{}, fieldType string) interface{} // See Core.convertFieldValueToLocalValue.
|
||||
convertRowsToResult(rows *sql.Rows) (Result, error) // See Core.convertRowsToResult.
|
||||
addSqlToTracing(ctx context.Context, sql *Sql) // See Core.addSqlToTracing.
|
||||
writeSqlToLogger(v *Sql) // See Core.writeSqlToLogger.
|
||||
GetCtx() context.Context // See Core.GetCtx.
|
||||
GetCore() *Core // See Core.GetCore
|
||||
GetChars() (charLeft string, charRight string) // See Core.GetChars.
|
||||
Tables(ctx context.Context, schema ...string) (tables []string, err error) // See Core.Tables.
|
||||
TableFields(ctx context.Context, table string, schema ...string) (map[string]*TableField, error) // See Core.TableFields.
|
||||
FilteredLink() string
|
||||
}
|
||||
|
||||
// Core is the base struct for database management.
|
||||
type Core struct {
|
||||
db DB // DB interface object.
|
||||
ctx context.Context // Context for chaining operation only.
|
||||
ctx context.Context // Context for chaining operation only. Do not set a default value in Core initialization.
|
||||
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.
|
||||
@ -213,16 +194,36 @@ type Driver interface {
|
||||
New(core *Core, node *ConfigNode) (DB, error)
|
||||
}
|
||||
|
||||
// Link is a common database function wrapper interface.
|
||||
type Link interface {
|
||||
Query(sql string, args ...interface{}) (*sql.Rows, error)
|
||||
Exec(sql string, args ...interface{}) (sql.Result, error)
|
||||
Prepare(sql string) (*sql.Stmt, error)
|
||||
QueryContext(ctx context.Context, sql string, args ...interface{}) (*sql.Rows, error)
|
||||
ExecContext(ctx context.Context, sql string, args ...interface{}) (sql.Result, error)
|
||||
PrepareContext(ctx context.Context, sql string) (*sql.Stmt, error)
|
||||
IsTransaction() bool
|
||||
}
|
||||
|
||||
// Sql is the sql recording struct.
|
||||
type Sql struct {
|
||||
Sql string // SQL string(may contain reserved char '?').
|
||||
Type string // SQL operation type.
|
||||
Args []interface{} // Arguments for this sql.
|
||||
Format string // Formatted sql which contains arguments in the sql.
|
||||
Error error // Execution result.
|
||||
Start int64 // Start execution timestamp in milliseconds.
|
||||
End int64 // End execution timestamp in milliseconds.
|
||||
Group string // Group is the group name of the configuration that the sql is executed from.
|
||||
Sql string // SQL string(may contain reserved char '?').
|
||||
Type string // SQL operation type.
|
||||
Args []interface{} // Arguments for this sql.
|
||||
Format string // Formatted sql which contains arguments in the sql.
|
||||
Error error // Execution result.
|
||||
Start int64 // Start execution timestamp in milliseconds.
|
||||
End int64 // End execution timestamp in milliseconds.
|
||||
Group string // Group is the group name of the configuration that the sql is executed from.
|
||||
IsTransaction bool // IsTransaction marks whether this sql is executed in transaction.
|
||||
}
|
||||
|
||||
// DoInsertOption is the input struct for function DoInsert.
|
||||
type DoInsertOption struct {
|
||||
OnDuplicateStr string
|
||||
OnDuplicateMap map[string]interface{}
|
||||
InsertOption int // Insert operation.
|
||||
BatchCount int // Batch count for batch inserting.
|
||||
}
|
||||
|
||||
// TableField is the struct for table field.
|
||||
@ -237,16 +238,6 @@ type TableField struct {
|
||||
Comment string // Comment.
|
||||
}
|
||||
|
||||
// Link is a common database function wrapper interface.
|
||||
type Link interface {
|
||||
Query(sql string, args ...interface{}) (*sql.Rows, error)
|
||||
Exec(sql string, args ...interface{}) (sql.Result, error)
|
||||
Prepare(sql string) (*sql.Stmt, error)
|
||||
QueryContext(ctx context.Context, sql string, args ...interface{}) (*sql.Rows, error)
|
||||
ExecContext(ctx context.Context, sql string, args ...interface{}) (sql.Result, error)
|
||||
PrepareContext(ctx context.Context, sql string) (*sql.Stmt, error)
|
||||
}
|
||||
|
||||
// Counter is the type for update count.
|
||||
type Counter struct {
|
||||
Field string
|
||||
@ -263,6 +254,10 @@ type (
|
||||
)
|
||||
|
||||
const (
|
||||
queryTypeNormal = 0
|
||||
queryTypeCount = 1
|
||||
unionTypeNormal = 0
|
||||
unionTypeAll = 1
|
||||
insertOptionDefault = 0
|
||||
insertOptionReplace = 1
|
||||
insertOptionSave = 2
|
||||
@ -298,12 +293,12 @@ var (
|
||||
|
||||
// regularFieldNameRegPattern is the regular expression pattern for a string
|
||||
// which is a regular field name of table.
|
||||
regularFieldNameRegPattern = `^[\w\.\-\_]+$`
|
||||
regularFieldNameRegPattern = `^[\w\.\-]+$`
|
||||
|
||||
// regularFieldNameWithoutDotRegPattern is similar to regularFieldNameRegPattern but not allows '.'.
|
||||
// Note that, although some databases allow char '.' in the field name, but it here does not allow '.'
|
||||
// in the field name as it conflicts with "db.table.field" pattern in SOME situations.
|
||||
regularFieldNameWithoutDotRegPattern = `^[\w\-\_]+$`
|
||||
regularFieldNameWithoutDotRegPattern = `^[\w\-]+$`
|
||||
|
||||
// internalCache is the memory cache for internal usage.
|
||||
internalCache = gcache.New()
|
||||
@ -339,7 +334,7 @@ func New(group ...string) (db DB, err error) {
|
||||
defer configs.RUnlock()
|
||||
|
||||
if len(configs.config) < 1 {
|
||||
return nil, gerror.New("empty database configuration")
|
||||
return nil, gerror.New("database configuration is empty, please set the database configuration before using")
|
||||
}
|
||||
if _, ok := configs.config[groupName]; ok {
|
||||
if node, err := getConfigNodeByGroup(groupName, true); err == nil {
|
||||
@ -358,13 +353,19 @@ func New(group ...string) (db DB, err error) {
|
||||
}
|
||||
return c.db, nil
|
||||
} else {
|
||||
return nil, gerror.New(fmt.Sprintf(`unsupported database type "%s"`, node.Type))
|
||||
return nil, gerror.Newf(
|
||||
`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,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
return nil, gerror.New(fmt.Sprintf(`database configuration node "%s" is not found`, groupName))
|
||||
return nil, gerror.Newf(
|
||||
`database configuration node "%s" is not found, did you misspell group name "%s" or miss the database configuration?`,
|
||||
groupName, groupName,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -11,19 +11,23 @@ import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/errors/gerror"
|
||||
"github.com/gogf/gf/text/gstr"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/gogf/gf/errors/gerror"
|
||||
"github.com/gogf/gf/internal/utils"
|
||||
"github.com/gogf/gf/text/gstr"
|
||||
|
||||
"github.com/gogf/gf/container/gvar"
|
||||
"github.com/gogf/gf/os/gtime"
|
||||
"github.com/gogf/gf/text/gregex"
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
)
|
||||
|
||||
// GetCore returns the underlying *Core object.
|
||||
func (c *Core) GetCore() *Core {
|
||||
return c
|
||||
}
|
||||
|
||||
// Ctx is a chaining function, which creates and returns a new DB that is a shallow copy
|
||||
// of current DB object and with given context in it.
|
||||
// Note that this returned DB object can be used only once, so do not assign it to
|
||||
@ -32,6 +36,11 @@ 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
|
||||
}
|
||||
// It makes a shallow copy of current db and changes its context for next chaining operation.
|
||||
var (
|
||||
err error
|
||||
newCore = &Core{}
|
||||
@ -39,10 +48,12 @@ func (c *Core) Ctx(ctx context.Context) DB {
|
||||
)
|
||||
*newCore = *c
|
||||
newCore.ctx = ctx
|
||||
// It creates a new DB object, which is commonly a wrapper for object `Core`.
|
||||
newCore.db, err = driverMap[configNode.Type].New(newCore, configNode)
|
||||
// Seldom error, just log it.
|
||||
if err != nil {
|
||||
c.db.GetLogger().Ctx(ctx).Error(err)
|
||||
// It is really a serious error here.
|
||||
// Do not let it continue.
|
||||
panic(err)
|
||||
}
|
||||
return newCore.db
|
||||
}
|
||||
@ -53,13 +64,13 @@ func (c *Core) GetCtx() context.Context {
|
||||
if c.ctx != nil {
|
||||
return c.ctx
|
||||
}
|
||||
return context.Background()
|
||||
return context.TODO()
|
||||
}
|
||||
|
||||
// GetCtxTimeout returns the context and cancel function for specified timeout type.
|
||||
func (c *Core) GetCtxTimeout(timeoutType int, ctx context.Context) (context.Context, context.CancelFunc) {
|
||||
if ctx == nil {
|
||||
ctx = c.db.GetCtx()
|
||||
ctx = c.GetCtx()
|
||||
} else {
|
||||
ctx = context.WithValue(ctx, "WrappedByGetCtxTimeout", nil)
|
||||
}
|
||||
@ -84,183 +95,41 @@ func (c *Core) GetCtxTimeout(timeoutType int, ctx context.Context) (context.Cont
|
||||
|
||||
// Master creates and returns a connection from master node if master-slave configured.
|
||||
// It returns the default connection if master-slave not configured.
|
||||
func (c *Core) Master() (*sql.DB, error) {
|
||||
return c.getSqlDb(true, c.schema.Val())
|
||||
func (c *Core) Master(schema ...string) (*sql.DB, error) {
|
||||
useSchema := ""
|
||||
if len(schema) > 0 && schema[0] != "" {
|
||||
useSchema = schema[0]
|
||||
} else {
|
||||
useSchema = c.schema.Val()
|
||||
}
|
||||
return c.getSqlDb(true, useSchema)
|
||||
}
|
||||
|
||||
// Slave creates and returns a connection from slave node if master-slave configured.
|
||||
// It returns the default connection if master-slave not configured.
|
||||
func (c *Core) Slave() (*sql.DB, error) {
|
||||
return c.getSqlDb(false, c.schema.Val())
|
||||
}
|
||||
|
||||
// Query commits one query SQL to underlying driver and returns the execution result.
|
||||
// It is most commonly used for data querying.
|
||||
func (c *Core) Query(sql string, args ...interface{}) (rows *sql.Rows, err error) {
|
||||
link, err := c.db.Slave()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return c.db.DoQuery(link, sql, args...)
|
||||
}
|
||||
|
||||
// DoQuery commits the sql string and its arguments to underlying driver
|
||||
// through given link object and returns the execution result.
|
||||
func (c *Core) DoQuery(link Link, sql string, args ...interface{}) (rows *sql.Rows, err error) {
|
||||
sql, args = formatSql(sql, args)
|
||||
sql, args = c.db.HandleSqlBeforeCommit(link, sql, args)
|
||||
ctx := c.db.GetCtx()
|
||||
if c.GetConfig().QueryTimeout > 0 {
|
||||
ctx, _ = context.WithTimeout(ctx, c.GetConfig().QueryTimeout)
|
||||
}
|
||||
mTime1 := gtime.TimestampMilli()
|
||||
rows, err = link.QueryContext(ctx, sql, args...)
|
||||
mTime2 := gtime.TimestampMilli()
|
||||
sqlObj := &Sql{
|
||||
Sql: sql,
|
||||
Type: "DB.QueryContext",
|
||||
Args: args,
|
||||
Format: FormatSqlWithArgs(sql, args),
|
||||
Error: err,
|
||||
Start: mTime1,
|
||||
End: mTime2,
|
||||
Group: c.db.GetGroup(),
|
||||
}
|
||||
c.addSqlToTracing(ctx, sqlObj)
|
||||
if c.db.GetDebug() {
|
||||
c.writeSqlToLogger(sqlObj)
|
||||
}
|
||||
if err == nil {
|
||||
return rows, nil
|
||||
func (c *Core) Slave(schema ...string) (*sql.DB, error) {
|
||||
useSchema := ""
|
||||
if len(schema) > 0 && schema[0] != "" {
|
||||
useSchema = schema[0]
|
||||
} else {
|
||||
err = formatError(err, sql, args...)
|
||||
useSchema = c.schema.Val()
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Exec commits one query SQL to underlying driver and returns the execution result.
|
||||
// It is most commonly used for data inserting and updating.
|
||||
func (c *Core) Exec(sql string, args ...interface{}) (result sql.Result, err error) {
|
||||
link, err := c.db.Master()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return c.db.DoExec(link, sql, args...)
|
||||
}
|
||||
|
||||
// DoExec commits the sql string and its arguments to underlying driver
|
||||
// through given link object and returns the execution result.
|
||||
func (c *Core) DoExec(link Link, sql string, args ...interface{}) (result sql.Result, err error) {
|
||||
sql, args = formatSql(sql, args)
|
||||
sql, args = c.db.HandleSqlBeforeCommit(link, sql, args)
|
||||
ctx := c.db.GetCtx()
|
||||
if c.GetConfig().ExecTimeout > 0 {
|
||||
var cancelFunc context.CancelFunc
|
||||
ctx, cancelFunc = context.WithTimeout(ctx, c.GetConfig().ExecTimeout)
|
||||
defer cancelFunc()
|
||||
}
|
||||
|
||||
mTime1 := gtime.TimestampMilli()
|
||||
if !c.db.GetDryRun() {
|
||||
result, err = link.ExecContext(ctx, sql, args...)
|
||||
} else {
|
||||
result = new(SqlResult)
|
||||
}
|
||||
mTime2 := gtime.TimestampMilli()
|
||||
sqlObj := &Sql{
|
||||
Sql: sql,
|
||||
Type: "DB.ExecContext",
|
||||
Args: args,
|
||||
Format: FormatSqlWithArgs(sql, args),
|
||||
Error: err,
|
||||
Start: mTime1,
|
||||
End: mTime2,
|
||||
Group: c.db.GetGroup(),
|
||||
}
|
||||
c.addSqlToTracing(ctx, sqlObj)
|
||||
if c.db.GetDebug() {
|
||||
c.writeSqlToLogger(sqlObj)
|
||||
}
|
||||
return result, formatError(err, sql, args...)
|
||||
}
|
||||
|
||||
// Prepare creates a prepared statement for later queries or executions.
|
||||
// Multiple queries or executions may be run concurrently from the
|
||||
// returned statement.
|
||||
// The caller must call the statement's Close method
|
||||
// when the statement is no longer needed.
|
||||
//
|
||||
// The parameter `execOnMaster` specifies whether executing the sql on master node,
|
||||
// or else it executes the sql on slave node if master-slave configured.
|
||||
func (c *Core) Prepare(sql string, execOnMaster ...bool) (*Stmt, error) {
|
||||
var (
|
||||
err error
|
||||
link Link
|
||||
)
|
||||
if len(execOnMaster) > 0 && execOnMaster[0] {
|
||||
if link, err = c.db.Master(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
if link, err = c.db.Slave(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return c.db.DoPrepare(link, sql)
|
||||
}
|
||||
|
||||
// doPrepare calls prepare function on given link object and returns the statement object.
|
||||
func (c *Core) DoPrepare(link Link, sql string) (*Stmt, error) {
|
||||
ctx := c.db.GetCtx()
|
||||
if c.GetConfig().PrepareTimeout > 0 {
|
||||
// DO NOT USE cancel function in prepare statement.
|
||||
ctx, _ = context.WithTimeout(ctx, c.GetConfig().PrepareTimeout)
|
||||
}
|
||||
var (
|
||||
mTime1 = gtime.TimestampMilli()
|
||||
stmt, err = link.PrepareContext(ctx, sql)
|
||||
mTime2 = gtime.TimestampMilli()
|
||||
sqlObj = &Sql{
|
||||
Sql: sql,
|
||||
Type: "DB.PrepareContext",
|
||||
Args: nil,
|
||||
Format: FormatSqlWithArgs(sql, nil),
|
||||
Error: err,
|
||||
Start: mTime1,
|
||||
End: mTime2,
|
||||
Group: c.db.GetGroup(),
|
||||
}
|
||||
)
|
||||
c.addSqlToTracing(ctx, sqlObj)
|
||||
if c.db.GetDebug() {
|
||||
c.writeSqlToLogger(sqlObj)
|
||||
}
|
||||
return &Stmt{
|
||||
Stmt: stmt,
|
||||
core: c,
|
||||
sql: sql,
|
||||
}, err
|
||||
return c.getSqlDb(false, useSchema)
|
||||
}
|
||||
|
||||
// GetAll queries and returns data records from database.
|
||||
func (c *Core) GetAll(sql string, args ...interface{}) (Result, error) {
|
||||
return c.db.DoGetAll(nil, sql, args...)
|
||||
return c.db.DoGetAll(c.GetCtx(), nil, sql, args...)
|
||||
}
|
||||
|
||||
// DoGetAll queries and returns data records from database.
|
||||
func (c *Core) DoGetAll(link Link, sql string, args ...interface{}) (result Result, err error) {
|
||||
if link == nil {
|
||||
link, err = c.db.Slave()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
rows, err := c.db.DoQuery(link, sql, args...)
|
||||
func (c *Core) DoGetAll(ctx context.Context, link Link, sql string, args ...interface{}) (result Result, err error) {
|
||||
rows, err := c.db.DoQuery(ctx, link, sql, args...)
|
||||
if err != nil || rows == nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
return c.db.convertRowsToResult(rows)
|
||||
return c.convertRowsToResult(rows)
|
||||
}
|
||||
|
||||
// GetOne queries and returns one record from database.
|
||||
@ -278,7 +147,7 @@ func (c *Core) GetOne(sql string, args ...interface{}) (Record, error) {
|
||||
// GetArray queries and returns data values as slice from database.
|
||||
// Note that if there are multiple columns in the result, it returns just one column values randomly.
|
||||
func (c *Core) GetArray(sql string, args ...interface{}) ([]Value, error) {
|
||||
all, err := c.db.DoGetAll(nil, sql, args...)
|
||||
all, err := c.db.DoGetAll(c.GetCtx(), nil, sql, args...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -355,6 +224,39 @@ func (c *Core) GetCount(sql string, args ...interface{}) (int, error) {
|
||||
return value.Int(), nil
|
||||
}
|
||||
|
||||
// Union does "(SELECT xxx FROM xxx) UNION (SELECT xxx FROM xxx) ..." statement.
|
||||
func (c *Core) Union(unions ...*Model) *Model {
|
||||
return c.doUnion(unionTypeNormal, unions...)
|
||||
}
|
||||
|
||||
// UnionAll does "(SELECT xxx FROM xxx) UNION ALL (SELECT xxx FROM xxx) ..." statement.
|
||||
func (c *Core) UnionAll(unions ...*Model) *Model {
|
||||
return c.doUnion(unionTypeAll, unions...)
|
||||
}
|
||||
|
||||
func (c *Core) doUnion(unionType int, unions ...*Model) *Model {
|
||||
var (
|
||||
unionTypeStr string
|
||||
composedSqlStr string
|
||||
composedArgs = make([]interface{}, 0)
|
||||
)
|
||||
if unionType == unionTypeAll {
|
||||
unionTypeStr = "UNION ALL"
|
||||
} else {
|
||||
unionTypeStr = "UNION"
|
||||
}
|
||||
for _, v := range unions {
|
||||
sqlWithHolder, holderArgs := v.getFormattedSqlAndArgs(queryTypeNormal, false)
|
||||
if composedSqlStr == "" {
|
||||
composedSqlStr += fmt.Sprintf(`(%s)`, sqlWithHolder)
|
||||
} else {
|
||||
composedSqlStr += fmt.Sprintf(` %s (%s)`, unionTypeStr, sqlWithHolder)
|
||||
}
|
||||
composedArgs = append(composedArgs, holderArgs...)
|
||||
}
|
||||
return c.db.Raw(composedSqlStr, composedArgs...)
|
||||
}
|
||||
|
||||
// PingMaster pings the master node to check authentication or keeps the connection alive.
|
||||
func (c *Core) PingMaster() error {
|
||||
if master, err := c.db.Master(); err != nil {
|
||||
@ -373,65 +275,6 @@ func (c *Core) PingSlave() error {
|
||||
}
|
||||
}
|
||||
|
||||
// Begin starts and returns the transaction object.
|
||||
// You should call Commit or Rollback functions of the transaction object
|
||||
// if you no longer use the transaction. Commit or Rollback functions will also
|
||||
// close the transaction automatically.
|
||||
func (c *Core) Begin() (*TX, error) {
|
||||
if master, err := c.db.Master(); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
//ctx := c.db.GetCtx()
|
||||
//if c.GetConfig().TranTimeout > 0 {
|
||||
// var cancelFunc context.CancelFunc
|
||||
// ctx, cancelFunc = context.WithTimeout(ctx, c.GetConfig().TranTimeout)
|
||||
// defer cancelFunc()
|
||||
//}
|
||||
if tx, err := master.Begin(); err == nil {
|
||||
return &TX{
|
||||
db: c.db,
|
||||
tx: tx,
|
||||
master: master,
|
||||
}, nil
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Transaction wraps the transaction logic using function `f`.
|
||||
// It rollbacks the transaction and returns the error from function `f` if
|
||||
// it returns non-nil error. It commits the transaction and returns nil if
|
||||
// function `f` returns nil.
|
||||
//
|
||||
// Note that, you should not Commit or Rollback the transaction in function `f`
|
||||
// as it is automatically handled by this function.
|
||||
func (c *Core) Transaction(f func(tx *TX) error) (err error) {
|
||||
var tx *TX
|
||||
tx, err = c.db.Begin()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
if err == nil {
|
||||
if e := recover(); e != nil {
|
||||
err = fmt.Errorf("%v", e)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
if e := tx.Rollback(); e != nil {
|
||||
err = e
|
||||
}
|
||||
} else {
|
||||
if e := tx.Commit(); e != nil {
|
||||
err = e
|
||||
}
|
||||
}
|
||||
}()
|
||||
err = f(tx)
|
||||
return
|
||||
}
|
||||
|
||||
// Insert does "INSERT INTO ..." statement for the table.
|
||||
// If there's already one unique record of the data in the table, it returns error.
|
||||
//
|
||||
@ -464,6 +307,14 @@ func (c *Core) InsertIgnore(table string, data interface{}, batch ...int) (sql.R
|
||||
return c.Model(table).Data(data).InsertIgnore()
|
||||
}
|
||||
|
||||
// InsertAndGetId performs action Insert and returns the last insert id that automatically generated.
|
||||
func (c *Core) InsertAndGetId(table string, data interface{}, batch ...int) (int64, error) {
|
||||
if len(batch) > 0 {
|
||||
return c.Model(table).Data(data).Batch(batch[0]).InsertAndGetId()
|
||||
}
|
||||
return c.Model(table).Data(data).InsertAndGetId()
|
||||
}
|
||||
|
||||
// Replace does "REPLACE INTO ..." statement for the table.
|
||||
// If there's already one unique record of the data in the table, it deletes the record
|
||||
// and inserts a new one.
|
||||
@ -501,7 +352,7 @@ func (c *Core) Save(table string, data interface{}, batch ...int) (sql.Result, e
|
||||
return c.Model(table).Data(data).Save()
|
||||
}
|
||||
|
||||
// doInsert inserts or updates data for given table.
|
||||
// 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.
|
||||
// Eg:
|
||||
@ -513,185 +364,15 @@ func (c *Core) Save(table string, data interface{}, batch ...int) (sql.Result, e
|
||||
// 1: replace: if there's unique/primary key in the data, it deletes it from table and inserts a new one;
|
||||
// 2: save: if there's unique/primary key in the data, it updates it or else inserts a new one;
|
||||
// 3: ignore: if there's unique/primary key in the data, it ignores the inserting;
|
||||
func (c *Core) DoInsert(link Link, table string, data interface{}, option int, batch ...int) (result sql.Result, err error) {
|
||||
table = c.db.QuotePrefixTableName(table)
|
||||
func (c *Core) DoInsert(ctx context.Context, link Link, table string, list List, option DoInsertOption) (result sql.Result, err error) {
|
||||
var (
|
||||
fields []string
|
||||
values []string
|
||||
params []interface{}
|
||||
dataMap Map
|
||||
reflectValue = reflect.ValueOf(data)
|
||||
reflectKind = reflectValue.Kind()
|
||||
keys []string // Field names.
|
||||
values []string // Value holder string array, like: (?,?,?)
|
||||
params []interface{} // Values that will be committed to underlying database driver.
|
||||
onDuplicateStr string // onDuplicateStr is used in "ON DUPLICATE KEY UPDATE" statement.
|
||||
)
|
||||
if reflectKind == reflect.Ptr {
|
||||
reflectValue = reflectValue.Elem()
|
||||
reflectKind = reflectValue.Kind()
|
||||
}
|
||||
switch reflectKind {
|
||||
case reflect.Slice, reflect.Array:
|
||||
return c.db.DoBatchInsert(link, table, data, option, batch...)
|
||||
case reflect.Struct:
|
||||
if _, ok := data.(apiInterfaces); ok {
|
||||
return c.db.DoBatchInsert(link, table, data, option, batch...)
|
||||
} else {
|
||||
dataMap = ConvertDataForTableRecord(data)
|
||||
}
|
||||
case reflect.Map:
|
||||
dataMap = ConvertDataForTableRecord(data)
|
||||
default:
|
||||
return result, gerror.New(fmt.Sprint("unsupported data type:", reflectKind))
|
||||
}
|
||||
if len(dataMap) == 0 {
|
||||
return nil, gerror.New("data cannot be empty")
|
||||
}
|
||||
var (
|
||||
charL, charR = c.db.GetChars()
|
||||
operation = GetInsertOperationByOption(option)
|
||||
updateStr = ""
|
||||
)
|
||||
for k, v := range dataMap {
|
||||
fields = append(fields, charL+k+charR)
|
||||
if s, ok := v.(Raw); ok {
|
||||
values = append(values, gconv.String(s))
|
||||
} else {
|
||||
values = append(values, "?")
|
||||
params = append(params, v)
|
||||
}
|
||||
}
|
||||
if option == insertOptionSave {
|
||||
for k, _ := range dataMap {
|
||||
// If it's SAVE operation,
|
||||
// do not automatically update the creating time.
|
||||
if c.isSoftCreatedFiledName(k) {
|
||||
continue
|
||||
}
|
||||
if len(updateStr) > 0 {
|
||||
updateStr += ","
|
||||
}
|
||||
updateStr += fmt.Sprintf(
|
||||
"%s%s%s=VALUES(%s%s%s)",
|
||||
charL, k, charR,
|
||||
charL, k, charR,
|
||||
)
|
||||
}
|
||||
updateStr = fmt.Sprintf("ON DUPLICATE KEY UPDATE %s", updateStr)
|
||||
}
|
||||
if link == nil {
|
||||
if link, err = c.db.Master(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return c.db.DoExec(
|
||||
link,
|
||||
fmt.Sprintf(
|
||||
"%s INTO %s(%s) VALUES(%s) %s",
|
||||
operation, table, strings.Join(fields, ","),
|
||||
strings.Join(values, ","), updateStr,
|
||||
),
|
||||
params...,
|
||||
)
|
||||
}
|
||||
|
||||
// BatchInsert batch inserts data.
|
||||
// The parameter `list` must be type of slice of map or struct.
|
||||
func (c *Core) BatchInsert(table string, list interface{}, batch ...int) (sql.Result, error) {
|
||||
if len(batch) > 0 {
|
||||
return c.Model(table).Data(list).Batch(batch[0]).Insert()
|
||||
}
|
||||
return c.Model(table).Data(list).Insert()
|
||||
}
|
||||
|
||||
// BatchInsertIgnore batch inserts data with ignore option.
|
||||
// The parameter `list` must be type of slice of map or struct.
|
||||
func (c *Core) BatchInsertIgnore(table string, list interface{}, batch ...int) (sql.Result, error) {
|
||||
if len(batch) > 0 {
|
||||
return c.Model(table).Data(list).Batch(batch[0]).InsertIgnore()
|
||||
}
|
||||
return c.Model(table).Data(list).InsertIgnore()
|
||||
}
|
||||
|
||||
// BatchReplace batch replaces data.
|
||||
// The parameter `list` must be type of slice of map or struct.
|
||||
func (c *Core) BatchReplace(table string, list interface{}, batch ...int) (sql.Result, error) {
|
||||
if len(batch) > 0 {
|
||||
return c.Model(table).Data(list).Batch(batch[0]).Replace()
|
||||
}
|
||||
return c.Model(table).Data(list).Replace()
|
||||
}
|
||||
|
||||
// BatchSave batch replaces data.
|
||||
// The parameter `list` must be type of slice of map or struct.
|
||||
func (c *Core) BatchSave(table string, list interface{}, batch ...int) (sql.Result, error) {
|
||||
if len(batch) > 0 {
|
||||
return c.Model(table).Data(list).Batch(batch[0]).Save()
|
||||
}
|
||||
return c.Model(table).Data(list).Save()
|
||||
}
|
||||
|
||||
// DoBatchInsert batch inserts/replaces/saves data.
|
||||
// This function is usually used for custom interface definition, you do not need call it manually.
|
||||
func (c *Core) DoBatchInsert(link Link, table string, list interface{}, option int, batch ...int) (result sql.Result, err error) {
|
||||
table = c.db.QuotePrefixTableName(table)
|
||||
var (
|
||||
keys []string // Field names.
|
||||
values []string // Value holder string array, like: (?,?,?)
|
||||
params []interface{} // Values that will be committed to underlying database driver.
|
||||
listMap List // The data list that passed from caller.
|
||||
)
|
||||
switch value := list.(type) {
|
||||
case Result:
|
||||
listMap = value.List()
|
||||
case Record:
|
||||
listMap = List{value.Map()}
|
||||
case List:
|
||||
listMap = value
|
||||
case Map:
|
||||
listMap = List{value}
|
||||
default:
|
||||
var (
|
||||
rv = reflect.ValueOf(list)
|
||||
kind = rv.Kind()
|
||||
)
|
||||
if kind == reflect.Ptr {
|
||||
rv = rv.Elem()
|
||||
kind = rv.Kind()
|
||||
}
|
||||
switch kind {
|
||||
// If it's slice type, it then converts it to List type.
|
||||
case reflect.Slice, reflect.Array:
|
||||
listMap = make(List, rv.Len())
|
||||
for i := 0; i < rv.Len(); i++ {
|
||||
listMap[i] = ConvertDataForTableRecord(rv.Index(i).Interface())
|
||||
}
|
||||
case reflect.Map:
|
||||
listMap = List{ConvertDataForTableRecord(value)}
|
||||
case reflect.Struct:
|
||||
if v, ok := value.(apiInterfaces); ok {
|
||||
var (
|
||||
array = v.Interfaces()
|
||||
list = make(List, len(array))
|
||||
)
|
||||
for i := 0; i < len(array); i++ {
|
||||
list[i] = ConvertDataForTableRecord(array[i])
|
||||
}
|
||||
listMap = list
|
||||
} else {
|
||||
listMap = List{ConvertDataForTableRecord(value)}
|
||||
}
|
||||
default:
|
||||
return result, gerror.New(fmt.Sprint("unsupported list type:", kind))
|
||||
}
|
||||
}
|
||||
if len(listMap) < 1 {
|
||||
return result, gerror.New("data list cannot be empty")
|
||||
}
|
||||
if link == nil {
|
||||
if link, err = c.db.Master(); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
// Handle the field names and place holders.
|
||||
for k, _ := range listMap[0] {
|
||||
for k, _ := range list[0] {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
// Prepare the batch result pointer.
|
||||
@ -699,59 +380,36 @@ func (c *Core) DoBatchInsert(link Link, table string, list interface{}, option i
|
||||
charL, charR = c.db.GetChars()
|
||||
batchResult = new(SqlResult)
|
||||
keysStr = charL + strings.Join(keys, charR+","+charL) + charR
|
||||
operation = GetInsertOperationByOption(option)
|
||||
updateStr = ""
|
||||
operation = GetInsertOperationByOption(option.InsertOption)
|
||||
)
|
||||
if option == insertOptionSave {
|
||||
for _, k := range keys {
|
||||
// If it's SAVE operation,
|
||||
// do not automatically update the creating time.
|
||||
if c.isSoftCreatedFiledName(k) {
|
||||
continue
|
||||
}
|
||||
if len(updateStr) > 0 {
|
||||
updateStr += ","
|
||||
}
|
||||
updateStr += fmt.Sprintf(
|
||||
"%s%s%s=VALUES(%s%s%s)",
|
||||
charL, k, charR,
|
||||
charL, k, charR,
|
||||
)
|
||||
}
|
||||
updateStr = fmt.Sprintf("ON DUPLICATE KEY UPDATE %s", updateStr)
|
||||
}
|
||||
batchNum := defaultBatchNumber
|
||||
if len(batch) > 0 && batch[0] > 0 {
|
||||
batchNum = batch[0]
|
||||
if option.InsertOption == insertOptionSave {
|
||||
onDuplicateStr = c.formatOnDuplicate(keys, option)
|
||||
}
|
||||
var (
|
||||
listMapLen = len(listMap)
|
||||
listLength = len(list)
|
||||
valueHolder = make([]string, 0)
|
||||
)
|
||||
for i := 0; i < listMapLen; i++ {
|
||||
for i := 0; i < listLength; i++ {
|
||||
values = values[:0]
|
||||
// Note that the map type is unordered,
|
||||
// so it should use slice+key to retrieve the value.
|
||||
for _, k := range keys {
|
||||
if s, ok := listMap[i][k].(Raw); ok {
|
||||
if s, ok := list[i][k].(Raw); ok {
|
||||
values = append(values, gconv.String(s))
|
||||
} else {
|
||||
values = append(values, "?")
|
||||
params = append(params, listMap[i][k])
|
||||
params = append(params, list[i][k])
|
||||
}
|
||||
}
|
||||
valueHolder = append(valueHolder, "("+gstr.Join(values, ",")+")")
|
||||
if len(valueHolder) == batchNum || (i == listMapLen-1 && len(valueHolder) > 0) {
|
||||
r, err := c.db.DoExec(
|
||||
link,
|
||||
fmt.Sprintf(
|
||||
"%s INTO %s(%s) VALUES%s %s",
|
||||
operation, table, keysStr,
|
||||
gstr.Join(valueHolder, ","),
|
||||
updateStr,
|
||||
),
|
||||
params...,
|
||||
)
|
||||
// Batch package checks: It meets the batch number or it is the last element.
|
||||
if len(valueHolder) == option.BatchCount || (i == listLength-1 && len(valueHolder) > 0) {
|
||||
r, err := c.db.DoExec(ctx, link, fmt.Sprintf(
|
||||
"%s INTO %s(%s) VALUES%s %s",
|
||||
operation, c.QuotePrefixTableName(table), keysStr,
|
||||
gstr.Join(valueHolder, ","),
|
||||
onDuplicateStr,
|
||||
), params...)
|
||||
if err != nil {
|
||||
return r, err
|
||||
}
|
||||
@ -768,6 +426,52 @@ func (c *Core) DoBatchInsert(link Link, table string, list interface{}, option i
|
||||
return batchResult, nil
|
||||
}
|
||||
|
||||
func (c *Core) formatOnDuplicate(columns []string, option DoInsertOption) string {
|
||||
var (
|
||||
onDuplicateStr string
|
||||
)
|
||||
if option.OnDuplicateStr != "" {
|
||||
onDuplicateStr = option.OnDuplicateStr
|
||||
} else if len(option.OnDuplicateMap) > 0 {
|
||||
for k, v := range option.OnDuplicateMap {
|
||||
if len(onDuplicateStr) > 0 {
|
||||
onDuplicateStr += ","
|
||||
}
|
||||
switch v.(type) {
|
||||
case Raw, *Raw:
|
||||
onDuplicateStr += fmt.Sprintf(
|
||||
"%s=%s",
|
||||
c.QuoteWord(k),
|
||||
v,
|
||||
)
|
||||
default:
|
||||
onDuplicateStr += fmt.Sprintf(
|
||||
"%s=VALUES(%s)",
|
||||
c.QuoteWord(k),
|
||||
c.QuoteWord(gconv.String(v)),
|
||||
)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for _, column := range columns {
|
||||
// If it's SAVE operation,
|
||||
// do not automatically update the creating time.
|
||||
if c.isSoftCreatedFilledName(column) {
|
||||
continue
|
||||
}
|
||||
if len(onDuplicateStr) > 0 {
|
||||
onDuplicateStr += ","
|
||||
}
|
||||
onDuplicateStr += fmt.Sprintf(
|
||||
"%s=VALUES(%s)",
|
||||
c.QuoteWord(column),
|
||||
c.QuoteWord(column),
|
||||
)
|
||||
}
|
||||
}
|
||||
return fmt.Sprintf("ON DUPLICATE KEY UPDATE %s", onDuplicateStr)
|
||||
}
|
||||
|
||||
// Update does "UPDATE ... " statement for the table.
|
||||
//
|
||||
// The parameter `data` can be type of string/map/gmap/struct/*struct, etc.
|
||||
@ -786,10 +490,10 @@ func (c *Core) Update(table string, data interface{}, condition interface{}, arg
|
||||
return c.Model(table).Data(data).Where(condition, args...).Update()
|
||||
}
|
||||
|
||||
// doUpdate does "UPDATE ... " statement for the table.
|
||||
// DoUpdate does "UPDATE ... " statement for the table.
|
||||
// This function is usually used for custom interface definition, you do not need call it manually.
|
||||
func (c *Core) DoUpdate(link Link, table string, data interface{}, condition string, args ...interface{}) (result sql.Result, err error) {
|
||||
table = c.db.QuotePrefixTableName(table)
|
||||
func (c *Core) DoUpdate(ctx context.Context, link Link, table string, data interface{}, condition string, args ...interface{}) (result sql.Result, err error) {
|
||||
table = c.QuotePrefixTableName(table)
|
||||
var (
|
||||
rv = reflect.ValueOf(data)
|
||||
kind = rv.Kind()
|
||||
@ -814,7 +518,7 @@ func (c *Core) DoUpdate(link Link, table string, data interface{}, condition str
|
||||
if value.Value != 0 {
|
||||
column := k
|
||||
if value.Field != "" {
|
||||
column = c.db.QuoteWord(value.Field)
|
||||
column = c.QuoteWord(value.Field)
|
||||
}
|
||||
fields = append(fields, fmt.Sprintf("%s=%s+?", column, column))
|
||||
params = append(params, value.Value)
|
||||
@ -823,16 +527,16 @@ func (c *Core) DoUpdate(link Link, table string, data interface{}, condition str
|
||||
if value.Value != 0 {
|
||||
column := k
|
||||
if value.Field != "" {
|
||||
column = c.db.QuoteWord(value.Field)
|
||||
column = c.QuoteWord(value.Field)
|
||||
}
|
||||
fields = append(fields, fmt.Sprintf("%s=%s+?", column, column))
|
||||
params = append(params, value.Value)
|
||||
}
|
||||
default:
|
||||
if s, ok := v.(Raw); ok {
|
||||
fields = append(fields, c.db.QuoteWord(k)+"="+gconv.String(s))
|
||||
fields = append(fields, c.QuoteWord(k)+"="+gconv.String(s))
|
||||
} else {
|
||||
fields = append(fields, c.db.QuoteWord(k)+"=?")
|
||||
fields = append(fields, c.QuoteWord(k)+"=?")
|
||||
params = append(params, v)
|
||||
}
|
||||
}
|
||||
@ -849,15 +553,11 @@ func (c *Core) DoUpdate(link Link, table string, data interface{}, condition str
|
||||
}
|
||||
// If no link passed, it then uses the master link.
|
||||
if link == nil {
|
||||
if link, err = c.db.Master(); err != nil {
|
||||
if link, err = c.MasterLink(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return c.db.DoExec(
|
||||
link,
|
||||
fmt.Sprintf("UPDATE %s SET %s%s", table, updates, condition),
|
||||
args...,
|
||||
)
|
||||
return c.db.DoExec(ctx, link, fmt.Sprintf("UPDATE %s SET %s%s", table, updates, condition), args...)
|
||||
}
|
||||
|
||||
// Delete does "DELETE FROM ... " statement for the table.
|
||||
@ -877,14 +577,14 @@ func (c *Core) Delete(table string, condition interface{}, args ...interface{})
|
||||
|
||||
// DoDelete does "DELETE FROM ... " statement for the table.
|
||||
// This function is usually used for custom interface definition, you do not need call it manually.
|
||||
func (c *Core) DoDelete(link Link, table string, condition string, args ...interface{}) (result sql.Result, err error) {
|
||||
func (c *Core) DoDelete(ctx context.Context, link Link, table string, condition string, args ...interface{}) (result sql.Result, err error) {
|
||||
if link == nil {
|
||||
if link, err = c.db.Master(); err != nil {
|
||||
if link, err = c.MasterLink(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
table = c.db.QuotePrefixTableName(table)
|
||||
return c.db.DoExec(link, fmt.Sprintf("DELETE FROM %s%s", table, condition), args...)
|
||||
table = c.QuotePrefixTableName(table)
|
||||
return c.db.DoExec(ctx, link, fmt.Sprintf("DELETE FROM %s%s", table, condition), args...)
|
||||
}
|
||||
|
||||
// convertRowsToResult converts underlying data record type sql.Rows to Result type.
|
||||
@ -920,7 +620,7 @@ func (c *Core) convertRowsToResult(rows *sql.Rows) (Result, error) {
|
||||
if value == nil {
|
||||
row[columnNames[i]] = gvar.New(nil)
|
||||
} else {
|
||||
row[columnNames[i]] = gvar.New(c.db.convertFieldValueToLocalValue(value, columnTypes[i]))
|
||||
row[columnNames[i]] = gvar.New(c.convertFieldValueToLocalValue(value, columnTypes[i]))
|
||||
}
|
||||
}
|
||||
records = append(records, row)
|
||||
@ -942,19 +642,25 @@ func (c *Core) MarshalJSON() ([]byte, error) {
|
||||
|
||||
// writeSqlToLogger outputs the sql object to logger.
|
||||
// It is enabled only if configuration "debug" is true.
|
||||
func (c *Core) writeSqlToLogger(v *Sql) {
|
||||
s := fmt.Sprintf("[%3d ms] [%s] %s", v.End-v.Start, v.Group, v.Format)
|
||||
if v.Error != nil {
|
||||
s += "\nError: " + v.Error.Error()
|
||||
c.logger.Ctx(c.db.GetCtx()).Error(s)
|
||||
func (c *Core) writeSqlToLogger(ctx context.Context, sql *Sql) {
|
||||
var transactionIdStr string
|
||||
if sql.IsTransaction {
|
||||
if v := ctx.Value(transactionIdForLoggerCtx); v != nil {
|
||||
transactionIdStr = fmt.Sprintf(`[%d] `, v.(uint64))
|
||||
}
|
||||
}
|
||||
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.Ctx(ctx).Error(s)
|
||||
} else {
|
||||
c.logger.Ctx(c.db.GetCtx()).Debug(s)
|
||||
c.logger.Ctx(ctx).Debug(s)
|
||||
}
|
||||
}
|
||||
|
||||
// HasTable determine whether the table name exists in the database.
|
||||
func (c *Core) HasTable(name string) (bool, error) {
|
||||
tableList, err := c.db.Tables()
|
||||
tableList, err := c.db.Tables(c.GetCtx())
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
@ -966,8 +672,8 @@ func (c *Core) HasTable(name string) (bool, error) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// isSoftCreatedFiledName checks and returns whether given filed name is an automatic-filled created time.
|
||||
func (c *Core) isSoftCreatedFiledName(fieldName string) bool {
|
||||
// isSoftCreatedFilledName checks and returns whether given filed name is an automatic-filled created time.
|
||||
func (c *Core) isSoftCreatedFilledName(fieldName string) bool {
|
||||
if fieldName == "" {
|
||||
return false
|
||||
}
|
||||
|
||||
@ -8,15 +8,12 @@ package gdb
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/os/gcache"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/os/glog"
|
||||
)
|
||||
"github.com/gogf/gf/os/gcache"
|
||||
|
||||
const (
|
||||
DefaultGroupName = "default" // Default group name.
|
||||
"github.com/gogf/gf/os/glog"
|
||||
)
|
||||
|
||||
// Config is the configuration management object.
|
||||
@ -33,13 +30,14 @@ type ConfigNode struct {
|
||||
Pass string `json:"pass"` // Authentication password.
|
||||
Name string `json:"name"` // Default used database name.
|
||||
Type string `json:"type"` // Database type: mysql, sqlite, mssql, pgsql, oracle.
|
||||
Link string `json:"link"` // (Optional) Custom link information, when it is used, configuration Host/Port/User/Pass/Name are ignored.
|
||||
Role string `json:"role"` // (Optional, "master" in default) Node role, used for master-slave mode: master, slave.
|
||||
Debug bool `json:"debug"` // (Optional) Debug mode enables debug information logging and output.
|
||||
Prefix string `json:"prefix"` // (Optional) Table prefix.
|
||||
DryRun bool `json:"dryRun"` // (Optional) Dry run, which does SELECT but no INSERT/UPDATE/DELETE statements.
|
||||
Weight int `json:"weight"` // (Optional) Weight for load balance calculating, it's useless if there's just one node.
|
||||
Charset string `json:"charset"` // (Optional, "utf8mb4" in default) Custom charset when operating on database.
|
||||
LinkInfo string `json:"link"` // (Optional) Custom link information, when it is used, configuration Host/Port/User/Pass/Name are ignored.
|
||||
Timezone string `json:"timezone"` // (Optional) Sets the time zone for displaying and interpreting time stamps.
|
||||
MaxIdleConnCount int `json:"maxIdle"` // (Optional) Max idle connection configuration for underlying connection pool.
|
||||
MaxOpenConnCount int `json:"maxOpen"` // (Optional) Max open connection configuration for underlying connection pool.
|
||||
MaxConnLifeTime time.Duration `json:"maxLifeTime"` // (Optional) Max amount of time a connection may be idle before being closed.
|
||||
@ -53,6 +51,10 @@ type ConfigNode struct {
|
||||
TimeMaintainDisabled bool `json:"timeMaintainDisabled"` // (Optional) Disable the automatic time maintaining feature.
|
||||
}
|
||||
|
||||
const (
|
||||
DefaultGroupName = "default" // Default group name.
|
||||
)
|
||||
|
||||
// configs is internal used configuration object.
|
||||
var configs struct {
|
||||
sync.RWMutex
|
||||
@ -185,7 +187,7 @@ func (node *ConfigNode) String() string {
|
||||
node.MaxIdleConnCount,
|
||||
node.MaxOpenConnCount,
|
||||
node.MaxConnLifeTime,
|
||||
node.LinkInfo,
|
||||
node.Link,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
31
database/gdb/gdb_core_link.go
Normal file
31
database/gdb/gdb_core_link.go
Normal file
@ -0,0 +1,31 @@
|
||||
// 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 (
|
||||
"database/sql"
|
||||
)
|
||||
|
||||
// dbLink is used to implement interface Link for DB.
|
||||
type dbLink struct {
|
||||
*sql.DB
|
||||
}
|
||||
|
||||
// txLink is used to implement interface Link for TX.
|
||||
type txLink struct {
|
||||
*sql.Tx
|
||||
}
|
||||
|
||||
// IsTransaction returns if current Link is a transaction.
|
||||
func (*dbLink) IsTransaction() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// IsTransaction returns if current Link is a transaction.
|
||||
func (*txLink) IsTransaction() bool {
|
||||
return true
|
||||
}
|
||||
@ -7,10 +7,11 @@
|
||||
package gdb
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/util/gutil"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/util/gutil"
|
||||
|
||||
"github.com/gogf/gf/text/gstr"
|
||||
|
||||
"github.com/gogf/gf/os/gtime"
|
||||
@ -99,7 +100,8 @@ func (c *Core) convertFieldValueToLocalValue(fieldValue interface{}, fieldType s
|
||||
|
||||
case
|
||||
"datetime",
|
||||
"timestamp":
|
||||
"timestamp",
|
||||
"timestamptz":
|
||||
if t, ok := fieldValue.(time.Time); ok {
|
||||
return gtime.NewFromTime(t)
|
||||
}
|
||||
@ -149,7 +151,7 @@ func (c *Core) convertFieldValueToLocalValue(fieldValue interface{}, fieldType s
|
||||
// mappingAndFilterData automatically mappings the map key to table field and removes
|
||||
// all key-value pairs that are not the field of given table.
|
||||
func (c *Core) mappingAndFilterData(schema, table string, data map[string]interface{}, filter bool) (map[string]interface{}, error) {
|
||||
if fieldsMap, err := c.db.TableFields(nil, table, schema); err == nil {
|
||||
if fieldsMap, err := c.db.TableFields(c.GetCtx(), table, schema); err == nil {
|
||||
fieldsKeyMap := make(map[string]interface{}, len(fieldsMap))
|
||||
for k, _ := range fieldsMap {
|
||||
fieldsKeyMap[k] = nil
|
||||
|
||||
@ -76,8 +76,8 @@ func (c *Core) addSqlToTracing(ctx context.Context, sql *Sql) {
|
||||
if c.db.GetConfig().User != "" {
|
||||
labels = append(labels, attribute.String(tracingAttrDbUser, c.db.GetConfig().User))
|
||||
}
|
||||
if filteredLinkInfo := c.db.FilteredLinkInfo(); filteredLinkInfo != "" {
|
||||
labels = append(labels, attribute.String(tracingAttrDbLink, c.db.FilteredLinkInfo()))
|
||||
if filteredLink := c.db.FilteredLink(); filteredLink != "" {
|
||||
labels = append(labels, attribute.String(tracingAttrDbLink, c.db.FilteredLink()))
|
||||
}
|
||||
if group := c.db.GetGroup(); group != "" {
|
||||
labels = append(labels, attribute.String(tracingAttrDbGroup, group))
|
||||
|
||||
@ -7,34 +7,190 @@
|
||||
package gdb
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"github.com/gogf/gf/container/gtype"
|
||||
"github.com/gogf/gf/os/gtime"
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
"reflect"
|
||||
"github.com/gogf/gf/util/guid"
|
||||
|
||||
"github.com/gogf/gf/text/gregex"
|
||||
)
|
||||
|
||||
// TX is the struct for transaction management.
|
||||
type TX struct {
|
||||
db DB // db is the current gdb database manager.
|
||||
tx *sql.Tx // tx is the raw and underlying transaction manager.
|
||||
master *sql.DB // master is the raw and underlying database manager.
|
||||
transactionCount int // transactionCount marks the times that Begins.
|
||||
db DB // db is the current gdb database manager.
|
||||
tx *sql.Tx // tx is the raw and underlying transaction manager.
|
||||
ctx context.Context // ctx is the context for this transaction only.
|
||||
master *sql.DB // master is the raw and underlying database manager.
|
||||
transactionId string // transactionId is an unique id generated by this object for this transaction.
|
||||
transactionCount int // transactionCount marks the times that Begins.
|
||||
}
|
||||
|
||||
const (
|
||||
transactionPointerPrefix = "transaction"
|
||||
transactionPointerPrefix = "transaction"
|
||||
contextTransactionKeyPrefix = "TransactionObjectForGroup_"
|
||||
transactionIdForLoggerCtx = "TransactionId"
|
||||
)
|
||||
|
||||
var (
|
||||
transactionIdGenerator = gtype.NewUint64()
|
||||
)
|
||||
|
||||
// Begin starts and returns the transaction object.
|
||||
// You should call Commit or Rollback functions of the transaction object
|
||||
// if you no longer use the transaction. Commit or Rollback functions will also
|
||||
// close the transaction automatically.
|
||||
func (c *Core) Begin() (tx *TX, err error) {
|
||||
return c.doBeginCtx(c.GetCtx())
|
||||
}
|
||||
|
||||
func (c *Core) doBeginCtx(ctx context.Context) (*TX, error) {
|
||||
if master, err := c.db.Master(); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
var (
|
||||
tx *TX
|
||||
sqlStr = "BEGIN"
|
||||
mTime1 = gtime.TimestampMilli()
|
||||
rawTx, err = master.Begin()
|
||||
mTime2 = gtime.TimestampMilli()
|
||||
sqlObj = &Sql{
|
||||
Sql: sqlStr,
|
||||
Type: "DB.Begin",
|
||||
Args: nil,
|
||||
Format: sqlStr,
|
||||
Error: err,
|
||||
Start: mTime1,
|
||||
End: mTime2,
|
||||
Group: c.db.GetGroup(),
|
||||
IsTransaction: true,
|
||||
}
|
||||
)
|
||||
if err == nil {
|
||||
tx = &TX{
|
||||
db: c.db,
|
||||
tx: rawTx,
|
||||
ctx: context.WithValue(ctx, transactionIdForLoggerCtx, transactionIdGenerator.Add(1)),
|
||||
master: master,
|
||||
transactionId: guid.S(),
|
||||
}
|
||||
ctx = tx.ctx
|
||||
}
|
||||
// Tracing and logging.
|
||||
c.addSqlToTracing(ctx, sqlObj)
|
||||
if c.db.GetDebug() {
|
||||
c.writeSqlToLogger(ctx, sqlObj)
|
||||
}
|
||||
return tx, err
|
||||
}
|
||||
}
|
||||
|
||||
// Transaction wraps the transaction logic using function `f`.
|
||||
// It rollbacks the transaction and returns the error from function `f` if
|
||||
// it returns non-nil error. It commits the transaction and returns nil if
|
||||
// function `f` returns nil.
|
||||
//
|
||||
// Note that, you should not Commit or Rollback the transaction in function `f`
|
||||
// as it is automatically handled by this function.
|
||||
func (c *Core) Transaction(ctx context.Context, f func(ctx context.Context, tx *TX) error) (err error) {
|
||||
var (
|
||||
tx *TX
|
||||
)
|
||||
if ctx == nil {
|
||||
ctx = c.GetCtx()
|
||||
}
|
||||
// Check transaction object from context.
|
||||
tx = TXFromCtx(ctx, c.db.GetGroup())
|
||||
if tx != nil {
|
||||
return tx.Transaction(ctx, f)
|
||||
}
|
||||
tx, err = c.doBeginCtx(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Inject transaction object into context.
|
||||
tx.ctx = WithTX(tx.ctx, tx)
|
||||
defer func() {
|
||||
if err == nil {
|
||||
if e := recover(); e != nil {
|
||||
err = fmt.Errorf("%v", e)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
if e := tx.Rollback(); e != nil {
|
||||
err = e
|
||||
}
|
||||
} else {
|
||||
if e := tx.Commit(); e != nil {
|
||||
err = e
|
||||
}
|
||||
}
|
||||
}()
|
||||
err = f(tx.ctx, tx)
|
||||
return
|
||||
}
|
||||
|
||||
// WithTX injects given transaction object into context and returns a new context.
|
||||
func WithTX(ctx context.Context, tx *TX) context.Context {
|
||||
if tx == nil {
|
||||
return ctx
|
||||
}
|
||||
// Check repeat injection from given.
|
||||
group := tx.db.GetGroup()
|
||||
if tx := TXFromCtx(ctx, group); tx != nil && tx.db.GetGroup() == group {
|
||||
return ctx
|
||||
}
|
||||
dbCtx := tx.db.GetCtx()
|
||||
if tx := TXFromCtx(dbCtx, group); tx != nil && tx.db.GetGroup() == group {
|
||||
return dbCtx
|
||||
}
|
||||
// Inject transaction object and id into context.
|
||||
ctx = context.WithValue(ctx, transactionKeyForContext(group), tx)
|
||||
return ctx
|
||||
}
|
||||
|
||||
// TXFromCtx retrieves and returns transaction object from context.
|
||||
// It is usually used in nested transaction feature, and it returns nil if it is not set previously.
|
||||
func TXFromCtx(ctx context.Context, group string) *TX {
|
||||
if ctx == nil {
|
||||
return nil
|
||||
}
|
||||
v := ctx.Value(transactionKeyForContext(group))
|
||||
if v != nil {
|
||||
tx := v.(*TX)
|
||||
tx.ctx = ctx
|
||||
return tx
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// transactionKeyForContext forms and returns a string for storing transaction object of certain database group into context.
|
||||
func transactionKeyForContext(group string) string {
|
||||
return contextTransactionKeyPrefix + group
|
||||
}
|
||||
|
||||
// transactionKeyForNestedPoint forms and returns the transaction key at current save point.
|
||||
func (tx *TX) transactionKeyForNestedPoint() string {
|
||||
return tx.db.GetCore().QuoteWord(transactionPointerPrefix + gconv.String(tx.transactionCount))
|
||||
}
|
||||
|
||||
// Ctx sets the context for current transaction.
|
||||
func (tx *TX) Ctx(ctx context.Context) *TX {
|
||||
tx.ctx = ctx
|
||||
return tx
|
||||
}
|
||||
|
||||
// Commit commits current transaction.
|
||||
// Note that it releases previous saved transaction point if it's in a nested transaction procedure,
|
||||
// or else it commits the hole transaction.
|
||||
func (tx *TX) Commit() error {
|
||||
if tx.transactionCount > 0 {
|
||||
tx.transactionCount--
|
||||
_, err := tx.Exec("RELEASE SAVEPOINT " + tx.transactionKey())
|
||||
_, err := tx.Exec("RELEASE SAVEPOINT " + tx.transactionKeyForNestedPoint())
|
||||
return err
|
||||
}
|
||||
var (
|
||||
@ -43,19 +199,20 @@ func (tx *TX) Commit() error {
|
||||
err = tx.tx.Commit()
|
||||
mTime2 = gtime.TimestampMilli()
|
||||
sqlObj = &Sql{
|
||||
Sql: sqlStr,
|
||||
Type: "TX.Commit",
|
||||
Args: nil,
|
||||
Format: sqlStr,
|
||||
Error: err,
|
||||
Start: mTime1,
|
||||
End: mTime2,
|
||||
Group: tx.db.GetGroup(),
|
||||
Sql: sqlStr,
|
||||
Type: "TX.Commit",
|
||||
Args: nil,
|
||||
Format: sqlStr,
|
||||
Error: err,
|
||||
Start: mTime1,
|
||||
End: mTime2,
|
||||
Group: tx.db.GetGroup(),
|
||||
IsTransaction: true,
|
||||
}
|
||||
)
|
||||
tx.db.addSqlToTracing(tx.db.GetCtx(), sqlObj)
|
||||
tx.db.GetCore().addSqlToTracing(tx.ctx, sqlObj)
|
||||
if tx.db.GetDebug() {
|
||||
tx.db.writeSqlToLogger(sqlObj)
|
||||
tx.db.GetCore().writeSqlToLogger(tx.ctx, sqlObj)
|
||||
}
|
||||
return err
|
||||
}
|
||||
@ -66,7 +223,7 @@ func (tx *TX) Commit() error {
|
||||
func (tx *TX) Rollback() error {
|
||||
if tx.transactionCount > 0 {
|
||||
tx.transactionCount--
|
||||
_, err := tx.Exec("ROLLBACK TO SAVEPOINT " + tx.transactionKey())
|
||||
_, err := tx.Exec("ROLLBACK TO SAVEPOINT " + tx.transactionKeyForNestedPoint())
|
||||
return err
|
||||
}
|
||||
var (
|
||||
@ -75,26 +232,27 @@ func (tx *TX) Rollback() error {
|
||||
err = tx.tx.Rollback()
|
||||
mTime2 = gtime.TimestampMilli()
|
||||
sqlObj = &Sql{
|
||||
Sql: sqlStr,
|
||||
Type: "TX.Rollback",
|
||||
Args: nil,
|
||||
Format: sqlStr,
|
||||
Error: err,
|
||||
Start: mTime1,
|
||||
End: mTime2,
|
||||
Group: tx.db.GetGroup(),
|
||||
Sql: sqlStr,
|
||||
Type: "TX.Rollback",
|
||||
Args: nil,
|
||||
Format: sqlStr,
|
||||
Error: err,
|
||||
Start: mTime1,
|
||||
End: mTime2,
|
||||
Group: tx.db.GetGroup(),
|
||||
IsTransaction: true,
|
||||
}
|
||||
)
|
||||
tx.db.addSqlToTracing(tx.db.GetCtx(), sqlObj)
|
||||
tx.db.GetCore().addSqlToTracing(tx.ctx, sqlObj)
|
||||
if tx.db.GetDebug() {
|
||||
tx.db.writeSqlToLogger(sqlObj)
|
||||
tx.db.GetCore().writeSqlToLogger(tx.ctx, sqlObj)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Begin starts a nested transaction procedure.
|
||||
func (tx *TX) Begin() error {
|
||||
_, err := tx.Exec("SAVEPOINT " + tx.transactionKey())
|
||||
_, err := tx.Exec("SAVEPOINT " + tx.transactionKeyForNestedPoint())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -105,22 +263,17 @@ func (tx *TX) Begin() error {
|
||||
// SavePoint performs `SAVEPOINT xxx` SQL statement that saves transaction at current point.
|
||||
// The parameter `point` specifies the point name that will be saved to server.
|
||||
func (tx *TX) SavePoint(point string) error {
|
||||
_, err := tx.Exec("SAVEPOINT " + tx.db.QuoteWord(point))
|
||||
_, err := tx.Exec("SAVEPOINT " + tx.db.GetCore().QuoteWord(point))
|
||||
return err
|
||||
}
|
||||
|
||||
// RollbackTo performs `ROLLBACK TO SAVEPOINT xxx` SQL statement that rollbacks to specified saved transaction.
|
||||
// The parameter `point` specifies the point name that was saved previously.
|
||||
func (tx *TX) RollbackTo(point string) error {
|
||||
_, err := tx.Exec("ROLLBACK TO SAVEPOINT " + tx.db.QuoteWord(point))
|
||||
_, err := tx.Exec("ROLLBACK TO SAVEPOINT " + tx.db.GetCore().QuoteWord(point))
|
||||
return err
|
||||
}
|
||||
|
||||
// transactionKey forms and returns the transaction key at current save point.
|
||||
func (tx *TX) transactionKey() string {
|
||||
return tx.db.QuoteWord(transactionPointerPrefix + gconv.String(tx.transactionCount))
|
||||
}
|
||||
|
||||
// Transaction wraps the transaction logic using function `f`.
|
||||
// It rollbacks the transaction and returns the error from function `f` if
|
||||
// it returns non-nil error. It commits the transaction and returns nil if
|
||||
@ -128,7 +281,15 @@ func (tx *TX) transactionKey() string {
|
||||
//
|
||||
// Note that, you should not Commit or Rollback the transaction in function `f`
|
||||
// as it is automatically handled by this function.
|
||||
func (tx *TX) Transaction(f func(tx *TX) error) (err error) {
|
||||
func (tx *TX) Transaction(ctx context.Context, f func(ctx context.Context, tx *TX) error) (err error) {
|
||||
if ctx != nil {
|
||||
tx.ctx = ctx
|
||||
}
|
||||
// Check transaction object from context.
|
||||
if TXFromCtx(tx.ctx, tx.db.GetGroup()) == nil {
|
||||
// Inject transaction object into context.
|
||||
tx.ctx = WithTX(tx.ctx, tx)
|
||||
}
|
||||
err = tx.Begin()
|
||||
if err != nil {
|
||||
return err
|
||||
@ -149,20 +310,20 @@ func (tx *TX) Transaction(f func(tx *TX) error) (err error) {
|
||||
}
|
||||
}
|
||||
}()
|
||||
err = f(tx)
|
||||
err = f(tx.ctx, tx)
|
||||
return
|
||||
}
|
||||
|
||||
// Query does query operation on transaction.
|
||||
// See Core.Query.
|
||||
func (tx *TX) Query(sql string, args ...interface{}) (rows *sql.Rows, err error) {
|
||||
return tx.db.DoQuery(tx.tx, sql, args...)
|
||||
return tx.db.DoQuery(tx.ctx, &txLink{tx.tx}, sql, args...)
|
||||
}
|
||||
|
||||
// Exec does none query operation on transaction.
|
||||
// See Core.Exec.
|
||||
func (tx *TX) Exec(sql string, args ...interface{}) (sql.Result, error) {
|
||||
return tx.db.DoExec(tx.tx, sql, args...)
|
||||
return tx.db.DoExec(tx.ctx, &txLink{tx.tx}, sql, args...)
|
||||
}
|
||||
|
||||
// Prepare creates a prepared statement for later queries or executions.
|
||||
@ -171,7 +332,7 @@ func (tx *TX) Exec(sql string, args ...interface{}) (sql.Result, error) {
|
||||
// The caller must call the statement's Close method
|
||||
// when the statement is no longer needed.
|
||||
func (tx *TX) Prepare(sql string) (*Stmt, error) {
|
||||
return tx.db.DoPrepare(tx.tx, sql)
|
||||
return tx.db.DoPrepare(tx.ctx, &txLink{tx.tx}, sql)
|
||||
}
|
||||
|
||||
// GetAll queries and returns data records from database.
|
||||
@ -181,7 +342,7 @@ func (tx *TX) GetAll(sql string, args ...interface{}) (Result, error) {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
return tx.db.convertRowsToResult(rows)
|
||||
return tx.db.GetCore().convertRowsToResult(rows)
|
||||
}
|
||||
|
||||
// GetOne queries and returns one record from database.
|
||||
@ -222,8 +383,8 @@ func (tx *TX) GetStructs(objPointerSlice interface{}, sql string, args ...interf
|
||||
// If parameter `pointer` is type of struct pointer, it calls GetStruct internally for
|
||||
// the conversion. If parameter `pointer` is type of slice, it calls GetStructs internally
|
||||
// for conversion.
|
||||
func (tx *TX) GetScan(objPointer interface{}, sql string, args ...interface{}) error {
|
||||
t := reflect.TypeOf(objPointer)
|
||||
func (tx *TX) GetScan(pointer interface{}, sql string, args ...interface{}) error {
|
||||
t := reflect.TypeOf(pointer)
|
||||
k := t.Kind()
|
||||
if k != reflect.Ptr {
|
||||
return fmt.Errorf("params should be type of pointer, but got: %v", k)
|
||||
@ -231,9 +392,9 @@ func (tx *TX) GetScan(objPointer interface{}, sql string, args ...interface{}) e
|
||||
k = t.Elem().Kind()
|
||||
switch k {
|
||||
case reflect.Array, reflect.Slice:
|
||||
return tx.db.GetStructs(objPointer, sql, args...)
|
||||
return tx.GetStructs(pointer, sql, args...)
|
||||
case reflect.Struct:
|
||||
return tx.db.GetStruct(objPointer, sql, args...)
|
||||
return tx.GetStruct(pointer, sql, args...)
|
||||
default:
|
||||
return fmt.Errorf("element type should be type of struct/slice, unsupported: %v", k)
|
||||
}
|
||||
@ -276,9 +437,9 @@ func (tx *TX) GetCount(sql string, args ...interface{}) (int, error) {
|
||||
// The parameter `batch` specifies the batch operation count when given data is slice.
|
||||
func (tx *TX) Insert(table string, data interface{}, batch ...int) (sql.Result, error) {
|
||||
if len(batch) > 0 {
|
||||
return tx.Model(table).Data(data).Batch(batch[0]).Insert()
|
||||
return tx.Model(table).Ctx(tx.ctx).Data(data).Batch(batch[0]).Insert()
|
||||
}
|
||||
return tx.Model(table).Data(data).Insert()
|
||||
return tx.Model(table).Ctx(tx.ctx).Data(data).Insert()
|
||||
}
|
||||
|
||||
// InsertIgnore does "INSERT IGNORE INTO ..." statement for the table.
|
||||
@ -292,9 +453,17 @@ func (tx *TX) Insert(table string, data interface{}, batch ...int) (sql.Result,
|
||||
// The parameter `batch` specifies the batch operation count when given data is slice.
|
||||
func (tx *TX) InsertIgnore(table string, data interface{}, batch ...int) (sql.Result, error) {
|
||||
if len(batch) > 0 {
|
||||
return tx.Model(table).Data(data).Batch(batch[0]).InsertIgnore()
|
||||
return tx.Model(table).Ctx(tx.ctx).Data(data).Batch(batch[0]).InsertIgnore()
|
||||
}
|
||||
return tx.Model(table).Data(data).InsertIgnore()
|
||||
return tx.Model(table).Ctx(tx.ctx).Data(data).InsertIgnore()
|
||||
}
|
||||
|
||||
// InsertAndGetId performs action Insert and returns the last insert id that automatically generated.
|
||||
func (tx *TX) InsertAndGetId(table string, data interface{}, batch ...int) (int64, error) {
|
||||
if len(batch) > 0 {
|
||||
return tx.Model(table).Ctx(tx.ctx).Data(data).Batch(batch[0]).InsertAndGetId()
|
||||
}
|
||||
return tx.Model(table).Ctx(tx.ctx).Data(data).InsertAndGetId()
|
||||
}
|
||||
|
||||
// Replace does "REPLACE INTO ..." statement for the table.
|
||||
@ -311,9 +480,9 @@ func (tx *TX) InsertIgnore(table string, data interface{}, batch ...int) (sql.Re
|
||||
// `batch` specifies the batch operation count.
|
||||
func (tx *TX) Replace(table string, data interface{}, batch ...int) (sql.Result, error) {
|
||||
if len(batch) > 0 {
|
||||
return tx.Model(table).Data(data).Batch(batch[0]).Replace()
|
||||
return tx.Model(table).Ctx(tx.ctx).Data(data).Batch(batch[0]).Replace()
|
||||
}
|
||||
return tx.Model(table).Data(data).Replace()
|
||||
return tx.Model(table).Ctx(tx.ctx).Data(data).Replace()
|
||||
}
|
||||
|
||||
// Save does "INSERT INTO ... ON DUPLICATE KEY UPDATE..." statement for the table.
|
||||
@ -329,45 +498,9 @@ func (tx *TX) Replace(table string, data interface{}, batch ...int) (sql.Result,
|
||||
// `batch` specifies the batch operation count.
|
||||
func (tx *TX) Save(table string, data interface{}, batch ...int) (sql.Result, error) {
|
||||
if len(batch) > 0 {
|
||||
return tx.Model(table).Data(data).Batch(batch[0]).Save()
|
||||
return tx.Model(table).Ctx(tx.ctx).Data(data).Batch(batch[0]).Save()
|
||||
}
|
||||
return tx.Model(table).Data(data).Save()
|
||||
}
|
||||
|
||||
// BatchInsert batch inserts data.
|
||||
// The parameter `list` must be type of slice of map or struct.
|
||||
func (tx *TX) BatchInsert(table string, list interface{}, batch ...int) (sql.Result, error) {
|
||||
if len(batch) > 0 {
|
||||
return tx.Model(table).Data(list).Batch(batch[0]).Insert()
|
||||
}
|
||||
return tx.Model(table).Data(list).Insert()
|
||||
}
|
||||
|
||||
// BatchInsertIgnore batch inserts data with ignore option.
|
||||
// The parameter `list` must be type of slice of map or struct.
|
||||
func (tx *TX) BatchInsertIgnore(table string, list interface{}, batch ...int) (sql.Result, error) {
|
||||
if len(batch) > 0 {
|
||||
return tx.Model(table).Data(list).Batch(batch[0]).InsertIgnore()
|
||||
}
|
||||
return tx.Model(table).Data(list).InsertIgnore()
|
||||
}
|
||||
|
||||
// BatchReplace batch replaces data.
|
||||
// The parameter `list` must be type of slice of map or struct.
|
||||
func (tx *TX) BatchReplace(table string, list interface{}, batch ...int) (sql.Result, error) {
|
||||
if len(batch) > 0 {
|
||||
return tx.Model(table).Data(list).Batch(batch[0]).Replace()
|
||||
}
|
||||
return tx.Model(table).Data(list).Replace()
|
||||
}
|
||||
|
||||
// BatchSave batch replaces data.
|
||||
// The parameter `list` must be type of slice of map or struct.
|
||||
func (tx *TX) BatchSave(table string, list interface{}, batch ...int) (sql.Result, error) {
|
||||
if len(batch) > 0 {
|
||||
return tx.Model(table).Data(list).Batch(batch[0]).Save()
|
||||
}
|
||||
return tx.Model(table).Data(list).Save()
|
||||
return tx.Model(table).Ctx(tx.ctx).Data(data).Save()
|
||||
}
|
||||
|
||||
// Update does "UPDATE ... " statement for the table.
|
||||
@ -385,7 +518,7 @@ func (tx *TX) BatchSave(table string, list interface{}, batch ...int) (sql.Resul
|
||||
// "age IN(?,?)", 18, 50
|
||||
// User{ Id : 1, UserName : "john"}
|
||||
func (tx *TX) Update(table string, data interface{}, condition interface{}, args ...interface{}) (sql.Result, error) {
|
||||
return tx.Model(table).Data(data).Where(condition, args...).Update()
|
||||
return tx.Model(table).Ctx(tx.ctx).Data(data).Where(condition, args...).Update()
|
||||
}
|
||||
|
||||
// Delete does "DELETE FROM ... " statement for the table.
|
||||
@ -400,5 +533,5 @@ func (tx *TX) Update(table string, data interface{}, condition interface{}, args
|
||||
// "age IN(?,?)", 18, 50
|
||||
// User{ Id : 1, UserName : "john"}
|
||||
func (tx *TX) Delete(table string, condition interface{}, args ...interface{}) (sql.Result, error) {
|
||||
return tx.Model(table).Where(condition, args...).Delete()
|
||||
return tx.Model(table).Ctx(tx.ctx).Where(condition, args...).Delete()
|
||||
}
|
||||
186
database/gdb/gdb_core_underlying.go
Normal file
186
database/gdb/gdb_core_underlying.go
Normal file
@ -0,0 +1,186 @@
|
||||
// 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"
|
||||
"database/sql"
|
||||
|
||||
"github.com/gogf/gf/os/gtime"
|
||||
)
|
||||
|
||||
// Query commits one query SQL to underlying driver and returns the execution result.
|
||||
// It is most commonly used for data querying.
|
||||
func (c *Core) Query(sql string, args ...interface{}) (rows *sql.Rows, err error) {
|
||||
return c.db.DoQuery(c.GetCtx(), nil, sql, args...)
|
||||
}
|
||||
|
||||
// DoQuery commits the sql string and its arguments to underlying driver
|
||||
// through given link object and returns the execution result.
|
||||
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 {
|
||||
return nil, err
|
||||
}
|
||||
} else if !link.IsTransaction() {
|
||||
if tx := TXFromCtx(ctx, c.db.GetGroup()); tx != nil {
|
||||
link = &txLink{tx.tx}
|
||||
}
|
||||
}
|
||||
// Link execution.
|
||||
sql, args = formatSql(sql, args)
|
||||
sql, args = c.db.DoCommit(ctx, link, sql, args)
|
||||
if c.GetConfig().QueryTimeout > 0 {
|
||||
ctx, _ = context.WithTimeout(ctx, c.GetConfig().QueryTimeout)
|
||||
}
|
||||
mTime1 := gtime.TimestampMilli()
|
||||
rows, err = link.QueryContext(ctx, sql, args...)
|
||||
mTime2 := gtime.TimestampMilli()
|
||||
sqlObj := &Sql{
|
||||
Sql: sql,
|
||||
Type: "DB.QueryContext",
|
||||
Args: args,
|
||||
Format: FormatSqlWithArgs(sql, args),
|
||||
Error: err,
|
||||
Start: mTime1,
|
||||
End: mTime2,
|
||||
Group: c.db.GetGroup(),
|
||||
IsTransaction: link.IsTransaction(),
|
||||
}
|
||||
// Tracing and logging.
|
||||
c.addSqlToTracing(ctx, sqlObj)
|
||||
if c.db.GetDebug() {
|
||||
c.writeSqlToLogger(ctx, sqlObj)
|
||||
}
|
||||
if err == nil {
|
||||
return rows, nil
|
||||
} else {
|
||||
err = formatError(err, sql, args...)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Exec commits one query SQL to underlying driver and returns the execution result.
|
||||
// It is most commonly used for data inserting and updating.
|
||||
func (c *Core) Exec(sql string, args ...interface{}) (result sql.Result, err error) {
|
||||
return c.db.DoExec(c.GetCtx(), nil, sql, args...)
|
||||
}
|
||||
|
||||
// DoExec commits the sql string and its arguments to underlying driver
|
||||
// through given link object and returns the execution result.
|
||||
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 {
|
||||
return nil, err
|
||||
}
|
||||
} else if !link.IsTransaction() {
|
||||
if tx := TXFromCtx(ctx, c.db.GetGroup()); tx != nil {
|
||||
link = &txLink{tx.tx}
|
||||
}
|
||||
}
|
||||
// Link execution.
|
||||
sql, args = formatSql(sql, args)
|
||||
sql, args = c.db.DoCommit(ctx, link, sql, args)
|
||||
if c.GetConfig().ExecTimeout > 0 {
|
||||
var cancelFunc context.CancelFunc
|
||||
ctx, cancelFunc = context.WithTimeout(ctx, c.GetConfig().ExecTimeout)
|
||||
defer cancelFunc()
|
||||
}
|
||||
|
||||
mTime1 := gtime.TimestampMilli()
|
||||
if !c.db.GetDryRun() {
|
||||
result, err = link.ExecContext(ctx, sql, args...)
|
||||
} else {
|
||||
result = new(SqlResult)
|
||||
}
|
||||
mTime2 := gtime.TimestampMilli()
|
||||
sqlObj := &Sql{
|
||||
Sql: sql,
|
||||
Type: "DB.ExecContext",
|
||||
Args: args,
|
||||
Format: FormatSqlWithArgs(sql, args),
|
||||
Error: err,
|
||||
Start: mTime1,
|
||||
End: mTime2,
|
||||
Group: c.db.GetGroup(),
|
||||
IsTransaction: link.IsTransaction(),
|
||||
}
|
||||
// Tracing and logging.
|
||||
c.addSqlToTracing(ctx, sqlObj)
|
||||
if c.db.GetDebug() {
|
||||
c.writeSqlToLogger(ctx, sqlObj)
|
||||
}
|
||||
return result, formatError(err, sql, args...)
|
||||
}
|
||||
|
||||
// Prepare creates a prepared statement for later queries or executions.
|
||||
// Multiple queries or executions may be run concurrently from the
|
||||
// returned statement.
|
||||
// The caller must call the statement's Close method
|
||||
// when the statement is no longer needed.
|
||||
//
|
||||
// The parameter `execOnMaster` specifies whether executing the sql on master node,
|
||||
// or else it executes the sql on slave node if master-slave configured.
|
||||
func (c *Core) Prepare(sql string, execOnMaster ...bool) (*Stmt, error) {
|
||||
var (
|
||||
err error
|
||||
link Link
|
||||
)
|
||||
if len(execOnMaster) > 0 && execOnMaster[0] {
|
||||
if link, err = c.MasterLink(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
if link, err = c.SlaveLink(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return c.db.DoPrepare(c.GetCtx(), link, sql)
|
||||
}
|
||||
|
||||
// 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() {
|
||||
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)
|
||||
}
|
||||
var (
|
||||
mTime1 = gtime.TimestampMilli()
|
||||
stmt, err = link.PrepareContext(ctx, sql)
|
||||
mTime2 = gtime.TimestampMilli()
|
||||
sqlObj = &Sql{
|
||||
Sql: sql,
|
||||
Type: "DB.PrepareContext",
|
||||
Args: nil,
|
||||
Format: FormatSqlWithArgs(sql, nil),
|
||||
Error: err,
|
||||
Start: mTime1,
|
||||
End: mTime2,
|
||||
Group: c.db.GetGroup(),
|
||||
IsTransaction: link.IsTransaction(),
|
||||
}
|
||||
)
|
||||
// Tracing and logging.
|
||||
c.addSqlToTracing(ctx, sqlObj)
|
||||
if c.db.GetDebug() {
|
||||
c.writeSqlToLogger(ctx, sqlObj)
|
||||
}
|
||||
return &Stmt{
|
||||
Stmt: stmt,
|
||||
core: c,
|
||||
link: link,
|
||||
sql: sql,
|
||||
}, err
|
||||
}
|
||||
@ -7,22 +7,26 @@
|
||||
|
||||
package gdb
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
)
|
||||
|
||||
// GetMaster acts like function Master but with additional `schema` parameter specifying
|
||||
// MasterLink acts like function Master but with additional `schema` parameter specifying
|
||||
// the schema for the connection. It is defined for internal usage.
|
||||
// Also see Master.
|
||||
func (c *Core) GetMaster(schema ...string) (*sql.DB, error) {
|
||||
return c.getSqlDb(true, schema...)
|
||||
func (c *Core) MasterLink(schema ...string) (Link, error) {
|
||||
db, err := c.db.Master(schema...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &dbLink{db}, nil
|
||||
}
|
||||
|
||||
// GetSlave acts like function Slave but with additional `schema` parameter specifying
|
||||
// SlaveLink acts like function Slave but with additional `schema` parameter specifying
|
||||
// the schema for the connection. It is defined for internal usage.
|
||||
// Also see Slave.
|
||||
func (c *Core) GetSlave(schema ...string) (*sql.DB, error) {
|
||||
return c.getSqlDb(false, schema...)
|
||||
func (c *Core) SlaveLink(schema ...string) (Link, error) {
|
||||
db, err := c.db.Slave(schema...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &dbLink{db}, nil
|
||||
}
|
||||
|
||||
// QuoteWord checks given string `s` a word, if true quotes it with security chars of the database
|
||||
@ -59,9 +63,11 @@ func (c *Core) GetChars() (charLeft string, charRight string) {
|
||||
return "", ""
|
||||
}
|
||||
|
||||
// HandleSqlBeforeCommit handles the sql before posts it to database.
|
||||
// It does nothing in default.
|
||||
func (c *Core) HandleSqlBeforeCommit(sql string) string {
|
||||
// DoCommit is a hook function, which deals with the sql string before it's committed to underlying driver.
|
||||
// The parameter `link` specifies the current database connection operation object. You can modify the sql
|
||||
// string `sql` and its arguments `args` as you wish before they're committed to driver.
|
||||
// Also see Core.DoCommit.
|
||||
func (c *Core) DoCommit(sql string) string {
|
||||
return sql
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user