Compare commits

...

69 Commits

Author SHA1 Message Date
91d8d71821 Merge branch 'master' of https://github.com/gogf/gf 2022-08-22 14:40:48 +08:00
9d1c6f2daa v2.1.3 release (#2084) 2022-08-22 14:40:36 +08:00
25d4ba320a improve command init: add go mod tidy for init project (#2083)
* CI updates

* improve command init
2022-08-22 14:31:35 +08:00
d1bbadc5b7 Merge branch 'master' of https://github.com/gogf/gf 2022-08-22 12:15:59 +08:00
3988a7ff6b add more UT cases for package gview (#2072)
* CI updates

* add more UT cases for package gview
2022-08-18 21:06:20 +08:00
26e3c7aeb8 fix issue 1914 (#2075)
* CI updates

* fix issue #1914
2022-08-18 21:05:58 +08:00
bfd2c8e38b Merge branch 'master' of https://github.com/gogf/gf 2022-08-16 21:28:35 +08:00
eff46bd1db fix issue #2047 (#2069) 2022-08-16 20:46:22 +08:00
7a3176ea77 Fix name of issue CI (#2071)
CI updates
2022-08-16 20:41:54 +08:00
1ec174101a CI updates 2022-08-16 20:16:21 +08:00
a656ad0941 add issue bot support (#2065) (#2066) 2022-08-15 21:52:33 +08:00
299573dd19 fixed inconsistent results when converting float64(NaN) to int/uint on multiple platforms (#2064) 2022-08-15 21:51:34 +08:00
43b84f4044 fix clickhouse in function TableFields when configuration using link (#2063) 2022-08-15 20:53:02 +08:00
897d6d9ad0 fix gctx init slice bounds out of range on ios platform (#2062) 2022-08-15 20:40:17 +08:00
e4c8cfc16b add interface DB.CheckLocalTypeForField for package gdb (#2059) 2022-08-11 21:47:35 +08:00
95888e0b77 add last insert id support for pgsql (#1994) 2022-08-09 19:45:05 +08:00
Gin
4ded89d453 improve gdb.CheckValueForLocalType for pgsql (#2040) 2022-08-08 19:56:06 +08:00
Gin
82a3391937 fix precision lost of int64 for package gcfg (#2044)
fix: gcfg lose precision

Co-authored-by: qinyuguang <qinyuguang@meican.com>
2022-08-03 21:50:17 +08:00
f580b7a488 improve header printing in json format for package glog; add golang v1.18 support for ci workflow (#2037) 2022-07-29 19:06:22 +08:00
9df0a9da0a fix issue #1648 (#2033) 2022-07-28 10:11:15 +08:00
6172862061 add MiddlewareJsonBody, improve error response handling for package ghttp (#2032) 2022-07-27 19:52:02 +08:00
1ae037f515 Update goai_path.go (#2029) 2022-07-26 22:48:40 +08:00
6f7cd96a7f feature: gen dao from tpl file path (#2021) 2022-07-25 20:55:48 +08:00
e00d3ff7ff fix issue in gstr.Nl2Br (#2028) 2022-07-25 20:54:42 +08:00
390b936153 fix gf-cli command 'gen dao' help infomation (#2022) 2022-07-25 19:43:47 +08:00
863bea1ad1 improve field type check from db to golang (#2023) 2022-07-22 16:44:24 +08:00
b7794a8783 use method name as its command name if no name defined in Meta of input struct for package gcmd (#2019) 2022-07-19 16:30:00 +08:00
bb3c51c6cc add interrupt for concurrent ci workflows(#2020) 2022-07-18 22:24:22 +08:00
c3c82cebd5 Feature/ci cache (#2010) 2022-07-18 16:02:21 +08:00
5d51e9fa2c improve package gerror, add HasCode/HasError function for package gerror (#2006) 2022-07-15 10:49:04 +08:00
2c70bb6a00 ci updates 2022-07-14 20:54:00 +08:00
98b2e8ab18 improve panic...recover of exit feature for package ghttp/gtimer/gfsnotify (#2000) 2022-07-13 20:20:38 +08:00
675ae9bade fix concurrent safety for package gdb (#1998) 2022-07-12 21:26:18 +08:00
3e7e8ba6f2 fix(gdb): panic when concurrent db config map read and write. (#1997) 2022-07-12 19:31:22 +08:00
f1766bdbdc add init ctx feature (#1995) 2022-07-12 19:27:42 +08:00
e3665cedaf new version v2.1.2 (#1993) 2022-07-12 14:08:19 +08:00
dd7caea910 fix UT issue for package gcron (#1992) 2022-07-12 09:55:46 +08:00
8ed57c6468 Improve the code coverage of the gtype module (#1975) 2022-07-11 21:29:32 +08:00
0e6becc36d Improve the code coverage of the gvar module (#1982) 2022-07-11 19:35:31 +08:00
e38c455252 Improve the code coverage of the gpool, gqueue, gring module (#1987) 2022-07-11 19:34:40 +08:00
4c1cf73005 Improve the code coverage of the gutil, grand module (#1989) 2022-07-11 19:33:59 +08:00
384fb3c4d5 fix go.sum of package contrib/drivers/pgsql (#1980) 2022-07-07 21:52:21 +08:00
afb90b0af3 Feature/pgsql add pgsql unit test (#1853) (#1973) 2022-07-07 21:42:20 +08:00
1530ffc926 Improve the code coverage of the gset module (#1977) 2022-07-07 21:28:23 +08:00
f876a56d2a some improves for ci yaml and package cmd/gf, database/gdb (#1972) 2022-07-07 21:16:26 +08:00
d26eadf5be gfcli: fix imports parse and update gofmt (#1979) 2022-07-07 20:57:20 +08:00
13fc0cb9eb feat: pgsql 字段类型 支持 数组类型 (#1881) 2022-07-06 21:06:20 +08:00
Gin
59b3f6e962 improve list tables for pgsql (#1790) 2022-07-06 20:05:12 +08:00
80442efe94 redis add sentinel slaveOnly filed (#1948) 2022-07-06 19:54:36 +08:00
ab929e465b update comment for ghttp.Request (#1968) 2022-07-06 19:48:43 +08:00
047c90466d improve UT for package gcron (#1966) 2022-07-04 21:18:20 +08:00
40e6b2b0f1 fix gf run custom arguments and gf gen dao specify config file path (#1879) 2022-07-04 21:01:55 +08:00
9159f00014 Fix goai repeat param (#1916) 2022-07-04 20:40:28 +08:00
59a9484970 gf gen service supports the generation of service files in the specified naming format (#1953) 2022-07-04 20:36:58 +08:00
8a853b1bb7 add file export (#1959) 2022-07-04 20:17:00 +08:00
2bcd6c4771 add full week/month name support for pattern, add seconds fix feature in some delay seconds for package gcron (#1960) 2022-07-01 09:53:01 +08:00
1acc1b8230 improve DeepCopy feature for bunch of components, especially the container and gtime (#1956) 2022-06-29 14:58:27 +08:00
2c169e2330 improve package glog; fix issue in package gtrace (#1952) 2022-06-28 15:47:16 +08:00
f57d71b6fa add cross building support for sqlite in command gen dao (#1944) 2022-06-27 11:09:55 +08:00
796bc008f8 improve configuration parsing for command gen dao (#1938) 2022-06-24 18:03:05 +08:00
d7faae0531 add gstr.IsGNUVersion (#1937) 2022-06-24 16:54:24 +08:00
f0511592b5 github.com/glebarez/go-sqlite (#1932) 2022-06-24 16:15:42 +08:00
68efab79ef improve command gen service 2022-06-24 15:35:16 +08:00
1ede8c77ba version updates 2022-06-24 15:21:36 +08:00
48f95d0009 fix issue in Response.WriteJson/Xml functions 2022-06-24 15:21:10 +08:00
0bb57b8989 go.sum update 2022-06-24 15:16:46 +08:00
0503c17867 feat/gfcli: replace gofmt&goimports with tools/imports (#1935) 2022-06-24 14:15:20 +08:00
19779cd342 Fix multiple gdb-group overrides (#1890)
Co-authored-by: longl <longlei@dealmap.cloud>
2022-06-23 21:30:29 +08:00
141f3512a9 fix glog bug (#1844) 2022-06-23 21:05:12 +08:00
191 changed files with 5284 additions and 1926 deletions

View File

@ -52,7 +52,7 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ github.ref }}
release_name: GoFrame CLI Release ${{ github.ref }}
release_name: GoFrame Release ${{ github.ref }}
draft: false
prerelease: false

View File

@ -0,0 +1,95 @@
version: "2"
services:
redis-master:
container_name: redis-master
image: "loads/redis:7.0"
environment:
- REDIS_REPLICATION_MODE=master
- REDIS_PASSWORD=111111
ports:
- 6380:6379
redis-slave1:
container_name: redis-slave1
image: "loads/redis:7.0"
environment:
- REDIS_REPLICATION_MODE=slave
- REDIS_MASTER_HOST=redis-master
- REDIS_MASTER_PASSWORD=111111
- REDIS_PASSWORD=111111
ports:
- 6381:6379
depends_on:
- redis-master
links:
- redis-master
redis-slave2:
container_name: redis-slave2
image: "loads/redis:7.0"
environment:
- REDIS_REPLICATION_MODE=slave
- REDIS_MASTER_HOST=redis-master
- REDIS_MASTER_PASSWORD=111111
- REDIS_PASSWORD=111111
ports:
- 6382:6379
depends_on:
- redis-master
links:
- redis-master
redis-sentinel-1:
container_name: redis-sentinel-1
image: "loads/redis-sentinel:7.0"
environment:
- REDIS_MASTER_HOST=redis-master
- REDIS_MASTER_PORT_NUMBER=6379
- REDIS_MASTER_PASSWORD=111111
depends_on:
- redis-master
- redis-slave1
- redis-slave2
ports:
- 26379:26379
links:
- redis-master
- redis-slave1
- redis-slave2
redis-sentinel-2:
container_name: redis-sentinel-2
image: "loads/redis-sentinel:7.0"
environment:
- REDIS_MASTER_HOST=redis-master
- REDIS_MASTER_PORT_NUMBER=6379
- REDIS_MASTER_PASSWORD=111111
depends_on:
- redis-master
- redis-slave1
- redis-slave2
links:
- redis-master
- redis-slave1
- redis-slave2
ports:
- 26380:26379
redis-sentinel-3:
container_name: redis-sentinel-3
image: "loads/redis-sentinel:7.0"
environment:
- REDIS_MASTER_HOST=redis-master
- REDIS_MASTER_PORT_NUMBER=6379
- REDIS_MASTER_PASSWORD=111111
depends_on:
- redis-master
- redis-slave1
- redis-slave2
ports:
- 26381:26379
links:
- redis-master
- redis-slave1
- redis-slave2

View File

@ -6,18 +6,27 @@ on:
branches:
- master
- develop
- personal/**
- feature/**
- enhance/**
- fix/**
pull_request:
branches:
- master
- develop
- personal/**
- feature/**
- enhance/**
- fix/**
# This allows a subsequently queued workflow run to interrupt previous runs
concurrency:
group: '${{ github.workflow }} @ ${{ github.event.pull_request.head.label || github.head_ref || github.ref }}'
cancel-in-progress: true
env:
GF_DEBUG: 0
TZ: "Asia/Shanghai"
jobs:
@ -53,7 +62,7 @@ jobs:
image: loads/postgres:13
env:
POSTGRES_PASSWORD: 12345678
POSTGRES_USER: root
POSTGRES_USER: postgres
POSTGRES_DB: test
TZ: Asia/Shanghai
ports:
@ -109,15 +118,13 @@ jobs:
- 1521:1521
strategy:
matrix:
go: [ "1.15", "1.16", "1.17" ]
go-version: [ "1.15", "1.16", "1.17", "1.18" ]
goarch: [ "386", "amd64" ]
steps:
- name: Set Up Timezone
- name: Setup Timezone
uses: szenius/set-timezone@v1.0
with:
timezoneLinux: "Asia/Shanghai"
@ -125,10 +132,30 @@ jobs:
- name: Checkout Repository
uses: actions/checkout@v2
- name: Set Up Go
- name: Setup Golang ${{ matrix.go-version }}
uses: actions/setup-go@v2
with:
go-version: ${{ matrix.go }}
go-version: ${{ matrix.go-version }}
- name: Setup Golang caches
uses: actions/cache@v3
with:
# In order:
# * Module download cache
# * Build cache (Linux)
# * Build cache (Mac)
# * Build cache (Windows)
path: |
~/go/pkg/mod
~/.cache/go-build
~/Library/Caches/go-build
~\AppData\Local\go-build
key: ${{ runner.os }}-go-${{ matrix.go-version }}-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-${{ matrix.go-version }}-
- name: Start containers
run: docker-compose -f ".github/workflows/docker/docker-compose.yml" up -d --build
- name: Before Script
run: |
@ -159,8 +186,11 @@ jobs:
cd -
done
- name: Stop containers
run: docker-compose -f ".github/workflows/docker/docker-compose.yml" down
- name: Report Coverage
uses: codecov/codecov-action@v2
with:
flags: go-${{ matrix.go }}-${{ matrix.goarch }}
flags: go-${{ matrix.go-version }}-${{ matrix.goarch }}

View File

@ -0,0 +1,28 @@
# 规则描述每天0点(GMT+8)执行一次将最近7天没有活跃且非BUG的ISSUE设置标签:inactive
name: Issue Check Inactive
on:
schedule:
- cron: "0 3 * * *"
env: # 设置环境变量
TZ: Asia/Shanghai #时区(设置时区可使页面中的`最近更新时间`使用时区时间)
permissions:
contents: read
jobs:
issue-check-inactive:
permissions:
issues: write # for actions-cool/issues-helper to update issues
# pull-requests: write # for actions-cool/issues-helper to update PRs
runs-on: ubuntu-latest
steps:
- name: check-inactive
uses: actions-cool/issues-helper@v3
with:
actions: 'check-inactive'
inactive-label: 'inactive'
inactive-day: 7
issue-state: open
exclude-labels: 'bug'

View File

@ -0,0 +1,22 @@
# 规则描述每天0点(GMT+8)执行一次将最近30天没有活跃且非BUG的ISSUE关闭
name: Issue Close Inactive
on:
schedule:
- cron: "0 3 * * *"
env: # 设置环境变量
TZ: Asia/Shanghai #时区(设置时区可使页面中的`最近更新时间`使用时区时间)
jobs:
close-issues:
runs-on: ubuntu-latest
steps:
- name: need close
uses: actions-cool/issues-helper@v3
with:
actions: "close-issues"
# token: ${{ secrets.GF_TOKEN }}
labels: 'inactive'
inactive-day: 30
exclude-labels: 'bug'

25
.github/workflows/issue-labeled.yml vendored Normal file
View File

@ -0,0 +1,25 @@
## 规则描述当issue被标记为help wanted 时,增加评论
name: Issue Labeled
on:
issues:
types: [labeled]
env: # 设置环境变量
TZ: Asia/Shanghai # 时区(设置时区可使页面中的`最近更新时间`使用时区时间)
jobs:
reply-labeled:
runs-on: ubuntu-latest
steps:
- name: contribution welcome
if: github.event.label.name == 'help wanted'
uses: actions-cool/issues-helper@v3
with:
actions: "create-comment, remove-labels"
# token: ${{ secrets.GF_TOKEN }}
issue-number: ${{ github.event.issue.number }}
body: |
Hello @${{ github.event.issue.user.login }}. We like your proposal/feedback and would appreciate a contribution via a Pull Request by you or another community member. We thank you in advance for your contribution and are looking forward to reviewing it!
你好 @${{ github.event.issue.user.login }}。我们喜欢您的提案/反馈,并希望您或其他社区成员通过拉取请求做出贡献。我们提前感谢您的贡献,并期待对其进行审查。

View File

@ -0,0 +1,29 @@
# 规则描述在issue没有活跃且尚未被关闭期间若issue作者更新或评论该ISSUE则移除其inactive标签
name: Issue Remove Inactive
on:
issues:
types: [edited]
issue_comment:
types: [created, edited]
env: # 设置环境变量
TZ: Asia/Shanghai #时区(设置时区可使页面中的`最近更新时间`使用时区时间)
permissions:
contents: read
jobs:
issue-remove-inactive:
permissions:
issues: write # for actions-cool/issues-helper to update issues
# pull-requests: write # for actions-cool/issues-helper to update PRs
runs-on: ubuntu-latest
steps:
- name: remove inactive
if: github.event.issue.state == 'open' && github.actor == github.event.issue.user.login
uses: actions-cool/issues-helper@v3
with:
actions: 'remove-labels'
issue-number: ${{ github.event.issue.number }}
labels: 'inactive'

View File

@ -0,0 +1,29 @@
# 规则描述将需要提供更多细节且暂未关闭的issue在issue作者评论后移除 need more details 标签
name: Issue Remove Need More Details
on:
issues:
types: [edited]
issue_comment:
types: [created, edited]
env: # 设置环境变量
TZ: Asia/Shanghai #时区(设置时区可使页面中的`最近更新时间`使用时区时间)
permissions:
contents: read
jobs:
issue-remove-need-more-details:
permissions:
issues: write # for actions-cool/issues-helper to update issues
# pull-requests: write # for actions-cool/issues-helper to update PRs
runs-on: ubuntu-latest
steps:
- name: remove need more details
if: github.event.issue.state == 'open' && github.actor == github.event.issue.user.login
uses: actions-cool/issues-helper@v3
with:
actions: 'remove-labels'
issue-number: ${{ github.event.issue.number }}
labels: 'need more details'

View File

@ -3,11 +3,13 @@ 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/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/contrib/drivers/sqlite/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 (

View File

@ -14,12 +14,15 @@ github.com/denisenkom/go-mssqldb v0.11.0 h1:9rHa233rhdOyrz2GcP9NM+gi2psgJZ4GWDpL
github.com/denisenkom/go-mssqldb v0.11.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI=
github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU=
github.com/glebarez/go-sqlite v1.17.3 h1:Rji9ROVSTTfjuWD6j5B+8DtkNvPILoUC3xRhkQzGxvk=
github.com/glebarez/go-sqlite v1.17.3/go.mod h1:Hg+PQuhUy98XCxWEJEaWob8x7lhJzhNYF1nZbUiRGIY=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
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=
@ -45,16 +48,20 @@ github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiu
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=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
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/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=
github.com/grokify/html-strip-tags-go v0.0.1/go.mod h1:2Su6romC5/1VXOQMaWL2yb618ARB8iVo6/DR99A6d78=
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/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
github.com/lib/pq v1.10.4 h1:SO9z7FRPzA03QhHKJrH5BXA6HU1rS4V2nIVrrNC1iYk=
github.com/lib/pq v1.10.4/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo=
@ -66,6 +73,7 @@ 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.12/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=
@ -85,12 +93,15 @@ 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/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk=
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
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=
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=
@ -100,17 +111,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=
@ -132,21 +148,29 @@ 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-20211007075335-d3039528d8ac/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-20220405052023-b1e9470b6e64/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=
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=
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-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
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=
@ -174,3 +198,29 @@ 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=
lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
modernc.org/cc/v3 v3.36.0/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI=
modernc.org/ccgo/v3 v3.0.0-20220428102840-41399a37e894/go.mod h1:eI31LL8EwEBKPpNpA4bU1/i+sKOwOrQy8D87zWUcRZc=
modernc.org/ccgo/v3 v3.0.0-20220430103911-bc99d88307be/go.mod h1:bwdAnOoaIt8Ax9YdWGjxWsdkPcZyRPHqrOvJxaKAKGw=
modernc.org/ccgo/v3 v3.16.4/go.mod h1:tGtX0gE9Jn7hdZFeU88slbTh1UtCYKusWOoCJuvkWsQ=
modernc.org/ccgo/v3 v3.16.6/go.mod h1:tGtX0gE9Jn7hdZFeU88slbTh1UtCYKusWOoCJuvkWsQ=
modernc.org/ccorpus v1.11.6/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ=
modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM=
modernc.org/libc v0.0.0-20220428101251-2d5f3daf273b/go.mod h1:p7Mg4+koNjc8jkqwcoFBJx7tXkpj00G77X7A72jXPXA=
modernc.org/libc v1.16.0/go.mod h1:N4LD6DBE9cf+Dzf9buBlzVJndKr/iJHG97vGLHYnb5A=
modernc.org/libc v1.16.1/go.mod h1:JjJE0eu4yeK7tab2n4S1w8tlWd9MxXLRzheaRnAKymU=
modernc.org/libc v1.16.7/go.mod h1:hYIV5VZczAmGZAnG15Vdngn5HSF5cSkbvfz2B7GRuVU=
modernc.org/libc v1.16.8 h1:Ux98PaOMvolgoFX/YwusFOHBnanXdGRmWgI8ciI2z4o=
modernc.org/libc v1.16.8/go.mod h1:hYIV5VZczAmGZAnG15Vdngn5HSF5cSkbvfz2B7GRuVU=
modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
modernc.org/mathutil v1.4.1 h1:ij3fYGe8zBF4Vu+g0oT7mB06r8sqGWKuJu1yXeR4by8=
modernc.org/mathutil v1.4.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
modernc.org/memory v1.1.1 h1:bDOL0DIDLQv7bWhP3gMvIrnoFw+Eo6F7a2QK9HPDiFU=
modernc.org/memory v1.1.1/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw=
modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
modernc.org/sqlite v1.17.3 h1:iE+coC5g17LtByDYDWKpR6m2Z9022YrSh3bumwOnIrI=
modernc.org/sqlite v1.17.3/go.mod h1:10hPVYar9C0kfXuTWGz8s0XtB8uAGymUy51ZzStYe3k=
modernc.org/strutil v1.1.1/go.mod h1:DE+MQQ/hjKBZS2zNInV5hhcipt5rLPWkmpbGeW5mmdw=
modernc.org/tcl v1.13.1/go.mod h1:XOLfOwzhkljL4itZkK6T72ckMgvj0BDsnKNdZVUOecw=
modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
modernc.org/z v1.5.1/go.mod h1:eWFB510QWW5Th9YGZT81s+LwvaAs3Q2yr4sP0rmLkv8=

View File

@ -1,811 +1,15 @@
package cmd
import (
"bytes"
"context"
"fmt"
"strings"
"github.com/gogf/gf/cmd/gf/v2/internal/consts"
"github.com/gogf/gf/cmd/gf/v2/internal/utility/mlog"
"github.com/gogf/gf/cmd/gf/v2/internal/utility/utils"
"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/gfile"
"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/gtag"
"github.com/olekukonko/tablewriter"
_ "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"
"github.com/gogf/gf/cmd/gf/v2/internal/cmd/gendao"
)
const (
cGenDaoConfig = `gfcli.gen.dao`
cGenDaoUsage = `gf gen dao [OPTION]`
cGenDaoBrief = `automatically generate go files for dao/do/entity`
cGenDaoEg = `
gf gen dao
gf gen dao -l "mysql:root:12345678@tcp(127.0.0.1:3306)/test"
gf gen dao -p ./model -g user-center -t user,user_detail,user_login
gf gen dao -r user_
`
cGenDaoAd = `
CONFIGURATION SUPPORT
Options are also supported by configuration file.
It's suggested using configuration file instead of command line arguments making producing.
The configuration node name is "gfcli.gen.dao", which also supports multiple databases, for example(config.yaml):
gfcli:
gen:
dao:
- link: "mysql:root:12345678@tcp(127.0.0.1:3306)/test"
tables: "order,products"
jsonCase: "CamelLower"
- link: "mysql:root:12345678@tcp(127.0.0.1:3306)/primary"
path: "./my-app"
prefix: "primary_"
tables: "user, userDetail"
`
cGenDaoBriefPath = `directory path for generated files`
cGenDaoBriefLink = `database configuration, the same as the ORM configuration of GoFrame`
cGenDaoBriefTables = `generate models only for given tables, multiple table names separated with ','`
cGenDaoBriefTablesEx = `generate models excluding given tables, multiple table names separated with ','`
cGenDaoBriefPrefix = `add prefix for all table of specified link/database tables`
cGenDaoBriefRemovePrefix = `remove specified prefix of the table, multiple prefix separated with ','`
cGenDaoBriefStdTime = `use time.Time from stdlib instead of gtime.Time for generated time/date fields of tables`
cGenDaoBriefWithTime = `add created time for auto produced go files`
cGenDaoBriefGJsonSupport = `use gJsonSupport to use *gjson.Json instead of string for generated json fields of tables`
cGenDaoBriefImportPrefix = `custom import prefix for generated go files`
cGenDaoBriefDaoPath = `directory path for storing generated dao files under path`
cGenDaoBriefDoPath = `directory path for storing generated do files under path`
cGenDaoBriefEntityPath = `directory path for storing generated entity files under path`
cGenDaoBriefOverwriteDao = `overwrite all dao files both inside/outside internal folder`
cGenDaoBriefModelFile = `custom file name for storing generated model content`
cGenDaoBriefModelFileForDao = `custom file name generating model for DAO operations like Where/Data. It's empty in default`
cGenDaoBriefDescriptionTag = `add comment to description tag for each field`
cGenDaoBriefNoJsonTag = `no json tag will be added for each field`
cGenDaoBriefNoModelComment = `no model comment will be added for each field`
cGenDaoBriefGroup = `
specifying the configuration group name of database for generated ORM instance,
it's not necessary and the default value is "default"
`
cGenDaoBriefJsonCase = `
generated json tag case for model struct, cases are as follows:
| Case | Example |
|---------------- |--------------------|
| Camel | AnyKindOfString |
| CamelLower | anyKindOfString | default
| Snake | any_kind_of_string |
| SnakeScreaming | ANY_KIND_OF_STRING |
| SnakeFirstUpper | rgb_code_md5 |
| Kebab | any-kind-of-string |
| KebabScreaming | ANY-KIND-OF-STRING |
`
tplVarTableName = `{TplTableName}`
tplVarTableNameCamelCase = `{TplTableNameCamelCase}`
tplVarTableNameCamelLowerCase = `{TplTableNameCamelLowerCase}`
tplVarPackageImports = `{TplPackageImports}`
tplVarImportPrefix = `{TplImportPrefix}`
tplVarStructDefine = `{TplStructDefine}`
tplVarColumnDefine = `{TplColumnDefine}`
tplVarColumnNames = `{TplColumnNames}`
tplVarGroupName = `{TplGroupName}`
tplVarDatetimeStr = `{TplDatetimeStr}`
)
var (
createdAt = gtime.Now()
)
func init() {
gtag.Sets(g.MapStrStr{
`cGenDaoConfig`: cGenDaoConfig,
`cGenDaoUsage`: cGenDaoUsage,
`cGenDaoBrief`: cGenDaoBrief,
`cGenDaoEg`: cGenDaoEg,
`cGenDaoAd`: cGenDaoAd,
`cGenDaoBriefPath`: cGenDaoBriefPath,
`cGenDaoBriefLink`: cGenDaoBriefLink,
`cGenDaoBriefTables`: cGenDaoBriefTables,
`cGenDaoBriefTablesEx`: cGenDaoBriefTablesEx,
`cGenDaoBriefPrefix`: cGenDaoBriefPrefix,
`cGenDaoBriefRemovePrefix`: cGenDaoBriefRemovePrefix,
`cGenDaoBriefStdTime`: cGenDaoBriefStdTime,
`cGenDaoBriefWithTime`: cGenDaoBriefWithTime,
`cGenDaoBriefDaoPath`: cGenDaoBriefDaoPath,
`cGenDaoBriefDoPath`: cGenDaoBriefDoPath,
`cGenDaoBriefEntityPath`: cGenDaoBriefEntityPath,
`cGenDaoBriefGJsonSupport`: cGenDaoBriefGJsonSupport,
`cGenDaoBriefImportPrefix`: cGenDaoBriefImportPrefix,
`cGenDaoBriefOverwriteDao`: cGenDaoBriefOverwriteDao,
`cGenDaoBriefModelFile`: cGenDaoBriefModelFile,
`cGenDaoBriefModelFileForDao`: cGenDaoBriefModelFileForDao,
`cGenDaoBriefDescriptionTag`: cGenDaoBriefDescriptionTag,
`cGenDaoBriefNoJsonTag`: cGenDaoBriefNoJsonTag,
`cGenDaoBriefNoModelComment`: cGenDaoBriefNoModelComment,
`cGenDaoBriefGroup`: cGenDaoBriefGroup,
`cGenDaoBriefJsonCase`: cGenDaoBriefJsonCase,
})
}
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"`
Link string `name:"link" short:"l" brief:"{cGenDaoBriefLink}"`
Tables string `name:"tables" short:"t" brief:"{cGenDaoBriefTables}"`
TablesEx string `name:"tablesEx" short:"x" brief:"{cGenDaoBriefTablesEx}"`
Group string `name:"group" short:"g" brief:"{cGenDaoBriefGroup}" d:"default"`
Prefix string `name:"prefix" short:"f" brief:"{cGenDaoBriefPrefix}"`
RemovePrefix string `name:"removePrefix" short:"r" brief:"{cGenDaoBriefRemovePrefix}"`
JsonCase string `name:"jsonCase" short:"j" brief:"{cGenDaoBriefJsonCase}" d:"CamelLower"`
ImportPrefix string `name:"importPrefix" short:"i" brief:"{cGenDaoBriefImportPrefix}"`
DaoPath string `name:"daoPath" short:"d" brief:"{cGenDaoBriefDaoPath}" d:"dao"`
DoPath string `name:"doPath" short:"o" brief:"{cGenDaoBriefDoPath}" d:"model/do"`
EntityPath string `name:"entityPath" short:"e" brief:"{cGenDaoBriefEntityPath}" d:"model/entity"`
StdTime bool `name:"stdTime" short:"s" brief:"{cGenDaoBriefStdTime}" orphan:"true"`
WithTime bool `name:"withTime" short:"w" brief:"{cGenDaoBriefWithTime}" orphan:"true"`
GJsonSupport bool `name:"gJsonSupport" short:"n" brief:"{cGenDaoBriefGJsonSupport}" orphan:"true"`
OverwriteDao bool `name:"overwriteDao" short:"v" brief:"{cGenDaoBriefOverwriteDao}" orphan:"true"`
DescriptionTag bool `name:"descriptionTag" short:"c" brief:"{cGenDaoBriefDescriptionTag}" orphan:"true"`
NoJsonTag bool `name:"noJsonTag" short:"k" brief:"{cGenDaoBriefNoJsonTag" orphan:"true"`
NoModelComment bool `name:"noModelComment" short:"m" brief:"{cGenDaoBriefNoModelComment}" orphan:"true"`
}
cGenDaoOutput struct{}
cGenDaoInternalInput struct {
cGenDaoInput
TableName string // TableName specifies the table name of the table.
NewTableName string // NewTableName specifies the prefix-stripped name of the table.
ModName string // ModName specifies the module name of current golang project, which is used for import purpose.
}
cGenDao = gendao.CGenDao
)
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() {
for i := 0; i < len(v.Interfaces()); i++ {
doGenDaoForArray(ctx, i, in)
}
} else {
doGenDaoForArray(ctx, -1, in)
}
} else {
doGenDaoForArray(ctx, -1, in)
}
mlog.Print("done!")
return
}
// doGenDaoForArray implements the "gen dao" command for configuration array.
func doGenDaoForArray(ctx context.Context, index int, in cGenDaoInput) {
var (
err error
db gdb.DB
modName string // Go module name, eg: github.com/gogf/gf.
)
if index >= 0 {
err = g.Cfg().MustGet(
ctx,
fmt.Sprintf(`%s.%d`, cGenDaoConfig, index),
).Scan(&in)
if err != nil {
mlog.Fatalf(`invalid configuration of "%s": %+v`, cGenDaoConfig, err)
}
}
if dirRealPath := gfile.RealPath(in.Path); dirRealPath == "" {
mlog.Fatalf(`path "%s" does not exist`, in.Path)
}
removePrefixArray := gstr.SplitAndTrim(in.RemovePrefix, ",")
if in.ImportPrefix == "" {
if !gfile.Exists("go.mod") {
mlog.Fatal("go.mod does not exist in current working directory")
}
var (
goModContent = gfile.GetContents("go.mod")
match, _ = gregex.MatchString(`^module\s+(.+)\s*`, goModContent)
)
if len(match) > 1 {
modName = gstr.Trim(match[1])
} else {
mlog.Fatal("module name does not found in go.mod")
}
}
// It uses user passed database configuration.
if in.Link != "" {
var (
tempGroup = gtime.TimestampNanoStr()
match, _ = gregex.MatchString(`([a-z]+):(.+)`, in.Link)
)
if len(match) == 3 {
gdb.AddConfigNode(tempGroup, gdb.ConfigNode{
Type: gstr.Trim(match[1]),
Link: gstr.Trim(match[2]),
})
if db, err = gdb.Instance(tempGroup); err != nil {
mlog.Debugf(`database initialization failed: %+v`, err)
}
} else {
mlog.Fatalf(`invalid database configuration: %s`, in.Link)
}
} else {
db = g.DB(in.Group)
}
if db == nil {
mlog.Fatal(`database initialization failed, may be invalid database configuration`)
}
var tableNames []string
if in.Tables != "" {
tableNames = gstr.SplitAndTrim(in.Tables, ",")
} else {
tableNames, err = db.Tables(context.TODO())
if err != nil {
mlog.Fatalf("fetching tables failed: %+v", err)
}
}
// Table excluding.
if in.TablesEx != "" {
array := garray.NewStrArrayFrom(tableNames)
for _, v := range gstr.SplitAndTrim(in.TablesEx, ",") {
array.RemoveValue(v)
}
tableNames = array.Slice()
}
// Generating dao & model go files one by one according to given table name.
newTableNames := make([]string, len(tableNames))
for i, tableName := range tableNames {
newTableName := tableName
for _, v := range removePrefixArray {
newTableName = gstr.TrimLeftStr(newTableName, v, 1)
}
newTableName = in.Prefix + newTableName
newTableNames[i] = newTableName
// Dao.
generateDao(ctx, db, cGenDaoInternalInput{
cGenDaoInput: in,
TableName: tableName,
NewTableName: newTableName,
ModName: modName,
})
}
// Do.
generateDo(ctx, db, tableNames, newTableNames, cGenDaoInternalInput{
cGenDaoInput: in,
ModName: modName,
})
// Entity.
generateEntity(ctx, db, tableNames, newTableNames, cGenDaoInternalInput{
cGenDaoInput: in,
ModName: modName,
})
}
// generateDaoContentFile generates the dao and model content of given table.
func generateDao(ctx context.Context, db gdb.DB, in cGenDaoInternalInput) {
// Generating table data preparing.
fieldMap, err := db.TableFields(ctx, in.TableName)
if err != nil {
mlog.Fatalf(`fetching tables fields failed for table "%s": %+v`, in.TableName, err)
}
var (
dirRealPath = gfile.RealPath(in.Path)
dirPathDao = gfile.Join(in.Path, in.DaoPath)
tableNameCamelCase = gstr.CaseCamel(in.NewTableName)
tableNameCamelLowerCase = gstr.CaseCamelLower(in.NewTableName)
tableNameSnakeCase = gstr.CaseSnake(in.NewTableName)
importPrefix = in.ImportPrefix
)
if importPrefix == "" {
if dirRealPath == "" {
dirRealPath = in.Path
importPrefix = dirRealPath
importPrefix = gstr.Trim(dirRealPath, "./")
} else {
importPrefix = gstr.Replace(dirRealPath, gfile.Pwd(), "")
}
importPrefix = gstr.Replace(importPrefix, gfile.Separator, "/")
importPrefix = gstr.Join(g.SliceStr{in.ModName, importPrefix, in.DaoPath}, "/")
importPrefix, _ = gregex.ReplaceString(`\/{2,}`, `/`, gstr.Trim(importPrefix, "/"))
} else {
importPrefix = gstr.Join(g.SliceStr{importPrefix, in.DaoPath}, "/")
}
fileName := gstr.Trim(tableNameSnakeCase, "-_.")
if len(fileName) > 5 && fileName[len(fileName)-5:] == "_test" {
// Add suffix to avoid the table name which contains "_test",
// which would make the go file a testing file.
fileName += "_table"
}
// dao - index
generateDaoIndex(in, tableNameCamelCase, tableNameCamelLowerCase, importPrefix, dirPathDao, fileName)
// dao - internal
generateDaoInternal(in, tableNameCamelCase, tableNameCamelLowerCase, importPrefix, dirPathDao, fileName, fieldMap)
}
func generateDo(ctx context.Context, db gdb.DB, tableNames, newTableNames []string, in cGenDaoInternalInput) {
var (
doDirPath = gfile.Join(in.Path, in.DoPath)
)
in.NoJsonTag = true
in.DescriptionTag = false
in.NoModelComment = false
// Model content.
for i, tableName := range tableNames {
in.TableName = tableName
fieldMap, err := db.TableFields(ctx, tableName)
if err != nil {
mlog.Fatalf("fetching tables fields failed for table '%s':\n%v", in.TableName, err)
}
var (
newTableName = newTableNames[i]
doFilePath = gfile.Join(doDirPath, gstr.CaseSnake(newTableName)+".go")
structDefinition = generateStructDefinition(generateStructDefinitionInput{
cGenDaoInternalInput: in,
StructName: gstr.CaseCamel(newTableName),
FieldMap: fieldMap,
IsDo: true,
})
)
// replace all types to interface{}.
structDefinition, _ = gregex.ReplaceStringFuncMatch(
"([A-Z]\\w*?)\\s+([\\w\\*\\.]+?)\\s+(//)",
structDefinition,
func(match []string) string {
// If the type is already a pointer/slice/map, it does nothing.
if !gstr.HasPrefix(match[2], "*") && !gstr.HasPrefix(match[2], "[]") && !gstr.HasPrefix(match[2], "map") {
return fmt.Sprintf(`%s interface{} %s`, match[1], match[3])
}
return match[0]
},
)
modelContent := generateDoContent(
in,
tableName,
gstr.CaseCamel(newTableName),
structDefinition,
)
err = gfile.PutContents(doFilePath, strings.TrimSpace(modelContent))
if err != nil {
mlog.Fatalf(`writing content to "%s" failed: %v`, doFilePath, err)
} else {
utils.GoFmt(doFilePath)
mlog.Print("generated:", doFilePath)
}
}
}
func generateEntity(ctx context.Context, db gdb.DB, tableNames, newTableNames []string, in cGenDaoInternalInput) {
var (
entityDirPath = gfile.Join(in.Path, in.EntityPath)
)
// Model content.
for i, tableName := range tableNames {
fieldMap, err := db.TableFields(ctx, tableName)
if err != nil {
mlog.Fatalf("fetching tables fields failed for table '%s':\n%v", in.TableName, err)
}
var (
newTableName = newTableNames[i]
entityFilePath = gfile.Join(entityDirPath, gstr.CaseSnake(newTableName)+".go")
entityContent = generateEntityContent(
in,
newTableName,
gstr.CaseCamel(newTableName),
generateStructDefinition(generateStructDefinitionInput{
cGenDaoInternalInput: in,
StructName: gstr.CaseCamel(newTableName),
FieldMap: fieldMap,
IsDo: false,
}),
)
)
err = gfile.PutContents(entityFilePath, strings.TrimSpace(entityContent))
if err != nil {
mlog.Fatalf("writing content to '%s' failed: %v", entityFilePath, err)
} else {
utils.GoFmt(entityFilePath)
mlog.Print("generated:", entityFilePath)
}
}
}
func getImportPartContent(source string, isDo bool) string {
var (
packageImportsArray = garray.NewStrArray()
)
if isDo {
packageImportsArray.Append(`"github.com/gogf/gf/v2/frame/g"`)
}
// Time package recognition.
if strings.Contains(source, "gtime.Time") {
packageImportsArray.Append(`"github.com/gogf/gf/v2/os/gtime"`)
} else if strings.Contains(source, "time.Time") {
packageImportsArray.Append(`"time"`)
}
// Json type.
if strings.Contains(source, "gjson.Json") {
packageImportsArray.Append(`"github.com/gogf/gf/v2/encoding/gjson"`)
}
// Generate and write content to golang file.
packageImportsStr := ""
if packageImportsArray.Len() > 0 {
packageImportsStr = fmt.Sprintf("import(\n%s\n)", packageImportsArray.Join("\n"))
}
return packageImportsStr
}
func generateEntityContent(in cGenDaoInternalInput, tableName, tableNameCamelCase, structDefine string) string {
entityContent := gstr.ReplaceByMap(consts.TemplateGenDaoEntityContent, g.MapStrStr{
tplVarTableName: tableName,
tplVarPackageImports: getImportPartContent(structDefine, false),
tplVarTableNameCamelCase: tableNameCamelCase,
tplVarStructDefine: structDefine,
})
entityContent = replaceDefaultVar(in, entityContent)
return entityContent
}
func generateDoContent(in cGenDaoInternalInput, tableName, tableNameCamelCase, structDefine string) string {
doContent := gstr.ReplaceByMap(consts.TemplateGenDaoDoContent, g.MapStrStr{
tplVarTableName: tableName,
tplVarPackageImports: getImportPartContent(structDefine, true),
tplVarTableNameCamelCase: tableNameCamelCase,
tplVarStructDefine: structDefine,
})
doContent = replaceDefaultVar(in, doContent)
return doContent
}
func generateDaoIndex(in cGenDaoInternalInput, tableNameCamelCase, tableNameCamelLowerCase, importPrefix, dirPathDao, fileName string) {
path := gfile.Join(dirPathDao, fileName+".go")
if in.OverwriteDao || !gfile.Exists(path) {
indexContent := gstr.ReplaceByMap(getTplDaoIndexContent(""), g.MapStrStr{
tplVarImportPrefix: importPrefix,
tplVarTableName: in.TableName,
tplVarTableNameCamelCase: tableNameCamelCase,
tplVarTableNameCamelLowerCase: tableNameCamelLowerCase,
})
indexContent = replaceDefaultVar(in, indexContent)
if err := gfile.PutContents(path, strings.TrimSpace(indexContent)); err != nil {
mlog.Fatalf("writing content to '%s' failed: %v", path, err)
} else {
utils.GoFmt(path)
mlog.Print("generated:", path)
}
}
}
func generateDaoInternal(
in cGenDaoInternalInput,
tableNameCamelCase, tableNameCamelLowerCase, importPrefix string,
dirPathDao, fileName string,
fieldMap map[string]*gdb.TableField,
) {
path := gfile.Join(dirPathDao, "internal", fileName+".go")
modelContent := gstr.ReplaceByMap(getTplDaoInternalContent(""), g.MapStrStr{
tplVarImportPrefix: importPrefix,
tplVarTableName: in.TableName,
tplVarGroupName: in.Group,
tplVarTableNameCamelCase: tableNameCamelCase,
tplVarTableNameCamelLowerCase: tableNameCamelLowerCase,
tplVarColumnDefine: gstr.Trim(generateColumnDefinitionForDao(fieldMap)),
tplVarColumnNames: gstr.Trim(generateColumnNamesForDao(fieldMap)),
})
modelContent = replaceDefaultVar(in, modelContent)
if err := gfile.PutContents(path, strings.TrimSpace(modelContent)); err != nil {
mlog.Fatalf("writing content to '%s' failed: %v", path, err)
} else {
utils.GoFmt(path)
mlog.Print("generated:", path)
}
}
func replaceDefaultVar(in cGenDaoInternalInput, origin string) string {
var tplDatetimeStr string
if in.WithTime {
tplDatetimeStr = fmt.Sprintf(`Created at %s`, createdAt.String())
}
return gstr.ReplaceByMap(origin, g.MapStrStr{
tplVarDatetimeStr: tplDatetimeStr,
})
}
type generateStructDefinitionInput struct {
cGenDaoInternalInput
StructName string // Struct name.
FieldMap map[string]*gdb.TableField // Table field map.
IsDo bool // Is generating DTO struct.
}
func generateStructDefinition(in generateStructDefinitionInput) string {
buffer := bytes.NewBuffer(nil)
array := make([][]string, len(in.FieldMap))
names := sortFieldKeyForDao(in.FieldMap)
for index, name := range names {
field := in.FieldMap[name]
array[index] = generateStructFieldDefinition(field, in)
}
tw := tablewriter.NewWriter(buffer)
tw.SetBorder(false)
tw.SetRowLine(false)
tw.SetAutoWrapText(false)
tw.SetColumnSeparator("")
tw.AppendBulk(array)
tw.Render()
stContent := buffer.String()
// Let's do this hack of table writer for indent!
stContent = gstr.Replace(stContent, " #", "")
stContent = gstr.Replace(stContent, "` ", "`")
stContent = gstr.Replace(stContent, "``", "")
buffer.Reset()
buffer.WriteString(fmt.Sprintf("type %s struct {\n", in.StructName))
if in.IsDo {
buffer.WriteString(fmt.Sprintf("g.Meta `orm:\"table:%s, do:true\"`\n", in.TableName))
}
buffer.WriteString(stContent)
buffer.WriteString("}")
return buffer.String()
}
// generateStructFieldForModel generates and returns the attribute definition for specified field.
func generateStructFieldDefinition(field *gdb.TableField, in generateStructDefinitionInput) []string {
var (
typeName string
jsonTag = getJsonTagFromCase(field.Name, in.JsonCase)
)
t, _ := gregex.ReplaceString(`\(.+\)`, "", field.Type)
t = gstr.Split(gstr.Trim(t), " ")[0]
t = gstr.ToLower(t)
switch t {
case "binary", "varbinary", "blob", "tinyblob", "mediumblob", "longblob":
typeName = "[]byte"
case "bit", "int", "int2", "tinyint", "small_int", "smallint", "medium_int", "mediumint", "serial":
if gstr.ContainsI(field.Type, "unsigned") {
typeName = "uint"
} else {
typeName = "int"
}
case "int4", "int8", "big_int", "bigint", "bigserial":
if gstr.ContainsI(field.Type, "unsigned") {
typeName = "uint64"
} else {
typeName = "int64"
}
case "real":
typeName = "float32"
case "float", "double", "decimal", "smallmoney", "numeric":
typeName = "float64"
case "bool":
typeName = "bool"
case "datetime", "timestamp", "date", "time":
if in.StdTime {
typeName = "time.Time"
} else {
typeName = "*gtime.Time"
}
case "json", "jsonb":
if in.GJsonSupport {
typeName = "*gjson.Json"
} else {
typeName = "string"
}
default:
// Automatically detect its data type.
switch {
case strings.Contains(t, "int"):
typeName = "int"
case strings.Contains(t, "text") || strings.Contains(t, "char"):
typeName = "string"
case strings.Contains(t, "float") || strings.Contains(t, "double"):
typeName = "float64"
case strings.Contains(t, "bool"):
typeName = "bool"
case strings.Contains(t, "binary") || strings.Contains(t, "blob"):
typeName = "[]byte"
case strings.Contains(t, "date") || strings.Contains(t, "time"):
if in.StdTime {
typeName = "time.Time"
} else {
typeName = "*gtime.Time"
}
default:
typeName = "string"
}
}
var (
tagKey = "`"
result = []string{
" #" + gstr.CaseCamel(field.Name),
" #" + typeName,
}
descriptionTag = gstr.Replace(formatComment(field.Comment), `"`, `\"`)
)
result = append(result, " #"+fmt.Sprintf(tagKey+`json:"%s"`, jsonTag))
result = append(result, " #"+fmt.Sprintf(`description:"%s"`+tagKey, descriptionTag))
result = append(result, " #"+fmt.Sprintf(`// %s`, formatComment(field.Comment)))
for k, v := range result {
if in.NoJsonTag {
v, _ = gregex.ReplaceString(`json:".+"`, ``, v)
}
if !in.DescriptionTag {
v, _ = gregex.ReplaceString(`description:".*"`, ``, v)
}
if in.NoModelComment {
v, _ = gregex.ReplaceString(`//.+`, ``, v)
}
result[k] = v
}
return result
}
// formatComment formats the comment string to fit the golang code without any lines.
func formatComment(comment string) string {
comment = gstr.ReplaceByArray(comment, g.SliceStr{
"\n", " ",
"\r", " ",
})
comment = gstr.Replace(comment, `\n`, " ")
comment = gstr.Trim(comment)
return comment
}
// generateColumnDefinitionForDao generates and returns the column names definition for specified table.
func generateColumnDefinitionForDao(fieldMap map[string]*gdb.TableField) string {
var (
buffer = bytes.NewBuffer(nil)
array = make([][]string, len(fieldMap))
names = sortFieldKeyForDao(fieldMap)
)
for index, name := range names {
var (
field = fieldMap[name]
comment = gstr.Trim(gstr.ReplaceByArray(field.Comment, g.SliceStr{
"\n", " ",
"\r", " ",
}))
)
array[index] = []string{
" #" + gstr.CaseCamel(field.Name),
" # " + "string",
" #" + fmt.Sprintf(`// %s`, comment),
}
}
tw := tablewriter.NewWriter(buffer)
tw.SetBorder(false)
tw.SetRowLine(false)
tw.SetAutoWrapText(false)
tw.SetColumnSeparator("")
tw.AppendBulk(array)
tw.Render()
defineContent := buffer.String()
// Let's do this hack of table writer for indent!
defineContent = gstr.Replace(defineContent, " #", "")
buffer.Reset()
buffer.WriteString(defineContent)
return buffer.String()
}
// generateColumnNamesForDao generates and returns the column names assignment content of column struct
// for specified table.
func generateColumnNamesForDao(fieldMap map[string]*gdb.TableField) string {
var (
buffer = bytes.NewBuffer(nil)
array = make([][]string, len(fieldMap))
names = sortFieldKeyForDao(fieldMap)
)
for index, name := range names {
field := fieldMap[name]
array[index] = []string{
" #" + gstr.CaseCamel(field.Name) + ":",
fmt.Sprintf(` #"%s",`, field.Name),
}
}
tw := tablewriter.NewWriter(buffer)
tw.SetBorder(false)
tw.SetRowLine(false)
tw.SetAutoWrapText(false)
tw.SetColumnSeparator("")
tw.AppendBulk(array)
tw.Render()
namesContent := buffer.String()
// Let's do this hack of table writer for indent!
namesContent = gstr.Replace(namesContent, " #", "")
buffer.Reset()
buffer.WriteString(namesContent)
return buffer.String()
}
func getTplDaoIndexContent(tplDaoIndexPath string) string {
if tplDaoIndexPath != "" {
return gfile.GetContents(tplDaoIndexPath)
}
return consts.TemplateDaoDaoIndexContent
}
func getTplDaoInternalContent(tplDaoInternalPath string) string {
if tplDaoInternalPath != "" {
return gfile.GetContents(tplDaoInternalPath)
}
return consts.TemplateDaoDaoInternalContent
}
// getJsonTagFromCase call gstr.Case* function to convert the s to specified case.
func getJsonTagFromCase(str, caseStr string) string {
switch gstr.ToLower(caseStr) {
case gstr.ToLower("Camel"):
return gstr.CaseCamel(str)
case gstr.ToLower("CamelLower"):
return gstr.CaseCamelLower(str)
case gstr.ToLower("Kebab"):
return gstr.CaseKebab(str)
case gstr.ToLower("KebabScreaming"):
return gstr.CaseKebabScreaming(str)
case gstr.ToLower("Snake"):
return gstr.CaseSnake(str)
case gstr.ToLower("SnakeFirstUpper"):
return gstr.CaseSnakeFirstUpper(str)
case gstr.ToLower("SnakeScreaming"):
return gstr.CaseSnakeScreaming(str)
}
return str
}
func sortFieldKeyForDao(fieldMap map[string]*gdb.TableField) []string {
names := make(map[int]string)
for _, field := range fieldMap {
names[field.Index] = field.Name
}
var (
i = 0
j = 0
result = make([]string, len(names))
)
for {
if len(names) == 0 {
break
}
if val, ok := names[i]; ok {
result[j] = val
j++
delete(names, i)
}
i++
}
return result
}

View File

@ -153,10 +153,10 @@ func doGenPbEntityForArray(ctx context.Context, index int, in cGenPbEntityInput)
if index >= 0 {
err = g.Cfg().MustGet(
ctx,
fmt.Sprintf(`%s.%d`, cGenDaoConfig, index),
fmt.Sprintf(`%s.%d`, cGenPbEntityConfig, index),
).Scan(&in)
if err != nil {
mlog.Fatalf(`invalid configuration of "%s": %+v`, cGenDaoConfig, err)
mlog.Fatalf(`invalid configuration of "%s": %+v`, cGenPbEntityConfig, err)
}
}
if in.Package == "" {

View File

@ -3,6 +3,8 @@ package cmd
import (
"context"
"fmt"
"go/parser"
"go/token"
"github.com/gogf/gf/cmd/gf/v2/internal/consts"
"github.com/gogf/gf/cmd/gf/v2/internal/utility/mlog"
@ -15,19 +17,68 @@ import (
"github.com/gogf/gf/v2/text/gregex"
"github.com/gogf/gf/v2/text/gstr"
"github.com/gogf/gf/v2/util/gconv"
"github.com/gogf/gf/v2/util/gtag"
)
const (
cGenServiceConfig = `gfcli.gen.service`
cGenServiceUsage = `gf gen service [OPTION]`
cGenServiceBrief = `parse struct and associated functions from packages to generate service go file`
cGenServiceEg = `
gf gen service
gf gen service -f Snake
`
cGenServiceBriefSrcFolder = `source folder path to be parsed. default: internal/logic`
cGenServiceBriefDstFolder = `destination folder path storing automatically generated go files. default: internal/service`
cGenServiceBriefFileNameCase = `
destination file name storing automatically generated go files, cases are as follows:
| Case | Example |
|---------------- |--------------------|
| Lower | anykindofstring |
| Camel | AnyKindOfString |
| CamelLower | anyKindOfString |
| Snake | any_kind_of_string | default
| SnakeScreaming | ANY_KIND_OF_STRING |
| SnakeFirstUpper | rgb_code_md5 |
| Kebab | any-kind-of-string |
| KebabScreaming | ANY-KIND-OF-STRING |
`
cGenServiceBriefWatchFile = `used in file watcher, it generates service go files only if given file is under srcFolder`
cGenServiceBriefStPattern = `regular expression matching struct name for generating service. default: s([A-Z]\\\\w+)`
cGenServiceBriefPackages = `produce go files only for given source packages`
cGenServiceBriefImportPrefix = `custom import prefix to calculate import path for generated importing go file of logic`
cGenServiceBriefOverWrite = `overwrite service go files that already exist in generating folder. default: true`
)
func init() {
gtag.Sets(g.MapStrStr{
`cGenServiceConfig`: cGenServiceConfig,
`cGenServiceUsage`: cGenServiceUsage,
`cGenServiceBrief`: cGenServiceBrief,
`cGenServiceEg`: cGenServiceEg,
`cGenServiceBriefSrcFolder`: cGenServiceBriefSrcFolder,
`cGenServiceBriefDstFolder`: cGenServiceBriefDstFolder,
`cGenServiceBriefFileNameCase`: cGenServiceBriefFileNameCase,
`cGenServiceBriefWatchFile`: cGenServiceBriefWatchFile,
`cGenServiceBriefStPattern`: cGenServiceBriefStPattern,
`cGenServiceBriefPackages`: cGenServiceBriefPackages,
`cGenServiceBriefImportPrefix`: cGenServiceBriefImportPrefix,
`cGenServiceBriefOverWrite`: cGenServiceBriefOverWrite,
})
}
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"`
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"`
g.Meta `name:"service" config:"{cGenServiceConfig}" usage:"{cGenServiceUsage}" brief:"{cGenServiceBrief}" eg:"{cGenServiceEg}"`
SrcFolder string `short:"s" name:"srcFolder" brief:"{cGenServiceBriefSrcFolder}" d:"internal/logic"`
DstFolder string `short:"d" name:"dstFolder" brief:"{cGenServiceBriefDstFolder}" d:"internal/service"`
DstFileNameCase string `short:"f" name:"dstFileNameCase" brief:"{cGenServiceBriefFileNameCase}" d:"Snake"`
WatchFile string `short:"w" name:"watchFile" brief:"{cGenServiceBriefWatchFile}"`
StPattern string `short:"a" name:"stPattern" brief:"{cGenServiceBriefStPattern}" d:"s([A-Z]\\w+)"`
Packages []string `short:"p" name:"packages" brief:"{cGenServiceBriefPackages}"`
ImportPrefix string `short:"i" name:"importPrefix" brief:"{cGenServiceBriefImportPrefix}"`
OverWrite bool `short:"o" name:"overwrite" brief:"{cGenServiceBriefOverWrite}" d:"true" orphan:"true"`
}
cGenServiceOutput struct{}
)
@ -171,11 +222,7 @@ func (c cGenService) Service(ctx context.Context, in cGenServiceInput) (out *cGe
}
}
// 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
}
@ -188,16 +235,23 @@ func (c cGenService) Service(ctx context.Context, in cGenServiceInput) (out *cGe
}
func (c cGenService) calculateImportedPackages(fileContent string, srcImportedPackages *garray.SortedStrArray) (err error) {
var match []string
match, err = gregex.MatchString(`\s+import\s+\(([\s\S]+?)\)`, fileContent)
f, err := parser.ParseFile(token.NewFileSet(), "", fileContent, parser.ImportsOnly)
if err != nil {
return err
}
if len(match) < 2 {
return nil
for _, s := range f.Imports {
if s.Path != nil {
if s.Name != nil {
// has alias and is not `_`
if pkgAlias := s.Name.String(); pkgAlias != "_" {
srcImportedPackages.Add(pkgAlias + " " + s.Path.Value)
}
} else {
// no alias
srcImportedPackages.Add(s.Path.Value)
}
}
}
importPart := gstr.Trim(match[1])
srcImportedPackages.Append(gstr.SplitAndTrim(importPart, "\n")...)
return nil
}
@ -264,7 +318,7 @@ func (c cGenService) generateServiceFiles(
)
for structName, funcArray := range srcPkgInterfaceMap {
var (
filePath = gfile.Join(in.DstFolder, gstr.ToLower(structName)+".go")
filePath = gfile.Join(in.DstFolder, c.getDstFileNameCase(structName, in.DstFileNameCase)+".go")
generatedContent = gstr.ReplaceByMap(consts.TemplateGenServiceContent, g.MapStrStr{
"{Imports}": srcImportedPackagesContent,
"{StructName}": structName,
@ -358,3 +412,30 @@ func (c cGenService) replaceGeneratedServiceContentGFV2(in cGenServiceInput) (er
return content
}, in.DstFolder, "*.go", false)
}
// getDstFileNameCase call gstr.Case* function to convert the s to specified case.
func (c cGenService) getDstFileNameCase(str, caseStr string) string {
switch gstr.ToLower(caseStr) {
case gstr.ToLower("Lower"):
return gstr.ToLower(str)
case gstr.ToLower("Camel"):
return gstr.CaseCamel(str)
case gstr.ToLower("CamelLower"):
return gstr.CaseCamelLower(str)
case gstr.ToLower("Kebab"):
return gstr.CaseKebab(str)
case gstr.ToLower("KebabScreaming"):
return gstr.CaseKebabScreaming(str)
case gstr.ToLower("SnakeFirstUpper"):
return gstr.CaseSnakeFirstUpper(str)
case gstr.ToLower("SnakeScreaming"):
return gstr.CaseSnakeScreaming(str)
}
return gstr.CaseSnake(str)
}

View File

@ -94,6 +94,7 @@ func (c cInit) Index(ctx context.Context, in cInitInput) (out *cInitOutput, err
// Update the GoFrame version.
if in.Update {
mlog.Print("update goframe...")
// go get -u github.com/gogf/gf/v2@latest
updateCommand := `go get -u github.com/gogf/gf/v2@latest`
if in.Name != "." {
updateCommand = fmt.Sprintf(`cd %s && %s`, in.Name, updateCommand)
@ -101,6 +102,14 @@ func (c cInit) Index(ctx context.Context, in cInitInput) (out *cInitOutput, err
if err = gproc.ShellRun(ctx, updateCommand); err != nil {
mlog.Fatal(err)
}
// go mod tidy
gomModTidyCommand := `go mod tidy`
if in.Name != "." {
gomModTidyCommand = fmt.Sprintf(`cd %s && %s`, in.Name, gomModTidyCommand)
}
if err = gproc.ShellRun(ctx, gomModTidyCommand); err != nil {
mlog.Fatal(err)
}
}
mlog.Print("initialization done! ")

View File

@ -13,7 +13,6 @@ import (
"github.com/gogf/gf/v2/os/gproc"
"github.com/gogf/gf/v2/os/gtime"
"github.com/gogf/gf/v2/os/gtimer"
"github.com/gogf/gf/v2/text/gstr"
"github.com/gogf/gf/v2/util/gtag"
)
@ -47,6 +46,7 @@ which compiles and runs the go codes asynchronously when codes change.
cRunFileBrief = `building file path.`
cRunPathBrief = `output directory path for built binary file. it's "manifest/output" in default`
cRunExtraBrief = `the same options as "go run"/"go build" except some options as follows defined`
cRunArgsBrief = `custom arguments for your process`
)
var (
@ -62,6 +62,7 @@ func init() {
`cRunFileBrief`: cRunFileBrief,
`cRunPathBrief`: cRunPathBrief,
`cRunExtraBrief`: cRunExtraBrief,
`cRunArgsBrief`: cRunArgsBrief,
})
}
@ -71,6 +72,7 @@ type (
File string `name:"FILE" arg:"true" brief:"{cRunFileBrief}" v:"required"`
Path string `name:"path" short:"p" brief:"{cRunPathBrief}" d:"./"`
Extra string `name:"extra" short:"e" brief:"{cRunExtraBrief}"`
Args string `name:"args" short:"a" brief:"{cRunArgsBrief}"`
}
cRunOutput struct{}
)
@ -85,6 +87,7 @@ func (c cRun) Index(ctx context.Context, in cRunInput) (out *cRunOutput, err err
File: in.File,
Path: in.Path,
Options: in.Extra,
Args: in.Args,
}
dirty := gtype.NewBool()
_, err = gfsnotify.Add(gfile.RealPath("."), func(event *gfsnotify.Event) {
@ -150,9 +153,9 @@ func (app *cRunApp) Run(ctx context.Context) {
if runtime.GOOS == "windows" {
// Special handling for windows platform.
// DO NOT USE "cmd /c" command.
process = gproc.NewProcess(outputPath, gstr.SplitAndTrim(" ", app.Args))
process = gproc.NewProcess(runCommand, nil)
} else {
process = gproc.NewProcessCmd(outputPath, gstr.SplitAndTrim(" ", app.Args))
process = gproc.NewProcessCmd(runCommand, nil)
}
if pid, err := process.Start(ctx); err != nil {
mlog.Printf("build running error: %s", err.Error())

View File

@ -0,0 +1,363 @@
package gendao
import (
"context"
"fmt"
"strings"
"github.com/gogf/gf/cmd/gf/v2/internal/utility/mlog"
"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/gfile"
"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/gtag"
)
const (
CGenDaoConfig = `gfcli.gen.dao`
CGenDaoUsage = `gf gen dao [OPTION]`
CGenDaoBrief = `automatically generate go files for dao/do/entity`
CGenDaoEg = `
gf gen dao
gf gen dao -l "mysql:root:12345678@tcp(127.0.0.1:3306)/test"
gf gen dao -p ./model -g user-center -t user,user_detail,user_login
gf gen dao -r user_
`
CGenDaoAd = `
CONFIGURATION SUPPORT
Options are also supported by configuration file.
It's suggested using configuration file instead of command line arguments making producing.
The configuration node name is "gfcli.gen.dao", which also supports multiple databases, for example(config.yaml):
gfcli:
gen:
dao:
- link: "mysql:root:12345678@tcp(127.0.0.1:3306)/test"
tables: "order,products"
jsonCase: "CamelLower"
- link: "mysql:root:12345678@tcp(127.0.0.1:3306)/primary"
path: "./my-app"
prefix: "primary_"
tables: "user, userDetail"
`
CGenDaoBriefPath = `directory path for generated files`
CGenDaoBriefLink = `database configuration, the same as the ORM configuration of GoFrame`
CGenDaoBriefTables = `generate models only for given tables, multiple table names separated with ','`
CGenDaoBriefTablesEx = `generate models excluding given tables, multiple table names separated with ','`
CGenDaoBriefPrefix = `add prefix for all table of specified link/database tables`
CGenDaoBriefRemovePrefix = `remove specified prefix of the table, multiple prefix separated with ','`
CGenDaoBriefStdTime = `use time.Time from stdlib instead of gtime.Time for generated time/date fields of tables`
CGenDaoBriefWithTime = `add created time for auto produced go files`
CGenDaoBriefGJsonSupport = `use gJsonSupport to use *gjson.Json instead of string for generated json fields of tables`
CGenDaoBriefImportPrefix = `custom import prefix for generated go files`
CGenDaoBriefDaoPath = `directory path for storing generated dao files under path`
CGenDaoBriefDoPath = `directory path for storing generated do files under path`
CGenDaoBriefEntityPath = `directory path for storing generated entity files under path`
CGenDaoBriefOverwriteDao = `overwrite all dao files both inside/outside internal folder`
CGenDaoBriefModelFile = `custom file name for storing generated model content`
CGenDaoBriefModelFileForDao = `custom file name generating model for DAO operations like Where/Data. It's empty in default`
CGenDaoBriefDescriptionTag = `add comment to description tag for each field`
CGenDaoBriefNoJsonTag = `no json tag will be added for each field`
CGenDaoBriefNoModelComment = `no model comment will be added for each field`
CGenDaoBriefGroup = `
specifying the configuration group name of database for generated ORM instance,
it's not necessary and the default value is "default"
`
CGenDaoBriefJsonCase = `
generated json tag case for model struct, cases are as follows:
| Case | Example |
|---------------- |--------------------|
| Camel | AnyKindOfString |
| CamelLower | anyKindOfString | default
| Snake | any_kind_of_string |
| SnakeScreaming | ANY_KIND_OF_STRING |
| SnakeFirstUpper | rgb_code_md5 |
| Kebab | any-kind-of-string |
| KebabScreaming | ANY-KIND-OF-STRING |
`
CGenDaoBriefTplDaoIndexPath = `template file path for dao index file`
CGenDaoBriefTplDaoInternalPath = `template file path for dao internal file`
CGenDaoBriefTplDaoDoPathPath = `template file path for dao do file`
CGenDaoBriefTplDaoEntityPath = `template file path for dao entity file`
tplVarTableName = `{TplTableName}`
tplVarTableNameCamelCase = `{TplTableNameCamelCase}`
tplVarTableNameCamelLowerCase = `{TplTableNameCamelLowerCase}`
tplVarPackageImports = `{TplPackageImports}`
tplVarImportPrefix = `{TplImportPrefix}`
tplVarStructDefine = `{TplStructDefine}`
tplVarColumnDefine = `{TplColumnDefine}`
tplVarColumnNames = `{TplColumnNames}`
tplVarGroupName = `{TplGroupName}`
tplVarDatetimeStr = `{TplDatetimeStr}`
tplVarCreatedAtDatetimeStr = `{TplCreatedAtDatetimeStr}`
)
var (
createdAt = gtime.Now()
)
func init() {
gtag.Sets(g.MapStrStr{
`CGenDaoConfig`: CGenDaoConfig,
`CGenDaoUsage`: CGenDaoUsage,
`CGenDaoBrief`: CGenDaoBrief,
`CGenDaoEg`: CGenDaoEg,
`CGenDaoAd`: CGenDaoAd,
`CGenDaoBriefPath`: CGenDaoBriefPath,
`CGenDaoBriefLink`: CGenDaoBriefLink,
`CGenDaoBriefTables`: CGenDaoBriefTables,
`CGenDaoBriefTablesEx`: CGenDaoBriefTablesEx,
`CGenDaoBriefPrefix`: CGenDaoBriefPrefix,
`CGenDaoBriefRemovePrefix`: CGenDaoBriefRemovePrefix,
`CGenDaoBriefStdTime`: CGenDaoBriefStdTime,
`CGenDaoBriefWithTime`: CGenDaoBriefWithTime,
`CGenDaoBriefDaoPath`: CGenDaoBriefDaoPath,
`CGenDaoBriefDoPath`: CGenDaoBriefDoPath,
`CGenDaoBriefEntityPath`: CGenDaoBriefEntityPath,
`CGenDaoBriefGJsonSupport`: CGenDaoBriefGJsonSupport,
`CGenDaoBriefImportPrefix`: CGenDaoBriefImportPrefix,
`CGenDaoBriefOverwriteDao`: CGenDaoBriefOverwriteDao,
`CGenDaoBriefModelFile`: CGenDaoBriefModelFile,
`CGenDaoBriefModelFileForDao`: CGenDaoBriefModelFileForDao,
`CGenDaoBriefDescriptionTag`: CGenDaoBriefDescriptionTag,
`CGenDaoBriefNoJsonTag`: CGenDaoBriefNoJsonTag,
`CGenDaoBriefNoModelComment`: CGenDaoBriefNoModelComment,
`CGenDaoBriefGroup`: CGenDaoBriefGroup,
`CGenDaoBriefJsonCase`: CGenDaoBriefJsonCase,
`CGenDaoBriefTplDaoIndexPath`: CGenDaoBriefTplDaoIndexPath,
`CGenDaoBriefTplDaoInternalPath`: CGenDaoBriefTplDaoInternalPath,
`CGenDaoBriefTplDaoDoPathPath`: CGenDaoBriefTplDaoDoPathPath,
`CGenDaoBriefTplDaoEntityPath`: CGenDaoBriefTplDaoEntityPath,
})
}
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"`
Link string `name:"link" short:"l" brief:"{CGenDaoBriefLink}"`
Tables string `name:"tables" short:"t" brief:"{CGenDaoBriefTables}"`
TablesEx string `name:"tablesEx" short:"x" brief:"{CGenDaoBriefTablesEx}"`
Group string `name:"group" short:"g" brief:"{CGenDaoBriefGroup}" d:"default"`
Prefix string `name:"prefix" short:"f" brief:"{CGenDaoBriefPrefix}"`
RemovePrefix string `name:"removePrefix" short:"r" brief:"{CGenDaoBriefRemovePrefix}"`
JsonCase string `name:"jsonCase" short:"j" brief:"{CGenDaoBriefJsonCase}" d:"CamelLower"`
ImportPrefix string `name:"importPrefix" short:"i" brief:"{CGenDaoBriefImportPrefix}"`
DaoPath string `name:"daoPath" short:"d" brief:"{CGenDaoBriefDaoPath}" d:"dao"`
DoPath string `name:"doPath" short:"o" brief:"{CGenDaoBriefDoPath}" d:"model/do"`
EntityPath string `name:"entityPath" short:"e" brief:"{CGenDaoBriefEntityPath}" d:"model/entity"`
TplDaoIndexPath string `name:"tplDaoIndexPath" short:"t1" brief:"{CGenDaoBriefTplDaoIndexPath}"`
TplDaoInternalPath string `name:"tplDaoInternalPath" short:"t2" brief:"{CGenDaoBriefTplDaoInternalPath}"`
TplDaoDoPath string `name:"tplDaoDoPath" short:"t3" brief:"{CGenDaoBriefTplDaoDoPathPath}"`
TplDaoEntitylPath string `name:"tplDaoEntityPath" short:"t4" brief:"{CGenDaoBriefTplDaoEntityPath}"`
StdTime bool `name:"stdTime" short:"s" brief:"{CGenDaoBriefStdTime}" orphan:"true"`
WithTime bool `name:"withTime" short:"w" brief:"{CGenDaoBriefWithTime}" orphan:"true"`
GJsonSupport bool `name:"gJsonSupport" short:"n" brief:"{CGenDaoBriefGJsonSupport}" orphan:"true"`
OverwriteDao bool `name:"overwriteDao" short:"v" brief:"{CGenDaoBriefOverwriteDao}" orphan:"true"`
DescriptionTag bool `name:"descriptionTag" short:"c" brief:"{CGenDaoBriefDescriptionTag}" orphan:"true"`
NoJsonTag bool `name:"noJsonTag" short:"k" brief:"{CGenDaoBriefNoJsonTag}" orphan:"true"`
NoModelComment bool `name:"noModelComment" short:"m" brief:"{CGenDaoBriefNoModelComment}" orphan:"true"`
}
CGenDaoOutput struct{}
CGenDaoInternalInput struct {
CGenDaoInput
TableName string // TableName specifies the table name of the table.
NewTableName string // NewTableName specifies the prefix-stripped name of the table.
ModName string // ModName specifies the module name of current golang project, which is used for import purpose.
}
)
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() {
for i := 0; i < len(v.Interfaces()); i++ {
doGenDaoForArray(ctx, i, in)
}
} else {
doGenDaoForArray(ctx, -1, in)
}
} else {
doGenDaoForArray(ctx, -1, in)
}
mlog.Print("done!")
return
}
// doGenDaoForArray implements the "gen dao" command for configuration array.
func doGenDaoForArray(ctx context.Context, index int, in CGenDaoInput) {
var (
err error
db gdb.DB
modName string // Go module name, eg: github.com/gogf/gf.
)
if index >= 0 {
err = g.Cfg().MustGet(
ctx,
fmt.Sprintf(`%s.%d`, CGenDaoConfig, index),
).Scan(&in)
if err != nil {
mlog.Fatalf(`invalid configuration of "%s": %+v`, CGenDaoConfig, err)
}
}
if dirRealPath := gfile.RealPath(in.Path); dirRealPath == "" {
mlog.Fatalf(`path "%s" does not exist`, in.Path)
}
removePrefixArray := gstr.SplitAndTrim(in.RemovePrefix, ",")
if in.ImportPrefix == "" {
if !gfile.Exists("go.mod") {
mlog.Fatal("go.mod does not exist in current working directory")
}
var (
goModContent = gfile.GetContents("go.mod")
match, _ = gregex.MatchString(`^module\s+(.+)\s*`, goModContent)
)
if len(match) > 1 {
modName = gstr.Trim(match[1])
} else {
mlog.Fatal("module name does not found in go.mod")
}
}
// It uses user passed database configuration.
if in.Link != "" {
var tempGroup = gtime.TimestampNanoStr()
gdb.AddConfigNode(tempGroup, gdb.ConfigNode{
Link: in.Link,
})
if db, err = gdb.Instance(tempGroup); err != nil {
mlog.Fatalf(`database initialization failed: %+v`, err)
}
} else {
db = g.DB(in.Group)
}
if db == nil {
mlog.Fatal(`database initialization failed, may be invalid database configuration`)
}
var tableNames []string
if in.Tables != "" {
tableNames = gstr.SplitAndTrim(in.Tables, ",")
} else {
tableNames, err = db.Tables(context.TODO())
if err != nil {
mlog.Fatalf("fetching tables failed: %+v", err)
}
}
// Table excluding.
if in.TablesEx != "" {
array := garray.NewStrArrayFrom(tableNames)
for _, v := range gstr.SplitAndTrim(in.TablesEx, ",") {
array.RemoveValue(v)
}
tableNames = array.Slice()
}
// Generating dao & model go files one by one according to given table name.
newTableNames := make([]string, len(tableNames))
for i, tableName := range tableNames {
newTableName := tableName
for _, v := range removePrefixArray {
newTableName = gstr.TrimLeftStr(newTableName, v, 1)
}
newTableName = in.Prefix + newTableName
newTableNames[i] = newTableName
// Dao.
generateDao(ctx, db, CGenDaoInternalInput{
CGenDaoInput: in,
TableName: tableName,
NewTableName: newTableName,
ModName: modName,
})
}
// Do.
generateDo(ctx, db, tableNames, newTableNames, CGenDaoInternalInput{
CGenDaoInput: in,
ModName: modName,
})
// Entity.
generateEntity(ctx, db, tableNames, newTableNames, CGenDaoInternalInput{
CGenDaoInput: in,
ModName: modName,
})
}
func getImportPartContent(source string, isDo bool) string {
var (
packageImportsArray = garray.NewStrArray()
)
if isDo {
packageImportsArray.Append(`"github.com/gogf/gf/v2/frame/g"`)
}
// Time package recognition.
if strings.Contains(source, "gtime.Time") {
packageImportsArray.Append(`"github.com/gogf/gf/v2/os/gtime"`)
} else if strings.Contains(source, "time.Time") {
packageImportsArray.Append(`"time"`)
}
// Json type.
if strings.Contains(source, "gjson.Json") {
packageImportsArray.Append(`"github.com/gogf/gf/v2/encoding/gjson"`)
}
// Generate and write content to golang file.
packageImportsStr := ""
if packageImportsArray.Len() > 0 {
packageImportsStr = fmt.Sprintf("import(\n%s\n)", packageImportsArray.Join("\n"))
}
return packageImportsStr
}
func replaceDefaultVar(in CGenDaoInternalInput, origin string) string {
var tplCreatedAtDatetimeStr string
var tplDatetimeStr string = createdAt.String()
if in.WithTime {
tplCreatedAtDatetimeStr = fmt.Sprintf(`Created at %s`, tplDatetimeStr)
}
return gstr.ReplaceByMap(origin, g.MapStrStr{
tplVarDatetimeStr: tplDatetimeStr,
tplVarCreatedAtDatetimeStr: tplCreatedAtDatetimeStr,
})
}
func sortFieldKeyForDao(fieldMap map[string]*gdb.TableField) []string {
names := make(map[int]string)
for _, field := range fieldMap {
names[field.Index] = field.Name
}
var (
i = 0
j = 0
result = make([]string, len(names))
)
for {
if len(names) == 0 {
break
}
if val, ok := names[i]; ok {
result[j] = val
j++
delete(names, i)
}
i++
}
return result
}
func getTemplateFromPathOrDefault(filePath string, def string) string {
if filePath != "" {
if contents := gfile.GetContents(filePath); contents != "" {
return contents
}
}
return def
}

View File

@ -0,0 +1,176 @@
package gendao
import (
"bytes"
"context"
"fmt"
"strings"
"github.com/gogf/gf/cmd/gf/v2/internal/consts"
"github.com/gogf/gf/cmd/gf/v2/internal/utility/mlog"
"github.com/gogf/gf/cmd/gf/v2/internal/utility/utils"
"github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gfile"
"github.com/gogf/gf/v2/text/gregex"
"github.com/gogf/gf/v2/text/gstr"
"github.com/olekukonko/tablewriter"
)
// generateDaoContentFile generates the dao and model content of given table.
func generateDao(ctx context.Context, db gdb.DB, in CGenDaoInternalInput) {
// Generating table data preparing.
fieldMap, err := db.TableFields(ctx, in.TableName)
if err != nil {
mlog.Fatalf(`fetching tables fields failed for table "%s": %+v`, in.TableName, err)
}
var (
dirRealPath = gfile.RealPath(in.Path)
dirPathDao = gfile.Join(in.Path, in.DaoPath)
tableNameCamelCase = gstr.CaseCamel(in.NewTableName)
tableNameCamelLowerCase = gstr.CaseCamelLower(in.NewTableName)
tableNameSnakeCase = gstr.CaseSnake(in.NewTableName)
importPrefix = in.ImportPrefix
)
if importPrefix == "" {
if dirRealPath == "" {
dirRealPath = in.Path
importPrefix = dirRealPath
importPrefix = gstr.Trim(dirRealPath, "./")
} else {
importPrefix = gstr.Replace(dirRealPath, gfile.Pwd(), "")
}
importPrefix = gstr.Replace(importPrefix, gfile.Separator, "/")
importPrefix = gstr.Join(g.SliceStr{in.ModName, importPrefix, in.DaoPath}, "/")
importPrefix, _ = gregex.ReplaceString(`\/{2,}`, `/`, gstr.Trim(importPrefix, "/"))
} else {
importPrefix = gstr.Join(g.SliceStr{importPrefix, in.DaoPath}, "/")
}
fileName := gstr.Trim(tableNameSnakeCase, "-_.")
if len(fileName) > 5 && fileName[len(fileName)-5:] == "_test" {
// Add suffix to avoid the table name which contains "_test",
// which would make the go file a testing file.
fileName += "_table"
}
// dao - index
generateDaoIndex(in, tableNameCamelCase, tableNameCamelLowerCase, importPrefix, dirPathDao, fileName)
// dao - internal
generateDaoInternal(in, tableNameCamelCase, tableNameCamelLowerCase, importPrefix, dirPathDao, fileName, fieldMap)
}
func generateDaoIndex(in CGenDaoInternalInput, tableNameCamelCase, tableNameCamelLowerCase, importPrefix, dirPathDao, fileName string) {
path := gfile.Join(dirPathDao, fileName+".go")
if in.OverwriteDao || !gfile.Exists(path) {
indexContent := gstr.ReplaceByMap(
getTemplateFromPathOrDefault(in.TplDaoIndexPath, consts.TemplateGenDaoIndexContent),
g.MapStrStr{
tplVarImportPrefix: importPrefix,
tplVarTableName: in.TableName,
tplVarTableNameCamelCase: tableNameCamelCase,
tplVarTableNameCamelLowerCase: tableNameCamelLowerCase,
})
indexContent = replaceDefaultVar(in, indexContent)
if err := gfile.PutContents(path, strings.TrimSpace(indexContent)); err != nil {
mlog.Fatalf("writing content to '%s' failed: %v", path, err)
} else {
utils.GoFmt(path)
mlog.Print("generated:", path)
}
}
}
func generateDaoInternal(
in CGenDaoInternalInput,
tableNameCamelCase, tableNameCamelLowerCase, importPrefix string,
dirPathDao, fileName string,
fieldMap map[string]*gdb.TableField,
) {
path := gfile.Join(dirPathDao, "internal", fileName+".go")
modelContent := gstr.ReplaceByMap(
getTemplateFromPathOrDefault(in.TplDaoInternalPath, consts.TemplateGenDaoInternalContent),
g.MapStrStr{
tplVarImportPrefix: importPrefix,
tplVarTableName: in.TableName,
tplVarGroupName: in.Group,
tplVarTableNameCamelCase: tableNameCamelCase,
tplVarTableNameCamelLowerCase: tableNameCamelLowerCase,
tplVarColumnDefine: gstr.Trim(generateColumnDefinitionForDao(fieldMap)),
tplVarColumnNames: gstr.Trim(generateColumnNamesForDao(fieldMap)),
})
modelContent = replaceDefaultVar(in, modelContent)
if err := gfile.PutContents(path, strings.TrimSpace(modelContent)); err != nil {
mlog.Fatalf("writing content to '%s' failed: %v", path, err)
} else {
utils.GoFmt(path)
mlog.Print("generated:", path)
}
}
// generateColumnNamesForDao generates and returns the column names assignment content of column struct
// for specified table.
func generateColumnNamesForDao(fieldMap map[string]*gdb.TableField) string {
var (
buffer = bytes.NewBuffer(nil)
array = make([][]string, len(fieldMap))
names = sortFieldKeyForDao(fieldMap)
)
for index, name := range names {
field := fieldMap[name]
array[index] = []string{
" #" + gstr.CaseCamel(field.Name) + ":",
fmt.Sprintf(` #"%s",`, field.Name),
}
}
tw := tablewriter.NewWriter(buffer)
tw.SetBorder(false)
tw.SetRowLine(false)
tw.SetAutoWrapText(false)
tw.SetColumnSeparator("")
tw.AppendBulk(array)
tw.Render()
namesContent := buffer.String()
// Let's do this hack of table writer for indent!
namesContent = gstr.Replace(namesContent, " #", "")
buffer.Reset()
buffer.WriteString(namesContent)
return buffer.String()
}
// generateColumnDefinitionForDao generates and returns the column names definition for specified table.
func generateColumnDefinitionForDao(fieldMap map[string]*gdb.TableField) string {
var (
buffer = bytes.NewBuffer(nil)
array = make([][]string, len(fieldMap))
names = sortFieldKeyForDao(fieldMap)
)
for index, name := range names {
var (
field = fieldMap[name]
comment = gstr.Trim(gstr.ReplaceByArray(field.Comment, g.SliceStr{
"\n", " ",
"\r", " ",
}))
)
array[index] = []string{
" #" + gstr.CaseCamel(field.Name),
" # " + "string",
" #" + fmt.Sprintf(`// %s`, comment),
}
}
tw := tablewriter.NewWriter(buffer)
tw.SetBorder(false)
tw.SetRowLine(false)
tw.SetAutoWrapText(false)
tw.SetColumnSeparator("")
tw.AppendBulk(array)
tw.Render()
defineContent := buffer.String()
// Let's do this hack of table writer for indent!
defineContent = gstr.Replace(defineContent, " #", "")
buffer.Reset()
buffer.WriteString(defineContent)
return buffer.String()
}

View File

@ -0,0 +1,82 @@
package gendao
import (
"context"
"fmt"
"strings"
"github.com/gogf/gf/cmd/gf/v2/internal/consts"
"github.com/gogf/gf/cmd/gf/v2/internal/utility/mlog"
"github.com/gogf/gf/cmd/gf/v2/internal/utility/utils"
"github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gfile"
"github.com/gogf/gf/v2/text/gregex"
"github.com/gogf/gf/v2/text/gstr"
)
func generateDo(ctx context.Context, db gdb.DB, tableNames, newTableNames []string, in CGenDaoInternalInput) {
var (
doDirPath = gfile.Join(in.Path, in.DoPath)
)
in.NoJsonTag = true
in.DescriptionTag = false
in.NoModelComment = false
// Model content.
for i, tableName := range tableNames {
in.TableName = tableName
fieldMap, err := db.TableFields(ctx, tableName)
if err != nil {
mlog.Fatalf("fetching tables fields failed for table '%s':\n%v", in.TableName, err)
}
var (
newTableName = newTableNames[i]
doFilePath = gfile.Join(doDirPath, gstr.CaseSnake(newTableName)+".go")
structDefinition = generateStructDefinition(ctx, generateStructDefinitionInput{
CGenDaoInternalInput: in,
DB: db,
StructName: gstr.CaseCamel(newTableName),
FieldMap: fieldMap,
IsDo: true,
})
)
// replace all types to interface{}.
structDefinition, _ = gregex.ReplaceStringFuncMatch(
"([A-Z]\\w*?)\\s+([\\w\\*\\.]+?)\\s+(//)",
structDefinition,
func(match []string) string {
// If the type is already a pointer/slice/map, it does nothing.
if !gstr.HasPrefix(match[2], "*") && !gstr.HasPrefix(match[2], "[]") && !gstr.HasPrefix(match[2], "map") {
return fmt.Sprintf(`%s interface{} %s`, match[1], match[3])
}
return match[0]
},
)
modelContent := generateDoContent(
in,
tableName,
gstr.CaseCamel(newTableName),
structDefinition,
)
err = gfile.PutContents(doFilePath, strings.TrimSpace(modelContent))
if err != nil {
mlog.Fatalf(`writing content to "%s" failed: %v`, doFilePath, err)
} else {
utils.GoFmt(doFilePath)
mlog.Print("generated:", doFilePath)
}
}
}
func generateDoContent(in CGenDaoInternalInput, tableName, tableNameCamelCase, structDefine string) string {
doContent := gstr.ReplaceByMap(
getTemplateFromPathOrDefault(in.TplDaoDoPath, consts.TemplateGenDaoDoContent),
g.MapStrStr{
tplVarTableName: tableName,
tplVarPackageImports: getImportPartContent(structDefine, true),
tplVarTableNameCamelCase: tableNameCamelCase,
tplVarStructDefine: structDefine,
})
doContent = replaceDefaultVar(in, doContent)
return doContent
}

View File

@ -0,0 +1,61 @@
package gendao
import (
"context"
"strings"
"github.com/gogf/gf/cmd/gf/v2/internal/consts"
"github.com/gogf/gf/cmd/gf/v2/internal/utility/mlog"
"github.com/gogf/gf/cmd/gf/v2/internal/utility/utils"
"github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gfile"
"github.com/gogf/gf/v2/text/gstr"
)
func generateEntity(ctx context.Context, db gdb.DB, tableNames, newTableNames []string, in CGenDaoInternalInput) {
var entityDirPath = gfile.Join(in.Path, in.EntityPath)
// Model content.
for i, tableName := range tableNames {
fieldMap, err := db.TableFields(ctx, tableName)
if err != nil {
mlog.Fatalf("fetching tables fields failed for table '%s':\n%v", in.TableName, err)
}
var (
newTableName = newTableNames[i]
entityFilePath = gfile.Join(entityDirPath, gstr.CaseSnake(newTableName)+".go")
entityContent = generateEntityContent(
in,
newTableName,
gstr.CaseCamel(newTableName),
generateStructDefinition(ctx, generateStructDefinitionInput{
CGenDaoInternalInput: in,
DB: db,
StructName: gstr.CaseCamel(newTableName),
FieldMap: fieldMap,
IsDo: false,
}),
)
)
err = gfile.PutContents(entityFilePath, strings.TrimSpace(entityContent))
if err != nil {
mlog.Fatalf("writing content to '%s' failed: %v", entityFilePath, err)
} else {
utils.GoFmt(entityFilePath)
mlog.Print("generated:", entityFilePath)
}
}
}
func generateEntityContent(in CGenDaoInternalInput, tableName, tableNameCamelCase, structDefine string) string {
entityContent := gstr.ReplaceByMap(
getTemplateFromPathOrDefault(in.TplDaoEntitylPath, consts.TemplateGenDaoEntityContent),
g.MapStrStr{
tplVarTableName: tableName,
tplVarPackageImports: getImportPartContent(structDefine, false),
tplVarTableNameCamelCase: tableNameCamelCase,
tplVarStructDefine: structDefine,
})
entityContent = replaceDefaultVar(in, entityContent)
return entityContent
}

View File

@ -0,0 +1,153 @@
package gendao
import (
"bytes"
"context"
"fmt"
"github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/text/gregex"
"github.com/gogf/gf/v2/text/gstr"
"github.com/olekukonko/tablewriter"
)
type generateStructDefinitionInput struct {
CGenDaoInternalInput
DB gdb.DB // Current DB.
StructName string // Struct name.
FieldMap map[string]*gdb.TableField // Table field map.
IsDo bool // Is generating DTO struct.
}
func generateStructDefinition(ctx context.Context, in generateStructDefinitionInput) string {
buffer := bytes.NewBuffer(nil)
array := make([][]string, len(in.FieldMap))
names := sortFieldKeyForDao(in.FieldMap)
for index, name := range names {
field := in.FieldMap[name]
array[index] = generateStructFieldDefinition(ctx, field, in)
}
tw := tablewriter.NewWriter(buffer)
tw.SetBorder(false)
tw.SetRowLine(false)
tw.SetAutoWrapText(false)
tw.SetColumnSeparator("")
tw.AppendBulk(array)
tw.Render()
stContent := buffer.String()
// Let's do this hack of table writer for indent!
stContent = gstr.Replace(stContent, " #", "")
stContent = gstr.Replace(stContent, "` ", "`")
stContent = gstr.Replace(stContent, "``", "")
buffer.Reset()
buffer.WriteString(fmt.Sprintf("type %s struct {\n", in.StructName))
if in.IsDo {
buffer.WriteString(fmt.Sprintf("g.Meta `orm:\"table:%s, do:true\"`\n", in.TableName))
}
buffer.WriteString(stContent)
buffer.WriteString("}")
return buffer.String()
}
// generateStructFieldForModel generates and returns the attribute definition for specified field.
func generateStructFieldDefinition(
ctx context.Context, field *gdb.TableField, in generateStructDefinitionInput,
) []string {
var (
err error
typeName string
jsonTag = getJsonTagFromCase(field.Name, in.JsonCase)
)
typeName, err = in.DB.CheckLocalTypeForField(ctx, field.Type, nil)
if err != nil {
panic(err)
}
switch typeName {
case gdb.LocalTypeDate, gdb.LocalTypeDatetime:
if in.StdTime {
typeName = "time.Time"
} else {
typeName = "*gtime.Time"
}
case gdb.LocalTypeInt64Bytes:
typeName = "int64"
case gdb.LocalTypeUint64Bytes:
typeName = "uint64"
// Special type handle.
case gdb.LocalTypeJson, gdb.LocalTypeJsonb:
if in.GJsonSupport {
typeName = "*gjson.Json"
} else {
typeName = "string"
}
}
var (
tagKey = "`"
result = []string{
" #" + gstr.CaseCamel(field.Name),
" #" + typeName,
}
descriptionTag = gstr.Replace(formatComment(field.Comment), `"`, `\"`)
)
result = append(result, " #"+fmt.Sprintf(tagKey+`json:"%s"`, jsonTag))
result = append(result, " #"+fmt.Sprintf(`description:"%s"`+tagKey, descriptionTag))
result = append(result, " #"+fmt.Sprintf(`// %s`, formatComment(field.Comment)))
for k, v := range result {
if in.NoJsonTag {
v, _ = gregex.ReplaceString(`json:".+"`, ``, v)
}
if !in.DescriptionTag {
v, _ = gregex.ReplaceString(`description:".*"`, ``, v)
}
if in.NoModelComment {
v, _ = gregex.ReplaceString(`//.+`, ``, v)
}
result[k] = v
}
return result
}
// formatComment formats the comment string to fit the golang code without any lines.
func formatComment(comment string) string {
comment = gstr.ReplaceByArray(comment, g.SliceStr{
"\n", " ",
"\r", " ",
})
comment = gstr.Replace(comment, `\n`, " ")
comment = gstr.Trim(comment)
return comment
}
// getJsonTagFromCase call gstr.Case* function to convert the s to specified case.
func getJsonTagFromCase(str, caseStr string) string {
switch gstr.ToLower(caseStr) {
case gstr.ToLower("Camel"):
return gstr.CaseCamel(str)
case gstr.ToLower("CamelLower"):
return gstr.CaseCamelLower(str)
case gstr.ToLower("Kebab"):
return gstr.CaseKebab(str)
case gstr.ToLower("KebabScreaming"):
return gstr.CaseKebabScreaming(str)
case gstr.ToLower("Snake"):
return gstr.CaseSnake(str)
case gstr.ToLower("SnakeFirstUpper"):
return gstr.CaseSnakeFirstUpper(str)
case gstr.ToLower("SnakeScreaming"):
return gstr.CaseSnakeScreaming(str)
}
return str
}

View File

@ -1,6 +1,6 @@
package consts
const TemplateDaoDaoIndexContent = `
const TemplateGenDaoIndexContent = `
// =================================================================================
// This is auto-generated by GoFrame CLI tool only once. Fill this file as you wish.
// =================================================================================
@ -31,15 +31,16 @@ var (
`
const TemplateDaoDaoInternalContent = `
const TemplateGenDaoInternalContent = `
// ==========================================================================
// Code generated by GoFrame CLI tool. DO NOT EDIT. {TplDatetimeStr}
// Code generated by GoFrame CLI tool. DO NOT EDIT. {TplCreatedAtDatetimeStr}
// ==========================================================================
package internal
import (
"context"
"github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/frame/g"
)

View File

@ -2,7 +2,7 @@ package consts
const TemplateGenDaoDoContent = `
// =================================================================================
// Code generated by GoFrame CLI tool. DO NOT EDIT. {TplDatetimeStr}
// Code generated by GoFrame CLI tool. DO NOT EDIT. {TplCreatedAtDatetimeStr}
// =================================================================================
package do

View File

@ -2,7 +2,7 @@ package consts
const TemplateGenDaoEntityContent = `
// =================================================================================
// Code generated by GoFrame CLI tool. DO NOT EDIT. {TplDatetimeStr}
// Code generated by GoFrame CLI tool. DO NOT EDIT. {TplCreatedAtDatetimeStr}
// =================================================================================
package entity

View File

@ -1,52 +1,37 @@
package utils
import (
"context"
"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`.
)
func init() {
// Wraps the command binary path with char '"' if there's space char in the path.
if gstr.Contains(gofmtPath, " ") {
gofmtPath = fmt.Sprintf(`"%s"`, gofmtPath)
}
if gstr.Contains(goimportsPath, " ") {
goimportsPath = fmt.Sprintf(`"%s"`, goimportsPath)
}
}
// GoFmt formats the source file using command `gofmt -w -s PATH`.
// GoFmt formats the source file and adds or removes import statements as necessary.
func GoFmt(path string) {
if gofmtPath == "" {
mlog.Fatal(`command "gofmt" not found`)
replaceFunc := func(path, content string) string {
res, err := imports.Process(path, []byte(content), nil)
if err != nil {
mlog.Printf(`error format "%s" go files: %v`, path, err)
return content
}
return string(res)
}
var command = fmt.Sprintf(`%s -w %s`, gofmtPath, path)
result, err := gproc.ShellExec(context.Background(), 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 err error
if gfile.IsFile(path) {
// File format.
if gfile.ExtName(path) != "go" {
return
}
err = gfile.ReplaceFileFunc(replaceFunc, path)
} else {
// Folder format.
err = gfile.ReplaceDirFunc(replaceFunc, path, "*.go", true)
}
var command = fmt.Sprintf(`%s -w %s`, goimportsPath, path)
result, err := gproc.ShellExec(context.Background(), command)
if err != nil {
mlog.Fatalf(`error executing command "%s": %s`, command, result)
mlog.Printf(`error format "%s" go files: %v`, path, err)
}
}

View File

@ -824,6 +824,9 @@ func (a *Array) IsEmpty() bool {
// DeepCopy implements interface for deep copy of current type.
func (a *Array) DeepCopy() interface{} {
if a == nil {
return nil
}
a.mu.RLock()
defer a.mu.RUnlock()
newSlice := make([]interface{}, len(a.array))

View File

@ -802,6 +802,9 @@ func (a *IntArray) IsEmpty() bool {
// DeepCopy implements interface for deep copy of current type.
func (a *IntArray) DeepCopy() interface{} {
if a == nil {
return nil
}
a.mu.RLock()
defer a.mu.RUnlock()
newSlice := make([]int, len(a.array))

View File

@ -815,6 +815,9 @@ func (a *StrArray) IsEmpty() bool {
// DeepCopy implements interface for deep copy of current type.
func (a *StrArray) DeepCopy() interface{} {
if a == nil {
return nil
}
a.mu.RLock()
defer a.mu.RUnlock()
newSlice := make([]string, len(a.array))

View File

@ -800,6 +800,9 @@ func (a *SortedArray) getComparator() func(a, b interface{}) int {
// DeepCopy implements interface for deep copy of current type.
func (a *SortedArray) DeepCopy() interface{} {
if a == nil {
return nil
}
a.mu.RLock()
defer a.mu.RUnlock()
newSlice := make([]interface{}, len(a.array))

View File

@ -747,6 +747,9 @@ func (a *SortedIntArray) getComparator() func(a, b int) int {
// DeepCopy implements interface for deep copy of current type.
func (a *SortedIntArray) DeepCopy() interface{} {
if a == nil {
return nil
}
a.mu.RLock()
defer a.mu.RUnlock()
newSlice := make([]int, len(a.array))

View File

@ -760,6 +760,9 @@ func (a *SortedStrArray) getComparator() func(a, b string) int {
// DeepCopy implements interface for deep copy of current type.
func (a *SortedStrArray) DeepCopy() interface{} {
if a == nil {
return nil
}
a.mu.RLock()
defer a.mu.RUnlock()
newSlice := make([]string, len(a.array))

View File

@ -550,6 +550,10 @@ func (l *List) UnmarshalValue(value interface{}) (err error) {
// DeepCopy implements interface for deep copy of current type.
func (l *List) DeepCopy() interface{} {
if l == nil {
return nil
}
l.mu.RLock()
defer l.mu.RUnlock()

View File

@ -15,6 +15,7 @@ import (
"github.com/gogf/gf/v2/util/gconv"
)
// AnyAnyMap wraps map type `map[interface{}]interface{}` and provides more map features.
type AnyAnyMap struct {
mu rwmutex.RWMutex
data map[interface{}]interface{}
@ -501,6 +502,10 @@ func (m *AnyAnyMap) UnmarshalValue(value interface{}) (err error) {
// DeepCopy implements interface for deep copy of current type.
func (m *AnyAnyMap) DeepCopy() interface{} {
if m == nil {
return nil
}
m.mu.RLock()
defer m.mu.RUnlock()
data := make(map[interface{}]interface{}, len(m.data))

View File

@ -503,6 +503,9 @@ func (m *IntAnyMap) UnmarshalValue(value interface{}) (err error) {
// DeepCopy implements interface for deep copy of current type.
func (m *IntAnyMap) DeepCopy() interface{} {
if m == nil {
return nil
}
m.mu.RLock()
defer m.mu.RUnlock()
data := make(map[int]interface{}, len(m.data))

View File

@ -473,6 +473,9 @@ func (m *IntIntMap) UnmarshalValue(value interface{}) (err error) {
// DeepCopy implements interface for deep copy of current type.
func (m *IntIntMap) DeepCopy() interface{} {
if m == nil {
return nil
}
m.mu.RLock()
defer m.mu.RUnlock()
data := make(map[int]int, len(m.data))

View File

@ -473,6 +473,9 @@ func (m *IntStrMap) UnmarshalValue(value interface{}) (err error) {
// DeepCopy implements interface for deep copy of current type.
func (m *IntStrMap) DeepCopy() interface{} {
if m == nil {
return nil
}
m.mu.RLock()
defer m.mu.RUnlock()
data := make(map[int]string, len(m.data))

View File

@ -489,6 +489,9 @@ func (m *StrAnyMap) UnmarshalValue(value interface{}) (err error) {
// DeepCopy implements interface for deep copy of current type.
func (m *StrAnyMap) DeepCopy() interface{} {
if m == nil {
return nil
}
m.mu.RLock()
defer m.mu.RUnlock()
data := make(map[string]interface{}, len(m.data))

View File

@ -477,6 +477,9 @@ func (m *StrIntMap) UnmarshalValue(value interface{}) (err error) {
// DeepCopy implements interface for deep copy of current type.
func (m *StrIntMap) DeepCopy() interface{} {
if m == nil {
return nil
}
m.mu.RLock()
defer m.mu.RUnlock()
data := make(map[string]int, len(m.data))

View File

@ -466,6 +466,9 @@ func (m *StrStrMap) UnmarshalValue(value interface{}) (err error) {
// DeepCopy implements interface for deep copy of current type.
func (m *StrStrMap) DeepCopy() interface{} {
if m == nil {
return nil
}
m.mu.RLock()
defer m.mu.RUnlock()
data := make(map[string]string, len(m.data))

View File

@ -594,6 +594,9 @@ func (m *ListMap) UnmarshalValue(value interface{}) (err error) {
// DeepCopy implements interface for deep copy of current type.
func (m *ListMap) DeepCopy() interface{} {
if m == nil {
return nil
}
m.mu.RLock()
defer m.mu.RUnlock()
data := make(map[interface{}]interface{}, len(m.data))

View File

@ -85,6 +85,7 @@ func Test_Gpool(t *testing.T) {
assertIndex = 0
p2.Close()
time.Sleep(3 * time.Second)
t.AssertNE(p2.Put(1), nil)
})
gtest.C(t, func(t *gtest.T) {
@ -95,4 +96,27 @@ func Test_Gpool(t *testing.T) {
t.Assert(err3, errors.New("pool is empty"))
t.Assert(v3, nil)
})
gtest.C(t, func(t *gtest.T) {
p := gpool.New(time.Millisecond*200, nil, func(i interface{}) {})
p.Put(1)
time.Sleep(time.Millisecond * 100)
p.Put(2)
time.Sleep(time.Millisecond * 200)
})
gtest.C(t, func(t *gtest.T) {
s := make([]int, 0)
p := gpool.New(time.Millisecond*200, nil, func(i interface{}) {
s = append(s, i.(int))
})
for i := 0; i < 5; i++ {
p.Put(i)
time.Sleep(time.Millisecond * 50)
}
val, err := p.Get()
t.Assert(val, 2)
t.AssertNil(err)
t.Assert(p.Size(), 2)
})
}

View File

@ -62,4 +62,12 @@ func TestQueue_Close(t *testing.T) {
t.Assert(q1.Len(), 2)
q1.Close()
})
gtest.C(t, func(t *gtest.T) {
q1 := gqueue.New(2)
q1.Push(1)
q1.Push(2)
time.Sleep(time.Millisecond)
t.Assert(q1.Len(), 2)
q1.Close()
})
}

View File

@ -183,3 +183,43 @@ func Test_Issue1394(t *testing.T) {
})
}
func TestRing_RLockIteratorNext(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
r := gring.New(10)
for i := 0; i < 10; i++ {
r.Set(i).Next()
}
iterVal := 0
r.RLockIteratorNext(func(value interface{}) bool {
if value.(int) == 0 {
iterVal = value.(int)
return false
}
return true
})
t.Assert(iterVal, 0)
})
}
func TestRing_RLockIteratorPrev(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
r := gring.New(10)
for i := 0; i < 10; i++ {
r.Set(i).Next()
}
iterVal := 0
r.RLockIteratorPrev(func(value interface{}) bool {
if value.(int) == 0 {
iterVal = value.(int)
return false
}
return true
})
t.Assert(iterVal, 0)
})
}

View File

@ -513,11 +513,14 @@ func (set *Set) UnmarshalValue(value interface{}) (err error) {
// DeepCopy implements interface for deep copy of current type.
func (set *Set) DeepCopy() interface{} {
if set == nil {
return nil
}
set.mu.RLock()
defer set.mu.RUnlock()
data := make(map[interface{}]struct{}, len(set.data))
for k, v := range set.data {
data[k] = v
data := make([]interface{}, 0)
for k, _ := range set.data {
data = append(data, k)
}
return NewFrom(data, set.mu.IsSafe())
}

View File

@ -472,6 +472,9 @@ func (set *IntSet) UnmarshalValue(value interface{}) (err error) {
// DeepCopy implements interface for deep copy of current type.
func (set *IntSet) DeepCopy() interface{} {
if set == nil {
return nil
}
set.mu.RLock()
defer set.mu.RUnlock()
var (

View File

@ -502,6 +502,9 @@ func (set *StrSet) UnmarshalValue(value interface{}) (err error) {
// DeepCopy implements interface for deep copy of current type.
func (set *StrSet) DeepCopy() interface{} {
if set == nil {
return nil
}
set.mu.RLock()
defer set.mu.RUnlock()
var (

View File

@ -126,11 +126,16 @@ func TestSet_Equal(t *testing.T) {
s1 := gset.NewSet()
s2 := gset.NewSet()
s3 := gset.NewSet()
s4 := gset.NewSet()
s1.Add(1, 2, 3)
s2.Add(1, 2, 3)
s3.Add(1, 2, 3, 4)
s4.Add(4, 5, 6)
t.Assert(s1.Equal(s2), true)
t.Assert(s1.Equal(s3), false)
t.Assert(s1.Equal(s4), false)
s5 := s1
t.Assert(s1.Equal(s5), true)
})
}
@ -147,6 +152,9 @@ func TestSet_IsSubsetOf(t *testing.T) {
t.Assert(s1.IsSubsetOf(s3), true)
t.Assert(s2.IsSubsetOf(s1), false)
t.Assert(s3.IsSubsetOf(s2), false)
s4 := s1
t.Assert(s1.IsSubsetOf(s4), true)
})
}
@ -175,6 +183,13 @@ func TestSet_Diff(t *testing.T) {
t.Assert(s3.Contains(2), true)
t.Assert(s3.Contains(3), false)
t.Assert(s3.Contains(4), false)
s4 := s1
s5 := s1.Diff(s2, s4)
t.Assert(s5.Contains(1), true)
t.Assert(s5.Contains(2), true)
t.Assert(s5.Contains(3), false)
t.Assert(s5.Contains(4), false)
})
}
@ -247,6 +262,10 @@ func TestSet_Join(t *testing.T) {
t.Assert(strings.Contains(str1, `\c`), true)
t.Assert(strings.Contains(str1, `a`), true)
})
gtest.C(t, func(t *gtest.T) {
s1 := gset.Set{}
t.Assert(s1.Join(","), "")
})
}
func TestSet_String(t *testing.T) {
@ -257,6 +276,13 @@ func TestSet_String(t *testing.T) {
t.Assert(strings.Contains(str1, "["), true)
t.Assert(strings.Contains(str1, "]"), true)
t.Assert(strings.Contains(str1, "a2"), true)
s1 = nil
t.Assert(s1.String(), "")
s2 := gset.New()
s2.Add(1)
t.Assert(s2.String(), "[1]")
})
}
@ -285,6 +311,7 @@ func TestSet_Sum(t *testing.T) {
func TestSet_Pop(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s := gset.New(true)
t.Assert(s.Pop(), nil)
s.Add(1, 2, 3, 4)
t.Assert(s.Size(), 4)
t.AssertIN(s.Pop(), []int{1, 2, 3, 4})
@ -354,6 +381,7 @@ func TestSet_AddIfNotExist(t *testing.T) {
t.Assert(s.AddIfNotExist(2), true)
t.Assert(s.Contains(2), true)
t.Assert(s.AddIfNotExist(2), false)
t.Assert(s.AddIfNotExist(nil), false)
t.Assert(s.Contains(2), true)
})
}
@ -370,6 +398,7 @@ func TestSet_AddIfNotExistFunc(t *testing.T) {
t.Assert(s.Contains(2), true)
t.Assert(s.AddIfNotExistFunc(2, func() bool { return true }), false)
t.Assert(s.Contains(2), true)
t.Assert(s.AddIfNotExistFunc(nil, func() bool { return false }), false)
})
gtest.C(t, func(t *gtest.T) {
s := gset.New(true)
@ -386,6 +415,10 @@ func TestSet_AddIfNotExistFunc(t *testing.T) {
s.Add(1)
wg.Wait()
})
gtest.C(t, func(t *gtest.T) {
s := gset.Set{}
t.Assert(s.AddIfNotExistFunc(1, func() bool { return true }), true)
})
}
func TestSet_Walk(t *testing.T) {
@ -424,6 +457,12 @@ func TestSet_AddIfNotExistFuncLock(t *testing.T) {
}()
wg.Wait()
})
gtest.C(t, func(t *gtest.T) {
s := gset.New(true)
t.Assert(s.AddIfNotExistFuncLock(nil, func() bool { return true }), false)
s1 := gset.Set{}
t.Assert(s1.AddIfNotExistFuncLock(1, func() bool { return true }), true)
})
}
func TestSet_UnmarshalValue(t *testing.T) {
@ -462,3 +501,18 @@ func TestSet_UnmarshalValue(t *testing.T) {
t.Assert(v.Set.Contains("k4"), false)
})
}
func TestSet_DeepCopy(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
set := gset.New()
set.Add(1, 2, 3)
copySet := set.DeepCopy().(*gset.Set)
copySet.Add(4)
t.AssertNE(set.Size(), copySet.Size())
t.AssertNE(set.String(), copySet.String())
set = nil
t.AssertNil(set.DeepCopy())
})
}

View File

@ -106,11 +106,16 @@ func TestIntSet_Equal(t *testing.T) {
s1 := gset.NewIntSet()
s2 := gset.NewIntSet()
s3 := gset.NewIntSet()
s4 := gset.NewIntSet()
s1.Add(1, 2, 3)
s2.Add(1, 2, 3)
s3.Add(1, 2, 3, 4)
s4.Add(4, 5, 6)
t.Assert(s1.Equal(s2), true)
t.Assert(s1.Equal(s3), false)
t.Assert(s1.Equal(s4), false)
s5 := s1
t.Assert(s1.Equal(s5), true)
})
}
@ -127,6 +132,9 @@ func TestIntSet_IsSubsetOf(t *testing.T) {
t.Assert(s1.IsSubsetOf(s3), true)
t.Assert(s2.IsSubsetOf(s1), false)
t.Assert(s3.IsSubsetOf(s2), false)
s4 := s1
t.Assert(s1.IsSubsetOf(s4), true)
})
}
@ -155,6 +163,13 @@ func TestIntSet_Diff(t *testing.T) {
t.Assert(s3.Contains(2), true)
t.Assert(s3.Contains(3), false)
t.Assert(s3.Contains(4), false)
s4 := s1
s5 := s1.Diff(s2, s4)
t.Assert(s5.Contains(1), true)
t.Assert(s5.Contains(2), true)
t.Assert(s5.Contains(3), false)
t.Assert(s5.Contains(4), false)
})
}
@ -212,6 +227,7 @@ func TestIntSet_Merge(t *testing.T) {
func TestIntSet_Join(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s1 := gset.NewIntSet()
t.Assert(s1.Join(","), "")
s1.Add(1, 2, 3)
s3 := s1.Join(",")
t.Assert(strings.Contains(s3, "1"), true)
@ -230,6 +246,8 @@ func TestIntSet_String(t *testing.T) {
t.Assert(strings.Contains(s3, "1"), true)
t.Assert(strings.Contains(s3, "2"), true)
t.Assert(strings.Contains(s3, "3"), true)
s1 = nil
t.Assert(s1.String(), "")
})
}
@ -248,6 +266,7 @@ func TestIntSet_Sum(t *testing.T) {
func TestIntSet_Pop(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s := gset.NewIntSet()
t.Assert(s.Pop(), 0)
s.Add(4, 2, 3)
t.Assert(s.Size(), 3)
t.AssertIN(s.Pop(), []int{4, 2, 3})
@ -291,6 +310,10 @@ func TestIntSet_AddIfNotExist(t *testing.T) {
t.Assert(s.AddIfNotExist(2), false)
t.Assert(s.Contains(2), true)
})
gtest.C(t, func(t *gtest.T) {
s := gset.IntSet{}
t.Assert(s.AddIfNotExist(1), true)
})
}
func TestIntSet_AddIfNotExistFunc(t *testing.T) {
@ -321,6 +344,10 @@ func TestIntSet_AddIfNotExistFunc(t *testing.T) {
s.Add(1)
wg.Wait()
})
gtest.C(t, func(t *gtest.T) {
s := gset.IntSet{}
t.Assert(s.AddIfNotExistFunc(1, func() bool { return true }), true)
})
}
func TestIntSet_AddIfNotExistFuncLock(t *testing.T) {
@ -346,6 +373,10 @@ func TestIntSet_AddIfNotExistFuncLock(t *testing.T) {
}()
wg.Wait()
})
gtest.C(t, func(t *gtest.T) {
s := gset.IntSet{}
t.Assert(s.AddIfNotExistFuncLock(1, func() bool { return true }), true)
})
}
func TestIntSet_Json(t *testing.T) {
@ -426,3 +457,18 @@ func TestIntSet_UnmarshalValue(t *testing.T) {
t.Assert(v.Set.Contains(4), false)
})
}
func TestIntSet_DeepCopy(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
set := gset.NewIntSet()
set.Add(1, 2, 3)
copySet := set.DeepCopy().(*gset.IntSet)
copySet.Add(4)
t.AssertNE(set.Size(), copySet.Size())
t.AssertNE(set.String(), copySet.String())
set = nil
t.AssertNil(set.DeepCopy())
})
}

View File

@ -69,6 +69,7 @@ func TestStrSet_ContainsI(t *testing.T) {
t.Assert(s.Contains("A"), false)
t.Assert(s.Contains("a"), true)
t.Assert(s.ContainsI("A"), true)
t.Assert(s.ContainsI("d"), false)
})
}
@ -116,11 +117,16 @@ func TestStrSet_Equal(t *testing.T) {
s1 := gset.NewStrSet()
s2 := gset.NewStrSet()
s3 := gset.NewStrSet()
s4 := gset.NewStrSet()
s1.Add("1", "2", "3")
s2.Add("1", "2", "3")
s3.Add("1", "2", "3", "4")
s4.Add("4", "5", "6")
t.Assert(s1.Equal(s2), true)
t.Assert(s1.Equal(s3), false)
t.Assert(s1.Equal(s4), false)
s5 := s1
t.Assert(s1.Equal(s5), true)
})
}
@ -137,6 +143,9 @@ func TestStrSet_IsSubsetOf(t *testing.T) {
t.Assert(s1.IsSubsetOf(s3), true)
t.Assert(s2.IsSubsetOf(s1), false)
t.Assert(s3.IsSubsetOf(s2), false)
s4 := s1
t.Assert(s1.IsSubsetOf(s4), true)
})
}
@ -165,6 +174,13 @@ func TestStrSet_Diff(t *testing.T) {
t.Assert(s3.Contains("2"), true)
t.Assert(s3.Contains("3"), false)
t.Assert(s3.Contains("4"), false)
s4 := s1
s5 := s1.Diff(s2, s4)
t.Assert(s5.Contains("1"), true)
t.Assert(s5.Contains("2"), true)
t.Assert(s5.Contains("3"), false)
t.Assert(s5.Contains("4"), false)
})
}
@ -239,6 +255,7 @@ func TestStrSet_Join(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
s1 := gset.NewStrSet()
t.Assert(s1.Join(","), "")
s1.Add("a", `"b"`, `\c`)
str1 := s1.Join(",")
t.Assert(strings.Contains(str1, `"b"`), true)
@ -253,6 +270,8 @@ func TestStrSet_String(t *testing.T) {
str1 := s1.String()
t.Assert(strings.Contains(str1, "b"), true)
t.Assert(strings.Contains(str1, "d"), false)
s1 = nil
t.Assert(s1.String(), "")
})
gtest.C(t, func(t *gtest.T) {
@ -300,6 +319,9 @@ func TestStrSet_Pop(t *testing.T) {
t.Assert(s.Size(), 3)
t.AssertIN(s.Pop(), a)
t.Assert(s.Size(), 2)
s1 := gset.StrSet{}
t.Assert(s1.Pop(), "")
})
}
@ -337,6 +359,10 @@ func TestStrSet_AddIfNotExist(t *testing.T) {
t.Assert(s.AddIfNotExist("2"), false)
t.Assert(s.Contains("2"), true)
})
gtest.C(t, func(t *gtest.T) {
s := gset.StrSet{}
t.Assert(s.AddIfNotExist("1"), true)
})
}
func TestStrSet_AddIfNotExistFunc(t *testing.T) {
@ -367,6 +393,10 @@ func TestStrSet_AddIfNotExistFunc(t *testing.T) {
s.Add("1")
wg.Wait()
})
gtest.C(t, func(t *gtest.T) {
s := gset.StrSet{}
t.Assert(s.AddIfNotExistFunc("1", func() bool { return true }), true)
})
}
func TestStrSet_AddIfNotExistFuncLock(t *testing.T) {
@ -392,6 +422,10 @@ func TestStrSet_AddIfNotExistFuncLock(t *testing.T) {
}()
wg.Wait()
})
gtest.C(t, func(t *gtest.T) {
s := gset.StrSet{}
t.Assert(s.AddIfNotExistFuncLock("1", func() bool { return true }), true)
})
}
func TestStrSet_Json(t *testing.T) {
@ -477,3 +511,18 @@ func TestStrSet_UnmarshalValue(t *testing.T) {
t.Assert(v.Set.Contains("4"), false)
})
}
func TestStrSet_DeepCopy(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
set := gset.NewStrSet()
set.Add("1", "2", "3")
copySet := set.DeepCopy().(*gset.StrSet)
copySet.Add("4")
t.AssertNE(set.Size(), copySet.Size())
t.AssertNE(set.String(), copySet.String())
set = nil
t.AssertNil(set.DeepCopy())
})
}

View File

@ -99,5 +99,8 @@ func (v *Bool) UnmarshalValue(value interface{}) error {
// DeepCopy implements interface for deep copy of current type.
func (v *Bool) DeepCopy() interface{} {
if v == nil {
return nil
}
return NewBool(v.Val())
}

View File

@ -78,5 +78,8 @@ func (v *Byte) UnmarshalValue(value interface{}) error {
// DeepCopy implements interface for deep copy of current type.
func (v *Byte) DeepCopy() interface{} {
if v == nil {
return nil
}
return NewByte(v.Val())
}

View File

@ -86,6 +86,9 @@ func (v *Bytes) UnmarshalValue(value interface{}) error {
// DeepCopy implements interface for deep copy of current type.
func (v *Bytes) DeepCopy() interface{} {
if v == nil {
return nil
}
oldBytes := v.Val()
newBytes := make([]byte, len(oldBytes))
copy(newBytes, oldBytes)

View File

@ -90,5 +90,8 @@ func (v *Float32) UnmarshalValue(value interface{}) error {
// DeepCopy implements interface for deep copy of current type.
func (v *Float32) DeepCopy() interface{} {
if v == nil {
return nil
}
return NewFloat32(v.Val())
}

View File

@ -90,5 +90,8 @@ func (v *Float64) UnmarshalValue(value interface{}) error {
// DeepCopy implements interface for deep copy of current type.
func (v *Float64) DeepCopy() interface{} {
if v == nil {
return nil
}
return NewFloat64(v.Val())
}

View File

@ -78,5 +78,8 @@ func (v *Int) UnmarshalValue(value interface{}) error {
// DeepCopy implements interface for deep copy of current type.
func (v *Int) DeepCopy() interface{} {
if v == nil {
return nil
}
return NewInt(v.Val())
}

View File

@ -78,5 +78,8 @@ func (v *Int32) UnmarshalValue(value interface{}) error {
// DeepCopy implements interface for deep copy of current type.
func (v *Int32) DeepCopy() interface{} {
if v == nil {
return nil
}
return NewInt32(v.Val())
}

View File

@ -78,5 +78,8 @@ func (v *Int64) UnmarshalValue(value interface{}) error {
// DeepCopy implements interface for deep copy of current type.
func (v *Int64) DeepCopy() interface{} {
if v == nil {
return nil
}
return NewInt64(v.Val())
}

View File

@ -75,5 +75,8 @@ func (v *Interface) UnmarshalValue(value interface{}) error {
// DeepCopy implements interface for deep copy of current type.
func (v *Interface) DeepCopy() interface{} {
if v == nil {
return nil
}
return NewInterface(deepcopy.Copy(v.Val()))
}

View File

@ -8,8 +8,9 @@ package gtype
import (
"bytes"
"github.com/gogf/gf/v2/util/gconv"
"sync/atomic"
"github.com/gogf/gf/v2/util/gconv"
)
// String is a struct for concurrent-safe operation for type string.
@ -69,3 +70,11 @@ func (v *String) UnmarshalValue(value interface{}) error {
v.Set(gconv.String(value))
return nil
}
// DeepCopy implements interface for deep copy of current type.
func (v *String) DeepCopy() interface{} {
if v == nil {
return nil
}
return NewString(v.Val())
}

View File

@ -78,5 +78,8 @@ func (v *Uint) UnmarshalValue(value interface{}) error {
// DeepCopy implements interface for deep copy of current type.
func (v *Uint) DeepCopy() interface{} {
if v == nil {
return nil
}
return NewUint(v.Val())
}

View File

@ -78,5 +78,8 @@ func (v *Uint32) UnmarshalValue(value interface{}) error {
// DeepCopy implements interface for deep copy of current type.
func (v *Uint32) DeepCopy() interface{} {
if v == nil {
return nil
}
return NewUint32(v.Val())
}

View File

@ -78,5 +78,8 @@ func (v *Uint64) UnmarshalValue(value interface{}) error {
// DeepCopy implements interface for deep copy of current type.
func (v *Uint64) DeepCopy() interface{} {
if v == nil {
return nil
}
return NewUint64(v.Val())
}

View File

@ -27,7 +27,19 @@ func Test_Bool(t *testing.T) {
t.AssertEQ(iClone1.Set(true), false)
t.AssertEQ(iClone1.Val(), true)
// 空参测试
t.AssertEQ(iClone1.Cas(false, true), false)
t.AssertEQ(iClone1.String(), "true")
t.AssertEQ(iClone1.Cas(true, false), true)
t.AssertEQ(iClone1.String(), "false")
copyVal := i1.DeepCopy()
iClone.Set(true)
t.AssertNE(copyVal, iClone.Val())
iClone = nil
copyVal = iClone.DeepCopy()
t.AssertNil(copyVal)
// empty param test
i2 := gtype.NewBool()
t.AssertEQ(i2.Val(), false)
})

View File

@ -34,9 +34,21 @@ func Test_Byte(t *testing.T) {
wg.Wait()
t.AssertEQ(byte(addTimes), i.Val())
// 空参测试
// empty param test
i1 := gtype.NewByte()
t.AssertEQ(i1.Val(), byte(0))
i2 := gtype.NewByte(byte(64))
t.AssertEQ(i2.String(), "64")
t.AssertEQ(i2.Cas(byte(63), byte(65)), false)
t.AssertEQ(i2.Cas(byte(64), byte(65)), true)
copyVal := i2.DeepCopy()
i2.Set(byte(65))
t.AssertNE(copyVal, iClone.Val())
i2 = nil
copyVal = i2.DeepCopy()
t.AssertNil(copyVal)
})
}

View File

@ -22,9 +22,19 @@ func Test_Bytes(t *testing.T) {
t.AssertEQ(iClone.Set([]byte("123")), []byte("abc"))
t.AssertEQ(iClone.Val(), []byte("123"))
// 空参测试
// empty param test
i1 := gtype.NewBytes()
t.AssertEQ(i1.Val(), nil)
i2 := gtype.NewBytes([]byte("abc"))
t.Assert(i2.String(), "abc")
copyVal := i2.DeepCopy()
i2.Set([]byte("def"))
t.AssertNE(copyVal, iClone.Val())
i2 = nil
copyVal = i2.DeepCopy()
t.AssertNil(copyVal)
})
}

View File

@ -23,9 +23,22 @@ func Test_Float32(t *testing.T) {
t.AssertEQ(iClone.Set(0.1), float32(0))
t.AssertEQ(iClone.Val(), float32(0.1))
// 空参测试
// empty param test
i1 := gtype.NewFloat32()
t.AssertEQ(i1.Val(), float32(0))
i2 := gtype.NewFloat32(1.23)
t.AssertEQ(i2.Add(3.21), float32(4.44))
t.AssertEQ(i2.Cas(4.45, 5.55), false)
t.AssertEQ(i2.Cas(4.44, 5.55), true)
t.AssertEQ(i2.String(), "5.55")
copyVal := i2.DeepCopy()
i2.Set(float32(6.66))
t.AssertNE(copyVal, iClone.Val())
i2 = nil
copyVal = i2.DeepCopy()
t.AssertNil(copyVal)
})
}

View File

@ -22,9 +22,22 @@ func Test_Float64(t *testing.T) {
iClone := i.Clone()
t.AssertEQ(iClone.Set(0.1), float64(0))
t.AssertEQ(iClone.Val(), float64(0.1))
// 空参测试
// empty param test
i1 := gtype.NewFloat64()
t.AssertEQ(i1.Val(), float64(0))
i2 := gtype.NewFloat64(1.1)
t.AssertEQ(i2.Add(3.3), 4.4)
t.AssertEQ(i2.Cas(4.5, 5.5), false)
t.AssertEQ(i2.Cas(4.4, 5.5), true)
t.AssertEQ(i2.String(), "5.5")
copyVal := i2.DeepCopy()
i2.Set(6.6)
t.AssertNE(copyVal, iClone.Val())
i2 = nil
copyVal = i2.DeepCopy()
t.AssertNil(copyVal)
})
}

View File

@ -35,9 +35,22 @@ func Test_Int32(t *testing.T) {
wg.Wait()
t.AssertEQ(int32(addTimes), i.Val())
// 空参测试
// empty param test
i1 := gtype.NewInt32()
t.AssertEQ(i1.Val(), int32(0))
i2 := gtype.NewInt32(11)
t.AssertEQ(i2.Add(1), int32(12))
t.AssertEQ(i2.Cas(11, 13), false)
t.AssertEQ(i2.Cas(12, 13), true)
t.AssertEQ(i2.String(), "13")
copyVal := i2.DeepCopy()
i2.Set(14)
t.AssertNE(copyVal, iClone.Val())
i2 = nil
copyVal = i2.DeepCopy()
t.AssertNil(copyVal)
})
}

View File

@ -35,9 +35,22 @@ func Test_Int64(t *testing.T) {
wg.Wait()
t.AssertEQ(int64(addTimes), i.Val())
// 空参测试
// empty param test
i1 := gtype.NewInt64()
t.AssertEQ(i1.Val(), int64(0))
i2 := gtype.NewInt64(11)
t.AssertEQ(i2.Add(1), int64(12))
t.AssertEQ(i2.Cas(11, 13), false)
t.AssertEQ(i2.Cas(12, 13), true)
t.AssertEQ(i2.String(), "13")
copyVal := i2.DeepCopy()
i2.Set(14)
t.AssertNE(copyVal, iClone.Val())
i2 = nil
copyVal = i2.DeepCopy()
t.AssertNil(copyVal)
})
}

View File

@ -34,9 +34,22 @@ func Test_Int(t *testing.T) {
wg.Wait()
t.AssertEQ(addTimes, i.Val())
// 空参测试
// empty param test
i1 := gtype.NewInt()
t.AssertEQ(i1.Val(), 0)
i2 := gtype.NewInt(11)
t.AssertEQ(i2.Add(1), 12)
t.AssertEQ(i2.Cas(11, 13), false)
t.AssertEQ(i2.Cas(12, 13), true)
t.AssertEQ(i2.String(), "13")
copyVal := i2.DeepCopy()
i2.Set(14)
t.AssertNE(copyVal, iClone.Val())
i2 = nil
copyVal = i2.DeepCopy()
t.AssertNil(copyVal)
})
}

View File

@ -24,9 +24,18 @@ func Test_Interface(t *testing.T) {
t.AssertEQ(iClone.Set(t2), t1)
t.AssertEQ(iClone.Val().(Temp), t2)
// 空参测试
// empty param test
i1 := gtype.New()
t.AssertEQ(i1.Val(), nil)
i2 := gtype.New("gf")
t.AssertEQ(i2.String(), "gf")
copyVal := i2.DeepCopy()
i2.Set("goframe")
t.AssertNE(copyVal, iClone.Val())
i2 = nil
copyVal = i2.DeepCopy()
t.AssertNil(copyVal)
})
}

View File

@ -21,8 +21,15 @@ func Test_String(t *testing.T) {
iClone := i.Clone()
t.AssertEQ(iClone.Set("123"), "abc")
t.AssertEQ(iClone.Val(), "123")
// 空参测试
t.AssertEQ(iClone.String(), "123")
//
copyVal := iClone.DeepCopy()
iClone.Set("124")
t.AssertNE(copyVal, iClone.Val())
iClone = nil
copyVal = iClone.DeepCopy()
t.AssertNil(copyVal)
// empty param test
i1 := gtype.NewString()
t.AssertEQ(i1.Val(), "")
})

View File

@ -35,9 +35,22 @@ func Test_Uint32(t *testing.T) {
wg.Wait()
t.AssertEQ(uint32(addTimes), i.Val())
// 空参测试
// empty param test
i1 := gtype.NewUint32()
t.AssertEQ(i1.Val(), uint32(0))
i2 := gtype.NewUint32(11)
t.AssertEQ(i2.Add(1), uint32(12))
t.AssertEQ(i2.Cas(11, 13), false)
t.AssertEQ(i2.Cas(12, 13), true)
t.AssertEQ(i2.String(), "13")
copyVal := i2.DeepCopy()
i2.Set(14)
t.AssertNE(copyVal, iClone.Val())
i2 = nil
copyVal = i2.DeepCopy()
t.AssertNil(copyVal)
})
}

View File

@ -40,9 +40,22 @@ func Test_Uint64(t *testing.T) {
wg.Wait()
t.AssertEQ(uint64(addTimes), i.Val())
// 空参测试
// empty param test
i1 := gtype.NewUint64()
t.AssertEQ(i1.Val(), uint64(0))
i2 := gtype.NewUint64(11)
t.AssertEQ(i2.Add(1), uint64(12))
t.AssertEQ(i2.Cas(11, 13), false)
t.AssertEQ(i2.Cas(12, 13), true)
t.AssertEQ(i2.String(), "13")
copyVal := i2.DeepCopy()
i2.Set(14)
t.AssertNE(copyVal, iClone.Val())
i2 = nil
copyVal = i2.DeepCopy()
t.AssertNil(copyVal)
})
}
func Test_Uint64_JSON(t *testing.T) {

View File

@ -34,9 +34,22 @@ func Test_Uint(t *testing.T) {
wg.Wait()
t.AssertEQ(uint(addTimes), i.Val())
// 空参测试
// empty param test
i1 := gtype.NewUint()
t.AssertEQ(i1.Val(), uint(0))
i2 := gtype.NewUint(11)
t.AssertEQ(i2.Add(1), uint(12))
t.AssertEQ(i2.Cas(11, 13), false)
t.AssertEQ(i2.Cas(12, 13), true)
t.AssertEQ(i2.String(), "13")
copyVal := i2.DeepCopy()
i2.Set(14)
t.AssertNE(copyVal, iClone.Val())
i2 = nil
copyVal = i2.DeepCopy()
t.AssertNil(copyVal)
})
}

View File

@ -198,5 +198,8 @@ func (v *Var) UnmarshalValue(value interface{}) error {
// DeepCopy implements interface for deep copy of current type.
func (v *Var) DeepCopy() interface{} {
if v == nil {
return nil
}
return New(deepcopy.Copy(v.Val()), v.safe)
}

View File

@ -12,7 +12,6 @@ import (
"github.com/gogf/gf/v2/container/gvar"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/internal/json"
"github.com/gogf/gf/v2/util/gconv"
)
// New
@ -647,14 +646,18 @@ func ExampleVar_MapStrVarDeep() {
var (
m1 = g.Map{"id": 1, "price": 100}
m2 = g.Map{"product": m1}
m3 = g.Map{}
v = gvar.New(m2)
v2 = v.MapStrVarDeep()
v3 = gvar.New(m3).MapStrVarDeep()
)
fmt.Println(v2["product"])
fmt.Println(v3)
// Output:
// {"id":1,"price":100}
// map[]
}
// Maps
@ -718,21 +721,23 @@ func ExampleVar_MapToMaps() {
// []map[string]string{map[string]string{"product":"{\"id\":1,\"price\":100}"}, map[string]string{"product":"{\"id\":2,\"price\":200}"}}
}
// MapToMapDeep
func ExampleVar_MapToMapDeep() {
// MapToMapsDeep
func ExampleVar_MapToMapsDeep() {
var (
p1 = gvar.New(g.MapStrAny{"product": g.Map{"id": 1, "price": 100}})
p2 = g.MapStrAny{}
p1 = g.MapStrAny{"product": g.Map{"id": 1, "price": 100}}
p2 = g.MapStrAny{"product": g.Map{"id": 2, "price": 200}}
v = gvar.New(g.ListStrAny{p1, p2})
v2 []g.MapStrStr
)
err := p1.MapToMap(&p2)
err := v.MapToMapsDeep(&v2)
if err != nil {
panic(err)
}
fmt.Printf("%#v", p2)
fmt.Printf("%#v", v2)
// Output:
// map[string]interface {}{"product":map[string]interface {}{"id":1, "price":100}}
// []map[string]string{map[string]string{"product":"{\"id\":1,\"price\":100}"}, map[string]string{"product":"{\"id\":2,\"price\":200}"}}
}
// Scan
@ -750,12 +755,11 @@ func ExampleVar_Scan() {
"Scores": []int{100, 99, 98},
}
)
if err := gconv.Scan(m, &s); err != nil {
panic(err)
v := gvar.New(m)
if err := v.Scan(&s); err == nil {
g.DumpWithType(s)
}
g.DumpWithType(s)
// Output:
// gvar_test.Student(3) {
// Id: *gvar.Var(1) "1",

View File

@ -50,6 +50,9 @@ func Test_Val(t *testing.T) {
objTwo := gvar.New(1, false)
objTwoOld, _ := objTwo.Val().(int)
t.Assert(objTwoOld, 1)
objOne = nil
t.Assert(objOne.Val(), nil)
})
}
func Test_Interface(t *testing.T) {
@ -328,3 +331,22 @@ func Test_Copy(t *testing.T) {
})
})
}
func Test_DeepCopy(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
src := g.Map{
"k1": "v1",
"k2": "v2",
}
srcVar := gvar.New(src)
copyVar := srcVar.DeepCopy().(*gvar.Var)
copyVar.Set(g.Map{
"k3": "v3",
"k4": "v4",
})
t.AssertNE(srcVar, copyVar)
srcVar = nil
t.AssertNil(srcVar.DeepCopy())
})
}

View File

@ -80,3 +80,17 @@ func TestVar_MapToMap(t *testing.T) {
t.Assert(m2["k2"], m1["k2"])
})
}
func TestVar_MapStrVar(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
m := g.Map{
"k1": "v1",
"k2": "v2",
}
objOne := gvar.New(m, true)
t.Assert(objOne.MapStrVar(), "{\"k1\":\"v1\",\"k2\":\"v2\"}")
objEmpty := gvar.New(g.Map{})
t.Assert(objEmpty.MapStrVar(), "")
})
}

View File

@ -108,5 +108,8 @@ func TestVar_Vars(t *testing.T) {
t.Assert(len(objOne.Vars()), 5)
t.Assert(objOne.Vars()[0].Int(), 1)
t.Assert(objOne.Vars()[4].Int(), 5)
objEmpty := gvar.New([]int{})
t.Assert(objEmpty.Vars(), nil)
})
}

View File

@ -13,6 +13,10 @@ import (
"database/sql/driver"
"errors"
"fmt"
"net/url"
"strings"
"time"
"github.com/ClickHouse/clickhouse-go/v2"
"github.com/gogf/gf/v2/container/gmap"
"github.com/gogf/gf/v2/database/gdb"
@ -24,9 +28,6 @@ import (
"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.
@ -148,7 +149,13 @@ func (d *Driver) TableFields(
if link, err = d.SlaveLink(useSchema); err != nil {
return nil
}
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)
var (
columns = "name,position,default_expression,comment,type,is_in_partition_key,is_in_sorting_key,is_in_primary_key,is_in_sampling_key"
getColumnsSql = fmt.Sprintf(
"select %s from `system`.columns c where `table` = '%s'",
columns, table,
)
)
result, err = d.DoSelect(ctx, link, getColumnsSql)
if err != nil {
return nil

View File

@ -127,6 +127,16 @@ func clickhouseConfigDB() gdb.DB {
return connect
}
func clickhouseLink() gdb.DB {
connect, err := gdb.New(gdb.ConfigNode{
Link: "clickhouse://default@127.0.0.1:9000,127.0.0.1:9000/default?dial_timeout=200ms&max_execution_time=60",
Type: "clickhouse",
})
gtest.AssertNil(err)
gtest.AssertNE(connect, nil)
return connect
}
func createClickhouseTableVisits(connect gdb.DB) error {
_, err := connect.Exec(context.Background(), sqlVisitsDDL)
return err
@ -204,7 +214,7 @@ func TestDriverClickhouse_TableFields_Use_Config(t *testing.T) {
}
func TestDriverClickhouse_TableFields_Use_Link(t *testing.T) {
connect := clickhouseConfigDB()
connect := clickhouseLink()
gtest.AssertNil(createClickhouseTableVisits(connect))
defer dropClickhouseTableVisits(connect)
field, err := connect.TableFields(context.Background(), "visits")

View File

@ -1190,24 +1190,26 @@ func Test_DB_TableField(t *testing.T) {
"field_varchar": "abc",
"field_varbinary": "aaa",
}
res, err := db.Model(name).Data(data).Insert()
if err != nil {
gtest.Fatal(err)
}
gtest.C(t, func(t *gtest.T) {
res, err := db.Model(name).Data(data).Insert()
if err != nil {
t.Fatal(err)
}
n, err := res.RowsAffected()
if err != nil {
gtest.Fatal(err)
} else {
gtest.Assert(n, 1)
}
n, err := res.RowsAffected()
if err != nil {
t.Fatal(err)
} else {
t.Assert(n, 1)
}
result, err := db.Model(name).Fields("*").Where("field_int = ?", 2).All()
if err != nil {
gtest.Fatal(err)
}
result, err := db.Model(name).Fields("*").Where("field_int = ?", 2).All()
if err != nil {
t.Fatal(err)
}
t.Assert(result[0], data)
})
gtest.Assert(result[0], data)
}
func Test_DB_Prefix(t *testing.T) {

View File

@ -11,6 +11,7 @@ import (
"testing"
"github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/os/glog"
"github.com/gogf/gf/v2/test/gtest"
)
@ -30,7 +31,7 @@ func Test_Ctx(t *testing.T) {
}
func Test_Ctx_Query(t *testing.T) {
db.GetLogger().SetCtxKeys("SpanId", "TraceId")
db.GetLogger().(*glog.Logger).SetCtxKeys("SpanId", "TraceId")
gtest.C(t, func(t *gtest.T) {
db.SetDebug(true)
defer db.SetDebug(false)
@ -48,7 +49,7 @@ func Test_Ctx_Query(t *testing.T) {
func Test_Ctx_Model(t *testing.T) {
table := createInitTable()
defer dropTable(table)
db.GetLogger().SetCtxKeys("SpanId", "TraceId")
db.GetLogger().(*glog.Logger).SetCtxKeys("SpanId", "TraceId")
gtest.C(t, func(t *gtest.T) {
db.SetDebug(true)
defer db.SetDebug(false)

View File

@ -22,6 +22,7 @@ import (
"github.com/gogf/gf/v2/encoding/gjson"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gfile"
"github.com/gogf/gf/v2/os/glog"
"github.com/gogf/gf/v2/os/gtime"
"github.com/gogf/gf/v2/test/gtest"
"github.com/gogf/gf/v2/text/gstr"
@ -978,8 +979,8 @@ func Test_Model_StructsWithOrmTag(t *testing.T) {
users []User
buffer = bytes.NewBuffer(nil)
)
dbInvalid.GetLogger().SetWriter(buffer)
defer dbInvalid.GetLogger().SetWriter(os.Stdout)
dbInvalid.GetLogger().(*glog.Logger).SetWriter(buffer)
defer dbInvalid.GetLogger().(*glog.Logger).SetWriter(os.Stdout)
dbInvalid.Model(table).Order("id asc").Scan(&users)
//fmt.Println(buffer.String())
t.Assert(

View File

@ -7,7 +7,6 @@
// Note:
// 1. It needs manually import: _ "github.com/lib/pq"
// 2. It does not support Save/Replace features.
// 3. It does not support LastInsertId.
// Package pgsql implements gdb.Driver, which supports operations for PostgreSql.
package pgsql
@ -16,15 +15,17 @@ import (
"context"
"database/sql"
"fmt"
_ "github.com/lib/pq"
"strings"
"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/text/gregex"
"github.com/gogf/gf/v2/text/gstr"
"github.com/gogf/gf/v2/util/gconv"
_ "github.com/lib/pq"
)
// Driver is the driver for postgresql database.
@ -37,6 +38,10 @@ var (
tableFieldsMap = gmap.New(true)
)
const (
internalPrimaryKeyInCtx gctx.StrKey = "primary_key"
)
func init() {
if err := gdb.Register(`pgsql`, New()); err != nil {
panic(err)
@ -69,10 +74,18 @@ func (d *Driver) Open(config *gdb.ConfigNode) (db *sql.DB, err error) {
source, _ = gregex.ReplaceString(`dbname=([\w\.\-]+)+`, "dbname="+config.Name, source)
}
} else {
source = fmt.Sprintf(
"user=%s password=%s host=%s port=%s dbname=%s sslmode=disable",
config.User, config.Pass, config.Host, config.Port, config.Name,
)
if config.Name != "" {
source = fmt.Sprintf(
"user=%s password=%s host=%s port=%s dbname=%s sslmode=disable",
config.User, config.Pass, config.Host, config.Port, config.Name,
)
} else {
source = fmt.Sprintf(
"user=%s password=%s host=%s port=%s sslmode=disable",
config.User, config.Pass, config.Host, config.Port,
)
}
if config.Timezone != "" {
source = fmt.Sprintf("%s timezone=%s", source, config.Timezone)
}
@ -108,6 +121,87 @@ func (d *Driver) GetChars() (charLeft string, charRight string) {
return `"`, `"`
}
// CheckLocalTypeForValue checks and returns corresponding local golang type for given db type.
func (d *Driver) CheckLocalTypeForValue(ctx context.Context, fieldType string, fieldValue interface{}) (string, error) {
var typeName string
match, _ := gregex.MatchString(`(.+?)\((.+)\)`, fieldType)
if len(match) == 3 {
typeName = gstr.Trim(match[1])
} else {
typeName = fieldType
}
typeName = strings.ToLower(typeName)
switch typeName {
case
// For pgsql, int2 = smallint.
"int2",
// For pgsql, int4 = integer
"int4":
return gdb.LocalTypeInt, nil
case
// For pgsql, int8 = bigint
"int8":
return gdb.LocalTypeInt64, nil
case
"_int2",
"_int4":
return gdb.LocalTypeIntSlice, nil
case
"_int8":
return gdb.LocalTypeInt64Slice, nil
default:
return d.Core.CheckLocalTypeForField(ctx, fieldType, fieldValue)
}
}
// ConvertValueForLocal converts value to local Golang type of value according field type name from database.
// The parameter `fieldType` is in lower case, like:
// `float(5,2)`, `unsigned double(5,2)`, `decimal(10,2)`, `char(45)`, `varchar(100)`, etc.
func (d *Driver) ConvertValueForLocal(ctx context.Context, fieldType string, fieldValue interface{}) (interface{}, error) {
typeName, _ := gregex.ReplaceString(`\(.+\)`, "", fieldType)
typeName = strings.ToLower(typeName)
switch typeName {
// For pgsql, int2 = smallint and int4 = integer.
case "int2", "int4":
return gconv.Int(gconv.String(fieldValue)), nil
// For pgsql, int8 = bigint.
case "int8":
return gconv.Int64(gconv.String(fieldValue)), nil
// Int32 slice.
case
"_int2", "_int4":
return gconv.Ints(
gstr.ReplaceByMap(gconv.String(fieldValue),
map[string]string{
"{": "[",
"}": "]",
},
),
), nil
// Int64 slice.
case
"_int8":
return gconv.Int64s(
gstr.ReplaceByMap(gconv.String(fieldValue),
map[string]string{
"{": "[",
"}": "]",
},
),
), nil
default:
return d.Core.ConvertValueForLocal(ctx, fieldType, fieldValue)
}
}
// DoFilter deals with the sql string before commits it to underlying sql driver.
func (d *Driver) DoFilter(ctx context.Context, link gdb.Link, sql string, args []interface{}) (newSql string, newArgs []interface{}, err error) {
defer func() {
@ -138,13 +232,28 @@ func (d *Driver) Tables(ctx context.Context, schema ...string) (tables []string,
if err != nil {
return nil, err
}
query := "SELECT TABLENAME FROM PG_TABLES WHERE SCHEMANAME = 'public' ORDER BY TABLENAME"
querySchema := "public"
if len(schema) > 0 && schema[0] != "" {
query = fmt.Sprintf(
"SELECT TABLENAME FROM PG_TABLES WHERE SCHEMANAME = '%s' ORDER BY TABLENAME",
schema[0],
)
querySchema = schema[0]
}
// list table names exclude partitions
query := fmt.Sprintf(`
SELECT
c.relname
FROM
pg_class c
INNER JOIN pg_namespace n ON
c.relnamespace = n.oid
WHERE
n.nspname = '%s'
AND c.relkind IN ('r', 'p')
AND c.relpartbound IS NULL
AND PG_TABLE_IS_VISIBLE(c.oid)
ORDER BY
c.relname`,
querySchema,
)
query, _ = gregex.ReplaceString(`[\n\r\s]+`, " ", gstr.Trim(query))
result, err = d.DoSelect(ctx, link, query)
if err != nil {
return
@ -241,7 +350,111 @@ func (d *Driver) DoInsert(ctx context.Context, link gdb.Link, table string, list
`Replace operation is not supported by pgsql driver`,
)
default:
return d.Core.DoInsert(ctx, link, table, list, option)
case gdb.InsertOptionIgnore:
return nil, gerror.NewCode(
gcode.CodeNotSupported,
`Insert ignore operation is not supported by pgsql driver`,
)
case gdb.InsertOptionDefault:
tableFields, err := d.TableFields(ctx, table)
if err == nil {
for _, field := range tableFields {
if field.Key == "pri" {
pkField := *field
ctx = context.WithValue(ctx, internalPrimaryKeyInCtx, pkField)
}
}
}
}
return d.Core.DoInsert(ctx, link, table, list, option)
}
func (d *Driver) DoExec(ctx context.Context, link gdb.Link, sql string, args ...interface{}) (result sql.Result, err error) {
var (
isUseCoreDoExec bool = false // Check whether the default method needs to be used
primaryKey string = ""
pkField gdb.TableField
)
// Transaction checks.
if link == nil {
if tx := gdb.TXFromCtx(ctx, d.GetGroup()); tx != nil {
isUseCoreDoExec = true
}
} else if link.IsTransaction() {
isUseCoreDoExec = true
}
if value := ctx.Value(internalPrimaryKeyInCtx); value != nil {
var ok bool
pkField, ok = value.(gdb.TableField)
if !ok {
isUseCoreDoExec = true
}
} else {
isUseCoreDoExec = true
}
// check if it is a insert operation.
if !isUseCoreDoExec && pkField.Name != "" && strings.Contains(sql, "INSERT INTO") {
primaryKey = pkField.Name
sql += " RETURNING " + primaryKey
} else {
// use default DoExec
return d.Core.DoExec(ctx, link, sql, args...)
}
// Only the insert operation with primary key can execute the following code
if d.GetConfig().ExecTimeout > 0 {
var cancelFunc context.CancelFunc
ctx, cancelFunc = context.WithTimeout(ctx, d.GetConfig().ExecTimeout)
defer cancelFunc()
}
// Sql filtering.
// TODO: internal function formatSql
// sql, args = formatSql(sql, args)
sql, args, err = d.DoFilter(ctx, link, sql, args)
if err != nil {
return nil, err
}
// Link execution.
var out gdb.DoCommitOutput
out, err = d.DoCommit(ctx, gdb.DoCommitInput{
Link: link,
Sql: sql,
Args: args,
Stmt: nil,
Type: gdb.SqlTypeQueryContext,
IsTransaction: link.IsTransaction(),
})
if err != nil {
return nil, err
}
affected := len(out.Records)
if affected > 0 {
if !strings.Contains(pkField.Type, "int") {
return Result{
affected: int64(affected),
lastInsertId: 0,
lastInsertIdError: gerror.NewCodef(
gcode.CodeNotSupported,
"LastInsertId is not supported by primary key type: %s", pkField.Type),
}, nil
}
if out.Records[affected-1][primaryKey] != nil {
lastInsertId := out.Records[affected-1][primaryKey].Int()
return Result{
affected: int64(affected),
lastInsertId: int64(lastInsertId),
}, nil
}
}
return Result{}, nil
}

View File

@ -0,0 +1,332 @@
// 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 pgsql_test
import (
"fmt"
"testing"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gtime"
"github.com/gogf/gf/v2/test/gtest"
)
func Test_DB_Query(t *testing.T) {
table := createTable("name")
defer dropTable(table)
gtest.C(t, func(t *gtest.T) {
_, err := db.Query(ctx, fmt.Sprintf("select * from %s ", table))
t.AssertNil(err)
})
}
func Test_DB_Exec(t *testing.T) {
table := createTable()
defer dropTable(table)
gtest.C(t, func(t *gtest.T) {
_, err := db.Exec(ctx, fmt.Sprintf("select * from %s ", table))
t.AssertNil(err)
})
}
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)
answer, err := db.GetAll(ctx, fmt.Sprintf("SELECT * FROM %s WHERE id=?", table), 1)
t.AssertNil(err)
t.Assert(len(answer), 1)
t.Assert(answer[0]["passport"], "t1")
t.Assert(answer[0]["password"], "25d55ad283aa400af464c76d713c07ad")
t.Assert(answer[0]["nickname"], "T1")
// 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)
answer, err = db.GetAll(ctx, fmt.Sprintf("SELECT * FROM %s WHERE id=?", table), 2)
t.AssertNil(err)
t.Assert(len(answer), 1)
t.Assert(answer[0]["passport"], "t2")
t.Assert(answer[0]["password"], "25d55ad283aa400af464c76d713c07ad")
t.Assert(answer[0]["nickname"], "name_2")
})
}
func Test_DB_Save(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.Save(ctx, "t_user", data, 10)
gtest.AssertNE(err, nil)
})
}
func Test_DB_Replace(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.Replace(ctx, "t_user", data, 10)
gtest.AssertNE(err, nil)
})
}
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 := createTable()
defer dropTable(table)
gtest.C(t, func(t *gtest.T) {
type User struct {
Id int
Passport string
Password string
Nickname string
CreateTime string
}
data := User{
Id: 1,
Passport: "user_1",
Password: "pass_1",
Nickname: "name_1",
CreateTime: "2020-10-10 12:00:01",
}
_, err := db.Insert(ctx, table, data)
t.AssertNil(err)
one, err := db.GetOne(ctx, fmt.Sprintf("SELECT * FROM %s WHERE id=?", table), 1)
t.AssertNil(err)
t.Assert(one["passport"], data.Passport)
t.Assert(one["create_time"], data.CreateTime)
t.Assert(one["nickname"], data.Nickname)
})
}
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_GetArray(t *testing.T) {
table := createInitTable()
defer dropTable(table)
gtest.C(t, func(t *gtest.T) {
array, err := db.GetArray(ctx, fmt.Sprintf("SELECT password FROM %s", table))
t.AssertNil(err)
arrays := make([]string, 0)
for i := 1; i <= TableSize; i++ {
arrays = append(arrays, fmt.Sprintf(`pass_%d`, i))
}
t.Assert(array, arrays)
})
}
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")
})
}
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(one["password"].String(), "987654321")
t.Assert(one["nickname"].String(), "name_3")
})
}
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, "id>3")
t.AssertNil(err)
n, _ := result.RowsAffected()
t.Assert(n, 7)
})
}
func Test_DB_Tables(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 tables[i] == result[j] {
find = true
break
}
}
gtest.AssertEQ(find, true)
}
})
}
func Test_DB_TableFields(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
table := createTable()
defer dropTable(table)
var expect = map[string][]interface{}{
//[]string: Index Type Null Key Default Comment
//id is bigserial so the default is a pgsql function
"id": {0, "int8", false, "pri", fmt.Sprintf("nextval('%s_id_seq'::regclass)", table), ""},
"passport": {1, "varchar", false, "", nil, ""},
"password": {2, "varchar", false, "", nil, ""},
"nickname": {3, "varchar", false, "", nil, ""},
"create_time": {4, "timestamp", false, "", nil, ""},
}
res, err := db.TableFields(ctx, table)
gtest.Assert(err, nil)
for k, v := range expect {
_, ok := res[k]
gtest.AssertEQ(ok, true)
gtest.AssertEQ(res[k].Index, v[0])
gtest.AssertEQ(res[k].Name, k)
gtest.AssertEQ(res[k].Type, v[1])
gtest.AssertEQ(res[k].Null, v[2])
gtest.AssertEQ(res[k].Key, v[3])
gtest.AssertEQ(res[k].Default, v[4])
gtest.AssertEQ(res[k].Comment, v[5])
}
})
}

View File

@ -0,0 +1,137 @@
// 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 pgsql_test
import (
"context"
"fmt"
_ "github.com/gogf/gf/contrib/drivers/pgsql/v2"
"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"
)
const (
TableSize = 10
TablePrefix = "t_"
SchemaName = "test"
TestDbUser = "postgres"
TestDbPass = "12345678"
CreateTime = "2018-10-24 10:00:00"
)
var (
db gdb.DB
configNode gdb.ConfigNode
ctx = context.TODO()
)
func init() {
configNode = gdb.ConfigNode{
Host: "127.0.0.1",
Port: "5432",
User: TestDbUser,
Pass: TestDbPass,
Timezone: "Asia/Shanghai", // For calculating UT cases of datetime zones in convenience.
Type: "pgsql",
Role: "master",
Charset: "utf8",
Weight: 1,
MaxIdleConnCount: 10,
MaxOpenConnCount: 10,
MaxConnLifeTime: 600,
}
//pgsql only permit to connect to the designation database.
//so you need to create the pgsql database before you use orm
gdb.AddConfigNode(gdb.DefaultGroupName, configNode)
if r, err := gdb.New(configNode); err != nil {
gtest.Fatal(err)
} else {
db = r
}
if configNode.Name == "" {
schemaTemplate := "SELECT 'CREATE DATABASE %s' WHERE NOT EXISTS (SELECT FROM pg_database WHERE datname = '%s')"
if _, err := db.Exec(ctx, fmt.Sprintf(schemaTemplate, SchemaName, SchemaName)); err != nil {
gtest.Error(err)
}
db = db.Schema(SchemaName)
} else {
db = db.Schema(configNode.Name)
}
}
func createTable(table ...string) string {
return createTableWithDb(db, table...)
}
func createInitTable(table ...string) string {
return createInitTableWithDb(db, table...)
}
func createTableWithDb(db gdb.DB, table ...string) (name string) {
if len(table) > 0 {
name = table[0]
} else {
name = fmt.Sprintf(`%s_%d`, TablePrefix+"test", gtime.TimestampNano())
}
dropTableWithDb(db, name)
if _, err := db.Exec(ctx, fmt.Sprintf(`
CREATE TABLE %s (
id bigserial NOT NULL,
passport varchar(45) NOT NULL,
password varchar(32) NOT NULL,
nickname varchar(45) NOT NULL,
create_time timestamp NOT NULL,
PRIMARY KEY (id)
) ;`, name,
)); err != nil {
gtest.Fatal(err)
}
return
}
func dropTable(table string) {
dropTableWithDb(db, table)
}
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)
}
}

View File

@ -0,0 +1,286 @@
// 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 pgsql_test
import (
"testing"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gtime"
"github.com/gogf/gf/v2/test/gtest"
)
func Test_Model_Insert(t *testing.T) {
table := createTable()
defer dropTable(table)
gtest.C(t, func(t *gtest.T) {
user := db.Model(table)
result, err := user.Data(g.Map{
"id": 1,
"uid": 1,
"passport": "t1",
"password": "25d55ad283aa400af464c76d713c07ad",
"nickname": "name_1",
"create_time": gtime.Now().String(),
}).Insert()
t.AssertNil(err)
n, _ := result.RowsAffected()
t.Assert(n, 1)
result, err = db.Model(table).Data(g.Map{
"id": "2",
"uid": "2",
"passport": "t2",
"password": "25d55ad283aa400af464c76d713c07ad",
"nickname": "name_2",
"create_time": gtime.Now().String(),
}).Insert()
t.AssertNil(err)
n, _ = result.RowsAffected()
t.Assert(n, 1)
type User struct {
Id int `gconv:"id"`
Uid int `gconv:"uid"`
Passport string `json:"passport"`
Password string `gconv:"password"`
Nickname string `gconv:"nickname"`
CreateTime *gtime.Time `json:"create_time"`
}
// Model inserting.
result, err = db.Model(table).Data(User{
Id: 3,
Uid: 3,
Passport: "t3",
Password: "25d55ad283aa400af464c76d713c07ad",
Nickname: "name_3",
CreateTime: gtime.Now(),
}).Insert()
t.AssertNil(err)
n, _ = result.RowsAffected()
t.Assert(n, 1)
value, err := db.Model(table).Fields("passport").Where("id=3").Value() //model value
t.AssertNil(err)
t.Assert(value.String(), "t3")
result, err = db.Model(table).Data(&User{
Id: 4,
Uid: 4,
Passport: "t4",
Password: "25d55ad283aa400af464c76d713c07ad",
Nickname: "T4",
CreateTime: gtime.Now(),
}).Insert()
t.AssertNil(err)
n, _ = result.RowsAffected()
t.Assert(n, 1)
value, err = db.Model(table).Fields("passport").Where("id=4").Value()
t.AssertNil(err)
t.Assert(value.String(), "t4")
result, err = db.Model(table).Where("id>?", 1).Delete() //model delete
t.AssertNil(err)
n, _ = result.RowsAffected()
t.Assert(n, 3)
})
}
func Test_Model_One(t *testing.T) {
table := createTable()
defer dropTable(table)
gtest.C(t, func(t *gtest.T) {
type User struct {
Id int
Passport string
Password string
Nickname string
CreateTime string
}
data := User{
Id: 1,
Passport: "user_1",
Password: "pass_1",
Nickname: "name_1",
CreateTime: "2020-10-10 12:00:01",
}
_, err := db.Model(table).Data(data).Insert()
t.AssertNil(err)
one, err := db.Model(table).WherePri(1).One() //model one
t.AssertNil(err)
t.Assert(one["passport"], data.Passport)
t.Assert(one["create_time"], data.CreateTime)
t.Assert(one["nickname"], data.Nickname)
})
}
func Test_Model_All(t *testing.T) {
table := createInitTable()
defer dropTable(table)
gtest.C(t, func(t *gtest.T) {
result, err := db.Model(table).All()
t.AssertNil(err)
t.Assert(len(result), TableSize)
})
}
func Test_Model_Delete(t *testing.T) {
table := createInitTable()
defer dropTable(table)
gtest.C(t, func(t *gtest.T) {
result, err := db.Model(table).Where("id", "2").Delete()
t.AssertNil(err)
n, _ := result.RowsAffected()
t.Assert(n, 1)
})
}
func Test_Model_Update(t *testing.T) {
table := createInitTable()
defer dropTable(table)
// Update + Data(string)
gtest.C(t, func(t *gtest.T) {
result, err := db.Model(table).Data("passport='user_33'").Where("passport='user_3'").Update()
t.AssertNil(err)
n, _ := result.RowsAffected()
t.Assert(n, 1)
})
// Update + Fields(string)
gtest.C(t, func(t *gtest.T) {
result, err := db.Model(table).Fields("passport").Data(g.Map{
"passport": "user_44",
"none": "none",
}).Where("passport='user_4'").Update()
t.AssertNil(err)
n, _ := result.RowsAffected()
t.Assert(n, 1)
})
}
func Test_Model_Array(t *testing.T) {
table := createInitTable()
defer dropTable(table)
gtest.C(t, func(t *gtest.T) {
all, err := db.Model(table).Where("id", g.Slice{1, 2, 3}).All()
t.AssertNil(err)
t.Assert(all.Array("id"), g.Slice{1, 2, 3})
t.Assert(all.Array("nickname"), g.Slice{"name_1", "name_2", "name_3"})
})
gtest.C(t, func(t *gtest.T) {
array, err := db.Model(table).Fields("nickname").Where("id", g.Slice{1, 2, 3}).Array()
t.AssertNil(err)
t.Assert(array, g.Slice{"name_1", "name_2", "name_3"})
})
gtest.C(t, func(t *gtest.T) {
array, err := db.Model(table).Array("nickname", "id", g.Slice{1, 2, 3})
t.AssertNil(err)
t.Assert(array, g.Slice{"name_1", "name_2", "name_3"})
})
}
func Test_Model_Scan(t *testing.T) {
table := createInitTable()
defer dropTable(table)
type User struct {
Id int
Passport string
Password string
NickName string
CreateTime gtime.Time
}
gtest.C(t, func(t *gtest.T) {
var users []User
err := db.Model(table).Scan(&users)
t.AssertNil(err)
t.Assert(len(users), TableSize)
})
}
func Test_Model_Count(t *testing.T) {
table := createInitTable()
defer dropTable(table)
gtest.C(t, func(t *gtest.T) {
count, err := db.Model(table).Count()
t.AssertNil(err)
t.Assert(count, TableSize)
})
gtest.C(t, func(t *gtest.T) {
count, err := db.Model(table).FieldsEx("id").Where("id>8").Count()
t.AssertNil(err)
t.Assert(count, 2)
})
}
func Test_Model_Where(t *testing.T) {
table := createInitTable()
defer dropTable(table)
// map + slice parameter
gtest.C(t, func(t *gtest.T) {
result, err := db.Model(table).Where(g.Map{
"id": g.Slice{1, 2, 3},
"passport": g.Slice{"user_2", "user_3"},
}).Where("id=? and nickname=?", g.Slice{3, "name_3"}).One()
t.AssertNil(err)
t.AssertGT(len(result), 0)
t.Assert(result["id"].Int(), 3)
})
// struct, automatic mapping and filtering.
gtest.C(t, func(t *gtest.T) {
type User struct {
Id int
Nickname string
}
result, err := db.Model(table).Where(User{3, "name_3"}).One()
t.AssertNil(err)
t.Assert(result["id"].Int(), 3)
result, err = db.Model(table).Where(&User{3, "name_3"}).One()
t.AssertNil(err)
t.Assert(result["id"].Int(), 3)
})
}
func Test_Model_Save(t *testing.T) {
table := createTable()
defer dropTable(table)
gtest.C(t, func(t *gtest.T) {
_, err := db.Model(table).Data(g.Map{
"id": 1,
"passport": "t111",
"password": "25d55ad283aa400af464c76d713c07ad",
"nickname": "T111",
"create_time": "2018-10-24 10:00:00",
}).Save()
t.Assert(err, "Save operation is not supported by pgsql driver")
})
}
func Test_Model_Replace(t *testing.T) {
table := createTable()
defer dropTable(table)
gtest.C(t, func(t *gtest.T) {
_, err := db.Model(table).Data(g.Map{
"id": 1,
"passport": "t11",
"password": "25d55ad283aa400af464c76d713c07ad",
"nickname": "T11",
"create_time": "2018-10-24 10:00:00",
}).Replace()
t.Assert(err, "Replace operation is not supported by pgsql driver")
})
}

View File

@ -0,0 +1,99 @@
// 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 pgsql_test
import (
"testing"
"github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/test/gtest"
)
func Test_Raw_Insert(t *testing.T) {
table := createTable()
defer dropTable(table)
gtest.C(t, func(t *gtest.T) {
user := db.Model(table)
result, err := user.Data(g.Map{
"passport": "port_1",
"password": "pass_1",
"nickname": "name_1",
"create_time": gdb.Raw("now()"),
}).Insert()
t.AssertNil(err)
n, _ := result.RowsAffected()
t.Assert(n, 1)
})
}
func Test_Raw_BatchInsert(t *testing.T) {
table := createTable()
defer dropTable(table)
gtest.C(t, func(t *gtest.T) {
user := db.Model(table)
result, err := user.Data(
g.List{
g.Map{
"passport": "port_2",
"password": "pass_2",
"nickname": "name_2",
"create_time": gdb.Raw("now()"),
},
g.Map{
"passport": "port_4",
"password": "pass_4",
"nickname": "name_4",
"create_time": gdb.Raw("now()"),
},
},
).Insert()
t.AssertNil(err)
n, _ := result.RowsAffected()
t.Assert(n, 2)
})
}
func Test_Raw_Delete(t *testing.T) {
table := createInitTable()
defer dropTable(table)
gtest.C(t, func(t *gtest.T) {
user := db.Model(table)
result, err := user.Data(g.Map{
"id": gdb.Raw("id"),
}).Where("id", 1).Delete()
t.AssertNil(err)
n, _ := result.RowsAffected()
t.Assert(n, 1)
})
}
func Test_Raw_Update(t *testing.T) {
table := createInitTable()
defer dropTable(table)
gtest.C(t, func(t *gtest.T) {
user := db.Model(table)
result, err := user.Data(g.Map{
"id": gdb.Raw("id+100"),
"create_time": gdb.Raw("now()"),
}).Where("id", 1).Update()
t.AssertNil(err)
n, _ := result.RowsAffected()
t.Assert(n, 1)
})
gtest.C(t, func(t *gtest.T) {
user := db.Model(table)
n, err := user.Where("id", 101).Count()
t.AssertNil(err)
t.Assert(n, 1)
})
}

View File

@ -0,0 +1,18 @@
package pgsql
import "database/sql"
type Result struct {
sql.Result
affected int64
lastInsertId int64
lastInsertIdError error
}
func (pgr Result) RowsAffected() (int64, error) {
return pgr.affected, nil
}
func (pgr Result) LastInsertId() (int64, error) {
return pgr.lastInsertId, pgr.lastInsertIdError
}

View File

@ -15,6 +15,36 @@ import (
"github.com/gogf/gf/v2/test/gtest"
)
func Test_LastInsertId(t *testing.T) {
// err not nil
gtest.C(t, func(t *gtest.T) {
_, err := db.Model("notexist").Insert(g.List{
{"name": "user1"},
{"name": "user2"},
{"name": "user3"},
})
t.AssertNE(err, nil)
})
gtest.C(t, func(t *gtest.T) {
tableName := createTable()
defer dropTable(tableName)
res, err := db.Model(tableName).Insert(g.List{
{"passport": "user1", "password": "pwd", "nickname": "nickname", "create_time": CreateTime},
{"passport": "user2", "password": "pwd", "nickname": "nickname", "create_time": CreateTime},
{"passport": "user3", "password": "pwd", "nickname": "nickname", "create_time": CreateTime},
})
t.Assert(err, nil)
lastInsertId, err := res.LastInsertId()
t.Assert(err, nil)
t.Assert(lastInsertId, int64(3))
rowsAffected, err := res.RowsAffected()
t.Assert(err, nil)
t.Assert(rowsAffected, int64(3))
})
}
func Test_Driver_DoFilter(t *testing.T) {
var (
ctx = gctx.New()

View File

@ -3,8 +3,8 @@ module github.com/gogf/gf/contrib/drivers/sqlite/v2
go 1.15
require (
github.com/glebarez/go-sqlite v1.17.3
github.com/gogf/gf/v2 v2.0.0
github.com/mattn/go-sqlite3 v1.14.10
)
replace github.com/gogf/gf/v2 => ../../../

View File

@ -12,12 +12,15 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI=
github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU=
github.com/glebarez/go-sqlite v1.17.3 h1:Rji9ROVSTTfjuWD6j5B+8DtkNvPILoUC3xRhkQzGxvk=
github.com/glebarez/go-sqlite v1.17.3/go.mod h1:Hg+PQuhUy98XCxWEJEaWob8x7lhJzhNYF1nZbUiRGIY=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
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=
@ -39,16 +42,20 @@ github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiu
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=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
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/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=
github.com/grokify/html-strip-tags-go v0.0.1/go.mod h1:2Su6romC5/1VXOQMaWL2yb618ARB8iVo6/DR99A6d78=
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/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo=
github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
github.com/mattn/go-colorable v0.1.9 h1:sqDoxXbdeALODt0DAeJCVp38ps9ZogZEAXjus69YV3U=
@ -58,8 +65,7 @@ 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/mattn/go-sqlite3 v1.14.12/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=
@ -79,6 +85,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/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk=
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
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=
@ -126,7 +134,9 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
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-20211007075335-d3039528d8ac/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-20220405052023-b1e9470b6e64/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=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
@ -137,6 +147,7 @@ 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-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
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/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@ -166,3 +177,29 @@ 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=
lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
modernc.org/cc/v3 v3.36.0/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI=
modernc.org/ccgo/v3 v3.0.0-20220428102840-41399a37e894/go.mod h1:eI31LL8EwEBKPpNpA4bU1/i+sKOwOrQy8D87zWUcRZc=
modernc.org/ccgo/v3 v3.0.0-20220430103911-bc99d88307be/go.mod h1:bwdAnOoaIt8Ax9YdWGjxWsdkPcZyRPHqrOvJxaKAKGw=
modernc.org/ccgo/v3 v3.16.4/go.mod h1:tGtX0gE9Jn7hdZFeU88slbTh1UtCYKusWOoCJuvkWsQ=
modernc.org/ccgo/v3 v3.16.6/go.mod h1:tGtX0gE9Jn7hdZFeU88slbTh1UtCYKusWOoCJuvkWsQ=
modernc.org/ccorpus v1.11.6/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ=
modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM=
modernc.org/libc v0.0.0-20220428101251-2d5f3daf273b/go.mod h1:p7Mg4+koNjc8jkqwcoFBJx7tXkpj00G77X7A72jXPXA=
modernc.org/libc v1.16.0/go.mod h1:N4LD6DBE9cf+Dzf9buBlzVJndKr/iJHG97vGLHYnb5A=
modernc.org/libc v1.16.1/go.mod h1:JjJE0eu4yeK7tab2n4S1w8tlWd9MxXLRzheaRnAKymU=
modernc.org/libc v1.16.7/go.mod h1:hYIV5VZczAmGZAnG15Vdngn5HSF5cSkbvfz2B7GRuVU=
modernc.org/libc v1.16.8 h1:Ux98PaOMvolgoFX/YwusFOHBnanXdGRmWgI8ciI2z4o=
modernc.org/libc v1.16.8/go.mod h1:hYIV5VZczAmGZAnG15Vdngn5HSF5cSkbvfz2B7GRuVU=
modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
modernc.org/mathutil v1.4.1 h1:ij3fYGe8zBF4Vu+g0oT7mB06r8sqGWKuJu1yXeR4by8=
modernc.org/mathutil v1.4.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
modernc.org/memory v1.1.1 h1:bDOL0DIDLQv7bWhP3gMvIrnoFw+Eo6F7a2QK9HPDiFU=
modernc.org/memory v1.1.1/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw=
modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
modernc.org/sqlite v1.17.3 h1:iE+coC5g17LtByDYDWKpR6m2Z9022YrSh3bumwOnIrI=
modernc.org/sqlite v1.17.3/go.mod h1:10hPVYar9C0kfXuTWGz8s0XtB8uAGymUy51ZzStYe3k=
modernc.org/strutil v1.1.1/go.mod h1:DE+MQQ/hjKBZS2zNInV5hhcipt5rLPWkmpbGeW5mmdw=
modernc.org/tcl v1.13.1/go.mod h1:XOLfOwzhkljL4itZkK6T72ckMgvj0BDsnKNdZVUOecw=
modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
modernc.org/z v1.5.1/go.mod h1:eWFB510QWW5Th9YGZT81s+LwvaAs3Q2yr4sP0rmLkv8=

View File

@ -5,7 +5,7 @@
// You can obtain one at https://github.com/gogf/gf.
//
// Note:
// 1. It needs manually import: _ "github.com/mattn/go-sqlite3"
// 1. It needs manually import: _ "github.com/glebarez/go-sqlite"
// 2. It does not support Save/Replace features.
// Package sqlite implements gdb.Driver, which supports operations for SQLite.
@ -17,7 +17,7 @@ import (
"fmt"
"strings"
_ "github.com/mattn/go-sqlite3"
_ "github.com/glebarez/go-sqlite"
"github.com/gogf/gf/v2/container/gmap"
"github.com/gogf/gf/v2/database/gdb"
@ -36,8 +36,7 @@ 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`)
ErrorSave = gerror.NewCode(gcode.CodeNotSupported, `Save operation is not supported by sqlite driver`)
)
func init() {
@ -63,7 +62,7 @@ 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 = "sqlite3"
underlyingDriverName = "sqlite"
)
if config.Link != "" {
source = config.Link

View File

@ -21,6 +21,7 @@ import (
"github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/encoding/gjson"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/glog"
"github.com/gogf/gf/v2/os/gtime"
"github.com/gogf/gf/v2/test/gtest"
"github.com/gogf/gf/v2/text/gstr"
@ -959,8 +960,8 @@ func Test_Model_StructsWithOrmTag(t *testing.T) {
users []User
buffer = bytes.NewBuffer(nil)
)
db.GetLogger().SetWriter(buffer)
defer db.GetLogger().SetWriter(os.Stdout)
db.GetLogger().(*glog.Logger).SetWriter(buffer)
defer db.GetLogger().(*glog.Logger).SetWriter(os.Stdout)
db.Model(table).Order("id asc").Scan(&users)
// fmt.Println(buffer.String())
t.Assert(

View File

@ -31,12 +31,12 @@ type Registry struct {
kv etcd3.KV
lease etcd3.Lease
keepaliveTTL time.Duration
logger *glog.Logger
logger glog.ILogger
}
// Option is the option for the etcd registry.
type Option struct {
Logger *glog.Logger
Logger glog.ILogger
KeepaliveTTL time.Duration
}

View File

@ -154,8 +154,8 @@ type DB interface {
GetGroup() string // See Core.GetGroup.
SetDryRun(enabled bool) // See Core.SetDryRun.
GetDryRun() bool // See Core.GetDryRun.
SetLogger(logger *glog.Logger) // See Core.SetLogger.
GetLogger() *glog.Logger // See Core.GetLogger.
SetLogger(logger glog.ILogger) // See Core.SetLogger.
GetLogger() glog.ILogger // See Core.GetLogger.
GetConfig() *ConfigNode // See Core.GetConfig.
SetMaxIdleConnCount(n int) // See Core.SetMaxIdleConnCount.
SetMaxOpenConnCount(n int) // See Core.SetMaxOpenConnCount.
@ -165,13 +165,15 @@ type DB interface {
// Utility methods.
// ===========================================================================
GetCtx() context.Context // See Core.GetCtx.
GetCore() *Core // See Core.GetCore
GetChars() (charLeft string, charRight string) // See Core.GetChars.
Tables(ctx context.Context, schema ...string) (tables []string, err error) // See Core.Tables.
TableFields(ctx context.Context, table string, schema ...string) (map[string]*TableField, error) // See Core.TableFields.
ConvertDataForRecord(ctx context.Context, data interface{}) (map[string]interface{}, error) // See Core.ConvertDataForRecord
FilteredLink() string // FilteredLink is used for filtering sensitive information in `Link` configuration before output it to tracing server.
GetCtx() context.Context // See Core.GetCtx.
GetCore() *Core // See Core.GetCore
GetChars() (charLeft string, charRight string) // See Core.GetChars.
Tables(ctx context.Context, schema ...string) (tables []string, err error) // See Core.Tables.
TableFields(ctx context.Context, table string, schema ...string) (map[string]*TableField, error) // See Core.TableFields.
ConvertDataForRecord(ctx context.Context, data interface{}) (map[string]interface{}, error) // See Core.ConvertDataForRecord
ConvertValueForLocal(ctx context.Context, fieldType string, fieldValue interface{}) (interface{}, error) // See Core.ConvertValueForLocal
CheckLocalTypeForField(ctx context.Context, fieldType string, fieldValue interface{}) (string, error) // See Core.CheckLocalTypeForField
FilteredLink() string // FilteredLink is used for filtering sensitive information in `Link` configuration before output it to tracing server.
}
// Core is the base struct for database management.
@ -183,7 +185,7 @@ type Core struct {
debug *gtype.Bool // Enable debug mode for the database, which can be changed in runtime.
cache *gcache.Cache // Cache manager, SQL result cache only.
links *gmap.StrAnyMap // links caches all created links by node.
logger *glog.Logger // Logger for logging functionality.
logger glog.ILogger // Logger for logging functionality.
config *ConfigNode // Current config node.
}
@ -311,6 +313,27 @@ const (
SqlTypeStmtQueryRowContext = "DB.Statement.QueryRowContext"
)
const (
LocalTypeString = "string"
LocalTypeDate = "date"
LocalTypeDatetime = "datetime"
LocalTypeInt = "int"
LocalTypeUint = "uint"
LocalTypeInt64 = "int64"
LocalTypeUint64 = "uint64"
LocalTypeIntSlice = "[]int"
LocalTypeInt64Slice = "[]int64"
LocalTypeUint64Slice = "[]uint64"
LocalTypeInt64Bytes = "int64-bytes"
LocalTypeUint64Bytes = "uint64-bytes"
LocalTypeFloat32 = "float32"
LocalTypeFloat64 = "float64"
LocalTypeBytes = "[]byte"
LocalTypeBool = "bool"
LocalTypeJson = "json"
LocalTypeJsonb = "jsonb"
)
var (
// instances is the management map for instances.
instances = gmap.NewStrAnyMap(true)
@ -373,16 +396,14 @@ func NewByGroup(group ...string) (db DB, err error) {
var node *ConfigNode
if node, err = getConfigNodeByGroup(groupName, true); err == nil {
return doNewByNode(*node, groupName)
} else {
return nil, err
}
} else {
return nil, gerror.NewCodef(
gcode.CodeInvalidConfiguration,
`database configuration node "%s" is not found, did you misspell group name "%s" or miss the database configuration?`,
groupName, groupName,
)
return nil, err
}
return nil, gerror.NewCodef(
gcode.CodeInvalidConfiguration,
`database configuration node "%s" is not found, did you misspell group name "%s" or miss the database configuration?`,
groupName, groupName,
)
}
// doNewByNode creates and returns an ORM object with given configuration node and group name.
@ -458,13 +479,12 @@ func getConfigNodeByGroup(group string, master bool) (*ConfigNode, error) {
} else {
return getConfigNodeByWeight(slaveList), nil
}
} else {
return nil, gerror.NewCodef(
gcode.CodeInvalidConfiguration,
"empty database configuration for item name '%s'",
group,
)
}
return nil, gerror.NewCodef(
gcode.CodeInvalidConfiguration,
"empty database configuration for item name '%s'",
group,
)
}
// getConfigNodeByWeight calculates the configuration weights and randomly returns a node.
@ -497,12 +517,13 @@ func getConfigNodeByWeight(cg ConfigGroup) *ConfigNode {
)
for i := 0; i < len(cg); i++ {
max = min + cg[i].Weight*100
// fmt.Printf("r: %d, min: %d, max: %d\n", r, min, max)
if random >= min && random < max {
return &cg[i]
} else {
min = max
// Return a copy of the ConfigNode.
node := ConfigNode{}
node = cg[i]
return &node
}
min = max
}
return nil
}
@ -517,6 +538,8 @@ func (c *Core) getSqlDb(master bool, schema ...string) (sqlDb *sql.DB, err error
)
// Load balance.
if c.group != "" {
configs.RLock()
defer configs.RUnlock()
node, err = getConfigNodeByGroup(c.group, master)
if err != nil {
return nil, err

View File

@ -156,12 +156,12 @@ func IsConfigured() bool {
}
// SetLogger sets the logger for orm.
func (c *Core) SetLogger(logger *glog.Logger) {
func (c *Core) SetLogger(logger glog.ILogger) {
c.logger = logger
}
// GetLogger returns the (logger) of the orm.
func (c *Core) GetLogger() *glog.Logger {
func (c *Core) GetLogger() glog.ILogger {
return c.logger
}

View File

@ -121,15 +121,19 @@ func (c *Core) ConvertDataForRecordValue(ctx context.Context, value interface{})
return convertedValue, nil
}
// convertFieldValueToLocalValue automatically checks and converts field value from database type
// to golang variable type as underlying value of Value.
func (c *Core) convertFieldValueToLocalValue(fieldValue interface{}, fieldType string) interface{} {
// If there's no type retrieved, it returns the `fieldValue` directly
// to use its original data type, as `fieldValue` is type of interface{}.
if fieldType == "" {
return fieldValue
// CheckLocalTypeForField checks and returns corresponding type for given db type.
func (c *Core) CheckLocalTypeForField(ctx context.Context, fieldType string, fieldValue interface{}) (string, error) {
var (
typeName string
typePattern string
)
match, _ := gregex.MatchString(`(.+?)\((.+)\)`, fieldType)
if len(match) == 3 {
typeName = gstr.Trim(match[1])
typePattern = gstr.Trim(match[2])
} else {
typeName = fieldType
}
typeName, _ := gregex.ReplaceString(`\(.+\)`, "", fieldType)
typeName = strings.ToLower(typeName)
switch typeName {
case
@ -139,7 +143,7 @@ func (c *Core) convertFieldValueToLocalValue(fieldValue interface{}, fieldType s
"tinyblob",
"mediumblob",
"longblob":
return gconv.Bytes(fieldValue)
return LocalTypeBytes, nil
case
"int",
@ -150,22 +154,22 @@ func (c *Core) convertFieldValueToLocalValue(fieldValue interface{}, fieldType s
"mediumint",
"serial":
if gstr.ContainsI(fieldType, "unsigned") {
gconv.Uint(gconv.String(fieldValue))
return LocalTypeUint, nil
}
return gconv.Int(gconv.String(fieldValue))
return LocalTypeInt, nil
case
"int8", // For pgsql, int8 = bigint.
"big_int",
"bigint",
"bigserial":
if gstr.ContainsI(fieldType, "unsigned") {
gconv.Uint64(gconv.String(fieldValue))
return LocalTypeUint64, nil
}
return gconv.Int64(gconv.String(fieldValue))
return LocalTypeInt64, nil
case "real":
return gconv.Float32(gconv.String(fieldValue))
case
"real":
return LocalTypeFloat32, nil
case
"float",
@ -174,80 +178,151 @@ func (c *Core) convertFieldValueToLocalValue(fieldValue interface{}, fieldType s
"money",
"numeric",
"smallmoney":
return gconv.Float64(gconv.String(fieldValue))
return LocalTypeFloat64, nil
case "bit":
case
"bit":
// It is suggested using bit(1) as boolean.
if typePattern == "1" {
return LocalTypeBool, nil
}
s := gconv.String(fieldValue)
// mssql is true|false string.
if strings.EqualFold(s, "true") {
return 1
if strings.EqualFold(s, "true") || strings.EqualFold(s, "false") {
return LocalTypeBool, nil
}
if strings.EqualFold(s, "false") {
return 0
if gstr.ContainsI(fieldType, "unsigned") {
return LocalTypeUint64Bytes, nil
}
return gbinary.BeDecodeToInt64(gconv.Bytes(fieldValue))
return LocalTypeInt64Bytes, nil
case "bool":
return gconv.Bool(fieldValue)
case
"bool":
return LocalTypeBool, nil
case "date":
// Date without time.
if t, ok := fieldValue.(time.Time); ok {
return gtime.NewFromTime(t).Format("Y-m-d")
}
t, _ := gtime.StrToTime(gconv.String(fieldValue))
return t.Format("Y-m-d")
case
"date":
return LocalTypeDate, nil
case
"datetime",
"timestamp",
"timestamptz":
if t, ok := fieldValue.(time.Time); ok {
return gtime.NewFromTime(t)
}
t, _ := gtime.StrToTime(gconv.String(fieldValue))
return t
return LocalTypeDatetime, nil
case
"json":
return LocalTypeJson, nil
case
"jsonb":
return LocalTypeJsonb, nil
default:
// Auto-detect field type, using key match.
switch {
case strings.Contains(typeName, "text") || strings.Contains(typeName, "char") || strings.Contains(typeName, "character"):
return gconv.String(fieldValue)
return LocalTypeString, nil
case strings.Contains(typeName, "float") || strings.Contains(typeName, "double") || strings.Contains(typeName, "numeric"):
return gconv.Float64(gconv.String(fieldValue))
return LocalTypeFloat64, nil
case strings.Contains(typeName, "bool"):
return gconv.Bool(gconv.String(fieldValue))
return LocalTypeBool, nil
case strings.Contains(typeName, "binary") || strings.Contains(typeName, "blob"):
return fieldValue
return LocalTypeBytes, nil
case strings.Contains(typeName, "int"):
return gconv.Int(gconv.String(fieldValue))
return LocalTypeInt, nil
case strings.Contains(typeName, "time"):
s := gconv.String(fieldValue)
t, err := gtime.StrToTime(s)
if err != nil {
return s
}
return t
return LocalTypeDatetime, nil
case strings.Contains(typeName, "date"):
s := gconv.String(fieldValue)
t, err := gtime.StrToTime(s)
if err != nil {
return s
}
return t
return LocalTypeDatetime, nil
default:
return gconv.String(fieldValue)
return LocalTypeString, nil
}
}
}
// ConvertValueForLocal converts value to local Golang type of value according field type name from database.
// The parameter `fieldType` is in lower case, like:
// `float(5,2)`, `unsigned double(5,2)`, `decimal(10,2)`, `char(45)`, `varchar(100)`, etc.
func (c *Core) ConvertValueForLocal(ctx context.Context, fieldType string, fieldValue interface{}) (interface{}, error) {
// If there's no type retrieved, it returns the `fieldValue` directly
// to use its original data type, as `fieldValue` is type of interface{}.
if fieldType == "" {
return fieldValue, nil
}
typeName, err := c.db.CheckLocalTypeForField(ctx, fieldType, fieldValue)
if err != nil {
return nil, err
}
switch typeName {
case LocalTypeBytes:
if strings.Contains(typeName, "binary") || strings.Contains(typeName, "blob") {
return fieldValue, nil
}
return gconv.Bytes(fieldValue), nil
case LocalTypeInt:
return gconv.Int(gconv.String(fieldValue)), nil
case LocalTypeUint:
return gconv.Uint(gconv.String(fieldValue)), nil
case LocalTypeInt64:
return gconv.Int64(gconv.String(fieldValue)), nil
case LocalTypeUint64:
return gconv.Uint64(gconv.String(fieldValue)), nil
case LocalTypeInt64Bytes:
return gbinary.BeDecodeToInt64(gconv.Bytes(fieldValue)), nil
case LocalTypeUint64Bytes:
return gbinary.BeDecodeToUint64(gconv.Bytes(fieldValue)), nil
case LocalTypeFloat32:
return gconv.Float32(gconv.String(fieldValue)), nil
case LocalTypeFloat64:
return gconv.Float64(gconv.String(fieldValue)), nil
case LocalTypeBool:
s := gconv.String(fieldValue)
// mssql is true|false string.
if strings.EqualFold(s, "true") {
return 1, nil
}
if strings.EqualFold(s, "false") {
return 0, nil
}
return gconv.Bool(fieldValue), nil
case LocalTypeDate:
// Date without time.
if t, ok := fieldValue.(time.Time); ok {
return gtime.NewFromTime(t).Format("Y-m-d"), nil
}
t, _ := gtime.StrToTime(gconv.String(fieldValue))
return t.Format("Y-m-d"), nil
case LocalTypeDatetime:
if t, ok := fieldValue.(time.Time); ok {
return gtime.NewFromTime(t), nil
}
t, _ := gtime.StrToTime(gconv.String(fieldValue))
return t, nil
default:
return gconv.String(fieldValue), nil
}
}
// mappingAndFilterData automatically mappings the map key to table field and removes
// all key-value pairs that are not the field of given table.
func (c *Core) mappingAndFilterData(ctx context.Context, schema, table string, data map[string]interface{}, filter bool) (map[string]interface{}, error) {

View File

@ -376,7 +376,11 @@ func (c *Core) RowsToResult(ctx context.Context, rows *sql.Rows) (Result, error)
if value == nil {
record[columnNames[i]] = gvar.New(nil)
} else {
record[columnNames[i]] = gvar.New(c.convertFieldValueToLocalValue(value, columnTypes[i]))
var convertedValue interface{}
if convertedValue, err = c.db.ConvertValueForLocal(ctx, columnTypes[i], value); err != nil {
return nil, err
}
record[columnNames[i]] = gvar.New(convertedValue)
}
}
result = append(result, record)

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