From d9422d00ac5637eb27eecb6cddf33774c0e36b16 Mon Sep 17 00:00:00 2001 From: fangxiaokai Date: Sun, 28 Jun 2020 16:50:13 +0800 Subject: [PATCH 001/348] =?UTF-8?q?=E4=BF=AE=E6=94=B9sql=E6=89=93=E5=8D=B0?= =?UTF-8?q?debug=E4=BF=A1=E6=81=AF=EF=BC=8C=E5=A2=9E=E5=8A=A0=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E5=BA=93=E9=85=8D=E7=BD=AE=E7=9A=84group=E5=90=8D?= =?UTF-8?q?=E7=A7=B0=EF=BC=8C=E7=94=A8=E4=BA=8E=E5=8C=BA=E5=88=86sql?= =?UTF-8?q?=E6=9D=A5=E6=BA=90=EF=BC=8C=E5=B0=A4=E5=85=B6=E6=98=AF=E5=A4=9A?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E5=BA=93=E9=85=8D=E7=BD=AE=E7=9A=84=E6=97=B6?= =?UTF-8?q?=E5=80=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- database/gdb/gdb.go | 16 +++++++++------- database/gdb/gdb_core.go | 18 ++++++++++-------- 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/database/gdb/gdb.go b/database/gdb/gdb.go index 3f3e14e38..842969bef 100644 --- a/database/gdb/gdb.go +++ b/database/gdb/gdb.go @@ -11,9 +11,10 @@ import ( "database/sql" "errors" "fmt" + "time" + "github.com/gogf/gf/container/gvar" "github.com/gogf/gf/internal/intlog" - "time" "github.com/gogf/gf/os/glog" @@ -144,12 +145,13 @@ type Driver interface { // Sql is the sql recording struct. type Sql struct { - Sql string // SQL string(may contain reserved char '?'). - 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. + Sql string // SQL string(may contain reserved char '?'). + 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. + DBGroupName string // DBGroupName is which database call the sql. } // TableField is the struct for table field. diff --git a/database/gdb/gdb_core.go b/database/gdb/gdb_core.go index ecdf9a3f7..58370ce73 100644 --- a/database/gdb/gdb_core.go +++ b/database/gdb/gdb_core.go @@ -11,11 +11,12 @@ import ( "database/sql" "errors" "fmt" - "github.com/gogf/gf/internal/utils" "reflect" "regexp" "strings" + "github.com/gogf/gf/internal/utils" + "github.com/gogf/gf/container/gvar" "github.com/gogf/gf/os/gtime" "github.com/gogf/gf/text/gregex" @@ -64,12 +65,13 @@ func (c *Core) DoQuery(link Link, sql string, args ...interface{}) (rows *sql.Ro rows, err = link.Query(sql, args...) mTime2 := gtime.TimestampMilli() s := &Sql{ - Sql: sql, - Args: args, - Format: FormatSqlWithArgs(sql, args), - Error: err, - Start: mTime1, - End: mTime2, + Sql: sql, + Args: args, + Format: FormatSqlWithArgs(sql, args), + Error: err, + Start: mTime1, + End: mTime2, + DBGroupName: c.group, } c.writeSqlToLogger(s) } else { @@ -781,7 +783,7 @@ func (c *Core) MarshalJSON() ([]byte, error) { // writeSqlToLogger outputs the sql object to logger. // It is enabled when configuration "debug" is true. func (c *Core) writeSqlToLogger(v *Sql) { - s := fmt.Sprintf("[%3d ms] %s", v.End-v.Start, v.Format) + s := fmt.Sprintf("[%s] [%3d ms] %s", v.DBGroupName, v.End-v.Start, v.Format) if v.Error != nil { s += "\nError: " + v.Error.Error() c.logger.StackWithFilter(gPATH_FILTER_KEY).Error(s) From 6e08eebcbe7025eb06b9da7655bd30509a241c7b Mon Sep 17 00:00:00 2001 From: fangxiaokai Date: Sun, 28 Jun 2020 16:58:43 +0800 Subject: [PATCH 002/348] =?UTF-8?q?=E4=BF=AE=E6=94=B9sql=E6=89=93=E5=8D=B0?= =?UTF-8?q?debug=E4=BF=A1=E6=81=AF=EF=BC=8C=E5=A2=9E=E5=8A=A0=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E5=BA=93=E9=85=8D=E7=BD=AE=E7=9A=84group=E5=90=8D?= =?UTF-8?q?=E7=A7=B0=EF=BC=8C=E7=94=A8=E4=BA=8E=E5=8C=BA=E5=88=86sql?= =?UTF-8?q?=E6=9D=A5=E6=BA=90=EF=BC=8C=E5=B0=A4=E5=85=B6=E6=98=AF=E5=A4=9A?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E5=BA=93=E9=85=8D=E7=BD=AE=E7=9A=84=E6=97=B6?= =?UTF-8?q?=E5=80=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- database/gdb/gdb_core.go | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/database/gdb/gdb_core.go b/database/gdb/gdb_core.go index 58370ce73..2e8fd8e8d 100644 --- a/database/gdb/gdb_core.go +++ b/database/gdb/gdb_core.go @@ -109,12 +109,13 @@ func (c *Core) DoExec(link Link, sql string, args ...interface{}) (result sql.Re } mTime2 := gtime.TimestampMilli() s := &Sql{ - Sql: sql, - Args: args, - Format: FormatSqlWithArgs(sql, args), - Error: err, - Start: mTime1, - End: mTime2, + Sql: sql, + Args: args, + Format: FormatSqlWithArgs(sql, args), + Error: err, + Start: mTime1, + End: mTime2, + DBGroupName: c.group, } c.writeSqlToLogger(s) } else { From edc67d9ec3360c20f1804673f91a3ba354b950ae Mon Sep 17 00:00:00 2001 From: muyakongali Date: Fri, 17 Jul 2020 10:39:14 +0800 Subject: [PATCH 003/348] bug/log-file-rotate: fix big file even with rotate-by-size and rotate-back-expire --- os/glog/glog_logger.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/os/glog/glog_logger.go b/os/glog/glog_logger.go index 6178c22d8..c6e3ce0b8 100644 --- a/os/glog/glog_logger.go +++ b/os/glog/glog_logger.go @@ -229,7 +229,6 @@ func (l *Logger) printToFile(now time.Time, buffer *bytes.Buffer) { gmlock.Lock(memoryLockKey) defer gmlock.Unlock(memoryLockKey) file := l.getFilePointer(logFilePath) - defer file.Close() // Rotation file size checks. if l.config.RotateSize > 0 { stat, err := file.Stat() @@ -239,12 +238,13 @@ func (l *Logger) printToFile(now time.Time, buffer *bytes.Buffer) { if stat.Size() > l.config.RotateSize { l.rotateFileBySize(now) file = l.getFilePointer(logFilePath) - defer file.Close() } } if _, err := file.Write(buffer.Bytes()); err != nil { + defer file.Close() panic(err) } + defer file.Close() } // getFilePointer retrieves and returns a file pointer from file pool. From 78536de1b5c8c711a75bb4c3916ae9777b731ddc Mon Sep 17 00:00:00 2001 From: chenghonour Date: Fri, 17 Jul 2020 11:28:47 +0800 Subject: [PATCH 004/348] add database method --- database/gdb/gdb.go | 1 + database/gdb/gdb_core.go | 14 ++++++++++++++ 2 files changed, 15 insertions(+) diff --git a/database/gdb/gdb.go b/database/gdb/gdb.go index 1814c19be..34b17c1df 100644 --- a/database/gdb/gdb.go +++ b/database/gdb/gdb.go @@ -142,6 +142,7 @@ type DB interface { QuotePrefixTableName(table string) string Tables(schema ...string) (tables []string, err error) TableFields(table string, schema ...string) (map[string]*TableField, error) + HasTable(name string) (bool, error) // HandleSqlBeforeCommit is a hook function, which deals with the sql string before // it's committed to underlying driver. The parameter specifies the current diff --git a/database/gdb/gdb_core.go b/database/gdb/gdb_core.go index 372a6f073..1a20bbbeb 100644 --- a/database/gdb/gdb_core.go +++ b/database/gdb/gdb_core.go @@ -778,3 +778,17 @@ func (c *Core) writeSqlToLogger(v *Sql) { c.logger.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() + if err != nil { + return false, err + } + for _, table := range tableList { + if table == name { + return true, nil + } + } + return false, nil +} \ No newline at end of file From 534cd3be1c934ae07b4c59cf6c747d2ee9c50b14 Mon Sep 17 00:00:00 2001 From: chenghonour Date: Fri, 17 Jul 2020 14:28:50 +0800 Subject: [PATCH 005/348] add table field method --- database/gdb/gdb_model_fields.go | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/database/gdb/gdb_model_fields.go b/database/gdb/gdb_model_fields.go index 580b57fc7..b051afba6 100644 --- a/database/gdb/gdb_model_fields.go +++ b/database/gdb/gdb_model_fields.go @@ -127,3 +127,24 @@ func (m *Model) FieldsExStr(fields string, prefix ...string) string { newFields = m.db.QuoteString(newFields) return newFields } + +// HasField determine whether the field exists in the table. +func (m *Model) HasField(field string) bool { + tableFields, err := m.db.TableFields(m.tables) + if err != nil { + panic(err) + } + if len(tableFields) == 0 { + panic(fmt.Sprintf(`empty table fields for table "%s"`, m.tables)) + } + fieldsArray := make([]string, len(tableFields)) + for k, v := range tableFields { + fieldsArray[v.Index] = k + } + for _, f := range fieldsArray { + if f == field { + return true + } + } + return false +} \ No newline at end of file From ca72d3b23aa4e5f80e11efa3b951e9f98e137464 Mon Sep 17 00:00:00 2001 From: john Date: Sat, 18 Jul 2020 08:32:35 +0800 Subject: [PATCH 006/348] remove gfcache usage from repo --- os/gfile/gfile_cache.go | 4 ++-- os/gproc/gproc_comm.go | 1 - os/gview/gview_parse.go | 1 - 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/os/gfile/gfile_cache.go b/os/gfile/gfile_cache.go index c34b42659..eb663576d 100644 --- a/os/gfile/gfile_cache.go +++ b/os/gfile/gfile_cache.go @@ -20,7 +20,7 @@ const ( var ( // Default expire time for file content caching. - cacheExpire = cmdenv.Get("gf.gfcache.expire", gDEFAULT_CACHE_EXPIRE).Duration() + cacheExpire = cmdenv.Get("gf.gfile.cache", gDEFAULT_CACHE_EXPIRE).Duration() ) // GetContents returns string content of given file by from cache. @@ -59,5 +59,5 @@ func GetBytesWithCache(path string, duration ...time.Duration) []byte { // cacheKey produces the cache key for gcache. func cacheKey(path string) string { - return "gf.gfcache:" + path + return "gf.gfile.cache:" + path } diff --git a/os/gproc/gproc_comm.go b/os/gproc/gproc_comm.go index 0b962ffa5..779ff6432 100644 --- a/os/gproc/gproc_comm.go +++ b/os/gproc/gproc_comm.go @@ -11,7 +11,6 @@ import ( "fmt" "github.com/gogf/gf/container/gmap" "github.com/gogf/gf/net/gtcp" - "github.com/gogf/gf/os/gfcache" "github.com/gogf/gf/os/gfile" "github.com/gogf/gf/util/gconv" ) diff --git a/os/gview/gview_parse.go b/os/gview/gview_parse.go index 094a0e776..8d0bc63f9 100644 --- a/os/gview/gview_parse.go +++ b/os/gview/gview_parse.go @@ -12,7 +12,6 @@ import ( "fmt" "github.com/gogf/gf/encoding/ghash" "github.com/gogf/gf/internal/intlog" - "github.com/gogf/gf/os/gfcache" "github.com/gogf/gf/os/gfsnotify" "github.com/gogf/gf/os/gmlock" "github.com/gogf/gf/text/gstr" From 7d278fea25e97016f923bbefd568d83ec502bcaa Mon Sep 17 00:00:00 2001 From: john Date: Sat, 18 Jul 2020 09:40:39 +0800 Subject: [PATCH 007/348] users update in readme --- README.MD | 11 +++++++++-- README_ZH.MD | 9 ++++++++- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/README.MD b/README.MD index 8fe86519a..96d564125 100644 --- a/README.MD +++ b/README.MD @@ -115,9 +115,16 @@ The concurrency starts from `100` to `10000`. `GF` is licensed under the [MIT License](LICENSE), 100% free and open-source, forever. -# Known Users +# Part Of Users -Logos are not authorized to be shown due to trademark copyrights. +- [Tencent](https://www.tecent.com/) +- [ZTE](https://www.zte.com.cn/china/) +- [Ant Financial Services](https://www.antfin.com/) +- [Medlinker](https://www.medlinker.com/) +- [Yesk](http://www.yesky.com) +- [KuCoin](https://www.kucoin.io/) + +> We list part of the users here, if your company or products are using `GoFrame`, let us know [here](https://github.com/gogf/gf/issues/168). # Contributors diff --git a/README_ZH.MD b/README_ZH.MD index efcb41889..ec80bf199 100644 --- a/README_ZH.MD +++ b/README_ZH.MD @@ -137,7 +137,14 @@ ab -t 10 -c 100 http://127.0.0.1:3000/json # 用户 -由于商标版权缘故,未经厂商商务部授权允许无法用于宣传展示。 +- [腾讯科技](https://www.tecent.com/) +- [中兴科技](https://www.zte.com.cn/china/) +- [蚂蚁金服](https://www.antfin.com/) +- [医联科技](https://www.medlinker.com/) +- [天极数码](http://www.yesky.com) +- [库币科技](https://www.kucoin.io/) + +> 在这里只列举了部分知名的用户,如果您的企业或者产品正在使用`GoFrame`,欢迎到 [这里] (https://github.com/gogf/gf/issues/168)留言。 # 贡献 From 51be255821c3a2f5e47251c74f10a3b279d005f7 Mon Sep 17 00:00:00 2001 From: john Date: Sat, 18 Jul 2020 09:44:15 +0800 Subject: [PATCH 008/348] remove gfcache usage from repo --- README.MD | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.MD b/README.MD index 96d564125..34a6b3371 100644 --- a/README.MD +++ b/README.MD @@ -124,7 +124,7 @@ The concurrency starts from `100` to `10000`. - [Yesk](http://www.yesky.com) - [KuCoin](https://www.kucoin.io/) -> We list part of the users here, if your company or products are using `GoFrame`, let us know [here](https://github.com/gogf/gf/issues/168). +> We list part of the users here, if your company or products are using `GoFrame`, please let us know [here](https://github.com/gogf/gf/issues/168). # Contributors From 540f4d2d0ccabf8c2e862c1d032ac419be0b3c1f Mon Sep 17 00:00:00 2001 From: john Date: Sat, 18 Jul 2020 10:06:26 +0800 Subject: [PATCH 009/348] donation update --- DONATOR.MD | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/DONATOR.MD b/DONATOR.MD index 0a96fcb47..f62a9ed48 100644 --- a/DONATOR.MD +++ b/DONATOR.MD @@ -1,7 +1,12 @@ # Donators -We currently accept donation by [Alipay](https://goframe.org/images/donate.png) / [Gitee](https://gitee.com/johng/gf), -please note your github/gitee account in your payment bill. +We currently accept donation by [Wechat](https://goframe.org/images/donate.png) / [Alipay](https://goframe.org/images/donate.png) / [Gitee](https://gitee.com/johng/gf), +please note your github/gitee account in your payment bill. All the donations will be used only for `GoFrame` project development and its community construction. + +> If you cannot see the donation image, please click [here](https://goframe.org/images/donate.png). + + + | Name | Channel | Amount | Comment |---|---|--- | --- @@ -95,5 +100,5 @@ please note your github/gitee account in your payment bill. - + From 9c8cb26bd665334e650096c75050d891d8e1c8bf Mon Sep 17 00:00:00 2001 From: john Date: Sat, 18 Jul 2020 10:17:05 +0800 Subject: [PATCH 010/348] readme update --- README.MD | 2 ++ README_ZH.MD | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/README.MD b/README.MD index 34a6b3371..8f0284848 100644 --- a/README.MD +++ b/README.MD @@ -123,6 +123,8 @@ The concurrency starts from `100` to `10000`. - [Medlinker](https://www.medlinker.com/) - [Yesk](http://www.yesky.com) - [KuCoin](https://www.kucoin.io/) +- [Leyoujia Holding Group](https://www.leyoujia.com/) + > We list part of the users here, if your company or products are using `GoFrame`, please let us know [here](https://github.com/gogf/gf/issues/168). diff --git a/README_ZH.MD b/README_ZH.MD index ec80bf199..bdddfe059 100644 --- a/README_ZH.MD +++ b/README_ZH.MD @@ -142,7 +142,8 @@ ab -t 10 -c 100 http://127.0.0.1:3000/json - [蚂蚁金服](https://www.antfin.com/) - [医联科技](https://www.medlinker.com/) - [天极数码](http://www.yesky.com) -- [库币科技](https://www.kucoin.io/) +- [库珀科技](https://www.kucoin.io/) +- [乐有家](https://www.leyoujia.com/) > 在这里只列举了部分知名的用户,如果您的企业或者产品正在使用`GoFrame`,欢迎到 [这里] (https://github.com/gogf/gf/issues/168)留言。 From 3fc5e43abed3c040a81e0c90f700b3be99c86d50 Mon Sep 17 00:00:00 2001 From: john Date: Sat, 18 Jul 2020 10:21:17 +0800 Subject: [PATCH 011/348] readme update --- README_ZH.MD | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README_ZH.MD b/README_ZH.MD index bdddfe059..ac7876de4 100644 --- a/README_ZH.MD +++ b/README_ZH.MD @@ -142,7 +142,7 @@ ab -t 10 -c 100 http://127.0.0.1:3000/json - [蚂蚁金服](https://www.antfin.com/) - [医联科技](https://www.medlinker.com/) - [天极数码](http://www.yesky.com) -- [库珀科技](https://www.kucoin.io/) +- [库币科技](https://www.kucoin.io/) - [乐有家](https://www.leyoujia.com/) > 在这里只列举了部分知名的用户,如果您的企业或者产品正在使用`GoFrame`,欢迎到 [这里] (https://github.com/gogf/gf/issues/168)留言。 From 835e07e8de1abfd0b2bac2916d10ba728c52822b Mon Sep 17 00:00:00 2001 From: zhigang Date: Sat, 18 Jul 2020 10:34:32 +0800 Subject: [PATCH 012/348] Update README.MD update tencent site --- README.MD | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.MD b/README.MD index 8f0284848..7a06c1290 100644 --- a/README.MD +++ b/README.MD @@ -117,7 +117,7 @@ The concurrency starts from `100` to `10000`. # Part Of Users -- [Tencent](https://www.tecent.com/) +- [Tencent](https://www.tencent.com/) - [ZTE](https://www.zte.com.cn/china/) - [Ant Financial Services](https://www.antfin.com/) - [Medlinker](https://www.medlinker.com/) From 3f5f76458d1692934fdc0cfc311f3345555de4e3 Mon Sep 17 00:00:00 2001 From: zhigang Date: Sat, 18 Jul 2020 10:36:48 +0800 Subject: [PATCH 013/348] Update README_ZH.MD update tencent site --- README_ZH.MD | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README_ZH.MD b/README_ZH.MD index ac7876de4..6f6941395 100644 --- a/README_ZH.MD +++ b/README_ZH.MD @@ -137,7 +137,7 @@ ab -t 10 -c 100 http://127.0.0.1:3000/json # 用户 -- [腾讯科技](https://www.tecent.com/) +- [腾讯科技](https://www.tencent.com/) - [中兴科技](https://www.zte.com.cn/china/) - [蚂蚁金服](https://www.antfin.com/) - [医联科技](https://www.medlinker.com/) From 7bbc2459bace1ba9f692f9fc0cb4f7633d1dddb5 Mon Sep 17 00:00:00 2001 From: tiancai <532722030@qq.com> Date: Sun, 19 Jul 2020 23:24:35 +0800 Subject: [PATCH 014/348] =?UTF-8?q?postgre=20=E6=8A=A5=E9=94=99=20pq:=20sy?= =?UTF-8?q?ntax=20error=20at=20or=20near=20$?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- database/gdb/gdb_driver_pgsql.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/database/gdb/gdb_driver_pgsql.go b/database/gdb/gdb_driver_pgsql.go index 134203248..ee638dedb 100644 --- a/database/gdb/gdb_driver_pgsql.go +++ b/database/gdb/gdb_driver_pgsql.go @@ -65,7 +65,7 @@ func (d *DriverPgsql) HandleSqlBeforeCommit(link Link, sql string, args []interf // Convert place holder char '?' to string "$vx". sql, _ = gregex.ReplaceStringFunc("\\?", sql, func(s string) string { index++ - return fmt.Sprintf("$v%d", index) + return fmt.Sprintf("$%d", index) }) sql, _ = gregex.ReplaceString(` LIMIT (\d+),\s*(\d+)`, ` LIMIT $2 OFFSET $1`, sql) return sql, args From d44ddae3dc60abf93a6e4ba8ba56a05e2fcf2484 Mon Sep 17 00:00:00 2001 From: tiancai <532722030@qq.com> Date: Mon, 20 Jul 2020 19:13:15 +0800 Subject: [PATCH 015/348] debug postgre --- database/gdb/gdb_driver_pgsql.go | 2 +- database/gdb/gdb_func.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/database/gdb/gdb_driver_pgsql.go b/database/gdb/gdb_driver_pgsql.go index ee638dedb..9bff38467 100644 --- a/database/gdb/gdb_driver_pgsql.go +++ b/database/gdb/gdb_driver_pgsql.go @@ -62,7 +62,7 @@ func (d *DriverPgsql) GetChars() (charLeft string, charRight string) { // HandleSqlBeforeCommit deals with the sql string before commits it to underlying sql driver. func (d *DriverPgsql) HandleSqlBeforeCommit(link Link, sql string, args []interface{}) (string, []interface{}) { var index int - // Convert place holder char '?' to string "$vx". + // Convert place holder char '?' to string "$x". sql, _ = gregex.ReplaceStringFunc("\\?", sql, func(s string) string { index++ return fmt.Sprintf("$%d", index) diff --git a/database/gdb/gdb_func.go b/database/gdb/gdb_func.go index 1b1000c95..e30508a2c 100644 --- a/database/gdb/gdb_func.go +++ b/database/gdb/gdb_func.go @@ -553,7 +553,7 @@ func formatError(err error, sql string, args ...interface{}) error { func FormatSqlWithArgs(sql string, args []interface{}) string { index := -1 newQuery, _ := gregex.ReplaceStringFunc( - `(\?|:v\d+|\$v\d+|@v\d+)`, sql, func(s string) string { + `(\?|:v\d+|\$\d+|@v\d+)`, sql, func(s string) string { index++ if len(args) > index { if args[index] == nil { From 5be30b36847bd47ec9ce0df7ba351f9444e151b4 Mon Sep 17 00:00:00 2001 From: john Date: Mon, 20 Jul 2020 21:32:28 +0800 Subject: [PATCH 016/348] fix issue in logging file rotation --- os/glog/glog_logger.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/os/glog/glog_logger.go b/os/glog/glog_logger.go index c6e3ce0b8..68a449851 100644 --- a/os/glog/glog_logger.go +++ b/os/glog/glog_logger.go @@ -233,6 +233,7 @@ func (l *Logger) printToFile(now time.Time, buffer *bytes.Buffer) { if l.config.RotateSize > 0 { stat, err := file.Stat() if err != nil { + file.Close() panic(err) } if stat.Size() > l.config.RotateSize { @@ -241,10 +242,10 @@ func (l *Logger) printToFile(now time.Time, buffer *bytes.Buffer) { } } if _, err := file.Write(buffer.Bytes()); err != nil { - defer file.Close() + file.Close() panic(err) } - defer file.Close() + file.Close() } // getFilePointer retrieves and returns a file pointer from file pool. From 48c84bf74aa16ddca180c2e04695c73c5355eee7 Mon Sep 17 00:00:00 2001 From: john Date: Mon, 20 Jul 2020 21:48:44 +0800 Subject: [PATCH 017/348] fix place holder for mssql --- database/gdb/gdb_driver_mssql.go | 4 ++-- database/gdb/gdb_func.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/database/gdb/gdb_driver_mssql.go b/database/gdb/gdb_driver_mssql.go index 520582156..59f12426a 100644 --- a/database/gdb/gdb_driver_mssql.go +++ b/database/gdb/gdb_driver_mssql.go @@ -64,10 +64,10 @@ func (d *DriverMssql) GetChars() (charLeft string, charRight string) { // HandleSqlBeforeCommit deals with the sql string before commits it to underlying sql driver. func (d *DriverMssql) HandleSqlBeforeCommit(link Link, sql string, args []interface{}) (string, []interface{}) { var index int - // Convert place holder char '?' to string "@vx". + // Convert place holder char '?' to string "@px". str, _ := gregex.ReplaceStringFunc("\\?", sql, func(s string) string { index++ - return fmt.Sprintf("@v%d", index) + return fmt.Sprintf("@p%d", index) }) str, _ = gregex.ReplaceString("\"", "", str) return d.parseSql(str), args diff --git a/database/gdb/gdb_func.go b/database/gdb/gdb_func.go index 1b1000c95..699eaae2f 100644 --- a/database/gdb/gdb_func.go +++ b/database/gdb/gdb_func.go @@ -553,7 +553,7 @@ func formatError(err error, sql string, args ...interface{}) error { func FormatSqlWithArgs(sql string, args []interface{}) string { index := -1 newQuery, _ := gregex.ReplaceStringFunc( - `(\?|:v\d+|\$v\d+|@v\d+)`, sql, func(s string) string { + `(\?|:v\d+|\$v\d+|@p\d+)`, sql, func(s string) string { index++ if len(args) > index { if args[index] == nil { From 52b6e8ef9d8d268dbe0bda1648bf602fc60bc210 Mon Sep 17 00:00:00 2001 From: john Date: Mon, 20 Jul 2020 21:55:34 +0800 Subject: [PATCH 018/348] fix place holder for mssql --- database/gdb/gdb_z_mysql_internal_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/database/gdb/gdb_z_mysql_internal_test.go b/database/gdb/gdb_z_mysql_internal_test.go index 251a61a6d..b010bb3db 100644 --- a/database/gdb/gdb_z_mysql_internal_test.go +++ b/database/gdb/gdb_z_mysql_internal_test.go @@ -75,7 +75,7 @@ func Test_Func_FormatSqlWithArgs(t *testing.T) { // mssql gtest.C(t, func(t *gtest.T) { var s string - s = FormatSqlWithArgs("select * from table where id>=@v1 and sex=@v2", []interface{}{100, 1}) + s = FormatSqlWithArgs("select * from table where id>=@p1 and sex=@p2", []interface{}{100, 1}) t.Assert(s, "select * from table where id>=100 and sex=1") }) // pgsql From ee5ddaab523d6824d0efc4fe3a207fe308ec53de Mon Sep 17 00:00:00 2001 From: john Date: Mon, 20 Jul 2020 22:47:12 +0800 Subject: [PATCH 019/348] fix place holder for pgsql --- database/gdb/gdb_z_mysql_internal_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/database/gdb/gdb_z_mysql_internal_test.go b/database/gdb/gdb_z_mysql_internal_test.go index b010bb3db..294b46bc3 100644 --- a/database/gdb/gdb_z_mysql_internal_test.go +++ b/database/gdb/gdb_z_mysql_internal_test.go @@ -81,7 +81,7 @@ func Test_Func_FormatSqlWithArgs(t *testing.T) { // pgsql gtest.C(t, func(t *gtest.T) { var s string - s = FormatSqlWithArgs("select * from table where id>=$v1 and sex=$v2", []interface{}{100, 1}) + s = FormatSqlWithArgs("select * from table where id>=$1 and sex=$2", []interface{}{100, 1}) t.Assert(s, "select * from table where id>=100 and sex=1") }) // oracle From 629175101409633d3e53d954a31721aa2e735b05 Mon Sep 17 00:00:00 2001 From: chenall Date: Tue, 21 Jul 2020 09:57:13 +0800 Subject: [PATCH 020/348] fix ghttp_client upload filename issue #809 --- net/ghttp/ghttp_client_request.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/ghttp/ghttp_client_request.go b/net/ghttp/ghttp_client_request.go index eb6e6d78b..45c84c2b7 100644 --- a/net/ghttp/ghttp_client_request.go +++ b/net/ghttp/ghttp_client_request.go @@ -135,7 +135,7 @@ func (c *Client) DoRequest(method, url string, data ...interface{}) (resp *Clien if !gfile.Exists(path) { return nil, errors.New(fmt.Sprintf(`"%s" does not exist`, path)) } - if file, err := writer.CreateFormFile(array[0], path); err == nil { + if file, err := writer.CreateFormFile(array[0], gfile.Basename(path)); err == nil { if f, err := os.Open(path); err == nil { if _, err = io.Copy(file, f); err != nil { f.Close() From 8167a398fc5567a08caf5efeada7cd23f3682a6b Mon Sep 17 00:00:00 2001 From: john Date: Tue, 21 Jul 2020 10:17:31 +0800 Subject: [PATCH 021/348] add function GetHeader for ghttp.Request --- net/ghttp/ghttp_request.go | 5 +++++ net/ghttp/ghttp_unit_param_test.go | 21 +++++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/net/ghttp/ghttp_request.go b/net/ghttp/ghttp_request.go index 5ea1c38c9..aab54b884 100644 --- a/net/ghttp/ghttp_request.go +++ b/net/ghttp/ghttp_request.go @@ -126,6 +126,11 @@ func (r *Request) IsExited() bool { return r.exit } +// GetHeader retrieves and returns the header value with given . +func (r *Request) GetHeader(key string) string { + return r.Header.Get(key) +} + // GetHost returns current request host name, which might be a domain or an IP without port. func (r *Request) GetHost() string { if len(r.parsedHost) == 0 { diff --git a/net/ghttp/ghttp_unit_param_test.go b/net/ghttp/ghttp_unit_param_test.go index 6e69e06ba..f345cd42a 100644 --- a/net/ghttp/ghttp_unit_param_test.go +++ b/net/ghttp/ghttp_unit_param_test.go @@ -404,6 +404,27 @@ func Test_Params_Basic(t *testing.T) { }) } +func Test_Params_Header(t *testing.T) { + p, _ := ports.PopRand() + s := g.Server(p) + s.BindHandler("/header", func(r *ghttp.Request) { + r.Response.Write(r.GetHeader("test")) + }) + s.SetPort(p) + s.SetDumpRouterMap(false) + s.Start() + defer s.Shutdown() + + time.Sleep(100 * time.Millisecond) + gtest.C(t, func(t *gtest.T) { + prefix := fmt.Sprintf("http://127.0.0.1:%d", p) + client := ghttp.NewClient() + client.SetPrefix(prefix) + + t.Assert(client.Header(g.MapStrStr{"test": "123456"}).GetContent("/header"), "123456") + }) +} + func Test_Params_SupportChars(t *testing.T) { p, _ := ports.PopRand() s := g.Server(p) From 2f44d9ae1831b3bf8f0d882c6d5fc37406f5aa37 Mon Sep 17 00:00:00 2001 From: chenghonour Date: Tue, 21 Jul 2020 12:37:04 +0800 Subject: [PATCH 022/348] add unit test --- database/gdb/gdb_model_fields.go | 11 +++++---- database/gdb/gdb_z_mysql_model_test.go | 34 ++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 5 deletions(-) diff --git a/database/gdb/gdb_model_fields.go b/database/gdb/gdb_model_fields.go index b051afba6..3e9421bb2 100644 --- a/database/gdb/gdb_model_fields.go +++ b/database/gdb/gdb_model_fields.go @@ -7,6 +7,7 @@ package gdb import ( + "errors" "fmt" "github.com/gogf/gf/container/gset" "github.com/gogf/gf/text/gstr" @@ -129,13 +130,13 @@ func (m *Model) FieldsExStr(fields string, prefix ...string) string { } // HasField determine whether the field exists in the table. -func (m *Model) HasField(field string) bool { +func (m *Model) HasField(field string) (bool, error) { tableFields, err := m.db.TableFields(m.tables) if err != nil { - panic(err) + return false, err } if len(tableFields) == 0 { - panic(fmt.Sprintf(`empty table fields for table "%s"`, m.tables)) + return false, errors.New(fmt.Sprintf(`empty table fields for table "%s"`, m.tables)) } fieldsArray := make([]string, len(tableFields)) for k, v := range tableFields { @@ -143,8 +144,8 @@ func (m *Model) HasField(field string) bool { } for _, f := range fieldsArray { if f == field { - return true + return true, nil } } - return false + return false, nil } \ No newline at end of file diff --git a/database/gdb/gdb_z_mysql_model_test.go b/database/gdb/gdb_z_mysql_model_test.go index eabf8d607..faa04202e 100644 --- a/database/gdb/gdb_z_mysql_model_test.go +++ b/database/gdb/gdb_z_mysql_model_test.go @@ -2437,3 +2437,37 @@ func Test_Model_Empty_Slice_Argument(t *testing.T) { t.Assert(len(result), 0) }) } + +func Test_Model_HasTable(t *testing.T) { + table := createTable() + defer dropTable(table) + + gtest.C(t, func(t *gtest.T) { + result, err := db.HasTable(table) + t.Assert(result, true) + t.Assert(err, nil) + }) + + gtest.C(t, func(t *gtest.T) { + result, err := db.HasTable("table12321") + t.Assert(result, false) + t.Assert(err, nil) + }) +} + +func Test_Model_HasField(t *testing.T) { + table := createTable() + defer dropTable(table) + + gtest.C(t, func(t *gtest.T) { + result, err := db.Table(table).HasField("id") + t.Assert(result, true) + t.Assert(err, nil) + }) + + gtest.C(t, func(t *gtest.T) { + result, err := db.Table(table).HasField("id123") + t.Assert(result, false) + t.Assert(err, nil) + }) +} \ No newline at end of file From 1d5e717a80f517dfb8f46bc01ca798f7247199c8 Mon Sep 17 00:00:00 2001 From: chenghonour Date: Tue, 21 Jul 2020 12:40:13 +0800 Subject: [PATCH 023/348] update err return --- database/gdb/gdb_model_fields.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/database/gdb/gdb_model_fields.go b/database/gdb/gdb_model_fields.go index 3e9421bb2..f1f735883 100644 --- a/database/gdb/gdb_model_fields.go +++ b/database/gdb/gdb_model_fields.go @@ -7,7 +7,6 @@ package gdb import ( - "errors" "fmt" "github.com/gogf/gf/container/gset" "github.com/gogf/gf/text/gstr" @@ -136,7 +135,7 @@ func (m *Model) HasField(field string) (bool, error) { return false, err } if len(tableFields) == 0 { - return false, errors.New(fmt.Sprintf(`empty table fields for table "%s"`, m.tables)) + return false, fmt.Errorf(`empty table fields for table "%s"`, m.tables) } fieldsArray := make([]string, len(tableFields)) for k, v := range tableFields { From d56835fc0022a9a4ed8915fb6ea5416246b91d87 Mon Sep 17 00:00:00 2001 From: chenghonour Date: Tue, 21 Jul 2020 13:28:25 +0800 Subject: [PATCH 024/348] go fmt --- database/gdb/gdb_core.go | 2 +- database/gdb/gdb_model_fields.go | 2 +- database/gdb/gdb_z_mysql_model_test.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/database/gdb/gdb_core.go b/database/gdb/gdb_core.go index 1a20bbbeb..22fac5e8c 100644 --- a/database/gdb/gdb_core.go +++ b/database/gdb/gdb_core.go @@ -791,4 +791,4 @@ func (c *Core) HasTable(name string) (bool, error) { } } return false, nil -} \ No newline at end of file +} diff --git a/database/gdb/gdb_model_fields.go b/database/gdb/gdb_model_fields.go index f1f735883..2fdd98b85 100644 --- a/database/gdb/gdb_model_fields.go +++ b/database/gdb/gdb_model_fields.go @@ -147,4 +147,4 @@ func (m *Model) HasField(field string) (bool, error) { } } return false, nil -} \ No newline at end of file +} diff --git a/database/gdb/gdb_z_mysql_model_test.go b/database/gdb/gdb_z_mysql_model_test.go index faa04202e..ec7d440fc 100644 --- a/database/gdb/gdb_z_mysql_model_test.go +++ b/database/gdb/gdb_z_mysql_model_test.go @@ -2470,4 +2470,4 @@ func Test_Model_HasField(t *testing.T) { t.Assert(result, false) t.Assert(err, nil) }) -} \ No newline at end of file +} From 9e7291903f683e693c5aee5197b2d54b7e3bc8a2 Mon Sep 17 00:00:00 2001 From: xbkaishui Date: Wed, 22 Jul 2020 13:28:45 +0800 Subject: [PATCH 025/348] support redis tls --- database/gredis/gredis.go | 4 ++++ database/gredis/gredis_config.go | 6 +++++ database/gredis/gredis_z_unit_test.go | 32 +++++++++++++++++++++++++++ 3 files changed, 42 insertions(+) diff --git a/database/gredis/gredis.go b/database/gredis/gredis.go index 2a2b008ba..a2fbf462f 100644 --- a/database/gredis/gredis.go +++ b/database/gredis/gredis.go @@ -46,6 +46,8 @@ type Config struct { IdleTimeout time.Duration // Maximum idle time for connection (default is 10 seconds, not allowed to be set to 0) MaxConnLifetime time.Duration // Maximum lifetime of the connection (default is 30 seconds, not allowed to be set to 0) ConnectTimeout time.Duration // Dial connection timeout. + TLS bool //support tls + TLSSkipVerify bool //tls skip verify } // Pool statistics. @@ -102,6 +104,8 @@ func New(config Config) *Redis { "tcp", fmt.Sprintf("%s:%d", config.Host, config.Port), redis.DialConnectTimeout(config.ConnectTimeout), + redis.DialUseTLS(config.TLS), + redis.DialTLSSkipVerify(config.TLSSkipVerify), ) if err != nil { return nil, err diff --git a/database/gredis/gredis_config.go b/database/gredis/gredis_config.go index eab96d73d..c8c2b9b01 100644 --- a/database/gredis/gredis_config.go +++ b/database/gredis/gredis_config.go @@ -110,6 +110,12 @@ func ConfigFromStr(str string) (config Config, err error) { if v, ok := parse["maxConnLifetime"]; ok { config.MaxConnLifetime = gconv.Duration(v) * time.Second } + if v, ok := parse["tls"]; ok { + config.TLS = gconv.Bool(v) + } + if v, ok := parse["skipVerify"]; ok { + config.TLSSkipVerify = gconv.Bool(v) + } return } array, _ = gregex.MatchString(`([^:]+):*(\d*),{0,1}(\d*),{0,1}(.*)`, str) diff --git a/database/gredis/gredis_z_unit_test.go b/database/gredis/gredis_z_unit_test.go index 23e5804f0..50bef9473 100644 --- a/database/gredis/gredis_z_unit_test.go +++ b/database/gredis/gredis_z_unit_test.go @@ -27,6 +27,14 @@ var ( Port: 6379, Db: 1, } + + tlsConfig = gredis.Config{ + Host: "127.0.0.1", + Port: 6379, + Db: 1, + TLS: true, + TLSSkipVerify: true, + } ) func Test_NewClose(t *testing.T) { @@ -382,3 +390,27 @@ func Test_Auto_MarshalSlice(t *testing.T) { t.Assert(users2, users1) }) } + +func Test_Conn_TLS(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + redis := gredis.New(tlsConfig) + defer redis.Close() + conn := redis.Conn() + defer conn.Close() + + key := gconv.String(gtime.TimestampNano()) + value := []byte("v") + r, err := conn.Do("SET", key, value) + t.Assert(err, nil) + + r, err = conn.Do("GET", key) + t.Assert(err, nil) + t.Assert(r, value) + + _, err = conn.Do("DEL", key) + t.Assert(err, nil) + r, err = conn.Do("GET", key) + t.Assert(err, nil) + t.Assert(r, nil) + }) +} From 208bdffdf7c23ce0f2980682479ca3baa7df1d20 Mon Sep 17 00:00:00 2001 From: xbkaishui Date: Wed, 22 Jul 2020 14:02:21 +0800 Subject: [PATCH 026/348] update comment --- database/gredis/gredis.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/database/gredis/gredis.go b/database/gredis/gredis.go index a2fbf462f..45de72de1 100644 --- a/database/gredis/gredis.go +++ b/database/gredis/gredis.go @@ -46,8 +46,8 @@ type Config struct { IdleTimeout time.Duration // Maximum idle time for connection (default is 10 seconds, not allowed to be set to 0) MaxConnLifetime time.Duration // Maximum lifetime of the connection (default is 30 seconds, not allowed to be set to 0) ConnectTimeout time.Duration // Dial connection timeout. - TLS bool //support tls - TLSSkipVerify bool //tls skip verify + TLS bool // Specifies the config to use when a TLS connection is dialed. + TLSSkipVerify bool // Disables server name verification when connecting over TLS } // Pool statistics. From 646280a6a998a79179ffcdf5086ee2859c2b3cb8 Mon Sep 17 00:00:00 2001 From: xbkaishui Date: Wed, 22 Jul 2020 15:08:32 +0800 Subject: [PATCH 027/348] remove tls unit test case --- database/gredis/gredis_z_unit_test.go | 25 +------------------------ 1 file changed, 1 insertion(+), 24 deletions(-) diff --git a/database/gredis/gredis_z_unit_test.go b/database/gredis/gredis_z_unit_test.go index 50bef9473..3270af08f 100644 --- a/database/gredis/gredis_z_unit_test.go +++ b/database/gredis/gredis_z_unit_test.go @@ -28,6 +28,7 @@ var ( Db: 1, } + //demo for tls config tlsConfig = gredis.Config{ Host: "127.0.0.1", Port: 6379, @@ -390,27 +391,3 @@ func Test_Auto_MarshalSlice(t *testing.T) { t.Assert(users2, users1) }) } - -func Test_Conn_TLS(t *testing.T) { - gtest.C(t, func(t *gtest.T) { - redis := gredis.New(tlsConfig) - defer redis.Close() - conn := redis.Conn() - defer conn.Close() - - key := gconv.String(gtime.TimestampNano()) - value := []byte("v") - r, err := conn.Do("SET", key, value) - t.Assert(err, nil) - - r, err = conn.Do("GET", key) - t.Assert(err, nil) - t.Assert(r, value) - - _, err = conn.Do("DEL", key) - t.Assert(err, nil) - r, err = conn.Do("GET", key) - t.Assert(err, nil) - t.Assert(r, nil) - }) -} From 8bac0614f578cd4d3cedeb86e0f9566bcd685368 Mon Sep 17 00:00:00 2001 From: xbkaishui Date: Wed, 22 Jul 2020 15:13:40 +0800 Subject: [PATCH 028/348] format code --- database/gredis/gredis.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/database/gredis/gredis.go b/database/gredis/gredis.go index 45de72de1..9b7d2d173 100644 --- a/database/gredis/gredis.go +++ b/database/gredis/gredis.go @@ -46,8 +46,8 @@ type Config struct { IdleTimeout time.Duration // Maximum idle time for connection (default is 10 seconds, not allowed to be set to 0) MaxConnLifetime time.Duration // Maximum lifetime of the connection (default is 30 seconds, not allowed to be set to 0) ConnectTimeout time.Duration // Dial connection timeout. - TLS bool // Specifies the config to use when a TLS connection is dialed. - TLSSkipVerify bool // Disables server name verification when connecting over TLS + TLS bool // Specifies the config to use when a TLS connection is dialed. + TLSSkipVerify bool // Disables server name verification when connecting over TLS } // Pool statistics. From 2798fa4444040e04135e6cb8e8aae4d95d48a783 Mon Sep 17 00:00:00 2001 From: xbkaishui Date: Wed, 22 Jul 2020 15:27:00 +0800 Subject: [PATCH 029/348] revert unit test --- database/gredis/gredis_z_unit_test.go | 9 --------- 1 file changed, 9 deletions(-) diff --git a/database/gredis/gredis_z_unit_test.go b/database/gredis/gredis_z_unit_test.go index 3270af08f..23e5804f0 100644 --- a/database/gredis/gredis_z_unit_test.go +++ b/database/gredis/gredis_z_unit_test.go @@ -27,15 +27,6 @@ var ( Port: 6379, Db: 1, } - - //demo for tls config - tlsConfig = gredis.Config{ - Host: "127.0.0.1", - Port: 6379, - Db: 1, - TLS: true, - TLSSkipVerify: true, - } ) func Test_NewClose(t *testing.T) { From a3cb4a6ae8959ad10984e001fa3389cbcc8a64e6 Mon Sep 17 00:00:00 2001 From: botphp Date: Wed, 22 Jul 2020 16:15:06 +0800 Subject: [PATCH 030/348] =?UTF-8?q?=E9=A9=BC=E5=B3=B0=E8=BD=AC=E6=8D=A2?= =?UTF-8?q?=E4=B8=8B=E5=88=92=E7=BA=BF=E6=97=B6=EF=BC=8C=E4=B8=8D=E8=BF=9E?= =?UTF-8?q?=E7=BB=AD=E5=A4=A7=E5=86=99=E5=AD=97=E6=AF=8D=E9=A6=96=E4=BD=8D?= =?UTF-8?q?=E5=8A=A0=E4=B8=8B=E5=88=92=E7=BA=BF=E8=A7=84=E8=8C=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- text/gstr/gstr_case.go | 30 ++++++++++++++++++++++++++++++ text/gstr/gstr_z_unit_case_test.go | 23 +++++++++++++++++++++++ 2 files changed, 53 insertions(+) diff --git a/text/gstr/gstr_case.go b/text/gstr/gstr_case.go index b5b3b560e..79d4eb3cc 100644 --- a/text/gstr/gstr_case.go +++ b/text/gstr/gstr_case.go @@ -53,6 +53,36 @@ func SnakeScreamingCase(s string) string { return DelimitedScreamingCase(s, '_', true) } +// SnakeFirstUpperCase converts a string from RGBCodeMd5 to rgb_code_md5. +// The length of word should not be too long +func SnakeFirstUpperCase(word string, underscore ...string) string { + replace := "_" + if len(underscore) > 0 { + replace = underscore[0] + } + + r := regexp.MustCompile(`([\w\W]*?)([_]?[A-Z]+)$`) + m := r.FindAllStringSubmatch(word, 1) + if len(m) > 0 { + word = m[0][1] + replace + TrimLeft(ToLower(m[0][2]), replace) + } + + r = regexp.MustCompile(`([A-Z]+)([A-Z]?[_a-z\d]+)|$`) + for { + m := r.FindAllStringSubmatch(word, 1) + if len(m) > 0 && m[0][1] != "" { + w := strings.ToLower(m[0][1]) + w = string(w[:len(w)-1]) + replace + string(w[len(w)-1]) + + word = strings.Replace(word, m[0][1], w, 1) + } else { + break + } + } + + return TrimLeft(word, replace) +} + // KebabCase converts a string to kebab-case func KebabCase(s string) string { return DelimitedCase(s, '-') diff --git a/text/gstr/gstr_z_unit_case_test.go b/text/gstr/gstr_z_unit_case_test.go index 4c9298658..f9df4fe6e 100644 --- a/text/gstr/gstr_z_unit_case_test.go +++ b/text/gstr/gstr_z_unit_case_test.go @@ -170,3 +170,26 @@ func Test_DelimitedScreamingCase(t *testing.T) { } } } + +func TestSnakeFirstUpperCase(t *testing.T) { + cases := [][]string{ + {"RGBCodeMd5", "rgb_code_md5"}, + {"testCase", "test_case"}, + {"Md5", "md5"}, + {"userID", "user_id"}, + {"RGB", "rgb"}, + {"RGBCode", "rgb_code"}, + {"_ID", "id"}, + {"User_ID", "user_id"}, + {"user_id", "user_id"}, + {"md5", "md5"}, + } + for _, i := range cases { + in := i[0] + out := i[1] + result := gstr.SnakeFirstUpperCase(in) + if result != out { + t.Error("'" + result + "' != '" + out + "'") + } + } +} From 854b2ed185ca7a24cb7ff57907386a6a09f7579e Mon Sep 17 00:00:00 2001 From: john Date: Thu, 23 Jul 2020 21:01:16 +0800 Subject: [PATCH 031/348] improve function convertValue for package gdb --- database/gdb/gdb_structure.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/database/gdb/gdb_structure.go b/database/gdb/gdb_structure.go index 664c4c3e8..1235d5f8c 100644 --- a/database/gdb/gdb_structure.go +++ b/database/gdb/gdb_structure.go @@ -22,6 +22,9 @@ import ( // convertValue automatically checks and converts field value from database type // to golang variable type. func (c *Core) convertValue(fieldValue []byte, fieldType string) interface{} { + if fieldType == "" { + return fieldValue + } t, _ := gregex.ReplaceString(`\(.+\)`, "", fieldType) t = strings.ToLower(t) switch t { @@ -127,7 +130,7 @@ func (c *Core) convertValue(fieldValue []byte, fieldType string) interface{} { return t.Format("Y-m-d") default: - return string(fieldValue) + return fieldValue } } } From d9da51933d1f7957704df162ba696e894e9ba562 Mon Sep 17 00:00:00 2001 From: john Date: Thu, 23 Jul 2020 21:15:54 +0800 Subject: [PATCH 032/348] improve gconv.Struct --- util/gconv/gconv_struct.go | 3 ++- util/gconv/gconv_z_unit_struct_test.go | 20 ++++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/util/gconv/gconv_struct.go b/util/gconv/gconv_struct.go index 99c0160f9..37a80e9bc 100644 --- a/util/gconv/gconv_struct.go +++ b/util/gconv/gconv_struct.go @@ -70,7 +70,8 @@ func doStruct(params interface{}, pointer interface{}, recursive bool, mapping . } // paramsMap is the map[string]interface{} type variable for params. - paramsMap := MapDeep(params) + // DO NOT use MapDeep here. + paramsMap := Map(params) if paramsMap == nil { return gerror.Newf("invalid params: %v", params) } diff --git a/util/gconv/gconv_z_unit_struct_test.go b/util/gconv/gconv_z_unit_struct_test.go index 6d5a2e8f0..1b5e0666b 100644 --- a/util/gconv/gconv_z_unit_struct_test.go +++ b/util/gconv/gconv_z_unit_struct_test.go @@ -961,3 +961,23 @@ func Test_Struct_WithInterfaceAttr(t *testing.T) { t.Assert(v1.TestInterface, nil) }) } + +func Test_Struct_To_Struct(t *testing.T) { + var TestA struct { + Id int `p:"id"` + Date time.Time `p:"date"` + } + + var TestB struct { + Id int `p:"id"` + Date time.Time `p:"date"` + } + TestB.Id = 666 + TestB.Date = time.Now() + + gtest.C(t, func(t *gtest.T) { + t.Assert(gconv.Struct(TestB, &TestA), nil) + t.Assert(TestA.Id, TestB.Id) + t.Assert(TestA.Date, TestB.Date) + }) +} From 1250b33220b21f994238f29c4bdb7aa130d25974 Mon Sep 17 00:00:00 2001 From: botphp Date: Fri, 24 Jul 2020 09:48:51 +0800 Subject: [PATCH 033/348] =?UTF-8?q?=E5=B0=86=E6=AD=A3=E5=88=99=E7=A7=BB?= =?UTF-8?q?=E5=88=B0=E5=85=A8=E5=B1=80=E6=8F=90=E9=AB=98=E6=95=88=E7=8E=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- text/gstr/gstr_case.go | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/text/gstr/gstr_case.go b/text/gstr/gstr_case.go index 79d4eb3cc..e264f1e2a 100644 --- a/text/gstr/gstr_case.go +++ b/text/gstr/gstr_case.go @@ -14,6 +14,7 @@ // | DelimitedScreamingCase(s, '.') | ANY.KIND.OF.STRING | // | CamelCase(s) | AnyKindOfString | // | CamelLowerCase(s) | anyKindOfString | +// | SnakeFirstUpperCase(RGBCodeMd5) | rgb_code_md5 | package gstr @@ -25,6 +26,9 @@ import ( var ( numberSequence = regexp.MustCompile(`([a-zA-Z])(\d+)([a-zA-Z]?)`) numberReplacement = []byte(`$1 $2 $3`) + + firstCamelCaseStart = regexp.MustCompile(`([A-Z]+)([A-Z]?[_a-z\d]+)|$`) + firstCamelCaseEnd = regexp.MustCompile(`([\w\W]*?)([_]?[A-Z]+)$`) ) // CamelCase converts a string to CamelCase. @@ -55,21 +59,20 @@ func SnakeScreamingCase(s string) string { // SnakeFirstUpperCase converts a string from RGBCodeMd5 to rgb_code_md5. // The length of word should not be too long +// TODO for efficiency should change regexp to traversing string in future func SnakeFirstUpperCase(word string, underscore ...string) string { replace := "_" if len(underscore) > 0 { replace = underscore[0] } - r := regexp.MustCompile(`([\w\W]*?)([_]?[A-Z]+)$`) - m := r.FindAllStringSubmatch(word, 1) + m := firstCamelCaseEnd.FindAllStringSubmatch(word, 1) if len(m) > 0 { word = m[0][1] + replace + TrimLeft(ToLower(m[0][2]), replace) } - r = regexp.MustCompile(`([A-Z]+)([A-Z]?[_a-z\d]+)|$`) for { - m := r.FindAllStringSubmatch(word, 1) + m := firstCamelCaseStart.FindAllStringSubmatch(word, 1) if len(m) > 0 && m[0][1] != "" { w := strings.ToLower(m[0][1]) w = string(w[:len(w)-1]) + replace + string(w[len(w)-1]) From f489e6273e015f93e272f2df239275ad6254ebe4 Mon Sep 17 00:00:00 2001 From: john Date: Sat, 25 Jul 2020 10:54:48 +0800 Subject: [PATCH 034/348] fix issue 819 --- database/gdb/gdb_core.go | 27 +++-- database/gdb/gdb_func.go | 117 +++++++++++++++++----- database/gdb/gdb_model_insert.go | 10 +- database/gdb/gdb_z_mysql_internal_test.go | 14 +++ util/gconv/gconv.go | 2 +- util/gconv/gconv_map.go | 6 +- util/gconv/gconv_struct.go | 2 +- 7 files changed, 137 insertions(+), 41 deletions(-) diff --git a/database/gdb/gdb_core.go b/database/gdb/gdb_core.go index 22fac5e8c..cf63c39a1 100644 --- a/database/gdb/gdb_core.go +++ b/database/gdb/gdb_core.go @@ -492,15 +492,15 @@ func (c *Core) DoBatchInsert(link Link, table string, list interface{}, option i params []interface{} listMap List ) - switch v := list.(type) { + switch value := list.(type) { case Result: - listMap = v.List() + listMap = value.List() case Record: - listMap = List{v.Map()} + listMap = List{value.Map()} case List: - listMap = v + listMap = value case Map: - listMap = List{v} + listMap = List{value} default: var ( rv = reflect.ValueOf(list) @@ -517,8 +517,21 @@ func (c *Core) DoBatchInsert(link Link, table string, list interface{}, option i for i := 0; i < rv.Len(); i++ { listMap[i] = DataToMapDeep(rv.Index(i).Interface()) } - case reflect.Map, reflect.Struct: - listMap = List{DataToMapDeep(v)} + case reflect.Map: + listMap = List{DataToMapDeep(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] = DataToMapDeep(array[i]) + } + listMap = list + } else { + listMap = List{DataToMapDeep(value)} + } default: return result, errors.New(fmt.Sprint("unsupported list type:", kind)) } diff --git a/database/gdb/gdb_func.go b/database/gdb/gdb_func.go index 206bad1e3..0ee8c9d51 100644 --- a/database/gdb/gdb_func.go +++ b/database/gdb/gdb_func.go @@ -55,6 +55,9 @@ const ( var ( // quoteWordReg is the regular expression object for a word check. quoteWordReg = regexp.MustCompile(`^[a-zA-Z0-9\-_]+$`) + + // Priority tags for struct converting for orm field mapping. + structTagPriority = append([]string{ORM_TAG_FOR_STRUCT}, gconv.StructTagPriority...) ) // ListItemValues is alias for gutil.ListItemValues. @@ -78,33 +81,99 @@ func GetInsertOperationByOption(option int) string { } // DataToMapDeep converts struct object to map type recursively. -func DataToMapDeep(obj interface{}) map[string]interface{} { - data := gconv.Map(obj, ORM_TAG_FOR_STRUCT) - for key, value := range data { - rv := reflect.ValueOf(value) - kind := rv.Kind() - if kind == reflect.Ptr { - rv = rv.Elem() - kind = rv.Kind() +// The parameter should be type of *map/map/*struct/struct. +// It supports inherit struct definition for struct. +func DataToMapDeep(value interface{}) map[string]interface{} { + if v, ok := value.(apiMapStrAny); ok { + return v.MapStrAny() + } + + var ( + rvValue reflect.Value + rvField reflect.Value + rvKind reflect.Kind + rtField reflect.StructField + ) + if v, ok := value.(reflect.Value); ok { + rvValue = v + } else { + rvValue = reflect.ValueOf(value) + } + rvKind = rvValue.Kind() + if rvKind == reflect.Ptr { + rvValue = rvValue.Elem() + rvKind = rvValue.Kind() + } + // If given is not a struct, it uses gconv.Map for converting. + if rvKind != reflect.Struct { + return gconv.Map(value, structTagPriority...) + } + // Struct handling. + var ( + fieldTag reflect.StructTag + rvType = rvValue.Type() + name = "" + data = make(map[string]interface{}) + ) + for i := 0; i < rvValue.NumField(); i++ { + rtField = rvType.Field(i) + rvField = rvValue.Field(i) + fieldName := rtField.Name + if !utils.IsLetterUpper(fieldName[0]) { + continue } - switch kind { - case reflect.Struct: - // The underlying driver supports time.Time/*time.Time types. - if _, ok := value.(time.Time); ok { - continue - } - if _, ok := value.(*time.Time); ok { - continue - } - // Use string conversion in default. - if s, ok := value.(apiString); ok { - data[key] = s.String() - continue - } - delete(data, key) - for k, v := range DataToMapDeep(value) { + // Struct attribute inherit + if rtField.Anonymous { + for k, v := range DataToMapDeep(rvField) { data[k] = v } + continue + } + // Other attributes. + name = "" + fieldTag = rtField.Tag + for _, tag := range structTagPriority { + if s := fieldTag.Get(tag); s != "" { + name = s + break + } + } + if name == "" { + name = fieldName + } else { + // The "orm" tag supports json tag feature: -, omitempty + // The "orm" tag would be like: "id,priority", so it should use splitting handling. + name = gstr.Trim(name) + if name == "-" { + continue + } + array := gstr.SplitAndTrim(name, ",") + if len(array) > 1 { + switch array[1] { + case "omitempty": + if empty.IsEmpty(rvField.Interface()) { + continue + } else { + name = array[0] + } + default: + name = array[0] + } + } + } + + // The underlying driver supports time.Time/*time.Time types. + fieldValue := rvField.Interface() + switch fieldValue.(type) { + case time.Time, *time.Time: + data[name] = fieldValue + default: + // Use string conversion in default. + if s, ok := fieldValue.(apiString); ok { + data[name] = s.String() + } else { + data[name] = fieldValue + } } } return data diff --git a/database/gdb/gdb_model_insert.go b/database/gdb/gdb_model_insert.go index 67fc378b0..ded9542a7 100644 --- a/database/gdb/gdb_model_insert.go +++ b/database/gdb/gdb_model_insert.go @@ -72,11 +72,11 @@ func (m *Model) Data(data ...interface{}) *Model { case reflect.Map: model.data = DataToMapDeep(data[0]) case reflect.Struct: - if v, ok := data[0].(apiMapStrAny); ok { - model.data = v.MapStrAny() - } else if v, ok := data[0].(apiInterfaces); ok { - array := v.Interfaces() - list := make(List, len(array)) + if v, ok := data[0].(apiInterfaces); ok { + var ( + array = v.Interfaces() + list = make(List, len(array)) + ) for i := 0; i < len(array); i++ { list[i] = DataToMapDeep(array[i]) } diff --git a/database/gdb/gdb_z_mysql_internal_test.go b/database/gdb/gdb_z_mysql_internal_test.go index 294b46bc3..0b0210035 100644 --- a/database/gdb/gdb_z_mysql_internal_test.go +++ b/database/gdb/gdb_z_mysql_internal_test.go @@ -8,6 +8,7 @@ package gdb import ( "fmt" + "github.com/go-sql-driver/mysql" "github.com/gogf/gf/os/gcmd" "github.com/gogf/gf/os/gtime" "github.com/gogf/gf/test/gtest" @@ -289,3 +290,16 @@ CREATE TABLE %s ( ) }) } + +// Fix issue: https://github.com/gogf/gf/issues/819 +func Test_Func_DataToMapDeep(t *testing.T) { + type Test struct { + ResetPasswordTokenAt mysql.NullTime `orm:"reset_password_token_at"` + } + gtest.C(t, func(t *gtest.T) { + m := DataToMapDeep(new(Test)) + t.Assert(len(m), 1) + t.AssertNE(m["reset_password_token_at"], nil) + t.Assert(m["reset_password_token_at"], new(mysql.NullTime)) + }) +} diff --git a/util/gconv/gconv.go b/util/gconv/gconv.go index 2586b00fe..c582c822c 100644 --- a/util/gconv/gconv.go +++ b/util/gconv/gconv.go @@ -34,7 +34,7 @@ var ( // Priority tags for Map*/Struct* functions. // Note, the "gconv", "param", "params" tags are used by old version of package. // It is strongly recommended using short tag "c" or "p" instead in the future. - structTagPriority = []string{"gconv", "param", "params", "c", "p", "json"} + StructTagPriority = []string{"gconv", "param", "params", "c", "p", "json"} ) // Convert converts the variable to the type , the type is specified by string. diff --git a/util/gconv/gconv_map.go b/util/gconv/gconv_map.go index c1f67df2f..671331649 100644 --- a/util/gconv/gconv_map.go +++ b/util/gconv/gconv_map.go @@ -168,15 +168,15 @@ func doMapConvert(value interface{}, recursive bool, tags ...string) map[string] rvKind reflect.Kind rt = rv.Type() name = "" - tagArray = structTagPriority + tagArray = StructTagPriority ) switch len(tags) { case 0: // No need handle. case 1: - tagArray = append(strings.Split(tags[0], ","), structTagPriority...) + tagArray = append(strings.Split(tags[0], ","), StructTagPriority...) default: - tagArray = append(tags, structTagPriority...) + tagArray = append(tags, StructTagPriority...) } for i := 0; i < rv.NumField(); i++ { rtField = rt.Field(i) diff --git a/util/gconv/gconv_struct.go b/util/gconv/gconv_struct.go index 37a80e9bc..44e9cf5c6 100644 --- a/util/gconv/gconv_struct.go +++ b/util/gconv/gconv_struct.go @@ -177,7 +177,7 @@ func doStruct(params interface{}, pointer interface{}, recursive bool, mapping . // The key of the tagMap is the attribute name of the struct, // and the value is its replaced tag name for later comparison to improve performance. tagMap := make(map[string]string) - for k, v := range structs.TagMapName(pointer, structTagPriority, true) { + for k, v := range structs.TagMapName(pointer, StructTagPriority, true) { tagMap[v] = replaceCharReg.ReplaceAllString(k, "") } From 937f8e69190920ef147508f9b3b84b3c7e1c57d7 Mon Sep 17 00:00:00 2001 From: chenall Date: Sat, 25 Jul 2020 10:57:40 +0800 Subject: [PATCH 035/348] fix configfile with UTF8-BOM issue --- encoding/gjson/gjson_api_new_load.go | 9 ++++++++- os/gcfg/gcfg_z_unit_test.go | 15 +++++++++++++-- os/gcfg/testdata/cfg-with-utf8-bom.toml | 4 ++++ 3 files changed, 25 insertions(+), 3 deletions(-) create mode 100644 os/gcfg/testdata/cfg-with-utf8-bom.toml diff --git a/encoding/gjson/gjson_api_new_load.go b/encoding/gjson/gjson_api_new_load.go index 9caf40081..52a5374d7 100644 --- a/encoding/gjson/gjson_api_new_load.go +++ b/encoding/gjson/gjson_api_new_load.go @@ -10,9 +10,10 @@ import ( "bytes" "errors" "fmt" - "github.com/gogf/gf/internal/json" "reflect" + "github.com/gogf/gf/internal/json" + "github.com/gogf/gf/encoding/gini" "github.com/gogf/gf/encoding/gtoml" "github.com/gogf/gf/encoding/gxml" @@ -188,6 +189,12 @@ func LoadContent(data interface{}, safe ...bool) (*Json, error) { if len(content) == 0 { return New(nil, safe...), nil } + + //ignore UTF8-BOM + if content[0] == 0xEF && content[1] == 0xBB && content[2] == 0xBF { + content = content[3:] + } + return doLoadContent(checkDataType(content), content, safe...) } diff --git a/os/gcfg/gcfg_z_unit_test.go b/os/gcfg/gcfg_z_unit_test.go index 2c81ca6a0..71653d516 100644 --- a/os/gcfg/gcfg_z_unit_test.go +++ b/os/gcfg/gcfg_z_unit_test.go @@ -9,11 +9,12 @@ package gcfg_test import ( - "github.com/gogf/gf/debug/gdebug" - "github.com/gogf/gf/os/gtime" "os" "testing" + "github.com/gogf/gf/debug/gdebug" + "github.com/gogf/gf/os/gtime" + "github.com/gogf/gf/encoding/gjson" "github.com/gogf/gf/frame/g" "github.com/gogf/gf/os/gcfg" @@ -475,3 +476,13 @@ func TestCfg_Config(t *testing.T) { t.Assert(gcfg.GetContent("name"), "") }) } + +func TestCfg_With_UTF8_BOM(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + cfg := g.Cfg() + t.Assert(cfg.SetPath("testdata"), nil) + cfg.SetFileName("cfg-with-utf8-bom.toml") + t.Assert(cfg.GetInt("test.testInt"), 1) + t.Assert(cfg.GetString("test.testStr"), "test") + }) +} diff --git a/os/gcfg/testdata/cfg-with-utf8-bom.toml b/os/gcfg/testdata/cfg-with-utf8-bom.toml new file mode 100644 index 000000000..19f8005ff --- /dev/null +++ b/os/gcfg/testdata/cfg-with-utf8-bom.toml @@ -0,0 +1,4 @@ + +[test] +testInt=1 +testStr="test" From 245c6d24a1e8e47e38eb130da14961bef758cbce Mon Sep 17 00:00:00 2001 From: john Date: Sat, 25 Jul 2020 11:24:35 +0800 Subject: [PATCH 036/348] improve ghttp.Client --- net/ghttp/ghttp_client_request.go | 4 ++++ net/ghttp/ghttp_client_response.go | 3 +++ 2 files changed, 7 insertions(+) diff --git a/net/ghttp/ghttp_client_request.go b/net/ghttp/ghttp_client_request.go index 45c84c2b7..0c1a6251a 100644 --- a/net/ghttp/ghttp_client_request.go +++ b/net/ghttp/ghttp_client_request.go @@ -227,6 +227,10 @@ func (c *Client) DoRequest(method, url string, data ...interface{}) (resp *Clien req.Body = utils.NewReadCloser(reqBodyContent, false) for { if resp.Response, err = c.Do(req); err != nil { + // The response might not be nil when err != nil. + if resp.Response != nil { + resp.Response.Body.Close() + } if c.retryCount > 0 { c.retryCount-- time.Sleep(c.retryInterval) diff --git a/net/ghttp/ghttp_client_response.go b/net/ghttp/ghttp_client_response.go index a1c391b4a..30995be5b 100644 --- a/net/ghttp/ghttp_client_response.go +++ b/net/ghttp/ghttp_client_response.go @@ -63,6 +63,9 @@ func (r *ClientResponse) ReadAllString() string { // Close closes the response when it will never be used. func (r *ClientResponse) Close() error { + if r == nil || r.Response == nil || r.Response.Close { + return nil + } r.Response.Close = true return r.Response.Body.Close() } From 04dee090a31fbd5b7a05134ea948149bc2ec1965 Mon Sep 17 00:00:00 2001 From: john Date: Sat, 25 Jul 2020 13:50:04 +0800 Subject: [PATCH 037/348] improve graceful reload feature for ghttp.Server --- internal/intlog/intlog.go | 1 + net/ghttp/ghttp_server.go | 3 +++ net/ghttp/ghttp_server_admin.go | 12 +++++++----- net/ghttp/ghttp_server_admin_process.go | 8 +++++++- net/ghttp/ghttp_server_config.go | 2 +- 5 files changed, 19 insertions(+), 7 deletions(-) diff --git a/internal/intlog/intlog.go b/internal/intlog/intlog.go index de62a0978..24d05d539 100644 --- a/internal/intlog/intlog.go +++ b/internal/intlog/intlog.go @@ -25,6 +25,7 @@ var ( ) func init() { + // Debugging configured. if !cmdenv.Get("GF_DEBUG").IsEmpty() { isGFDebug = true return diff --git a/net/ghttp/ghttp_server.go b/net/ghttp/ghttp_server.go index 3d7a6cdb1..c620b2ec8 100644 --- a/net/ghttp/ghttp_server.go +++ b/net/ghttp/ghttp_server.go @@ -210,7 +210,10 @@ func serverProcessInit() { // Process message handler. // It's enabled only graceful feature is enabled. if gracefulEnabled { + intlog.Printf("%d: graceful reload feature is enabled", gproc.Pid()) go handleProcessMessage() + } else { + intlog.Printf("%d: graceful reload feature is disabled", gproc.Pid()) } // It's an ugly calling for better initializing the main package path diff --git a/net/ghttp/ghttp_server_admin.go b/net/ghttp/ghttp_server_admin.go index 5f79692c2..474094a77 100644 --- a/net/ghttp/ghttp_server_admin.go +++ b/net/ghttp/ghttp_server_admin.go @@ -7,7 +7,7 @@ package ghttp import ( - "os" + "github.com/gogf/gf/os/gfile" "strings" "time" @@ -22,8 +22,9 @@ type utilAdmin struct{} // Index shows the administration page. func (p *utilAdmin) Index(r *Request) { data := map[string]interface{}{ - "pid": gproc.Pid(), - "uri": strings.TrimRight(r.URL.Path, "/"), + "pid": gproc.Pid(), + "path": gfile.SelfPath(), + "uri": strings.TrimRight(r.URL.Path, "/"), } buffer, _ := gview.ParseContent(` @@ -31,7 +32,8 @@ func (p *utilAdmin) Index(r *Request) { GoFrame Web Server Admin -

PID: {{.pid}}

+

Pid: {{.pid}}

+

File Path: {{.path}}

Restart

Shutdown

@@ -46,7 +48,7 @@ func (p *utilAdmin) Restart(r *Request) { // Custom start binary path when this process exits. path := r.GetQueryString("newExeFilePath") if path == "" { - path = os.Args[0] + path = gfile.SelfPath() } if len(path) > 0 { err = RestartAllServer(path) diff --git a/net/ghttp/ghttp_server_admin_process.go b/net/ghttp/ghttp_server_admin_process.go index f88212278..ad4032c0f 100644 --- a/net/ghttp/ghttp_server_admin_process.go +++ b/net/ghttp/ghttp_server_admin_process.go @@ -10,6 +10,7 @@ import ( "bytes" "errors" "fmt" + "github.com/gogf/gf/internal/intlog" "github.com/gogf/gf/text/gstr" "os" "runtime" @@ -49,6 +50,9 @@ var serverProcessStatus = gtype.NewInt() // RestartAllServer restarts all the servers of the process. // The optional parameter specifies the new binary file for creating process. func RestartAllServer(newExeFilePath ...string) error { + if !gracefulEnabled { + return errors.New("graceful reload feature is disabled") + } serverActionLocker.Lock() defer serverActionLocker.Unlock() if err := checkProcessStatus(); err != nil { @@ -147,7 +151,7 @@ func forkRestartProcess(newExeFilePath ...string) error { env = append(env, gADMIN_ACTION_RESTART_ENVKEY+"=1") p := gproc.NewProcess(path, os.Args, env) if _, err := p.Start(); err != nil { - glog.Errorf("%d: fork process failed, error:%s", gproc.Pid(), err.Error()) + glog.Errorf(`%d: fork process failed, error:%s, are you running using "go run"?`, gproc.Pid(), err.Error()) return err } return nil @@ -257,8 +261,10 @@ func handleProcessMessage() { for { if msg := gproc.Receive(gADMIN_GPROC_COMM_GROUP); msg != nil { if bytes.EqualFold(msg.Data, []byte("exit")) { + intlog.Printf("%d: process message: exit", gproc.Pid()) gracefulShutdownWebServers() allDoneChan <- struct{}{} + intlog.Printf("%d: process message: exit done", gproc.Pid()) return } } diff --git a/net/ghttp/ghttp_server_config.go b/net/ghttp/ghttp_server_config.go index 4adb34210..b4983af9d 100644 --- a/net/ghttp/ghttp_server_config.go +++ b/net/ghttp/ghttp_server_config.go @@ -267,7 +267,7 @@ func Config() ServerConfig { ClientMaxBodySize: 8 * 1024 * 1024, // 8MB FormParsingMemory: 1024 * 1024, // 1MB Rewrites: make(map[string]string), - Graceful: true, + Graceful: false, } } From 437fc046205a46a5f2450e1887d8572d9ce79bd6 Mon Sep 17 00:00:00 2001 From: chenall Date: Sat, 25 Jul 2020 14:07:33 +0800 Subject: [PATCH 038/348] improve testCfg_With_UTF8_BOM unit_test --- os/gcfg/gcfg_z_unit_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/os/gcfg/gcfg_z_unit_test.go b/os/gcfg/gcfg_z_unit_test.go index 71653d516..3cec43324 100644 --- a/os/gcfg/gcfg_z_unit_test.go +++ b/os/gcfg/gcfg_z_unit_test.go @@ -479,7 +479,7 @@ func TestCfg_Config(t *testing.T) { func TestCfg_With_UTF8_BOM(t *testing.T) { gtest.C(t, func(t *gtest.T) { - cfg := g.Cfg() + cfg := g.Cfg("test-cfg-with-utf8-bom") t.Assert(cfg.SetPath("testdata"), nil) cfg.SetFileName("cfg-with-utf8-bom.toml") t.Assert(cfg.GetInt("test.testInt"), 1) From 9b8d63e21b7943c8de079d2781b56cc13aea806a Mon Sep 17 00:00:00 2001 From: john Date: Sat, 25 Jul 2020 14:09:03 +0800 Subject: [PATCH 039/348] improve graceful reload feature for ghttp.Server --- .example/net/ghttp/server/reload/admin.go | 1 + i18n/gi18n/gi18n_manager.go | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/.example/net/ghttp/server/reload/admin.go b/.example/net/ghttp/server/reload/admin.go index 94caa4040..a4147ccc8 100644 --- a/.example/net/ghttp/server/reload/admin.go +++ b/.example/net/ghttp/server/reload/admin.go @@ -6,6 +6,7 @@ import ( func main() { s := g.Server() + s.SetConfigWithMap(g.Map{"Graceful": true}) s.EnableAdmin() s.SetPort(8199) s.Run() diff --git a/i18n/gi18n/gi18n_manager.go b/i18n/gi18n/gi18n_manager.go index 745632877..e72171e1b 100644 --- a/i18n/gi18n/gi18n_manager.go +++ b/i18n/gi18n/gi18n_manager.go @@ -222,10 +222,10 @@ func (m *Manager) init() { } else if m.options.Path != "" { files, _ := gfile.ScanDirFile(m.options.Path, "*.*", true) if len(files) == 0 { - intlog.Printf( - "no i18n files found in configured directory: %s", - m.options.Path, - ) + //intlog.Printf( + // "no i18n files found in configured directory: %s", + // m.options.Path, + //) return } var ( From e252d8b740a888e6be1ba3fb889aed7642180c46 Mon Sep 17 00:00:00 2001 From: john Date: Sat, 25 Jul 2020 15:05:08 +0800 Subject: [PATCH 040/348] add function ListItemValuesUnique for package gdb --- database/gdb/gdb_func.go | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/database/gdb/gdb_func.go b/database/gdb/gdb_func.go index 0ee8c9d51..3334acdea 100644 --- a/database/gdb/gdb_func.go +++ b/database/gdb/gdb_func.go @@ -60,12 +60,29 @@ var ( structTagPriority = append([]string{ORM_TAG_FOR_STRUCT}, gconv.StructTagPriority...) ) -// ListItemValues is alias for gutil.ListItemValues. +// ListItemValues retrieves and returns the elements of all item struct/map with key . +// Note that the parameter should be type of slice which contains elements of map or struct, +// or else it returns an empty slice. +// +// The parameter supports types like: +// []map[string]interface{} +// []map[string]sub-map +// []struct +// []struct:sub-struct +// Note that the sub-map/sub-struct makes sense only if the optional parameter is given. // See gutil.ListItemValues. func ListItemValues(list interface{}, key interface{}, subKey ...interface{}) (values []interface{}) { return gutil.ListItemValues(list, key, subKey...) } +// ListItemValuesUnique retrieves and returns the unique elements of all struct/map with key . +// Note that the parameter should be type of slice which contains elements of map or struct, +// or else it returns an empty slice. +// See gutil.ListItemValuesUnique. +func ListItemValuesUnique(list interface{}, key string, subKey ...interface{}) []interface{} { + return gutil.ListItemValuesUnique(list, key, subKey...) +} + // GetInsertOperationByOption returns proper insert option with given parameter