Merge pull request #78 from go-jet/develop

Merge develop to master for 2.5.0 release
This commit is contained in:
go-jet 2021-05-21 17:20:27 +02:00 committed by GitHub
commit f0bf2c36b3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
80 changed files with 2905 additions and 461 deletions

View file

@ -6,7 +6,7 @@ jobs:
build-postgres-and-mysql: build-postgres-and-mysql:
docker: docker:
# specify the version # specify the version
- image: circleci/golang:1.11 - image: circleci/golang:1.13
- image: circleci/postgres:10.8-alpine - image: circleci/postgres:10.8-alpine
environment: # environment variables for primary container environment: # environment variables for primary container
@ -14,7 +14,7 @@ jobs:
POSTGRES_PASSWORD: jet POSTGRES_PASSWORD: jet
POSTGRES_DB: jetdb POSTGRES_DB: jetdb
- image: circleci/mysql:8.0 - image: circleci/mysql:8.0.16
command: [--default-authentication-plugin=mysql_native_password] command: [--default-authentication-plugin=mysql_native_password]
environment: environment:
MYSQL_ROOT_PASSWORD: jet MYSQL_ROOT_PASSWORD: jet
@ -41,17 +41,9 @@ jobs:
- run: - run:
name: Install dependencies name: Install dependencies
command: | command: |
go get github.com/google/uuid cd /go/src/github.com/go-jet/jet
go get github.com/lib/pq
go get github.com/go-sql-driver/mysql
go get github.com/pkg/profile
go get github.com/stretchr/testify/assert
go get github.com/google/go-cmp/cmp
go get github.com/davecgh/go-spew/spew
go get github.com/jstemmer/go-junit-report go get github.com/jstemmer/go-junit-report
go build -o /home/circleci/.local/bin/jet ./cmd/jet/
go install github.com/go-jet/jet/cmd/jet
- run: - run:
name: Waiting for Postgres to be ready name: Waiting for Postgres to be ready
@ -85,6 +77,7 @@ jobs:
mysql -h 127.0.0.1 -u root -pjet -e "grant all privileges on *.* to 'jet'@'%';" mysql -h 127.0.0.1 -u root -pjet -e "grant all privileges on *.* to 'jet'@'%';"
mysql -h 127.0.0.1 -u root -pjet -e "set global sql_mode = 'STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION';" mysql -h 127.0.0.1 -u root -pjet -e "set global sql_mode = 'STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION';"
mysql -h 127.0.0.1 -u jet -pjet -e "create database test_sample" mysql -h 127.0.0.1 -u jet -pjet -e "create database test_sample"
mysql -h 127.0.0.1 -u jet -pjet -e "create database dvds2"
- run: - run:
name: Init Postgres database name: Init Postgres database
@ -95,7 +88,7 @@ jobs:
- run: mkdir -p $TEST_RESULTS - run: mkdir -p $TEST_RESULTS
- run: go test -v ./... -coverpkg=github.com/go-jet/jet/postgres/...,github.com/go-jet/jet/mysql/...,github.com/go-jet/jet/qrm/...,github.com/go-jet/jet/generator/...,github.com/go-jet/jet/internal/... -coverprofile=cover.out 2>&1 | go-junit-report > $TEST_RESULTS/results.xml - run: MY_SQL_SOURCE=MySQL go test -v ./... -coverpkg=github.com/go-jet/jet/postgres/...,github.com/go-jet/jet/mysql/...,github.com/go-jet/jet/qrm/...,github.com/go-jet/jet/generator/...,github.com/go-jet/jet/internal/... -coverprofile=cover.out 2>&1 | go-junit-report > $TEST_RESULTS/results.xml
- run: - run:
name: Upload code coverage name: Upload code coverage
@ -110,7 +103,7 @@ jobs:
build-mariadb: build-mariadb:
docker: docker:
# specify the version # specify the version
- image: circleci/golang:1.11 - image: circleci/golang:1.13
- image: circleci/mariadb:10.3 - image: circleci/mariadb:10.3
command: [--default-authentication-plugin=mysql_native_password] command: [--default-authentication-plugin=mysql_native_password]
@ -138,17 +131,9 @@ jobs:
- run: - run:
name: Install dependencies name: Install dependencies
command: | command: |
go get github.com/google/uuid cd /go/src/github.com/go-jet/jet
go get github.com/lib/pq
go get github.com/go-sql-driver/mysql
go get github.com/pkg/profile
go get github.com/stretchr/testify/assert
go get github.com/google/go-cmp/cmp
go get github.com/davecgh/go-spew/spew
go get github.com/jstemmer/go-junit-report go get github.com/jstemmer/go-junit-report
go build -o /home/circleci/.local/bin/jet ./cmd/jet/
go install github.com/go-jet/jet/cmd/jet
- run: - run:
name: Install MySQL CLI; name: Install MySQL CLI;
@ -161,6 +146,7 @@ jobs:
mysql -h 127.0.0.1 -u root -pjet -e "grant all privileges on *.* to 'jet'@'%';" mysql -h 127.0.0.1 -u root -pjet -e "grant all privileges on *.* to 'jet'@'%';"
mysql -h 127.0.0.1 -u root -pjet -e "set global sql_mode = 'STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION';" mysql -h 127.0.0.1 -u root -pjet -e "set global sql_mode = 'STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION';"
mysql -h 127.0.0.1 -u jet -pjet -e "create database test_sample" mysql -h 127.0.0.1 -u jet -pjet -e "create database test_sample"
mysql -h 127.0.0.1 -u jet -pjet -e "create database dvds2"
- run: - run:
name: Init MariaDB database name: Init MariaDB database
@ -172,7 +158,7 @@ jobs:
- run: - run:
name: Run MariaDB tests name: Run MariaDB tests
command: | command: |
go test -v ./tests/mysql/ -source=MariaDB MY_SQL_SOURCE=MariaDB go test -v ./tests/mysql/
workflows: workflows:
version: 2 version: 2

View file

