From 4cd4559784908df3205bd60515ffd4faeaff8551 Mon Sep 17 00:00:00 2001 From: Anson <77931774@qq.com> Date: Thu, 12 Aug 2021 00:21:36 +0800 Subject: [PATCH 1/8] fix tcp demo error content-length --- .example/net/gtcp/gtcp_conn.go | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/.example/net/gtcp/gtcp_conn.go b/.example/net/gtcp/gtcp_conn.go index a312c4ead..4008053ef 100644 --- a/.example/net/gtcp/gtcp_conn.go +++ b/.example/net/gtcp/gtcp_conn.go @@ -6,7 +6,7 @@ import ( "os" "github.com/gogf/gf/net/gtcp" - "github.com/gogf/gf/util/gconv" + "strconv" ) func main() { @@ -16,7 +16,7 @@ func main() { } defer conn.Close() - if err := conn.Send([]byte("GET / HTTP/1.1\n\n")); err != nil { + if err := conn.Send([]byte("GET / HTTP/1.1\r\n\r\n")); err != nil { panic(err) } @@ -30,13 +30,17 @@ func main() { array := bytes.Split(data, []byte(": ")) // 获得页面内容长度 if contentLength == 0 && len(array) == 2 && bytes.EqualFold([]byte("Content-Length"), array[0]) { - contentLength = gconv.Int(array[1]) + // http 以\r\n换行,需要把\r也去掉 + contentLength, err = strconv.Atoi(string(array[1][:len(array[1])-1])) + if err != nil { + fmt.Println(err) + } } header = append(header, data...) header = append(header, '\n') } - // header读取完毕,读取文本内容 - if contentLength > 0 && len(data) == 0 { + // header读取完毕,读取文本内容, 1为\r + if contentLength > 0 && len(data) == 1 { content, _ = conn.Recv(contentLength) break } From 2bd76dfdded37c0c9da71c20211d17503327099c Mon Sep 17 00:00:00 2001 From: ansionfor <77931774@qq.com> Date: Thu, 12 Aug 2021 09:35:57 +0800 Subject: [PATCH 2/8] go fmt code --- .example/net/gtcp/gtcp_conn.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.example/net/gtcp/gtcp_conn.go b/.example/net/gtcp/gtcp_conn.go index 4008053ef..7cd01001e 100644 --- a/.example/net/gtcp/gtcp_conn.go +++ b/.example/net/gtcp/gtcp_conn.go @@ -32,9 +32,9 @@ func main() { if contentLength == 0 && len(array) == 2 && bytes.EqualFold([]byte("Content-Length"), array[0]) { // http 以\r\n换行,需要把\r也去掉 contentLength, err = strconv.Atoi(string(array[1][:len(array[1])-1])) - if err != nil { - fmt.Println(err) - } + if err != nil { + fmt.Println(err) + } } header = append(header, data...) header = append(header, '\n') From 91cd4f96f0b9b9ad464b50c2d8de9bdc34b9e07f Mon Sep 17 00:00:00 2001 From: Anson <77931774@qq.com> Date: Thu, 12 Aug 2021 10:50:46 +0800 Subject: [PATCH 3/8] Update gtcp_conn.go --- .example/net/gtcp/gtcp_conn.go | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/.example/net/gtcp/gtcp_conn.go b/.example/net/gtcp/gtcp_conn.go index 7cd01001e..323595751 100644 --- a/.example/net/gtcp/gtcp_conn.go +++ b/.example/net/gtcp/gtcp_conn.go @@ -6,7 +6,7 @@ import ( "os" "github.com/gogf/gf/net/gtcp" - "strconv" + "github.com/gogf/gf/util/gconv" ) func main() { @@ -31,10 +31,7 @@ func main() { // 获得页面内容长度 if contentLength == 0 && len(array) == 2 && bytes.EqualFold([]byte("Content-Length"), array[0]) { // http 以\r\n换行,需要把\r也去掉 - contentLength, err = strconv.Atoi(string(array[1][:len(array[1])-1])) - if err != nil { - fmt.Println(err) - } + contentLength = gconv.Int(string(array[1][:len(array[1])-1])) } header = append(header, data...) header = append(header, '\n') From ef36cf84463b831e48005097198fae580169a75d Mon Sep 17 00:00:00 2001 From: John Date: Fri, 20 Aug 2021 23:15:48 +0800 Subject: [PATCH 4/8] fix issue in example of package gjson --- encoding/gjson/gjson_z_example_conversion_test.go | 5 +++-- go.mod | 7 ------- net/ghttp/ghttp.go | 6 +++--- 3 files changed, 6 insertions(+), 12 deletions(-) diff --git a/encoding/gjson/gjson_z_example_conversion_test.go b/encoding/gjson/gjson_z_example_conversion_test.go index c55d9c409..d2f4bdd9f 100644 --- a/encoding/gjson/gjson_z_example_conversion_test.go +++ b/encoding/gjson/gjson_z_example_conversion_test.go @@ -19,6 +19,7 @@ func Example_conversionNormalFormats() { "array" : ["John", "Ming"] } }` + if j, err := gjson.DecodeToJson(data); err != nil { panic(err) } else { @@ -48,8 +49,8 @@ func Example_conversionNormalFormats() { // YAML: // users: // array: - // - John - // - Ming + // - John + // - Ming // count: 1 // // ====================== diff --git a/go.mod b/go.mod index d07ddc00e..19fc61f7f 100644 --- a/go.mod +++ b/go.mod @@ -5,24 +5,17 @@ go 1.14 require ( github.com/BurntSushi/toml v0.3.1 github.com/clbanning/mxj v1.8.5-0.20200714211355-ff02cfb8ea28 - github.com/davecgh/go-spew v1.1.1 // indirect github.com/fatih/color v1.12.0 github.com/fsnotify/fsnotify v1.4.9 github.com/gogf/mysql v1.6.1-0.20210603073548-16164ae25579 github.com/gomodule/redigo v2.0.0+incompatible github.com/gorilla/websocket v1.4.2 github.com/grokify/html-strip-tags-go v0.0.0-20190921062105-daaa06bf1aaf - github.com/kr/text v0.2.0 // indirect - github.com/mattn/go-runewidth v0.0.10 // indirect - github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect github.com/olekukonko/tablewriter v0.0.5 go.opentelemetry.io/otel v1.0.0-RC2 go.opentelemetry.io/otel/oteltest v1.0.0-RC2 go.opentelemetry.io/otel/trace v1.0.0-RC2 golang.org/x/net v0.0.0-20210520170846-37e1c6afe023 - golang.org/x/sys v0.0.0-20210616094352-59db8d763f22 // indirect golang.org/x/text v0.3.6 - golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect - gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b ) diff --git a/net/ghttp/ghttp.go b/net/ghttp/ghttp.go index 3c539cbb5..f4f55c34b 100644 --- a/net/ghttp/ghttp.go +++ b/net/ghttp/ghttp.go @@ -23,12 +23,12 @@ type ( Server struct { name string // Unique name for instance management. config ServerConfig // Configuration. - plugins []Plugin // Plugin array to extends server functionality. + plugins []Plugin // Plugin array to extend server functionality. servers []*gracefulServer // Underlying http.Server array. serverCount *gtype.Int // Underlying http.Server count. closeChan chan struct{} // Used for underlying server closing event notification. serveTree map[string]interface{} // The route map tree. - serveCache *gcache.Cache // Server cache for internal usage. + serveCache *gcache.Cache // Server caches for internal usage. routesMap map[string][]registeredRouteItem // Route map mainly for route dumps and repeated route checks. statusHandlerMap map[string][]HandlerFunc // Custom status handler map. sessionManager *gsession.Manager // Session manager. @@ -142,7 +142,7 @@ var ( serverMapping = gmap.NewStrAnyMap(true) // serverRunning marks the running server count. - // If there no successful server running or all servers shutdown, this value is 0. + // If there is no successful server running or all servers' shutdown, this value is 0. serverRunning = gtype.NewInt() // wsUpGrader is the default up-grader configuration for websocket. From 6be582355c682c087211697fe9990f856cfbfa38 Mon Sep 17 00:00:00 2001 From: John Guo Date: Wed, 25 Aug 2021 10:58:29 +0800 Subject: [PATCH 5/8] improve DB.Close for package gdb --- database/gdb/gdb_core.go | 1 + 1 file changed, 1 insertion(+) diff --git a/database/gdb/gdb_core.go b/database/gdb/gdb_core.go index 5a63e285c..7b76f5dca 100644 --- a/database/gdb/gdb_core.go +++ b/database/gdb/gdb_core.go @@ -111,6 +111,7 @@ func (c *Core) Close(ctx context.Context) (err error) { if err != nil { return } + delete(m, k) } } }) From 59397fd8a5c1e6b810c79bd61c6bff2171e4365c Mon Sep 17 00:00:00 2001 From: John Guo Date: Wed, 25 Aug 2021 20:00:53 +0800 Subject: [PATCH 6/8] add buildin function add/minus/times/divide for package gview --- errors/gcode/gcode.go | 38 +------------------------ errors/gcode/gcode_local.go | 43 ++++++++++++++++++++++++++++ errors/gcode/gcode_test.go | 23 +++++++++++++++ os/gview/gview.go | 6 +++- os/gview/gview_buildin.go | 47 ++++++++++++++++++++++++++++++- os/gview/gview_config.go | 8 +++--- os/gview/gview_instance.go | 2 +- os/gview/gview_parse.go | 24 ++++++++-------- os/gview/gview_unit_basic_test.go | 37 ++++++++++++++++++++++++ 9 files changed, 172 insertions(+), 56 deletions(-) create mode 100644 errors/gcode/gcode_local.go create mode 100644 errors/gcode/gcode_test.go diff --git a/errors/gcode/gcode.go b/errors/gcode/gcode.go index 6248ed0c7..8665b8a27 100644 --- a/errors/gcode/gcode.go +++ b/errors/gcode/gcode.go @@ -7,8 +7,6 @@ // Package gcode provides universal error code definition and common error codes implements. package gcode -import "fmt" - // Code is universal error code interface definition. type Code interface { // Code returns the integer number of current error code. @@ -22,13 +20,6 @@ type Code interface { Detail() interface{} } -// localCode is an implementer for interface Code for internal usage only. -type localCode struct { - code int // Error code, usually an integer. - message string // Brief message for this error code. - detail interface{} // As type of interface, it is mainly designed as an extension field for error code. -} - // ================================================================================================================ // Common error code definition. // There are reserved internal error code by framework: code < 1000. @@ -52,7 +43,7 @@ var ( CodeSecurityReason = localCode{62, "Security Reason", nil} // Security Reason. CodeServerBusy = localCode{63, "Server Is Busy", nil} // Server is busy, please try again later. CodeUnknown = localCode{64, "Unknown Error", nil} // Unknown error. - CodeResourceNotExist = localCode{65, "Resource Not Exist", nil} // Resource does not exist. + CodeNotFound = localCode{65, "Not Found", nil} // Resource does not exist. CodeInvalidRequest = localCode{66, "Invalid Request", nil} // Invalid request. CodeBusinessValidationFailed = localCode{300, "Business Validation Failed", nil} // Business validation failed. ) @@ -66,30 +57,3 @@ func New(code int, message string, detail interface{}) Code { detail: detail, } } - -// Code returns the integer number of current error code. -func (c localCode) Code() int { - return c.code -} - -// Message returns the brief message for current error code. -func (c localCode) Message() string { - return c.message -} - -// Detail returns the detailed information of current error code, -// which is mainly designed as an extension field for error code. -func (c localCode) Detail() interface{} { - return c.detail -} - -// String returns current error code as a string. -func (c localCode) String() string { - if c.detail != nil { - return fmt.Sprintf(`%d:%s %v`, c.code, c.message, c.detail) - } - if c.message != "" { - return fmt.Sprintf(`%d:%s`, c.code, c.message) - } - return fmt.Sprintf(`%d`, c.code) -} diff --git a/errors/gcode/gcode_local.go b/errors/gcode/gcode_local.go new file mode 100644 index 000000000..1ec1d1ea9 --- /dev/null +++ b/errors/gcode/gcode_local.go @@ -0,0 +1,43 @@ +// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gcode + +import "fmt" + +// localCode is an implementer for interface Code for internal usage only. +type localCode struct { + code int // Error code, usually an integer. + message string // Brief message for this error code. + detail interface{} // As type of interface, it is mainly designed as an extension field for error code. +} + +// Code returns the integer number of current error code. +func (c localCode) Code() int { + return c.code +} + +// Message returns the brief message for current error code. +func (c localCode) Message() string { + return c.message +} + +// Detail returns the detailed information of current error code, +// which is mainly designed as an extension field for error code. +func (c localCode) Detail() interface{} { + return c.detail +} + +// String returns current error code as a string. +func (c localCode) String() string { + if c.detail != nil { + return fmt.Sprintf(`%d:%s %v`, c.code, c.message, c.detail) + } + if c.message != "" { + return fmt.Sprintf(`%d:%s`, c.code, c.message) + } + return fmt.Sprintf(`%d`, c.code) +} diff --git a/errors/gcode/gcode_test.go b/errors/gcode/gcode_test.go new file mode 100644 index 000000000..68a4e7da4 --- /dev/null +++ b/errors/gcode/gcode_test.go @@ -0,0 +1,23 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package gcode_test + +import ( + "github.com/gogf/gf/errors/gcode" + "testing" + + "github.com/gogf/gf/test/gtest" +) + +func Test_Nil(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + c := gcode.New(1, "custom error", "detailed description") + t.Assert(c.Code(), 1) + t.Assert(c.Message(), "custom error") + t.Assert(c.Detail(), "detailed description") + }) +} diff --git a/os/gview/gview.go b/os/gview/gview.go index 1b072f6e3..e6823d072 100644 --- a/os/gview/gview.go +++ b/os/gview/gview.go @@ -61,7 +61,7 @@ func ParseContent(ctx context.Context, content string, params ...Params) (string } // New returns a new view object. -// The parameter specifies the template directory path to load template files. +// The parameter `path` specifies the template directory path to load template files. func New(path ...string) *View { view := &View{ paths: garray.NewStrArray(), @@ -143,6 +143,10 @@ func New(path ...string) *View { "map": view.buildInFuncMap, "maps": view.buildInFuncMaps, "json": view.buildInFuncJson, + "plus": view.buildInFuncPlus, + "minus": view.buildInFuncMinus, + "times": view.buildInFuncTimes, + "divide": view.buildInFuncDivide, }) return view diff --git a/os/gview/gview_buildin.go b/os/gview/gview_buildin.go index c838750ee..da22f78fa 100644 --- a/os/gview/gview_buildin.go +++ b/os/gview/gview_buildin.go @@ -219,8 +219,53 @@ func (view *View) buildInFuncNl2Br(str interface{}) string { } // buildInFuncJson implements build-in template function: json , -// which encodes and returns as JSON string. +// which encodes and returns `value` as JSON string. func (view *View) buildInFuncJson(value interface{}) (string, error) { b, err := json.Marshal(value) return gconv.UnsafeBytesToStr(b), err } + +// buildInFuncPlus implements build-in template function: plus , +// which returns the result that pluses all `deltas` to `value`. +func (view *View) buildInFuncPlus(value interface{}, deltas ...interface{}) string { + result := gconv.Float64(value) + for _, v := range deltas { + result += gconv.Float64(v) + } + return gconv.String(result) +} + +// buildInFuncMinus implements build-in template function: minus , +// which returns the result that subtracts all `deltas` from `value`. +func (view *View) buildInFuncMinus(value interface{}, deltas ...interface{}) string { + result := gconv.Float64(value) + for _, v := range deltas { + result -= gconv.Float64(v) + } + return gconv.String(result) +} + +// buildInFuncTimes implements build-in template function: times , +// which returns the result that multiplies `value` by all of `values`. +func (view *View) buildInFuncTimes(value interface{}, values ...interface{}) string { + result := gconv.Float64(value) + for _, v := range values { + result *= gconv.Float64(v) + } + return gconv.String(result) +} + +// buildInFuncDivide implements build-in template function: divide , +// which returns the result that divides `value` by all of `values`. +func (view *View) buildInFuncDivide(value interface{}, values ...interface{}) string { + result := gconv.Float64(value) + for _, v := range values { + value2Float64 := gconv.Float64(v) + if value2Float64 == 0 { + // Invalid `value2`. + return "0" + } + result /= value2Float64 + } + return gconv.String(result) +} diff --git a/os/gview/gview_config.go b/os/gview/gview_config.go index 79dd6eced..cc62d1e88 100644 --- a/os/gview/gview_config.go +++ b/os/gview/gview_config.go @@ -95,7 +95,7 @@ func (view *View) SetConfigWithMap(m map[string]interface{}) error { } // SetPath sets the template directory path for template file search. -// The parameter can be absolute or relative path, but absolute path is suggested. +// The parameter `path` can be absolute or relative path, but absolute path is suggested. func (view *View) SetPath(path string) error { var ( isDir = false @@ -239,9 +239,9 @@ func (view *View) SetAutoEncode(enable bool) { view.config.AutoEncode = enable } -// BindFunc registers customized global template function named -// with given function to current view object. -// The is the function name which can be called in template content. +// BindFunc registers customized global template function named `name` +// with given function `function` to current view object. +// The `name` is the function name which can be called in template content. func (view *View) BindFunc(name string, function interface{}) { view.funcMap[name] = function // Clear global template object cache. diff --git a/os/gview/gview_instance.go b/os/gview/gview_instance.go index 7781dcd0c..8845e5d21 100644 --- a/os/gview/gview_instance.go +++ b/os/gview/gview_instance.go @@ -19,7 +19,7 @@ var ( ) // Instance returns an instance of View with default settings. -// The parameter is the name for the instance. +// The parameter `name` is the name for the instance. func Instance(name ...string) *View { key := DefaultName if len(name) > 0 && name[0] != "" { diff --git a/os/gview/gview_parse.go b/os/gview/gview_parse.go index cd50e7a8b..c8a4811ee 100644 --- a/os/gview/gview_parse.go +++ b/os/gview/gview_parse.go @@ -53,7 +53,7 @@ var ( resourceTryFolders = []string{"template/", "template", "/template", "/template/"} ) -// Parse parses given template file with given template variables +// Parse parses given template file `file` with given template variables `params` // and returns the parsed template content. func (view *View) Parse(ctx context.Context, file string, params ...Params) (result string, err error) { var tpl interface{} @@ -65,7 +65,7 @@ func (view *View) Parse(ctx context.Context, file string, params ...Params) (res content string resource *gres.File ) - // Searching the absolute file path for . + // Searching the absolute file path for `file`. path, folder, resource, err = view.searchFile(file) if err != nil { return nil @@ -100,7 +100,7 @@ func (view *View) Parse(ctx context.Context, file string, params ...Params) (res if item.content == "" { return "", nil } - // Get the template object instance for . + // Get the template object instance for `folder`. tpl, err = view.getTemplate(item.path, item.folder, fmt.Sprintf(`*%s`, gfile.Ext(item.path))) if err != nil { return "", err @@ -120,7 +120,7 @@ func (view *View) Parse(ctx context.Context, file string, params ...Params) (res return "", err } // Note that the template variable assignment cannot change the value - // of the existing or view.data because both variables are pointers. + // of the existing `params` or view.data because both variables are pointers. // It needs to merge the values of the two maps into a new map. variables := gutil.MapMergeCopy(params...) if len(view.data) > 0 { @@ -154,7 +154,7 @@ func (view *View) ParseDefault(ctx context.Context, params ...Params) (result st return view.Parse(ctx, view.config.DefaultFile, params...) } -// ParseContent parses given template content with template variables +// ParseContent parses given template content `content` with template variables `params` // and returns the parsed content in []byte. func (view *View) ParseContent(ctx context.Context, content string, params ...Params) (string, error) { // It's not necessary continuing parsing if template content is empty. @@ -188,7 +188,7 @@ func (view *View) ParseContent(ctx context.Context, content string, params ...Pa return "", err } // Note that the template variable assignment cannot change the value - // of the existing or view.data because both variables are pointers. + // of the existing `params` or view.data because both variables are pointers. // It needs to merge the values of the two maps into a new map. variables := gutil.MapMergeCopy(params...) if len(view.data) > 0 { @@ -216,10 +216,10 @@ func (view *View) ParseContent(ctx context.Context, content string, params ...Pa return result, nil } -// getTemplate returns the template object associated with given template file . +// getTemplate returns the template object associated with given template file `path`. // It uses template cache to enhance performance, that is, it will return the same template object -// with the same given . It will also automatically refresh the template cache -// if the template files under changes (recursively). +// with the same given `path`. It will also automatically refresh the template cache +// if the template files under `path` changes (recursively). func (view *View) getTemplate(filePath, folderPath, pattern string) (tpl interface{}, err error) { // Key for template cache. key := fmt.Sprintf("%s_%v", filePath, view.config.Delimiters) @@ -304,9 +304,9 @@ func (view *View) formatTemplateObjectCreatingError(filePath, tplName string, er return nil } -// searchFile returns the found absolute path for and its template folder path. -// Note that, the returned is the template folder path, but not the folder of -// the returned template file . +// searchFile returns the found absolute path for `file` and its template folder path. +// Note that, the returned `folder` is the template folder path, but not the folder of +// the returned template file `path`. func (view *View) searchFile(file string) (path string, folder string, resource *gres.File, err error) { // Firstly checking the resource manager. if !gres.IsEmpty() { diff --git a/os/gview/gview_unit_basic_test.go b/os/gview/gview_unit_basic_test.go index 4cad9247c..7bbdcd390 100644 --- a/os/gview/gview_unit_basic_test.go +++ b/os/gview/gview_unit_basic_test.go @@ -9,6 +9,7 @@ package gview_test import ( "context" "github.com/gogf/gf/encoding/ghtml" + "github.com/gogf/gf/os/gctx" "github.com/gogf/gf/os/gtime" "github.com/gogf/gf/util/gconv" "io/ioutil" @@ -426,3 +427,39 @@ func Test_BuildInFuncJson(t *testing.T) { t.Assert(r, `{"name":"john"}`) }) } + +func Test_BuildInFuncPlus(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + v := gview.New() + r, err := v.ParseContent(gctx.New(), "{{plus 1 2 3}}") + t.Assert(err, nil) + t.Assert(r, `6`) + }) +} + +func Test_BuildInFuncMinus(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + v := gview.New() + r, err := v.ParseContent(gctx.New(), "{{minus 1 2 3}}") + t.Assert(err, nil) + t.Assert(r, `-4`) + }) +} + +func Test_BuildInFuncTimes(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + v := gview.New() + r, err := v.ParseContent(gctx.New(), "{{times 1 2 3 4}}") + t.Assert(err, nil) + t.Assert(r, `24`) + }) +} + +func Test_BuildInFuncDivide(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + v := gview.New() + r, err := v.ParseContent(gctx.New(), "{{divide 8 2 2}}") + t.Assert(err, nil) + t.Assert(r, `2`) + }) +} From f9eaa8f930b17d0c57dbaa515a96bfd2b700af12 Mon Sep 17 00:00:00 2001 From: John Guo Date: Wed, 25 Aug 2021 22:31:59 +0800 Subject: [PATCH 7/8] fix issue of nested transaction missing in function operations for package gdb --- database/gdb/gdb_core.go | 4 --- database/gdb/gdb_core_underlying.go | 30 ++++++++++++++++-- database/gdb/gdb_z_mysql_transaction_test.go | 32 ++++++++++++++++++++ 3 files changed, 59 insertions(+), 7 deletions(-) diff --git a/database/gdb/gdb_core.go b/database/gdb/gdb_core.go index 7b76f5dca..58519e927 100644 --- a/database/gdb/gdb_core.go +++ b/database/gdb/gdb_core.go @@ -38,10 +38,6 @@ func (c *Core) Ctx(ctx context.Context) DB { if ctx == nil { return c.db } - // It is already set context in previous chaining operation. - if c.ctx != nil { - return c.db - } ctx = context.WithValue(ctx, ctxStrictKeyName, 1) // It makes a shallow copy of current db and changes its context for next chaining operation. var ( diff --git a/database/gdb/gdb_core_underlying.go b/database/gdb/gdb_core_underlying.go index 8e88d0938..036ed3494 100644 --- a/database/gdb/gdb_core_underlying.go +++ b/database/gdb/gdb_core_underlying.go @@ -27,10 +27,15 @@ func (c *Core) Query(sql string, args ...interface{}) (rows *sql.Rows, err error func (c *Core) DoQuery(ctx context.Context, link Link, sql string, args ...interface{}) (rows *sql.Rows, err error) { // Transaction checks. if link == nil { - if link, err = c.SlaveLink(); err != nil { + if tx := TXFromCtx(ctx, c.db.GetGroup()); tx != nil { + // Firstly, check and retrieve transaction link from context. + link = &txLink{tx.tx} + } else if link, err = c.SlaveLink(); err != nil { + // Or else it creates one from master node. return nil, err } } else if !link.IsTransaction() { + // If current link is not transaction link, it checks and retrieves transaction from context. if tx := TXFromCtx(ctx, c.db.GetGroup()); tx != nil { link = &txLink{tx.tx} } @@ -84,10 +89,15 @@ func (c *Core) Exec(sql string, args ...interface{}) (result sql.Result, err err func (c *Core) DoExec(ctx context.Context, link Link, sql string, args ...interface{}) (result sql.Result, err error) { // Transaction checks. if link == nil { - if link, err = c.MasterLink(); err != nil { + if tx := TXFromCtx(ctx, c.db.GetGroup()); tx != nil { + // Firstly, check and retrieve transaction link from context. + link = &txLink{tx.tx} + } else if link, err = c.MasterLink(); err != nil { + // Or else it creates one from master node. return nil, err } } else if !link.IsTransaction() { + // If current link is not transaction link, it checks and retrieves transaction from context. if tx := TXFromCtx(ctx, c.db.GetGroup()); tx != nil { link = &txLink{tx.tx} } @@ -170,11 +180,25 @@ func (c *Core) Prepare(sql string, execOnMaster ...bool) (*Stmt, error) { // DoPrepare calls prepare function on given link object and returns the statement object. func (c *Core) DoPrepare(ctx context.Context, link Link, sql string) (*Stmt, error) { - if link != nil && !link.IsTransaction() { + // Transaction checks. + if link == nil { + if tx := TXFromCtx(ctx, c.db.GetGroup()); tx != nil { + // Firstly, check and retrieve transaction link from context. + link = &txLink{tx.tx} + } else { + // Or else it creates one from master node. + var err error + if link, err = c.MasterLink(); err != nil { + return nil, err + } + } + } else if !link.IsTransaction() { + // If current link is not transaction link, it checks and retrieves transaction from context. if tx := TXFromCtx(ctx, c.db.GetGroup()); tx != nil { link = &txLink{tx.tx} } } + if c.GetConfig().PrepareTimeout > 0 { // DO NOT USE cancel function in prepare statement. ctx, _ = context.WithTimeout(ctx, c.GetConfig().PrepareTimeout) diff --git a/database/gdb/gdb_z_mysql_transaction_test.go b/database/gdb/gdb_z_mysql_transaction_test.go index 669055145..d12ff51e4 100644 --- a/database/gdb/gdb_z_mysql_transaction_test.go +++ b/database/gdb/gdb_z_mysql_transaction_test.go @@ -9,6 +9,7 @@ package gdb_test import ( "context" "fmt" + "github.com/gogf/gf/os/gctx" "testing" "github.com/gogf/gf/database/gdb" @@ -1116,3 +1117,34 @@ func Test_Transaction_Nested_SavePoint_RollbackTo(t *testing.T) { t.Assert(all[0]["id"], 1) }) } + +func Test_Transaction_Method(t *testing.T) { + table := createTable() + defer dropTable(table) + + gtest.C(t, func(t *gtest.T) { + var err error + err = db.Transaction(gctx.New(), func(ctx context.Context, tx *gdb.TX) error { + _, err = db.Model(table).Ctx(ctx).Data(g.Map{ + "id": 1, + "passport": "t1", + "password": "25d55ad283aa400af464c76d713c07ad", + "nickname": "T1", + "create_time": gtime.Now().String(), + }).Insert() + t.AssertNil(err) + + _, err = db.Ctx(ctx).Exec(fmt.Sprintf( + "insert into %s(`passport`,`password`,`nickname`,`create_time`,`id`) "+ + "VALUES('t2','25d55ad283aa400af464c76d713c07ad','T2','2021-08-25 21:53:00',2) ", + table)) + t.AssertNil(err) + return gerror.New("rollback") + }) + t.AssertNE(err, nil) + + count, err := db.Model(table).Count() + t.AssertNil(err) + t.Assert(count, 0) + }) +} From e0a3233ea3cc05bdc5e92e5d3a16335a0afa6250 Mon Sep 17 00:00:00 2001 From: John Date: Thu, 26 Aug 2021 00:11:27 +0800 Subject: [PATCH 8/8] improve package gcron --- go.sum | 24 +++---------- os/gcron/gcron_schedule.go | 73 +++++++++++++++++++++----------------- 2 files changed, 45 insertions(+), 52 deletions(-) diff --git a/go.sum b/go.sum index 181576aa5..45bd2270a 100644 --- a/go.sum +++ b/go.sum @@ -2,10 +2,8 @@ github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/clbanning/mxj v1.8.5-0.20200714211355-ff02cfb8ea28 h1:LdXxtjzvZYhhUaonAaAKArG3pyC67kGL3YY+6hGG8G4= github.com/clbanning/mxj v1.8.5-0.20200714211355-ff02cfb8ea28/go.mod h1:BVjHeAH+rl9rs6f+QIpeRl0tfu10SXn1pUSa5PVGJng= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/fatih/color v1.12.0 h1:mRhaKNwANqRgUBGKmnI5ZxEk7QXmjQeCcuYFMX2bfcc= github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= @@ -20,25 +18,16 @@ github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0U github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/grokify/html-strip-tags-go v0.0.0-20190921062105-daaa06bf1aaf h1:wIOAyJMMen0ELGiFzlmqxdcV1yGbkyHBAB6PolcNbLA= github.com/grokify/html-strip-tags-go v0.0.0-20190921062105-daaa06bf1aaf/go.mod h1:2Su6romC5/1VXOQMaWL2yb618ARB8iVo6/DR99A6d78= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8= github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= -github.com/mattn/go-runewidth v0.0.10 h1:CoZ3S2P7pvtP45xOtBw+/mDL2z0RKI576gSkzRRpdGg= -github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rivo/uniseg v0.1.0 h1:+2KBaVoUmb9XzDsrx/Ct0W/EYOSFf/nWTauy++DprtY= -github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= @@ -55,19 +44,16 @@ golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da h1:b3NXsE2LusjYGGjL5bxEVZZORm/YEFFrWFjR8eFrw/c= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210616094352-59db8d763f22 h1:RqytpXGR1iVNX7psjB3ff8y7sNFinVFvkx1c8SjBkio= -golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= diff --git a/os/gcron/gcron_schedule.go b/os/gcron/gcron_schedule.go index 7ff33d03e..8bfb68a35 100644 --- a/os/gcron/gcron_schedule.go +++ b/os/gcron/gcron_schedule.go @@ -33,6 +33,10 @@ type cronSchedule struct { const ( // regular expression for cron pattern, which contains 6 parts of time units. regexForCron = `^([\-/\d\*\?,]+)\s+([\-/\d\*\?,]+)\s+([\-/\d\*\?,]+)\s+([\-/\d\*\?,]+)\s+([\-/\d\*\?,A-Za-z]+)\s+([\-/\d\*\?,A-Za-z]+)$` + + patternItemTypeUnknown = iota + patternItemTypeWeek + patternItemTypeMonth ) var ( @@ -103,37 +107,37 @@ func newSchedule(pattern string) (*cronSchedule, error) { pattern: pattern, } // Second. - if m, err := parseItem(match[1], 0, 59, false); err != nil { + if m, err := parsePatternItem(match[1], 0, 59, false); err != nil { return nil, err } else { schedule.second = m } // Minute. - if m, err := parseItem(match[2], 0, 59, false); err != nil { + if m, err := parsePatternItem(match[2], 0, 59, false); err != nil { return nil, err } else { schedule.minute = m } // Hour. - if m, err := parseItem(match[3], 0, 23, false); err != nil { + if m, err := parsePatternItem(match[3], 0, 23, false); err != nil { return nil, err } else { schedule.hour = m } // Day. - if m, err := parseItem(match[4], 1, 31, true); err != nil { + if m, err := parsePatternItem(match[4], 1, 31, true); err != nil { return nil, err } else { schedule.day = m } // Month. - if m, err := parseItem(match[5], 1, 12, false); err != nil { + if m, err := parsePatternItem(match[5], 1, 12, false); err != nil { return nil, err } else { schedule.month = m } // Week. - if m, err := parseItem(match[6], 0, 6, true); err != nil { + if m, err := parsePatternItem(match[6], 0, 6, true); err != nil { return nil, err } else { schedule.week = m @@ -144,52 +148,55 @@ func newSchedule(pattern string) (*cronSchedule, error) { } } -// parseItem parses every item in the pattern and returns the result as map. -func parseItem(item string, min int, max int, allowQuestionMark bool) (map[int]struct{}, error) { +// parsePatternItem parses every item in the pattern and returns the result as map, which is used for indexing. +func parsePatternItem(item string, min int, max int, allowQuestionMark bool) (map[int]struct{}, error) { m := make(map[int]struct{}, max-min+1) if item == "*" || (allowQuestionMark && item == "?") { for i := min; i <= max; i++ { m[i] = struct{}{} } } else { + // Like: MON,FRI for _, item := range strings.Split(item, ",") { - interval := 1 - intervalArray := strings.Split(item, "/") + var ( + interval = 1 + intervalArray = strings.Split(item, "/") + ) if len(intervalArray) == 2 { - if i, err := strconv.Atoi(intervalArray[1]); err != nil { + if number, err := strconv.Atoi(intervalArray[1]); err != nil { return nil, gerror.NewCodef(gcode.CodeInvalidParameter, `invalid pattern item: "%s"`, item) } else { - interval = i + interval = number } } var ( rangeMin = min rangeMax = max - fieldType = byte(0) + itemType = patternItemTypeUnknown rangeArray = strings.Split(intervalArray[0], "-") // Like: 1-30, JAN-DEC ) switch max { case 6: // It's checking week field. - fieldType = 'w' + itemType = patternItemTypeWeek case 12: // It's checking month field. - fieldType = 'm' + itemType = patternItemTypeMonth } // Eg: */5 if rangeArray[0] != "*" { - if i, err := parseItemValue(rangeArray[0], fieldType); err != nil { + if number, err := parsePatternItemValue(rangeArray[0], itemType); err != nil { return nil, gerror.NewCodef(gcode.CodeInvalidParameter, `invalid pattern item: "%s"`, item) } else { - rangeMin = i - rangeMax = i + rangeMin = number + rangeMax = number } } if len(rangeArray) == 2 { - if i, err := parseItemValue(rangeArray[1], fieldType); err != nil { + if number, err := parsePatternItemValue(rangeArray[1], itemType); err != nil { return nil, gerror.NewCodef(gcode.CodeInvalidParameter, `invalid pattern item: "%s"`, item) } else { - rangeMax = i + rangeMax = number } } for i := rangeMin; i <= rangeMax; i += interval { @@ -200,24 +207,24 @@ func parseItem(item string, min int, max int, allowQuestionMark bool) (map[int]s return m, nil } -// parseItemValue parses the field value to a number according to its field type. -func parseItemValue(value string, fieldType byte) (int, error) { +// parsePatternItemValue parses the field value to a number according to its field type. +func parsePatternItemValue(value string, itemType int) (int, error) { if gregex.IsMatchString(`^\d+$`, value) { - // Pure number. - if i, err := strconv.Atoi(value); err == nil { - return i, nil + // It is pure number. + if number, err := strconv.Atoi(value); err == nil { + return number, nil } } else { - // Check if contains letter, + // Check if it contains letter, // it converts the value to number according to predefined map. - switch fieldType { - case 'm': - if i, ok := monthMap[strings.ToLower(value)]; ok { - return i, nil + switch itemType { + case patternItemTypeWeek: + if number, ok := monthMap[strings.ToLower(value)]; ok { + return number, nil } - case 'w': - if i, ok := weekMap[strings.ToLower(value)]; ok { - return i, nil + case patternItemTypeMonth: + if number, ok := weekMap[strings.ToLower(value)]; ok { + return number, nil } } }