Compare commits

..

113 Commits

Author SHA1 Message Date
4cd7e4e5a0 version updates 2021-06-23 21:57:13 +08:00
e6688b9e86 add more unit testing cases for package ghttp 2021-06-23 21:39:12 +08:00
65131c6f22 add automatic fields mapping and filtering for Model.Where statements 2021-06-23 12:04:16 +08:00
7667aca4c2 improve Order feature for package gdb 2021-06-23 09:42:10 +08:00
816791b9c1 improve function Increment/Decrement for package gdb 2021-06-23 09:34:53 +08:00
d6ea2220f7 add auto fields filtering feature for function Scan of package gdb; mark funtcion Struct/Structs deprecated for gdb.Model 2021-06-22 21:48:56 +08:00
69dd5db774 improve record converting for package gdb 2021-06-22 20:05:37 +08:00
adca9222ab improve transaction feature for package gdb 2021-06-22 17:42:31 +08:00
7144aa6999 improve caller path filtering for package gdebug 2021-06-22 15:34:26 +08:00
fbad5f60eb improve caller path filtering for package gdebug 2021-06-22 15:09:08 +08:00
266f592739 improve raw sql count statement for package gdb 2021-06-21 19:21:38 +08:00
7c8bbcb3af version update 2021-06-21 09:38:19 +08:00
ba18e2bf6b comment update 2021-06-21 09:37:12 +08:00
5fefe97b87 add Raw Model for package gdb 2021-06-18 15:27:49 +08:00
5fba250a14 add Raw Model for package gdb 2021-06-18 15:20:27 +08:00
2606ad83ac add OnDuplicate/OnDuplicateEx feature for package gdb 2021-06-16 21:51:44 +08:00
d450de8e0d add OnDuplicate/OnDuplicateEx feature for package gdb 2021-06-16 21:44:31 +08:00
e4b0de0d4f Merge branch 'master' of https://github.com/gogf/gf 2021-06-15 19:58:10 +08:00
2af4fd86cc rename configuration node name from LinkInfo to Link 2021-06-15 19:57:55 +08:00
ddd171bc18 Merge pull request #1284 from qinyuguang/gutil
add gutil.SliceToMapWithColumnAsKey
2021-06-11 09:52:30 +08:00
2679f92aa8 comment update for package gerror 2021-06-10 20:45:22 +08:00
cca438d77f fix issue #1209 2021-06-10 20:17:53 +08:00
f2bc29e5c1 add gutil.SliceToMapWithColumnAsKey 2021-06-09 18:49:49 +08:00
fe7209e76d rename HandleSqlBeforeCommit to DoCommit for package gdb 2021-06-08 21:55:55 +08:00
7c4a0453b7 improve handler feature for package glog 2021-06-08 21:35:54 +08:00
97879834bc remove deprecated functions for package gdb 2021-06-08 21:28:41 +08:00
332535901f Merge branch 'master' of https://github.com/gogf/gf 2021-06-08 20:32:52 +08:00
e68e7a3224 remove Batch*/DoBatchInsert functions for package gdb 2021-06-08 20:32:34 +08:00
65befd5ac4 Merge pull request #1276 from qinyuguang/gdb_config_timezone
add timezone configuration for package gdb
2021-06-07 20:01:06 +08:00
78bdb5ef71 Merge pull request #1277 from weicut/master
Fixed incorrect type conversion
2021-06-07 19:58:27 +08:00
6eb7261dfd add timezone configuration for package gdb, effective for mysql and pgsql 2021-06-07 13:45:40 +08:00
4f82be5bc0 Fixed incorrect type conversion 2021-06-07 10:17:23 +08:00
3ac5772059 add UNION/UNION ALL feature for package gdb 2021-06-06 23:06:39 +08:00
eb723e47c2 fix issue in gconv.MapDeep 2021-06-05 08:58:54 +08:00
8aa7f08350 improve DB interface for package gdb 2021-06-04 09:54:19 +08:00
a54559d016 add WhereLT/WhereLTE/WhereGT/WhereGTE/WhereOrLT/WhereOrLTE/WhereOrGT/WhereOrGTE functions for gdb.Model 2021-06-04 09:27:41 +08:00
8e1f6abac5 change github.com/go-sql-driver/mysql to github.com/gogf/mysql 2021-06-03 15:38:33 +08:00
7f3a2207a3 Merge branch 'master' of https://github.com/gogf/gf 2021-06-02 21:12:52 +08:00
742c7913ea fix issue in function Parse for package ghttp 2021-06-02 21:12:27 +08:00
2d8ab726e2 Merge pull request #1265 from weicut/master 2021-06-02 13:11:59 +08:00
702a296258 improve handler feature for package glog 2021-06-02 09:53:08 +08:00
c3c5414ce2 add custom handler feature 2021-06-02 09:42:27 +08:00
ee3d375532 补充浮点数单元测试 2021-06-02 09:18:52 +08:00
e3f5c9175c 补充浮点数单元测试 2021-06-02 09:17:50 +08:00
7c24449a24 Merge branch 'master' of github.com:gogf/gf 2021-06-02 09:08:45 +08:00
1c09846d3e fix issue #1272 2021-06-01 20:09:52 +08:00
db94346863 gitee/github template update 2021-06-01 19:59:57 +08:00
3e6b9864d5 fix issue #1256 2021-06-01 19:47:02 +08:00
392c81ad69 remove Link parameter from function TableFields for package gdb 2021-05-29 17:26:08 +08:00
fe142c93fd add function Transaction for gdb.Model 2021-05-29 17:07:49 +08:00
578e7d634b comment updates for package gvalid 2021-05-29 16:47:39 +08:00
6659a49869 Merge branch 'master' of https://github.com/gogf/gf 2021-05-29 16:45:19 +08:00
d9bc8b05e1 add example for package gvalid 2021-05-29 16:45:08 +08:00
571405597b Merge pull request #1271 from wangle201210/master
add valid size method
2021-05-29 16:16:21 +08:00
1441ce7f5f add examples for package gproc 2021-05-29 15:47:07 +08:00
071a587755 Merge remote-tracking branch 'upstream/master' 2021-05-29 12:35:56 +08:00
c1db01425e fix remarks 2021-05-29 12:25:50 +08:00
7bfd48e2ab valid field length 2021-05-29 12:15:37 +08:00
e80309af32 Merge branch 'master' of https://github.com/gogf/gf 2021-05-29 11:30:47 +08:00
fa1814ff54 add custom rule fucntions feature for package gvalid 2021-05-29 11:30:34 +08:00
d7b8a2684a Revert ""gvalid length" compatible with only one parameter"
This reverts commit b2d3c7d1fd.
2021-05-29 11:28:26 +08:00
8559cd299d Merge pull request #1268 from wangle201210/master
"gvalid  length" compatible with only one parameter
2021-05-28 13:44:36 +08:00
b2d3c7d1fd "gvalid length" compatible with only one parameter 2021-05-28 11:34:51 +08:00
5100e0e8b7 add sub-query feature for orm 2021-05-27 22:18:16 +08:00
8acb921ee3 improve package gtimer for times limitation 2021-05-27 13:10:10 +08:00
fc88001a8c revert name from Job to Entry for package gtimer/gcron 2021-05-26 09:55:33 +08:00
fab6c4048d comment update 2021-05-25 22:16:55 +08:00
7b32791006 修复浮点型排序
原因:
返回值,强制转成 int 类型,会导致浮点型比较不准确,例如:0.33,转成 int 类型时,会变成 0
2021-05-25 16:15:02 +08:00
aa04948319 修复浮点型排序
原因:
返回值,强制转成 int 类型,会导致浮点型比较不准确,例如:0.33,转成 int 类型时,会变成 0
2021-05-25 16:10:52 +08:00
5903eb8ceb fix issue #1254 2021-05-25 09:56:23 +08:00
211e62a8e7 add timestamptz type support for pgsql for package gdb 2021-05-24 13:30:04 +08:00
64a9b06e64 comment update 2021-05-23 00:02:49 +08:00
6d81aa4462 infract internal link 2021-05-21 15:38:56 +08:00
406b6bf410 infract internal link 2021-05-21 15:30:21 +08:00
4e41d8aff8 improve context and nested transaction feature for package gdb 2021-05-21 13:25:53 +08:00
017c6e4e1f add example for i18n feature of package gvalid 2021-05-19 23:17:13 +08:00
7d3233c7ad add context for custom validation rule function;add function WhereNot for gdb.Model 2021-05-19 22:33:50 +08:00
3e2662582c improve nested transaction feature for package gdb 2021-05-19 21:17:21 +08:00
c94dee8191 improve nested transaction feature for package gdb 2021-05-19 21:11:51 +08:00
ab2ef13d99 improve transaction for package gdb 2021-05-19 20:33:19 +08:00
fac9ab5c01 improve package gvalid 2021-05-19 19:00:34 +08:00
420e0b9ca4 improve package gvalid 2021-05-19 13:29:40 +08:00
ea0340db8e improve validation manager feature for package gvalid 2021-05-19 09:25:49 +08:00
0bd1ea07a7 example update 2021-05-18 20:52:39 +08:00
c8c28770fb change Error from struct to interface for package gvalid;error string update for package gdb 2021-05-18 20:51:31 +08:00
0dfd968824 comment update for package gconv/gproc 2021-05-17 21:26:39 +08:00
b84ca9cc13 debug 2021-05-17 20:25:00 +08:00
e5734425ba debug 2021-05-17 20:18:21 +08:00
2274a10cfd fx issue #1250 2021-05-17 19:59:34 +08:00
302e234bfe comment update for package gfsnotify 2021-05-17 19:17:04 +08:00
2b942bf06b Merge pull request #1247 from notnear/master
add websocket client
2021-05-17 13:52:39 +08:00
522f6cb455 fix issue of failing in folder watching with no recursive option for package gfsnotify 2021-05-17 13:43:36 +08:00
a757fbd37d improve package gcfg 2021-05-17 00:07:06 +08:00
33567ef338 remove unit testing file for package internal/json 2021-05-15 23:42:39 +08:00
b2a15c259e improve unit testing case for package internal/json 2021-05-15 23:21:38 +08:00
d12409b118 revert json.UnmarshalUseNumber to json.Unmarshal for code in the example folder 2021-05-15 23:13:31 +08:00
8aed1eca13 version updates 2021-05-15 23:02:44 +08:00
f389688caa add unit testing cases for package internal/json 2021-05-15 23:02:21 +08:00
7003c284d0 replace json.Unmarshal with json.UnmarshalUseNumber for packages 2021-05-15 22:38:07 +08:00
facb2949c3 improve unit testing cases for package gtimer 2021-05-15 18:31:46 +08:00
1b1355a595 improve package gtimer 2021-05-15 18:27:46 +08:00
d76e4c8aed refract package gtimer for more stable 2021-05-15 18:13:51 +08:00
cc1224e032 add context for package gvalid.Check* functions 2021-05-13 20:56:52 +08:00
tom
09de115670 add websocket client 2021-05-13 17:51:06 +08:00
23110b5d19 fix issue in unit testing case of package gins 2021-05-13 08:53:54 +08:00
a326f4a989 improve package gi18n/gview/gvalid for more flexable i18n feature controll,add custom error configuration and i18n translation for custom error message 2021-05-13 00:16:45 +08:00
d21b9d58e1 improve with feature by supporting attributes struct which also have 'with' tag 2021-05-12 21:34:15 +08:00
9d362c3738 merge feature/improve_struct_validation 2021-05-12 00:15:53 +08:00
b06580d343 improve struct validation for package gvalid 2021-05-12 00:01:52 +08:00
a4240bdfb7 improve struct validation for package gvalid 2021-05-11 20:57:30 +08:00
1eab1cb367 add more unit testing cases,remove function Filter usage in unit testing cases for package gdb 2021-05-11 20:14:06 +08:00
2e38416e12 improve struct embedded association case of with feature for package gdb 2021-05-11 20:00:50 +08:00
034a3f1808 improve struct validation 2021-05-11 19:20:39 +08:00
273 changed files with 7923 additions and 4412 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1 +1,3 @@
OrderPaid = "您已成功完成订单号 #%d 支付,支付金额¥%.2f。"
OrderPaid = "您已成功完成订单号 #%d 支付,支付金额¥%.2f。"
hello = "你好"
world = "世界"

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

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

