mirror of
https://gitee.com/johng/gf
synced 2026-06-07 18:26:02 +08:00
Compare commits
65 Commits
v2.1.0-rc3
...
contrib/dr
| Author | SHA1 | Date | |
|---|---|---|---|
| 68efab79ef | |||
| 1ede8c77ba | |||
| 48f95d0009 | |||
| 0bb57b8989 | |||
| 0503c17867 | |||
| 19779cd342 | |||
| 141f3512a9 | |||
| 3b9e5c71bf | |||
| 2bcee014f7 | |||
| f0568b4e22 | |||
| 8670f29c4e | |||
| 33a528af76 | |||
| 52056644d4 | |||
| 3ae23df4b3 | |||
| 1b327b8abd | |||
| bb5cd3e224 | |||
| 1e8237446e | |||
| b2b2044786 | |||
| 64c5222623 | |||
| 1597291ac3 | |||
| c2e742335b | |||
| cf5884bc60 | |||
| cbf5ee9649 | |||
| 8ac177a6de | |||
| cdd4473df5 | |||
| aaebaa7250 | |||
| 7443a8baa1 | |||
| c6ff95a3f4 | |||
| 7957016ae2 | |||
| 17ab0e2ced | |||
| b0650f3402 | |||
| f4f73f2765 | |||
| babc69e13d | |||
| 481c50f233 | |||
| b62b2f3598 | |||
| 4f37abac6a | |||
| 31a23e724d | |||
| 7d5ab1f8db | |||
| 0d8952dcde | |||
| bd7ec5d0b0 | |||
| 9e6e8001ca | |||
| 0639becccc | |||
| 88844649eb | |||
| 31c5d5a5f5 | |||
| 6abbc57c96 | |||
| 39af6e51c4 | |||
| ef04c8a09e | |||
| 4505d61604 | |||
| 04d32e7a91 | |||
| 26066965c3 | |||
| 7f199527f8 | |||
| ea79b3cbb8 | |||
| 0ca81bd11a | |||
| 4d13ffdc26 | |||
| 331a29024e | |||
| 6aa5c2b2ef | |||
| 8c969b2a84 | |||
| 0d7e28ee75 | |||
| ab5062663e | |||
| 896b9fa105 | |||
| 6a01275499 | |||
| 61bf0a0092 | |||
| d7c5a08d20 | |||
| 350ee9f0a2 | |||
| 7753fc6fe1 |
34
.github/workflows/gf.yml
vendored
34
.github/workflows/gf.yml
vendored
@ -28,7 +28,7 @@ jobs:
|
||||
services:
|
||||
# Redis backend server.
|
||||
redis:
|
||||
image : redis
|
||||
image : loads/redis:latest
|
||||
options: >-
|
||||
--health-cmd "redis-cli ping"
|
||||
--health-interval 10s
|
||||
@ -40,7 +40,7 @@ jobs:
|
||||
|
||||
# MySQL backend server.
|
||||
mysql:
|
||||
image: mysql:5.7
|
||||
image: loads/mysql:5.7
|
||||
env:
|
||||
MYSQL_DATABASE : test
|
||||
MYSQL_ROOT_PASSWORD: 12345678
|
||||
@ -50,7 +50,7 @@ jobs:
|
||||
|
||||
# PostgreSQL backend server.
|
||||
postgres:
|
||||
image: postgres:13
|
||||
image: loads/postgres:13
|
||||
env:
|
||||
POSTGRES_PASSWORD: 12345678
|
||||
POSTGRES_USER: root
|
||||
@ -67,7 +67,7 @@ jobs:
|
||||
|
||||
# MSSQL backend server.
|
||||
mssql:
|
||||
image: mcmoe/mssqldocker:latest
|
||||
image: loads/mssqldocker:latest
|
||||
env:
|
||||
ACCEPT_EULA: Y
|
||||
SA_PASSWORD: LoremIpsum86
|
||||
@ -85,21 +85,35 @@ jobs:
|
||||
|
||||
# ClickHouse backend server.
|
||||
clickhouse-server:
|
||||
image: yandex/clickhouse-server
|
||||
image: loads/clickhouse-server:latest
|
||||
ports:
|
||||
- 9000:9000
|
||||
- 8123:8123
|
||||
- 9001:9001
|
||||
|
||||
polaris:
|
||||
image: huyuanxin/polaris-server-with-config:latest
|
||||
image: polarismesh/polaris-server-standalone:latest
|
||||
ports:
|
||||
- 8090:8090
|
||||
- 8091:8091
|
||||
|
||||
#oracle 11g server
|
||||
oracle-server:
|
||||
image: loads/oracle-xe-11g-r2:latest
|
||||
env:
|
||||
ORACLE_ALLOW_REMOTE: true
|
||||
ORACLE_SID: XE
|
||||
ORACLE_DB_USER_NAME: system
|
||||
ORACLE_DB_PASSWORD: oracle
|
||||
ports:
|
||||
- 1521:1521
|
||||
|
||||
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
go: ["1.15", "1.16", "1.17"]
|
||||
goarch: ["386", "amd64"]
|
||||
go: [ "1.15", "1.16", "1.17" ]
|
||||
goarch: [ "386", "amd64" ]
|
||||
|
||||
|
||||
steps:
|
||||
@ -130,7 +144,9 @@ jobs:
|
||||
dirpath=$(dirname $file)
|
||||
|
||||
if [ "oracle" = $(basename $dirpath) ]; then
|
||||
continue 1
|
||||
if ! go version|grep -q "1.17"; then
|
||||
continue 1
|
||||
fi
|
||||
fi
|
||||
|
||||
cd $dirpath
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@ -15,3 +15,5 @@ cbuild
|
||||
**/.DS_Store
|
||||
.test/
|
||||
cmd/gf/main
|
||||
cmd/gf/gf
|
||||
go.work
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
## This file contains all available configuration options
|
||||
## with their default values.
|
||||
#
|
||||
|
||||
# See https://github.com/golangci/golangci-lint#config-file
|
||||
run:
|
||||
issues-exit-code: 1 #Default
|
||||
|
||||
@ -3,12 +3,12 @@ module github.com/gogf/gf/cmd/gf/v2
|
||||
go 1.15
|
||||
|
||||
require (
|
||||
github.com/gogf/gf/contrib/drivers/mssql/v2 v2.0.6
|
||||
github.com/gogf/gf/contrib/drivers/mysql/v2 v2.0.6
|
||||
github.com/gogf/gf/contrib/drivers/pgsql/v2 v2.0.6
|
||||
github.com/gogf/gf/contrib/drivers/sqlite/v2 v2.0.6
|
||||
github.com/gogf/gf/v2 v2.0.6
|
||||
github.com/gogf/gf/contrib/drivers/mssql/v2 v2.1.0
|
||||
github.com/gogf/gf/contrib/drivers/mysql/v2 v2.1.0
|
||||
github.com/gogf/gf/contrib/drivers/pgsql/v2 v2.1.0
|
||||
github.com/gogf/gf/v2 v2.1.0
|
||||
github.com/olekukonko/tablewriter v0.0.5
|
||||
golang.org/x/tools v0.1.11
|
||||
)
|
||||
|
||||
replace (
|
||||
|
||||
@ -66,8 +66,6 @@ github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9
|
||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
|
||||
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||
github.com/mattn/go-sqlite3 v1.14.10 h1:MLn+5bFRlWMGoSRmJour3CL1w/qL96mvipqpwQW/Sfk=
|
||||
github.com/mattn/go-sqlite3 v1.14.10/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
|
||||
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
|
||||
@ -93,6 +91,7 @@ github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMT
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
go.opentelemetry.io/otel v1.7.0 h1:Z2lA3Tdch0iDcrhJXDIlC94XE+bxok1F9B+4Lz/lGsM=
|
||||
go.opentelemetry.io/otel v1.7.0/go.mod h1:5BdUoMIz5WEs0vt0CUEMtSSaTSHBBVwrhnz7+nrD5xk=
|
||||
go.opentelemetry.io/otel/sdk v1.7.0 h1:4OmStpcKVOfvDOgCt7UriAPtKolwIhxpnSNI/yK+1B0=
|
||||
@ -102,17 +101,22 @@ go.opentelemetry.io/otel/trace v1.7.0/go.mod h1:fzLSB9nqR2eXzxPXb2JW9IKE+ScyXA48
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
|
||||
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 h1:CIJ76btIcR3eFI5EgSo6k1qKw9KJexJuRLI9G7Hp5wE=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
@ -134,8 +138,10 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad h1:ntjMns5wyP/fN65tdBD4g8J5w8n015+iIIs9rtjXkY0=
|
||||
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
@ -143,12 +149,15 @@ golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9sn
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.3.8-0.20211105212822-18b340fc7af2 h1:GLw7MR8AfAG2GmGcmVgObFOHXYypgGjnGno25RDwn3Y=
|
||||
golang.org/x/text v0.3.8-0.20211105212822-18b340fc7af2/go.mod h1:EFNZuWvGYxIRUEX+K8UmCFwYmZjqcrnq15ZuVldZkZ0=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
|
||||
golang.org/x/tools v0.1.11 h1:loJ25fNOEhSXfHrpoGj91eCUThwdNX6u24rO1xnNteY=
|
||||
golang.org/x/tools v0.1.11/go.mod h1:SgwaegtQh8clINPpECJMqnxLv9I09HLqnW3RMqW0CA4=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
|
||||
@ -206,11 +206,14 @@ func (c cBuild) Index(ctx context.Context, in cBuildInput) (out *cBuildOutput, e
|
||||
}
|
||||
packCmd := fmt.Sprintf(`gf pack %s %s`, in.PackSrc, in.PackDst)
|
||||
mlog.Print(packCmd)
|
||||
gproc.MustShellRun(packCmd)
|
||||
gproc.MustShellRun(ctx, packCmd)
|
||||
}
|
||||
|
||||
// Injected information by building flags.
|
||||
ldFlags := fmt.Sprintf(`-X 'github.com/gogf/gf/v2/os/gbuild.builtInVarStr=%v'`, c.getBuildInVarStr(in))
|
||||
ldFlags := fmt.Sprintf(
|
||||
`-X 'github.com/gogf/gf/v2/os/gbuild.builtInVarStr=%v'`,
|
||||
c.getBuildInVarStr(ctx, in),
|
||||
)
|
||||
|
||||
// start building
|
||||
mlog.Print("start building...")
|
||||
@ -261,7 +264,7 @@ func (c cBuild) Index(ctx context.Context, in cBuildInput) (out *cBuildOutput, e
|
||||
// It's not necessary printing the complete command string.
|
||||
cmdShow, _ := gregex.ReplaceString(`\s+(-ldflags ".+?")\s+`, " ", cmd)
|
||||
mlog.Print(cmdShow)
|
||||
if result, err := gproc.ShellExec(cmd); err != nil {
|
||||
if result, err := gproc.ShellExec(ctx, cmd); err != nil {
|
||||
mlog.Printf(
|
||||
"failed to build, os:%s, arch:%s, error:\n%s\n\n%s\n",
|
||||
system, arch, gstr.Trim(result),
|
||||
@ -287,12 +290,12 @@ buildDone:
|
||||
|
||||
// getBuildInVarMapJson retrieves and returns the custom build-in variables in configuration
|
||||
// file as json.
|
||||
func (c cBuild) getBuildInVarStr(in cBuildInput) string {
|
||||
func (c cBuild) getBuildInVarStr(ctx context.Context, in cBuildInput) string {
|
||||
buildInVarMap := in.VarMap
|
||||
if buildInVarMap == nil {
|
||||
buildInVarMap = make(g.Map)
|
||||
}
|
||||
buildInVarMap["builtGit"] = c.getGitCommit()
|
||||
buildInVarMap["builtGit"] = c.getGitCommit(ctx)
|
||||
buildInVarMap["builtTime"] = gtime.Now().String()
|
||||
b, err := json.Marshal(buildInVarMap)
|
||||
if err != nil {
|
||||
@ -302,13 +305,13 @@ func (c cBuild) getBuildInVarStr(in cBuildInput) string {
|
||||
}
|
||||
|
||||
// getGitCommit retrieves and returns the latest git commit hash string if present.
|
||||
func (c cBuild) getGitCommit() string {
|
||||
func (c cBuild) getGitCommit(ctx context.Context) string {
|
||||
if gproc.SearchBinary("git") == "" {
|
||||
return ""
|
||||
}
|
||||
var (
|
||||
cmd = `git log -1 --format="%cd %H" --date=format:"%Y-%m-%d %H:%M:%S"`
|
||||
s, _ = gproc.ShellExec(cmd)
|
||||
s, _ = gproc.ShellExec(ctx, cmd)
|
||||
)
|
||||
mlog.Debug(cmd)
|
||||
if s != "" {
|
||||
|
||||
@ -88,14 +88,14 @@ func (c cDocker) Index(ctx context.Context, in cDockerInput) (out *cDockerOutput
|
||||
// Binary build.
|
||||
in.Build += " --exit"
|
||||
if in.Main != "" {
|
||||
if err = gproc.ShellRun(fmt.Sprintf(`gf build %s %s`, in.Main, in.Build)); err != nil {
|
||||
if err = gproc.ShellRun(ctx, fmt.Sprintf(`gf build %s %s`, in.Main, in.Build)); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Shell executing.
|
||||
if in.Shell != "" && gfile.Exists(in.Shell) {
|
||||
if err = c.exeDockerShell(in.Shell); err != nil {
|
||||
if err = c.exeDockerShell(ctx, in.Shell); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
@ -116,7 +116,7 @@ func (c cDocker) Index(ctx context.Context, in cDockerInput) (out *cDockerOutput
|
||||
}
|
||||
for i, dockerTag := range dockerTags {
|
||||
if i > 0 {
|
||||
err = gproc.ShellRun(fmt.Sprintf(`docker tag %s %s`, dockerTagBase, dockerTag))
|
||||
err = gproc.ShellRun(ctx, fmt.Sprintf(`docker tag %s %s`, dockerTagBase, dockerTag))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@ -130,7 +130,7 @@ func (c cDocker) Index(ctx context.Context, in cDockerInput) (out *cDockerOutput
|
||||
if in.Extra != "" {
|
||||
dockerBuildOptions = fmt.Sprintf(`%s %s`, dockerBuildOptions, in.Extra)
|
||||
}
|
||||
err = gproc.ShellRun(fmt.Sprintf(`docker build -f %s . %s`, in.File, dockerBuildOptions))
|
||||
err = gproc.ShellRun(ctx, fmt.Sprintf(`docker build -f %s . %s`, in.File, dockerBuildOptions))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@ -144,7 +144,7 @@ func (c cDocker) Index(ctx context.Context, in cDockerInput) (out *cDockerOutput
|
||||
if dockerTag == "" {
|
||||
continue
|
||||
}
|
||||
err = gproc.ShellRun(fmt.Sprintf(`docker push %s`, dockerTag))
|
||||
err = gproc.ShellRun(ctx, fmt.Sprintf(`docker push %s`, dockerTag))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@ -152,10 +152,10 @@ func (c cDocker) Index(ctx context.Context, in cDockerInput) (out *cDockerOutput
|
||||
return
|
||||
}
|
||||
|
||||
func (c cDocker) exeDockerShell(shellFilePath string) error {
|
||||
func (c cDocker) exeDockerShell(ctx context.Context, shellFilePath string) error {
|
||||
if gfile.ExtName(shellFilePath) == "sh" && runtime.GOOS == "windows" {
|
||||
mlog.Debugf(`ignore shell file "%s", as it cannot be run on windows system`, shellFilePath)
|
||||
return nil
|
||||
}
|
||||
return gproc.ShellRun(gfile.GetContents(shellFilePath))
|
||||
return gproc.ShellRun(ctx, gfile.GetContents(shellFilePath))
|
||||
}
|
||||
|
||||
@ -26,7 +26,7 @@ type cEnvInput struct {
|
||||
type cEnvOutput struct{}
|
||||
|
||||
func (c cEnv) Index(ctx context.Context, in cEnvInput) (out *cEnvOutput, err error) {
|
||||
result, err := gproc.ShellExec("go env")
|
||||
result, err := gproc.ShellExec(ctx, "go env")
|
||||
if err != nil {
|
||||
mlog.Fatal(err)
|
||||
}
|
||||
|
||||
@ -11,6 +11,10 @@ var (
|
||||
|
||||
type cGen struct {
|
||||
g.Meta `name:"gen" brief:"{cGenBrief}" dc:"{cGenDc}"`
|
||||
cGenDao
|
||||
cGenPb
|
||||
cGenPbEntity
|
||||
cGenService
|
||||
}
|
||||
|
||||
const (
|
||||
|
||||
@ -22,7 +22,7 @@ import (
|
||||
_ "github.com/gogf/gf/contrib/drivers/mssql/v2"
|
||||
_ "github.com/gogf/gf/contrib/drivers/mysql/v2"
|
||||
_ "github.com/gogf/gf/contrib/drivers/pgsql/v2"
|
||||
_ "github.com/gogf/gf/contrib/drivers/sqlite/v2"
|
||||
//_ "github.com/gogf/gf/contrib/drivers/sqlite/v2"
|
||||
//_ "github.com/gogf/gf/contrib/drivers/oracle/v2"
|
||||
)
|
||||
|
||||
@ -138,6 +138,7 @@ func init() {
|
||||
}
|
||||
|
||||
type (
|
||||
cGenDao struct{}
|
||||
cGenDaoInput struct {
|
||||
g.Meta `name:"dao" config:"{cGenDaoConfig}" usage:"{cGenDaoUsage}" brief:"{cGenDaoBrief}" eg:"{cGenDaoEg}" ad:"{cGenDaoAd}"`
|
||||
Path string `name:"path" short:"p" brief:"{cGenDaoBriefPath}" d:"internal"`
|
||||
@ -170,7 +171,7 @@ type (
|
||||
}
|
||||
)
|
||||
|
||||
func (c cGen) Dao(ctx context.Context, in cGenDaoInput) (out *cGenDaoOutput, err error) {
|
||||
func (c cGenDao) Dao(ctx context.Context, in cGenDaoInput) (out *cGenDaoOutput, err error) {
|
||||
if g.Cfg().Available(ctx) {
|
||||
v := g.Cfg().MustGet(ctx, cGenDaoConfig)
|
||||
if v.IsSlice() {
|
||||
|
||||
@ -13,13 +13,14 @@ import (
|
||||
)
|
||||
|
||||
type (
|
||||
cGenPb struct{}
|
||||
cGenPbInput struct {
|
||||
g.Meta `name:"pb" brief:"parse proto files and generate protobuf go files"`
|
||||
}
|
||||
cGenPbOutput struct{}
|
||||
)
|
||||
|
||||
func (c cGen) Pb(ctx context.Context, in cGenPbInput) (out *cGenPbOutput, err error) {
|
||||
func (c cGenPb) Pb(ctx context.Context, in cGenPbInput) (out *cGenPbOutput, err error) {
|
||||
// Necessary check.
|
||||
if gproc.SearchBinary("protoc") == "" {
|
||||
mlog.Fatalf(`command "protoc" not found in your environment, please install protoc first to proceed this command`)
|
||||
@ -56,7 +57,7 @@ func (c cGen) Pb(ctx context.Context, in cGenPbInput) (out *cGenPbOutput, err er
|
||||
parsingCommand += " -I" + goPathSrc
|
||||
}
|
||||
mlog.Print(parsingCommand)
|
||||
if output, err := gproc.ShellExec(parsingCommand); err != nil {
|
||||
if output, err := gproc.ShellExec(ctx, parsingCommand); err != nil {
|
||||
mlog.Print(output)
|
||||
mlog.Fatal(err)
|
||||
}
|
||||
|
||||
@ -19,6 +19,29 @@ import (
|
||||
"github.com/olekukonko/tablewriter"
|
||||
)
|
||||
|
||||
type (
|
||||
cGenPbEntity struct{}
|
||||
cGenPbEntityInput struct {
|
||||
g.Meta `name:"pbentity" config:"{cGenPbEntityConfig}" brief:"{cGenPbEntityBrief}" eg:"{cGenPbEntityEg}" ad:"{cGenPbEntityAd}"`
|
||||
Path string `name:"path" short:"p" brief:"{cGenPbEntityBriefPath}"`
|
||||
Package string `name:"package" short:"k" brief:"{cGenPbEntityBriefPackage}"`
|
||||
Link string `name:"link" short:"l" brief:"{cGenPbEntityBriefLink}"`
|
||||
Tables string `name:"tables" short:"t" brief:"{cGenPbEntityBriefTables}"`
|
||||
Prefix string `name:"prefix" short:"f" brief:"{cGenPbEntityBriefPrefix}"`
|
||||
RemovePrefix string `name:"removePrefix" short:"r" brief:"{cGenPbEntityBriefRemovePrefix}"`
|
||||
NameCase string `name:"nameCase" short:"n" brief:"{cGenPbEntityBriefNameCase}" d:"Camel"`
|
||||
JsonCase string `name:"jsonCase" short:"j" brief:"{cGenPbEntityBriefJsonCase}" d:"CamelLower"`
|
||||
Option string `name:"option" short:"o" brief:"{cGenPbEntityBriefOption}"`
|
||||
}
|
||||
cGenPbEntityOutput struct{}
|
||||
|
||||
cGenPbEntityInternalInput struct {
|
||||
cGenPbEntityInput
|
||||
TableName string // TableName specifies the table name of the table.
|
||||
NewTableName string // NewTableName specifies the prefix-stripped name of the table.
|
||||
}
|
||||
)
|
||||
|
||||
const (
|
||||
cGenPbEntityConfig = `gfcli.gen.pbentity`
|
||||
cGenPbEntityBrief = `generate entity message files in protobuf3 format`
|
||||
@ -83,28 +106,6 @@ set it to "none" to ignore json tag generating.
|
||||
`
|
||||
)
|
||||
|
||||
type (
|
||||
cGenPbEntityInput struct {
|
||||
g.Meta `name:"pbentity" config:"{cGenPbEntityConfig}" brief:"{cGenPbEntityBrief}" eg:"{cGenPbEntityEg}" ad:"{cGenPbEntityAd}"`
|
||||
Path string `name:"path" short:"p" brief:"{cGenPbEntityBriefPath}"`
|
||||
Package string `name:"package" short:"k" brief:"{cGenPbEntityBriefPackage}"`
|
||||
Link string `name:"link" short:"l" brief:"{cGenPbEntityBriefLink}"`
|
||||
Tables string `name:"tables" short:"t" brief:"{cGenPbEntityBriefTables}"`
|
||||
Prefix string `name:"prefix" short:"f" brief:"{cGenPbEntityBriefPrefix}"`
|
||||
RemovePrefix string `name:"removePrefix" short:"r" brief:"{cGenPbEntityBriefRemovePrefix}"`
|
||||
NameCase string `name:"nameCase" short:"n" brief:"{cGenPbEntityBriefNameCase}" d:"Camel"`
|
||||
JsonCase string `name:"jsonCase" short:"j" brief:"{cGenPbEntityBriefJsonCase}" d:"CamelLower"`
|
||||
Option string `name:"option" short:"o" brief:"{cGenPbEntityBriefOption}"`
|
||||
}
|
||||
cGenPbEntityOutput struct{}
|
||||
|
||||
cGenPbEntityInternalInput struct {
|
||||
cGenPbEntityInput
|
||||
TableName string // TableName specifies the table name of the table.
|
||||
NewTableName string // NewTableName specifies the prefix-stripped name of the table.
|
||||
}
|
||||
)
|
||||
|
||||
func init() {
|
||||
gtag.Sets(g.MapStrStr{
|
||||
`cGenPbEntityConfig`: cGenPbEntityConfig,
|
||||
@ -124,7 +125,7 @@ func init() {
|
||||
})
|
||||
}
|
||||
|
||||
func (c cGen) PbEntity(ctx context.Context, in cGenPbEntityInput) (out *cGenPbEntityOutput, err error) {
|
||||
func (c cGenPbEntity) PbEntity(ctx context.Context, in cGenPbEntityInput) (out *cGenPbEntityOutput, err error) {
|
||||
var (
|
||||
config = g.Cfg()
|
||||
)
|
||||
|
||||
@ -18,15 +18,16 @@ import (
|
||||
)
|
||||
|
||||
type (
|
||||
cGenService struct{}
|
||||
cGenServiceInput struct {
|
||||
g.Meta `name:"service" config:"gfcli.gen.service" brief:"parse struct and associated functions from packages to generate service go file"`
|
||||
SrcFolder string `short:"s" name:"srcFolder" brief:"source folder path to be parsed. default: internal/logic" d:"internal/logic"`
|
||||
DstFolder string `short:"d" name:"dstFolder" brief:"destination folder path storing automatically generated go files. default: internal/service" d:"internal/service"`
|
||||
WatchFile string `short:"w" name:"watchFile" brief:"used in file watcher, it generates service go files only if given file is under srcFolder"`
|
||||
StPattern string `short:"a" name:"stPattern" brief:"regular expression matching struct name for generating service. default: s([A-Z]\\w+)" d:"s([A-Z]\\w+)"`
|
||||
Packages string `short:"p" name:"packages" brief:"produce go files only for given source packages, multiple packages joined with char ','"`
|
||||
ImportPrefix string `short:"i" name:"importPrefix" brief:"custom import prefix to calculate import path for generated importing go file of logic"`
|
||||
OverWrite bool `short:"o" name:"overwrite" brief:"overwrite service go files that already exist in generating folder. default: true" d:"true" orphan:"true"`
|
||||
SrcFolder string `short:"s" name:"srcFolder" brief:"source folder path to be parsed. default: internal/logic" d:"internal/logic"`
|
||||
DstFolder string `short:"d" name:"dstFolder" brief:"destination folder path storing automatically generated go files. default: internal/service" d:"internal/service"`
|
||||
WatchFile string `short:"w" name:"watchFile" brief:"used in file watcher, it generates service go files only if given file is under srcFolder"`
|
||||
StPattern string `short:"a" name:"stPattern" brief:"regular expression matching struct name for generating service. default: s([A-Z]\\\\w+)" d:"s([A-Z]\\w+)"`
|
||||
Packages []string `short:"p" name:"packages" brief:"produce go files only for given source packages"`
|
||||
ImportPrefix string `short:"i" name:"importPrefix" brief:"custom import prefix to calculate import path for generated importing go file of logic"`
|
||||
OverWrite bool `short:"o" name:"overwrite" brief:"overwrite service go files that already exist in generating folder. default: true" d:"true" orphan:"true"`
|
||||
}
|
||||
cGenServiceOutput struct{}
|
||||
)
|
||||
@ -35,7 +36,7 @@ const (
|
||||
genServiceFileLockSeconds = 10
|
||||
)
|
||||
|
||||
func (c cGen) Service(ctx context.Context, in cGenServiceInput) (out *cGenServiceOutput, err error) {
|
||||
func (c cGenService) Service(ctx context.Context, in cGenServiceInput) (out *cGenServiceOutput, err error) {
|
||||
// File lock to avoid multiple processes.
|
||||
var (
|
||||
flockFilePath = gfile.Temp("gf.cli.gen.service.lock")
|
||||
@ -65,7 +66,7 @@ func (c cGen) Service(ctx context.Context, in cGenServiceInput) (out *cGenServic
|
||||
)
|
||||
mlog.Debug("watchFileDir:", watchFileDir)
|
||||
mlog.Debug("logicFolderDir:", srcFolderDir)
|
||||
if !gstr.HasSuffix(srcFolderDir, in.SrcFolder) {
|
||||
if !gstr.HasSuffix(gstr.Replace(srcFolderDir, `\`, `/`), in.SrcFolder) {
|
||||
mlog.Printf(`ignore watch file "%s", not in source path "%s"`, in.WatchFile, in.SrcFolder)
|
||||
return
|
||||
}
|
||||
@ -79,7 +80,7 @@ func (c cGen) Service(ctx context.Context, in cGenServiceInput) (out *cGenServic
|
||||
`%s gen service -packages=%s`,
|
||||
gfile.SelfName(), gfile.Basename(watchFileDir),
|
||||
)
|
||||
err = gproc.ShellRun(command)
|
||||
err = gproc.ShellRun(ctx, command)
|
||||
return
|
||||
}
|
||||
|
||||
@ -101,13 +102,12 @@ func (c cGen) Service(ctx context.Context, in cGenServiceInput) (out *cGenServic
|
||||
}
|
||||
|
||||
var (
|
||||
isDirty bool
|
||||
files []string
|
||||
fileContent string
|
||||
matches [][]string
|
||||
importSrcPackages []string
|
||||
inputPackages = gstr.SplitAndTrim(in.Packages, ",")
|
||||
dstPackageName = gstr.ToLower(gfile.Basename(in.DstFolder))
|
||||
isDirty bool
|
||||
files []string
|
||||
fileContent string
|
||||
initImportSrcPackages []string
|
||||
inputPackages = in.Packages
|
||||
dstPackageName = gstr.ToLower(gfile.Basename(in.DstFolder))
|
||||
)
|
||||
srcFolders, err := gfile.ScanDir(in.SrcFolder, "*", false)
|
||||
if err != nil {
|
||||
@ -125,56 +125,25 @@ func (c cGen) Service(ctx context.Context, in cGenServiceInput) (out *cGenServic
|
||||
}
|
||||
var (
|
||||
// StructName => FunctionDefinitions
|
||||
srcPkgInterfaceMap = make(map[string]*garray.StrArray)
|
||||
srcPkgInterfaceFuncArray *garray.StrArray
|
||||
ok bool
|
||||
srcPkgInterfaceMap = make(map[string]*garray.StrArray)
|
||||
srcImportedPackages = garray.NewSortedStrArray().SetUnique(true)
|
||||
ok bool
|
||||
)
|
||||
for _, file := range files {
|
||||
fileContent = gfile.GetContents(file)
|
||||
matches, err = gregex.MatchAllString(`func \((.+?)\) ([\s\S]+?) {`, fileContent)
|
||||
// Calculate imported packages of source go files.
|
||||
err = c.calculateImportedPackages(fileContent, srcImportedPackages)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, match := range matches {
|
||||
var (
|
||||
structName string
|
||||
structMatch []string
|
||||
funcReceiver = gstr.Trim(match[1])
|
||||
receiverArray = gstr.SplitAndTrim(funcReceiver, " ")
|
||||
functionHead = gstr.Trim(gstr.Replace(match[2], "\n", ""))
|
||||
)
|
||||
if len(receiverArray) > 1 {
|
||||
structName = receiverArray[1]
|
||||
} else {
|
||||
structName = receiverArray[0]
|
||||
}
|
||||
structName = gstr.Trim(structName, "*")
|
||||
|
||||
// Xxx(\n ctx context.Context, req *v1.XxxReq,\n) -> Xxx(ctx context.Context, req *v1.XxxReq)
|
||||
functionHead = gstr.Replace(functionHead, `,)`, `)`)
|
||||
functionHead, _ = gregex.ReplaceString(`\(\s+`, `(`, functionHead)
|
||||
functionHead, _ = gregex.ReplaceString(`\s{2,}`, ` `, functionHead)
|
||||
if !gstr.IsLetterUpper(functionHead[0]) {
|
||||
continue
|
||||
}
|
||||
if structMatch, err = gregex.MatchString(in.StPattern, structName); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(structMatch) < 1 {
|
||||
continue
|
||||
}
|
||||
structName = gstr.CaseCamel(structMatch[1])
|
||||
if srcPkgInterfaceFuncArray, ok = srcPkgInterfaceMap[structName]; !ok {
|
||||
srcPkgInterfaceMap[structName] = garray.NewStrArray()
|
||||
srcPkgInterfaceFuncArray = srcPkgInterfaceMap[structName]
|
||||
}
|
||||
// Remove package name calls of `dstPackageName` in produced codes.
|
||||
functionHead, _ = gregex.ReplaceString(fmt.Sprintf(`\*{0,1}%s\.`, dstPackageName), ``, functionHead)
|
||||
srcPkgInterfaceFuncArray.Append(functionHead)
|
||||
// Calculate functions and interfaces for service generating.
|
||||
err = c.calculateInterfaceFunctions(in, fileContent, srcPkgInterfaceMap, dstPackageName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
importSrcPackages = append(
|
||||
importSrcPackages,
|
||||
initImportSrcPackages = append(
|
||||
initImportSrcPackages,
|
||||
fmt.Sprintf(`%s/%s`, in.ImportPrefix, gfile.Basename(srcFolder)),
|
||||
)
|
||||
// Ignore source packages if input packages given.
|
||||
@ -186,7 +155,7 @@ func (c cGen) Service(ctx context.Context, in cGenServiceInput) (out *cGenServic
|
||||
continue
|
||||
}
|
||||
// Generating go files for service.
|
||||
if ok, err = c.generateServiceFiles(in, srcPkgInterfaceMap, dstPackageName); err != nil {
|
||||
if ok, err = c.generateServiceFiles(in, srcPkgInterfaceMap, srcImportedPackages.Slice(), dstPackageName); err != nil {
|
||||
return
|
||||
}
|
||||
if ok {
|
||||
@ -196,17 +165,13 @@ func (c cGen) Service(ctx context.Context, in cGenServiceInput) (out *cGenServic
|
||||
|
||||
if isDirty {
|
||||
// Generate initialization go file.
|
||||
if len(importSrcPackages) > 0 {
|
||||
if err = c.generateInitializationFile(in, importSrcPackages); err != nil {
|
||||
if len(initImportSrcPackages) > 0 {
|
||||
if err = c.generateInitializationFile(in, initImportSrcPackages); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Go imports updating.
|
||||
mlog.Printf(`goimports go files in "%s", it may take seconds...`, in.DstFolder)
|
||||
utils.GoImports(in.DstFolder)
|
||||
|
||||
// Replica v1 to v2 for GoFrame.
|
||||
// Replace v1 to v2 for GoFrame.
|
||||
if err = c.replaceGeneratedServiceContentGFV2(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -218,13 +183,86 @@ func (c cGen) Service(ctx context.Context, in cGenServiceInput) (out *cGenServic
|
||||
return
|
||||
}
|
||||
|
||||
func (c cGen) generateServiceFiles(
|
||||
in cGenServiceInput, srcPkgInterfaceMap map[string]*garray.StrArray, dstPackageName string,
|
||||
func (c cGenService) calculateImportedPackages(fileContent string, srcImportedPackages *garray.SortedStrArray) (err error) {
|
||||
var match []string
|
||||
match, err = gregex.MatchString(`\s+import\s+\(([\s\S]+?)\)`, fileContent)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(match) < 2 {
|
||||
return nil
|
||||
}
|
||||
importPart := gstr.Trim(match[1])
|
||||
srcImportedPackages.Append(gstr.SplitAndTrim(importPart, "\n")...)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c cGenService) calculateInterfaceFunctions(
|
||||
in cGenServiceInput, fileContent string, srcPkgInterfaceMap map[string]*garray.StrArray, dstPackageName string,
|
||||
) (err error) {
|
||||
var (
|
||||
ok bool
|
||||
matches [][]string
|
||||
srcPkgInterfaceFuncArray *garray.StrArray
|
||||
)
|
||||
matches, err = gregex.MatchAllString(`func \((.+?)\) ([\s\S]+?) {`, fileContent)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, match := range matches {
|
||||
var (
|
||||
structName string
|
||||
structMatch []string
|
||||
funcReceiver = gstr.Trim(match[1])
|
||||
receiverArray = gstr.SplitAndTrim(funcReceiver, " ")
|
||||
functionHead = gstr.Trim(gstr.Replace(match[2], "\n", ""))
|
||||
)
|
||||
if len(receiverArray) > 1 {
|
||||
structName = receiverArray[1]
|
||||
} else {
|
||||
structName = receiverArray[0]
|
||||
}
|
||||
structName = gstr.Trim(structName, "*")
|
||||
|
||||
// Xxx(\n ctx context.Context, req *v1.XxxReq,\n) -> Xxx(ctx context.Context, req *v1.XxxReq)
|
||||
functionHead = gstr.Replace(functionHead, `,)`, `)`)
|
||||
functionHead, _ = gregex.ReplaceString(`\(\s+`, `(`, functionHead)
|
||||
functionHead, _ = gregex.ReplaceString(`\s{2,}`, ` `, functionHead)
|
||||
if !gstr.IsLetterUpper(functionHead[0]) {
|
||||
continue
|
||||
}
|
||||
if structMatch, err = gregex.MatchString(in.StPattern, structName); err != nil {
|
||||
return err
|
||||
}
|
||||
if len(structMatch) < 1 {
|
||||
continue
|
||||
}
|
||||
structName = gstr.CaseCamel(structMatch[1])
|
||||
if srcPkgInterfaceFuncArray, ok = srcPkgInterfaceMap[structName]; !ok {
|
||||
srcPkgInterfaceMap[structName] = garray.NewStrArray()
|
||||
srcPkgInterfaceFuncArray = srcPkgInterfaceMap[structName]
|
||||
}
|
||||
// Remove package name calls of `dstPackageName` in produced codes.
|
||||
functionHead, _ = gregex.ReplaceString(fmt.Sprintf(`\*{0,1}%s\.`, dstPackageName), ``, functionHead)
|
||||
srcPkgInterfaceFuncArray.Append(functionHead)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c cGenService) generateServiceFiles(
|
||||
in cGenServiceInput,
|
||||
srcPkgInterfaceMap map[string]*garray.StrArray,
|
||||
srcImportedPackages []string,
|
||||
dstPackageName string,
|
||||
) (ok bool, err error) {
|
||||
srcImportedPackagesContent := fmt.Sprintf(
|
||||
"import (\n%s\n)", gstr.Join(srcImportedPackages, "\n"),
|
||||
)
|
||||
for structName, funcArray := range srcPkgInterfaceMap {
|
||||
var (
|
||||
filePath = gfile.Join(in.DstFolder, gstr.ToLower(structName)+".go")
|
||||
generatedContent = gstr.ReplaceByMap(consts.TemplateGenServiceContent, g.MapStrStr{
|
||||
"{Imports}": srcImportedPackagesContent,
|
||||
"{StructName}": structName,
|
||||
"{PackageName}": dstPackageName,
|
||||
"{FuncDefinition}": funcArray.Join("\n\t"),
|
||||
@ -250,7 +288,7 @@ func (c cGen) generateServiceFiles(
|
||||
}
|
||||
|
||||
// isToGenerateServiceGoFile checks and returns whether the service content dirty.
|
||||
func (c cGen) isToGenerateServiceGoFile(filePath string, funcArray *garray.StrArray) bool {
|
||||
func (c cGenService) isToGenerateServiceGoFile(filePath string, funcArray *garray.StrArray) bool {
|
||||
if !utils.IsFileDoNotEdit(filePath) {
|
||||
mlog.Debugf(`ignore file as it is manually maintained: %s`, filePath)
|
||||
return false
|
||||
@ -280,7 +318,7 @@ func (c cGen) isToGenerateServiceGoFile(filePath string, funcArray *garray.StrAr
|
||||
return false
|
||||
}
|
||||
|
||||
func (c cGen) generateInitializationFile(in cGenServiceInput, importSrcPackages []string) (err error) {
|
||||
func (c cGenService) generateInitializationFile(in cGenServiceInput, importSrcPackages []string) (err error) {
|
||||
var (
|
||||
srcPackageName = gstr.ToLower(gfile.Basename(in.SrcFolder))
|
||||
srcFilePath = gfile.Join(in.SrcFolder, srcPackageName+".go")
|
||||
@ -306,7 +344,7 @@ func (c cGen) generateInitializationFile(in cGenServiceInput, importSrcPackages
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c cGen) replaceGeneratedServiceContentGFV2(in cGenServiceInput) (err error) {
|
||||
func (c cGenService) replaceGeneratedServiceContentGFV2(in cGenServiceInput) (err error) {
|
||||
return gfile.ReplaceDirFunc(func(path, content string) string {
|
||||
if gstr.Contains(content, `"github.com/gogf/gf`) && !gstr.Contains(content, `"github.com/gogf/gf/v2`) {
|
||||
content = gstr.Replace(content, `"github.com/gogf/gf"`, `"github.com/gogf/gf/v2"`)
|
||||
|
||||
@ -10,6 +10,7 @@ import (
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gcmd"
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
"github.com/gogf/gf/v2/os/gproc"
|
||||
"github.com/gogf/gf/v2/os/gres"
|
||||
"github.com/gogf/gf/v2/util/gtag"
|
||||
)
|
||||
@ -48,7 +49,8 @@ func init() {
|
||||
type cInitInput struct {
|
||||
g.Meta `name:"init"`
|
||||
Name string `name:"NAME" arg:"true" v:"required" brief:"{cInitNameBrief}"`
|
||||
Mono bool `name:"mono" short:"m" brief:"initialize a mono-repo instead a single-repo" orphan:"true"`
|
||||
Mono bool `name:"mono" short:"m" brief:"initialize a mono-repo instead a single-repo" orphan:"true"`
|
||||
Update bool `name:"update" short:"u" brief:"update to the latest goframe version" orphan:"true"`
|
||||
}
|
||||
type cInitOutput struct{}
|
||||
|
||||
@ -89,6 +91,18 @@ func (c cInit) Index(ctx context.Context, in cInitInput) (out *cInitOutput, err
|
||||
return
|
||||
}
|
||||
|
||||
// Update the GoFrame version.
|
||||
if in.Update {
|
||||
mlog.Print("update goframe...")
|
||||
updateCommand := `go get -u github.com/gogf/gf/v2@latest`
|
||||
if in.Name != "." {
|
||||
updateCommand = fmt.Sprintf(`cd %s && %s`, in.Name, updateCommand)
|
||||
}
|
||||
if err = gproc.ShellRun(ctx, updateCommand); err != nil {
|
||||
mlog.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
mlog.Print("initialization done! ")
|
||||
if !in.Mono {
|
||||
enjoyCommand := `gf run main.go`
|
||||
|
||||
@ -99,17 +99,17 @@ func (c cRun) Index(ctx context.Context, in cRunInput) (out *cRunOutput, err err
|
||||
gtimer.SetTimeout(ctx, 1500*gtime.MS, func(ctx context.Context) {
|
||||
defer dirty.Set(false)
|
||||
mlog.Printf(`go file changes: %s`, event.String())
|
||||
app.Run()
|
||||
app.Run(ctx)
|
||||
})
|
||||
})
|
||||
if err != nil {
|
||||
mlog.Fatal(err)
|
||||
}
|
||||
go app.Run()
|
||||
go app.Run(ctx)
|
||||
select {}
|
||||
}
|
||||
|
||||
func (app *cRunApp) Run() {
|
||||
func (app *cRunApp) Run(ctx context.Context) {
|
||||
// Rebuild and run the codes.
|
||||
renamePath := ""
|
||||
mlog.Printf("build: %s", app.File)
|
||||
@ -132,7 +132,7 @@ func (app *cRunApp) Run() {
|
||||
app.File,
|
||||
)
|
||||
mlog.Print(buildCommand)
|
||||
result, err := gproc.ShellExec(buildCommand)
|
||||
result, err := gproc.ShellExec(ctx, buildCommand)
|
||||
if err != nil {
|
||||
mlog.Printf("build error: \n%s%s", result, err.Error())
|
||||
return
|
||||
@ -154,7 +154,7 @@ func (app *cRunApp) Run() {
|
||||
} else {
|
||||
process = gproc.NewProcessCmd(outputPath, gstr.SplitAndTrim(" ", app.Args))
|
||||
}
|
||||
if pid, err := process.Start(); err != nil {
|
||||
if pid, err := process.Start(ctx); err != nil {
|
||||
mlog.Printf("build running error: %s", err.Error())
|
||||
} else {
|
||||
mlog.Printf("build running pid: %d", pid)
|
||||
|
||||
@ -7,6 +7,8 @@ const TemplateGenServiceContent = `
|
||||
|
||||
package {PackageName}
|
||||
|
||||
{Imports}
|
||||
|
||||
type I{StructName} interface {
|
||||
{FuncDefinition}
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -1,41 +1,17 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/consts"
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/utility/mlog"
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
"github.com/gogf/gf/v2/os/gproc"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
"golang.org/x/tools/imports"
|
||||
)
|
||||
|
||||
var (
|
||||
gofmtPath = gproc.SearchBinaryPath("gofmt") // gofmtPath is the binary path of command `gofmt`.
|
||||
goimportsPath = gproc.SearchBinaryPath("goimports") // gofmtPath is the binary path of command `goimports`.
|
||||
)
|
||||
|
||||
// GoFmt formats the source file using command `gofmt -w -s PATH`.
|
||||
// GoFmt formats the source file.
|
||||
func GoFmt(path string) {
|
||||
if gofmtPath == "" {
|
||||
mlog.Fatal(`command "gofmt" not found`)
|
||||
}
|
||||
var command = fmt.Sprintf(`%s -w %s`, gofmtPath, path)
|
||||
result, err := gproc.ShellExec(command)
|
||||
if err != nil {
|
||||
mlog.Fatalf(`error executing command "%s": %s`, command, result)
|
||||
}
|
||||
}
|
||||
|
||||
// GoImports formats the source file using command `goimports -w PATH`.
|
||||
func GoImports(path string) {
|
||||
if goimportsPath == "" {
|
||||
mlog.Fatal(`command "goimports" not found`)
|
||||
}
|
||||
var command = fmt.Sprintf(`%s -w %s`, goimportsPath, path)
|
||||
result, err := gproc.ShellExec(command)
|
||||
if err != nil {
|
||||
mlog.Fatalf(`error executing command "%s": %s`, command, result)
|
||||
if err := doGoFmt(path, true); err != nil {
|
||||
mlog.Fatalf(`error format "%s" go files: %v`, path, err)
|
||||
}
|
||||
}
|
||||
|
||||
@ -46,3 +22,33 @@ func IsFileDoNotEdit(filePath string) bool {
|
||||
}
|
||||
return gstr.Contains(gfile.GetContents(filePath), consts.DoNotEditKey)
|
||||
}
|
||||
|
||||
// doGoFmt format go file and adds or removes import statements as necessary.
|
||||
func doGoFmt(path string, formatOnly ...bool) error {
|
||||
var genOpt *imports.Options
|
||||
if len(formatOnly) > 0 {
|
||||
genOpt = &imports.Options{
|
||||
Comments: true,
|
||||
TabIndent: true,
|
||||
TabWidth: 8,
|
||||
FormatOnly: true,
|
||||
}
|
||||
}
|
||||
replaceFunc := func(path, content string) string {
|
||||
res, err := imports.Process(path, []byte(content), genOpt)
|
||||
if err != nil {
|
||||
mlog.Printf(`pretty go file "%s" failed: %v`, path, err)
|
||||
return content
|
||||
}
|
||||
return string(res)
|
||||
}
|
||||
// File format.
|
||||
if gfile.IsFile(path) {
|
||||
if gfile.ExtName(path) != "go" {
|
||||
return nil
|
||||
}
|
||||
return gfile.ReplaceFileFunc(replaceFunc, path)
|
||||
}
|
||||
// Folder format.
|
||||
return gfile.ReplaceDirFunc(replaceFunc, path, "*.go", true)
|
||||
}
|
||||
|
||||
@ -14,6 +14,7 @@ import (
|
||||
|
||||
"github.com/gogf/gf/v2/errors/gcode"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/internal/deepcopy"
|
||||
"github.com/gogf/gf/v2/internal/empty"
|
||||
"github.com/gogf/gf/v2/internal/json"
|
||||
"github.com/gogf/gf/v2/internal/rwmutex"
|
||||
@ -820,3 +821,14 @@ func (a *Array) Walk(f func(value interface{}) interface{}) *Array {
|
||||
func (a *Array) IsEmpty() bool {
|
||||
return a.Len() == 0
|
||||
}
|
||||
|
||||
// DeepCopy implements interface for deep copy of current type.
|
||||
func (a *Array) DeepCopy() interface{} {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
newSlice := make([]interface{}, len(a.array))
|
||||
for i, v := range a.array {
|
||||
newSlice[i] = deepcopy.Copy(v)
|
||||
}
|
||||
return NewArrayFrom(newSlice, a.mu.IsSafe())
|
||||
}
|
||||
|
||||
@ -799,3 +799,12 @@ func (a *IntArray) Walk(f func(value int) int) *IntArray {
|
||||
func (a *IntArray) IsEmpty() bool {
|
||||
return a.Len() == 0
|
||||
}
|
||||
|
||||
// DeepCopy implements interface for deep copy of current type.
|
||||
func (a *IntArray) DeepCopy() interface{} {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
newSlice := make([]int, len(a.array))
|
||||
copy(newSlice, a.array)
|
||||
return NewIntArrayFrom(newSlice, a.mu.IsSafe())
|
||||
}
|
||||
|
||||
@ -812,3 +812,12 @@ func (a *StrArray) Walk(f func(value string) string) *StrArray {
|
||||
func (a *StrArray) IsEmpty() bool {
|
||||
return a.Len() == 0
|
||||
}
|
||||
|
||||
// DeepCopy implements interface for deep copy of current type.
|
||||
func (a *StrArray) DeepCopy() interface{} {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
newSlice := make([]string, len(a.array))
|
||||
copy(newSlice, a.array)
|
||||
return NewStrArrayFrom(newSlice, a.mu.IsSafe())
|
||||
}
|
||||
|
||||
@ -12,6 +12,7 @@ import (
|
||||
"math"
|
||||
"sort"
|
||||
|
||||
"github.com/gogf/gf/v2/internal/deepcopy"
|
||||
"github.com/gogf/gf/v2/internal/empty"
|
||||
"github.com/gogf/gf/v2/internal/json"
|
||||
"github.com/gogf/gf/v2/internal/rwmutex"
|
||||
@ -796,3 +797,14 @@ func (a *SortedArray) getComparator() func(a, b interface{}) int {
|
||||
}
|
||||
return a.comparator
|
||||
}
|
||||
|
||||
// DeepCopy implements interface for deep copy of current type.
|
||||
func (a *SortedArray) DeepCopy() interface{} {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
newSlice := make([]interface{}, len(a.array))
|
||||
for i, v := range a.array {
|
||||
newSlice[i] = deepcopy.Copy(v)
|
||||
}
|
||||
return NewSortedArrayFrom(newSlice, a.comparator, a.mu.IsSafe())
|
||||
}
|
||||
|
||||
@ -744,3 +744,12 @@ func (a *SortedIntArray) getComparator() func(a, b int) int {
|
||||
}
|
||||
return a.comparator
|
||||
}
|
||||
|
||||
// DeepCopy implements interface for deep copy of current type.
|
||||
func (a *SortedIntArray) DeepCopy() interface{} {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
newSlice := make([]int, len(a.array))
|
||||
copy(newSlice, a.array)
|
||||
return NewSortedIntArrayFrom(newSlice, a.mu.IsSafe())
|
||||
}
|
||||
|
||||
@ -757,3 +757,12 @@ func (a *SortedStrArray) getComparator() func(a, b string) int {
|
||||
}
|
||||
return a.comparator
|
||||
}
|
||||
|
||||
// DeepCopy implements interface for deep copy of current type.
|
||||
func (a *SortedStrArray) DeepCopy() interface{} {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
newSlice := make([]string, len(a.array))
|
||||
copy(newSlice, a.array)
|
||||
return NewSortedStrArrayFrom(newSlice, a.mu.IsSafe())
|
||||
}
|
||||
|
||||
@ -46,6 +46,8 @@ func ExampleNew() {
|
||||
a.Set(0, 100)
|
||||
fmt.Println(a.Slice())
|
||||
|
||||
fmt.Println(a.At(0))
|
||||
|
||||
// Search item and return its index.
|
||||
fmt.Println(a.Search(5))
|
||||
|
||||
@ -66,6 +68,7 @@ func ExampleNew() {
|
||||
// false
|
||||
// [0 1 2 3 4 5 6 7 8 9 10 11]
|
||||
// [100 1 2 3 4 5 6 7 8 9 10 11]
|
||||
// 100
|
||||
// 5
|
||||
// [1 2 3 4 5 6 7 8 9 10 11]
|
||||
// [1 2 3 4 5 6 7 8 9 10 11]
|
||||
|
||||
@ -54,6 +54,14 @@ func ExampleNewIntArraySize() {
|
||||
// [10 20 15] 3 5
|
||||
}
|
||||
|
||||
func ExampleNewIntArrayRange() {
|
||||
s := garray.NewIntArrayRange(1, 5, 1)
|
||||
fmt.Println(s.Slice(), s.Len(), cap(s.Slice()))
|
||||
|
||||
// Output:
|
||||
// [1 2 3 4 5] 5 5
|
||||
}
|
||||
|
||||
func ExampleNewIntArrayFrom() {
|
||||
s := garray.NewIntArrayFrom(g.SliceInt{10, 20, 15, 30})
|
||||
fmt.Println(s.Slice(), s.Len(), cap(s.Slice()))
|
||||
@ -83,9 +91,12 @@ func ExampleIntArray_Get() {
|
||||
s := garray.NewIntArrayFrom(g.SliceInt{10, 20, 15, 30})
|
||||
sGet, sBool := s.Get(3)
|
||||
fmt.Println(sGet, sBool)
|
||||
sGet, sBool = s.Get(99)
|
||||
fmt.Println(sGet, sBool)
|
||||
|
||||
// Output:
|
||||
// 30 true
|
||||
// 0 false
|
||||
}
|
||||
|
||||
func ExampleIntArray_Set() {
|
||||
|
||||
@ -25,9 +25,24 @@ func Test_Array_Basic(t *testing.T) {
|
||||
array := garray.NewArrayFrom(expect)
|
||||
array2 := garray.NewArrayFrom(expect)
|
||||
array3 := garray.NewArrayFrom([]interface{}{})
|
||||
array4 := garray.NewArrayRange(1, 5, 1)
|
||||
|
||||
t.Assert(array.Slice(), expect)
|
||||
t.Assert(array.Interfaces(), expect)
|
||||
array.Set(0, 100)
|
||||
err := array.Set(0, 100)
|
||||
t.AssertNil(err)
|
||||
|
||||
err = array.Set(100, 100)
|
||||
t.AssertNE(err, nil)
|
||||
|
||||
t.Assert(array.IsEmpty(), false)
|
||||
|
||||
copyArray := array.DeepCopy()
|
||||
ca := copyArray.(*garray.Array)
|
||||
ca.Set(0, 1)
|
||||
cval, _ := ca.Get(0)
|
||||
val, _ := array.Get(0)
|
||||
t.AssertNE(cval, val)
|
||||
|
||||
v, ok := array.Get(0)
|
||||
t.Assert(v, 100)
|
||||
@ -37,6 +52,10 @@ func Test_Array_Basic(t *testing.T) {
|
||||
t.Assert(v, 1)
|
||||
t.Assert(ok, true)
|
||||
|
||||
v, ok = array.Get(4)
|
||||
t.Assert(v, nil)
|
||||
t.Assert(ok, false)
|
||||
|
||||
t.Assert(array.Search(100), 0)
|
||||
t.Assert(array3.Search(100), -1)
|
||||
t.Assert(array.Contains(100), true)
|
||||
@ -71,6 +90,12 @@ func Test_Array_Basic(t *testing.T) {
|
||||
array.InsertAfter(6, 400)
|
||||
t.Assert(array.Slice(), []interface{}{100, 200, 2, 2, 3, 300, 4, 400})
|
||||
t.Assert(array.Clear().Len(), 0)
|
||||
err = array.InsertBefore(99, 9900)
|
||||
t.AssertNE(err, nil)
|
||||
err = array.InsertAfter(99, 9900)
|
||||
t.AssertNE(err, nil)
|
||||
|
||||
t.Assert(array4.String(), "[1,2,3,4,5]")
|
||||
})
|
||||
}
|
||||
|
||||
@ -99,6 +124,11 @@ func TestArray_Unique(t *testing.T) {
|
||||
array := garray.NewArrayFrom(expect)
|
||||
t.Assert(array.Unique().Slice(), []interface{}{1, 2, 3, 4, 5})
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
expect := []interface{}{}
|
||||
array := garray.NewArrayFrom(expect)
|
||||
t.Assert(array.Unique().Slice(), []interface{}{})
|
||||
})
|
||||
}
|
||||
|
||||
func TestArray_PushAndPop(t *testing.T) {
|
||||
@ -382,6 +412,21 @@ func TestArray_Rand(t *testing.T) {
|
||||
t.Assert(a1.Contains(i1), true)
|
||||
t.Assert(a1.Len(), 4)
|
||||
})
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []interface{}{}
|
||||
array1 := garray.NewArrayFrom(a1)
|
||||
rand, found := array1.Rand()
|
||||
t.AssertNil(rand)
|
||||
t.Assert(found, false)
|
||||
})
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []interface{}{}
|
||||
array1 := garray.NewArrayFrom(a1)
|
||||
rand := array1.Rands(1)
|
||||
t.AssertNil(rand)
|
||||
})
|
||||
}
|
||||
|
||||
func TestArray_Shuffle(t *testing.T) {
|
||||
@ -412,6 +457,12 @@ func TestArray_Join(t *testing.T) {
|
||||
array1 := garray.NewArrayFrom(a1)
|
||||
t.Assert(array1.Join("."), `0.1."a".\a`)
|
||||
})
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []interface{}{}
|
||||
array1 := garray.NewArrayFrom(a1)
|
||||
t.Assert(len(array1.Join(".")), 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestArray_String(t *testing.T) {
|
||||
@ -419,6 +470,8 @@ func TestArray_String(t *testing.T) {
|
||||
a1 := []interface{}{0, 1, 2, 3, 4, 5, 6}
|
||||
array1 := garray.NewArrayFrom(a1)
|
||||
t.Assert(array1.String(), `[0,1,2,3,4,5,6]`)
|
||||
array1 = nil
|
||||
t.Assert(array1.String(), "")
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@ -63,6 +63,19 @@ func Test_IntArray_Basic(t *testing.T) {
|
||||
array.InsertAfter(6, 400)
|
||||
t.Assert(array.Slice(), []int{100, 200, 1, 2, 3, 300, 4, 400})
|
||||
t.Assert(array.Clear().Len(), 0)
|
||||
err := array.InsertBefore(99, 300)
|
||||
t.AssertNE(err, nil)
|
||||
err = array.InsertAfter(99, 400)
|
||||
t.AssertNE(err, nil)
|
||||
})
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewIntArrayFrom([]int{0, 1, 2, 3})
|
||||
copyArray := array.DeepCopy().(*garray.IntArray)
|
||||
copyArray.Set(0, 1)
|
||||
cval, _ := copyArray.Get(0)
|
||||
val, _ := array.Get(0)
|
||||
t.AssertNE(cval, val)
|
||||
})
|
||||
}
|
||||
|
||||
@ -89,6 +102,8 @@ func TestIntArray_Unique(t *testing.T) {
|
||||
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, 4, 5})
|
||||
array2 := garray.NewIntArrayFrom([]int{})
|
||||
t.Assert(array2.Unique().Slice(), []int{})
|
||||
})
|
||||
}
|
||||
|
||||
@ -374,6 +389,14 @@ func TestIntArray_Rand(t *testing.T) {
|
||||
v, ok := array1.Rand()
|
||||
t.AssertIN(v, a1)
|
||||
t.Assert(ok, true)
|
||||
|
||||
array2 := garray.NewIntArrayFrom([]int{})
|
||||
v, ok = array2.Rand()
|
||||
t.Assert(v, 0)
|
||||
t.Assert(ok, false)
|
||||
|
||||
intSlices := array2.Rands(1)
|
||||
t.Assert(intSlices, nil)
|
||||
})
|
||||
}
|
||||
|
||||
@ -420,6 +443,8 @@ func TestIntArray_String(t *testing.T) {
|
||||
a1 := []int{0, 1, 2, 3, 4, 5, 6}
|
||||
array1 := garray.NewIntArrayFrom(a1)
|
||||
t.Assert(array1.String(), "[0,1,2,3,4,5,6]")
|
||||
array1 = nil
|
||||
t.Assert(array1.String(), "")
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@ -34,6 +34,10 @@ func Test_StrArray_Basic(t *testing.T) {
|
||||
t.Assert(v, 100)
|
||||
t.Assert(ok, true)
|
||||
|
||||
v, ok = array3.Get(0)
|
||||
t.Assert(v, "")
|
||||
t.Assert(ok, false)
|
||||
|
||||
t.Assert(array.Search("100"), 0)
|
||||
t.Assert(array.Contains("100"), true)
|
||||
|
||||
@ -61,12 +65,28 @@ func Test_StrArray_Basic(t *testing.T) {
|
||||
t.Assert(array.Clear().Len(), 0)
|
||||
t.Assert(array2.Slice(), expect)
|
||||
t.Assert(array3.Search("100"), -1)
|
||||
err := array.InsertBefore(99, "300")
|
||||
t.AssertNE(err, nil)
|
||||
array.InsertAfter(99, "400")
|
||||
t.AssertNE(err, nil)
|
||||
|
||||
})
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewStrArrayFrom([]string{"0", "1", "2", "3"})
|
||||
|
||||
copyArray := array.DeepCopy().(*garray.StrArray)
|
||||
copyArray.Set(0, "1")
|
||||
cval, _ := copyArray.Get(0)
|
||||
val, _ := array.Get(0)
|
||||
t.AssertNE(cval, val)
|
||||
})
|
||||
}
|
||||
|
||||
func TestStrArray_ContainsI(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s := garray.NewStrArray()
|
||||
t.Assert(s.Contains("A"), false)
|
||||
s.Append("a", "b", "C")
|
||||
t.Assert(s.Contains("A"), false)
|
||||
t.Assert(s.Contains("a"), true)
|
||||
@ -94,6 +114,8 @@ func TestStrArray_Unique(t *testing.T) {
|
||||
expect := []string{"1", "1", "2", "2", "3", "3", "2", "2"}
|
||||
array := garray.NewStrArrayFrom(expect)
|
||||
t.Assert(array.Unique().Slice(), []string{"1", "2", "3"})
|
||||
array1 := garray.NewStrArrayFrom([]string{})
|
||||
t.Assert(array1.Unique().Slice(), []string{})
|
||||
})
|
||||
}
|
||||
|
||||
@ -364,6 +386,13 @@ func TestStrArray_Rand(t *testing.T) {
|
||||
v, ok := array1.Rand()
|
||||
t.Assert(ok, true)
|
||||
t.AssertIN(v, a1)
|
||||
|
||||
array2 := garray.NewStrArrayFrom([]string{})
|
||||
v, ok = array2.Rand()
|
||||
t.Assert(ok, false)
|
||||
t.Assert(v, "")
|
||||
strArray := array2.Rands(1)
|
||||
t.AssertNil(strArray)
|
||||
})
|
||||
}
|
||||
|
||||
@ -406,6 +435,11 @@ func TestStrArray_Join(t *testing.T) {
|
||||
array1 := garray.NewStrArrayFrom(a1)
|
||||
t.Assert(array1.Join("."), `0.1."a".\a`)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []string{}
|
||||
array1 := garray.NewStrArrayFrom(a1)
|
||||
t.Assert(array1.Join("."), "")
|
||||
})
|
||||
}
|
||||
|
||||
func TestStrArray_String(t *testing.T) {
|
||||
@ -413,6 +447,9 @@ func TestStrArray_String(t *testing.T) {
|
||||
a1 := []string{"0", "1", "2", "3", "4", "5", "6"}
|
||||
array1 := garray.NewStrArrayFrom(a1)
|
||||
t.Assert(array1.String(), `["0","1","2","3","4","5","6"]`)
|
||||
|
||||
array1 = nil
|
||||
t.Assert(array1.String(), "")
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@ -61,6 +61,18 @@ func TestNewSortedArrayFromCopy(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestNewSortedArrayRange(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
func1 := func(v1, v2 interface{}) int {
|
||||
return gconv.Int(v1) - gconv.Int(v2)
|
||||
}
|
||||
|
||||
array1 := garray.NewSortedArrayRange(1, 5, 1, func1)
|
||||
t.Assert(array1.Len(), 5)
|
||||
t.Assert(array1, []interface{}{1, 2, 3, 4, 5})
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedArray_SetArray(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []interface{}{"a", "f", "c"}
|
||||
@ -106,10 +118,26 @@ func TestSortedArray_Get(t *testing.T) {
|
||||
v, ok = array1.Get(1)
|
||||
t.Assert(v, "c")
|
||||
t.Assert(ok, true)
|
||||
|
||||
v, ok = array1.Get(99)
|
||||
t.Assert(v, nil)
|
||||
t.Assert(ok, false)
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func TestSortedArray_At(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []interface{}{"a", "f", "c"}
|
||||
func1 := func(v1, v2 interface{}) int {
|
||||
return strings.Compare(gconv.String(v1), gconv.String(v2))
|
||||
}
|
||||
array1 := garray.NewSortedArrayFrom(a1, func1)
|
||||
v := array1.At(2)
|
||||
t.Assert(v, "f")
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedArray_Remove(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []interface{}{"a", "d", "c", "b"}
|
||||
@ -461,6 +489,11 @@ func TestSortedArray_Rand(t *testing.T) {
|
||||
t.Assert(ok, true)
|
||||
t.AssertIN(i1, []interface{}{"a", "d", "c"})
|
||||
t.Assert(array1.Len(), 3)
|
||||
|
||||
array2 := garray.NewSortedArrayFrom([]interface{}{}, func1)
|
||||
v, ok := array2.Rand()
|
||||
t.Assert(ok, false)
|
||||
t.Assert(v, nil)
|
||||
})
|
||||
}
|
||||
|
||||
@ -479,6 +512,10 @@ func TestSortedArray_Rands(t *testing.T) {
|
||||
|
||||
i1 = array1.Rands(4)
|
||||
t.Assert(len(i1), 4)
|
||||
|
||||
array2 := garray.NewSortedArrayFrom([]interface{}{}, func1)
|
||||
v := array2.Rands(1)
|
||||
t.Assert(v, nil)
|
||||
})
|
||||
}
|
||||
|
||||
@ -498,6 +535,12 @@ func TestSortedArray_Join(t *testing.T) {
|
||||
array1 := garray.NewSortedArrayFrom(a1, gutil.ComparatorString)
|
||||
t.Assert(array1.Join("."), `"a".0.1.\a`)
|
||||
})
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []interface{}{}
|
||||
array1 := garray.NewSortedArrayFrom(a1, gutil.ComparatorString)
|
||||
t.Assert(array1.Join("."), "")
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedArray_String(t *testing.T) {
|
||||
@ -505,6 +548,9 @@ func TestSortedArray_String(t *testing.T) {
|
||||
a1 := []interface{}{0, 1, "a", "b"}
|
||||
array1 := garray.NewSortedArrayFrom(a1, gutil.ComparatorString)
|
||||
t.Assert(array1.String(), `[0,1,"a","b"]`)
|
||||
|
||||
array1 = nil
|
||||
t.Assert(array1.String(), "")
|
||||
})
|
||||
}
|
||||
|
||||
@ -541,6 +587,11 @@ func TestSortedArray_Unique(t *testing.T) {
|
||||
array1.Unique()
|
||||
t.Assert(array1.Len(), 5)
|
||||
t.Assert(array1, []interface{}{1, 2, 3, 4, 5})
|
||||
|
||||
array2 := garray.NewSortedArrayFrom([]interface{}{}, gutil.ComparatorInt)
|
||||
array2.Unique()
|
||||
t.Assert(array2.Len(), 0)
|
||||
t.Assert(array2, []interface{}{})
|
||||
})
|
||||
}
|
||||
|
||||
@ -878,3 +929,22 @@ func TestSortedArray_Walk(t *testing.T) {
|
||||
}), g.Slice{"key-1", "key-2"})
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedArray_IsEmpty(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewSortedArrayFrom([]interface{}{}, gutil.ComparatorString)
|
||||
t.Assert(array.IsEmpty(), true)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedArray_DeepCopy(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewSortedArrayFrom([]interface{}{1, 2, 3, 4, 5}, gutil.ComparatorString)
|
||||
copyArray := array.DeepCopy().(*garray.SortedArray)
|
||||
array.Add(6)
|
||||
copyArray.Add(7)
|
||||
cval, _ := copyArray.Get(5)
|
||||
val, _ := array.Get(5)
|
||||
t.AssertNE(cval, val)
|
||||
})
|
||||
}
|
||||
|
||||
@ -19,6 +19,26 @@ import (
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
|
||||
func TestNewSortedIntArrayComparator(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []int{0, 3, 2, 1, 4, 5, 6}
|
||||
array1 := garray.NewSortedIntArrayComparator(func(a, b int) int {
|
||||
return a - b
|
||||
}, true)
|
||||
array1.Append(a1...)
|
||||
t.Assert(array1.Len(), 7)
|
||||
t.Assert(array1.Interfaces(), []int{0, 1, 2, 3, 4, 5, 6})
|
||||
})
|
||||
}
|
||||
|
||||
func TestNewSortedIntArrayRange(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array1 := garray.NewSortedIntArrayRange(1, 5, 1)
|
||||
t.Assert(array1.Len(), 5)
|
||||
t.Assert(array1.Interfaces(), []int{1, 2, 3, 4, 5})
|
||||
})
|
||||
}
|
||||
|
||||
func TestNewSortedIntArrayFrom(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []int{0, 3, 2, 1, 4, 5, 6}
|
||||
@ -37,6 +57,17 @@ func TestNewSortedIntArrayFromCopy(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedIntArray_At(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []int{0, 3, 2, 1}
|
||||
|
||||
array1 := garray.NewSortedIntArrayFrom(a1)
|
||||
v := array1.At(1)
|
||||
|
||||
t.Assert(v, 1)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedIntArray_SetArray(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []int{0, 1, 2, 3}
|
||||
@ -78,6 +109,10 @@ func TestSortedIntArray_Get(t *testing.T) {
|
||||
v, ok = array1.Get(3)
|
||||
t.Assert(v, 5)
|
||||
t.Assert(ok, true)
|
||||
|
||||
v, ok = array1.Get(99)
|
||||
t.Assert(v, 0)
|
||||
t.Assert(ok, false)
|
||||
})
|
||||
}
|
||||
|
||||
@ -294,6 +329,9 @@ func TestSortedIntArray_Join(t *testing.T) {
|
||||
a1 := []int{1, 3, 5}
|
||||
array1 := garray.NewSortedIntArrayFrom(a1)
|
||||
t.Assert(array1.Join("."), `1.3.5`)
|
||||
|
||||
array2 := garray.NewSortedIntArrayFrom([]int{})
|
||||
t.Assert(array2.Join("."), "")
|
||||
})
|
||||
}
|
||||
|
||||
@ -302,6 +340,9 @@ func TestSortedIntArray_String(t *testing.T) {
|
||||
a1 := []int{1, 3, 5}
|
||||
array1 := garray.NewSortedIntArrayFrom(a1)
|
||||
t.Assert(array1.String(), `[1,3,5]`)
|
||||
|
||||
array1 = nil
|
||||
t.Assert(array1.String(), "")
|
||||
})
|
||||
}
|
||||
|
||||
@ -406,6 +447,11 @@ func TestSortedIntArray_Rand(t *testing.T) {
|
||||
ns1, ok := array1.Rand()
|
||||
t.AssertIN(ns1, a1)
|
||||
t.Assert(ok, true)
|
||||
|
||||
array2 := garray.NewSortedIntArrayFrom([]int{})
|
||||
ns2, ok := array2.Rand()
|
||||
t.Assert(ns2, 0)
|
||||
t.Assert(ok, false)
|
||||
})
|
||||
}
|
||||
|
||||
@ -419,6 +465,10 @@ func TestSortedIntArray_Rands(t *testing.T) {
|
||||
|
||||
ns2 := array1.Rands(6)
|
||||
t.Assert(len(ns2), 6)
|
||||
|
||||
array2 := garray.NewSortedIntArrayFrom([]int{})
|
||||
val := array2.Rands(1)
|
||||
t.Assert(val, nil)
|
||||
})
|
||||
}
|
||||
|
||||
@ -450,6 +500,10 @@ func TestSortedIntArray_Unique(t *testing.T) {
|
||||
array1.Unique()
|
||||
t.Assert(array1.Len(), 5)
|
||||
t.Assert(array1, []int{1, 2, 3, 4, 5})
|
||||
|
||||
array2 := garray.NewSortedIntArrayFrom([]int{})
|
||||
array2.Unique()
|
||||
t.Assert(array2.Len(), 0)
|
||||
})
|
||||
}
|
||||
|
||||
@ -731,3 +785,22 @@ func TestSortedIntArray_Walk(t *testing.T) {
|
||||
}), g.Slice{11, 12})
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedIntArray_IsEmpty(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewSortedIntArrayFrom([]int{})
|
||||
t.Assert(array.IsEmpty(), true)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedIntArray_DeepCopy(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewSortedIntArrayFrom([]int{1, 2, 3, 4, 5})
|
||||
copyArray := array.DeepCopy().(*garray.SortedIntArray)
|
||||
array.Add(6)
|
||||
copyArray.Add(7)
|
||||
cval, _ := copyArray.Get(5)
|
||||
val, _ := array.Get(5)
|
||||
t.AssertNE(cval, val)
|
||||
})
|
||||
}
|
||||
|
||||
@ -9,6 +9,7 @@
|
||||
package garray_test
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@ -19,6 +20,18 @@ import (
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
|
||||
func TestNewSortedStrArrayComparator(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []string{"a", "d", "c", "b"}
|
||||
s1 := garray.NewSortedStrArrayComparator(func(a, b string) int {
|
||||
return gstr.Compare(a, b)
|
||||
})
|
||||
s1.Add(a1...)
|
||||
t.Assert(s1.Len(), 4)
|
||||
t.Assert(s1, []string{"a", "b", "c", "d"})
|
||||
})
|
||||
}
|
||||
|
||||
func TestNewSortedStrArrayFrom(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []string{"a", "d", "c", "b"}
|
||||
@ -58,6 +71,9 @@ func TestSortedStrArray_ContainsI(t *testing.T) {
|
||||
t.Assert(s.Contains("A"), false)
|
||||
t.Assert(s.Contains("a"), true)
|
||||
t.Assert(s.ContainsI("A"), true)
|
||||
|
||||
s = garray.NewSortedStrArray()
|
||||
t.Assert(s.Contains("A"), false)
|
||||
})
|
||||
}
|
||||
|
||||
@ -85,6 +101,10 @@ func TestSortedStrArray_Get(t *testing.T) {
|
||||
v, ok = array1.Get(0)
|
||||
t.Assert(v, "a")
|
||||
t.Assert(ok, true)
|
||||
|
||||
v, ok = array1.Get(99)
|
||||
t.Assert(v, "")
|
||||
t.Assert(ok, false)
|
||||
})
|
||||
}
|
||||
|
||||
@ -361,6 +381,11 @@ func TestSortedStrArray_Rand(t *testing.T) {
|
||||
v, ok := array1.Rand()
|
||||
t.AssertIN(v, []string{"e", "a", "d"})
|
||||
t.Assert(ok, true)
|
||||
|
||||
array2 := garray.NewSortedStrArrayFrom([]string{})
|
||||
v, ok = array2.Rand()
|
||||
t.Assert(v, "")
|
||||
t.Assert(ok, false)
|
||||
})
|
||||
}
|
||||
|
||||
@ -375,6 +400,10 @@ func TestSortedStrArray_Rands(t *testing.T) {
|
||||
|
||||
s1 = array1.Rands(4)
|
||||
t.Assert(len(s1), 4)
|
||||
|
||||
array2 := garray.NewSortedStrArrayFrom([]string{})
|
||||
val := array2.Rands(1)
|
||||
t.Assert(val, nil)
|
||||
})
|
||||
}
|
||||
|
||||
@ -391,6 +420,11 @@ func TestSortedStrArray_Join(t *testing.T) {
|
||||
array1 := garray.NewSortedStrArrayFrom(a1)
|
||||
t.Assert(array1.Join("."), `"b".\c.a`)
|
||||
})
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array1 := garray.NewSortedStrArrayFrom([]string{})
|
||||
t.Assert(array1.Join("."), "")
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedStrArray_String(t *testing.T) {
|
||||
@ -398,6 +432,9 @@ func TestSortedStrArray_String(t *testing.T) {
|
||||
a1 := []string{"e", "a", "d"}
|
||||
array1 := garray.NewSortedStrArrayFrom(a1)
|
||||
t.Assert(array1.String(), `["a","d","e"]`)
|
||||
|
||||
array1 = nil
|
||||
t.Assert(array1.String(), "")
|
||||
})
|
||||
}
|
||||
|
||||
@ -469,6 +506,11 @@ func TestSortedStrArray_Unique(t *testing.T) {
|
||||
array1.Unique()
|
||||
t.Assert(array1.Len(), 3)
|
||||
t.Assert(array1, []string{"1", "2", "3"})
|
||||
|
||||
array2 := garray.NewSortedStrArrayFrom([]string{})
|
||||
array2.Unique()
|
||||
t.Assert(array2.Len(), 0)
|
||||
t.Assert(array2, []string{})
|
||||
})
|
||||
}
|
||||
|
||||
@ -755,3 +797,15 @@ func TestSortedStrArray_Walk(t *testing.T) {
|
||||
}), g.Slice{"key-1", "key-2"})
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedStrArray_DeepCopy(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewSortedStrArrayFrom([]string{"a", "b", "c", "d"})
|
||||
copyArray := array.DeepCopy().(*garray.SortedStrArray)
|
||||
array.Add("e")
|
||||
copyArray.Add("f")
|
||||
cval, _ := copyArray.Get(4)
|
||||
val, _ := array.Get(4)
|
||||
t.AssertNE(cval, val)
|
||||
})
|
||||
}
|
||||
|
||||
@ -12,6 +12,7 @@ import (
|
||||
"bytes"
|
||||
"container/list"
|
||||
|
||||
"github.com/gogf/gf/v2/internal/deepcopy"
|
||||
"github.com/gogf/gf/v2/internal/json"
|
||||
"github.com/gogf/gf/v2/internal/rwmutex"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
@ -546,3 +547,23 @@ func (l *List) UnmarshalValue(value interface{}) (err error) {
|
||||
l.PushBacks(array)
|
||||
return err
|
||||
}
|
||||
|
||||
// DeepCopy implements interface for deep copy of current type.
|
||||
func (l *List) DeepCopy() interface{} {
|
||||
l.mu.RLock()
|
||||
defer l.mu.RUnlock()
|
||||
|
||||
if l.list == nil {
|
||||
return nil
|
||||
}
|
||||
var (
|
||||
length = l.list.Len()
|
||||
values = make([]interface{}, length)
|
||||
)
|
||||
if length > 0 {
|
||||
for i, e := 0, l.list.Front(); i < length; i, e = i+1, e.Next() {
|
||||
values[i] = deepcopy.Copy(e.Value)
|
||||
}
|
||||
}
|
||||
return NewFrom(values, l.mu.IsSafe())
|
||||
}
|
||||
|
||||
@ -755,3 +755,13 @@ func TestList_UnmarshalValue(t *testing.T) {
|
||||
t.Assert(tlist.List.FrontAll(), []interface{}{1, 2, 3})
|
||||
})
|
||||
}
|
||||
|
||||
func TestList_DeepCopy(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := NewFrom([]interface{}{1, 2, "a", `"b"`, `\c`})
|
||||
copyList := l.DeepCopy()
|
||||
cl := copyList.(*List)
|
||||
cl.PopBack()
|
||||
t.AssertNE(l.Size(), cl.Size())
|
||||
})
|
||||
}
|
||||
|
||||
@ -8,6 +8,7 @@ package gmap
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/v2/container/gvar"
|
||||
"github.com/gogf/gf/v2/internal/deepcopy"
|
||||
"github.com/gogf/gf/v2/internal/empty"
|
||||
"github.com/gogf/gf/v2/internal/json"
|
||||
"github.com/gogf/gf/v2/internal/rwmutex"
|
||||
@ -72,7 +73,7 @@ func (m *AnyAnyMap) Map() map[interface{}]interface{} {
|
||||
return data
|
||||
}
|
||||
|
||||
// MapCopy returns a copy of the underlying data of the hash map.
|
||||
// MapCopy returns a shallow copy of the underlying data of the hash map.
|
||||
func (m *AnyAnyMap) MapCopy() map[interface{}]interface{} {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
@ -497,3 +498,14 @@ func (m *AnyAnyMap) UnmarshalValue(value interface{}) (err error) {
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy implements interface for deep copy of current type.
|
||||
func (m *AnyAnyMap) DeepCopy() interface{} {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
data := make(map[interface{}]interface{}, len(m.data))
|
||||
for k, v := range m.data {
|
||||
data[k] = deepcopy.Copy(v)
|
||||
}
|
||||
return NewFrom(data, m.mu.IsSafe())
|
||||
}
|
||||
|
||||
@ -9,6 +9,7 @@ package gmap
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/v2/container/gvar"
|
||||
"github.com/gogf/gf/v2/internal/deepcopy"
|
||||
"github.com/gogf/gf/v2/internal/empty"
|
||||
"github.com/gogf/gf/v2/internal/json"
|
||||
"github.com/gogf/gf/v2/internal/rwmutex"
|
||||
@ -499,3 +500,14 @@ func (m *IntAnyMap) UnmarshalValue(value interface{}) (err error) {
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy implements interface for deep copy of current type.
|
||||
func (m *IntAnyMap) DeepCopy() interface{} {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
data := make(map[int]interface{}, len(m.data))
|
||||
for k, v := range m.data {
|
||||
data[k] = deepcopy.Copy(v)
|
||||
}
|
||||
return NewIntAnyMapFrom(data, m.mu.IsSafe())
|
||||
}
|
||||
|
||||
@ -470,3 +470,14 @@ func (m *IntIntMap) UnmarshalValue(value interface{}) (err error) {
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy implements interface for deep copy of current type.
|
||||
func (m *IntIntMap) DeepCopy() interface{} {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
data := make(map[int]int, len(m.data))
|
||||
for k, v := range m.data {
|
||||
data[k] = v
|
||||
}
|
||||
return NewIntIntMapFrom(data, m.mu.IsSafe())
|
||||
}
|
||||
|
||||
@ -470,3 +470,14 @@ func (m *IntStrMap) UnmarshalValue(value interface{}) (err error) {
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy implements interface for deep copy of current type.
|
||||
func (m *IntStrMap) DeepCopy() interface{} {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
data := make(map[int]string, len(m.data))
|
||||
for k, v := range m.data {
|
||||
data[k] = v
|
||||
}
|
||||
return NewIntStrMapFrom(data, m.mu.IsSafe())
|
||||
}
|
||||
|
||||
@ -9,6 +9,7 @@ package gmap
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/v2/container/gvar"
|
||||
"github.com/gogf/gf/v2/internal/deepcopy"
|
||||
"github.com/gogf/gf/v2/internal/empty"
|
||||
"github.com/gogf/gf/v2/internal/json"
|
||||
"github.com/gogf/gf/v2/internal/rwmutex"
|
||||
@ -485,3 +486,14 @@ func (m *StrAnyMap) UnmarshalValue(value interface{}) (err error) {
|
||||
m.data = gconv.Map(value)
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy implements interface for deep copy of current type.
|
||||
func (m *StrAnyMap) DeepCopy() interface{} {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
data := make(map[string]interface{}, len(m.data))
|
||||
for k, v := range m.data {
|
||||
data[k] = deepcopy.Copy(v)
|
||||
}
|
||||
return NewStrAnyMapFrom(data, m.mu.IsSafe())
|
||||
}
|
||||
|
||||
@ -474,3 +474,14 @@ func (m *StrIntMap) UnmarshalValue(value interface{}) (err error) {
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy implements interface for deep copy of current type.
|
||||
func (m *StrIntMap) DeepCopy() interface{} {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
data := make(map[string]int, len(m.data))
|
||||
for k, v := range m.data {
|
||||
data[k] = v
|
||||
}
|
||||
return NewStrIntMapFrom(data, m.mu.IsSafe())
|
||||
}
|
||||
|
||||
@ -463,3 +463,14 @@ func (m *StrStrMap) UnmarshalValue(value interface{}) (err error) {
|
||||
m.data = gconv.MapStrStr(value)
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy implements interface for deep copy of current type.
|
||||
func (m *StrStrMap) DeepCopy() interface{} {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
data := make(map[string]string, len(m.data))
|
||||
for k, v := range m.data {
|
||||
data[k] = v
|
||||
}
|
||||
return NewStrStrMapFrom(data, m.mu.IsSafe())
|
||||
}
|
||||
|
||||
@ -12,6 +12,7 @@ import (
|
||||
|
||||
"github.com/gogf/gf/v2/container/glist"
|
||||
"github.com/gogf/gf/v2/container/gvar"
|
||||
"github.com/gogf/gf/v2/internal/deepcopy"
|
||||
"github.com/gogf/gf/v2/internal/empty"
|
||||
"github.com/gogf/gf/v2/internal/json"
|
||||
"github.com/gogf/gf/v2/internal/rwmutex"
|
||||
@ -590,3 +591,19 @@ func (m *ListMap) UnmarshalValue(value interface{}) (err error) {
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy implements interface for deep copy of current type.
|
||||
func (m *ListMap) DeepCopy() interface{} {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
data := make(map[interface{}]interface{}, len(m.data))
|
||||
if m.list != nil {
|
||||
var node *gListMapNode
|
||||
m.list.IteratorAsc(func(e *glist.Element) bool {
|
||||
node = e.Value.(*gListMapNode)
|
||||
data[node.key] = deepcopy.Copy(node.value)
|
||||
return true
|
||||
})
|
||||
}
|
||||
return NewListMapFrom(data, m.mu.IsSafe())
|
||||
}
|
||||
|
||||
@ -602,8 +602,12 @@ func ExampleAnyAnyMap_String() {
|
||||
|
||||
fmt.Println(m.String())
|
||||
|
||||
var m1 *gmap.Map = nil
|
||||
fmt.Println(len(m1.String()))
|
||||
|
||||
// Output:
|
||||
// {"k1":"v1"}
|
||||
// 0
|
||||
}
|
||||
|
||||
func ExampleAnyAnyMap_MarshalJSON() {
|
||||
|
||||
@ -601,8 +601,12 @@ func ExampleIntAnyMap_String() {
|
||||
|
||||
fmt.Println(m.String())
|
||||
|
||||
var m1 *gmap.IntAnyMap = nil
|
||||
fmt.Println(len(m1.String()))
|
||||
|
||||
// Output:
|
||||
// {"1":"v1"}
|
||||
// 0
|
||||
}
|
||||
|
||||
func ExampleIntAnyMap_MarshalJSON() {
|
||||
|
||||
@ -529,8 +529,12 @@ func ExampleIntIntMap_String() {
|
||||
|
||||
fmt.Println(m.String())
|
||||
|
||||
var m1 *gmap.IntIntMap = nil
|
||||
fmt.Println(len(m1.String()))
|
||||
|
||||
// Output:
|
||||
// {"1":1}
|
||||
// 0
|
||||
}
|
||||
|
||||
func ExampleIntIntMap_MarshalJSON() {
|
||||
|
||||
@ -599,8 +599,12 @@ func ExampleStrAnyMap_String() {
|
||||
|
||||
fmt.Println(m.String())
|
||||
|
||||
var m1 *gmap.StrAnyMap = nil
|
||||
fmt.Println(len(m1.String()))
|
||||
|
||||
// Output:
|
||||
// {"k1":"v1"}
|
||||
// 0
|
||||
}
|
||||
|
||||
func ExampleStrAnyMap_MarshalJSON() {
|
||||
|
||||
@ -8,11 +8,10 @@ package gmap_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/v2/container/gmap"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/internal/json"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
|
||||
"github.com/gogf/gf/v2/container/gmap"
|
||||
)
|
||||
|
||||
func ExampleStrIntMap_Iterator() {
|
||||
@ -535,8 +534,12 @@ func ExampleStrIntMap_String() {
|
||||
|
||||
fmt.Println(m.String())
|
||||
|
||||
var m1 *gmap.StrIntMap = nil
|
||||
fmt.Println(len(m1.String()))
|
||||
|
||||
// Output:
|
||||
// {"k1":1}
|
||||
// 0
|
||||
}
|
||||
|
||||
func ExampleStrIntMap_MarshalJSON() {
|
||||
|
||||
@ -531,8 +531,12 @@ func ExampleStrStrMap_String() {
|
||||
|
||||
fmt.Println(m.String())
|
||||
|
||||
var m1 *gmap.StrStrMap = nil
|
||||
fmt.Println(len(m1.String()))
|
||||
|
||||
// Output:
|
||||
// {"k1":"v1"}
|
||||
// 0
|
||||
}
|
||||
|
||||
func ExampleStrStrMap_MarshalJSON() {
|
||||
|
||||
@ -90,7 +90,7 @@ func ExampleNewFrom() {
|
||||
}
|
||||
|
||||
func ExampleNewHashMap() {
|
||||
m := gmap.New()
|
||||
m := gmap.NewHashMap()
|
||||
|
||||
m.Set("key1", "val1")
|
||||
fmt.Println(m)
|
||||
@ -105,7 +105,7 @@ func ExampleNewHashMapFrom() {
|
||||
m.Set("key1", "val1")
|
||||
fmt.Println(m)
|
||||
|
||||
n := gmap.NewFrom(m.MapCopy(), true)
|
||||
n := gmap.NewHashMapFrom(m.MapCopy(), true)
|
||||
fmt.Println(n)
|
||||
|
||||
// Output:
|
||||
|
||||
@ -171,6 +171,9 @@ func Test_AnyAnyMap_Merge(t *testing.T) {
|
||||
m2.Set(2, "2")
|
||||
m1.Merge(m2)
|
||||
t.Assert(m1.Map(), map[interface{}]interface{}{1: 1, 2: "2"})
|
||||
m3 := gmap.NewAnyAnyMapFrom(nil)
|
||||
m3.Merge(m2)
|
||||
t.Assert(m3.Map(), m2.Map())
|
||||
})
|
||||
}
|
||||
|
||||
@ -296,6 +299,10 @@ func Test_AnyAnyMap_Pop(t *testing.T) {
|
||||
|
||||
t.AssertNE(k1, k2)
|
||||
t.AssertNE(v1, v2)
|
||||
|
||||
k3, v3 := m.Pop()
|
||||
t.AssertNil(k3)
|
||||
t.AssertNil(v3)
|
||||
})
|
||||
}
|
||||
|
||||
@ -327,6 +334,11 @@ func Test_AnyAnyMap_Pops(t *testing.T) {
|
||||
|
||||
t.Assert(kArray.Unique().Len(), 3)
|
||||
t.Assert(vArray.Unique().Len(), 3)
|
||||
|
||||
v := m.Pops(1)
|
||||
t.AssertNil(v)
|
||||
v = m.Pops(-1)
|
||||
t.AssertNil(v)
|
||||
})
|
||||
}
|
||||
|
||||
@ -365,3 +377,17 @@ func TestAnyAnyMap_UnmarshalValue(t *testing.T) {
|
||||
t.Assert(v.Map.Get("k2"), "v2")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_AnyAnyMap_DeepCopy(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewAnyAnyMapFrom(g.MapAnyAny{
|
||||
"k1": "v1",
|
||||
"k2": "v2",
|
||||
})
|
||||
t.Assert(m.Size(), 2)
|
||||
|
||||
n := m.DeepCopy().(*gmap.AnyAnyMap)
|
||||
n.Set("k1", "val1")
|
||||
t.AssertNE(m.Get("k1"), n.Get("k1"))
|
||||
})
|
||||
}
|
||||
|
||||
@ -82,6 +82,12 @@ func Test_IntAnyMap_Basic(t *testing.T) {
|
||||
m2 := gmap.NewIntAnyMapFrom(map[int]interface{}{1: 1, 2: "2"})
|
||||
t.Assert(m2.Map(), map[int]interface{}{1: 1, 2: "2"})
|
||||
})
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewIntAnyMap(true)
|
||||
m.Set(1, 1)
|
||||
t.Assert(m.Map(), map[int]interface{}{1: 1})
|
||||
})
|
||||
}
|
||||
|
||||
func Test_IntAnyMap_Set_Fun(t *testing.T) {
|
||||
@ -171,6 +177,9 @@ func Test_IntAnyMap_Merge(t *testing.T) {
|
||||
m2.Set(2, "2")
|
||||
m1.Merge(m2)
|
||||
t.Assert(m1.Map(), map[int]interface{}{1: 1, 2: "2"})
|
||||
m3 := gmap.NewIntAnyMapFrom(nil)
|
||||
m3.Merge(m2)
|
||||
t.Assert(m3.Map(), m2.Map())
|
||||
})
|
||||
}
|
||||
|
||||
@ -271,6 +280,10 @@ func Test_IntAnyMap_Pop(t *testing.T) {
|
||||
|
||||
t.AssertNE(k1, k2)
|
||||
t.AssertNE(v1, v2)
|
||||
|
||||
k3, v3 := m.Pop()
|
||||
t.Assert(k3, 0)
|
||||
t.AssertNil(v3)
|
||||
})
|
||||
}
|
||||
|
||||
@ -302,6 +315,11 @@ func Test_IntAnyMap_Pops(t *testing.T) {
|
||||
|
||||
t.Assert(kArray.Unique().Len(), 3)
|
||||
t.Assert(vArray.Unique().Len(), 3)
|
||||
|
||||
v := m.Pops(1)
|
||||
t.AssertNil(v)
|
||||
v = m.Pops(-1)
|
||||
t.AssertNil(v)
|
||||
})
|
||||
}
|
||||
|
||||
@ -340,3 +358,17 @@ func TestIntAnyMap_UnmarshalValue(t *testing.T) {
|
||||
t.Assert(v.Map.Get(2), "v2")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_IntAnyMap_DeepCopy(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewIntAnyMapFrom(g.MapIntAny{
|
||||
1: "v1",
|
||||
2: "v2",
|
||||
})
|
||||
t.Assert(m.Size(), 2)
|
||||
|
||||
n := m.DeepCopy().(*gmap.IntAnyMap)
|
||||
n.Set(1, "val1")
|
||||
t.AssertNE(m.Get(1), n.Get(1))
|
||||
})
|
||||
}
|
||||
|
||||
@ -86,6 +86,12 @@ func Test_IntIntMap_Basic(t *testing.T) {
|
||||
m2 := gmap.NewIntIntMapFrom(map[int]int{1: 1, 2: 2})
|
||||
t.Assert(m2.Map(), map[int]int{1: 1, 2: 2})
|
||||
})
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewIntIntMap(true)
|
||||
m.Set(1, 1)
|
||||
t.Assert(m.Map(), map[int]int{1: 1})
|
||||
})
|
||||
}
|
||||
|
||||
func Test_IntIntMap_Set_Fun(t *testing.T) {
|
||||
@ -102,6 +108,11 @@ func Test_IntIntMap_Set_Fun(t *testing.T) {
|
||||
t.Assert(m.SetIfNotExistFuncLock(2, getInt), false)
|
||||
t.Assert(m.SetIfNotExistFuncLock(4, getInt), true)
|
||||
})
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewIntIntMapFrom(nil)
|
||||
t.Assert(m.GetOrSetFuncLock(1, getInt), getInt())
|
||||
})
|
||||
}
|
||||
|
||||
func Test_IntIntMap_Batch(t *testing.T) {
|
||||
@ -177,6 +188,9 @@ func Test_IntIntMap_Merge(t *testing.T) {
|
||||
m2.Set(2, 2)
|
||||
m1.Merge(m2)
|
||||
t.Assert(m1.Map(), map[int]int{1: 1, 2: 2})
|
||||
m3 := gmap.NewIntIntMapFrom(nil)
|
||||
m3.Merge(m2)
|
||||
t.Assert(m3.Map(), m2.Map())
|
||||
})
|
||||
}
|
||||
|
||||
@ -277,6 +291,10 @@ func Test_IntIntMap_Pop(t *testing.T) {
|
||||
|
||||
t.AssertNE(k1, k2)
|
||||
t.AssertNE(v1, v2)
|
||||
|
||||
k3, v3 := m.Pop()
|
||||
t.Assert(k3, 0)
|
||||
t.Assert(v3, 0)
|
||||
})
|
||||
}
|
||||
|
||||
@ -308,6 +326,11 @@ func Test_IntIntMap_Pops(t *testing.T) {
|
||||
|
||||
t.Assert(kArray.Unique().Len(), 3)
|
||||
t.Assert(vArray.Unique().Len(), 3)
|
||||
|
||||
v := m.Pops(1)
|
||||
t.AssertNil(v)
|
||||
v = m.Pops(-1)
|
||||
t.AssertNil(v)
|
||||
})
|
||||
}
|
||||
|
||||
@ -346,3 +369,17 @@ func TestIntIntMap_UnmarshalValue(t *testing.T) {
|
||||
t.Assert(v.Map.Get(2), "2")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_IntIntMap_DeepCopy(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewIntIntMapFrom(g.MapIntInt{
|
||||
1: 1,
|
||||
2: 2,
|
||||
})
|
||||
t.Assert(m.Size(), 2)
|
||||
|
||||
n := m.DeepCopy().(*gmap.IntIntMap)
|
||||
n.Set(1, 2)
|
||||
t.AssertNE(m.Get(1), n.Get(1))
|
||||
})
|
||||
}
|
||||
|
||||
@ -63,6 +63,7 @@ func Test_IntStrMap_Basic(t *testing.T) {
|
||||
t.Assert(m.Size(), 1)
|
||||
t.Assert(m.IsEmpty(), false)
|
||||
|
||||
t.Assert(m.GetOrSet(1, "a"), "a")
|
||||
t.Assert(m.GetOrSet(2, "b"), "b")
|
||||
t.Assert(m.SetIfNotExist(2, "b"), false)
|
||||
|
||||
@ -90,6 +91,29 @@ func Test_IntStrMap_Basic(t *testing.T) {
|
||||
m2 := gmap.NewIntStrMapFrom(map[int]string{1: "a", 2: "b"})
|
||||
t.Assert(m2.Map(), map[int]string{1: "a", 2: "b"})
|
||||
})
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewIntStrMap(true)
|
||||
m.Set(1, "val1")
|
||||
t.Assert(m.Map(), map[int]string{1: "val1"})
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntStrMap_MapStrAny(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewIntStrMap()
|
||||
m.GetOrSetFunc(1, getStr)
|
||||
m.GetOrSetFuncLock(2, getStr)
|
||||
t.Assert(m.MapStrAny(), g.MapStrAny{"1": "z", "2": "z"})
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntStrMap_Sets(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewIntStrMapFrom(nil)
|
||||
m.Sets(g.MapIntStr{1: "z", 2: "z"})
|
||||
t.Assert(len(m.Map()), 2)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_IntStrMap_Set_Fun(t *testing.T) {
|
||||
@ -97,6 +121,8 @@ func Test_IntStrMap_Set_Fun(t *testing.T) {
|
||||
m := gmap.NewIntStrMap()
|
||||
m.GetOrSetFunc(1, getStr)
|
||||
m.GetOrSetFuncLock(2, getStr)
|
||||
t.Assert(m.GetOrSetFunc(1, getStr), "z")
|
||||
t.Assert(m.GetOrSetFuncLock(2, getStr), "z")
|
||||
t.Assert(m.Get(1), "z")
|
||||
t.Assert(m.Get(2), "z")
|
||||
t.Assert(m.SetIfNotExistFunc(1, getStr), false)
|
||||
@ -105,6 +131,16 @@ func Test_IntStrMap_Set_Fun(t *testing.T) {
|
||||
t.Assert(m.SetIfNotExistFuncLock(2, getStr), false)
|
||||
t.Assert(m.SetIfNotExistFuncLock(4, getStr), true)
|
||||
})
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewIntStrMapFrom(nil)
|
||||
t.Assert(m.GetOrSetFuncLock(1, getStr), "z")
|
||||
})
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewIntStrMapFrom(nil)
|
||||
t.Assert(m.SetIfNotExistFuncLock(1, getStr), true)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_IntStrMap_Batch(t *testing.T) {
|
||||
@ -176,6 +212,10 @@ func Test_IntStrMap_Merge(t *testing.T) {
|
||||
m2.Set(2, "b")
|
||||
m1.Merge(m2)
|
||||
t.Assert(m1.Map(), map[int]string{1: "a", 2: "b"})
|
||||
|
||||
m3 := gmap.NewIntStrMapFrom(nil)
|
||||
m3.Merge(m2)
|
||||
t.Assert(m3.Map(), m2.Map())
|
||||
})
|
||||
}
|
||||
|
||||
@ -275,6 +315,10 @@ func Test_IntStrMap_Pop(t *testing.T) {
|
||||
|
||||
t.AssertNE(k1, k2)
|
||||
t.AssertNE(v1, v2)
|
||||
|
||||
k3, v3 := m.Pop()
|
||||
t.Assert(k3, 0)
|
||||
t.Assert(v3, "")
|
||||
})
|
||||
}
|
||||
|
||||
@ -306,6 +350,11 @@ func Test_IntStrMap_Pops(t *testing.T) {
|
||||
|
||||
t.Assert(kArray.Unique().Len(), 3)
|
||||
t.Assert(vArray.Unique().Len(), 3)
|
||||
|
||||
v := m.Pops(1)
|
||||
t.AssertNil(v)
|
||||
v = m.Pops(-1)
|
||||
t.AssertNil(v)
|
||||
})
|
||||
}
|
||||
|
||||
@ -344,3 +393,55 @@ func TestIntStrMap_UnmarshalValue(t *testing.T) {
|
||||
t.Assert(v.Map.Get(2), "v2")
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntStrMap_Replace(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewIntStrMapFrom(g.MapIntStr{
|
||||
1: "v1",
|
||||
2: "v2",
|
||||
3: "v3",
|
||||
})
|
||||
|
||||
t.Assert(m.Get(1), "v1")
|
||||
t.Assert(m.Get(2), "v2")
|
||||
t.Assert(m.Get(3), "v3")
|
||||
|
||||
m.Replace(g.MapIntStr{
|
||||
1: "v2",
|
||||
2: "v3",
|
||||
3: "v1",
|
||||
})
|
||||
|
||||
t.Assert(m.Get(1), "v2")
|
||||
t.Assert(m.Get(2), "v3")
|
||||
t.Assert(m.Get(3), "v1")
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntStrMap_String(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewIntStrMapFrom(g.MapIntStr{
|
||||
1: "v1",
|
||||
2: "v2",
|
||||
3: "v3",
|
||||
})
|
||||
t.Assert(m.String(), "{\"1\":\"v1\",\"2\":\"v2\",\"3\":\"v3\"}")
|
||||
|
||||
m = nil
|
||||
t.Assert(len(m.String()), 0)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_IntStrMap_DeepCopy(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewIntStrMapFrom(g.MapIntStr{
|
||||
1: "val1",
|
||||
2: "val2",
|
||||
})
|
||||
t.Assert(m.Size(), 2)
|
||||
|
||||
n := m.DeepCopy().(*gmap.IntStrMap)
|
||||
n.Set(1, "v1")
|
||||
t.AssertNE(m.Get(1), n.Get(1))
|
||||
})
|
||||
}
|
||||
|
||||
@ -169,6 +169,10 @@ func Test_StrAnyMap_Merge(t *testing.T) {
|
||||
m2.Set("b", "2")
|
||||
m1.Merge(m2)
|
||||
t.Assert(m1.Map(), map[string]interface{}{"a": 1, "b": "2"})
|
||||
|
||||
m3 := gmap.NewStrAnyMapFrom(nil)
|
||||
m3.Merge(m2)
|
||||
t.Assert(m3.Map(), m2.Map())
|
||||
})
|
||||
}
|
||||
|
||||
@ -283,6 +287,10 @@ func Test_StrAnyMap_Pop(t *testing.T) {
|
||||
|
||||
t.AssertNE(k1, k2)
|
||||
t.AssertNE(v1, v2)
|
||||
|
||||
k3, v3 := m.Pop()
|
||||
t.Assert(k3, "")
|
||||
t.Assert(v3, "")
|
||||
})
|
||||
}
|
||||
|
||||
@ -314,6 +322,11 @@ func Test_StrAnyMap_Pops(t *testing.T) {
|
||||
|
||||
t.Assert(kArray.Unique().Len(), 3)
|
||||
t.Assert(vArray.Unique().Len(), 3)
|
||||
|
||||
v := m.Pops(1)
|
||||
t.AssertNil(v)
|
||||
v = m.Pops(-1)
|
||||
t.AssertNil(v)
|
||||
})
|
||||
}
|
||||
|
||||
@ -352,3 +365,17 @@ func TestStrAnyMap_UnmarshalValue(t *testing.T) {
|
||||
t.Assert(v.Map.Get("k2"), "v2")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_StrAnyMap_DeepCopy(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewStrAnyMapFrom(g.MapStrAny{
|
||||
"key1": "val1",
|
||||
"key2": "val2",
|
||||
})
|
||||
t.Assert(m.Size(), 2)
|
||||
|
||||
n := m.DeepCopy().(*gmap.StrAnyMap)
|
||||
n.Set("key1", "v1")
|
||||
t.AssertNE(m.Get("key1"), n.Get("key1"))
|
||||
})
|
||||
}
|
||||
|
||||
@ -100,6 +100,11 @@ func Test_StrIntMap_Set_Fun(t *testing.T) {
|
||||
t.Assert(m.SetIfNotExistFuncLock("b", getInt), false)
|
||||
t.Assert(m.SetIfNotExistFuncLock("d", getInt), true)
|
||||
})
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewStrIntMapFrom(nil)
|
||||
t.Assert(m.GetOrSetFuncLock("a", getInt), 123)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_StrIntMap_Batch(t *testing.T) {
|
||||
@ -173,6 +178,9 @@ func Test_StrIntMap_Merge(t *testing.T) {
|
||||
m2.Set("b", 2)
|
||||
m1.Merge(m2)
|
||||
t.Assert(m1.Map(), map[string]int{"a": 1, "b": 2})
|
||||
m3 := gmap.NewStrIntMapFrom(nil)
|
||||
m3.Merge(m2)
|
||||
t.Assert(m3.Map(), m2.Map())
|
||||
})
|
||||
}
|
||||
|
||||
@ -287,6 +295,10 @@ func Test_StrIntMap_Pop(t *testing.T) {
|
||||
|
||||
t.AssertNE(k1, k2)
|
||||
t.AssertNE(v1, v2)
|
||||
|
||||
k3, v3 := m.Pop()
|
||||
t.Assert(k3, "")
|
||||
t.Assert(v3, 0)
|
||||
})
|
||||
}
|
||||
|
||||
@ -318,6 +330,11 @@ func Test_StrIntMap_Pops(t *testing.T) {
|
||||
|
||||
t.Assert(kArray.Unique().Len(), 3)
|
||||
t.Assert(vArray.Unique().Len(), 3)
|
||||
|
||||
v := m.Pops(1)
|
||||
t.AssertNil(v)
|
||||
v = m.Pops(-1)
|
||||
t.AssertNil(v)
|
||||
})
|
||||
}
|
||||
|
||||
@ -356,3 +373,17 @@ func TestStrIntMap_UnmarshalValue(t *testing.T) {
|
||||
t.Assert(v.Map.Get("k2"), 2)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_StrIntMap_DeepCopy(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewStrIntMapFrom(g.MapStrInt{
|
||||
"key1": 1,
|
||||
"key2": 2,
|
||||
})
|
||||
t.Assert(m.Size(), 2)
|
||||
|
||||
n := m.DeepCopy().(*gmap.StrIntMap)
|
||||
n.Set("key1", 2)
|
||||
t.AssertNE(m.Get("key1"), n.Get("key1"))
|
||||
})
|
||||
}
|
||||
|
||||
@ -98,6 +98,12 @@ func Test_StrStrMap_Set_Fun(t *testing.T) {
|
||||
t.Assert(m.SetIfNotExistFuncLock("b", getStr), false)
|
||||
t.Assert(m.SetIfNotExistFuncLock("d", getStr), true)
|
||||
})
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewStrStrMapFrom(nil)
|
||||
|
||||
t.Assert(m.GetOrSetFuncLock("b", getStr), "z")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_StrStrMap_Batch(t *testing.T) {
|
||||
@ -170,6 +176,9 @@ func Test_StrStrMap_Merge(t *testing.T) {
|
||||
m2.Set("b", "b")
|
||||
m1.Merge(m2)
|
||||
t.Assert(m1.Map(), map[string]string{"a": "a", "b": "b"})
|
||||
m3 := gmap.NewStrStrMapFrom(nil)
|
||||
m3.Merge(m2)
|
||||
t.Assert(m3.Map(), m2.Map())
|
||||
})
|
||||
}
|
||||
|
||||
@ -284,6 +293,10 @@ func Test_StrStrMap_Pop(t *testing.T) {
|
||||
|
||||
t.AssertNE(k1, k2)
|
||||
t.AssertNE(v1, v2)
|
||||
|
||||
k3, v3 := m.Pop()
|
||||
t.Assert(k3, "")
|
||||
t.Assert(v3, "")
|
||||
})
|
||||
}
|
||||
|
||||
@ -315,6 +328,11 @@ func Test_StrStrMap_Pops(t *testing.T) {
|
||||
|
||||
t.Assert(kArray.Unique().Len(), 3)
|
||||
t.Assert(vArray.Unique().Len(), 3)
|
||||
|
||||
v := m.Pops(1)
|
||||
t.AssertNil(v)
|
||||
v = m.Pops(-1)
|
||||
t.AssertNil(v)
|
||||
})
|
||||
}
|
||||
|
||||
@ -353,3 +371,17 @@ func TestStrStrMap_UnmarshalValue(t *testing.T) {
|
||||
t.Assert(v.Map.Get("k2"), "v2")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_StrStrMap_DeepCopy(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewStrStrMapFrom(g.MapStrStr{
|
||||
"key1": "val1",
|
||||
"key2": "val2",
|
||||
})
|
||||
t.Assert(m.Size(), 2)
|
||||
|
||||
n := m.DeepCopy().(*gmap.StrStrMap)
|
||||
n.Set("key1", "v1")
|
||||
t.AssertNE(m.Get("key1"), n.Get("key1"))
|
||||
})
|
||||
}
|
||||
|
||||
@ -154,6 +154,9 @@ func Test_ListMap_Basic_Merge(t *testing.T) {
|
||||
m2.Set("key2", "val2")
|
||||
m1.Merge(m2)
|
||||
t.Assert(m1.Map(), map[interface{}]interface{}{"key1": "val1", "key2": "val2"})
|
||||
m3 := gmap.NewListMapFrom(nil)
|
||||
m3.Merge(m2)
|
||||
t.Assert(m3.Map(), m2.Map())
|
||||
})
|
||||
}
|
||||
|
||||
@ -266,6 +269,10 @@ func Test_ListMap_Pop(t *testing.T) {
|
||||
|
||||
t.AssertNE(k1, k2)
|
||||
t.AssertNE(v1, v2)
|
||||
|
||||
k3, v3 := m.Pop()
|
||||
t.AssertNil(k3)
|
||||
t.AssertNil(v3)
|
||||
})
|
||||
}
|
||||
|
||||
@ -297,6 +304,11 @@ func Test_ListMap_Pops(t *testing.T) {
|
||||
|
||||
t.Assert(kArray.Unique().Len(), 3)
|
||||
t.Assert(vArray.Unique().Len(), 3)
|
||||
|
||||
v := m.Pops(1)
|
||||
t.AssertNil(v)
|
||||
v = m.Pops(-1)
|
||||
t.AssertNil(v)
|
||||
})
|
||||
}
|
||||
|
||||
@ -335,3 +347,44 @@ func TestListMap_UnmarshalValue(t *testing.T) {
|
||||
t.Assert(v.Map.Get("2"), "v2")
|
||||
})
|
||||
}
|
||||
|
||||
func TestListMap_String(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewListMap()
|
||||
m.Set(1, "")
|
||||
m.Set(2, "2")
|
||||
t.Assert(m.String(), "{\"1\":\"\",\"2\":\"2\"}")
|
||||
|
||||
m1 := gmap.NewListMapFrom(nil)
|
||||
t.Assert(m1.String(), "{}")
|
||||
})
|
||||
}
|
||||
|
||||
func TestListMap_MarshalJSON(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewListMap()
|
||||
m.Set(1, "")
|
||||
m.Set(2, "2")
|
||||
res, err := m.MarshalJSON()
|
||||
t.Assert(res, []byte("{\"1\":\"\",\"2\":\"2\"}"))
|
||||
t.AssertNil(err)
|
||||
|
||||
m1 := gmap.NewListMapFrom(nil)
|
||||
res, err = m1.MarshalJSON()
|
||||
t.Assert(res, []byte("{}"))
|
||||
t.AssertNil(err)
|
||||
})
|
||||
}
|
||||
|
||||
func TestListMap_DeepCopy(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewListMap()
|
||||
m.Set(1, "1")
|
||||
m.Set(2, "2")
|
||||
t.Assert(m.Size(), 2)
|
||||
|
||||
n := m.DeepCopy().(*gmap.ListMap)
|
||||
n.Set(1, "val1")
|
||||
t.AssertNE(m.Get(1), n.Get(1))
|
||||
})
|
||||
}
|
||||
|
||||
@ -510,3 +510,14 @@ func (set *Set) UnmarshalValue(value interface{}) (err error) {
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy implements interface for deep copy of current type.
|
||||
func (set *Set) DeepCopy() interface{} {
|
||||
set.mu.RLock()
|
||||
defer set.mu.RUnlock()
|
||||
data := make(map[interface{}]struct{}, len(set.data))
|
||||
for k, v := range set.data {
|
||||
data[k] = v
|
||||
}
|
||||
return NewFrom(data, set.mu.IsSafe())
|
||||
}
|
||||
|
||||
@ -469,3 +469,18 @@ func (set *IntSet) UnmarshalValue(value interface{}) (err error) {
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy implements interface for deep copy of current type.
|
||||
func (set *IntSet) DeepCopy() interface{} {
|
||||
set.mu.RLock()
|
||||
defer set.mu.RUnlock()
|
||||
var (
|
||||
slice = make([]int, len(set.data))
|
||||
index = 0
|
||||
)
|
||||
for k := range set.data {
|
||||
slice[index] = k
|
||||
index++
|
||||
}
|
||||
return NewIntSetFrom(slice, set.mu.IsSafe())
|
||||
}
|
||||
|
||||
@ -499,3 +499,18 @@ func (set *StrSet) UnmarshalValue(value interface{}) (err error) {
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy implements interface for deep copy of current type.
|
||||
func (set *StrSet) DeepCopy() interface{} {
|
||||
set.mu.RLock()
|
||||
defer set.mu.RUnlock()
|
||||
var (
|
||||
slice = make([]string, len(set.data))
|
||||
index = 0
|
||||
)
|
||||
for k := range set.data {
|
||||
slice[index] = k
|
||||
index++
|
||||
}
|
||||
return NewStrSetFrom(slice, set.mu.IsSafe())
|
||||
}
|
||||
|
||||
@ -96,3 +96,8 @@ func (v *Bool) UnmarshalValue(value interface{}) error {
|
||||
v.Set(gconv.Bool(value))
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopy implements interface for deep copy of current type.
|
||||
func (v *Bool) DeepCopy() interface{} {
|
||||
return NewBool(v.Val())
|
||||
}
|
||||
|
||||
@ -75,3 +75,8 @@ func (v *Byte) UnmarshalValue(value interface{}) error {
|
||||
v.Set(gconv.Byte(value))
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopy implements interface for deep copy of current type.
|
||||
func (v *Byte) DeepCopy() interface{} {
|
||||
return NewByte(v.Val())
|
||||
}
|
||||
|
||||
@ -30,7 +30,7 @@ func NewBytes(value ...[]byte) *Bytes {
|
||||
return t
|
||||
}
|
||||
|
||||
// Clone clones and returns a new concurrent-safe object for []byte type.
|
||||
// Clone clones and returns a new shallow copy object for []byte type.
|
||||
func (v *Bytes) Clone() *Bytes {
|
||||
return NewBytes(v.Val())
|
||||
}
|
||||
@ -83,3 +83,11 @@ func (v *Bytes) UnmarshalValue(value interface{}) error {
|
||||
v.Set(gconv.Bytes(value))
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopy implements interface for deep copy of current type.
|
||||
func (v *Bytes) DeepCopy() interface{} {
|
||||
oldBytes := v.Val()
|
||||
newBytes := make([]byte, len(oldBytes))
|
||||
copy(newBytes, oldBytes)
|
||||
return NewBytes(newBytes)
|
||||
}
|
||||
|
||||
@ -87,3 +87,8 @@ func (v *Float32) UnmarshalValue(value interface{}) error {
|
||||
v.Set(gconv.Float32(value))
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopy implements interface for deep copy of current type.
|
||||
func (v *Float32) DeepCopy() interface{} {
|
||||
return NewFloat32(v.Val())
|
||||
}
|
||||
|
||||
@ -87,3 +87,8 @@ func (v *Float64) UnmarshalValue(value interface{}) error {
|
||||
v.Set(gconv.Float64(value))
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopy implements interface for deep copy of current type.
|
||||
func (v *Float64) DeepCopy() interface{} {
|
||||
return NewFloat64(v.Val())
|
||||
}
|
||||
|
||||
@ -75,3 +75,8 @@ func (v *Int) UnmarshalValue(value interface{}) error {
|
||||
v.Set(gconv.Int(value))
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopy implements interface for deep copy of current type.
|
||||
func (v *Int) DeepCopy() interface{} {
|
||||
return NewInt(v.Val())
|
||||
}
|
||||
|
||||
@ -75,3 +75,8 @@ func (v *Int32) UnmarshalValue(value interface{}) error {
|
||||
v.Set(gconv.Int32(value))
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopy implements interface for deep copy of current type.
|
||||
func (v *Int32) DeepCopy() interface{} {
|
||||
return NewInt32(v.Val())
|
||||
}
|
||||
|
||||
@ -75,3 +75,8 @@ func (v *Int64) UnmarshalValue(value interface{}) error {
|
||||
v.Set(gconv.Int64(value))
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopy implements interface for deep copy of current type.
|
||||
func (v *Int64) DeepCopy() interface{} {
|
||||
return NewInt64(v.Val())
|
||||
}
|
||||
|
||||
@ -9,6 +9,7 @@ package gtype
|
||||
import (
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/gogf/gf/v2/internal/deepcopy"
|
||||
"github.com/gogf/gf/v2/internal/json"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
@ -71,3 +72,8 @@ func (v *Interface) UnmarshalValue(value interface{}) error {
|
||||
v.Set(value)
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopy implements interface for deep copy of current type.
|
||||
func (v *Interface) DeepCopy() interface{} {
|
||||
return NewInterface(deepcopy.Copy(v.Val()))
|
||||
}
|
||||
|
||||
@ -75,3 +75,8 @@ func (v *Uint) UnmarshalValue(value interface{}) error {
|
||||
v.Set(gconv.Uint(value))
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopy implements interface for deep copy of current type.
|
||||
func (v *Uint) DeepCopy() interface{} {
|
||||
return NewUint(v.Val())
|
||||
}
|
||||
|
||||
@ -75,3 +75,8 @@ func (v *Uint32) UnmarshalValue(value interface{}) error {
|
||||
v.Set(gconv.Uint32(value))
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopy implements interface for deep copy of current type.
|
||||
func (v *Uint32) DeepCopy() interface{} {
|
||||
return NewUint32(v.Val())
|
||||
}
|
||||
|
||||
@ -75,3 +75,8 @@ func (v *Uint64) UnmarshalValue(value interface{}) error {
|
||||
v.Set(gconv.Uint64(value))
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopy implements interface for deep copy of current type.
|
||||
func (v *Uint64) DeepCopy() interface{} {
|
||||
return NewUint64(v.Val())
|
||||
}
|
||||
|
||||
@ -11,9 +11,11 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/v2/container/gtype"
|
||||
"github.com/gogf/gf/v2/internal/deepcopy"
|
||||
"github.com/gogf/gf/v2/internal/json"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
"github.com/gogf/gf/v2/util/gutil"
|
||||
)
|
||||
|
||||
// Var is an universal variable type implementer.
|
||||
@ -37,6 +39,11 @@ func New(value interface{}, safe ...bool) *Var {
|
||||
}
|
||||
}
|
||||
|
||||
// Copy does a deep copy of current Var and returns a pointer to this Var.
|
||||
func (v *Var) Copy() *Var {
|
||||
return New(gutil.Copy(v.Val()), v.safe)
|
||||
}
|
||||
|
||||
// Clone does a shallow copy of current Var and returns a pointer to this Var.
|
||||
func (v *Var) Clone() *Var {
|
||||
return New(v.Val(), v.safe)
|
||||
@ -188,3 +195,8 @@ func (v *Var) UnmarshalValue(value interface{}) error {
|
||||
v.Set(value)
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopy implements interface for deep copy of current type.
|
||||
func (v *Var) DeepCopy() interface{} {
|
||||
return New(deepcopy.Copy(v.Val()), v.safe)
|
||||
}
|
||||
|
||||
@ -13,6 +13,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/v2/container/gvar"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/test/gtest"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
@ -303,3 +304,27 @@ func Test_UnmarshalValue(t *testing.T) {
|
||||
t.Assert(v.Var.String(), "v")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Copy(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
src := g.Map{
|
||||
"k1": "v1",
|
||||
"k2": "v2",
|
||||
}
|
||||
srcVar := gvar.New(src)
|
||||
dstVar := srcVar.Copy()
|
||||
t.Assert(srcVar.Map(), src)
|
||||
t.Assert(dstVar.Map(), src)
|
||||
|
||||
dstVar.Map()["k3"] = "v3"
|
||||
t.Assert(srcVar.Map(), g.Map{
|
||||
"k1": "v1",
|
||||
"k2": "v2",
|
||||
})
|
||||
t.Assert(dstVar.Map(), g.Map{
|
||||
"k1": "v1",
|
||||
"k2": "v2",
|
||||
"k3": "v3",
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@ -40,7 +40,7 @@ import _ "github.com/gogf/gf/contrib/drivers/mysql/v2"
|
||||
import _ "github.com/gogf/gf/contrib/drivers/sqlite/v2"
|
||||
```
|
||||
Note:
|
||||
- It does not support `Save/Replace` features.
|
||||
- It does not support `Save` features.
|
||||
|
||||
## PostgreSQL
|
||||
```
|
||||
@ -75,8 +75,7 @@ Note:
|
||||
- It does not support `InsertIgnore/InsertGetId` features.
|
||||
- It does not support `Save/Replace` features.
|
||||
- It does not support `Transaction` feature.
|
||||
- It does not support `Transaction` feature.
|
||||
|
||||
- It does not support `RowsAffected` feature.
|
||||
|
||||
# Custom Drivers
|
||||
|
||||
|
||||
@ -10,19 +10,23 @@ package clickhouse
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"database/sql/driver"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/ClickHouse/clickhouse-go"
|
||||
|
||||
"github.com/ClickHouse/clickhouse-go/v2"
|
||||
"github.com/gogf/gf/v2/container/gmap"
|
||||
"github.com/gogf/gf/v2/database/gdb"
|
||||
"github.com/gogf/gf/v2/errors/gcode"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/os/gctx"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
"github.com/gogf/gf/v2/text/gregex"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
"github.com/google/uuid"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Driver is the driver for postgresql database.
|
||||
@ -32,13 +36,23 @@ type Driver struct {
|
||||
|
||||
var (
|
||||
// tableFieldsMap caches the table information retrieved from database.
|
||||
tableFieldsMap = gmap.New(true)
|
||||
errUnsupportedInsertIgnore = errors.New("unsupported method: InsertIgnore")
|
||||
errUnsupportedInsertGetId = errors.New("unsupported method: InsertGetId")
|
||||
errUnsupportedReplace = errors.New("unsupported method: Replace")
|
||||
errUnsupportedBegin = errors.New("unsupported method: Begin")
|
||||
errUnsupportedTransaction = errors.New("unsupported method: Transaction")
|
||||
errSQLNull = errors.New("SQL cannot be null")
|
||||
tableFieldsMap = gmap.New(true)
|
||||
|
||||
errUnsupportedInsertIgnore = errors.New("unsupported method:InsertIgnore")
|
||||
errUnsupportedInsertGetId = errors.New("unsupported method:InsertGetId")
|
||||
errUnsupportedReplace = errors.New("unsupported method:Replace")
|
||||
errUnsupportedBegin = errors.New("unsupported method:Begin")
|
||||
errUnsupportedTransaction = errors.New("unsupported method:Transaction")
|
||||
)
|
||||
|
||||
const (
|
||||
updateFilterPattern = `(?i)UPDATE[\s]+?(\w+[\.]?\w+)[\s]+?SET`
|
||||
deleteFilterPattern = `(?i)DELETE[\s]+?FROM[\s]+?(\w+[\.]?\w+)`
|
||||
filterTypePattern = `(?i)^UPDATE|DELETE`
|
||||
replaceSchemaPattern = `@(.+?)/([\w\.\-]+)+`
|
||||
needParsedSqlInCtx gctx.StrKey = "NeedParsedSql"
|
||||
OrmTagForStruct = "orm"
|
||||
driverName = "clickhouse"
|
||||
)
|
||||
|
||||
func init() {
|
||||
@ -62,27 +76,28 @@ func (d *Driver) New(core *gdb.Core, node *gdb.ConfigNode) (gdb.DB, error) {
|
||||
|
||||
// Open creates and returns an underlying sql.DB object for clickhouse.
|
||||
func (d *Driver) Open(config *gdb.ConfigNode) (*sql.DB, error) {
|
||||
var (
|
||||
source string
|
||||
driver = "clickhouse"
|
||||
)
|
||||
// clickhouse://username:password@host1:9000,host2:9000/database?dial_timeout=200ms&max_execution_time=60
|
||||
if config.Link != "" {
|
||||
source = config.Link
|
||||
// Custom changing the schema in runtime.
|
||||
if config.Name != "" {
|
||||
source, _ = gregex.ReplaceString(`@(.+?)/([\w\.\-]+)+`, "@$1/"+config.Name, source)
|
||||
config.Link, _ = gregex.ReplaceString(replaceSchemaPattern, "@$1/"+config.Name, config.Link)
|
||||
} else {
|
||||
// If no schema, the link is matched for replacement
|
||||
dbName, _ := gregex.MatchString(replaceSchemaPattern, config.Link)
|
||||
if len(dbName) > 0 {
|
||||
config.Name = dbName[len(dbName)-1]
|
||||
}
|
||||
}
|
||||
} else if config.Pass != "" {
|
||||
source = fmt.Sprintf(
|
||||
"clickhouse://%s:%s@%s:%s/%s?charset=%s&debug=%s",
|
||||
config.User, config.Pass, config.Host, config.Port, config.Name, config.Charset, gconv.String(config.Debug))
|
||||
config.Link = fmt.Sprintf(
|
||||
"clickhouse://%s:%s@%s:%s/%s?charset=%s&debug=%t",
|
||||
config.User, url.PathEscape(config.Pass), config.Host, config.Port, config.Name, config.Charset, config.Debug)
|
||||
} else {
|
||||
source = fmt.Sprintf(
|
||||
"clickhouse://%s@%s:%s/%s?charset=%s&debug=%s",
|
||||
config.User, config.Host, config.Port, config.Name, config.Charset, gconv.String(config.Debug))
|
||||
config.Link = fmt.Sprintf(
|
||||
"clickhouse://%s@%s:%s/%s?charset=%s&debug=%t",
|
||||
config.User, config.Host, config.Port, config.Name, config.Charset, config.Debug)
|
||||
}
|
||||
db, err := sql.Open(driver, source)
|
||||
db, err := sql.Open(driverName, config.Link)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -133,7 +148,7 @@ func (d *Driver) TableFields(
|
||||
if link, err = d.SlaveLink(useSchema); err != nil {
|
||||
return nil
|
||||
}
|
||||
getColumnsSql := fmt.Sprintf("select name,position,default_expression,comment from `system`.columns c where database = '%s' and `table` = '%s'", d.GetConfig().Name, table)
|
||||
getColumnsSql := fmt.Sprintf("select name,position,default_expression,comment,type,is_in_partition_key,is_in_sorting_key,is_in_primary_key,is_in_sampling_key from `system`.columns c where database = '%s' and `table` = '%s'", d.GetConfig().Name, table)
|
||||
result, err = d.DoSelect(ctx, link, getColumnsSql)
|
||||
if err != nil {
|
||||
return nil
|
||||
@ -215,29 +230,55 @@ func (d *Driver) ping(conn *sql.DB) error {
|
||||
func (d *Driver) DoFilter(
|
||||
ctx context.Context, link gdb.Link, originSql string, args []interface{},
|
||||
) (newSql string, newArgs []interface{}, err error) {
|
||||
// It replaces STD SQL to Clickhouse SQL grammar.
|
||||
// MySQL eg: UPDATE `table` SET xxx
|
||||
// Clickhouse eg: ALTER TABLE `table` UPDATE xxx
|
||||
// MySQL eg: DELETE FROM `table`
|
||||
// Clickhouse eg: ALTER TABLE `table` DELETE WHERE filter_expr
|
||||
result, err := gregex.MatchString("(?i)^UPDATE|DELETE", originSql)
|
||||
if len(args) == 0 {
|
||||
return originSql, args, nil
|
||||
}
|
||||
|
||||
var index int
|
||||
// Convert placeholder char '?' to string "$x".
|
||||
originSql, _ = gregex.ReplaceStringFunc(`\?`, originSql, func(s string) string {
|
||||
index++
|
||||
return fmt.Sprintf(`$%d`, index)
|
||||
})
|
||||
|
||||
// Only SQL generated through the framework is processed.
|
||||
if !d.getNeedParsedSqlFromCtx(ctx) {
|
||||
return originSql, args, nil
|
||||
}
|
||||
|
||||
// replace STD SQL to Clickhouse SQL grammar
|
||||
modeRes, err := gregex.MatchString(filterTypePattern, strings.TrimSpace(originSql))
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
if len(result) != 0 {
|
||||
sqlSlice := strings.Split(originSql, " ")
|
||||
if len(sqlSlice) < 3 {
|
||||
return "", nil, errSQLNull
|
||||
if len(modeRes) == 0 {
|
||||
return originSql, args, nil
|
||||
}
|
||||
|
||||
// Only delete/ UPDATE statements require filter
|
||||
switch strings.ToUpper(modeRes[0]) {
|
||||
case "UPDATE":
|
||||
// MySQL eg: UPDATE table_name SET field1=new-value1, field2=new-value2 [WHERE Clause]
|
||||
// Clickhouse eg: ALTER TABLE [db.]table UPDATE column1 = expr1 [, ...] WHERE filter_expr
|
||||
newSql, err = gregex.ReplaceStringFuncMatch(updateFilterPattern, originSql, func(s []string) string {
|
||||
return fmt.Sprintf("ALTER TABLE %s UPDATE", s[1])
|
||||
})
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
ck := []string{"ALTER", "TABLE"}
|
||||
switch strings.ToUpper(result[0]) {
|
||||
case "UPDATE":
|
||||
sqlSlice = append(append(append(ck, sqlSlice[1]), result[0]), sqlSlice[3:]...)
|
||||
return strings.Join(sqlSlice, " "), args, nil
|
||||
case "DELETE":
|
||||
sqlSlice = append(append(append(ck, sqlSlice[2]), result[0]), sqlSlice[3:]...)
|
||||
return strings.Join(sqlSlice, " "), args, nil
|
||||
return newSql, args, nil
|
||||
|
||||
case "DELETE":
|
||||
// MySQL eg: DELETE FROM table_name [WHERE Clause]
|
||||
// Clickhouse eg: ALTER TABLE [db.]table [ON CLUSTER cluster] DELETE WHERE filter_expr
|
||||
newSql, err = gregex.ReplaceStringFuncMatch(deleteFilterPattern, originSql, func(s []string) string {
|
||||
return fmt.Sprintf("ALTER TABLE %s DELETE", s[1])
|
||||
})
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
return newSql, args, nil
|
||||
|
||||
}
|
||||
return originSql, args, nil
|
||||
}
|
||||
@ -295,6 +336,82 @@ func (d *Driver) DoInsert(
|
||||
return stdSqlResult, tx.Commit()
|
||||
}
|
||||
|
||||
// ConvertDataForRecord converting for any data that will be inserted into table/collection as a record.
|
||||
func (d *Driver) ConvertDataForRecord(ctx context.Context, value interface{}) (map[string]interface{}, error) {
|
||||
m := gconv.Map(value, OrmTagForStruct)
|
||||
|
||||
// transforms a value of a particular type
|
||||
for k, v := range m {
|
||||
switch itemValue := v.(type) {
|
||||
|
||||
case time.Time:
|
||||
m[k] = itemValue
|
||||
// If the time is zero, it then updates it to nil,
|
||||
// which will insert/update the value to database as "null".
|
||||
if itemValue.IsZero() {
|
||||
m[k] = nil
|
||||
}
|
||||
|
||||
case uuid.UUID:
|
||||
m[k] = itemValue
|
||||
|
||||
case *time.Time:
|
||||
m[k] = itemValue
|
||||
// If the time is zero, it then updates it to nil,
|
||||
// which will insert/update the value to database as "null".
|
||||
if itemValue == nil || itemValue.IsZero() {
|
||||
m[k] = nil
|
||||
}
|
||||
|
||||
case gtime.Time:
|
||||
// for gtime type, needs to get time.Time
|
||||
m[k] = itemValue.Time
|
||||
// If the time is zero, it then updates it to nil,
|
||||
// which will insert/update the value to database as "null".
|
||||
if itemValue.IsZero() {
|
||||
m[k] = nil
|
||||
}
|
||||
|
||||
case *gtime.Time:
|
||||
// for gtime type, needs to get time.Time
|
||||
if itemValue != nil {
|
||||
m[k] = itemValue.Time
|
||||
}
|
||||
// If the time is zero, it then updates it to nil,
|
||||
// which will insert/update the value to database as "null".
|
||||
if itemValue == nil || itemValue.IsZero() {
|
||||
m[k] = nil
|
||||
}
|
||||
|
||||
default:
|
||||
// if the other type implements valuer for the driver package
|
||||
// the converted result is used
|
||||
// otherwise the interface data is committed
|
||||
valuer, ok := itemValue.(driver.Valuer)
|
||||
if !ok {
|
||||
m[k] = itemValue
|
||||
continue
|
||||
}
|
||||
convertedValue, err := valuer.Value()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
m[k] = convertedValue
|
||||
}
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func (d *Driver) DoDelete(ctx context.Context, link gdb.Link, table string, condition string, args ...interface{}) (result sql.Result, err error) {
|
||||
ctx = d.injectNeedParsedSql(ctx)
|
||||
return d.Core.DoDelete(ctx, link, table, condition, args...)
|
||||
}
|
||||
|
||||
func (d *Driver) DoUpdate(ctx context.Context, link gdb.Link, table string, data interface{}, condition string, args ...interface{}) (result sql.Result, err error) {
|
||||
ctx = d.injectNeedParsedSql(ctx)
|
||||
return d.Core.DoUpdate(ctx, link, table, data, condition, args...)
|
||||
}
|
||||
|
||||
// InsertIgnore Other queries for modifying data parts are not supported: REPLACE, MERGE, UPSERT, INSERT UPDATE.
|
||||
func (d *Driver) InsertIgnore(ctx context.Context, table string, data interface{}, batch ...int) (sql.Result, error) {
|
||||
return nil, errUnsupportedInsertIgnore
|
||||
@ -317,3 +434,17 @@ func (d *Driver) Begin(ctx context.Context) (tx *gdb.TX, err error) {
|
||||
func (d *Driver) Transaction(ctx context.Context, f func(ctx context.Context, tx *gdb.TX) error) error {
|
||||
return errUnsupportedTransaction
|
||||
}
|
||||
|
||||
func (d *Driver) injectNeedParsedSql(ctx context.Context) context.Context {
|
||||
if ctx.Value(needParsedSqlInCtx) != nil {
|
||||
return ctx
|
||||
}
|
||||
return context.WithValue(ctx, needParsedSqlInCtx, true)
|
||||
}
|
||||
|
||||
func (d *Driver) getNeedParsedSqlFromCtx(ctx context.Context) bool {
|
||||
if ctx.Value(needParsedSqlInCtx) != nil {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@ -3,6 +3,10 @@ package clickhouse
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
"github.com/gogf/gf/v2/util/guid"
|
||||
"github.com/google/uuid"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@ -13,18 +17,103 @@ import (
|
||||
"github.com/gogf/gf/v2/util/grand"
|
||||
)
|
||||
|
||||
// table DDL
|
||||
// CREATE TABLE visits
|
||||
// (
|
||||
// id UInt64,
|
||||
// duration Float64,
|
||||
// url String,
|
||||
// created DateTime
|
||||
//)
|
||||
// ENGINE = MergeTree()
|
||||
// PRIMARY KEY id
|
||||
// ORDER BY id
|
||||
func InitClickhouse() gdb.DB {
|
||||
const (
|
||||
sqlVisitsDDL = `
|
||||
CREATE TABLE IF NOT EXISTS visits (
|
||||
id UInt64,
|
||||
duration Float64,
|
||||
url String,
|
||||
created DateTime
|
||||
) ENGINE = MergeTree()
|
||||
PRIMARY KEY id
|
||||
ORDER BY id
|
||||
`
|
||||
dimSqlDDL = `
|
||||
CREATE TABLE IF NOT EXISTS dim (
|
||||
"code" String COMMENT '编码',
|
||||
"translation" String COMMENT '译文',
|
||||
"superior" UInt64 COMMENT '上级ID',
|
||||
"row_number" UInt16 COMMENT '行号',
|
||||
"is_active" UInt8 COMMENT '是否激活',
|
||||
"is_preset" UInt8 COMMENT '是否预置',
|
||||
"category" String COMMENT '类别',
|
||||
"tree_path" Array(String) COMMENT '树路径',
|
||||
"id" UInt64 COMMENT '代理主键ID',
|
||||
"scd" UInt64 COMMENT '缓慢变化维ID',
|
||||
"version" UInt64 COMMENT 'Merge版本ID',
|
||||
"sign" Int8 COMMENT '标识位',
|
||||
"created_by" UInt64 COMMENT '创建者ID',
|
||||
"created_at" DateTime64(3,'Asia/Shanghai') COMMENT '创建时间',
|
||||
"updated_by" UInt64 COMMENT '最后修改者ID',
|
||||
"updated_at" DateTime64(3,'Asia/Shanghai') COMMENT '最后修改时间',
|
||||
"updated_tick" UInt16 COMMENT '累计修改次数'
|
||||
) ENGINE = ReplacingMergeTree("version")
|
||||
ORDER BY ("id","scd")
|
||||
COMMENT '会计准则';
|
||||
`
|
||||
dimSqlDML = `
|
||||
insert into dim (code, translation, superior, row_number, is_active, is_preset, category, tree_path, id, scd, version, sign, created_by, created_at, updated_by, updated_at, updated_tick)
|
||||
values ('CN', '{"zh_CN":"中国大陆会计准则","en_US":"Chinese mainland accounting legislation"}', 0, 1, 1, 1, 1, '[''CN'']', 607972403489804288, 0, 0, 0, 607536279118155777, '2017-09-06 00:00:00', 607536279118155777, '2017-09-06 00:00:00', 0),
|
||||
('HK', '{"zh_CN":"中国香港会计准则","en_US":"Chinese Hong Kong accounting legislation"}', 0, 2, 1, 1, 1, '[''HK'']', 607972558544834566, 0, 0, 0, 607536279118155777, '2017-09-06 00:00:00', 607536279118155777, '2017-09-06 00:00:00', 0);
|
||||
`
|
||||
factSqlDDL = `
|
||||
CREATE TABLE IF NOT EXISTS fact (
|
||||
"adjustment_level" UInt64 COMMENT '调整层ID',
|
||||
"data_version" UInt64 COMMENT '数据版本ID',
|
||||
"accounting_legislation" UInt64 COMMENT '会计准则ID',
|
||||
"fiscal_year" UInt16 COMMENT '会计年度',
|
||||
"fiscal_period" UInt8 COMMENT '会计期间',
|
||||
"fiscal_year_period" UInt32 COMMENT '会计年度期间',
|
||||
"legal_entity" UInt64 COMMENT '法人主体ID',
|
||||
"cost_center" UInt64 COMMENT '成本中心ID',
|
||||
"legal_entity_partner" UInt64 COMMENT '内部关联方ID',
|
||||
"financial_posting" UInt64 COMMENT '凭证头ID',
|
||||
"line" UInt16 COMMENT '行号',
|
||||
"general_ledger_account" UInt64 COMMENT '总账科目ID',
|
||||
"debit" Decimal64(9) COMMENT '借方金额',
|
||||
"credit" Decimal64(9) COMMENT '贷方金额',
|
||||
"transaction_currency" UInt64 COMMENT '交易币种ID',
|
||||
"debit_tc" Decimal64(9) COMMENT '借方金额(交易币种)',
|
||||
"credit_tc" Decimal64(9) COMMENT '贷方金额(交易币种)',
|
||||
"posting_date" Date32 COMMENT '过账日期',
|
||||
"gc_year" UInt16 COMMENT '公历年',
|
||||
"gc_quarter" UInt8 COMMENT '公历季',
|
||||
"gc_month" UInt8 COMMENT '公历月',
|
||||
"gc_week" UInt8 COMMENT '公历周',
|
||||
"raw_info" String COMMENT '源信息',
|
||||
"summary" String COMMENT '摘要',
|
||||
"id" UInt64 COMMENT '代理主键ID',
|
||||
"version" UInt64 COMMENT 'Merge版本ID',
|
||||
"sign" Int8 COMMENT '标识位'
|
||||
) ENGINE = ReplacingMergeTree("version")
|
||||
ORDER BY ("adjustment_level","data_version","legal_entity","fiscal_year","fiscal_period","financial_posting","line")
|
||||
PARTITION BY ("adjustment_level","data_version","legal_entity","fiscal_year","fiscal_period")
|
||||
COMMENT '数据主表';
|
||||
`
|
||||
factSqlDML = `
|
||||
insert into fact (adjustment_level, data_version, accounting_legislation, fiscal_year, fiscal_period, fiscal_year_period, legal_entity, cost_center, legal_entity_partner, financial_posting, line, general_ledger_account, debit, credit, transaction_currency, debit_tc, credit_tc, posting_date, gc_year, gc_quarter, gc_month, gc_week, raw_info, summary, id, version, sign)
|
||||
values (607970943242866688, 607973669943119880, 607972403489804288, 2022, 3, 202203, 607974511316307985, 0, 607976190010986520, 607996702456025136, 1, 607985607569838111, 8674.39, 0, 607974898261823505, 8674.39, 0, '2022-03-05', 2022, 1, 3, 11, '{}', '摘要', 607992882741121073, 0, 0),
|
||||
(607970943242866688, 607973669943119880, 607972403489804288, 2022, 4, 202204, 607974511316307985, 0, 607976190010986520, 607993586419503145, 1, 607985607569838111, 9999.88, 0, 607974898261823505, 9999.88, 0, '2022-04-10', 2022, 2, 4, 18, '{}', '摘要', 607996939140599857, 0, 0);
|
||||
`
|
||||
expmSqlDDL = `
|
||||
CREATE TABLE IF NOT EXISTS data_type (
|
||||
Col1 UInt8 COMMENT '列1'
|
||||
, Col2 Nullable(String) COMMENT '列2'
|
||||
, Col3 FixedString(3) COMMENT '列3'
|
||||
, Col4 String COMMENT '列4'
|
||||
, Col5 Map(String, UInt8) COMMENT '列5'
|
||||
, Col6 Array(String) COMMENT '列6'
|
||||
, Col7 Tuple(String, UInt8, Array(Map(String, String))) COMMENT '列7'
|
||||
, Col8 DateTime COMMENT '列8'
|
||||
, Col9 UUID COMMENT '列9'
|
||||
, Col10 DateTime COMMENT '列10'
|
||||
) ENGINE = MergeTree()
|
||||
PRIMARY KEY Col4
|
||||
ORDER BY Col4
|
||||
`
|
||||
)
|
||||
|
||||
func clickhouseConfigDB() gdb.DB {
|
||||
connect, err := gdb.New(gdb.ConfigNode{
|
||||
Host: "127.0.0.1",
|
||||
Port: "9000",
|
||||
@ -38,135 +127,150 @@ func InitClickhouse() gdb.DB {
|
||||
return connect
|
||||
}
|
||||
|
||||
func TestDriverClickhouse_Create(t *testing.T) {
|
||||
gtest.AssertNil(createClickhouseTable(InitClickhouse()))
|
||||
}
|
||||
|
||||
func createClickhouseTable(connect gdb.DB) error {
|
||||
sqlStr := "CREATE TABLE IF NOT EXISTS visits (id UInt64,duration Float64,url String,created DateTime) ENGINE = MergeTree() PRIMARY KEY id ORDER BY id"
|
||||
_, err := connect.Exec(context.Background(), sqlStr)
|
||||
func createClickhouseTableVisits(connect gdb.DB) error {
|
||||
_, err := connect.Exec(context.Background(), sqlVisitsDDL)
|
||||
return err
|
||||
}
|
||||
|
||||
func dropClickhouseTable(conn gdb.DB) {
|
||||
func createClickhouseTableDim(connect gdb.DB) error {
|
||||
_, err := connect.Exec(context.Background(), dimSqlDDL)
|
||||
return err
|
||||
}
|
||||
|
||||
func createClickhouseTableFact(connect gdb.DB) error {
|
||||
_, err := connect.Exec(context.Background(), factSqlDDL)
|
||||
return err
|
||||
}
|
||||
|
||||
func createClickhouseExampleTable(connect gdb.DB) error {
|
||||
_, err := connect.Exec(context.Background(), expmSqlDDL)
|
||||
return err
|
||||
}
|
||||
|
||||
func dropClickhouseTableVisits(conn gdb.DB) {
|
||||
sqlStr := fmt.Sprintf("DROP TABLE IF EXISTS `visits`")
|
||||
_, _ = conn.Exec(context.Background(), sqlStr)
|
||||
}
|
||||
|
||||
func dropClickhouseTableDim(conn gdb.DB) {
|
||||
sqlStr := fmt.Sprintf("DROP TABLE IF EXISTS `dim`")
|
||||
_, _ = conn.Exec(context.Background(), sqlStr)
|
||||
}
|
||||
|
||||
func dropClickhouseTableFact(conn gdb.DB) {
|
||||
sqlStr := fmt.Sprintf("DROP TABLE IF EXISTS `fact`")
|
||||
_, _ = conn.Exec(context.Background(), sqlStr)
|
||||
}
|
||||
|
||||
func dropClickhouseExampleTable(conn gdb.DB) {
|
||||
sqlStr := fmt.Sprintf("DROP TABLE IF EXISTS `data_type`")
|
||||
_, _ = conn.Exec(context.Background(), sqlStr)
|
||||
}
|
||||
|
||||
func TestDriverClickhouse_Create(t *testing.T) {
|
||||
gtest.AssertNil(createClickhouseTableVisits(clickhouseConfigDB()))
|
||||
}
|
||||
|
||||
func TestDriverClickhouse_New(t *testing.T) {
|
||||
connect := InitClickhouse()
|
||||
connect := clickhouseConfigDB()
|
||||
gtest.AssertNE(connect, nil)
|
||||
gtest.AssertNil(connect.PingMaster())
|
||||
gtest.AssertNil(connect.PingSlave())
|
||||
}
|
||||
|
||||
func TestDriverClickhouse_OpenLink_Ping(t *testing.T) {
|
||||
connect := clickhouseConfigDB()
|
||||
gtest.AssertNE(connect, nil)
|
||||
gtest.AssertNil(connect.PingMaster())
|
||||
}
|
||||
|
||||
func TestDriverClickhouse_Tables(t *testing.T) {
|
||||
connect := InitClickhouse()
|
||||
gtest.AssertEQ(createClickhouseTable(connect), nil)
|
||||
defer dropClickhouseTable(connect)
|
||||
connect := clickhouseConfigDB()
|
||||
gtest.AssertEQ(createClickhouseTableVisits(connect), nil)
|
||||
defer dropClickhouseTableVisits(connect)
|
||||
tables, err := connect.Tables(context.Background())
|
||||
gtest.AssertNil(err)
|
||||
gtest.AssertNE(len(tables), 0)
|
||||
}
|
||||
|
||||
func TestDriverClickhouse_TableFields_Use_Config(t *testing.T) {
|
||||
connect := clickhouseConfigDB()
|
||||
gtest.AssertNil(createClickhouseTableVisits(connect))
|
||||
defer dropClickhouseTableVisits(connect)
|
||||
field, err := connect.TableFields(context.Background(), "visits")
|
||||
gtest.AssertNil(err)
|
||||
gtest.AssertEQ(len(field), 4)
|
||||
gtest.AssertNQ(field, nil)
|
||||
}
|
||||
|
||||
func TestDriverClickhouse_TableFields_Use_Link(t *testing.T) {
|
||||
connect := clickhouseConfigDB()
|
||||
gtest.AssertNil(createClickhouseTableVisits(connect))
|
||||
defer dropClickhouseTableVisits(connect)
|
||||
field, err := connect.TableFields(context.Background(), "visits")
|
||||
gtest.AssertNil(err)
|
||||
gtest.AssertEQ(len(field), 4)
|
||||
gtest.AssertNQ(field, nil)
|
||||
}
|
||||
|
||||
func TestDriverClickhouse_Transaction(t *testing.T) {
|
||||
connect := InitClickhouse()
|
||||
defer dropClickhouseTable(connect)
|
||||
connect := clickhouseConfigDB()
|
||||
defer dropClickhouseTableVisits(connect)
|
||||
gtest.AssertNE(connect.Transaction(context.Background(), func(ctx context.Context, tx *gdb.TX) error {
|
||||
return nil
|
||||
}), nil)
|
||||
}
|
||||
|
||||
func TestDriverClickhouse_DoDelete(t *testing.T) {
|
||||
connect := InitClickhouse()
|
||||
gtest.AssertEQ(createClickhouseTable(connect), nil)
|
||||
defer dropClickhouseTable(connect)
|
||||
_, err := connect.Model("visits").Where("created >", "2021-01-01 00:00:00").Delete()
|
||||
gtest.AssertNil(err)
|
||||
}
|
||||
|
||||
func TestDriverClickhouse_DoUpdate(t *testing.T) {
|
||||
connect := InitClickhouse()
|
||||
gtest.AssertEQ(createClickhouseTable(connect), nil)
|
||||
defer dropClickhouseTable(connect)
|
||||
_, err := connect.Model("visits").Where("created > ", "2021-01-01 15:15:15").Data(g.Map{
|
||||
"created": time.Now().Format("2006-01-02 15:04:05"),
|
||||
}).Update()
|
||||
gtest.AssertNil(err)
|
||||
_, err = connect.Model("visits").Data(g.Map{
|
||||
"created": time.Now().Format("2006-01-02 15:04:05"),
|
||||
}).Update()
|
||||
gtest.AssertNE(err, nil)
|
||||
_, err = connect.Model("visits").Update()
|
||||
gtest.AssertNE(err, nil)
|
||||
}
|
||||
|
||||
func TestDriverClickhouse_Select(t *testing.T) {
|
||||
connect := InitClickhouse()
|
||||
gtest.AssertEQ(createClickhouseTable(connect), nil)
|
||||
defer dropClickhouseTable(connect)
|
||||
data, err := connect.Model("visits").All()
|
||||
gtest.AssertNil(err)
|
||||
gtest.AssertEQ(len(data), 0)
|
||||
}
|
||||
|
||||
func TestDriver_InsertIgnore(t *testing.T) {
|
||||
connect := InitClickhouse()
|
||||
func TestDriverClickhouse_InsertIgnore(t *testing.T) {
|
||||
connect := clickhouseConfigDB()
|
||||
_, err := connect.InsertIgnore(context.Background(), "", nil)
|
||||
gtest.AssertEQ(err, errUnsupportedInsertIgnore)
|
||||
}
|
||||
|
||||
func TestDriver_InsertAndGetId(t *testing.T) {
|
||||
connect := InitClickhouse()
|
||||
func TestDriverClickhouse_InsertAndGetId(t *testing.T) {
|
||||
connect := clickhouseConfigDB()
|
||||
_, err := connect.InsertAndGetId(context.Background(), "", nil)
|
||||
gtest.AssertEQ(err, errUnsupportedInsertGetId)
|
||||
}
|
||||
|
||||
func TestDriver_Replace(t *testing.T) {
|
||||
connect := InitClickhouse()
|
||||
_, err := connect.Replace(context.Background(), "", nil)
|
||||
gtest.AssertEQ(err, errUnsupportedReplace)
|
||||
}
|
||||
|
||||
func TestDriverClickhouse_DoInsertOne(t *testing.T) {
|
||||
connect := InitClickhouse()
|
||||
gtest.AssertEQ(createClickhouseTable(connect), nil)
|
||||
defer dropClickhouseTable(connect)
|
||||
func TestDriverClickhouse_InsertOne(t *testing.T) {
|
||||
connect := clickhouseConfigDB()
|
||||
gtest.AssertEQ(createClickhouseTableVisits(connect), nil)
|
||||
defer dropClickhouseTableVisits(connect)
|
||||
_, err := connect.Model("visits").Data(g.Map{
|
||||
"id": grand.Intn(999),
|
||||
"duration": float64(grand.Intn(999)),
|
||||
"url": gconv.String(grand.Intn(999)),
|
||||
"created": time.Now().Format("2006-01-02 15:04:05"),
|
||||
"created": time.Now(),
|
||||
}).Insert()
|
||||
gtest.AssertNil(err)
|
||||
}
|
||||
|
||||
func TestDriver_DoInsertMany(t *testing.T) {
|
||||
connect := InitClickhouse()
|
||||
gtest.AssertEQ(createClickhouseTable(connect), nil)
|
||||
defer dropClickhouseTable(connect)
|
||||
func TestDriverClickhouse_InsertMany(t *testing.T) {
|
||||
connect := clickhouseConfigDB()
|
||||
gtest.AssertEQ(createClickhouseTableVisits(connect), nil)
|
||||
defer dropClickhouseTableVisits(connect)
|
||||
tx, err := connect.Begin(context.Background())
|
||||
gtest.AssertEQ(err, errUnsupportedBegin)
|
||||
gtest.AssertNil(tx)
|
||||
}
|
||||
|
||||
func TestDriverClickhouse_DoInsert(t *testing.T) {
|
||||
connect := InitClickhouse()
|
||||
gtest.AssertEQ(createClickhouseTable(connect), nil)
|
||||
func TestDriverClickhouse_Insert(t *testing.T) {
|
||||
connect := clickhouseConfigDB()
|
||||
gtest.AssertEQ(createClickhouseTableVisits(connect), nil)
|
||||
defer dropClickhouseTableVisits(connect)
|
||||
type insertItem struct {
|
||||
Id int `orm:"id"`
|
||||
Duration float64 `orm:"duration"`
|
||||
Url string `orm:"url"`
|
||||
Created string `orm:"created"`
|
||||
Id uint64 `orm:"id"`
|
||||
Duration float64 `orm:"duration"`
|
||||
Url string `orm:"url"`
|
||||
Created time.Time `orm:"created"`
|
||||
}
|
||||
var (
|
||||
insertUrl = "https://goframe.org"
|
||||
total = 0
|
||||
item = insertItem{
|
||||
Id: 0,
|
||||
Duration: 1,
|
||||
Url: insertUrl,
|
||||
Created: time.Now().Format("2006-01-02 15:04:05"),
|
||||
Created: time.Now(),
|
||||
}
|
||||
)
|
||||
_, err := connect.Model("visits").Data(item).Insert()
|
||||
@ -176,13 +280,12 @@ func TestDriverClickhouse_DoInsert(t *testing.T) {
|
||||
total, err = connect.Model("visits").Count()
|
||||
gtest.AssertNil(err)
|
||||
gtest.AssertEQ(total, 2)
|
||||
list := []*insertItem{}
|
||||
var list []*insertItem
|
||||
for i := 0; i < 50; i++ {
|
||||
list = append(list, &insertItem{
|
||||
Id: grand.Intn(999),
|
||||
Duration: float64(grand.Intn(999)),
|
||||
Url: insertUrl,
|
||||
Created: time.Now().Format("2006-01-02 15:04:05"),
|
||||
Created: time.Now(),
|
||||
})
|
||||
}
|
||||
_, err = connect.Model("visits").Data(list).Insert()
|
||||
@ -192,63 +295,248 @@ func TestDriverClickhouse_DoInsert(t *testing.T) {
|
||||
total, err = connect.Model("visits").Count()
|
||||
gtest.AssertNil(err)
|
||||
gtest.AssertEQ(total, 102)
|
||||
dropClickhouseTable(connect)
|
||||
}
|
||||
|
||||
func TestDriverClickhouse_DoExec(t *testing.T) {
|
||||
connect := InitClickhouse()
|
||||
gtest.AssertNil(createClickhouseTable(connect))
|
||||
defer dropClickhouseTable(connect)
|
||||
func TestDriverClickhouse_Insert_Use_Exec(t *testing.T) {
|
||||
connect := clickhouseConfigDB()
|
||||
gtest.AssertEQ(createClickhouseTableFact(connect), nil)
|
||||
defer dropClickhouseTableFact(connect)
|
||||
_, err := connect.Exec(context.Background(), factSqlDML)
|
||||
gtest.AssertNil(err)
|
||||
}
|
||||
|
||||
func TestDriverClickhouse_Delete(t *testing.T) {
|
||||
connect := clickhouseConfigDB()
|
||||
gtest.AssertEQ(createClickhouseTableVisits(connect), nil)
|
||||
defer dropClickhouseTableVisits(connect)
|
||||
_, err := connect.Model("visits").Where("created >", "2021-01-01 00:00:00").Delete()
|
||||
gtest.AssertNil(err)
|
||||
_, err = connect.Model("visits").
|
||||
Where("created >", "2021-01-01 00:00:00").
|
||||
Where("duration > ", 0).
|
||||
Where("url is not null").
|
||||
Delete()
|
||||
gtest.AssertNil(err)
|
||||
}
|
||||
|
||||
func TestDriverClickhouse_Update(t *testing.T) {
|
||||
connect := clickhouseConfigDB()
|
||||
gtest.AssertEQ(createClickhouseTableVisits(connect), nil)
|
||||
defer dropClickhouseTableVisits(connect)
|
||||
_, err := connect.Model("visits").Where("created > ", "2021-01-01 15:15:15").Data(g.Map{
|
||||
"created": time.Now().Format("2006-01-02 15:04:05"),
|
||||
}).Update()
|
||||
gtest.AssertNil(err)
|
||||
_, err = connect.Model("visits").
|
||||
Where("created > ", "2021-01-01 15:15:15").
|
||||
Where("duration > ", 0).
|
||||
Where("url is not null").
|
||||
Data(g.Map{
|
||||
"created": time.Now().Format("2006-01-02 15:04:05"),
|
||||
}).Update()
|
||||
}
|
||||
|
||||
func TestDriverClickhouse_Replace(t *testing.T) {
|
||||
connect := clickhouseConfigDB()
|
||||
_, err := connect.Replace(context.Background(), "", nil)
|
||||
gtest.AssertEQ(err, errUnsupportedReplace)
|
||||
}
|
||||
|
||||
func TestDriverClickhouse_DoFilter(t *testing.T) {
|
||||
rawSQL := "select * from visits where 1 = 1"
|
||||
this := Driver{}
|
||||
replaceSQL, _, err := this.DoFilter(context.Background(), nil, rawSQL, []interface{}{1})
|
||||
gtest.AssertNil(err)
|
||||
gtest.AssertEQ(rawSQL, replaceSQL)
|
||||
|
||||
// this SQL can't run ,clickhouse will report an error because there is no WHERE statement
|
||||
rawSQL = "update visit set url = '1'"
|
||||
replaceSQL, _, err = this.DoFilter(context.Background(), nil, rawSQL, []interface{}{1})
|
||||
gtest.AssertNil(err)
|
||||
|
||||
// this SQL can't run ,clickhouse will report an error because there is no WHERE statement
|
||||
rawSQL = "delete from visit"
|
||||
replaceSQL, _, err = this.DoFilter(context.Background(), nil, rawSQL, []interface{}{1})
|
||||
gtest.AssertNil(err)
|
||||
|
||||
ctx := this.injectNeedParsedSql(context.Background())
|
||||
rawSQL = "UPDATE visit SET url = '1' WHERE url = '0'"
|
||||
replaceSQL, _, err = this.DoFilter(ctx, nil, rawSQL, []interface{}{1})
|
||||
gtest.AssertNil(err)
|
||||
gtest.AssertEQ(replaceSQL, "ALTER TABLE visit UPDATE url = '1' WHERE url = '0'")
|
||||
|
||||
rawSQL = "DELETE FROM visit WHERE url = '0'"
|
||||
replaceSQL, _, err = this.DoFilter(ctx, nil, rawSQL, []interface{}{1})
|
||||
gtest.AssertNil(err)
|
||||
gtest.AssertEQ(replaceSQL, "ALTER TABLE visit DELETE WHERE url = '0'")
|
||||
}
|
||||
|
||||
func TestDriverClickhouse_Select(t *testing.T) {
|
||||
connect := clickhouseConfigDB()
|
||||
gtest.AssertNil(createClickhouseTableVisits(connect))
|
||||
defer dropClickhouseTableVisits(connect)
|
||||
_, err := connect.Model("visits").Data(g.Map{
|
||||
"url": "goframe.org",
|
||||
"duration": float64(1),
|
||||
}).Insert()
|
||||
gtest.AssertNil(err)
|
||||
temp, err := connect.Model("visits").Where("url", "goframe.org").Where("duration >= ", 1).One()
|
||||
gtest.AssertNil(err)
|
||||
gtest.AssertEQ(temp.IsEmpty(), false)
|
||||
_, err = connect.Model("visits").Data(g.Map{
|
||||
"url": "goframe.org",
|
||||
"duration": float64(2),
|
||||
}).Insert()
|
||||
gtest.AssertNil(err)
|
||||
data, err := connect.Model("visits").Where("url", "goframe.org").Where("duration >= ", 1).All()
|
||||
gtest.AssertNil(err)
|
||||
gtest.AssertEQ(len(data), 2)
|
||||
}
|
||||
|
||||
func TestDriverClickhouse_Exec_OPTIMIZE(t *testing.T) {
|
||||
connect := clickhouseConfigDB()
|
||||
gtest.AssertNil(createClickhouseTableVisits(connect))
|
||||
defer dropClickhouseTableVisits(connect)
|
||||
sqlStr := "OPTIMIZE table visits"
|
||||
_, err := connect.Exec(context.Background(), sqlStr)
|
||||
gtest.AssertNil(err)
|
||||
}
|
||||
|
||||
func TestDriver_DoFilter(t *testing.T) {
|
||||
rawSQL := "select * from visits where 1 = 1"
|
||||
this := Driver{}
|
||||
replaceSQL, _, err := this.DoFilter(nil, nil, rawSQL, nil)
|
||||
func TestDriverClickhouse_ExecInsert(t *testing.T) {
|
||||
connect := clickhouseConfigDB()
|
||||
gtest.AssertEQ(createClickhouseTableDim(connect), nil)
|
||||
defer dropClickhouseTableDim(connect)
|
||||
_, err := connect.Exec(context.Background(), dimSqlDML)
|
||||
gtest.AssertNil(err)
|
||||
gtest.AssertEQ(rawSQL, replaceSQL)
|
||||
rawSQL = "update visit set url = '1'"
|
||||
replaceSQL, _, err = this.DoFilter(nil, nil, rawSQL, nil)
|
||||
gtest.AssertNil(err)
|
||||
// this SQL can't run ,clickhouse will report an error because there is no WHERE statement
|
||||
gtest.AssertEQ(replaceSQL, "ALTER TABLE visit update url = '1'")
|
||||
rawSQL = "delete from visit"
|
||||
replaceSQL, _, err = this.DoFilter(nil, nil, rawSQL, nil)
|
||||
gtest.AssertNil(err)
|
||||
// this SQL can't run ,clickhouse will report an error because there is no WHERE statement
|
||||
gtest.AssertEQ(replaceSQL, "ALTER TABLE visit delete")
|
||||
|
||||
rawSQL = "update visit set url = '1' where url = '0'"
|
||||
replaceSQL, _, err = this.DoFilter(nil, nil, rawSQL, nil)
|
||||
gtest.AssertNil(err)
|
||||
// this SQL can't run ,clickhouse will report an error because there is no WHERE statement
|
||||
gtest.AssertEQ(replaceSQL, "ALTER TABLE visit update url = '1' where url = '0'")
|
||||
rawSQL = "delete from visit where url='0'"
|
||||
replaceSQL, _, err = this.DoFilter(nil, nil, rawSQL, nil)
|
||||
gtest.AssertNil(err)
|
||||
// this SQL can't run ,clickhouse will report an error because there is no WHERE statement
|
||||
gtest.AssertEQ(replaceSQL, "ALTER TABLE visit delete where url='0'")
|
||||
}
|
||||
|
||||
func TestDriver_TableFields(t *testing.T) {
|
||||
connect := InitClickhouse()
|
||||
gtest.AssertNil(createClickhouseTable(connect))
|
||||
defer dropClickhouseTable(connect)
|
||||
field, err := connect.TableFields(context.Background(), "visits")
|
||||
func TestDriverClickhouse_NilTime(t *testing.T) {
|
||||
connect := clickhouseConfigDB()
|
||||
gtest.AssertNil(createClickhouseExampleTable(connect))
|
||||
defer dropClickhouseExampleTable(connect)
|
||||
type testNilTime struct {
|
||||
Col1 uint8
|
||||
Col2 string
|
||||
Col3 string
|
||||
Col4 string
|
||||
Col5 map[string]uint8
|
||||
Col6 []string
|
||||
Col7 []interface{}
|
||||
Col8 *time.Time
|
||||
Col9 uuid.UUID
|
||||
Col10 *gtime.Time
|
||||
}
|
||||
insertData := []*testNilTime{}
|
||||
for i := 0; i < 10000; i++ {
|
||||
insertData = append(insertData, &testNilTime{
|
||||
Col4: "Inc.",
|
||||
Col9: uuid.New(),
|
||||
Col7: []interface{}{ // Tuple(String, UInt8, Array(Map(String, String)))
|
||||
"String Value", uint8(5), []map[string]string{
|
||||
map[string]string{"key": "value"},
|
||||
map[string]string{"key": "value"},
|
||||
map[string]string{"key": "value"},
|
||||
}},
|
||||
})
|
||||
}
|
||||
_, err := connect.Model("data_type").Data(insertData).Insert()
|
||||
gtest.AssertNil(err)
|
||||
gtest.AssertEQ(len(field), 4)
|
||||
gtest.AssertNQ(field, nil)
|
||||
count, err := connect.Model("data_type").Where("Col4", "Inc.").Count()
|
||||
gtest.AssertNil(err)
|
||||
gtest.AssertEQ(count, 10000)
|
||||
}
|
||||
|
||||
func TestDriver_OpenLink(t *testing.T) {
|
||||
connect, err := gdb.New(gdb.ConfigNode{
|
||||
Link: "clickhouse://default@127.0.0.1:9000/default?dial_timeout=200ms&max_execution_time=60&skip_verify=true&secure=false&compress=true",
|
||||
func TestDriverClickhouse_BatchInsert(t *testing.T) {
|
||||
// example from
|
||||
// https://github.com/ClickHouse/clickhouse-go/blob/v2/examples/std/batch/main.go
|
||||
connect := clickhouseConfigDB()
|
||||
gtest.AssertNil(createClickhouseExampleTable(connect))
|
||||
defer dropClickhouseExampleTable(connect)
|
||||
insertData := []g.Map{}
|
||||
for i := 0; i < 10000; i++ {
|
||||
insertData = append(insertData, g.Map{
|
||||
"Col1": uint8(42),
|
||||
"Col2": "ClickHouse",
|
||||
"Col3": "Inc",
|
||||
"Col4": guid.S(),
|
||||
"Col5": map[string]uint8{"key": 1}, // Map(String, UInt8)
|
||||
"Col6": []string{"Q", "W", "E", "R", "T", "Y"}, // Array(String)
|
||||
"Col7": []interface{}{ // Tuple(String, UInt8, Array(Map(String, String)))
|
||||
"String Value", uint8(5), []map[string]string{
|
||||
map[string]string{"key": "value"},
|
||||
map[string]string{"key": "value"},
|
||||
map[string]string{"key": "value"},
|
||||
},
|
||||
},
|
||||
"Col8": gtime.Now(),
|
||||
"Col9": uuid.New(),
|
||||
"Col10": nil,
|
||||
})
|
||||
}
|
||||
_, err := connect.Model("data_type").Data(insertData).Insert()
|
||||
gtest.AssertNil(err)
|
||||
count, err := connect.Model("data_type").Where("Col2", "ClickHouse").Where("Col3", "Inc").Count()
|
||||
gtest.AssertNil(err)
|
||||
gtest.AssertEQ(count, 10000)
|
||||
}
|
||||
|
||||
func TestDriverClickhouse_Open(t *testing.T) {
|
||||
// link
|
||||
// DSM
|
||||
// clickhouse://username:password@host1:9000,host2:9000/database?dial_timeout=200ms&max_execution_time=60
|
||||
link := "clickhouse://default@127.0.0.1:9000,127.0.0.1:9000/default?dial_timeout=200ms&max_execution_time=60"
|
||||
db, err := gdb.New(gdb.ConfigNode{
|
||||
Link: link,
|
||||
Type: "clickhouse",
|
||||
})
|
||||
gtest.AssertNil(err)
|
||||
gtest.AssertNE(connect, nil)
|
||||
gtest.AssertNil(connect.PingMaster())
|
||||
gtest.AssertNil(db.PingMaster())
|
||||
}
|
||||
|
||||
func TestDriverClickhouse_ReplaceConfig(t *testing.T) {
|
||||
db := &Driver{}
|
||||
// parse link's name set to config
|
||||
c1 := &gdb.ConfigNode{}
|
||||
c1.Link = "clickhouse://default@127.0.0.1:9000,127.0.0.1:9000/default?dial_timeout=200ms&max_execution_time=60"
|
||||
_, _ = db.Open(c1)
|
||||
gtest.AssertEQ(c1.Name, "default")
|
||||
// replace link's name from config
|
||||
c2 := &gdb.ConfigNode{}
|
||||
c2.Name = "clickhouseJohn"
|
||||
c2.Link = "clickhouse://default@127.0.0.1:9000,127.0.0.1:9000/default?dial_timeout=200ms&max_execution_time=60"
|
||||
_, _ = db.Open(c2)
|
||||
gtest.AssertEQ(strings.Contains(c2.Link, "clickhouseJohn"), true)
|
||||
}
|
||||
|
||||
func TestDriverClickhouse_TableFields(t *testing.T) {
|
||||
connect := clickhouseConfigDB()
|
||||
gtest.AssertNil(createClickhouseExampleTable(connect))
|
||||
defer dropClickhouseExampleTable(connect)
|
||||
dataTypeTable, err := connect.TableFields(context.Background(), "data_type")
|
||||
gtest.AssertNil(err)
|
||||
gtest.AssertNE(dataTypeTable, nil)
|
||||
|
||||
var result = map[string][]interface{}{
|
||||
"Col1": {1, "Col1", "UInt8", false, "", "", "", "列1"},
|
||||
"Col2": {2, "Col2", "String", true, "", "", "", "列2"},
|
||||
"Col3": {3, "Col3", "FixedString(3)", false, "", "", "", "列3"},
|
||||
"Col4": {4, "Col4", "String", false, "", "", "", "列4"},
|
||||
"Col5": {5, "Col5", "Map(String, UInt8)", false, "", "", "", "列5"},
|
||||
"Col6": {6, "Col6", "Array(String)", false, "", "", "", "列6"},
|
||||
"Col7": {7, "Col7", "Tuple(String, UInt8, Array(Map(String, String)))", false, "", "", "", "列7"},
|
||||
"Col8": {8, "Col8", "DateTime", false, "", "", "", "列8"},
|
||||
"Col9": {9, "Col9", "UUID", false, "", "", "", "列9"},
|
||||
"Col10": {10, "Col10", "DateTime", false, "", "", "", "列10"},
|
||||
}
|
||||
for k, v := range result {
|
||||
_, ok := dataTypeTable[k]
|
||||
gtest.AssertEQ(ok, true)
|
||||
gtest.AssertEQ(dataTypeTable[k].Index, v[0])
|
||||
gtest.AssertEQ(dataTypeTable[k].Name, v[1])
|
||||
gtest.AssertEQ(dataTypeTable[k].Type, v[2])
|
||||
gtest.AssertEQ(dataTypeTable[k].Null, v[3])
|
||||
gtest.AssertEQ(dataTypeTable[k].Key, v[4])
|
||||
gtest.AssertEQ(dataTypeTable[k].Default, v[5])
|
||||
gtest.AssertEQ(dataTypeTable[k].Comment, v[7])
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,8 +3,12 @@ module github.com/gogf/gf/contrib/drivers/clickhouse/v2
|
||||
go 1.15
|
||||
|
||||
require (
|
||||
github.com/ClickHouse/clickhouse-go v1.5.2
|
||||
github.com/ClickHouse/clickhouse-go/v2 v2.0.15
|
||||
github.com/gogf/gf/v2 v2.0.0
|
||||
github.com/google/uuid v1.3.0
|
||||
)
|
||||
|
||||
replace github.com/gogf/gf/v2 => ../../../
|
||||
replace (
|
||||
github.com/ClickHouse/clickhouse-go/v2 => github.com/gogf/clickhouse-go/v2 v2.0.15-compatible
|
||||
github.com/gogf/gf/v2 => ../../../
|
||||
)
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
github.com/BurntSushi/toml v1.1.0 h1:ksErzDEI1khOiGPgpwuI7x2ebx/uXQNw7xJpn9Eq1+I=
|
||||
github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
||||
github.com/ClickHouse/clickhouse-go v1.5.2 h1:yXgaOZ8WEHrd+okvZXjzulSt1zS33nM4ujfx9lVncl8=
|
||||
github.com/ClickHouse/clickhouse-go v1.5.2/go.mod h1:EaI/sW7Azgz9UATzd5ZdZHRUhHgv5+JMS9NSr2smCJI=
|
||||
github.com/bkaradzic/go-lz4 v1.0.0 h1:RXc4wYsyz985CkXXeX04y4VnZFGG8Rd43pRaHsOXAKk=
|
||||
github.com/ClickHouse/clickhouse-go v1.5.4 h1:cKjXeYLNWVJIx2J1K6H2CqyRmfwVJVY1OV1coaaFcI0=
|
||||
github.com/ClickHouse/clickhouse-go v1.5.4/go.mod h1:EaI/sW7Azgz9UATzd5ZdZHRUhHgv5+JMS9NSr2smCJI=
|
||||
github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
|
||||
github.com/bkaradzic/go-lz4 v1.0.0/go.mod h1:0YdlkowM3VswSROI7qDxhRvJ3sLhlFrRRwjwegp5jy4=
|
||||
github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
|
||||
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
@ -11,7 +11,6 @@ github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5P
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/clbanning/mxj/v2 v2.5.5 h1:oT81vUeEiQQ/DcHbzSytRngP6Ky9O+L+0Bw0zSJag9E=
|
||||
github.com/clbanning/mxj/v2 v2.5.5/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s=
|
||||
github.com/cloudflare/golz4 v0.0.0-20150217214814-ef862a3cdc58 h1:F1EaeKL/ta07PY/k9Os/UFtwERei2/XzGemhpGnBKNg=
|
||||
github.com/cloudflare/golz4 v0.0.0-20150217214814-ef862a3cdc58/go.mod h1:EOBUe0h4xcZ5GoxqC5SDxFQ8gwyZPKQoEzownBlhI80=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
@ -29,10 +28,15 @@ github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0=
|
||||
github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM=
|
||||
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||
github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI=
|
||||
github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo=
|
||||
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||
github.com/gogf/clickhouse-go/v2 v2.0.15-compatible h1:VYgibtmI/u+hUQmtJpC7zzg1YJsDCXS052R7vCqGpDc=
|
||||
github.com/gogf/clickhouse-go/v2 v2.0.15-compatible/go.mod h1:Z21o82zD8FFqefOQDg93c0XITlxGbTsWQuRm588Azkk=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
@ -50,6 +54,10 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
||||
github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o=
|
||||
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
|
||||
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gorilla/handlers v1.4.2/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ=
|
||||
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
|
||||
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/grokify/html-strip-tags-go v0.0.1 h1:0fThFwLbW7P/kOiTBs03FsJSV9RM2M/Q/MOnCQxKMo0=
|
||||
@ -57,6 +65,8 @@ github.com/grokify/html-strip-tags-go v0.0.1/go.mod h1:2Su6romC5/1VXOQMaWL2yb618
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks=
|
||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo=
|
||||
github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
|
||||
@ -68,6 +78,7 @@ github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27k
|
||||
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
|
||||
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||
github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
||||
github.com/mkevac/debugcharts v0.0.0-20191222103121-ae1c48aa8615/go.mod h1:Ad7oeElCZqA1Ufj0U9/liOF4BtVepxRcTvr2ey7zTvM=
|
||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
|
||||
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
|
||||
@ -85,17 +96,33 @@ github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1y
|
||||
github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
|
||||
github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE=
|
||||
github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs=
|
||||
github.com/paulmach/orb v0.7.1 h1:Zha++Z5OX/l168sqHK3k4z18LDvr+YAO/VjK0ReQ9rU=
|
||||
github.com/paulmach/orb v0.7.1/go.mod h1:FWRlTgl88VI1RBx/MkrwWDRhQ96ctqMCh8boXhmqB/A=
|
||||
github.com/paulmach/protoscan v0.2.1/go.mod h1:SpcSwydNLrxUGSDvXvO0P7g7AuhJ7lcKfDlhJCDw2gY=
|
||||
github.com/pierrec/lz4 v2.0.5+incompatible h1:2xWsjqPFWcplujydGg4WmhC/6fZqK42wMM8aXeqhl0I=
|
||||
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
|
||||
github.com/pierrec/lz4/v4 v4.1.14 h1:+fL8AQEZtz/ijeNnpduH0bROTu0O3NZAlPjQxGn8LwE=
|
||||
github.com/pierrec/lz4/v4 v4.1.14/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/shirou/gopsutil v2.19.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
|
||||
github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
|
||||
github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4/go.mod h1:qsXQc7+bwAM3Q1u/4XEfrquwF8Lw7D7y5cD8CuHnfIc=
|
||||
github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8=
|
||||
github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.2 h1:4jaiDzPyXQvSd7D0EjG45355tLlV3VOECpq10pLC+8s=
|
||||
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
|
||||
github.com/tklauser/go-sysconf v0.3.10/go.mod h1:C8XykCvCb+Gn0oNCWPIlcb0RuglQTYaQ2hGm7jmxEFk=
|
||||
github.com/tklauser/numcpus v0.4.0/go.mod h1:1+UI3pD8NW14VMwdgJNJ1ESk2UnwhAnz5hMwiKKqXCQ=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
||||
go.opentelemetry.io/otel v1.7.0 h1:Z2lA3Tdch0iDcrhJXDIlC94XE+bxok1F9B+4Lz/lGsM=
|
||||
go.opentelemetry.io/otel v1.7.0/go.mod h1:5BdUoMIz5WEs0vt0CUEMtSSaTSHBBVwrhnz7+nrD5xk=
|
||||
go.opentelemetry.io/otel/sdk v1.7.0 h1:4OmStpcKVOfvDOgCt7UriAPtKolwIhxpnSNI/yK+1B0=
|
||||
@ -105,11 +132,13 @@ go.opentelemetry.io/otel/trace v1.7.0/go.mod h1:fzLSB9nqR2eXzxPXb2JW9IKE+ScyXA48
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
|
||||
@ -118,15 +147,18 @@ golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 h1:CIJ76btIcR3eFI5EgSo6k1qKw
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191220220014-0732a990476f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@ -138,8 +170,10 @@ golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad h1:ntjMns5wyP/fN65tdBD4g8J5w8n015+iIIs9rtjXkY0=
|
||||
golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220429233432-b5fbb4746d32 h1:Js08h5hqB5xyWR789+QqueR6sDE8mk+YvpETZ+F6X9Y=
|
||||
golang.org/x/sys v0.0.0-20220429233432-b5fbb4746d32/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
@ -148,7 +182,9 @@ golang.org/x/text v0.3.8-0.20211105212822-18b340fc7af2 h1:GLw7MR8AfAG2GmGcmVgObF
|
||||
golang.org/x/text v0.3.8-0.20211105212822-18b340fc7af2/go.mod h1:EFNZuWvGYxIRUEX+K8UmCFwYmZjqcrnq15ZuVldZkZ0=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
@ -162,8 +198,9 @@ google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miE
|
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=
|
||||
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
@ -175,5 +212,6 @@ gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
||||
@ -299,6 +299,7 @@ ORDER BY a.id,a.colorder`,
|
||||
}
|
||||
fields = make(map[string]*gdb.TableField)
|
||||
for i, m := range result {
|
||||
|
||||
fields[m["Field"].String()] = &gdb.TableField{
|
||||
Index: i,
|
||||
Name: m["Field"].String(),
|
||||
|
||||
@ -63,32 +63,42 @@ func TestTableFields(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
createTable("t_user")
|
||||
defer dropTable("t_user")
|
||||
var expect map[string]string = map[string]string{
|
||||
"ID": "numeric(10,0)",
|
||||
"PASSPORT": "VARCHAR(45)",
|
||||
"PASSWORD": "CHAR(32)",
|
||||
"NICKNAME": "VARCHAR(45)",
|
||||
"CREATE_TIME": "time",
|
||||
var expect = map[string][]interface{}{
|
||||
"ID": {"numeric(10,0)", false, "PRI", "", "", ""},
|
||||
"PASSPORT": {"varchar(45)", true, "", "", "", ""},
|
||||
"PASSWORD": {"varchar(32)", true, "", "", "", ""},
|
||||
"NICKNAME": {"varchar(45)", true, "", "", "", ""},
|
||||
"CREATE_TIME": {"datetime", true, "", "", "", ""},
|
||||
}
|
||||
|
||||
res, err := db.TableFields(context.Background(), "t_user")
|
||||
gtest.Assert(err, nil)
|
||||
|
||||
for k, _ := range expect {
|
||||
for k, v := range expect {
|
||||
_, ok := res[k]
|
||||
gtest.AssertEQ(ok, true)
|
||||
|
||||
gtest.AssertEQ(res[k].Name, k)
|
||||
gtest.AssertEQ(res[k].Type, v[0])
|
||||
gtest.AssertEQ(res[k].Null, v[1])
|
||||
gtest.AssertEQ(res[k].Key, v[2])
|
||||
gtest.AssertEQ(res[k].Default, v[3])
|
||||
gtest.AssertEQ(res[k].Extra, v[4])
|
||||
gtest.AssertEQ(res[k].Comment, v[5])
|
||||
}
|
||||
|
||||
res, err = db.TableFields(context.Background(), "t_user", "test")
|
||||
gtest.Assert(err, nil)
|
||||
|
||||
for k, _ := range expect {
|
||||
for k, v := range expect {
|
||||
_, ok := res[k]
|
||||
gtest.AssertEQ(ok, true)
|
||||
|
||||
gtest.AssertEQ(res[k].Name, k)
|
||||
gtest.AssertEQ(res[k].Type, v[0])
|
||||
gtest.AssertEQ(res[k].Null, v[1])
|
||||
gtest.AssertEQ(res[k].Key, v[2])
|
||||
gtest.AssertEQ(res[k].Default, v[3])
|
||||
gtest.AssertEQ(res[k].Extra, v[4])
|
||||
gtest.AssertEQ(res[k].Comment, v[5])
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@ -14,6 +14,7 @@ import (
|
||||
"net/url"
|
||||
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
|
||||
"github.com/gogf/gf/v2/container/gmap"
|
||||
"github.com/gogf/gf/v2/database/gdb"
|
||||
@ -34,8 +35,15 @@ var (
|
||||
)
|
||||
|
||||
func init() {
|
||||
if err := gdb.Register(`mysql`, New()); err != nil {
|
||||
panic(err)
|
||||
var (
|
||||
err error
|
||||
driverObj = New()
|
||||
driverNames = g.SliceStr{"mysql", "mariadb", "tidb"}
|
||||
)
|
||||
for _, driverName := range driverNames {
|
||||
if err = gdb.Register(driverName, driverObj); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -82,3 +82,27 @@ func Test_Model_InnerJoinOnField(t *testing.T) {
|
||||
t.Assert(r[1]["id"], "2")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Model_FieldsPrefix(t *testing.T) {
|
||||
var (
|
||||
table1 = gtime.TimestampNanoStr() + "_table1"
|
||||
table2 = gtime.TimestampNanoStr() + "_table2"
|
||||
)
|
||||
createInitTable(table1)
|
||||
defer dropTable(table1)
|
||||
createInitTable(table2)
|
||||
defer dropTable(table2)
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
r, err := db.Model(table1).
|
||||
FieldsPrefix(table1, "id").
|
||||
FieldsPrefix(table2, "nickname").
|
||||
LeftJoinOnField(table2, "id").
|
||||
WhereIn("id", g.Slice{1, 2}).
|
||||
Order("id asc").All()
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(r), 2)
|
||||
t.Assert(r[0]["id"], "1")
|
||||
t.Assert(r[0]["nickname"], "name_1")
|
||||
})
|
||||
}
|
||||
|
||||
@ -1,10 +1,36 @@
|
||||
module github.com/gogf/gf/contrib/drivers/oracle/v2
|
||||
|
||||
go 1.15
|
||||
go 1.17
|
||||
|
||||
require (
|
||||
github.com/gogf/gf/v2 v2.0.0
|
||||
github.com/mattn/go-oci8 v0.1.1
|
||||
github.com/sijms/go-ora/v2 v2.4.20
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/BurntSushi/toml v1.1.0 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.1.2 // indirect
|
||||
github.com/clbanning/mxj/v2 v2.5.5 // indirect
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||
github.com/fatih/color v1.13.0 // indirect
|
||||
github.com/fsnotify/fsnotify v1.5.4 // indirect
|
||||
github.com/go-logr/logr v1.2.3 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/go-redis/redis/v8 v8.11.5 // indirect
|
||||
github.com/gorilla/websocket v1.5.0 // indirect
|
||||
github.com/grokify/html-strip-tags-go v0.0.1 // indirect
|
||||
github.com/magiconair/properties v1.8.6 // indirect
|
||||
github.com/mattn/go-colorable v0.1.9 // indirect
|
||||
github.com/mattn/go-isatty v0.0.14 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.9 // indirect
|
||||
github.com/olekukonko/tablewriter v0.0.5 // indirect
|
||||
go.opentelemetry.io/otel v1.7.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk v1.7.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.7.0 // indirect
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 // indirect
|
||||
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad // indirect
|
||||
golang.org/x/text v0.3.8-0.20211105212822-18b340fc7af2 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
|
||||
)
|
||||
|
||||
replace github.com/gogf/gf/v2 => ../../../
|
||||
|
||||
@ -34,7 +34,6 @@ github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:W
|
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
@ -56,8 +55,6 @@ github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
|
||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||
github.com/mattn/go-oci8 v0.1.1 h1:aEUDxNAyDG0tv8CA3TArnDQNyc4EhnWlsfxRgDHABHM=
|
||||
github.com/mattn/go-oci8 v0.1.1/go.mod h1:wjDx6Xm9q7dFtHJvIlrI99JytznLw5wQ4R+9mNXJwGI=
|
||||
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
|
||||
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||
@ -70,7 +67,6 @@ github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108
|
||||
github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
|
||||
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
|
||||
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
|
||||
github.com/onsi/ginkgo/v2 v2.0.0 h1:CcuG/HvWNkkaqCUpJifQY8z7qEMBJya6aLPx6ftGyjQ=
|
||||
github.com/onsi/ginkgo/v2 v2.0.0/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c=
|
||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
||||
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
||||
@ -79,6 +75,8 @@ github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE=
|
||||
github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/sijms/go-ora/v2 v2.4.20 h1:9e3z7VLBQXRAHGiIda1GEFtRhfxata0LghyMZqvLKew=
|
||||
github.com/sijms/go-ora/v2 v2.4.20/go.mod h1:EHxlY6x7y9HAsdfumurRfTd+v8NrEOTR3Xl4FWlH6xk=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
|
||||
@ -142,7 +140,6 @@ golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
@ -151,7 +148,6 @@ google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miE
|
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
//
|
||||
// Note:
|
||||
// 1. It needs manually import: _ "github.com/mattn/go-oci8"
|
||||
// 1. It needs manually import: _ "github.com/sijms/go-ora/v2"
|
||||
// 2. It does not support Save/Replace features.
|
||||
// 3. It does not support LastInsertId.
|
||||
|
||||
@ -16,12 +16,7 @@ import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
_ "github.com/mattn/go-oci8"
|
||||
gora "github.com/sijms/go-ora/v2"
|
||||
|
||||
"github.com/gogf/gf/v2/container/gmap"
|
||||
"github.com/gogf/gf/v2/database/gdb"
|
||||
@ -30,6 +25,8 @@ import (
|
||||
"github.com/gogf/gf/v2/text/gregex"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Driver is the driver for oracle database.
|
||||
@ -65,9 +62,18 @@ func (d *Driver) New(core *gdb.Core, node *gdb.ConfigNode) (gdb.DB, error) {
|
||||
func (d *Driver) Open(config *gdb.ConfigNode) (db *sql.DB, err error) {
|
||||
var (
|
||||
source string
|
||||
underlyingDriverName = "oci8"
|
||||
underlyingDriverName = "oracle"
|
||||
)
|
||||
// [username/[password]@]host[:port][/service_name][?param1=value1&...¶mN=valueN]
|
||||
|
||||
options := map[string]string{
|
||||
"CONNECTION TIMEOUT": "60",
|
||||
"PREFETCH_ROWS": "25",
|
||||
}
|
||||
|
||||
if config.Debug {
|
||||
options["TRACE FILE"] = "oracle_trace.log"
|
||||
}
|
||||
// [username:[password]@]host[:port][/service_name][?param1=value1&...¶mN=valueN]
|
||||
if config.Link != "" {
|
||||
source = config.Link
|
||||
// Custom changing the schema in runtime.
|
||||
@ -75,10 +81,7 @@ func (d *Driver) Open(config *gdb.ConfigNode) (db *sql.DB, err error) {
|
||||
source, _ = gregex.ReplaceString(`@(.+?)/([\w\.\-]+)+`, "@$1/"+config.Name, source)
|
||||
}
|
||||
} else {
|
||||
source = fmt.Sprintf(
|
||||
"%s/%s@%s:%s/%s",
|
||||
config.User, config.Pass, config.Host, config.Port, config.Name,
|
||||
)
|
||||
source = gora.BuildUrl(config.Host, gconv.Int(config.Port), config.Name, config.User, config.Pass, options)
|
||||
}
|
||||
|
||||
if db, err = sql.Open(underlyingDriverName, source); err != nil {
|
||||
@ -99,8 +102,8 @@ func (d *Driver) FilteredLink() string {
|
||||
return ""
|
||||
}
|
||||
s, _ := gregex.ReplaceString(
|
||||
`(.+?)\s*/\s*(.+)\s*@\s*(.+)\s*:\s*(\d+)\s*/\s*(.+)`,
|
||||
`$1/xxx@$3:$4/$5`,
|
||||
`(.+?)\s*:\s*(.+)\s*@\s*(.+)\s*:\s*(\d+)\s*/\s*(.+)`,
|
||||
`$1:xxx@$3:$4/$5`,
|
||||
linkInfo,
|
||||
)
|
||||
return s
|
||||
@ -124,16 +127,7 @@ func (d *Driver) DoFilter(ctx context.Context, link gdb.Link, sql string, args [
|
||||
return fmt.Sprintf(":v%d", index)
|
||||
})
|
||||
newSql, _ = gregex.ReplaceString("\"", "", newSql)
|
||||
// Handle string datetime argument.
|
||||
for i, v := range args {
|
||||
if reflect.TypeOf(v).Kind() == reflect.String {
|
||||
valueStr := gconv.String(v)
|
||||
if gregex.IsMatchString(`^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$`, valueStr) {
|
||||
// args[i] = fmt.Sprintf(`TO_DATE('%s','yyyy-MM-dd HH:MI:SS')`, valueStr)
|
||||
args[i], _ = time.ParseInLocation("2006-01-02 15:04:05", valueStr, time.Local)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
newSql = d.parseSql(newSql)
|
||||
newArgs = args
|
||||
return
|
||||
@ -168,7 +162,7 @@ func (d *Driver) parseSql(sql string) string {
|
||||
strings.EqualFold(queryExpr[3], "LIMIT") == false {
|
||||
break
|
||||
}
|
||||
first, limit := 0, 0
|
||||
page, limit := 0, 0
|
||||
for i := 1; i < len(allMatch[index]); i++ {
|
||||
if len(strings.TrimSpace(allMatch[index][i])) == 0 {
|
||||
continue
|
||||
@ -176,8 +170,16 @@ func (d *Driver) parseSql(sql string) string {
|
||||
|
||||
if strings.HasPrefix(allMatch[index][i], "LIMIT") {
|
||||
if allMatch[index][i+2] != "" {
|
||||
first, _ = strconv.Atoi(allMatch[index][i+1])
|
||||
page, _ = strconv.Atoi(allMatch[index][i+1])
|
||||
limit, _ = strconv.Atoi(allMatch[index][i+2])
|
||||
|
||||
if page <= 0 {
|
||||
page = 1
|
||||
}
|
||||
|
||||
limit = (page/limit + 1) * limit
|
||||
|
||||
page, _ = strconv.Atoi(allMatch[index][i+1])
|
||||
} else {
|
||||
limit, _ = strconv.Atoi(allMatch[index][i+1])
|
||||
}
|
||||
@ -187,8 +189,8 @@ func (d *Driver) parseSql(sql string) string {
|
||||
sql = fmt.Sprintf(
|
||||
"SELECT * FROM "+
|
||||
"(SELECT GFORM.*, ROWNUM ROWNUM_ FROM (%s %s) GFORM WHERE ROWNUM <= %d)"+
|
||||
" WHERE ROWNUM_ >= %d",
|
||||
queryExpr[1], queryExpr[2], limit, first,
|
||||
" WHERE ROWNUM_ > %d",
|
||||
queryExpr[1], queryExpr[2], limit, page,
|
||||
)
|
||||
}
|
||||
return sql
|
||||
@ -241,7 +243,7 @@ SELECT
|
||||
CASE DATA_TYPE
|
||||
WHEN 'NUMBER' THEN DATA_TYPE||'('||DATA_PRECISION||','||DATA_SCALE||')'
|
||||
WHEN 'FLOAT' THEN DATA_TYPE||'('||DATA_PRECISION||','||DATA_SCALE||')'
|
||||
ELSE DATA_TYPE||'('||DATA_LENGTH||')' END AS TYPE
|
||||
ELSE DATA_TYPE||'('||DATA_LENGTH||')' END AS TYPE,NULLABLE
|
||||
FROM USER_TAB_COLUMNS WHERE TABLE_NAME = '%s' ORDER BY COLUMN_ID`,
|
||||
strings.ToUpper(table),
|
||||
)
|
||||
@ -256,10 +258,16 @@ FROM USER_TAB_COLUMNS WHERE TABLE_NAME = '%s' ORDER BY COLUMN_ID`,
|
||||
}
|
||||
fields = make(map[string]*gdb.TableField)
|
||||
for i, m := range result {
|
||||
fields[strings.ToLower(m["FIELD"].String())] = &gdb.TableField{
|
||||
isNull := false
|
||||
if m["NULLABLE"].String() == "Y" {
|
||||
isNull = true
|
||||
}
|
||||
|
||||
fields[m["FIELD"].String()] = &gdb.TableField{
|
||||
Index: i,
|
||||
Name: strings.ToLower(m["FIELD"].String()),
|
||||
Type: strings.ToLower(m["TYPE"].String()),
|
||||
Name: m["FIELD"].String(),
|
||||
Type: m["TYPE"].String(),
|
||||
Null: isNull,
|
||||
}
|
||||
}
|
||||
return fields
|
||||
@ -288,10 +296,10 @@ func (d *Driver) DoInsert(
|
||||
) (result sql.Result, err error) {
|
||||
switch option.InsertOption {
|
||||
case gdb.InsertOptionSave:
|
||||
return nil, gerror.NewCode(gcode.CodeNotSupported, `Save operation is not supported by mssql driver`)
|
||||
return nil, gerror.NewCode(gcode.CodeNotSupported, `Save operation is not supported by oracle driver`)
|
||||
|
||||
case gdb.InsertOptionReplace:
|
||||
return nil, gerror.NewCode(gcode.CodeNotSupported, `Replace operation is not supported by mssql driver`)
|
||||
return nil, gerror.NewCode(gcode.CodeNotSupported, `Replace operation is not supported by oracle driver`)
|
||||
}
|
||||
|
||||
var (
|
||||
|
||||
162
contrib/drivers/oracle/oracle_init_test.go
Normal file
162
contrib/drivers/oracle/oracle_init_test.go
Normal file
@ -0,0 +1,162 @@
|
||||
// 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 this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package oracle_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/v2/container/garray"
|
||||
"github.com/gogf/gf/v2/database/gdb"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
"github.com/gogf/gf/v2/test/gtest"
|
||||
_ "github.com/sijms/go-ora/v2"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
db gdb.DB
|
||||
dblink gdb.DB
|
||||
dbErr gdb.DB
|
||||
ctx context.Context
|
||||
)
|
||||
|
||||
const (
|
||||
TableSize = 10
|
||||
TableName = "t_user"
|
||||
TestSchema1 = "test1"
|
||||
TestSchema2 = "test2"
|
||||
TableNamePrefix1 = "gf_"
|
||||
TestSchema = "XE"
|
||||
)
|
||||
|
||||
const (
|
||||
TestDbIP = "127.0.0.1"
|
||||
TestDbPort = "1521"
|
||||
TestDbUser = "system"
|
||||
TestDbPass = "oracle"
|
||||
TestDbName = "XE"
|
||||
TestDbType = "oracle"
|
||||
)
|
||||
|
||||
func init() {
|
||||
node := gdb.ConfigNode{
|
||||
Host: TestDbIP,
|
||||
Port: TestDbPort,
|
||||
User: TestDbUser,
|
||||
Pass: TestDbPass,
|
||||
Name: TestDbName,
|
||||
Type: TestDbType,
|
||||
Role: "master",
|
||||
Charset: "utf8",
|
||||
Weight: 1,
|
||||
MaxIdleConnCount: 10,
|
||||
MaxOpenConnCount: 10,
|
||||
}
|
||||
|
||||
nodeLink := gdb.ConfigNode{
|
||||
Type: TestDbType,
|
||||
Name: TestDbName,
|
||||
Link: fmt.Sprintf("%s://%s:%s@%s:%s/%s",
|
||||
TestDbType, TestDbUser, TestDbPass, TestDbIP, TestDbPort, TestDbName),
|
||||
}
|
||||
|
||||
nodeErr := gdb.ConfigNode{
|
||||
Host: TestDbIP,
|
||||
Port: TestDbPort,
|
||||
User: TestDbUser,
|
||||
Pass: "1234",
|
||||
Name: TestDbName,
|
||||
Type: TestDbType,
|
||||
Role: "master",
|
||||
Charset: "utf8",
|
||||
Weight: 1,
|
||||
}
|
||||
|
||||
gdb.AddConfigNode(gdb.DefaultGroupName, node)
|
||||
if r, err := gdb.New(node); err != nil {
|
||||
gtest.Fatal(err)
|
||||
} else {
|
||||
db = r
|
||||
}
|
||||
|
||||
gdb.AddConfigNode("dblink", nodeLink)
|
||||
if r, err := gdb.New(nodeLink); err != nil {
|
||||
gtest.Fatal(err)
|
||||
} else {
|
||||
dblink = r
|
||||
}
|
||||
|
||||
gdb.AddConfigNode("dbErr", nodeErr)
|
||||
if r, err := gdb.New(nodeErr); err != nil {
|
||||
gtest.Fatal(err)
|
||||
} else {
|
||||
dbErr = r
|
||||
}
|
||||
|
||||
ctx = context.Background()
|
||||
}
|
||||
|
||||
func createTable(table ...string) (name string) {
|
||||
if len(table) > 0 {
|
||||
name = table[0]
|
||||
} else {
|
||||
name = fmt.Sprintf("user_%d", gtime.Timestamp())
|
||||
}
|
||||
|
||||
dropTable(name)
|
||||
|
||||
if _, err := db.Exec(ctx, fmt.Sprintf(`
|
||||
CREATE TABLE %s (
|
||||
ID NUMBER(10) NOT NULL,
|
||||
PASSPORT VARCHAR(45) NOT NULL,
|
||||
PASSWORD CHAR(32) NOT NULL,
|
||||
NICKNAME VARCHAR(45) NOT NULL,
|
||||
CREATE_TIME varchar(45),
|
||||
PRIMARY KEY (ID))
|
||||
`, name)); err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
|
||||
//db.Schema("test")
|
||||
return
|
||||
}
|
||||
|
||||
func createInitTable(table ...string) (name string) {
|
||||
name = createTable(table...)
|
||||
array := garray.New(true)
|
||||
for i := 1; i <= TableSize; i++ {
|
||||
array.Append(g.Map{
|
||||
"id": i,
|
||||
"passport": fmt.Sprintf(`user_%d`, i),
|
||||
"password": fmt.Sprintf(`pass_%d`, i),
|
||||
"nickname": fmt.Sprintf(`name_%d`, i),
|
||||
"create_time": gtime.Now().String(),
|
||||
})
|
||||
}
|
||||
result, err := db.Insert(context.Background(), name, array.Slice())
|
||||
gtest.Assert(err, nil)
|
||||
|
||||
n, e := result.RowsAffected()
|
||||
gtest.Assert(e, nil)
|
||||
gtest.Assert(n, TableSize)
|
||||
return
|
||||
}
|
||||
|
||||
func dropTable(table string) {
|
||||
count, err := db.GetCount(ctx, "SELECT COUNT(*) FROM USER_TABLES WHERE TABLE_NAME = ?", strings.ToUpper(table))
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
|
||||
if count == 0 {
|
||||
return
|
||||
}
|
||||
if _, err := db.Exec(ctx, fmt.Sprintf("DROP TABLE %s", table)); err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
}
|
||||
665
contrib/drivers/oracle/oracle_z_basic_test.go
Normal file
665
contrib/drivers/oracle/oracle_z_basic_test.go
Normal file
@ -0,0 +1,665 @@
|
||||
// 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 this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package oracle_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
"github.com/gogf/gf/v2/test/gtest"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestTables(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
tables := []string{"t_user1", "pop", "haha"}
|
||||
|
||||
for _, v := range tables {
|
||||
createTable(v)
|
||||
}
|
||||
|
||||
result, err := db.Tables(ctx)
|
||||
gtest.Assert(err, nil)
|
||||
|
||||
for i := 0; i < len(tables); i++ {
|
||||
find := false
|
||||
for j := 0; j < len(result); j++ {
|
||||
if strings.ToUpper(tables[i]) == result[j] {
|
||||
find = true
|
||||
break
|
||||
}
|
||||
}
|
||||
gtest.AssertEQ(find, true)
|
||||
}
|
||||
|
||||
result, err = db.Tables(ctx, TestSchema)
|
||||
gtest.Assert(err, nil)
|
||||
for i := 0; i < len(tables); i++ {
|
||||
find := false
|
||||
for j := 0; j < len(result); j++ {
|
||||
if strings.ToUpper(tables[i]) == result[j] {
|
||||
find = true
|
||||
break
|
||||
}
|
||||
}
|
||||
gtest.AssertEQ(find, true)
|
||||
}
|
||||
|
||||
for _, v := range tables {
|
||||
dropTable(v)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestTableFields(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
createTable("t_user")
|
||||
defer dropTable("t_user")
|
||||
var expect = map[string][]interface{}{
|
||||
"ID": {"NUMBER(10,0)", false},
|
||||
"PASSPORT": {"VARCHAR2(45)", false},
|
||||
"PASSWORD": {"CHAR(32)", false},
|
||||
"NICKNAME": {"VARCHAR2(45)", false},
|
||||
"CREATE_TIME": {"VARCHAR2(45)", true},
|
||||
}
|
||||
|
||||
_, err := dbErr.TableFields(ctx, "t_user")
|
||||
gtest.AssertNE(err, nil)
|
||||
|
||||
res, err := db.TableFields(ctx, "t_user")
|
||||
gtest.Assert(err, nil)
|
||||
|
||||
for k, v := range expect {
|
||||
_, ok := res[k]
|
||||
gtest.AssertEQ(ok, true)
|
||||
|
||||
gtest.AssertEQ(res[k].Name, k)
|
||||
gtest.Assert(res[k].Type, v[0])
|
||||
gtest.Assert(res[k].Null, v[1])
|
||||
}
|
||||
|
||||
res, err = db.TableFields(ctx, "t_user", TestSchema)
|
||||
gtest.Assert(err, nil)
|
||||
|
||||
for k, v := range expect {
|
||||
_, ok := res[k]
|
||||
gtest.AssertEQ(ok, true)
|
||||
|
||||
gtest.AssertEQ(res[k].Name, k)
|
||||
gtest.Assert(res[k].Type, v[0])
|
||||
gtest.Assert(res[k].Null, v[1])
|
||||
}
|
||||
})
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
_, err := db.TableFields(ctx, "t_user t_user2")
|
||||
gtest.AssertNE(err, nil)
|
||||
})
|
||||
}
|
||||
|
||||
func TestFilteredLink(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s := dblink.FilteredLink()
|
||||
gtest.AssertEQ(s, "oracle:xxx@127.0.0.1:1521/XE")
|
||||
})
|
||||
|
||||
}
|
||||
func TestDoInsert(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
createTable("t_user")
|
||||
defer dropTable("t_user")
|
||||
|
||||
i := 10
|
||||
data := g.Map{
|
||||
"ID": i,
|
||||
"PASSPORT": fmt.Sprintf(`t%d`, i),
|
||||
"PASSWORD": fmt.Sprintf(`p%d`, i),
|
||||
"NICKNAME": fmt.Sprintf(`T%d`, i),
|
||||
"CREATE_TIME": gtime.Now().String(),
|
||||
}
|
||||
_, err := db.Insert(ctx, "t_user", data)
|
||||
gtest.Assert(err, nil)
|
||||
|
||||
})
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
createTable("t_user")
|
||||
defer dropTable("t_user")
|
||||
|
||||
i := 10
|
||||
data := g.Map{
|
||||
"ID": i,
|
||||
"PASSPORT": fmt.Sprintf(`t%d`, i),
|
||||
"PASSWORD": fmt.Sprintf(`p%d`, i),
|
||||
"NICKNAME": fmt.Sprintf(`T%d`, i),
|
||||
"CREATE_TIME": gtime.Now().String(),
|
||||
}
|
||||
_, err := db.Save(ctx, "t_user", data, 10)
|
||||
gtest.AssertNE(err, nil)
|
||||
|
||||
_, err = db.Replace(ctx, "t_user", data, 10)
|
||||
gtest.AssertNE(err, nil)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_DB_Ping(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
err1 := db.PingMaster()
|
||||
err2 := db.PingSlave()
|
||||
t.Assert(err1, nil)
|
||||
t.Assert(err2, nil)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_DB_Query(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
_, err := db.Query(ctx, "SELECT ? from dual", 1)
|
||||
t.AssertNil(err)
|
||||
|
||||
_, err = db.Query(ctx, "SELECT ?+? from dual", 1, 2)
|
||||
t.AssertNil(err)
|
||||
|
||||
_, err = db.Query(ctx, "SELECT ?+? from dual", g.Slice{1, 2})
|
||||
t.AssertNil(err)
|
||||
|
||||
_, err = db.Query(ctx, "ERROR")
|
||||
t.AssertNE(err, nil)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_DB_Exec(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
_, err := db.Exec(ctx, "SELECT ? from dual", 1)
|
||||
t.AssertNil(err)
|
||||
|
||||
_, err = db.Exec(ctx, "ERROR")
|
||||
t.AssertNE(err, nil)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_DB_Insert(t *testing.T) {
|
||||
table := createTable()
|
||||
defer dropTable(table)
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
_, err := db.Insert(ctx, table, g.Map{
|
||||
"ID": 1,
|
||||
"PASSPORT": "t1",
|
||||
"PASSWORD": "25d55ad283aa400af464c76d713c07ad",
|
||||
"NICKNAME": "T1",
|
||||
"CREATE_TIME": gtime.Now().String(),
|
||||
})
|
||||
t.AssertNil(err)
|
||||
|
||||
// normal map
|
||||
result, err := db.Insert(ctx, table, g.Map{
|
||||
"ID": "2",
|
||||
"PASSPORT": "t2",
|
||||
"PASSWORD": "25d55ad283aa400af464c76d713c07ad",
|
||||
"NICKNAME": "name_2",
|
||||
"CREATE_TIME": gtime.Now().String(),
|
||||
})
|
||||
t.AssertNil(err)
|
||||
n, _ := result.RowsAffected()
|
||||
t.Assert(n, 1)
|
||||
|
||||
// struct
|
||||
type User struct {
|
||||
Id int `gconv:"ID"`
|
||||
Passport string `json:"PASSPORT"`
|
||||
Password string `gconv:"PASSWORD"`
|
||||
Nickname string `gconv:"NICKNAME"`
|
||||
CreateTime string `json:"CREATE_TIME"`
|
||||
}
|
||||
timeStr := gtime.Now().String()
|
||||
result, err = db.Insert(ctx, table, User{
|
||||
Id: 3,
|
||||
Passport: "user_3",
|
||||
Password: "25d55ad283aa400af464c76d713c07ad",
|
||||
Nickname: "name_3",
|
||||
CreateTime: timeStr,
|
||||
})
|
||||
t.AssertNil(err)
|
||||
n, _ = result.RowsAffected()
|
||||
t.Assert(n, 1)
|
||||
|
||||
one, err := db.Model(table).Where("ID", 3).One()
|
||||
t.AssertNil(err)
|
||||
fmt.Println(one)
|
||||
t.Assert(one["ID"].Int(), 3)
|
||||
t.Assert(one["PASSPORT"].String(), "user_3")
|
||||
t.Assert(one["PASSWORD"].String(), "25d55ad283aa400af464c76d713c07ad")
|
||||
t.Assert(one["NICKNAME"].String(), "name_3")
|
||||
t.Assert(one["CREATE_TIME"].GTime().String(), timeStr)
|
||||
|
||||
// *struct
|
||||
timeStr = gtime.Now().String()
|
||||
result, err = db.Insert(ctx, table, &User{
|
||||
Id: 4,
|
||||
Passport: "t4",
|
||||
Password: "25d55ad283aa400af464c76d713c07ad",
|
||||
Nickname: "name_4",
|
||||
CreateTime: timeStr,
|
||||
})
|
||||
t.AssertNil(err)
|
||||
n, _ = result.RowsAffected()
|
||||
t.Assert(n, 1)
|
||||
|
||||
one, err = db.Model(table).Where("ID", 4).One()
|
||||
t.AssertNil(err)
|
||||
t.Assert(one["ID"].Int(), 4)
|
||||
t.Assert(one["PASSPORT"].String(), "t4")
|
||||
t.Assert(one["PASSWORD"].String(), "25d55ad283aa400af464c76d713c07ad")
|
||||
t.Assert(one["NICKNAME"].String(), "name_4")
|
||||
t.Assert(one["CREATE_TIME"].GTime().String(), timeStr)
|
||||
|
||||
// batch with Insert
|
||||
timeStr = gtime.Now().String()
|
||||
r, err := db.Insert(ctx, table, g.Slice{
|
||||
g.Map{
|
||||
"ID": 200,
|
||||
"PASSPORT": "t200",
|
||||
"PASSWORD": "25d55ad283aa400af464c76d71qw07ad",
|
||||
"NICKNAME": "T200",
|
||||
"CREATE_TIME": timeStr,
|
||||
},
|
||||
g.Map{
|
||||
"ID": 300,
|
||||
"PASSPORT": "t300",
|
||||
"PASSWORD": "25d55ad283aa400af464c76d713c07ad",
|
||||
"NICKNAME": "T300",
|
||||
"CREATE_TIME": timeStr,
|
||||
},
|
||||
})
|
||||
t.AssertNil(err)
|
||||
n, _ = r.RowsAffected()
|
||||
t.Assert(n, 2)
|
||||
|
||||
one, err = db.Model(table).Where("ID", 200).One()
|
||||
t.AssertNil(err)
|
||||
t.Assert(one["ID"].Int(), 200)
|
||||
t.Assert(one["PASSPORT"].String(), "t200")
|
||||
t.Assert(one["PASSWORD"].String(), "25d55ad283aa400af464c76d71qw07ad")
|
||||
t.Assert(one["NICKNAME"].String(), "T200")
|
||||
t.Assert(one["CREATE_TIME"].GTime().String(), timeStr)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_DB_BatchInsert(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
table := createTable()
|
||||
defer dropTable(table)
|
||||
r, err := db.Insert(ctx, table, g.List{
|
||||
{
|
||||
"ID": 2,
|
||||
"PASSPORT": "t2",
|
||||
"PASSWORD": "25d55ad283aa400af464c76d713c07ad",
|
||||
"NICKNAME": "name_2",
|
||||
"CREATE_TIME": gtime.Now().String(),
|
||||
},
|
||||
{
|
||||
"ID": 3,
|
||||
"PASSPORT": "user_3",
|
||||
"PASSWORD": "25d55ad283aa400af464c76d713c07ad",
|
||||
"NICKNAME": "name_3",
|
||||
"CREATE_TIME": gtime.Now().String(),
|
||||
},
|
||||
}, 1)
|
||||
t.AssertNil(err)
|
||||
n, _ := r.RowsAffected()
|
||||
t.Assert(n, 2)
|
||||
|
||||
})
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
table := createTable()
|
||||
defer dropTable(table)
|
||||
// []interface{}
|
||||
r, err := db.Insert(ctx, table, g.Slice{
|
||||
g.Map{
|
||||
"ID": 2,
|
||||
"PASSPORT": "t2",
|
||||
"PASSWORD": "25d55ad283aa400af464c76d713c07ad",
|
||||
"NICKNAME": "name_2",
|
||||
"CREATE_TIME": gtime.Now().String(),
|
||||
},
|
||||
g.Map{
|
||||
"ID": 3,
|
||||
"PASSPORT": "user_3",
|
||||
"PASSWORD": "25d55ad283aa400af464c76d713c07ad",
|
||||
"NICKNAME": "name_3",
|
||||
"CREATE_TIME": gtime.Now().String(),
|
||||
},
|
||||
}, 1)
|
||||
t.AssertNil(err)
|
||||
n, _ := r.RowsAffected()
|
||||
t.Assert(n, 2)
|
||||
})
|
||||
|
||||
// batch insert map
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
table := createTable()
|
||||
defer dropTable(table)
|
||||
result, err := db.Insert(ctx, table, g.Map{
|
||||
"ID": 1,
|
||||
"PASSPORT": "t1",
|
||||
"PASSWORD": "p1",
|
||||
"NICKNAME": "T1",
|
||||
"CREATE_TIME": gtime.Now().String(),
|
||||
})
|
||||
t.AssertNil(err)
|
||||
n, _ := result.RowsAffected()
|
||||
t.Assert(n, 1)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_DB_BatchInsert_Struct(t *testing.T) {
|
||||
// batch insert struct
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
table := createTable()
|
||||
defer dropTable(table)
|
||||
|
||||
type User struct {
|
||||
Id int `c:"ID"`
|
||||
Passport string `c:"PASSPORT"`
|
||||
Password string `c:"PASSWORD"`
|
||||
NickName string `c:"NICKNAME"`
|
||||
CreateTime *gtime.Time `c:"CREATE_TIME"`
|
||||
}
|
||||
user := &User{
|
||||
Id: 1,
|
||||
Passport: "t1",
|
||||
Password: "p1",
|
||||
NickName: "T1",
|
||||
CreateTime: gtime.Now(),
|
||||
}
|
||||
result, err := db.Insert(ctx, table, user)
|
||||
t.AssertNil(err)
|
||||
n, _ := result.RowsAffected()
|
||||
t.Assert(n, 1)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_DB_Update(t *testing.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
result, err := db.Update(ctx, table, "password='987654321'", "id=3")
|
||||
t.AssertNil(err)
|
||||
n, _ := result.RowsAffected()
|
||||
t.Assert(n, 1)
|
||||
|
||||
one, err := db.Model(table).Where("ID", 3).One()
|
||||
t.AssertNil(err)
|
||||
t.Assert(one["ID"].Int(), 3)
|
||||
t.Assert(one["PASSPORT"].String(), "user_3")
|
||||
t.Assert(strings.TrimSpace(one["PASSWORD"].String()), "987654321")
|
||||
t.Assert(one["NICKNAME"].String(), "name_3")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_DB_GetAll(t *testing.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
result, err := db.GetAll(ctx, fmt.Sprintf("SELECT * FROM %s WHERE id=?", table), 1)
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(result), 1)
|
||||
t.Assert(result[0]["ID"].Int(), 1)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
result, err := db.GetAll(ctx, fmt.Sprintf("SELECT * FROM %s WHERE id=?", table), g.Slice{1})
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(result), 1)
|
||||
t.Assert(result[0]["ID"].Int(), 1)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
result, err := db.GetAll(ctx, fmt.Sprintf("SELECT * FROM %s WHERE id in(?)", table), g.Slice{1, 2, 3})
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(result), 3)
|
||||
t.Assert(result[0]["ID"].Int(), 1)
|
||||
t.Assert(result[1]["ID"].Int(), 2)
|
||||
t.Assert(result[2]["ID"].Int(), 3)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
result, err := db.GetAll(ctx, fmt.Sprintf("SELECT * FROM %s WHERE id in(?,?,?)", table), g.Slice{1, 2, 3})
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(result), 3)
|
||||
t.Assert(result[0]["ID"].Int(), 1)
|
||||
t.Assert(result[1]["ID"].Int(), 2)
|
||||
t.Assert(result[2]["ID"].Int(), 3)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
result, err := db.GetAll(ctx, fmt.Sprintf("SELECT * FROM %s WHERE id in(?,?,?)", table), g.Slice{1, 2, 3}...)
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(result), 3)
|
||||
t.Assert(result[0]["ID"].Int(), 1)
|
||||
t.Assert(result[1]["ID"].Int(), 2)
|
||||
t.Assert(result[2]["ID"].Int(), 3)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
result, err := db.GetAll(ctx, fmt.Sprintf("SELECT * FROM %s WHERE id>=? AND id <=?", table), g.Slice{1, 3})
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(result), 3)
|
||||
t.Assert(result[0]["ID"].Int(), 1)
|
||||
t.Assert(result[1]["ID"].Int(), 2)
|
||||
t.Assert(result[2]["ID"].Int(), 3)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_DB_GetOne(t *testing.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
record, err := db.GetOne(ctx, fmt.Sprintf("SELECT * FROM %s WHERE passport=?", table), "user_1")
|
||||
t.AssertNil(err)
|
||||
t.Assert(record["NICKNAME"].String(), "name_1")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_DB_GetValue(t *testing.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
value, err := db.GetValue(ctx, fmt.Sprintf("SELECT id FROM %s WHERE passport=?", table), "user_3")
|
||||
t.AssertNil(err)
|
||||
t.Assert(value.Int(), 3)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_DB_GetCount(t *testing.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
count, err := db.GetCount(ctx, fmt.Sprintf("SELECT * FROM %s", table))
|
||||
t.AssertNil(err)
|
||||
t.Assert(count, TableSize)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_DB_GetStruct(t *testing.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type User struct {
|
||||
Id int
|
||||
Passport string
|
||||
Password string
|
||||
NickName string
|
||||
CreateTime gtime.Time
|
||||
}
|
||||
user := new(User)
|
||||
err := db.GetScan(ctx, user, fmt.Sprintf("SELECT * FROM %s WHERE id=?", table), 3)
|
||||
t.AssertNil(err)
|
||||
t.Assert(user.NickName, "name_3")
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type User struct {
|
||||
Id int
|
||||
Passport string
|
||||
Password string
|
||||
NickName string
|
||||
CreateTime *gtime.Time
|
||||
}
|
||||
user := new(User)
|
||||
err := db.GetScan(ctx, user, fmt.Sprintf("SELECT * FROM %s WHERE id=?", table), 3)
|
||||
t.AssertNil(err)
|
||||
t.Assert(user.NickName, "name_3")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_DB_GetStructs(t *testing.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type User struct {
|
||||
Id int
|
||||
Passport string
|
||||
Password string
|
||||
NickName string
|
||||
CreateTime gtime.Time
|
||||
}
|
||||
var users []User
|
||||
err := db.GetScan(ctx, &users, fmt.Sprintf("SELECT * FROM %s WHERE id>?", table), 1)
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(users), TableSize-1)
|
||||
t.Assert(users[0].Id, 2)
|
||||
t.Assert(users[1].Id, 3)
|
||||
t.Assert(users[2].Id, 4)
|
||||
t.Assert(users[0].NickName, "name_2")
|
||||
t.Assert(users[1].NickName, "name_3")
|
||||
t.Assert(users[2].NickName, "name_4")
|
||||
})
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type User struct {
|
||||
Id int
|
||||
Passport string
|
||||
Password string
|
||||
NickName string
|
||||
CreateTime *gtime.Time
|
||||
}
|
||||
var users []User
|
||||
err := db.GetScan(ctx, &users, fmt.Sprintf("SELECT * FROM %s WHERE id>?", table), 1)
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(users), TableSize-1)
|
||||
t.Assert(users[0].Id, 2)
|
||||
t.Assert(users[1].Id, 3)
|
||||
t.Assert(users[2].Id, 4)
|
||||
t.Assert(users[0].NickName, "name_2")
|
||||
t.Assert(users[1].NickName, "name_3")
|
||||
t.Assert(users[2].NickName, "name_4")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_DB_GetScan(t *testing.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type User struct {
|
||||
Id int
|
||||
Passport string
|
||||
Password string
|
||||
NickName string
|
||||
CreateTime gtime.Time
|
||||
}
|
||||
user := new(User)
|
||||
err := db.GetScan(ctx, user, fmt.Sprintf("SELECT * FROM %s WHERE id=?", table), 3)
|
||||
t.AssertNil(err)
|
||||
t.Assert(user.NickName, "name_3")
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type User struct {
|
||||
Id int
|
||||
Passport string
|
||||
Password string
|
||||
NickName string
|
||||
CreateTime gtime.Time
|
||||
}
|
||||
var user *User
|
||||
err := db.GetScan(ctx, &user, fmt.Sprintf("SELECT * FROM %s WHERE id=?", table), 3)
|
||||
t.AssertNil(err)
|
||||
t.Assert(user.NickName, "name_3")
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type User struct {
|
||||
Id int
|
||||
Passport string
|
||||
Password string
|
||||
NickName string
|
||||
CreateTime *gtime.Time
|
||||
}
|
||||
user := new(User)
|
||||
err := db.GetScan(ctx, user, fmt.Sprintf("SELECT * FROM %s WHERE id=?", table), 3)
|
||||
t.AssertNil(err)
|
||||
t.Assert(user.NickName, "name_3")
|
||||
})
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type User struct {
|
||||
Id int
|
||||
Passport string
|
||||
Password string
|
||||
NickName string
|
||||
CreateTime gtime.Time
|
||||
}
|
||||
var users []User
|
||||
err := db.GetScan(ctx, &users, fmt.Sprintf("SELECT * FROM %s WHERE id>?", table), 1)
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(users), TableSize-1)
|
||||
t.Assert(users[0].Id, 2)
|
||||
t.Assert(users[1].Id, 3)
|
||||
t.Assert(users[2].Id, 4)
|
||||
t.Assert(users[0].NickName, "name_2")
|
||||
t.Assert(users[1].NickName, "name_3")
|
||||
t.Assert(users[2].NickName, "name_4")
|
||||
})
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type User struct {
|
||||
Id int
|
||||
Passport string
|
||||
Password string
|
||||
NickName string
|
||||
CreateTime *gtime.Time
|
||||
}
|
||||
var users []User
|
||||
err := db.GetScan(ctx, &users, fmt.Sprintf("SELECT * FROM %s WHERE id>?", table), 1)
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(users), TableSize-1)
|
||||
t.Assert(users[0].Id, 2)
|
||||
t.Assert(users[1].Id, 3)
|
||||
t.Assert(users[2].Id, 4)
|
||||
t.Assert(users[0].NickName, "name_2")
|
||||
t.Assert(users[1].NickName, "name_3")
|
||||
t.Assert(users[2].NickName, "name_4")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_DB_Delete(t *testing.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
result, err := db.Delete(ctx, table, "1=1")
|
||||
t.AssertNil(err)
|
||||
n, _ := result.RowsAffected()
|
||||
t.Assert(n, TableSize)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Empty_Slice_Argument(t *testing.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
result, err := db.GetAll(ctx, fmt.Sprintf(`select * from %s where id in(?)`, table), g.Slice{})
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(result), 0)
|
||||
})
|
||||
}
|
||||
1113
contrib/drivers/oracle/oracle_z_model_test.go
Normal file
1113
contrib/drivers/oracle/oracle_z_model_test.go
Normal file
File diff suppressed because it is too large
Load Diff
@ -25,6 +25,7 @@ import (
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
|
||||
// Driver is the driver for sqlite database.
|
||||
@ -35,6 +36,8 @@ type Driver struct {
|
||||
var (
|
||||
// tableFieldsMap caches the table information retrieved from database.
|
||||
tableFieldsMap = gmap.New(true)
|
||||
// Error
|
||||
ErrorSave = gerror.NewCode(gcode.CodeNotSupported, `Save operation is not supported by sqlite driver`)
|
||||
)
|
||||
|
||||
func init() {
|
||||
@ -147,10 +150,17 @@ func (d *Driver) TableFields(ctx context.Context, table string, schema ...string
|
||||
}
|
||||
fields = make(map[string]*gdb.TableField)
|
||||
for i, m := range result {
|
||||
mKey := ""
|
||||
if m["pk"].Bool() {
|
||||
mKey = "pri"
|
||||
}
|
||||
fields[strings.ToLower(m["name"].String())] = &gdb.TableField{
|
||||
Index: i,
|
||||
Name: strings.ToLower(m["name"].String()),
|
||||
Type: strings.ToLower(m["type"].String()),
|
||||
Index: i,
|
||||
Name: strings.ToLower(m["name"].String()),
|
||||
Type: strings.ToLower(m["type"].String()),
|
||||
Key: mKey,
|
||||
Default: m["dflt_value"].Val(),
|
||||
Null: !m["notnull"].Bool(),
|
||||
}
|
||||
}
|
||||
return fields
|
||||
@ -166,11 +176,73 @@ func (d *Driver) TableFields(ctx context.Context, table string, schema ...string
|
||||
func (d *Driver) DoInsert(ctx context.Context, link gdb.Link, table string, list gdb.List, option gdb.DoInsertOption) (result sql.Result, err error) {
|
||||
switch option.InsertOption {
|
||||
case gdb.InsertOptionSave:
|
||||
return nil, gerror.NewCode(gcode.CodeNotSupported, `Save operation is not supported by sqlite driver`)
|
||||
|
||||
case gdb.InsertOptionReplace:
|
||||
return nil, gerror.NewCode(gcode.CodeNotSupported, `Replace operation is not supported by sqlite driver`)
|
||||
return nil, ErrorSave
|
||||
case gdb.InsertOptionIgnore, gdb.InsertOptionReplace:
|
||||
var (
|
||||
keys []string // Field names.
|
||||
values []string // Value holder string array, like: (?,?,?)
|
||||
params []interface{} // Values that will be committed to underlying database driver.
|
||||
onDuplicateStr string // onDuplicateStr is used in "ON DUPLICATE KEY UPDATE" statement.
|
||||
)
|
||||
// Handle the field names and placeholders.
|
||||
for k := range list[0] {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
// Prepare the batch result pointer.
|
||||
var (
|
||||
charL, charR = d.GetChars()
|
||||
batchResult = new(gdb.SqlResult)
|
||||
keysStr = charL + strings.Join(keys, charR+","+charL) + charR
|
||||
operation = "INSERT OR IGNORE"
|
||||
)
|
||||
|
||||
if option.InsertOption == gdb.InsertOptionReplace {
|
||||
operation = "INSERT OR REPLACE"
|
||||
}
|
||||
var (
|
||||
listLength = len(list)
|
||||
valueHolder = make([]string, 0)
|
||||
)
|
||||
for i := 0; i < listLength; i++ {
|
||||
values = values[:0]
|
||||
// Note that the map type is unordered,
|
||||
// so it should use slice+key to retrieve the value.
|
||||
for _, k := range keys {
|
||||
if s, ok := list[i][k].(gdb.Raw); ok {
|
||||
values = append(values, gconv.String(s))
|
||||
} else {
|
||||
values = append(values, "?")
|
||||
params = append(params, list[i][k])
|
||||
}
|
||||
}
|
||||
valueHolder = append(valueHolder, "("+gstr.Join(values, ",")+")")
|
||||
// Batch package checks: It meets the batch number, or it is the last element.
|
||||
if len(valueHolder) == option.BatchCount || (i == listLength-1 && len(valueHolder) > 0) {
|
||||
var (
|
||||
stdSqlResult sql.Result
|
||||
affectedRows int64
|
||||
)
|
||||
stdSqlResult, err = d.DoExec(ctx, link, fmt.Sprintf(
|
||||
"%s INTO %s(%s) VALUES%s %s",
|
||||
operation, d.QuotePrefixTableName(table), keysStr,
|
||||
gstr.Join(valueHolder, ","),
|
||||
onDuplicateStr,
|
||||
), params...)
|
||||
if err != nil {
|
||||
return stdSqlResult, err
|
||||
}
|
||||
if affectedRows, err = stdSqlResult.RowsAffected(); err != nil {
|
||||
err = gerror.WrapCode(gcode.CodeDbOperationError, err, `sql.Result.RowsAffected failed`)
|
||||
return stdSqlResult, err
|
||||
} else {
|
||||
batchResult.Result = stdSqlResult
|
||||
batchResult.Affected += affectedRows
|
||||
}
|
||||
params = params[:0]
|
||||
valueHolder = valueHolder[:0]
|
||||
}
|
||||
}
|
||||
return batchResult, nil
|
||||
default:
|
||||
return d.Core.DoInsert(ctx, link, table, list, option)
|
||||
}
|
||||
|
||||
161
contrib/drivers/sqlite/sqlite_0_test.go
Normal file
161
contrib/drivers/sqlite/sqlite_0_test.go
Normal file
@ -0,0 +1,161 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package sqlite_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/gogf/gf/v2/container/garray"
|
||||
"github.com/gogf/gf/v2/database/gdb"
|
||||
"github.com/gogf/gf/v2/errors/gcode"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gctx"
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
"github.com/gogf/gf/v2/test/gtest"
|
||||
)
|
||||
|
||||
var (
|
||||
db gdb.DB
|
||||
dbPrefix gdb.DB
|
||||
dbInvalid gdb.DB
|
||||
configNode gdb.ConfigNode
|
||||
dbDir = gfile.Temp("sqlite")
|
||||
ctx = gctx.New()
|
||||
|
||||
// Error
|
||||
ErrorSave = gerror.NewCode(gcode.CodeNotSupported, `Save operation is not supported by sqlite driver`)
|
||||
)
|
||||
|
||||
const (
|
||||
TableSize = 10
|
||||
TableName = "user"
|
||||
TestSchema1 = "test1"
|
||||
TestSchema2 = "test2"
|
||||
TableNamePrefix = "gf_"
|
||||
CreateTime = "2018-10-24 10:00:00"
|
||||
DBGroupTest = "test"
|
||||
DBGroupPrefix = "prefix"
|
||||
DBGroupInvalid = "invalid"
|
||||
)
|
||||
|
||||
func init() {
|
||||
fmt.Println("init sqlite db start")
|
||||
|
||||
if err := gfile.Mkdir(dbDir); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
|
||||
fmt.Println("init sqlite db dir: ", dbDir)
|
||||
|
||||
configNode = gdb.ConfigNode{
|
||||
Type: "sqlite",
|
||||
Link: gfile.Join(dbDir, "test.db"),
|
||||
Charset: "utf8",
|
||||
}
|
||||
nodePrefix := configNode
|
||||
nodePrefix.Prefix = TableNamePrefix
|
||||
|
||||
nodeInvalid := configNode
|
||||
|
||||
gdb.AddConfigNode(DBGroupTest, configNode)
|
||||
gdb.AddConfigNode(DBGroupPrefix, nodePrefix)
|
||||
gdb.AddConfigNode(DBGroupInvalid, nodeInvalid)
|
||||
gdb.AddConfigNode(gdb.DefaultGroupName, configNode)
|
||||
|
||||
// Default db.
|
||||
if r, err := gdb.NewByGroup(); err != nil {
|
||||
gtest.Error(err)
|
||||
} else {
|
||||
db = r
|
||||
}
|
||||
|
||||
// Prefix db.
|
||||
if r, err := gdb.NewByGroup(DBGroupPrefix); err != nil {
|
||||
gtest.Error(err)
|
||||
} else {
|
||||
dbPrefix = r
|
||||
}
|
||||
|
||||
// Invalid db.
|
||||
if r, err := gdb.NewByGroup(DBGroupInvalid); err != nil {
|
||||
gtest.Error(err)
|
||||
} else {
|
||||
dbInvalid = r
|
||||
}
|
||||
|
||||
fmt.Println("init sqlite db finish")
|
||||
}
|
||||
|
||||
func createTable(table ...string) string {
|
||||
return createTableWithDb(db, table...)
|
||||
}
|
||||
|
||||
func createInitTable(table ...string) string {
|
||||
return createInitTableWithDb(db, table...)
|
||||
}
|
||||
|
||||
func dropTable(table string) {
|
||||
dropTableWithDb(db, table)
|
||||
}
|
||||
|
||||
func createTableWithDb(db gdb.DB, table ...string) (name string) {
|
||||
if len(table) > 0 {
|
||||
name = table[0]
|
||||
} else {
|
||||
name = fmt.Sprintf(`%s_%d`, TableName, gtime.TimestampNano())
|
||||
}
|
||||
dropTableWithDb(db, name)
|
||||
|
||||
if _, err := db.Exec(ctx, fmt.Sprintf(`
|
||||
CREATE TABLE %s (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT
|
||||
UNIQUE
|
||||
NOT NULL,
|
||||
passport VARCHAR(45) NOT NULL
|
||||
DEFAULT passport,
|
||||
password VARCHAR(128) NOT NULL
|
||||
DEFAULT password,
|
||||
nickname VARCHAR(45),
|
||||
create_time DATETIME
|
||||
);
|
||||
`, name,
|
||||
)); err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func createInitTableWithDb(db gdb.DB, table ...string) (name string) {
|
||||
name = createTableWithDb(db, table...)
|
||||
array := garray.New(true)
|
||||
for i := 1; i <= TableSize; i++ {
|
||||
array.Append(g.Map{
|
||||
"id": i,
|
||||
"passport": fmt.Sprintf(`user_%d`, i),
|
||||
"password": fmt.Sprintf(`pass_%d`, i),
|
||||
"nickname": fmt.Sprintf(`name_%d`, i),
|
||||
"create_time": gtime.NewFromStr(CreateTime).String(),
|
||||
})
|
||||
}
|
||||
|
||||
result, err := db.Insert(ctx, name, array.Slice())
|
||||
gtest.AssertNil(err)
|
||||
|
||||
n, e := result.RowsAffected()
|
||||
gtest.Assert(e, nil)
|
||||
gtest.Assert(n, TableSize)
|
||||
return
|
||||
}
|
||||
|
||||
func dropTableWithDb(db gdb.DB, table string) {
|
||||
if _, err := db.Exec(ctx, fmt.Sprintf("DROP TABLE IF EXISTS `%s`", table)); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
}
|
||||
1551
contrib/drivers/sqlite/sqlite_core_test.go
Normal file
1551
contrib/drivers/sqlite/sqlite_core_test.go
Normal file
File diff suppressed because it is too large
Load Diff
4084
contrib/drivers/sqlite/sqlite_model_test.go
Normal file
4084
contrib/drivers/sqlite/sqlite_model_test.go
Normal file
File diff suppressed because it is too large
Load Diff
79
contrib/registry/etcd/README.MD
Normal file
79
contrib/registry/etcd/README.MD
Normal file
@ -0,0 +1,79 @@
|
||||
# GoFrame Etcd Registry
|
||||
|
||||
|
||||
Use `etcd` as service registration and discovery management.
|
||||
|
||||
|
||||
## Installation
|
||||
```
|
||||
go get -u -v github.com/gogf/gf/contrib/registry/etcd/v2
|
||||
```
|
||||
suggested using `go.mod`:
|
||||
```
|
||||
require github.com/gogf/gf/contrib/registry/etcd/v2 latest
|
||||
```
|
||||
|
||||
|
||||
## Example
|
||||
|
||||
### Reference example
|
||||
|
||||
[server](example/registry/etcd/server/main.go)
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/contrib/registry/etcd/v2"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/net/ghttp"
|
||||
"github.com/gogf/gf/v2/net/gsvc"
|
||||
)
|
||||
|
||||
func main() {
|
||||
gsvc.SetRegistry(etcd.New(`127.0.0.1:2379`))
|
||||
|
||||
s := g.Server(`hello.svc`)
|
||||
s.BindHandler("/", func(r *ghttp.Request) {
|
||||
g.Log().Info(r.Context(), `request received`)
|
||||
r.Response.Write(`Hello world`)
|
||||
})
|
||||
s.Run()
|
||||
}
|
||||
```
|
||||
|
||||
[client](example/registry/etcd/client/main.go)
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/contrib/registry/etcd/v2"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/net/gsel"
|
||||
"github.com/gogf/gf/v2/net/gsvc"
|
||||
"github.com/gogf/gf/v2/os/gctx"
|
||||
)
|
||||
|
||||
func main() {
|
||||
gsvc.SetRegistry(etcd.New(`127.0.0.1:2379`))
|
||||
gsel.SetBuilder(gsel.NewBuilderRoundRobin())
|
||||
|
||||
client := g.Client()
|
||||
for i := 0; i < 100; i++ {
|
||||
res, err := client.Get(gctx.New(), `http://hello.svc/`)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println(res.ReadAllString())
|
||||
res.Close()
|
||||
time.Sleep(time.Second)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
`GoFrame etcd` is licensed under the [MIT License](../../../LICENSE), 100% free and open-source, forever.
|
||||
|
||||
@ -8,6 +8,7 @@
|
||||
package etcd
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"time"
|
||||
|
||||
etcd3 "go.etcd.io/etcd/client/v3"
|
||||
@ -79,26 +80,34 @@ func NewWithClient(client *etcd3.Client, option ...Option) *Registry {
|
||||
}
|
||||
|
||||
// extractResponseToServices extracts etcd watch response context to service list.
|
||||
func extractResponseToServices(res *etcd3.GetResponse) ([]*gsvc.Service, error) {
|
||||
func extractResponseToServices(res *etcd3.GetResponse) ([]gsvc.Service, error) {
|
||||
if res == nil || res.Kvs == nil {
|
||||
return nil, nil
|
||||
}
|
||||
var (
|
||||
services []*gsvc.Service
|
||||
services []gsvc.Service
|
||||
serviceKey string
|
||||
serviceMap = make(map[string]*gsvc.Service)
|
||||
serviceMap = make(map[string]*gsvc.LocalService)
|
||||
)
|
||||
for _, kv := range res.Kvs {
|
||||
service, err := gsvc.NewServiceWithKV(kv.Key, kv.Value)
|
||||
service, err := gsvc.NewServiceWithKV(string(kv.Key), string(kv.Value))
|
||||
if err != nil {
|
||||
return services, err
|
||||
}
|
||||
if service != nil {
|
||||
serviceKey = service.KeyWithoutEndpoints()
|
||||
if s, ok := serviceMap[serviceKey]; ok {
|
||||
s.Endpoints = append(s.Endpoints, service.Endpoints...)
|
||||
localService, ok := service.(*gsvc.LocalService)
|
||||
if !ok {
|
||||
return nil, gerror.Newf(
|
||||
`service from "gsvc.NewServiceWithKV" is not "*gsvc.LocalService", but "%s"`,
|
||||
reflect.TypeOf(service),
|
||||
)
|
||||
}
|
||||
if localService != nil {
|
||||
serviceKey = localService.GetPrefix()
|
||||
var localServiceInMap *gsvc.LocalService
|
||||
if localServiceInMap, ok = serviceMap[serviceKey]; ok {
|
||||
localServiceInMap.Endpoints = append(localServiceInMap.Endpoints, localService.Endpoints...)
|
||||
} else {
|
||||
serviceMap[serviceKey] = service
|
||||
serviceMap[serviceKey] = localService
|
||||
services = append(services, service)
|
||||
}
|
||||
}
|
||||
|
||||
@ -15,8 +15,12 @@ import (
|
||||
)
|
||||
|
||||
// Search is the etcd discovery search function.
|
||||
func (r *Registry) Search(ctx context.Context, in gsvc.SearchInput) ([]*gsvc.Service, error) {
|
||||
res, err := r.kv.Get(ctx, in.Key(), etcd3.WithPrefix())
|
||||
func (r *Registry) Search(ctx context.Context, in gsvc.SearchInput) ([]gsvc.Service, error) {
|
||||
if in.Prefix == "" && in.Name != "" {
|
||||
in.Prefix = gsvc.NewServiceWithName(in.Name).GetPrefix()
|
||||
}
|
||||
|
||||
res, err := r.kv.Get(ctx, in.Prefix, etcd3.WithPrefix())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -25,18 +29,12 @@ func (r *Registry) Search(ctx context.Context, in gsvc.SearchInput) ([]*gsvc.Ser
|
||||
return nil, err
|
||||
}
|
||||
// Service filter.
|
||||
filteredServices := make([]*gsvc.Service, 0)
|
||||
filteredServices := make([]gsvc.Service, 0)
|
||||
for _, v := range services {
|
||||
if in.Deployment != "" && in.Deployment != v.Deployment {
|
||||
if in.Name != "" && in.Name != v.GetName() {
|
||||
continue
|
||||
}
|
||||
if in.Namespace != "" && in.Namespace != v.Namespace {
|
||||
continue
|
||||
}
|
||||
if in.Name != "" && in.Name != v.Name {
|
||||
continue
|
||||
}
|
||||
if in.Version != "" && in.Version != v.Version {
|
||||
if in.Version != "" && in.Version != v.GetVersion() {
|
||||
continue
|
||||
}
|
||||
service := v
|
||||
|
||||
@ -16,19 +16,19 @@ import (
|
||||
)
|
||||
|
||||
// Register implements the gsvc.Register interface.
|
||||
func (r *Registry) Register(ctx context.Context, service *gsvc.Service) error {
|
||||
func (r *Registry) Register(ctx context.Context, service gsvc.Service) (gsvc.Service, error) {
|
||||
r.lease = etcd3.NewLease(r.client)
|
||||
grant, err := r.lease.Grant(ctx, int64(r.keepaliveTTL.Seconds()))
|
||||
if err != nil {
|
||||
return gerror.Wrapf(err, `etcd grant failed with keepalive ttl "%s"`, r.keepaliveTTL)
|
||||
return nil, gerror.Wrapf(err, `etcd grant failed with keepalive ttl "%s"`, r.keepaliveTTL)
|
||||
}
|
||||
var (
|
||||
key = service.Key()
|
||||
value = service.Value()
|
||||
key = service.GetKey()
|
||||
value = service.GetValue()
|
||||
)
|
||||
_, err = r.client.Put(ctx, key, value, etcd3.WithLease(grant.ID))
|
||||
if err != nil {
|
||||
return gerror.Wrapf(
|
||||
return nil, gerror.Wrapf(
|
||||
err,
|
||||
`etcd put failed with key "%s", value "%s", lease "%d"`,
|
||||
key, value, grant.ID,
|
||||
@ -41,16 +41,15 @@ func (r *Registry) Register(ctx context.Context, service *gsvc.Service) error {
|
||||
)
|
||||
keepAliceCh, err := r.client.KeepAlive(context.Background(), grant.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
go r.doKeepAlive(grant.ID, keepAliceCh)
|
||||
service.Separator = gsvc.DefaultSeparator
|
||||
return nil
|
||||
return service, nil
|
||||
}
|
||||
|
||||
// Deregister implements the gsvc.Deregister interface.
|
||||
func (r *Registry) Deregister(ctx context.Context, service *gsvc.Service) error {
|
||||
_, err := r.client.Delete(ctx, service.Key())
|
||||
func (r *Registry) Deregister(ctx context.Context, service gsvc.Service) error {
|
||||
_, err := r.client.Delete(ctx, service.GetKey())
|
||||
if r.lease != nil {
|
||||
_ = r.lease.Close()
|
||||
}
|
||||
|
||||
@ -35,15 +35,14 @@ func newWatcher(key string, client *etcd3.Client) (*watcher, error) {
|
||||
}
|
||||
w.ctx, w.cancel = context.WithCancel(context.Background())
|
||||
w.watchChan = w.watcher.Watch(w.ctx, key, etcd3.WithPrefix(), etcd3.WithRev(0))
|
||||
err := w.watcher.RequestProgress(context.Background())
|
||||
if err != nil {
|
||||
if err := w.watcher.RequestProgress(context.Background()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return w, nil
|
||||
}
|
||||
|
||||
// Proceed is used to watch the key.
|
||||
func (w *watcher) Proceed() ([]*gsvc.Service, error) {
|
||||
func (w *watcher) Proceed() ([]gsvc.Service, error) {
|
||||
select {
|
||||
case <-w.ctx.Done():
|
||||
return nil, w.ctx.Err()
|
||||
@ -58,7 +57,7 @@ func (w *watcher) Close() error {
|
||||
return w.watcher.Close()
|
||||
}
|
||||
|
||||
func (w *watcher) getServicesByPrefix() ([]*gsvc.Service, error) {
|
||||
func (w *watcher) getServicesByPrefix() ([]gsvc.Service, error) {
|
||||
res, err := w.kv.Get(w.ctx, w.key, etcd3.WithPrefix())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@ -4,7 +4,7 @@ go 1.15
|
||||
|
||||
require (
|
||||
github.com/gogf/gf/v2 v2.0.0
|
||||
go.etcd.io/etcd/client/v3 v3.5.1
|
||||
go.etcd.io/etcd/client/v3 v3.5.4
|
||||
)
|
||||
|
||||
replace github.com/gogf/gf/v2 => ../../../
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user