Compare commits

...

275 Commits

Author SHA1 Message Date
262f27748c improve custom validation rule feature for package gvalid 2020-09-22 08:45:22 +08:00
a29aef7e1c improve package gvalid for custom rule 2020-09-21 23:51:30 +08:00
1671391195 comment updates 2020-09-21 22:56:48 +08:00
28b0d59c61 improve package gcfg/gjson for data loading 2020-09-21 21:30:58 +08:00
86433cef25 improve gtime.New, gconv.String 2020-09-18 23:59:49 +08:00
e96ccd5f71 v1.13.6 2020-09-17 23:49:03 +08:00
50a087bb3d Merge branch 'master' of https://github.com/gogf/gf 2020-09-17 23:41:23 +08:00
1ec049c52f improve gf server for routes check while starting 2020-09-17 23:27:10 +08:00
ec38805542 Merge pull request #908 from cai1111/patch-2
Update gdb_driver_mssql.go
2020-09-17 19:23:25 +08:00
4c8517d075 Update gdb_driver_mssql.go 2020-09-17 11:06:24 +08:00
edf06da6ea add struct which implements Interfaces function parameter support for gdb.Model.Insert/Save 2020-09-13 11:21:10 +08:00
eb43a2040e add ghttp.Client.RedirectLimit and gfile.ScanDirFileFunc 2020-09-09 22:26:46 +08:00
9d0ecc7d3e add more unit testing case for package gdb 2020-09-07 20:25:59 +08:00
ad943c5e02 improve gjson.New by using gconv.MapDeep for map/struct 2020-09-07 19:44:11 +08:00
bdb4fd0d25 donator updates 2020-09-07 19:10:04 +08:00
2440e05457 fix issue in limit operations for database driver oracle 2020-09-03 22:38:07 +08:00
1337c6c0d1 add sub query sql support for join functions 2020-09-03 21:57:58 +08:00
957689e07c up 2020-09-02 23:22:16 +08:00
3952d74f87 up 2020-09-02 21:36:21 +08:00
6dc4b81693 add max recursive depth for directory scanning for function gfile.Scan* 2020-09-02 21:25:02 +08:00
9cd953b7be improve function FieldsEx by filtering fields from custom fields specified by function Fields for package gdb 2020-09-02 20:37:02 +08:00
631810dda2 add function String for package gmap 2020-09-02 19:53:58 +08:00
8c12bc5506 change panic to internal logging for package glog 2020-09-01 21:22:19 +08:00
d4091a4826 improve some transaction operations by directly calling model operations, making their implements logic the same 2020-08-31 15:57:04 +08:00
a7c269886b improve support for dynamic database configurations in codes 2020-08-31 15:39:27 +08:00
f54593037b improve CURD functions for package gdb 2020-08-31 00:59:42 +08:00
0415cf6a08 fix issue in nil gtime attribute for model entity for package gdb 2020-08-31 00:39:12 +08:00
87c22d32b0 version updates 2020-08-26 14:49:53 +08:00
27dd15b403 improve initialization for logger of package glog 2020-08-26 14:37:02 +08:00
dd452c19ce add example for custom uploading file name for ghttp.Server 2020-08-24 23:05:30 +08:00
acc0846cf3 fix issue in unit testing case of package gvalid 2020-08-24 22:26:12 +08:00
5a6738841f travis ci update 2020-08-22 00:21:24 +08:00
bea451c9d6 improve date type support for package gdb; improve datetime string for gtime.StrToTime; improve function gtime.New for more types 2020-08-21 23:41:12 +08:00
676e904ec6 improve package gvalid 2020-08-21 00:00:00 +08:00
49aa5c61bc improve package gvalid 2020-08-20 23:37:02 +08:00
a2272b852c improve package gvalid 2020-08-20 23:28:58 +08:00
1fa77630f9 improve package gvalid 2020-08-20 23:28:10 +08:00
a841c4cc05 improve package gvalid 2020-08-20 23:25:36 +08:00
1874808e3b improve unit testing case for package gcache 2020-08-19 21:37:26 +08:00
149b67916b README updates 2020-08-19 21:10:40 +08:00
7fb6f58162 README updates 2020-08-19 21:09:25 +08:00
a309114a18 donator updates 2020-08-15 21:58:03 +08:00
35cbde9530 comment update for package gstr 2020-08-15 21:28:24 +08:00
b9211b182a comment update for function ghttp.Request. 2020-08-15 12:35:56 +08:00
81ec499ae9 Merge pull request #867 from pingyeaa/master
Solve the problem of invalid modification of request parameters
2020-08-15 12:33:24 +08:00
f2e276eabd add function Test_Params_Modify for ghttp_unit_param_test.go 2020-08-14 20:02:52 +08:00
f6dbaba1f8 add function ReloadParam for ghttp_request.go 2020-08-14 18:34:43 +08:00
65dcff052a add function ReloadParam for ghttp_request.go 2020-08-14 18:19:48 +08:00
e3861567c7 add function ReloadParam for ghttp_request.go 2020-08-14 16:22:37 +08:00
e89a20c725 add ReloadParam func to ghttp_request.go 2020-08-14 15:19:56 +08:00
a8acc6bd28 update unit testing case for package gdb 2020-08-14 00:51:22 +08:00
eb5efc735e improve unit testing case of ScanList for package gdb 2020-08-13 23:45:22 +08:00
737af527cd improve *Struct functions for ghttp.Request 2020-08-13 23:29:00 +08:00
875d2b7e63 donator updates 2020-08-13 19:25:51 +08:00
bf1cb0e1bd donator updates 2020-08-13 19:23:31 +08:00
2bfeb1b06c README updates 2020-08-13 18:59:13 +08:00
820e4302b7 improve function *Struct for ghttp.Request 2020-08-13 18:51:59 +08:00
84d761b418 add function AddWithRecover for package grpool 2020-08-12 23:53:05 +08:00
e5e27f2ac4 add function GetRouterMap for ghtt.Request 2020-08-12 21:01:17 +08:00
efca9b18a8 improve content type checks for package gjson 2020-08-12 20:53:47 +08:00
607821ecbc add function LoadContentType for package gjson 2020-08-12 20:38:48 +08:00
7cc1b239d4 add functions Update/UpdateExpire/GetExpire for package gcache 2020-08-12 20:13:13 +08:00
efa8de34da Merge pull request #860 from XWR940711/master
add func gcache.SetVar/gcache.SetExpire/gcacge.GetExpire.
2020-08-12 19:19:19 +08:00
bcf45e3c5a CI-glog失败重测 2020-08-12 10:32:40 +08:00
0a3cd1d2ab 修正方法代码.使用Benchmark测试. 2020-08-12 09:57:47 +08:00
3e621856c8 change the port range for unit testing cases of ghttp.Server 2020-08-12 09:10:50 +08:00
32e4d64ddb improve package gjson for automatic content type checking 2020-08-12 00:11:25 +08:00
46e45ca84b improve package gjson for automatic content type checking 2020-08-11 23:46:21 +08:00
fcb13bd8ee improve package gjson for automatic content type checking 2020-08-11 23:36:40 +08:00
eacad9b453 improve package gjson for automatic content type checking 2020-08-11 23:22:09 +08:00
ed479e2a13 improve package gjson for automatic content type checking 2020-08-11 23:20:22 +08:00
19937cb75d improve MapDeep for package gconv 2020-08-11 20:13:47 +08:00
a95093222c add GetRemoteIp for ghttp.Request; add *Var feature for ghttp.Client 2020-08-11 15:23:42 +08:00
7b599d1882 Merge branch 'master' of https://github.com/gogf/gf 2020-08-10 23:04:28 +08:00
4d501fd2f4 improve function Set for package gjson 2020-08-10 23:03:35 +08:00
c3d3053ded improve GetOrSetFunc for package gcache 2020-08-09 21:28:31 +08:00
14d5fd3e11 add translation format feature for package gi18n 2020-08-08 16:46:52 +08:00
9f79453334 improve ghttp.Server by closing the request and response body to release the file descriptor in time 2020-08-08 11:09:58 +08:00
c7f1c881c0 Merge pull request #850 from coderzhuang/master
fix: Access-Control-Request-Headers
2020-08-08 09:59:45 +08:00
c39c032b4e Merge pull request #844 from qinyuguang/scan
gcmd.Scan supports read line that contains whitespace
2020-08-08 09:58:32 +08:00
fa96c18308 add function MapOmitEmpty for package gutil 2020-08-08 09:53:32 +08:00
d90145a47f add goroutine id retrieving feature for package gdebug 2020-08-08 09:42:24 +08:00
4871f86346 adjust TestCache_Expire_SetVar Test Code. 2020-08-07 16:26:26 +08:00
919eaf1e9a adjust TestCache_Expire_SetVar Test Code. 2020-08-07 14:05:21 +08:00
8a84ca16d1 add func gcache.SetVar/gcache.SetExpire/gcacge.GetExpire.
add test TestCache_Expire_SetVar.
2020-08-07 13:24:41 +08:00
12b4fdd692 fix: Access-Control-Request-Headers 2020-08-04 11:57:42 +08:00
3d8451d5d0 comment update for gdb.Model.Page 2020-08-04 09:22:21 +08:00
cf88f28519 gcmd.Scanf supports read line that contains whitespace 2020-08-03 21:00:02 +08:00
15d99eee46 add more unit testing case for ghttp.Server 2020-08-03 20:49:19 +08:00
6d68277db8 improve cookie feature for ghttp.Server 2020-08-03 20:00:00 +08:00
3e3b5557f7 readme updates 2020-08-03 18:58:43 +08:00
ba1a9d9f8e gcmd.Scan supports read line that contains whitespace 2020-08-01 02:13:42 +08:00
3e1a7953ec Merge branch 'master' of https://github.com/gogf/gf 2020-07-31 09:57:45 +08:00
073fb2d717 improve sql logging structure for package gdb 2020-07-31 09:57:26 +08:00
7f33021184 Merge pull request #825 from chenall/gjson-support-utf8-with-bom
fix configfile with UTF8-BOM issue
2020-07-31 00:08:05 +08:00
b396096721 improve dry-run feature by adding global dry-run variable reading from environment or command options 2020-07-30 23:08:27 +08:00
0a5c6d832f add configration group name for logging content for package gdb 2020-07-30 23:00:20 +08:00
d279566114 Merge pull request #769 from fxk2006/master 2020-07-30 22:55:39 +08:00
2693cbb136 add more unit testing case for ghttp.Server 2020-07-30 22:45:50 +08:00
f7a9be4292 improve package glog for rotation feature 2020-07-30 21:09:45 +08:00
a926033b66 move Separator from const to variable for package gfile 2020-07-30 20:57:08 +08:00
dbcdd06b19 Merge branch 'master' of https://github.com/gogf/gf 2020-07-30 20:49:32 +08:00
ff5dab5c70 improve package glog for rotation feature 2020-07-30 20:49:11 +08:00
84b576418f Merge pull request #835 from XWR940711/master
add func gtimer.Entry.Reset()
2020-07-30 20:20:50 +08:00
253b124903 fix issue in database configuration for package gind 2020-07-30 20:18:18 +08:00
33dc5ddf79 fix issue in database configuration for package gind 2020-07-30 18:52:27 +08:00
a456fa537c fix logger configure issue for package gdb 2020-07-30 17:16:29 +08:00
9e1fb93e08 adjust gtimer.Entry.Reset() Test Code. 2020-07-30 11:00:19 +08:00
5d24f702be adjust gtimer.Entry.Reset() Test Code. 2020-07-30 10:37:48 +08:00
ca8e2c2986 version updates 2020-07-29 23:19:59 +08:00
2f0e5a45c5 improve package gcron 2020-07-29 23:03:31 +08:00
c4b28b0bc4 improve gtime.ParseDuration 2020-07-29 11:44:58 +08:00
6cc4747965 add func gtimer.Entry.Reset() 2020-07-29 11:22:46 +08:00
91cd34b26a improve multiple seperator chars handling for router of ghttp.Server 2020-07-29 00:32:54 +08:00
e35a2f028c add timeout feature for Do/Receive functions of package gredis 2020-07-28 23:48:12 +08:00
9cff2bd10f add function ParseDuration and time unit 'd' support for package gtime 2020-07-28 22:46:15 +08:00
6d406498db improve result data type converting for package gdb 2020-07-28 20:31:50 +08:00
939ae37baf example codes update for package gdb 2020-07-28 19:55:13 +08:00
4d6c8744e5 upgrade mxj 2020-07-25 15:31:51 +08:00
e252d8b740 add function ListItemValuesUnique for package gdb 2020-07-25 15:05:08 +08:00
9b8d63e21b improve graceful reload feature for ghttp.Server 2020-07-25 14:09:03 +08:00
437fc04620 improve testCfg_With_UTF8_BOM unit_test 2020-07-25 14:07:33 +08:00
04dee090a3 improve graceful reload feature for ghttp.Server 2020-07-25 13:50:04 +08:00
245c6d24a1 improve ghttp.Client 2020-07-25 11:24:35 +08:00
e92fd05f9f Merge pull request #815 from tikrgo/camelcaseConvert 2020-07-25 11:07:51 +08:00
937f8e6919 fix configfile with UTF8-BOM issue 2020-07-25 10:57:40 +08:00
f489e6273e fix issue 819 2020-07-25 10:54:48 +08:00
4f99bdbc87 Merge branch 'master' of https://github.com/gogf/gf into camelcaseConvert 2020-07-24 09:49:53 +08:00
1250b33220 将正则移到全局提高效率 2020-07-24 09:48:51 +08:00
b57aee4595 Merge pull request #800 from chenghonour/master
Add database method
2020-07-23 21:30:24 +08:00
d9da51933d improve gconv.Struct 2020-07-23 21:15:54 +08:00
9fca93e7d8 Merge branch 'master' of https://github.com/gogf/gf 2020-07-23 21:01:37 +08:00
854b2ed185 improve function convertValue for package gdb 2020-07-23 21:01:16 +08:00
cfac03bc40 Merge pull request #814 from xbkaishui/support-redis-tls
support redis tls
2020-07-22 19:49:29 +08:00
a3cb4a6ae8 驼峰转换下划线时,不连续大写字母首位加下划线规范 2020-07-22 16:15:06 +08:00
2798fa4444 revert unit test 2020-07-22 15:27:00 +08:00
8bac0614f5 format code 2020-07-22 15:13:40 +08:00
646280a6a9 remove tls unit test case 2020-07-22 15:08:32 +08:00
208bdffdf7 update comment 2020-07-22 14:02:21 +08:00
9e7291903f support redis tls 2020-07-22 13:28:45 +08:00
d56835fc00 go fmt 2020-07-21 13:28:25 +08:00
1d5e717a80 update err return 2020-07-21 12:40:13 +08:00
2f44d9ae18 add unit test 2020-07-21 12:37:04 +08:00
0627ab81d6 Merge pull request #810 from chenall/master
fix ghttp_client upload filename issue #809
2020-07-21 10:52:39 +08:00
8167a398fc add function GetHeader for ghttp.Request 2020-07-21 10:17:31 +08:00
6291751014 fix ghttp_client upload filename issue #809 2020-07-21 09:57:13 +08:00
ee5ddaab52 fix place holder for pgsql 2020-07-20 22:47:12 +08:00
207476be1f Merge pull request #807 from tiancai45/master
fix issue pq: syntax error at or near $
2020-07-20 22:15:20 +08:00
b9b470c2ae Merge branch 'master' into master 2020-07-20 22:14:29 +08:00
52b6e8ef9d fix place holder for mssql 2020-07-20 21:55:34 +08:00
48c84bf74a fix place holder for mssql 2020-07-20 21:48:44 +08:00
5be30b3684 fix issue in logging file rotation 2020-07-20 21:32:28 +08:00
54a2b13825 Merge pull request #802 from csrgxtu/bug/log-file-rotate
bug/log-file-rotate: fix big file even with rotate-by-size and rotate…
2020-07-20 21:30:52 +08:00
d44ddae3dc debug postgre 2020-07-20 19:13:15 +08:00
7bbc2459ba postgre 报错 pq: syntax error at or near $ 2020-07-19 23:24:35 +08:00
0cb77caa2a Merge pull request #803 from housemecn/master
update tencent site
2020-07-18 10:40:18 +08:00
3f5f76458d Update README_ZH.MD
update tencent site
2020-07-18 10:36:48 +08:00
835e07e8de Update README.MD
update tencent site
2020-07-18 10:34:32 +08:00
3fc5e43abe readme update 2020-07-18 10:21:17 +08:00
9c8cb26bd6 readme update 2020-07-18 10:17:05 +08:00
540f4d2d0c donation update 2020-07-18 10:06:26 +08:00
51be255821 remove gfcache usage from repo 2020-07-18 09:44:15 +08:00
7d278fea25 users update in readme 2020-07-18 09:40:39 +08:00
ca72d3b23a remove gfcache usage from repo 2020-07-18 08:32:35 +08:00
534cd3be1c add table field method 2020-07-17 14:28:50 +08:00
78536de1b5 add database method 2020-07-17 11:28:47 +08:00
edc67d9ec3 bug/log-file-rotate: fix big file even with rotate-by-size and rotate-back-expire 2020-07-17 10:39:14 +08:00
4dd12434b7 add file cache feature for package gfile and remove package gfcache 2020-07-16 12:31:13 +08:00
f654bb2eda comment update for package gtree 2020-07-16 11:46:11 +08:00
205f98cfeb improve gconv.Struct for interface UnmarshalValue 2020-07-15 23:30:07 +08:00
69fa5bf464 improve package gtime for nil gtime.Time object 2020-07-15 20:37:13 +08:00
e7dc58ac6c unit testing case update for package gdb 2020-07-15 20:21:04 +08:00
2033299632 improve placeholder for pgsql 2020-07-15 20:15:09 +08:00
639d34d5d9 fix issue in incorrect debug sql information for package gdb 2020-07-15 20:10:54 +08:00
605181da32 fix issue in gins.Database 2020-07-15 19:17:42 +08:00
183f631190 improve Model.One for package gdb 2020-07-15 09:15:03 +08:00
64c99b9871 add configuration updating feature for package gcfg 2020-07-15 00:07:07 +08:00
d29b0a27ff Merge pull request #760 from kirileec/master
add a SetProxy function and a chaining function Proxy for ghttp.Clien…
2020-07-14 23:09:06 +08:00
aaa726e6dc add limit 1 for function One of package gdb 2020-07-14 21:42:28 +08:00
05cc0c4644 improve gconv.Struct for nil attribute interface 2020-07-14 21:34:29 +08:00
e1c0a92e60 improve the performance when converting struct that implements UnmarshalValue 2020-07-13 23:51:36 +08:00
c770e4779a Merge pull request #758 from wnstar/log
log added function name display
2020-07-13 23:21:49 +08:00
c135122ca1 improve package gconv for detailed handling of interface attributes 2020-07-13 23:13:50 +08:00
1d87df2afe add more unit testing case for package ghttp 2020-07-13 22:45:20 +08:00
8efc0ca0ea comment update for package gvalid 2020-07-12 10:33:24 +08:00
001d524ff7 readme update 2020-07-12 10:25:15 +08:00
5c5dce9dc3 comment update for admin controller of package ghttp 2020-07-12 09:56:07 +08:00
293256c2ca improve package gmode 2020-07-12 09:34:43 +08:00
4e027c1de3 improve argument handling for empty slice for package gdb 2020-07-11 09:53:16 +08:00
6712a33164 fix issue 785 in function Unique for packge garray 2020-07-10 22:26:02 +08:00
98531143a6 donator updates 2020-07-10 21:32:53 +08:00
71127ba308 improve package gins 2020-07-10 00:20:46 +08:00
0f6f571ccb remove the space trimming of the validation value for package gvalid 2020-07-08 20:53:56 +08:00
9b6936a4fb deprecate function DB.Table; improve the configuration node name case-insensitive and ignoring special chars 2020-07-08 20:48:29 +08:00
8b78609412 add more unit testing case for package gdb 2020-07-08 19:12:48 +08:00
4a31082f8c improve function Struct/StructDeep for package gconv 2020-07-08 11:32:35 +08:00
3643a69d8d improve function Struct/StructDeep for package gconv 2020-07-08 10:52:45 +08:00
adbaa4aa89 donator updates 2020-07-07 22:06:35 +08:00
0e025eda1b improve function Struct for package gconv 2020-07-06 22:37:37 +08:00
7c44bf8e94 Merge branch 'develop' of https://github.com/gogf/gf into develop 2020-07-06 21:18:51 +08:00
e6718f1113 improve function Struct for package gconv 2020-07-06 21:18:25 +08:00
f3f6adb03a read Client's timeout for net.Dialer
make sure Client.Transport is *http.Transport
2020-07-05 20:52:33 +08:00
977827e453 add examples for SetProxy function and Proxy chaining function 2020-07-05 20:52:33 +08:00
bcc9153991 add a SetProxy function and a chaining function Proxy for ghttp.Client to do HTTP request via proxy. #285 2020-07-05 20:52:32 +08:00
c04be14cd9 add custom validation rule feature for package gvalid 2020-07-05 18:55:38 +08:00
de8f29751d improve function ScanList for package gdb 2020-07-05 12:17:56 +08:00
12d58e4d08 add ScanList feature for gdb.Model 2020-07-05 11:54:37 +08:00
3ae44185f4 add ScanList feature for gdb.Result 2020-07-05 11:23:39 +08:00
1290f42f75 improving package gdb 2020-07-04 08:50:52 +08:00
17b91dcad7 add ListItemValues/ListItemValuesUnique functions and associated unit testing cases for package gutil/gvar 2020-07-02 23:40:34 +08:00
72da1642ee add more unit testing case for ghttp.Client 2020-07-02 19:12:01 +08:00
76d93b3a61 donator updates 2020-07-01 23:05:39 +08:00
7be0ee9566 donator updates 2020-07-01 23:02:07 +08:00
326e1f8da5 donator updates 2020-07-01 22:53:34 +08:00
e4e44ddd19 revert gvar.Var from interface to struct 2020-06-29 17:23:49 +08:00
46bdde9265 revert gvar.Var from interface to struct 2020-06-29 13:40:19 +08:00
09eba58927 add more unit testing case for ghttp.Client 2020-06-28 23:23:09 +08:00
0e884c78f5 fix issue of https://github.com/gogf/gf/issues/765 2020-06-28 23:03:41 +08:00
2f44721086 fix issue in reload feature in ghttp.Server 2020-06-28 20:52:33 +08:00
6e08eebcbe 修改sql打印debug信息,增加数据库配置的group名称,用于区分sql来源,尤其是多数据库配置的时候 2020-06-28 16:58:43 +08:00
d9422d00ac 修改sql打印debug信息,增加数据库配置的group名称,用于区分sql来源,尤其是多数据库配置的时候 2020-06-28 16:50:13 +08:00
efc0501548 Merge branch 'master' of https://github.com/gogf/gf 2020-06-22 21:56:29 +08:00
bb07c60838 rename CopySlice to SliceCopy for package gutil 2020-06-22 21:56:10 +08:00
330ea05ca3 Merge pull request #755 from wnstar/master
add SetRedirectLimit function to do HTTP client. #714
2020-06-22 21:21:05 +08:00
1f534b48a3 Merge pull request #757 from leoleoasd/patch-1
Fix typo in git ignore
2020-06-22 20:30:35 +08:00
aceae5eee3 add todo for package gpool 2020-06-22 20:08:27 +08:00
d4d66fd529 improve package gtime 2020-06-22 19:46:39 +08:00
b7686d6d37 log added function name display 2020-06-22 11:04:57 +08:00
Leo
b97e397354 Fix typo in gitignore 2020-06-22 09:41:02 +08:00
ab8fbf171e add SetRedirectLimit function to client 2020-06-21 19:17:39 +08:00
2c0cfa24b0 fix issue in package gjson for struct to map converting 2020-06-18 20:40:24 +08:00
f28a76d470 merge develop 2020-06-17 23:59:20 +08:00
f3525c84a3 fix issue in debug infor for soft deleting feature of package gdb 2020-06-17 23:47:47 +08:00
abe9b54112 embarrassing jsoniter of another incompatibility with stdlib json 2020-06-17 21:43:51 +08:00
b2aa59d893 import third-party library json-iterator to improve the performance for json operations 2020-06-17 21:16:25 +08:00
9e9865afa7 author updates 2020-06-17 18:58:24 +08:00
ecc439d4e8 author update 2020-06-17 14:44:16 +08:00
452f0fc99e add Scan/ScanDeep for package gvar 2020-06-17 11:40:37 +08:00
54f47845f6 entity feature developing for package gdb 2020-06-17 11:37:45 +08:00
cb238cd6d9 add todo for gconv.Convert 2020-06-16 20:44:57 +08:00
897a9f4584 Merge branch 'develop' 2020-06-16 20:42:26 +08:00
55f9b121de Merge pull request #738 from fxk2006/fix_SessionRedis 2020-06-16 20:41:53 +08:00
386f38af5e improve gconv.Struct 2020-06-16 20:18:40 +08:00
65cea430c2 improva gconv.Struct 2020-06-16 19:30:35 +08:00
4d38b508a3 improve gvar by changig gvar.Var from type struct to interface 2020-06-16 17:38:05 +08:00
5c774fd391 improve function Structs for package gconv 2020-06-16 11:37:00 +08:00
f6d760e90f 1、修复redis存储session时,最大过期时间问题: ttl.Seconds()返回float64在redis中报错 2020-06-16 11:19:08 +08:00
c3ffa40bad improve Response/ResponseWriter by implementing http.Flusher for package ghttp 2020-06-15 23:36:20 +08:00
5ce5d0e593 improve auto marshal for struct slice for package gredis 2020-06-15 23:19:38 +08:00
b83f1efde8 remove panic for stdout error for package glog 2020-06-15 22:57:27 +08:00
ca5f14c366 comment updates for package gtcp 2020-06-15 22:51:44 +08:00
95a8b51fb4 improve map slice converting for package gconv 2020-06-15 18:59:18 +08:00
2b64979730 Merge branch 'master' of https://github.com/gogf/gf 2020-06-15 16:48:17 +08:00
508cb7db88 add and improve Scan/ScanDeep feature for package gdb/gvar/gjson/gconv 2020-06-15 16:46:48 +08:00
e8d6d96883 fix issue in error field name lost for package gvalid 2020-06-14 17:28:48 +08:00
9378a6e7bf readme update 2020-06-13 16:22:49 +08:00
1d609fc5c7 fix issue in unit testing case for package ghttp 2020-06-13 14:52:36 +08:00
49d1e3dbaf fix issue of attribute name lost in package gvalid 2020-06-12 22:25:58 +08:00
b0e9334852 go.mod updates 2020-06-12 14:10:33 +08:00
95bd369959 improve function GetClientIp for ghttp.Request 2020-06-11 18:47:56 +08:00
a433890097 Merge pull request #721 from kuiye/master
add get client realIp
2020-06-11 18:45:42 +08:00
c9c3be517c fix issue in example of package glist 2020-06-11 18:43:35 +08:00
89e122cd31 marge getip func 2020-06-11 09:49:16 +08:00
27c2a03ea8 add example for package gset 2020-06-10 23:07:07 +08:00
b56eb3a948 example update for package garray 2020-06-10 19:50:22 +08:00
fb1b0bfd88 example update for package g/garray/gmap/gset/ghttp 2020-06-10 19:47:25 +08:00
dd10167ec2 example updates for package garray 2020-06-10 19:09:14 +08:00
3138ad10ec add get client realIp 2020-06-10 10:44:41 +08:00
080ca82605 release updates 2020-06-09 21:29:28 +08:00
0290de2360 improve dump feature for ghttp.Request 2020-06-09 20:46:04 +08:00
9b330adc1d Merge pull request #718 from sanrentai/master
improve mssql TableFields func
2020-06-09 19:04:56 +08:00
f3ff1ae08b improve example for package gmap 2020-06-09 16:24:54 +08:00
4f699af051 improve pointer type convertion for package gconv 2020-06-09 14:19:23 +08:00
7ebc7259cb improve mssql TableFields func 2020-06-09 14:02:43 +08:00
9d22edbdb8 donator updates 2020-06-08 23:08:58 +08:00
276 changed files with 8314 additions and 1857 deletions

