Compare commits

..

69 Commits

Author SHA1 Message Date
Kevin Wan
f7f3730e1a update goctl version to 1.1.10 (#874) 2021-08-04 19:29:40 +08:00
Kevin Wan
0ee7654407 fix #792 (#873) 2021-08-04 18:45:05 +08:00
neosu
16cc990fdd fix context missing (#872)
Co-authored-by: suzhenpeng <suzhenpeng@ecoplants.tech>
2021-08-04 17:46:51 +08:00
neosu
00061c2e5b add goctl rpc template home flag (#871)
Co-authored-by: suzhenpeng <suzhenpeng@ecoplants.tech>
2021-08-04 15:54:43 +08:00
Kevin Wan
6793f7a1de fix bug that proc.SetTimeToForceQuit not working in windows (#869) 2021-08-04 11:31:33 +08:00
anqiansong
c8428a7f65 fix issue #861 (#862)
* fix issue #861

* fix issue #861

Co-authored-by: anqiansong <anqiansong@xiaoheiban.cn>
2021-08-01 23:00:57 +08:00
toutou_o
a5e1d0d0dc add correct example for pg's url (#857) 2021-07-30 13:58:44 +08:00
anqiansong
8270c7deed optimize typo (#855) 2021-07-29 21:53:16 +08:00
anqiansong
9f4a882a1b fix issue #831 (#850)
* fix issue #831

* fix typo

Co-authored-by: anqiansong <anqiansong@xiaoheiban.cn>
2021-07-28 16:32:15 +08:00
anqiansong
cb7b7cb72e fix issue #836 (#849)
Co-authored-by: anqiansong <anqiansong@xiaoheiban.cn>
2021-07-27 22:13:17 +08:00
Kevin Wan
603c93aa4a upgrade grpc package (#845) 2021-07-24 22:29:02 +08:00
masonchen2014
cb8d9d413a simplify timeoutinterceptor (#840)
Co-authored-by: chenmusheng <chenmusheng@laoyuegou.com>
2021-07-24 21:51:46 +08:00
Kevin Wan
ff7443c6a7 fix #796 (#844) 2021-07-24 12:58:14 +08:00
fangjianwei
b812e74d6f Fixed http listener error. (#843) 2021-07-24 12:57:56 +08:00
anqiansong
089cdaa75f Feature model postgresql (#842)
* Support postgresql generate

* Update template Var

* Support to generate postgresql model

* Support to generate postgresql model

* Update template

Co-authored-by: anqiansong <anqiansong@xiaoheiban.cn>
2021-07-23 11:45:15 +08:00
fangjianwei
476026e393 Added database prefix of cache key. (#835) 2021-07-22 11:29:09 +08:00
Kevin Wan
75952308f9 remove faq for old versions (#828) 2021-07-19 22:54:21 +08:00
Kevin Wan
df0550d6dc add go-zero users, update faq (#827) 2021-07-19 17:13:33 +08:00
neosu
e481b63b21 Fix the error stream method name (#826) 2021-07-18 22:05:28 +08:00
Kevin Wan
e47079f0f4 go format with extra rules (#821) 2021-07-17 20:51:23 +08:00
anqiansong
9b2a279948 Fix issues: #725, #740 (#813)
* Fix issues: #725, #740

* Update filed sort

Co-authored-by: anqiansong <anqiansong@xiaoheiban.cn>
2021-07-16 22:55:39 +08:00
anqiansong
db87fd3239 To generate grpc stream, fix issue #616 (#815)
Co-authored-by: anqiansong <anqiansong@xiaoheiban.cn>
2021-07-16 22:54:07 +08:00
aaffo
598fda0c97 optimized (#819) 2021-07-15 23:50:44 +08:00
Chen Quan
b0e335e7b0 Fix rpc generator bug (#799)
* Fix rpc自动生成generate bug

* Delete mock
2021-07-10 13:12:52 +08:00
anqiansong
efdf475da4 Add --go_opt flag to adapt to the version after 1.4.0 of protoc-gen-go (#767)
Co-authored-by: anqiansong <anqiansong@xiaoheiban.cn>
2021-07-08 10:11:11 +08:00
Chen Quan
22a1315136 [WIP]Add parse headers info (#805)
* Add parse headers info

* Update parse headers info
2021-07-07 23:20:09 +08:00
Kevin Wan
5b22823018 fix bug that empty query in transaction (#801) 2021-06-29 23:18:32 +08:00
Kevin Wan
9ccb997ed8 refactor mapping (#782) 2021-06-23 14:57:37 +08:00
skykiss
01c92a6bc5 fix: Fix problems with non support for multidimensional arrays and basic type pointer arrays (#778)
Co-authored-by: shaoqian <shaoqian.zhang@appshahe.com>
2021-06-23 10:58:01 +08:00
lucaq
c9a2a60e28 Add Sinter,Sinterstore & Modify TestRedis_Set (#779)
* Add Sinter,Sinterstore; Modify TestRedis_Set

* Update redis_test.go

fix test failure

Co-authored-by: lucq <lucq@toopsoon.com>
Co-authored-by: Kevin Wan <wanjunfeng@gmail.com>
2021-06-23 10:46:16 +08:00
Kevin Wan
b0739d63c0 update readme images (#776) 2021-06-21 16:45:44 +08:00
Kevin Wan
c22f84cb5f update image rendering in readme (#775) 2021-06-21 16:34:14 +08:00
Kevin Wan
60450bab02 disable load & stat logs for goctl (#773) 2021-06-21 14:25:33 +08:00
Kevin Wan
3e8cec5c78 upgrade grpc & etcd dependencies (#771) 2021-06-21 09:05:20 +08:00
Kevin Wan
74ee163761 fix bug that etcd stream cancelled without re-watch (#770) 2021-06-17 18:46:16 +08:00
anqiansong
ea4f680052 Fix issue #747 (#765)
Co-authored-by: anqiansong <anqiansong@xiaoheiban.cn>
2021-06-15 18:54:41 +08:00
heyanfu
58cdba2c5d remove useless annotation (#761) 2021-06-14 16:00:47 +08:00
Kevin Wan
a2fbc14c70 add roadmap (#764) 2021-06-13 11:59:30 +08:00
Kevin Wan
158df8c270 fix broken link (#763) 2021-06-13 11:35:19 +08:00
Kevin Wan
30ec236a87 add contributing guid (#762) 2021-06-13 11:33:29 +08:00
Kevin Wan
ac3653b3f9 add code of conduct (#760) 2021-06-12 22:17:00 +08:00
Kevin Wan
8520db4fd9 refactor fx (#759)
* refactor fx

* refactor fx, format code
2021-06-10 19:57:36 +08:00
Chen Quan
14141fed62 Add some stream features (#712)
* Add some stream features

* Update empty

* Fix initialization loop

* Delete ForeachOrdered && Fix FindFirst

* Add test case && Delete redundant code

* Update test case

* Delete SplitSteam

* Delete redundant code
2021-06-10 18:20:40 +08:00
Kevin Wan
5d86cc2f20 add go-zero users (#756) 2021-06-07 14:08:54 +08:00
Kevin Wan
8a6e4b7580 add go-zero users (#751) 2021-06-03 15:07:21 +08:00
anqiansong
453f949638 replace cache key with colon (#746)
Co-authored-by: anqiansong <anqiansong@xiaoheiban.cn>
2021-06-02 10:37:49 +08:00
Kevin Wan
75a330184d add go-zero users (#739) 2021-05-29 23:58:43 +08:00
kingxt
546fcd8bab fix #736 (#738)
* optimize performance

* rename

* rename

* revert
2021-05-29 23:01:02 +08:00
Xavier Cheng
3022f93b6d Fix a typo (#729)
alread -> already
2021-05-28 23:39:07 +08:00
Kevin Wan
8ffc392c66 add go-zero users, update slack invite link (#728) 2021-05-28 14:32:47 +08:00
Kevin Wan
ae7d85dadf add go-zero users (#726) 2021-05-28 10:50:33 +08:00
Kevin Wan
e89268ac37 add go-zero users. (#723)
* add go-zero users

* add go-zero users
2021-05-27 22:53:55 +08:00
Kevin Wan
aaa3623404 optimize nested conditional (#709) 2021-05-22 23:18:38 +08:00
heyanfu
8998f16054 optimize nested conditional (#708)
Co-authored-by: heyanfu <heyanfu@kingsoft.com>
2021-05-22 22:56:06 +08:00
anqiansong
94417be018 Add document & comment for spec (#703)
* Add document & comment for spec

* remove duplicate field

* use alias
2021-05-21 10:40:59 +08:00
Kevin Wan
f300408fc0 fix golint issues, and optimize code (#705) 2021-05-21 10:38:38 +08:00
Kevin Wan
aaa39e17a3 print entire sql statements in logx if necessary (#704) 2021-05-20 16:14:44 +08:00
Bo-Yi Wu
73906f996d chore(format): change by gofumpt tool (#697)
Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
2021-05-18 14:43:09 +08:00
Kevin Wan
73417f54db update goctl version to 1.1.8 (#696) 2021-05-17 15:13:48 +08:00
Kevin Wan
491213afb8 fix #683 (#690)
* fix #683

* fix errors
2021-05-15 15:37:24 +08:00
Julian-Chu
edf743cd72 fix invalid link (#689) 2021-05-15 15:37:10 +08:00
Kevin Wan
78a88be787 add go-zero users (#688) 2021-05-14 22:52:19 +08:00
anqiansong
9f6a574f97 resolve #610 (#684) 2021-05-13 18:42:05 +08:00
anqiansong
ea01cc78f0 Optimize model nl (#686) 2021-05-12 12:28:23 +08:00
Kevin Wan
a87978568a fix #676 (#682) 2021-05-10 23:10:57 +08:00
Kevin Wan
14cecb9b31 update readme for documents links (#681) 2021-05-10 22:22:32 +08:00
_ksco
0ce54100a4 fix zh_cn document url (#678) 2021-05-10 22:18:33 +08:00
anqiansong
d28ac35ff7 fix issue: https://github.com/zeromicro/goctl-swagger/issues/6 (#680) 2021-05-10 19:57:12 +08:00
heyanfu
a5962f677f fix some typo (#677) 2021-05-10 00:09:00 +08:00
134 changed files with 2630 additions and 1114 deletions

102
CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,102 @@
# Contributing
Welcome to go-zero!
- [Before you get started](#before-you-get-started)
- [Code of Conduct](#code-of-conduct)
- [Community Expectations](#community-expectations)
- [Getting started](#getting-started)
- [Your First Contribution](#your-first-contribution)
- [Find something to work on](#find-something-to-work-on)
- [Find a good first topic](#find-a-good-first-topic)
- [Work on an Issue](#work-on-an-issue)
- [File an Issue](#file-an-issue)
- [Contributor Workflow](#contributor-workflow)
- [Creating Pull Requests](#creating-pull-requests)
- [Code Review](#code-review)
- [Testing](#testing)
# Before you get started
## Code of Conduct
Please make sure to read and observe our [Code of Conduct](/code-of-conduct.md).
## Community Expectations
go-zero is a community project driven by its community which strives to promote a healthy, friendly and productive environment.
go-zero is a web and rpc framework written in Go. It's born to ensure the stability of the busy sites with resilient design. Builtin goctl greatly improves the development productivity.
# Getting started
- Fork the repository on GitHub.
- Make your changes on your fork repository.
- Submit a PR.
# Your First Contribution
We will help you to contribute in different areas like filing issues, developing features, fixing critical bugs and
getting your work reviewed and merged.
If you have questions about the development process,
feel free to [file an issue](https://github.com/tal-tech/go-zero/issues/new/choose).
## Find something to work on
We are always in need of help, be it fixing documentation, reporting bugs or writing some code.
Look at places where you feel best coding practices aren't followed, code refactoring is needed or tests are missing.
Here is how you get started.
### Find a good first topic
[go-zero](https://github.com/tal-tech/go-zero) has beginner-friendly issues that provide a good first issue.
For example, [go-zero](https://github.com/tal-tech/go-zero) has
[help wanted](https://github.com/tal-tech/go-zero/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22) and
[good first issue](https://github.com/tal-tech/go-zero/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22)
labels for issues that should not need deep knowledge of the system.
We can help new contributors who wish to work on such issues.
Another good way to contribute is to find a documentation improvement, such as a missing/broken link.
Please see [Contributing](#contributing) below for the workflow.
#### Work on an issue
When you are willing to take on an issue, just reply on the issue. The maintainer will assign it to you.
### File an Issue
While we encourage everyone to contribute code, it is also appreciated when someone reports an issue.
Please follow the prompted submission guidelines while opening an issue.
# Contributor Workflow
Please do not ever hesitate to ask a question or send a pull request.
This is a rough outline of what a contributor's workflow looks like:
- Create a topic branch from where to base the contribution. This is usually master.
- Make commits of logical units.
- Push changes in a topic branch to a personal fork of the repository.
- Submit a pull request to [go-zero](https://github.com/tal-tech/go-zero).
## Creating Pull Requests
Pull requests are often called simply "PR".
go-zero generally follows the standard [github pull request](https://help.github.com/articles/about-pull-requests/) process.
To submit a proposed change, please develop the code/fix and add new test cases.
After that, run these local verifications before submitting pull request to predict the pass or
fail of continuous integration.
* Format the code with `gofmt`
* Run the test with data race enabled `go test -race ./…`
## Code Review
To make it easier for your PR to receive reviews, consider the reviewers will need you to:
* follow [good coding guidelines](https://github.com/golang/go/wiki/CodeReviewComments).
* write [good commit messages](https://chris.beams.io/posts/git-commit/).
* break large changes into a logical series of smaller patches which individually make easily understandable changes, and in aggregate solve a broader issue.

21
ROADMAP.md Normal file
View File

@@ -0,0 +1,21 @@
# go-zero Roadmap
This document defines a high level roadmap for go-zero development and upcoming releases.
Community and contributor involvement is vital for successfully implementing all desired items for each release.
We hope that the items listed below will inspire further engagement from the community to keep go-zero progressing and shipping exciting and valuable features.
## 2021 Q2
- Support TLS in redis connections
- Support service discovery through K8S watch api
- Log full sql statements for easier sql problem solving
## 2021 Q3
- Support `goctl mock` command to start a mocking server with given `.api` file
- Adapt builtin tracing mechanism to opentracing solutions
- Support `goctl model pg` to support PostgreSQL code generation
## 2021 Q4
- Support `goctl doctor` command to report potential issues for given service
- Support `context` in redis related methods for timeout and tracing
- Support `context` in sql related methods for timeout and tracing
- Support `context` in mongodb related methods for timeout and tracing

76
code-of-conduct.md Normal file
View File

@@ -0,0 +1,76 @@
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to make participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, sex characteristics, gender identity and expression,
level of experience, education, socio-economic status, nationality, personal
appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies within all project spaces, and it also applies when
an individual is representing the project or its community in public spaces.
Examples of representing a project or community include using an official
project e-mail address, posting via an official social media account, or acting
as an appointed representative at an online or offline event. Representation of
a project may be further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at [INSERT EMAIL ADDRESS]. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see
https://www.contributor-covenant.org/faq

View File

@@ -94,7 +94,7 @@ func (b *googleBreaker) markFailure() {
b.stat.Add(0)
}
func (b *googleBreaker) history() (accepts int64, total int64) {
func (b *googleBreaker) history() (accepts, total int64) {
b.stat.Reduce(func(b *collection.Bucket) {
accepts += int64(b.Sum)
total += b.Count

View File

@@ -106,9 +106,7 @@ func (s *Set) KeysInt() []int {
var keys []int
for key := range s.data {
if intKey, ok := key.(int); !ok {
continue
} else {
if intKey, ok := key.(int); ok {
keys = append(keys, intKey)
}
}
@@ -121,9 +119,7 @@ func (s *Set) KeysInt64() []int64 {
var keys []int64
for key := range s.data {
if intKey, ok := key.(int64); !ok {
continue
} else {
if intKey, ok := key.(int64); ok {
keys = append(keys, intKey)
}
}
@@ -136,9 +132,7 @@ func (s *Set) KeysUint() []uint {
var keys []uint
for key := range s.data {
if intKey, ok := key.(uint); !ok {
continue
} else {
if intKey, ok := key.(uint); ok {
keys = append(keys, intKey)
}
}
@@ -151,9 +145,7 @@ func (s *Set) KeysUint64() []uint64 {
var keys []uint64
for key := range s.data {
if intKey, ok := key.(uint64); !ok {
continue
} else {
if intKey, ok := key.(uint64); ok {
keys = append(keys, intKey)
}
}
@@ -166,9 +158,7 @@ func (s *Set) KeysStr() []string {
var keys []string
for key := range s.data {
if strKey, ok := key.(string); !ok {
continue
} else {
if strKey, ok := key.(string); ok {
keys = append(keys, strKey)
}
}

View File

@@ -151,7 +151,7 @@ func (tw *TimingWheel) drainAll(fn func(key, value interface{})) {
}
}
func (tw *TimingWheel) getPositionAndCircle(d time.Duration) (pos int, circle int) {
func (tw *TimingWheel) getPositionAndCircle(d time.Duration) (pos, circle int) {
steps := int(d / tw.interval)
pos = (tw.tickedPos + steps) % tw.numSlots
circle = (steps - 1) / tw.numSlots

View File

@@ -5,7 +5,7 @@ package internal
import (
"context"
"go.etcd.io/etcd/clientv3"
clientv3 "go.etcd.io/etcd/client/v3"
"google.golang.org/grpc"
)

View File

@@ -6,10 +6,11 @@ package internal
import (
context "context"
gomock "github.com/golang/mock/gomock"
clientv3 "go.etcd.io/etcd/clientv3"
grpc "google.golang.org/grpc"
reflect "reflect"
gomock "github.com/golang/mock/gomock"
clientv3 "go.etcd.io/etcd/client/v3"
grpc "google.golang.org/grpc"
)
// MockEtcdClient is a mock of EtcdClient interface

View File

@@ -2,5 +2,5 @@ package internal
// Listener interface wraps the OnUpdate method.
type Listener interface {
OnUpdate(keys []string, values []string, newKey string)
OnUpdate(keys, values []string, newKey string)
}

View File

@@ -14,7 +14,7 @@ import (
"github.com/tal-tech/go-zero/core/logx"
"github.com/tal-tech/go-zero/core/syncx"
"github.com/tal-tech/go-zero/core/threading"
"go.etcd.io/etcd/clientv3"
clientv3 "go.etcd.io/etcd/client/v3"
)
var (
@@ -260,26 +260,34 @@ func (c *cluster) reload(cli EtcdClient) {
}
func (c *cluster) watch(cli EtcdClient, key string) {
for {
if c.watchStream(cli, key) {
return
}
}
}
func (c *cluster) watchStream(cli EtcdClient, key string) bool {
rch := cli.Watch(clientv3.WithRequireLeader(c.context(cli)), makeKeyPrefix(key), clientv3.WithPrefix())
for {
select {
case wresp, ok := <-rch:
if !ok {
logx.Error("etcd monitor chan has been closed")
return
return false
}
if wresp.Canceled {
logx.Error("etcd monitor chan has been canceled")
return
logx.Errorf("etcd monitor chan has been canceled, error: %v", wresp.Err())
return false
}
if wresp.Err() != nil {
logx.Error(fmt.Sprintf("etcd monitor chan error: %v", wresp.Err()))
return
return false
}
c.handleWatchEvents(key, wresp.Events)
case <-c.done:
return
return true
}
}
}

View File

@@ -8,10 +8,11 @@ import (
"github.com/golang/mock/gomock"
"github.com/stretchr/testify/assert"
"github.com/tal-tech/go-zero/core/contextx"
"github.com/tal-tech/go-zero/core/lang"
"github.com/tal-tech/go-zero/core/logx"
"github.com/tal-tech/go-zero/core/stringx"
"go.etcd.io/etcd/clientv3"
"go.etcd.io/etcd/mvcc/mvccpb"
"go.etcd.io/etcd/api/v3/mvccpb"
clientv3 "go.etcd.io/etcd/client/v3"
)
var mockLock sync.Mutex
@@ -202,11 +203,13 @@ func TestClusterWatch_RespFailures(t *testing.T) {
restore := setMockClient(cli)
defer restore()
ch := make(chan clientv3.WatchResponse)
cli.EXPECT().Watch(gomock.Any(), "any/", gomock.Any()).Return(ch)
cli.EXPECT().Watch(gomock.Any(), "any/", gomock.Any()).Return(ch).AnyTimes()
cli.EXPECT().Ctx().Return(context.Background()).AnyTimes()
c := new(cluster)
c.done = make(chan lang.PlaceholderType)
go func() {
ch <- resp
close(c.done)
}()
c.watch(cli, "any")
})
@@ -220,11 +223,13 @@ func TestClusterWatch_CloseChan(t *testing.T) {
restore := setMockClient(cli)
defer restore()
ch := make(chan clientv3.WatchResponse)
cli.EXPECT().Watch(gomock.Any(), "any/", gomock.Any()).Return(ch)
cli.EXPECT().Watch(gomock.Any(), "any/", gomock.Any()).Return(ch).AnyTimes()
cli.EXPECT().Ctx().Return(context.Background()).AnyTimes()
c := new(cluster)
c.done = make(chan lang.PlaceholderType)
go func() {
close(ch)
close(c.done)
}()
c.watch(cli, "any")
}

View File

@@ -7,7 +7,7 @@ import (
"github.com/tal-tech/go-zero/core/proc"
"github.com/tal-tech/go-zero/core/syncx"
"github.com/tal-tech/go-zero/core/threading"
"go.etcd.io/etcd/clientv3"
clientv3 "go.etcd.io/etcd/client/v3"
)
type (

View File

@@ -11,7 +11,7 @@ import (
"github.com/tal-tech/go-zero/core/discov/internal"
"github.com/tal-tech/go-zero/core/lang"
"github.com/tal-tech/go-zero/core/logx"
"go.etcd.io/etcd/clientv3"
clientv3 "go.etcd.io/etcd/client/v3"
)
func init() {

View File

@@ -49,6 +49,11 @@ type (
}
)
// Concat returns a concatenated Stream.
func Concat(s Stream, others ...Stream) Stream {
return s.Concat(others...)
}
// From constructs a Stream from the given GenerateFunc.
func From(generate GenerateFunc) Stream {
source := make(chan interface{})
@@ -79,16 +84,42 @@ func Range(source <-chan interface{}) Stream {
}
}
// AllMach returns whether all elements of this stream match the provided predicate.
// May not evaluate the predicate on all elements if not necessary for determining the result.
// If the stream is empty then true is returned and the predicate is not evaluated.
func (s Stream) AllMach(predicate func(item interface{}) bool) bool {
for item := range s.source {
if !predicate(item) {
return false
}
}
return true
}
// AnyMach returns whether any elements of this stream match the provided predicate.
// May not evaluate the predicate on all elements if not necessary for determining the result.
// If the stream is empty then false is returned and the predicate is not evaluated.
func (s Stream) AnyMach(predicate func(item interface{}) bool) bool {
for item := range s.source {
if predicate(item) {
return true
}
}
return false
}
// Buffer buffers the items into a queue with size n.
// It can balance the producer and the consumer if their processing throughput don't match.
func (p Stream) Buffer(n int) Stream {
func (s Stream) Buffer(n int) Stream {
if n < 0 {
n = 0
}
source := make(chan interface{}, n)
go func() {
for item := range p.source {
for item := range s.source {
source <- item
}
close(source)
@@ -97,23 +128,51 @@ func (p Stream) Buffer(n int) Stream {
return Range(source)
}
// Concat returns a Stream that concatenated other streams
func (s Stream) Concat(others ...Stream) Stream {
source := make(chan interface{})
go func() {
group := threading.NewRoutineGroup()
group.Run(func() {
for item := range s.source {
source <- item
}
})
for _, each := range others {
each := each
group.Run(func() {
for item := range each.source {
source <- item
}
})
}
group.Wait()
close(source)
}()
return Range(source)
}
// Count counts the number of elements in the result.
func (p Stream) Count() (count int) {
for range p.source {
func (s Stream) Count() (count int) {
for range s.source {
count++
}
return
}
// Distinct removes the duplicated items base on the given KeyFunc.
func (p Stream) Distinct(fn KeyFunc) Stream {
func (s Stream) Distinct(fn KeyFunc) Stream {
source := make(chan interface{})
threading.GoSafe(func() {
defer close(source)
keys := make(map[interface{}]lang.PlaceholderType)
for item := range p.source {
for item := range s.source {
key := fn(item)
if _, ok := keys[key]; !ok {
source <- item
@@ -126,14 +185,14 @@ func (p Stream) Distinct(fn KeyFunc) Stream {
}
// Done waits all upstreaming operations to be done.
func (p Stream) Done() {
for range p.source {
func (s Stream) Done() {
for range s.source {
}
}
// Filter filters the items by the given FilterFunc.
func (p Stream) Filter(fn FilterFunc, opts ...Option) Stream {
return p.Walk(func(item interface{}, pipe chan<- interface{}) {
func (s Stream) Filter(fn FilterFunc, opts ...Option) Stream {
return s.Walk(func(item interface{}, pipe chan<- interface{}) {
if fn(item) {
pipe <- item
}
@@ -141,21 +200,21 @@ func (p Stream) Filter(fn FilterFunc, opts ...Option) Stream {
}
// ForAll handles the streaming elements from the source and no later streams.
func (p Stream) ForAll(fn ForAllFunc) {
fn(p.source)
func (s Stream) ForAll(fn ForAllFunc) {
fn(s.source)
}
// ForEach seals the Stream with the ForEachFunc on each item, no successive operations.
func (p Stream) ForEach(fn ForEachFunc) {
for item := range p.source {
func (s Stream) ForEach(fn ForEachFunc) {
for item := range s.source {
fn(item)
}
}
// Group groups the elements into different groups based on their keys.
func (p Stream) Group(fn KeyFunc) Stream {
func (s Stream) Group(fn KeyFunc) Stream {
groups := make(map[interface{}][]interface{})
for item := range p.source {
for item := range s.source {
key := fn(item)
groups[key] = append(groups[key], item)
}
@@ -172,7 +231,7 @@ func (p Stream) Group(fn KeyFunc) Stream {
}
// Head returns the first n elements in p.
func (p Stream) Head(n int64) Stream {
func (s Stream) Head(n int64) Stream {
if n < 1 {
panic("n must be greater than 0")
}
@@ -180,7 +239,7 @@ func (p Stream) Head(n int64) Stream {
source := make(chan interface{})
go func() {
for item := range p.source {
for item := range s.source {
n--
if n >= 0 {
source <- item
@@ -201,16 +260,16 @@ func (p Stream) Head(n int64) Stream {
}
// Map converts each item to another corresponding item, which means it's a 1:1 model.
func (p Stream) Map(fn MapFunc, opts ...Option) Stream {
return p.Walk(func(item interface{}, pipe chan<- interface{}) {
func (s Stream) Map(fn MapFunc, opts ...Option) Stream {
return s.Walk(func(item interface{}, pipe chan<- interface{}) {
pipe <- fn(item)
}, opts...)
}
// Merge merges all the items into a slice and generates a new stream.
func (p Stream) Merge() Stream {
func (s Stream) Merge() Stream {
var items []interface{}
for item := range p.source {
for item := range s.source {
items = append(items, item)
}
@@ -222,21 +281,21 @@ func (p Stream) Merge() Stream {
}
// Parallel applies the given ParallelFunc to each item concurrently with given number of workers.
func (p Stream) Parallel(fn ParallelFunc, opts ...Option) {
p.Walk(func(item interface{}, pipe chan<- interface{}) {
func (s Stream) Parallel(fn ParallelFunc, opts ...Option) {
s.Walk(func(item interface{}, pipe chan<- interface{}) {
fn(item)
}, opts...).Done()
}
// Reduce is a utility method to let the caller deal with the underlying channel.
func (p Stream) Reduce(fn ReduceFunc) (interface{}, error) {
return fn(p.source)
func (s Stream) Reduce(fn ReduceFunc) (interface{}, error) {
return fn(s.source)
}
// Reverse reverses the elements in the stream.
func (p Stream) Reverse() Stream {
func (s Stream) Reverse() Stream {
var items []interface{}
for item := range p.source {
for item := range s.source {
items = append(items, item)
}
// reverse, official method
@@ -248,10 +307,36 @@ func (p Stream) Reverse() Stream {
return Just(items...)
}
// Skip returns a Stream that skips size elements.
func (s Stream) Skip(n int64) Stream {
if n < 0 {
panic("n must not be negative")
}
if n == 0 {
return s
}
source := make(chan interface{})
go func() {
for item := range s.source {
n--
if n >= 0 {
continue
} else {
source <- item
}
}
close(source)
}()
return Range(source)
}
// Sort sorts the items from the underlying source.
func (p Stream) Sort(less LessFunc) Stream {
func (s Stream) Sort(less LessFunc) Stream {
var items []interface{}
for item := range p.source {
for item := range s.source {
items = append(items, item)
}
sort.Slice(items, func(i, j int) bool {
@@ -263,7 +348,7 @@ func (p Stream) Sort(less LessFunc) Stream {
// Split splits the elements into chunk with size up to n,
// might be less than n on tailing elements.
func (p Stream) Split(n int) Stream {
func (s Stream) Split(n int) Stream {
if n < 1 {
panic("n should be greater than 0")
}
@@ -271,7 +356,7 @@ func (p Stream) Split(n int) Stream {
source := make(chan interface{})
go func() {
var chunk []interface{}
for item := range p.source {
for item := range s.source {
chunk = append(chunk, item)
if len(chunk) == n {
source <- chunk
@@ -288,7 +373,7 @@ func (p Stream) Split(n int) Stream {
}
// Tail returns the last n elements in p.
func (p Stream) Tail(n int64) Stream {
func (s Stream) Tail(n int64) Stream {
if n < 1 {
panic("n should be greater than 0")
}
@@ -297,7 +382,7 @@ func (p Stream) Tail(n int64) Stream {
go func() {
ring := collection.NewRing(int(n))
for item := range p.source {
for item := range s.source {
ring.Add(item)
}
for _, item := range ring.Take() {
@@ -310,16 +395,16 @@ func (p Stream) Tail(n int64) Stream {
}
// Walk lets the callers handle each item, the caller may write zero, one or more items base on the given item.
func (p Stream) Walk(fn WalkFunc, opts ...Option) Stream {
func (s Stream) Walk(fn WalkFunc, opts ...Option) Stream {
option := buildOptions(opts...)
if option.unlimitedWorkers {
return p.walkUnlimited(fn, option)
return s.walkUnlimited(fn, option)
}
return p.walkLimited(fn, option)
return s.walkLimited(fn, option)
}
func (p Stream) walkLimited(fn WalkFunc, option *rxOptions) Stream {
func (s Stream) walkLimited(fn WalkFunc, option *rxOptions) Stream {
pipe := make(chan interface{}, option.workers)
go func() {
@@ -328,7 +413,7 @@ func (p Stream) walkLimited(fn WalkFunc, option *rxOptions) Stream {
for {
pool <- lang.Placeholder
item, ok := <-p.source
item, ok := <-s.source
if !ok {
<-pool
break
@@ -353,14 +438,14 @@ func (p Stream) walkLimited(fn WalkFunc, option *rxOptions) Stream {
return Range(pipe)
}
func (p Stream) walkUnlimited(fn WalkFunc, option *rxOptions) Stream {
func (s Stream) walkUnlimited(fn WalkFunc, option *rxOptions) Stream {
pipe := make(chan interface{}, defaultWorkers)
go func() {
var wg sync.WaitGroup
for {
item, ok := <-p.source
item, ok := <-s.source
if !ok {
break
}

View File

@@ -3,7 +3,10 @@ package fx
import (
"io/ioutil"
"log"
"math/rand"
"reflect"
"runtime"
"sort"
"sync"
"sync/atomic"
"testing"
@@ -330,6 +333,29 @@ func TestWalk(t *testing.T) {
assert.Equal(t, 9, result)
}
func BenchmarkParallelMapReduce(b *testing.B) {
b.ReportAllocs()
mapper := func(v interface{}) interface{} {
return v.(int64) * v.(int64)
}
reducer := func(input <-chan interface{}) (interface{}, error) {
var result int64
for v := range input {
result += v.(int64)
}
return result, nil
}
b.ResetTimer()
From(func(input chan<- interface{}) {
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
input <- int64(rand.Int())
}
})
}).Map(mapper).Reduce(reducer)
}
func BenchmarkMapReduce(b *testing.B) {
b.ReportAllocs()
@@ -343,12 +369,103 @@ func BenchmarkMapReduce(b *testing.B) {
}
return result, nil
}
b.ResetTimer()
From(func(input chan<- interface{}) {
for i := 0; i < b.N; i++ {
input <- int64(rand.Int())
}
}).Map(mapper).Reduce(reducer)
}
for i := 0; i < b.N; i++ {
From(func(input chan<- interface{}) {
for j := 0; j < 2; j++ {
input <- int64(j)
}
}).Map(mapper).Reduce(reducer)
func equal(t *testing.T, stream Stream, data []interface{}) {
items := make([]interface{}, 0)
for item := range stream.source {
items = append(items, item)
}
if !reflect.DeepEqual(items, data) {
t.Errorf(" %v, want %v", items, data)
}
}
func assetEqual(t *testing.T, except, data interface{}) {
if !reflect.DeepEqual(except, data) {
t.Errorf(" %v, want %v", data, except)
}
}
func TestStream_AnyMach(t *testing.T) {
assetEqual(t, false, Just(1, 2, 3).AnyMach(func(item interface{}) bool {
return 4 == item.(int)
}))
assetEqual(t, false, Just(1, 2, 3).AnyMach(func(item interface{}) bool {
return 0 == item.(int)
}))
assetEqual(t, true, Just(1, 2, 3).AnyMach(func(item interface{}) bool {
return 2 == item.(int)
}))
assetEqual(t, true, Just(1, 2, 3).AnyMach(func(item interface{}) bool {
return 2 == item.(int)
}))
}
func TestStream_AllMach(t *testing.T) {
assetEqual(
t, true, Just(1, 2, 3).AllMach(func(item interface{}) bool {
return true
}),
)
assetEqual(
t, false, Just(1, 2, 3).AllMach(func(item interface{}) bool {
return false
}),
)
assetEqual(
t, false, Just(1, 2, 3).AllMach(func(item interface{}) bool {
return item.(int) == 1
}),
)
}
func TestConcat(t *testing.T) {
a1 := []interface{}{1, 2, 3}
a2 := []interface{}{4, 5, 6}
s1 := Just(a1...)
s2 := Just(a2...)
stream := Concat(s1, s2)
var items []interface{}
for item := range stream.source {
items = append(items, item)
}
sort.Slice(items, func(i, j int) bool {
return items[i].(int) < items[j].(int)
})
ints := make([]interface{}, 0)
ints = append(ints, a1...)
ints = append(ints, a2...)
assetEqual(t, ints, items)
}
func TestStream_Skip(t *testing.T) {
assetEqual(t, 3, Just(1, 2, 3, 4).Skip(1).Count())
assetEqual(t, 1, Just(1, 2, 3, 4).Skip(3).Count())
assetEqual(t, 4, Just(1, 2, 3, 4).Skip(0).Count())
equal(t, Just(1, 2, 3, 4).Skip(3), []interface{}{4})
assert.Panics(t, func() {
Just(1, 2, 3, 4).Skip(-1)
})
}
func TestStream_Concat(t *testing.T) {
stream := Just(1).Concat(Just(2), Just(3))
var items []interface{}
for item := range stream.source {
items = append(items, item)
}
sort.Slice(items, func(i, j int) bool {
return items[i].(int) < items[j].(int)
})
assetEqual(t, []interface{}{1, 2, 3}, items)
just := Just(1)
equal(t, just.Concat(just), []interface{}{1})
}

View File

@@ -83,7 +83,7 @@ func (h *ConsistentHash) AddWithReplicas(node interface{}, replicas int) {
h.ring[hash] = append(h.ring[hash], node)
}
sort.Slice(h.keys, func(i int, j int) bool {
sort.Slice(h.keys, func(i, j int) bool {
return h.keys[i] < h.keys[j]
})
}

View File

@@ -112,5 +112,5 @@ func (t mockTrace) Follow(ctx context.Context, serviceName, operationName string
return nil, nil
}
func (t mockTrace) Visit(fn func(key string, val string) bool) {
func (t mockTrace) Visit(fn func(key, val string) bool) {
}

View File

@@ -457,6 +457,10 @@ func (u *Unmarshaler) fillSlice(fieldType reflect.Type, value reflect.Value, map
} else {
conv.Index(i).Set(target.Elem())
}
case reflect.Slice:
if err := u.fillSlice(dereffedBaseType, conv.Index(i), ithValue); err != nil {
return err
}
default:
if err := u.fillSliceValue(conv, i, dereffedBaseKind, ithValue); err != nil {
return err
@@ -492,18 +496,31 @@ func (u *Unmarshaler) fillSliceFromString(fieldType reflect.Type, value reflect.
}
func (u *Unmarshaler) fillSliceValue(slice reflect.Value, index int, baseKind reflect.Kind, value interface{}) error {
ithVal := slice.Index(index)
switch v := value.(type) {
case json.Number:
return setValue(baseKind, slice.Index(index), v.String())
return setValue(baseKind, ithVal, v.String())
default:
// don't need to consider the difference between int, int8, int16, int32, int64,
// uint, uint8, uint16, uint32, uint64, because they're handled as json.Number.
if slice.Index(index).Kind() != reflect.TypeOf(value).Kind() {
return errTypeMismatch
}
if ithVal.Kind() == reflect.Ptr {
baseType := Deref(ithVal.Type())
if baseType.Kind() != reflect.TypeOf(value).Kind() {
return errTypeMismatch
}
slice.Index(index).Set(reflect.ValueOf(value))
return nil
target := reflect.New(baseType).Elem()
target.Set(reflect.ValueOf(value))
ithVal.Set(target.Addr())
return nil
} else {
if ithVal.Kind() != reflect.TypeOf(value).Kind() {
return errTypeMismatch
}
ithVal.Set(reflect.ValueOf(value))
return nil
}
}
}

View File

@@ -3,6 +3,7 @@ package mapping
import (
"encoding/json"
"strconv"
"strings"
"testing"
"time"
@@ -2480,3 +2481,40 @@ func BenchmarkUnmarshal(b *testing.B) {
UnmarshalKey(data, &an)
}
}
func TestUnmarshalJsonReaderMultiArray(t *testing.T) {
payload := `{"a": "133", "b": [["add", "cccd"], ["eeee"]]}`
var res struct {
A string `json:"a"`
B [][]string `json:"b"`
}
reader := strings.NewReader(payload)
err := UnmarshalJsonReader(reader, &res)
assert.Nil(t, err)
assert.Equal(t, 2, len(res.B))
}
func TestUnmarshalJsonReaderPtrMultiArray(t *testing.T) {
payload := `{"a": "133", "b": [["add", "cccd"], ["eeee"]]}`
var res struct {
A string `json:"a"`
B [][]*string `json:"b"`
}
reader := strings.NewReader(payload)
err := UnmarshalJsonReader(reader, &res)
assert.Nil(t, err)
assert.Equal(t, 2, len(res.B))
assert.Equal(t, 2, len(res.B[0]))
}
func TestUnmarshalJsonReaderPtrArray(t *testing.T) {
payload := `{"a": "133", "b": ["add", "cccd", "eeee"]}`
var res struct {
A string `json:"a"`
B []*string `json:"b"`
}
reader := strings.NewReader(payload)
err := UnmarshalJsonReader(reader, &res)
assert.Nil(t, err)
assert.Equal(t, 3, len(res.B))
}

View File

@@ -286,7 +286,7 @@ func parseNumberRange(str string) (*numberRange, error) {
}, nil
}
func parseOption(fieldOpts *fieldOptions, fieldName string, option string) error {
func parseOption(fieldOpts *fieldOptions, fieldName, option string) error {
switch {
case option == stringOption:
fieldOpts.FromString = true

View File

@@ -136,14 +136,16 @@ func MapReduceWithSource(source <-chan interface{}, mapper MapperFunc, reducer R
go func() {
defer func() {
drain(collector)
if r := recover(); r != nil {
cancel(fmt.Errorf("%v", r))
} else {
finish()
}
}()
reducer(collector, writer, cancel)
drain(collector)
}()
go executeMappers(func(item interface{}, w Writer) {
@@ -165,7 +167,6 @@ func MapReduceWithSource(source <-chan interface{}, mapper MapperFunc, reducer R
func MapReduceVoid(generate GenerateFunc, mapper MapperFunc, reducer VoidReducerFunc, opts ...Option) error {
_, err := MapReduce(generate, mapper, func(input <-chan interface{}, writer Writer, cancel func(error)) {
reducer(input, cancel)
drain(input)
// We need to write a placeholder to let MapReduce to continue on reducer done,
// otherwise, all goroutines are waiting. The placeholder will be discarded by MapReduce.
writer.Write(lang.Placeholder)

View File

@@ -14,5 +14,5 @@ func AddWrapUpListener(fn func()) func() {
return fn
}
func SetTimeoutToForceQuit(duration time.Duration) {
func SetTimeToForceQuit(duration time.Duration) {
}

View File

@@ -71,7 +71,7 @@ func NewQueue(producerFactory ProducerFactory, consumerFactory ConsumerFactory)
return q
}
// AddListener adds a litener to q.
// AddListener adds a listener to q.
func (q *Queue) AddListener(listener Listener) {
q.listeners = append(q.listeners, listener)
}

View File

@@ -1,6 +1,9 @@
package search
import "errors"
import (
"errors"
"fmt"
)
const (
colon = ':'
@@ -8,16 +11,16 @@ const (
)
var (
// ErrDupItem means adding duplicated item.
ErrDupItem = errors.New("duplicated item")
// ErrDupSlash means item is started with more than one slash.
ErrDupSlash = errors.New("duplicated slash")
// ErrEmptyItem means adding empty item.
ErrEmptyItem = errors.New("empty item")
// ErrInvalidState means search tree is in an invalid state.
ErrInvalidState = errors.New("search tree is in an invalid state")
// ErrNotFromRoot means path is not starting with slash.
ErrNotFromRoot = errors.New("path should start with /")
// errDupItem means adding duplicated item.
errDupItem = errors.New("duplicated item")
// errDupSlash means item is started with more than one slash.
errDupSlash = errors.New("duplicated slash")
// errEmptyItem means adding empty item.
errEmptyItem = errors.New("empty item")
// errInvalidState means search tree is in an invalid state.
errInvalidState = errors.New("search tree is in an invalid state")
// errNotFromRoot means path is not starting with slash.
errNotFromRoot = errors.New("path should start with /")
// NotFound is used to hold the not found result.
NotFound Result
@@ -58,14 +61,22 @@ func NewTree() *Tree {
// Add adds item to associate with route.
func (t *Tree) Add(route string, item interface{}) error {
if len(route) == 0 || route[0] != slash {
return ErrNotFromRoot
return errNotFromRoot
}
if item == nil {
return ErrEmptyItem
return errEmptyItem
}
return add(t.root, route[1:], item)
err := add(t.root, route[1:], item)
switch err {
case errDupItem:
return duplicatedItem(route)
case errDupSlash:
return duplicatedSlash(route)
default:
return err
}
}
// Search searches item that associates with given route.
@@ -86,22 +97,22 @@ func (t *Tree) next(n *node, route string, result *Result) bool {
}
for i := range route {
if route[i] == slash {
token := route[:i]
return n.forEach(func(k string, v *node) bool {
if r := match(k, token); r.found {
if t.next(v, route[i+1:], result) {
if r.named {
addParam(result, r.key, r.value)
}
return true
}
}
return false
})
if route[i] != slash {
continue
}
token := route[:i]
return n.forEach(func(k string, v *node) bool {
r := match(k, token)
if !r.found || !t.next(v, route[i+1:], result) {
return false
}
if r.named {
addParam(result, r.key, r.value)
}
return true
})
}
return n.forEach(func(k string, v *node) bool {
@@ -141,7 +152,7 @@ func (nd *node) getChildren(route string) map[string]*node {
func add(nd *node, route string, item interface{}) error {
if len(route) == 0 {
if nd.item != nil {
return ErrDupItem
return errDupItem
}
nd.item = item
@@ -149,31 +160,33 @@ func add(nd *node, route string, item interface{}) error {
}
if route[0] == slash {
return ErrDupSlash
return errDupSlash
}
for i := range route {
if route[i] == slash {
token := route[:i]
children := nd.getChildren(token)
if child, ok := children[token]; ok {
if child != nil {
return add(child, route[i+1:], item)
}
if route[i] != slash {
continue
}
return ErrInvalidState
token := route[:i]
children := nd.getChildren(token)
if child, ok := children[token]; ok {
if child != nil {
return add(child, route[i+1:], item)
}
child := newNode(nil)
children[token] = child
return add(child, route[i+1:], item)
return errInvalidState
}
child := newNode(nil)
children[token] = child
return add(child, route[i+1:], item)
}
children := nd.getChildren(route)
if child, ok := children[route]; ok {
if child.item != nil {
return ErrDupItem
return errDupItem
}
child.item = item
@@ -192,6 +205,14 @@ func addParam(result *Result, k, v string) {
result.Params[k] = v
}
func duplicatedItem(item string) error {
return fmt.Errorf("duplicated item for %s", item)
}
func duplicatedSlash(item string) error {
return fmt.Errorf("duplicated slash for %s", item)
}
func match(pat, token string) innerResult {
if pat[0] == colon {
return innerResult{

View File

@@ -151,9 +151,9 @@ func TestAddDuplicate(t *testing.T) {
err := tree.Add("/a/b", 1)
assert.Nil(t, err)
err = tree.Add("/a/b", 2)
assert.Equal(t, ErrDupItem, err)
assert.Error(t, errDupItem, err)
err = tree.Add("/a/b/", 2)
assert.Equal(t, ErrDupItem, err)
assert.Error(t, errDupItem, err)
}
func TestPlain(t *testing.T) {
@@ -169,19 +169,19 @@ func TestPlain(t *testing.T) {
func TestSearchWithDoubleSlashes(t *testing.T) {
tree := NewTree()
err := tree.Add("//a", 1)
assert.Error(t, ErrDupSlash, err)
assert.Error(t, errDupSlash, err)
}
func TestSearchInvalidRoute(t *testing.T) {
tree := NewTree()
err := tree.Add("", 1)
assert.Equal(t, ErrNotFromRoot, err)
assert.Equal(t, errNotFromRoot, err)
err = tree.Add("bad", 1)
assert.Equal(t, ErrNotFromRoot, err)
assert.Equal(t, errNotFromRoot, err)
}
func TestSearchInvalidItem(t *testing.T) {
tree := NewTree()
err := tree.Add("/", nil)
assert.Equal(t, ErrEmptyItem, err)
assert.Equal(t, errEmptyItem, err)
}

View File

@@ -22,7 +22,7 @@ var (
cores uint64
)
// if /proc not present, ignore the cpu calcuation, like wsl linux
// if /proc not present, ignore the cpu calculation, like wsl linux
func init() {
cpus, err := perCpuUsage()
if err != nil {

View File

@@ -205,7 +205,7 @@ func (c cacheNode) doTake(v interface{}, key string, query func(v interface{}) e
return jsonx.Unmarshal(val.([]byte), v)
}
func (c cacheNode) processCache(key string, data string, v interface{}) error {
func (c cacheNode) processCache(key, data string, v interface{}) error {
err := jsonx.Unmarshal([]byte(data), v)
if err == nil {
return nil

View File

@@ -17,7 +17,7 @@ type (
// Store interface represents a KV store.
Store interface {
Del(keys ...string) (int, error)
Eval(script string, key string, args ...interface{}) (interface{}, error)
Eval(script, key string, args ...interface{}) (interface{}, error)
Exists(key string) (bool, error)
Expire(key string, seconds int) error
Expireat(key string, expireTime int64) error
@@ -39,7 +39,7 @@ type (
Llen(key string) (int, error)
Lpop(key string) (string, error)
Lpush(key string, values ...interface{}) (int, error)
Lrange(key string, start int, stop int) ([]string, error)
Lrange(key string, start, stop int) ([]string, error)
Lrem(key string, count int, value string) (int, error)
Persist(key string) (bool, error)
Pfadd(key string, values ...interface{}) (bool, error)
@@ -47,7 +47,7 @@ type (
Rpush(key string, values ...interface{}) (int, error)
Sadd(key string, values ...interface{}) (int, error)
Scard(key string) (int64, error)
Set(key string, value string) error
Set(key, value string) error
Setex(key, value string, seconds int) error
Setnx(key, value string) (bool, error)
SetnxEx(key, value string, seconds int) (bool, error)
@@ -74,7 +74,7 @@ type (
Zrevrange(key string, start, stop int64) ([]string, error)
ZrevrangebyscoreWithScores(key string, start, stop int64) ([]redis.Pair, error)
ZrevrangebyscoreWithScoresAndLimit(key string, start, stop int64, page, size int) ([]redis.Pair, error)
Zscore(key string, value string) (int64, error)
Zscore(key, value string) (int64, error)
Zrevrank(key, field string) (int64, error)
}
@@ -123,7 +123,7 @@ func (cs clusterStore) Del(keys ...string) (int, error) {
return val, be.Err()
}
func (cs clusterStore) Eval(script string, key string, args ...interface{}) (interface{}, error) {
func (cs clusterStore) Eval(script, key string, args ...interface{}) (interface{}, error) {
node, err := cs.getRedis(key)
if err != nil {
return nil, err
@@ -321,7 +321,7 @@ func (cs clusterStore) Lpush(key string, values ...interface{}) (int, error) {
return node.Lpush(key, values...)
}
func (cs clusterStore) Lrange(key string, start int, stop int) ([]string, error) {
func (cs clusterStore) Lrange(key string, start, stop int) ([]string, error) {
node, err := cs.getRedis(key)
if err != nil {
return nil, err
@@ -393,7 +393,7 @@ func (cs clusterStore) Scard(key string) (int64, error) {
return node.Scard(key)
}
func (cs clusterStore) Set(key string, value string) error {
func (cs clusterStore) Set(key, value string) error {
node, err := cs.getRedis(key)
if err != nil {
return err
@@ -648,7 +648,7 @@ func (cs clusterStore) Zrevrank(key, field string) (int64, error) {
return node.Zrevrank(key, field)
}
func (cs clusterStore) Zscore(key string, value string) (int64, error) {
func (cs clusterStore) Zscore(key, value string) (int64, error) {
node, err := cs.getRedis(key)
if err != nil {
return 0, err

View File

@@ -24,11 +24,11 @@ type (
CachedCollection interface {
Count(query interface{}) (int, error)
DelCache(keys ...string) error
FindAllNoCache(v interface{}, query interface{}, opts ...QueryOption) error
FindAllNoCache(v, query interface{}, opts ...QueryOption) error
FindOne(v interface{}, key string, query interface{}) error
FindOneNoCache(v interface{}, query interface{}) error
FindOneNoCache(v, query interface{}) error
FindOneId(v interface{}, key string, id interface{}) error
FindOneIdNoCache(v interface{}, id interface{}) error
FindOneIdNoCache(v, id interface{}) error
GetCache(key string, v interface{}) error
Insert(docs ...interface{}) error
Pipe(pipeline interface{}) mongo.Pipe
@@ -68,7 +68,7 @@ func (c *cachedCollection) DelCache(keys ...string) error {
return c.cache.Del(keys...)
}
func (c *cachedCollection) FindAllNoCache(v interface{}, query interface{}, opts ...QueryOption) error {
func (c *cachedCollection) FindAllNoCache(v, query interface{}, opts ...QueryOption) error {
q := c.collection.Find(query)
for _, opt := range opts {
q = opt(q)
@@ -83,7 +83,7 @@ func (c *cachedCollection) FindOne(v interface{}, key string, query interface{})
})
}
func (c *cachedCollection) FindOneNoCache(v interface{}, query interface{}) error {
func (c *cachedCollection) FindOneNoCache(v, query interface{}) error {
q := c.collection.Find(query)
return q.One(v)
}
@@ -95,7 +95,7 @@ func (c *cachedCollection) FindOneId(v interface{}, key string, id interface{})
})
}
func (c *cachedCollection) FindOneIdNoCache(v interface{}, id interface{}) error {
func (c *cachedCollection) FindOneIdNoCache(v, id interface{}) error {
q := c.collection.FindId(id)
return q.One(v)
}

View File

@@ -75,7 +75,7 @@ func (mm *Model) GetCollection(session *mgo.Session) CachedCollection {
}
// FindAllNoCache finds all records without cache.
func (mm *Model) FindAllNoCache(v interface{}, query interface{}, opts ...QueryOption) error {
func (mm *Model) FindAllNoCache(v, query interface{}, opts ...QueryOption) error {
return mm.execute(func(c CachedCollection) error {
return c.FindAllNoCache(v, query, opts...)
})
@@ -89,7 +89,7 @@ func (mm *Model) FindOne(v interface{}, key string, query interface{}) error {
}
// FindOneNoCache unmarshals a record into v with query, without cache.
func (mm *Model) FindOneNoCache(v interface{}, query interface{}) error {
func (mm *Model) FindOneNoCache(v, query interface{}) error {
return mm.execute(func(c CachedCollection) error {
return c.FindOneNoCache(v, query)
})
@@ -103,7 +103,7 @@ func (mm *Model) FindOneId(v interface{}, key string, id interface{}) error {
}
// FindOneIdNoCache unmarshals a record into v with query, without cache.
func (mm *Model) FindOneIdNoCache(v interface{}, id interface{}) error {
func (mm *Model) FindOneIdNoCache(v, id interface{}) error {
return mm.execute(func(c CachedCollection) error {
return c.FindOneIdNoCache(v, id)
})

View File

@@ -180,7 +180,7 @@ func (s *Redis) BitOpXor(destKey string, keys ...string) (val int64, err error)
}
// BitPos is redis bitpos command implementation.
func (s *Redis) BitPos(key string, bit int64, start, end int64) (val int64, err error) {
func (s *Redis) BitPos(key string, bit, start, end int64) (val int64, err error) {
err = s.brk.DoWithAcceptable(func() error {
conn, err := getRedis(s)
if err != nil {
@@ -346,7 +346,7 @@ func (s *Redis) GeoAdd(key string, geoLocation ...*GeoLocation) (val int64, err
}
// GeoDist is the implementation of redis geodist command.
func (s *Redis) GeoDist(key string, member1, member2, unit string) (val float64, err error) {
func (s *Redis) GeoDist(key, member1, member2, unit string) (val float64, err error) {
err = s.brk.DoWithAcceptable(func() error {
conn, err := getRedis(s)
if err != nil {
@@ -795,7 +795,7 @@ func (s *Redis) Lpush(key string, values ...interface{}) (val int, err error) {
}
// Lrange is the implementation of redis lrange command.
func (s *Redis) Lrange(key string, start int, stop int) (val []string, err error) {
func (s *Redis) Lrange(key string, start, stop int) (val []string, err error) {
err = s.brk.DoWithAcceptable(func() error {
conn, err := getRedis(s)
if err != nil {
@@ -1074,7 +1074,7 @@ func (s *Redis) ScriptLoad(script string) (string, error) {
}
// Set is the implementation of redis set command.
func (s *Redis) Set(key string, value string) error {
func (s *Redis) Set(key, value string) error {
return s.brk.DoWithAcceptable(func() error {
conn, err := getRedis(s)
if err != nil {
@@ -1282,6 +1282,41 @@ func (s *Redis) Sdiffstore(destination string, keys ...string) (val int, err err
return
}
// Sinter is the implementation of redis sinter command.
func (s *Redis) Sinter(keys ...string) (val []string, err error) {
err = s.brk.DoWithAcceptable(func() error {
conn, err := getRedis(s)
if err != nil {
return err
}
val, err = conn.SInter(keys...).Result()
return err
}, acceptable)
return
}
// Sinterstore is the implementation of redis sinterstore command.
func (s *Redis) Sinterstore(destination string, keys ...string) (val int, err error) {
err = s.brk.DoWithAcceptable(func() error {
conn, err := getRedis(s)
if err != nil {
return err
}
v, err := conn.SInterStore(destination, keys...).Result()
if err != nil {
return err
}
val = int(v)
return nil
}, acceptable)
return
}
// Ttl is the implementation of redis ttl command.
func (s *Redis) Ttl(key string) (val int, err error) {
err = s.brk.DoWithAcceptable(func() error {
@@ -1412,7 +1447,7 @@ func (s *Redis) Zincrby(key string, increment int64, field string) (val int64, e
}
// Zscore is the implementation of redis zscore command.
func (s *Redis) Zscore(key string, value string) (val int64, err error) {
func (s *Redis) Zscore(key, value string) (val int64, err error) {
err = s.brk.DoWithAcceptable(func() error {
conn, err := getRedis(s)
if err != nil {
@@ -1684,7 +1719,7 @@ func (s *Redis) ZrevrangebyscoreWithScoresAndLimit(key string, start, stop int64
}
// Zrevrank is the implementation of redis zrevrank command.
func (s *Redis) Zrevrank(key string, field string) (val int64, err error) {
func (s *Redis) Zrevrank(key, field string) (val int64, err error) {
err = s.brk.DoWithAcceptable(func() error {
conn, err := getRedis(s)
if err != nil {

View File

@@ -638,6 +638,16 @@ func TestRedis_Set(t *testing.T) {
num, err = client.Sdiffstore("key4", "key1", "key2")
assert.Nil(t, err)
assert.Equal(t, 1, num)
_, err = New(client.Addr, badType()).Sinter("key1", "key2")
assert.NotNil(t, err)
vals, err = client.Sinter("key1", "key2")
assert.Nil(t, err)
assert.ElementsMatch(t, []string{"2", "3", "4"}, vals)
_, err = New(client.Addr, badType()).Sinterstore("key4", "key1", "key2")
assert.NotNil(t, err)
num, err = client.Sinterstore("key4", "key1", "key2")
assert.Nil(t, err)
assert.Equal(t, 3, num)
})
}

View File

@@ -56,7 +56,8 @@ type (
}
statement struct {
stmt *sql.Stmt
query string
stmt *sql.Stmt
}
stmtConn interface {
@@ -111,7 +112,8 @@ func (db *commonSqlConn) Prepare(query string) (stmt StmtSession, err error) {
}
stmt = statement{
stmt: st,
query: query,
stmt: st,
}
return nil
}, db.acceptable)
@@ -181,29 +183,29 @@ func (s statement) Close() error {
}
func (s statement) Exec(args ...interface{}) (sql.Result, error) {
return execStmt(s.stmt, args...)
return execStmt(s.stmt, s.query, args...)
}
func (s statement) QueryRow(v interface{}, args ...interface{}) error {
return queryStmt(s.stmt, func(rows *sql.Rows) error {
return unmarshalRow(v, rows, true)
}, args...)
}, s.query, args...)
}
func (s statement) QueryRowPartial(v interface{}, args ...interface{}) error {
return queryStmt(s.stmt, func(rows *sql.Rows) error {
return unmarshalRow(v, rows, false)
}, args...)
}, s.query, args...)
}
func (s statement) QueryRows(v interface{}, args ...interface{}) error {
return queryStmt(s.stmt, func(rows *sql.Rows) error {
return unmarshalRows(v, rows, true)
}, args...)
}, s.query, args...)
}
func (s statement) QueryRowsPartial(v interface{}, args ...interface{}) error {
return queryStmt(s.stmt, func(rows *sql.Rows) error {
return unmarshalRows(v, rows, false)
}, args...)
}, s.query, args...)
}

View File

@@ -2,7 +2,6 @@ package sqlx
import (
"database/sql"
"fmt"
"time"
"github.com/tal-tech/go-zero/core/logx"
@@ -12,10 +11,14 @@ import (
const slowThreshold = time.Millisecond * 500
func exec(conn sessionConn, q string, args ...interface{}) (sql.Result, error) {
stmt, err := format(q, args...)
if err != nil {
return nil, err
}
startTime := timex.Now()
result, err := conn.Exec(q, args...)
duration := timex.Since(startTime)
stmt := formatForPrint(q, args)
if duration > slowThreshold {
logx.WithDuration(duration).Slowf("[SQL] exec: slowcall - %s", stmt)
} else {
@@ -28,11 +31,15 @@ func exec(conn sessionConn, q string, args ...interface{}) (sql.Result, error) {
return result, err
}
func execStmt(conn stmtConn, args ...interface{}) (sql.Result, error) {
func execStmt(conn stmtConn, q string, args ...interface{}) (sql.Result, error) {
stmt, err := format(q, args...)
if err != nil {
return nil, err
}
startTime := timex.Now()
result, err := conn.Exec(args...)
duration := timex.Since(startTime)
stmt := fmt.Sprint(args...)
if duration > slowThreshold {
logx.WithDuration(duration).Slowf("[SQL] execStmt: slowcall - %s", stmt)
} else {
@@ -46,10 +53,14 @@ func execStmt(conn stmtConn, args ...interface{}) (sql.Result, error) {
}
func query(conn sessionConn, scanner func(*sql.Rows) error, q string, args ...interface{}) error {
stmt, err := format(q, args...)
if err != nil {
return err
}
startTime := timex.Now()
rows, err := conn.Query(q, args...)
duration := timex.Since(startTime)
stmt := fmt.Sprint(args...)
if duration > slowThreshold {
logx.WithDuration(duration).Slowf("[SQL] query: slowcall - %s", stmt)
} else {
@@ -64,8 +75,12 @@ func query(conn sessionConn, scanner func(*sql.Rows) error, q string, args ...in
return scanner(rows)
}
func queryStmt(conn stmtConn, scanner func(*sql.Rows) error, args ...interface{}) error {
stmt := fmt.Sprint(args...)
func queryStmt(conn stmtConn, scanner func(*sql.Rows) error, q string, args ...interface{}) error {
stmt, err := format(q, args...)
if err != nil {
return err
}
startTime := timex.Now()
rows, err := conn.Query(args...)
duration := timex.Since(startTime)

View File

@@ -14,6 +14,7 @@ var errMockedPlaceholder = errors.New("placeholder")
func TestStmt_exec(t *testing.T) {
tests := []struct {
name string
query string
args []interface{}
delay bool
hasError bool
@@ -23,18 +24,28 @@ func TestStmt_exec(t *testing.T) {
}{
{
name: "normal",
query: "select user from users where id=?",
args: []interface{}{1},
lastInsertId: 1,
rowsAffected: 2,
},
{
name: "exec error",
query: "select user from users where id=?",
args: []interface{}{1},
hasError: true,
err: errors.New("exec"),
},
{
name: "exec more args error",
query: "select user from users where id=? and name=?",
args: []interface{}{1},
hasError: true,
err: errors.New("exec"),
},
{
name: "slowcall",
query: "select user from users where id=?",
args: []interface{}{1},
delay: true,
lastInsertId: 1,
@@ -51,7 +62,7 @@ func TestStmt_exec(t *testing.T) {
rowsAffected: test.rowsAffected,
err: test.err,
delay: test.delay,
}, "select user from users where id=?", args...)
}, test.query, args...)
},
func(args ...interface{}) (sql.Result, error) {
return execStmt(&mockedStmtConn{
@@ -59,7 +70,7 @@ func TestStmt_exec(t *testing.T) {
rowsAffected: test.rowsAffected,
err: test.err,
delay: test.delay,
}, args...)
}, test.query, args...)
},
}
@@ -89,23 +100,34 @@ func TestStmt_exec(t *testing.T) {
func TestStmt_query(t *testing.T) {
tests := []struct {
name string
query string
args []interface{}
delay bool
hasError bool
err error
}{
{
name: "normal",
args: []interface{}{1},
name: "normal",
query: "select user from users where id=?",
args: []interface{}{1},
},
{
name: "query error",
query: "select user from users where id=?",
args: []interface{}{1},
hasError: true,
err: errors.New("exec"),
},
{
name: "query more args error",
query: "select user from users where id=? and name=?",
args: []interface{}{1},
hasError: true,
err: errors.New("exec"),
},
{
name: "slowcall",
query: "select user from users where id=?",
args: []interface{}{1},
delay: true,
},
@@ -120,7 +142,7 @@ func TestStmt_query(t *testing.T) {
delay: test.delay,
}, func(rows *sql.Rows) error {
return nil
}, "select user from users where id=?", args...)
}, test.query, args...)
},
func(args ...interface{}) error {
return queryStmt(&mockedStmtConn{
@@ -128,7 +150,7 @@ func TestStmt_query(t *testing.T) {
delay: test.delay,
}, func(rows *sql.Rows) error {
return nil
}, args...)
}, test.query, args...)
},
}
@@ -143,7 +165,7 @@ func TestStmt_query(t *testing.T) {
return
}
assert.Equal(t, errMockedPlaceholder, err)
assert.NotNil(t, err)
})
}
}

View File

@@ -30,7 +30,8 @@ func (t txSession) Prepare(q string) (StmtSession, error) {
}
return statement{
stmt: stmt,
query: q,
stmt: stmt,
}, nil
}

View File

@@ -2,6 +2,7 @@ package sqlx
import (
"fmt"
"strconv"
"strings"
"github.com/tal-tech/go-zero/core/logx"
@@ -45,24 +46,6 @@ func escape(input string) string {
return b.String()
}
func formatForPrint(query string, args ...interface{}) string {
if len(args) == 0 {
return query
}
var vals []string
for _, arg := range args {
vals = append(vals, fmt.Sprintf("%q", mapping.Repr(arg)))
}
var b strings.Builder
b.WriteByte('[')
b.WriteString(strings.Join(vals, ", "))
b.WriteByte(']')
return strings.Join([]string{query, b.String()}, " ")
}
func format(query string, args ...interface{}) (string, error) {
numArgs := len(args)
if numArgs == 0 {
@@ -70,38 +53,52 @@ func format(query string, args ...interface{}) (string, error) {
}
var b strings.Builder
argIndex := 0
var argIndex int
bytes := len(query)
for _, ch := range query {
if ch == '?' {
for i := 0; i < bytes; i++ {
ch := query[i]
switch ch {
case '?':
if argIndex >= numArgs {
return "", fmt.Errorf("error: %d ? in sql, but less arguments provided", argIndex)
}
arg := args[argIndex]
writeValue(&b, args[argIndex])
argIndex++
switch v := arg.(type) {
case bool:
if v {
b.WriteByte('1')
} else {
b.WriteByte('0')
case '$':
var j int
for j = i + 1; j < bytes; j++ {
char := query[j]
if char < '0' || '9' < char {
break
}
case string:
b.WriteByte('\'')
b.WriteString(escape(v))
b.WriteByte('\'')
default:
b.WriteString(mapping.Repr(v))
}
} else {
b.WriteRune(ch)
if j > i+1 {
index, err := strconv.Atoi(query[i+1 : j])
if err != nil {
return "", err
}
// index starts from 1 for pg
if index > argIndex {
argIndex = index
}
index--
if index < 0 || numArgs <= index {
return "", fmt.Errorf("error: wrong index %d in sql", index)
}
writeValue(&b, args[index])
i = j - 1
}
default:
b.WriteByte(ch)
}
}
if argIndex < numArgs {
return "", fmt.Errorf("error: %d ? in sql, but more arguments provided", argIndex)
return "", fmt.Errorf("error: %d arguments provided, not matching sql", argIndex)
}
return b.String(), nil
@@ -117,3 +114,20 @@ func logSqlError(stmt string, err error) {
logx.Errorf("stmt: %s, error: %s", stmt, err.Error())
}
}
func writeValue(buf *strings.Builder, arg interface{}) {
switch v := arg.(type) {
case bool:
if v {
buf.WriteByte('1')
} else {
buf.WriteByte('0')
}
case string:
buf.WriteByte('\'')
buf.WriteString(escape(v))
buf.WriteByte('\'')
default:
buf.WriteString(mapping.Repr(v))
}
}

View File

@@ -29,30 +29,63 @@ func TestDesensitize_WithoutAccount(t *testing.T) {
assert.True(t, strings.Contains(datasource, "tcp(111.222.333.44:3306)"))
}
func TestFormatForPrint(t *testing.T) {
func TestFormat(t *testing.T) {
tests := []struct {
name string
query string
args []interface{}
expect string
hasErr bool
}{
{
name: "no args",
query: "select user, name from table where id=?",
expect: `select user, name from table where id=?`,
name: "mysql normal",
query: "select name, age from users where bool=? and phone=?",
args: []interface{}{true, "133"},
expect: "select name, age from users where bool=1 and phone='133'",
},
{
name: "one arg",
query: "select user, name from table where id=?",
args: []interface{}{"kevin"},
expect: `select user, name from table where id=? ["kevin"]`,
name: "mysql normal",
query: "select name, age from users where bool=? and phone=?",
args: []interface{}{false, "133"},
expect: "select name, age from users where bool=0 and phone='133'",
},
{
name: "pg normal",
query: "select name, age from users where bool=$1 and phone=$2",
args: []interface{}{true, "133"},
expect: "select name, age from users where bool=1 and phone='133'",
},
{
name: "pg normal reverse",
query: "select name, age from users where bool=$2 and phone=$1",
args: []interface{}{"133", false},
expect: "select name, age from users where bool=0 and phone='133'",
},
{
name: "pg error not number",
query: "select name, age from users where bool=$a and phone=$1",
args: []interface{}{"133", false},
hasErr: true,
},
{
name: "pg error more args",
query: "select name, age from users where bool=$2 and phone=$1 and nickname=$3",
args: []interface{}{"133", false},
hasErr: true,
},
}
for _, test := range tests {
test := test
t.Run(test.name, func(t *testing.T) {
actual := formatForPrint(test.query, test.args...)
assert.Equal(t, test.expect, actual)
t.Parallel()
actual, err := format(test.query, test.args...)
if test.hasErr {
assert.NotNil(t, err)
} else {
assert.Equal(t, test.expect, actual)
}
})
}
}

View File

@@ -86,7 +86,7 @@ func Reverse(s string) string {
}
// Substr returns runes between start and stop [start, stop) regardless of the chars are ascii or utf8.
func Substr(str string, start int, stop int) (string, error) {
func Substr(str string, start, stop int) (string, error) {
rs := []rune(str)
length := len(rs)

View File

@@ -18,7 +18,7 @@ func NewManagedResource(generate func() interface{}, equals func(a, b interface{
}
}
// MarkBroken marks the resouce broken.
// MarkBroken marks the resource broken.
func (mr *ManagedResource) MarkBroken(resource interface{}) {
mr.lock.Lock()
defer mr.lock.Unlock()

View File

@@ -2,7 +2,7 @@ package syncx
import "sync"
// Once returns a func that guanartees fn can only called once.
// Once returns a func that guarantees fn can only called once.
func Once(fn func()) func() {
once := new(sync.Once)
return func() {

View File

@@ -2,7 +2,7 @@ package syncx
import "sync/atomic"
// A OnceGuard is used to make sure a resouce can be taken once.
// A OnceGuard is used to make sure a resource can be taken once.
type OnceGuard struct {
done uint32
}

View File

@@ -19,7 +19,7 @@ type (
// A Pool is used to pool resources.
// The difference between sync.Pool is that:
// 1. the limit of the resouces
// 1. the limit of the resources
// 2. max age of the resources can be set
// 3. the method to destroy resources can be customized
Pool struct {
@@ -56,7 +56,7 @@ func NewPool(n int, create func() interface{}, destroy func(interface{}), opts .
return pool
}
// Get gets a resouce.
// Get gets a resource.
func (p *Pool) Get() interface{} {
p.lock.Lock()
defer p.lock.Unlock()

View File

@@ -8,7 +8,7 @@ import (
// ErrUseOfCleaned is an error that indicates using a cleaned resource.
var ErrUseOfCleaned = errors.New("using a cleaned resource")
// A RefResource is used to reference counting a resouce.
// A RefResource is used to reference counting a resource.
type RefResource struct {
lock sync.Mutex
ref int32

42
go.mod
View File

@@ -16,48 +16,36 @@ require (
github.com/go-redis/redis v6.15.7+incompatible
github.com/go-sql-driver/mysql v1.5.0
github.com/go-xorm/builder v0.3.4
github.com/gogo/protobuf v1.3.1 // indirect
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect
github.com/golang/mock v1.4.3
github.com/golang/protobuf v1.4.2
github.com/google/uuid v1.1.1
github.com/gorilla/websocket v1.4.2 // indirect
github.com/grpc-ecosystem/grpc-gateway v1.14.3 // indirect
github.com/golang/protobuf v1.5.2
github.com/google/uuid v1.1.2
github.com/iancoleman/strcase v0.1.2
github.com/justinas/alice v1.2.0
github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect
github.com/kr/pretty v0.2.0 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/lib/pq v1.3.0
github.com/logrusorgru/aurora v2.0.3+incompatible
github.com/mattn/go-colorable v0.1.6 // indirect
github.com/mattn/go-runewidth v0.0.9 // indirect
github.com/olekukonko/tablewriter v0.0.4
github.com/olekukonko/tablewriter v0.0.5
github.com/onsi/ginkgo v1.7.0 // indirect
github.com/onsi/gomega v1.5.0 // indirect
github.com/pierrec/lz4 v2.5.1+incompatible // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/prometheus/client_golang v1.5.1
github.com/prometheus/client_golang v1.11.0
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/spaolacci/murmur3 v1.1.0
github.com/stretchr/testify v1.5.1
github.com/tmc/grpc-websocket-proxy v0.0.0-20171017195756-830351dc03c6 // indirect
github.com/stretchr/testify v1.7.0
github.com/urfave/cli v1.22.5
github.com/xwb1989/sqlparser v0.0.0-20180606152119-120387863bf2
github.com/zeromicro/antlr v0.0.1 // indirect
go.etcd.io/etcd v0.0.0-20200402134248-51bdeb39e698
github.com/zeromicro/antlr v0.0.1
github.com/zeromicro/ddl-parser v0.0.0-20210712021150-63520aca7348 // indirect
go.etcd.io/etcd/api/v3 v3.5.0
go.etcd.io/etcd/client/v3 v3.5.0
go.uber.org/automaxprocs v1.3.0
golang.org/x/lint v0.0.0-20200302205851-738671d3881b // indirect
golang.org/x/net v0.0.0-20200707034311-ab3426394381
golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1 // indirect
golang.org/x/text v0.3.3 // indirect
golang.org/x/time v0.0.0-20191024005414-555d28b269f0
golang.org/x/tools v0.0.0-20200410132612-ae9902aceb98 // indirect
google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f // indirect
google.golang.org/grpc v1.29.1
google.golang.org/protobuf v1.25.0 // indirect
golang.org/x/net v0.0.0-20210716203947-853a461950ff
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c // indirect
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba
google.golang.org/genproto v0.0.0-20210722135532-667f2b7c528f // indirect
google.golang.org/grpc v1.39.0
gopkg.in/cheggaaa/pb.v1 v1.0.28
gopkg.in/h2non/gock.v1 v1.0.15
gopkg.in/yaml.v2 v2.4.0
honnef.co/go/tools v0.0.1-2020.1.4 // indirect
sigs.k8s.io/yaml v1.2.0 // indirect
)

308
go.sum
View File

@@ -1,30 +1,28 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/ClickHouse/clickhouse-go v1.4.3 h1:iAFMa2UrQdR5bHJ2/yaSLffZkxpcOYQMCUuKeNXGdqc=
github.com/ClickHouse/clickhouse-go v1.4.3/go.mod h1:EaI/sW7Azgz9UATzd5ZdZHRUhHgv5+JMS9NSr2smCJI=
github.com/DATA-DOG/go-sqlmock v1.4.1 h1:ThlnYciV1iM/V0OSF/dtkqWb6xo5qITT1TJBG1MRDJM=
github.com/DATA-DOG/go-sqlmock v1.4.1/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a h1:HbKu58rmZpUGpz5+4FfNmIU+FmZg2P3Xaj2v2bfNWmk=
github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc=
github.com/alicebob/miniredis/v2 v2.14.1 h1:GjlbSeoJ24bzdLRs13HoMEeaRZx9kg5nHoRW7QV/nCs=
github.com/alicebob/miniredis/v2 v2.14.1/go.mod h1:uS970Sw5Gs9/iK3yBg0l9Uj9s25wXxSpQUE9EaJ/Blg=
github.com/antihax/optional v0.0.0-20180407024304-ca021399b1a6/go.mod h1:V8iCPQYkqmusNa815XgQio277wI47sdRh1dUOLdyC6Q=
github.com/antlr/antlr4 v0.0.0-20210105212045-464bcbc32de2 h1:rL2miklL5rhxUaZO7hntBcy/VHaiyuPQ4EJoy/NMwaM=
github.com/antlr/antlr4 v0.0.0-20210105212045-464bcbc32de2/go.mod h1:T7PbCXFs94rrTttyxjbyT5+/1V8T2TYDejxUfHJjw1Y=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/antlr/antlr4/runtime/Go/antlr v0.0.0-20210521184019-c5ad59b459ec h1:EEyRvzmpEUZ+I8WmD5cw/vY8EqhambkOqy5iFr0908A=
github.com/antlr/antlr4/runtime/Go/antlr v0.0.0-20210521184019-c5ad59b459ec/go.mod h1:F7bn7fEU90QkQ3tnmaTx3LTKLEDqnwWODIYppRQ5hnY=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/bkaradzic/go-lz4 v1.0.0 h1:RXc4wYsyz985CkXXeX04y4VnZFGG8Rd43pRaHsOXAKk=
github.com/bkaradzic/go-lz4 v1.0.0/go.mod h1:0YdlkowM3VswSROI7qDxhRvJ3sLhlFrRRwjwegp5jy4=
github.com/census-instrumentation/opencensus-proto v0.2.1 h1:glEXhBS5PSLLv4IXzLA5yPRVX4bilULVyxxbrfOtDAk=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
@@ -35,31 +33,31 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk
github.com/cloudflare/golz4 v0.0.0-20150217214814-ef862a3cdc58 h1:F1EaeKL/ta07PY/k9Os/UFtwERei2/XzGemhpGnBKNg=
github.com/cloudflare/golz4 v0.0.0-20150217214814-ef862a3cdc58/go.mod h1:EOBUe0h4xcZ5GoxqC5SDxFQ8gwyZPKQoEzownBlhI80=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa h1:OaNxuTZr7kxeODyLWsRMC+OD03aFUH+mW6r2d+MWa5Y=
github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
github.com/coreos/go-semver v0.2.0 h1:3Jm3tLmsgAYcjC+4Up7hJrFBPr+n7rAqYeSw/SZazuY=
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd/v22 v22.0.0 h1:XJIw/+VlJ+87J+doOxznsAWIdmWuViOVhkQamW5YV28=
github.com/coreos/go-systemd/v22 v22.0.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY=
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM=
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd/v22 v22.3.2 h1:D9/bQk5vlXQFZ6Kwuu6zaiXJ9oTPe68++AzAJc1DzSI=
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4 h1:qk/FSDDxo05wdJH28W+p5yivv7LuLYLRXPPD8KQCtZs=
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/emicklei/proto v1.9.0 h1:l0QiNT6Qs7Yj0Mb4X6dnWBQer4ebei2BFcgQLbGqUDc=
github.com/emicklei/proto v1.9.0/go.mod h1:rn1FgRS/FANiZdD2djyH7TMA9jdRDcYQ9IEN9yvjX0A=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s=
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
github.com/fatih/structtag v1.2.0 h1:/OdNE99OxoI/PqaW/SuSK9uxxT3f/tcSZgon/ssNSx4=
@@ -73,137 +71,111 @@ github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8 h1:DujepqpGd1hyOd7a
github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
github.com/go-redis/redis v6.15.7+incompatible h1:3skhDh95XQMpnqeqNftPkQD9jL9e5e36z/1SUm6dy1U=
github.com/go-redis/redis v6.15.7+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-xorm/builder v0.3.4 h1:FxkeGB4Cggdw3tPwutLCpfjng2jugfkg6LDMrd/KsoY=
github.com/go-xorm/builder v0.3.4/go.mod h1:KxkQkNN1DpPKTedxXyTQcmH+rXfvk4LZ9SOOBoZBAxw=
github.com/go-xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a h1:9wScpmSP5A3Bk8V3XHWUcJmYTh+ZnlHVyc+A4oZYS3Y=
github.com/go-xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:56xuuqnHyryaerycW3BfssRdxQstACi0Epw/yC5E2xM=
github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls=
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.4.3 h1:GV+pQPG/EUUbkh47niozDcADz6go/dUwhVzdUQHIVRw=
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0 h1:oOuy+ugB+P/kBdUnG5QaMXSIyJ1q38wWSojYCb3z5VQ=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4 h1:z53tR0945TRRQO/fLEVPI6SMv7ZflF0TEaTAoU7tOzg=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho=
github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/grpc-ecosystem/grpc-gateway v1.14.3 h1:OCJlWkOUoTnl0neNGlf4fUm3TmbEtguw7vR+nGtnDjY=
github.com/grpc-ecosystem/grpc-gateway v1.14.3/go.mod h1:6CwZWGDSPRJidgKAtJVvND6soZe6fT7iteq8wDPdhb0=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw=
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI=
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/iancoleman/strcase v0.1.2 h1:gnomlvw9tnV3ITTAxzKSgTF+8kFWcU/f+TgttpXGz1U=
github.com/iancoleman/strcase v0.1.2/go.mod h1:SK73tn/9oHe+/Y0h39VT4UCxmurVJkR5NA7kMEAOgSE=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks=
github.com/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns=
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
github.com/justinas/alice v1.2.0 h1:+MHSA/vccVCF4Uq37S42jwlkvI2Xzl7zTPCN5BnZNVo=
github.com/justinas/alice v1.2.0/go.mod h1:fN5HRH/reO/zrUflLfTN43t3vXvKzvZIENsNEe7i7qA=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s=
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.3.0 h1:/qkRGz8zljWiDcFvgpwUpwIAPu3r07TDvs3Rws+o/pU=
github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8=
github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA=
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.6 h1:6Su7aK7lXmJ/U79bYtBjLNaha4Fs1Rg9plHpcH+vvnE=
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-runewidth v0.0.7 h1:Ei8KR0497xHyKJPAv59M1dkC+rOZCMBJ+t3fZ+twI54=
github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32 h1:W6apQkHrMkS0Muv8G/TipAy/FJl/rCYT0+EuS8+Z0z4=
github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms=
github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
github.com/olekukonko/tablewriter v0.0.4 h1:vHD/YYe1Wolo78koG299f7V/VAS08c6IpCLn+Ejf/w8=
github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA=
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs=
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
@@ -213,7 +185,6 @@ github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi
github.com/pierrec/lz4 v2.5.1+incompatible h1:Yq0up0149Hh5Ekhm/91lgkZuD1ZDnXNM26bycpTzYBM=
github.com/pierrec/lz4 v2.5.1+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
@@ -221,125 +192,119 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
github.com/prometheus/client_golang v1.5.1 h1:bdHYieyGlH+6OLEk2YQha8THib30KP0/yD0YH9m6xcA=
github.com/prometheus/client_golang v1.5.1/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU=
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
github.com/prometheus/client_golang v1.11.0 h1:HNkLOAEQMIDv/K+04rukrLx6ch7msSRwf3/SASFAGtQ=
github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4 h1:gQz4mCbXsO+nc9n1hCxHcGA3Zx3Eo+UHZoInFGUIXNM=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M=
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.9.1 h1:KOMtN28tlbam3/7ZKEYKHhKoJZYYj3gMH4uc62x7X7U=
github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4=
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
github.com/prometheus/common v0.26.0 h1:iMAkS2TDoNWnKM+Kopnx/8tnEStIfpYA0ur0xQzzhMQ=
github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.8 h1:+fpWZdT24pJBiqJdAwYBjPSk+5YmQzYNPYzQsdzLkt8=
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
github.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4=
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/soheilhy/cmux v0.1.4 h1:0HKaf1o97UwFjHH9o5XsHUOF+tqmdA7KEzXLpiyaw0E=
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
github.com/spf13/pflag v1.0.1 h1:aCvUg6QPl3ibpQUxyLkrEkCHtPqYJL4x9AuhqVqFis4=
github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/tmc/grpc-websocket-proxy v0.0.0-20171017195756-830351dc03c6 h1:lYIiVDtZnyTWlNwiAxLj0bbpTcx1BWCFhXjfsvmPdNc=
github.com/tmc/grpc-websocket-proxy v0.0.0-20171017195756-830351dc03c6/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/urfave/cli v1.22.5 h1:lNq9sAHXK2qfdI8W+GRItjCEkI+2oR4d+MEHy1CKXoU=
github.com/urfave/cli v1.22.5/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/xwb1989/sqlparser v0.0.0-20180606152119-120387863bf2 h1:zzrxE1FKn5ryBNl9eKOeqQ58Y/Qpo3Q9QNxKHX5uzzQ=
github.com/xwb1989/sqlparser v0.0.0-20180606152119-120387863bf2/go.mod h1:hzfGeIUDq/j97IG+FhNqkowIyEcD88LrW6fyU3K3WqY=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/gopher-lua v0.0.0-20191220021717-ab39c6098bdb h1:ZkM6LRnq40pR1Ox0hTHlnpkcOTuFIDQpZ1IN8rKKhX0=
github.com/yuin/gopher-lua v0.0.0-20191220021717-ab39c6098bdb/go.mod h1:gqRgreBUhTSL0GeU64rtZ3Uq3wtjOa/TB2YfrtkCbVQ=
github.com/zeromicro/antlr v0.0.0-20210508120604-8d7a7786f5c4 h1:wt86gT5lsN+xWmij6lFCrDIqoxeOnqM2MxiO7cNm+Lo=
github.com/zeromicro/antlr v0.0.0-20210508120604-8d7a7786f5c4/go.mod h1:nfpjEwFR6Q4xGDJMcZnCL9tEfQRgszMwu3rDz2Z+p5M=
github.com/zeromicro/antlr v0.0.1 h1:CQpIn/dc0pUjgGQ81y98s/NGOm2Hfru2NNio2I9mQgk=
github.com/zeromicro/antlr v0.0.1/go.mod h1:nfpjEwFR6Q4xGDJMcZnCL9tEfQRgszMwu3rDz2Z+p5M=
go.etcd.io/bbolt v1.3.4 h1:hi1bXHMVrlQh6WwxAy+qZCV/SYIlqo+Ushwdpa4tAKg=
go.etcd.io/bbolt v1.3.4/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
go.etcd.io/etcd v0.0.0-20200402134248-51bdeb39e698 h1:jWtjCJX1qxhHISBMLRztWwR+EXkI7MJAF2HjHAE/x/I=
go.etcd.io/etcd v0.0.0-20200402134248-51bdeb39e698/go.mod h1:YoUyTScD3Vcv2RBm3eGVOq7i1ULiz3OuXoQFWOirmAM=
go.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk=
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
github.com/zeromicro/ddl-parser v0.0.0-20210710132903-bc9dbb9789b1 h1:zItUIfobEHTYD9X0fAt9QWEWIFWDa8CypF+Z62zIR+M=
github.com/zeromicro/ddl-parser v0.0.0-20210710132903-bc9dbb9789b1/go.mod h1:ISU/8NuPyEpl9pa17Py9TBPetMjtsiHrb9f5XGiYbo8=
github.com/zeromicro/ddl-parser v0.0.0-20210712021150-63520aca7348 h1:OhxL9tn28gDeJVzreIUiE5oVxZCjL3tBJ0XBNw8p5R8=
github.com/zeromicro/ddl-parser v0.0.0-20210712021150-63520aca7348/go.mod h1:ISU/8NuPyEpl9pa17Py9TBPetMjtsiHrb9f5XGiYbo8=
go.etcd.io/etcd/api/v3 v3.5.0 h1:GsV3S+OfZEOCNXdtNkBSR7kgLobAa/SO6tCxRa0GAYw=
go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs=
go.etcd.io/etcd/client/pkg/v3 v3.5.0 h1:2aQv6F436YnN7I4VbI8PPYrBhu+SmrTaADcf8Mi/6PU=
go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=
go.etcd.io/etcd/client/v3 v3.5.0 h1:62Eh0XOro+rDwkrypAGDfgmNh5Joq+z+W9HZdlXMzek=
go.etcd.io/etcd/client/v3 v3.5.0/go.mod h1:AIKXXVX/DQXtfTEqBryiLTUXwON+GuvO6Z7lLS/oTh0=
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/automaxprocs v1.3.0 h1:II28aZoGdaglS5vVNnspf28lnZpXScxtIozx1lAjdb0=
go.uber.org/automaxprocs v1.3.0/go.mod h1:9CWT6lKIep8U41DDaPiH6eFscnTyjfTANNQNx6LrIcA=
go.uber.org/multierr v1.5.0 h1:KCa4XfM8CWFCpxXRGok+Q0SS/0XBhMDbHHGABQLvD2A=
go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4=
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
go.uber.org/zap v1.14.1 h1:nYDKopTbvAPq/NrUVZwT15y2lpROBiLLyoRTbXOYWOo=
go.uber.org/zap v1.14.1/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc=
go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
go.uber.org/zap v1.17.0 h1:MTjgFu6ZLKvY6Pvaqk97GlxNBuMpV4Hy/3P6tRGlI2U=
go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191002192127-34f69633bfdc/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
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/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
golang.org/x/lint v0.0.0-20200302205851-738671d3881b h1:Wh+f8QHJXR411sJR8/vRBTZ7YapZaRvUcLFFJhusH0k=
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.2.0 h1:KU7oHjnv3XNWfa5COkzUifxZmxp1TyI7ImMXqFxLwvQ=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191002035440-2ec189313ef0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200707034311-ab3426394381 h1:VXak5I6aEWmAXeQjA+QSZzlgNrpq9mjcfDemuexIKsU=
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210614182718-04defd469f4e h1:XpT3nA5TvE525Ne3hInMh6+GETgn27Zfm9dxsThnX2Q=
golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210716203947-853a461950ff h1:j2EK/QoxYNBsXI4R7fQkkRUk8y6wnOBI+6hgPdP/6Ds=
golang.org/x/net v0.0.0-20210716203947-853a461950ff/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190204203706-41f3e6584952/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -347,80 +312,94 @@ golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5h
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1 h1:sIky/MyNRSHTrdxfsiUSS4WIAMvInbeXljJz+jDjeYE=
golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22 h1:RqytpXGR1iVNX7psjB3ff8y7sNFinVFvkx1c8SjBkio=
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba h1:O8mE0/t419eoIwhTFpKVkHiTs/Igowgfkj25AcZrtiE=
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200410132612-ae9902aceb98 h1:ibc1eDGW5ajwA4qzFTj0WHlD9eofMe1gAre+A0a3Vhs=
golang.org/x/tools v0.0.0-20200410132612-ae9902aceb98/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7 h1:9zdDQZ7Thm29KFXgAX/+yaf3eVbP7djjWp/dXAppNCc=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
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 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190927181202-20e1ac93f88c/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f h1:ohwtWcCwB/fZUxh/vjazHorYmBnua3NmY3CAjwC7mEA=
google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
google.golang.org/genproto v0.0.0-20210617175327-b9e0b3197ced h1:c5geK1iMU3cDKtFrCVQIcjR3W+JOZMuhIyICMCTbtus=
google.golang.org/genproto v0.0.0-20210617175327-b9e0b3197ced/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24=
google.golang.org/genproto v0.0.0-20210722135532-667f2b7c528f h1:YORWxaStkWBnWgELOHTmDrqNlFXuVGEbhwbB5iK94bQ=
google.golang.org/genproto v0.0.0-20210722135532-667f2b7c528f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.24.0/go.mod h1:XDChyiUovWa60DnaeDeZmSW86xtLtjtZbwvSiRnRtcA=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.26.0 h1:2dTRdpdFEEhJYQD8EMLB61nnrzSCTbG38PhqdhvOltg=
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.29.1 h1:EC2SB8S04d2r73uptxphDSUG+kTKVgjRPF+N3xpxRB4=
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.38.0 h1:/9BgsAsa5nWe26HqOlvlgJnqBuktYOLCgjCPqsa56W0=
google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
google.golang.org/grpc v1.39.0 h1:Klz8I9kdtkIN6EpHHUOMLCYhTn/2WAe5a0s1hcBkdTI=
google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0 h1:qdOKuR/EIArgaWNjetjgTzgVTAZ+S/WXVrq9HW9zimw=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.24.0 h1:UhZDfRO8JRQru4/+LlLE0BRKGF8L+PICnvYZmx/fEGA=
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
gopkg.in/cheggaaa/pb.v1 v1.0.28 h1:n1tBJnnK2r7g9OW2btFH91V92STTUevLXYFb8gy9EMk=
gopkg.in/cheggaaa/pb.v1 v1.0.28/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
@@ -428,30 +407,23 @@ gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/h2non/gock.v1 v1.0.15 h1:SzLqcIlb/fDfg7UvukMpNcWsu7sI5tWwL+KCATZqks0=
gopkg.in/h2non/gock.v1 v1.0.15/go.mod h1:sX4zAkdYX1TRGJ2JY156cFspQn4yRWn6p9EMdODlynE=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.5 h1:ymVxjfMaHvXD8RqPRmzHHsB3VvucivSkIAvJFDI5O3c=
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
honnef.co/go/tools v0.0.1-2020.1.4 h1:UoveltGrhghAA7ePc+e+QYDHXrBps2PqFZiHkGR/xK8=
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
rsc.io/quote/v3 v3.1.0 h1:9JKUTTIUgS6kzR9mK1YuGKv6Nl+DijDNIc0ghT58FaY=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0 h1:7uVkIFmeBqHfdjD+gZwtXXI+RODJ2Wc4O7MPEh/QiW4=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=

View File

@@ -26,7 +26,7 @@ go-zero 包含极简的 API 定义和生成工具 goctl可以根据定义的
* 自动校验客户端请求参数合法性
* 大量微服务治理和并发工具包
<img src="https://gitee.com/kevwan/static/raw/master/doc/images/architecture.png" alt="架构图" width="1500" />
![架构图](https://raw.githubusercontent.com/tal-tech/zero-doc/main/doc/images/architecture.png)
## 1. go-zero 框架背景
@@ -78,7 +78,7 @@ go-zero 是一个集成了各种工程实践的包含 web 和 rpc 框架,有
如下图,我们从多个层面保障了整体服务的高可用:
![弹性设计](https://gitee.com/kevwan/static/raw/master/doc/images/resilience.jpg)
![弹性设计](https://raw.githubusercontent.com/tal-tech/zero-doc/main/doc/images/resilience.jpg)
觉得不错的话,别忘 **star** 👏
@@ -151,7 +151,7 @@ GO111MODULE=on GOPROXY=https://goproxy.cn/,direct go get -u github.com/tal-tech/
## 6. Benchmark
![benchmark](https://gitee.com/kevwan/static/raw/master/doc/images/benchmark.png)
![benchmark](https://raw.githubusercontent.com/tal-tech/zero-doc/main/doc/images/benchmark.png)
[测试代码见这里](https://github.com/smallnest/go-web-framework-benchmark)
@@ -159,19 +159,10 @@ GO111MODULE=on GOPROXY=https://goproxy.cn/,direct go get -u github.com/tal-tech/
* API 文档
[https://go-zero.dev/zh-hans/](https://zeromicro.github.io/go-zero)
* 常见问题
* 因为 `etcd` 和 `grpc` 兼容性问题,请使用 `grpc@v1.29.1`
`google.golang.org/grpc v1.29.1`
* 因为 `protobuf` 兼容性问题,请使用 `protocol-gen@v1.3.2`
`go get -u github.com/golang/protobuf/protoc-gen-go@v1.3.2`
[https://go-zero.dev/cn/](https://go-zero.dev/cn/)
* awesome 系列(更多文章见『微服务实践』公众号)
* [快速构建高并发微服务](https://github.com/tal-tech/zero-doc/blob/main/doc/shorturl.md)
* [快速构建高并发微服务 - 多 RPC 版](https://github.com/tal-tech/zero-doc/blob/main/docs/zero/bookstore.md)
* [goctl 使用帮助](https://github.com/tal-tech/zero-doc/blob/main/doc/goctl.md)
@@ -208,6 +199,23 @@ go-zero 已被许多公司用于生产部署,接入场景如在线教育、电
>17. 三七游戏
>18. 成都创道夫科技有限公司
>19. 联想Lenovo
>20. 云犀
>21. 高盈国际
>22. 北京中科生活服务有限公司
>23. Indochat 印尼艾希英
>24. 数赞
>25. 量冠科技
>26. 杭州又拍云科技有限公司
>27. 深圳市点购电子商务控股股份有限公司
>28. 深圳市宁克沃德科技有限公司
>29. 桂林优利特医疗电子有限公司
>30. 成都智橙互动科技有限公司
>31. 深圳市班班科技有限公司
>32. 飞视(苏州)数字技术有限公司
>33. 上海鲸思智能科技有限公司
>34. 南宁宸升计算机科技有限公司
>35. 秦皇岛2084team
>36. 天翼云股份有限公司
如果贵公司也已使用 go-zero欢迎在 [登记地址](https://github.com/tal-tech/go-zero/issues/602) 登记,仅仅为了推广,不做其它用途。
@@ -215,7 +223,7 @@ go-zero 已被许多公司用于生产部署,接入场景如在线教育、电
`go-zero` 相关文章和视频都会在 `微服务实践` 公众号整理呈现,欢迎扫码关注 👏
<img src="https://gitee.com/kevwan/static/raw/master/images/wechat-micro.jpg" alt="wechat" width="300" />
<img src="https://raw.githubusercontent.com/tal-tech/zero-doc/main/doc/images/wechat-micro.jpg" alt="wechat" width="300" />
## 10. 微信交流群

View File

@@ -97,7 +97,7 @@ go get -u github.com/tal-tech/go-zero
[Rapid development of microservice systems](https://github.com/tal-tech/zero-doc/blob/main/doc/shorturl-en.md)
[Rapid development of microservice systems - multiple RPCs](https://github.com/tal-tech/zero-doc/blob/main/doc/bookstore-en.md)
[Rapid development of microservice systems - multiple RPCs](https://github.com/tal-tech/zero-doc/blob/main/docs/zero/bookstore-en.md)
1. install goctl
@@ -200,31 +200,20 @@ go get -u github.com/tal-tech/go-zero
## 7. Benchmark
Document: [https://go-zero.dev/en/](https://go-zero.dev/en/)
![benchmark](https://raw.githubusercontent.com/tal-tech/zero-doc/main/doc/images/benchmark.png)
[Checkout the test code](https://github.com/smallnest/go-web-framework-benchmark)
## 8. Documents (adding)
* [Documents](https://go-zero.dev/en/)
* [Rapid development of microservice systems](https://github.com/tal-tech/zero-doc/blob/main/doc/shorturl-en.md)
* [Rapid development of microservice systems - multiple RPCs](https://github.com/tal-tech/zero-doc/blob/main/docs/zero/bookstore-en.md)
* [Examples](https://github.com/zeromicro/zero-examples)
## 9. Important notes
## 9. Chat group
* Use grpc 1.29.1, because etcd lib doesnt support latter versions.
`google.golang.org/grpc v1.29.1`
* For protobuf compatibility, use `protocol-gen@v1.3.2`.
` go get -u github.com/golang/protobuf/protoc-gen-go@v1.3.2`
## 10. Chat group
Join the chat via https://join.slack.com/t/go-zeroworkspace/shared_invite/zt-m39xssxc-kgIqERa7aVsujKNj~XuPKg
Join the chat via https://join.slack.com/t/go-zero/shared_invite/zt-thyennhc-_fNXFpeUJcGE_tQNZFpsdA
## Give a Star! ⭐

View File

@@ -159,7 +159,7 @@ type mockedRouter struct{}
func (m mockedRouter) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
}
func (m mockedRouter) Handle(method string, path string, handler http.Handler) error {
func (m mockedRouter) Handle(method, path string, handler http.Handler) error {
return errors.New("foo")
}

View File

@@ -12,6 +12,7 @@ import (
const (
formKey = "form"
pathKey = "path"
headerKey = "header"
emptyJson = "{}"
maxMemory = 32 << 20 // 32MB
maxBodyLen = 8 << 20 // 8MB
@@ -20,8 +21,9 @@ const (
)
var (
formUnmarshaler = mapping.NewUnmarshaler(formKey, mapping.WithStringValues())
pathUnmarshaler = mapping.NewUnmarshaler(pathKey, mapping.WithStringValues())
formUnmarshaler = mapping.NewUnmarshaler(formKey, mapping.WithStringValues())
pathUnmarshaler = mapping.NewUnmarshaler(pathKey, mapping.WithStringValues())
headerUnmarshaler = mapping.NewUnmarshaler(headerKey, mapping.WithStringValues())
)
// Parse parses the request.
@@ -34,9 +36,28 @@ func Parse(r *http.Request, v interface{}) error {
return err
}
if err := ParseHeaders(r, v); err != nil {
return err
}
return ParseJsonBody(r, v)
}
// ParseHeaders parses the headers request.
func ParseHeaders(r *http.Request, v interface{}) error {
m := map[string]interface{}{}
for k, v := range r.Header {
k = strings.ToLower(k)
if len(v) == 1 {
m[k] = v[0]
} else {
m[k] = v
}
}
return headerUnmarshaler.Unmarshal(m, v)
}
// ParseForm parses the form request.
func ParseForm(r *http.Request, v interface{}) error {
if err := r.ParseForm(); err != nil {

View File

@@ -201,3 +201,26 @@ func BenchmarkParseAuto(b *testing.B) {
}
}
}
func TestParseHeaders(t *testing.T) {
v := struct {
Name string `header:"name"`
Percent string `header:"percent"`
Addrs []string `header:"addrs"`
}{}
request, err := http.NewRequest("POST", "http://hello.com/", nil)
if err != nil {
t.Fatal(err)
}
request.Header.Set("name", "chenquan")
request.Header.Set("percent", "1")
request.Header.Add("addrs", "addr1")
request.Header.Add("addrs", "addr2")
err = ParseHeaders(request, &v)
if err != nil {
t.Fatal(err)
}
assert.Equal(t, "chenquan", v.Name)
assert.Equal(t, "1", v.Percent)
assert.Equal(t, []string{"addr1", "addr2"}, v.Addrs)
}

View File

@@ -5,7 +5,7 @@ import "net/http"
// Router interface represents a http router that handles http requests.
type Router interface {
http.Handler
Handle(method string, path string, handler http.Handler) error
Handle(method, path string, handler http.Handler) error
SetNotFoundHandler(handler http.Handler)
SetNotAllowedHandler(handler http.Handler)
}

View File

@@ -23,7 +23,7 @@ func StartHttps(host string, port int, certFile, keyFile string, handler http.Ha
})
}
func start(host string, port int, handler http.Handler, run func(srv *http.Server) error) error {
func start(host string, port int, handler http.Handler, run func(srv *http.Server) error) (err error) {
server := &http.Server{
Addr: fmt.Sprintf("%s:%d", host, port),
Handler: handler,
@@ -31,7 +31,11 @@ func start(host string, port int, handler http.Handler, run func(srv *http.Serve
waitForCalled := proc.AddWrapUpListener(func() {
server.Shutdown(context.Background())
})
defer waitForCalled()
defer func() {
if err == http.ErrServerClosed {
waitForCalled()
}
}()
return run(server)
}

View File

@@ -33,7 +33,7 @@ const (
`
)
func genDoc(api *spec.ApiSpec, dir string, filename string) error {
func genDoc(api *spec.ApiSpec, dir, filename string) error {
fp, _, err := util.MaybeCreateFile(dir, "", filename)
if err != nil {
return err

View File

@@ -86,12 +86,17 @@ func ApiFormatByPath(apiFilePath string) error {
return err
}
result, err := apiFormat(string(data))
abs, err := filepath.Abs(apiFilePath)
if err != nil {
return err
}
_, err = parser.ParseContent(result)
result, err := apiFormat(string(data), abs)
if err != nil {
return err
}
_, err = parser.ParseContent(result, abs)
if err != nil {
return err
}
@@ -99,8 +104,8 @@ func ApiFormatByPath(apiFilePath string) error {
return ioutil.WriteFile(apiFilePath, []byte(result), os.ModePerm)
}
func apiFormat(data string) (string, error) {
_, err := parser.ParseContent(data)
func apiFormat(data string, filename ...string) (string, error) {
_, err := parser.ParseContent(data, filename...)
if err != nil {
return "", err
}

View File

@@ -30,7 +30,11 @@ func GoCommand(c *cli.Context) error {
apiFile := c.String("api")
dir := c.String("dir")
namingStyle := c.String("style")
home := c.String("home")
if len(home) > 0 {
util.RegisterGoctlHome(home)
}
if len(apiFile) == 0 {
return errors.New("missing -api")
}
@@ -54,14 +58,19 @@ func DoGenProject(apiFile, dir, style string) error {
}
logx.Must(util.MkdirIfNotExist(dir))
rootPkg, err := getParentPackage(dir)
if err != nil {
return err
}
logx.Must(genEtc(dir, cfg, api))
logx.Must(genConfig(dir, cfg, api))
logx.Must(genMain(dir, cfg, api))
logx.Must(genServiceContext(dir, cfg, api))
logx.Must(genMain(dir, rootPkg, cfg, api))
logx.Must(genServiceContext(dir, rootPkg, cfg, api))
logx.Must(genTypes(dir, cfg, api))
logx.Must(genRoutes(dir, cfg, api))
logx.Must(genHandlers(dir, cfg, api))
logx.Must(genLogic(dir, cfg, api))
logx.Must(genRoutes(dir, rootPkg, cfg, api))
logx.Must(genHandlers(dir, rootPkg, cfg, api))
logx.Must(genLogic(dir, rootPkg, cfg, api))
logx.Must(genMiddleware(dir, cfg, api))
if err := backupAndSweep(apiFile); err != nil {

View File

@@ -49,18 +49,14 @@ type handlerInfo struct {
HasRequest bool
}
func genHandler(dir string, cfg *config.Config, group spec.Group, route spec.Route) error {
func genHandler(dir, rootPkg string, cfg *config.Config, group spec.Group, route spec.Route) error {
handler := getHandlerName(route)
if getHandlerFolderPath(group, route) != handlerDir {
handler = strings.Title(handler)
}
parentPkg, err := getParentPackage(dir)
if err != nil {
return err
}
return doGenToFile(dir, handler, cfg, group, route, handlerInfo{
ImportPackages: genHandlerImports(group, route, parentPkg),
ImportPackages: genHandlerImports(group, route, rootPkg),
HandlerName: handler,
RequestType: util.Title(route.RequestTypeName()),
LogicType: strings.Title(getLogicName(route)),
@@ -89,10 +85,10 @@ func doGenToFile(dir, handler string, cfg *config.Config, group spec.Group,
})
}
func genHandlers(dir string, cfg *config.Config, api *spec.ApiSpec) error {
func genHandlers(dir, rootPkg string, cfg *config.Config, api *spec.ApiSpec) error {
for _, group := range api.Service.Groups {
for _, route := range group.Routes {
if err := genHandler(dir, cfg, group, route); err != nil {
if err := genHandler(dir, rootPkg, cfg, group, route); err != nil {
return err
}
}

View File

@@ -39,10 +39,10 @@ func (l *{{.logic}}) {{.function}}({{.request}}) {{.responseType}} {
}
`
func genLogic(dir string, cfg *config.Config, api *spec.ApiSpec) error {
func genLogic(dir, rootPkg string, cfg *config.Config, api *spec.ApiSpec) error {
for _, g := range api.Service.Groups {
for _, r := range g.Routes {
err := genLogicByRoute(dir, cfg, g, r)
err := genLogicByRoute(dir, rootPkg, cfg, g, r)
if err != nil {
return err
}
@@ -51,19 +51,14 @@ func genLogic(dir string, cfg *config.Config, api *spec.ApiSpec) error {
return nil
}
func genLogicByRoute(dir string, cfg *config.Config, group spec.Group, route spec.Route) error {
func genLogicByRoute(dir, rootPkg string, cfg *config.Config, group spec.Group, route spec.Route) error {
logic := getLogicName(route)
goFile, err := format.FileNamingFormat(cfg.NamingFormat, logic)
if err != nil {
return err
}
parentPkg, err := getParentPackage(dir)
if err != nil {
return err
}
imports := genLogicImports(route, parentPkg)
imports := genLogicImports(route, rootPkg)
var responseString string
var returnString string
var requestString string

View File

@@ -39,7 +39,7 @@ func main() {
}
`
func genMain(dir string, cfg *config.Config, api *spec.ApiSpec) error {
func genMain(dir, rootPkg string, cfg *config.Config, api *spec.ApiSpec) error {
name := strings.ToLower(api.Service.Name)
if strings.HasSuffix(name, "-api") {
name = strings.ReplaceAll(name, "-api", "")
@@ -49,11 +49,6 @@ func genMain(dir string, cfg *config.Config, api *spec.ApiSpec) error {
return err
}
parentPkg, err := getParentPackage(dir)
if err != nil {
return err
}
return genFile(fileGenConfig{
dir: dir,
subdir: "",
@@ -63,7 +58,7 @@ func genMain(dir string, cfg *config.Config, api *spec.ApiSpec) error {
templateFile: mainTemplateFile,
builtinTemplate: mainTemplate,
data: map[string]string{
"importPackages": genMainImports(parentPkg),
"importPackages": genMainImports(rootPkg),
"serviceName": api.Service.Name,
},
})

View File

@@ -62,7 +62,7 @@ type (
}
)
func genRoutes(dir string, cfg *config.Config, api *spec.ApiSpec) error {
func genRoutes(dir, rootPkg string, cfg *config.Config, api *spec.ApiSpec) error {
var builder strings.Builder
groups, err := getRoutes(api)
if err != nil {
@@ -116,11 +116,6 @@ func genRoutes(dir string, cfg *config.Config, api *spec.ApiSpec) error {
}
}
parentPkg, err := getParentPackage(dir)
if err != nil {
return err
}
routeFilename, err := format.FileNamingFormat(cfg.NamingFormat, routesFilename)
if err != nil {
return err
@@ -139,7 +134,7 @@ func genRoutes(dir string, cfg *config.Config, api *spec.ApiSpec) error {
templateFile: "",
builtinTemplate: routesTemplate,
data: map[string]string{
"importPackages": genRouteImports(parentPkg, api),
"importPackages": genRouteImports(rootPkg, api),
"routesAdditions": strings.TrimSpace(builder.String()),
},
})

View File

@@ -33,7 +33,7 @@ func NewServiceContext(c {{.config}}) *ServiceContext {
`
)
func genServiceContext(dir string, cfg *config.Config, api *spec.ApiSpec) error {
func genServiceContext(dir, rootPkg string, cfg *config.Config, api *spec.ApiSpec) error {
filename, err := format.FileNamingFormat(cfg.NamingFormat, contextFilename)
if err != nil {
return err
@@ -45,11 +45,6 @@ func genServiceContext(dir string, cfg *config.Config, api *spec.ApiSpec) error
auths = append(auths, fmt.Sprintf("%s config.AuthConfig", item))
}
parentPkg, err := getParentPackage(dir)
if err != nil {
return err
}
var middlewareStr string
var middlewareAssignment string
middlewares := getMiddleware(api)
@@ -61,9 +56,9 @@ func genServiceContext(dir string, cfg *config.Config, api *spec.ApiSpec) error
fmt.Sprintf("middleware.New%s().%s", strings.Title(name), "Handle"))
}
configImport := "\"" + ctlutil.JoinPackages(parentPkg, configDir) + "\""
configImport := "\"" + ctlutil.JoinPackages(rootPkg, configDir) + "\""
if len(middlewareStr) > 0 {
configImport += "\n\t\"" + ctlutil.JoinPackages(parentPkg, middlewareDir) + "\""
configImport += "\n\t\"" + ctlutil.JoinPackages(rootPkg, middlewareDir) + "\""
configImport += fmt.Sprintf("\n\t\"%s/rest\"", vars.ProjectOpenSourceURL)
}

View File

@@ -42,7 +42,7 @@ func (v *ApiVisitor) VisitApi(ctx *api.ApiContext) interface{} {
return &final
}
func (v *ApiVisitor) acceptService(root *Api, final *Api) {
func (v *ApiVisitor) acceptService(root, final *Api) {
for _, service := range root.Service {
if _, ok := final.serviceM[service.ServiceApi.Name.Text()]; !ok && len(final.serviceM) > 0 {
v.panic(service.ServiceApi.Name, fmt.Sprintf("mutiple service declaration"))
@@ -104,7 +104,7 @@ func (v *ApiVisitor) duplicateServerItemCheck(service *Service) {
}
}
func (v *ApiVisitor) acceptType(root *Api, final *Api) {
func (v *ApiVisitor) acceptType(root, final *Api) {
for _, tp := range root.Type {
if _, ok := final.typeM[tp.NameExpr().Text()]; ok {
v.panic(tp.NameExpr(), fmt.Sprintf("duplicate type '%s'", tp.NameExpr().Text()))
@@ -115,7 +115,7 @@ func (v *ApiVisitor) acceptType(root *Api, final *Api) {
}
}
func (v *ApiVisitor) acceptInfo(root *Api, final *Api) {
func (v *ApiVisitor) acceptInfo(root, final *Api) {
if root.Info != nil {
infoM := map[string]PlaceHolder{}
if final.Info != nil {
@@ -133,7 +133,7 @@ func (v *ApiVisitor) acceptInfo(root *Api, final *Api) {
}
}
func (v *ApiVisitor) acceptImport(root *Api, final *Api) {
func (v *ApiVisitor) acceptImport(root, final *Api) {
for _, imp := range root.Import {
if _, ok := final.importM[imp.Value.Text()]; ok {
v.panic(imp.Import, fmt.Sprintf("duplicate import '%s'", imp.Value.Text()))
@@ -144,7 +144,7 @@ func (v *ApiVisitor) acceptImport(root *Api, final *Api) {
}
}
func (v *ApiVisitor) acceptSyntax(root *Api, final *Api) {
func (v *ApiVisitor) acceptSyntax(root, final *Api) {
if root.Syntax != nil {
if final.Syntax != nil {
v.panic(root.Syntax.Syntax, fmt.Sprintf("mutiple syntax declaration"))

View File

@@ -18,6 +18,7 @@ type (
debug bool
log console.Console
antlr.DefaultErrorListener
src string
}
// ParserOption defines an function with argument Parser
@@ -70,6 +71,12 @@ func (p *Parser) Accept(fn func(p *api.ApiParserParser, visitor *ApiVisitor) int
// Parse is used to parse the api from the specified file name
func (p *Parser) Parse(filename string) (*Api, error) {
abs, err := filepath.Abs(filename)
if err != nil {
return nil, err
}
p.src = abs
data, err := p.readContent(filename)
if err != nil {
return nil, err
@@ -79,8 +86,19 @@ func (p *Parser) Parse(filename string) (*Api, error) {
}
// ParseContent is used to parse the api from the specified content
func (p *Parser) ParseContent(content string) (*Api, error) {
return p.parse("", content)
func (p *Parser) ParseContent(content string, filename ...string) (*Api, error) {
var f string
if len(filename) > 0 {
f = filename[0]
abs, err := filepath.Abs(f)
if err != nil {
return nil, err
}
p.src = abs
}
return p.parse(f, content)
}
// parse is used to parse api from the content
@@ -94,7 +112,8 @@ func (p *Parser) parse(filename, content string) (*Api, error) {
var apiAstList []*Api
apiAstList = append(apiAstList, root)
for _, imp := range root.Import {
path := imp.Value.Text()
dir := filepath.Dir(p.src)
path := filepath.Join(dir, imp.Value.Text())
data, err := p.readContent(path)
if err != nil {
return nil, err
@@ -158,7 +177,7 @@ func (p *Parser) invoke(linePrefix, content string) (v *Api, err error) {
return
}
func (p *Parser) valid(mainApi *Api, nestedApi *Api) error {
func (p *Parser) valid(mainApi, nestedApi *Api) error {
err := p.nestedApiCheck(mainApi, nestedApi)
if err != nil {
return err
@@ -218,7 +237,7 @@ func (p *Parser) valid(mainApi *Api, nestedApi *Api) error {
return nil
}
func (p *Parser) duplicateRouteCheck(nestedApi *Api, mainHandlerMap map[string]PlaceHolder, mainRouteMap map[string]PlaceHolder) error {
func (p *Parser) duplicateRouteCheck(nestedApi *Api, mainHandlerMap, mainRouteMap map[string]PlaceHolder) error {
for _, each := range nestedApi.Service {
for _, r := range each.ServiceApi.ServiceRoute {
handler := r.GetHandler()
@@ -241,7 +260,7 @@ func (p *Parser) duplicateRouteCheck(nestedApi *Api, mainHandlerMap map[string]P
return nil
}
func (p *Parser) nestedApiCheck(mainApi *Api, nestedApi *Api) error {
func (p *Parser) nestedApiCheck(mainApi, nestedApi *Api) error {
if len(nestedApi.Import) > 0 {
importToken := nestedApi.Import[0].Import
return fmt.Errorf("%s line %d:%d the nested api does not support import",
@@ -415,12 +434,7 @@ func (p *Parser) checkType(linePrefix string, types map[string]TypeExpr, expr Da
func (p *Parser) readContent(filename string) (string, error) {
filename = strings.ReplaceAll(filename, `"`, "")
abs, err := filepath.Abs(filename)
if err != nil {
return "", err
}
data, err := ioutil.ReadFile(abs)
data, err := ioutil.ReadFile(filename)
if err != nil {
return "", err
}

View File

@@ -114,8 +114,10 @@ func (v *ApiVisitor) VisitTypeLit(ctx *api.TypeLitContext) interface{} {
return alias
}
doc := v.getDoc(ctx)
st, ok := typeLit.(*TypeStruct)
if ok {
st.DocExpr = doc
return st
}

View File

@@ -34,9 +34,9 @@ func Parse(filename string) (*spec.ApiSpec, error) {
}
// ParseContent parses the api content
func ParseContent(content string) (*spec.ApiSpec, error) {
func ParseContent(content string, filename ...string) (*spec.ApiSpec, error) {
astParser := ast.NewParser()
ast, err := astParser.ParseContent(content)
ast, err := astParser.ParseContent(content, filename...)
if err != nil {
return nil, err
}
@@ -76,14 +76,22 @@ func (p parser) fillInfo() {
func (p parser) fillSyntax() {
if p.ast.Syntax != nil {
p.spec.Syntax = spec.ApiSyntax{Version: p.ast.Syntax.Version.Text()}
p.spec.Syntax = spec.ApiSyntax{
Version: p.ast.Syntax.Version.Text(),
Doc: p.stringExprs(p.ast.Syntax.DocExpr),
Comment: p.stringExprs([]ast.Expr{p.ast.Syntax.CommentExpr}),
}
}
}
func (p parser) fillImport() {
if len(p.ast.Import) > 0 {
for _, item := range p.ast.Import {
p.spec.Imports = append(p.spec.Imports, spec.Import{Value: item.Value.Text()})
p.spec.Imports = append(p.spec.Imports, spec.Import{
Value: item.Value.Text(),
Doc: p.stringExprs(item.DocExpr),
Comment: p.stringExprs([]ast.Expr{item.CommentExpr}),
})
}
}
}
@@ -173,10 +181,14 @@ func (p parser) astTypeToSpec(in ast.DataType) spec.Type {
case *ast.Literal:
raw := v.Literal.Text()
if api.IsBasicType(raw) {
return spec.PrimitiveType{RawName: raw}
return spec.PrimitiveType{
RawName: raw,
}
}
return spec.DefineStruct{RawName: raw}
return spec.DefineStruct{
RawName: raw,
}
case *ast.Interface:
return spec.InterfaceType{RawName: v.Literal.Text()}
case *ast.Map:
@@ -198,6 +210,9 @@ func (p parser) astTypeToSpec(in ast.DataType) spec.Type {
func (p parser) stringExprs(docs []ast.Expr) []string {
var result []string
for _, item := range docs {
if item == nil {
continue
}
result = append(result, item.Text())
}
return result
@@ -222,9 +237,13 @@ func (p parser) fillService() error {
AtServerAnnotation: spec.Annotation{},
Method: astRoute.Route.Method.Text(),
Path: astRoute.Route.Path.Text(),
Doc: p.stringExprs(astRoute.Route.DocExpr),
Comment: p.stringExprs([]ast.Expr{astRoute.Route.CommentExpr}),
}
if astRoute.AtHandler != nil {
route.Handler = astRoute.AtHandler.Name.Text()
route.HandlerDoc = append(route.HandlerDoc, p.stringExprs(astRoute.AtHandler.DocExpr)...)
route.HandlerComment = append(route.HandlerComment, p.stringExprs([]ast.Expr{astRoute.AtHandler.CommentExpr})...)
}
err := p.fillRouteAtServer(astRoute, &route)

View File

@@ -0,0 +1,28 @@
package parser
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/tal-tech/go-zero/tools/goctl/api/spec"
)
var testApi = "// syntax doc\nsyntax = \"v1\" // syntax comment\n\n// type doc\ntype Request {\n\tName string `path:\"name,options=you|me\"`\n}\n\ntype Response {\n\tMessage string `json:\"message\"`\n}\n\n// service doc\nservice greet-api {\n\t// handler doc\n\t@handler GreetHandler // handler comment\n\tget /from/:name(Request) returns (Response);\n}"
func TestParseContent(t *testing.T) {
sp, err := ParseContent(testApi)
assert.Nil(t, err)
assert.Equal(t, spec.Doc{`// syntax doc`}, sp.Syntax.Doc)
assert.Equal(t, spec.Doc{`// syntax comment`}, sp.Syntax.Comment)
for _, tp := range sp.Types {
if tp.Name() == "Request" {
assert.Equal(t, []string{`// type doc`}, tp.Documents())
}
}
for _, e := range sp.Service.Routes() {
if e.Handler == "GreetHandler" {
assert.Equal(t, spec.Doc{"// handler doc"}, e.HandlerDoc)
assert.Equal(t, spec.Doc{"// handler comment"}, e.HandlerComment)
}
}
}

View File

@@ -5,27 +5,87 @@ func (t PrimitiveType) Name() string {
return t.RawName
}
// Comments returns the comments of struct
func (t PrimitiveType) Comments() []string {
return nil
}
// Documents returns the documents of struct
func (t PrimitiveType) Documents() []string {
return nil
}
// Name returns a structure string, such as User
func (t DefineStruct) Name() string {
return t.RawName
}
// Comments returns the comments of struct
func (t DefineStruct) Comments() []string {
return nil
}
// Documents returns the documents of struct
func (t DefineStruct) Documents() []string {
return t.Docs
}
// Name returns a map string, such as map[string]int
func (t MapType) Name() string {
return t.RawName
}
// Comments returns the comments of struct
func (t MapType) Comments() []string {
return nil
}
// Documents returns the documents of struct
func (t MapType) Documents() []string {
return nil
}
// Name returns a slice string, such as []int
func (t ArrayType) Name() string {
return t.RawName
}
// Comments returns the comments of struct
func (t ArrayType) Comments() []string {
return nil
}
// Documents returns the documents of struct
func (t ArrayType) Documents() []string {
return nil
}
// Name returns a pointer string, such as *User
func (t PointerType) Name() string {
return t.RawName
}
// Comments returns the comments of struct
func (t PointerType) Comments() []string {
return nil
}
// Documents returns the documents of struct
func (t PointerType) Documents() []string {
return nil
}
// Name returns a interface string, Its fixed value is interface{}
func (t InterfaceType) Name() string {
return t.RawName
}
// Comments returns the comments of struct
func (t InterfaceType) Comments() []string {
return nil
}
// Documents returns the documents of struct
func (t InterfaceType) Documents() []string {
return nil
}

View File

@@ -12,6 +12,8 @@ type (
// ApiSyntax describes the syntax grammar
ApiSyntax struct {
Version string
Doc Doc
Comment Doc
}
// ApiSpec describes a api file
@@ -25,7 +27,9 @@ type (
// Import describes api import
Import struct {
Value string
Value string
Doc Doc
Comment Doc
}
// Group defines a set of routing information
@@ -71,6 +75,10 @@ type (
Docs Doc
Handler string
AtDoc AtDoc
HandlerDoc Doc
HandlerComment Doc
Doc Doc
Comment Doc
}
// Service describes api service
@@ -82,6 +90,8 @@ type (
// Type defines api type
Type interface {
Name() string
Comments() []string
Documents() []string
}
// DefineStruct describes api structure

View File

@@ -42,6 +42,12 @@ func DockerCommand(c *cli.Context) (err error) {
}()
goFile := c.String("go")
home := c.String("home")
if len(home) > 0 {
util.RegisterGoctlHome(home)
}
if len(goFile) == 0 {
return errors.New("-go can't be empty")
}

View File

@@ -5,7 +5,10 @@ import (
"os"
"runtime"
"github.com/logrusorgru/aurora"
"github.com/tal-tech/go-zero/core/load"
"github.com/tal-tech/go-zero/core/logx"
"github.com/tal-tech/go-zero/core/stat"
"github.com/tal-tech/go-zero/tools/goctl/api/apigen"
"github.com/tal-tech/go-zero/tools/goctl/api/dartgen"
"github.com/tal-tech/go-zero/tools/goctl/api/docgen"
@@ -29,7 +32,7 @@ import (
)
var (
buildVersion = "1.1.6"
buildVersion = "1.1.10"
commands = []cli.Command{
{
Name: "upgrade",
@@ -114,6 +117,10 @@ var (
Name: "style",
Usage: "the file naming format, see [https://github.com/tal-tech/go-zero/tree/master/tools/goctl/config/readme.md]",
},
cli.StringFlag{
Name: "home",
Usage: "the goctl home path of the template",
},
},
Action: gogen.GoCommand,
},
@@ -231,6 +238,10 @@ var (
Usage: "the port to expose, default none",
Value: 0,
},
cli.StringFlag{
Name: "home",
Usage: "the goctl home path of the template",
},
},
Action: docker.DockerCommand,
},
@@ -316,6 +327,10 @@ var (
Usage: "the max replicas of deploy",
Value: 10,
},
cli.StringFlag{
Name: "home",
Usage: "the goctl home path of the template",
},
},
Action: kube.DeploymentCommand,
},
@@ -337,6 +352,10 @@ var (
Name: "idea",
Usage: "whether the command execution environment is from idea plugin. [optional]",
},
cli.StringFlag{
Name: "home",
Usage: "the goctl home path of the template",
},
},
Action: rpc.RPCNew,
},
@@ -348,6 +367,10 @@ var (
Name: "out, o",
Usage: "the target path of proto",
},
cli.StringFlag{
Name: "home",
Usage: "the goctl home path of the template",
},
},
Action: rpc.RPCTemplate,
},
@@ -363,6 +386,10 @@ var (
Name: "proto_path, I",
Usage: `native command of protoc, specify the directory in which to search for imports. [optional]`,
},
cli.StringSliceFlag{
Name: "go_opt",
Usage: `native command of protoc-gen-go, specify the mapping from proto to go, eg --go_opt=proto_import=go_package_import. [optional]`,
},
cli.StringFlag{
Name: "dir, d",
Usage: `the target path of the code`,
@@ -375,6 +402,10 @@ var (
Name: "idea",
Usage: "whether the command execution environment is from idea plugin. [optional]",
},
cli.StringFlag{
Name: "home",
Usage: "the goctl home path of the template",
},
},
Action: rpc.RPC,
},
@@ -412,6 +443,14 @@ var (
Name: "idea",
Usage: "for idea plugin [optional]",
},
cli.StringFlag{
Name: "database, db",
Usage: "the name of database [optional]",
},
cli.StringFlag{
Name: "home",
Usage: "the goctl home path of the template",
},
},
Action: model.MysqlDDL,
},
@@ -421,7 +460,7 @@ var (
Flags: []cli.Flag{
cli.StringFlag{
Name: "url",
Usage: `the data source of database,like "root:password@tcp(127.0.0.1:3306)/database`,
Usage: `the data source of database,like "root:password@tcp(127.0.0.1:3306)/database"`,
},
cli.StringFlag{
Name: "table, t",
@@ -443,8 +482,57 @@ var (
Name: "idea",
Usage: "for idea plugin [optional]",
},
cli.StringFlag{
Name: "home",
Usage: "the goctl home path of the template",
},
},
Action: model.MyDataSource,
Action: model.MySqlDataSource,
},
},
},
{
Name: "pg",
Usage: `generate postgresql model`,
Subcommands: []cli.Command{
{
Name: "datasource",
Usage: `generate model from datasource`,
Flags: []cli.Flag{
cli.StringFlag{
Name: "url",
Usage: `the data source of database,like "postgres://root:password@127.0.0.1:54332/database?sslmode=disable"`,
},
cli.StringFlag{
Name: "table, t",
Usage: `the table or table globbing patterns in the database`,
},
cli.StringFlag{
Name: "schema, s",
Usage: `the table schema, default is [public]`,
},
cli.BoolFlag{
Name: "cache, c",
Usage: "generate code with cache [optional]",
},
cli.StringFlag{
Name: "dir, d",
Usage: "the target dir",
},
cli.StringFlag{
Name: "style",
Usage: "the file naming format, see [https://github.com/tal-tech/go-zero/tree/master/tools/goctl/config/readme.md]",
},
cli.BoolFlag{
Name: "idea",
Usage: "for idea plugin [optional]",
},
cli.StringFlag{
Name: "home",
Usage: "the goctl home path of the template",
},
},
Action: model.PostgreSqlDataSource,
},
},
},
@@ -468,6 +556,10 @@ var (
Name: "style",
Usage: "the file naming format, see [https://github.com/tal-tech/go-zero/tree/master/tools/goctl/config/readme.md]",
},
cli.StringFlag{
Name: "home",
Usage: "the goctl home path of the template",
},
},
Action: mongo.Action,
},
@@ -489,13 +581,25 @@ var (
Usage: "template operation",
Subcommands: []cli.Command{
{
Name: "init",
Usage: "initialize the all templates(force update)",
Name: "init",
Usage: "initialize the all templates(force update)",
Flags: []cli.Flag{
cli.StringFlag{
Name: "home",
Usage: "the goctl home path of the template",
},
},
Action: tpl.GenTemplates,
},
{
Name: "clean",
Usage: "clean the all cache templates",
Name: "clean",
Usage: "clean the all cache templates",
Flags: []cli.Flag{
cli.StringFlag{
Name: "home",
Usage: "the goctl home path of the template",
},
},
Action: tpl.CleanTemplates,
},
{
@@ -506,6 +610,10 @@ var (
Name: "category,c",
Usage: "the category of template, enum [api,rpc,model,docker,kube]",
},
cli.StringFlag{
Name: "home",
Usage: "the goctl home path of the template",
},
},
Action: tpl.UpdateTemplates,
},
@@ -521,6 +629,10 @@ var (
Name: "name,n",
Usage: "the target file name of template",
},
cli.StringFlag{
Name: "home",
Usage: "the goctl home path of the template",
},
},
Action: tpl.RevertTemplates,
},
@@ -531,6 +643,8 @@ var (
func main() {
logx.Disable()
load.Disable()
stat.DisableLog()
app := cli.NewApp()
app.Usage = "a cli tool to generate code"
@@ -538,6 +652,6 @@ func main() {
app.Commands = commands
// cli already print error messages
if err := app.Run(os.Args); err != nil {
fmt.Println("error:", err)
fmt.Println(aurora.Red("error: " + err.Error()))
}
}

View File

@@ -54,8 +54,8 @@ type (
createRequest struct {
innerType
name string `form:"name"` // niha
age int `form:"age,optional"` // nihaod
name string `form:"name"`
age int `form:"age,optional"`
address []address `json:"address,optional"`
}

View File

@@ -40,6 +40,12 @@ type Deployment struct {
// DeploymentCommand is used to generate the kubernetes deployment yaml files.
func DeploymentCommand(c *cli.Context) error {
nodePort := c.Int("nodePort")
home := c.String("home")
if len(home) > 0 {
util.RegisterGoctlHome(home)
}
// 0 to disable the nodePort type
if nodePort != 0 && (nodePort < basePort || nodePort > portLimit) {
return errors.New("nodePort should be between 30000 and 32767")

View File

@@ -7,6 +7,7 @@ import (
"github.com/tal-tech/go-zero/tools/goctl/config"
"github.com/tal-tech/go-zero/tools/goctl/model/mongo/generate"
file "github.com/tal-tech/go-zero/tools/goctl/util"
"github.com/urfave/cli"
)
@@ -16,6 +17,12 @@ func Action(ctx *cli.Context) error {
c := ctx.Bool("cache")
o := strings.TrimSpace(ctx.String("dir"))
s := ctx.String("style")
home := ctx.String("home")
if len(home) > 0 {
file.RegisterGoctlHome(home)
}
if len(tp) == 0 {
return errors.New("missing type")
}

View File

@@ -11,7 +11,7 @@ import (
"github.com/tal-tech/go-zero/core/stores/mongoc"
)
{{if .Cache}}var prefix{{.Type}}CacheKey = "cache#{{.Type}}#"{{end}}
{{if .Cache}}var prefix{{.Type}}CacheKey = "cache:{{.Type}}:"{{end}}
type {{.Type}}Model interface{
Insert(ctx context.Context,data *{{.Type}}) error

View File

@@ -264,6 +264,7 @@ OPTIONS:
--style value the file naming format, see [https://github.com/tal-tech/go-zero/tree/master/tools/goctl/config/readme.md]
--cache, -c generate code with cache [optional]
--idea for idea plugin [optional]
--database, -db the name of database [optional]
```
* datasource

View File

@@ -3,6 +3,7 @@ package builderx
import (
"fmt"
"reflect"
"strings"
"github.com/go-xorm/builder"
)
@@ -81,13 +82,18 @@ func FieldNames(in interface{}) []string {
}
// RawFieldNames converts golang struct field into slice string
func RawFieldNames(in interface{}) []string {
func RawFieldNames(in interface{}, postgresSql ...bool) []string {
out := make([]string, 0)
v := reflect.ValueOf(in)
if v.Kind() == reflect.Ptr {
v = v.Elem()
}
var pg bool
if len(postgresSql) > 0 {
pg = postgresSql[0]
}
// we only accept structs
if v.Kind() != reflect.Struct {
panic(fmt.Errorf("ToMap only accepts structs; got %T", v))
@@ -98,11 +104,32 @@ func RawFieldNames(in interface{}) []string {
// gets us a StructField
fi := typ.Field(i)
if tagv := fi.Tag.Get(dbTag); tagv != "" {
out = append(out, fmt.Sprintf("`%s`", tagv))
if pg {
out = append(out, fmt.Sprintf("%s", tagv))
} else {
out = append(out, fmt.Sprintf("`%s`", tagv))
}
} else {
out = append(out, fmt.Sprintf(`"%s"`, fi.Name))
if pg {
out = append(out, fmt.Sprintf("%s", fi.Name))
} else {
out = append(out, fmt.Sprintf("`%s`", fi.Name))
}
}
}
return out
}
func PostgreSqlJoin(elems []string) string {
var b = new(strings.Builder)
for index, e := range elems {
b.WriteString(fmt.Sprintf("%s = $%d, ", e, index+1))
}
if b.Len() == 0 {
return b.String()
}
return b.String()[0 : b.Len()-2]
}

View File

@@ -118,3 +118,8 @@ func TestBuildSqlLike(t *testing.T) {
assert.Equal(t, sql, actualSQL)
assert.Equal(t, args, actualArgs)
}
func TestJoin(t *testing.T) {
ret := PostgreSqlJoin([]string{"name", "age"})
assert.Equal(t, "name = $1, age = $2", ret)
}

View File

@@ -2,29 +2,32 @@ package command
import (
"errors"
"io/ioutil"
"path/filepath"
"strings"
"github.com/go-sql-driver/mysql"
"github.com/tal-tech/go-zero/core/logx"
"github.com/tal-tech/go-zero/core/stores/postgres"
"github.com/tal-tech/go-zero/core/stores/sqlx"
"github.com/tal-tech/go-zero/tools/goctl/config"
"github.com/tal-tech/go-zero/tools/goctl/model/sql/gen"
"github.com/tal-tech/go-zero/tools/goctl/model/sql/model"
"github.com/tal-tech/go-zero/tools/goctl/model/sql/util"
file "github.com/tal-tech/go-zero/tools/goctl/util"
"github.com/tal-tech/go-zero/tools/goctl/util/console"
"github.com/urfave/cli"
)
const (
flagSrc = "src"
flagDir = "dir"
flagCache = "cache"
flagIdea = "idea"
flagURL = "url"
flagTable = "table"
flagStyle = "style"
flagSrc = "src"
flagDir = "dir"
flagCache = "cache"
flagIdea = "idea"
flagURL = "url"
flagTable = "table"
flagStyle = "style"
flagDatabase = "database"
flagSchema = "schema"
)
var errNotMatched = errors.New("sql not matched")
@@ -36,31 +39,70 @@ func MysqlDDL(ctx *cli.Context) error {
cache := ctx.Bool(flagCache)
idea := ctx.Bool(flagIdea)
style := ctx.String(flagStyle)
database := ctx.String(flagDatabase)
home := ctx.String("home")
if len(home) > 0 {
file.RegisterGoctlHome(home)
}
cfg, err := config.NewConfig(style)
if err != nil {
return err
}
return fromDDl(src, dir, cfg, cache, idea)
return fromDDl(src, dir, cfg, cache, idea, database)
}
// MyDataSource generates model code from datasource
func MyDataSource(ctx *cli.Context) error {
// MySqlDataSource generates model code from datasource
func MySqlDataSource(ctx *cli.Context) error {
url := strings.TrimSpace(ctx.String(flagURL))
dir := strings.TrimSpace(ctx.String(flagDir))
cache := ctx.Bool(flagCache)
idea := ctx.Bool(flagIdea)
style := ctx.String(flagStyle)
home := ctx.String("home")
if len(home) > 0 {
file.RegisterGoctlHome(home)
}
pattern := strings.TrimSpace(ctx.String(flagTable))
cfg, err := config.NewConfig(style)
if err != nil {
return err
}
return fromDataSource(url, pattern, dir, cfg, cache, idea)
return fromMysqlDataSource(url, pattern, dir, cfg, cache, idea)
}
func fromDDl(src, dir string, cfg *config.Config, cache, idea bool) error {
// PostgreSqlDataSource generates model code from datasource
func PostgreSqlDataSource(ctx *cli.Context) error {
url := strings.TrimSpace(ctx.String(flagURL))
dir := strings.TrimSpace(ctx.String(flagDir))
cache := ctx.Bool(flagCache)
idea := ctx.Bool(flagIdea)
style := ctx.String(flagStyle)
schema := ctx.String(flagSchema)
home := ctx.String("home")
if len(home) > 0 {
file.RegisterGoctlHome(home)
}
if len(schema) == 0 {
schema = "public"
}
pattern := strings.TrimSpace(ctx.String(flagTable))
cfg, err := config.NewConfig(style)
if err != nil {
return err
}
return fromPostgreSqlDataSource(url, pattern, dir, schema, cfg, cache, idea)
}
func fromDDl(src, dir string, cfg *config.Config, cache, idea bool, database string) error {
log := console.NewConsole(idea)
src = strings.TrimSpace(src)
if len(src) == 0 {
@@ -76,25 +118,22 @@ func fromDDl(src, dir string, cfg *config.Config, cache, idea bool) error {
return errNotMatched
}
var source []string
for _, file := range files {
data, err := ioutil.ReadFile(file)
if err != nil {
return err
}
source = append(source, string(data))
}
generator, err := gen.NewDefaultGenerator(dir, cfg, gen.WithConsoleOption(log))
if err != nil {
return err
}
return generator.StartFromDDL(strings.Join(source, "\n"), cache)
for _, file := range files {
err = generator.StartFromDDL(file, cache, database)
if err != nil {
return err
}
}
return nil
}
func fromDataSource(url, pattern, dir string, cfg *config.Config, cache, idea bool) error {
func fromMysqlDataSource(url, pattern, dir string, cfg *config.Config, cache, idea bool) error {
log := console.NewConsole(idea)
if len(url) == 0 {
log.Error("%v", "expected data source of mysql, but nothing found")
@@ -156,3 +195,58 @@ func fromDataSource(url, pattern, dir string, cfg *config.Config, cache, idea bo
return generator.StartFromInformationSchema(matchTables, cache)
}
func fromPostgreSqlDataSource(url, pattern, dir, schema string, cfg *config.Config, cache, idea bool) error {
log := console.NewConsole(idea)
if len(url) == 0 {
log.Error("%v", "expected data source of postgresql, but nothing found")
return nil
}
if len(pattern) == 0 {
log.Error("%v", "expected table or table globbing patterns, but nothing found")
return nil
}
db := postgres.New(url)
im := model.NewPostgreSqlModel(db)
tables, err := im.GetAllTables(schema)
if err != nil {
return err
}
matchTables := make(map[string]*model.Table)
for _, item := range tables {
match, err := filepath.Match(pattern, item)
if err != nil {
return err
}
if !match {
continue
}
columnData, err := im.FindColumns(schema, item)
if err != nil {
return err
}
table, err := columnData.Convert()
if err != nil {
return err
}
matchTables[item] = table
}
if len(matchTables) == 0 {
return errors.New("no tables matched")
}
generator, err := gen.NewDefaultGenerator(dir, cfg, gen.WithConsoleOption(log), gen.WithPostgreSql())
if err != nil {
return err
}
return generator.StartFromInformationSchema(matchTables, cache)
}

View File

@@ -24,12 +24,12 @@ func TestFromDDl(t *testing.T) {
err := gen.Clean()
assert.Nil(t, err)
err = fromDDl("./user.sql", t.TempDir(), cfg, true, false)
err = fromDDl("./user.sql", t.TempDir(), cfg, true, false, "go_zero")
assert.Equal(t, errNotMatched, err)
// case dir is not exists
unknownDir := filepath.Join(t.TempDir(), "test", "user.sql")
err = fromDDl(unknownDir, t.TempDir(), cfg, true, false)
err = fromDDl(unknownDir, t.TempDir(), cfg, true, false, "go_zero")
assert.True(t, func() bool {
switch err.(type) {
case *os.PathError:
@@ -40,7 +40,7 @@ func TestFromDDl(t *testing.T) {
}())
// case empty src
err = fromDDl("", t.TempDir(), cfg, true, false)
err = fromDDl("", t.TempDir(), cfg, true, false, "go_zero")
if err != nil {
assert.Equal(t, "expected path or path globbing patterns, but nothing found", err.Error())
}
@@ -70,7 +70,7 @@ func TestFromDDl(t *testing.T) {
_, err = os.Stat(user2Sql)
assert.Nil(t, err)
err = fromDDl(filepath.Join(tempDir, "user*.sql"), tempDir, cfg, true, false)
err = fromDDl(filepath.Join(tempDir, "user*.sql"), tempDir, cfg, true, false, "go_zero")
assert.Nil(t, err)
_, err = os.Stat(filepath.Join(tempDir, "usermodel.go"))

View File

@@ -3,9 +3,57 @@ package converter
import (
"fmt"
"strings"
"github.com/zeromicro/ddl-parser/parser"
)
var commonMysqlDataTypeMap = map[string]string{
var commonMysqlDataTypeMap = map[int]string{
// For consistency, all integer types are converted to int64
// number
parser.Bool: "int64",
parser.Boolean: "int64",
parser.TinyInt: "int64",
parser.SmallInt: "int64",
parser.MediumInt: "int64",
parser.Int: "int64",
parser.MiddleInt: "int64",
parser.Int1: "int64",
parser.Int2: "int64",
parser.Int3: "int64",
parser.Int4: "int64",
parser.Int8: "int64",
parser.Integer: "int64",
parser.BigInt: "int64",
parser.Float: "float64",
parser.Float4: "float64",
parser.Float8: "float64",
parser.Double: "float64",
parser.Decimal: "float64",
// date&time
parser.Date: "time.Time",
parser.DateTime: "time.Time",
parser.Timestamp: "time.Time",
parser.Time: "string",
parser.Year: "int64",
// string
parser.Char: "string",
parser.VarChar: "string",
parser.Binary: "string",
parser.VarBinary: "string",
parser.TinyText: "string",
parser.Text: "string",
parser.MediumText: "string",
parser.LongText: "string",
parser.Enum: "string",
parser.Set: "string",
parser.Json: "string",
parser.Blob: "string",
parser.LongBlob: "string",
parser.MediumBlob: "string",
parser.TinyBlob: "string",
}
var commonMysqlDataTypeMap2 = map[string]string{
// For consistency, all integer types are converted to int64
// number
"bool": "int64",
@@ -37,13 +85,27 @@ var commonMysqlDataTypeMap = map[string]string{
"enum": "string",
"set": "string",
"json": "string",
"blob": "string",
"longblob": "string",
"mediumblob": "string",
"tinyblob": "string",
}
// ConvertDataType converts mysql column type into golang type
func ConvertDataType(dataBaseType string, isDefaultNull bool) (string, error) {
tp, ok := commonMysqlDataTypeMap[strings.ToLower(dataBaseType)]
func ConvertDataType(dataBaseType int, isDefaultNull bool) (string, error) {
tp, ok := commonMysqlDataTypeMap[dataBaseType]
if !ok {
return "", fmt.Errorf("unexpected database type: %s", dataBaseType)
return "", fmt.Errorf("unsupported database type: %v", dataBaseType)
}
return mayConvertNullType(tp, isDefaultNull), nil
}
// ConvertStringDataType converts mysql column type into golang type
func ConvertStringDataType(dataBaseType string, isDefaultNull bool) (string, error) {
tp, ok := commonMysqlDataTypeMap2[strings.ToLower(dataBaseType)]
if !ok {
return "", fmt.Errorf("unsupported database type: %s", dataBaseType)
}
return mayConvertNullType(tp, isDefaultNull), nil

View File

@@ -4,25 +4,23 @@ import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/zeromicro/ddl-parser/parser"
)
func TestConvertDataType(t *testing.T) {
v, err := ConvertDataType("tinyint", false)
v, err := ConvertDataType(parser.TinyInt, false)
assert.Nil(t, err)
assert.Equal(t, "int64", v)
v, err = ConvertDataType("tinyint", true)
v, err = ConvertDataType(parser.TinyInt, true)
assert.Nil(t, err)
assert.Equal(t, "sql.NullInt64", v)
v, err = ConvertDataType("timestamp", false)
v, err = ConvertDataType(parser.Timestamp, false)
assert.Nil(t, err)
assert.Equal(t, "time.Time", v)
v, err = ConvertDataType("timestamp", true)
v, err = ConvertDataType(parser.Timestamp, true)
assert.Nil(t, err)
assert.Equal(t, "sql.NullTime", v)
_, err = ConvertDataType("float32", false)
assert.NotNil(t, err)
}

View File

@@ -1,19 +1,20 @@
-- 用户表 --
CREATE TABLE `user` (
`id` bigint(10) NOT NULL AUTO_INCREMENT,
`user` varchar(50) NOT NULL DEFAULT '' COMMENT '用户',
`name` varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '用户名称',
`password` varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '用户密码',
`mobile` varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '手机号',
`gender` char(5) COLLATE utf8mb4_general_ci NOT NULL COMMENT '男|女|未公开',
`nickname` varchar(255) COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '用户昵称',
`create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
`update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `name_index` (`name`),
UNIQUE KEY `name_index2` (`name`),
UNIQUE KEY `user_index` (`user`),
UNIQUE KEY `mobile_index` (`mobile`)
CREATE TABLE `user`
(
`id` bigint(10) NOT NULL AUTO_INCREMENT,
`user` varchar(50) NOT NULL DEFAULT '' COMMENT '用户',
`name` varchar(255) COLLATE utf8mb4_general_ci NULL COMMENT '用户\t名称',
`password` varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '用户\n密码',
`mobile` varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '手机号',
`gender` char(5) COLLATE utf8mb4_general_ci NOT NULL COMMENT '男|女|未公\r开',
`nickname` varchar(255) COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '用户昵称',
`create_time` timestamp NULL,
`update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `name_index` (`name`),
UNIQUE KEY `name_index2` (`name`),
UNIQUE KEY `user_index` (`user`),
UNIQUE KEY `mobile_index` (`mobile`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
CREATE TABLE `student` (

View File

@@ -9,7 +9,7 @@ import (
"github.com/tal-tech/go-zero/tools/goctl/util/stringx"
)
func genDelete(table Table, withCache bool) (string, string, error) {
func genDelete(table Table, withCache, postgreSql bool) (string, string, error) {
keySet := collection.NewSet()
keyVariableSet := collection.NewSet()
keySet.AddStr(table.PrimaryCacheKey.KeyExpression)
@@ -34,8 +34,9 @@ func genDelete(table Table, withCache bool) (string, string, error) {
"lowerStartCamelPrimaryKey": stringx.From(table.PrimaryKey.Name.ToCamel()).Untitle(),
"dataType": table.PrimaryKey.DataType,
"keys": strings.Join(keySet.KeysStr(), "\n"),
"originalPrimaryKey": wrapWithRawString(table.PrimaryKey.Name.Source()),
"originalPrimaryKey": wrapWithRawString(table.PrimaryKey.Name.Source(), postgreSql),
"keyValues": strings.Join(keyVariableSet.KeysStr(), ", "),
"postgreSql": postgreSql,
})
if err != nil {
return "", "", err

View File

@@ -6,7 +6,7 @@ import (
"github.com/tal-tech/go-zero/tools/goctl/util/stringx"
)
func genFindOne(table Table, withCache bool) (string, string, error) {
func genFindOne(table Table, withCache, postgreSql bool) (string, string, error) {
camel := table.Name.ToCamel()
text, err := util.LoadTemplate(category, findOneTemplateFile, template.FindOne)
if err != nil {
@@ -19,11 +19,12 @@ func genFindOne(table Table, withCache bool) (string, string, error) {
"withCache": withCache,
"upperStartCamelObject": camel,
"lowerStartCamelObject": stringx.From(camel).Untitle(),
"originalPrimaryKey": wrapWithRawString(table.PrimaryKey.Name.Source()),
"originalPrimaryKey": wrapWithRawString(table.PrimaryKey.Name.Source(), postgreSql),
"lowerStartCamelPrimaryKey": stringx.From(table.PrimaryKey.Name.ToCamel()).Untitle(),
"dataType": table.PrimaryKey.DataType,
"cacheKey": table.PrimaryCacheKey.KeyExpression,
"cacheKeyVariable": table.PrimaryCacheKey.KeyLeft,
"postgreSql": postgreSql,
})
if err != nil {
return "", "", err

View File

@@ -15,7 +15,7 @@ type findOneCode struct {
cacheExtra string
}
func genFindOneByField(table Table, withCache bool) (*findOneCode, error) {
func genFindOneByField(table Table, withCache, postgreSql bool) (*findOneCode, error) {
text, err := util.LoadTemplate(category, findOneByFieldTemplateFile, template.FindOneByField)
if err != nil {
return nil, err
@@ -25,7 +25,7 @@ func genFindOneByField(table Table, withCache bool) (*findOneCode, error) {
var list []string
camelTableName := table.Name.ToCamel()
for _, key := range table.UniqueCacheKey {
in, paramJoinString, originalFieldString := convertJoin(key)
in, paramJoinString, originalFieldString := convertJoin(key, postgreSql)
output, err := t.Execute(map[string]interface{}{
"upperStartCamelObject": camelTableName,
@@ -38,6 +38,7 @@ func genFindOneByField(table Table, withCache bool) (*findOneCode, error) {
"lowerStartCamelField": paramJoinString,
"upperStartCamelPrimaryKey": table.PrimaryKey.Name.ToCamel(),
"originalField": originalFieldString,
"postgreSql": postgreSql,
})
if err != nil {
return nil, err
@@ -87,7 +88,8 @@ func genFindOneByField(table Table, withCache bool) (*findOneCode, error) {
"upperStartCamelObject": camelTableName,
"primaryKeyLeft": table.PrimaryCacheKey.VarLeft,
"lowerStartCamelObject": stringx.From(camelTableName).Untitle(),
"originalPrimaryField": wrapWithRawString(table.PrimaryKey.Name.Source()),
"originalPrimaryField": wrapWithRawString(table.PrimaryKey.Name.Source(), postgreSql),
"postgreSql": postgreSql,
})
if err != nil {
return nil, err
@@ -106,13 +108,17 @@ func genFindOneByField(table Table, withCache bool) (*findOneCode, error) {
}, nil
}
func convertJoin(key Key) (in, paramJoinString, originalFieldString string) {
func convertJoin(key Key, postgreSql bool) (in, paramJoinString, originalFieldString string) {
var inJoin, paramJoin, argJoin Join
for _, f := range key.Fields {
for index, f := range key.Fields {
param := stringx.From(f.Name.ToCamel()).Untitle()
inJoin = append(inJoin, fmt.Sprintf("%s %s", param, f.DataType))
paramJoin = append(paramJoin, param)
argJoin = append(argJoin, fmt.Sprintf("%s = ?", wrapWithRawString(f.Name.Source())))
if postgreSql {
argJoin = append(argJoin, fmt.Sprintf("%s = $%d", wrapWithRawString(f.Name.Source(), postgreSql), index+1))
} else {
argJoin = append(argJoin, fmt.Sprintf("%s = ?", wrapWithRawString(f.Name.Source(), postgreSql)))
}
}
if len(inJoin) > 0 {
in = inJoin.With(", ").Source()

View File

@@ -29,8 +29,9 @@ type (
// source string
dir string
console.Console
pkg string
cfg *config.Config
pkg string
cfg *config.Config
isPostgreSql bool
}
// Option defines a function with argument defaultGenerator
@@ -84,14 +85,21 @@ func WithConsoleOption(c console.Console) Option {
}
}
// WithPostgreSql marks defaultGenerator.isPostgreSql true
func WithPostgreSql() Option {
return func(generator *defaultGenerator) {
generator.isPostgreSql = true
}
}
func newDefaultOption() Option {
return func(generator *defaultGenerator) {
generator.Console = console.NewColorConsole()
}
}
func (g *defaultGenerator) StartFromDDL(source string, withCache bool) error {
modelList, err := g.genFromDDL(source, withCache)
func (g *defaultGenerator) StartFromDDL(filename string, withCache bool, database string) error {
modelList, err := g.genFromDDL(filename, withCache, database)
if err != nil {
return err
}
@@ -174,21 +182,20 @@ func (g *defaultGenerator) createFile(modelList map[string]string) error {
}
// ret1: key-table name,value-code
func (g *defaultGenerator) genFromDDL(source string, withCache bool) (map[string]string, error) {
ddlList := g.split(source)
func (g *defaultGenerator) genFromDDL(filename string, withCache bool, database string) (map[string]string, error) {
m := make(map[string]string)
for _, ddl := range ddlList {
table, err := parser.Parse(ddl)
tables, err := parser.Parse(filename, database)
if err != nil {
return nil, err
}
for _, e := range tables {
code, err := g.genModel(*e, withCache)
if err != nil {
return nil, err
}
code, err := g.genModel(*table, withCache)
if err != nil {
return nil, err
}
m[table.Name.Source()] = code
m[e.Name.Source()] = code
}
return m, nil
@@ -220,34 +227,34 @@ func (g *defaultGenerator) genModel(in parser.Table, withCache bool) (string, er
table.UniqueCacheKey = uniqueKey
table.ContainsUniqueCacheKey = len(uniqueKey) > 0
varsCode, err := genVars(table, withCache)
varsCode, err := genVars(table, withCache, g.isPostgreSql)
if err != nil {
return "", err
}
insertCode, insertCodeMethod, err := genInsert(table, withCache)
insertCode, insertCodeMethod, err := genInsert(table, withCache, g.isPostgreSql)
if err != nil {
return "", err
}
findCode := make([]string, 0)
findOneCode, findOneCodeMethod, err := genFindOne(table, withCache)
findOneCode, findOneCodeMethod, err := genFindOne(table, withCache, g.isPostgreSql)
if err != nil {
return "", err
}
ret, err := genFindOneByField(table, withCache)
ret, err := genFindOneByField(table, withCache, g.isPostgreSql)
if err != nil {
return "", err
}
findCode = append(findCode, findOneCode, ret.findOneMethod)
updateCode, updateCodeMethod, err := genUpdate(table, withCache)
updateCode, updateCodeMethod, err := genUpdate(table, withCache, g.isPostgreSql)
if err != nil {
return "", err
}
deleteCode, deleteCodeMethod, err := genDelete(table, withCache)
deleteCode, deleteCodeMethod, err := genDelete(table, withCache, g.isPostgreSql)
if err != nil {
return "", err
}
@@ -259,7 +266,7 @@ func (g *defaultGenerator) genModel(in parser.Table, withCache bool) (string, er
return "", err
}
newCode, err := genNew(table, withCache)
newCode, err := genNew(table, withCache, g.isPostgreSql)
if err != nil {
return "", err
}
@@ -310,7 +317,11 @@ func (g *defaultGenerator) executeModel(code *code) (*bytes.Buffer, error) {
return output, nil
}
func wrapWithRawString(v string) string {
func wrapWithRawString(v string, postgreSql bool) string {
if postgreSql {
return v
}
if v == "`" {
return v
}

View File

@@ -2,6 +2,7 @@ package gen
import (
"database/sql"
"io/ioutil"
"os"
"path/filepath"
"strings"
@@ -20,6 +21,11 @@ var source = "CREATE TABLE `test_user` (\n `id` bigint NOT NULL AUTO_INCREMENT,
func TestCacheModel(t *testing.T) {
logx.Disable()
_ = Clean()
sqlFile := filepath.Join(t.TempDir(), "tmp.sql")
err := ioutil.WriteFile(sqlFile, []byte(source), 0o777)
assert.Nil(t, err)
dir := filepath.Join(t.TempDir(), "./testmodel")
cacheDir := filepath.Join(dir, "cache")
noCacheDir := filepath.Join(dir, "nocache")
@@ -28,7 +34,7 @@ func TestCacheModel(t *testing.T) {
})
assert.Nil(t, err)
err = g.StartFromDDL(source, true)
err = g.StartFromDDL(sqlFile, true, "go_zero")
assert.Nil(t, err)
assert.True(t, func() bool {
_, err := os.Stat(filepath.Join(cacheDir, "TestUserModel.go"))
@@ -39,7 +45,7 @@ func TestCacheModel(t *testing.T) {
})
assert.Nil(t, err)
err = g.StartFromDDL(source, false)
err = g.StartFromDDL(sqlFile, false, "go_zero")
assert.Nil(t, err)
assert.True(t, func() bool {
_, err := os.Stat(filepath.Join(noCacheDir, "testusermodel.go"))
@@ -50,6 +56,11 @@ func TestCacheModel(t *testing.T) {
func TestNamingModel(t *testing.T) {
logx.Disable()
_ = Clean()
sqlFile := filepath.Join(t.TempDir(), "tmp.sql")
err := ioutil.WriteFile(sqlFile, []byte(source), 0o777)
assert.Nil(t, err)
dir, _ := filepath.Abs("./testmodel")
camelDir := filepath.Join(dir, "camel")
snakeDir := filepath.Join(dir, "snake")
@@ -61,7 +72,7 @@ func TestNamingModel(t *testing.T) {
})
assert.Nil(t, err)
err = g.StartFromDDL(source, true)
err = g.StartFromDDL(sqlFile, true, "go_zero")
assert.Nil(t, err)
assert.True(t, func() bool {
_, err := os.Stat(filepath.Join(camelDir, "TestUserModel.go"))
@@ -72,7 +83,7 @@ func TestNamingModel(t *testing.T) {
})
assert.Nil(t, err)
err = g.StartFromDDL(source, true)
err = g.StartFromDDL(sqlFile, true, "go_zero")
assert.Nil(t, err)
assert.True(t, func() bool {
_, err := os.Stat(filepath.Join(snakeDir, "test_user_model.go"))
@@ -81,10 +92,11 @@ func TestNamingModel(t *testing.T) {
}
func TestWrapWithRawString(t *testing.T) {
assert.Equal(t, "``", wrapWithRawString(""))
assert.Equal(t, "``", wrapWithRawString("``"))
assert.Equal(t, "`a`", wrapWithRawString("a"))
assert.Equal(t, "` `", wrapWithRawString(" "))
assert.Equal(t, "``", wrapWithRawString("", false))
assert.Equal(t, "``", wrapWithRawString("``", false))
assert.Equal(t, "`a`", wrapWithRawString("a", false))
assert.Equal(t, "a", wrapWithRawString("a", true))
assert.Equal(t, "` `", wrapWithRawString(" ", false))
}
func TestFields(t *testing.T) {

View File

@@ -1,6 +1,7 @@
package gen
import (
"fmt"
"strings"
"github.com/tal-tech/go-zero/core/collection"
@@ -9,7 +10,7 @@ import (
"github.com/tal-tech/go-zero/tools/goctl/util/stringx"
)
func genInsert(table Table, withCache bool) (string, string, error) {
func genInsert(table Table, withCache, postgreSql bool) (string, string, error) {
keySet := collection.NewSet()
keyVariableSet := collection.NewSet()
for _, key := range table.UniqueCacheKey {
@@ -19,6 +20,7 @@ func genInsert(table Table, withCache bool) (string, string, error) {
expressions := make([]string, 0)
expressionValues := make([]string, 0)
var count int
for _, field := range table.Fields {
camel := field.Name.ToCamel()
if camel == "CreateTime" || camel == "UpdateTime" {
@@ -31,7 +33,12 @@ func genInsert(table Table, withCache bool) (string, string, error) {
}
}
expressions = append(expressions, "?")
count += 1
if postgreSql {
expressions = append(expressions, fmt.Sprintf("$%d", count))
} else {
expressions = append(expressions, "?")
}
expressionValues = append(expressionValues, "data."+camel)
}

View File

@@ -13,9 +13,9 @@ import (
type Key struct {
// VarLeft describes the variable of cache key expression which likes cacheUserIdPrefix
VarLeft string
// VarRight describes the value of cache key expression which likes "cache#user#id#"
// VarRight describes the value of cache key expression which likes "cache:user:id:"
VarRight string
// VarExpression describes the cache key expression which likes cacheUserIdPrefix = "cache#user#id#"
// VarExpression describes the cache key expression which likes cacheUserIdPrefix = "cache:user:id:"
VarExpression string
// KeyLeft describes the variable of key definition expression which likes userKey
KeyLeft string
@@ -39,9 +39,9 @@ type Join []string
func genCacheKeys(table parser.Table) (Key, []Key) {
var primaryKey Key
var uniqueKey []Key
primaryKey = genCacheKey(table.Name, []*parser.Field{&table.PrimaryKey.Field})
primaryKey = genCacheKey(table.Db, table.Name, []*parser.Field{&table.PrimaryKey.Field})
for _, each := range table.UniqueIndex {
uniqueKey = append(uniqueKey, genCacheKey(table.Name, each))
uniqueKey = append(uniqueKey, genCacheKey(table.Db, table.Name, each))
}
sort.Slice(uniqueKey, func(i, j int) bool {
return uniqueKey[i].VarLeft < uniqueKey[j].VarLeft
@@ -50,7 +50,7 @@ func genCacheKeys(table parser.Table) (Key, []Key) {
return primaryKey, uniqueKey
}
func genCacheKey(table stringx.String, in []*parser.Field) Key {
func genCacheKey(db stringx.String, table stringx.String, in []*parser.Field) Key {
var (
varLeftJoin, varRightJon, fieldNameJoin Join
varLeft, varRight, varExpression string
@@ -59,9 +59,9 @@ func genCacheKey(table stringx.String, in []*parser.Field) Key {
keyLeft, keyRight, dataKeyRight, keyExpression, dataKeyExpression string
)
varLeftJoin = append(varLeftJoin, "cache", table.Source())
varRightJon = append(varRightJon, "cache", table.Source())
keyLeftJoin = append(keyLeftJoin, table.Source())
varLeftJoin = append(varLeftJoin, "cache", db.Source(), table.Source())
varRightJon = append(varRightJon, "cache", db.Source(), table.Source())
keyLeftJoin = append(keyLeftJoin, db.Source(), table.Source())
for _, each := range in {
varLeftJoin = append(varLeftJoin, each.Name.Source())
@@ -76,12 +76,12 @@ func genCacheKey(table stringx.String, in []*parser.Field) Key {
keyLeftJoin = append(keyLeftJoin, "key")
varLeft = varLeftJoin.Camel().With("").Untitle()
varRight = fmt.Sprintf(`"%s"`, varRightJon.Camel().Untitle().With("#").Source()+"#")
varRight = fmt.Sprintf(`"%s"`, varRightJon.Camel().Untitle().With(":").Source()+":")
varExpression = fmt.Sprintf(`%s = %s`, varLeft, varRight)
keyLeft = keyLeftJoin.Camel().With("").Untitle()
keyRight = fmt.Sprintf(`fmt.Sprintf("%s%s", %s, %s)`, "%s", keyRightArgJoin.With("").Source(), varLeft, keyRightJoin.With(", ").Source())
dataKeyRight = fmt.Sprintf(`fmt.Sprintf("%s%s", %s, %s)`, "%s", keyRightArgJoin.With("").Source(), varLeft, dataRightJoin.With(", ").Source())
keyRight = fmt.Sprintf(`fmt.Sprintf("%s%s", %s, %s)`, "%s", keyRightArgJoin.With(":").Source(), varLeft, keyRightJoin.With(", ").Source())
dataKeyRight = fmt.Sprintf(`fmt.Sprintf("%s%s", %s, %s)`, "%s", keyRightArgJoin.With(":").Source(), varLeft, dataRightJoin.With(", ").Source())
keyExpression = fmt.Sprintf("%s := %s", keyLeft, keyRight)
dataKeyExpression = fmt.Sprintf("%s := %s", keyLeft, dataKeyRight)

View File

@@ -11,35 +11,32 @@ import (
func TestGenCacheKeys(t *testing.T) {
primaryField := &parser.Field{
Name: stringx.From("id"),
DataBaseType: "bigint",
DataType: "int64",
Comment: "自增id",
SeqInIndex: 1,
Name: stringx.From("id"),
DataType: "int64",
Comment: "自增id",
SeqInIndex: 1,
}
mobileField := &parser.Field{
Name: stringx.From("mobile"),
DataBaseType: "varchar",
DataType: "string",
Comment: "手机号",
SeqInIndex: 1,
Name: stringx.From("mobile"),
DataType: "string",
Comment: "手机号",
SeqInIndex: 1,
}
classField := &parser.Field{
Name: stringx.From("class"),
DataBaseType: "varchar",
DataType: "string",
Comment: "班级",
SeqInIndex: 1,
Name: stringx.From("class"),
DataType: "string",
Comment: "班级",
SeqInIndex: 1,
}
nameField := &parser.Field{
Name: stringx.From("name"),
DataBaseType: "varchar",
DataType: "string",
Comment: "姓名",
SeqInIndex: 2,
Name: stringx.From("name"),
DataType: "string",
Comment: "姓名",
SeqInIndex: 2,
}
primariCacheKey, uniqueCacheKey := genCacheKeys(parser.Table{
Name: stringx.From("user"),
Db: stringx.From("go_zero"),
PrimaryKey: parser.Primary{
Field: *primaryField,
AutoIncrement: true,
@@ -53,23 +50,20 @@ func TestGenCacheKeys(t *testing.T) {
nameField,
},
},
NormalIndex: nil,
Fields: []*parser.Field{
primaryField,
mobileField,
classField,
nameField,
{
Name: stringx.From("createTime"),
DataBaseType: "timestamp",
DataType: "time.Time",
Comment: "创建时间",
Name: stringx.From("createTime"),
DataType: "time.Time",
Comment: "创建时间",
},
{
Name: stringx.From("updateTime"),
DataBaseType: "timestamp",
DataType: "time.Time",
Comment: "更新时间",
Name: stringx.From("updateTime"),
DataType: "time.Time",
Comment: "更新时间",
},
},
})
@@ -77,14 +71,14 @@ func TestGenCacheKeys(t *testing.T) {
t.Run("primaryCacheKey", func(t *testing.T) {
assert.Equal(t, true, func() bool {
return cacheKeyEqual(primariCacheKey, Key{
VarLeft: "cacheUserIdPrefix",
VarRight: `"cache#user#id#"`,
VarExpression: `cacheUserIdPrefix = "cache#user#id#"`,
KeyLeft: "userIdKey",
KeyRight: `fmt.Sprintf("%s%v", cacheUserIdPrefix, id)`,
DataKeyRight: `fmt.Sprintf("%s%v", cacheUserIdPrefix, data.Id)`,
KeyExpression: `userIdKey := fmt.Sprintf("%s%v", cacheUserIdPrefix, id)`,
DataKeyExpression: `userIdKey := fmt.Sprintf("%s%v", cacheUserIdPrefix, data.Id)`,
VarLeft: "cacheGoZeroUserIdPrefix",
VarRight: `"cache:goZero:user:id:"`,
VarExpression: `cacheGoZeroUserIdPrefix = "cache:goZero:user:id:"`,
KeyLeft: "goZeroUserIdKey",
KeyRight: `fmt.Sprintf("%s%v", cacheGoZeroUserIdPrefix, id)`,
DataKeyRight: `fmt.Sprintf("%s%v", cacheGoZeroUserIdPrefix, data.Id)`,
KeyExpression: `goZeroUserIdKey := fmt.Sprintf("%s%v", cacheGoZeroUserIdPrefix, id)`,
DataKeyExpression: `goZeroUserIdKey := fmt.Sprintf("%s%v", cacheGoZeroUserIdPrefix, data.Id)`,
FieldNameJoin: []string{"id"},
})
}())
@@ -94,25 +88,25 @@ func TestGenCacheKeys(t *testing.T) {
assert.Equal(t, true, func() bool {
expected := []Key{
{
VarLeft: "cacheUserClassNamePrefix",
VarRight: `"cache#user#class#name#"`,
VarExpression: `cacheUserClassNamePrefix = "cache#user#class#name#"`,
KeyLeft: "userClassNameKey",
KeyRight: `fmt.Sprintf("%s%v%v", cacheUserClassNamePrefix, class, name)`,
DataKeyRight: `fmt.Sprintf("%s%v%v", cacheUserClassNamePrefix, data.Class, data.Name)`,
KeyExpression: `userClassNameKey := fmt.Sprintf("%s%v%v", cacheUserClassNamePrefix, class, name)`,
DataKeyExpression: `userClassNameKey := fmt.Sprintf("%s%v%v", cacheUserClassNamePrefix, data.Class, data.Name)`,
VarLeft: "cacheGoZeroUserClassNamePrefix",
VarRight: `"cache:goZero:user:class:name:"`,
VarExpression: `cacheGoZeroUserClassNamePrefix = "cache:goZero:user:class:name:"`,
KeyLeft: "goZeroUserClassNameKey",
KeyRight: `fmt.Sprintf("%s%v:%v", cacheGoZeroUserClassNamePrefix, class, name)`,
DataKeyRight: `fmt.Sprintf("%s%v:%v", cacheGoZeroUserClassNamePrefix, data.Class, data.Name)`,
KeyExpression: `goZeroUserClassNameKey := fmt.Sprintf("%s%v:%v", cacheGoZeroUserClassNamePrefix, class, name)`,
DataKeyExpression: `goZeroUserClassNameKey := fmt.Sprintf("%s%v:%v", cacheGoZeroUserClassNamePrefix, data.Class, data.Name)`,
FieldNameJoin: []string{"class", "name"},
},
{
VarLeft: "cacheUserMobilePrefix",
VarRight: `"cache#user#mobile#"`,
VarExpression: `cacheUserMobilePrefix = "cache#user#mobile#"`,
KeyLeft: "userMobileKey",
KeyRight: `fmt.Sprintf("%s%v", cacheUserMobilePrefix, mobile)`,
DataKeyRight: `fmt.Sprintf("%s%v", cacheUserMobilePrefix, data.Mobile)`,
KeyExpression: `userMobileKey := fmt.Sprintf("%s%v", cacheUserMobilePrefix, mobile)`,
DataKeyExpression: `userMobileKey := fmt.Sprintf("%s%v", cacheUserMobilePrefix, data.Mobile)`,
VarLeft: "cacheGoZeroUserMobilePrefix",
VarRight: `"cache:goZero:user:mobile:"`,
VarExpression: `cacheGoZeroUserMobilePrefix = "cache:goZero:user:mobile:"`,
KeyLeft: "goZeroUserMobileKey",
KeyRight: `fmt.Sprintf("%s%v", cacheGoZeroUserMobilePrefix, mobile)`,
DataKeyRight: `fmt.Sprintf("%s%v", cacheGoZeroUserMobilePrefix, data.Mobile)`,
KeyExpression: `goZeroUserMobileKey := fmt.Sprintf("%s%v", cacheGoZeroUserMobilePrefix, mobile)`,
DataKeyExpression: `goZeroUserMobileKey := fmt.Sprintf("%s%v", cacheGoZeroUserMobilePrefix, data.Mobile)`,
FieldNameJoin: []string{"mobile"},
},
}
@@ -136,7 +130,7 @@ func TestGenCacheKeys(t *testing.T) {
})
}
func cacheKeyEqual(k1 Key, k2 Key) bool {
func cacheKeyEqual(k1, k2 Key) bool {
k1Join := k1.FieldNameJoin
k2Join := k2.FieldNameJoin
sort.Strings(k1Join)

View File

@@ -1,20 +1,27 @@
package gen
import (
"fmt"
"github.com/tal-tech/go-zero/tools/goctl/model/sql/template"
"github.com/tal-tech/go-zero/tools/goctl/util"
)
func genNew(table Table, withCache bool) (string, error) {
func genNew(table Table, withCache, postgreSql bool) (string, error) {
text, err := util.LoadTemplate(category, modelNewTemplateFile, template.New)
if err != nil {
return "", err
}
t := fmt.Sprintf(`"%s"`, wrapWithRawString(table.Name.Source(), postgreSql))
if postgreSql {
t = "`" + fmt.Sprintf(`"%s"."%s"`, table.Db.Source(), table.Name.Source()) + "`"
}
output, err := util.With("new").
Parse(text).
Execute(map[string]interface{}{
"table": wrapWithRawString(table.Name.Source()),
"table": t,
"withCache": withCache,
"upperStartCamelObject": table.Name.ToCamel(),
})

View File

@@ -9,7 +9,7 @@ import (
"github.com/tal-tech/go-zero/tools/goctl/util/stringx"
)
func genUpdate(table Table, withCache bool) (string, string, error) {
func genUpdate(table Table, withCache, postgreSql bool) (string, string, error) {
expressionValues := make([]string, 0)
for _, field := range table.Fields {
camel := field.Name.ToCamel()
@@ -50,8 +50,9 @@ func genUpdate(table Table, withCache bool) (string, string, error) {
"primaryCacheKey": table.PrimaryCacheKey.DataKeyExpression,
"primaryKeyVariable": table.PrimaryCacheKey.KeyLeft,
"lowerStartCamelObject": stringx.From(camelTableName).Untitle(),
"originalPrimaryKey": wrapWithRawString(table.PrimaryKey.Name.Source()),
"originalPrimaryKey": wrapWithRawString(table.PrimaryKey.Name.Source(), postgreSql),
"expressionValues": strings.Join(expressionValues, ", "),
"postgreSql": postgreSql,
})
if err != nil {
return "", "", nil

View File

@@ -8,7 +8,7 @@ import (
"github.com/tal-tech/go-zero/tools/goctl/util/stringx"
)
func genVars(table Table, withCache bool) (string, error) {
func genVars(table Table, withCache, postgreSql bool) (string, error) {
keys := make([]string, 0)
keys = append(keys, table.PrimaryCacheKey.VarExpression)
for _, v := range table.UniqueCacheKey {
@@ -27,8 +27,9 @@ func genVars(table Table, withCache bool) (string, error) {
"upperStartCamelObject": camel,
"cacheKeys": strings.Join(keys, "\n"),
"autoIncrement": table.PrimaryKey.AutoIncrement,
"originalPrimaryKey": wrapWithRawString(table.PrimaryKey.Name.Source()),
"originalPrimaryKey": wrapWithRawString(table.PrimaryKey.Name.Source(), postgreSql),
"withCache": withCache,
"postgreSql": postgreSql,
})
if err != nil {
return "", err

View File

@@ -5,6 +5,7 @@ import (
"sort"
"github.com/tal-tech/go-zero/core/stores/sqlx"
"github.com/tal-tech/go-zero/tools/goctl/model/sql/util"
)
const indexPri = "PRIMARY"
@@ -151,6 +152,7 @@ func (c *ColumnData) Convert() (*Table, error) {
m := make(map[string][]*Column)
for _, each := range c.Columns {
each.Comment = util.TrimNewLine(each.Comment)
if each.Index != nil {
m[each.Index.IndexName] = append(m[each.Index.IndexName], each)
}

View File

@@ -0,0 +1,234 @@
package model
import (
"database/sql"
"strings"
"github.com/tal-tech/go-zero/core/stores/sqlx"
)
var (
p2m = map[string]string{
"int8": "bigint",
"numeric": "bigint",
"float8": "double",
"float4": "float",
"int2": "smallint",
"int4": "integer",
}
)
// PostgreSqlModel gets table information from information_schema、pg_catalog
type PostgreSqlModel struct {
conn sqlx.SqlConn
}
// PostgreColumn describes a column in table
type PostgreColumn struct {
Num sql.NullInt32 `db:"num"`
Field sql.NullString `db:"field"`
Type sql.NullString `db:"type"`
NotNull sql.NullBool `db:"not_null"`
Comment sql.NullString `db:"comment"`
ColumnDefault sql.NullString `db:"column_default"`
IdentityIncrement sql.NullInt32 `db:"identity_increment"`
}
// PostgreIndex describes an index for a column
type PostgreIndex struct {
IndexName sql.NullString `db:"index_name"`
IndexId sql.NullInt32 `db:"index_id"`
IsUnique sql.NullBool `db:"is_unique"`
IsPrimary sql.NullBool `db:"is_primary"`
ColumnName sql.NullString `db:"column_name"`
IndexSort sql.NullInt32 `db:"index_sort"`
}
// NewPostgreSqlModel creates an instance and return
func NewPostgreSqlModel(conn sqlx.SqlConn) *PostgreSqlModel {
return &PostgreSqlModel{
conn: conn,
}
}
// GetAllTables selects all tables from TABLE_SCHEMA
func (m *PostgreSqlModel) GetAllTables(schema string) ([]string, error) {
query := `select table_name from information_schema.tables where table_schema = $1`
var tables []string
err := m.conn.QueryRows(&tables, query, schema)
if err != nil {
return nil, err
}
return tables, nil
}
// FindColumns return columns in specified database and table
func (m *PostgreSqlModel) FindColumns(schema, table string) (*ColumnData, error) {
querySql := `select t.num,t.field,t.type,t.not_null,t.comment, c.column_default, identity_increment
from (
SELECT a.attnum AS num,
c.relname,
a.attname AS field,
t.typname AS type,
a.atttypmod AS lengthvar,
a.attnotnull AS not_null,
b.description AS comment
FROM pg_class c,
pg_attribute a
LEFT OUTER JOIN pg_description b ON a.attrelid = b.objoid AND a.attnum = b.objsubid,
pg_type t
WHERE c.relname = $1
and a.attnum > 0
and a.attrelid = c.oid
and a.atttypid = t.oid
ORDER BY a.attnum) AS t
left join information_schema.columns AS c on t.relname = c.table_name
and t.field = c.column_name and c.table_schema = $2`
var reply []*PostgreColumn
err := m.conn.QueryRowsPartial(&reply, querySql, table, schema)
if err != nil {
return nil, err
}
list, err := m.getColumns(schema, table, reply)
if err != nil {
return nil, err
}
var columnData ColumnData
columnData.Db = schema
columnData.Table = table
columnData.Columns = list
return &columnData, nil
}
func (m *PostgreSqlModel) getColumns(schema, table string, in []*PostgreColumn) ([]*Column, error) {
index, err := m.getIndex(schema, table)
if err != nil {
return nil, err
}
var list []*Column
for _, e := range in {
var dft interface{}
if len(e.ColumnDefault.String) > 0 {
dft = e.ColumnDefault
}
isNullAble := "YES"
if e.NotNull.Bool {
isNullAble = "NO"
}
extra := "auto_increment"
if e.IdentityIncrement.Int32 != 1 {
extra = ""
}
if len(index[e.Field.String]) > 0 {
for _, i := range index[e.Field.String] {
list = append(list, &Column{
DbColumn: &DbColumn{
Name: e.Field.String,
DataType: m.convertPostgreSqlTypeIntoMysqlType(e.Type.String),
Extra: extra,
Comment: e.Comment.String,
ColumnDefault: dft,
IsNullAble: isNullAble,
OrdinalPosition: int(e.Num.Int32),
},
Index: i,
})
}
} else {
list = append(list, &Column{
DbColumn: &DbColumn{
Name: e.Field.String,
DataType: m.convertPostgreSqlTypeIntoMysqlType(e.Type.String),
Extra: extra,
Comment: e.Comment.String,
ColumnDefault: dft,
IsNullAble: isNullAble,
OrdinalPosition: int(e.Num.Int32),
},
})
}
}
return list, nil
}
func (m *PostgreSqlModel) convertPostgreSqlTypeIntoMysqlType(in string) string {
r, ok := p2m[strings.ToLower(in)]
if ok {
return r
}
return in
}
func (m *PostgreSqlModel) getIndex(schema, table string) (map[string][]*DbIndex, error) {
indexes, err := m.FindIndex(schema, table)
if err != nil {
return nil, err
}
var index = make(map[string][]*DbIndex)
for _, e := range indexes {
if e.IsPrimary.Bool {
index[e.ColumnName.String] = append(index[e.ColumnName.String], &DbIndex{
IndexName: indexPri,
SeqInIndex: int(e.IndexSort.Int32),
})
continue
}
nonUnique := 0
if !e.IsUnique.Bool {
nonUnique = 1
}
index[e.ColumnName.String] = append(index[e.ColumnName.String], &DbIndex{
IndexName: e.IndexName.String,
NonUnique: nonUnique,
SeqInIndex: int(e.IndexSort.Int32),
})
}
return index, nil
}
// FindIndex finds index with given schema, table and column.
func (m *PostgreSqlModel) FindIndex(schema, table string) ([]*PostgreIndex, error) {
querySql := `select A.INDEXNAME AS index_name,
C.INDEXRELID AS index_id,
C.INDISUNIQUE AS is_unique,
C.INDISPRIMARY AS is_primary,
G.ATTNAME AS column_name,
G.attnum AS index_sort
from PG_AM B
left join PG_CLASS F on
B.OID = F.RELAM
left join PG_STAT_ALL_INDEXES E on
F.OID = E.INDEXRELID
left join PG_INDEX C on
E.INDEXRELID = C.INDEXRELID
left outer join PG_DESCRIPTION D on
C.INDEXRELID = D.OBJOID,
PG_INDEXES A,
pg_attribute G
where A.SCHEMANAME = E.SCHEMANAME
and A.TABLENAME = E.RELNAME
and A.INDEXNAME = E.INDEXRELNAME
and F.oid = G.attrelid
and E.SCHEMANAME = $1
and E.RELNAME = $2
order by C.INDEXRELID,G.attnum`
var reply []*PostgreIndex
err := m.conn.QueryRowsPartial(&reply, querySql, schema, table)
if err != nil {
return nil, err
}
return reply, nil
}

View File

@@ -1,11 +0,0 @@
package parser
import (
"errors"
)
var (
errUnsupportDDL = errors.New("unexpected type")
errTableBodyNotFound = errors.New("create table spec not found")
errPrimaryKey = errors.New("unexpected join primary key")
)

View File

@@ -2,15 +2,17 @@ package parser
import (
"fmt"
"path/filepath"
"sort"
"strings"
"github.com/tal-tech/go-zero/core/collection"
"github.com/tal-tech/go-zero/tools/goctl/model/sql/converter"
"github.com/tal-tech/go-zero/tools/goctl/model/sql/model"
"github.com/tal-tech/go-zero/tools/goctl/model/sql/util"
"github.com/tal-tech/go-zero/tools/goctl/util/console"
"github.com/tal-tech/go-zero/tools/goctl/util/stringx"
"github.com/xwb1989/sqlparser"
"github.com/zeromicro/ddl-parser/parser"
)
const timeImport = "time.Time"
@@ -19,9 +21,9 @@ type (
// Table describes a mysql table
Table struct {
Name stringx.String
Db stringx.String
PrimaryKey Primary
UniqueIndex map[string][]*Field
NormalIndex map[string][]*Field
Fields []*Field
}
@@ -34,7 +36,6 @@ type (
// Field describes a table field
Field struct {
Name stringx.String
DataBaseType string
DataType string
Comment string
SeqInIndex int
@@ -46,73 +47,116 @@ type (
)
// Parse parses ddl into golang structure
func Parse(ddl string) (*Table, error) {
stmt, err := sqlparser.ParseStrictDDL(ddl)
func Parse(filename string, database string) ([]*Table, error) {
p := parser.NewParser()
tables, err := p.From(filename)
if err != nil {
return nil, err
}
ddlStmt, ok := stmt.(*sqlparser.DDL)
if !ok {
return nil, errUnsupportDDL
indexNameGen := func(column ...string) string {
return strings.Join(column, "_")
}
action := ddlStmt.Action
if action != sqlparser.CreateStr {
return nil, fmt.Errorf("expected [CREATE] action,but found: %s", action)
}
prefix := filepath.Base(filename)
var list []*Table
for _, e := range tables {
columns := e.Columns
tableName := ddlStmt.NewName.Name.String()
tableSpec := ddlStmt.TableSpec
if tableSpec == nil {
return nil, errTableBodyNotFound
}
var (
primaryColumnSet = collection.NewSet()
columns := tableSpec.Columns
indexes := tableSpec.Indexes
primaryColumn, uniqueKeyMap, normalKeyMap, err := convertIndexes(indexes)
if err != nil {
return nil, err
}
primaryColumn string
uniqueKeyMap = make(map[string][]string)
normalKeyMap = make(map[string][]string)
)
primaryKey, fieldM, err := convertColumns(columns, primaryColumn)
if err != nil {
return nil, err
}
for _, column := range columns {
if column.Constraint != nil {
if column.Constraint.Primary {
primaryColumnSet.AddStr(column.Name)
}
var fields []*Field
for _, e := range fieldM {
fields = append(fields, e)
}
if column.Constraint.Unique {
indexName := indexNameGen(column.Name, "unique")
uniqueKeyMap[indexName] = []string{column.Name}
}
var (
uniqueIndex = make(map[string][]*Field)
normalIndex = make(map[string][]*Field)
)
for indexName, each := range uniqueKeyMap {
for _, columnName := range each {
uniqueIndex[indexName] = append(uniqueIndex[indexName], fieldM[columnName])
if column.Constraint.Key {
indexName := indexNameGen(column.Name, "idx")
uniqueKeyMap[indexName] = []string{column.Name}
}
}
}
}
for indexName, each := range normalKeyMap {
for _, columnName := range each {
normalIndex[indexName] = append(normalIndex[indexName], fieldM[columnName])
for _, e := range e.Constraints {
if len(e.ColumnPrimaryKey) > 1 {
return nil, fmt.Errorf("%s: unexpected join primary key", prefix)
}
if len(e.ColumnPrimaryKey) == 1 {
primaryColumn = e.ColumnPrimaryKey[0]
primaryColumnSet.AddStr(e.ColumnPrimaryKey[0])
}
if len(e.ColumnUniqueKey) > 0 {
list := append([]string(nil), e.ColumnUniqueKey...)
list = append(list, "unique")
indexName := indexNameGen(list...)
uniqueKeyMap[indexName] = e.ColumnUniqueKey
}
}
if primaryColumnSet.Count() > 1 {
return nil, fmt.Errorf("%s: unexpected join primary key", prefix)
}
primaryKey, fieldM, err := convertColumns(columns, primaryColumn)
if err != nil {
return nil, err
}
var fields []*Field
// sort
for _, c := range columns {
field, ok := fieldM[c.Name]
if ok {
fields = append(fields, field)
}
}
var (
uniqueIndex = make(map[string][]*Field)
normalIndex = make(map[string][]*Field)
)
for indexName, each := range uniqueKeyMap {
for _, columnName := range each {
uniqueIndex[indexName] = append(uniqueIndex[indexName], fieldM[columnName])
}
}
for indexName, each := range normalKeyMap {
for _, columnName := range each {
normalIndex[indexName] = append(normalIndex[indexName], fieldM[columnName])
}
}
checkDuplicateUniqueIndex(uniqueIndex, e.Name)
list = append(list, &Table{
Name: stringx.From(e.Name),
Db: stringx.From(database),
PrimaryKey: primaryKey,
UniqueIndex: uniqueIndex,
Fields: fields,
})
}
checkDuplicateUniqueIndex(uniqueIndex, tableName, normalIndex)
return &Table{
Name: stringx.From(tableName),
PrimaryKey: primaryKey,
UniqueIndex: uniqueIndex,
NormalIndex: normalIndex,
Fields: fields,
}, nil
return list, nil
}
func checkDuplicateUniqueIndex(uniqueIndex map[string][]*Field, tableName string, normalIndex map[string][]*Field) {
func checkDuplicateUniqueIndex(uniqueIndex map[string][]*Field, tableName string) {
log := console.NewColorConsole()
uniqueSet := collection.NewSet()
for k, i := range uniqueIndex {
@@ -130,26 +174,9 @@ func checkDuplicateUniqueIndex(uniqueIndex map[string][]*Field, tableName string
uniqueSet.AddStr(joinRet)
}
normalIndexSet := collection.NewSet()
for k, i := range normalIndex {
var list []string
for _, e := range i {
list = append(list, e.Name.Source())
}
joinRet := strings.Join(list, ",")
if normalIndexSet.Contains(joinRet) {
log.Warning("table %s: duplicate index %s", tableName, joinRet)
delete(normalIndex, k)
continue
}
normalIndexSet.Add(joinRet)
}
}
func convertColumns(columns []*sqlparser.ColumnDefinition, primaryColumn string) (Primary, map[string]*Field, error) {
func convertColumns(columns []*parser.Column, primaryColumn string) (Primary, map[string]*Field, error) {
var (
primaryKey Primary
fieldM = make(map[string]*Field)
@@ -160,37 +187,35 @@ func convertColumns(columns []*sqlparser.ColumnDefinition, primaryColumn string)
continue
}
var comment string
if column.Type.Comment != nil {
comment = string(column.Type.Comment.Val)
}
var (
comment string
isDefaultNull bool
)
isDefaultNull := true
if column.Type.NotNull {
isDefaultNull = false
} else {
if column.Type.Default == nil {
isDefaultNull = false
} else if string(column.Type.Default.Val) != "null" {
if column.Constraint != nil {
comment = column.Constraint.Comment
isDefaultNull = !column.Constraint.HasDefaultValue
if column.Name == primaryColumn && column.Constraint.AutoIncrement {
isDefaultNull = false
}
}
dataType, err := converter.ConvertDataType(column.Type.Type, isDefaultNull)
dataType, err := converter.ConvertDataType(column.DataType.Type(), isDefaultNull)
if err != nil {
return Primary{}, nil, err
}
var field Field
field.Name = stringx.From(column.Name.String())
field.DataBaseType = column.Type.Type
field.Name = stringx.From(column.Name)
field.DataType = dataType
field.Comment = comment
field.Comment = util.TrimNewLine(comment)
if field.Name.Source() == primaryColumn {
primaryKey = Primary{
Field: field,
AutoIncrement: bool(column.Type.Autoincrement),
Field: field,
}
if column.Constraint != nil {
primaryKey.AutoIncrement = column.Constraint.AutoIncrement
}
}
@@ -199,60 +224,6 @@ func convertColumns(columns []*sqlparser.ColumnDefinition, primaryColumn string)
return primaryKey, fieldM, nil
}
func convertIndexes(indexes []*sqlparser.IndexDefinition) (string, map[string][]string, map[string][]string, error) {
var primaryColumn string
uniqueKeyMap := make(map[string][]string)
normalKeyMap := make(map[string][]string)
isCreateTimeOrUpdateTime := func(name string) bool {
camelColumnName := stringx.From(name).ToCamel()
// by default, createTime|updateTime findOne is not used.
return camelColumnName == "CreateTime" || camelColumnName == "UpdateTime"
}
for _, index := range indexes {
info := index.Info
if info == nil {
continue
}
indexName := index.Info.Name.String()
if info.Primary {
if len(index.Columns) > 1 {
return "", nil, nil, errPrimaryKey
}
columnName := index.Columns[0].Column.String()
if isCreateTimeOrUpdateTime(columnName) {
continue
}
primaryColumn = columnName
continue
} else if info.Unique {
for _, each := range index.Columns {
columnName := each.Column.String()
if isCreateTimeOrUpdateTime(columnName) {
break
}
uniqueKeyMap[indexName] = append(uniqueKeyMap[indexName], columnName)
}
} else if info.Spatial {
// do nothing
} else {
for _, each := range index.Columns {
columnName := each.Column.String()
if isCreateTimeOrUpdateTime(columnName) {
break
}
normalKeyMap[indexName] = append(normalKeyMap[indexName], each.Column.String())
}
}
}
return primaryColumn, uniqueKeyMap, normalKeyMap, nil
}
// ContainsTime returns true if contains golang type time.Time
func (t *Table) ContainsTime() bool {
for _, item := range t.Fields {
@@ -266,15 +237,15 @@ func (t *Table) ContainsTime() bool {
// ConvertDataType converts mysql data type into golang data type
func ConvertDataType(table *model.Table) (*Table, error) {
isPrimaryDefaultNull := table.PrimaryKey.ColumnDefault == nil && table.PrimaryKey.IsNullAble == "YES"
primaryDataType, err := converter.ConvertDataType(table.PrimaryKey.DataType, isPrimaryDefaultNull)
primaryDataType, err := converter.ConvertStringDataType(table.PrimaryKey.DataType, isPrimaryDefaultNull)
if err != nil {
return nil, err
}
var reply Table
reply.UniqueIndex = map[string][]*Field{}
reply.NormalIndex = map[string][]*Field{}
reply.Name = stringx.From(table.Table)
reply.Db = stringx.From(table.Db)
seqInIndex := 0
if table.PrimaryKey.Index != nil {
seqInIndex = table.PrimaryKey.Index.SeqInIndex
@@ -283,7 +254,6 @@ func ConvertDataType(table *model.Table) (*Table, error) {
reply.PrimaryKey = Primary{
Field: Field{
Name: stringx.From(table.PrimaryKey.Name),
DataBaseType: table.PrimaryKey.DataType,
DataType: primaryDataType,
Comment: table.PrimaryKey.Comment,
SeqInIndex: seqInIndex,
@@ -339,29 +309,6 @@ func ConvertDataType(table *model.Table) (*Table, error) {
reply.UniqueIndex[indexName] = list
}
normalIndexSet := collection.NewSet()
for indexName, each := range table.NormalIndex {
var list []*Field
var normalJoin []string
for _, c := range each {
list = append(list, fieldM[c.Name])
normalJoin = append(normalJoin, c.Name)
}
normalKey := strings.Join(normalJoin, ",")
if normalIndexSet.Contains(normalKey) {
log.Warning("table %s: duplicate index, %s", table.Table, normalKey)
continue
}
normalIndexSet.AddStr(normalKey)
sort.Slice(list, func(i, j int) bool {
return list[i].SeqInIndex < list[j].SeqInIndex
})
reply.NormalIndex[indexName] = list
}
return &reply, nil
}
@@ -369,7 +316,7 @@ func getTableFields(table *model.Table) (map[string]*Field, error) {
fieldM := make(map[string]*Field)
for _, each := range table.Columns {
isDefaultNull := each.ColumnDefault == nil && each.IsNullAble == "YES"
dt, err := converter.ConvertDataType(each.DataType, isDefaultNull)
dt, err := converter.ConvertStringDataType(each.DataType, isDefaultNull)
if err != nil {
return nil, err
}
@@ -380,7 +327,6 @@ func getTableFields(table *model.Table) (map[string]*Field, error) {
field := &Field{
Name: stringx.From(each.Name),
DataBaseType: each.DataType,
DataType: dt,
Comment: each.Comment,
SeqInIndex: columnSeqInIndex,

View File

@@ -1,86 +1,55 @@
package parser
import (
"sort"
"io/ioutil"
"path/filepath"
"testing"
"github.com/stretchr/testify/assert"
"github.com/tal-tech/go-zero/tools/goctl/model/sql/model"
"github.com/tal-tech/go-zero/tools/goctl/util/stringx"
"github.com/tal-tech/go-zero/tools/goctl/model/sql/util"
)
func TestParsePlainText(t *testing.T) {
_, err := Parse("plain text")
sqlFile := filepath.Join(t.TempDir(), "tmp.sql")
err := ioutil.WriteFile(sqlFile, []byte("plain text"), 0o777)
assert.Nil(t, err)
_, err = Parse(sqlFile, "go_zero")
assert.NotNil(t, err)
}
func TestParseSelect(t *testing.T) {
_, err := Parse("select * from user")
assert.Equal(t, errUnsupportDDL, err)
sqlFile := filepath.Join(t.TempDir(), "tmp.sql")
err := ioutil.WriteFile(sqlFile, []byte("select * from user"), 0o777)
assert.Nil(t, err)
tables, err := Parse(sqlFile, "go_zero")
assert.Nil(t, err)
assert.Equal(t, 0, len(tables))
}
func TestParseCreateTable(t *testing.T) {
table, err := Parse("CREATE TABLE `test_user` (\n `id` bigint NOT NULL AUTO_INCREMENT,\n `mobile` varchar(255) COLLATE utf8mb4_bin NOT NULL,\n `class` bigint NOT NULL,\n `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL,\n `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP,\n `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n PRIMARY KEY (`id`),\n UNIQUE KEY `mobile_unique` (`mobile`),\n UNIQUE KEY `class_name_unique` (`class`,`name`),\n KEY `create_index` (`create_time`),\n KEY `name_index` (`name`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;")
sqlFile := filepath.Join(t.TempDir(), "tmp.sql")
err := ioutil.WriteFile(sqlFile, []byte("CREATE TABLE `test_user` (\n `id` bigint NOT NULL AUTO_INCREMENT,\n `mobile` varchar(255) COLLATE utf8mb4_bin NOT NULL comment '手\\t机 号',\n `class` bigint NOT NULL comment '班级',\n `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL comment '姓\n 名',\n `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP comment '创建\\r时间',\n `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n PRIMARY KEY (`id`),\n UNIQUE KEY `mobile_unique` (`mobile`),\n UNIQUE KEY `class_name_unique` (`class`,`name`),\n KEY `create_index` (`create_time`),\n KEY `name_index` (`name`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;"), 0o777)
assert.Nil(t, err)
tables, err := Parse(sqlFile, "go_zero")
assert.Equal(t, 1, len(tables))
table := tables[0]
assert.Nil(t, err)
assert.Equal(t, "test_user", table.Name.Source())
assert.Equal(t, "id", table.PrimaryKey.Name.Source())
assert.Equal(t, true, table.ContainsTime())
assert.Equal(t, true, func() bool {
mobileUniqueIndex, ok := table.UniqueIndex["mobile_unique"]
if !ok {
return false
}
classNameUniqueIndex, ok := table.UniqueIndex["class_name_unique"]
if !ok {
return false
}
equal := func(f1, f2 []*Field) bool {
sort.Slice(f1, func(i, j int) bool {
return f1[i].Name.Source() < f1[j].Name.Source()
})
sort.Slice(f2, func(i, j int) bool {
return f2[i].Name.Source() < f2[j].Name.Source()
})
if len(f2) != len(f2) {
assert.Equal(t, 2, len(table.UniqueIndex))
assert.True(t, func() bool {
for _, e := range table.Fields {
if e.Comment != util.TrimNewLine(e.Comment) {
return false
}
for index, f := range f1 {
if f1[index].Name.Source() != f.Name.Source() {
return false
}
}
return true
}
if !equal(mobileUniqueIndex, []*Field{
{
Name: stringx.From("mobile"),
DataBaseType: "varchar",
DataType: "string",
SeqInIndex: 1,
},
}) {
return false
}
return equal(classNameUniqueIndex, []*Field{
{
Name: stringx.From("class"),
DataBaseType: "bigint",
DataType: "int64",
SeqInIndex: 1,
},
{
Name: stringx.From("name"),
DataBaseType: "varchar",
DataType: "string",
SeqInIndex: 2,
},
})
return true
}())
}

View File

@@ -10,9 +10,9 @@ func (m *default{{.upperStartCamelObject}}Model) Delete({{.lowerStartCamelPrimar
{{.keys}}
_, err {{if .containsIndexCache}}={{else}}:={{end}} m.Exec(func(conn sqlx.SqlConn) (result sql.Result, err error) {
query := fmt.Sprintf("delete from %s where {{.originalPrimaryKey}} = ?", m.table)
query := fmt.Sprintf("delete from %s where {{.originalPrimaryKey}} = {{if .postgreSql}}$1{{else}}?{{end}}", m.table)
return conn.Exec(query, {{.lowerStartCamelPrimaryKey}})
}, {{.keyValues}}){{else}}query := fmt.Sprintf("delete from %s where {{.originalPrimaryKey}} = ?", m.table)
}, {{.keyValues}}){{else}}query := fmt.Sprintf("delete from %s where {{.originalPrimaryKey}} = {{if .postgreSql}}$1{{else}}?{{end}}", m.table)
_,err:=m.conn.Exec(query, {{.lowerStartCamelPrimaryKey}}){{end}}
return err
}

View File

@@ -6,7 +6,7 @@ func (m *default{{.upperStartCamelObject}}Model) FindOne({{.lowerStartCamelPrima
{{if .withCache}}{{.cacheKey}}
var resp {{.upperStartCamelObject}}
err := m.QueryRow(&resp, {{.cacheKeyVariable}}, func(conn sqlx.SqlConn, v interface{}) error {
query := fmt.Sprintf("select %s from %s where {{.originalPrimaryKey}} = ? limit 1", {{.lowerStartCamelObject}}Rows, m.table)
query := fmt.Sprintf("select %s from %s where {{.originalPrimaryKey}} = {{if .postgreSql}}$1{{else}}?{{end}} limit 1", {{.lowerStartCamelObject}}Rows, m.table)
return conn.QueryRow(v, query, {{.lowerStartCamelPrimaryKey}})
})
switch err {
@@ -16,7 +16,7 @@ func (m *default{{.upperStartCamelObject}}Model) FindOne({{.lowerStartCamelPrima
return nil, ErrNotFound
default:
return nil, err
}{{else}}query := fmt.Sprintf("select %s from %s where {{.originalPrimaryKey}} = ? limit 1", {{.lowerStartCamelObject}}Rows, m.table)
}{{else}}query := fmt.Sprintf("select %s from %s where {{.originalPrimaryKey}} = {{if .postgreSql}}$1{{else}}?{{end}} limit 1", {{.lowerStartCamelObject}}Rows, m.table)
var resp {{.upperStartCamelObject}}
err := m.conn.QueryRow(&resp, query, {{.lowerStartCamelPrimaryKey}})
switch err {
@@ -71,7 +71,7 @@ func (m *default{{.upperStartCamelObject}}Model) formatPrimary(primary interface
}
func (m *default{{.upperStartCamelObject}}Model) queryPrimary(conn sqlx.SqlConn, v, primary interface{}) error {
query := fmt.Sprintf("select %s from %s where {{.originalPrimaryField}} = ? limit 1", {{.lowerStartCamelObject}}Rows, m.table )
query := fmt.Sprintf("select %s from %s where {{.originalPrimaryField}} = {{if .postgreSql}}$1{{else}}?{{end}} limit 1", {{.lowerStartCamelObject}}Rows, m.table )
return conn.QueryRow(v, query, primary)
}
`

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