View File

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

View File

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

View File

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

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

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

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

View File

@ -0,0 +1,14 @@
# MySQL.
[database]
[database.default]
link = "mysql:root:12345678@tcp(127.0.0.1:3306)/test"
debug = true

View File

@ -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:两次密码输入不相等]]

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

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

@ -1,14 +0,0 @@
"gf.gvalid.required" = "字段不能为空"

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

View File

@ -0,0 +1,16 @@
"gf.gvalid.required" = "字段不能为空"
"ReuiredUserName" = "请输入用户名称"
"ReuiredUserType" = "请选择用户类型"
"MustSize" = ":attribute长度必须为:size"

View File

@ -1,4 +1,7 @@
<!-- 为高效率地交流并解决问题请按照以下模板提交issue感谢 -->
<!-- 为高效处理您的疑问如果觉得是BUG类问题请您务必提供可复现该问题的最小可运行代码 -->
<!-- 为高效处理您的疑问如果觉得是BUG类问题请您务必提供可复现该问题的最小可运行代码 -->
<!-- 为高效处理您的疑问如果觉得是BUG类问题请您务必提供可复现该问题的最小可运行代码 -->
<!-- 重要的事情说三遍! -->
### 1. 您当前使用的`Go`版本,及系统版本、系统架构?

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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.
@ -905,7 +605,7 @@ func (c *Core) convertRowsToResult(rows *sql.Rows) (Result, error) {
}
var (
values = make([]interface{}, len(columnNames))
records = make(Result, 0)
result = make(Result, 0)
scanArgs = make([]interface{}, len(values))
)
for i := range values {
@ -913,22 +613,22 @@ func (c *Core) convertRowsToResult(rows *sql.Rows) (Result, error) {
}
for {
if err := rows.Scan(scanArgs...); err != nil {
return records, err
return result, err
}
row := make(Record)
record := Record{}
for i, value := range values {
if value == nil {
row[columnNames[i]] = gvar.New(nil)
record[columnNames[i]] = gvar.New(nil)
} else {
row[columnNames[i]] = gvar.New(c.db.convertFieldValueToLocalValue(value, columnTypes[i]))
record[columnNames[i]] = gvar.New(c.convertFieldValueToLocalValue(value, columnTypes[i]))
}
}
records = append(records, row)
result = append(result, record)
if !rows.Next() {
break
}
}
return records, nil
return result, nil
}
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
@ -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
}

View File

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

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

View File

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

View File

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

View File

@ -7,34 +7,194 @@
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.
isClosed bool // isClosed marks this transaction has already been committed or rolled back.
}
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)
if tx.IsClosed() {
return nil
}
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 +203,21 @@ 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.isClosed = true
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 +228,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 +237,33 @@ 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.isClosed = true
tx.db.GetCore().addSqlToTracing(tx.ctx, sqlObj)
if tx.db.GetDebug() {
tx.db.writeSqlToLogger(sqlObj)
tx.db.GetCore().writeSqlToLogger(tx.ctx, sqlObj)
}
return err
}
// IsClosed checks and returns this transaction has already been committed or rolled back.
func (tx *TX) IsClosed() bool {
return tx.isClosed
}
// 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 +274,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 +292,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 +321,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 +343,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 +353,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 +394,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 +403,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 +448,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 +464,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 +491,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 +509,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 +529,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 +544,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()
}

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

View File

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