View File

@ -512,16 +512,6 @@ func mapToStruct() {
}
}
// getQueriedSqls
func getQueriedSqls() {
for k, v := range db.GetQueriedSqls() {
fmt.Println(k, ":")
fmt.Println("Sql :", v.Sql)
fmt.Println("Args :", v.Args)
fmt.Println("Error:", v.Error)
}
}
func main() {
db.PingMaster()

View File

@ -2,7 +2,6 @@ package main
import (
"fmt"
"github.com/gogf/gf/frame/g"
)
@ -11,11 +10,19 @@ func main() {
// 开启调试模式以便于记录所有执行的SQL
db.SetDebug(true)
r, e := db.Table("test").Order("id asc").All()
r, e := db.GetAll("SELECT * from `user` where id in(?)", g.Slice{})
if e != nil {
fmt.Println(e)
}
if r != nil {
fmt.Println(r.List())
fmt.Println(r)
}
return
//r, e := db.Table("user").Where("id in(?)", g.Slice{}).All()
//if e != nil {
// fmt.Println(e)
//}
//if r != nil {
// fmt.Println(r.List())
//}
}

View File

@ -8,9 +8,6 @@ import (
func main() {
db := g.DB()
db.SetMaxIdleConnCount(10)
db.SetMaxOpenConnCount(10)
db.SetMaxConnLifetime(time.Minute)
// 开启调试模式以便于记录所有执行的SQL
db.SetDebug(true)
@ -19,7 +16,7 @@ func main() {
for i := 0; i < 10; i++ {
go db.Table("user").All()
}
time.Sleep(time.Second)
time.Sleep(time.Millisecond * 100)
}
}

View File

@ -8,5 +8,5 @@ func main() {
db := g.DB()
db.SetDebug(true)
db.Table("user").Fields("DISTINCT id,nickname").Filter().All()
db.Table("user").Data("num=num+1").Where("id", 8).Update()
}

View File

@ -513,16 +513,6 @@ func mapToStruct() {
}
}
// getQueriedSqls
func getQueriedSqls() {
for k, v := range db.GetQueriedSqls() {
fmt.Println(k, ":")
fmt.Println("Sql :", v.Sql)
fmt.Println("Args :", v.Args)
fmt.Println("Error:", v.Error)
}
}
func main() {
db.PingMaster()

View File

@ -6,12 +6,8 @@ import (
func main() {
s := g.Server()
s.SetConfigWithMap(g.Map{"Graceful": true})
s.EnableAdmin()
//s.BindHookHandler("/admin/*any", ghttp.HOOK_BEFORE_SERVE, func(r *ghttp.Request) {
// if !r.BasicAuth("admin", "123", "") {
// r.Exit()
// }
//})
s.SetPort(8199)
s.Run()
}

View File

@ -1,29 +0,0 @@
package main
import (
"fmt"
"time"
"github.com/gogf/gf/os/gfcache"
"github.com/gogf/gf/os/gfile"
)
func main() {
s := 0
r := ""
path := gfile.TempDir() + gfile.Separator + "temp"
gfile.PutContents(path, "hello")
s = gfcache.GetSize()
r = gfcache.GetContents(path)
fmt.Println(s, r)
gfile.PutContentsAppend(path, " john")
// 等待1秒以便gfsnotify回调能处理完成
time.Sleep(time.Second)
s = gfcache.GetSize()
r = gfcache.GetContents(path)
fmt.Println(s, r)
}

4
.gitignore vendored
View File

@ -6,7 +6,7 @@
.idea/
.settings/
.vscode/
vender/
vendor/
composer.lock
gitpush.sh
pkg/
@ -15,4 +15,4 @@ cbuild
**/.DS_Store
.vscode/
go.sum
.example/other/
.example/other/

View File

@ -5,6 +5,7 @@ go:
- "1.12.x"
- "1.13.x"
- "1.14.x"
- "1.15.x"
branches:
only:

View File

@ -1,13 +1,17 @@
# Donators
We currently accept donation by Alipay/WechatPay, 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).
<img src="https://goframe.org/images/donate.png?20200718"/>
> If you cannot view the donation image, please click [here](https://goframe.org/images/donate.png).
| Name | Channel | Amount | Comment
|---|---|--- | ---
|[hailaz](https://gitee.com/hailaz)|gitee|¥20.00 |
|[ireadx](https://github.com/ireadx)|alipay|¥301.00 |
|[ireadx](https://github.com/ireadx)|alipay|¥501.00 |
|[mg91](https://gitee.com/mg91)|gitee|¥10.00 |
|[pibigstar](https://github.com/pibigstar)|alipay|¥10.00 |
|[tiangenglan](https://gitee.com/tiangenglan)|gitee|¥30.00 |
@ -46,7 +50,7 @@ We currently accept donation by Alipay/WechatPay, please note your github/gitee
|struggler|alipay|¥18.80|
|*铁|wechat|¥0.01|
|C*e|wechat|¥66.66| GF越来越好棒👍
|(佚名)|wechat|¥6.66| (名字打不出来也没备注捐赠时间2020-02-21 14:24:34)
|---P คิดถึง|wechat|¥6.66|
|[王飞](https://gitee.com/wang_2018)|gitee|¥20.00| 感谢您的开源项目!
|[Zeroing-ZY](https://gitee.com/yunjieg)|gitee|¥20.00| 感谢您的开源项目!
|[katydid酱](https://gitee.com/katydid2005)|gitee|¥50.00| 感谢您的开源项目!框架给予了很大的帮助!谢谢大佬!
@ -84,13 +88,39 @@ We currently accept donation by Alipay/WechatPay, please note your github/gitee
|千年|wechat|¥1.08|
|[szzxing](https://github.com/szzxing)|wechat|¥50.00|
|伟客互联|wechat|¥66.66|
|sbilly|wechat|¥99.00|
|[sbilly](https://github.com/sbilly)|wechat|¥99.00|
|**阳|alipay|¥100.01|
|**亮|alipay|¥10.00|
|[mingzaily](https://github.com/mingzaily)|alipay|¥30.00|
|[seny0929](https://gitee.com/seny0929)|alipay|¥9.90|
|Fly的狐狸|alipay|¥100.00|
|六七 ·|wechat|¥88.88| gf越来越好
|Tom|wechat|¥500.00| 一点心意 希望GF越来越好
|Chao|wechat|¥166.00|
|汤sir|wechat|¥10.00| 强哥,喝杯咖啡☕️
|秋叶、|wechat|¥66.66| GF带我一起玩
||wechat|¥20.00|
|程凤明|wechat|¥18.80|
|Glowworm|wechat|¥8.88| 感谢大佬
|徒行|wechat|¥10.00| 目前项目在转用这个框架,鼓励一下大佬吧👍👍👍
|产品设计.软件开发|wechat|¥10.00| 祝愿走向更成功!
|[charlieccGuo](https://github.com/charlieccGuo)|wechat|¥10.00|
|yiran|wechat|¥20.00| 赏给大佬喝茶🍵
|长夏朔酒|wechat|¥20.00| 请大佬喝茶
|💥聪จุ๊บ 🇨🇳|wechat|¥66.66| 请大佬喝茶
|Even_|wechat|¥10.00| 日照市民发来贺电!
|智慧人生|wechat|¥50.00| 智慧人生
|一滴水|wechat|¥10.00| goframe 越来越强大
|[wenzi1](https://github.com/wenzi1)|alipay|¥100.00|
|[hyuant](https://github.com/hyuant)|alipay|¥66.66|
|*庆|alipay|¥9.99| 支持一下gf越来越好
|**君|alipay|¥10.00| 加油
|向回走的闹钟|wechat|¥20.00| 越来越好
|金毛|wechat|¥100.00|
|莫失莫忘|wechat|¥100.00|
|**航|alipay|¥20.00|
<img src="https://goframe.org/images/donate.png"/>

View File

@ -31,6 +31,11 @@ require github.com/gogf/gf latest
golang version >= 1.11
```
# Architecture
<div align=center>
<img src="https://goframe.org/images/arch.png?v=12"/>
</div>
# Packages
1. **Primary Package**
@ -40,11 +45,6 @@ golang version >= 1.11
The community packages are contributed and maintained by community members, which are hosted in `gogf` organization. Some of the community packages are separated from the `gf` repository, which are not of common usage or are with heavy dependencies.
# Architecture
<div align=center>
<img src="https://goframe.org/images/arch.png?v=11"/>
</div>
# Performance
Here's the most popular Golang frameworks and libraries performance testing result in `WEB Server`. Performance testing cases source codes are hosted at: https://github.com/gogf/gf-performance
@ -115,9 +115,18 @@ 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.tencent.com/)
- [ZTE](https://www.zte.com.cn/china/)
- [Ant Financial Services](https://www.antfin.com/)
- [MedLinker](https://www.medlinker.com/)
- [KuCoin](https://www.kucoin.io/)
- [LeYouJia](https://www.leyoujia.com/)
- [IGG](https://igg.com)
- [XiMaLaYa](https://www.ximalaya.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).
# Contributors

View File

@ -45,6 +45,13 @@ require github.com/gogf/gf latest
golang版本 >= 1.11
```
# 架构
<div align=center>
<img src="https://goframe.org/images/arch.png?v=12"/>
</div>
# 模块
1. **核心模块**
@ -56,11 +63,6 @@ golang版本 >= 1.11
社区模块主要由社区贡献并维护,大部分也是由`gf`主仓库的贡献者提供及维护,存放于`gogf`空间下,与`gf`主仓库处于同一级别。有的社区模块是从`gf`主仓库中剥离出来单独维护的模块,这些模块并不是特别常用,或者对外部依赖较重。
# 架构
<div align=center>
<img src="https://goframe.org/images/arch.png?v=11"/>
</div>
# 性能
以下是目前最流行的`WEB Server` Golang框架/类库性能测试结果。
@ -135,7 +137,16 @@ ab -t 10 -c 100 http://127.0.0.1:3000/json
# 用户
由于商标版权缘故,未经厂商商务部授权允许无法用于宣传展示。
- [腾讯科技](https://www.tencent.com/)
- [中兴科技](https://www.zte.com.cn/china/)
- [蚂蚁金服](https://www.antfin.com/)
- [医联科技](https://www.medlinker.com/)
- [库币科技](https://www.kucoin.io/)
- [乐有家](https://www.leyoujia.com/)
- [IGG](https://igg.com)
- [喜马拉雅](https://www.ximalaya.com)
> 在这里只列举了部分知名的用户,如果您的企业或者产品正在使用`GoFrame`,欢迎到 [这里](https://github.com/gogf/gf/issues/168) 留言。
# 贡献

View File

@ -1,3 +1,213 @@
# `v1.13.1` (2020-06-10)
# GoFrame
`GF(Go Frame)`是一款模块化、高性能、生产级的Go基础开发框架。实现了比较完善的基础设施建设以及开发工具链提供了常用的基础开发模块缓存、日志、队列、数组、集合、容器、定时器、命令行、内存锁、对象池、配置管理、资源管理、数据校验、数据编码、定时任务、数据库ORM、TCP/UDP组件、进程管理/通信等等。并提供了Web服务开发的系列核心组件Router、Cookie、Session、Middleware、服务注册、模板引擎等等支持热重启、热更新、域名绑定、TLS/HTTPS、Rewrite等特性。
## 特点
* 模块化、松耦合设计;
* 模块丰富、开箱即用;
* 简便易用、易于维护;
* 高代码质量、高单元测试覆盖率;
* 社区活跃,大牛谦逊低调脾气好;
* 详尽的开发文档及示例;
* 完善的本地中文化支持;
* 设计为团队及企业使用;
## 发展
`GoFrame`开始得比较早,`2011`年始于北京一个智能物联网平台项目,那时还没有这么多物联网的现行标准,`Go`的标准库以及生态也未如此丰富。`2017`年的时候`GF`才开始发布测试版,`2018`年`1024`程序员节日的时候才发布`v1.0`正式版,为`Go`生态发展添砖加瓦。开源以来快速迭代、发展成长,广受开发者和企业的青睐,也有许多的开发者加入了贡献行列。`GF`原本是为开发团队设计的,因此她的开发效率和可维护性做得非常好,有着很高的代码质量以及丰富的单元测试和示例,并且`GF`是目前中文化文档做的最好的`Golang`开发框架。
# Change Log
1. 应多数开发者的要求,框架要求的最低`Golang`运行版本降级为了`v1.11`。
1. 新增`GoFrame`视频教程地址:
- bilibilihttps://www.bilibili.com/video/av94410029
- 西瓜视频: https://www.ixigua.com/pseries/6809291194665796100/
1. 将不常用的`guuid`模块迁移到 github.com/gogf/guuid 作为社区模块维护,保持`gf`主仓库的轻量级。
1. 新增`guid`模块用于高效轻量级的唯一字符串生成https://goframe.org/util/guid/index
## `tool chain`
1. 工具链更新https://goframe.org/toolchain/cli
1. 新增`gf env`命令,更优雅地查看当前`Golang`环境变量信息。
1. 新增`gf mod path`命令,用于将当前`go modules`包拷贝到`GOPATH`中,以便使用原始的`GOPATH`方式开发项目。
1. 对现有`cli`命令进行了一些改进,提高使用体验;预编译二进制版本在部分平台下提供了`upx`压缩,使得下载的文件更小。
## `container`
1. `garray`
- https://goframe.org/container/garray/index
- 简化数组使用方式,支持类似于`var garray.Array`的变量定义使用方式;
- 增加`Walk`方法,用于自定义的数组元素处理方法;
- 增加`ContainsI`方法,用于大小写忽略匹配的数组元素项存在性查找;
- 完善单元测试,代码覆盖率`94%`
- 代码改进,提高性能;
- 修复一些问题;
1. `gchan`
- 由于该封装包实际意义不是很大,因此从主框架中删除;
1. `glist`
- https://goframe.org/container/glist/index
- 简化链表使用方式,支持类似于`var glist.List`的变量定义使用方式;
- 完善单元测试,代码覆盖率`99%`
1. `gmap`
- https://goframe.org/container/gmap/index
- 简化`Map`使用方式,支持类似于`var gmap.Map`的变量定义使用方式;
- 完善单元测试,代码覆盖率`81%`
- 代码改进,提高性能;
1. `gset`
- https://goframe.org/container/gset/index
- 简化集合使用方式,支持类似于`var gset.Set`的变量定义使用方式;
- 增加`Walk`方法,用于自定义的集合元素处理方法;
- 完善单元测试,代码覆盖率`90%`
- 代码改进,提高性能;
1. `gtree`
- https://goframe.org/container/gtree/index
- 简化树型使用方式,支持类似于`var gtree.BTree`的变量定义使用方式;
- 完善单元测试,代码覆盖率`90%`
1. `gvar`
- https://goframe.org/container/gvar/index
- 完善单元测试,代码覆盖率`69%`
- 代码组织结构调整,提高维护性;
- 代码改进,提高性能;
## `database`
1. `gdb`
- 增加`Transaction(f func(tx *TX) error) (err error)`接口方法用于通过闭包实现事务封装处理https://goframe.org/database/gdb/transaction
- 去掉不常用的`From`接口方法,改进`Table`及`Model`方法的参数为不定参数并支持通过不定参数传递表别名https://goframe.org/database/gdb/chaining/select
- 增加`DryRun`特性,支持空跑时只执行查询不执行写入/更新/删除操作https://goframe.org/database/gdb/senior
- 增加`create_at`, `update_at`写入时间、更新时间字段自动填充特性https://goframe.org/database/gdb/chaining/auto-time
- 增加`delete_at`软删除特性https://goframe.org/database/gdb/chaining/auto-time
- 增加`Having`链式操作方法,用于`having`条件查询https://goframe.org/database/gdb/chaining/select
- `Result`结果对象增加`Chunk`方法用于自定义的数据分批处理https://goframe.org/database/gdb/result
- 改进`Schema`数据库运行时切换特性;
- 改进对`pgsql`, `mssql`, `sqlite`, `oracle`数据库字段类型的支持;
- 进一步完善单元测试;
- 代码组织结构调整,提高维护性;
- 代码改进,提高性能;
1. `gredis`
- 增加`MaxActive`连接池参数默认配置为`100`,限制默认的连接数量;
- 改进`Conn`连接对象的`Do`方法,支持对`map/slice/struct`类型进行自动的`json.Marshal`处理,注意获取数据时使用`DoVar`方法获取https://goframe.org/database/gredis/usage
- 完善单元测试,代码覆盖率`72%`
## `net`
1. `ghttp`
- 增加`Prefix`及`Retry`客户端链式操作方法;
- 增加客户端原始请求打印特性https://goframe.org/net/ghttp/client/demo/dump
- 增加`ClientMaxBodySize`的服务端配置,用于限制客户端提交的`Body`大小,默认为`8MB`在涉及到上传的Server中需要增加该配置的大小在配置文件中指定对应的大小即可如`ClientMaxBodySize="100MB"`https://goframe.org/net/ghttp/config
- 改进`SessionId`生成的随机性,提高`Session`安全性https://goframe.org/os/gsession/index
- 改进`ghttp.Server`实现了标准库的`http.Handler`接口,便于与其他第三方的服务如`Prometheus`进行代码集成;
- 其他大量的代码细节改进工作,提高性能及持久维护性;
- 完善单元测试,代码覆盖率`61%`
1. `gipv4`
- 增加`GetIpArray`方法用于获取当前主机的所有IPv4地址
- 增加`GetMacArray`及`GetMac`方法,用于获取当前主机的`MAC`地址信息;
- 修改`IntranetIP`方法名称为`GetIntranetIp`,修改`IntranetIPArray`方法名称为`GetIntranetIpArray`
## `encoding`
1. `gjson`
- 新增`GetMaps`获取`JSON`内部节点变量方法;
- 改进`NewWithTag`方法对`map/struct`的处理;
- 完善单元测试,代码覆盖率`77%`
1. `gyaml`
- 升级依赖的第三方`yaml`解析包,解决了`map[interface{}]interface{}`转换问题;
## `error`
1. `gerror`
- 新增`NewfSkip`方法,用于创建`skip`指定堆栈的错误对象;
- 放开框架所有的堆栈链路打印,展示错误时真实的链路调用详情;
## `os`
1. `gcache`
- 增加`GetVar`方法,用于获得可以便捷转换为其他数据类型的"泛型"变量;
- 标记`Removes`方法废弃,改进`Remove`方法参数为不定参数,统一使用`Remove`方法删除单个/多个键值对;
- 完善单元测试,代码覆盖率`96%`
1. `genv`
- 增加`GetVar`方法,用于获得可以便捷转换为其他数据类型的"泛型"变量;
1. `gfile`
- 改进`CopyDir/CopyFile`复制目录/文件方法;
- 新增`ScanDirFunc`方法,用于支持自定义处理回调的目录检索;
- 完善单元测试,代码覆盖率`64%`
1. `glog`
- 增加支持`Context`上下文变量的日志打印特性https://goframe.org/os/glog/context
1. `gres`
- 改进打包特性增强生成二进制文件及Go文件的压缩比比旧版本增加`20%`压缩率,使得编译生成的二进制文件体积更小;
- 代码结构改进,提高执行效率及可持久维护性;
1. `gsession`
- 改进`SessionId`默认生成方法,采用`guid.S`方法生成;
- 增加`SetId`及`SetIdFunc`方法,用于自定义`SessionId`及自定义的`SessionId`生成方法;
## `frame`
1. `g`
- 新增`g.Table`方法,用于快速创建数据库模型操作对象;
## `i18n`
1. `gi18n`
- 新增`GetContent`方法,用于获取指定`i18n`关键字为转译内容;
- 改进代码细节,提高性能和持久可维护性;
- 完善单元测试,代码覆盖率`74%`
## `test`
1. `gtest`
- 增加`AssertNQ`断言方法,用于强类型的不相等判断;
## `text`
1. `gstr`
- 增加`SubStrRune`方法,用于支持`unicode`的字符串截取;
- 增加`StrLimitRune`方法,用于支持`unicode`的字符串截断隐藏;
- 增加`LenRune`方法,用于替换`RuneLen`方法,统一方法命名风格;
- 增加`PosRune/PosIRune/PosRRune/PosRIRune`方法,用于支持`unicode`的字符串左右位置查找;
- 增加`CompareVersionGo`方法,用于`Golang`风格的版本号大小比较;
- 完善单元测试,代码覆盖率`75%`
## `util`
1. `gconv`
- 改进`Convert`转换方法,支持常见`map`类型的转换;
- 改进类型转换过程中异常错误的捕获,通过`error`返回;
- 其他一些细节改进;
- 完善单元测试,代码覆盖率`63%`
1. `grand`
- 增加`B`方法,用于获得随机的二进制数据;
- 改进代码底层实现,部分接口性能提高`50%`
- 完善单元测试,代码覆盖率`74%`
1. `guid`
- 新增`guid`模块用于高效轻量级的唯一字符串生成https://goframe.org/util/guid/index
1. `gutil`
- 增加`MapContains`方法用于判断map中是否包含指定键名
- 增加`MapDelete`方法用于删除map中指定的键名可以为多个键名
- 增加`MapMerge`方法用于合并两个map
- 增加`MapMergeCopy`方法用于拷贝多个map
- 增加`MapContainsPossibleKey`方法,用于查找指定键名,忽略大小写及字符`'-'/'_'/'.'/' '`
1. `gvalid`
- 所有默认的错误提示改为了英文;
- 错误提示的配置改为了通过`i18n`来配置实现以便支持国际化https://goframe.org/util/gvalid/message
- 身份证号规则名称从`id-number`改为了`resident-id `
- 银行卡号规则名称从`luhn`改为了`bank-card`
- 完善单元测试,代码覆盖率`96%`
## Bug Fix
1. 修复`gcompress`的多文件`zip`压缩问题;
1. 修复`ghttp.Client`获取返回的过期`Cookie`的问题;
1. 修复`gres.File`对于`http.File`接口的实现细节;
1. 修复`garray.Pop*`方法的边界问题;
1. 修复`gres`中`Readdir`方法参数为`0`时报错的问题;
1. 其他一些修复https://github.com/gogf/gf/issues?q=is%3Aissue+label%3Abug
# `v1.12.1` (2020-03-31)
大家好啊!久等啦!

View File

@ -8,10 +8,10 @@ package garray
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"github.com/gogf/gf/internal/empty"
"github.com/gogf/gf/internal/json"
"github.com/gogf/gf/text/gstr"
"math"
"sort"
@ -494,9 +494,11 @@ func (a *Array) Search(value interface{}) int {
func (a *Array) Unique() *Array {
a.mu.Lock()
for i := 0; i < len(a.array)-1; i++ {
for j := i + 1; j < len(a.array); j++ {
for j := i + 1; j < len(a.array); {
if a.array[i] == a.array[j] {
a.array = append(a.array[:j], a.array[j+1:]...)
} else {
j++
}
}
}

View File

@ -8,9 +8,9 @@ package garray
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"github.com/gogf/gf/internal/json"
"math"
"sort"
@ -510,9 +510,11 @@ func (a *IntArray) Search(value int) int {
func (a *IntArray) Unique() *IntArray {
a.mu.Lock()
for i := 0; i < len(a.array)-1; i++ {
for j := i + 1; j < len(a.array); j++ {
for j := i + 1; j < len(a.array); {
if a.array[i] == a.array[j] {
a.array = append(a.array[:j], a.array[j+1:]...)
} else {
j++
}
}
}

View File

@ -8,9 +8,9 @@ package garray
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"github.com/gogf/gf/internal/json"
"github.com/gogf/gf/text/gstr"
"math"
"sort"
@ -514,9 +514,11 @@ func (a *StrArray) Search(value string) int {
func (a *StrArray) Unique() *StrArray {
a.mu.Lock()
for i := 0; i < len(a.array)-1; i++ {
for j := i + 1; j < len(a.array); j++ {
for j := i + 1; j < len(a.array); {
if a.array[i] == a.array[j] {
a.array = append(a.array[:j], a.array[j+1:]...)
} else {
j++
}
}
}

View File

@ -8,9 +8,9 @@ package garray
import (
"bytes"
"encoding/json"
"fmt"
"github.com/gogf/gf/internal/empty"
"github.com/gogf/gf/internal/json"
"github.com/gogf/gf/text/gstr"
"github.com/gogf/gf/util/gutil"
"math"

View File

@ -8,8 +8,8 @@ package garray
import (
"bytes"
"encoding/json"
"fmt"
"github.com/gogf/gf/internal/json"
"math"
"sort"

View File

@ -8,7 +8,7 @@ package garray
import (
"bytes"
"encoding/json"
"github.com/gogf/gf/internal/json"
"github.com/gogf/gf/text/gstr"
"math"
"sort"

View File

@ -13,7 +13,7 @@ import (
"github.com/gogf/gf/container/garray"
)
func Example_basic() {
func ExampleNew() {
// A normal array.
a := garray.New()
@ -72,18 +72,18 @@ func Example_basic() {
// []
}
func Example_iterate() {
array := garray.NewStrArrayFrom(g.SliceStr{"a", "b", "c"})
func ExampleArray_Iterator() {
array := garray.NewArrayFrom(g.Slice{"a", "b", "c"})
// Iterator is alias of IteratorAsc, which iterates the array readonly in ascending order
// with given callback function <f>.
// If <f> returns true, then it continues iterating; or false to stop.
array.Iterator(func(k int, v string) bool {
array.Iterator(func(k int, v interface{}) bool {
fmt.Println(k, v)
return true
})
// IteratorDesc iterates the array readonly in descending order with given callback function <f>.
// If <f> returns true, then it continues iterating; or false to stop.
array.IteratorDesc(func(k int, v string) bool {
array.IteratorDesc(func(k int, v interface{}) bool {
fmt.Println(k, v)
return true
})
@ -97,7 +97,7 @@ func Example_iterate() {
// 0 a
}
func Example_reverse() {
func ExampleArray_Reverse() {
array := garray.NewFrom(g.Slice{1, 2, 3, 4, 5, 6, 7, 8, 9})
// Reverse makes array with elements in reverse order.
@ -107,14 +107,14 @@ func Example_reverse() {
// [9 8 7 6 5 4 3 2 1]
}
func Example_shuffle() {
func ExampleArray_Shuffle() {
array := garray.NewFrom(g.Slice{1, 2, 3, 4, 5, 6, 7, 8, 9})
// Shuffle randomly shuffles the array.
fmt.Println(array.Shuffle().Slice())
}
func Example_rand() {
func ExampleArray_Rands() {
array := garray.NewFrom(g.Slice{1, 2, 3, 4, 5, 6, 7, 8, 9})
// Randomly retrieve and return 2 items from the array.
@ -126,7 +126,19 @@ func Example_rand() {
fmt.Println(array.PopRand())
}
func Example_join() {
func ExampleArray_PopRand() {
array := garray.NewFrom(g.Slice{1, 2, 3, 4, 5, 6, 7, 8, 9})
// Randomly retrieve and return 2 items from the array.
// It does not delete the items from array.
fmt.Println(array.Rands(2))
// Randomly pick and return one item from the array.
// It deletes the picked up item from array.
fmt.Println(array.PopRand())
}
func ExampleArray_Join() {
array := garray.NewFrom(g.Slice{"a", "b", "c", "d"})
fmt.Println(array.Join(","))
@ -134,7 +146,7 @@ func Example_join() {
// a,b,c,d
}
func Example_chunk() {
func ExampleArray_Chunk() {
array := garray.NewFrom(g.Slice{1, 2, 3, 4, 5, 6, 7, 8, 9})
// Chunk splits an array into multiple arrays,
@ -146,7 +158,7 @@ func Example_chunk() {
// [[1 2] [3 4] [5 6] [7 8] [9]]
}
func Example_popItem() {
func ExampleArray_PopLeft() {
array := garray.NewFrom([]interface{}{1, 2, 3, 4, 5, 6, 7, 8, 9})
// Any Pop* functions pick, delete and return the item from array.
@ -163,22 +175,58 @@ func Example_popItem() {
// [7 8]
}
func Example_walk() {
var array garray.StrArray
tables := g.SliceStr{"user", "user_detail"}
prefix := "gf_"
array.Append(tables...)
// Add prefix for given table names.
array.Walk(func(value string) string {
return prefix + value
})
fmt.Println(array.Slice())
func ExampleArray_PopLefts() {
array := garray.NewFrom([]interface{}{1, 2, 3, 4, 5, 6, 7, 8, 9})
// Any Pop* functions pick, delete and return the item from array.
fmt.Println(array.PopLeft())
fmt.Println(array.PopLefts(2))
fmt.Println(array.PopRight())
fmt.Println(array.PopRights(2))
// Output:
// [gf_user gf_user_detail]
// 1 true
// [2 3]
// 9 true
// [7 8]
}
func Example_contains() {
func ExampleArray_PopRight() {
array := garray.NewFrom([]interface{}{1, 2, 3, 4, 5, 6, 7, 8, 9})
// Any Pop* functions pick, delete and return the item from array.
fmt.Println(array.PopLeft())
fmt.Println(array.PopLefts(2))
fmt.Println(array.PopRight())
fmt.Println(array.PopRights(2))
// Output:
// 1 true
// [2 3]
// 9 true
// [7 8]
}
func ExampleArray_PopRights() {
array := garray.NewFrom([]interface{}{1, 2, 3, 4, 5, 6, 7, 8, 9})
// Any Pop* functions pick, delete and return the item from array.
fmt.Println(array.PopLeft())
fmt.Println(array.PopLefts(2))
fmt.Println(array.PopRight())
fmt.Println(array.PopRights(2))
// Output:
// 1 true
// [2 3]
// 9 true
// [7 8]
}
func ExampleArray_Contains() {
var array garray.StrArray
array.Append("a")
fmt.Println(array.Contains("a"))
@ -191,7 +239,7 @@ func Example_contains() {
// true
}
func Example_mergeArray() {
func ExampleArray_Merge() {
array1 := garray.NewFrom(g.Slice{1, 2})
array2 := garray.NewFrom(g.Slice{3, 4})
slice1 := g.Slice{5, 6}
@ -210,7 +258,18 @@ func Example_mergeArray() {
// [1 2 1 2 3 4 5 6 7 8 9 0]
}
func Example_filter() {
func ExampleArray_FilterEmpty() {
array1 := garray.NewFrom(g.Slice{0, 1, 2, nil, "", g.Slice{}, "john"})
array2 := garray.NewFrom(g.Slice{0, 1, 2, nil, "", g.Slice{}, "john"})
fmt.Printf("%#v\n", array1.FilterNil().Slice())
fmt.Printf("%#v\n", array2.FilterEmpty().Slice())
// Output:
// []interface {}{0, 1, 2, "", []interface {}{}, "john"}
// []interface {}{1, 2, "john"}
}
func ExampleArray_FilterNil() {
array1 := garray.NewFrom(g.Slice{0, 1, 2, nil, "", g.Slice{}, "john"})
array2 := garray.NewFrom(g.Slice{0, 1, 2, nil, "", g.Slice{}, "john"})
fmt.Printf("%#v\n", array1.FilterNil().Slice())

View File

@ -0,0 +1,28 @@
// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
package garray_test
import (
"fmt"
"github.com/gogf/gf/container/garray"
"github.com/gogf/gf/frame/g"
)
func ExampleStrArray_Walk() {
var array garray.StrArray
tables := g.SliceStr{"user", "user_detail"}
prefix := "gf_"
array.Append(tables...)
// Add prefix for given table names.
array.Walk(func(value string) string {
return prefix + value
})
fmt.Println(array.Slice())
// Output:
// [gf_user gf_user_detail]
}

View File

@ -9,8 +9,8 @@
package garray_test
import (
"encoding/json"
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/internal/json"
"testing"
"time"
@ -95,9 +95,9 @@ func TestArray_Sort(t *testing.T) {
func TestArray_Unique(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
expect := []interface{}{1, 1, 2, 3}
expect := []interface{}{1, 2, 3, 4, 5, 3, 2, 2, 3, 5, 5}
array := garray.NewArrayFrom(expect)
t.Assert(array.Unique().Slice(), []interface{}{1, 2, 3})
t.Assert(array.Unique().Slice(), []interface{}{1, 2, 3, 4, 5})
})
}

View File

@ -9,8 +9,8 @@
package garray_test
import (
"encoding/json"
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/internal/json"
"testing"
"time"
@ -87,9 +87,9 @@ func TestIntArray_Sort(t *testing.T) {
func TestIntArray_Unique(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
expect := []int{1, 1, 2, 3}
expect := []int{1, 2, 3, 4, 5, 3, 2, 2, 3, 5, 5}
array := garray.NewIntArrayFrom(expect)
t.Assert(array.Unique().Slice(), []int{1, 2, 3})
t.Assert(array.Unique().Slice(), []int{1, 2, 3, 4, 5})
})
}

View File

@ -9,8 +9,8 @@
package garray_test
import (
"encoding/json"
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/internal/json"
"strings"
"testing"
"time"
@ -91,7 +91,7 @@ func TestStrArray_Sort(t *testing.T) {
func TestStrArray_Unique(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
expect := []string{"1", "1", "2", "3"}
expect := []string{"1", "1", "2", "2", "3", "3", "2", "2"}
array := garray.NewStrArrayFrom(expect)
t.Assert(array.Unique().Slice(), []string{"1", "2", "3"})
})

View File

@ -9,8 +9,8 @@
package garray_test
import (
"encoding/json"
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/internal/json"
"github.com/gogf/gf/util/gutil"
"strings"
"testing"
@ -526,15 +526,21 @@ func TestSortedArray_CountValues(t *testing.T) {
func TestSortedArray_SetUnique(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []interface{}{"a", "d", "c", "c"}
func1 := func(v1, v2 interface{}) int {
return strings.Compare(gconv.String(v1), gconv.String(v2))
}
array1 := garray.NewSortedArrayFrom(a1, func1)
a1 := []interface{}{1, 2, 3, 4, 5, 3, 2, 2, 3, 5, 5}
array1 := garray.NewSortedArrayFrom(a1, gutil.ComparatorInt)
array1.SetUnique(true)
t.Assert(array1.Len(), 3)
t.Assert(array1, []interface{}{"a", "c", "d"})
t.Assert(array1.Len(), 5)
t.Assert(array1, []interface{}{1, 2, 3, 4, 5})
})
}
func TestSortedArray_Unique(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []interface{}{1, 2, 3, 4, 5, 3, 2, 2, 3, 5, 5}
array1 := garray.NewSortedArrayFrom(a1, gutil.ComparatorInt)
array1.Unique()
t.Assert(array1.Len(), 5)
t.Assert(array1, []interface{}{1, 2, 3, 4, 5})
})
}

View File

@ -9,8 +9,8 @@
package garray_test
import (
"encoding/json"
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/internal/json"
"testing"
"time"
@ -436,7 +436,7 @@ func TestSortedIntArray_CountValues(t *testing.T) {
func TestSortedIntArray_SetUnique(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []int{1, 2, 3, 4, 5, 3}
a1 := []int{1, 2, 3, 4, 5, 3, 2, 2, 3, 5, 5}
array1 := garray.NewSortedIntArrayFrom(a1)
array1.SetUnique(true)
t.Assert(array1.Len(), 5)
@ -444,6 +444,16 @@ func TestSortedIntArray_SetUnique(t *testing.T) {
})
}
func TestSortedIntArray_Unique(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []int{1, 2, 3, 4, 5, 3, 2, 2, 3, 5, 5}
array1 := garray.NewSortedIntArrayFrom(a1)
array1.Unique()
t.Assert(array1.Len(), 5)
t.Assert(array1, []int{1, 2, 3, 4, 5})
})
}
func TestSortedIntArray_LockFunc(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s1 := []int{1, 2, 3, 4}

View File

@ -9,8 +9,8 @@
package garray_test
import (
"encoding/json"
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/internal/json"
"testing"
"time"
@ -454,11 +454,21 @@ func TestSortedStrArray_Chunk(t *testing.T) {
func TestSortedStrArray_SetUnique(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []string{"e", "a", "d", "a", "c"}
a1 := []string{"1", "1", "2", "2", "3", "3", "2", "2"}
array1 := garray.NewSortedStrArrayFrom(a1)
array2 := array1.SetUnique(true)
t.Assert(array2.Len(), 4)
t.Assert(array2, []string{"a", "c", "d", "e"})
t.Assert(array2.Len(), 3)
t.Assert(array2, []string{"1", "2", "3"})
})
}
func TestSortedStrArray_Unique(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
a1 := []string{"1", "1", "2", "2", "3", "3", "2", "2"}
array1 := garray.NewSortedStrArrayFrom(a1)
array1.Unique()
t.Assert(array1.Len(), 3)
t.Assert(array1, []string{"1", "2", "3"})
})
}

View File

@ -11,7 +11,7 @@ package glist
import (
"bytes"
"container/list"
"encoding/json"
"github.com/gogf/gf/internal/json"
"github.com/gogf/gf/util/gconv"
"github.com/gogf/gf/internal/rwmutex"

View File

@ -15,7 +15,7 @@ import (
"github.com/gogf/gf/container/glist"
)
func Example_basic() {
func ExampleNew() {
n := 10
l := glist.New()
for i := 0; i < n; i++ {
@ -32,14 +32,14 @@ func Example_basic() {
fmt.Println(l.Len())
// Output:
//10
//[0 1 2 3 4 5 6 7 8 9]
//[9 8 7 6 5 4 3 2 1 0]
//0123456789
//0
// 10
// [0 1 2 3 4 5 6 7 8 9]
// [9 8 7 6 5 4 3 2 1 0]
// 0123456789
// 0
}
func Example_iterate() {
func ExampleList_RLockFunc() {
// concurrent-safe list.
l := glist.NewFrom(garray.NewArrayRange(1, 10, 1).Slice(), true)
// iterate reading from head.
@ -63,21 +63,39 @@ func Example_iterate() {
})
fmt.Println()
// Output:
// 12345678910
// 10987654321
}
func ExampleList_IteratorAsc() {
// concurrent-safe list.
l := glist.NewFrom(garray.NewArrayRange(1, 10, 1).Slice(), true)
// iterate reading from head using IteratorAsc.
l.IteratorAsc(func(e *glist.Element) bool {
fmt.Print(e.Value)
return true
})
fmt.Println()
// Output:
// 12345678910
}
func ExampleList_IteratorDesc() {
// concurrent-safe list.
l := glist.NewFrom(garray.NewArrayRange(1, 10, 1).Slice(), true)
// iterate reading from tail using IteratorDesc.
l.IteratorDesc(func(e *glist.Element) bool {
fmt.Print(e.Value)
return true
})
// Output:
// 10987654321
}
fmt.Println()
func ExampleList_LockFunc() {
// concurrent-safe list.
l := glist.NewFrom(garray.NewArrayRange(1, 10, 1).Slice(), true)
// iterate writing from head.
l.LockFunc(func(list *list.List) {
length := list.Len()
@ -92,30 +110,46 @@ func Example_iterate() {
})
fmt.Println(l)
//output:
//12345678910
//10987654321
//12345678910
//10987654321
//[1,2,3,4,5,M,7,8,9,10]
// Output:
// [1,2,3,4,5,M,7,8,9,10]
}
func Example_popItem() {
func ExampleList_PopBack() {
l := glist.NewFrom(g.Slice{1, 2, 3, 4, 5, 6, 7, 8, 9})
fmt.Println(l.PopBack())
fmt.Println(l.PopBacks(2))
fmt.Println(l.PopFront())
fmt.Println(l.PopFronts(2))
// Output:
// 9
// [8 7]
// 1
// [2 3]
}
func ExampleList_PopBacks() {
l := glist.NewFrom(g.Slice{1, 2, 3, 4, 5, 6, 7, 8, 9})
fmt.Println(l.PopBacks(2))
// Output:
// [9 8]
}
func Example_join() {
func ExampleList_PopFront() {
l := glist.NewFrom(g.Slice{1, 2, 3, 4, 5, 6, 7, 8, 9})
fmt.Println(l.PopFront())
// Output:
// 1
}
func ExampleList_PopFronts() {
l := glist.NewFrom(g.Slice{1, 2, 3, 4, 5, 6, 7, 8, 9})
fmt.Println(l.PopFronts(2))
// Output:
// [1 2]
}
func ExampleList_Join() {
var l glist.List
l.PushBacks(g.Slice{"a", "b", "c", "d"})

View File

@ -8,7 +8,7 @@ package glist
import (
"container/list"
"encoding/json"
"github.com/gogf/gf/internal/json"
"github.com/gogf/gf/test/gtest"
"github.com/gogf/gf/util/gconv"

View File

@ -7,7 +7,7 @@
package gmap
import (
"encoding/json"
"github.com/gogf/gf/internal/json"
"github.com/gogf/gf/internal/empty"
@ -457,6 +457,12 @@ func (m *AnyAnyMap) Merge(other *AnyAnyMap) {
}
}
// String returns the map as a string.
func (m *AnyAnyMap) String() string {
b, _ := m.MarshalJSON()
return gconv.UnsafeBytesToStr(b)
}
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
func (m *AnyAnyMap) MarshalJSON() ([]byte, error) {
return json.Marshal(gconv.Map(m.Map()))

View File

@ -8,7 +8,7 @@
package gmap
import (
"encoding/json"
"github.com/gogf/gf/internal/json"
"github.com/gogf/gf/internal/empty"
@ -455,6 +455,12 @@ func (m *IntAnyMap) Merge(other *IntAnyMap) {
}
}
// String returns the map as a string.
func (m *IntAnyMap) String() string {
b, _ := m.MarshalJSON()
return gconv.UnsafeBytesToStr(b)
}
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
func (m *IntAnyMap) MarshalJSON() ([]byte, error) {
m.mu.RLock()

View File

@ -7,7 +7,7 @@
package gmap
import (
"encoding/json"
"github.com/gogf/gf/internal/json"
"github.com/gogf/gf/util/gconv"
"github.com/gogf/gf/internal/empty"
@ -426,6 +426,12 @@ func (m *IntIntMap) Merge(other *IntIntMap) {
}
}
// String returns the map as a string.
func (m *IntIntMap) String() string {
b, _ := m.MarshalJSON()
return gconv.UnsafeBytesToStr(b)
}
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
func (m *IntIntMap) MarshalJSON() ([]byte, error) {
m.mu.RLock()

View File

@ -7,7 +7,7 @@
package gmap
import (
"encoding/json"
"github.com/gogf/gf/internal/json"
"github.com/gogf/gf/internal/empty"
@ -426,6 +426,12 @@ func (m *IntStrMap) Merge(other *IntStrMap) {
}
}
// String returns the map as a string.
func (m *IntStrMap) String() string {
b, _ := m.MarshalJSON()
return gconv.UnsafeBytesToStr(b)
}
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
func (m *IntStrMap) MarshalJSON() ([]byte, error) {
m.mu.RLock()

View File

@ -8,7 +8,7 @@
package gmap
import (
"encoding/json"
"github.com/gogf/gf/internal/json"
"github.com/gogf/gf/internal/empty"
@ -451,6 +451,12 @@ func (m *StrAnyMap) Merge(other *StrAnyMap) {
}
}
// String returns the map as a string.
func (m *StrAnyMap) String() string {
b, _ := m.MarshalJSON()
return gconv.UnsafeBytesToStr(b)
}
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
func (m *StrAnyMap) MarshalJSON() ([]byte, error) {
m.mu.RLock()

View File

@ -8,7 +8,7 @@
package gmap
import (
"encoding/json"
"github.com/gogf/gf/internal/json"
"github.com/gogf/gf/internal/empty"
"github.com/gogf/gf/internal/rwmutex"
@ -429,6 +429,12 @@ func (m *StrIntMap) Merge(other *StrIntMap) {
}
}
// String returns the map as a string.
func (m *StrIntMap) String() string {
b, _ := m.MarshalJSON()
return gconv.UnsafeBytesToStr(b)
}
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
func (m *StrIntMap) MarshalJSON() ([]byte, error) {
m.mu.RLock()

View File

@ -8,7 +8,7 @@
package gmap
import (
"encoding/json"
"github.com/gogf/gf/internal/json"
"github.com/gogf/gf/util/gconv"
"github.com/gogf/gf/internal/empty"
@ -429,6 +429,12 @@ func (m *StrStrMap) Merge(other *StrStrMap) {
}
}
// String returns the map as a string.
func (m *StrStrMap) String() string {
b, _ := m.MarshalJSON()
return gconv.UnsafeBytesToStr(b)
}
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
func (m *StrStrMap) MarshalJSON() ([]byte, error) {
m.mu.RLock()

View File

@ -7,7 +7,7 @@
package gmap
import (
"encoding/json"
"github.com/gogf/gf/internal/json"
"github.com/gogf/gf/internal/empty"
@ -510,6 +510,12 @@ func (m *ListMap) Merge(other *ListMap) {
})
}
// String returns the map as a string.
func (m *ListMap) String() string {
b, _ := m.MarshalJSON()
return gconv.UnsafeBytesToStr(b)
}
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
func (m *ListMap) MarshalJSON() ([]byte, error) {
return json.Marshal(gconv.Map(m.Map()))

View File

@ -0,0 +1,207 @@
// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with gm file,
// You can obtain one at https://github.com/gogf/gf.
package gmap_test
import (
"fmt"
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/container/gmap"
)
func ExampleNew() {
m := gmap.New()
// Add data.
m.Set("key1", "val1")
// Print size.
fmt.Println(m.Size())
addMap := make(map[interface{}]interface{})
addMap["key2"] = "val2"
addMap["key3"] = "val3"
addMap[1] = 1
fmt.Println(m.Values())
// Batch add data.
m.Sets(addMap)
// Gets the value of the corresponding key.
fmt.Println(m.Get("key3"))
// Get the value by key, or set it with given key-value if not exist.
fmt.Println(m.GetOrSet("key4", "val4"))
// Set key-value if the key does not exist, then return true; or else return false.
fmt.Println(m.SetIfNotExist("key3", "val3"))
// Remove key
m.Remove("key2")
fmt.Println(m.Keys())
// Batch remove keys.
m.Removes([]interface{}{"key1", 1})
fmt.Println(m.Keys())
// Contains checks whether a key exists.
fmt.Println(m.Contains("key3"))
// Flip exchanges key-value of the map, it will change key-value to value-key.
m.Flip()
fmt.Println(m.Map())
// Clear deletes all data of the map.
m.Clear()
fmt.Println(m.Size())
// May Output:
// 1
// [val1]
// val3
// val4
// false
// [key4 key1 key3 1]
// [key4 key3]
// true
// map[val3:key3 val4:key4]
// 0
}
func ExampleAnyAnyMap_Keys() {
var m gmap.Map
m.Sets(g.MapAnyAny{
"k1": "v1",
"k2": "v2",
"k3": "v3",
"k4": "v4",
})
fmt.Println(m.Keys())
fmt.Println(m.Values())
// May Output:
// [k1 k2 k3 k4]
// [v2 v3 v4 v1]
}
func ExampleAnyAnyMap_Values() {
var m gmap.Map
m.Sets(g.MapAnyAny{
"k1": "v1",
"k2": "v2",
"k3": "v3",
"k4": "v4",
})
fmt.Println(m.Keys())
fmt.Println(m.Values())
// May Output:
// [k1 k2 k3 k4]
// [v2 v3 v4 v1]
}
func ExampleAnyAnyMap_Flip() {
var m gmap.Map
m.Sets(g.MapAnyAny{
"k1": "v1",
"k2": "v2",
})
m.Flip()
fmt.Println(m.Map())
// May Output:
// map[v1:k1 v2:k2]
}
func ExampleAnyAnyMap_Pop() {
var m gmap.Map
m.Sets(g.MapAnyAny{
"k1": "v1",
"k2": "v2",
"k3": "v3",
"k4": "v4",
})
fmt.Println(m.Pop())
fmt.Println(m.Pops(2))
fmt.Println(m.Size())
// May Output:
// k1 v1
// map[k2:v2 k4:v4]
// 1
}
func ExampleAnyAnyMap_Pops() {
var m gmap.Map
m.Sets(g.MapAnyAny{
"k1": "v1",
"k2": "v2",
"k3": "v3",
"k4": "v4",
})
fmt.Println(m.Pop())
fmt.Println(m.Pops(2))
fmt.Println(m.Size())
// May Output:
// k1 v1
// map[k2:v2 k4:v4]
// 1
}
func ExampleAnyAnyMap_FilterEmpty() {
m := gmap.NewFrom(g.MapAnyAny{
"k1": "",
"k2": nil,
"k3": 0,
"k4": 1,
})
m.FilterEmpty()
fmt.Println(m.Map())
// May Output:
// map[k4:1]
}
func ExampleAnyAnyMap_FilterNil() {
m := gmap.NewFrom(g.MapAnyAny{
"k1": "",
"k2": nil,
"k3": 0,
"k4": 1,
})
m.FilterNil()
fmt.Println(m.Map())
// May Output:
// map[k1: k3:0 k4:1]
}
func ExampleAnyAnyMap_SetIfNotExist() {
var m gmap.Map
fmt.Println(m.SetIfNotExist("k1", "v1"))
fmt.Println(m.SetIfNotExist("k1", "v1"))
fmt.Println(m.Map())
// Output:
// true
// false
// map[k1:v1]
}
func ExampleAnyAnyMap_Merge() {
var m1, m2 gmap.Map
m1.Set("key1", "val1")
m2.Set("key2", "val2")
m1.Merge(&m2)
fmt.Println(m1.Map())
// May Output:
// map[key1:val1 key2:val2]
}

View File

@ -1,162 +0,0 @@
// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with gm file,
// You can obtain one at https://github.com/gogf/gf.
package gmap_test
import (
"fmt"
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/container/gmap"
)
func Example_normalBasic() {
m := gmap.New()
//Add data
m.Set("key1", "val1")
//Print size
fmt.Println(m.Size())
//output 1
add_map := make(map[interface{}]interface{})
add_map["key2"] = "val2"
add_map["key3"] = "val3"
add_map[1] = 1
fmt.Println(m.Values())
//Batch add data
m.Sets(add_map)
//Gets the value of the corresponding key
key3_val := m.Get("key3")
fmt.Println(key3_val)
//Get the value by key, or set it with given key-value if not exist.
get_or_set_val := m.GetOrSet("key4", "val4")
fmt.Println(get_or_set_val)
// Set key-value if the key does not exist, then return true; or else return false.
is_set := m.SetIfNotExist("key3", "val3")
fmt.Println(is_set)
//Remove key
m.Remove("key2")
fmt.Println(m.Keys())
//Batch remove keys
remove_keys := []interface{}{"key1", 1}
m.Removes(remove_keys)
fmt.Println(m.Keys())
//Contains checks whether a key exists.
is_contain := m.Contains("key3")
fmt.Println(is_contain)
//Flip exchanges key-value of the map, it will change key-value to value-key.
m.Flip()
fmt.Println(m.Map())
// Clear deletes all data of the map,
m.Clear()
fmt.Println(m.Size())
}
func Example_keysValues() {
var m gmap.Map
m.Sets(g.MapAnyAny{
"k1": "v1",
"k2": "v2",
"k3": "v3",
"k4": "v4",
})
fmt.Println(m.Keys())
fmt.Println(m.Values())
// May Output:
// [k1 k2 k3 k4]
// [v2 v3 v4 v1]
}
func Example_flip() {
var m gmap.Map
m.Sets(g.MapAnyAny{
"k1": "v1",
"k2": "v2",
})
m.Flip()
fmt.Println(m.Map())
// May Output:
// map[v1:k1 v2:k2]
}
func Example_pop() {
var m gmap.Map
m.Sets(g.MapAnyAny{
"k1": "v1",
"k2": "v2",
"k3": "v3",
"k4": "v4",
})
fmt.Println(m.Pop())
fmt.Println(m.Pops(2))
fmt.Println(m.Size())
// May Output:
// k1 v1
// map[k2:v2 k4:v4]
// 1
}
func Example_filter() {
m1 := gmap.NewFrom(g.MapAnyAny{
"k1": "",
"k2": nil,
"k3": 0,
"k4": 1,
})
m2 := gmap.NewFrom(g.MapAnyAny{
"k1": "",
"k2": nil,
"k3": 0,
"k4": 1,
})
m1.FilterEmpty()
m2.FilterNil()
fmt.Println(m1.Map())
fmt.Println(m2.Map())
// Output:
// map[k4:1]
// map[k1: k3:0 k4:1]
}
func Example_setIfNotExist() {
var m gmap.Map
fmt.Println(m.SetIfNotExist("k1", "v1"))
fmt.Println(m.SetIfNotExist("k1", "v1"))
fmt.Println(m.Map())
// Output:
// true
// false
// map[k1:v1]
}
func Example_normalMerge() {
var m1, m2 gmap.Map
m1.Set("key1", "val1")
m2.Set("key2", "val2")
m1.Merge(&m2)
fmt.Println(m1.Map())
// May Output:
// map[key1:val1 key2:val2]
}

View File

@ -7,10 +7,10 @@
package gmap_test
import (
"encoding/json"
"github.com/gogf/gf/container/garray"
"github.com/gogf/gf/container/gmap"
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/internal/json"
"github.com/gogf/gf/test/gtest"
"github.com/gogf/gf/util/gconv"
"testing"

View File

@ -7,9 +7,9 @@
package gmap_test
import (
"encoding/json"
"github.com/gogf/gf/container/garray"
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/internal/json"
"github.com/gogf/gf/util/gconv"
"testing"

View File

@ -7,9 +7,9 @@
package gmap_test
import (
"encoding/json"
"github.com/gogf/gf/container/garray"
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/internal/json"
"github.com/gogf/gf/util/gconv"
"testing"

View File

@ -7,9 +7,9 @@
package gmap_test
import (
"encoding/json"
"github.com/gogf/gf/container/garray"
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/internal/json"
"github.com/gogf/gf/util/gconv"
"testing"

View File

@ -7,8 +7,8 @@
package gmap_test
import (
"encoding/json"
"github.com/gogf/gf/container/garray"
"github.com/gogf/gf/internal/json"
"github.com/gogf/gf/util/gconv"
"testing"

View File

@ -7,9 +7,9 @@
package gmap_test
import (
"encoding/json"
"github.com/gogf/gf/container/garray"
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/internal/json"
"github.com/gogf/gf/util/gconv"
"testing"

View File

@ -7,9 +7,9 @@
package gmap_test
import (
"encoding/json"
"github.com/gogf/gf/container/garray"
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/internal/json"
"github.com/gogf/gf/util/gconv"
"testing"

View File

@ -7,9 +7,9 @@
package gmap_test
import (
"encoding/json"
"github.com/gogf/gf/container/garray"
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/internal/json"
"github.com/gogf/gf/util/gconv"
"testing"

View File

@ -7,8 +7,8 @@
package gmap_test
import (
"encoding/json"
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/internal/json"
"github.com/gogf/gf/util/gconv"
"testing"

View File

@ -170,6 +170,7 @@ func (p *Pool) checkExpireItems() {
if r := p.list.PopFront(); r != nil {
item := r.(*poolItem)
latestExpire = item.expire
// TODO improve the auto-expiration mechanism of the pool.
if item.expire > timestampMilli {
p.list.PushFront(item)
break

View File

@ -9,7 +9,7 @@ package gset
import (
"bytes"
"encoding/json"
"github.com/gogf/gf/internal/json"
"github.com/gogf/gf/internal/rwmutex"
"github.com/gogf/gf/text/gstr"
"github.com/gogf/gf/util/gconv"

View File

@ -9,7 +9,7 @@ package gset
import (
"bytes"
"encoding/json"
"github.com/gogf/gf/internal/json"
"github.com/gogf/gf/internal/rwmutex"
"github.com/gogf/gf/util/gconv"
)

View File

@ -9,7 +9,7 @@ package gset
import (
"bytes"
"encoding/json"
"github.com/gogf/gf/internal/json"
"github.com/gogf/gf/internal/rwmutex"
"github.com/gogf/gf/text/gstr"
"github.com/gogf/gf/util/gconv"

View File

@ -12,7 +12,7 @@ import (
"github.com/gogf/gf/frame/g"
)
func Example_intersectDiffUnionComplement() {
func ExampleSet_Intersect() {
s1 := gset.NewFrom(g.Slice{1, 2, 3})
s2 := gset.NewFrom(g.Slice{4, 5, 6})
s3 := gset.NewFrom(g.Slice{1, 2, 3, 4, 5, 6, 7})
@ -29,7 +29,58 @@ func Example_intersectDiffUnionComplement() {
// [4 5 6 7]
}
func Example_isSubsetOf() {
func ExampleSet_Diff() {
s1 := gset.NewFrom(g.Slice{1, 2, 3})
s2 := gset.NewFrom(g.Slice{4, 5, 6})
s3 := gset.NewFrom(g.Slice{1, 2, 3, 4, 5, 6, 7})
fmt.Println(s3.Intersect(s1).Slice())
fmt.Println(s3.Diff(s1).Slice())
fmt.Println(s1.Union(s2).Slice())
fmt.Println(s1.Complement(s3).Slice())
// May Output:
// [2 3 1]
// [5 6 7 4]
// [6 1 2 3 4 5]
// [4 5 6 7]
}
func ExampleSet_Union() {
s1 := gset.NewFrom(g.Slice{1, 2, 3})
s2 := gset.NewFrom(g.Slice{4, 5, 6})
s3 := gset.NewFrom(g.Slice{1, 2, 3, 4, 5, 6, 7})
fmt.Println(s3.Intersect(s1).Slice())
fmt.Println(s3.Diff(s1).Slice())
fmt.Println(s1.Union(s2).Slice())
fmt.Println(s1.Complement(s3).Slice())
// May Output:
// [2 3 1]
// [5 6 7 4]
// [6 1 2 3 4 5]
// [4 5 6 7]
}
func ExampleSet_Complement() {
s1 := gset.NewFrom(g.Slice{1, 2, 3})
s2 := gset.NewFrom(g.Slice{4, 5, 6})
s3 := gset.NewFrom(g.Slice{1, 2, 3, 4, 5, 6, 7})
fmt.Println(s3.Intersect(s1).Slice())
fmt.Println(s3.Diff(s1).Slice())
fmt.Println(s1.Union(s2).Slice())
fmt.Println(s1.Complement(s3).Slice())
// May Output:
// [2 3 1]
// [5 6 7 4]
// [6 1 2 3 4 5]
// [4 5 6 7]
}
func ExampleSet_IsSubsetOf() {
var s1, s2 gset.Set
s1.Add(g.Slice{1, 2, 3}...)
s2.Add(g.Slice{2, 3}...)
@ -41,7 +92,7 @@ func Example_isSubsetOf() {
// true
}
func Example_addIfNotExist() {
func ExampleSet_AddIfNotExist() {
var set gset.Set
fmt.Println(set.AddIfNotExist(1))
fmt.Println(set.AddIfNotExist(1))
@ -53,7 +104,7 @@ func Example_addIfNotExist() {
// [1]
}
func Example_pop() {
func ExampleSet_Pop() {
var set gset.Set
set.Add(1, 2, 3, 4)
fmt.Println(set.Pop())
@ -66,7 +117,20 @@ func Example_pop() {
// 1
}
func Example_join() {
func ExampleSet_Pops() {
var set gset.Set
set.Add(1, 2, 3, 4)
fmt.Println(set.Pop())
fmt.Println(set.Pops(2))
fmt.Println(set.Size())
// May Output:
// 1
// [2 3]
// 1
}
func ExampleSet_Join() {
var set gset.Set
set.Add("a", "b", "c", "d")
fmt.Println(set.Join(","))
@ -75,7 +139,7 @@ func Example_join() {
// a,b,c,d
}
func Example_contains() {
func ExampleSet_Contains() {
var set gset.StrSet
set.Add("a")
fmt.Println(set.Contains("a"))
@ -88,7 +152,7 @@ func Example_contains() {
// true
}
func Example_Contains() {
func ExampleSet_ContainsI() {
var set gset.StrSet
set.Add("a")
fmt.Println(set.Contains("a"))
@ -100,20 +164,3 @@ func Example_Contains() {
// false
// true
}
func Example_walk() {
var (
set gset.StrSet
names = g.SliceStr{"user", "user_detail"}
prefix = "gf_"
)
set.Add(names...)
// Add prefix for given table names.
set.Walk(func(item string) string {
return prefix + item
})
fmt.Println(set.Slice())
// May Output:
// [gf_user gf_user_detail]
}

View File

@ -0,0 +1,23 @@
// Copyright 2020 gf Author(https://github.com/gogf/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with gm file,
// You can obtain one at https://github.com/gogf/gf.
package gset_test
import (
"fmt"
"github.com/gogf/gf/container/gset"
)
func ExampleIntSet_Contains() {
var set gset.IntSet
set.Add(1)
fmt.Println(set.Contains(1))
fmt.Println(set.Contains(2))
// Output:
// true
// false
}

View File

@ -0,0 +1,43 @@
// Copyright 2020 gf Author(https://github.com/gogf/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with gm file,
// You can obtain one at https://github.com/gogf/gf.
package gset_test
import (
"fmt"
"github.com/gogf/gf/container/gset"
"github.com/gogf/gf/frame/g"
)
func ExampleStrSet_Contains() {
var set gset.StrSet
set.Add("a")
fmt.Println(set.Contains("a"))
fmt.Println(set.Contains("A"))
fmt.Println(set.ContainsI("A"))
// Output:
// true
// false
// true
}
func ExampleStrSet_Walk() {
var (
set gset.StrSet
names = g.SliceStr{"user", "user_detail"}
prefix = "gf_"
)
set.Add(names...)
// Add prefix for given table names.
set.Walk(func(item string) string {
return prefix + item
})
fmt.Println(set.Slice())
// May Output:
// [gf_user gf_user_detail]
}

View File

@ -9,8 +9,8 @@
package gset_test
import (
"encoding/json"
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/internal/json"
"github.com/gogf/gf/util/gconv"
"strings"
"sync"

View File

@ -9,8 +9,8 @@
package gset_test
import (
"encoding/json"
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/internal/json"
"github.com/gogf/gf/util/gconv"
"strings"
"sync"

View File

@ -9,8 +9,8 @@
package gset_test
import (
"encoding/json"
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/internal/json"
"github.com/gogf/gf/util/gconv"
"strings"
"sync"

View File

@ -5,4 +5,7 @@
// You can obtain one at https://github.com/gogf/gf.
// Package gtree provides concurrent-safe/unsafe tree containers.
//
// Some implements are from: https://github.com/emirpasic/gods
// Thanks!
package gtree

View File

@ -7,8 +7,8 @@
package gtree
import (
"encoding/json"
"fmt"
"github.com/gogf/gf/internal/json"
"github.com/gogf/gf/util/gconv"

View File

@ -8,8 +8,8 @@ package gtree
import (
"bytes"
"encoding/json"
"fmt"
"github.com/gogf/gf/internal/json"
"strings"
"github.com/gogf/gf/util/gconv"

View File

@ -7,8 +7,8 @@
package gtree
import (
"encoding/json"
"fmt"
"github.com/gogf/gf/internal/json"
"github.com/gogf/gf/util/gconv"
"github.com/gogf/gf/util/gutil"

View File

@ -7,7 +7,7 @@
package gtype
import (
"encoding/json"
"github.com/gogf/gf/internal/json"
"github.com/gogf/gf/util/gconv"
"sync/atomic"
)

View File

@ -9,8 +9,8 @@
package gtype_test
import (
"encoding/json"
"github.com/gogf/gf/container/gtype"
"github.com/gogf/gf/internal/json"
"testing"
)

View File

@ -7,7 +7,7 @@
package gtype_test
import (
"encoding/json"
"github.com/gogf/gf/internal/json"
"github.com/gogf/gf/util/gconv"
"testing"

View File

@ -7,7 +7,7 @@
package gtype_test
import (
"encoding/json"
"github.com/gogf/gf/internal/json"
"github.com/gogf/gf/util/gconv"
"sync"
"testing"

View File

@ -7,7 +7,7 @@
package gtype_test
import (
"encoding/json"
"github.com/gogf/gf/internal/json"
"github.com/gogf/gf/util/gconv"
"testing"

View File

@ -7,8 +7,8 @@
package gtype_test
import (
"encoding/json"
"github.com/gogf/gf/container/gtype"
"github.com/gogf/gf/internal/json"
"github.com/gogf/gf/test/gtest"
"github.com/gogf/gf/util/gconv"
"math"

View File

@ -7,8 +7,8 @@
package gtype_test
import (
"encoding/json"
"github.com/gogf/gf/container/gtype"
"github.com/gogf/gf/internal/json"
"github.com/gogf/gf/test/gtest"
"github.com/gogf/gf/util/gconv"
"math"

View File

@ -7,8 +7,8 @@
package gtype_test
import (
"encoding/json"
"github.com/gogf/gf/container/gtype"
"github.com/gogf/gf/internal/json"
"github.com/gogf/gf/test/gtest"
"github.com/gogf/gf/util/gconv"
"math"

View File

@ -7,8 +7,8 @@
package gtype_test
import (
"encoding/json"
"github.com/gogf/gf/container/gtype"
"github.com/gogf/gf/internal/json"
"github.com/gogf/gf/test/gtest"
"github.com/gogf/gf/util/gconv"
"math"

View File

@ -7,8 +7,8 @@
package gtype_test
import (
"encoding/json"
"github.com/gogf/gf/container/gtype"
"github.com/gogf/gf/internal/json"
"github.com/gogf/gf/test/gtest"
"github.com/gogf/gf/util/gconv"
"sync"

View File

@ -7,8 +7,8 @@
package gtype_test
import (
"encoding/json"
"github.com/gogf/gf/container/gtype"
"github.com/gogf/gf/internal/json"
"github.com/gogf/gf/test/gtest"
"github.com/gogf/gf/util/gconv"
"testing"

View File

@ -7,8 +7,8 @@
package gtype_test
import (
"encoding/json"
"github.com/gogf/gf/container/gtype"
"github.com/gogf/gf/internal/json"
"github.com/gogf/gf/test/gtest"
"github.com/gogf/gf/util/gconv"
"testing"

View File

@ -7,8 +7,8 @@
package gtype_test
import (
"encoding/json"
"github.com/gogf/gf/container/gtype"
"github.com/gogf/gf/internal/json"
"github.com/gogf/gf/test/gtest"
"github.com/gogf/gf/util/gconv"
"math"

View File

@ -7,7 +7,7 @@
package gtype_test
import (
"encoding/json"
"github.com/gogf/gf/internal/json"
"github.com/gogf/gf/util/gconv"
"math"
"sync"

View File

@ -7,8 +7,8 @@
package gtype_test
import (
"encoding/json"
"github.com/gogf/gf/container/gtype"
"github.com/gogf/gf/internal/json"
"github.com/gogf/gf/test/gtest"
"github.com/gogf/gf/util/gconv"
"sync"

View File

@ -8,7 +8,7 @@
package gvar
import (
"encoding/json"
"github.com/gogf/gf/internal/json"
"time"
"github.com/gogf/gf/internal/empty"
@ -18,17 +18,23 @@ import (
"github.com/gogf/gf/util/gconv"
)
// Var is an universal variable type.
// Var is an universal variable type implementer.
type Var struct {
value interface{} // Underlying value.
safe bool // Concurrent safe or not.
}
// New creates and returns a new *Var with given <value>.
// New creates and returns a new Var with given <value>.
// The optional parameter <safe> specifies whether Var is used in concurrent-safety,
// which is false in default.
func New(value interface{}, safe ...bool) *Var {
v := Create(value, safe...)
v := Var{}
if len(safe) > 0 && !safe[0] {
v.safe = true
v.value = gtype.NewInterface(value)
} else {
v.value = value
}
return &v
}
@ -202,7 +208,7 @@ func (v *Var) Array() []interface{} {
return v.Interfaces()
}
// Vars converts and returns <v> as []*Var.
// Vars converts and returns <v> as []Var.
func (v *Var) Vars() []*Var {
array := gconv.Interfaces(v.Val())
if len(array) == 0 {

View File

@ -0,0 +1,25 @@
// Copyright 2020 gf Author(https://github.com/gogf/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
package gvar
import (
"github.com/gogf/gf/util/gutil"
)
// ListItemValues retrieves and returns the elements of all item struct/map with key <key>.
// Note that the parameter <list> should be type of slice which contains elements of map or struct,
// or else it returns an empty slice.
func (v *Var) ListItemValues(key interface{}) (values []interface{}) {
return gutil.ListItemValues(v.Val(), key)
}
// ListItemValuesUnique retrieves and returns the unique elements of all struct/map with key <key>.
// Note that the parameter <list> should be type of slice which contains elements of map or struct,
// or else it returns an empty slice.
func (v *Var) ListItemValuesUnique(key string) []interface{} {
return gutil.ListItemValuesUnique(v.Val(), key)
}

View File

@ -13,12 +13,17 @@ func (v *Var) Map(tags ...string) map[string]interface{} {
return gconv.Map(v.Val(), tags...)
}
// MapStrAny is like function Map, but implements the interface of MapStrAny.
func (v *Var) MapStrAny() map[string]interface{} {
return v.Map()
}
// MapStrStr converts and returns <v> as map[string]string.
func (v *Var) MapStrStr(tags ...string) map[string]string {
return gconv.MapStrStr(v.Val(), tags...)
}
// MapStrVar converts and returns <v> as map[string]*Var.
// MapStrVar converts and returns <v> as map[string]Var.
func (v *Var) MapStrVar(tags ...string) map[string]*Var {
m := v.Map(tags...)
if len(m) > 0 {
@ -62,15 +67,15 @@ func (v *Var) Maps(tags ...string) []map[string]interface{} {
// MapToMap converts any map type variable <params> to another map type variable <pointer>.
// See gconv.MapToMap.
func (v *Var) MapToMap(pointer interface{}) (err error) {
return gconv.MapToMap(v.Val(), pointer)
func (v *Var) MapToMap(pointer interface{}, mapping ...map[string]string) (err error) {
return gconv.MapToMap(v.Val(), pointer, mapping...)
}
// MapToMapDeep converts any map type variable <params> to another map type variable
// <pointer> recursively.
// See gconv.MapToMapDeep.
func (v *Var) MapToMapDeep(pointer interface{}) (err error) {
return gconv.MapToMapDeep(v.Val(), pointer)
func (v *Var) MapToMapDeep(pointer interface{}, mapping ...map[string]string) (err error) {
return gconv.MapToMapDeep(v.Val(), pointer, mapping...)
}
// MapToMaps converts any map type variable <params> to another map type variable <pointer>.

View File

@ -6,7 +6,9 @@
package gvar
import "github.com/gogf/gf/util/gconv"
import (
"github.com/gogf/gf/util/gconv"
)
// Struct maps value of <v> to <pointer>.
// The parameter <pointer> should be a pointer to a struct instance.
@ -23,11 +25,27 @@ func (v *Var) StructDeep(pointer interface{}, mapping ...map[string]string) erro
}
// Structs converts and returns <v> as given struct slice.
func (v *Var) Structs(pointer interface{}, mapping ...map[string]string) (err error) {
func (v *Var) Structs(pointer interface{}, mapping ...map[string]string) error {
return gconv.Structs(v.Val(), pointer, mapping...)
}
// StructsDeep converts and returns <v> as given struct slice recursively.
func (v *Var) StructsDeep(pointer interface{}, mapping ...map[string]string) (err error) {
func (v *Var) StructsDeep(pointer interface{}, mapping ...map[string]string) error {
return gconv.StructsDeep(v.Val(), pointer, mapping...)
}
// Scan automatically calls Struct or Structs function according to the type of parameter
// <pointer> to implement the converting.
// It calls function Struct if <pointer> is type of *struct/**struct to do the converting.
// It calls function Structs if <pointer> is type of *[]struct/*[]*struct to do the converting.
func (v *Var) Scan(pointer interface{}, mapping ...map[string]string) error {
return gconv.Scan(v.Val(), pointer, mapping...)
}
// ScanDeep automatically calls StructDeep or StructsDeep function according to the type of
// parameter <pointer> to implement the converting.
// It calls function StructDeep if <pointer> is type of *struct/**struct to do the converting.
// It calls function StructsDeep if <pointer> is type of *[]struct/*[]*struct to do the converting.
func (v *Var) ScanDeep(pointer interface{}, mapping ...map[string]string) error {
return gconv.ScanDeep(v.Val(), pointer, mapping...)
}

View File

@ -7,8 +7,8 @@
package gvar_test
import (
"encoding/json"
"github.com/gogf/gf/container/gvar"
"github.com/gogf/gf/internal/json"
"github.com/gogf/gf/test/gtest"
"math"
"testing"

View File

@ -0,0 +1,115 @@
// Copyright 2020 gf Author(https://github.com/gogf/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
package gvar_test
import (
"github.com/gogf/gf/container/gvar"
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/test/gtest"
"testing"
)
func Test_ListItemValues_Map(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
listMap := g.List{
g.Map{"id": 1, "score": 100},
g.Map{"id": 2, "score": 99},
g.Map{"id": 3, "score": 99},
}
t.Assert(gvar.New(listMap).ListItemValues("id"), g.Slice{1, 2, 3})
t.Assert(gvar.New(listMap).ListItemValues("score"), g.Slice{100, 99, 99})
})
gtest.C(t, func(t *gtest.T) {
listMap := g.List{
g.Map{"id": 1, "score": 100},
g.Map{"id": 2, "score": nil},
g.Map{"id": 3, "score": 0},
}
t.Assert(gvar.New(listMap).ListItemValues("id"), g.Slice{1, 2, 3})
t.Assert(gvar.New(listMap).ListItemValues("score"), g.Slice{100, nil, 0})
})
}
func Test_ListItemValues_Struct(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
type T struct {
Id int
Score float64
}
listStruct := g.Slice{
T{1, 100},
T{2, 99},
T{3, 0},
}
t.Assert(gvar.New(listStruct).ListItemValues("Id"), g.Slice{1, 2, 3})
t.Assert(gvar.New(listStruct).ListItemValues("Score"), g.Slice{100, 99, 0})
})
// Pointer items.
gtest.C(t, func(t *gtest.T) {
type T struct {
Id int
Score float64
}
listStruct := g.Slice{
&T{1, 100},
&T{2, 99},
&T{3, 0},
}
t.Assert(gvar.New(listStruct).ListItemValues("Id"), g.Slice{1, 2, 3})
t.Assert(gvar.New(listStruct).ListItemValues("Score"), g.Slice{100, 99, 0})
})
// Nil element value.
gtest.C(t, func(t *gtest.T) {
type T struct {
Id int
Score interface{}
}
listStruct := g.Slice{
T{1, 100},
T{2, nil},
T{3, 0},
}
t.Assert(gvar.New(listStruct).ListItemValues("Id"), g.Slice{1, 2, 3})
t.Assert(gvar.New(listStruct).ListItemValues("Score"), g.Slice{100, nil, 0})
})
}
func Test_ListItemValuesUnique(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
listMap := g.List{
g.Map{"id": 1, "score": 100},
g.Map{"id": 2, "score": 100},
g.Map{"id": 3, "score": 100},
g.Map{"id": 4, "score": 100},
g.Map{"id": 5, "score": 100},
}
t.Assert(gvar.New(listMap).ListItemValuesUnique("id"), g.Slice{1, 2, 3, 4, 5})
t.Assert(gvar.New(listMap).ListItemValuesUnique("score"), g.Slice{100})
})
gtest.C(t, func(t *gtest.T) {
listMap := g.List{
g.Map{"id": 1, "score": 100},
g.Map{"id": 2, "score": 100},
g.Map{"id": 3, "score": 100},
g.Map{"id": 4, "score": 100},
g.Map{"id": 5, "score": 99},
}
t.Assert(gvar.New(listMap).ListItemValuesUnique("id"), g.Slice{1, 2, 3, 4, 5})
t.Assert(gvar.New(listMap).ListItemValuesUnique("score"), g.Slice{100, 99})
})
gtest.C(t, func(t *gtest.T) {
listMap := g.List{
g.Map{"id": 1, "score": 100},
g.Map{"id": 2, "score": 100},
g.Map{"id": 3, "score": 0},
g.Map{"id": 4, "score": 100},
g.Map{"id": 5, "score": 99},
}
t.Assert(gvar.New(listMap).ListItemValuesUnique("id"), g.Slice{1, 2, 3, 4, 5})
t.Assert(gvar.New(listMap).ListItemValuesUnique("score"), g.Slice{100, 0, 99})
})
}

View File

@ -10,6 +10,7 @@ import (
"github.com/gogf/gf/container/gvar"
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/test/gtest"
"github.com/gogf/gf/util/gconv"
"testing"
)
@ -40,3 +41,36 @@ func Test_Struct(t *testing.T) {
t.Assert(o.Test, -25)
})
}
func Test_Var_Attribute_Struct(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
type User struct {
Uid int
Name string
}
user := new(User)
err := gconv.Struct(
g.Map{
"uid": gvar.New(1),
"name": gvar.New("john"),
}, user)
t.Assert(err, nil)
t.Assert(user.Uid, 1)
t.Assert(user.Name, "john")
})
gtest.C(t, func(t *gtest.T) {
type User struct {
Uid int
Name string
}
var user *User
err := gconv.Struct(
g.Map{
"uid": gvar.New(1),
"name": gvar.New("john"),
}, &user)
t.Assert(err, nil)
t.Assert(user.Uid, 1)
t.Assert(user.Name, "john")
})
}

View File

@ -11,9 +11,11 @@ import (
"database/sql"
"errors"
"fmt"
"github.com/gogf/gf/internal/cmdenv"
"time"
"github.com/gogf/gf/container/gvar"
"github.com/gogf/gf/internal/intlog"
"time"
"github.com/gogf/gf/os/glog"
@ -25,46 +27,32 @@ import (
// DB defines the interfaces for ORM operations.
type DB interface {
// ===========================================================================
// Model creation.
// ===========================================================================
// The DB interface is designed not only for
// relational databases but also for NoSQL databases in the future. The name
// "Table" is not proper for that purpose any more.
Table(table ...string) *Model
Model(table ...string) *Model
Schema(schema string) *Schema
// Open creates a raw connection object for database with given node configuration.
// Note that it is not recommended using the this function manually.
Open(config *ConfigNode) (*sql.DB, error)
// ===========================================================================
// Query APIs.
// ===========================================================================
Query(sql string, args ...interface{}) (*sql.Rows, error)
Exec(sql string, args ...interface{}) (sql.Result, error)
Prepare(sql string, execOnMaster ...bool) (*sql.Stmt, error)
// Internal APIs for CURD, which can be overwrote for custom CURD implements.
DoQuery(link Link, sql string, args ...interface{}) (rows *sql.Rows, err error)
DoGetAll(link Link, sql string, args ...interface{}) (result Result, err error)
DoExec(link Link, sql string, args ...interface{}) (result sql.Result, err error)
DoPrepare(link Link, sql string) (*sql.Stmt, error)
DoInsert(link Link, table string, data interface{}, option int, batch ...int) (result sql.Result, err error)
DoBatchInsert(link Link, table string, list interface{}, option int, batch ...int) (result sql.Result, err error)
DoUpdate(link Link, table string, data interface{}, condition string, args ...interface{}) (result sql.Result, err error)
DoDelete(link Link, table string, condition string, args ...interface{}) (result sql.Result, err error)
// Query APIs for convenience purpose.
GetAll(sql string, args ...interface{}) (Result, error)
GetOne(sql string, args ...interface{}) (Record, error)
GetValue(sql string, args ...interface{}) (Value, error)
GetArray(sql string, args ...interface{}) ([]Value, error)
GetCount(sql string, args ...interface{}) (int, error)
GetStruct(objPointer interface{}, sql string, args ...interface{}) error
GetStructs(objPointerSlice interface{}, sql string, args ...interface{}) error
GetScan(objPointer interface{}, sql string, args ...interface{}) error
// Master/Slave specification support.
Master() (*sql.DB, error)
Slave() (*sql.DB, error)
// Ping-Pong.
PingMaster() error
PingSlave() error
// Transaction.
Begin() (*TX, error)
Transaction(f func(tx *TX) error) (err error)
// ===========================================================================
// Common APIs for CURD.
// ===========================================================================
Insert(table string, data interface{}, batch ...int) (sql.Result, error)
InsertIgnore(table string, data interface{}, batch ...int) (sql.Result, error)
@ -78,12 +66,57 @@ type DB interface {
Update(table string, data interface{}, condition interface{}, args ...interface{}) (sql.Result, error)
Delete(table string, condition interface{}, args ...interface{}) (sql.Result, error)
// Model creation.
Table(table ...string) *Model
Model(table ...string) *Model
Schema(schema string) *Schema
// ===========================================================================
// Internal APIs for CURD, which can be overwrote for custom CURD implements.
// ===========================================================================
DoQuery(link Link, sql string, args ...interface{}) (rows *sql.Rows, err error)
DoGetAll(link Link, sql string, args ...interface{}) (result Result, err error)
DoExec(link Link, sql string, args ...interface{}) (result sql.Result, err error)
DoPrepare(link Link, sql string) (*sql.Stmt, error)
DoInsert(link Link, table string, data interface{}, option int, batch ...int) (result sql.Result, err error)
DoBatchInsert(link Link, table string, list interface{}, option int, batch ...int) (result sql.Result, err error)
DoUpdate(link Link, table string, data interface{}, condition string, args ...interface{}) (result sql.Result, err error)
DoDelete(link Link, table string, condition string, args ...interface{}) (result sql.Result, err error)
// ===========================================================================
// Query APIs for convenience purpose.
// ===========================================================================
GetAll(sql string, args ...interface{}) (Result, error)
GetOne(sql string, args ...interface{}) (Record, error)
GetValue(sql string, args ...interface{}) (Value, error)
GetArray(sql string, args ...interface{}) ([]Value, error)
GetCount(sql string, args ...interface{}) (int, error)
GetStruct(objPointer interface{}, sql string, args ...interface{}) error
GetStructs(objPointerSlice interface{}, sql string, args ...interface{}) error
GetScan(objPointer interface{}, sql string, args ...interface{}) error
// ===========================================================================
// Master/Slave specification support.
// ===========================================================================
Master() (*sql.DB, error)
Slave() (*sql.DB, error)
// ===========================================================================
// Ping-Pong.
// ===========================================================================
PingMaster() error
PingSlave() error
// ===========================================================================
// Transaction.
// ===========================================================================
Begin() (*TX, error)
Transaction(f func(tx *TX) error) (err error)
// ===========================================================================
// Configuration methods.
// ===========================================================================
GetCache() *gcache.Cache
SetDebug(debug bool)
GetDebug() bool
@ -99,7 +132,10 @@ type DB interface {
SetMaxOpenConnCount(n int)
SetMaxConnLifetime(d time.Duration)
// ===========================================================================
// Utility methods.
// ===========================================================================
GetChars() (charLeft string, charRight string)
GetMaster(schema ...string) (*sql.DB, error)
GetSlave(schema ...string) (*sql.DB, error)
@ -108,6 +144,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 <link> specifies the current
@ -115,9 +152,12 @@ type DB interface {
// arguments <args> as you wish before they're committed to driver.
HandleSqlBeforeCommit(link Link, sql string, args []interface{}) (string, []interface{})
// ===========================================================================
// Internal methods.
// ===========================================================================
filterFields(schema, table string, data map[string]interface{}) map[string]interface{}
convertValue(fieldValue []byte, fieldType string) interface{}
convertValue(fieldValue interface{}, fieldType string) interface{}
rowsToResult(rows *sql.Rows) (Result, error)
}
@ -150,6 +190,7 @@ type Sql struct {
Error error // Execution result.
Start int64 // Start execution timestamp in milliseconds.
End int64 // End execution timestamp in milliseconds.
Group string // Group is the group name of the configuration that the sql is executed from.
}
// TableField is the struct for table field.
@ -202,8 +243,10 @@ const (
var (
// ErrNoRows is alias of sql.ErrNoRows.
ErrNoRows = sql.ErrNoRows
// instances is the management map for instances.
instances = gmap.NewStrAnyMap(true)
// driverMap manages all custom registered driver.
driverMap = map[string]Driver{
"mysql": &DriverMysql{},
@ -212,8 +255,25 @@ var (
"oracle": &DriverOracle{},
"sqlite": &DriverSqlite{},
}
// lastOperatorRegPattern is the regular expression pattern for a string
// which has operator at its tail.
lastOperatorRegPattern = `[<>=]+\s*$`
// regularFieldNameRegPattern is the regular expression pattern for a string
// which is a regular field name of table.
regularFieldNameRegPattern = `^[\w\.\-]+$`
// allDryRun sets dry-run feature for all database connections.
// It is commonly used for command options for convenience.
allDryRun = false
)
func init() {
// allDryRun is initialized from environment or command options.
allDryRun = cmdenv.Get("gf.gdb.dryrun", false).Bool()
}
// Register registers custom database driver to gdb.
func Register(name string, driver Driver) error {
driverMap[name] = driver
@ -223,10 +283,10 @@ func Register(name string, driver Driver) error {
// New creates and returns an ORM object with global configurations.
// The parameter <name> specifies the configuration group name,
// which is DEFAULT_GROUP_NAME in default.
func New(name ...string) (db DB, err error) {
group := configs.group
if len(name) > 0 && name[0] != "" {
group = name[0]
func New(group ...string) (db DB, err error) {
groupName := configs.group
if len(group) > 0 && group[0] != "" {
groupName = group[0]
}
configs.RLock()
defer configs.RUnlock()
@ -234,10 +294,10 @@ func New(name ...string) (db DB, err error) {
if len(configs.config) < 1 {
return nil, errors.New("empty database configuration")
}
if _, ok := configs.config[group]; ok {
if node, err := getConfigNodeByGroup(group, true); err == nil {
if _, ok := configs.config[groupName]; ok {
if node, err := getConfigNodeByGroup(groupName, true); err == nil {
c := &Core{
group: group,
group: groupName,
debug: gtype.NewBool(),
cache: gcache.New(),
schema: gtype.NewString(),
@ -260,7 +320,7 @@ func New(name ...string) (db DB, err error) {
return nil, err
}
} else {
return nil, errors.New(fmt.Sprintf(`database configuration node "%s" is not found`, group))
return nil, errors.New(fmt.Sprintf(`database configuration node "%s" is not found`, groupName))
}
}

View File

@ -11,27 +11,17 @@ 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"
"github.com/gogf/gf/util/gconv"
)
const (
gPATH_FILTER_KEY = "/database/gdb/gdb"
)
var (
// lastOperatorReg is the regular expression object for a string
// which has operator at its tail.
lastOperatorReg = regexp.MustCompile(`[<>=]+\s*$`)
)
// Master creates and returns a connection from master node if master-slave configured.
// It returns the default connection if master-slave not configured.
func (c *Core) Master() (*sql.DB, error) {
@ -70,6 +60,7 @@ func (c *Core) DoQuery(link Link, sql string, args ...interface{}) (rows *sql.Ro
Error: err,
Start: mTime1,
End: mTime2,
Group: c.DB.GetGroup(),
}
c.writeSqlToLogger(s)
} else {
@ -113,6 +104,7 @@ func (c *Core) DoExec(link Link, sql string, args ...interface{}) (result sql.Re
Error: err,
Start: mTime1,
End: mTime2,
Group: c.DB.GetGroup(),
}
c.writeSqlToLogger(s)
} else {
@ -244,12 +236,12 @@ func (c *Core) GetScan(pointer interface{}, sql string, args ...interface{}) err
func (c *Core) GetValue(sql string, args ...interface{}) (Value, error) {
one, err := c.DB.GetOne(sql, args...)
if err != nil {
return nil, err
return gvar.New(nil), err
}
for _, v := range one {
return v, nil
}
return nil, nil
return gvar.New(nil), nil
}
// GetCount queries and returns the count from database.
@ -342,7 +334,10 @@ func (c *Core) Transaction(f func(tx *TX) error) (err error) {
//
// The parameter <batch> specifies the batch operation count when given data is slice.
func (c *Core) Insert(table string, data interface{}, batch ...int) (sql.Result, error) {
return c.DB.DoInsert(nil, table, data, gINSERT_OPTION_DEFAULT, batch...)
if len(batch) > 0 {
return c.Model(table).Data(data).Batch(batch[0]).Insert()
}
return c.Model(table).Data(data).Insert()
}
// InsertIgnore does "INSERT IGNORE INTO ..." statement for the table.
@ -355,7 +350,10 @@ func (c *Core) Insert(table string, data interface{}, batch ...int) (sql.Result,
//
// The parameter <batch> specifies the batch operation count when given data is slice.
func (c *Core) InsertIgnore(table string, data interface{}, batch ...int) (sql.Result, error) {
return c.DB.DoInsert(nil, table, data, gINSERT_OPTION_IGNORE, batch...)
if len(batch) > 0 {
return c.Model(table).Data(data).Batch(batch[0]).InsertIgnore()
}
return c.Model(table).Data(data).InsertIgnore()
}
// Replace does "REPLACE INTO ..." statement for the table.
@ -371,7 +369,10 @@ func (c *Core) InsertIgnore(table string, data interface{}, batch ...int) (sql.R
// If given data is type of slice, it then does batch replacing, and the optional parameter
// <batch> specifies the batch operation count.
func (c *Core) Replace(table string, data interface{}, batch ...int) (sql.Result, error) {
return c.DB.DoInsert(nil, table, data, gINSERT_OPTION_REPLACE, batch...)
if len(batch) > 0 {
return c.Model(table).Data(data).Batch(batch[0]).Replace()
}
return c.Model(table).Data(data).Replace()
}
// Save does "INSERT INTO ... ON DUPLICATE KEY UPDATE..." statement for the table.
@ -386,11 +387,14 @@ func (c *Core) Replace(table string, data interface{}, batch ...int) (sql.Result
// If given data is type of slice, it then does batch saving, and the optional parameter
// <batch> specifies the batch operation count.
func (c *Core) Save(table string, data interface{}, batch ...int) (sql.Result, error) {
return c.DB.DoInsert(nil, table, data, gINSERT_OPTION_SAVE, batch...)
if len(batch) > 0 {
return c.Model(table).Data(data).Batch(batch[0]).Save()
}
return c.Model(table).Data(data).Save()
}
// doInsert inserts or updates data for given table.
//
// This function is usually used for custom interface definition, you do not need call it manually.
// The parameter <data> can be type of map/gmap/struct/*struct/[]map/[]struct, etc.
// Eg:
// Data(g.Map{"uid": 10000, "name":"john"})
@ -418,7 +422,13 @@ func (c *Core) DoInsert(link Link, table string, data interface{}, option int, b
switch reflectKind {
case reflect.Slice, reflect.Array:
return c.DB.DoBatchInsert(link, table, data, option, batch...)
case reflect.Map, reflect.Struct:
case reflect.Struct:
if _, ok := data.(apiInterfaces); ok {
return c.DB.DoBatchInsert(link, table, data, option, batch...)
} else {
dataMap = DataToMapDeep(data)
}
case reflect.Map:
dataMap = DataToMapDeep(data)
default:
return result, errors.New(fmt.Sprint("unsupported data type:", reflectKind))
@ -473,28 +483,41 @@ func (c *Core) DoInsert(link Link, table string, data interface{}, option int, b
// BatchInsert batch inserts data.
// The parameter <list> must be type of slice of map or struct.
func (c *Core) BatchInsert(table string, list interface{}, batch ...int) (sql.Result, error) {
return c.DB.DoBatchInsert(nil, table, list, gINSERT_OPTION_DEFAULT, batch...)
if len(batch) > 0 {
return c.Model(table).Data(list).Batch(batch[0]).Insert()
}
return c.Model(table).Data(list).Insert()
}
// BatchInsert batch inserts data with ignore option.
// BatchInsertIgnore batch inserts data with ignore option.
// The parameter <list> must be type of slice of map or struct.
func (c *Core) BatchInsertIgnore(table string, list interface{}, batch ...int) (sql.Result, error) {
return c.DB.DoBatchInsert(nil, table, list, gINSERT_OPTION_IGNORE, batch...)
if len(batch) > 0 {
return c.Model(table).Data(list).Batch(batch[0]).InsertIgnore()
}
return c.Model(table).Data(list).InsertIgnore()
}
// BatchReplace batch replaces data.
// The parameter <list> must be type of slice of map or struct.
func (c *Core) BatchReplace(table string, list interface{}, batch ...int) (sql.Result, error) {
return c.DB.DoBatchInsert(nil, table, list, gINSERT_OPTION_REPLACE, batch...)
if len(batch) > 0 {
return c.Model(table).Data(list).Batch(batch[0]).Replace()
}
return c.Model(table).Data(list).Replace()
}
// BatchSave batch replaces data.
// The parameter <list> must be type of slice of map or struct.
func (c *Core) BatchSave(table string, list interface{}, batch ...int) (sql.Result, error) {
return c.DB.DoBatchInsert(nil, table, list, gINSERT_OPTION_SAVE, batch...)
if len(batch) > 0 {
return c.Model(table).Data(list).Batch(batch[0]).Save()
}
return c.Model(table).Data(list).Save()
}
// DoBatchInsert batch inserts/replaces/saves data.
// This function is usually used for custom interface definition, you do not need call it manually.
func (c *Core) DoBatchInsert(link Link, table string, list interface{}, option int, batch ...int) (result sql.Result, err error) {
table = c.DB.QuotePrefixTableName(table)
var (
@ -503,15 +526,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)
@ -528,8 +551,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))
}
@ -631,15 +667,11 @@ func (c *Core) DoBatchInsert(link Link, table string, list interface{}, option i
// "age IN(?,?)", 18, 50
// User{ Id : 1, UserName : "john"}
func (c *Core) Update(table string, data interface{}, condition interface{}, args ...interface{}) (sql.Result, error) {
newWhere, newArgs := formatWhere(c.DB, condition, args, false)
if newWhere != "" {
newWhere = " WHERE " + newWhere
}
return c.DB.DoUpdate(nil, table, data, newWhere, newArgs...)
return c.Model(table).Data(data).Where(condition, args...).Update()
}
// doUpdate does "UPDATE ... " statement for the table.
// Also see Update.
// This function is usually used for custom interface definition, you do not need call it manually.
func (c *Core) DoUpdate(link Link, table string, data interface{}, condition string, args ...interface{}) (result sql.Result, err error) {
table = c.DB.QuotePrefixTableName(table)
var (
@ -699,15 +731,11 @@ func (c *Core) DoUpdate(link Link, table string, data interface{}, condition str
// "age IN(?,?)", 18, 50
// User{ Id : 1, UserName : "john"}
func (c *Core) Delete(table string, condition interface{}, args ...interface{}) (result sql.Result, err error) {
newWhere, newArgs := formatWhere(c.DB, condition, args, false)
if newWhere != "" {
newWhere = " WHERE " + newWhere
}
return c.DB.DoDelete(nil, table, newWhere, newArgs...)
return c.Model(table).Where(condition, args...).Delete()
}
// DoDelete does "DELETE FROM ... " statement for the table.
// Also see Delete.
// This function is usually used for custom interface definition, you do not need call it manually.
func (c *Core) DoDelete(link Link, table string, condition string, args ...interface{}) (result sql.Result, err error) {
if link == nil {
if link, err = c.DB.Master(); err != nil {
@ -735,7 +763,7 @@ func (c *Core) rowsToResult(rows *sql.Rows) (Result, error) {
columnNames[k] = v.Name()
}
var (
values = make([]sql.RawBytes, len(columnNames))
values = make([]interface{}, len(columnNames))
records = make(Result, 0)
scanArgs = make([]interface{}, len(values))
)
@ -746,19 +774,12 @@ func (c *Core) rowsToResult(rows *sql.Rows) (Result, error) {
if err := rows.Scan(scanArgs...); err != nil {
return records, err
}
// Creates a new row object.
row := make(Record)
// Note that the internal looping variable <value> is type of []byte,
// which points to the same memory address. So it should do a copy.
for i, value := range values {
if value == nil {
row[columnNames[i]] = gvar.New(nil)
} else {
// As sql.RawBytes is type of slice,
// it should do a copy of it.
v := make([]byte, len(value))
copy(v, value)
row[columnNames[i]] = gvar.New(c.DB.convertValue(v, columnTypes[i]))
row[columnNames[i]] = gvar.New(c.DB.convertValue(value, columnTypes[i]))
}
}
records = append(records, row)
@ -781,11 +802,25 @@ 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("[%3d ms] [%s] %s", v.End-v.Start, v.Group, v.Format)
if v.Error != nil {
s += "\nError: " + v.Error.Error()
c.logger.StackWithFilter(gPATH_FILTER_KEY).Error(s)
c.logger.Error(s)
} else {
c.logger.StackWithFilter(gPATH_FILTER_KEY).Debug(s)
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
}

View File

@ -115,6 +115,14 @@ func GetDefaultGroup() string {
return configs.group
}
// IsConfigured checks and returns whether the database configured.
// It returns true if any configuration exists.
func IsConfigured() bool {
configs.RLock()
defer configs.RUnlock()
return len(configs.config) > 0
}
// SetLogger sets the logger for orm.
func (c *Core) SetLogger(logger *glog.Logger) {
c.logger = logger
@ -186,6 +194,10 @@ func (c *Core) SetDryRun(dryrun bool) {
// GetDryRun returns the DryRun value.
func (c *Core) GetDryRun() bool {
if allDryRun {
// Globally set.
return true
}
return c.dryrun.Val()
}

View File

@ -15,11 +15,12 @@ import (
"database/sql"
"errors"
"fmt"
"github.com/gogf/gf/internal/intlog"
"github.com/gogf/gf/text/gstr"
"strconv"
"strings"
"github.com/gogf/gf/internal/intlog"
"github.com/gogf/gf/text/gstr"
"github.com/gogf/gf/text/gregex"
)
@ -206,23 +207,43 @@ func (d *DriverMssql) TableFields(table string, schema ...string) (fields map[st
return nil
}
result, err = d.DB.DoGetAll(link, fmt.Sprintf(`
SELECT c.name as FIELD, CASE t.name
WHEN 'numeric' THEN t.name + '(' + convert(varchar(20),c.xprec) + ',' + convert(varchar(20),c.xscale) + ')'
WHEN 'char' THEN t.name + '(' + convert(varchar(20),c.length)+ ')'
WHEN 'varchar' THEN t.name + '(' + convert(varchar(20),c.length)+ ')'
ELSE t.name + '(' + convert(varchar(20),c.length)+ ')' END as TYPE
FROM systypes t,syscolumns c WHERE t.xtype=c.xtype
AND c.id = (SELECT id FROM sysobjects WHERE name='%s')
ORDER BY c.colid`, strings.ToUpper(table)))
SELECT a.name Field,
CASE b.name
WHEN 'datetime' THEN 'datetime'
WHEN 'numeric' THEN b.name + '(' + convert(varchar(20),a.xprec) + ',' + convert(varchar(20),a.xscale) + ')'
WHEN 'char' THEN b.name + '(' + convert(varchar(20),a.length)+ ')'
WHEN 'varchar' THEN b.name + '(' + convert(varchar(20),a.length)+ ')'
ELSE b.name + '(' + convert(varchar(20),a.length)+ ')' END as TYPE,
case when a.isnullable=1 then 'YES'else 'NO' end as [Null],
case when exists(SELECT 1 FROM sysobjects where xtype='PK' and name in (
SELECT name FROM sysindexes WHERE indid in(
SELECT indid FROM sysindexkeys WHERE id = a.id AND colid=a.colid
))) then 'PRI' else '' end AS [Key],
case when COLUMNPROPERTY(a.id,a.name,'IsIdentity')=1 then 'auto_increment'else '' end Extra,
isnull(e.text,'') as [Default],
isnull(g.[value],'') AS [Comment]
FROM syscolumns a
left join systypes b on a.xtype=b.xtype and a.xusertype=b.xusertype
inner join sysobjects d on a.id=d.id and d.xtype='U' and d.name<>'dtproperties'
left join syscomments e on a.cdefault=e.id
left join sys.extended_properties g on a.id=g.major_id and a.colid=g.minor_id
left join sys.extended_properties f on d.id=f.major_id and f.minor_id =0
where d.name='%s'
order by a.id,a.colorder`, strings.ToUpper(table)))
if err != nil {
return nil
}
fields = make(map[string]*TableField)
for i, m := range result {
fields[strings.ToLower(m["FIELD"].String())] = &TableField{
Index: i,
Name: strings.ToLower(m["FIELD"].String()),
Type: strings.ToLower(m["TYPE"].String()),
Index: i,
Name: strings.ToLower(m["FIELD"].String()),
Type: strings.ToLower(m["TYPE"].String()),
Null: m["Null"].Bool(),
Key: m["Key"].String(),
Default: m["Default"].Val(),
Extra: m["Extra"].String(),
Comment: m["Comment"].String(),
}
}
return fields

View File

@ -66,10 +66,10 @@ func (d *DriverOracle) GetChars() (charLeft string, charRight string) {
// HandleSqlBeforeCommit deals with the sql string before commits it to underlying sql driver.
func (d *DriverOracle) HandleSqlBeforeCommit(link Link, sql string, args []interface{}) (string, []interface{}) {
var index int
// Convert place holder char '?' to string ":x".
// Convert place holder char '?' to string ":vx".
str, _ := gregex.ReplaceStringFunc("\\?", sql, func(s string) string {
index++
return fmt.Sprintf(":%d", index)
return fmt.Sprintf(":v%d", index)
})
str, _ = gregex.ReplaceString("\"", "", str)
return d.parseSql(str), args
@ -78,44 +78,45 @@ func (d *DriverOracle) HandleSqlBeforeCommit(link Link, sql string, args []inter
// parseSql does some replacement of the sql before commits it to underlying driver,
// for support of oracle server.
func (d *DriverOracle) parseSql(sql string) string {
patten := `^\s*(?i)(SELECT)|(LIMIT\s*(\d+)\s*,\s*(\d+))`
if gregex.IsMatchString(patten, sql) == false {
var (
patten = `^\s*(?i)(SELECT)|(LIMIT\s*(\d+)\s*,{0,1}\s*(\d*))`
allMatch, _ = gregex.MatchAllString(patten, sql)
)
if len(allMatch) == 0 {
return sql
}
res, err := gregex.MatchAllString(patten, sql)
if err != nil {
return ""
}
index := 0
keyword := strings.TrimSpace(res[index][0])
keyword = strings.ToUpper(keyword)
var (
index = 0
keyword = strings.ToUpper(strings.TrimSpace(allMatch[index][0]))
)
index++
switch keyword {
case "SELECT":
if len(res) < 2 || (strings.HasPrefix(res[index][0], "LIMIT") == false &&
strings.HasPrefix(res[index][0], "limit") == false) {
if len(allMatch) < 2 || strings.HasPrefix(allMatch[index][0], "LIMIT") == false {
break
}
if gregex.IsMatchString("((?i)SELECT)(.+)((?i)LIMIT)", sql) == false {
break
}
queryExpr, _ := gregex.MatchString("((?i)SELECT)(.+)((?i)LIMIT)", sql)
if len(queryExpr) != 4 || strings.EqualFold(queryExpr[1], "SELECT") == false ||
if len(queryExpr) != 4 ||
strings.EqualFold(queryExpr[1], "SELECT") == false ||
strings.EqualFold(queryExpr[3], "LIMIT") == false {
break
}
first, limit := 0, 0
for i := 1; i < len(res[index]); i++ {
if len(strings.TrimSpace(res[index][i])) == 0 {
for i := 1; i < len(allMatch[index]); i++ {
if len(strings.TrimSpace(allMatch[index][i])) == 0 {
continue
}
if strings.HasPrefix(res[index][i], "LIMIT") || strings.HasPrefix(res[index][i], "limit") {
first, _ = strconv.Atoi(res[index][i+1])
limit, _ = strconv.Atoi(res[index][i+2])
if strings.HasPrefix(allMatch[index][i], "LIMIT") {
if allMatch[index][i+2] != "" {
first, _ = strconv.Atoi(allMatch[index][i+1])
limit, _ = strconv.Atoi(allMatch[index][i+2])
} else {
limit, _ = strconv.Atoi(allMatch[index][i+1])
}
break
}
}

View File

@ -13,6 +13,7 @@ import (
"github.com/gogf/gf/internal/empty"
"github.com/gogf/gf/internal/utils"
"github.com/gogf/gf/os/gtime"
"github.com/gogf/gf/util/gutil"
"reflect"
"regexp"
"strings"
@ -54,8 +55,34 @@ 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 retrieves and returns the elements of all item struct/map with key <key>.
// Note that the parameter <list> should be type of slice which contains elements of map or struct,
// or else it returns an empty slice.
//
// The parameter <list> 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 <subKey> 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 <key>.
// Note that the parameter <list> 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 <option>.
func GetInsertOperationByOption(option int) string {
var operator string
@ -71,33 +98,98 @@ 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 <obj> 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 <value> 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, gtime.Time, *gtime.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
@ -224,8 +316,10 @@ func GetPrimaryKeyCondition(primary string, where ...interface{}) (newWhereCondi
return where
}
if len(where) == 1 {
rv := reflect.ValueOf(where[0])
kind := rv.Kind()
var (
rv = reflect.ValueOf(where[0])
kind = rv.Kind()
)
if kind == reflect.Ptr {
rv = rv.Elem()
kind = rv.Kind()
@ -247,7 +341,7 @@ func GetPrimaryKeyCondition(primary string, where ...interface{}) (newWhereCondi
// formatSql formats the sql string and its arguments before executing.
// The internal handleArguments function might be called twice during the SQL procedure,
// but do not worry about it, it's safe and efficient.
func formatSql(sql string, args []interface{}) (newQuery string, newArgs []interface{}) {
func formatSql(sql string, args []interface{}) (newSql string, newArgs []interface{}) {
sql = gstr.Trim(sql)
sql = gstr.Replace(sql, "\n", " ")
sql, _ = gregex.ReplaceString(`\s{2,}`, ` `, sql)
@ -257,9 +351,11 @@ func formatSql(sql string, args []interface{}) (newQuery string, newArgs []inter
// formatWhere formats where statement and its arguments.
// TODO []interface{} type support for parameter <where> does not completed yet.
func formatWhere(db DB, where interface{}, args []interface{}, omitEmpty bool) (newWhere string, newArgs []interface{}) {
buffer := bytes.NewBuffer(nil)
rv := reflect.ValueOf(where)
kind := rv.Kind()
var (
buffer = bytes.NewBuffer(nil)
rv = reflect.ValueOf(where)
kind = rv.Kind()
)
if kind == reflect.Ptr {
rv = rv.Elem()
kind = rv.Kind()
@ -270,7 +366,7 @@ func formatWhere(db DB, where interface{}, args []interface{}, omitEmpty bool) (
case reflect.Map:
for key, value := range DataToMapDeep(where) {
if omitEmpty && empty.IsEmpty(value) {
if gregex.IsMatchString(regularFieldNameRegPattern, key) && omitEmpty && empty.IsEmpty(value) {
continue
}
newArgs = formatWhereKeyValue(db, buffer, newArgs, key, value)
@ -283,10 +379,11 @@ func formatWhere(db DB, where interface{}, args []interface{}, omitEmpty bool) (
// which implement apiIterator interface and are index-friendly for where conditions.
if iterator, ok := where.(apiIterator); ok {
iterator.Iterator(func(key, value interface{}) bool {
if omitEmpty && empty.IsEmpty(value) {
ketStr := gconv.String(key)
if gregex.IsMatchString(regularFieldNameRegPattern, ketStr) && omitEmpty && empty.IsEmpty(value) {
return true
}
newArgs = formatWhereKeyValue(db, buffer, newArgs, gconv.String(key), value)
newArgs = formatWhereKeyValue(db, buffer, newArgs, ketStr, value)
return true
})
break
@ -309,10 +406,10 @@ func formatWhere(db DB, where interface{}, args []interface{}, omitEmpty bool) (
newWhere = buffer.String()
if len(newArgs) > 0 {
if gstr.Pos(newWhere, "?") == -1 {
if lastOperatorReg.MatchString(newWhere) {
if gregex.IsMatchString(lastOperatorRegPattern, newWhere) {
// Eg: Where/And/Or("uid>=", 1)
newWhere += "?"
} else if gregex.IsMatchString(`^[\w\.\-]+$`, newWhere) {
} else if gregex.IsMatchString(regularFieldNameRegPattern, newWhere) {
newWhere = db.QuoteString(newWhere)
if len(newArgs) > 0 {
if utils.IsArray(newArgs[0]) {
@ -334,7 +431,7 @@ func formatWhere(db DB, where interface{}, args []interface{}, omitEmpty bool) (
}
// formatWhereInterfaces formats <where> as []interface{}.
// TODO []interface{} type support for parameter <where> does not completed yet.
// TODO supporting for parameter <where> with []interface{} type is not completed yet.
func formatWhereInterfaces(db DB, where []interface{}, buffer *bytes.Buffer, newArgs []interface{}) []interface{} {
var str string
var array []interface{}
@ -362,8 +459,10 @@ func formatWhereKeyValue(db DB, buffer *bytes.Buffer, newArgs []interface{}, key
// If the value is type of slice, and there's only one '?' holder in
// the key string, it automatically adds '?' holder chars according to its arguments count
// and converts it to "IN" statement.
rv := reflect.ValueOf(value)
kind := rv.Kind()
var (
rv = reflect.ValueOf(value)
kind = rv.Kind()
)
switch kind {
case reflect.Slice, reflect.Array:
count := gstr.Count(quotedKey, "?")
@ -379,7 +478,7 @@ func formatWhereKeyValue(db DB, buffer *bytes.Buffer, newArgs []interface{}, key
}
default:
if value == nil || empty.IsNil(rv) {
if gregex.IsMatchString(`^[\w\.\-]+$`, key) {
if gregex.IsMatchString(regularFieldNameRegPattern, key) {
// The key is a single field name.
buffer.WriteString(quotedKey + " IS NULL")
} else {
@ -392,11 +491,24 @@ func formatWhereKeyValue(db DB, buffer *bytes.Buffer, newArgs []interface{}, key
if gstr.Pos(quotedKey, "?") == -1 {
like := " like"
if len(quotedKey) > len(like) && gstr.Equal(quotedKey[len(quotedKey)-len(like):], like) {
// Eg: Where(g.Map{"name like": "john%"})
buffer.WriteString(quotedKey + " ?")
} else if lastOperatorReg.MatchString(quotedKey) {
} else if gregex.IsMatchString(lastOperatorRegPattern, quotedKey) {
// Eg: Where(g.Map{"age > ": 16})
buffer.WriteString(quotedKey + " ?")
} else {
} else if gregex.IsMatchString(regularFieldNameRegPattern, key) {
// The key is a regular field name.
buffer.WriteString(quotedKey + "=?")
} else {
// The key is not a regular field name.
// Eg: Where(g.Map{"age > 16": nil})
// Issue: https://github.com/gogf/gf/issues/765
if empty.IsEmpty(value) {
buffer.WriteString(quotedKey)
break
} else {
buffer.WriteString(quotedKey + "=?")
}
}
} else {
buffer.WriteString(quotedKey)
@ -432,9 +544,26 @@ func handleArguments(sql string, args []interface{}) (newSql string, newArgs []i
newArgs = append(newArgs, arg)
continue
}
for i := 0; i < rv.Len(); i++ {
newArgs = append(newArgs, rv.Index(i).Interface())
if rv.Len() == 0 {
// Empty slice argument, it converts the sql to a false sql.
// Eg:
// Query("select * from xxx where id in(?)", g.Slice{}) -> select * from xxx where 0=1
// Where("id in(?)", g.Slice{}) -> WHERE 0=1
if gstr.Contains(newSql, "?") {
whereKeyWord := " WHERE "
if p := gstr.PosI(newSql, whereKeyWord); p == -1 {
return "0=1", []interface{}{}
} else {
return gstr.SubStr(newSql, 0, p+len(whereKeyWord)) + "0=1", []interface{}{}
}
}
} else {
for i := 0; i < rv.Len(); i++ {
newArgs = append(newArgs, rv.Index(i).Interface())
}
}
// If the '?' holder count equals the length of the slice,
// it does not implement the arguments splitting logic.
// Eg: db.Query("SELECT ?+?", g.Slice{1, 2})
@ -511,14 +640,16 @@ func formatError(err error, sql string, args ...interface{}) error {
func FormatSqlWithArgs(sql string, args []interface{}) string {
index := -1
newQuery, _ := gregex.ReplaceStringFunc(
`(\?|:\d+|\$\d+|@p\d+)`, sql, func(s string) string {
`(\?|:v\d+|\$\d+|@p\d+)`, sql, func(s string) string {
index++
if len(args) > index {
if args[index] == nil {
return "null"
}
rv := reflect.ValueOf(args[index])
kind := rv.Kind()
var (
rv = reflect.ValueOf(args[index])
kind = rv.Kind()
)
if kind == reflect.Ptr {
if rv.IsNil() || !rv.IsValid() {
return "null"

View File

@ -140,7 +140,7 @@ func (m *Model) Offset(offset int) *Model {
// Page sets the paging number for the model.
// The parameter <page> is started from 1 for paging.
// Note that, it differs that the Limit function start from 0 for "LIMIT" statement.
// Note that, it differs that the Limit function starts from 0 for "LIMIT" statement.
func (m *Model) Page(page, limit int) *Model {
model := m.getModel()
if page <= 0 {

View File

@ -40,9 +40,9 @@ func (m *Model) Delete(where ...interface{}) (result sql.Result, err error) {
return m.db.DoUpdate(
m.getLink(true),
m.tables,
fmt.Sprintf(`%s='%s'`, m.db.QuoteString(fieldNameDelete), gtime.Now().String()),
fmt.Sprintf(`%s=?`, m.db.QuoteString(fieldNameDelete)),
conditionWhere+conditionExtra,
conditionArgs...,
append([]interface{}{gtime.Now().String()}, conditionArgs...),
)
}
return m.db.DoDelete(m.getLink(true), m.tables, conditionWhere+conditionExtra, conditionArgs...)

View File

@ -33,40 +33,20 @@ func (m *Model) Fields(fields string) *Model {
// FieldsEx sets the excluded operation fields of the model, multiple fields joined using char ','.
// Note that this function supports only single table operations.
func (m *Model) FieldsEx(fields string) *Model {
if gstr.Contains(m.tables, " ") {
panic("function FieldsEx supports only single table operations")
}
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))
}
model := m.getModel()
model.fieldsEx = fields
fieldsExSet := gset.NewStrSetFrom(gstr.SplitAndTrim(fields, ","))
fieldsArray := make([]string, len(tableFields))
for k, v := range tableFields {
fieldsArray[v.Index] = k
}
model.fields = ""
for _, k := range fieldsArray {
if fieldsExSet.Contains(k) {
continue
}
if len(model.fields) > 0 {
model.fields += ","
}
model.fields += k
}
model.fields = model.db.QuoteString(model.fields)
return model
}
// Deprecated, use GetFieldsStr instead.
// This function name confuses the user that it was a chaining function.
func (m *Model) FieldsStr(prefix ...string) string {
return m.GetFieldsStr(prefix...)
}
// FieldsStr retrieves and returns all fields from the table, joined with char ','.
// The optional parameter <prefix> specifies the prefix for each field, eg: FieldsStr("u.").
func (m *Model) FieldsStr(prefix ...string) string {
func (m *Model) GetFieldsStr(prefix ...string) string {
prefixStr := ""
if len(prefix) > 0 {
prefixStr = prefix[0]
@ -93,11 +73,17 @@ func (m *Model) FieldsStr(prefix ...string) string {
return newFields
}
// Deprecated, use GetFieldsExStr instead.
// This function name confuses the user that it was a chaining function.
func (m *Model) FieldsExStr(fields string, prefix ...string) string {
return m.GetFieldsExStr(fields, prefix...)
}
// FieldsExStr retrieves and returns fields which are not in parameter <fields> from the table,
// joined with char ','.
// The parameter <fields> specifies the fields that are excluded.
// The optional parameter <prefix> specifies the prefix for each field, eg: FieldsExStr("id", "u.").
func (m *Model) FieldsExStr(fields string, prefix ...string) string {
func (m *Model) GetFieldsExStr(fields string, prefix ...string) string {
prefixStr := ""
if len(prefix) > 0 {
prefixStr = prefix[0]
@ -127,3 +113,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, error) {
tableFields, err := m.db.TableFields(m.tables)
if err != nil {
return false, err
}
if len(tableFields) == 0 {
return false, fmt.Errorf(`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, nil
}
}
return false, nil
}

View File

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

View File

@ -6,7 +6,21 @@
package gdb
import "fmt"
import (
"fmt"
"github.com/gogf/gf/text/gstr"
)
// isSubQuery checks and returns whether given string a sub-query sql string.
func isSubQuery(s string) bool {
s = gstr.TrimLeft(s)
if p := gstr.Pos(s, " "); p != -1 {
if gstr.Equal(s[:p], "select") {
return true
}
}
return false
}
// LeftJoin does "LEFT JOIN ... ON ..." statement on the model.
// The parameter <table> can be joined table and its joined condition,
@ -14,19 +28,32 @@ import "fmt"
// Table("user").LeftJoin("user_detail", "user_detail.uid=user.uid")
// Table("user", "u").LeftJoin("user_detail", "ud", "ud.uid=u.uid")
func (m *Model) LeftJoin(table ...string) *Model {
model := m.getModel()
var (
model = m.getModel()
joinStr = ""
)
if len(table) > 0 {
if isSubQuery(table[0]) {
joinStr = "(" + table[0] + ")"
} else {
joinStr = m.db.QuotePrefixTableName(table[0])
}
}
if len(table) > 2 {
model.tables += fmt.Sprintf(
" LEFT JOIN %s AS %s ON (%s)",
m.db.QuotePrefixTableName(table[0]), m.db.QuoteWord(table[1]), table[2],
joinStr, m.db.QuoteWord(table[1]), table[2],
)
} else if len(table) == 2 {
model.tables += fmt.Sprintf(
" LEFT JOIN %s ON (%s)",
m.db.QuotePrefixTableName(table[0]), table[1],
joinStr, table[1],
)
} else if len(table) == 1 {
model.tables += fmt.Sprintf(
" LEFT JOIN %s",
joinStr,
)
} else {
panic("invalid join table parameter")
}
return model
}
@ -37,19 +64,32 @@ func (m *Model) LeftJoin(table ...string) *Model {
// Table("user").RightJoin("user_detail", "user_detail.uid=user.uid")
// Table("user", "u").RightJoin("user_detail", "ud", "ud.uid=u.uid")
func (m *Model) RightJoin(table ...string) *Model {
model := m.getModel()
var (
model = m.getModel()
joinStr = ""
)
if len(table) > 0 {
if isSubQuery(table[0]) {
joinStr = "(" + table[0] + ")"
} else {
joinStr = m.db.QuotePrefixTableName(table[0])
}
}
if len(table) > 2 {
model.tables += fmt.Sprintf(
" RIGHT JOIN %s AS %s ON (%s)",
m.db.QuotePrefixTableName(table[0]), m.db.QuoteWord(table[1]), table[2],
joinStr, m.db.QuoteWord(table[1]), table[2],
)
} else if len(table) == 2 {
model.tables += fmt.Sprintf(
" RIGHT JOIN %s ON (%s)",
m.db.QuotePrefixTableName(table[0]), table[1],
joinStr, table[1],
)
} else if len(table) == 1 {
model.tables += fmt.Sprintf(
" RIGHT JOIN %s",
joinStr,
)
} else {
panic("invalid join table parameter")
}
return model
}
@ -60,19 +100,32 @@ func (m *Model) RightJoin(table ...string) *Model {
// Table("user").InnerJoin("user_detail", "user_detail.uid=user.uid")
// Table("user", "u").InnerJoin("user_detail", "ud", "ud.uid=u.uid")
func (m *Model) InnerJoin(table ...string) *Model {
model := m.getModel()
var (
model = m.getModel()
joinStr = ""
)
if len(table) > 0 {
if isSubQuery(table[0]) {
joinStr = "(" + table[0] + ")"
} else {
joinStr = m.db.QuotePrefixTableName(table[0])
}
}
if len(table) > 2 {
model.tables += fmt.Sprintf(
" INNER JOIN %s AS %s ON (%s)",
m.db.QuotePrefixTableName(table[0]), m.db.QuoteWord(table[1]), table[2],
joinStr, m.db.QuoteWord(table[1]), table[2],
)
} else if len(table) == 2 {
model.tables += fmt.Sprintf(
" INNER JOIN %s ON (%s)",
m.db.QuotePrefixTableName(table[0]), table[1],
joinStr, table[1],
)
} else if len(table) == 1 {
model.tables += fmt.Sprintf(
" INNER JOIN %s",
joinStr,
)
} else {
panic("invalid join table parameter")
}
return model
}

View File

@ -8,6 +8,9 @@ package gdb
import (
"fmt"
"github.com/gogf/gf/container/gset"
"github.com/gogf/gf/container/gvar"
"github.com/gogf/gf/text/gstr"
"github.com/gogf/gf/util/gconv"
"reflect"
)
@ -26,12 +29,23 @@ func (m *Model) Select(where ...interface{}) (Result, error) {
// The optional parameter <where> is the same as the parameter of Model.Where function,
// see Model.Where.
func (m *Model) All(where ...interface{}) (Result, error) {
return m.doGetAll(false, where...)
}
// doGetAll does "SELECT FROM ..." statement for the model.
// It retrieves the records from table and returns the result as slice type.
// It returns nil if there's no record retrieved with the given conditions from table.
//
// The parameter <limit1> specifies whether limits querying only one record if m.limit is not set.
// The optional parameter <where> is the same as the parameter of Model.Where function,
// see Model.Where.
func (m *Model) doGetAll(limit1 bool, where ...interface{}) (Result, error) {
if len(where) > 0 {
return m.Where(where[0], where[1:]...).All()
}
var (
softDeletingCondition = m.getConditionForSoftDeleting()
conditionWhere, conditionExtra, conditionArgs = m.formatCondition(false)
conditionWhere, conditionExtra, conditionArgs = m.formatCondition(limit1)
)
if !m.unscoped && softDeletingCondition != "" {
if conditionWhere == "" {
@ -41,12 +55,13 @@ func (m *Model) All(where ...interface{}) (Result, error) {
}
conditionWhere += softDeletingCondition
}
// DO NOT quote the m.fields where, in case of fields like:
// DISTINCT t.user_id uid
return m.doGetAll(
return m.doGetAllBySql(
fmt.Sprintf(
"SELECT %s FROM %s%s",
m.fields,
m.getFieldsFiltered(),
m.tables,
conditionWhere+conditionExtra,
),
@ -54,6 +69,53 @@ func (m *Model) All(where ...interface{}) (Result, error) {
)
}
// getFieldsFiltered checks the fields and fieldsEx attributes, filters and returns the fields that will
// really be committed to underlying database driver.
func (m *Model) getFieldsFiltered() string {
if m.fieldsEx == "" {
// No filtering.
return m.fields
}
var (
fieldsArray []string
fieldsExSet = gset.NewStrSetFrom(gstr.SplitAndTrim(m.fieldsEx, ","))
)
if m.fields != "*" {
// Filter custom fields with fieldEx.
fieldsArray = make([]string, 0, 8)
for _, v := range gstr.SplitAndTrim(m.fields, ",") {
fieldsArray = append(fieldsArray, v[gstr.PosR(v, "-")+1:])
}
} else {
if gstr.Contains(m.tables, " ") {
panic("function FieldsEx supports only single table operations")
}
// Filter table fields with fieldEx.
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
}
}
newFields := ""
for _, k := range fieldsArray {
if fieldsExSet.Contains(k) {
continue
}
if len(newFields) > 0 {
newFields += ","
}
newFields += k
}
return newFields
}
// Chunk iterates the query result with given size and callback function.
func (m *Model) Chunk(limit int, callback func(result Result, err error) bool) {
page := m.start
@ -90,7 +152,7 @@ func (m *Model) One(where ...interface{}) (Record, error) {
if len(where) > 0 {
return m.Where(where[0], where[1:]...).One()
}
all, err := m.All()
all, err := m.doGetAll(true)
if err != nil {
return nil, err
}
@ -118,12 +180,12 @@ func (m *Model) Value(fieldsAndWhere ...interface{}) (Value, error) {
}
one, err := m.One()
if err != nil {
return nil, err
return gvar.New(nil), err
}
for _, v := range one {
return v, nil
}
return nil, nil
return gvar.New(nil), nil
}
// Array queries and returns data values as slice from database.
@ -157,7 +219,7 @@ func (m *Model) Array(fieldsAndWhere ...interface{}) ([]Value, error) {
// see Model.Where.
//
// Note that it returns sql.ErrNoRows if there's no record retrieved with the given conditions
// from table.
// from table and <pointer> is not nil.
//
// Eg:
// user := new(User)
@ -181,14 +243,14 @@ func (m *Model) Struct(pointer interface{}, where ...interface{}) error {
// see Model.Where.
//
// Note that it returns sql.ErrNoRows if there's no record retrieved with the given conditions
// from table.
// from table and <pointer> is not empty.
//
// Eg:
// users := ([]User)(nil)
// err := db.Table("user").Structs(&users)
// err := db.Table("user").Structs(&users)
//
// users := ([]*User)(nil)
// err := db.Table("user").Structs(&users)
// err := db.Table("user").Structs(&users)
func (m *Model) Structs(pointer interface{}, where ...interface{}) error {
all, err := m.All(where...)
if err != nil {
@ -215,10 +277,10 @@ func (m *Model) Structs(pointer interface{}, where ...interface{}) error {
// err := db.Table("user").Where("id", 1).Struct(&user)
//
// users := ([]User)(nil)
// err := db.Table("user").Structs(&users)
// err := db.Table("user").Structs(&users)
//
// users := ([]*User)(nil)
// err := db.Table("user").Structs(&users)
// err := db.Table("user").Structs(&users)
func (m *Model) Scan(pointer interface{}, where ...interface{}) error {
t := reflect.TypeOf(pointer)
k := t.Kind()
@ -226,13 +288,41 @@ func (m *Model) Scan(pointer interface{}, where ...interface{}) error {
return fmt.Errorf("params should be type of pointer, but got: %v", k)
}
switch t.Elem().Kind() {
case reflect.Array:
case reflect.Slice:
case reflect.Array, reflect.Slice:
return m.Structs(pointer, where...)
default:
return m.Struct(pointer, where...)
}
return nil
}
// ScanList converts <r> to struct slice which contains other complex struct attributes.
// Note that the parameter <listPointer> should be type of *[]struct/*[]*struct.
// Usage example:
//
// type Entity struct {
// User *EntityUser
// UserDetail *EntityUserDetail
// UserScores []*EntityUserScores
// }
// var users []*Entity
// or
// var users []Entity
//
// ScanList(&users, "User")
// ScanList(&users, "UserDetail", "User", "uid:Uid")
// ScanList(&users, "UserScores", "User", "uid:Uid")
// The parameters "User"/"UserDetail"/"UserScores" in the example codes specify the target attribute struct
// that current result will be bound to.
// The "uid" in the example codes is the table field name of the result, and the "Uid" is the relational
// struct attribute name. It automatically calculates the HasOne/HasMany relationship with given <relation>
// parameter.
// See the example or unit testing cases for clear understanding for this function.
func (m *Model) ScanList(listPointer interface{}, attributeName string, relation ...string) (err error) {
all, err := m.All()
if err != nil {
return err
}
return all.ScanList(listPointer, attributeName, relation...)
}
// Count does "SELECT COUNT(x) FROM ..." statement for the model.
@ -265,7 +355,7 @@ func (m *Model) Count(where ...interface{}) (int, error) {
if len(m.groupBy) > 0 {
s = fmt.Sprintf("SELECT COUNT(1) FROM (%s) count_alias", s)
}
list, err := m.doGetAll(s, conditionArgs...)
list, err := m.doGetAllBySql(s, conditionArgs...)
if err != nil {
return 0, err
}
@ -338,8 +428,8 @@ func (m *Model) FindScan(pointer interface{}, where ...interface{}) error {
return m.Scan(pointer)
}
// doGetAll does the select statement on the database.
func (m *Model) doGetAll(sql string, args ...interface{}) (result Result, err error) {
// doGetAllBySql does the select statement on the database.
func (m *Model) doGetAllBySql(sql string, args ...interface{}) (result Result, err error) {
cacheKey := ""
// Retrieve from cache.
if m.cacheEnabled && m.tx == nil {

View File

@ -86,8 +86,8 @@ func (m *Model) getConditionForSoftDeleting() string {
// Base table.
match, _ := gregex.MatchString(`(.+?) [A-Z]+ JOIN`, m.tables)
conditionArray.Append(m.getConditionOfTableStringForSoftDeleting(match[1]))
// Multiple joined tables.
matches, _ := gregex.MatchAllString(`JOIN (.+?) ON`, m.tables)
// Multiple joined tables, exclude the sub query sql which contains char '(' and ')'.
matches, _ := gregex.MatchAllString(`JOIN ([^()]+?) ON`, m.tables)
for _, match := range matches {
conditionArray.Append(m.getConditionOfTableStringForSoftDeleting(match[1]))
}

View File

@ -157,8 +157,8 @@ func (m *Model) getPrimaryKey() string {
// formatCondition formats where arguments of the model and returns a new condition sql and its arguments.
// Note that this function does not change any attribute value of the <m>.
//
// The parameter <limit> specifies whether limits querying only one record if m.limit is not set.
func (m *Model) formatCondition(limit bool) (conditionWhere string, conditionExtra string, conditionArgs []interface{}) {
// The parameter <limit1> specifies whether limits querying only one record if m.limit is not set.
func (m *Model) formatCondition(limit1 bool) (conditionWhere string, conditionExtra string, conditionArgs []interface{}) {
if len(m.whereHolder) > 0 {
for _, v := range m.whereHolder {
switch v.operator {
@ -231,7 +231,7 @@ func (m *Model) formatCondition(limit bool) (conditionWhere string, conditionExt
} else {
conditionExtra += fmt.Sprintf(" LIMIT %d", m.limit)
}
} else if limit {
} else if limit1 {
conditionExtra += " LIMIT 1"
}
if m.offset >= 0 {

View File

@ -8,6 +8,7 @@ package gdb
import (
"strings"
"time"
"github.com/gogf/gf/text/gstr"
@ -21,7 +22,12 @@ import (
// convertValue automatically checks and converts field value from database type
// to golang variable type.
func (c *Core) convertValue(fieldValue []byte, fieldType string) interface{} {
func (c *Core) convertValue(fieldValue interface{}, fieldType string) interface{} {
// If there's no type retrieved, it returns the <fieldValue> directly
// to use its original data type, as <fieldValue> is type of interface{}.
if fieldType == "" {
return fieldValue
}
t, _ := gregex.ReplaceString(`\(.+\)`, "", fieldType)
t = strings.ToLower(t)
switch t {
@ -32,7 +38,7 @@ func (c *Core) convertValue(fieldValue []byte, fieldType string) interface{} {
"tinyblob",
"mediumblob",
"longblob":
return fieldValue
return gconv.Bytes(fieldValue)
case
"int",
@ -43,21 +49,21 @@ func (c *Core) convertValue(fieldValue []byte, fieldType string) interface{} {
"mediumint",
"serial":
if gstr.ContainsI(fieldType, "unsigned") {
gconv.Uint(string(fieldValue))
gconv.Uint(gconv.String(fieldValue))
}
return gconv.Int(string(fieldValue))
return gconv.Int(gconv.String(fieldValue))
case
"big_int",
"bigint",
"bigserial":
if gstr.ContainsI(fieldType, "unsigned") {
gconv.Uint64(string(fieldValue))
gconv.Uint64(gconv.String(fieldValue))
}
return gconv.Int64(string(fieldValue))
return gconv.Int64(gconv.String(fieldValue))
case "real":
return gconv.Float32(string(fieldValue))
return gconv.Float32(gconv.String(fieldValue))
case
"float",
@ -66,10 +72,10 @@ func (c *Core) convertValue(fieldValue []byte, fieldType string) interface{} {
"money",
"numeric",
"smallmoney":
return gconv.Float64(string(fieldValue))
return gconv.Float64(gconv.String(fieldValue))
case "bit":
s := string(fieldValue)
s := gconv.String(fieldValue)
// mssql is true|false string.
if strings.EqualFold(s, "true") {
return 1
@ -77,41 +83,47 @@ func (c *Core) convertValue(fieldValue []byte, fieldType string) interface{} {
if strings.EqualFold(s, "false") {
return 0
}
return gbinary.BeDecodeToInt64(fieldValue)
return gbinary.BeDecodeToInt64(gconv.Bytes(fieldValue))
case "bool":
return gconv.Bool(fieldValue)
case "date":
t, _ := gtime.StrToTime(string(fieldValue))
if t, ok := fieldValue.(time.Time); ok {
return gtime.NewFromTime(t).Format("Y-m-d")
}
t, _ := gtime.StrToTime(gconv.String(fieldValue))
return t.Format("Y-m-d")
case
"datetime",
"timestamp":
t, _ := gtime.StrToTime(string(fieldValue))
if t, ok := fieldValue.(time.Time); ok {
return gtime.NewFromTime(t)
}
t, _ := gtime.StrToTime(gconv.String(fieldValue))
return t.String()
default:
// Auto detect field type, using key match.
switch {
case strings.Contains(t, "text") || strings.Contains(t, "char") || strings.Contains(t, "character"):
return string(fieldValue)
return gconv.String(fieldValue)
case strings.Contains(t, "float") || strings.Contains(t, "double") || strings.Contains(t, "numeric"):
return gconv.Float64(string(fieldValue))
return gconv.Float64(gconv.String(fieldValue))
case strings.Contains(t, "bool"):
return gconv.Bool(string(fieldValue))
return gconv.Bool(gconv.String(fieldValue))
case strings.Contains(t, "binary") || strings.Contains(t, "blob"):
return fieldValue
case strings.Contains(t, "int"):
return gconv.Int(string(fieldValue))
return gconv.Int(gconv.String(fieldValue))
case strings.Contains(t, "time"):
s := string(fieldValue)
s := gconv.String(fieldValue)
t, err := gtime.StrToTime(s)
if err != nil {
return s
@ -119,7 +131,7 @@ func (c *Core) convertValue(fieldValue []byte, fieldType string) interface{} {
return t.String()
case strings.Contains(t, "date"):
s := string(fieldValue)
s := gconv.String(fieldValue)
t, err := gtime.StrToTime(s)
if err != nil {
return s
@ -127,7 +139,7 @@ func (c *Core) convertValue(fieldValue []byte, fieldType string) interface{} {
return t.Format("Y-m-d")
default:
return string(fieldValue)
return gconv.String(fieldValue)
}
}
}

Some files were not shown because too many files have changed in this diff Show More