From 961ca0879d0fb513218f5523a27eb3e2b9a20fd7 Mon Sep 17 00:00:00 2001 From: John Date: Tue, 2 Jul 2019 16:56:10 +0800 Subject: [PATCH 1/7] improve gbase64 --- g/encoding/gbase64/gbase64.go | 32 +++++++++++++++++++++++-------- g/net/ghttp/ghttp_request_auth.go | 7 ++++--- geg/other/test.go | 17 +++++++--------- 3 files changed, 35 insertions(+), 21 deletions(-) diff --git a/g/encoding/gbase64/gbase64.go b/g/encoding/gbase64/gbase64.go index 1ed8e92ab..1ce7267ae 100644 --- a/g/encoding/gbase64/gbase64.go +++ b/g/encoding/gbase64/gbase64.go @@ -4,20 +4,36 @@ // If a copy of the MIT was not distributed with this file, // You can obtain one at https://github.com/gogf/gf. -// Package gbase64 provides useful API for BASE64 encoding/decoding algorithms. +// Package gbase64 provides useful API for BASE64 encoding/decoding algorithm. package gbase64 import ( "encoding/base64" ) -// base64 encode -func Encode(str string) string { - return base64.StdEncoding.EncodeToString([]byte(str)) +// Encode encodes bytes with BASE64 algorithm. +func Encode(src []byte) []byte { + dst := make([]byte, base64.StdEncoding.EncodedLen(len(src))) + base64.StdEncoding.Encode(dst, src) + return dst } -// base64 decode -func Decode(str string) (string, error) { - s, e := base64.StdEncoding.DecodeString(str) - return string(s), e +// Decode decodes bytes with BASE64 algorithm. +func Decode(dst []byte) ([]byte, error) { + src := make([]byte, base64.StdEncoding.DecodedLen(len(dst))) + _, err := base64.StdEncoding.Decode(src, dst) + if err != nil { + return nil, err + } + return src, nil +} + +// EncodeString encodes bytes with BASE64 algorithm. +func EncodeString(src []byte) string { + return string(Encode(src)) +} + +// DecodeString decodes string with BASE64 algorithm. +func DecodeString(str string) ([]byte, error) { + return Decode([]byte(str)) } diff --git a/g/net/ghttp/ghttp_request_auth.go b/g/net/ghttp/ghttp_request_auth.go index 219c2c391..a097f4327 100644 --- a/g/net/ghttp/ghttp_request_auth.go +++ b/g/net/ghttp/ghttp_request_auth.go @@ -8,9 +8,10 @@ package ghttp import ( "fmt" - "github.com/gogf/gf/g/encoding/gbase64" "net/http" "strings" + + "github.com/gogf/gf/g/encoding/gbase64" ) // 设置Basic Auth校验提示 @@ -40,12 +41,12 @@ func (r *Request) BasicAuth(user, pass string, tips ...string) bool { } switch authArray[0] { case "Basic": - authStr, err := gbase64.Decode(authArray[1]) + authBytes, err := gbase64.DecodeString(authArray[1]) if err != nil { r.Response.WriteStatus(http.StatusForbidden, err.Error()) return false } - authArray := strings.SplitN(string(authStr), ":", 2) + authArray := strings.SplitN(string(authBytes), ":", 2) if len(authArray) != 2 { r.Response.WriteStatus(http.StatusForbidden) return false diff --git a/geg/other/test.go b/geg/other/test.go index 1b134b6c5..073e90196 100644 --- a/geg/other/test.go +++ b/geg/other/test.go @@ -1,7 +1,9 @@ package main import ( - "github.com/gogf/gf/g" + "fmt" + + "github.com/gogf/gf/g/encoding/gbase64" "github.com/gogf/gf/g/net/ghttp" ) @@ -12,13 +14,8 @@ func (order *Order) Get(r *ghttp.Request) { } func main() { - s := g.Server() - s.BindHookHandlerByMap("/api.v1/*any", map[string]ghttp.HandlerFunc{ - "BeforeServe": func(r *ghttp.Request) { - r.Response.CORSDefault() - }, - }) - s.BindObjectRest("/api.v1/{.struct}", new(Order)) - s.SetPort(8199) - s.Run() + s := `BgsnyD6IBEzExNDUzNjEzNDg4MzYxOTMzNjQSShAJGgzns7vnu5/pgJrnn6UiOGh0dHA6Ly9wdWItbWVkLWxvZ28uaW1ncy5tZWRsaW5rZXIubmV0L25ldy1zeXN0ZW1AM3gucG5` + b, err := gbase64.DecodeString(s) + fmt.Println(err) + fmt.Println(string(b)) } From 5572ab858e1c3a220a7a8d16a608082822ce66cc Mon Sep 17 00:00:00 2001 From: John Date: Tue, 2 Jul 2019 17:30:18 +0800 Subject: [PATCH 2/7] improve gbase64 --- g/encoding/gbase64/gbase64.go | 4 ++-- g/encoding/gbase64/gbase64_test.go | 20 ++++++++++++++------ geg/other/test.go | 19 +++---------------- 3 files changed, 19 insertions(+), 24 deletions(-) diff --git a/g/encoding/gbase64/gbase64.go b/g/encoding/gbase64/gbase64.go index 1ce7267ae..e19733002 100644 --- a/g/encoding/gbase64/gbase64.go +++ b/g/encoding/gbase64/gbase64.go @@ -21,11 +21,11 @@ func Encode(src []byte) []byte { // Decode decodes bytes with BASE64 algorithm. func Decode(dst []byte) ([]byte, error) { src := make([]byte, base64.StdEncoding.DecodedLen(len(dst))) - _, err := base64.StdEncoding.Decode(src, dst) + n, err := base64.StdEncoding.Decode(src, dst) if err != nil { return nil, err } - return src, nil + return src[:n], nil } // EncodeString encodes bytes with BASE64 algorithm. diff --git a/g/encoding/gbase64/gbase64_test.go b/g/encoding/gbase64/gbase64_test.go index 0d6afb304..5e6cb4e91 100644 --- a/g/encoding/gbase64/gbase64_test.go +++ b/g/encoding/gbase64/gbase64_test.go @@ -6,9 +6,10 @@ package gbase64_test import ( + "testing" + "github.com/gogf/gf/g/encoding/gbase64" "github.com/gogf/gf/g/test/gtest" - "testing" ) type testpair struct { @@ -42,10 +43,17 @@ var pairs = []testpair{ } func TestBase64(t *testing.T) { - for k := range pairs { - gtest.Assert(gbase64.Encode(pairs[k].decoded), pairs[k].encoded) + gtest.Case(t, func() { + for k := range pairs { + // []byte + gtest.Assert(gbase64.Encode([]byte(pairs[k].decoded)), []byte(pairs[k].encoded)) + e1, _ := gbase64.Decode([]byte(pairs[k].encoded)) + gtest.Assert(e1, []byte(pairs[k].decoded)) - e, _ := gbase64.Decode(pairs[k].encoded) - gtest.Assert(e, pairs[k].decoded) - } + // string + gtest.Assert(gbase64.EncodeString([]byte(pairs[k].decoded)), pairs[k].encoded) + e2, _ := gbase64.DecodeString(pairs[k].encoded) + gtest.Assert(e2, []byte(pairs[k].decoded)) + } + }) } diff --git a/geg/other/test.go b/geg/other/test.go index 073e90196..4a403748a 100644 --- a/geg/other/test.go +++ b/geg/other/test.go @@ -1,21 +1,8 @@ package main -import ( - "fmt" - - "github.com/gogf/gf/g/encoding/gbase64" - "github.com/gogf/gf/g/net/ghttp" -) - -type Order struct{} - -func (order *Order) Get(r *ghttp.Request) { - r.Response.Write("GET") -} +import "fmt" func main() { - s := `BgsnyD6IBEzExNDUzNjEzNDg4MzYxOTMzNjQSShAJGgzns7vnu5/pgJrnn6UiOGh0dHA6Ly9wdWItbWVkLWxvZ28uaW1ncy5tZWRsaW5rZXIubmV0L25ldy1zeXN0ZW1AM3gucG5` - b, err := gbase64.DecodeString(s) - fmt.Println(err) - fmt.Println(string(b)) + s := "123" + fmt.Println([]byte(s)) } From 31921905a97b337510c9f6cc7d4fbaa1c5c2c42a Mon Sep 17 00:00:00 2001 From: John Date: Tue, 2 Jul 2019 17:40:16 +0800 Subject: [PATCH 3/7] update unit test cases for gaes because of changes of gbase64 --- g/crypto/gaes/gaes_test.go | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/g/crypto/gaes/gaes_test.go b/g/crypto/gaes/gaes_test.go index 119900065..249e99c87 100644 --- a/g/crypto/gaes/gaes_test.go +++ b/g/crypto/gaes/gaes_test.go @@ -9,20 +9,21 @@ package gaes_test import ( - "github.com/gogf/gf/g/encoding/gbase64" "testing" + "github.com/gogf/gf/g/encoding/gbase64" + "github.com/gogf/gf/g/crypto/gaes" "github.com/gogf/gf/g/test/gtest" ) var ( content = []byte("pibigstar") - content_16, _ = gbase64.Decode("v1jqsGHId/H8onlVHR8Vaw==") - content_24, _ = gbase64.Decode("0TXOaj5KMoLhNWmJ3lxY1A==") - content_32, _ = gbase64.Decode("qM/Waw1kkWhrwzek24rCSA==") - content_16_iv, _ = gbase64.Decode("DqQUXiHgW/XFb6Qs98+hrA==") - content_32_iv, _ = gbase64.Decode("ZuLgAOii+lrD5KJoQ7yQ8Q==") + content_16, _ = gbase64.DecodeString("v1jqsGHId/H8onlVHR8Vaw==") + content_24, _ = gbase64.DecodeString("0TXOaj5KMoLhNWmJ3lxY1A==") + content_32, _ = gbase64.DecodeString("qM/Waw1kkWhrwzek24rCSA==") + content_16_iv, _ = gbase64.DecodeString("DqQUXiHgW/XFb6Qs98+hrA==") + content_32_iv, _ = gbase64.DecodeString("ZuLgAOii+lrD5KJoQ7yQ8Q==") // iv 长度必须等于blockSize,只能为16 iv = []byte("Hello My GoFrame") key_16 = []byte("1234567891234567") @@ -35,7 +36,7 @@ var ( // cfb模式blockSize补位长度, add by zseeker padding_size = 16 - len(content) - content_16_cfb, _ = gbase64.Decode("oSmget3aBDT1nJnBp8u6kA==") + content_16_cfb, _ = gbase64.DecodeString("oSmget3aBDT1nJnBp8u6kA==") ) func TestEncrypt(t *testing.T) { From e63e989d41a7becfc350ffeea511650b046e2efb Mon Sep 17 00:00:00 2001 From: John Date: Tue, 2 Jul 2019 19:22:06 +0800 Subject: [PATCH 4/7] fix issue of dead lock in gcache.doSetWithLockCheck --- g/os/gcache/gcache_mem_cache.go | 8 ++++---- geg/other/test.go | 26 +++++++++++++++++++++++--- 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/g/os/gcache/gcache_mem_cache.go b/g/os/gcache/gcache_mem_cache.go index fcdee9716..edb648c10 100644 --- a/g/os/gcache/gcache_mem_cache.go +++ b/g/os/gcache/gcache_mem_cache.go @@ -7,14 +7,15 @@ package gcache import ( + "math" + "sync" + "github.com/gogf/gf/g/container/glist" "github.com/gogf/gf/g/container/gset" "github.com/gogf/gf/g/container/gtype" "github.com/gogf/gf/g/os/gtime" "github.com/gogf/gf/g/os/gtimer" "github.com/gogf/gf/g/util/gconv" - "math" - "sync" ) // Internal cache object. @@ -121,8 +122,8 @@ func (c *memCache) Set(key interface{}, value interface{}, expire int) { func (c *memCache) doSetWithLockCheck(key interface{}, value interface{}, expire int) interface{} { expireTimestamp := c.getInternalExpire(expire) c.dataMu.Lock() + defer c.dataMu.Unlock() if v, ok := c.data[key]; ok && !v.IsExpired() { - c.dataMu.Unlock() return v.v } if f, ok := value.(func() interface{}); ok { @@ -132,7 +133,6 @@ func (c *memCache) doSetWithLockCheck(key interface{}, value interface{}, expire return nil } c.data[key] = memCacheItem{v: value, e: expireTimestamp} - c.dataMu.Unlock() c.eventList.PushBack(&memCacheEvent{k: key, e: expireTimestamp}) return value } diff --git a/geg/other/test.go b/geg/other/test.go index 4a403748a..42a1b9963 100644 --- a/geg/other/test.go +++ b/geg/other/test.go @@ -1,8 +1,28 @@ package main -import "fmt" +import ( + "github.com/gogf/gf/g/os/glog" + + "github.com/gogf/gf/g/os/gcache" +) + +func localCache() { + result := gcache.GetOrSetFunc("test.key.1", func() interface{} { + return nil + }, 1000*60*2) + if result == nil { + glog.Error("未获取到值") + } else { + glog.Infofln("result is $v", result) + } +} + +func TestCache() { + for i := 0; i < 100; i++ { + localCache() + } +} func main() { - s := "123" - fmt.Println([]byte(s)) + TestCache() } From c948f0c287d5af1d6fcd03f6f39cb9c1e93f27e4 Mon Sep 17 00:00:00 2001 From: John Date: Tue, 2 Jul 2019 23:14:40 +0800 Subject: [PATCH 5/7] improve gdb.Update/Delete feature to support orderby/limit features --- g/database/gdb/gdb.go | 3 +- g/database/gdb/gdb_base.go | 51 ++++++++-------- g/database/gdb/gdb_func.go | 11 ++-- g/database/gdb/gdb_model.go | 59 +++++++++++-------- g/database/gdb/gdb_transaction.go | 13 +++- g/database/gdb/gdb_unit_init_test.go | 19 +++--- g/database/gdb/gdb_unit_model_test.go | 46 ++++++++++++--- .../{orm => gdb}/mssql/gdb_sqlserver.go | 0 geg/database/{orm => gdb}/mysql/config.toml | 0 geg/database/{orm => gdb}/mysql/gdb.go | 0 geg/database/{orm => gdb}/mysql/gdb_binary.go | 0 geg/database/{orm => gdb}/mysql/gdb_cache.go | 0 geg/database/{orm => gdb}/mysql/gdb_config.go | 0 .../{orm => gdb}/mysql/gdb_datetime.go | 0 geg/database/{orm => gdb}/mysql/gdb_debug.go | 6 +- geg/database/{orm => gdb}/mysql/gdb_insert.go | 0 .../{orm => gdb}/mysql/gdb_json_xml.go | 0 geg/database/{orm => gdb}/mysql/gdb_pool.go | 0 .../{orm => gdb}/mysql/gdb_update_union.go | 0 geg/database/{orm => gdb}/mysql/gdb_value.go | 0 geg/database/{orm => gdb}/oracle/gdb.go | 0 geg/database/{orm => gdb}/sqlite/sqlite.go | 0 geg/database/{redis => gredis}/config.toml | 0 geg/database/{redis => gredis}/gredis.go | 0 geg/database/{redis => gredis}/gredis2.go | 0 .../{redis => gredis}/gredis_conn_do.go | 0 .../{redis => gredis}/gredis_conn_do_var.go | 0 .../{redis => gredis}/gredis_conn_send.go | 0 .../{redis => gredis}/gredis_conn_send_var.go | 0 .../gredis_conn_subscribe.go | 0 .../gredis_conn_subscribe_var.go | 0 31 files changed, 136 insertions(+), 72 deletions(-) rename geg/database/{orm => gdb}/mssql/gdb_sqlserver.go (100%) rename geg/database/{orm => gdb}/mysql/config.toml (100%) rename geg/database/{orm => gdb}/mysql/gdb.go (100%) rename geg/database/{orm => gdb}/mysql/gdb_binary.go (100%) rename geg/database/{orm => gdb}/mysql/gdb_cache.go (100%) rename geg/database/{orm => gdb}/mysql/gdb_config.go (100%) rename geg/database/{orm => gdb}/mysql/gdb_datetime.go (100%) rename geg/database/{orm => gdb}/mysql/gdb_debug.go (94%) rename geg/database/{orm => gdb}/mysql/gdb_insert.go (100%) rename geg/database/{orm => gdb}/mysql/gdb_json_xml.go (100%) rename geg/database/{orm => gdb}/mysql/gdb_pool.go (100%) rename geg/database/{orm => gdb}/mysql/gdb_update_union.go (100%) rename geg/database/{orm => gdb}/mysql/gdb_value.go (100%) rename geg/database/{orm => gdb}/oracle/gdb.go (100%) rename geg/database/{orm => gdb}/sqlite/sqlite.go (100%) rename geg/database/{redis => gredis}/config.toml (100%) rename geg/database/{redis => gredis}/gredis.go (100%) rename geg/database/{redis => gredis}/gredis2.go (100%) rename geg/database/{redis => gredis}/gredis_conn_do.go (100%) rename geg/database/{redis => gredis}/gredis_conn_do_var.go (100%) rename geg/database/{redis => gredis}/gredis_conn_send.go (100%) rename geg/database/{redis => gredis}/gredis_conn_send_var.go (100%) rename geg/database/{redis => gredis}/gredis_conn_subscribe.go (100%) rename geg/database/{redis => gredis}/gredis_conn_subscribe_var.go (100%) diff --git a/g/database/gdb/gdb.go b/g/database/gdb/gdb.go index d4c14ce8c..fa30812e3 100644 --- a/g/database/gdb/gdb.go +++ b/g/database/gdb/gdb.go @@ -14,13 +14,14 @@ import ( "database/sql" "errors" "fmt" + "time" + "github.com/gogf/gf/g/container/gmap" "github.com/gogf/gf/g/container/gring" "github.com/gogf/gf/g/container/gtype" "github.com/gogf/gf/g/container/gvar" "github.com/gogf/gf/g/os/gcache" "github.com/gogf/gf/g/util/grand" - "time" ) // 数据库操作接口 diff --git a/g/database/gdb/gdb_base.go b/g/database/gdb/gdb_base.go index 21708c0ac..045d5e450 100644 --- a/g/database/gdb/gdb_base.go +++ b/g/database/gdb/gdb_base.go @@ -11,13 +11,14 @@ import ( "database/sql" "errors" "fmt" + "reflect" + "strings" + "github.com/gogf/gf/g/container/gvar" "github.com/gogf/gf/g/os/gcache" "github.com/gogf/gf/g/os/gtime" "github.com/gogf/gf/g/text/gregex" "github.com/gogf/gf/g/util/gconv" - "reflect" - "strings" ) const ( @@ -498,7 +499,10 @@ func (bs *dbBase) doBatchInsert(link dbLink, table string, list interface{}, opt // CURD操作:数据更新,统一采用sql预处理。 // data参数支持string/map/struct/*struct类型。 func (bs *dbBase) Update(table string, data interface{}, condition interface{}, args ...interface{}) (sql.Result, error) { - newWhere, newArgs := formatCondition(condition, args) + newWhere, newArgs := formatWhere(condition, args) + if newWhere != "" { + newWhere = " WHERE " + newWhere + } return bs.db.doUpdate(nil, table, data, newWhere, newArgs...) } @@ -537,15 +541,15 @@ func (bs *dbBase) doUpdate(link dbLink, table string, data interface{}, conditio return nil, err } } - if len(condition) == 0 { - return bs.db.doExec(link, fmt.Sprintf("UPDATE %s SET %s", table, updates), args...) - } - return bs.db.doExec(link, fmt.Sprintf("UPDATE %s SET %s WHERE %s", table, updates, condition), args...) + return bs.db.doExec(link, fmt.Sprintf("UPDATE %s SET %s%s", table, updates, condition), args...) } // CURD操作:删除数据 func (bs *dbBase) Delete(table string, condition interface{}, args ...interface{}) (result sql.Result, err error) { - newWhere, newArgs := formatCondition(condition, args) + newWhere, newArgs := formatWhere(condition, args) + if newWhere != "" { + newWhere = " WHERE " + newWhere + } return bs.db.doDelete(nil, table, newWhere, newArgs...) } @@ -556,10 +560,7 @@ func (bs *dbBase) doDelete(link dbLink, table string, condition string, args ... return nil, err } } - if len(condition) == 0 { - return bs.db.doExec(link, fmt.Sprintf("DELETE FROM %s", table), args...) - } - return bs.db.doExec(link, fmt.Sprintf("DELETE FROM %s WHERE %s", table, condition), args...) + return bs.db.doExec(link, fmt.Sprintf("DELETE FROM %s%s", table, condition), args...) } // 获得缓存对象 @@ -570,12 +571,15 @@ func (bs *dbBase) getCache() *gcache.Cache { // 将数据查询的列表数据*sql.Rows转换为Result类型 func (bs *dbBase) rowsToResult(rows *sql.Rows) (Result, error) { // 列信息列表, 名称与类型 - types := make([]string, 0) - columns := make([]string, 0) - columnTypes, _ := rows.ColumnTypes() - for _, t := range columnTypes { - types = append(types, t.DatabaseTypeName()) - columns = append(columns, t.Name()) + columnTypes, err := rows.ColumnTypes() + if err != nil { + return nil, err + } + types := make([]string, len(columnTypes)) + columns := make([]string, len(columnTypes)) + for k, v := range columnTypes { + types[k] = v.DatabaseTypeName() + columns[k] = v.Name() } // 返回结构组装 values := make([]sql.RawBytes, len(columns)) @@ -589,14 +593,15 @@ func (bs *dbBase) rowsToResult(rows *sql.Rows) (Result, error) { return records, err } row := make(Record) - // 注意col字段是一个[]byte类型(slice类型本身是一个指针),多个记录循环时该变量指向的是同一个内存地址 - for i, col := range values { - if col == nil { + // 注意col字段是一个[]byte类型(slice类型本身是一个引用类型), + // 多个记录循环时该变量指向的是同一个内存地址 + for i, column := range values { + if column == nil { row[columns[i]] = gvar.New(nil, true) } else { // 由于 sql.RawBytes 是slice类型, 这里必须使用值复制 - v := make([]byte, len(col)) - copy(v, col) + v := make([]byte, len(column)) + copy(v, column) row[columns[i]] = gvar.New(bs.db.convertValue(v, types[i]), true) } } diff --git a/g/database/gdb/gdb_func.go b/g/database/gdb/gdb_func.go index a225719d5..e874fbb51 100644 --- a/g/database/gdb/gdb_func.go +++ b/g/database/gdb/gdb_func.go @@ -10,14 +10,15 @@ import ( "bytes" "errors" "fmt" + "reflect" + "strings" + "time" + "github.com/gogf/gf/g/os/glog" "github.com/gogf/gf/g/os/gtime" "github.com/gogf/gf/g/text/gregex" "github.com/gogf/gf/g/text/gstr" "github.com/gogf/gf/g/util/gconv" - "reflect" - "strings" - "time" ) // Type assert api for String(). @@ -25,8 +26,8 @@ type apiString interface { String() string } -// 格式化SQL查询条件 -func formatCondition(where interface{}, args []interface{}) (newWhere string, newArgs []interface{}) { +// 格式化Where查询条件 +func formatWhere(where interface{}, args []interface{}) (newWhere string, newArgs []interface{}) { // 条件字符串处理 buffer := bytes.NewBuffer(nil) // 使用反射进行类型判断 diff --git a/g/database/gdb/gdb_model.go b/g/database/gdb/gdb_model.go index d64b43558..b9168016f 100644 --- a/g/database/gdb/gdb_model.go +++ b/g/database/gdb/gdb_model.go @@ -12,8 +12,9 @@ import ( "database/sql" "errors" "fmt" - "github.com/gogf/gf/g/util/gconv" "reflect" + + "github.com/gogf/gf/g/util/gconv" ) // 数据库链式操作模型对象 @@ -29,13 +30,13 @@ type Model struct { orderBy string // 排序语句 start int // 分页开始 limit int // 分页条数 - data interface{} // 操作记录(支持Map/List/string类型) + data interface{} // 操作数据(注意仅支持Map/List/string类型) batch int // 批量操作条数 filter bool // 是否按照表字段过滤data参数 cacheEnabled bool // 当前SQL操作是否开启查询缓存功能 cacheTime int // 查询缓存时间 cacheName string // 查询缓存名称 - safe bool // 当前模型是否运行安全模式(可修改当前模型,否则每一次链式操作都是返回新的模型对象) + safe bool // 当前模型是否安全模式(默认非安全表示链式操作直接修改当前模型属性;否则每一次链式操作都是返回新的模型对象) } // 链式操作,数据表字段,可支持多个表,以半角逗号连接 @@ -45,6 +46,7 @@ func (bs *dbBase) Table(tables string) *Model { tablesInit: tables, tables: tables, fields: "*", + start: -1, safe: false, } } @@ -149,7 +151,7 @@ func (md *Model) Where(where interface{}, args ...interface{}) *Model { if model.where != "" { return md.And(where, args...) } - newWhere, newArgs := formatCondition(where, args) + newWhere, newArgs := formatWhere(where, args) model.where = newWhere model.whereArgs = newArgs return model @@ -158,7 +160,7 @@ func (md *Model) Where(where interface{}, args ...interface{}) *Model { // 链式操作,添加AND条件到Where中 func (md *Model) And(where interface{}, args ...interface{}) *Model { model := md.getModel() - newWhere, newArgs := formatCondition(where, args) + newWhere, newArgs := formatWhere(where, args) if len(model.where) > 0 && model.where[0] == '(' { model.where = fmt.Sprintf(`%s AND (%s)`, model.where, newWhere) } else { @@ -171,7 +173,7 @@ func (md *Model) And(where interface{}, args ...interface{}) *Model { // 链式操作,添加OR条件到Where中 func (md *Model) Or(where interface{}, args ...interface{}) *Model { model := md.getModel() - newWhere, newArgs := formatCondition(where, args) + newWhere, newArgs := formatWhere(where, args) if len(model.where) > 0 && model.where[0] == '(' { model.where = fmt.Sprintf(`%s OR (%s)`, model.where, newWhere) } else { @@ -195,11 +197,20 @@ func (md *Model) OrderBy(orderBy string) *Model { return model } -// 链式操作,limit -func (md *Model) Limit(start int, limit int) *Model { +// 链式操作,limit。 +// +// 如果给定一个参数,那么生成的SQL为:LIMIT limit[0] +// +// 如果给定两个参数,那么生成的SQL为:LIMIT limit[0], limit[1] +func (md *Model) Limit(limit ...int) *Model { model := md.getModel() - model.start = start - model.limit = limit + switch len(limit) { + case 1: + model.limit = limit[0] + case 2: + model.start = limit[0] + model.limit = limit[1] + } return model } @@ -425,9 +436,9 @@ func (md *Model) Update() (result sql.Result, err error) { } } if md.tx == nil { - return md.db.doUpdate(nil, md.tables, md.data, md.where, md.whereArgs...) + return md.db.doUpdate(nil, md.tables, md.data, md.getConditionSql(), md.whereArgs...) } else { - return md.tx.doUpdate(md.tables, md.data, md.where, md.whereArgs...) + return md.tx.doUpdate(md.tables, md.data, md.getConditionSql(), md.whereArgs...) } } @@ -439,9 +450,9 @@ func (md *Model) Delete() (result sql.Result, err error) { } }() if md.tx == nil { - return md.db.doDelete(nil, md.tables, md.where, md.whereArgs...) + return md.db.doDelete(nil, md.tables, md.getConditionSql(), md.whereArgs...) } else { - return md.tx.doDelete(md.tables, md.where, md.whereArgs...) + return md.tx.doDelete(md.tables, md.getConditionSql(), md.whereArgs...) } } @@ -452,7 +463,7 @@ func (md *Model) Select() (Result, error) { // 链式操作,查询所有记录 func (md *Model) All() (Result, error) { - return md.getAll(md.getFormattedSql(), md.whereArgs...) + return md.getAll(fmt.Sprintf("SELECT %s FROM %s %s", md.fields, md.tables, md.getConditionSql()), md.whereArgs...) } // 链式操作,查询单条记录 @@ -530,7 +541,7 @@ func (md *Model) Count() (int, error) { } else { md.fields = fmt.Sprintf(`COUNT(%s)`, md.fields) } - s := md.getFormattedSql() + s := fmt.Sprintf("SELECT %s FROM %s %s", md.fields, md.tables, md.getConditionSql()) if len(md.groupBy) > 0 { s = fmt.Sprintf("SELECT COUNT(1) FROM (%s) count_alias", s) } @@ -583,12 +594,9 @@ func (md *Model) checkAndRemoveCache() { } } -// 格式化当前输入参数,返回可执行的SQL语句(不带参数) -func (md *Model) getFormattedSql() string { - if md.fields == "" { - md.fields = "*" - } - s := fmt.Sprintf("SELECT %s FROM %s", md.fields, md.tables) +// 格式化当前输入参数,返回SQL条件语句(不带参数) +func (md *Model) getConditionSql() string { + s := "" if md.where != "" { s += " WHERE " + md.where } @@ -599,7 +607,12 @@ func (md *Model) getFormattedSql() string { s += " ORDER BY " + md.orderBy } if md.limit != 0 { - s += fmt.Sprintf(" LIMIT %d, %d", md.start, md.limit) + if md.start >= 0 { + s += fmt.Sprintf(" LIMIT %d, %d", md.start, md.limit) + } else { + s += fmt.Sprintf(" LIMIT %d", md.limit) + } + } return s } diff --git a/g/database/gdb/gdb_transaction.go b/g/database/gdb/gdb_transaction.go index 84922f10b..b91cac7c5 100644 --- a/g/database/gdb/gdb_transaction.go +++ b/g/database/gdb/gdb_transaction.go @@ -9,8 +9,9 @@ package gdb import ( "database/sql" "fmt" - "github.com/gogf/gf/g/text/gregex" "reflect" + + "github.com/gogf/gf/g/text/gregex" ) // 数据库事务对象 @@ -164,7 +165,10 @@ func (tx *TX) BatchSave(table string, list interface{}, batch ...int) (sql.Resul // CURD操作:数据更新,统一采用sql预处理, // data参数支持字符串或者关联数组类型,内部会自行做判断处理. func (tx *TX) Update(table string, data interface{}, condition interface{}, args ...interface{}) (sql.Result, error) { - newWhere, newArgs := formatCondition(condition, args) + newWhere, newArgs := formatWhere(condition, args) + if newWhere != "" { + newWhere = " WHERE " + newWhere + } return tx.doUpdate(table, data, newWhere, newArgs...) } @@ -175,7 +179,10 @@ func (tx *TX) doUpdate(table string, data interface{}, condition string, args .. // CURD操作:删除数据 func (tx *TX) Delete(table string, condition interface{}, args ...interface{}) (sql.Result, error) { - newWhere, newArgs := formatCondition(condition, args) + newWhere, newArgs := formatWhere(condition, args) + if newWhere != "" { + newWhere = " WHERE " + newWhere + } return tx.doDelete(table, newWhere, newArgs...) } diff --git a/g/database/gdb/gdb_unit_init_test.go b/g/database/gdb/gdb_unit_init_test.go index 4121438ef..223c2ed20 100644 --- a/g/database/gdb/gdb_unit_init_test.go +++ b/g/database/gdb/gdb_unit_init_test.go @@ -8,12 +8,14 @@ package gdb_test import ( "fmt" + "os" + "github.com/gogf/gf/g" "github.com/gogf/gf/g/container/garray" + "github.com/gogf/gf/g/database/gdb" "github.com/gogf/gf/g/os/gtime" "github.com/gogf/gf/g/test/gtest" - "os" ) const ( @@ -88,14 +90,6 @@ func createTable(table ...string) (name string) { return } -// 删除指定表. -func dropTable(table string) { - if _, err := db.Exec(fmt.Sprintf("DROP TABLE IF EXISTS `%s`", table)); err != nil { - gtest.Fatal(err) - } -} - -// See createTable. // 创建测试表,并初始化默认数据。 func createInitTable(table ...string) (name string) { name = createTable(table...) @@ -117,3 +111,10 @@ func createInitTable(table ...string) (name string) { gtest.Assert(n, INIT_DATA_SIZE) return } + +// 删除指定表. +func dropTable(table string) { + if _, err := db.Exec(fmt.Sprintf("DROP TABLE IF EXISTS `%s`", table)); err != nil { + gtest.Fatal(err) + } +} diff --git a/g/database/gdb/gdb_unit_model_test.go b/g/database/gdb/gdb_unit_model_test.go index 55067785e..9422a6c53 100644 --- a/g/database/gdb/gdb_unit_model_test.go +++ b/g/database/gdb/gdb_unit_model_test.go @@ -7,10 +7,11 @@ package gdb_test import ( + "testing" + "github.com/gogf/gf/g" "github.com/gogf/gf/g/os/gtime" "github.com/gogf/gf/g/test/gtest" - "testing" ) // 基本测试 @@ -187,6 +188,25 @@ func TestModel_Save(t *testing.T) { } func TestModel_Update(t *testing.T) { + table := createInitTable() + // UPDATE...LIMIT + gtest.Case(t, func() { + result, err := db.Table(table).Data("nickname", "T100").OrderBy("id desc").Limit(2).Update() + if err != nil { + gtest.Fatal(err) + } + n, _ := result.RowsAffected() + gtest.Assert(n, 2) + + v1, err := db.Table(table).Fields("nickname").Where("id", 10).Value() + gtest.Assert(err, nil) + gtest.Assert(v1.String(), "T100") + + v2, err := db.Table(table).Fields("nickname").Where("id", 8).Value() + gtest.Assert(err, nil) + gtest.Assert(v2.String(), "T8") + }) + gtest.Case(t, func() { result, err := db.Table("user").Data("passport", "t22").Where("passport=?", "t2").Update() if err != nil { @@ -644,10 +664,22 @@ func TestModel_Where(t *testing.T) { } func TestModel_Delete(t *testing.T) { - result, err := db.Table("user").Delete() - if err != nil { - gtest.Fatal(err) - } - n, _ := result.RowsAffected() - gtest.Assert(n, 3) + // DELETE...LIMIT + gtest.Case(t, func() { + result, err := db.Table("user").Limit(2).Delete() + if err != nil { + gtest.Fatal(err) + } + n, _ := result.RowsAffected() + gtest.Assert(n, 2) + }) + + gtest.Case(t, func() { + result, err := db.Table("user").Delete() + if err != nil { + gtest.Fatal(err) + } + n, _ := result.RowsAffected() + gtest.Assert(n, 1) + }) } diff --git a/geg/database/orm/mssql/gdb_sqlserver.go b/geg/database/gdb/mssql/gdb_sqlserver.go similarity index 100% rename from geg/database/orm/mssql/gdb_sqlserver.go rename to geg/database/gdb/mssql/gdb_sqlserver.go diff --git a/geg/database/orm/mysql/config.toml b/geg/database/gdb/mysql/config.toml similarity index 100% rename from geg/database/orm/mysql/config.toml rename to geg/database/gdb/mysql/config.toml diff --git a/geg/database/orm/mysql/gdb.go b/geg/database/gdb/mysql/gdb.go similarity index 100% rename from geg/database/orm/mysql/gdb.go rename to geg/database/gdb/mysql/gdb.go diff --git a/geg/database/orm/mysql/gdb_binary.go b/geg/database/gdb/mysql/gdb_binary.go similarity index 100% rename from geg/database/orm/mysql/gdb_binary.go rename to geg/database/gdb/mysql/gdb_binary.go diff --git a/geg/database/orm/mysql/gdb_cache.go b/geg/database/gdb/mysql/gdb_cache.go similarity index 100% rename from geg/database/orm/mysql/gdb_cache.go rename to geg/database/gdb/mysql/gdb_cache.go diff --git a/geg/database/orm/mysql/gdb_config.go b/geg/database/gdb/mysql/gdb_config.go similarity index 100% rename from geg/database/orm/mysql/gdb_config.go rename to geg/database/gdb/mysql/gdb_config.go diff --git a/geg/database/orm/mysql/gdb_datetime.go b/geg/database/gdb/mysql/gdb_datetime.go similarity index 100% rename from geg/database/orm/mysql/gdb_datetime.go rename to geg/database/gdb/mysql/gdb_datetime.go diff --git a/geg/database/orm/mysql/gdb_debug.go b/geg/database/gdb/mysql/gdb_debug.go similarity index 94% rename from geg/database/orm/mysql/gdb_debug.go rename to geg/database/gdb/mysql/gdb_debug.go index f171f68a4..8354514e2 100644 --- a/geg/database/orm/mysql/gdb_debug.go +++ b/geg/database/gdb/mysql/gdb_debug.go @@ -2,6 +2,7 @@ package main import ( "fmt" + "github.com/gogf/gf/g" "github.com/gogf/gf/g/database/gdb" "github.com/gogf/gf/g/os/glog" @@ -22,8 +23,11 @@ func main() { if err != nil { panic(err) } - glog.SetPath("/tmp") db.SetDebug(true) + db.Table("user").Limit(2).Delete() + return + glog.SetPath("/tmp") + // 执行3条SQL查询 for i := 1; i <= 3; i++ { db.Table("user").Where("uid=?", i).One() diff --git a/geg/database/orm/mysql/gdb_insert.go b/geg/database/gdb/mysql/gdb_insert.go similarity index 100% rename from geg/database/orm/mysql/gdb_insert.go rename to geg/database/gdb/mysql/gdb_insert.go diff --git a/geg/database/orm/mysql/gdb_json_xml.go b/geg/database/gdb/mysql/gdb_json_xml.go similarity index 100% rename from geg/database/orm/mysql/gdb_json_xml.go rename to geg/database/gdb/mysql/gdb_json_xml.go diff --git a/geg/database/orm/mysql/gdb_pool.go b/geg/database/gdb/mysql/gdb_pool.go similarity index 100% rename from geg/database/orm/mysql/gdb_pool.go rename to geg/database/gdb/mysql/gdb_pool.go diff --git a/geg/database/orm/mysql/gdb_update_union.go b/geg/database/gdb/mysql/gdb_update_union.go similarity index 100% rename from geg/database/orm/mysql/gdb_update_union.go rename to geg/database/gdb/mysql/gdb_update_union.go diff --git a/geg/database/orm/mysql/gdb_value.go b/geg/database/gdb/mysql/gdb_value.go similarity index 100% rename from geg/database/orm/mysql/gdb_value.go rename to geg/database/gdb/mysql/gdb_value.go diff --git a/geg/database/orm/oracle/gdb.go b/geg/database/gdb/oracle/gdb.go similarity index 100% rename from geg/database/orm/oracle/gdb.go rename to geg/database/gdb/oracle/gdb.go diff --git a/geg/database/orm/sqlite/sqlite.go b/geg/database/gdb/sqlite/sqlite.go similarity index 100% rename from geg/database/orm/sqlite/sqlite.go rename to geg/database/gdb/sqlite/sqlite.go diff --git a/geg/database/redis/config.toml b/geg/database/gredis/config.toml similarity index 100% rename from geg/database/redis/config.toml rename to geg/database/gredis/config.toml diff --git a/geg/database/redis/gredis.go b/geg/database/gredis/gredis.go similarity index 100% rename from geg/database/redis/gredis.go rename to geg/database/gredis/gredis.go diff --git a/geg/database/redis/gredis2.go b/geg/database/gredis/gredis2.go similarity index 100% rename from geg/database/redis/gredis2.go rename to geg/database/gredis/gredis2.go diff --git a/geg/database/redis/gredis_conn_do.go b/geg/database/gredis/gredis_conn_do.go similarity index 100% rename from geg/database/redis/gredis_conn_do.go rename to geg/database/gredis/gredis_conn_do.go diff --git a/geg/database/redis/gredis_conn_do_var.go b/geg/database/gredis/gredis_conn_do_var.go similarity index 100% rename from geg/database/redis/gredis_conn_do_var.go rename to geg/database/gredis/gredis_conn_do_var.go diff --git a/geg/database/redis/gredis_conn_send.go b/geg/database/gredis/gredis_conn_send.go similarity index 100% rename from geg/database/redis/gredis_conn_send.go rename to geg/database/gredis/gredis_conn_send.go diff --git a/geg/database/redis/gredis_conn_send_var.go b/geg/database/gredis/gredis_conn_send_var.go similarity index 100% rename from geg/database/redis/gredis_conn_send_var.go rename to geg/database/gredis/gredis_conn_send_var.go diff --git a/geg/database/redis/gredis_conn_subscribe.go b/geg/database/gredis/gredis_conn_subscribe.go similarity index 100% rename from geg/database/redis/gredis_conn_subscribe.go rename to geg/database/gredis/gredis_conn_subscribe.go diff --git a/geg/database/redis/gredis_conn_subscribe_var.go b/geg/database/gredis/gredis_conn_subscribe_var.go similarity index 100% rename from geg/database/redis/gredis_conn_subscribe_var.go rename to geg/database/gredis/gredis_conn_subscribe_var.go From 418cbb420bf91d18e26db9208ed08acfa6df4cea Mon Sep 17 00:00:00 2001 From: John Date: Tue, 2 Jul 2019 23:48:38 +0800 Subject: [PATCH 6/7] TODO updates; version updates --- TODO.MD | 1 + version.go | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/TODO.MD b/TODO.MD index c56610e9c..1f48705ae 100644 --- a/TODO.MD +++ b/TODO.MD @@ -48,6 +48,7 @@ 1. 改进gdb对pgsql/mssql/oracle的支持,使用方法覆盖的方式改进操作,而不是完全依靠正则替换的方式; 1. gdb的Cache缓存功能增加可自定义缓存接口,以便支持外部缓存功能,缓存接口可以通过io.ReadWriter接口实现; 1. grpool增加支持阻塞添加任务接口; +1. gdb.Model在链式安全的对象创建中增加sync.Pool的使用; # DONE diff --git a/version.go b/version.go index ad997480a..ca2f542ef 100644 --- a/version.go +++ b/version.go @@ -1,4 +1,4 @@ package gf -const VERSION = "v1.7.0" +const VERSION = "v1.7.1" const AUTHORS = "john" From c90ed0d4242527435a3b4c9d7c27742d29c9aaa1 Mon Sep 17 00:00:00 2001 From: John Date: Tue, 2 Jul 2019 23:57:49 +0800 Subject: [PATCH 7/7] comment updates for gfsnotify --- g/os/gfsnotify/gfsnotify.go | 3 ++- g/os/gfsnotify/gfsnotify_watcher.go | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/g/os/gfsnotify/gfsnotify.go b/g/os/gfsnotify/gfsnotify.go index 15c3c1257..c51ec0041 100644 --- a/g/os/gfsnotify/gfsnotify.go +++ b/g/os/gfsnotify/gfsnotify.go @@ -12,6 +12,7 @@ package gfsnotify import ( "errors" "fmt" + "github.com/gogf/gf/g/container/glist" "github.com/gogf/gf/g/container/gmap" "github.com/gogf/gf/g/container/gqueue" @@ -92,7 +93,7 @@ func New() (*Watcher, error) { return w, nil } -// 添加对指定文件/目录的监听,并给定回调函数;如果给定的是一个目录,默认非递归监控。 +// 添加对指定文件/目录的监听,并给定回调函数;如果给定的是一个目录,默认递归监控。 func Add(path string, callbackFunc func(event *Event), recursive ...bool) (callback *Callback, err error) { return defaultWatcher.Add(path, callbackFunc, recursive...) } diff --git a/g/os/gfsnotify/gfsnotify_watcher.go b/g/os/gfsnotify/gfsnotify_watcher.go index 1e9b99562..0e153d42d 100644 --- a/g/os/gfsnotify/gfsnotify_watcher.go +++ b/g/os/gfsnotify/gfsnotify_watcher.go @@ -9,10 +9,11 @@ package gfsnotify import ( "errors" "fmt" + "github.com/gogf/gf/g/container/glist" ) -// 添加监控,path参数支持文件或者目录路径,recursive为非必需参数,默认为非递归监控(当path为目录时)。 +// 添加监控,path参数支持文件或者目录路径,recursive为非必需参数,默认为递归监控(当path为目录时)。 // 如果添加目录,这里只会返回目录的callback,按照callback删除时会递归删除。 func (w *Watcher) Add(path string, callbackFunc func(event *Event), recursive ...bool) (callback *Callback, err error) { // 首先添加这个文件/目录