@ -62,24 +62,33 @@ To install Jet package, you need to install Go and set your Go workspace first.
### Installation ### Installation
Use the bellow command to add jet as a dependency into `go.mod` project: Use the command bellow to add jet as a dependency into `go.mod` project:
```sh ```sh
$ go get -u github.com/go-jet/jet/v2 $ go get -u github.com/go-jet/jet/v2
``` ```
Use the bellow command to add jet as a dependency into `GOPATH` project: Jet generator can be install in the following ways:
```sh
$ go get -u github.com/go-jet/jet
```
Install jet generator to GOPATH bin folder. This will allow generating jet files from the command line.
1) Install jet generator to GOPATH/bin folder:
```sh ```sh
cd $GOPATH/src/ && GO111MODULE=off go get -u github.com/go-jet/jet/cmd/jet cd $GOPATH/src/ && GO111MODULE=off go get -u github.com/go-jet/jet/cmd/jet
``` ```
*Make sure GOPATH/bin folder is added to the PATH environment variable.*
*Make sure GOPATH bin folder is added to the PATH environment variable.* 2) Install jet generator to specific folder:
```sh
git clone https://github.com/go-jet/jet.git
cd jet && go build -o dir_path ./cmd/jet
```
*Make sure `dir_path` folder is added to the PATH environment variable.*
3) (Go1.16+) Install jet generator using go install:
```sh
go install github.com/go-jet/jet/v2/cmd/jet@latest
```
*Jet generator is installed to the directory named by the GOBIN environment variable,
which defaults to $GOPATH/bin or $HOME/go/bin if the GOPATH environment variable is not set.*
### Quick Start ### Quick Start
For this quick start example we will use PostgreSQL sample _'dvd rental'_ database. Full database dump can be found in [./tests/testdata/init/postgres/dvds.sql](./tests/testdata/init/postgres/dvds.sql). For this quick start example we will use PostgreSQL sample _'dvd rental'_ database. Full database dump can be found in [./tests/testdata/init/postgres/dvds.sql](./tests/testdata/init/postgres/dvds.sql).
@ -519,26 +528,17 @@ The biggest benefit is speed. Speed is improved in 3 major areas:
##### Speed of development ##### Speed of development
Writing SQL queries is faster and easier, because the developers have help of SQL code completion and SQL type safety directly from Go. Writing SQL queries is faster and easier as the developers have help of SQL code completion and SQL type safety directly from Go.
Automatic scan to arbitrary structure removes a lot of headache and boilerplate code needed to structure database query result. Automatic scan to arbitrary structure removes a lot of headache and boilerplate code needed to structure database query result.
##### Speed of execution ##### Speed of execution
While ORM libraries can introduce significant performance penalties due to number of round-trips to the database, While ORM libraries can introduce significant performance penalties due to number of round-trips to the database,
Jet will always perform much better, because of the single database call. Jet will always perform better as developers can write complex query and retrieve result with a single database call.
Thus handler time lost on latency between server and database can be constant. Handler execution will be proportional
only to the query complexity and the number of rows returned from database.
Common web and database server usually are not on the same physical machine, and there is some latency between them. With Jet it is even possible to join the whole database and store the whole structured result in one database call.
Latency can vary from 5ms to 50+ms. In majority of cases query executed on database is simple query lasting no more than 1ms.
In those cases web server handler execution time is directly proportional to latency between server and database.
This is not such a big problem if handler calls database couple of times, but what if web server is using ORM to retrieve data from database.
ORM sometimes can access the database once for every object needed. Now lets say latency is 30ms and there are 100
different objects required from the database. This handler will last 3 seconds !!!.
With Jet, handler time lost on latency between server and database is constant. Because we can write complex query and
return result in one database call. Handler execution will be only proportional to the number of rows returned from database.
ORM example replaced with jet will take just 30ms + 'result scan time' = 31ms (rough estimate).
With Jet you can even join the whole database and store the whole structured result in one database call.
This is exactly what is being done in one of the tests: [TestJoinEverything](/tests/postgres/chinook_db_test.go#L40). This is exactly what is being done in one of the tests: [TestJoinEverything](/tests/postgres/chinook_db_test.go#L40).
The whole test database is joined and query result(~10,000 rows) is stored in a structured variable in less than 0.7s. The whole test database is joined and query result(~10,000 rows) is stored in a structured variable in less than 0.7s.
@ -570,6 +570,7 @@ To run the tests, additional dependencies are required:
- `github.com/pkg/profile` - `github.com/pkg/profile`
- `github.com/stretchr/testify` - `github.com/stretchr/testify`
- `github.com/google/go-cmp` - `github.com/google/go-cmp`
- `github.com/jackc/pgx/v4`
## Versioning ## Versioning
@ -577,5 +578,5 @@ To run the tests, additional dependencies are required:
## License ## License
Copyright 2019-2020 Goran Bjelanovic Copyright 2019-2021 Goran Bjelanovic
Licensed under the Apache License, Version 2.0. Licensed under the Apache License, Version 2.0.

View file

@ -47,7 +47,7 @@ func main() {
flag.Usage = func() { flag.Usage = func() {
_, _ = fmt.Fprint(os.Stdout, ` _, _ = fmt.Fprint(os.Stdout, `
Jet generator 2.3.0 Jet generator 2.5.0
Usage: Usage:
-source string -source string

View file

@ -11,7 +11,7 @@ import (
"github.com/go-jet/jet/v2/postgres" "github.com/go-jet/jet/v2/postgres"
) )
var Actor = newActorTable() var Actor = newActorTable("dvds", "actor", "")
type actorTable struct { type actorTable struct {
postgres.Table postgres.Table
@ -33,20 +33,23 @@ type ActorTable struct {
} }
// AS creates new ActorTable with assigned alias // AS creates new ActorTable with assigned alias
func (a *ActorTable) AS(alias string) *ActorTable { func (a ActorTable) AS(alias string) *ActorTable {
aliasTable := newActorTable() return newActorTable(a.SchemaName(), a.TableName(), alias)
aliasTable.Table.AS(alias)
return aliasTable
} }
func newActorTable() *ActorTable { // Schema creates new ActorTable with assigned schema name
func (a ActorTable) FromSchema(schemaName string) *ActorTable {
return newActorTable(schemaName, a.TableName(), a.Alias())
}
func newActorTable(schemaName, tableName, alias string) *ActorTable {
return &ActorTable{ return &ActorTable{
actorTable: newActorTableImpl("dvds", "actor"), actorTable: newActorTableImpl(schemaName, tableName, alias),
EXCLUDED: newActorTableImpl("", "excluded"), EXCLUDED: newActorTableImpl("", "excluded", ""),
} }
} }
func newActorTableImpl(schemaName, tableName string) actorTable { func newActorTableImpl(schemaName, tableName, alias string) actorTable {
var ( var (
ActorIDColumn = postgres.IntegerColumn("actor_id") ActorIDColumn = postgres.IntegerColumn("actor_id")
FirstNameColumn = postgres.StringColumn("first_name") FirstNameColumn = postgres.StringColumn("first_name")
@ -57,7 +60,7 @@ func newActorTableImpl(schemaName, tableName string) actorTable {
) )
return actorTable{ return actorTable{
Table: postgres.NewTable(schemaName, tableName, allColumns...), Table: postgres.NewTable(schemaName, tableName, alias, allColumns...),
//Columns //Columns
ActorID: ActorIDColumn, ActorID: ActorIDColumn,

View file

@ -11,7 +11,7 @@ import (
"github.com/go-jet/jet/v2/postgres" "github.com/go-jet/jet/v2/postgres"
) )
var Category = newCategoryTable() var Category = newCategoryTable("dvds", "category", "")
type categoryTable struct { type categoryTable struct {
postgres.Table postgres.Table
@ -32,20 +32,23 @@ type CategoryTable struct {
} }
// AS creates new CategoryTable with assigned alias // AS creates new CategoryTable with assigned alias
func (a *CategoryTable) AS(alias string) *CategoryTable { func (a CategoryTable) AS(alias string) *CategoryTable {
aliasTable := newCategoryTable() return newCategoryTable(a.SchemaName(), a.TableName(), alias)
aliasTable.Table.AS(alias)
return aliasTable
} }
func newCategoryTable() *CategoryTable { // Schema creates new CategoryTable with assigned schema name
func (a CategoryTable) FromSchema(schemaName string) *CategoryTable {
return newCategoryTable(schemaName, a.TableName(), a.Alias())
}
func newCategoryTable(schemaName, tableName, alias string) *CategoryTable {
return &CategoryTable{ return &CategoryTable{
categoryTable: newCategoryTableImpl("dvds", "category"), categoryTable: newCategoryTableImpl(schemaName, tableName, alias),
EXCLUDED: newCategoryTableImpl("", "excluded"), EXCLUDED: newCategoryTableImpl("", "excluded", ""),
} }
} }
func newCategoryTableImpl(schemaName, tableName string) categoryTable { func newCategoryTableImpl(schemaName, tableName, alias string) categoryTable {
var ( var (
CategoryIDColumn = postgres.IntegerColumn("category_id") CategoryIDColumn = postgres.IntegerColumn("category_id")
NameColumn = postgres.StringColumn("name") NameColumn = postgres.StringColumn("name")
@ -55,7 +58,7 @@ func newCategoryTableImpl(schemaName, tableName string) categoryTable {
) )
return categoryTable{ return categoryTable{
Table: postgres.NewTable(schemaName, tableName, allColumns...), Table: postgres.NewTable(schemaName, tableName, alias, allColumns...),
//Columns //Columns
CategoryID: CategoryIDColumn, CategoryID: CategoryIDColumn,

View file

@ -11,7 +11,7 @@ import (
"github.com/go-jet/jet/v2/postgres" "github.com/go-jet/jet/v2/postgres"
) )
var Film = newFilmTable() var Film = newFilmTable("dvds", "film", "")
type filmTable struct { type filmTable struct {
postgres.Table postgres.Table
@ -42,20 +42,23 @@ type FilmTable struct {
} }
// AS creates new FilmTable with assigned alias // AS creates new FilmTable with assigned alias
func (a *FilmTable) AS(alias string) *FilmTable { func (a FilmTable) AS(alias string) *FilmTable {
aliasTable := newFilmTable() return newFilmTable(a.SchemaName(), a.TableName(), alias)
aliasTable.Table.AS(alias)
return aliasTable
} }
func newFilmTable() *FilmTable { // Schema creates new FilmTable with assigned schema name
func (a FilmTable) FromSchema(schemaName string) *FilmTable {
return newFilmTable(schemaName, a.TableName(), a.Alias())
}
func newFilmTable(schemaName, tableName, alias string) *FilmTable {
return &FilmTable{ return &FilmTable{
filmTable: newFilmTableImpl("dvds", "film"), filmTable: newFilmTableImpl(schemaName, tableName, alias),
EXCLUDED: newFilmTableImpl("", "excluded"), EXCLUDED: newFilmTableImpl("", "excluded", ""),
} }
} }
func newFilmTableImpl(schemaName, tableName string) filmTable { func newFilmTableImpl(schemaName, tableName, alias string) filmTable {
var ( var (
FilmIDColumn = postgres.IntegerColumn("film_id") FilmIDColumn = postgres.IntegerColumn("film_id")
TitleColumn = postgres.StringColumn("title") TitleColumn = postgres.StringColumn("title")
@ -75,7 +78,7 @@ func newFilmTableImpl(schemaName, tableName string) filmTable {
) )
return filmTable{ return filmTable{
Table: postgres.NewTable(schemaName, tableName, allColumns...), Table: postgres.NewTable(schemaName, tableName, alias, allColumns...),
//Columns //Columns
FilmID: FilmIDColumn, FilmID: FilmIDColumn,

View file

@ -11,7 +11,7 @@ import (
"github.com/go-jet/jet/v2/postgres" "github.com/go-jet/jet/v2/postgres"
) )
var FilmActor = newFilmActorTable() var FilmActor = newFilmActorTable("dvds", "film_actor", "")
type filmActorTable struct { type filmActorTable struct {
postgres.Table postgres.Table
@ -32,20 +32,23 @@ type FilmActorTable struct {
} }
// AS creates new FilmActorTable with assigned alias // AS creates new FilmActorTable with assigned alias
func (a *FilmActorTable) AS(alias string) *FilmActorTable { func (a FilmActorTable) AS(alias string) *FilmActorTable {
aliasTable := newFilmActorTable() return newFilmActorTable(a.SchemaName(), a.TableName(), alias)
aliasTable.Table.AS(alias)
return aliasTable
} }
func newFilmActorTable() *FilmActorTable { // Schema creates new FilmActorTable with assigned schema name
func (a FilmActorTable) FromSchema(schemaName string) *FilmActorTable {
return newFilmActorTable(schemaName, a.TableName(), a.Alias())
}
func newFilmActorTable(schemaName, tableName, alias string) *FilmActorTable {
return &FilmActorTable{ return &FilmActorTable{
filmActorTable: newFilmActorTableImpl("dvds", "film_actor"), filmActorTable: newFilmActorTableImpl(schemaName, tableName, alias),
EXCLUDED: newFilmActorTableImpl("", "excluded"), EXCLUDED: newFilmActorTableImpl("", "excluded", ""),
} }
} }
func newFilmActorTableImpl(schemaName, tableName string) filmActorTable { func newFilmActorTableImpl(schemaName, tableName, alias string) filmActorTable {
var ( var (
ActorIDColumn = postgres.IntegerColumn("actor_id") ActorIDColumn = postgres.IntegerColumn("actor_id")
FilmIDColumn = postgres.IntegerColumn("film_id") FilmIDColumn = postgres.IntegerColumn("film_id")
@ -55,7 +58,7 @@ func newFilmActorTableImpl(schemaName, tableName string) filmActorTable {
) )
return filmActorTable{ return filmActorTable{
Table: postgres.NewTable(schemaName, tableName, allColumns...), Table: postgres.NewTable(schemaName, tableName, alias, allColumns...),
//Columns //Columns
ActorID: ActorIDColumn, ActorID: ActorIDColumn,

View file

@ -11,7 +11,7 @@ import (
"github.com/go-jet/jet/v2/postgres" "github.com/go-jet/jet/v2/postgres"
) )
var FilmCategory = newFilmCategoryTable() var FilmCategory = newFilmCategoryTable("dvds", "film_category", "")
type filmCategoryTable struct { type filmCategoryTable struct {
postgres.Table postgres.Table
@ -32,20 +32,23 @@ type FilmCategoryTable struct {
} }
// AS creates new FilmCategoryTable with assigned alias // AS creates new FilmCategoryTable with assigned alias
func (a *FilmCategoryTable) AS(alias string) *FilmCategoryTable { func (a FilmCategoryTable) AS(alias string) *FilmCategoryTable {
aliasTable := newFilmCategoryTable() return newFilmCategoryTable(a.SchemaName(), a.TableName(), alias)
aliasTable.Table.AS(alias)
return aliasTable
} }
func newFilmCategoryTable() *FilmCategoryTable { // Schema creates new FilmCategoryTable with assigned schema name
func (a FilmCategoryTable) FromSchema(schemaName string) *FilmCategoryTable {
return newFilmCategoryTable(schemaName, a.TableName(), a.Alias())
}
func newFilmCategoryTable(schemaName, tableName, alias string) *FilmCategoryTable {
return &FilmCategoryTable{ return &FilmCategoryTable{
filmCategoryTable: newFilmCategoryTableImpl("dvds", "film_category"), filmCategoryTable: newFilmCategoryTableImpl(schemaName, tableName, alias),
EXCLUDED: newFilmCategoryTableImpl("", "excluded"), EXCLUDED: newFilmCategoryTableImpl("", "excluded", ""),
} }
} }
func newFilmCategoryTableImpl(schemaName, tableName string) filmCategoryTable { func newFilmCategoryTableImpl(schemaName, tableName, alias string) filmCategoryTable {
var ( var (
FilmIDColumn = postgres.IntegerColumn("film_id") FilmIDColumn = postgres.IntegerColumn("film_id")
CategoryIDColumn = postgres.IntegerColumn("category_id") CategoryIDColumn = postgres.IntegerColumn("category_id")
@ -55,7 +58,7 @@ func newFilmCategoryTableImpl(schemaName, tableName string) filmCategoryTable {
) )
return filmCategoryTable{ return filmCategoryTable{
Table: postgres.NewTable(schemaName, tableName, allColumns...), Table: postgres.NewTable(schemaName, tableName, alias, allColumns...),
//Columns //Columns
FilmID: FilmIDColumn, FilmID: FilmIDColumn,

View file

@ -11,7 +11,7 @@ import (
"github.com/go-jet/jet/v2/postgres" "github.com/go-jet/jet/v2/postgres"
) )
var Language = newLanguageTable() var Language = newLanguageTable("dvds", "language", "")
type languageTable struct { type languageTable struct {
postgres.Table postgres.Table
@ -32,20 +32,23 @@ type LanguageTable struct {
} }
// AS creates new LanguageTable with assigned alias // AS creates new LanguageTable with assigned alias
func (a *LanguageTable) AS(alias string) *LanguageTable { func (a LanguageTable) AS(alias string) *LanguageTable {
aliasTable := newLanguageTable() return newLanguageTable(a.SchemaName(), a.TableName(), alias)
aliasTable.Table.AS(alias)
return aliasTable
} }
func newLanguageTable() *LanguageTable { // Schema creates new LanguageTable with assigned schema name
func (a LanguageTable) FromSchema(schemaName string) *LanguageTable {
return newLanguageTable(schemaName, a.TableName(), a.Alias())
}
func newLanguageTable(schemaName, tableName, alias string) *LanguageTable {
return &LanguageTable{ return &LanguageTable{
languageTable: newLanguageTableImpl("dvds", "language"), languageTable: newLanguageTableImpl(schemaName, tableName, alias),
EXCLUDED: newLanguageTableImpl("", "excluded"), EXCLUDED: newLanguageTableImpl("", "excluded", ""),
} }
} }
func newLanguageTableImpl(schemaName, tableName string) languageTable { func newLanguageTableImpl(schemaName, tableName, alias string) languageTable {
var ( var (
LanguageIDColumn = postgres.IntegerColumn("language_id") LanguageIDColumn = postgres.IntegerColumn("language_id")
NameColumn = postgres.StringColumn("name") NameColumn = postgres.StringColumn("name")
@ -55,7 +58,7 @@ func newLanguageTableImpl(schemaName, tableName string) languageTable {
) )
return languageTable{ return languageTable{
Table: postgres.NewTable(schemaName, tableName, allColumns...), Table: postgres.NewTable(schemaName, tableName, alias, allColumns...),
//Columns //Columns
LanguageID: LanguageIDColumn, LanguageID: LanguageIDColumn,

View file

@ -11,7 +11,7 @@ import (
"github.com/go-jet/jet/v2/postgres" "github.com/go-jet/jet/v2/postgres"
) )
var ActorInfo = newActorInfoTable() var ActorInfo = newActorInfoTable("dvds", "actor_info", "")
type actorInfoTable struct { type actorInfoTable struct {
postgres.Table postgres.Table
@ -33,20 +33,23 @@ type ActorInfoTable struct {
} }
// AS creates new ActorInfoTable with assigned alias // AS creates new ActorInfoTable with assigned alias
func (a *ActorInfoTable) AS(alias string) *ActorInfoTable { func (a ActorInfoTable) AS(alias string) *ActorInfoTable {
aliasTable := newActorInfoTable() return newActorInfoTable(a.SchemaName(), a.TableName(), alias)
aliasTable.Table.AS(alias)
return aliasTable
} }
func newActorInfoTable() *ActorInfoTable { // Schema creates new ActorInfoTable with assigned schema name
func (a ActorInfoTable) FromSchema(schemaName string) *ActorInfoTable {
return newActorInfoTable(schemaName, a.TableName(), a.Alias())
}
func newActorInfoTable(schemaName, tableName, alias string) *ActorInfoTable {
return &ActorInfoTable{ return &ActorInfoTable{
actorInfoTable: newActorInfoTableImpl("dvds", "actor_info"), actorInfoTable: newActorInfoTableImpl(schemaName, tableName, alias),
EXCLUDED: newActorInfoTableImpl("", "excluded"), EXCLUDED: newActorInfoTableImpl("", "excluded", ""),
} }
} }
func newActorInfoTableImpl(schemaName, tableName string) actorInfoTable { func newActorInfoTableImpl(schemaName, tableName, alias string) actorInfoTable {
var ( var (
ActorIDColumn = postgres.IntegerColumn("actor_id") ActorIDColumn = postgres.IntegerColumn("actor_id")
FirstNameColumn = postgres.StringColumn("first_name") FirstNameColumn = postgres.StringColumn("first_name")
@ -57,7 +60,7 @@ func newActorInfoTableImpl(schemaName, tableName string) actorInfoTable {
) )
return actorInfoTable{ return actorInfoTable{
Table: postgres.NewTable(schemaName, tableName, allColumns...), Table: postgres.NewTable(schemaName, tableName, alias, allColumns...),
//Columns //Columns
ActorID: ActorIDColumn, ActorID: ActorIDColumn,

View file

@ -11,7 +11,7 @@ import (
"github.com/go-jet/jet/v2/postgres" "github.com/go-jet/jet/v2/postgres"
) )
var CustomerList = newCustomerListTable() var CustomerList = newCustomerListTable("dvds", "customer_list", "")
type customerListTable struct { type customerListTable struct {
postgres.Table postgres.Table
@ -38,20 +38,23 @@ type CustomerListTable struct {
} }
// AS creates new CustomerListTable with assigned alias // AS creates new CustomerListTable with assigned alias
func (a *CustomerListTable) AS(alias string) *CustomerListTable { func (a CustomerListTable) AS(alias string) *CustomerListTable {
aliasTable := newCustomerListTable() return newCustomerListTable(a.SchemaName(), a.TableName(), alias)
aliasTable.Table.AS(alias)
return aliasTable
} }
func newCustomerListTable() *CustomerListTable { // Schema creates new CustomerListTable with assigned schema name
func (a CustomerListTable) FromSchema(schemaName string) *CustomerListTable {
return newCustomerListTable(schemaName, a.TableName(), a.Alias())
}
func newCustomerListTable(schemaName, tableName, alias string) *CustomerListTable {
return &CustomerListTable{ return &CustomerListTable{
customerListTable: newCustomerListTableImpl("dvds", "customer_list"), customerListTable: newCustomerListTableImpl(schemaName, tableName, alias),
EXCLUDED: newCustomerListTableImpl("", "excluded"), EXCLUDED: newCustomerListTableImpl("", "excluded", ""),
} }
} }
func newCustomerListTableImpl(schemaName, tableName string) customerListTable { func newCustomerListTableImpl(schemaName, tableName, alias string) customerListTable {
var ( var (
IDColumn = postgres.IntegerColumn("id") IDColumn = postgres.IntegerColumn("id")
NameColumn = postgres.StringColumn("name") NameColumn = postgres.StringColumn("name")
@ -67,7 +70,7 @@ func newCustomerListTableImpl(schemaName, tableName string) customerListTable {
) )
return customerListTable{ return customerListTable{
Table: postgres.NewTable(schemaName, tableName, allColumns...), Table: postgres.NewTable(schemaName, tableName, alias, allColumns...),
//Columns //Columns
ID: IDColumn, ID: IDColumn,

View file

@ -4,9 +4,10 @@ import (
"database/sql" "database/sql"
"encoding/json" "encoding/json"
"fmt" "fmt"
_ "github.com/lib/pq"
"io/ioutil" "io/ioutil"
_ "github.com/lib/pq"
// dot import so that jet go code would resemble as much as native SQL // dot import so that jet go code would resemble as much as native SQL
// dot import is not mandatory // dot import is not mandatory
. "github.com/go-jet/jet/v2/examples/quick-start/.gen/jetdb/dvds/table" . "github.com/go-jet/jet/v2/examples/quick-start/.gen/jetdb/dvds/table"

View file

@ -23,7 +23,7 @@ import (
"github.com/go-jet/jet/v2/{{dialect.PackageName}}" "github.com/go-jet/jet/v2/{{dialect.PackageName}}"
) )
var {{ToGoIdentifier .Name}} = new{{.GoStructName}}() var {{ToGoIdentifier .Name}} = new{{.GoStructName}}("{{.SchemaName}}", "{{.Name}}", "")
type {{.GoStructName}} struct { type {{.GoStructName}} struct {
{{dialect.PackageName}}.Table {{dialect.PackageName}}.Table
@ -38,13 +38,16 @@ type {{.GoStructName}} struct {
} }
// AS creates new {{.GoStructName}} with assigned alias // AS creates new {{.GoStructName}} with assigned alias
func (a *{{.GoStructName}}) AS(alias string) {{.GoStructName}} { func (a {{.GoStructName}}) AS(alias string) {{.GoStructName}} {
aliasTable := new{{.GoStructName}}() return new{{.GoStructName}}(a.SchemaName(), a.TableName(), alias)
aliasTable.Table.AS(alias)
return aliasTable
} }
func new{{.GoStructName}}() {{.GoStructName}} { // Schema creates new {{.GoStructName}} with assigned schema name
func (a {{.GoStructName}}) FromSchema(schemaName string) {{.GoStructName}} {
return new{{.GoStructName}}(schemaName, a.TableName(), a.Alias())
}
func new{{.GoStructName}}(schemaName, tableName, alias string) {{.GoStructName}} {
var ( var (
{{- range .Columns}} {{- range .Columns}}
{{ToGoIdentifier .Name}}Column = {{dialect.PackageName}}.{{.SqlBuilderColumnType}}Column("{{.Name}}") {{ToGoIdentifier .Name}}Column = {{dialect.PackageName}}.{{.SqlBuilderColumnType}}Column("{{.Name}}")
@ -54,7 +57,7 @@ func new{{.GoStructName}}() {{.GoStructName}} {
) )
return {{.GoStructName}}{ return {{.GoStructName}}{
Table: {{dialect.PackageName}}.NewTable("{{.SchemaName}}", "{{.Name}}", allColumns...), Table: {{dialect.PackageName}}.NewTable(schemaName, tableName, alias, allColumns...),
//Columns //Columns
{{- range .Columns}} {{- range .Columns}}
@ -80,7 +83,7 @@ import (
"github.com/go-jet/jet/v2/{{dialect.PackageName}}" "github.com/go-jet/jet/v2/{{dialect.PackageName}}"
) )
var {{ToGoIdentifier .Name}} = new{{.GoStructName}}() var {{ToGoIdentifier .Name}} = new{{.GoStructName}}("{{.SchemaName}}", "{{.Name}}", "")
type {{.GoStructImplName}} struct { type {{.GoStructImplName}} struct {
{{dialect.PackageName}}.Table {{dialect.PackageName}}.Table
@ -101,20 +104,23 @@ type {{.GoStructName}} struct {
} }
// AS creates new {{.GoStructName}} with assigned alias // AS creates new {{.GoStructName}} with assigned alias
func (a *{{.GoStructName}}) AS(alias string) *{{.GoStructName}} { func (a {{.GoStructName}}) AS(alias string) *{{.GoStructName}} {
aliasTable := new{{.GoStructName}}() return new{{.GoStructName}}(a.SchemaName(), a.TableName(), alias)
aliasTable.Table.AS(alias)
return aliasTable
} }
func new{{.GoStructName}}() *{{.GoStructName}} { // Schema creates new {{.GoStructName}} with assigned schema name
func (a {{.GoStructName}}) FromSchema(schemaName string) *{{.GoStructName}} {
return new{{.GoStructName}}(schemaName, a.TableName(), a.Alias())
}
func new{{.GoStructName}}(schemaName, tableName, alias string) *{{.GoStructName}} {
return &{{.GoStructName}}{ return &{{.GoStructName}}{
{{.GoStructImplName}}: new{{.GoStructName}}Impl("{{.SchemaName}}", "{{.Name}}"), {{.GoStructImplName}}: new{{.GoStructName}}Impl(schemaName, tableName, alias),
EXCLUDED: new{{.GoStructName}}Impl("", "excluded"), EXCLUDED: new{{.GoStructName}}Impl("", "excluded", ""),
} }
} }
func new{{.GoStructName}}Impl(schemaName, tableName string) {{.GoStructImplName}} { func new{{.GoStructName}}Impl(schemaName, tableName, alias string) {{.GoStructImplName}} {
var ( var (
{{- range .Columns}} {{- range .Columns}}
{{ToGoIdentifier .Name}}Column = {{dialect.PackageName}}.{{.SqlBuilderColumnType}}Column("{{.Name}}") {{ToGoIdentifier .Name}}Column = {{dialect.PackageName}}.{{.SqlBuilderColumnType}}Column("{{.Name}}")
@ -124,7 +130,7 @@ func new{{.GoStructName}}Impl(schemaName, tableName string) {{.GoStructImplName}
) )
return {{.GoStructImplName}}{ return {{.GoStructImplName}}{
Table: {{dialect.PackageName}}.NewTable(schemaName, tableName, allColumns...), Table: {{dialect.PackageName}}.NewTable(schemaName, tableName, alias, allColumns...),
//Columns //Columns
{{- range .Columns}} {{- range .Columns}}

8
go.mod
View file

@ -4,9 +4,11 @@ go 1.11
require ( require (
github.com/go-sql-driver/mysql v1.5.0 github.com/go-sql-driver/mysql v1.5.0
github.com/google/go-cmp v0.5.0 github.com/google/go-cmp v0.5.0 //tests
github.com/google/uuid v1.1.1 github.com/google/uuid v1.1.1
github.com/jackc/pgx/v4 v4.11.0 //tests
github.com/lib/pq v1.7.0 github.com/lib/pq v1.7.0
github.com/pkg/profile v1.5.0 github.com/pkg/profile v1.5.0 //tests
github.com/stretchr/testify v1.6.1 github.com/shopspring/decimal v1.2.0 // tests
github.com/stretchr/testify v1.6.1 // tests
) )

463
go.sum
View file

@ -1,23 +1,482 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
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/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc=
github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g=
github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
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/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A=
github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU=
github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g=
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/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ=
github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I=
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI=
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/go-jet/jet v1.0.0 h1:ENxUe/6lH82qLykIGAdZIlskZrpTeNfxjHz4VHtkVmA= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/go-jet/jet v2.3.0+incompatible h1:Yg7JSERDC0f9x3dHUBMA2cxe9/qC6qlozDDO/s38USU= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
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/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
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/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4=
github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
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/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o=
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-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 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gofrs/uuid v3.2.0+incompatible h1:y12jRkkFxsd7GpqdSZ+/KCs/fJbqpEXSGd4+jfEaewE=
github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
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-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
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/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
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/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w= 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.0/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 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
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/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE=
github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo=
github.com/jackc/chunkreader v1.0.0 h1:4s39bBR8ByfqH+DKm8rQA3E1LHZWB9XWcrz8fqaZbe0=
github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo=
github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8=
github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA=
github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE=
github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsUgOEh9hBm+xYTstcNHg7UPMVJqRfQxq4s=
github.com/jackc/pgconn v1.4.0/go.mod h1:Y2O3ZDF0q4mMacyWV3AstPJpeHXWGEetiFttmq5lahk=
github.com/jackc/pgconn v1.5.0/go.mod h1:QeD3lBfpTFe8WUnPZWN5KY/mB8FGMIYRdd8P8Jr0fAI=
github.com/jackc/pgconn v1.5.1-0.20200601181101-fa742c524853/go.mod h1:QeD3lBfpTFe8WUnPZWN5KY/mB8FGMIYRdd8P8Jr0fAI=
github.com/jackc/pgconn v1.8.1 h1:ySBX7Q87vOMqKU2bbmKbUvtYhauDFclYbNDYIE1/h6s=
github.com/jackc/pgconn v1.8.1/go.mod h1:JV6m6b6jhjdmzchES0drzCcYcAHS1OPD5xu3OZ/lE2g=
github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE=
github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8=
github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2 h1:JVX6jT/XfzNqIjye4717ITLaNwV9mWbJx0dLCpcRzdA=
github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE=
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
github.com/jackc/pgproto3 v1.1.0 h1:FYYE4yRw+AgI8wXIinMlNjBbp/UitDJwfj5LqqewP1A=
github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78=
github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA=
github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg=
github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=
github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=
github.com/jackc/pgproto3/v2 v2.0.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
github.com/jackc/pgproto3/v2 v2.0.6 h1:b1105ZGEMFe7aCvrT1Cca3VoVb4ZFMaFJLJcg/3zD+8=
github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
github.com/jackc/pgservicefile v0.0.0-20200307190119-3430c5407db8/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b h1:C8S2+VttkHFdOOCXJe+YGfa4vHYwlt4Zx+IVXQ97jYg=
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=
github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg=
github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc=
github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw=
github.com/jackc/pgtype v1.2.0/go.mod h1:5m2OfMh1wTK7x+Fk952IDmI4nw3nPrvtQdM0ZT4WpC0=
github.com/jackc/pgtype v1.3.1-0.20200510190516-8cd94a14c75a/go.mod h1:vaogEUkALtxZMCH411K+tKzNpwzCKU+AnPzBKZ+I+Po=
github.com/jackc/pgtype v1.3.1-0.20200606141011-f6355165a91c/go.mod h1:cvk9Bgu/VzJ9/lxTO5R5sf80p0DiucVtN7ZxvaC4GmQ=
github.com/jackc/pgtype v1.7.0 h1:6f4kVsW01QftE38ufBYxKciO6gyioXSC0ABIRLcZrGs=
github.com/jackc/pgtype v1.7.0/go.mod h1:ZnHF+rMePVqDKaOfJVI4Q8IVvAQMryDlDkZnKOI75BE=
github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y=
github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM=
github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc=
github.com/jackc/pgx/v4 v4.5.0/go.mod h1:EpAKPLdnTorwmPUUsqrPxy5fphV18j9q3wrfRXgo+kA=
github.com/jackc/pgx/v4 v4.6.1-0.20200510190926-94ba730bb1e9/go.mod h1:t3/cdRQl6fOLDxqtlyhe9UWgfIi9R8+8v8GKV5TRA/o=
github.com/jackc/pgx/v4 v4.6.1-0.20200606145419-4e5062306904/go.mod h1:ZDaNWkt9sW1JMiNn0kdYBaLelIhw7Pg4qd+Vk6tw7Hg=
github.com/jackc/pgx/v4 v4.11.0 h1:J86tSWd3Y7nKjwT/43xZBvpi04keQWx8gNC2YkdJhZI=
github.com/jackc/pgx/v4 v4.11.0/go.mod h1:i62xJgdrtVDsnL3U8ekyrQXEwGNTRoG7/8r+CIdYfcc=
github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v1.1.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v1.1.1/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
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.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
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/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/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
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/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.7.0 h1:h93mCPfUSkaul3Ka/VG8uZdmW1uMHDGxzu0NWHuJmHY= github.com/lib/pq v1.7.0 h1:h93mCPfUSkaul3Ka/VG8uZdmW1uMHDGxzu0NWHuJmHY=
github.com/lib/pq v1.7.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lib/pq v1.7.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM=
github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4=
github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
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/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
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/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg=
github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU=
github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k=
github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w=
github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs=
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis=
github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74=
github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA=
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac=
github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc=
github.com/pierrec/lz4 v2.0.5+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/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA=
github.com/pkg/profile v1.5.0 h1:042Buzk+NhDI+DeSAA62RwJL8VAuZUMQZUjCsRz1Mug= github.com/pkg/profile v1.5.0 h1:042Buzk+NhDI+DeSAA62RwJL8VAuZUMQZUjCsRz1Mug=
github.com/pkg/profile v1.5.0/go.mod h1:qBsxPvzyUincmltOk6iyRVxHYg4adc0OFOv72ZdLa18= github.com/pkg/profile v1.5.0/go.mod h1:qBsxPvzyUincmltOk6iyRVxHYg4adc0OFOv72ZdLa18=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/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/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU=
github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E=
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
github.com/shopspring/decimal v0.0.0-20200227202807-02e2044944cc/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ=
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
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.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY=
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 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/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
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/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg=
go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/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-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 h1:It14KIkyBFYkHkwZ7k45minvA9aorojkyjGk9KJ5B/w=
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
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-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
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/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-20181023162649-9b4f9f5ad519/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-20181201002055-351d144fa1fc/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-20190125091013-d26f9f9a57f3/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-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
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-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
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/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-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
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-20181026203630-95b1ffbd15a5/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-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
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-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
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/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/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-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/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-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
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-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
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-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
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-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o=
gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
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.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
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/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU=

View file

@ -45,19 +45,25 @@ func (s *ClauseSelect) Serialize(statementType StatementType, out *SQLBuilder, o
// ClauseFrom struct // ClauseFrom struct
type ClauseFrom struct { type ClauseFrom struct {
Table Serializer Tables []Serializer
} }
// Serialize serializes clause into SQLBuilder // Serialize serializes clause into SQLBuilder
func (f *ClauseFrom) Serialize(statementType StatementType, out *SQLBuilder, options ...SerializeOption) { func (f *ClauseFrom) Serialize(statementType StatementType, out *SQLBuilder, options ...SerializeOption) {
if f.Table == nil { if len(f.Tables) == 0 { // SELECT statement does not have to have FROM clause
return return
} }
out.NewLine() out.NewLine()
out.WriteString("FROM") out.WriteString("FROM")
out.IncreaseIdent() out.IncreaseIdent()
f.Table.serialize(statementType, out, FallTrough(options)...) for i, table := range f.Tables {
if i > 0 {
out.WriteString(",")
out.NewLine()
}
table.serialize(statementType, out, FallTrough(options)...)
}
out.DecreaseIdent() out.DecreaseIdent()
} }
@ -302,7 +308,7 @@ func (s *SetClause) Serialize(statementType StatementType, out *SQLBuilder, opti
panic("jet: nil column in columns list for SET clause") panic("jet: nil column in columns list for SET clause")
} }
out.WriteString(column.Name()) out.WriteIdentifier(column.Name())
out.WriteString(" = ") out.WriteString(" = ")

View file

@ -801,3 +801,8 @@ func newTimestampzFunc(name string, expressions ...Expression) *timestampzFunc {
return timestampzFunc return timestampzFunc
} }
// Func can be used to call an custom or as of yet unsupported function in the database.
func Func(name string, expressions ...Expression) Expression {
return newFunc(name, expressions, nil)
}

View file

@ -176,3 +176,7 @@ func TestTO_ASCII(t *testing.T) {
assertClauseSerialize(t, TO_ASCII(String("Karel")), `TO_ASCII($1)`, "Karel") assertClauseSerialize(t, TO_ASCII(String("Karel")), `TO_ASCII($1)`, "Karel")
assertClauseSerialize(t, TO_ASCII(String("Karel")), `TO_ASCII($1)`, "Karel") assertClauseSerialize(t, TO_ASCII(String("Karel")), `TO_ASCII($1)`, "Karel")
} }
func TestFunc(t *testing.T) {
assertClauseSerialize(t, Func("FOO", String("test"), NULL, MAX(Int(1))), "FOO($1, NULL, MAX($2))", "test", int64(1))
}

View file

@ -67,8 +67,7 @@ type integerLiteralExpression struct {
integerInterfaceImpl integerInterfaceImpl
} }
// Int creates new integer literal func intLiteral(value interface{}) IntegerExpression {
func Int(value int64) IntegerExpression {
numLiteral := &integerLiteralExpression{} numLiteral := &integerLiteralExpression{}
numLiteral.literalExpressionImpl = *literal(value) numLiteral.literalExpressionImpl = *literal(value)
@ -79,6 +78,46 @@ func Int(value int64) IntegerExpression {
return numLiteral return numLiteral
} }
// Int creates a new 64 bit signed integer literal
func Int(value int64) IntegerExpression {
return intLiteral(value)
}
// Int8 creates a new 8 bit signed integer literal
func Int8(value int8) IntegerExpression {
return intLiteral(value)
}
// Int16 creates a new 16 bit signed integer literal
func Int16(value int16) IntegerExpression {
return intLiteral(value)
}
// Int32 creates a new 32 bit signed integer literal
func Int32(value int32) IntegerExpression {
return intLiteral(value)
}
// Uint8 creates a new 8 bit unsigned integer literal
func Uint8(value uint8) IntegerExpression {
return intLiteral(value)
}
// Uint16 creates a new 16 bit unsigned integer literal
func Uint16(value uint16) IntegerExpression {
return intLiteral(value)
}
// Uint32 creates a new 32 bit unsigned integer literal
func Uint32(value uint32) IntegerExpression {
return intLiteral(value)
}
// Uint64 creates a new 64 bit unsigned integer literal
func Uint64(value uint64) IntegerExpression {
return intLiteral(value)
}
//---------------------------------------------------// //---------------------------------------------------//
type boolLiteralExpression struct { type boolLiteralExpression struct {
boolInterfaceImpl boolInterfaceImpl
@ -101,7 +140,7 @@ type floatLiteral struct {
literalExpressionImpl literalExpressionImpl
} }
// Float creates new float literal // Float creates new float literal from float64 value
func Float(value float64) FloatExpression { func Float(value float64) FloatExpression {
floatLiteral := floatLiteral{} floatLiteral := floatLiteral{}
floatLiteral.literalExpressionImpl = *literal(value) floatLiteral.literalExpressionImpl = *literal(value)
@ -111,6 +150,16 @@ func Float(value float64) FloatExpression {
return &floatLiteral return &floatLiteral
} }
// Decimal creates new float literal from string value
func Decimal(value string) FloatExpression {
floatLiteral := floatLiteral{}
floatLiteral.literalExpressionImpl = *literal(value)
floatLiteral.floatInterfaceImpl.parent = &floatLiteral
return &floatLiteral
}
//---------------------------------------------------// //---------------------------------------------------//
type stringLiteral struct { type stringLiteral struct {
stringInterfaceImpl stringInterfaceImpl
@ -346,17 +395,93 @@ type rawExpression struct {
ExpressionInterfaceImpl ExpressionInterfaceImpl
Raw string Raw string
NamedArgument map[string]interface{}
noWrap bool
} }
func (n *rawExpression) serialize(statement StatementType, out *SQLBuilder, options ...SerializeOption) { func (n *rawExpression) serialize(statement StatementType, out *SQLBuilder, options ...SerializeOption) {
out.WriteString(n.Raw) if !n.noWrap && !contains(options, NoWrap) {
out.WriteByte('(')
}
out.insertRawQuery(n.Raw, n.NamedArgument)
if !n.noWrap && !contains(options, NoWrap) {
out.WriteByte(')')
}
} }
// Raw can be used for any unsupported functions, operators or expressions. // Raw can be used for any unsupported functions, operators or expressions.
// For example: Raw("current_database()") // For example: Raw("current_database()")
func Raw(raw string, parent ...Expression) Expression { func Raw(raw string, namedArgs ...map[string]interface{}) Expression {
rawExp := &rawExpression{Raw: raw} var namedArguments map[string]interface{}
if len(namedArgs) > 0 {
namedArguments = namedArgs[0]
}
rawExp := &rawExpression{
Raw: raw,
NamedArgument: namedArguments,
}
rawExp.ExpressionInterfaceImpl.Parent = rawExp
return rawExp
}
// RawWithParent is a Raw constructor used for construction dialect specific expression
func RawWithParent(raw string, parent ...Expression) Expression {
rawExp := &rawExpression{
Raw: raw,
noWrap: true,
}
rawExp.ExpressionInterfaceImpl.Parent = OptionalOrDefaultExpression(rawExp, parent...) rawExp.ExpressionInterfaceImpl.Parent = OptionalOrDefaultExpression(rawExp, parent...)
return rawExp return rawExp
} }
// RawInt helper that for integer expressions
func RawInt(raw string, namedArgs ...map[string]interface{}) IntegerExpression {
return IntExp(Raw(raw, namedArgs...))
}
// RawFloat helper that for float expressions
func RawFloat(raw string, namedArgs ...map[string]interface{}) FloatExpression {
return FloatExp(Raw(raw, namedArgs...))
}
// RawString helper that for string expressions
func RawString(raw string, namedArgs ...map[string]interface{}) StringExpression {
return StringExp(Raw(raw, namedArgs...))
}
// RawTime helper that for time expressions
func RawTime(raw string, namedArgs ...map[string]interface{}) TimeExpression {
return TimeExp(Raw(raw, namedArgs...))
}
// RawTimez helper that for time with time zone expressions
func RawTimez(raw string, namedArgs ...map[string]interface{}) TimezExpression {
return TimezExp(Raw(raw, namedArgs...))
}
// RawTimestamp helper that for timestamp expressions
func RawTimestamp(raw string, namedArgs ...map[string]interface{}) TimestampExpression {
return TimestampExp(Raw(raw, namedArgs...))
}
// RawTimestampz helper that for timestamp with time zone expressions
func RawTimestampz(raw string, namedArgs ...map[string]interface{}) TimestampzExpression {
return TimestampzExp(Raw(raw, namedArgs...))
}
// RawDate helper that for date expressions
func RawDate(raw string, namedArgs ...map[string]interface{}) DateExpression {
return DateExp(Raw(raw, namedArgs...))
}
// UUID is a helper function to create string literal expression from uuid object
// value can be any uuid type with a String method
func UUID(value fmt.Stringer) StringExpression {
return String(value.String())
}

View file

@ -6,7 +6,7 @@ import (
) )
func TestRawExpression(t *testing.T) { func TestRawExpression(t *testing.T) {
assertClauseSerialize(t, Raw("current_database()"), "current_database()") assertClauseSerialize(t, Raw("current_database()"), "(current_database())")
var timeT = time.Date(2009, 11, 17, 20, 34, 58, 651387237, time.UTC) var timeT = time.Date(2009, 11, 17, 20, 34, 58, 651387237, time.UTC)

View file

@ -0,0 +1,47 @@
package jet
type rawStatementImpl struct {
serializerStatementInterfaceImpl
RawQuery string
NamedArguments map[string]interface{}
}
// RawStatement creates new sql statements from raw query and optional map of named arguments
func RawStatement(dialect Dialect, rawQuery string, namedArgument ...map[string]interface{}) Statement {
newRawStatement := rawStatementImpl{
serializerStatementInterfaceImpl: serializerStatementInterfaceImpl{
dialect: dialect,
statementType: "",
parent: nil,
},
RawQuery: rawQuery,
}
if len(namedArgument) > 0 {
newRawStatement.NamedArguments = namedArgument[0]
}
newRawStatement.parent = &newRawStatement
return &newRawStatement
}
func (s *rawStatementImpl) projections() ProjectionList {
return nil
}
func (s *rawStatementImpl) serialize(statement StatementType, out *SQLBuilder, options ...SerializeOption) {
if !contains(options, NoWrap) {
out.WriteString("(")
out.IncreaseIdent()
}
out.insertRawQuery(s.RawQuery, s.NamedArguments)
if !contains(options, NoWrap) {
out.DecreaseIdent()
out.NewLine()
out.WriteString(")")
}
}

View file

@ -13,8 +13,8 @@ type selectTableImpl struct {
} }
// NewSelectTable func // NewSelectTable func
func NewSelectTable(selectStmt SerializerStatement, alias string) SelectTable { func NewSelectTable(selectStmt SerializerStatement, alias string) selectTableImpl {
selectTable := &selectTableImpl{selectStmt: selectStmt, alias: alias} selectTable := selectTableImpl{selectStmt: selectStmt, alias: alias}
return selectTable return selectTable
} }
@ -38,3 +38,22 @@ func (s selectTableImpl) serialize(statement StatementType, out *SQLBuilder, opt
out.WriteString("AS") out.WriteString("AS")
out.WriteIdentifier(s.alias) out.WriteIdentifier(s.alias)
} }
// --------------------------------------
type lateralImpl struct {
selectTableImpl
}
// NewLateral creates new lateral expression from select statement with alias
func NewLateral(selectStmt SerializerStatement, alias string) SelectTable {
return lateralImpl{selectTableImpl: NewSelectTable(selectStmt, alias)}
}
func (s lateralImpl) serialize(statement StatementType, out *SQLBuilder, options ...SerializeOption) {
out.WriteString("LATERAL")
s.selectStmt.serialize(statement, out)
out.WriteString("AS")
out.WriteIdentifier(s.alias)
}

View file

@ -7,6 +7,7 @@ import (
"github.com/go-jet/jet/v2/internal/utils" "github.com/go-jet/jet/v2/internal/utils"
"github.com/google/uuid" "github.com/google/uuid"
"reflect" "reflect"
"sort"
"strconv" "strconv"
"strings" "strings"
"time" "time"
@ -135,6 +136,73 @@ func (s *SQLBuilder) insertParametrizedArgument(arg interface{}) {
s.WriteString(argPlaceholder) s.WriteString(argPlaceholder)
} }
func (s *SQLBuilder) insertRawQuery(raw string, namedArg map[string]interface{}) {
type namedArgumentPosition struct {
Name string
Value interface{}
Position int
}
var namedArgumentPositions []namedArgumentPosition
for namedArg, value := range namedArg {
rawCopy := raw
rawIndex := 0
exists := false
// one named argument can occur multiple times inside raw string
for {
index := strings.Index(rawCopy, namedArg)
if index == -1 {
break
}
exists = true
namedArgumentPositions = append(namedArgumentPositions, namedArgumentPosition{
Name: namedArg,
Value: value,
Position: rawIndex + index,
})
rawCopy = rawCopy[index+len(namedArg):]
rawIndex += index + len(namedArg)
}
if !exists {
panic("jet: named argument '" + namedArg + "' does not appear in raw query")
}
}
sort.Slice(namedArgumentPositions, func(i, j int) bool {
return namedArgumentPositions[i].Position < namedArgumentPositions[j].Position
})
for _, namedArgumentPos := range namedArgumentPositions {
// if named argument does not exists in raw string do not add argument to the list of arguments
// It can happen if the same argument occurs multiple times in postgres query.
if !strings.Contains(raw, namedArgumentPos.Name) {
continue
}
s.Args = append(s.Args, namedArgumentPos.Value)
currentArgNum := len(s.Args)
placeholder := s.Dialect.ArgumentPlaceholder()(currentArgNum)
// if placeholder is not unique identifier ($1, $2, etc..), we will replace just one occurrence of the argument
toReplace := -1 // all occurrences
if placeholder == "?" {
toReplace = 1 // just one occurrence
}
if s.Debug {
placeholder = argToString(namedArgumentPos.Value)
}
raw = strings.Replace(raw, namedArgumentPos.Name, placeholder, toReplace)
}
s.WriteString(raw)
}
func argToString(value interface{}) string { func argToString(value interface{}) string {
if utils.IsNil(value) { if utils.IsNil(value) {
return "NULL" return "NULL"

View file

@ -13,19 +13,30 @@ type Statement interface {
// DebugSql returns debug query where every parametrized placeholder is replaced with its argument. // DebugSql returns debug query where every parametrized placeholder is replaced with its argument.
// Do not use it in production. Use it only for debug purposes. // Do not use it in production. Use it only for debug purposes.
DebugSql() (query string) DebugSql() (query string)
// Query executes statement over database connection db and stores row result in destination. // Query executes statement over database connection/transaction db and stores row result in destination.
// Destination can be either pointer to struct or pointer to a slice. // Destination can be either pointer to struct or pointer to a slice.
// If destination is pointer to struct and query result set is empty, method returns qrm.ErrNoRows. // If destination is pointer to struct and query result set is empty, method returns qrm.ErrNoRows.
Query(db qrm.DB, destination interface{}) error Query(db qrm.DB, destination interface{}) error
// QueryContext executes statement with a context over database connection db and stores row result in destination. // QueryContext executes statement with a context over database connection/transaction db and stores row result in destination.
// Destination can be either pointer to struct or pointer to a slice. // Destination can be either pointer to struct or pointer to a slice.
// If destination is pointer to struct and query result set is empty, method returns qrm.ErrNoRows. // If destination is pointer to struct and query result set is empty, method returns qrm.ErrNoRows.
QueryContext(ctx context.Context, db qrm.DB, destination interface{}) error QueryContext(ctx context.Context, db qrm.DB, destination interface{}) error
//Exec executes statement over db connection/transaction without returning any rows.
//Exec executes statement over db connection without returning any rows.
Exec(db qrm.DB) (sql.Result, error) Exec(db qrm.DB) (sql.Result, error)
//Exec executes statement with context over db connection without returning any rows. //Exec executes statement with context over db connection/transaction without returning any rows.
ExecContext(ctx context.Context, db qrm.DB) (sql.Result, error) ExecContext(ctx context.Context, db qrm.DB) (sql.Result, error)
// Rows executes statements over db connection/transaction and returns rows
Rows(ctx context.Context, db qrm.DB) (*Rows, error)
}
// Rows wraps sql.Rows type to add query result mapping for Scan method
type Rows struct {
*sql.Rows
}
// Scan will map the Row values into struct destination
func (r *Rows) Scan(destination interface{}) error {
return qrm.ScanOneRowToDest(r.Rows, destination)
} }
// SerializerStatement interface // SerializerStatement interface
@ -99,6 +110,20 @@ func (s *serializerStatementInterfaceImpl) ExecContext(ctx context.Context, db q
return db.ExecContext(ctx, query, args...) return db.ExecContext(ctx, query, args...)
} }
func (s *serializerStatementInterfaceImpl) Rows(ctx context.Context, db qrm.DB) (*Rows, error) {
query, args := s.Sql()
callLogger(ctx, s)
rows, err := db.QueryContext(ctx, query, args...)
if err != nil {
return nil, err
}
return &Rows{rows}, nil
}
func callLogger(ctx context.Context, statement Statement) { func callLogger(ctx context.Context, statement Statement) {
if logger != nil { if logger != nil {
logger(ctx, statement) logger(ctx, statement)

View file

@ -15,20 +15,27 @@ type Table interface {
columns() []Column columns() []Column
SchemaName() string SchemaName() string
TableName() string TableName() string
AS(alias string) Alias() string
} }
// NewTable creates new table with schema Name, table Name and list of columns // NewTable creates new table with schema Name, table Name and list of columns
func NewTable(schemaName, name string, columns ...ColumnExpression) SerializerTable { func NewTable(schemaName, name, alias string, columns ...ColumnExpression) SerializerTable {
t := tableImpl{ t := tableImpl{
schemaName: schemaName, schemaName: schemaName,
name: name, name: name,
alias: alias,
columnList: columns, columnList: columns,
} }
columnTableName := name
if alias != "" {
columnTableName = alias
}
for _, c := range columns { for _, c := range columns {
c.setTableName(name) c.setTableName(columnTableName)
} }
return &t return &t
@ -41,14 +48,6 @@ type tableImpl struct {
columnList []ColumnExpression columnList []ColumnExpression
} }
func (t *tableImpl) AS(alias string) {
t.alias = alias
for _, c := range t.columnList {
c.setTableName(alias)
}
}
func (t *tableImpl) SchemaName() string { func (t *tableImpl) SchemaName() string {
return t.schemaName return t.schemaName
} }
@ -67,13 +66,21 @@ func (t *tableImpl) columns() []Column {
return ret return ret
} }
func (t *tableImpl) Alias() string {
return t.alias
}
func (t *tableImpl) serialize(statement StatementType, out *SQLBuilder, options ...SerializeOption) { func (t *tableImpl) serialize(statement StatementType, out *SQLBuilder, options ...SerializeOption) {
if t == nil { if t == nil {
panic("jet: tableImpl is nil") panic("jet: tableImpl is nil")
} }
// Use default schema if the schema name is not set
if len(t.schemaName) > 0 {
out.WriteIdentifier(t.schemaName) out.WriteIdentifier(t.schemaName)
out.WriteString(".") out.WriteString(".")
}
out.WriteIdentifier(t.name) out.WriteIdentifier(t.name)
if len(t.alias) > 0 { if len(t.alias) > 0 {
@ -129,9 +136,6 @@ func (t *joinTableImpl) TableName() string {
return "" return ""
} }
func (t *joinTableImpl) AS(alias string) {
}
func (t *joinTableImpl) columns() []Column { func (t *joinTableImpl) columns() []Column {
var ret []Column var ret []Column
@ -145,6 +149,10 @@ func (t *joinTableImpl) columns() []Column {
return ret return ret
} }
func (t *joinTableImpl) Alias() string {
return ""
}
func (t *joinTableImpl) serialize(statement StatementType, out *SQLBuilder, options ...SerializeOption) { func (t *joinTableImpl) serialize(statement StatementType, out *SQLBuilder, options ...SerializeOption) {
if t == nil { if t == nil {
panic("jet: Join table is nil. ") panic("jet: Join table is nil. ")

View file

@ -6,7 +6,7 @@ import (
) )
func TestNewTable(t *testing.T) { func TestNewTable(t *testing.T) {
newTable := NewTable("schema", "table", IntegerColumn("intCol")) newTable := NewTable("schema", "table", "", IntegerColumn("intCol"))
require.Equal(t, newTable.SchemaName(), "schema") require.Equal(t, newTable.SchemaName(), "schema")
require.Equal(t, newTable.TableName(), "table") require.Equal(t, newTable.TableName(), "table")
@ -16,8 +16,8 @@ func TestNewTable(t *testing.T) {
} }
func TestNewJoinTable(t *testing.T) { func TestNewJoinTable(t *testing.T) {
newTable1 := NewTable("schema", "table", IntegerColumn("intCol1")) newTable1 := NewTable("schema", "table", "", IntegerColumn("intCol1"))
newTable2 := NewTable("schema", "table2", IntegerColumn("intCol2")) newTable2 := NewTable("schema", "table2", "", IntegerColumn("intCol2"))
joinTable := NewJoinTable(newTable1, newTable2, InnerJoin, IntegerColumn("intCol1").EQ(IntegerColumn("intCol2"))) joinTable := NewJoinTable(newTable1, newTable2, InnerJoin, IntegerColumn("intCol1").EQ(IntegerColumn("intCol2")))

View file

@ -26,7 +26,7 @@ var (
table1ColBool = BoolColumn("col_bool") table1ColBool = BoolColumn("col_bool")
table1ColDate = DateColumn("col_date") table1ColDate = DateColumn("col_date")
) )
var table1 = NewTable("db", "table1", table1Col1, table1ColInt, table1ColFloat, table1Col3, table1ColTime, table1ColTimez, table1ColBool, table1ColDate, table1ColTimestamp, table1ColTimestampz) var table1 = NewTable("db", "table1", "", table1Col1, table1ColInt, table1ColFloat, table1Col3, table1ColTime, table1ColTimez, table1ColBool, table1ColDate, table1ColTimestamp, table1ColTimestampz)
var ( var (
table2Col3 = IntegerColumn("col3") table2Col3 = IntegerColumn("col3")
@ -41,14 +41,14 @@ var (
table2ColTimestampz = TimestampzColumn("col_timestampz") table2ColTimestampz = TimestampzColumn("col_timestampz")
table2ColDate = DateColumn("col_date") table2ColDate = DateColumn("col_date")
) )
var table2 = NewTable("db", "table2", table2Col3, table2Col4, table2ColInt, table2ColFloat, table2ColStr, table2ColBool, table2ColTime, table2ColTimez, table2ColDate, table2ColTimestamp, table2ColTimestampz) var table2 = NewTable("db", "table2", "", table2Col3, table2Col4, table2ColInt, table2ColFloat, table2ColStr, table2ColBool, table2ColTime, table2ColTimez, table2ColDate, table2ColTimestamp, table2ColTimestampz)
var ( var (
table3Col1 = IntegerColumn("col1") table3Col1 = IntegerColumn("col1")
table3ColInt = IntegerColumn("col_int") table3ColInt = IntegerColumn("col_int")
table3StrCol = StringColumn("col2") table3StrCol = StringColumn("col2")
) )
var table3 = NewTable("db", "table3", table3Col1, table3ColInt, table3StrCol) var table3 = NewTable("db", "table3", "", table3Col1, table3ColInt, table3StrCol)
func assertClauseSerialize(t *testing.T, clause Serializer, query string, args ...interface{}) { func assertClauseSerialize(t *testing.T, clause Serializer, query string, args ...interface{}) {
out := SQLBuilder{Dialect: defaultDialect} out := SQLBuilder{Dialect: defaultDialect}

View file

@ -8,6 +8,7 @@ import (
"github.com/go-jet/jet/v2/internal/utils" "github.com/go-jet/jet/v2/internal/utils"
"github.com/go-jet/jet/v2/qrm" "github.com/go-jet/jet/v2/qrm"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"io/ioutil" "io/ioutil"
"os" "os"
@ -89,7 +90,7 @@ func AssertJSONFile(t *testing.T, data interface{}, testRelativePath string) {
// AssertStatementSql check if statement Sql() is the same as expectedQuery and expectedArgs // AssertStatementSql check if statement Sql() is the same as expectedQuery and expectedArgs
func AssertStatementSql(t *testing.T, query jet.Statement, expectedQuery string, expectedArgs ...interface{}) { func AssertStatementSql(t *testing.T, query jet.Statement, expectedQuery string, expectedArgs ...interface{}) {
queryStr, args := query.Sql() queryStr, args := query.Sql()
require.Equal(t, queryStr, expectedQuery) assertQueryString(t, queryStr, expectedQuery)
if len(expectedArgs) == 0 { if len(expectedArgs) == 0 {
return return
@ -115,8 +116,8 @@ func AssertDebugStatementSql(t *testing.T, query jet.Statement, expectedQuery st
AssertDeepEqual(t, args, expectedArgs, "arguments are not equal") AssertDeepEqual(t, args, expectedArgs, "arguments are not equal")
} }
debuqSql := query.DebugSql() debugSql := query.DebugSql()
require.Equal(t, debuqSql, expectedQuery) assertQueryString(t, debugSql, expectedQuery)
} }
// AssertSerialize checks if clause serialize produces expected query and args // AssertSerialize checks if clause serialize produces expected query and args
@ -133,18 +134,6 @@ func AssertSerialize(t *testing.T, dialect jet.Dialect, serializer jet.Serialize
} }
} }
// AssertClauseSerialize checks if clause serialize produces expected query and args
func AssertClauseSerialize(t *testing.T, dialect jet.Dialect, clause jet.Clause, query string, args ...interface{}) {
out := jet.SQLBuilder{Dialect: dialect}
clause.Serialize(jet.SelectStatementType, &out)
require.Equal(t, out.Buff.String(), query)
if len(args) > 0 {
AssertDeepEqual(t, out.Args, args)
}
}
// AssertDebugSerialize checks if clause serialize produces expected debug query and args // AssertDebugSerialize checks if clause serialize produces expected debug query and args
func AssertDebugSerialize(t *testing.T, dialect jet.Dialect, clause jet.Serializer, query string, args ...interface{}) { func AssertDebugSerialize(t *testing.T, dialect jet.Dialect, clause jet.Serializer, query string, args ...interface{}) {
out := jet.SQLBuilder{Dialect: dialect, Debug: true} out := jet.SQLBuilder{Dialect: dialect, Debug: true}
@ -157,6 +146,18 @@ func AssertDebugSerialize(t *testing.T, dialect jet.Dialect, clause jet.Serializ
} }
} }
// AssertClauseSerialize checks if clause serialize produces expected query and args
func AssertClauseSerialize(t *testing.T, dialect jet.Dialect, clause jet.Clause, query string, args ...interface{}) {
out := jet.SQLBuilder{Dialect: dialect}
clause.Serialize(jet.SelectStatementType, &out)
require.Equal(t, out.Buff.String(), query)
if len(args) > 0 {
AssertDeepEqual(t, out.Args, args)
}
}
// AssertPanicErr checks if running a function fun produces a panic with errorStr string // AssertPanicErr checks if running a function fun produces a panic with errorStr string
func AssertPanicErr(t *testing.T, fun func(), errorStr string) { func AssertPanicErr(t *testing.T, fun func(), errorStr string) {
defer func() { defer func() {
@ -194,7 +195,7 @@ func AssertQueryPanicErr(t *testing.T, stmt jet.Statement, db qrm.DB, dest inter
require.Equal(t, r, errString) require.Equal(t, r, errString)
}() }()
stmt.Query(db, dest) _ = stmt.Query(db, dest)
} }
// AssertFileContent check if file content at filePath contains expectedContent text. // AssertFileContent check if file content at filePath contains expectedContent text.
@ -223,7 +224,24 @@ func AssertFileNamesEqual(t *testing.T, fileInfos []os.FileInfo, fileNames ...st
// AssertDeepEqual checks if actual and expected objects are deeply equal. // AssertDeepEqual checks if actual and expected objects are deeply equal.
func AssertDeepEqual(t *testing.T, actual, expected interface{}, msg ...string) { func AssertDeepEqual(t *testing.T, actual, expected interface{}, msg ...string) {
require.True(t, cmp.Equal(actual, expected), msg) if !assert.True(t, cmp.Equal(actual, expected), msg) {
printDiff(actual, expected)
t.FailNow()
}
}
func assertQueryString(t *testing.T, actual, expected string) {
if !assert.Equal(t, actual, expected) {
printDiff(actual, expected)
t.FailNow()
}
}
func printDiff(actual, expected interface{}) {
fmt.Println("Actual: ")
fmt.Println(actual)
fmt.Println("Expected: ")
fmt.Println(expected)
} }
// BoolPtr returns address of bool parameter // BoolPtr returns address of bool parameter

View file

@ -7,7 +7,7 @@ type DeleteStatement interface {
Statement Statement
WHERE(expression BoolExpression) DeleteStatement WHERE(expression BoolExpression) DeleteStatement
ORDER_BY(orderByClauses ...jet.OrderByClause) DeleteStatement ORDER_BY(orderByClauses ...OrderByClause) DeleteStatement
LIMIT(limit int64) DeleteStatement LIMIT(limit int64) DeleteStatement
} }
@ -38,7 +38,7 @@ func (d *deleteStatementImpl) WHERE(expression BoolExpression) DeleteStatement {
return d return d
} }
func (d *deleteStatementImpl) ORDER_BY(orderByClauses ...jet.OrderByClause) DeleteStatement { func (d *deleteStatementImpl) ORDER_BY(orderByClauses ...OrderByClause) DeleteStatement {
d.OrderBy.List = orderByClauses d.OrderBy.List = orderByClauses
return d return d
} }

View file

@ -3,7 +3,7 @@ package mysql
import "github.com/go-jet/jet/v2/internal/jet" import "github.com/go-jet/jet/v2/internal/jet"
// Expression is common interface for all expressions. // Expression is common interface for all expressions.
// Can be Bool, Int, Float, String, Date, Time, Timez, Timestamp or Timestampz expressions. // Can be Bool, Int, Float, String, Date, Time or Timestamp expressions.
type Expression = jet.Expression type Expression = jet.Expression
// BoolExpression interface // BoolExpression interface
@ -70,9 +70,25 @@ var DateTimeExp = jet.TimestampExp
// Does not add sql cast to generated sql builder output. // Does not add sql cast to generated sql builder output.
var TimestampExp = jet.TimestampExp var TimestampExp = jet.TimestampExp
// RawArgs is type used to pass optional arguments to Raw method
type RawArgs = map[string]interface{}
// Raw can be used for any unsupported functions, operators or expressions. // Raw can be used for any unsupported functions, operators or expressions.
// For example: Raw("current_database()") // For example: Raw("current_database()")
var Raw = jet.Raw // Raw helper methods for each of the mysql types
var (
Raw = jet.Raw
RawInt = jet.RawInt
RawFloat = jet.RawFloat
RawString = jet.RawString
RawTime = jet.RawTime
RawTimestamp = jet.RawTimestamp
RawDate = jet.RawDate
)
// Func can be used to call an custom or as of yet unsupported function in the database.
var Func = jet.Func
// NewEnumValue creates new named enum value // NewEnumValue creates new named enum value
var NewEnumValue = jet.NewEnumValue var NewEnumValue = jet.NewEnumValue

62
mysql/expressions_test.go Normal file
View file

@ -0,0 +1,62 @@
package mysql
import (
"testing"
time2 "time"
"github.com/stretchr/testify/require"
)
func TestRaw(t *testing.T) {
assertSerialize(t, Raw("current_database()"), "(current_database())")
assertDebugSerialize(t, Raw("current_database()"), "(current_database())")
assertSerialize(t, Raw(":first_arg + table.colInt + :second_arg", RawArgs{":first_arg": 11, ":second_arg": 22}),
"(? + table.colInt + ?)", 11, 22)
assertDebugSerialize(t, Raw(":first_arg + table.colInt + :second_arg", RawArgs{":first_arg": 11, ":second_arg": 22}),
"(11 + table.colInt + 22)")
assertSerialize(t,
Int(700).ADD(RawInt("#1 + table.colInt + #2", RawArgs{"#1": 11, "#2": 22})),
"(? + (? + table.colInt + ?))",
int64(700), 11, 22)
assertDebugSerialize(t,
Int(700).ADD(RawInt("#1 + table.colInt + #2", RawArgs{"#1": 11, "#2": 22})),
"(700 + (11 + table.colInt + 22))")
}
func TestRawDuplicateArguments(t *testing.T) {
assertSerialize(t, Raw(":arg + table.colInt + :arg", RawArgs{":arg": 11}),
"(? + table.colInt + ?)", 11, 11)
assertSerialize(t, Raw("#age + table.colInt + #year + #age + #year + 11", RawArgs{"#age": 11, "#year": 2000}),
"(? + table.colInt + ? + ? + ? + 11)", 11, 2000, 11, 2000)
assertSerialize(t, Raw("#1 + all_types.integer + #2 + #1 + #2 + #3 + #4",
RawArgs{"#1": 11, "#2": 22, "#3": 33, "#4": 44}),
`(? + all_types.integer + ? + ? + ? + ? + ?)`, 11, 22, 11, 22, 33, 44)
}
func TestRawInvalidArguments(t *testing.T) {
defer func() {
r := recover()
require.Equal(t, "jet: named argument 'first_arg' does not appear in raw query", r)
}()
assertSerialize(t, Raw("table.colInt + :second_arg", RawArgs{"first_arg": 11}), "(table.colInt + ?)", 22)
}
func TestRawType(t *testing.T) {
assertSerialize(t, RawFloat("table.colInt + &float", RawArgs{"&float": 11.22}).EQ(Float(3.14)),
"((table.colInt + ?) = ?)", 11.22, 3.14)
assertSerialize(t, RawString("table.colStr || str", RawArgs{"str": "doe"}).EQ(String("john doe")),
"((table.colStr || ?) = ?)", "doe", "john doe")
time := time2.Now()
assertSerialize(t, RawTime("table.colTime").EQ(TimeT(time)),
"((table.colTime) = CAST(? AS TIME))", time)
assertSerialize(t, RawTimestamp("table.colTimestamp").EQ(TimestampT(time)),
"((table.colTimestamp) = TIMESTAMP(?))", time)
assertSerialize(t, RawDate("table.colDate").EQ(DateT(time)),
"((table.colDate) = CAST(? AS DATE))", time)
}

View file

@ -253,8 +253,3 @@ var EXISTS = jet.EXISTS
// CASE create CASE operator with optional list of expressions // CASE create CASE operator with optional list of expressions
var CASE = jet.CASE var CASE = jet.CASE
//----------------- Bit operators ---------------//
// BIT_NOT inverts every bit in integer expression
var BIT_NOT = jet.BIT_NOT

View file

@ -97,7 +97,7 @@ func INTERVAL(value interface{}, unitType unitType) Interval {
// INTERVALe creates new temporal interval from expresion and unit type. // INTERVALe creates new temporal interval from expresion and unit type.
func INTERVALe(expr Expression, unitType unitType) Interval { func INTERVALe(expr Expression, unitType unitType) Interval {
return jet.NewInterval(jet.ListSerializer{ return jet.NewInterval(jet.ListSerializer{
Serializers: []jet.Serializer{expr, jet.Raw(string(unitType))}, Serializers: []jet.Serializer{expr, jet.RawWithParent(string(unitType))},
Separator: " ", Separator: " ",
}) })
} }

24
mysql/lateral.go Normal file
View file

@ -0,0 +1,24 @@
package mysql
import "github.com/go-jet/jet/v2/internal/jet"
// LATERAL derived tables constructor from select statement
func LATERAL(selectStmt SelectStatement) lateralImpl {
return lateralImpl{
selectStmt: selectStmt,
}
}
type lateralImpl struct {
selectStmt SelectStatement
}
func (l lateralImpl) AS(alias string) SelectTable {
subQuery := &selectTableImpl{
SelectTable: jet.NewLateral(l.selectStmt, alias),
}
subQuery.readableTableInterfaceImpl.parent = subQuery
return subQuery
}

View file

@ -15,15 +15,46 @@ var (
// Bool creates new bool literal expression // Bool creates new bool literal expression
var Bool = jet.Bool var Bool = jet.Bool
// Int is constructor for integer expressions literals. // Int is constructor for 64 bit signed integer expressions literals.
var Int = jet.Int var Int = jet.Int
// Float creates new float literal expression // Int8 is constructor for 8 bit signed integer expressions literals.
var Int8 = jet.Int8
// Int16 is constructor for 16 bit signed integer expressions literals.
var Int16 = jet.Int16
// Int32 is constructor for 32 bit signed integer expressions literals.
var Int32 = jet.Int32
// Int64 is constructor for 64 bit signed integer expressions literals.
var Int64 = jet.Int
// Uint8 is constructor for 8 bit unsigned integer expressions literals.
var Uint8 = jet.Uint8
// Uint16 is constructor for 16 bit unsigned integer expressions literals.
var Uint16 = jet.Uint16
// Uint32 is constructor for 32 bit unsigned integer expressions literals.
var Uint32 = jet.Uint32
// Uint64 is constructor for 64 bit unsigned integer expressions literals.
var Uint64 = jet.Uint64
// Float creates new float literal expression from float64 value
var Float = jet.Float var Float = jet.Float
// Decimal creates new float literal expression from string value
var Decimal = jet.Decimal
// String creates new string literal expression // String creates new string literal expression
var String = jet.String var String = jet.String
// UUID is a helper function to create string literal expression from uuid object
// value can be any uuid type with a String method
var UUID = jet.UUID
// Date creates new date literal // Date creates new date literal
var Date = func(year int, month time.Month, day int) DateExpression { var Date = func(year int, month time.Month, day int) DateExpression {
return CAST(jet.Date(year, month, day)).AS_DATE() return CAST(jet.Date(year, month, day)).AS_DATE()

View file

@ -1,6 +1,7 @@
package mysql package mysql
import ( import (
"math"
"testing" "testing"
"time" "time"
) )
@ -13,6 +14,46 @@ func TestInt(t *testing.T) {
assertSerialize(t, Int(11), `?`, int64(11)) assertSerialize(t, Int(11), `?`, int64(11))
} }
func TestInt8(t *testing.T) {
val := int8(math.MinInt8)
assertSerialize(t, Int8(val), `?`, val)
}
func TestInt16(t *testing.T) {
val := int16(math.MinInt16)
assertSerialize(t, Int16(val), `?`, val)
}
func TestInt32(t *testing.T) {
val := int32(math.MinInt32)
assertSerialize(t, Int32(val), `?`, val)
}
func TestInt64(t *testing.T) {
val := int64(math.MinInt64)
assertSerialize(t, Int64(val), `?`, val)
}
func TestUint8(t *testing.T) {
val := uint8(math.MaxUint8)
assertSerialize(t, Uint8(val), `?`, val)
}
func TestUint16(t *testing.T) {
val := uint16(math.MaxUint16)
assertSerialize(t, Uint16(val), `?`, val)
}
func TestUint32(t *testing.T) {
val := uint32(math.MaxUint32)
assertSerialize(t, Uint32(val), `?`, val)
}
func TestUint64(t *testing.T) {
val := uint64(math.MaxUint64)
assertSerialize(t, Uint64(val), `?`, val)
}
func TestFloat(t *testing.T) { func TestFloat(t *testing.T) {
assertSerialize(t, Float(12.34), `?`, float64(12.34)) assertSerialize(t, Float(12.34), `?`, float64(12.34))
} }

9
mysql/operators.go Normal file
View file

@ -0,0 +1,9 @@
package mysql
import "github.com/go-jet/jet/v2/internal/jet"
// NOT returns negation of bool expression result
var NOT = jet.NOT
// BIT_NOT inverts every bit in integer expression result
var BIT_NOT = jet.BIT_NOT

View file

@ -41,12 +41,12 @@ type SelectStatement interface {
Expression Expression
DISTINCT() SelectStatement DISTINCT() SelectStatement
FROM(table ReadableTable) SelectStatement FROM(tables ...ReadableTable) SelectStatement
WHERE(expression BoolExpression) SelectStatement WHERE(expression BoolExpression) SelectStatement
GROUP_BY(groupByClauses ...jet.GroupByClause) SelectStatement GROUP_BY(groupByClauses ...jet.GroupByClause) SelectStatement
HAVING(boolExpression BoolExpression) SelectStatement HAVING(boolExpression BoolExpression) SelectStatement
WINDOW(name string) windowExpand WINDOW(name string) windowExpand
ORDER_BY(orderByClauses ...jet.OrderByClause) SelectStatement ORDER_BY(orderByClauses ...OrderByClause) SelectStatement
LIMIT(limit int64) SelectStatement LIMIT(limit int64) SelectStatement
OFFSET(offset int64) SelectStatement OFFSET(offset int64) SelectStatement
FOR(lock RowLock) SelectStatement FOR(lock RowLock) SelectStatement
@ -70,7 +70,9 @@ func newSelectStatement(table ReadableTable, projections []Projection) SelectSta
&newSelect.Limit, &newSelect.Offset, &newSelect.For, &newSelect.ShareLock) &newSelect.Limit, &newSelect.Offset, &newSelect.For, &newSelect.ShareLock)
newSelect.Select.ProjectionList = projections newSelect.Select.ProjectionList = projections
newSelect.From.Table = table if table != nil {
newSelect.From.Tables = []jet.Serializer{table}
}
newSelect.Limit.Count = -1 newSelect.Limit.Count = -1
newSelect.Offset.Count = -1 newSelect.Offset.Count = -1
newSelect.ShareLock.Name = "LOCK IN SHARE MODE" newSelect.ShareLock.Name = "LOCK IN SHARE MODE"
@ -103,8 +105,11 @@ func (s *selectStatementImpl) DISTINCT() SelectStatement {
return s return s
} }
func (s *selectStatementImpl) FROM(table ReadableTable) SelectStatement { func (s *selectStatementImpl) FROM(tables ...ReadableTable) SelectStatement {
s.From.Table = table s.From.Tables = nil
for _, table := range tables {
s.From.Tables = append(s.From.Tables, table)
}
return s return s
} }
@ -128,7 +133,7 @@ func (s *selectStatementImpl) WINDOW(name string) windowExpand {
return windowExpand{selectStatement: s} return windowExpand{selectStatement: s}
} }
func (s *selectStatementImpl) ORDER_BY(orderByClauses ...jet.OrderByClause) SelectStatement { func (s *selectStatementImpl) ORDER_BY(orderByClauses ...OrderByClause) SelectStatement {
s.OrderBy.List = orderByClauses s.OrderBy.List = orderByClauses
return s return s
} }

View file

@ -132,3 +132,25 @@ FROM db.table1
LOCK IN SHARE MODE; LOCK IN SHARE MODE;
`) `)
} }
func TestSelect_NOT_EXISTS(t *testing.T) {
testutils.AssertStatementSql(t,
SELECT(table1ColInt).
FROM(table1).
WHERE(
NOT(EXISTS(
SELECT(table2ColInt).
FROM(table2).
WHERE(
table1ColInt.EQ(table2ColInt),
),
))), `
SELECT table1.col_int AS "table1.col_int"
FROM db.table1
WHERE (NOT (EXISTS (
SELECT table2.col_int AS "table2.col_int"
FROM db.table2
WHERE table1.col_int = table2.col_int
)));
`)
}

View file

@ -17,7 +17,7 @@ func UNION_ALL(lhs, rhs jet.SerializerStatement, selects ...jet.SerializerStatem
type setStatement interface { type setStatement interface {
setOperators setOperators
ORDER_BY(orderByClauses ...jet.OrderByClause) setStatement ORDER_BY(orderByClauses ...OrderByClause) setStatement
LIMIT(limit int64) setStatement LIMIT(limit int64) setStatement
OFFSET(offset int64) setStatement OFFSET(offset int64) setStatement
@ -70,7 +70,7 @@ func newSetStatementImpl(operator string, all bool, selects []jet.SerializerStat
return newSetStatement return newSetStatement
} }
func (s *setStatementImpl) ORDER_BY(orderByClauses ...jet.OrderByClause) setStatement { func (s *setStatementImpl) ORDER_BY(orderByClauses ...OrderByClause) setStatement {
s.setOperator.OrderBy.List = orderByClauses s.setOperator.OrderBy.List = orderByClauses
return s return s
} }

8
mysql/statement.go Normal file
View file

@ -0,0 +1,8 @@
package mysql
import "github.com/go-jet/jet/v2/internal/jet"
// RawStatement creates new sql statements from raw query and optional map of named arguments
func RawStatement(rawQuery string, namedArguments ...RawArgs) Statement {
return jet.RawStatement(Dialect, rawQuery, namedArguments...)
}

View file

@ -77,9 +77,9 @@ func (r readableTableInterfaceImpl) CROSS_JOIN(table ReadableTable) joinSelectUp
} }
// NewTable creates new table with schema Name, table Name and list of columns // NewTable creates new table with schema Name, table Name and list of columns
func NewTable(schemaName, name string, columns ...jet.ColumnExpression) Table { func NewTable(schemaName, name, alias string, columns ...jet.ColumnExpression) Table {
t := &tableImpl{ t := &tableImpl{
SerializerTable: jet.NewTable(schemaName, name, columns...), SerializerTable: jet.NewTable(schemaName, name, alias, columns...),
} }
t.readableTableInterfaceImpl.parent = t t.readableTableInterfaceImpl.parent = t

View file

@ -17,5 +17,8 @@ type ColumnAssigment = jet.ColumnAssigment
// PrintableStatement is a statement which sql query can be logged // PrintableStatement is a statement which sql query can be logged
type PrintableStatement = jet.PrintableStatement type PrintableStatement = jet.PrintableStatement
// OrderByClause is the combination of an expression and the wanted ordering to use as input for ORDER BY.
type OrderByClause = jet.OrderByClause
// SetLogger sets automatic statement logging // SetLogger sets automatic statement logging
var SetLogger = jet.SetLoggerFunc var SetLogger = jet.SetLoggerFunc

View file

@ -2,6 +2,7 @@ package mysql
import ( import (
"fmt" "fmt"
"strings"
"testing" "testing"
) )
@ -52,11 +53,29 @@ WHERE table1.col1 = ?;
). ).
WHERE(table1Col1.EQ(Int(2))) WHERE(table1Col1.EQ(Int(2)))
//fmt.Println(stmt.Sql())
assertStatementSql(t, stmt, expectedSQL, int64(2)) assertStatementSql(t, stmt, expectedSQL, int64(2))
} }
func TestUpdateReservedWorldColumn(t *testing.T) {
type table struct {
Load string
}
loadColumn := StringColumn("Load")
assertStatementSql(t,
table1.UPDATE(loadColumn).
MODEL(
table{
Load: "foo",
},
).
WHERE(loadColumn.EQ(String("bar"))), strings.Replace(`
UPDATE db.table1
SET ''Load'' = ?
WHERE ''Load'' = ?;
`, "''", "`", -1), "foo", "bar")
}
func TestInvalidInputs(t *testing.T) { func TestInvalidInputs(t *testing.T) {
assertStatementSqlErr(t, table1.UPDATE(table1ColInt).SET(1), "jet: WHERE clause not set") assertStatementSqlErr(t, table1.UPDATE(table1ColInt).SET(1), "jet: WHERE clause not set")
assertStatementSqlErr(t, table1.UPDATE(nil).SET(1), "jet: nil column in columns list for SET clause") assertStatementSqlErr(t, table1.UPDATE(nil).SET(1), "jet: nil column in columns list for SET clause")

View file

@ -16,19 +16,7 @@ var table1ColTimestamp = TimestampColumn("col_timestamp")
var table1ColDate = DateColumn("col_date") var table1ColDate = DateColumn("col_date")
var table1ColTime = TimeColumn("col_time") var table1ColTime = TimeColumn("col_time")
var table1 = NewTable( var table1 = NewTable("db", "table1", "", table1Col1, table1ColInt, table1ColFloat, table1ColString, table1Col3, table1ColBool, table1ColDate, table1ColTimestamp, table1ColTime)
"db",
"table1",
table1Col1,
table1ColInt,
table1ColFloat,
table1ColString,
table1Col3,
table1ColBool,
table1ColDate,
table1ColTimestamp,
table1ColTime,
)
var table2Col3 = IntegerColumn("col3") var table2Col3 = IntegerColumn("col3")
var table2Col4 = IntegerColumn("col4") var table2Col4 = IntegerColumn("col4")
@ -39,28 +27,12 @@ var table2ColBool = BoolColumn("col_bool")
var table2ColTimestamp = TimestampColumn("col_timestamp") var table2ColTimestamp = TimestampColumn("col_timestamp")
var table2ColDate = DateColumn("col_date") var table2ColDate = DateColumn("col_date")
var table2 = NewTable( var table2 = NewTable("db", "table2", "", table2Col3, table2Col4, table2ColInt, table2ColFloat, table2ColStr, table2ColBool, table2ColDate, table2ColTimestamp)
"db",
"table2",
table2Col3,
table2Col4,
table2ColInt,
table2ColFloat,
table2ColStr,
table2ColBool,
table2ColDate,
table2ColTimestamp,
)
var table3Col1 = IntegerColumn("col1") var table3Col1 = IntegerColumn("col1")
var table3ColInt = IntegerColumn("col_int") var table3ColInt = IntegerColumn("col_int")
var table3StrCol = StringColumn("col2") var table3StrCol = StringColumn("col2")
var table3 = NewTable( var table3 = NewTable("db", "table3", "", table3Col1, table3ColInt, table3StrCol)
"db",
"table3",
table3Col1,
table3ColInt,
table3StrCol)
func assertSerialize(t *testing.T, clause jet.Serializer, query string, args ...interface{}) { func assertSerialize(t *testing.T, clause jet.Serializer, query string, args ...interface{}) {
testutils.AssertSerialize(t, Dialect, clause, query, args...) testutils.AssertSerialize(t, Dialect, clause, query, args...)

View file

@ -80,13 +80,7 @@ func TestReservedWordEscaped(t *testing.T) {
var table1ColVariadic = IntervalColumn("VARIADIC") var table1ColVariadic = IntervalColumn("VARIADIC")
var table1ColProcedure = IntervalColumn("procedure") var table1ColProcedure = IntervalColumn("procedure")
_ = NewTable( _ = NewTable("db", "table1", "", table1ColUser, table1ColVariadic, table1ColProcedure)
"db",
"table1",
table1ColUser,
table1ColVariadic,
table1ColProcedure,
)
assertSerialize(t, table1ColUser, `table1."user"`) assertSerialize(t, table1ColUser, `table1."user"`)
assertSerialize(t, table1ColVariadic, `table1."VARIADIC"`) assertSerialize(t, table1ColVariadic, `table1."VARIADIC"`)

View file

@ -81,9 +81,27 @@ var TimestampExp = jet.TimestampExp
// Does not add sql cast to generated sql builder output. // Does not add sql cast to generated sql builder output.
var TimestampzExp = jet.TimestampzExp var TimestampzExp = jet.TimestampzExp
// RawArgs is type used to pass optional arguments to Raw method
type RawArgs = map[string]interface{}
// Raw can be used for any unsupported functions, operators or expressions. // Raw can be used for any unsupported functions, operators or expressions.
// For example: Raw("current_database()") // For example: Raw("current_database()")
var Raw = jet.Raw // Raw helper methods for each of the postgres types
var (
Raw = jet.Raw
RawInt = jet.RawInt
RawFloat = jet.RawFloat
RawString = jet.RawString
RawTime = jet.RawTime
RawTimez = jet.RawTimez
RawTimestamp = jet.RawTimestamp
RawTimestampz = jet.RawTimestampz
RawDate = jet.RawDate
)
// Func can be used to call an custom or as of yet unsupported function in the database.
var Func = jet.Func
// NewEnumValue creates new named enum value // NewEnumValue creates new named enum value
var NewEnumValue = jet.NewEnumValue var NewEnumValue = jet.NewEnumValue

View file

@ -0,0 +1,76 @@
package postgres
import (
"testing"
"time"
"github.com/stretchr/testify/require"
)
func TestRaw(t *testing.T) {
assertSerialize(t, Raw("current_database()"), "(current_database())")
assertDebugSerialize(t, Raw("current_database()"), "(current_database())")
assertSerialize(t, Raw(":first_arg + table.colInt + :second_arg", RawArgs{":first_arg": 11, ":second_arg": 22}),
"($1 + table.colInt + $2)", 11, 22)
assertDebugSerialize(t, Raw(":first_arg + table.colInt + :second_arg", RawArgs{":first_arg": 11, ":second_arg": 22}),
"(11 + table.colInt + 22)")
assertSerialize(t,
Int(700).ADD(RawInt(":first_arg + table.colInt + :second_arg", RawArgs{":first_arg": 11, ":second_arg": 22})),
"($1 + ($2 + table.colInt + $3))",
int64(700), 11, 22)
assertDebugSerialize(t,
Int(700).ADD(RawInt(":first_arg + table.colInt + :second_arg", RawArgs{":first_arg": 11, ":second_arg": 22})),
"(700 + (11 + table.colInt + 22))")
}
func TestDuplicateArguments(t *testing.T) {
assertSerialize(t, Raw(":arg + table.colInt + :arg", RawArgs{":arg": 11}),
"($1 + table.colInt + $1)", 11)
assertDebugSerialize(t, Raw(":arg + table.colInt + :arg", RawArgs{":arg": 11}),
"(11 + table.colInt + 11)")
assertSerialize(t, Raw("#age + table.colInt + #year + #age + #year + 11", RawArgs{"#age": 11, "#year": 2000}),
"($1 + table.colInt + $2 + $1 + $2 + 11)", 11, 2000)
assertDebugSerialize(t, Raw("#age + table.colInt + #year + #age + #year + 11", RawArgs{"#age": 11, "#year": 2000}),
"(11 + table.colInt + 2000 + 11 + 2000 + 11)")
assertSerialize(t, Raw("#1 + all_types.integer + #2 + #1 + #2 + #3 + #4",
RawArgs{"#1": 11, "#2": 22, "#3": 33, "#4": 44}),
`($1 + all_types.integer + $2 + $1 + $2 + $3 + $4)`, 11, 22, 33, 44)
assertDebugSerialize(t, Raw("#1 + all_types.integer + #2 + #1 + #2 + #3 + #4",
RawArgs{"#1": 11, "#2": 22, "#3": 33, "#4": 44}),
`(11 + all_types.integer + 22 + 11 + 22 + 33 + 44)`)
}
func TestRawInvalidArguments(t *testing.T) {
defer func() {
r := recover()
require.Equal(t, "jet: named argument 'first_arg' does not appear in raw query", r)
}()
assertSerialize(t, Raw("table.colInt + :second_arg", RawArgs{
"first_arg": 11,
"second_arg": 22,
}), "(table.colInt + $1)", 22)
}
func TestRawHelperMethods(t *testing.T) {
assertSerialize(t, RawFloat("table.colInt + :float", RawArgs{":float": 11.22}).EQ(Float(3.14)),
"((table.colInt + $1) = $2)", 11.22, 3.14)
assertSerialize(t, RawString("table.colStr || str", RawArgs{"str": "doe"}).EQ(String("john doe")),
"((table.colStr || $1) = $2)", "doe", "john doe")
now := time.Now()
assertSerialize(t, RawTime("table.colTime").EQ(TimeT(now)),
"((table.colTime) = $1::time without time zone)", now)
assertSerialize(t, RawTimez("table.colTime").EQ(TimezT(now)),
"((table.colTime) = $1::time with time zone)", now)
assertSerialize(t, RawTimestamp("table.colTimestamp").EQ(TimestampT(now)),
"((table.colTimestamp) = $1::timestamp without time zone)", now)
assertSerialize(t, RawTimestampz("table.colTimestampz").EQ(TimestampzT(now)),
"((table.colTimestampz) = $1::timestamp with time zone)", now)
assertSerialize(t, RawDate("table.colDate").EQ(DateT(now)),
"((table.colDate) = $1::date)", now)
}

View file

@ -128,7 +128,7 @@ func INTERVAL(quantityAndUnit ...quantityAndUnit) IntervalExpression {
newInterval := &intervalExpression{} newInterval := &intervalExpression{}
newInterval.Expression = jet.Raw(intervalStr, newInterval) newInterval.Expression = jet.RawWithParent(intervalStr, newInterval)
newInterval.intervalInterfaceImpl.parent = newInterval newInterval.intervalInterfaceImpl.parent = newInterval
return newInterval return newInterval

24
postgres/lateral.go Normal file
View file

@ -0,0 +1,24 @@
package postgres
import "github.com/go-jet/jet/v2/internal/jet"
// LATERAL derived tables constructor from select statement
func LATERAL(selectStmt SelectStatement) lateralImpl {
return lateralImpl{
selectStmt: selectStmt,
}
}
type lateralImpl struct {
selectStmt SelectStatement
}
func (l lateralImpl) AS(alias string) SelectTable {
subQuery := &selectTableImpl{
SelectTable: jet.NewLateral(l.selectStmt, alias),
}
subQuery.readableTableInterfaceImpl.parent = subQuery
return subQuery
}

14
postgres/lateral_test.go Normal file
View file

@ -0,0 +1,14 @@
package postgres
import "testing"
func TestLATERAL(t *testing.T) {
assertSerialize(t,
LATERAL(
SELECT(Int(1)),
).AS("lat1"),
`LATERAL (
SELECT $1
) AS lat1`)
}

View file

@ -1,25 +1,62 @@
package postgres package postgres
import ( import (
"github.com/go-jet/jet/v2/internal/jet"
"time" "time"
"github.com/go-jet/jet/v2/internal/jet"
) )
// Bool creates new bool literal expression // Bool creates new bool literal expression
var Bool = jet.Bool var Bool = jet.Bool
// Int creates new integer literal expression // Int is constructor for 64 bit signed integer expressions literals.
var Int = jet.Int var Int = jet.Int
// Int8 is constructor for 8 bit signed integer expressions literals.
var Int8 = jet.Int8
// Int16 is constructor for 16 bit signed integer expressions literals.
var Int16 = jet.Int16
// Int32 is constructor for 32 bit signed integer expressions literals.
var Int32 = jet.Int32
// Int64 is constructor for 64 bit signed integer expressions literals.
var Int64 = jet.Int
// Uint8 is constructor for 8 bit unsigned integer expressions literals.
var Uint8 = jet.Uint8
// Uint16 is constructor for 16 bit unsigned integer expressions literals.
var Uint16 = jet.Uint16
// Uint32 is constructor for 32 bit unsigned integer expressions literals.
var Uint32 = jet.Uint32
// Uint64 is constructor for 64 bit unsigned integer expressions literals.
var Uint64 = jet.Uint64
// Float creates new float literal expression // Float creates new float literal expression
var Float = jet.Float var Float = jet.Float
// Decimal creates new float literal expression
var Decimal = jet.Decimal
// String creates new string literal expression // String creates new string literal expression
var String = jet.String var String = jet.String
// Bytea craates new bytea literal expression // UUID is a helper function to create string literal expression from uuid object
var Bytea = func(value string) StringExpression { // value can be any uuid type with a String method
return CAST(jet.String(value)).AS_BYTEA() var UUID = jet.UUID
// Bytea creates new bytea literal expression
var Bytea = func(value interface{}) StringExpression {
switch value.(type) {
case string, []byte:
default:
panic("Bytea parameter value has to be of the type string or []byte")
}
return CAST(jet.Literal(value)).AS_BYTEA()
} }
// Date creates new date literal expression // Date creates new date literal expression

View file

@ -1,6 +1,7 @@
package postgres package postgres
import ( import (
"math"
"testing" "testing"
"time" "time"
) )
@ -13,6 +14,46 @@ func TestInt(t *testing.T) {
assertSerialize(t, Int(11), `$1`, int64(11)) assertSerialize(t, Int(11), `$1`, int64(11))
} }
func TestInt8(t *testing.T) {
val := int8(math.MinInt8)
assertSerialize(t, Int8(val), `$1`, val)
}
func TestInt16(t *testing.T) {
val := int16(math.MinInt16)
assertSerialize(t, Int16(val), `$1`, val)
}
func TestInt32(t *testing.T) {
val := int32(math.MinInt32)
assertSerialize(t, Int32(val), `$1`, val)
}
func TestInt64(t *testing.T) {
val := int64(math.MinInt64)
assertSerialize(t, Int64(val), `$1`, val)
}
func TestUint8(t *testing.T) {
val := uint8(math.MaxUint8)
assertSerialize(t, Uint8(val), `$1`, val)
}
func TestUint16(t *testing.T) {
val := uint16(math.MaxUint16)
assertSerialize(t, Uint16(val), `$1`, val)
}
func TestUint32(t *testing.T) {
val := uint32(math.MaxUint32)
assertSerialize(t, Uint32(val), `$1`, val)
}
func TestUint64(t *testing.T) {
val := uint64(math.MaxUint64)
assertSerialize(t, Uint64(val), `$1`, val)
}
func TestFloat(t *testing.T) { func TestFloat(t *testing.T) {
assertSerialize(t, Float(12.34), `$1`, float64(12.34)) assertSerialize(t, Float(12.34), `$1`, float64(12.34))
} }
@ -21,6 +62,11 @@ func TestString(t *testing.T) {
assertSerialize(t, String("Some text"), `$1`, "Some text") assertSerialize(t, String("Some text"), `$1`, "Some text")
} }
func TestBytea(t *testing.T) {
assertSerialize(t, Bytea("Some text"), `$1::bytea`, "Some text")
assertSerialize(t, Bytea([]byte("Some byte array")), `$1::bytea`, []byte("Some byte array"))
}
func TestDate(t *testing.T) { func TestDate(t *testing.T) {
assertSerialize(t, Date(2014, time.January, 2), `$1::date`, "2014-01-02") assertSerialize(t, Date(2014, time.January, 2), `$1::date`, "2014-01-02")
assertSerialize(t, DateT(time.Now()), `$1::date`) assertSerialize(t, DateT(time.Now()), `$1::date`)

View file

@ -44,12 +44,12 @@ type SelectStatement interface {
Expression Expression
DISTINCT() SelectStatement DISTINCT() SelectStatement
FROM(table ReadableTable) SelectStatement FROM(tables ...ReadableTable) SelectStatement
WHERE(expression BoolExpression) SelectStatement WHERE(expression BoolExpression) SelectStatement
GROUP_BY(groupByClauses ...jet.GroupByClause) SelectStatement GROUP_BY(groupByClauses ...jet.GroupByClause) SelectStatement
HAVING(boolExpression BoolExpression) SelectStatement HAVING(boolExpression BoolExpression) SelectStatement
WINDOW(name string) windowExpand WINDOW(name string) windowExpand
ORDER_BY(orderByClauses ...jet.OrderByClause) SelectStatement ORDER_BY(orderByClauses ...OrderByClause) SelectStatement
LIMIT(limit int64) SelectStatement LIMIT(limit int64) SelectStatement
OFFSET(offset int64) SelectStatement OFFSET(offset int64) SelectStatement
FOR(lock RowLock) SelectStatement FOR(lock RowLock) SelectStatement
@ -76,7 +76,9 @@ func newSelectStatement(table ReadableTable, projections []Projection) SelectSta
&newSelect.Limit, &newSelect.Offset, &newSelect.For) &newSelect.Limit, &newSelect.Offset, &newSelect.For)
newSelect.Select.ProjectionList = projections newSelect.Select.ProjectionList = projections
newSelect.From.Table = table if table != nil {
newSelect.From.Tables = []jet.Serializer{table}
}
newSelect.Limit.Count = -1 newSelect.Limit.Count = -1
newSelect.Offset.Count = -1 newSelect.Offset.Count = -1
@ -106,8 +108,11 @@ func (s *selectStatementImpl) DISTINCT() SelectStatement {
return s return s
} }
func (s *selectStatementImpl) FROM(table ReadableTable) SelectStatement { func (s *selectStatementImpl) FROM(tables ...ReadableTable) SelectStatement {
s.From.Table = table s.From.Tables = nil
for _, table := range tables {
s.From.Tables = append(s.From.Tables, table)
}
return s return s
} }
@ -131,7 +136,7 @@ func (s *selectStatementImpl) WINDOW(name string) windowExpand {
return windowExpand{selectStatement: s} return windowExpand{selectStatement: s}
} }
func (s *selectStatementImpl) ORDER_BY(orderByClauses ...jet.OrderByClause) SelectStatement { func (s *selectStatementImpl) ORDER_BY(orderByClauses ...OrderByClause) SelectStatement {
s.OrderBy.List = orderByClauses s.OrderBy.List = orderByClauses
return s return s
} }

View file

@ -41,7 +41,7 @@ func EXCEPT_ALL(lhs, rhs jet.SerializerStatement) setStatement {
type setStatement interface { type setStatement interface {
setOperators setOperators
ORDER_BY(orderByClauses ...jet.OrderByClause) setStatement ORDER_BY(orderByClauses ...OrderByClause) setStatement
LIMIT(limit int64) setStatement LIMIT(limit int64) setStatement
OFFSET(offset int64) setStatement OFFSET(offset int64) setStatement
@ -114,7 +114,7 @@ func newSetStatementImpl(operator string, all bool, selects []jet.SerializerStat
return newSetStatement return newSetStatement
} }
func (s *setStatementImpl) ORDER_BY(orderByClauses ...jet.OrderByClause) setStatement { func (s *setStatementImpl) ORDER_BY(orderByClauses ...OrderByClause) setStatement {
s.setOperator.OrderBy.List = orderByClauses s.setOperator.OrderBy.List = orderByClauses
return s return s
} }

8
postgres/statement.go Normal file
View file

@ -0,0 +1,8 @@
package postgres
import "github.com/go-jet/jet/v2/internal/jet"
// RawStatement creates new sql statements from raw query and optional map of named arguments
func RawStatement(rawQuery string, namedArguments ...RawArgs) Statement {
return jet.RawStatement(Dialect, rawQuery, namedArguments...)
}

View file

@ -109,10 +109,10 @@ type tableImpl struct {
} }
// NewTable creates new table with schema Name, table Name and list of columns // NewTable creates new table with schema Name, table Name and list of columns
func NewTable(schemaName, name string, columns ...jet.ColumnExpression) Table { func NewTable(schemaName, name, alias string, columns ...jet.ColumnExpression) Table {
t := &tableImpl{ t := &tableImpl{
SerializerTable: jet.NewTable(schemaName, name, columns...), SerializerTable: jet.NewTable(schemaName, name, alias, columns...),
} }
t.readableTableInterfaceImpl.parent = t t.readableTableInterfaceImpl.parent = t

View file

@ -99,3 +99,26 @@ CROSS JOIN db.table2`)
CROSS JOIN db.table2 CROSS JOIN db.table2
CROSS JOIN db.table3`) CROSS JOIN db.table3`)
} }
func TestImplicitCROSS_JOIN(t *testing.T) {
assertDebugStatementSql(t,
SELECT(table1Col1, table2Col3).
FROM(table1, table2),
`
SELECT table1.col1 AS "table1.col1",
table2.col3 AS "table2.col3"
FROM db.table1,
db.table2;
`)
assertDebugStatementSql(t,
SELECT(
table1Col1, table2Col3,
).FROM(table1, table2, table3),
`
SELECT table1.col1 AS "table1.col1",
table2.col3 AS "table2.col3"
FROM db.table1,
db.table2,
db.table3;
`)
}

View file

@ -17,5 +17,8 @@ type ColumnAssigment = jet.ColumnAssigment
// PrintableStatement is a statement which sql query can be logged // PrintableStatement is a statement which sql query can be logged
type PrintableStatement = jet.PrintableStatement type PrintableStatement = jet.PrintableStatement
// OrderByClause is the combination of an expression and the wanted ordering to use as input for ORDER BY.
type OrderByClause = jet.OrderByClause
// SetLogger sets automatic statement logging // SetLogger sets automatic statement logging
var SetLogger = jet.SetLoggerFunc var SetLogger = jet.SetLoggerFunc

View file

@ -21,6 +21,7 @@ var table1ColInterval = IntervalColumn("col_interval")
var table1 = NewTable( var table1 = NewTable(
"db", "db",
"table1", "table1",
"",
table1Col1, table1Col1,
table1ColInt, table1ColInt,
table1ColFloat, table1ColFloat,
@ -46,37 +47,21 @@ var table2ColTimestampz = TimestampzColumn("col_timestampz")
var table2ColDate = DateColumn("col_date") var table2ColDate = DateColumn("col_date")
var table2ColInterval = IntervalColumn("col_interval") var table2ColInterval = IntervalColumn("col_interval")
var table2 = NewTable( var table2 = NewTable("db", "table2", "", table2Col3, table2Col4, table2ColInt, table2ColFloat, table2ColStr, table2ColBool, table2ColTime, table2ColTimez, table2ColDate, table2ColTimestamp, table2ColTimestampz, table2ColInterval)
"db",
"table2",
table2Col3,
table2Col4,
table2ColInt,
table2ColFloat,
table2ColStr,
table2ColBool,
table2ColTime,
table2ColTimez,
table2ColDate,
table2ColTimestamp,
table2ColTimestampz,
table2ColInterval,
)
var table3Col1 = IntegerColumn("col1") var table3Col1 = IntegerColumn("col1")
var table3ColInt = IntegerColumn("col_int") var table3ColInt = IntegerColumn("col_int")
var table3StrCol = StringColumn("col2") var table3StrCol = StringColumn("col2")
var table3 = NewTable( var table3 = NewTable("db", "table3", "", table3Col1, table3ColInt, table3StrCol)
"db",
"table3",
table3Col1,
table3ColInt,
table3StrCol)
func assertSerialize(t *testing.T, serializer jet.Serializer, query string, args ...interface{}) { func assertSerialize(t *testing.T, serializer jet.Serializer, query string, args ...interface{}) {
testutils.AssertSerialize(t, Dialect, serializer, query, args...) testutils.AssertSerialize(t, Dialect, serializer, query, args...)
} }
func assertDebugSerialize(t *testing.T, serializer jet.Serializer, query string, args ...interface{}) {
testutils.AssertDebugSerialize(t, Dialect, serializer, query, args...)
}
func assertClauseSerialize(t *testing.T, clause jet.Clause, query string, args ...interface{}) { func assertClauseSerialize(t *testing.T, clause jet.Clause, query string, args ...interface{}) {
testutils.AssertClauseSerialize(t, Dialect, clause, query, args...) testutils.AssertClauseSerialize(t, Dialect, clause, query, args...)
} }

View file

@ -5,7 +5,8 @@ import (
"database/sql" "database/sql"
) )
// DB is common database interface used by jet execution // DB is common database interface used by query result mapping
// Both *sql.DB and *sql.Tx implements DB interface
type DB interface { type DB interface {
Exec(query string, args ...interface{}) (sql.Result, error) Exec(query string, args ...interface{}) (sql.Result, error)
ExecContext(ctx context.Context, query string, args ...interface{}) (sql.Result, error) ExecContext(ctx context.Context, query string, args ...interface{}) (sql.Result, error)

View file

@ -2,9 +2,12 @@ package qrm
import ( import (
"context" "context"
"database/sql"
"errors" "errors"
"github.com/go-jet/jet/v2/internal/utils" "fmt"
"reflect" "reflect"
"github.com/go-jet/jet/v2/internal/utils"
) )
// ErrNoRows is returned by Query when query result set is empty // ErrNoRows is returned by Query when query result set is empty
@ -56,6 +59,52 @@ func Query(ctx context.Context, db DB, query string, args []interface{}, destPtr
} }
} }
// ScanOneRowToDest will scan one row into struct destination
func ScanOneRowToDest(rows *sql.Rows, destPtr interface{}) error {
utils.MustBeInitializedPtr(destPtr, "jet: destination is nil")
utils.MustBe(destPtr, reflect.Ptr, "jet: destination has to be a pointer to slice or pointer to struct")
scanContext, err := newScanContext(rows)
if err != nil {
return fmt.Errorf("failed to create scan context, %w", err)
}
if len(scanContext.row) == 0 {
return errors.New("empty row slice")
}
err = rows.Scan(scanContext.row...)
if err != nil {
return fmt.Errorf("rows scan error, %w", err)
}
destinationPtrType := reflect.TypeOf(destPtr)
tempSlicePtrValue := reflect.New(reflect.SliceOf(destinationPtrType))
tempSliceValue := tempSlicePtrValue.Elem()
_, err = mapRowToSlice(scanContext, "", tempSlicePtrValue, nil)
if err != nil {
return fmt.Errorf("failed to map a row, %w", err)
}
// edge case when row result set contains only NULLs.
if tempSliceValue.Len() == 0 {
return nil
}
destValue := reflect.ValueOf(destPtr).Elem()
firstTempSliceValue := tempSliceValue.Index(0).Elem()
if destValue.Type().AssignableTo(firstTempSliceValue.Type()) {
destValue.Set(tempSliceValue.Index(0).Elem())
}
return nil
}
func queryToSlice(ctx context.Context, db DB, query string, args []interface{}, slicePtr interface{}) (rowsProcessed int64, err error) { func queryToSlice(ctx context.Context, db DB, query string, args []interface{}, slicePtr interface{}) (rowsProcessed int64, err error) {
if ctx == nil { if ctx == nil {
ctx = context.Background() ctx = context.Background()

View file

@ -7,6 +7,7 @@ import (
"github.com/go-jet/jet/v2/qrm/internal" "github.com/go-jet/jet/v2/qrm/internal"
"github.com/google/uuid" "github.com/google/uuid"
"reflect" "reflect"
"strconv"
"strings" "strings"
"time" "time"
) )
@ -182,12 +183,16 @@ func isIntegerType(value reflect.Type) bool {
return false return false
} }
func tryAssign(source, destination reflect.Value) bool { func isNumber(valueType reflect.Type) bool {
if source.Type().ConvertibleTo(destination.Type()) { return isIntegerType(valueType) || valueType == float64Type || valueType == float32Type
source = source.Convert(destination.Type()) }
}
if isIntegerType(source.Type()) && destination.Type() == boolType { func tryAssign(source, destination reflect.Value) bool {
switch {
case source.Type().ConvertibleTo(destination.Type()):
source = source.Convert(destination.Type())
case isIntegerType(source.Type()) && destination.Type() == boolType:
intValue := source.Int() intValue := source.Int()
if intValue == 1 { if intValue == 1 {
@ -195,6 +200,18 @@ func tryAssign(source, destination reflect.Value) bool {
} else if intValue == 0 { } else if intValue == 0 {
source = reflect.ValueOf(false) source = reflect.ValueOf(false)
} }
case source.Type() == stringType && isNumber(destination.Type()):
// if source is string and destination is a number(int8, int32, float32, ...), we first parse string to float64 number
// and then parsed number is converted into destination type
f, err := strconv.ParseFloat(source.String(), 64)
if err != nil {
return false
}
source = reflect.ValueOf(f)
if source.Type().ConvertibleTo(destination.Type()) {
source = source.Convert(destination.Type())
}
} }
if source.Type().AssignableTo(destination.Type()) { if source.Type().AssignableTo(destination.Type()) {
@ -281,6 +298,9 @@ var int32Type = reflect.TypeOf(int32(1))
var uint32Type = reflect.TypeOf(uint32(1)) var uint32Type = reflect.TypeOf(uint32(1))
var int64Type = reflect.TypeOf(int64(1)) var int64Type = reflect.TypeOf(int64(1))
var uint64Type = reflect.TypeOf(uint64(1)) var uint64Type = reflect.TypeOf(uint64(1))
var float32Type = reflect.TypeOf(float32(1))
var float64Type = reflect.TypeOf(float64(1))
var stringType = reflect.TypeOf("")
var nullBoolType = reflect.TypeOf(sql.NullBool{}) var nullBoolType = reflect.TypeOf(sql.NullBool{})
var nullInt8Type = reflect.TypeOf(internal.NullInt8{}) var nullInt8Type = reflect.TypeOf(internal.NullInt8{})
@ -308,7 +328,7 @@ func newScanType(columnType *sql.ColumnType) reflect.Type {
return nullStringType return nullStringType
case "FLOAT4": case "FLOAT4":
return nullFloat32Type return nullFloat32Type
case "FLOAT8", "NUMERIC", "DECIMAL", "FLOAT", "DOUBLE": case "FLOAT8", "FLOAT", "DOUBLE":
return nullFloat64Type return nullFloat64Type
case "BOOL": case "BOOL":
return nullBoolType return nullBoolType

View file

@ -35,3 +35,48 @@ func TestIsSimpleModelType(t *testing.T) {
require.Equal(t, isSimpleModelType(reflect.TypeOf([]string{"str"})), false) require.Equal(t, isSimpleModelType(reflect.TypeOf([]string{"str"})), false)
require.Equal(t, isSimpleModelType(reflect.TypeOf([]int{1, 2})), false) require.Equal(t, isSimpleModelType(reflect.TypeOf([]int{1, 2})), false)
} }
func TestTryAssign(t *testing.T) {
convertible := int16(16)
intBool1 := int32(1)
intBool0 := int32(0)
intBool2 := int32(2)
floatStr := "1.11"
floatErr := "1.abcd2"
str := "some string"
destination := struct {
Convertible int64
IntBool1 bool
IntBool0 bool
IntBool2 bool
FloatStr float64
FloatErr float64
Str string
}{}
testValue := reflect.ValueOf(&destination).Elem()
// convertible
require.True(t, tryAssign(reflect.ValueOf(convertible), testValue.FieldByName("Convertible")))
require.Equal(t, int64(16), destination.Convertible)
// 1/0 to bool
require.True(t, tryAssign(reflect.ValueOf(intBool1), testValue.FieldByName("IntBool1")))
require.Equal(t, true, destination.IntBool1)
require.True(t, tryAssign(reflect.ValueOf(intBool0), testValue.FieldByName("IntBool0")))
require.Equal(t, false, destination.IntBool0)
require.False(t, tryAssign(reflect.ValueOf(intBool2), testValue.FieldByName("IntBool2")))
require.Equal(t, false, destination.IntBool2)
// string to float
require.True(t, tryAssign(reflect.ValueOf(floatStr), testValue.FieldByName("FloatStr")))
require.Equal(t, 1.11, destination.FloatStr)
require.False(t, tryAssign(reflect.ValueOf(floatErr), testValue.FieldByName("FloatErr")))
require.Equal(t, 0.00, destination.FloatErr)
// string to string
require.True(t, tryAssign(reflect.ValueOf(str), testValue.FieldByName("Str")))
require.Equal(t, str, destination.Str)
}

View file

@ -46,6 +46,7 @@ func initMySQLDB() {
mySQLDBs := []string{ mySQLDBs := []string{
"dvds", "dvds",
"dvds2",
"test_sample", "test_sample",
} }
@ -89,6 +90,7 @@ func initPostgresDB() {
"dvds", "dvds",
"test_sample", "test_sample",
"chinook", "chinook",
"chinook2",
"northwind", "northwind",
} }

View file

@ -1,7 +1,7 @@
package mysql package mysql
import ( import (
"fmt" "github.com/shopspring/decimal"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"strings" "strings"
"testing" "testing"
@ -78,7 +78,7 @@ func TestUUID(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
require.True(t, dest.StrUUID != nil) require.True(t, dest.StrUUID != nil)
require.True(t, dest.UUID.String() != uuid.UUID{}.String()) require.True(t, dest.UUID.String() != uuid.UUID{}.String())
require.True(t, dest.StrUUID.String() != uuid.UUID{}.String()) require.Equal(t, dest.StrUUID.String(), "dc8daae3-b83b-11e9-8eb4-98ded00c39c6")
require.Equal(t, dest.StrUUID.String(), dest.BinUUID.String()) require.Equal(t, dest.StrUUID.String(), dest.BinUUID.String())
requireLogged(t, query) requireLogged(t, query)
} }
@ -89,14 +89,17 @@ func TestExpressionOperators(t *testing.T) {
AllTypes.DatePtr.IS_NOT_NULL().AS("result.is_not_null"), AllTypes.DatePtr.IS_NOT_NULL().AS("result.is_not_null"),
AllTypes.SmallIntPtr.IN(Int(11), Int(22)).AS("result.in"), AllTypes.SmallIntPtr.IN(Int(11), Int(22)).AS("result.in"),
AllTypes.SmallIntPtr.IN(AllTypes.SELECT(AllTypes.Integer)).AS("result.in_select"), AllTypes.SmallIntPtr.IN(AllTypes.SELECT(AllTypes.Integer)).AS("result.in_select"),
Raw("CURRENT_USER()").AS("result.raw"),
Raw(":first + COALESCE(all_types.small_int_ptr, 0) + :second", RawArgs{":first": 78, ":second": 56}).
AS("result.raw_arg"),
Raw("#1 + all_types.integer + #2 + #1 + #3 + #4", RawArgs{"#1": 11, "#2": 22, "#3": 33, "#4": 44}).
AS("result.raw_arg2"),
AllTypes.SmallIntPtr.NOT_IN(Int(11), Int(22), NULL).AS("result.not_in"), AllTypes.SmallIntPtr.NOT_IN(Int(11), Int(22), NULL).AS("result.not_in"),
AllTypes.SmallIntPtr.NOT_IN(AllTypes.SELECT(AllTypes.Integer)).AS("result.not_in_select"), AllTypes.SmallIntPtr.NOT_IN(AllTypes.SELECT(AllTypes.Integer)).AS("result.not_in_select"),
Raw("DATABASE()"),
).LIMIT(2) ).LIMIT(2)
//fmt.Println(query.Sql())
testutils.AssertStatementSql(t, query, strings.Replace(` testutils.AssertStatementSql(t, query, strings.Replace(`
SELECT all_types.'integer' IS NULL AS "result.is_null", SELECT all_types.'integer' IS NULL AS "result.is_null",
all_types.date_ptr IS NOT NULL AS "result.is_not_null", all_types.date_ptr IS NOT NULL AS "result.is_not_null",
@ -105,46 +108,34 @@ SELECT all_types.'integer' IS NULL AS "result.is_null",
SELECT all_types.'integer' AS "all_types.integer" SELECT all_types.'integer' AS "all_types.integer"
FROM test_sample.all_types FROM test_sample.all_types
))) AS "result.in_select", ))) AS "result.in_select",
(CURRENT_USER()) AS "result.raw",
(? + COALESCE(all_types.small_int_ptr, 0) + ?) AS "result.raw_arg",
(? + all_types.integer + ? + ? + ? + ?) AS "result.raw_arg2",
(all_types.small_int_ptr NOT IN (?, ?, NULL)) AS "result.not_in", (all_types.small_int_ptr NOT IN (?, ?, NULL)) AS "result.not_in",
(all_types.small_int_ptr NOT IN (( (all_types.small_int_ptr NOT IN ((
SELECT all_types.'integer' AS "all_types.integer" SELECT all_types.'integer' AS "all_types.integer"
FROM test_sample.all_types FROM test_sample.all_types
))) AS "result.not_in_select", ))) AS "result.not_in_select"
DATABASE()
FROM test_sample.all_types FROM test_sample.all_types
LIMIT ?; LIMIT ?;
`, "'", "`", -1), int64(11), int64(22), int64(11), int64(22), int64(2)) `, "'", "`", -1), int64(11), int64(22), 78, 56, 11, 22, 11, 33, 44, int64(11), int64(22), int64(2))
var dest []struct { var dest []struct {
common.ExpressionTestResult `alias:"result.*"` common.ExpressionTestResult `alias:"result.*"`
} }
err := query.Query(db, &dest) err := query.Query(db, &dest)
require.NoError(t, err) require.NoError(t, err)
//testutils.PrintJson(dest) require.Equal(t, *dest[0].IsNull, false)
require.Equal(t, *dest[0].IsNotNull, true)
testutils.AssertJSON(t, dest, ` require.Equal(t, *dest[0].In, false)
[ require.Equal(t, *dest[0].InSelect, false)
{ require.True(t, strings.Contains(*dest[0].Raw, "jet"))
"IsNull": false, require.Equal(t, *dest[0].RawArg, int32(148))
"IsNotNull": true, require.Equal(t, *dest[0].RawArg2, int32(-1479))
"In": false, require.Nil(t, dest[0].NotIn)
"InSelect": false, require.Equal(t, *dest[0].NotInSelect, true)
"NotIn": null,
"NotInSelect": true
},
{
"IsNull": false,
"IsNotNull": false,
"In": null,
"InSelect": null,
"NotIn": null,
"NotInSelect": null
}
]
`)
} }
func TestBoolOperators(t *testing.T) { func TestBoolOperators(t *testing.T) {
@ -974,7 +965,7 @@ func TestAllTypesInsert(t *testing.T) {
stmt := AllTypes.INSERT(AllTypes.AllColumns). stmt := AllTypes.INSERT(AllTypes.AllColumns).
MODEL(toInsert) MODEL(toInsert)
fmt.Println(stmt.DebugSql()) //fmt.Println(stmt.DebugSql())
testutils.AssertExec(t, stmt, tx, 1) testutils.AssertExec(t, stmt, tx, 1)
@ -1028,7 +1019,7 @@ func TestAllTypesInsertOnDuplicateKeyUpdate(t *testing.T) {
AllTypes.Date.SET(DateT(time.Now())), AllTypes.Date.SET(DateT(time.Now())),
) )
fmt.Println(stmt.DebugSql()) //fmt.Println(stmt.DebugSql())
_, err = stmt.Exec(tx) _, err = stmt.Exec(tx)
require.NoError(t, err) require.NoError(t, err)
@ -1257,7 +1248,7 @@ FROM test_sample.user;
err := stmt.Query(db, &dest) err := stmt.Query(db, &dest)
require.NoError(t, err) require.NoError(t, err)
testutils.PrintJson(dest) //testutils.PrintJson(dest)
testutils.AssertJSON(t, dest, ` testutils.AssertJSON(t, dest, `
[ [
@ -1279,3 +1270,99 @@ FROM test_sample.user;
] ]
`) `)
} }
func TestExactDecimals(t *testing.T) {
type floats struct {
model.Floats
Numeric decimal.Decimal
NumericPtr decimal.Decimal
Decimal decimal.Decimal
DecimalPtr decimal.Decimal
}
t.Run("should query decimal", func(t *testing.T) {
query := SELECT(
Floats.AllColumns,
).FROM(
Floats,
).WHERE(Floats.Decimal.EQ(Decimal("1.11111111111111111111")))
var result floats
err := query.Query(db, &result)
require.NoError(t, err)
require.Equal(t, "1.11111111111111111111", result.Decimal.String())
require.Equal(t, "0", result.DecimalPtr.String()) // NULL
require.Equal(t, "2.22222222222222222222", result.Numeric.String())
require.Equal(t, "0", result.NumericPtr.String()) // NULL
require.Equal(t, 1.1111111111111112, result.Floats.Decimal) // precision loss
require.Equal(t, (*float64)(nil), result.Floats.DecimalPtr)
require.Equal(t, 2.2222222222222223, result.Floats.Numeric) // precision loss
require.Equal(t, (*float64)(nil), result.Floats.NumericPtr)
// floating point
require.Equal(t, 3.3333333, result.Floats.Float) // precision loss
require.Equal(t, (*float64)(nil), result.Floats.FloatPtr)
require.Equal(t, 4.444444444444445, result.Floats.Double) // precision loss
require.Equal(t, (*float64)(nil), result.Floats.DoublePtr)
require.Equal(t, 5.555555555555555, result.Floats.Real) // precision loss
require.Equal(t, (*float64)(nil), result.Floats.RealPtr)
})
t.Run("should insert decimal", func(t *testing.T) {
insertQuery := Floats.INSERT(
Floats.AllColumns,
).MODEL(
floats{
Floats: model.Floats{
// overwritten by wrapped(floats) scope
Numeric: 0.1,
NumericPtr: testutils.Float64Ptr(0.1),
Decimal: 0.1,
DecimalPtr: testutils.Float64Ptr(0.1),
// not overwritten
Float: 0.2,
FloatPtr: testutils.Float64Ptr(0.22),
Double: 0.3,
DoublePtr: testutils.Float64Ptr(0.33),
Real: 0.4,
RealPtr: testutils.Float64Ptr(0.44),
},
Numeric: decimal.RequireFromString("12.35"),
NumericPtr: decimal.RequireFromString("56.79"),
Decimal: decimal.RequireFromString("91.23"),
DecimalPtr: decimal.RequireFromString("45.67"),
},
)
testutils.AssertDebugStatementSql(t, insertQuery, strings.Replace(`
INSERT INTO test_sample.floats (''decimal'', decimal_ptr, ''numeric'', numeric_ptr, ''float'', float_ptr, ''double'', double_ptr, ''real'', real_ptr)
VALUES ('91.23', '45.67', '12.35', '56.79', 0.2, 0.22, 0.3, 0.33, 0.4, 0.44);
`, "''", "`", -1))
_, err := insertQuery.Exec(db)
require.NoError(t, err)
var result floats
err = SELECT(Floats.AllColumns).
FROM(Floats).
WHERE(Floats.Numeric.EQ(Float(12.35))).
Query(db, &result)
require.NoError(t, err)
require.Equal(t, "12.35", result.Numeric.String())
require.Equal(t, "56.79", result.NumericPtr.String())
require.Equal(t, "91.23", result.Decimal.String())
require.Equal(t, "45.67", result.DecimalPtr.String())
require.Equal(t, 12.35, result.Floats.Numeric)
require.Equal(t, 56.79, *result.Floats.NumericPtr)
require.Equal(t, 91.23, result.Floats.Decimal)
require.Equal(t, 45.67, *result.Floats.DecimalPtr)
})
}

View file

@ -35,12 +35,7 @@ func TestGenerator(t *testing.T) {
} }
func TestCmdGenerator(t *testing.T) { func TestCmdGenerator(t *testing.T) {
goInstallJet := exec.Command("sh", "-c", "cd $GOPATH/src/ && GO111MODULE=off go get github.com/go-jet/jet/cmd/jet") err := os.RemoveAll(genTestDir3)
goInstallJet.Stderr = os.Stderr
err := goInstallJet.Run()
require.NoError(t, err)
err = os.RemoveAll(genTestDir3)
require.NoError(t, err) require.NoError(t, err)
cmd := exec.Command("jet", "-source=MySQL", "-dbname=dvds", "-host=localhost", "-port=3306", cmd := exec.Command("jet", "-source=MySQL", "-dbname=dvds", "-host=localhost", "-port=3306",
@ -140,7 +135,7 @@ import (
"github.com/go-jet/jet/v2/mysql" "github.com/go-jet/jet/v2/mysql"
) )
var Actor = newActorTable() var Actor = newActorTable("dvds", "actor", "")
type ActorTable struct { type ActorTable struct {
mysql.Table mysql.Table
@ -156,13 +151,16 @@ type ActorTable struct {
} }
// AS creates new ActorTable with assigned alias // AS creates new ActorTable with assigned alias
func (a *ActorTable) AS(alias string) ActorTable { func (a ActorTable) AS(alias string) ActorTable {
aliasTable := newActorTable() return newActorTable(a.SchemaName(), a.TableName(), alias)
aliasTable.Table.AS(alias)
return aliasTable
} }
func newActorTable() ActorTable { // Schema creates new ActorTable with assigned schema name
func (a ActorTable) FromSchema(schemaName string) ActorTable {
return newActorTable(schemaName, a.TableName(), a.Alias())
}
func newActorTable(schemaName, tableName, alias string) ActorTable {
var ( var (
ActorIDColumn = mysql.IntegerColumn("actor_id") ActorIDColumn = mysql.IntegerColumn("actor_id")
FirstNameColumn = mysql.StringColumn("first_name") FirstNameColumn = mysql.StringColumn("first_name")
@ -173,7 +171,7 @@ func newActorTable() ActorTable {
) )
return ActorTable{ return ActorTable{
Table: mysql.NewTable("dvds", "actor", allColumns...), Table: mysql.NewTable(schemaName, tableName, alias, allColumns...),
//Columns //Columns
ActorID: ActorIDColumn, ActorID: ActorIDColumn,
@ -223,7 +221,7 @@ import (
"github.com/go-jet/jet/v2/mysql" "github.com/go-jet/jet/v2/mysql"
) )
var ActorInfo = newActorInfoTable() var ActorInfo = newActorInfoTable("dvds", "actor_info", "")
type ActorInfoTable struct { type ActorInfoTable struct {
mysql.Table mysql.Table
@ -239,13 +237,16 @@ type ActorInfoTable struct {
} }
// AS creates new ActorInfoTable with assigned alias // AS creates new ActorInfoTable with assigned alias
func (a *ActorInfoTable) AS(alias string) ActorInfoTable { func (a ActorInfoTable) AS(alias string) ActorInfoTable {
aliasTable := newActorInfoTable() return newActorInfoTable(a.SchemaName(), a.TableName(), alias)
aliasTable.Table.AS(alias)
return aliasTable
} }
func newActorInfoTable() ActorInfoTable { // Schema creates new ActorInfoTable with assigned schema name
func (a ActorInfoTable) FromSchema(schemaName string) ActorInfoTable {
return newActorInfoTable(schemaName, a.TableName(), a.Alias())
}
func newActorInfoTable(schemaName, tableName, alias string) ActorInfoTable {
var ( var (
ActorIDColumn = mysql.IntegerColumn("actor_id") ActorIDColumn = mysql.IntegerColumn("actor_id")
FirstNameColumn = mysql.StringColumn("first_name") FirstNameColumn = mysql.StringColumn("first_name")
@ -256,7 +257,7 @@ func newActorInfoTable() ActorInfoTable {
) )
return ActorInfoTable{ return ActorInfoTable{
Table: mysql.NewTable("dvds", "actor_info", allColumns...), Table: mysql.NewTable(schemaName, tableName, alias, allColumns...),
//Columns //Columns
ActorID: ActorIDColumn, ActorID: ActorIDColumn,

View file

@ -3,7 +3,6 @@ package mysql
import ( import (
"context" "context"
"database/sql" "database/sql"
"flag"
jetmysql "github.com/go-jet/jet/v2/mysql" jetmysql "github.com/go-jet/jet/v2/mysql"
"github.com/go-jet/jet/v2/postgres" "github.com/go-jet/jet/v2/postgres"
"github.com/go-jet/jet/v2/tests/dbconfig" "github.com/go-jet/jet/v2/tests/dbconfig"
@ -25,8 +24,7 @@ var source string
const MariaDB = "MariaDB" const MariaDB = "MariaDB"
func init() { func init() {
flag.StringVar(&source, "source", "", "MySQL or MariaDB") source = os.Getenv("MY_SQL_SOURCE")
flag.Parse()
} }
func sourceIsMariaDB() bool { func sourceIsMariaDB() bool {
@ -66,3 +64,9 @@ func requireLogged(t *testing.T, statement postgres.Statement) {
require.Equal(t, loggedSQLArgs, args) require.Equal(t, loggedSQLArgs, args)
require.Equal(t, loggedDebugSQL, statement.DebugSql()) require.Equal(t, loggedDebugSQL, statement.DebugSql())
} }
func skipForMariaDB(t *testing.T) {
if sourceIsMariaDB() {
t.SkipNow()
}
}

View file

@ -0,0 +1,123 @@
package mysql
import (
"context"
"testing"
"time"
"github.com/stretchr/testify/require"
"github.com/go-jet/jet/v2/internal/testutils"
"github.com/go-jet/jet/v2/tests/.gentestdata/mysql/dvds/model"
. "github.com/go-jet/jet/v2/mysql"
)
func TestRawStatementSelect(t *testing.T) {
stmt := RawStatement(`
SELECT actor.first_name AS "actor.first_name"
FROM dvds.actor
WHERE actor.actor_id = 2`)
testutils.AssertStatementSql(t, stmt, `
SELECT actor.first_name AS "actor.first_name"
FROM dvds.actor
WHERE actor.actor_id = 2;
`)
testutils.AssertDebugStatementSql(t, stmt, `
SELECT actor.first_name AS "actor.first_name"
FROM dvds.actor
WHERE actor.actor_id = 2;
`)
var actor model.Actor
err := stmt.Query(db, &actor)
require.NoError(t, err)
require.Equal(t, actor.FirstName, "NICK")
}
func TestRawStatementSelectWithArguments(t *testing.T) {
stmt := RawStatement(`
SELECT DISTINCT actor.actor_id AS "actor.actor_id",
actor.first_name AS "actor.first_name",
actor.last_name AS "actor.last_name",
actor.last_update AS "actor.last_update"
FROM dvds.actor
WHERE actor.actor_id IN (#actorID1, #actorID2, #actorID3) AND ((#actorID1 / #actorID2) <> (#actorID2 * #actorID3))
ORDER BY actor.actor_id`,
RawArgs{
"#actorID1": int64(1),
"#actorID2": int64(2),
"#actorID3": int64(3),
},
)
testutils.AssertStatementSql(t, stmt, `
SELECT DISTINCT actor.actor_id AS "actor.actor_id",
actor.first_name AS "actor.first_name",
actor.last_name AS "actor.last_name",
actor.last_update AS "actor.last_update"
FROM dvds.actor
WHERE actor.actor_id IN (?, ?, ?) AND ((? / ?) <> (? * ?))
ORDER BY actor.actor_id;
`, int64(1), int64(2), int64(3), int64(1), int64(2), int64(2), int64(3))
testutils.AssertDebugStatementSql(t, stmt, `
SELECT DISTINCT actor.actor_id AS "actor.actor_id",
actor.first_name AS "actor.first_name",
actor.last_name AS "actor.last_name",
actor.last_update AS "actor.last_update"
FROM dvds.actor
WHERE actor.actor_id IN (1, 2, 3) AND ((1 / 2) <> (2 * 3))
ORDER BY actor.actor_id;
`)
var actor []model.Actor
err := stmt.Query(db, &actor)
require.NoError(t, err)
testutils.AssertDeepEqual(t, actor[1], model.Actor{
ActorID: 2,
FirstName: "NICK",
LastName: "WAHLBERG",
LastUpdate: *testutils.TimestampWithoutTimeZone("2006-02-15 04:34:33", 2),
})
}
func TestRawStatementRows(t *testing.T) {
stmt := RawStatement(`
SELECT actor.actor_id AS "actor.actor_id",
actor.first_name AS "actor.first_name",
actor.last_name AS "actor.last_name",
actor.last_update AS "actor.last_update"
FROM dvds.actor
ORDER BY actor.actor_id`)
rows, err := stmt.Rows(context.Background(), db)
require.NoError(t, err)
for rows.Next() {
var actor model.Actor
err := rows.Scan(&actor)
require.NoError(t, err)
require.NotEqual(t, actor.ActorID, int16(0))
require.NotEqual(t, actor.FirstName, "")
require.NotEqual(t, actor.LastName, "")
require.NotEqual(t, actor.LastUpdate, time.Time{})
if actor.ActorID == 54 {
require.Equal(t, actor.ActorID, uint16(54))
require.Equal(t, actor.FirstName, "PENELOPE")
require.Equal(t, actor.LastName, "PINKETT")
require.Equal(t, actor.LastUpdate.Format(time.RFC3339), "2006-02-15T04:34:33Z")
}
}
err = rows.Close()
require.NoError(t, err)
err = rows.Err()
require.NoError(t, err)
requireLogged(t, stmt)
}

View file

@ -1,15 +1,19 @@
package mysql package mysql
import ( import (
"context"
"strings"
"testing"
"time"
"github.com/go-jet/jet/v2/internal/testutils" "github.com/go-jet/jet/v2/internal/testutils"
. "github.com/go-jet/jet/v2/mysql" . "github.com/go-jet/jet/v2/mysql"
"github.com/go-jet/jet/v2/tests/.gentestdata/mysql/dvds/enum" "github.com/go-jet/jet/v2/tests/.gentestdata/mysql/dvds/enum"
"github.com/go-jet/jet/v2/tests/.gentestdata/mysql/dvds/model" "github.com/go-jet/jet/v2/tests/.gentestdata/mysql/dvds/model"
. "github.com/go-jet/jet/v2/tests/.gentestdata/mysql/dvds/table" . "github.com/go-jet/jet/v2/tests/.gentestdata/mysql/dvds/table"
"github.com/go-jet/jet/v2/tests/.gentestdata/mysql/dvds/view" "github.com/go-jet/jet/v2/tests/.gentestdata/mysql/dvds/view"
"github.com/stretchr/testify/require"
"testing" "github.com/stretchr/testify/require"
) )
func TestSelect_ScanToStruct(t *testing.T) { func TestSelect_ScanToStruct(t *testing.T) {
@ -744,3 +748,227 @@ LIMIT 3;
require.Equal(t, len(dest), 3) require.Equal(t, len(dest), 3)
} }
func Test_SchemaRename(t *testing.T) {
Film := Film.FromSchema("dvds2")
Language := Language.FromSchema("dvds2")
stmt := SELECT(
Film.FilmID,
Film.Title,
Language.LanguageID,
Language.Name,
).FROM(
Language.
INNER_JOIN(Film, Film.LanguageID.EQ(Language.LanguageID)),
).WHERE(
Language.LanguageID.EQ(Int(1)),
).ORDER_BY(
Language.LanguageID, Film.FilmID,
).LIMIT(5)
testutils.AssertDebugStatementSql(t, stmt, `
SELECT film.film_id AS "film.film_id",
film.title AS "film.title",
language.language_id AS "language.language_id",
language.name AS "language.name"
FROM dvds2.language
INNER JOIN dvds2.film ON (film.language_id = language.language_id)
WHERE language.language_id = 1
ORDER BY language.language_id, film.film_id
LIMIT 5;
`)
dest := struct {
model.Language
Films []model.Film
}{}
err := stmt.Query(db, &dest)
require.NoError(t, err)
require.Len(t, dest.Films, 5)
require.Equal(t, dest.Films[0].Title, "ACADEMY DINOSAUR")
require.Equal(t, dest.Films[1].Title, "ACE GOLDFINGER")
require.Equal(t, dest.Films[4].Title, "AFRICAN EGG")
}
func TestLateral(t *testing.T) {
skipForMariaDB(t) // MariaDB does not implement LATERAL
languages := LATERAL(
SELECT(
Language.AllColumns,
).FROM(
Language,
).WHERE(
Language.Name.NOT_IN(String("spanish")).
AND(Film.LanguageID.EQ(Language.LanguageID)),
),
).AS("films")
stmt := SELECT(
Film.FilmID,
Film.Title,
languages.AllColumns(),
).FROM(
Film.CROSS_JOIN(languages),
).WHERE(
Film.FilmID.EQ(Int(1)),
).ORDER_BY(
Film.FilmID,
).LIMIT(1)
testutils.AssertDebugStatementSql(t, stmt, strings.Replace(`
SELECT film.film_id AS "film.film_id",
film.title AS "film.title",
films.''language.language_id'' AS "language.language_id",
films.''language.name'' AS "language.name",
films.''language.last_update'' AS "language.last_update"
FROM dvds.film
CROSS JOIN LATERAL (
SELECT language.language_id AS "language.language_id",
language.name AS "language.name",
language.last_update AS "language.last_update"
FROM dvds.language
WHERE (language.name NOT IN ('spanish')) AND (film.language_id = language.language_id)
) AS films
WHERE film.film_id = 1
ORDER BY film.film_id
LIMIT 1;
`, "''", "`", -1))
type FilmLanguage struct {
model.Film
model.Language
}
var dest []FilmLanguage
err := stmt.Query(db, &dest)
require.NoError(t, err)
require.Equal(t, dest[0].Film.Title, "ACADEMY DINOSAUR")
require.Equal(t, dest[0].Language.Name, "English")
t.Run("implicit cross join", func(t *testing.T) {
stmt2 := SELECT(
Film.FilmID,
Film.Title,
languages.AllColumns(),
).FROM(
Film,
languages,
).WHERE(
Film.FilmID.EQ(Int(1)),
).ORDER_BY(
Film.FilmID,
).LIMIT(1)
testutils.AssertDebugStatementSql(t, stmt2, strings.Replace(`
SELECT film.film_id AS "film.film_id",
film.title AS "film.title",
films.''language.language_id'' AS "language.language_id",
films.''language.name'' AS "language.name",
films.''language.last_update'' AS "language.last_update"
FROM dvds.film,
LATERAL (
SELECT language.language_id AS "language.language_id",
language.name AS "language.name",
language.last_update AS "language.last_update"
FROM dvds.language
WHERE (language.name NOT IN ('spanish')) AND (film.language_id = language.language_id)
) AS films
WHERE film.film_id = 1
ORDER BY film.film_id
LIMIT 1;
`, "''", "`", -1))
var dest2 []FilmLanguage
err2 := stmt2.Query(db, &dest2)
require.NoError(t, err2)
require.Equal(t, dest, dest2)
})
}
func TestRowsScan(t *testing.T) {
stmt := SELECT(
Inventory.AllColumns,
).FROM(
Inventory,
).ORDER_BY(
Inventory.InventoryID.ASC(),
)
rows, err := stmt.Rows(context.Background(), db)
require.NoError(t, err)
for rows.Next() {
var inventory model.Inventory
err = rows.Scan(&inventory)
require.NoError(t, err)
require.NotEqual(t, inventory.InventoryID, uint32(0))
require.NotEqual(t, inventory.FilmID, uint16(0))
require.NotEqual(t, inventory.StoreID, uint16(0))
require.NotEqual(t, inventory.LastUpdate, time.Time{})
if inventory.InventoryID == 2103 {
require.Equal(t, inventory.FilmID, uint16(456))
require.Equal(t, inventory.StoreID, uint8(2))
require.Equal(t, inventory.LastUpdate.Format(time.RFC3339), "2006-02-15T05:09:17Z")
}
}
err = rows.Close()
require.NoError(t, err)
err = rows.Err()
require.NoError(t, err)
requireLogged(t, stmt)
}
func TestScanNumericToNumber(t *testing.T) {
type Number struct {
Int8 int8
UInt8 uint8
Int16 int16
UInt16 uint16
Int32 int32
UInt32 uint32
Int64 int64
UInt64 uint64
Float32 float32
Float64 float64
}
numeric := CAST(Decimal("1234567890.111")).AS_DECIMAL()
stmt := SELECT(
numeric.AS("number.int8"),
numeric.AS("number.uint8"),
numeric.AS("number.int16"),
numeric.AS("number.uint16"),
numeric.AS("number.int32"),
numeric.AS("number.uint32"),
numeric.AS("number.int64"),
numeric.AS("number.uint64"),
numeric.AS("number.float32"),
numeric.AS("number.float64"),
)
var number Number
err := stmt.Query(db, &number)
require.NoError(t, err)
require.Equal(t, number.Int8, int8(-46)) // overflow
require.Equal(t, number.UInt8, uint8(210)) // overflow
require.Equal(t, number.Int16, int16(722)) // overflow
require.Equal(t, number.UInt16, uint16(722)) // overflow
require.Equal(t, number.Int32, int32(1234567890))
require.Equal(t, number.UInt32, uint32(1234567890))
require.Equal(t, number.Int64, int64(1234567890))
require.Equal(t, number.UInt64, uint64(1234567890))
require.Equal(t, number.Float32, float32(1.234568e+09))
require.Equal(t, number.Float64, float64(1.23456789e+09))
}

View file

@ -2,7 +2,6 @@ package mysql
import ( import (
"context" "context"
"fmt"
"github.com/go-jet/jet/v2/internal/testutils" "github.com/go-jet/jet/v2/internal/testutils"
. "github.com/go-jet/jet/v2/mysql" . "github.com/go-jet/jet/v2/mysql"
"github.com/go-jet/jet/v2/tests/.gentestdata/mysql/dvds/table" "github.com/go-jet/jet/v2/tests/.gentestdata/mysql/dvds/table"
@ -193,7 +192,7 @@ SET url = 'http://www.duckduckgo.com',
description = NULL description = NULL
WHERE link.id = 201; WHERE link.id = 201;
` `
fmt.Println(stmt.DebugSql()) //fmt.Println(stmt.DebugSql())
testutils.AssertDebugStatementSql(t, stmt, expectedSQL, "http://www.duckduckgo.com", "DuckDuckGo", nil, int64(201)) testutils.AssertDebugStatementSql(t, stmt, expectedSQL, "http://www.duckduckgo.com", "DuckDuckGo", nil, int64(201))
testutils.AssertExec(t, stmt, db) testutils.AssertExec(t, stmt, db)

View file

@ -1,20 +1,24 @@
package postgres package postgres
import ( import (
"github.com/stretchr/testify/require"
"testing" "testing"
"time" "time"
"github.com/stretchr/testify/require"
"github.com/google/uuid"
"github.com/go-jet/jet/v2/internal/testutils" "github.com/go-jet/jet/v2/internal/testutils"
. "github.com/go-jet/jet/v2/postgres" . "github.com/go-jet/jet/v2/postgres"
"github.com/go-jet/jet/v2/tests/.gentestdata/jetdb/test_sample/model" "github.com/go-jet/jet/v2/tests/.gentestdata/jetdb/test_sample/model"
. "github.com/go-jet/jet/v2/tests/.gentestdata/jetdb/test_sample/table" . "github.com/go-jet/jet/v2/tests/.gentestdata/jetdb/test_sample/table"
"github.com/go-jet/jet/v2/tests/.gentestdata/jetdb/test_sample/view" "github.com/go-jet/jet/v2/tests/.gentestdata/jetdb/test_sample/view"
"github.com/go-jet/jet/v2/tests/testdata/results/common" "github.com/go-jet/jet/v2/tests/testdata/results/common"
"github.com/google/uuid"
) )
func TestAllTypesSelect(t *testing.T) { func TestAllTypesSelect(t *testing.T) {
skipForPgxDriver(t) // pgx driver returns time with time zone as string
dest := []model.AllTypes{} dest := []model.AllTypes{}
err := AllTypes.SELECT(AllTypes.AllColumns).Query(db, &dest) err := AllTypes.SELECT(AllTypes.AllColumns).Query(db, &dest)
@ -25,6 +29,8 @@ func TestAllTypesSelect(t *testing.T) {
} }
func TestAllTypesViewSelect(t *testing.T) { func TestAllTypesViewSelect(t *testing.T) {
skipForPgxDriver(t) // pgx driver returns time with time zone as string
type AllTypesView model.AllTypes type AllTypesView model.AllTypes
dest := []AllTypesView{} dest := []AllTypesView{}
@ -37,6 +43,8 @@ func TestAllTypesViewSelect(t *testing.T) {
} }
func TestAllTypesInsertModel(t *testing.T) { func TestAllTypesInsertModel(t *testing.T) {
skipForPgxDriver(t) // pgx driver does not handle well time with time zone
query := AllTypes.INSERT(AllTypes.AllColumns). query := AllTypes.INSERT(AllTypes.AllColumns).
MODEL(allTypesRow0). MODEL(allTypesRow0).
MODEL(&allTypesRow1). MODEL(&allTypesRow1).
@ -52,6 +60,8 @@ func TestAllTypesInsertModel(t *testing.T) {
} }
func TestAllTypesInsertQuery(t *testing.T) { func TestAllTypesInsertQuery(t *testing.T) {
skipForPgxDriver(t) // pgx driver does not handle well time with time zone
query := AllTypes.INSERT(AllTypes.AllColumns). query := AllTypes.INSERT(AllTypes.AllColumns).
QUERY( QUERY(
AllTypes. AllTypes.
@ -70,6 +80,7 @@ func TestAllTypesInsertQuery(t *testing.T) {
} }
func TestAllTypesFromSubQuery(t *testing.T) { func TestAllTypesFromSubQuery(t *testing.T) {
skipForPgxDriver(t)
subQuery := SELECT(AllTypes.AllColumns). subQuery := SELECT(AllTypes.AllColumns).
FROM(AllTypes). FROM(AllTypes).
@ -221,12 +232,16 @@ func TestExpressionOperators(t *testing.T) {
AllTypes.DatePtr.IS_NOT_NULL().AS("result.is_not_null"), AllTypes.DatePtr.IS_NOT_NULL().AS("result.is_not_null"),
AllTypes.SmallIntPtr.IN(Int(11), Int(22)).AS("result.in"), AllTypes.SmallIntPtr.IN(Int(11), Int(22)).AS("result.in"),
AllTypes.SmallIntPtr.IN(AllTypes.SELECT(AllTypes.Integer)).AS("result.in_select"), AllTypes.SmallIntPtr.IN(AllTypes.SELECT(AllTypes.Integer)).AS("result.in_select"),
Raw("CURRENT_USER").AS("result.raw"),
Raw("#1 + COALESCE(all_types.small_int_ptr, 0) + #2", RawArgs{"#1": 78, "#2": 56}).AS("result.raw_arg"),
Raw("#1 + all_types.integer + #2 + #1 + #3 + #4",
RawArgs{"#1": 11, "#2": 22, "#3": 33, "#4": 44}).AS("result.raw_arg2"),
AllTypes.SmallIntPtr.NOT_IN(Int(11), Int(22), NULL).AS("result.not_in"), AllTypes.SmallIntPtr.NOT_IN(Int(11), Int(22), NULL).AS("result.not_in"),
AllTypes.SmallIntPtr.NOT_IN(AllTypes.SELECT(AllTypes.Integer)).AS("result.not_in_select"), AllTypes.SmallIntPtr.NOT_IN(AllTypes.SELECT(AllTypes.Integer)).AS("result.not_in_select"),
).LIMIT(2) ).LIMIT(2)
//fmt.Println(query.Sql())
testutils.AssertStatementSql(t, query, ` testutils.AssertStatementSql(t, query, `
SELECT all_types.integer IS NULL AS "result.is_null", SELECT all_types.integer IS NULL AS "result.is_null",
all_types.date_ptr IS NOT NULL AS "result.is_not_null", all_types.date_ptr IS NOT NULL AS "result.is_not_null",
@ -235,14 +250,17 @@ SELECT all_types.integer IS NULL AS "result.is_null",
SELECT all_types.integer AS "all_types.integer" SELECT all_types.integer AS "all_types.integer"
FROM test_sample.all_types FROM test_sample.all_types
))) AS "result.in_select", ))) AS "result.in_select",
(all_types.small_int_ptr NOT IN ($3, $4, NULL)) AS "result.not_in", (CURRENT_USER) AS "result.raw",
($3 + COALESCE(all_types.small_int_ptr, 0) + $4) AS "result.raw_arg",
($5 + all_types.integer + $6 + $5 + $7 + $8) AS "result.raw_arg2",
(all_types.small_int_ptr NOT IN ($9, $10, NULL)) AS "result.not_in",
(all_types.small_int_ptr NOT IN (( (all_types.small_int_ptr NOT IN ((
SELECT all_types.integer AS "all_types.integer" SELECT all_types.integer AS "all_types.integer"
FROM test_sample.all_types FROM test_sample.all_types
))) AS "result.not_in_select" ))) AS "result.not_in_select"
FROM test_sample.all_types FROM test_sample.all_types
LIMIT $5; LIMIT $11;
`, int64(11), int64(22), int64(11), int64(22), int64(2)) `, int64(11), int64(22), 78, 56, 11, 22, 33, 44, int64(11), int64(22), int64(2))
var dest []struct { var dest []struct {
common.ExpressionTestResult `alias:"result.*"` common.ExpressionTestResult `alias:"result.*"`
@ -261,6 +279,9 @@ LIMIT $5;
"IsNotNull": true, "IsNotNull": true,
"In": false, "In": false,
"InSelect": false, "InSelect": false,
"Raw": "jet",
"RawArg": 148,
"RawArg2": 421,
"NotIn": null, "NotIn": null,
"NotInSelect": true "NotInSelect": true
}, },
@ -269,6 +290,9 @@ LIMIT $5;
"IsNotNull": false, "IsNotNull": false,
"In": null, "In": null,
"InSelect": null, "InSelect": null,
"Raw": "jet",
"RawArg": 134,
"RawArg2": 421,
"NotIn": null, "NotIn": null,
"NotInSelect": null "NotInSelect": null
} }
@ -278,6 +302,8 @@ LIMIT $5;
func TestExpressionCast(t *testing.T) { func TestExpressionCast(t *testing.T) {
skipForPgxDriver(t) // for some reason, pgx driver, 150:char(12) returns as int value
query := AllTypes.SELECT( query := AllTypes.SELECT(
CAST(Int(150)).AS_CHAR(12).AS("char12"), CAST(Int(150)).AS_CHAR(12).AS("char12"),
CAST(String("TRUE")).AS_BOOL(), CAST(String("TRUE")).AS_BOOL(),
@ -323,6 +349,8 @@ func TestExpressionCast(t *testing.T) {
} }
func TestStringOperators(t *testing.T) { func TestStringOperators(t *testing.T) {
skipForPgxDriver(t) // pgx driver returns text column as int value
query := AllTypes.SELECT( query := AllTypes.SELECT(
AllTypes.Text.EQ(AllTypes.Char), AllTypes.Text.EQ(AllTypes.Char),
AllTypes.Text.EQ(String("Text")), AllTypes.Text.EQ(String("Text")),
@ -838,6 +866,7 @@ func TestInterval(t *testing.T) {
} }
func TestSubQueryColumnReference(t *testing.T) { func TestSubQueryColumnReference(t *testing.T) {
skipForPgxDriver(t) // pgx driver returns time with time zone as string value
type expected struct { type expected struct {
sql string sql string
@ -1015,6 +1044,7 @@ FROM`
} }
func TestTimeLiterals(t *testing.T) { func TestTimeLiterals(t *testing.T) {
skipForPgxDriver(t) // pgx driver returns time with time zone as string
loc, err := time.LoadLocation("Europe/Berlin") loc, err := time.LoadLocation("Europe/Berlin")
require.NoError(t, err) require.NoError(t, err)

View file

@ -2,7 +2,6 @@ package postgres
import ( import (
"context" "context"
"fmt"
"github.com/go-jet/jet/v2/internal/testutils" "github.com/go-jet/jet/v2/internal/testutils"
. "github.com/go-jet/jet/v2/postgres" . "github.com/go-jet/jet/v2/postgres"
"github.com/go-jet/jet/v2/tests/.gentestdata/jetdb/chinook/model" "github.com/go-jet/jet/v2/tests/.gentestdata/jetdb/chinook/model"
@ -17,7 +16,7 @@ func TestSelect(t *testing.T) {
SELECT(Album.AllColumns). SELECT(Album.AllColumns).
ORDER_BY(Album.AlbumId.ASC()) ORDER_BY(Album.AlbumId.ASC())
fmt.Println(stmt.DebugSql()) //fmt.Println(stmt.DebugSql())
testutils.AssertDebugStatementSql(t, stmt, ` testutils.AssertDebugStatementSql(t, stmt, `
SELECT "Album"."AlbumId" AS "Album.AlbumId", SELECT "Album"."AlbumId" AS "Album.AlbumId",
@ -330,8 +329,71 @@ ORDER BY "first10Artist"."Artist.ArtistId";
err := stmt.Query(db, &dest) err := stmt.Query(db, &dest)
require.NoError(t, err) require.NoError(t, err)
}
//spew.Dump(dest) func Test_SchemaRename(t *testing.T) {
Artist2 := Artist.FromSchema("chinook2")
Album2 := Album.FromSchema("chinook2")
first10Artist := Artist2.
SELECT(Artist2.AllColumns).
ORDER_BY(Artist2.ArtistId).
LIMIT(10).
AsTable("first10Artist")
artistID := Artist2.ArtistId.From(first10Artist)
first10Albums := Album2.
SELECT(Album2.AllColumns).
ORDER_BY(Album2.AlbumId).
LIMIT(10).
AsTable("first10Albums")
albumArtistID := Album2.ArtistId.From(first10Albums)
stmt := SELECT(first10Artist.AllColumns(), first10Albums.AllColumns()).
FROM(first10Artist.
INNER_JOIN(first10Albums, artistID.EQ(albumArtistID))).
ORDER_BY(artistID)
testutils.AssertDebugStatementSql(t, stmt, `
SELECT "first10Artist"."Artist.ArtistId" AS "Artist.ArtistId",
"first10Artist"."Artist.Name" AS "Artist.Name",
"first10Albums"."Album.AlbumId" AS "Album.AlbumId",
"first10Albums"."Album.Title" AS "Album.Title",
"first10Albums"."Album.ArtistId" AS "Album.ArtistId"
FROM (
SELECT "Artist"."ArtistId" AS "Artist.ArtistId",
"Artist"."Name" AS "Artist.Name"
FROM chinook2."Artist"
ORDER BY "Artist"."ArtistId"
LIMIT 10
) AS "first10Artist"
INNER JOIN (
SELECT "Album"."AlbumId" AS "Album.AlbumId",
"Album"."Title" AS "Album.Title",
"Album"."ArtistId" AS "Album.ArtistId"
FROM chinook2."Album"
ORDER BY "Album"."AlbumId"
LIMIT 10
) AS "first10Albums" ON ("first10Artist"."Artist.ArtistId" = "first10Albums"."Album.ArtistId")
ORDER BY "first10Artist"."Artist.ArtistId";
`)
var dest []struct {
model.Artist
Album []model.Album
}
err := stmt.Query(db, &dest)
require.NoError(t, err)
require.Len(t, dest, 2)
require.Equal(t, *dest[0].Artist.Name, "Apocalyptica")
require.Len(t, dest[0].Album, 1)
require.Equal(t, dest[0].Album[0].Title, "Plays Metallica By Four Cellos")
} }
var album1 = model.Album{ var album1 = model.Album{

View file

@ -46,12 +46,7 @@ func TestGeneratedModel(t *testing.T) {
const genTestDir2 = "./.gentestdata2" const genTestDir2 = "./.gentestdata2"
func TestCmdGenerator(t *testing.T) { func TestCmdGenerator(t *testing.T) {
goInstallJet := exec.Command("sh", "-c", "cd $GOPATH/src/ && GO111MODULE=off go get github.com/go-jet/jet/cmd/jet") err := os.RemoveAll(genTestDir2)
goInstallJet.Stderr = os.Stderr
err := goInstallJet.Run()
require.NoError(t, err)
err = os.RemoveAll(genTestDir2)
require.NoError(t, err) require.NoError(t, err)
cmd := exec.Command("jet", "-source=PostgreSQL", "-dbname=jetdb", "-host=localhost", "-port=5432", cmd := exec.Command("jet", "-source=PostgreSQL", "-dbname=jetdb", "-host=localhost", "-port=5432",
@ -173,7 +168,7 @@ import (
"github.com/go-jet/jet/v2/postgres" "github.com/go-jet/jet/v2/postgres"
) )
var Actor = newActorTable() var Actor = newActorTable("dvds", "actor", "")
type actorTable struct { type actorTable struct {
postgres.Table postgres.Table
@ -195,20 +190,23 @@ type ActorTable struct {
} }
// AS creates new ActorTable with assigned alias // AS creates new ActorTable with assigned alias
func (a *ActorTable) AS(alias string) *ActorTable { func (a ActorTable) AS(alias string) *ActorTable {
aliasTable := newActorTable() return newActorTable(a.SchemaName(), a.TableName(), alias)
aliasTable.Table.AS(alias)
return aliasTable
} }
func newActorTable() *ActorTable { // Schema creates new ActorTable with assigned schema name
func (a ActorTable) FromSchema(schemaName string) *ActorTable {
return newActorTable(schemaName, a.TableName(), a.Alias())
}
func newActorTable(schemaName, tableName, alias string) *ActorTable {
return &ActorTable{ return &ActorTable{
actorTable: newActorTableImpl("dvds", "actor"), actorTable: newActorTableImpl(schemaName, tableName, alias),
EXCLUDED: newActorTableImpl("", "excluded"), EXCLUDED: newActorTableImpl("", "excluded", ""),
} }
} }
func newActorTableImpl(schemaName, tableName string) actorTable { func newActorTableImpl(schemaName, tableName, alias string) actorTable {
var ( var (
ActorIDColumn = postgres.IntegerColumn("actor_id") ActorIDColumn = postgres.IntegerColumn("actor_id")
FirstNameColumn = postgres.StringColumn("first_name") FirstNameColumn = postgres.StringColumn("first_name")
@ -219,7 +217,7 @@ func newActorTableImpl(schemaName, tableName string) actorTable {
) )
return actorTable{ return actorTable{
Table: postgres.NewTable(schemaName, tableName, allColumns...), Table: postgres.NewTable(schemaName, tableName, alias, allColumns...),
//Columns //Columns
ActorID: ActorIDColumn, ActorID: ActorIDColumn,
@ -269,7 +267,7 @@ import (
"github.com/go-jet/jet/v2/postgres" "github.com/go-jet/jet/v2/postgres"
) )
var ActorInfo = newActorInfoTable() var ActorInfo = newActorInfoTable("dvds", "actor_info", "")
type actorInfoTable struct { type actorInfoTable struct {
postgres.Table postgres.Table
@ -291,20 +289,23 @@ type ActorInfoTable struct {
} }
// AS creates new ActorInfoTable with assigned alias // AS creates new ActorInfoTable with assigned alias
func (a *ActorInfoTable) AS(alias string) *ActorInfoTable { func (a ActorInfoTable) AS(alias string) *ActorInfoTable {
aliasTable := newActorInfoTable() return newActorInfoTable(a.SchemaName(), a.TableName(), alias)
aliasTable.Table.AS(alias)
return aliasTable
} }
func newActorInfoTable() *ActorInfoTable { // Schema creates new ActorInfoTable with assigned schema name
func (a ActorInfoTable) FromSchema(schemaName string) *ActorInfoTable {
return newActorInfoTable(schemaName, a.TableName(), a.Alias())
}
func newActorInfoTable(schemaName, tableName, alias string) *ActorInfoTable {
return &ActorInfoTable{ return &ActorInfoTable{
actorInfoTable: newActorInfoTableImpl("dvds", "actor_info"), actorInfoTable: newActorInfoTableImpl(schemaName, tableName, alias),
EXCLUDED: newActorInfoTableImpl("", "excluded"), EXCLUDED: newActorInfoTableImpl("", "excluded", ""),
} }
} }
func newActorInfoTableImpl(schemaName, tableName string) actorInfoTable { func newActorInfoTableImpl(schemaName, tableName, alias string) actorInfoTable {
var ( var (
ActorIDColumn = postgres.IntegerColumn("actor_id") ActorIDColumn = postgres.IntegerColumn("actor_id")
FirstNameColumn = postgres.StringColumn("first_name") FirstNameColumn = postgres.StringColumn("first_name")
@ -315,7 +316,7 @@ func newActorInfoTableImpl(schemaName, tableName string) actorInfoTable {
) )
return actorInfoTable{ return actorInfoTable{
Table: postgres.NewTable(schemaName, tableName, allColumns...), Table: postgres.NewTable(schemaName, tableName, alias, allColumns...),
//Columns //Columns
ActorID: ActorIDColumn, ActorID: ActorIDColumn,
@ -345,7 +346,7 @@ func TestGeneratedAllTypesSQLBuilderFiles(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
testutils.AssertFileNamesEqual(t, modelFiles, "all_types.go", "all_types_view.go", "employee.go", "link.go", testutils.AssertFileNamesEqual(t, modelFiles, "all_types.go", "all_types_view.go", "employee.go", "link.go",
"mood.go", "person.go", "person_phone.go", "weird_names_table.go", "level.go", "user.go") "mood.go", "person.go", "person_phone.go", "weird_names_table.go", "level.go", "user.go", "floats.go")
testutils.AssertFileContent(t, modelDir+"all_types.go", allTypesModelContent) testutils.AssertFileContent(t, modelDir+"all_types.go", allTypesModelContent)
@ -353,7 +354,7 @@ func TestGeneratedAllTypesSQLBuilderFiles(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
testutils.AssertFileNamesEqual(t, tableFiles, "all_types.go", "employee.go", "link.go", testutils.AssertFileNamesEqual(t, tableFiles, "all_types.go", "employee.go", "link.go",
"person.go", "person_phone.go", "weird_names_table.go", "user.go") "person.go", "person_phone.go", "weird_names_table.go", "user.go", "floats.go")
testutils.AssertFileContent(t, tableDir+"all_types.go", allTypesTableContent) testutils.AssertFileContent(t, tableDir+"all_types.go", allTypesTableContent)
} }
@ -502,7 +503,7 @@ import (
"github.com/go-jet/jet/v2/postgres" "github.com/go-jet/jet/v2/postgres"
) )
var AllTypes = newAllTypesTable() var AllTypes = newAllTypesTable("test_sample", "all_types", "")
type allTypesTable struct { type allTypesTable struct {
postgres.Table postgres.Table
@ -581,20 +582,23 @@ type AllTypesTable struct {
} }
// AS creates new AllTypesTable with assigned alias // AS creates new AllTypesTable with assigned alias
func (a *AllTypesTable) AS(alias string) *AllTypesTable { func (a AllTypesTable) AS(alias string) *AllTypesTable {
aliasTable := newAllTypesTable() return newAllTypesTable(a.SchemaName(), a.TableName(), alias)
aliasTable.Table.AS(alias)
return aliasTable
} }
func newAllTypesTable() *AllTypesTable { // Schema creates new AllTypesTable with assigned schema name
func (a AllTypesTable) FromSchema(schemaName string) *AllTypesTable {
return newAllTypesTable(schemaName, a.TableName(), a.Alias())
}
func newAllTypesTable(schemaName, tableName, alias string) *AllTypesTable {
return &AllTypesTable{ return &AllTypesTable{
allTypesTable: newAllTypesTableImpl("test_sample", "all_types"), allTypesTable: newAllTypesTableImpl(schemaName, tableName, alias),
EXCLUDED: newAllTypesTableImpl("", "excluded"), EXCLUDED: newAllTypesTableImpl("", "excluded", ""),
} }
} }
func newAllTypesTableImpl(schemaName, tableName string) allTypesTable { func newAllTypesTableImpl(schemaName, tableName, alias string) allTypesTable {
var ( var (
SmallIntPtrColumn = postgres.IntegerColumn("small_int_ptr") SmallIntPtrColumn = postgres.IntegerColumn("small_int_ptr")
SmallIntColumn = postgres.IntegerColumn("small_int") SmallIntColumn = postgres.IntegerColumn("small_int")
@ -662,7 +666,7 @@ func newAllTypesTableImpl(schemaName, tableName string) allTypesTable {
) )
return allTypesTable{ return allTypesTable{
Table: postgres.NewTable(schemaName, tableName, allColumns...), Table: postgres.NewTable(schemaName, tableName, alias, allColumns...),
//Columns //Columns
SmallIntPtr: SmallIntPtrColumn, SmallIntPtr: SmallIntPtrColumn,

View file

@ -3,17 +3,23 @@ package postgres
import ( import (
"context" "context"
"database/sql" "database/sql"
"github.com/go-jet/jet/v2/postgres" "fmt"
"github.com/go-jet/jet/v2/tests/dbconfig"
_ "github.com/lib/pq"
"github.com/pkg/profile"
"github.com/stretchr/testify/require"
"math/rand" "math/rand"
"os" "os"
"os/exec" "os/exec"
"strings" "strings"
"testing" "testing"
"time" "time"
"github.com/jackc/pgx/v4/stdlib"
"github.com/go-jet/jet/v2/postgres"
"github.com/go-jet/jet/v2/tests/dbconfig"
_ "github.com/lib/pq"
"github.com/pkg/profile"
"github.com/stretchr/testify/require"
_ "github.com/jackc/pgx/v4/stdlib"
) )
var db *sql.DB var db *sql.DB
@ -25,16 +31,23 @@ func TestMain(m *testing.M) {
setTestRoot() setTestRoot()
for _, driverName := range []string{"postgres", "pgx"} {
func() {
var err error var err error
db, err = sql.Open("postgres", dbconfig.PostgresConnectString) db, err = sql.Open(driverName, dbconfig.PostgresConnectString)
if err != nil { if err != nil {
fmt.Println(err.Error())
panic("Failed to connect to test db") panic("Failed to connect to test db")
} }
defer db.Close() defer db.Close()
ret := m.Run() ret := m.Run()
if ret != 0 {
os.Exit(ret) os.Exit(ret)
}
}()
}
} }
func setTestRoot() { func setTestRoot() {
@ -64,3 +77,10 @@ func requireLogged(t *testing.T, statement postgres.Statement) {
require.Equal(t, loggedSQLArgs, args) require.Equal(t, loggedSQLArgs, args)
require.Equal(t, loggedDebugSQL, statement.DebugSql()) require.Equal(t, loggedDebugSQL, statement.DebugSql())
} }
func skipForPgxDriver(t *testing.T) {
switch db.Driver().(type) {
case *stdlib.Driver:
t.SkipNow()
}
}

View file

@ -0,0 +1,179 @@
package postgres
import (
"context"
"testing"
"time"
"github.com/stretchr/testify/require"
"github.com/go-jet/jet/v2/internal/testutils"
"github.com/go-jet/jet/v2/tests/.gentestdata/jetdb/dvds/model"
model2 "github.com/go-jet/jet/v2/tests/.gentestdata/mysql/test_sample/model"
. "github.com/go-jet/jet/v2/postgres"
)
func TestRawStatementSelect(t *testing.T) {
stmt := RawStatement(`
SELECT actor.first_name AS "actor.first_name"
FROM dvds.actor
WHERE actor.actor_id = 2`)
testutils.AssertStatementSql(t, stmt, `
SELECT actor.first_name AS "actor.first_name"
FROM dvds.actor
WHERE actor.actor_id = 2;
`)
testutils.AssertDebugStatementSql(t, stmt, `
SELECT actor.first_name AS "actor.first_name"
FROM dvds.actor
WHERE actor.actor_id = 2;
`)
var actor model.Actor
err := stmt.Query(db, &actor)
require.NoError(t, err)
require.Equal(t, actor.FirstName, "Nick")
}
func TestRawStatementSelectWithArguments(t *testing.T) {
stmt := RawStatement(`
SELECT DISTINCT actor.actor_id AS "actor.actor_id",
actor.first_name AS "actor.first_name",
actor.last_name AS "actor.last_name",
actor.last_update AS "actor.last_update"
FROM dvds.actor
WHERE actor.actor_id IN (#actorID1, #actorID2, #actorID3) AND ((#actorID1 / #actorID2) <> (#actorID2 * #actorID3))
ORDER BY actor.actor_id`,
RawArgs{
"#actorID1": int64(1),
"#actorID2": int64(2),
"#actorID3": int64(3),
},
)
testutils.AssertStatementSql(t, stmt, `
SELECT DISTINCT actor.actor_id AS "actor.actor_id",
actor.first_name AS "actor.first_name",
actor.last_name AS "actor.last_name",
actor.last_update AS "actor.last_update"
FROM dvds.actor
WHERE actor.actor_id IN ($1, $2, $3) AND (($1 / $2) <> ($2 * $3))
ORDER BY actor.actor_id;
`, int64(1), int64(2), int64(3))
testutils.AssertDebugStatementSql(t, stmt, `
SELECT DISTINCT actor.actor_id AS "actor.actor_id",
actor.first_name AS "actor.first_name",
actor.last_name AS "actor.last_name",
actor.last_update AS "actor.last_update"
FROM dvds.actor
WHERE actor.actor_id IN (1, 2, 3) AND ((1 / 2) <> (2 * 3))
ORDER BY actor.actor_id;
`)
var actor []model.Actor
err := stmt.Query(db, &actor)
require.NoError(t, err)
testutils.AssertDeepEqual(t, actor[1], model.Actor{
ActorID: 2,
FirstName: "Nick",
LastName: "Wahlberg",
LastUpdate: *testutils.TimestampWithoutTimeZone("2013-05-26 14:47:57.62", 2),
})
}
func TestRawInsert(t *testing.T) {
cleanUpLinkTable(t)
stmt := RawStatement(`
INSERT INTO test_sample.link (id, url, name, description)
VALUES (@id1, @url1, @name1, DEFAULT),
(200, @url1, @name1, NULL),
(@id2, @url2, @name2, DEFAULT),
(@id3, @url3, @name3, NULL)
RETURNING link.id AS "link.id",
link.url AS "link.url",
link.name AS "link.name",
link.description AS "link.description"`,
RawArgs{
"@id1": 100, "@url1": "http://www.postgresqltutorial.com", "@name1": "PostgreSQL Tutorial",
"@id2": 101, "@url2": "http://www.google.com", "@name2": "Google",
"@id3": 102, "@url3": "http://www.yahoo.com", "@name3": "Yahoo",
})
testutils.AssertStatementSql(t, stmt, `
INSERT INTO test_sample.link (id, url, name, description)
VALUES ($1, $2, $3, DEFAULT),
(200, $2, $3, NULL),
($4, $5, $6, DEFAULT),
($7, $8, $9, NULL)
RETURNING link.id AS "link.id",
link.url AS "link.url",
link.name AS "link.name",
link.description AS "link.description";
`, 100, "http://www.postgresqltutorial.com", "PostgreSQL Tutorial",
101, "http://www.google.com", "Google",
102, "http://www.yahoo.com", "Yahoo")
testutils.AssertDebugStatementSql(t, stmt, `
INSERT INTO test_sample.link (id, url, name, description)
VALUES (100, 'http://www.postgresqltutorial.com', 'PostgreSQL Tutorial', DEFAULT),
(200, 'http://www.postgresqltutorial.com', 'PostgreSQL Tutorial', NULL),
(101, 'http://www.google.com', 'Google', DEFAULT),
(102, 'http://www.yahoo.com', 'Yahoo', NULL)
RETURNING link.id AS "link.id",
link.url AS "link.url",
link.name AS "link.name",
link.description AS "link.description";
`)
var links []model2.Link
err := stmt.Query(db, &links)
require.NoError(t, err)
require.Len(t, links, 4)
require.Equal(t, links[0].ID, int32(100))
require.Equal(t, links[1].URL, "http://www.postgresqltutorial.com")
require.Equal(t, links[2].Name, "Google")
require.Nil(t, links[2].Description)
}
func TestRawStatementRows(t *testing.T) {
stmt := RawStatement(`
SELECT actor.actor_id AS "actor.actor_id",
actor.first_name AS "actor.first_name",
actor.last_name AS "actor.last_name",
actor.last_update AS "actor.last_update"
FROM dvds.actor
ORDER BY actor.actor_id`)
rows, err := stmt.Rows(context.Background(), db)
require.NoError(t, err)
for rows.Next() {
var actor model.Actor
err := rows.Scan(&actor)
require.NoError(t, err)
require.NotEqual(t, actor.ActorID, int32(0))
require.NotEqual(t, actor.FirstName, "")
require.NotEqual(t, actor.LastName, "")
require.NotEqual(t, actor.LastUpdate, time.Time{})
if actor.ActorID == 54 {
require.Equal(t, actor.ActorID, int32(54))
require.Equal(t, actor.FirstName, "Penelope")
require.Equal(t, actor.LastName, "Pinkett")
require.Equal(t, actor.LastUpdate.Format(time.RFC3339), "2013-05-26T14:47:57Z")
}
}
err = rows.Close()
require.NoError(t, err)
err = rows.Err()
require.NoError(t, err)
requireLogged(t, stmt)
}

View file

@ -1,19 +1,26 @@
package postgres package postgres
import ( import (
"testing"
"github.com/google/uuid"
"github.com/stretchr/testify/require"
"github.com/go-jet/jet/v2/internal/testutils" "github.com/go-jet/jet/v2/internal/testutils"
. "github.com/go-jet/jet/v2/postgres" . "github.com/go-jet/jet/v2/postgres"
"github.com/go-jet/jet/v2/tests/.gentestdata/jetdb/test_sample/model" "github.com/go-jet/jet/v2/tests/.gentestdata/jetdb/test_sample/model"
. "github.com/go-jet/jet/v2/tests/.gentestdata/jetdb/test_sample/table" . "github.com/go-jet/jet/v2/tests/.gentestdata/jetdb/test_sample/table"
"github.com/google/uuid"
"github.com/stretchr/testify/require" "github.com/shopspring/decimal"
"testing"
) )
func TestUUIDType(t *testing.T) { func TestUUIDType(t *testing.T) {
id := uuid.MustParse("a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11")
query := AllTypes. query := AllTypes.
SELECT(AllTypes.UUID, AllTypes.UUIDPtr). SELECT(AllTypes.UUID, AllTypes.UUIDPtr).
WHERE(AllTypes.UUID.EQ(String("a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11"))) WHERE(AllTypes.UUID.EQ(UUID(id)))
testutils.AssertDebugStatementSql(t, query, ` testutils.AssertDebugStatementSql(t, query, `
SELECT all_types.uuid AS "all_types.uuid", SELECT all_types.uuid AS "all_types.uuid",
@ -31,6 +38,110 @@ WHERE all_types.uuid = 'a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11';
requireLogged(t, query) requireLogged(t, query)
} }
func TestExactDecimals(t *testing.T) {
type floats struct {
model.Floats
Numeric decimal.Decimal
NumericPtr decimal.Decimal
Decimal decimal.Decimal
DecimalPtr decimal.Decimal
}
t.Run("should query decimal", func(t *testing.T) {
query := SELECT(
Floats.AllColumns,
).FROM(
Floats,
).WHERE(Floats.Decimal.EQ(Decimal("1.11111111111111111111")))
var result floats
err := query.Query(db, &result)
require.NoError(t, err)
require.Equal(t, "1.11111111111111111111", result.Decimal.String())
require.Equal(t, "0", result.DecimalPtr.String()) // NULL
require.Equal(t, "2.22222222222222222222", result.Numeric.String())
require.Equal(t, "0", result.NumericPtr.String()) // NULL
require.Equal(t, 1.1111111111111112, result.Floats.Decimal) // precision loss
require.Equal(t, (*float64)(nil), result.Floats.DecimalPtr)
require.Equal(t, 2.2222222222222223, result.Floats.Numeric) // precision loss
require.Equal(t, (*float64)(nil), result.Floats.NumericPtr)
// floating point
require.Equal(t, float32(3.3333333), result.Floats.Real) // precision loss
require.Equal(t, (*float32)(nil), result.Floats.RealPtr)
require.Equal(t, 4.444444444444445, result.Floats.Double) // precision loss
require.Equal(t, (*float64)(nil), result.Floats.DoublePtr)
})
t.Run("should insert decimal", func(t *testing.T) {
insertQuery := Floats.INSERT(
Floats.AllColumns,
).MODEL(
floats{
Floats: model.Floats{
// overwritten by wrapped(floats) scope
Numeric: 0.1,
NumericPtr: testutils.Float64Ptr(0.1),
Decimal: 0.1,
DecimalPtr: testutils.Float64Ptr(0.1),
// not overwritten
Real: 0.4,
RealPtr: testutils.Float32Ptr(0.44),
Double: 0.3,
DoublePtr: testutils.Float64Ptr(0.33),
},
Numeric: decimal.RequireFromString("0.1234567890123456789"),
NumericPtr: decimal.RequireFromString("1.1111111111111111111"),
Decimal: decimal.RequireFromString("2.2222222222222222222"),
DecimalPtr: decimal.RequireFromString("3.3333333333333333333"),
},
).RETURNING(
Floats.AllColumns,
)
testutils.AssertDebugStatementSql(t, insertQuery, `
INSERT INTO test_sample.floats (decimal_ptr, decimal, numeric_ptr, numeric, real_ptr, real, double_ptr, double)
VALUES ('3.3333333333333333333', '2.2222222222222222222', '1.1111111111111111111', '0.1234567890123456789', 0.4399999976158142, 0.4000000059604645, 0.33, 0.3)
RETURNING floats.decimal_ptr AS "floats.decimal_ptr",
floats.decimal AS "floats.decimal",
floats.numeric_ptr AS "floats.numeric_ptr",
floats.numeric AS "floats.numeric",
floats.real_ptr AS "floats.real_ptr",
floats.real AS "floats.real",
floats.double_ptr AS "floats.double_ptr",
floats.double AS "floats.double";
`)
var result floats
err := insertQuery.Query(db, &result)
require.NoError(t, err)
// exact decimal
require.Equal(t, "0.1234567890123456789", result.Numeric.String())
require.Equal(t, "1.1111111111111111111", result.NumericPtr.String())
require.Equal(t, "2.2222222222222222222", result.Decimal.String())
require.Equal(t, "3.3333333333333333333", result.DecimalPtr.String())
// precision loss
require.Equal(t, 0.12345678901234568, result.Floats.Numeric)
require.Equal(t, 1.1111111111111112, *result.Floats.NumericPtr)
require.Equal(t, 2.2222222222222223, result.Floats.Decimal)
require.Equal(t, 3.3333333333333335, *result.Floats.DecimalPtr)
// floating points numbers
require.Equal(t, float32(0.4), result.Floats.Real)
require.Equal(t, float32(0.44), *result.Floats.RealPtr)
require.Equal(t, 0.3, result.Floats.Double)
require.Equal(t, 0.33, *result.Floats.DoublePtr)
})
}
func TestUUIDComplex(t *testing.T) { func TestUUIDComplex(t *testing.T) {
query := Person.INNER_JOIN(PersonPhone, PersonPhone.PersonID.EQ(Person.PersonID)). query := Person.INNER_JOIN(PersonPhone, PersonPhone.PersonID.EQ(Person.PersonID)).
SELECT(Person.AllColumns, PersonPhone.AllColumns). SELECT(Person.AllColumns, PersonPhone.AllColumns).
@ -364,7 +475,7 @@ FROM test_sample."User";
err := stmt.Query(db, &dest) err := stmt.Query(db, &dest)
require.NoError(t, err) require.NoError(t, err)
testutils.PrintJson(dest) //testutils.PrintJson(dest)
testutils.AssertJSON(t, dest, ` testutils.AssertJSON(t, dest, `
[ [
@ -386,3 +497,54 @@ FROM test_sample."User";
] ]
`) `)
} }
func TestBytea(t *testing.T) {
byteArrHex := "\\x48656c6c6f20476f7068657221"
byteArrBin := []byte("\x48\x65\x6c\x6c\x6f\x20\x47\x6f\x70\x68\x65\x72\x21")
insertStmt := AllTypes.INSERT(AllTypes.Bytea, AllTypes.ByteaPtr).
VALUES(byteArrHex, byteArrBin).
RETURNING(AllTypes.Bytea, AllTypes.ByteaPtr)
testutils.AssertStatementSql(t, insertStmt, `
INSERT INTO test_sample.all_types (bytea, bytea_ptr)
VALUES ($1, $2)
RETURNING all_types.bytea AS "all_types.bytea",
all_types.bytea_ptr AS "all_types.bytea_ptr";
`, byteArrHex, byteArrBin)
var inserted model.AllTypes
err := insertStmt.Query(db, &inserted)
require.NoError(t, err)
require.Equal(t, string(*inserted.ByteaPtr), "Hello Gopher!")
// It is not possible to initiate bytea column using hex format '\xDEADBEEF' with pq driver.
// pq driver always encodes parameter string if destination column is of type bytea.
// Probably pq driver error.
// require.Equal(t, string(inserted.Bytea), "Hello Gopher!")
stmt := SELECT(
AllTypes.Bytea,
AllTypes.ByteaPtr,
).FROM(
AllTypes,
).WHERE(
AllTypes.ByteaPtr.EQ(Bytea(byteArrBin)),
)
testutils.AssertStatementSql(t, stmt, `
SELECT all_types.bytea AS "all_types.bytea",
all_types.bytea_ptr AS "all_types.bytea_ptr"
FROM test_sample.all_types
WHERE all_types.bytea_ptr = $1::bytea;
`, byteArrBin)
var dest model.AllTypes
err = stmt.Query(db, &dest)
require.NoError(t, err)
require.Equal(t, string(*dest.ByteaPtr), "Hello Gopher!")
// Probably pq driver error.
// require.Equal(t, string(dest.Bytea), "Hello Gopher!")
}

View file

@ -1,15 +1,18 @@
package postgres package postgres
import ( import (
"fmt" "context"
"testing"
"time"
"github.com/google/uuid"
"github.com/stretchr/testify/require"
"github.com/go-jet/jet/v2/internal/testutils" "github.com/go-jet/jet/v2/internal/testutils"
. "github.com/go-jet/jet/v2/postgres" . "github.com/go-jet/jet/v2/postgres"
"github.com/go-jet/jet/v2/qrm" "github.com/go-jet/jet/v2/qrm"
"github.com/go-jet/jet/v2/tests/.gentestdata/jetdb/dvds/model" "github.com/go-jet/jet/v2/tests/.gentestdata/jetdb/dvds/model"
. "github.com/go-jet/jet/v2/tests/.gentestdata/jetdb/dvds/table" . "github.com/go-jet/jet/v2/tests/.gentestdata/jetdb/dvds/table"
"github.com/google/uuid"
"github.com/stretchr/testify/require"
"testing"
) )
var oneInventoryQuery = Inventory. var oneInventoryQuery = Inventory.
@ -93,7 +96,7 @@ func TestScanToStruct(t *testing.T) {
SELECT(Inventory.AllColumns). SELECT(Inventory.AllColumns).
ORDER_BY(Inventory.InventoryID) ORDER_BY(Inventory.InventoryID)
fmt.Println(query.DebugSql()) //fmt.Println(query.DebugSql())
t.Run("one struct", func(t *testing.T) { t.Run("one struct", func(t *testing.T) {
dest := model.Inventory{} dest := model.Inventory{}
@ -723,6 +726,89 @@ func TestStructScanAllNull(t *testing.T) {
}{}) }{})
} }
func TestRowsScan(t *testing.T) {
stmt := SELECT(
Inventory.AllColumns,
).FROM(
Inventory,
).ORDER_BY(
Inventory.InventoryID.ASC(),
)
rows, err := stmt.Rows(context.Background(), db)
require.NoError(t, err)
for rows.Next() {
var inventory model.Inventory
err = rows.Scan(&inventory)
require.NoError(t, err)
require.NotEqual(t, inventory.InventoryID, int32(0))
require.NotEqual(t, inventory.FilmID, int16(0))
require.NotEqual(t, inventory.StoreID, int16(0))
require.NotEqual(t, inventory.LastUpdate, time.Time{})
if inventory.InventoryID == 2103 {
require.Equal(t, inventory.FilmID, int16(456))
require.Equal(t, inventory.StoreID, int16(2))
require.Equal(t, inventory.LastUpdate.Format(time.RFC3339), "2006-02-15T10:09:17Z")
}
}
err = rows.Close()
require.NoError(t, err)
err = rows.Err()
require.NoError(t, err)
requireLogged(t, stmt)
}
func TestScanNumericToNumber(t *testing.T) {
type Number struct {
Int8 int8
UInt8 uint8
Int16 int16
UInt16 uint16
Int32 int32
UInt32 uint32
Int64 int64
UInt64 uint64
Float32 float32
Float64 float64
}
numeric := CAST(Decimal("1234567890.111")).AS_NUMERIC()
stmt := SELECT(
numeric.AS("number.int8"),
numeric.AS("number.uint8"),
numeric.AS("number.int16"),
numeric.AS("number.uint16"),
numeric.AS("number.int32"),
numeric.AS("number.uint32"),
numeric.AS("number.int64"),
numeric.AS("number.uint64"),
numeric.AS("number.float32"),
numeric.AS("number.float64"),
)
var number Number
err := stmt.Query(db, &number)
require.NoError(t, err)
require.Equal(t, number.Int8, int8(-46)) // overflow
require.Equal(t, number.UInt8, uint8(210)) // overflow
require.Equal(t, number.Int16, int16(722)) // overflow
require.Equal(t, number.UInt16, uint16(722)) // overflow
require.Equal(t, number.Int32, int32(1234567890))
require.Equal(t, number.UInt32, uint32(1234567890))
require.Equal(t, number.Int64, int64(1234567890))
require.Equal(t, number.UInt64, uint64(1234567890))
require.Equal(t, number.Float32, float32(1.234568e+09))
require.Equal(t, number.Float64, float64(1.234567890111e+09))
}
var address256 = model.Address{ var address256 = model.Address{
AddressID: 256, AddressID: 256,
Address: "1497 Yuzhou Drive", Address: "1497 Yuzhou Drive",

View file

@ -1,16 +1,17 @@
package postgres package postgres
import ( import (
"fmt" "testing"
"time"
"github.com/stretchr/testify/require"
"github.com/go-jet/jet/v2/internal/testutils" "github.com/go-jet/jet/v2/internal/testutils"
. "github.com/go-jet/jet/v2/postgres" . "github.com/go-jet/jet/v2/postgres"
"github.com/go-jet/jet/v2/tests/.gentestdata/jetdb/dvds/enum" "github.com/go-jet/jet/v2/tests/.gentestdata/jetdb/dvds/enum"
"github.com/go-jet/jet/v2/tests/.gentestdata/jetdb/dvds/model" "github.com/go-jet/jet/v2/tests/.gentestdata/jetdb/dvds/model"
. "github.com/go-jet/jet/v2/tests/.gentestdata/jetdb/dvds/table" . "github.com/go-jet/jet/v2/tests/.gentestdata/jetdb/dvds/table"
"github.com/go-jet/jet/v2/tests/.gentestdata/jetdb/dvds/view" "github.com/go-jet/jet/v2/tests/.gentestdata/jetdb/dvds/view"
"github.com/stretchr/testify/require"
"testing"
"time"
) )
func TestSelect_ScanToStruct(t *testing.T) { func TestSelect_ScanToStruct(t *testing.T) {
@ -1255,7 +1256,7 @@ OFFSET 20;
LIMIT(10). LIMIT(10).
OFFSET(20) OFFSET(20)
fmt.Println(query.DebugSql()) //fmt.Println(query.DebugSql())
testutils.AssertDebugStatementSql(t, query, expectedQuery, float64(100), float64(200), int64(10), int64(20)) testutils.AssertDebugStatementSql(t, query, expectedQuery, float64(100), float64(200), int64(10), int64(20))
@ -1788,7 +1789,7 @@ func TestJoinViewWithTable(t *testing.T) {
Rentals []model.Rental Rentals []model.Rental
} }
fmt.Println(query.DebugSql()) //fmt.Println(query.DebugSql())
err := query.Query(db, &dest) err := query.Query(db, &dest)
require.NoError(t, err) require.NoError(t, err)
@ -1894,3 +1895,100 @@ WHERE ($1 AND (customer.customer_id = $2)) AND (customer.activebool = $3);
require.Len(t, dest, 1) require.Len(t, dest, 1)
testutils.AssertDeepEqual(t, dest[0], customer0) testutils.AssertDeepEqual(t, dest[0], customer0)
} }
func TestLateral(t *testing.T) {
languages := LATERAL(
SELECT(
Language.AllColumns,
).FROM(
Language,
).WHERE(
Language.Name.NOT_IN(String("spanish")).
AND(Film.LanguageID.EQ(Language.LanguageID)),
),
).AS("films")
stmt := SELECT(
Film.FilmID,
Film.Title,
languages.AllColumns(),
).FROM(
Film.CROSS_JOIN(languages),
).WHERE(
Film.FilmID.EQ(Int(1)),
).ORDER_BY(
Film.FilmID,
).LIMIT(1)
testutils.AssertDebugStatementSql(t, stmt, `
SELECT film.film_id AS "film.film_id",
film.title AS "film.title",
films."language.language_id" AS "language.language_id",
films."language.name" AS "language.name",
films."language.last_update" AS "language.last_update"
FROM dvds.film
CROSS JOIN LATERAL (
SELECT language.language_id AS "language.language_id",
language.name AS "language.name",
language.last_update AS "language.last_update"
FROM dvds.language
WHERE (language.name NOT IN ('spanish')) AND (film.language_id = language.language_id)
) AS films
WHERE film.film_id = 1
ORDER BY film.film_id
LIMIT 1;
`)
type FilmLanguage struct {
model.Film
model.Language
}
var dest []FilmLanguage
err := stmt.Query(db, &dest)
require.NoError(t, err)
require.Equal(t, dest[0].Film.Title, "Academy Dinosaur")
require.Equal(t, dest[0].Language.Name, "English ")
t.Run("implicit cross join", func(t *testing.T) {
stmt2 := SELECT(
Film.FilmID,
Film.Title,
languages.AllColumns(),
).FROM(
Film,
languages,
).WHERE(
Film.FilmID.EQ(Int(1)),
).ORDER_BY(
Film.FilmID,
).LIMIT(1)
testutils.AssertDebugStatementSql(t, stmt2, `
SELECT film.film_id AS "film.film_id",
film.title AS "film.title",
films."language.language_id" AS "language.language_id",
films."language.name" AS "language.name",
films."language.last_update" AS "language.last_update"
FROM dvds.film,
LATERAL (
SELECT language.language_id AS "language.language_id",
language.name AS "language.name",
language.last_update AS "language.last_update"
FROM dvds.language
WHERE (language.name NOT IN ('spanish')) AND (film.language_id = language.language_id)
) AS films
WHERE film.film_id = 1
ORDER BY film.film_id
LIMIT 1;
`)
var dest2 []FilmLanguage
err2 := stmt2.Query(db, &dest2)
require.NoError(t, err2)
require.Equal(t, dest, dest2)
})
}

@ -1 +1 @@
Subproject commit ed53a505eb738d1be457877eee251f9ba0418df1 Subproject commit a6c1975a167645f913496131ae81d4cabc070046