From 9a3c29b504dd3c5ac4f1fb2fb71f4701e9847f11 Mon Sep 17 00:00:00 2001 From: Joonas Haapsaari Date: Wed, 29 Jul 2020 16:37:55 +0300 Subject: [PATCH 01/41] MySQL NOT operator support - Suppot for NOT operator in MySQL, similarly as in PostgreSQL. - Add test to verify NOT EXISTS query in MySQL --- mysql/functions.go | 5 ----- mysql/operators.go | 9 +++++++++ mysql/select_statement_test.go | 22 ++++++++++++++++++++++ 3 files changed, 31 insertions(+), 5 deletions(-) create mode 100644 mysql/operators.go diff --git a/mysql/functions.go b/mysql/functions.go index 347b433d..6d8193d9 100644 --- a/mysql/functions.go +++ b/mysql/functions.go @@ -253,8 +253,3 @@ var EXISTS = jet.EXISTS // CASE create CASE operator with optional list of expressions var CASE = jet.CASE - -//----------------- Bit operators ---------------// - -// BIT_NOT inverts every bit in integer expression -var BIT_NOT = jet.BIT_NOT diff --git a/mysql/operators.go b/mysql/operators.go new file mode 100644 index 00000000..55855118 --- /dev/null +++ b/mysql/operators.go @@ -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 diff --git a/mysql/select_statement_test.go b/mysql/select_statement_test.go index 261ac20a..2312a60c 100644 --- a/mysql/select_statement_test.go +++ b/mysql/select_statement_test.go @@ -132,3 +132,25 @@ FROM db.table1 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 + ))); +`) +} From b7bcb3527e69075703a0027b40d475b9a994f04a Mon Sep 17 00:00:00 2001 From: Joonas Haapsaari Date: Tue, 18 Aug 2020 15:47:10 +0300 Subject: [PATCH 02/41] Schema rename support - Support for renaming table schemas * Table support for renaming schema * Empty schema name is left out (using default schema for the database connection) * Generated code support for obtaining a version of the table with renamed schema, similarly as the `AS` function works * Unit tests for setting and clearing the schema name --- generator/internal/template/templates.go | 18 ++++++++++++ internal/jet/table.go | 25 +++++++++++++++-- internal/jet/table_test.go | 35 ++++++++++++++++++++++++ 3 files changed, 76 insertions(+), 2 deletions(-) diff --git a/generator/internal/template/templates.go b/generator/internal/template/templates.go index 56dba2ca..5288ef40 100644 --- a/generator/internal/template/templates.go +++ b/generator/internal/template/templates.go @@ -41,6 +41,15 @@ type {{.GoStructName}} struct { func (a *{{.GoStructName}}) AS(alias string) {{.GoStructName}} { aliasTable := new{{.GoStructName}}() aliasTable.Table.AS(alias) + aliasTable.Table.Schema(a.Table.SchemaName()) + return aliasTable +} + +// Schema creates new {{.GoStructName}} with assigned schema name +func (a *{{.GoStructName}}) Schema(schemaName string) {{.GoStructName}} { + aliasTable := new{{.GoStructName}}() + aliasTable.Table.AS(a.Table.Alias()) + aliasTable.Table.Schema(schemaName) return aliasTable } @@ -104,6 +113,15 @@ type {{.GoStructName}} struct { func (a *{{.GoStructName}}) AS(alias string) *{{.GoStructName}} { aliasTable := new{{.GoStructName}}() aliasTable.Table.AS(alias) + aliasTable.Table.Schema(a.Table.SchemaName()) + return aliasTable +} + +// Schema creates new {{.GoStructName}} with assigned schema name +func (a *{{.GoStructName}}) Schema(schemaName string) *{{.GoStructName}} { + aliasTable := new{{.GoStructName}}() + aliasTable.Table.AS(a.Table.Alias()) + aliasTable.Table.Schema(schemaName) return aliasTable } diff --git a/internal/jet/table.go b/internal/jet/table.go index 45d46e9a..68f5962d 100644 --- a/internal/jet/table.go +++ b/internal/jet/table.go @@ -16,6 +16,8 @@ type Table interface { SchemaName() string TableName() string AS(alias string) + Alias() string + Schema(schemaName string) } // NewTable creates new table with schema Name, table Name and list of columns @@ -67,13 +69,25 @@ func (t *tableImpl) columns() []Column { return ret } +func (t *tableImpl) Alias() string { + return t.alias +} + +func (t *tableImpl) Schema(schemaName string) { + t.schemaName = schemaName +} + func (t *tableImpl) serialize(statement StatementType, out *SQLBuilder, options ...SerializeOption) { if t == nil { panic("jet: tableImpl is nil") } - out.WriteIdentifier(t.schemaName) - out.WriteString(".") + // Use default schema if the schema name is not set + if len(t.schemaName) > 0 { + out.WriteIdentifier(t.schemaName) + out.WriteString(".") + } + out.WriteIdentifier(t.name) if len(t.alias) > 0 { @@ -145,6 +159,13 @@ func (t *joinTableImpl) columns() []Column { return ret } +func (t *joinTableImpl) Alias() string { + return "" +} + +func (t *joinTableImpl) Schema(schemaName string) { +} + func (t *joinTableImpl) serialize(statement StatementType, out *SQLBuilder, options ...SerializeOption) { if t == nil { panic("jet: Join table is nil. ") diff --git a/internal/jet/table_test.go b/internal/jet/table_test.go index 66646b2e..47923cdf 100644 --- a/internal/jet/table_test.go +++ b/internal/jet/table_test.go @@ -31,3 +31,38 @@ INNER JOIN schema.table2 ON ("intCol1" = "intCol2")`) require.Equal(t, joinTable.columns()[0].Name(), "intCol1") require.Equal(t, joinTable.columns()[1].Name(), "intCol2") } + +func TestSchemaNameSet(t *testing.T) { + newTable := NewTable("schema", "table") + newTable.Schema("foo") + newTable.AS("bar") + assertClauseSerialize(t, newTable, `foo.table AS bar`) +} + +func TestSchemaNameClear(t *testing.T) { + newTable := NewTable("schema", "table") + newTable.Schema("") + newTable.AS("bar") + assertClauseSerialize(t, newTable, `table AS bar`) +} + +func TestNewJoinTableSchemaNameSet(t *testing.T) { + newTable1 := NewTable("schema", "table", IntegerColumn("intCol1")) + newTable2 := NewTable("schema", "table2", IntegerColumn("intCol2")) + + newTable1.Schema("foo") + newTable2.Schema("foo") + + joinTable := NewJoinTable(newTable1, newTable2, InnerJoin, IntegerColumn("intCol1").EQ(IntegerColumn("intCol2"))) + joinTable.Schema("xxx") + + assertClauseSerialize(t, joinTable, `foo.table +INNER JOIN foo.table2 ON ("intCol1" = "intCol2")`) + + require.Equal(t, joinTable.SchemaName(), "foo") + require.Equal(t, joinTable.TableName(), "") + + require.Equal(t, len(joinTable.columns()), 2) + require.Equal(t, joinTable.columns()[0].Name(), "intCol1") + require.Equal(t, joinTable.columns()[1].Name(), "intCol2") +} From 1c435f5c7fb532a5178d516850c8cccd26b7153c Mon Sep 17 00:00:00 2001 From: Richard Date: Thu, 3 Sep 2020 13:25:23 +0200 Subject: [PATCH 03/41] export OrderByClause type through alias enables e.g. creating a collection of clauses beforehand and dynamically adding to it. this resolves #48. --- mysql/delete_statement.go | 4 ++-- mysql/select_statement.go | 4 ++-- mysql/set_statement.go | 4 ++-- mysql/types.go | 3 +++ postgres/select_statement.go | 4 ++-- postgres/set_statement.go | 4 ++-- postgres/types.go | 3 +++ 7 files changed, 16 insertions(+), 10 deletions(-) diff --git a/mysql/delete_statement.go b/mysql/delete_statement.go index 5857e168..7f0fe6f9 100644 --- a/mysql/delete_statement.go +++ b/mysql/delete_statement.go @@ -7,7 +7,7 @@ type DeleteStatement interface { Statement WHERE(expression BoolExpression) DeleteStatement - ORDER_BY(orderByClauses ...jet.OrderByClause) DeleteStatement + ORDER_BY(orderByClauses ...OrderByClause) DeleteStatement LIMIT(limit int64) DeleteStatement } @@ -38,7 +38,7 @@ func (d *deleteStatementImpl) WHERE(expression BoolExpression) DeleteStatement { return d } -func (d *deleteStatementImpl) ORDER_BY(orderByClauses ...jet.OrderByClause) DeleteStatement { +func (d *deleteStatementImpl) ORDER_BY(orderByClauses ...OrderByClause) DeleteStatement { d.OrderBy.List = orderByClauses return d } diff --git a/mysql/select_statement.go b/mysql/select_statement.go index 8882077c..37ac96b0 100644 --- a/mysql/select_statement.go +++ b/mysql/select_statement.go @@ -46,7 +46,7 @@ type SelectStatement interface { GROUP_BY(groupByClauses ...jet.GroupByClause) SelectStatement HAVING(boolExpression BoolExpression) SelectStatement WINDOW(name string) windowExpand - ORDER_BY(orderByClauses ...jet.OrderByClause) SelectStatement + ORDER_BY(orderByClauses ...OrderByClause) SelectStatement LIMIT(limit int64) SelectStatement OFFSET(offset int64) SelectStatement FOR(lock RowLock) SelectStatement @@ -128,7 +128,7 @@ func (s *selectStatementImpl) WINDOW(name string) windowExpand { 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 return s } diff --git a/mysql/set_statement.go b/mysql/set_statement.go index df1ed8a9..ec9f8fa9 100644 --- a/mysql/set_statement.go +++ b/mysql/set_statement.go @@ -17,7 +17,7 @@ func UNION_ALL(lhs, rhs jet.SerializerStatement, selects ...jet.SerializerStatem type setStatement interface { setOperators - ORDER_BY(orderByClauses ...jet.OrderByClause) setStatement + ORDER_BY(orderByClauses ...OrderByClause) setStatement LIMIT(limit int64) setStatement OFFSET(offset int64) setStatement @@ -70,7 +70,7 @@ func newSetStatementImpl(operator string, all bool, selects []jet.SerializerStat return newSetStatement } -func (s *setStatementImpl) ORDER_BY(orderByClauses ...jet.OrderByClause) setStatement { +func (s *setStatementImpl) ORDER_BY(orderByClauses ...OrderByClause) setStatement { s.setOperator.OrderBy.List = orderByClauses return s } diff --git a/mysql/types.go b/mysql/types.go index 9ebaa271..8c6608f8 100644 --- a/mysql/types.go +++ b/mysql/types.go @@ -17,5 +17,8 @@ type ColumnAssigment = jet.ColumnAssigment // PrintableStatement is a statement which sql query can be logged 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 var SetLogger = jet.SetLoggerFunc diff --git a/postgres/select_statement.go b/postgres/select_statement.go index 7b7e9694..b31909f3 100644 --- a/postgres/select_statement.go +++ b/postgres/select_statement.go @@ -49,7 +49,7 @@ type SelectStatement interface { GROUP_BY(groupByClauses ...jet.GroupByClause) SelectStatement HAVING(boolExpression BoolExpression) SelectStatement WINDOW(name string) windowExpand - ORDER_BY(orderByClauses ...jet.OrderByClause) SelectStatement + ORDER_BY(orderByClauses ...OrderByClause) SelectStatement LIMIT(limit int64) SelectStatement OFFSET(offset int64) SelectStatement FOR(lock RowLock) SelectStatement @@ -131,7 +131,7 @@ func (s *selectStatementImpl) WINDOW(name string) windowExpand { 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 return s } diff --git a/postgres/set_statement.go b/postgres/set_statement.go index 1f63f2e8..236f8de7 100644 --- a/postgres/set_statement.go +++ b/postgres/set_statement.go @@ -41,7 +41,7 @@ func EXCEPT_ALL(lhs, rhs jet.SerializerStatement) setStatement { type setStatement interface { setOperators - ORDER_BY(orderByClauses ...jet.OrderByClause) setStatement + ORDER_BY(orderByClauses ...OrderByClause) setStatement LIMIT(limit int64) setStatement OFFSET(offset int64) setStatement @@ -114,7 +114,7 @@ func newSetStatementImpl(operator string, all bool, selects []jet.SerializerStat return newSetStatement } -func (s *setStatementImpl) ORDER_BY(orderByClauses ...jet.OrderByClause) setStatement { +func (s *setStatementImpl) ORDER_BY(orderByClauses ...OrderByClause) setStatement { s.setOperator.OrderBy.List = orderByClauses return s } diff --git a/postgres/types.go b/postgres/types.go index e2a51eb9..05354b73 100644 --- a/postgres/types.go +++ b/postgres/types.go @@ -17,5 +17,8 @@ type ColumnAssigment = jet.ColumnAssigment // PrintableStatement is a statement which sql query can be logged 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 var SetLogger = jet.SetLoggerFunc From bc104d7dbbb8ad7c2108236b83615d86a961101d Mon Sep 17 00:00:00 2001 From: tolfino <39185647+tolfino@users.noreply.github.com> Date: Tue, 29 Sep 2020 15:29:35 -0700 Subject: [PATCH 04/41] Add support for custom functions. This allows expressions like jet.Func("FOO", String("test")) to be emitted as FOO($1). --- internal/jet/func_expression.go | 5 +++++ internal/jet/func_expression_test.go | 4 ++++ mysql/expressions.go | 3 +++ postgres/expressions.go | 3 +++ 4 files changed, 15 insertions(+) diff --git a/internal/jet/func_expression.go b/internal/jet/func_expression.go index d4eaa57f..606e7e1c 100644 --- a/internal/jet/func_expression.go +++ b/internal/jet/func_expression.go @@ -801,3 +801,8 @@ func newTimestampzFunc(name string, expressions ...Expression) *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) +} diff --git a/internal/jet/func_expression_test.go b/internal/jet/func_expression_test.go index c5a1e404..264be956 100644 --- a/internal/jet/func_expression_test.go +++ b/internal/jet/func_expression_test.go @@ -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") } + +func TestFunc(t *testing.T) { + assertClauseSerialize(t, Func("FOO", String("test"), NULL, MAX(Int(1))), "FOO($1, NULL, MAX($2))", "test", int64(1)) +} diff --git a/mysql/expressions.go b/mysql/expressions.go index 22921088..5c5810d8 100644 --- a/mysql/expressions.go +++ b/mysql/expressions.go @@ -74,5 +74,8 @@ var TimestampExp = jet.TimestampExp // For example: Raw("current_database()") var Raw = jet.Raw +// 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 var NewEnumValue = jet.NewEnumValue diff --git a/postgres/expressions.go b/postgres/expressions.go index 61c04495..57a33359 100644 --- a/postgres/expressions.go +++ b/postgres/expressions.go @@ -85,5 +85,8 @@ var TimestampzExp = jet.TimestampzExp // For example: Raw("current_database()") var Raw = jet.Raw +// 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 var NewEnumValue = jet.NewEnumValue From 92818acd5058fe1b95abf0c7055035df14e9fabb Mon Sep 17 00:00:00 2001 From: go-jet Date: Sun, 24 Jan 2021 16:47:06 +0100 Subject: [PATCH 05/41] Update circle-ci config --- .circleci/config.yml | 34 +++++++++----------------------- tests/mysql/generator_test.go | 7 +------ tests/mysql/main_test.go | 4 +--- tests/postgres/generator_test.go | 7 +------ 4 files changed, 12 insertions(+), 40 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index b1cb643b..43eae460 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -6,7 +6,7 @@ jobs: build-postgres-and-mysql: docker: # specify the version - - image: circleci/golang:1.11 + - image: circleci/golang:1.13 - image: circleci/postgres:10.8-alpine environment: # environment variables for primary container @@ -14,7 +14,7 @@ jobs: POSTGRES_PASSWORD: jet POSTGRES_DB: jetdb - - image: circleci/mysql:8.0 + - image: circleci/mysql:8.0.16 command: [--default-authentication-plugin=mysql_native_password] environment: MYSQL_ROOT_PASSWORD: jet @@ -41,17 +41,9 @@ jobs: - run: name: Install dependencies command: | - go get github.com/google/uuid - 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 + cd /go/src/github.com/go-jet/jet go get github.com/jstemmer/go-junit-report - - go install github.com/go-jet/jet/cmd/jet + go build -o /home/circleci/.local/bin/jet ./cmd/jet/ - run: name: Waiting for Postgres to be ready @@ -95,7 +87,7 @@ jobs: - 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: name: Upload code coverage @@ -110,7 +102,7 @@ jobs: build-mariadb: docker: # specify the version - - image: circleci/golang:1.11 + - image: circleci/golang:1.13 - image: circleci/mariadb:10.3 command: [--default-authentication-plugin=mysql_native_password] @@ -138,17 +130,9 @@ jobs: - run: name: Install dependencies command: | - go get github.com/google/uuid - 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 + cd /go/src/github.com/go-jet/jet go get github.com/jstemmer/go-junit-report - - go install github.com/go-jet/jet/cmd/jet + go build -o /home/circleci/.local/bin/jet ./cmd/jet/ - run: name: Install MySQL CLI; @@ -172,7 +156,7 @@ jobs: - run: name: Run MariaDB tests command: | - go test -v ./tests/mysql/ -source=MariaDB + MY_SQL_SOURCE=MariaDB go test -v ./tests/mysql/ workflows: version: 2 diff --git a/tests/mysql/generator_test.go b/tests/mysql/generator_test.go index 2cc471e0..1e9a51da 100644 --- a/tests/mysql/generator_test.go +++ b/tests/mysql/generator_test.go @@ -35,12 +35,7 @@ func TestGenerator(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") - goInstallJet.Stderr = os.Stderr - err := goInstallJet.Run() - require.NoError(t, err) - - err = os.RemoveAll(genTestDir3) + err := os.RemoveAll(genTestDir3) require.NoError(t, err) cmd := exec.Command("jet", "-source=MySQL", "-dbname=dvds", "-host=localhost", "-port=3306", diff --git a/tests/mysql/main_test.go b/tests/mysql/main_test.go index 5790ef62..4f9268cf 100644 --- a/tests/mysql/main_test.go +++ b/tests/mysql/main_test.go @@ -3,7 +3,6 @@ package mysql import ( "context" "database/sql" - "flag" jetmysql "github.com/go-jet/jet/v2/mysql" "github.com/go-jet/jet/v2/postgres" "github.com/go-jet/jet/v2/tests/dbconfig" @@ -25,8 +24,7 @@ var source string const MariaDB = "MariaDB" func init() { - flag.StringVar(&source, "source", "", "MySQL or MariaDB") - flag.Parse() + source = os.Getenv("MY_SQL_SOURCE") } func sourceIsMariaDB() bool { diff --git a/tests/postgres/generator_test.go b/tests/postgres/generator_test.go index 50e7223c..1e23016f 100644 --- a/tests/postgres/generator_test.go +++ b/tests/postgres/generator_test.go @@ -46,12 +46,7 @@ func TestGeneratedModel(t *testing.T) { const genTestDir2 = "./.gentestdata2" func TestCmdGenerator(t *testing.T) { - goInstallJet := exec.Command("sh", "-c", "cd $GOPATH/src/ && GO111MODULE=off go get github.com/go-jet/jet/cmd/jet") - goInstallJet.Stderr = os.Stderr - err := goInstallJet.Run() - require.NoError(t, err) - - err = os.RemoveAll(genTestDir2) + err := os.RemoveAll(genTestDir2) require.NoError(t, err) cmd := exec.Command("jet", "-source=PostgreSQL", "-dbname=jetdb", "-host=localhost", "-port=5432", From 6b06bc6a3773b0a505bb09f6fa50c928f863867d Mon Sep 17 00:00:00 2001 From: DevDevious Date: Mon, 22 Feb 2021 13:58:28 -0500 Subject: [PATCH 06/41] Added unsigned integer literals. --- internal/jet/literal_expression.go | 43 ++++++++++++++++++++++++++++-- mysql/expressions.go | 2 +- mysql/literal.go | 26 +++++++++++++++++- mysql/literal_test.go | 41 ++++++++++++++++++++++++++++ postgres/literal.go | 26 +++++++++++++++++- postgres/literal_test.go | 41 ++++++++++++++++++++++++++++ 6 files changed, 174 insertions(+), 5 deletions(-) diff --git a/internal/jet/literal_expression.go b/internal/jet/literal_expression.go index 8cdb3d74..aed31b06 100644 --- a/internal/jet/literal_expression.go +++ b/internal/jet/literal_expression.go @@ -67,8 +67,7 @@ type integerLiteralExpression struct { integerInterfaceImpl } -// Int creates new integer literal -func Int(value int64) IntegerExpression { +func intLiteral(value interface{}) IntegerExpression { numLiteral := &integerLiteralExpression{} numLiteral.literalExpressionImpl = *literal(value) @@ -79,6 +78,46 @@ func Int(value int64) IntegerExpression { 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 { boolInterfaceImpl diff --git a/mysql/expressions.go b/mysql/expressions.go index 22921088..5435580a 100644 --- a/mysql/expressions.go +++ b/mysql/expressions.go @@ -3,7 +3,7 @@ package mysql import "github.com/go-jet/jet/v2/internal/jet" // 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 // BoolExpression interface diff --git a/mysql/literal.go b/mysql/literal.go index 74a9f240..7523d55e 100644 --- a/mysql/literal.go +++ b/mysql/literal.go @@ -15,9 +15,33 @@ var ( // Bool creates new bool literal expression 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 +// 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 var Float = jet.Float diff --git a/mysql/literal_test.go b/mysql/literal_test.go index 09d331e7..fb966417 100644 --- a/mysql/literal_test.go +++ b/mysql/literal_test.go @@ -1,6 +1,7 @@ package mysql import ( + "math" "testing" "time" ) @@ -13,6 +14,46 @@ func TestInt(t *testing.T) { 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) { assertSerialize(t, Float(12.34), `?`, float64(12.34)) } diff --git a/postgres/literal.go b/postgres/literal.go index 0063cf45..a0b0224e 100644 --- a/postgres/literal.go +++ b/postgres/literal.go @@ -8,9 +8,33 @@ import ( // Bool creates new bool literal expression var Bool = jet.Bool -// Int creates new integer literal expression +// Int is constructor for 64 bit signed integer expressions literals. 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 var Float = jet.Float diff --git a/postgres/literal_test.go b/postgres/literal_test.go index 5206aaa1..197fff72 100644 --- a/postgres/literal_test.go +++ b/postgres/literal_test.go @@ -1,6 +1,7 @@ package postgres import ( + "math" "testing" "time" ) @@ -13,6 +14,46 @@ func TestInt(t *testing.T) { 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) { assertSerialize(t, Float(12.34), `$1`, float64(12.34)) } From 2fb93a0bdb5d16809a91cfa74d74f1bd2697975a Mon Sep 17 00:00:00 2001 From: go-jet Date: Sun, 24 Jan 2021 16:47:06 +0100 Subject: [PATCH 07/41] Update circle-ci config --- .circleci/config.yml | 34 +++++++++----------------------- tests/mysql/generator_test.go | 7 +------ tests/mysql/main_test.go | 4 +--- tests/postgres/generator_test.go | 7 +------ 4 files changed, 12 insertions(+), 40 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index b1cb643b..43eae460 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -6,7 +6,7 @@ jobs: build-postgres-and-mysql: docker: # specify the version - - image: circleci/golang:1.11 + - image: circleci/golang:1.13 - image: circleci/postgres:10.8-alpine environment: # environment variables for primary container @@ -14,7 +14,7 @@ jobs: POSTGRES_PASSWORD: jet POSTGRES_DB: jetdb - - image: circleci/mysql:8.0 + - image: circleci/mysql:8.0.16 command: [--default-authentication-plugin=mysql_native_password] environment: MYSQL_ROOT_PASSWORD: jet @@ -41,17 +41,9 @@ jobs: - run: name: Install dependencies command: | - go get github.com/google/uuid - 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 + cd /go/src/github.com/go-jet/jet go get github.com/jstemmer/go-junit-report - - go install github.com/go-jet/jet/cmd/jet + go build -o /home/circleci/.local/bin/jet ./cmd/jet/ - run: name: Waiting for Postgres to be ready @@ -95,7 +87,7 @@ jobs: - 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: name: Upload code coverage @@ -110,7 +102,7 @@ jobs: build-mariadb: docker: # specify the version - - image: circleci/golang:1.11 + - image: circleci/golang:1.13 - image: circleci/mariadb:10.3 command: [--default-authentication-plugin=mysql_native_password] @@ -138,17 +130,9 @@ jobs: - run: name: Install dependencies command: | - go get github.com/google/uuid - 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 + cd /go/src/github.com/go-jet/jet go get github.com/jstemmer/go-junit-report - - go install github.com/go-jet/jet/cmd/jet + go build -o /home/circleci/.local/bin/jet ./cmd/jet/ - run: name: Install MySQL CLI; @@ -172,7 +156,7 @@ jobs: - run: name: Run MariaDB tests command: | - go test -v ./tests/mysql/ -source=MariaDB + MY_SQL_SOURCE=MariaDB go test -v ./tests/mysql/ workflows: version: 2 diff --git a/tests/mysql/generator_test.go b/tests/mysql/generator_test.go index 2cc471e0..1e9a51da 100644 --- a/tests/mysql/generator_test.go +++ b/tests/mysql/generator_test.go @@ -35,12 +35,7 @@ func TestGenerator(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") - goInstallJet.Stderr = os.Stderr - err := goInstallJet.Run() - require.NoError(t, err) - - err = os.RemoveAll(genTestDir3) + err := os.RemoveAll(genTestDir3) require.NoError(t, err) cmd := exec.Command("jet", "-source=MySQL", "-dbname=dvds", "-host=localhost", "-port=3306", diff --git a/tests/mysql/main_test.go b/tests/mysql/main_test.go index 5790ef62..4f9268cf 100644 --- a/tests/mysql/main_test.go +++ b/tests/mysql/main_test.go @@ -3,7 +3,6 @@ package mysql import ( "context" "database/sql" - "flag" jetmysql "github.com/go-jet/jet/v2/mysql" "github.com/go-jet/jet/v2/postgres" "github.com/go-jet/jet/v2/tests/dbconfig" @@ -25,8 +24,7 @@ var source string const MariaDB = "MariaDB" func init() { - flag.StringVar(&source, "source", "", "MySQL or MariaDB") - flag.Parse() + source = os.Getenv("MY_SQL_SOURCE") } func sourceIsMariaDB() bool { diff --git a/tests/postgres/generator_test.go b/tests/postgres/generator_test.go index 50e7223c..1e23016f 100644 --- a/tests/postgres/generator_test.go +++ b/tests/postgres/generator_test.go @@ -46,12 +46,7 @@ func TestGeneratedModel(t *testing.T) { const genTestDir2 = "./.gentestdata2" func TestCmdGenerator(t *testing.T) { - goInstallJet := exec.Command("sh", "-c", "cd $GOPATH/src/ && GO111MODULE=off go get github.com/go-jet/jet/cmd/jet") - goInstallJet.Stderr = os.Stderr - err := goInstallJet.Run() - require.NoError(t, err) - - err = os.RemoveAll(genTestDir2) + err := os.RemoveAll(genTestDir2) require.NoError(t, err) cmd := exec.Command("jet", "-source=PostgreSQL", "-dbname=jetdb", "-host=localhost", "-port=5432", From 5b4a1ebd63f3ca0c407bc26d15784947f90eccc0 Mon Sep 17 00:00:00 2001 From: DevDevious Date: Mon, 22 Feb 2021 13:58:28 -0500 Subject: [PATCH 08/41] Added unsigned integer literals. --- internal/jet/literal_expression.go | 43 ++++++++++++++++++++++++++++-- mysql/expressions.go | 2 +- mysql/literal.go | 26 +++++++++++++++++- mysql/literal_test.go | 41 ++++++++++++++++++++++++++++ postgres/literal.go | 26 +++++++++++++++++- postgres/literal_test.go | 41 ++++++++++++++++++++++++++++ 6 files changed, 174 insertions(+), 5 deletions(-) diff --git a/internal/jet/literal_expression.go b/internal/jet/literal_expression.go index 8cdb3d74..aed31b06 100644 --- a/internal/jet/literal_expression.go +++ b/internal/jet/literal_expression.go @@ -67,8 +67,7 @@ type integerLiteralExpression struct { integerInterfaceImpl } -// Int creates new integer literal -func Int(value int64) IntegerExpression { +func intLiteral(value interface{}) IntegerExpression { numLiteral := &integerLiteralExpression{} numLiteral.literalExpressionImpl = *literal(value) @@ -79,6 +78,46 @@ func Int(value int64) IntegerExpression { 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 { boolInterfaceImpl diff --git a/mysql/expressions.go b/mysql/expressions.go index 5c5810d8..3ee660d6 100644 --- a/mysql/expressions.go +++ b/mysql/expressions.go @@ -3,7 +3,7 @@ package mysql import "github.com/go-jet/jet/v2/internal/jet" // 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 // BoolExpression interface diff --git a/mysql/literal.go b/mysql/literal.go index 74a9f240..7523d55e 100644 --- a/mysql/literal.go +++ b/mysql/literal.go @@ -15,9 +15,33 @@ var ( // Bool creates new bool literal expression 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 +// 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 var Float = jet.Float diff --git a/mysql/literal_test.go b/mysql/literal_test.go index 09d331e7..fb966417 100644 --- a/mysql/literal_test.go +++ b/mysql/literal_test.go @@ -1,6 +1,7 @@ package mysql import ( + "math" "testing" "time" ) @@ -13,6 +14,46 @@ func TestInt(t *testing.T) { 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) { assertSerialize(t, Float(12.34), `?`, float64(12.34)) } diff --git a/postgres/literal.go b/postgres/literal.go index 0063cf45..a0b0224e 100644 --- a/postgres/literal.go +++ b/postgres/literal.go @@ -8,9 +8,33 @@ import ( // Bool creates new bool literal expression var Bool = jet.Bool -// Int creates new integer literal expression +// Int is constructor for 64 bit signed integer expressions literals. 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 var Float = jet.Float diff --git a/postgres/literal_test.go b/postgres/literal_test.go index 5206aaa1..197fff72 100644 --- a/postgres/literal_test.go +++ b/postgres/literal_test.go @@ -1,6 +1,7 @@ package postgres import ( + "math" "testing" "time" ) @@ -13,6 +14,46 @@ func TestInt(t *testing.T) { 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) { assertSerialize(t, Float(12.34), `$1`, float64(12.34)) } From 753d3bac9a9c13a46e01db1402e85cb34202d6b5 Mon Sep 17 00:00:00 2001 From: go-jet Date: Sun, 21 Mar 2021 17:14:15 +0100 Subject: [PATCH 09/41] Remove methods from Table interface that affects receiver object Modifying SQL builder receiver object can produce unwanted side effects. --- internal/jet/table.go | 28 +++++++++----------------- internal/jet/table_test.go | 41 +++----------------------------------- internal/jet/testutils.go | 6 +++--- 3 files changed, 15 insertions(+), 60 deletions(-) diff --git a/internal/jet/table.go b/internal/jet/table.go index 68f5962d..78abb990 100644 --- a/internal/jet/table.go +++ b/internal/jet/table.go @@ -15,22 +15,27 @@ type Table interface { columns() []Column SchemaName() string TableName() string - AS(alias string) Alias() string - Schema(schemaName string) } // 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{ schemaName: schemaName, name: name, + alias: alias, columnList: columns, } + columnTableName := name + + if alias != "" { + columnTableName = alias + } + for _, c := range columns { - c.setTableName(name) + c.setTableName(columnTableName) } return &t @@ -43,14 +48,6 @@ type tableImpl struct { columnList []ColumnExpression } -func (t *tableImpl) AS(alias string) { - t.alias = alias - - for _, c := range t.columnList { - c.setTableName(alias) - } -} - func (t *tableImpl) SchemaName() string { return t.schemaName } @@ -73,10 +70,6 @@ func (t *tableImpl) Alias() string { return t.alias } -func (t *tableImpl) Schema(schemaName string) { - t.schemaName = schemaName -} - func (t *tableImpl) serialize(statement StatementType, out *SQLBuilder, options ...SerializeOption) { if t == nil { panic("jet: tableImpl is nil") @@ -163,9 +156,6 @@ func (t *joinTableImpl) Alias() string { return "" } -func (t *joinTableImpl) Schema(schemaName string) { -} - func (t *joinTableImpl) serialize(statement StatementType, out *SQLBuilder, options ...SerializeOption) { if t == nil { panic("jet: Join table is nil. ") diff --git a/internal/jet/table_test.go b/internal/jet/table_test.go index 47923cdf..b880784f 100644 --- a/internal/jet/table_test.go +++ b/internal/jet/table_test.go @@ -6,7 +6,7 @@ import ( ) 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.TableName(), "table") @@ -16,8 +16,8 @@ func TestNewTable(t *testing.T) { } func TestNewJoinTable(t *testing.T) { - newTable1 := NewTable("schema", "table", IntegerColumn("intCol1")) - newTable2 := NewTable("schema", "table2", IntegerColumn("intCol2")) + newTable1 := NewTable("schema", "table", "", IntegerColumn("intCol1")) + newTable2 := NewTable("schema", "table2", "", IntegerColumn("intCol2")) joinTable := NewJoinTable(newTable1, newTable2, InnerJoin, IntegerColumn("intCol1").EQ(IntegerColumn("intCol2"))) @@ -31,38 +31,3 @@ INNER JOIN schema.table2 ON ("intCol1" = "intCol2")`) require.Equal(t, joinTable.columns()[0].Name(), "intCol1") require.Equal(t, joinTable.columns()[1].Name(), "intCol2") } - -func TestSchemaNameSet(t *testing.T) { - newTable := NewTable("schema", "table") - newTable.Schema("foo") - newTable.AS("bar") - assertClauseSerialize(t, newTable, `foo.table AS bar`) -} - -func TestSchemaNameClear(t *testing.T) { - newTable := NewTable("schema", "table") - newTable.Schema("") - newTable.AS("bar") - assertClauseSerialize(t, newTable, `table AS bar`) -} - -func TestNewJoinTableSchemaNameSet(t *testing.T) { - newTable1 := NewTable("schema", "table", IntegerColumn("intCol1")) - newTable2 := NewTable("schema", "table2", IntegerColumn("intCol2")) - - newTable1.Schema("foo") - newTable2.Schema("foo") - - joinTable := NewJoinTable(newTable1, newTable2, InnerJoin, IntegerColumn("intCol1").EQ(IntegerColumn("intCol2"))) - joinTable.Schema("xxx") - - assertClauseSerialize(t, joinTable, `foo.table -INNER JOIN foo.table2 ON ("intCol1" = "intCol2")`) - - require.Equal(t, joinTable.SchemaName(), "foo") - require.Equal(t, joinTable.TableName(), "") - - require.Equal(t, len(joinTable.columns()), 2) - require.Equal(t, joinTable.columns()[0].Name(), "intCol1") - require.Equal(t, joinTable.columns()[1].Name(), "intCol2") -} diff --git a/internal/jet/testutils.go b/internal/jet/testutils.go index 268ae065..43f1f5d6 100644 --- a/internal/jet/testutils.go +++ b/internal/jet/testutils.go @@ -26,7 +26,7 @@ var ( table1ColBool = BoolColumn("col_bool") 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 ( table2Col3 = IntegerColumn("col3") @@ -41,14 +41,14 @@ var ( table2ColTimestampz = TimestampzColumn("col_timestampz") 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 ( table3Col1 = IntegerColumn("col1") table3ColInt = IntegerColumn("col_int") 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{}) { out := SQLBuilder{Dialect: defaultDialect} From b375733dfa6cc82bc0111fbf5ec2c16fb35a0dc6 Mon Sep 17 00:00:00 2001 From: go-jet Date: Sun, 21 Mar 2021 17:17:44 +0100 Subject: [PATCH 10/41] Add schema rename support Using SchemaFrom("schemaName") it is possible to set SQL builder table to point to a different schema. --- generator/internal/template/templates.go | 46 ++++++---------- mysql/table.go | 4 +- mysql/utils_test.go | 34 ++---------- postgres/dialect_test.go | 8 +-- postgres/table.go | 4 +- postgres/utils_test.go | 25 ++------- tests/init/init.go | 2 + tests/mysql/alltypes_test.go | 7 ++- tests/mysql/generator_test.go | 34 +++++++----- tests/mysql/select_test.go | 43 +++++++++++++++ tests/mysql/update_test.go | 3 +- tests/postgres/chinook_db_test.go | 68 +++++++++++++++++++++-- tests/postgres/generator_test.go | 69 +++++++++++++----------- tests/postgres/sample_test.go | 2 +- tests/postgres/scan_test.go | 3 +- tests/postgres/select_test.go | 5 +- tests/testdata | 2 +- 17 files changed, 206 insertions(+), 153 deletions(-) diff --git a/generator/internal/template/templates.go b/generator/internal/template/templates.go index 5288ef40..186ade02 100644 --- a/generator/internal/template/templates.go +++ b/generator/internal/template/templates.go @@ -23,7 +23,7 @@ import ( "github.com/go-jet/jet/v2/{{dialect.PackageName}}" ) -var {{ToGoIdentifier .Name}} = new{{.GoStructName}}() +var {{ToGoIdentifier .Name}} = new{{.GoStructName}}("{{.SchemaName}}", "{{.Name}}", "") type {{.GoStructName}} struct { {{dialect.PackageName}}.Table @@ -38,22 +38,16 @@ type {{.GoStructName}} struct { } // AS creates new {{.GoStructName}} with assigned alias -func (a *{{.GoStructName}}) AS(alias string) {{.GoStructName}} { - aliasTable := new{{.GoStructName}}() - aliasTable.Table.AS(alias) - aliasTable.Table.Schema(a.Table.SchemaName()) - return aliasTable +func (a {{.GoStructName}}) AS(alias string) {{.GoStructName}} { + return new{{.GoStructName}}(a.SchemaName(), a.TableName(), alias) } // Schema creates new {{.GoStructName}} with assigned schema name -func (a *{{.GoStructName}}) Schema(schemaName string) {{.GoStructName}} { - aliasTable := new{{.GoStructName}}() - aliasTable.Table.AS(a.Table.Alias()) - aliasTable.Table.Schema(schemaName) - return aliasTable +func (a {{.GoStructName}}) FromSchema(schemaName string) {{.GoStructName}} { + return new{{.GoStructName}}(schemaName, a.TableName(), a.Alias()) } -func new{{.GoStructName}}() {{.GoStructName}} { +func new{{.GoStructName}}(schemaName, tableName, alias string) {{.GoStructName}} { var ( {{- range .Columns}} {{ToGoIdentifier .Name}}Column = {{dialect.PackageName}}.{{.SqlBuilderColumnType}}Column("{{.Name}}") @@ -63,7 +57,7 @@ func new{{.GoStructName}}() {{.GoStructName}} { ) return {{.GoStructName}}{ - Table: {{dialect.PackageName}}.NewTable("{{.SchemaName}}", "{{.Name}}", allColumns...), + Table: {{dialect.PackageName}}.NewTable(schemaName, tableName, alias, allColumns...), //Columns {{- range .Columns}} @@ -89,7 +83,7 @@ import ( "github.com/go-jet/jet/v2/{{dialect.PackageName}}" ) -var {{ToGoIdentifier .Name}} = new{{.GoStructName}}() +var {{ToGoIdentifier .Name}} = new{{.GoStructName}}("{{.SchemaName}}", "{{.Name}}", "") type {{.GoStructImplName}} struct { {{dialect.PackageName}}.Table @@ -110,29 +104,23 @@ type {{.GoStructName}} struct { } // AS creates new {{.GoStructName}} with assigned alias -func (a *{{.GoStructName}}) AS(alias string) *{{.GoStructName}} { - aliasTable := new{{.GoStructName}}() - aliasTable.Table.AS(alias) - aliasTable.Table.Schema(a.Table.SchemaName()) - return aliasTable +func (a {{.GoStructName}}) AS(alias string) *{{.GoStructName}} { + return new{{.GoStructName}}(a.SchemaName(), a.TableName(), alias) } // Schema creates new {{.GoStructName}} with assigned schema name -func (a *{{.GoStructName}}) Schema(schemaName string) *{{.GoStructName}} { - aliasTable := new{{.GoStructName}}() - aliasTable.Table.AS(a.Table.Alias()) - aliasTable.Table.Schema(schemaName) - return aliasTable +func (a {{.GoStructName}}) FromSchema(schemaName string) *{{.GoStructName}} { + return new{{.GoStructName}}(schemaName, a.TableName(), a.Alias()) } -func new{{.GoStructName}}() *{{.GoStructName}} { +func new{{.GoStructName}}(schemaName, tableName, alias string) *{{.GoStructName}} { return &{{.GoStructName}}{ - {{.GoStructImplName}}: new{{.GoStructName}}Impl("{{.SchemaName}}", "{{.Name}}"), - EXCLUDED: new{{.GoStructName}}Impl("", "excluded"), + {{.GoStructImplName}}: new{{.GoStructName}}Impl(schemaName, tableName, alias), + EXCLUDED: new{{.GoStructName}}Impl("", "excluded", ""), } } -func new{{.GoStructName}}Impl(schemaName, tableName string) {{.GoStructImplName}} { +func new{{.GoStructName}}Impl(schemaName, tableName, alias string) {{.GoStructImplName}} { var ( {{- range .Columns}} {{ToGoIdentifier .Name}}Column = {{dialect.PackageName}}.{{.SqlBuilderColumnType}}Column("{{.Name}}") @@ -142,7 +130,7 @@ func new{{.GoStructName}}Impl(schemaName, tableName string) {{.GoStructImplName} ) return {{.GoStructImplName}}{ - Table: {{dialect.PackageName}}.NewTable(schemaName, tableName, allColumns...), + Table: {{dialect.PackageName}}.NewTable(schemaName, tableName, alias, allColumns...), //Columns {{- range .Columns}} diff --git a/mysql/table.go b/mysql/table.go index 5630966b..0ae7ee8e 100644 --- a/mysql/table.go +++ b/mysql/table.go @@ -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 -func NewTable(schemaName, name string, columns ...jet.ColumnExpression) Table { +func NewTable(schemaName, name, alias string, columns ...jet.ColumnExpression) Table { t := &tableImpl{ - SerializerTable: jet.NewTable(schemaName, name, columns...), + SerializerTable: jet.NewTable(schemaName, name, alias, columns...), } t.readableTableInterfaceImpl.parent = t diff --git a/mysql/utils_test.go b/mysql/utils_test.go index cc8b3c38..d95638a8 100644 --- a/mysql/utils_test.go +++ b/mysql/utils_test.go @@ -16,19 +16,7 @@ var table1ColTimestamp = TimestampColumn("col_timestamp") var table1ColDate = DateColumn("col_date") var table1ColTime = TimeColumn("col_time") -var table1 = NewTable( - "db", - "table1", - table1Col1, - table1ColInt, - table1ColFloat, - table1ColString, - table1Col3, - table1ColBool, - table1ColDate, - table1ColTimestamp, - table1ColTime, -) +var table1 = NewTable("db", "table1", "", table1Col1, table1ColInt, table1ColFloat, table1ColString, table1Col3, table1ColBool, table1ColDate, table1ColTimestamp, table1ColTime) var table2Col3 = IntegerColumn("col3") var table2Col4 = IntegerColumn("col4") @@ -39,28 +27,12 @@ var table2ColBool = BoolColumn("col_bool") var table2ColTimestamp = TimestampColumn("col_timestamp") var table2ColDate = DateColumn("col_date") -var table2 = NewTable( - "db", - "table2", - table2Col3, - table2Col4, - table2ColInt, - table2ColFloat, - table2ColStr, - table2ColBool, - table2ColDate, - table2ColTimestamp, -) +var table2 = NewTable("db", "table2", "", table2Col3, table2Col4, table2ColInt, table2ColFloat, table2ColStr, table2ColBool, table2ColDate, table2ColTimestamp) var table3Col1 = IntegerColumn("col1") var table3ColInt = IntegerColumn("col_int") var table3StrCol = StringColumn("col2") -var table3 = NewTable( - "db", - "table3", - table3Col1, - table3ColInt, - table3StrCol) +var table3 = NewTable("db", "table3", "", table3Col1, table3ColInt, table3StrCol) func assertSerialize(t *testing.T, clause jet.Serializer, query string, args ...interface{}) { testutils.AssertSerialize(t, Dialect, clause, query, args...) diff --git a/postgres/dialect_test.go b/postgres/dialect_test.go index 7bf92425..9b7b3d16 100644 --- a/postgres/dialect_test.go +++ b/postgres/dialect_test.go @@ -80,13 +80,7 @@ func TestReservedWordEscaped(t *testing.T) { var table1ColVariadic = IntervalColumn("VARIADIC") var table1ColProcedure = IntervalColumn("procedure") - _ = NewTable( - "db", - "table1", - table1ColUser, - table1ColVariadic, - table1ColProcedure, - ) + _ = NewTable("db", "table1", "", table1ColUser, table1ColVariadic, table1ColProcedure) assertSerialize(t, table1ColUser, `table1."user"`) assertSerialize(t, table1ColVariadic, `table1."VARIADIC"`) diff --git a/postgres/table.go b/postgres/table.go index ac153d30..f90c114b 100644 --- a/postgres/table.go +++ b/postgres/table.go @@ -109,10 +109,10 @@ type tableImpl struct { } // 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{ - SerializerTable: jet.NewTable(schemaName, name, columns...), + SerializerTable: jet.NewTable(schemaName, name, alias, columns...), } t.readableTableInterfaceImpl.parent = t diff --git a/postgres/utils_test.go b/postgres/utils_test.go index ef5f72e6..bd59b432 100644 --- a/postgres/utils_test.go +++ b/postgres/utils_test.go @@ -21,6 +21,7 @@ var table1ColInterval = IntervalColumn("col_interval") var table1 = NewTable( "db", "table1", + "", table1Col1, table1ColInt, table1ColFloat, @@ -46,32 +47,12 @@ var table2ColTimestampz = TimestampzColumn("col_timestampz") var table2ColDate = DateColumn("col_date") var table2ColInterval = IntervalColumn("col_interval") -var table2 = NewTable( - "db", - "table2", - table2Col3, - table2Col4, - table2ColInt, - table2ColFloat, - table2ColStr, - table2ColBool, - table2ColTime, - table2ColTimez, - table2ColDate, - table2ColTimestamp, - table2ColTimestampz, - table2ColInterval, -) +var table2 = NewTable("db", "table2", "", table2Col3, table2Col4, table2ColInt, table2ColFloat, table2ColStr, table2ColBool, table2ColTime, table2ColTimez, table2ColDate, table2ColTimestamp, table2ColTimestampz, table2ColInterval) var table3Col1 = IntegerColumn("col1") var table3ColInt = IntegerColumn("col_int") var table3StrCol = StringColumn("col2") -var table3 = NewTable( - "db", - "table3", - table3Col1, - table3ColInt, - table3StrCol) +var table3 = NewTable("db", "table3", "", table3Col1, table3ColInt, table3StrCol) func assertSerialize(t *testing.T, serializer jet.Serializer, query string, args ...interface{}) { testutils.AssertSerialize(t, Dialect, serializer, query, args...) diff --git a/tests/init/init.go b/tests/init/init.go index 356806d3..a2f6eb39 100644 --- a/tests/init/init.go +++ b/tests/init/init.go @@ -46,6 +46,7 @@ func initMySQLDB() { mySQLDBs := []string{ "dvds", + "dvds2", "test_sample", } @@ -89,6 +90,7 @@ func initPostgresDB() { "dvds", "test_sample", "chinook", + "chinook2", "northwind", } diff --git a/tests/mysql/alltypes_test.go b/tests/mysql/alltypes_test.go index 57287258..6fbb1364 100644 --- a/tests/mysql/alltypes_test.go +++ b/tests/mysql/alltypes_test.go @@ -1,7 +1,6 @@ package mysql import ( - "fmt" "github.com/stretchr/testify/require" "strings" "testing" @@ -974,7 +973,7 @@ func TestAllTypesInsert(t *testing.T) { stmt := AllTypes.INSERT(AllTypes.AllColumns). MODEL(toInsert) - fmt.Println(stmt.DebugSql()) + //fmt.Println(stmt.DebugSql()) testutils.AssertExec(t, stmt, tx, 1) @@ -1028,7 +1027,7 @@ func TestAllTypesInsertOnDuplicateKeyUpdate(t *testing.T) { AllTypes.Date.SET(DateT(time.Now())), ) - fmt.Println(stmt.DebugSql()) + //fmt.Println(stmt.DebugSql()) _, err = stmt.Exec(tx) require.NoError(t, err) @@ -1257,7 +1256,7 @@ FROM test_sample.user; err := stmt.Query(db, &dest) require.NoError(t, err) - testutils.PrintJson(dest) + //testutils.PrintJson(dest) testutils.AssertJSON(t, dest, ` [ diff --git a/tests/mysql/generator_test.go b/tests/mysql/generator_test.go index 1e9a51da..c9dcc1a6 100644 --- a/tests/mysql/generator_test.go +++ b/tests/mysql/generator_test.go @@ -135,7 +135,7 @@ import ( "github.com/go-jet/jet/v2/mysql" ) -var Actor = newActorTable() +var Actor = newActorTable("dvds", "actor", "") type ActorTable struct { mysql.Table @@ -151,13 +151,16 @@ type ActorTable struct { } // AS creates new ActorTable with assigned alias -func (a *ActorTable) AS(alias string) ActorTable { - aliasTable := newActorTable() - aliasTable.Table.AS(alias) - return aliasTable +func (a ActorTable) AS(alias string) ActorTable { + return newActorTable(a.SchemaName(), a.TableName(), alias) } -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 ( ActorIDColumn = mysql.IntegerColumn("actor_id") FirstNameColumn = mysql.StringColumn("first_name") @@ -168,7 +171,7 @@ func newActorTable() ActorTable { ) return ActorTable{ - Table: mysql.NewTable("dvds", "actor", allColumns...), + Table: mysql.NewTable(schemaName, tableName, alias, allColumns...), //Columns ActorID: ActorIDColumn, @@ -218,7 +221,7 @@ import ( "github.com/go-jet/jet/v2/mysql" ) -var ActorInfo = newActorInfoTable() +var ActorInfo = newActorInfoTable("dvds", "actor_info", "") type ActorInfoTable struct { mysql.Table @@ -234,13 +237,16 @@ type ActorInfoTable struct { } // AS creates new ActorInfoTable with assigned alias -func (a *ActorInfoTable) AS(alias string) ActorInfoTable { - aliasTable := newActorInfoTable() - aliasTable.Table.AS(alias) - return aliasTable +func (a ActorInfoTable) AS(alias string) ActorInfoTable { + return newActorInfoTable(a.SchemaName(), a.TableName(), alias) +} + +// Schema creates new ActorInfoTable with assigned schema name +func (a ActorInfoTable) FromSchema(schemaName string) ActorInfoTable { + return newActorInfoTable(schemaName, a.TableName(), a.Alias()) } -func newActorInfoTable() ActorInfoTable { +func newActorInfoTable(schemaName, tableName, alias string) ActorInfoTable { var ( ActorIDColumn = mysql.IntegerColumn("actor_id") FirstNameColumn = mysql.StringColumn("first_name") @@ -251,7 +257,7 @@ func newActorInfoTable() ActorInfoTable { ) return ActorInfoTable{ - Table: mysql.NewTable("dvds", "actor_info", allColumns...), + Table: mysql.NewTable(schemaName, tableName, alias, allColumns...), //Columns ActorID: ActorIDColumn, diff --git a/tests/mysql/select_test.go b/tests/mysql/select_test.go index dc6af0d2..f447218e 100644 --- a/tests/mysql/select_test.go +++ b/tests/mysql/select_test.go @@ -744,3 +744,46 @@ LIMIT 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") +} diff --git a/tests/mysql/update_test.go b/tests/mysql/update_test.go index bef22f97..3b6aa759 100644 --- a/tests/mysql/update_test.go +++ b/tests/mysql/update_test.go @@ -2,7 +2,6 @@ package mysql import ( "context" - "fmt" "github.com/go-jet/jet/v2/internal/testutils" . "github.com/go-jet/jet/v2/mysql" "github.com/go-jet/jet/v2/tests/.gentestdata/mysql/dvds/table" @@ -193,7 +192,7 @@ SET url = 'http://www.duckduckgo.com', description = NULL 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.AssertExec(t, stmt, db) diff --git a/tests/postgres/chinook_db_test.go b/tests/postgres/chinook_db_test.go index 50f5e607..553f920a 100644 --- a/tests/postgres/chinook_db_test.go +++ b/tests/postgres/chinook_db_test.go @@ -2,7 +2,6 @@ package postgres import ( "context" - "fmt" "github.com/go-jet/jet/v2/internal/testutils" . "github.com/go-jet/jet/v2/postgres" "github.com/go-jet/jet/v2/tests/.gentestdata/jetdb/chinook/model" @@ -17,7 +16,7 @@ func TestSelect(t *testing.T) { SELECT(Album.AllColumns). ORDER_BY(Album.AlbumId.ASC()) - fmt.Println(stmt.DebugSql()) + //fmt.Println(stmt.DebugSql()) testutils.AssertDebugStatementSql(t, stmt, ` SELECT "Album"."AlbumId" AS "Album.AlbumId", @@ -330,8 +329,71 @@ ORDER BY "first10Artist"."Artist.ArtistId"; err := stmt.Query(db, &dest) require.NoError(t, err) +} + +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) - //spew.Dump(dest) + 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{ diff --git a/tests/postgres/generator_test.go b/tests/postgres/generator_test.go index 1e23016f..833e2a43 100644 --- a/tests/postgres/generator_test.go +++ b/tests/postgres/generator_test.go @@ -168,7 +168,7 @@ import ( "github.com/go-jet/jet/v2/postgres" ) -var Actor = newActorTable() +var Actor = newActorTable("dvds", "actor", "") type actorTable struct { postgres.Table @@ -190,20 +190,23 @@ type ActorTable struct { } // AS creates new ActorTable with assigned alias -func (a *ActorTable) AS(alias string) *ActorTable { - aliasTable := newActorTable() - aliasTable.Table.AS(alias) - return aliasTable +func (a ActorTable) AS(alias string) *ActorTable { + return newActorTable(a.SchemaName(), a.TableName(), alias) } -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{ - actorTable: newActorTableImpl("dvds", "actor"), - EXCLUDED: newActorTableImpl("", "excluded"), + actorTable: newActorTableImpl(schemaName, tableName, alias), + EXCLUDED: newActorTableImpl("", "excluded", ""), } } -func newActorTableImpl(schemaName, tableName string) actorTable { +func newActorTableImpl(schemaName, tableName, alias string) actorTable { var ( ActorIDColumn = postgres.IntegerColumn("actor_id") FirstNameColumn = postgres.StringColumn("first_name") @@ -214,7 +217,7 @@ func newActorTableImpl(schemaName, tableName string) actorTable { ) return actorTable{ - Table: postgres.NewTable(schemaName, tableName, allColumns...), + Table: postgres.NewTable(schemaName, tableName, alias, allColumns...), //Columns ActorID: ActorIDColumn, @@ -264,7 +267,7 @@ import ( "github.com/go-jet/jet/v2/postgres" ) -var ActorInfo = newActorInfoTable() +var ActorInfo = newActorInfoTable("dvds", "actor_info", "") type actorInfoTable struct { postgres.Table @@ -286,20 +289,23 @@ type ActorInfoTable struct { } // AS creates new ActorInfoTable with assigned alias -func (a *ActorInfoTable) AS(alias string) *ActorInfoTable { - aliasTable := newActorInfoTable() - aliasTable.Table.AS(alias) - return aliasTable +func (a ActorInfoTable) AS(alias string) *ActorInfoTable { + return newActorInfoTable(a.SchemaName(), a.TableName(), alias) +} + +// Schema creates new ActorInfoTable with assigned schema name +func (a ActorInfoTable) FromSchema(schemaName string) *ActorInfoTable { + return newActorInfoTable(schemaName, a.TableName(), a.Alias()) } -func newActorInfoTable() *ActorInfoTable { +func newActorInfoTable(schemaName, tableName, alias string) *ActorInfoTable { return &ActorInfoTable{ - actorInfoTable: newActorInfoTableImpl("dvds", "actor_info"), - EXCLUDED: newActorInfoTableImpl("", "excluded"), + actorInfoTable: newActorInfoTableImpl(schemaName, tableName, alias), + EXCLUDED: newActorInfoTableImpl("", "excluded", ""), } } -func newActorInfoTableImpl(schemaName, tableName string) actorInfoTable { +func newActorInfoTableImpl(schemaName, tableName, alias string) actorInfoTable { var ( ActorIDColumn = postgres.IntegerColumn("actor_id") FirstNameColumn = postgres.StringColumn("first_name") @@ -310,7 +316,7 @@ func newActorInfoTableImpl(schemaName, tableName string) actorInfoTable { ) return actorInfoTable{ - Table: postgres.NewTable(schemaName, tableName, allColumns...), + Table: postgres.NewTable(schemaName, tableName, alias, allColumns...), //Columns ActorID: ActorIDColumn, @@ -497,7 +503,7 @@ import ( "github.com/go-jet/jet/v2/postgres" ) -var AllTypes = newAllTypesTable() +var AllTypes = newAllTypesTable("test_sample", "all_types", "") type allTypesTable struct { postgres.Table @@ -576,20 +582,23 @@ type AllTypesTable struct { } // AS creates new AllTypesTable with assigned alias -func (a *AllTypesTable) AS(alias string) *AllTypesTable { - aliasTable := newAllTypesTable() - aliasTable.Table.AS(alias) - return aliasTable +func (a AllTypesTable) AS(alias string) *AllTypesTable { + return newAllTypesTable(a.SchemaName(), a.TableName(), alias) +} + +// Schema creates new AllTypesTable with assigned schema name +func (a AllTypesTable) FromSchema(schemaName string) *AllTypesTable { + return newAllTypesTable(schemaName, a.TableName(), a.Alias()) } -func newAllTypesTable() *AllTypesTable { +func newAllTypesTable(schemaName, tableName, alias string) *AllTypesTable { return &AllTypesTable{ - allTypesTable: newAllTypesTableImpl("test_sample", "all_types"), - EXCLUDED: newAllTypesTableImpl("", "excluded"), + allTypesTable: newAllTypesTableImpl(schemaName, tableName, alias), + EXCLUDED: newAllTypesTableImpl("", "excluded", ""), } } -func newAllTypesTableImpl(schemaName, tableName string) allTypesTable { +func newAllTypesTableImpl(schemaName, tableName, alias string) allTypesTable { var ( SmallIntPtrColumn = postgres.IntegerColumn("small_int_ptr") SmallIntColumn = postgres.IntegerColumn("small_int") @@ -657,7 +666,7 @@ func newAllTypesTableImpl(schemaName, tableName string) allTypesTable { ) return allTypesTable{ - Table: postgres.NewTable(schemaName, tableName, allColumns...), + Table: postgres.NewTable(schemaName, tableName, alias, allColumns...), //Columns SmallIntPtr: SmallIntPtrColumn, diff --git a/tests/postgres/sample_test.go b/tests/postgres/sample_test.go index bcb7361a..327604f0 100644 --- a/tests/postgres/sample_test.go +++ b/tests/postgres/sample_test.go @@ -364,7 +364,7 @@ FROM test_sample."User"; err := stmt.Query(db, &dest) require.NoError(t, err) - testutils.PrintJson(dest) + //testutils.PrintJson(dest) testutils.AssertJSON(t, dest, ` [ diff --git a/tests/postgres/scan_test.go b/tests/postgres/scan_test.go index 8eb32764..def30979 100644 --- a/tests/postgres/scan_test.go +++ b/tests/postgres/scan_test.go @@ -1,7 +1,6 @@ package postgres import ( - "fmt" "github.com/go-jet/jet/v2/internal/testutils" . "github.com/go-jet/jet/v2/postgres" "github.com/go-jet/jet/v2/qrm" @@ -93,7 +92,7 @@ func TestScanToStruct(t *testing.T) { SELECT(Inventory.AllColumns). ORDER_BY(Inventory.InventoryID) - fmt.Println(query.DebugSql()) + //fmt.Println(query.DebugSql()) t.Run("one struct", func(t *testing.T) { dest := model.Inventory{} diff --git a/tests/postgres/select_test.go b/tests/postgres/select_test.go index e9dd7d2a..8bd6f53d 100644 --- a/tests/postgres/select_test.go +++ b/tests/postgres/select_test.go @@ -1,7 +1,6 @@ package postgres import ( - "fmt" "github.com/go-jet/jet/v2/internal/testutils" . "github.com/go-jet/jet/v2/postgres" "github.com/go-jet/jet/v2/tests/.gentestdata/jetdb/dvds/enum" @@ -1255,7 +1254,7 @@ OFFSET 20; LIMIT(10). OFFSET(20) - fmt.Println(query.DebugSql()) + //fmt.Println(query.DebugSql()) testutils.AssertDebugStatementSql(t, query, expectedQuery, float64(100), float64(200), int64(10), int64(20)) @@ -1788,7 +1787,7 @@ func TestJoinViewWithTable(t *testing.T) { Rentals []model.Rental } - fmt.Println(query.DebugSql()) + //fmt.Println(query.DebugSql()) err := query.Query(db, &dest) require.NoError(t, err) diff --git a/tests/testdata b/tests/testdata index ed53a505..391d9365 160000 --- a/tests/testdata +++ b/tests/testdata @@ -1 +1 @@ -Subproject commit ed53a505eb738d1be457877eee251f9ba0418df1 +Subproject commit 391d936515d2f826df073707697de44907a7f67d From d63e56f5740650b531819b489e807a033b286da2 Mon Sep 17 00:00:00 2001 From: go-jet Date: Sun, 21 Mar 2021 17:18:43 +0100 Subject: [PATCH 11/41] Update quick-start example with updated auto generated files --- .../.gen/jetdb/dvds/table/actor.go | 23 +++++++++++-------- .../.gen/jetdb/dvds/table/category.go | 23 +++++++++++-------- .../quick-start/.gen/jetdb/dvds/table/film.go | 23 +++++++++++-------- .../.gen/jetdb/dvds/table/film_actor.go | 23 +++++++++++-------- .../.gen/jetdb/dvds/table/film_category.go | 23 +++++++++++-------- .../.gen/jetdb/dvds/table/language.go | 23 +++++++++++-------- .../.gen/jetdb/dvds/view/actor_info.go | 23 +++++++++++-------- .../.gen/jetdb/dvds/view/customer_list.go | 23 +++++++++++-------- 8 files changed, 104 insertions(+), 80 deletions(-) diff --git a/examples/quick-start/.gen/jetdb/dvds/table/actor.go b/examples/quick-start/.gen/jetdb/dvds/table/actor.go index 376d73e1..bbeb7e8f 100644 --- a/examples/quick-start/.gen/jetdb/dvds/table/actor.go +++ b/examples/quick-start/.gen/jetdb/dvds/table/actor.go @@ -11,7 +11,7 @@ import ( "github.com/go-jet/jet/v2/postgres" ) -var Actor = newActorTable() +var Actor = newActorTable("dvds", "actor", "") type actorTable struct { postgres.Table @@ -33,20 +33,23 @@ type ActorTable struct { } // AS creates new ActorTable with assigned alias -func (a *ActorTable) AS(alias string) *ActorTable { - aliasTable := newActorTable() - aliasTable.Table.AS(alias) - return aliasTable +func (a ActorTable) AS(alias string) *ActorTable { + return newActorTable(a.SchemaName(), a.TableName(), alias) } -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{ - actorTable: newActorTableImpl("dvds", "actor"), - EXCLUDED: newActorTableImpl("", "excluded"), + actorTable: newActorTableImpl(schemaName, tableName, alias), + EXCLUDED: newActorTableImpl("", "excluded", ""), } } -func newActorTableImpl(schemaName, tableName string) actorTable { +func newActorTableImpl(schemaName, tableName, alias string) actorTable { var ( ActorIDColumn = postgres.IntegerColumn("actor_id") FirstNameColumn = postgres.StringColumn("first_name") @@ -57,7 +60,7 @@ func newActorTableImpl(schemaName, tableName string) actorTable { ) return actorTable{ - Table: postgres.NewTable(schemaName, tableName, allColumns...), + Table: postgres.NewTable(schemaName, tableName, alias, allColumns...), //Columns ActorID: ActorIDColumn, diff --git a/examples/quick-start/.gen/jetdb/dvds/table/category.go b/examples/quick-start/.gen/jetdb/dvds/table/category.go index ac2caac0..563938d9 100644 --- a/examples/quick-start/.gen/jetdb/dvds/table/category.go +++ b/examples/quick-start/.gen/jetdb/dvds/table/category.go @@ -11,7 +11,7 @@ import ( "github.com/go-jet/jet/v2/postgres" ) -var Category = newCategoryTable() +var Category = newCategoryTable("dvds", "category", "") type categoryTable struct { postgres.Table @@ -32,20 +32,23 @@ type CategoryTable struct { } // AS creates new CategoryTable with assigned alias -func (a *CategoryTable) AS(alias string) *CategoryTable { - aliasTable := newCategoryTable() - aliasTable.Table.AS(alias) - return aliasTable +func (a CategoryTable) AS(alias string) *CategoryTable { + return newCategoryTable(a.SchemaName(), a.TableName(), alias) } -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{ - categoryTable: newCategoryTableImpl("dvds", "category"), - EXCLUDED: newCategoryTableImpl("", "excluded"), + categoryTable: newCategoryTableImpl(schemaName, tableName, alias), + EXCLUDED: newCategoryTableImpl("", "excluded", ""), } } -func newCategoryTableImpl(schemaName, tableName string) categoryTable { +func newCategoryTableImpl(schemaName, tableName, alias string) categoryTable { var ( CategoryIDColumn = postgres.IntegerColumn("category_id") NameColumn = postgres.StringColumn("name") @@ -55,7 +58,7 @@ func newCategoryTableImpl(schemaName, tableName string) categoryTable { ) return categoryTable{ - Table: postgres.NewTable(schemaName, tableName, allColumns...), + Table: postgres.NewTable(schemaName, tableName, alias, allColumns...), //Columns CategoryID: CategoryIDColumn, diff --git a/examples/quick-start/.gen/jetdb/dvds/table/film.go b/examples/quick-start/.gen/jetdb/dvds/table/film.go index 2f8c68e6..65db900a 100644 --- a/examples/quick-start/.gen/jetdb/dvds/table/film.go +++ b/examples/quick-start/.gen/jetdb/dvds/table/film.go @@ -11,7 +11,7 @@ import ( "github.com/go-jet/jet/v2/postgres" ) -var Film = newFilmTable() +var Film = newFilmTable("dvds", "film", "") type filmTable struct { postgres.Table @@ -42,20 +42,23 @@ type FilmTable struct { } // AS creates new FilmTable with assigned alias -func (a *FilmTable) AS(alias string) *FilmTable { - aliasTable := newFilmTable() - aliasTable.Table.AS(alias) - return aliasTable +func (a FilmTable) AS(alias string) *FilmTable { + return newFilmTable(a.SchemaName(), a.TableName(), alias) } -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{ - filmTable: newFilmTableImpl("dvds", "film"), - EXCLUDED: newFilmTableImpl("", "excluded"), + filmTable: newFilmTableImpl(schemaName, tableName, alias), + EXCLUDED: newFilmTableImpl("", "excluded", ""), } } -func newFilmTableImpl(schemaName, tableName string) filmTable { +func newFilmTableImpl(schemaName, tableName, alias string) filmTable { var ( FilmIDColumn = postgres.IntegerColumn("film_id") TitleColumn = postgres.StringColumn("title") @@ -75,7 +78,7 @@ func newFilmTableImpl(schemaName, tableName string) filmTable { ) return filmTable{ - Table: postgres.NewTable(schemaName, tableName, allColumns...), + Table: postgres.NewTable(schemaName, tableName, alias, allColumns...), //Columns FilmID: FilmIDColumn, diff --git a/examples/quick-start/.gen/jetdb/dvds/table/film_actor.go b/examples/quick-start/.gen/jetdb/dvds/table/film_actor.go index 1f6bb575..30c3ad35 100644 --- a/examples/quick-start/.gen/jetdb/dvds/table/film_actor.go +++ b/examples/quick-start/.gen/jetdb/dvds/table/film_actor.go @@ -11,7 +11,7 @@ import ( "github.com/go-jet/jet/v2/postgres" ) -var FilmActor = newFilmActorTable() +var FilmActor = newFilmActorTable("dvds", "film_actor", "") type filmActorTable struct { postgres.Table @@ -32,20 +32,23 @@ type FilmActorTable struct { } // AS creates new FilmActorTable with assigned alias -func (a *FilmActorTable) AS(alias string) *FilmActorTable { - aliasTable := newFilmActorTable() - aliasTable.Table.AS(alias) - return aliasTable +func (a FilmActorTable) AS(alias string) *FilmActorTable { + return newFilmActorTable(a.SchemaName(), a.TableName(), alias) } -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{ - filmActorTable: newFilmActorTableImpl("dvds", "film_actor"), - EXCLUDED: newFilmActorTableImpl("", "excluded"), + filmActorTable: newFilmActorTableImpl(schemaName, tableName, alias), + EXCLUDED: newFilmActorTableImpl("", "excluded", ""), } } -func newFilmActorTableImpl(schemaName, tableName string) filmActorTable { +func newFilmActorTableImpl(schemaName, tableName, alias string) filmActorTable { var ( ActorIDColumn = postgres.IntegerColumn("actor_id") FilmIDColumn = postgres.IntegerColumn("film_id") @@ -55,7 +58,7 @@ func newFilmActorTableImpl(schemaName, tableName string) filmActorTable { ) return filmActorTable{ - Table: postgres.NewTable(schemaName, tableName, allColumns...), + Table: postgres.NewTable(schemaName, tableName, alias, allColumns...), //Columns ActorID: ActorIDColumn, diff --git a/examples/quick-start/.gen/jetdb/dvds/table/film_category.go b/examples/quick-start/.gen/jetdb/dvds/table/film_category.go index 41fa6f33..83681777 100644 --- a/examples/quick-start/.gen/jetdb/dvds/table/film_category.go +++ b/examples/quick-start/.gen/jetdb/dvds/table/film_category.go @@ -11,7 +11,7 @@ import ( "github.com/go-jet/jet/v2/postgres" ) -var FilmCategory = newFilmCategoryTable() +var FilmCategory = newFilmCategoryTable("dvds", "film_category", "") type filmCategoryTable struct { postgres.Table @@ -32,20 +32,23 @@ type FilmCategoryTable struct { } // AS creates new FilmCategoryTable with assigned alias -func (a *FilmCategoryTable) AS(alias string) *FilmCategoryTable { - aliasTable := newFilmCategoryTable() - aliasTable.Table.AS(alias) - return aliasTable +func (a FilmCategoryTable) AS(alias string) *FilmCategoryTable { + return newFilmCategoryTable(a.SchemaName(), a.TableName(), alias) } -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{ - filmCategoryTable: newFilmCategoryTableImpl("dvds", "film_category"), - EXCLUDED: newFilmCategoryTableImpl("", "excluded"), + filmCategoryTable: newFilmCategoryTableImpl(schemaName, tableName, alias), + EXCLUDED: newFilmCategoryTableImpl("", "excluded", ""), } } -func newFilmCategoryTableImpl(schemaName, tableName string) filmCategoryTable { +func newFilmCategoryTableImpl(schemaName, tableName, alias string) filmCategoryTable { var ( FilmIDColumn = postgres.IntegerColumn("film_id") CategoryIDColumn = postgres.IntegerColumn("category_id") @@ -55,7 +58,7 @@ func newFilmCategoryTableImpl(schemaName, tableName string) filmCategoryTable { ) return filmCategoryTable{ - Table: postgres.NewTable(schemaName, tableName, allColumns...), + Table: postgres.NewTable(schemaName, tableName, alias, allColumns...), //Columns FilmID: FilmIDColumn, diff --git a/examples/quick-start/.gen/jetdb/dvds/table/language.go b/examples/quick-start/.gen/jetdb/dvds/table/language.go index 89470c24..5bddeebb 100644 --- a/examples/quick-start/.gen/jetdb/dvds/table/language.go +++ b/examples/quick-start/.gen/jetdb/dvds/table/language.go @@ -11,7 +11,7 @@ import ( "github.com/go-jet/jet/v2/postgres" ) -var Language = newLanguageTable() +var Language = newLanguageTable("dvds", "language", "") type languageTable struct { postgres.Table @@ -32,20 +32,23 @@ type LanguageTable struct { } // AS creates new LanguageTable with assigned alias -func (a *LanguageTable) AS(alias string) *LanguageTable { - aliasTable := newLanguageTable() - aliasTable.Table.AS(alias) - return aliasTable +func (a LanguageTable) AS(alias string) *LanguageTable { + return newLanguageTable(a.SchemaName(), a.TableName(), alias) } -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{ - languageTable: newLanguageTableImpl("dvds", "language"), - EXCLUDED: newLanguageTableImpl("", "excluded"), + languageTable: newLanguageTableImpl(schemaName, tableName, alias), + EXCLUDED: newLanguageTableImpl("", "excluded", ""), } } -func newLanguageTableImpl(schemaName, tableName string) languageTable { +func newLanguageTableImpl(schemaName, tableName, alias string) languageTable { var ( LanguageIDColumn = postgres.IntegerColumn("language_id") NameColumn = postgres.StringColumn("name") @@ -55,7 +58,7 @@ func newLanguageTableImpl(schemaName, tableName string) languageTable { ) return languageTable{ - Table: postgres.NewTable(schemaName, tableName, allColumns...), + Table: postgres.NewTable(schemaName, tableName, alias, allColumns...), //Columns LanguageID: LanguageIDColumn, diff --git a/examples/quick-start/.gen/jetdb/dvds/view/actor_info.go b/examples/quick-start/.gen/jetdb/dvds/view/actor_info.go index 65d25601..5bfa25d3 100644 --- a/examples/quick-start/.gen/jetdb/dvds/view/actor_info.go +++ b/examples/quick-start/.gen/jetdb/dvds/view/actor_info.go @@ -11,7 +11,7 @@ import ( "github.com/go-jet/jet/v2/postgres" ) -var ActorInfo = newActorInfoTable() +var ActorInfo = newActorInfoTable("dvds", "actor_info", "") type actorInfoTable struct { postgres.Table @@ -33,20 +33,23 @@ type ActorInfoTable struct { } // AS creates new ActorInfoTable with assigned alias -func (a *ActorInfoTable) AS(alias string) *ActorInfoTable { - aliasTable := newActorInfoTable() - aliasTable.Table.AS(alias) - return aliasTable +func (a ActorInfoTable) AS(alias string) *ActorInfoTable { + return newActorInfoTable(a.SchemaName(), a.TableName(), alias) } -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{ - actorInfoTable: newActorInfoTableImpl("dvds", "actor_info"), - EXCLUDED: newActorInfoTableImpl("", "excluded"), + actorInfoTable: newActorInfoTableImpl(schemaName, tableName, alias), + EXCLUDED: newActorInfoTableImpl("", "excluded", ""), } } -func newActorInfoTableImpl(schemaName, tableName string) actorInfoTable { +func newActorInfoTableImpl(schemaName, tableName, alias string) actorInfoTable { var ( ActorIDColumn = postgres.IntegerColumn("actor_id") FirstNameColumn = postgres.StringColumn("first_name") @@ -57,7 +60,7 @@ func newActorInfoTableImpl(schemaName, tableName string) actorInfoTable { ) return actorInfoTable{ - Table: postgres.NewTable(schemaName, tableName, allColumns...), + Table: postgres.NewTable(schemaName, tableName, alias, allColumns...), //Columns ActorID: ActorIDColumn, diff --git a/examples/quick-start/.gen/jetdb/dvds/view/customer_list.go b/examples/quick-start/.gen/jetdb/dvds/view/customer_list.go index b4a2c8fc..c03a5ef5 100644 --- a/examples/quick-start/.gen/jetdb/dvds/view/customer_list.go +++ b/examples/quick-start/.gen/jetdb/dvds/view/customer_list.go @@ -11,7 +11,7 @@ import ( "github.com/go-jet/jet/v2/postgres" ) -var CustomerList = newCustomerListTable() +var CustomerList = newCustomerListTable("dvds", "customer_list", "") type customerListTable struct { postgres.Table @@ -38,20 +38,23 @@ type CustomerListTable struct { } // AS creates new CustomerListTable with assigned alias -func (a *CustomerListTable) AS(alias string) *CustomerListTable { - aliasTable := newCustomerListTable() - aliasTable.Table.AS(alias) - return aliasTable +func (a CustomerListTable) AS(alias string) *CustomerListTable { + return newCustomerListTable(a.SchemaName(), a.TableName(), alias) } -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{ - customerListTable: newCustomerListTableImpl("dvds", "customer_list"), - EXCLUDED: newCustomerListTableImpl("", "excluded"), + customerListTable: newCustomerListTableImpl(schemaName, tableName, alias), + EXCLUDED: newCustomerListTableImpl("", "excluded", ""), } } -func newCustomerListTableImpl(schemaName, tableName string) customerListTable { +func newCustomerListTableImpl(schemaName, tableName, alias string) customerListTable { var ( IDColumn = postgres.IntegerColumn("id") NameColumn = postgres.StringColumn("name") @@ -67,7 +70,7 @@ func newCustomerListTableImpl(schemaName, tableName string) customerListTable { ) return customerListTable{ - Table: postgres.NewTable(schemaName, tableName, allColumns...), + Table: postgres.NewTable(schemaName, tableName, alias, allColumns...), //Columns ID: IDColumn, From f8cfaaace151a93a8aeedb302921dc19c1f788fb Mon Sep 17 00:00:00 2001 From: go-jet Date: Sun, 21 Mar 2021 17:23:42 +0100 Subject: [PATCH 12/41] Update circle-ci Add new test database. --- .circleci/config.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index 43eae460..35e0a6d6 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -77,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 "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 dvds2" - run: name: Init Postgres database @@ -145,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 "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 dvds2" - run: name: Init MariaDB database From 1e511654fdd691a9961d04e2849cf354b5f47f3a Mon Sep 17 00:00:00 2001 From: Joonas Haapsaari Date: Tue, 18 Aug 2020 15:47:10 +0300 Subject: [PATCH 13/41] Schema rename support - Support for renaming table schemas * Table support for renaming schema * Empty schema name is left out (using default schema for the database connection) * Generated code support for obtaining a version of the table with renamed schema, similarly as the `AS` function works * Unit tests for setting and clearing the schema name --- generator/internal/template/templates.go | 18 ++++++++++++ internal/jet/table.go | 25 +++++++++++++++-- internal/jet/table_test.go | 35 ++++++++++++++++++++++++ 3 files changed, 76 insertions(+), 2 deletions(-) diff --git a/generator/internal/template/templates.go b/generator/internal/template/templates.go index 56dba2ca..5288ef40 100644 --- a/generator/internal/template/templates.go +++ b/generator/internal/template/templates.go @@ -41,6 +41,15 @@ type {{.GoStructName}} struct { func (a *{{.GoStructName}}) AS(alias string) {{.GoStructName}} { aliasTable := new{{.GoStructName}}() aliasTable.Table.AS(alias) + aliasTable.Table.Schema(a.Table.SchemaName()) + return aliasTable +} + +// Schema creates new {{.GoStructName}} with assigned schema name +func (a *{{.GoStructName}}) Schema(schemaName string) {{.GoStructName}} { + aliasTable := new{{.GoStructName}}() + aliasTable.Table.AS(a.Table.Alias()) + aliasTable.Table.Schema(schemaName) return aliasTable } @@ -104,6 +113,15 @@ type {{.GoStructName}} struct { func (a *{{.GoStructName}}) AS(alias string) *{{.GoStructName}} { aliasTable := new{{.GoStructName}}() aliasTable.Table.AS(alias) + aliasTable.Table.Schema(a.Table.SchemaName()) + return aliasTable +} + +// Schema creates new {{.GoStructName}} with assigned schema name +func (a *{{.GoStructName}}) Schema(schemaName string) *{{.GoStructName}} { + aliasTable := new{{.GoStructName}}() + aliasTable.Table.AS(a.Table.Alias()) + aliasTable.Table.Schema(schemaName) return aliasTable } diff --git a/internal/jet/table.go b/internal/jet/table.go index 45d46e9a..68f5962d 100644 --- a/internal/jet/table.go +++ b/internal/jet/table.go @@ -16,6 +16,8 @@ type Table interface { SchemaName() string TableName() string AS(alias string) + Alias() string + Schema(schemaName string) } // NewTable creates new table with schema Name, table Name and list of columns @@ -67,13 +69,25 @@ func (t *tableImpl) columns() []Column { return ret } +func (t *tableImpl) Alias() string { + return t.alias +} + +func (t *tableImpl) Schema(schemaName string) { + t.schemaName = schemaName +} + func (t *tableImpl) serialize(statement StatementType, out *SQLBuilder, options ...SerializeOption) { if t == nil { panic("jet: tableImpl is nil") } - out.WriteIdentifier(t.schemaName) - out.WriteString(".") + // Use default schema if the schema name is not set + if len(t.schemaName) > 0 { + out.WriteIdentifier(t.schemaName) + out.WriteString(".") + } + out.WriteIdentifier(t.name) if len(t.alias) > 0 { @@ -145,6 +159,13 @@ func (t *joinTableImpl) columns() []Column { return ret } +func (t *joinTableImpl) Alias() string { + return "" +} + +func (t *joinTableImpl) Schema(schemaName string) { +} + func (t *joinTableImpl) serialize(statement StatementType, out *SQLBuilder, options ...SerializeOption) { if t == nil { panic("jet: Join table is nil. ") diff --git a/internal/jet/table_test.go b/internal/jet/table_test.go index 66646b2e..47923cdf 100644 --- a/internal/jet/table_test.go +++ b/internal/jet/table_test.go @@ -31,3 +31,38 @@ INNER JOIN schema.table2 ON ("intCol1" = "intCol2")`) require.Equal(t, joinTable.columns()[0].Name(), "intCol1") require.Equal(t, joinTable.columns()[1].Name(), "intCol2") } + +func TestSchemaNameSet(t *testing.T) { + newTable := NewTable("schema", "table") + newTable.Schema("foo") + newTable.AS("bar") + assertClauseSerialize(t, newTable, `foo.table AS bar`) +} + +func TestSchemaNameClear(t *testing.T) { + newTable := NewTable("schema", "table") + newTable.Schema("") + newTable.AS("bar") + assertClauseSerialize(t, newTable, `table AS bar`) +} + +func TestNewJoinTableSchemaNameSet(t *testing.T) { + newTable1 := NewTable("schema", "table", IntegerColumn("intCol1")) + newTable2 := NewTable("schema", "table2", IntegerColumn("intCol2")) + + newTable1.Schema("foo") + newTable2.Schema("foo") + + joinTable := NewJoinTable(newTable1, newTable2, InnerJoin, IntegerColumn("intCol1").EQ(IntegerColumn("intCol2"))) + joinTable.Schema("xxx") + + assertClauseSerialize(t, joinTable, `foo.table +INNER JOIN foo.table2 ON ("intCol1" = "intCol2")`) + + require.Equal(t, joinTable.SchemaName(), "foo") + require.Equal(t, joinTable.TableName(), "") + + require.Equal(t, len(joinTable.columns()), 2) + require.Equal(t, joinTable.columns()[0].Name(), "intCol1") + require.Equal(t, joinTable.columns()[1].Name(), "intCol2") +} From 38776e35ab45b72fe33287c5f78dc48324d83108 Mon Sep 17 00:00:00 2001 From: go-jet Date: Sun, 21 Mar 2021 17:14:15 +0100 Subject: [PATCH 14/41] Remove methods from Table interface that affects receiver object Modifying SQL builder receiver object can produce unwanted side effects. --- internal/jet/table.go | 28 +++++++++----------------- internal/jet/table_test.go | 41 +++----------------------------------- internal/jet/testutils.go | 6 +++--- 3 files changed, 15 insertions(+), 60 deletions(-) diff --git a/internal/jet/table.go b/internal/jet/table.go index 68f5962d..78abb990 100644 --- a/internal/jet/table.go +++ b/internal/jet/table.go @@ -15,22 +15,27 @@ type Table interface { columns() []Column SchemaName() string TableName() string - AS(alias string) Alias() string - Schema(schemaName string) } // 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{ schemaName: schemaName, name: name, + alias: alias, columnList: columns, } + columnTableName := name + + if alias != "" { + columnTableName = alias + } + for _, c := range columns { - c.setTableName(name) + c.setTableName(columnTableName) } return &t @@ -43,14 +48,6 @@ type tableImpl struct { columnList []ColumnExpression } -func (t *tableImpl) AS(alias string) { - t.alias = alias - - for _, c := range t.columnList { - c.setTableName(alias) - } -} - func (t *tableImpl) SchemaName() string { return t.schemaName } @@ -73,10 +70,6 @@ func (t *tableImpl) Alias() string { return t.alias } -func (t *tableImpl) Schema(schemaName string) { - t.schemaName = schemaName -} - func (t *tableImpl) serialize(statement StatementType, out *SQLBuilder, options ...SerializeOption) { if t == nil { panic("jet: tableImpl is nil") @@ -163,9 +156,6 @@ func (t *joinTableImpl) Alias() string { return "" } -func (t *joinTableImpl) Schema(schemaName string) { -} - func (t *joinTableImpl) serialize(statement StatementType, out *SQLBuilder, options ...SerializeOption) { if t == nil { panic("jet: Join table is nil. ") diff --git a/internal/jet/table_test.go b/internal/jet/table_test.go index 47923cdf..b880784f 100644 --- a/internal/jet/table_test.go +++ b/internal/jet/table_test.go @@ -6,7 +6,7 @@ import ( ) 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.TableName(), "table") @@ -16,8 +16,8 @@ func TestNewTable(t *testing.T) { } func TestNewJoinTable(t *testing.T) { - newTable1 := NewTable("schema", "table", IntegerColumn("intCol1")) - newTable2 := NewTable("schema", "table2", IntegerColumn("intCol2")) + newTable1 := NewTable("schema", "table", "", IntegerColumn("intCol1")) + newTable2 := NewTable("schema", "table2", "", IntegerColumn("intCol2")) joinTable := NewJoinTable(newTable1, newTable2, InnerJoin, IntegerColumn("intCol1").EQ(IntegerColumn("intCol2"))) @@ -31,38 +31,3 @@ INNER JOIN schema.table2 ON ("intCol1" = "intCol2")`) require.Equal(t, joinTable.columns()[0].Name(), "intCol1") require.Equal(t, joinTable.columns()[1].Name(), "intCol2") } - -func TestSchemaNameSet(t *testing.T) { - newTable := NewTable("schema", "table") - newTable.Schema("foo") - newTable.AS("bar") - assertClauseSerialize(t, newTable, `foo.table AS bar`) -} - -func TestSchemaNameClear(t *testing.T) { - newTable := NewTable("schema", "table") - newTable.Schema("") - newTable.AS("bar") - assertClauseSerialize(t, newTable, `table AS bar`) -} - -func TestNewJoinTableSchemaNameSet(t *testing.T) { - newTable1 := NewTable("schema", "table", IntegerColumn("intCol1")) - newTable2 := NewTable("schema", "table2", IntegerColumn("intCol2")) - - newTable1.Schema("foo") - newTable2.Schema("foo") - - joinTable := NewJoinTable(newTable1, newTable2, InnerJoin, IntegerColumn("intCol1").EQ(IntegerColumn("intCol2"))) - joinTable.Schema("xxx") - - assertClauseSerialize(t, joinTable, `foo.table -INNER JOIN foo.table2 ON ("intCol1" = "intCol2")`) - - require.Equal(t, joinTable.SchemaName(), "foo") - require.Equal(t, joinTable.TableName(), "") - - require.Equal(t, len(joinTable.columns()), 2) - require.Equal(t, joinTable.columns()[0].Name(), "intCol1") - require.Equal(t, joinTable.columns()[1].Name(), "intCol2") -} diff --git a/internal/jet/testutils.go b/internal/jet/testutils.go index 268ae065..43f1f5d6 100644 --- a/internal/jet/testutils.go +++ b/internal/jet/testutils.go @@ -26,7 +26,7 @@ var ( table1ColBool = BoolColumn("col_bool") 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 ( table2Col3 = IntegerColumn("col3") @@ -41,14 +41,14 @@ var ( table2ColTimestampz = TimestampzColumn("col_timestampz") 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 ( table3Col1 = IntegerColumn("col1") table3ColInt = IntegerColumn("col_int") 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{}) { out := SQLBuilder{Dialect: defaultDialect} From fae8dde639b3ce254783c317d6de29a918a26102 Mon Sep 17 00:00:00 2001 From: go-jet Date: Sun, 21 Mar 2021 17:17:44 +0100 Subject: [PATCH 15/41] Add schema rename support Using SchemaFrom("schemaName") it is possible to set SQL builder table to point to a different schema. --- generator/internal/template/templates.go | 46 ++++++---------- mysql/table.go | 4 +- mysql/utils_test.go | 34 ++---------- postgres/dialect_test.go | 8 +-- postgres/table.go | 4 +- postgres/utils_test.go | 25 ++------- tests/init/init.go | 2 + tests/mysql/alltypes_test.go | 7 ++- tests/mysql/generator_test.go | 34 +++++++----- tests/mysql/select_test.go | 43 +++++++++++++++ tests/mysql/update_test.go | 3 +- tests/postgres/chinook_db_test.go | 68 +++++++++++++++++++++-- tests/postgres/generator_test.go | 69 +++++++++++++----------- tests/postgres/sample_test.go | 2 +- tests/postgres/scan_test.go | 3 +- tests/postgres/select_test.go | 5 +- tests/testdata | 2 +- 17 files changed, 206 insertions(+), 153 deletions(-) diff --git a/generator/internal/template/templates.go b/generator/internal/template/templates.go index 5288ef40..186ade02 100644 --- a/generator/internal/template/templates.go +++ b/generator/internal/template/templates.go @@ -23,7 +23,7 @@ import ( "github.com/go-jet/jet/v2/{{dialect.PackageName}}" ) -var {{ToGoIdentifier .Name}} = new{{.GoStructName}}() +var {{ToGoIdentifier .Name}} = new{{.GoStructName}}("{{.SchemaName}}", "{{.Name}}", "") type {{.GoStructName}} struct { {{dialect.PackageName}}.Table @@ -38,22 +38,16 @@ type {{.GoStructName}} struct { } // AS creates new {{.GoStructName}} with assigned alias -func (a *{{.GoStructName}}) AS(alias string) {{.GoStructName}} { - aliasTable := new{{.GoStructName}}() - aliasTable.Table.AS(alias) - aliasTable.Table.Schema(a.Table.SchemaName()) - return aliasTable +func (a {{.GoStructName}}) AS(alias string) {{.GoStructName}} { + return new{{.GoStructName}}(a.SchemaName(), a.TableName(), alias) } // Schema creates new {{.GoStructName}} with assigned schema name -func (a *{{.GoStructName}}) Schema(schemaName string) {{.GoStructName}} { - aliasTable := new{{.GoStructName}}() - aliasTable.Table.AS(a.Table.Alias()) - aliasTable.Table.Schema(schemaName) - return aliasTable +func (a {{.GoStructName}}) FromSchema(schemaName string) {{.GoStructName}} { + return new{{.GoStructName}}(schemaName, a.TableName(), a.Alias()) } -func new{{.GoStructName}}() {{.GoStructName}} { +func new{{.GoStructName}}(schemaName, tableName, alias string) {{.GoStructName}} { var ( {{- range .Columns}} {{ToGoIdentifier .Name}}Column = {{dialect.PackageName}}.{{.SqlBuilderColumnType}}Column("{{.Name}}") @@ -63,7 +57,7 @@ func new{{.GoStructName}}() {{.GoStructName}} { ) return {{.GoStructName}}{ - Table: {{dialect.PackageName}}.NewTable("{{.SchemaName}}", "{{.Name}}", allColumns...), + Table: {{dialect.PackageName}}.NewTable(schemaName, tableName, alias, allColumns...), //Columns {{- range .Columns}} @@ -89,7 +83,7 @@ import ( "github.com/go-jet/jet/v2/{{dialect.PackageName}}" ) -var {{ToGoIdentifier .Name}} = new{{.GoStructName}}() +var {{ToGoIdentifier .Name}} = new{{.GoStructName}}("{{.SchemaName}}", "{{.Name}}", "") type {{.GoStructImplName}} struct { {{dialect.PackageName}}.Table @@ -110,29 +104,23 @@ type {{.GoStructName}} struct { } // AS creates new {{.GoStructName}} with assigned alias -func (a *{{.GoStructName}}) AS(alias string) *{{.GoStructName}} { - aliasTable := new{{.GoStructName}}() - aliasTable.Table.AS(alias) - aliasTable.Table.Schema(a.Table.SchemaName()) - return aliasTable +func (a {{.GoStructName}}) AS(alias string) *{{.GoStructName}} { + return new{{.GoStructName}}(a.SchemaName(), a.TableName(), alias) } // Schema creates new {{.GoStructName}} with assigned schema name -func (a *{{.GoStructName}}) Schema(schemaName string) *{{.GoStructName}} { - aliasTable := new{{.GoStructName}}() - aliasTable.Table.AS(a.Table.Alias()) - aliasTable.Table.Schema(schemaName) - return aliasTable +func (a {{.GoStructName}}) FromSchema(schemaName string) *{{.GoStructName}} { + return new{{.GoStructName}}(schemaName, a.TableName(), a.Alias()) } -func new{{.GoStructName}}() *{{.GoStructName}} { +func new{{.GoStructName}}(schemaName, tableName, alias string) *{{.GoStructName}} { return &{{.GoStructName}}{ - {{.GoStructImplName}}: new{{.GoStructName}}Impl("{{.SchemaName}}", "{{.Name}}"), - EXCLUDED: new{{.GoStructName}}Impl("", "excluded"), + {{.GoStructImplName}}: new{{.GoStructName}}Impl(schemaName, tableName, alias), + EXCLUDED: new{{.GoStructName}}Impl("", "excluded", ""), } } -func new{{.GoStructName}}Impl(schemaName, tableName string) {{.GoStructImplName}} { +func new{{.GoStructName}}Impl(schemaName, tableName, alias string) {{.GoStructImplName}} { var ( {{- range .Columns}} {{ToGoIdentifier .Name}}Column = {{dialect.PackageName}}.{{.SqlBuilderColumnType}}Column("{{.Name}}") @@ -142,7 +130,7 @@ func new{{.GoStructName}}Impl(schemaName, tableName string) {{.GoStructImplName} ) return {{.GoStructImplName}}{ - Table: {{dialect.PackageName}}.NewTable(schemaName, tableName, allColumns...), + Table: {{dialect.PackageName}}.NewTable(schemaName, tableName, alias, allColumns...), //Columns {{- range .Columns}} diff --git a/mysql/table.go b/mysql/table.go index 5630966b..0ae7ee8e 100644 --- a/mysql/table.go +++ b/mysql/table.go @@ -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 -func NewTable(schemaName, name string, columns ...jet.ColumnExpression) Table { +func NewTable(schemaName, name, alias string, columns ...jet.ColumnExpression) Table { t := &tableImpl{ - SerializerTable: jet.NewTable(schemaName, name, columns...), + SerializerTable: jet.NewTable(schemaName, name, alias, columns...), } t.readableTableInterfaceImpl.parent = t diff --git a/mysql/utils_test.go b/mysql/utils_test.go index cc8b3c38..d95638a8 100644 --- a/mysql/utils_test.go +++ b/mysql/utils_test.go @@ -16,19 +16,7 @@ var table1ColTimestamp = TimestampColumn("col_timestamp") var table1ColDate = DateColumn("col_date") var table1ColTime = TimeColumn("col_time") -var table1 = NewTable( - "db", - "table1", - table1Col1, - table1ColInt, - table1ColFloat, - table1ColString, - table1Col3, - table1ColBool, - table1ColDate, - table1ColTimestamp, - table1ColTime, -) +var table1 = NewTable("db", "table1", "", table1Col1, table1ColInt, table1ColFloat, table1ColString, table1Col3, table1ColBool, table1ColDate, table1ColTimestamp, table1ColTime) var table2Col3 = IntegerColumn("col3") var table2Col4 = IntegerColumn("col4") @@ -39,28 +27,12 @@ var table2ColBool = BoolColumn("col_bool") var table2ColTimestamp = TimestampColumn("col_timestamp") var table2ColDate = DateColumn("col_date") -var table2 = NewTable( - "db", - "table2", - table2Col3, - table2Col4, - table2ColInt, - table2ColFloat, - table2ColStr, - table2ColBool, - table2ColDate, - table2ColTimestamp, -) +var table2 = NewTable("db", "table2", "", table2Col3, table2Col4, table2ColInt, table2ColFloat, table2ColStr, table2ColBool, table2ColDate, table2ColTimestamp) var table3Col1 = IntegerColumn("col1") var table3ColInt = IntegerColumn("col_int") var table3StrCol = StringColumn("col2") -var table3 = NewTable( - "db", - "table3", - table3Col1, - table3ColInt, - table3StrCol) +var table3 = NewTable("db", "table3", "", table3Col1, table3ColInt, table3StrCol) func assertSerialize(t *testing.T, clause jet.Serializer, query string, args ...interface{}) { testutils.AssertSerialize(t, Dialect, clause, query, args...) diff --git a/postgres/dialect_test.go b/postgres/dialect_test.go index 7bf92425..9b7b3d16 100644 --- a/postgres/dialect_test.go +++ b/postgres/dialect_test.go @@ -80,13 +80,7 @@ func TestReservedWordEscaped(t *testing.T) { var table1ColVariadic = IntervalColumn("VARIADIC") var table1ColProcedure = IntervalColumn("procedure") - _ = NewTable( - "db", - "table1", - table1ColUser, - table1ColVariadic, - table1ColProcedure, - ) + _ = NewTable("db", "table1", "", table1ColUser, table1ColVariadic, table1ColProcedure) assertSerialize(t, table1ColUser, `table1."user"`) assertSerialize(t, table1ColVariadic, `table1."VARIADIC"`) diff --git a/postgres/table.go b/postgres/table.go index ac153d30..f90c114b 100644 --- a/postgres/table.go +++ b/postgres/table.go @@ -109,10 +109,10 @@ type tableImpl struct { } // 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{ - SerializerTable: jet.NewTable(schemaName, name, columns...), + SerializerTable: jet.NewTable(schemaName, name, alias, columns...), } t.readableTableInterfaceImpl.parent = t diff --git a/postgres/utils_test.go b/postgres/utils_test.go index ef5f72e6..bd59b432 100644 --- a/postgres/utils_test.go +++ b/postgres/utils_test.go @@ -21,6 +21,7 @@ var table1ColInterval = IntervalColumn("col_interval") var table1 = NewTable( "db", "table1", + "", table1Col1, table1ColInt, table1ColFloat, @@ -46,32 +47,12 @@ var table2ColTimestampz = TimestampzColumn("col_timestampz") var table2ColDate = DateColumn("col_date") var table2ColInterval = IntervalColumn("col_interval") -var table2 = NewTable( - "db", - "table2", - table2Col3, - table2Col4, - table2ColInt, - table2ColFloat, - table2ColStr, - table2ColBool, - table2ColTime, - table2ColTimez, - table2ColDate, - table2ColTimestamp, - table2ColTimestampz, - table2ColInterval, -) +var table2 = NewTable("db", "table2", "", table2Col3, table2Col4, table2ColInt, table2ColFloat, table2ColStr, table2ColBool, table2ColTime, table2ColTimez, table2ColDate, table2ColTimestamp, table2ColTimestampz, table2ColInterval) var table3Col1 = IntegerColumn("col1") var table3ColInt = IntegerColumn("col_int") var table3StrCol = StringColumn("col2") -var table3 = NewTable( - "db", - "table3", - table3Col1, - table3ColInt, - table3StrCol) +var table3 = NewTable("db", "table3", "", table3Col1, table3ColInt, table3StrCol) func assertSerialize(t *testing.T, serializer jet.Serializer, query string, args ...interface{}) { testutils.AssertSerialize(t, Dialect, serializer, query, args...) diff --git a/tests/init/init.go b/tests/init/init.go index 356806d3..a2f6eb39 100644 --- a/tests/init/init.go +++ b/tests/init/init.go @@ -46,6 +46,7 @@ func initMySQLDB() { mySQLDBs := []string{ "dvds", + "dvds2", "test_sample", } @@ -89,6 +90,7 @@ func initPostgresDB() { "dvds", "test_sample", "chinook", + "chinook2", "northwind", } diff --git a/tests/mysql/alltypes_test.go b/tests/mysql/alltypes_test.go index 57287258..6fbb1364 100644 --- a/tests/mysql/alltypes_test.go +++ b/tests/mysql/alltypes_test.go @@ -1,7 +1,6 @@ package mysql import ( - "fmt" "github.com/stretchr/testify/require" "strings" "testing" @@ -974,7 +973,7 @@ func TestAllTypesInsert(t *testing.T) { stmt := AllTypes.INSERT(AllTypes.AllColumns). MODEL(toInsert) - fmt.Println(stmt.DebugSql()) + //fmt.Println(stmt.DebugSql()) testutils.AssertExec(t, stmt, tx, 1) @@ -1028,7 +1027,7 @@ func TestAllTypesInsertOnDuplicateKeyUpdate(t *testing.T) { AllTypes.Date.SET(DateT(time.Now())), ) - fmt.Println(stmt.DebugSql()) + //fmt.Println(stmt.DebugSql()) _, err = stmt.Exec(tx) require.NoError(t, err) @@ -1257,7 +1256,7 @@ FROM test_sample.user; err := stmt.Query(db, &dest) require.NoError(t, err) - testutils.PrintJson(dest) + //testutils.PrintJson(dest) testutils.AssertJSON(t, dest, ` [ diff --git a/tests/mysql/generator_test.go b/tests/mysql/generator_test.go index 1e9a51da..c9dcc1a6 100644 --- a/tests/mysql/generator_test.go +++ b/tests/mysql/generator_test.go @@ -135,7 +135,7 @@ import ( "github.com/go-jet/jet/v2/mysql" ) -var Actor = newActorTable() +var Actor = newActorTable("dvds", "actor", "") type ActorTable struct { mysql.Table @@ -151,13 +151,16 @@ type ActorTable struct { } // AS creates new ActorTable with assigned alias -func (a *ActorTable) AS(alias string) ActorTable { - aliasTable := newActorTable() - aliasTable.Table.AS(alias) - return aliasTable +func (a ActorTable) AS(alias string) ActorTable { + return newActorTable(a.SchemaName(), a.TableName(), alias) } -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 ( ActorIDColumn = mysql.IntegerColumn("actor_id") FirstNameColumn = mysql.StringColumn("first_name") @@ -168,7 +171,7 @@ func newActorTable() ActorTable { ) return ActorTable{ - Table: mysql.NewTable("dvds", "actor", allColumns...), + Table: mysql.NewTable(schemaName, tableName, alias, allColumns...), //Columns ActorID: ActorIDColumn, @@ -218,7 +221,7 @@ import ( "github.com/go-jet/jet/v2/mysql" ) -var ActorInfo = newActorInfoTable() +var ActorInfo = newActorInfoTable("dvds", "actor_info", "") type ActorInfoTable struct { mysql.Table @@ -234,13 +237,16 @@ type ActorInfoTable struct { } // AS creates new ActorInfoTable with assigned alias -func (a *ActorInfoTable) AS(alias string) ActorInfoTable { - aliasTable := newActorInfoTable() - aliasTable.Table.AS(alias) - return aliasTable +func (a ActorInfoTable) AS(alias string) ActorInfoTable { + return newActorInfoTable(a.SchemaName(), a.TableName(), alias) +} + +// Schema creates new ActorInfoTable with assigned schema name +func (a ActorInfoTable) FromSchema(schemaName string) ActorInfoTable { + return newActorInfoTable(schemaName, a.TableName(), a.Alias()) } -func newActorInfoTable() ActorInfoTable { +func newActorInfoTable(schemaName, tableName, alias string) ActorInfoTable { var ( ActorIDColumn = mysql.IntegerColumn("actor_id") FirstNameColumn = mysql.StringColumn("first_name") @@ -251,7 +257,7 @@ func newActorInfoTable() ActorInfoTable { ) return ActorInfoTable{ - Table: mysql.NewTable("dvds", "actor_info", allColumns...), + Table: mysql.NewTable(schemaName, tableName, alias, allColumns...), //Columns ActorID: ActorIDColumn, diff --git a/tests/mysql/select_test.go b/tests/mysql/select_test.go index dc6af0d2..f447218e 100644 --- a/tests/mysql/select_test.go +++ b/tests/mysql/select_test.go @@ -744,3 +744,46 @@ LIMIT 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") +} diff --git a/tests/mysql/update_test.go b/tests/mysql/update_test.go index bef22f97..3b6aa759 100644 --- a/tests/mysql/update_test.go +++ b/tests/mysql/update_test.go @@ -2,7 +2,6 @@ package mysql import ( "context" - "fmt" "github.com/go-jet/jet/v2/internal/testutils" . "github.com/go-jet/jet/v2/mysql" "github.com/go-jet/jet/v2/tests/.gentestdata/mysql/dvds/table" @@ -193,7 +192,7 @@ SET url = 'http://www.duckduckgo.com', description = NULL 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.AssertExec(t, stmt, db) diff --git a/tests/postgres/chinook_db_test.go b/tests/postgres/chinook_db_test.go index 50f5e607..553f920a 100644 --- a/tests/postgres/chinook_db_test.go +++ b/tests/postgres/chinook_db_test.go @@ -2,7 +2,6 @@ package postgres import ( "context" - "fmt" "github.com/go-jet/jet/v2/internal/testutils" . "github.com/go-jet/jet/v2/postgres" "github.com/go-jet/jet/v2/tests/.gentestdata/jetdb/chinook/model" @@ -17,7 +16,7 @@ func TestSelect(t *testing.T) { SELECT(Album.AllColumns). ORDER_BY(Album.AlbumId.ASC()) - fmt.Println(stmt.DebugSql()) + //fmt.Println(stmt.DebugSql()) testutils.AssertDebugStatementSql(t, stmt, ` SELECT "Album"."AlbumId" AS "Album.AlbumId", @@ -330,8 +329,71 @@ ORDER BY "first10Artist"."Artist.ArtistId"; err := stmt.Query(db, &dest) require.NoError(t, err) +} + +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) - //spew.Dump(dest) + 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{ diff --git a/tests/postgres/generator_test.go b/tests/postgres/generator_test.go index 1e23016f..833e2a43 100644 --- a/tests/postgres/generator_test.go +++ b/tests/postgres/generator_test.go @@ -168,7 +168,7 @@ import ( "github.com/go-jet/jet/v2/postgres" ) -var Actor = newActorTable() +var Actor = newActorTable("dvds", "actor", "") type actorTable struct { postgres.Table @@ -190,20 +190,23 @@ type ActorTable struct { } // AS creates new ActorTable with assigned alias -func (a *ActorTable) AS(alias string) *ActorTable { - aliasTable := newActorTable() - aliasTable.Table.AS(alias) - return aliasTable +func (a ActorTable) AS(alias string) *ActorTable { + return newActorTable(a.SchemaName(), a.TableName(), alias) } -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{ - actorTable: newActorTableImpl("dvds", "actor"), - EXCLUDED: newActorTableImpl("", "excluded"), + actorTable: newActorTableImpl(schemaName, tableName, alias), + EXCLUDED: newActorTableImpl("", "excluded", ""), } } -func newActorTableImpl(schemaName, tableName string) actorTable { +func newActorTableImpl(schemaName, tableName, alias string) actorTable { var ( ActorIDColumn = postgres.IntegerColumn("actor_id") FirstNameColumn = postgres.StringColumn("first_name") @@ -214,7 +217,7 @@ func newActorTableImpl(schemaName, tableName string) actorTable { ) return actorTable{ - Table: postgres.NewTable(schemaName, tableName, allColumns...), + Table: postgres.NewTable(schemaName, tableName, alias, allColumns...), //Columns ActorID: ActorIDColumn, @@ -264,7 +267,7 @@ import ( "github.com/go-jet/jet/v2/postgres" ) -var ActorInfo = newActorInfoTable() +var ActorInfo = newActorInfoTable("dvds", "actor_info", "") type actorInfoTable struct { postgres.Table @@ -286,20 +289,23 @@ type ActorInfoTable struct { } // AS creates new ActorInfoTable with assigned alias -func (a *ActorInfoTable) AS(alias string) *ActorInfoTable { - aliasTable := newActorInfoTable() - aliasTable.Table.AS(alias) - return aliasTable +func (a ActorInfoTable) AS(alias string) *ActorInfoTable { + return newActorInfoTable(a.SchemaName(), a.TableName(), alias) +} + +// Schema creates new ActorInfoTable with assigned schema name +func (a ActorInfoTable) FromSchema(schemaName string) *ActorInfoTable { + return newActorInfoTable(schemaName, a.TableName(), a.Alias()) } -func newActorInfoTable() *ActorInfoTable { +func newActorInfoTable(schemaName, tableName, alias string) *ActorInfoTable { return &ActorInfoTable{ - actorInfoTable: newActorInfoTableImpl("dvds", "actor_info"), - EXCLUDED: newActorInfoTableImpl("", "excluded"), + actorInfoTable: newActorInfoTableImpl(schemaName, tableName, alias), + EXCLUDED: newActorInfoTableImpl("", "excluded", ""), } } -func newActorInfoTableImpl(schemaName, tableName string) actorInfoTable { +func newActorInfoTableImpl(schemaName, tableName, alias string) actorInfoTable { var ( ActorIDColumn = postgres.IntegerColumn("actor_id") FirstNameColumn = postgres.StringColumn("first_name") @@ -310,7 +316,7 @@ func newActorInfoTableImpl(schemaName, tableName string) actorInfoTable { ) return actorInfoTable{ - Table: postgres.NewTable(schemaName, tableName, allColumns...), + Table: postgres.NewTable(schemaName, tableName, alias, allColumns...), //Columns ActorID: ActorIDColumn, @@ -497,7 +503,7 @@ import ( "github.com/go-jet/jet/v2/postgres" ) -var AllTypes = newAllTypesTable() +var AllTypes = newAllTypesTable("test_sample", "all_types", "") type allTypesTable struct { postgres.Table @@ -576,20 +582,23 @@ type AllTypesTable struct { } // AS creates new AllTypesTable with assigned alias -func (a *AllTypesTable) AS(alias string) *AllTypesTable { - aliasTable := newAllTypesTable() - aliasTable.Table.AS(alias) - return aliasTable +func (a AllTypesTable) AS(alias string) *AllTypesTable { + return newAllTypesTable(a.SchemaName(), a.TableName(), alias) +} + +// Schema creates new AllTypesTable with assigned schema name +func (a AllTypesTable) FromSchema(schemaName string) *AllTypesTable { + return newAllTypesTable(schemaName, a.TableName(), a.Alias()) } -func newAllTypesTable() *AllTypesTable { +func newAllTypesTable(schemaName, tableName, alias string) *AllTypesTable { return &AllTypesTable{ - allTypesTable: newAllTypesTableImpl("test_sample", "all_types"), - EXCLUDED: newAllTypesTableImpl("", "excluded"), + allTypesTable: newAllTypesTableImpl(schemaName, tableName, alias), + EXCLUDED: newAllTypesTableImpl("", "excluded", ""), } } -func newAllTypesTableImpl(schemaName, tableName string) allTypesTable { +func newAllTypesTableImpl(schemaName, tableName, alias string) allTypesTable { var ( SmallIntPtrColumn = postgres.IntegerColumn("small_int_ptr") SmallIntColumn = postgres.IntegerColumn("small_int") @@ -657,7 +666,7 @@ func newAllTypesTableImpl(schemaName, tableName string) allTypesTable { ) return allTypesTable{ - Table: postgres.NewTable(schemaName, tableName, allColumns...), + Table: postgres.NewTable(schemaName, tableName, alias, allColumns...), //Columns SmallIntPtr: SmallIntPtrColumn, diff --git a/tests/postgres/sample_test.go b/tests/postgres/sample_test.go index bcb7361a..327604f0 100644 --- a/tests/postgres/sample_test.go +++ b/tests/postgres/sample_test.go @@ -364,7 +364,7 @@ FROM test_sample."User"; err := stmt.Query(db, &dest) require.NoError(t, err) - testutils.PrintJson(dest) + //testutils.PrintJson(dest) testutils.AssertJSON(t, dest, ` [ diff --git a/tests/postgres/scan_test.go b/tests/postgres/scan_test.go index 8eb32764..def30979 100644 --- a/tests/postgres/scan_test.go +++ b/tests/postgres/scan_test.go @@ -1,7 +1,6 @@ package postgres import ( - "fmt" "github.com/go-jet/jet/v2/internal/testutils" . "github.com/go-jet/jet/v2/postgres" "github.com/go-jet/jet/v2/qrm" @@ -93,7 +92,7 @@ func TestScanToStruct(t *testing.T) { SELECT(Inventory.AllColumns). ORDER_BY(Inventory.InventoryID) - fmt.Println(query.DebugSql()) + //fmt.Println(query.DebugSql()) t.Run("one struct", func(t *testing.T) { dest := model.Inventory{} diff --git a/tests/postgres/select_test.go b/tests/postgres/select_test.go index e9dd7d2a..8bd6f53d 100644 --- a/tests/postgres/select_test.go +++ b/tests/postgres/select_test.go @@ -1,7 +1,6 @@ package postgres import ( - "fmt" "github.com/go-jet/jet/v2/internal/testutils" . "github.com/go-jet/jet/v2/postgres" "github.com/go-jet/jet/v2/tests/.gentestdata/jetdb/dvds/enum" @@ -1255,7 +1254,7 @@ OFFSET 20; LIMIT(10). OFFSET(20) - fmt.Println(query.DebugSql()) + //fmt.Println(query.DebugSql()) testutils.AssertDebugStatementSql(t, query, expectedQuery, float64(100), float64(200), int64(10), int64(20)) @@ -1788,7 +1787,7 @@ func TestJoinViewWithTable(t *testing.T) { Rentals []model.Rental } - fmt.Println(query.DebugSql()) + //fmt.Println(query.DebugSql()) err := query.Query(db, &dest) require.NoError(t, err) diff --git a/tests/testdata b/tests/testdata index ed53a505..391d9365 160000 --- a/tests/testdata +++ b/tests/testdata @@ -1 +1 @@ -Subproject commit ed53a505eb738d1be457877eee251f9ba0418df1 +Subproject commit 391d936515d2f826df073707697de44907a7f67d From 616f27306e3207694de9cf7e09517971fdd58802 Mon Sep 17 00:00:00 2001 From: go-jet Date: Sun, 21 Mar 2021 17:18:43 +0100 Subject: [PATCH 16/41] Update quick-start example with updated auto generated files --- .../.gen/jetdb/dvds/table/actor.go | 23 +++++++++++-------- .../.gen/jetdb/dvds/table/category.go | 23 +++++++++++-------- .../quick-start/.gen/jetdb/dvds/table/film.go | 23 +++++++++++-------- .../.gen/jetdb/dvds/table/film_actor.go | 23 +++++++++++-------- .../.gen/jetdb/dvds/table/film_category.go | 23 +++++++++++-------- .../.gen/jetdb/dvds/table/language.go | 23 +++++++++++-------- .../.gen/jetdb/dvds/view/actor_info.go | 23 +++++++++++-------- .../.gen/jetdb/dvds/view/customer_list.go | 23 +++++++++++-------- 8 files changed, 104 insertions(+), 80 deletions(-) diff --git a/examples/quick-start/.gen/jetdb/dvds/table/actor.go b/examples/quick-start/.gen/jetdb/dvds/table/actor.go index 376d73e1..bbeb7e8f 100644 --- a/examples/quick-start/.gen/jetdb/dvds/table/actor.go +++ b/examples/quick-start/.gen/jetdb/dvds/table/actor.go @@ -11,7 +11,7 @@ import ( "github.com/go-jet/jet/v2/postgres" ) -var Actor = newActorTable() +var Actor = newActorTable("dvds", "actor", "") type actorTable struct { postgres.Table @@ -33,20 +33,23 @@ type ActorTable struct { } // AS creates new ActorTable with assigned alias -func (a *ActorTable) AS(alias string) *ActorTable { - aliasTable := newActorTable() - aliasTable.Table.AS(alias) - return aliasTable +func (a ActorTable) AS(alias string) *ActorTable { + return newActorTable(a.SchemaName(), a.TableName(), alias) } -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{ - actorTable: newActorTableImpl("dvds", "actor"), - EXCLUDED: newActorTableImpl("", "excluded"), + actorTable: newActorTableImpl(schemaName, tableName, alias), + EXCLUDED: newActorTableImpl("", "excluded", ""), } } -func newActorTableImpl(schemaName, tableName string) actorTable { +func newActorTableImpl(schemaName, tableName, alias string) actorTable { var ( ActorIDColumn = postgres.IntegerColumn("actor_id") FirstNameColumn = postgres.StringColumn("first_name") @@ -57,7 +60,7 @@ func newActorTableImpl(schemaName, tableName string) actorTable { ) return actorTable{ - Table: postgres.NewTable(schemaName, tableName, allColumns...), + Table: postgres.NewTable(schemaName, tableName, alias, allColumns...), //Columns ActorID: ActorIDColumn, diff --git a/examples/quick-start/.gen/jetdb/dvds/table/category.go b/examples/quick-start/.gen/jetdb/dvds/table/category.go index ac2caac0..563938d9 100644 --- a/examples/quick-start/.gen/jetdb/dvds/table/category.go +++ b/examples/quick-start/.gen/jetdb/dvds/table/category.go @@ -11,7 +11,7 @@ import ( "github.com/go-jet/jet/v2/postgres" ) -var Category = newCategoryTable() +var Category = newCategoryTable("dvds", "category", "") type categoryTable struct { postgres.Table @@ -32,20 +32,23 @@ type CategoryTable struct { } // AS creates new CategoryTable with assigned alias -func (a *CategoryTable) AS(alias string) *CategoryTable { - aliasTable := newCategoryTable() - aliasTable.Table.AS(alias) - return aliasTable +func (a CategoryTable) AS(alias string) *CategoryTable { + return newCategoryTable(a.SchemaName(), a.TableName(), alias) } -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{ - categoryTable: newCategoryTableImpl("dvds", "category"), - EXCLUDED: newCategoryTableImpl("", "excluded"), + categoryTable: newCategoryTableImpl(schemaName, tableName, alias), + EXCLUDED: newCategoryTableImpl("", "excluded", ""), } } -func newCategoryTableImpl(schemaName, tableName string) categoryTable { +func newCategoryTableImpl(schemaName, tableName, alias string) categoryTable { var ( CategoryIDColumn = postgres.IntegerColumn("category_id") NameColumn = postgres.StringColumn("name") @@ -55,7 +58,7 @@ func newCategoryTableImpl(schemaName, tableName string) categoryTable { ) return categoryTable{ - Table: postgres.NewTable(schemaName, tableName, allColumns...), + Table: postgres.NewTable(schemaName, tableName, alias, allColumns...), //Columns CategoryID: CategoryIDColumn, diff --git a/examples/quick-start/.gen/jetdb/dvds/table/film.go b/examples/quick-start/.gen/jetdb/dvds/table/film.go index 2f8c68e6..65db900a 100644 --- a/examples/quick-start/.gen/jetdb/dvds/table/film.go +++ b/examples/quick-start/.gen/jetdb/dvds/table/film.go @@ -11,7 +11,7 @@ import ( "github.com/go-jet/jet/v2/postgres" ) -var Film = newFilmTable() +var Film = newFilmTable("dvds", "film", "") type filmTable struct { postgres.Table @@ -42,20 +42,23 @@ type FilmTable struct { } // AS creates new FilmTable with assigned alias -func (a *FilmTable) AS(alias string) *FilmTable { - aliasTable := newFilmTable() - aliasTable.Table.AS(alias) - return aliasTable +func (a FilmTable) AS(alias string) *FilmTable { + return newFilmTable(a.SchemaName(), a.TableName(), alias) } -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{ - filmTable: newFilmTableImpl("dvds", "film"), - EXCLUDED: newFilmTableImpl("", "excluded"), + filmTable: newFilmTableImpl(schemaName, tableName, alias), + EXCLUDED: newFilmTableImpl("", "excluded", ""), } } -func newFilmTableImpl(schemaName, tableName string) filmTable { +func newFilmTableImpl(schemaName, tableName, alias string) filmTable { var ( FilmIDColumn = postgres.IntegerColumn("film_id") TitleColumn = postgres.StringColumn("title") @@ -75,7 +78,7 @@ func newFilmTableImpl(schemaName, tableName string) filmTable { ) return filmTable{ - Table: postgres.NewTable(schemaName, tableName, allColumns...), + Table: postgres.NewTable(schemaName, tableName, alias, allColumns...), //Columns FilmID: FilmIDColumn, diff --git a/examples/quick-start/.gen/jetdb/dvds/table/film_actor.go b/examples/quick-start/.gen/jetdb/dvds/table/film_actor.go index 1f6bb575..30c3ad35 100644 --- a/examples/quick-start/.gen/jetdb/dvds/table/film_actor.go +++ b/examples/quick-start/.gen/jetdb/dvds/table/film_actor.go @@ -11,7 +11,7 @@ import ( "github.com/go-jet/jet/v2/postgres" ) -var FilmActor = newFilmActorTable() +var FilmActor = newFilmActorTable("dvds", "film_actor", "") type filmActorTable struct { postgres.Table @@ -32,20 +32,23 @@ type FilmActorTable struct { } // AS creates new FilmActorTable with assigned alias -func (a *FilmActorTable) AS(alias string) *FilmActorTable { - aliasTable := newFilmActorTable() - aliasTable.Table.AS(alias) - return aliasTable +func (a FilmActorTable) AS(alias string) *FilmActorTable { + return newFilmActorTable(a.SchemaName(), a.TableName(), alias) } -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{ - filmActorTable: newFilmActorTableImpl("dvds", "film_actor"), - EXCLUDED: newFilmActorTableImpl("", "excluded"), + filmActorTable: newFilmActorTableImpl(schemaName, tableName, alias), + EXCLUDED: newFilmActorTableImpl("", "excluded", ""), } } -func newFilmActorTableImpl(schemaName, tableName string) filmActorTable { +func newFilmActorTableImpl(schemaName, tableName, alias string) filmActorTable { var ( ActorIDColumn = postgres.IntegerColumn("actor_id") FilmIDColumn = postgres.IntegerColumn("film_id") @@ -55,7 +58,7 @@ func newFilmActorTableImpl(schemaName, tableName string) filmActorTable { ) return filmActorTable{ - Table: postgres.NewTable(schemaName, tableName, allColumns...), + Table: postgres.NewTable(schemaName, tableName, alias, allColumns...), //Columns ActorID: ActorIDColumn, diff --git a/examples/quick-start/.gen/jetdb/dvds/table/film_category.go b/examples/quick-start/.gen/jetdb/dvds/table/film_category.go index 41fa6f33..83681777 100644 --- a/examples/quick-start/.gen/jetdb/dvds/table/film_category.go +++ b/examples/quick-start/.gen/jetdb/dvds/table/film_category.go @@ -11,7 +11,7 @@ import ( "github.com/go-jet/jet/v2/postgres" ) -var FilmCategory = newFilmCategoryTable() +var FilmCategory = newFilmCategoryTable("dvds", "film_category", "") type filmCategoryTable struct { postgres.Table @@ -32,20 +32,23 @@ type FilmCategoryTable struct { } // AS creates new FilmCategoryTable with assigned alias -func (a *FilmCategoryTable) AS(alias string) *FilmCategoryTable { - aliasTable := newFilmCategoryTable() - aliasTable.Table.AS(alias) - return aliasTable +func (a FilmCategoryTable) AS(alias string) *FilmCategoryTable { + return newFilmCategoryTable(a.SchemaName(), a.TableName(), alias) } -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{ - filmCategoryTable: newFilmCategoryTableImpl("dvds", "film_category"), - EXCLUDED: newFilmCategoryTableImpl("", "excluded"), + filmCategoryTable: newFilmCategoryTableImpl(schemaName, tableName, alias), + EXCLUDED: newFilmCategoryTableImpl("", "excluded", ""), } } -func newFilmCategoryTableImpl(schemaName, tableName string) filmCategoryTable { +func newFilmCategoryTableImpl(schemaName, tableName, alias string) filmCategoryTable { var ( FilmIDColumn = postgres.IntegerColumn("film_id") CategoryIDColumn = postgres.IntegerColumn("category_id") @@ -55,7 +58,7 @@ func newFilmCategoryTableImpl(schemaName, tableName string) filmCategoryTable { ) return filmCategoryTable{ - Table: postgres.NewTable(schemaName, tableName, allColumns...), + Table: postgres.NewTable(schemaName, tableName, alias, allColumns...), //Columns FilmID: FilmIDColumn, diff --git a/examples/quick-start/.gen/jetdb/dvds/table/language.go b/examples/quick-start/.gen/jetdb/dvds/table/language.go index 89470c24..5bddeebb 100644 --- a/examples/quick-start/.gen/jetdb/dvds/table/language.go +++ b/examples/quick-start/.gen/jetdb/dvds/table/language.go @@ -11,7 +11,7 @@ import ( "github.com/go-jet/jet/v2/postgres" ) -var Language = newLanguageTable() +var Language = newLanguageTable("dvds", "language", "") type languageTable struct { postgres.Table @@ -32,20 +32,23 @@ type LanguageTable struct { } // AS creates new LanguageTable with assigned alias -func (a *LanguageTable) AS(alias string) *LanguageTable { - aliasTable := newLanguageTable() - aliasTable.Table.AS(alias) - return aliasTable +func (a LanguageTable) AS(alias string) *LanguageTable { + return newLanguageTable(a.SchemaName(), a.TableName(), alias) } -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{ - languageTable: newLanguageTableImpl("dvds", "language"), - EXCLUDED: newLanguageTableImpl("", "excluded"), + languageTable: newLanguageTableImpl(schemaName, tableName, alias), + EXCLUDED: newLanguageTableImpl("", "excluded", ""), } } -func newLanguageTableImpl(schemaName, tableName string) languageTable { +func newLanguageTableImpl(schemaName, tableName, alias string) languageTable { var ( LanguageIDColumn = postgres.IntegerColumn("language_id") NameColumn = postgres.StringColumn("name") @@ -55,7 +58,7 @@ func newLanguageTableImpl(schemaName, tableName string) languageTable { ) return languageTable{ - Table: postgres.NewTable(schemaName, tableName, allColumns...), + Table: postgres.NewTable(schemaName, tableName, alias, allColumns...), //Columns LanguageID: LanguageIDColumn, diff --git a/examples/quick-start/.gen/jetdb/dvds/view/actor_info.go b/examples/quick-start/.gen/jetdb/dvds/view/actor_info.go index 65d25601..5bfa25d3 100644 --- a/examples/quick-start/.gen/jetdb/dvds/view/actor_info.go +++ b/examples/quick-start/.gen/jetdb/dvds/view/actor_info.go @@ -11,7 +11,7 @@ import ( "github.com/go-jet/jet/v2/postgres" ) -var ActorInfo = newActorInfoTable() +var ActorInfo = newActorInfoTable("dvds", "actor_info", "") type actorInfoTable struct { postgres.Table @@ -33,20 +33,23 @@ type ActorInfoTable struct { } // AS creates new ActorInfoTable with assigned alias -func (a *ActorInfoTable) AS(alias string) *ActorInfoTable { - aliasTable := newActorInfoTable() - aliasTable.Table.AS(alias) - return aliasTable +func (a ActorInfoTable) AS(alias string) *ActorInfoTable { + return newActorInfoTable(a.SchemaName(), a.TableName(), alias) } -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{ - actorInfoTable: newActorInfoTableImpl("dvds", "actor_info"), - EXCLUDED: newActorInfoTableImpl("", "excluded"), + actorInfoTable: newActorInfoTableImpl(schemaName, tableName, alias), + EXCLUDED: newActorInfoTableImpl("", "excluded", ""), } } -func newActorInfoTableImpl(schemaName, tableName string) actorInfoTable { +func newActorInfoTableImpl(schemaName, tableName, alias string) actorInfoTable { var ( ActorIDColumn = postgres.IntegerColumn("actor_id") FirstNameColumn = postgres.StringColumn("first_name") @@ -57,7 +60,7 @@ func newActorInfoTableImpl(schemaName, tableName string) actorInfoTable { ) return actorInfoTable{ - Table: postgres.NewTable(schemaName, tableName, allColumns...), + Table: postgres.NewTable(schemaName, tableName, alias, allColumns...), //Columns ActorID: ActorIDColumn, diff --git a/examples/quick-start/.gen/jetdb/dvds/view/customer_list.go b/examples/quick-start/.gen/jetdb/dvds/view/customer_list.go index b4a2c8fc..c03a5ef5 100644 --- a/examples/quick-start/.gen/jetdb/dvds/view/customer_list.go +++ b/examples/quick-start/.gen/jetdb/dvds/view/customer_list.go @@ -11,7 +11,7 @@ import ( "github.com/go-jet/jet/v2/postgres" ) -var CustomerList = newCustomerListTable() +var CustomerList = newCustomerListTable("dvds", "customer_list", "") type customerListTable struct { postgres.Table @@ -38,20 +38,23 @@ type CustomerListTable struct { } // AS creates new CustomerListTable with assigned alias -func (a *CustomerListTable) AS(alias string) *CustomerListTable { - aliasTable := newCustomerListTable() - aliasTable.Table.AS(alias) - return aliasTable +func (a CustomerListTable) AS(alias string) *CustomerListTable { + return newCustomerListTable(a.SchemaName(), a.TableName(), alias) } -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{ - customerListTable: newCustomerListTableImpl("dvds", "customer_list"), - EXCLUDED: newCustomerListTableImpl("", "excluded"), + customerListTable: newCustomerListTableImpl(schemaName, tableName, alias), + EXCLUDED: newCustomerListTableImpl("", "excluded", ""), } } -func newCustomerListTableImpl(schemaName, tableName string) customerListTable { +func newCustomerListTableImpl(schemaName, tableName, alias string) customerListTable { var ( IDColumn = postgres.IntegerColumn("id") NameColumn = postgres.StringColumn("name") @@ -67,7 +70,7 @@ func newCustomerListTableImpl(schemaName, tableName string) customerListTable { ) return customerListTable{ - Table: postgres.NewTable(schemaName, tableName, allColumns...), + Table: postgres.NewTable(schemaName, tableName, alias, allColumns...), //Columns ID: IDColumn, From 1146afe343041cb76ce9d458ddc4caf0ed2379cf Mon Sep 17 00:00:00 2001 From: go-jet Date: Sun, 21 Mar 2021 17:23:42 +0100 Subject: [PATCH 17/41] Update circle-ci Add new test database. --- .circleci/config.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index 43eae460..35e0a6d6 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -77,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 "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 dvds2" - run: name: Init Postgres database @@ -145,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 "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 dvds2" - run: name: Init MariaDB database From 0cba1f6401957d597dbd58e0d69a0d7f46190bf9 Mon Sep 17 00:00:00 2001 From: go-jet Date: Fri, 16 Oct 2020 13:26:53 +0200 Subject: [PATCH 18/41] Lateral - initial commit. --- internal/jet/select_table.go | 22 ++++++++++++++++++++-- postgres/lateral.go | 13 +++++++++++++ postgres/lateral_test.go | 9 +++++++++ tests/postgres/select_test.go | 31 +++++++++++++++++++++++++++++++ 4 files changed, 73 insertions(+), 2 deletions(-) create mode 100644 postgres/lateral.go create mode 100644 postgres/lateral_test.go diff --git a/internal/jet/select_table.go b/internal/jet/select_table.go index 52689d4e..1421b14e 100644 --- a/internal/jet/select_table.go +++ b/internal/jet/select_table.go @@ -13,8 +13,8 @@ type selectTableImpl struct { } // NewSelectTable func -func NewSelectTable(selectStmt SerializerStatement, alias string) SelectTable { - selectTable := &selectTableImpl{selectStmt: selectStmt, alias: alias} +func NewSelectTable(selectStmt SerializerStatement, alias string) selectTableImpl { + selectTable := selectTableImpl{selectStmt: selectStmt, alias: alias} return selectTable } @@ -38,3 +38,21 @@ func (s selectTableImpl) serialize(statement StatementType, out *SQLBuilder, opt out.WriteString("AS") out.WriteIdentifier(s.alias) } + +// -------------------------------------- + +type lateralImpl struct { + selectTableImpl +} + +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) +} diff --git a/postgres/lateral.go b/postgres/lateral.go new file mode 100644 index 00000000..fbbe9530 --- /dev/null +++ b/postgres/lateral.go @@ -0,0 +1,13 @@ +package postgres + +import "github.com/go-jet/jet/v2/internal/jet" + +func LATERAL(selectStmt SelectStatement, alias string) SelectTable { + subQuery := &selectTableImpl{ + SelectTable: jet.NewLateral(selectStmt, alias), + } + + subQuery.readableTableInterfaceImpl.parent = subQuery + + return subQuery +} diff --git a/postgres/lateral_test.go b/postgres/lateral_test.go new file mode 100644 index 00000000..a401a1e9 --- /dev/null +++ b/postgres/lateral_test.go @@ -0,0 +1,9 @@ +package postgres + +import "testing" + +func TestLATERAL(t *testing.T) { + assertSerialize(t, LATERAL(SELECT(Int(1)), "lat1"), `LATERAL ( + SELECT $1 +) AS lat1`) +} diff --git a/tests/postgres/select_test.go b/tests/postgres/select_test.go index 8bd6f53d..afa72304 100644 --- a/tests/postgres/select_test.go +++ b/tests/postgres/select_test.go @@ -1893,3 +1893,34 @@ WHERE ($1 AND (customer.customer_id = $2)) AND (customer.activebool = $3); require.Len(t, dest, 1) 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)), + ), + "films") + + stmt := SELECT( + Film.AllColumns, + languages.AllColumns(), + ).FROM( + Film.CROSS_JOIN(languages), + ).WHERE( + Film.FilmID.EQ(Int(1)), + ) + + var dest []struct { + model.Film + model.Language + } + + err := stmt.Query(db, &dest) + require.NoError(t, err) +} From 4ef0113f6b08f0e1ced31aaae050c2c8bac19f8f Mon Sep 17 00:00:00 2001 From: go-jet Date: Mon, 3 May 2021 18:48:15 +0200 Subject: [PATCH 19/41] Add implicit cross join support --- internal/jet/clause.go | 12 +++++++++--- internal/jet/table.go | 3 --- internal/testutils/test_utils.go | 8 +++++++- mysql/select_statement.go | 12 ++++++++---- postgres/select_statement.go | 12 ++++++++---- postgres/table_test.go | 23 +++++++++++++++++++++++ 6 files changed, 55 insertions(+), 15 deletions(-) diff --git a/internal/jet/clause.go b/internal/jet/clause.go index 8654547a..42dad7ee 100644 --- a/internal/jet/clause.go +++ b/internal/jet/clause.go @@ -45,19 +45,25 @@ func (s *ClauseSelect) Serialize(statementType StatementType, out *SQLBuilder, o // ClauseFrom struct type ClauseFrom struct { - Table Serializer + Tables []Serializer } // Serialize serializes clause into SQLBuilder 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 } out.NewLine() out.WriteString("FROM") 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() } diff --git a/internal/jet/table.go b/internal/jet/table.go index 78abb990..c69a1dcf 100644 --- a/internal/jet/table.go +++ b/internal/jet/table.go @@ -136,9 +136,6 @@ func (t *joinTableImpl) TableName() string { return "" } -func (t *joinTableImpl) AS(alias string) { -} - func (t *joinTableImpl) columns() []Column { var ret []Column diff --git a/internal/testutils/test_utils.go b/internal/testutils/test_utils.go index 556ed6c0..272cf5fd 100644 --- a/internal/testutils/test_utils.go +++ b/internal/testutils/test_utils.go @@ -8,6 +8,7 @@ import ( "github.com/go-jet/jet/v2/internal/utils" "github.com/go-jet/jet/v2/qrm" "github.com/google/uuid" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "io/ioutil" "os" @@ -116,7 +117,12 @@ func AssertDebugStatementSql(t *testing.T, query jet.Statement, expectedQuery st } debuqSql := query.DebugSql() - require.Equal(t, debuqSql, expectedQuery) + if !assert.Equal(t, debuqSql, expectedQuery) { + fmt.Println("Expected: ") + fmt.Println(expectedQuery) + fmt.Println("Got: ") + fmt.Println(debuqSql) + } } // AssertSerialize checks if clause serialize produces expected query and args diff --git a/mysql/select_statement.go b/mysql/select_statement.go index 37ac96b0..fa6dd9cc 100644 --- a/mysql/select_statement.go +++ b/mysql/select_statement.go @@ -41,7 +41,7 @@ type SelectStatement interface { Expression DISTINCT() SelectStatement - FROM(table ReadableTable) SelectStatement + FROM(tables ...ReadableTable) SelectStatement WHERE(expression BoolExpression) SelectStatement GROUP_BY(groupByClauses ...jet.GroupByClause) SelectStatement HAVING(boolExpression BoolExpression) SelectStatement @@ -70,7 +70,9 @@ func newSelectStatement(table ReadableTable, projections []Projection) SelectSta &newSelect.Limit, &newSelect.Offset, &newSelect.For, &newSelect.ShareLock) newSelect.Select.ProjectionList = projections - newSelect.From.Table = table + if table != nil { + newSelect.From.Tables = []jet.Serializer{table} + } newSelect.Limit.Count = -1 newSelect.Offset.Count = -1 newSelect.ShareLock.Name = "LOCK IN SHARE MODE" @@ -103,8 +105,10 @@ func (s *selectStatementImpl) DISTINCT() SelectStatement { return s } -func (s *selectStatementImpl) FROM(table ReadableTable) SelectStatement { - s.From.Table = table +func (s *selectStatementImpl) FROM(tables ...ReadableTable) SelectStatement { + for _, table := range tables { + s.From.Tables = append(s.From.Tables, table) + } return s } diff --git a/postgres/select_statement.go b/postgres/select_statement.go index b31909f3..a0d3e275 100644 --- a/postgres/select_statement.go +++ b/postgres/select_statement.go @@ -44,7 +44,7 @@ type SelectStatement interface { Expression DISTINCT() SelectStatement - FROM(table ReadableTable) SelectStatement + FROM(tables ...ReadableTable) SelectStatement WHERE(expression BoolExpression) SelectStatement GROUP_BY(groupByClauses ...jet.GroupByClause) SelectStatement HAVING(boolExpression BoolExpression) SelectStatement @@ -76,7 +76,9 @@ func newSelectStatement(table ReadableTable, projections []Projection) SelectSta &newSelect.Limit, &newSelect.Offset, &newSelect.For) newSelect.Select.ProjectionList = projections - newSelect.From.Table = table + if table != nil { + newSelect.From.Tables = []jet.Serializer{table} + } newSelect.Limit.Count = -1 newSelect.Offset.Count = -1 @@ -106,8 +108,10 @@ func (s *selectStatementImpl) DISTINCT() SelectStatement { return s } -func (s *selectStatementImpl) FROM(table ReadableTable) SelectStatement { - s.From.Table = table +func (s *selectStatementImpl) FROM(tables ...ReadableTable) SelectStatement { + for _, table := range tables { + s.From.Tables = append(s.From.Tables, table) + } return s } diff --git a/postgres/table_test.go b/postgres/table_test.go index 3b6f498b..fa122285 100644 --- a/postgres/table_test.go +++ b/postgres/table_test.go @@ -99,3 +99,26 @@ CROSS JOIN db.table2`) CROSS JOIN db.table2 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; +`) +} From 0f773b26d6910934c31250fef56ef594524fe6f8 Mon Sep 17 00:00:00 2001 From: go-jet Date: Mon, 3 May 2021 19:31:04 +0200 Subject: [PATCH 20/41] Add LATERAL query support --- mysql/lateral.go | 23 ++++++++ postgres/lateral.go | 14 ++++- postgres/lateral_test.go | 7 ++- tests/mysql/main_test.go | 6 ++ tests/mysql/select_test.go | 104 +++++++++++++++++++++++++++++++++- tests/postgres/select_test.go | 82 ++++++++++++++++++++++++--- 6 files changed, 224 insertions(+), 12 deletions(-) create mode 100644 mysql/lateral.go diff --git a/mysql/lateral.go b/mysql/lateral.go new file mode 100644 index 00000000..8ba974b1 --- /dev/null +++ b/mysql/lateral.go @@ -0,0 +1,23 @@ +package mysql + +import "github.com/go-jet/jet/v2/internal/jet" + +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 +} diff --git a/postgres/lateral.go b/postgres/lateral.go index fbbe9530..8d2d4f8a 100644 --- a/postgres/lateral.go +++ b/postgres/lateral.go @@ -2,9 +2,19 @@ package postgres import "github.com/go-jet/jet/v2/internal/jet" -func LATERAL(selectStmt SelectStatement, alias string) SelectTable { +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(selectStmt, alias), + SelectTable: jet.NewLateral(l.selectStmt, alias), } subQuery.readableTableInterfaceImpl.parent = subQuery diff --git a/postgres/lateral_test.go b/postgres/lateral_test.go index a401a1e9..35a04290 100644 --- a/postgres/lateral_test.go +++ b/postgres/lateral_test.go @@ -3,7 +3,12 @@ package postgres import "testing" func TestLATERAL(t *testing.T) { - assertSerialize(t, LATERAL(SELECT(Int(1)), "lat1"), `LATERAL ( + assertSerialize(t, + LATERAL( + SELECT(Int(1)), + ).AS("lat1"), + + `LATERAL ( SELECT $1 ) AS lat1`) } diff --git a/tests/mysql/main_test.go b/tests/mysql/main_test.go index 4f9268cf..e2be933a 100644 --- a/tests/mysql/main_test.go +++ b/tests/mysql/main_test.go @@ -64,3 +64,9 @@ func requireLogged(t *testing.T, statement postgres.Statement) { require.Equal(t, loggedSQLArgs, args) require.Equal(t, loggedDebugSQL, statement.DebugSql()) } + +func skipForMariaDB(t *testing.T) { + if sourceIsMariaDB() { + t.SkipNow() + } +} diff --git a/tests/mysql/select_test.go b/tests/mysql/select_test.go index f447218e..1a60a429 100644 --- a/tests/mysql/select_test.go +++ b/tests/mysql/select_test.go @@ -1,15 +1,17 @@ package mysql import ( + "strings" + "testing" + "github.com/go-jet/jet/v2/internal/testutils" . "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/model" . "github.com/go-jet/jet/v2/tests/.gentestdata/mysql/dvds/table" "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) { @@ -787,3 +789,101 @@ LIMIT 5; 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) + }) +} diff --git a/tests/postgres/select_test.go b/tests/postgres/select_test.go index afa72304..59fc44a8 100644 --- a/tests/postgres/select_test.go +++ b/tests/postgres/select_test.go @@ -1,15 +1,17 @@ package postgres import ( + "testing" + "time" + + "github.com/stretchr/testify/require" + "github.com/go-jet/jet/v2/internal/testutils" . "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/model" . "github.com/go-jet/jet/v2/tests/.gentestdata/jetdb/dvds/table" "github.com/go-jet/jet/v2/tests/.gentestdata/jetdb/dvds/view" - "github.com/stretchr/testify/require" - "testing" - "time" ) func TestSelect_ScanToStruct(t *testing.T) { @@ -1905,22 +1907,88 @@ func TestLateral(t *testing.T) { Language.Name.NOT_IN(String("spanish")). AND(Film.LanguageID.EQ(Language.LanguageID)), ), - "films") + ).AS("films") stmt := SELECT( - Film.AllColumns, + Film.FilmID, + Film.Title, languages.AllColumns(), ).FROM( Film.CROSS_JOIN(languages), ).WHERE( Film.FilmID.EQ(Int(1)), - ) + ).ORDER_BY( + Film.FilmID, + ).LIMIT(1) - var dest []struct { + 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) + }) } From 059515f52bfea4932c3616cdc294801152a2bd6a Mon Sep 17 00:00:00 2001 From: go-jet Date: Sat, 14 Nov 2020 12:14:08 +0100 Subject: [PATCH 21/41] QRM: Convert lossless decimal types first to string. --- go.mod | 1 + go.sum | 12 +++++++++-- qrm/utill.go | 4 ++-- tests/postgres/sample_test.go | 39 ++++++++++++++++++++++++++++++++--- 4 files changed, 49 insertions(+), 7 deletions(-) diff --git a/go.mod b/go.mod index 665b9bda..bc0d574f 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/go-jet/jet/v2 go 1.11 require ( + github.com/ericlagergren/decimal v0.0.0-20191206042408-88212e6cfca9 github.com/go-sql-driver/mysql v1.5.0 github.com/google/go-cmp v0.5.0 github.com/google/uuid v1.1.1 diff --git a/go.sum b/go.sum index 93217210..17146cf0 100644 --- a/go.sum +++ b/go.sum @@ -1,23 +1,31 @@ +github.com/apmckinlay/gsuneido v0.0.0-20190404155041-0b6cd442a18f/go.mod h1:JU2DOj5Fc6rol0yaT79Csr47QR0vONGwJtBNGRD7jmc= +github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= 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/go-jet/jet v1.0.0 h1:ENxUe/6lH82qLykIGAdZIlskZrpTeNfxjHz4VHtkVmA= -github.com/go-jet/jet v2.3.0+incompatible h1:Yg7JSERDC0f9x3dHUBMA2cxe9/qC6qlozDDO/s38USU= +github.com/ericlagergren/decimal v0.0.0-20191206042408-88212e6cfca9 h1:mMVotm9OVwoOS2IFGRRS5AfMTFWhtf8wj34JEYh47/k= +github.com/ericlagergren/decimal v0.0.0-20191206042408-88212e6cfca9/go.mod h1:ZWP59etEywfyMG2lAqnoi3t8uoiZCiTmLtwt6iESIsQ= 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/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/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.7.0 h1:h93mCPfUSkaul3Ka/VG8uZdmW1uMHDGxzu0NWHuJmHY= github.com/lib/pq v1.7.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/profile v1.5.0 h1:042Buzk+NhDI+DeSAA62RwJL8VAuZUMQZUjCsRz1Mug= 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/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +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= +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/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= 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= diff --git a/qrm/utill.go b/qrm/utill.go index 5d8455d8..233c44ac 100644 --- a/qrm/utill.go +++ b/qrm/utill.go @@ -255,7 +255,7 @@ func setReflectValue(source, destination reflect.Value) { } } - panic("jet: can't set " + source.Type().String() + " to " + destination.Type().String()) + //panic("jet: can't set " + source.Type().String() + " to " + destination.Type().String()) } func createScanValue(columnTypes []*sql.ColumnType) []interface{} { @@ -308,7 +308,7 @@ func newScanType(columnType *sql.ColumnType) reflect.Type { return nullStringType case "FLOAT4": return nullFloat32Type - case "FLOAT8", "NUMERIC", "DECIMAL", "FLOAT", "DOUBLE": + case "FLOAT8", "FLOAT", "DOUBLE": return nullFloat64Type case "BOOL": return nullBoolType diff --git a/tests/postgres/sample_test.go b/tests/postgres/sample_test.go index 327604f0..7ce224ce 100644 --- a/tests/postgres/sample_test.go +++ b/tests/postgres/sample_test.go @@ -1,13 +1,17 @@ package postgres 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/postgres" "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/google/uuid" - "github.com/stretchr/testify/require" - "testing" + + "github.com/ericlagergren/decimal/sql/postgres" ) func TestUUIDType(t *testing.T) { @@ -31,6 +35,35 @@ WHERE all_types.uuid = 'a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11'; requireLogged(t, query) } +func TestExactDecimals(t *testing.T) { + query := SELECT( + AllTypes.Numeric, + AllTypes.NumericPtr, + AllTypes.Decimal, + AllTypes.DecimalPtr, + ).FROM( + AllTypes, + ).WHERE(AllTypes.UUID.EQ(String("a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11"))) + + type AllTypes struct { + model.AllTypes + Numeric postgres.Decimal + NumericPtr postgres.Decimal + Decimal postgres.Decimal + DecimalPtr postgres.Decimal + } + + var result AllTypes + + err := query.Query(db, &result) + require.NoError(t, err) + + require.Equal(t, result.Decimal.V.String(), "1.11") + require.Equal(t, result.DecimalPtr.V.String(), "1.11") + require.Equal(t, result.Numeric.V.String(), "2.220") + require.Equal(t, result.NumericPtr.V.String(), "2.220") +} + func TestUUIDComplex(t *testing.T) { query := Person.INNER_JOIN(PersonPhone, PersonPhone.PersonID.EQ(Person.PersonID)). SELECT(Person.AllColumns, PersonPhone.AllColumns). From a5f50919036752da62ae600ea9c519d50c24885e Mon Sep 17 00:00:00 2001 From: David Racine Date: Mon, 16 Nov 2020 15:33:34 -0500 Subject: [PATCH 22/41] keep support destination type float64 for decimal/numeric --- qrm/utill.go | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/qrm/utill.go b/qrm/utill.go index 233c44ac..a49943c2 100644 --- a/qrm/utill.go +++ b/qrm/utill.go @@ -3,12 +3,15 @@ package qrm import ( "database/sql" "fmt" - "github.com/go-jet/jet/v2/internal/utils" - "github.com/go-jet/jet/v2/qrm/internal" - "github.com/google/uuid" "reflect" + "strconv" "strings" "time" + + "github.com/google/uuid" + + "github.com/go-jet/jet/v2/internal/utils" + "github.com/go-jet/jet/v2/qrm/internal" ) var scannerInterfaceType = reflect.TypeOf((*sql.Scanner)(nil)).Elem() @@ -197,6 +200,15 @@ func tryAssign(source, destination reflect.Value) bool { } } + if source.Type() == stringType && destination.Type() == float64Type { + strValue := source.String() + f, err := strconv.ParseFloat(strValue, 64) + if err != nil { + return false + } + source = reflect.ValueOf(f) + } + if source.Type().AssignableTo(destination.Type()) { destination.Set(source) return true @@ -255,7 +267,7 @@ func setReflectValue(source, destination reflect.Value) { } } - //panic("jet: can't set " + source.Type().String() + " to " + destination.Type().String()) + panic("jet: can't set " + source.Type().String() + " to " + destination.Type().String()) } func createScanValue(columnTypes []*sql.ColumnType) []interface{} { @@ -281,6 +293,8 @@ var int32Type = reflect.TypeOf(int32(1)) var uint32Type = reflect.TypeOf(uint32(1)) var int64Type = reflect.TypeOf(int64(1)) var uint64Type = reflect.TypeOf(uint64(1)) +var float64Type = reflect.TypeOf(float64(1)) +var stringType = reflect.TypeOf("") var nullBoolType = reflect.TypeOf(sql.NullBool{}) var nullInt8Type = reflect.TypeOf(internal.NullInt8{}) From f1ac6561b981e950cfc82992fa87cd4c84a3f7c4 Mon Sep 17 00:00:00 2001 From: David Racine Date: Mon, 16 Nov 2020 15:51:32 -0500 Subject: [PATCH 23/41] fix mixup imports --- qrm/utill.go | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/qrm/utill.go b/qrm/utill.go index a49943c2..253d5ac1 100644 --- a/qrm/utill.go +++ b/qrm/utill.go @@ -3,15 +3,14 @@ package qrm import ( "database/sql" "fmt" + "github.com/go-jet/jet/v2/internal/utils" + "github.com/go-jet/jet/v2/qrm/internal" + "github.com/google/uuid" "reflect" - "strconv" "strings" "time" - "github.com/google/uuid" - - "github.com/go-jet/jet/v2/internal/utils" - "github.com/go-jet/jet/v2/qrm/internal" + "strconv" ) var scannerInterfaceType = reflect.TypeOf((*sql.Scanner)(nil)).Elem() From 1404bf5e165f96986b6f0a7b8edc37e701fe2a71 Mon Sep 17 00:00:00 2001 From: David Racine Date: Mon, 16 Nov 2020 15:52:36 -0500 Subject: [PATCH 24/41] fix mixup imports --- qrm/utill.go | 1 - 1 file changed, 1 deletion(-) diff --git a/qrm/utill.go b/qrm/utill.go index 253d5ac1..2b208eb7 100644 --- a/qrm/utill.go +++ b/qrm/utill.go @@ -9,7 +9,6 @@ import ( "reflect" "strings" "time" - "strconv" ) From 08cc3161014254118b772a78a387f4d2a723b8db Mon Sep 17 00:00:00 2001 From: David Racine Date: Thu, 19 Nov 2020 14:02:41 -0500 Subject: [PATCH 25/41] fix insert of decimal by changing decimal library --- go.mod | 2 +- go.sum | 14 ++---- tests/postgres/sample_test.go | 87 +++++++++++++++++++++++++---------- 3 files changed, 68 insertions(+), 35 deletions(-) diff --git a/go.mod b/go.mod index bc0d574f..923117ce 100644 --- a/go.mod +++ b/go.mod @@ -3,11 +3,11 @@ module github.com/go-jet/jet/v2 go 1.11 require ( - github.com/ericlagergren/decimal v0.0.0-20191206042408-88212e6cfca9 github.com/go-sql-driver/mysql v1.5.0 github.com/google/go-cmp v0.5.0 github.com/google/uuid v1.1.1 github.com/lib/pq v1.7.0 github.com/pkg/profile v1.5.0 + github.com/shopspring/decimal v1.2.0 github.com/stretchr/testify v1.6.1 ) diff --git a/go.sum b/go.sum index 17146cf0..e4afd349 100644 --- a/go.sum +++ b/go.sum @@ -1,31 +1,25 @@ -github.com/apmckinlay/gsuneido v0.0.0-20190404155041-0b6cd442a18f/go.mod h1:JU2DOj5Fc6rol0yaT79Csr47QR0vONGwJtBNGRD7jmc= -github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= 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/ericlagergren/decimal v0.0.0-20191206042408-88212e6cfca9 h1:mMVotm9OVwoOS2IFGRRS5AfMTFWhtf8wj34JEYh47/k= -github.com/ericlagergren/decimal v0.0.0-20191206042408-88212e6cfca9/go.mod h1:ZWP59etEywfyMG2lAqnoi3t8uoiZCiTmLtwt6iESIsQ= +github.com/go-jet/jet v1.0.0 h1:ENxUe/6lH82qLykIGAdZIlskZrpTeNfxjHz4VHtkVmA= +github.com/go-jet/jet v2.3.0+incompatible h1:Yg7JSERDC0f9x3dHUBMA2cxe9/qC6qlozDDO/s38USU= 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/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/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.7.0 h1:h93mCPfUSkaul3Ka/VG8uZdmW1uMHDGxzu0NWHuJmHY= github.com/lib/pq v1.7.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/profile v1.5.0 h1:042Buzk+NhDI+DeSAA62RwJL8VAuZUMQZUjCsRz1Mug= 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/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= +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/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -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= -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/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= 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= diff --git a/tests/postgres/sample_test.go b/tests/postgres/sample_test.go index 7ce224ce..d9208b43 100644 --- a/tests/postgres/sample_test.go +++ b/tests/postgres/sample_test.go @@ -11,7 +11,7 @@ import ( "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/ericlagergren/decimal/sql/postgres" + "github.com/shopspring/decimal" ) func TestUUIDType(t *testing.T) { @@ -36,32 +36,71 @@ WHERE all_types.uuid = 'a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11'; } func TestExactDecimals(t *testing.T) { - query := SELECT( - AllTypes.Numeric, - AllTypes.NumericPtr, - AllTypes.Decimal, - AllTypes.DecimalPtr, - ).FROM( - AllTypes, - ).WHERE(AllTypes.UUID.EQ(String("a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11"))) - - type AllTypes struct { - model.AllTypes - Numeric postgres.Decimal - NumericPtr postgres.Decimal - Decimal postgres.Decimal - DecimalPtr postgres.Decimal - } + t.Run("should query decimal", func(t *testing.T) { + query := SELECT( + AllTypes.Numeric, + AllTypes.NumericPtr, + AllTypes.Decimal, + AllTypes.DecimalPtr, + ).FROM( + AllTypes, + ).WHERE(AllTypes.UUID.EQ(String("a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11"))) + + type AllTypes struct { + model.AllTypes + Numeric decimal.Decimal + NumericPtr decimal.Decimal + Decimal decimal.Decimal + DecimalPtr decimal.Decimal + } - var result AllTypes + var result AllTypes - err := query.Query(db, &result) - require.NoError(t, err) + err := query.Query(db, &result) + require.NoError(t, err) + + require.Equal(t, "1.11", result.Decimal.String()) + require.Equal(t, "1.11", result.DecimalPtr.String()) + require.Equal(t, "2.22", result.Numeric.String()) + require.Equal(t, "2.22", result.NumericPtr.String()) + }) - require.Equal(t, result.Decimal.V.String(), "1.11") - require.Equal(t, result.DecimalPtr.V.String(), "1.11") - require.Equal(t, result.Numeric.V.String(), "2.220") - require.Equal(t, result.NumericPtr.V.String(), "2.220") + t.Run("should insert decimal", func(t *testing.T) { + type allTypes struct { + model.AllTypes + Numeric decimal.Decimal + NumericPtr decimal.Decimal + Decimal decimal.Decimal + DecimalPtr decimal.Decimal + } + + m := allTypes{ + AllTypes: allTypesRow0, + Numeric: decimal.RequireFromString("12.345"), + NumericPtr: decimal.RequireFromString("56.789"), + Decimal: decimal.RequireFromString("91.23"), + DecimalPtr: decimal.RequireFromString("45.67"), + } + + insertQuery := AllTypes.INSERT( + AllTypes.MutableColumns, + ).MODEL(m). + RETURNING( + AllTypes.Numeric, + AllTypes.NumericPtr, + AllTypes.Decimal, + AllTypes.DecimalPtr, + ) + + var result allTypes + err := insertQuery.Query(db, &result) + require.NoError(t, err) + + require.Equal(t, "12.345", result.Numeric.String()) + require.Equal(t, "56.789", result.NumericPtr.String()) + require.Equal(t, "91.23", result.Decimal.String()) + require.Equal(t, "45.67", result.DecimalPtr.String()) + }) } func TestUUIDComplex(t *testing.T) { From 92d02fef78229ae586f7ec2d37699058d9a4c47f Mon Sep 17 00:00:00 2001 From: go-jet Date: Sun, 9 May 2021 16:25:54 +0200 Subject: [PATCH 26/41] Add DECIMAL constructor for Float literal. DECIMAL constructor is used to pass a decimal number to the SQL query without precision loss. --- internal/jet/literal_expression.go | 12 +++++++++++- mysql/literal.go | 5 ++++- postgres/literal.go | 3 +++ 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/internal/jet/literal_expression.go b/internal/jet/literal_expression.go index aed31b06..b86cb8e8 100644 --- a/internal/jet/literal_expression.go +++ b/internal/jet/literal_expression.go @@ -140,7 +140,7 @@ type floatLiteral struct { literalExpressionImpl } -// Float creates new float literal +// Float creates new float literal from float64 value func Float(value float64) FloatExpression { floatLiteral := floatLiteral{} floatLiteral.literalExpressionImpl = *literal(value) @@ -150,6 +150,16 @@ func Float(value float64) FloatExpression { 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 { stringInterfaceImpl diff --git a/mysql/literal.go b/mysql/literal.go index 7523d55e..4a6544ca 100644 --- a/mysql/literal.go +++ b/mysql/literal.go @@ -42,9 +42,12 @@ 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 from float64 value var Float = jet.Float +// Decimal creates new float literal expression from string value +var Decimal = jet.Decimal + // String creates new string literal expression var String = jet.String diff --git a/postgres/literal.go b/postgres/literal.go index a0b0224e..ebfd85c5 100644 --- a/postgres/literal.go +++ b/postgres/literal.go @@ -38,6 +38,9 @@ var Uint64 = jet.Uint64 // Float creates new float literal expression var Float = jet.Float +// Decimal creates new float literal expression +var Decimal = jet.Decimal + // String creates new string literal expression var String = jet.String From 063b17ca05cfb4d332341cc3c6665be29bf53a60 Mon Sep 17 00:00:00 2001 From: go-jet Date: Sun, 9 May 2021 16:37:16 +0200 Subject: [PATCH 27/41] Update lossless decimal tests to use new floats test table and DECIMAL literal constructor. --- go.mod | 8 +- qrm/utill.go | 14 ++-- qrm/utill_test.go | 45 +++++++++++ tests/mysql/alltypes_test.go | 97 +++++++++++++++++++++++ tests/postgres/generator_test.go | 4 +- tests/postgres/sample_test.go | 132 ++++++++++++++++++++----------- tests/testdata | 2 +- 7 files changed, 239 insertions(+), 63 deletions(-) diff --git a/go.mod b/go.mod index 923117ce..b6a39511 100644 --- a/go.mod +++ b/go.mod @@ -4,10 +4,10 @@ go 1.11 require ( 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/lib/pq v1.7.0 - github.com/pkg/profile v1.5.0 - github.com/shopspring/decimal v1.2.0 - github.com/stretchr/testify v1.6.1 + github.com/pkg/profile v1.5.0 //tests + github.com/shopspring/decimal v1.2.0 // tests + github.com/stretchr/testify v1.6.1 // tests ) diff --git a/qrm/utill.go b/qrm/utill.go index 2b208eb7..15745992 100644 --- a/qrm/utill.go +++ b/qrm/utill.go @@ -7,9 +7,9 @@ import ( "github.com/go-jet/jet/v2/qrm/internal" "github.com/google/uuid" "reflect" + "strconv" "strings" "time" - "strconv" ) var scannerInterfaceType = reflect.TypeOf((*sql.Scanner)(nil)).Elem() @@ -184,11 +184,11 @@ func isIntegerType(value reflect.Type) bool { } func tryAssign(source, destination reflect.Value) bool { - if source.Type().ConvertibleTo(destination.Type()) { - source = source.Convert(destination.Type()) - } - if isIntegerType(source.Type()) && destination.Type() == boolType { + switch { + case source.Type().ConvertibleTo(destination.Type()): + source = source.Convert(destination.Type()) + case isIntegerType(source.Type()) && destination.Type() == boolType: intValue := source.Int() if intValue == 1 { @@ -196,9 +196,7 @@ func tryAssign(source, destination reflect.Value) bool { } else if intValue == 0 { source = reflect.ValueOf(false) } - } - - if source.Type() == stringType && destination.Type() == float64Type { + case source.Type() == stringType && destination.Type() == float64Type: strValue := source.String() f, err := strconv.ParseFloat(strValue, 64) if err != nil { diff --git a/qrm/utill_test.go b/qrm/utill_test.go index 897bb2c9..e23fa158 100644 --- a/qrm/utill_test.go +++ b/qrm/utill_test.go @@ -35,3 +35,48 @@ func TestIsSimpleModelType(t *testing.T) { require.Equal(t, isSimpleModelType(reflect.TypeOf([]string{"str"})), 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) +} diff --git a/tests/mysql/alltypes_test.go b/tests/mysql/alltypes_test.go index 6fbb1364..8c1539a8 100644 --- a/tests/mysql/alltypes_test.go +++ b/tests/mysql/alltypes_test.go @@ -1,6 +1,7 @@ package mysql import ( + "github.com/shopspring/decimal" "github.com/stretchr/testify/require" "strings" "testing" @@ -1278,3 +1279,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) + }) +} diff --git a/tests/postgres/generator_test.go b/tests/postgres/generator_test.go index 833e2a43..0571157c 100644 --- a/tests/postgres/generator_test.go +++ b/tests/postgres/generator_test.go @@ -346,7 +346,7 @@ func TestGeneratedAllTypesSQLBuilderFiles(t *testing.T) { require.NoError(t, err) 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) @@ -354,7 +354,7 @@ func TestGeneratedAllTypesSQLBuilderFiles(t *testing.T) { require.NoError(t, err) 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) } diff --git a/tests/postgres/sample_test.go b/tests/postgres/sample_test.go index d9208b43..bc82c7e2 100644 --- a/tests/postgres/sample_test.go +++ b/tests/postgres/sample_test.go @@ -36,70 +36,106 @@ WHERE all_types.uuid = 'a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11'; } 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( - AllTypes.Numeric, - AllTypes.NumericPtr, - AllTypes.Decimal, - AllTypes.DecimalPtr, + Floats.AllColumns, ).FROM( - AllTypes, - ).WHERE(AllTypes.UUID.EQ(String("a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11"))) - - type AllTypes struct { - model.AllTypes - Numeric decimal.Decimal - NumericPtr decimal.Decimal - Decimal decimal.Decimal - DecimalPtr decimal.Decimal - } + Floats, + ).WHERE(Floats.Decimal.EQ(Decimal("1.11111111111111111111"))) - var result AllTypes + var result floats err := query.Query(db, &result) require.NoError(t, err) - require.Equal(t, "1.11", result.Decimal.String()) - require.Equal(t, "1.11", result.DecimalPtr.String()) - require.Equal(t, "2.22", result.Numeric.String()) - require.Equal(t, "2.22", result.NumericPtr.String()) + 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) { - type allTypes struct { - model.AllTypes - Numeric decimal.Decimal - NumericPtr decimal.Decimal - Decimal decimal.Decimal - DecimalPtr decimal.Decimal - } - m := allTypes{ - AllTypes: allTypesRow0, - Numeric: decimal.RequireFromString("12.345"), - NumericPtr: decimal.RequireFromString("56.789"), - Decimal: decimal.RequireFromString("91.23"), - DecimalPtr: decimal.RequireFromString("45.67"), - } + 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"; +`) - insertQuery := AllTypes.INSERT( - AllTypes.MutableColumns, - ).MODEL(m). - RETURNING( - AllTypes.Numeric, - AllTypes.NumericPtr, - AllTypes.Decimal, - AllTypes.DecimalPtr, - ) - - var result allTypes + var result floats err := insertQuery.Query(db, &result) require.NoError(t, err) - require.Equal(t, "12.345", result.Numeric.String()) - require.Equal(t, "56.789", result.NumericPtr.String()) - require.Equal(t, "91.23", result.Decimal.String()) - require.Equal(t, "45.67", result.DecimalPtr.String()) + // 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) }) } diff --git a/tests/testdata b/tests/testdata index 391d9365..1c977643 160000 --- a/tests/testdata +++ b/tests/testdata @@ -1 +1 @@ -Subproject commit 391d936515d2f826df073707697de44907a7f67d +Subproject commit 1c977643ceb0df149fc953ad617e2a86c6ecdd65 From 256be8a406ce55332c35845be82f463a65a4050d Mon Sep 17 00:00:00 2001 From: go-jet Date: Sun, 9 May 2021 17:17:14 +0200 Subject: [PATCH 28/41] [BUG] Update statement reserved word not escaped Update statement, using MODEL struct, now generates escaped SQL identifier if column name is reserved word. --- internal/jet/clause.go | 2 +- internal/testutils/test_utils.go | 28 +++++++++++++++++++--------- mysql/update_statement_test.go | 23 +++++++++++++++++++++-- 3 files changed, 41 insertions(+), 12 deletions(-) diff --git a/internal/jet/clause.go b/internal/jet/clause.go index 42dad7ee..a6a49d85 100644 --- a/internal/jet/clause.go +++ b/internal/jet/clause.go @@ -308,7 +308,7 @@ func (s *SetClause) Serialize(statementType StatementType, out *SQLBuilder, opti panic("jet: nil column in columns list for SET clause") } - out.WriteString(column.Name()) + out.WriteIdentifier(column.Name()) out.WriteString(" = ") diff --git a/internal/testutils/test_utils.go b/internal/testutils/test_utils.go index 272cf5fd..9dd63180 100644 --- a/internal/testutils/test_utils.go +++ b/internal/testutils/test_utils.go @@ -90,7 +90,7 @@ func AssertJSONFile(t *testing.T, data interface{}, testRelativePath string) { // AssertStatementSql check if statement Sql() is the same as expectedQuery and expectedArgs func AssertStatementSql(t *testing.T, query jet.Statement, expectedQuery string, expectedArgs ...interface{}) { queryStr, args := query.Sql() - require.Equal(t, queryStr, expectedQuery) + assertQueryString(t, queryStr, expectedQuery) if len(expectedArgs) == 0 { return @@ -117,12 +117,7 @@ func AssertDebugStatementSql(t *testing.T, query jet.Statement, expectedQuery st } debuqSql := query.DebugSql() - if !assert.Equal(t, debuqSql, expectedQuery) { - fmt.Println("Expected: ") - fmt.Println(expectedQuery) - fmt.Println("Got: ") - fmt.Println(debuqSql) - } + assertQueryString(t, debuqSql, expectedQuery) } // AssertSerialize checks if clause serialize produces expected query and args @@ -200,7 +195,7 @@ func AssertQueryPanicErr(t *testing.T, stmt jet.Statement, db qrm.DB, dest inter require.Equal(t, r, errString) }() - stmt.Query(db, dest) + _ = stmt.Query(db, dest) } // AssertFileContent check if file content at filePath contains expectedContent text. @@ -229,7 +224,22 @@ func AssertFileNamesEqual(t *testing.T, fileInfos []os.FileInfo, fileNames ...st // AssertDeepEqual checks if actual and expected objects are deeply equal. 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) + } +} + +func assertQueryString(t *testing.T, actual, expected string) { + if !assert.Equal(t, actual, expected) { + printDiff(actual, expected) + } +} + +func printDiff(actual, expected interface{}) { + fmt.Println("Actual: ") + fmt.Println(actual) + fmt.Println("Expected: ") + fmt.Println(expected) } // BoolPtr returns address of bool parameter diff --git a/mysql/update_statement_test.go b/mysql/update_statement_test.go index fe3be01a..f5284a78 100644 --- a/mysql/update_statement_test.go +++ b/mysql/update_statement_test.go @@ -2,6 +2,7 @@ package mysql import ( "fmt" + "strings" "testing" ) @@ -52,11 +53,29 @@ WHERE table1.col1 = ?; ). WHERE(table1Col1.EQ(Int(2))) - //fmt.Println(stmt.Sql()) - 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) { 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") From f30cbb9e89a4db4fb301fcf676f387306266ad05 Mon Sep 17 00:00:00 2001 From: go-jet Date: Tue, 11 May 2021 13:20:07 +0200 Subject: [PATCH 29/41] Add UUID helper function UUID creates string literal expression from uuid object. uuid can be any uuid type with a String method. --- internal/jet/literal_expression.go | 6 ++++++ mysql/literal.go | 4 ++++ postgres/literal.go | 4 ++++ tests/mysql/alltypes_test.go | 2 +- tests/postgres/sample_test.go | 5 ++++- 5 files changed, 19 insertions(+), 2 deletions(-) diff --git a/internal/jet/literal_expression.go b/internal/jet/literal_expression.go index b86cb8e8..29560e4c 100644 --- a/internal/jet/literal_expression.go +++ b/internal/jet/literal_expression.go @@ -409,3 +409,9 @@ func Raw(raw string, parent ...Expression) Expression { return rawExp } + +// 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()) +} diff --git a/mysql/literal.go b/mysql/literal.go index 4a6544ca..0a66eb37 100644 --- a/mysql/literal.go +++ b/mysql/literal.go @@ -51,6 +51,10 @@ var Decimal = jet.Decimal // String creates new string literal expression 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 var Date = func(year int, month time.Month, day int) DateExpression { return CAST(jet.Date(year, month, day)).AS_DATE() diff --git a/postgres/literal.go b/postgres/literal.go index ebfd85c5..efba9ba8 100644 --- a/postgres/literal.go +++ b/postgres/literal.go @@ -44,6 +44,10 @@ var Decimal = jet.Decimal // String creates new string literal expression 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 + // Bytea craates new bytea literal expression var Bytea = func(value string) StringExpression { return CAST(jet.String(value)).AS_BYTEA() diff --git a/tests/mysql/alltypes_test.go b/tests/mysql/alltypes_test.go index 8c1539a8..70ca2771 100644 --- a/tests/mysql/alltypes_test.go +++ b/tests/mysql/alltypes_test.go @@ -78,7 +78,7 @@ func TestUUID(t *testing.T) { require.NoError(t, err) require.True(t, dest.StrUUID != nil) 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()) requireLogged(t, query) } diff --git a/tests/postgres/sample_test.go b/tests/postgres/sample_test.go index bc82c7e2..b5544318 100644 --- a/tests/postgres/sample_test.go +++ b/tests/postgres/sample_test.go @@ -15,9 +15,12 @@ import ( ) func TestUUIDType(t *testing.T) { + + id := uuid.MustParse("a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11") + query := AllTypes. 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, ` SELECT all_types.uuid AS "all_types.uuid", From 9385f462dfe71fc178240da0a92e3370ba6cedd0 Mon Sep 17 00:00:00 2001 From: go-jet Date: Wed, 12 May 2021 12:29:22 +0200 Subject: [PATCH 30/41] Allow Bytea literal constructor to accept byte array. Bytea literal constructor now accepts string or []byte a a parameter. --- postgres/literal.go | 14 +++++++--- postgres/literal_test.go | 5 ++++ tests/postgres/sample_test.go | 51 +++++++++++++++++++++++++++++++++++ 3 files changed, 66 insertions(+), 4 deletions(-) diff --git a/postgres/literal.go b/postgres/literal.go index efba9ba8..8ee32352 100644 --- a/postgres/literal.go +++ b/postgres/literal.go @@ -1,8 +1,9 @@ package postgres import ( - "github.com/go-jet/jet/v2/internal/jet" "time" + + "github.com/go-jet/jet/v2/internal/jet" ) // Bool creates new bool literal expression @@ -48,9 +49,14 @@ var String = jet.String // value can be any uuid type with a String method var UUID = jet.UUID -// Bytea craates new bytea literal expression -var Bytea = func(value string) StringExpression { - return CAST(jet.String(value)).AS_BYTEA() +// 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 diff --git a/postgres/literal_test.go b/postgres/literal_test.go index 197fff72..52a15a0a 100644 --- a/postgres/literal_test.go +++ b/postgres/literal_test.go @@ -62,6 +62,11 @@ func TestString(t *testing.T) { 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) { assertSerialize(t, Date(2014, time.January, 2), `$1::date`, "2014-01-02") assertSerialize(t, DateT(time.Now()), `$1::date`) diff --git a/tests/postgres/sample_test.go b/tests/postgres/sample_test.go index b5544318..f1d99992 100644 --- a/tests/postgres/sample_test.go +++ b/tests/postgres/sample_test.go @@ -497,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!") +} From 7af9072b8d9a6278f4f436bc42c6f2f1d66b09a1 Mon Sep 17 00:00:00 2001 From: go-jet Date: Fri, 14 May 2021 12:15:35 +0200 Subject: [PATCH 31/41] Allow Raw helper to accept named arguments --- internal/jet/literal_expression.go | 136 ++++++++++++++++++++++++++++- internal/testutils/test_utils.go | 2 + mysql/expressions.go | 19 +++- mysql/expressions_test.go | 56 ++++++++++++ mysql/interval.go | 2 +- mysql/select_statement_test.go | 18 ++-- postgres/expressions.go | 21 ++++- postgres/expressions_test.go | 64 ++++++++++++++ postgres/interval_expression.go | 2 +- tests/mysql/alltypes_test.go | 25 ++++-- tests/postgres/alltypes_test.go | 29 ++++-- tests/testdata | 2 +- 12 files changed, 340 insertions(+), 36 deletions(-) create mode 100644 mysql/expressions_test.go create mode 100644 postgres/expressions_test.go diff --git a/internal/jet/literal_expression.go b/internal/jet/literal_expression.go index 29560e4c..b5ac9f01 100644 --- a/internal/jet/literal_expression.go +++ b/internal/jet/literal_expression.go @@ -2,6 +2,8 @@ package jet import ( "fmt" + "sort" + "strings" "time" ) @@ -394,22 +396,148 @@ func WRAP(expression ...Expression) Expression { type rawExpression struct { ExpressionInterfaceImpl - Raw string + Raw string + NamedArgument map[string]interface{} + noWrap bool } func (n *rawExpression) serialize(statement StatementType, out *SQLBuilder, options ...SerializeOption) { - out.WriteString(n.Raw) + raw := n.Raw + + type namedArgumentPosition struct { + Name string + Value interface{} + Position int + } + + var namedArgumentPositions []namedArgumentPosition + + for namedArg, value := range n.NamedArgument { + rawCopy := n.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 + } + out.Args = append(out.Args, namedArgumentPos.Value) + currentArgNum := len(out.Args) + + dialectPlaceholder := out.Dialect.ArgumentPlaceholder()(currentArgNum) + // if placeholder is not unique identifier ($1, $2, etc..), we will replace just one occurence of the argument + toReplace := -1 // all occurrences + if dialectPlaceholder == "?" { + toReplace = 1 // just one occurrence + } + raw = strings.Replace(raw, namedArgumentPos.Name, dialectPlaceholder, toReplace) + } + + if !n.noWrap && !contains(options, NoWrap) { + raw = "(" + raw + ")" + } + + out.WriteString(raw) } // Raw can be used for any unsupported functions, operators or expressions. // For example: Raw("current_database()") -func Raw(raw string, parent ...Expression) Expression { - rawExp := &rawExpression{Raw: raw} +func Raw(raw string, namedArgs ...map[string]interface{}) Expression { + 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...) return rawExp } +// Raw helper that for integer expressions +func RawInt(raw string, namedArgs ...map[string]interface{}) IntegerExpression { + return IntExp(Raw(raw, namedArgs...)) +} + +// Raw helper that for float expressions +func RawFloat(raw string, namedArgs ...map[string]interface{}) FloatExpression { + return FloatExp(Raw(raw, namedArgs...)) +} + +// Raw helper that for string expressions +func RawString(raw string, namedArgs ...map[string]interface{}) StringExpression { + return StringExp(Raw(raw, namedArgs...)) +} + +// Raw helper that for time expressions +func RawTime(raw string, namedArgs ...map[string]interface{}) TimeExpression { + return TimeExp(Raw(raw, namedArgs...)) +} + +// Raw helper that for time with time zone expressions +func RawTimez(raw string, namedArgs ...map[string]interface{}) TimezExpression { + return TimezExp(Raw(raw, namedArgs...)) +} + +// Raw helper that for timestamp expressions +func RawTimestamp(raw string, namedArgs ...map[string]interface{}) TimestampExpression { + return TimestampExp(Raw(raw, namedArgs...)) +} + +// Raw helper that for timestamp with time zone expressions +func RawTimestampz(raw string, namedArgs ...map[string]interface{}) TimestampzExpression { + return TimestampzExp(Raw(raw, namedArgs...)) +} + +// Raw 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 { diff --git a/internal/testutils/test_utils.go b/internal/testutils/test_utils.go index 9dd63180..f8492193 100644 --- a/internal/testutils/test_utils.go +++ b/internal/testutils/test_utils.go @@ -226,12 +226,14 @@ func AssertFileNamesEqual(t *testing.T, fileInfos []os.FileInfo, fileNames ...st func AssertDeepEqual(t *testing.T, actual, expected interface{}, msg ...string) { 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() } } diff --git a/mysql/expressions.go b/mysql/expressions.go index 3ee660d6..7c13939b 100644 --- a/mysql/expressions.go +++ b/mysql/expressions.go @@ -70,9 +70,22 @@ var DateTimeExp = jet.TimestampExp // Does not add sql cast to generated sql builder output. var TimestampExp = jet.TimestampExp -// Raw can be used for any unsupported functions, operators or expressions. -// For example: Raw("current_database()") -var Raw = jet.Raw +// RawArgs is type used to pass optional arguments to Raw method +type RawArgs = map[string]interface{} + +var ( + // Raw can be used for any unsupported functions, operators or expressions. + // For example: Raw("current_database()") + Raw = jet.Raw + + // Raw helper methods for each of the mysql type + 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 diff --git a/mysql/expressions_test.go b/mysql/expressions_test.go new file mode 100644 index 00000000..127fccdc --- /dev/null +++ b/mysql/expressions_test.go @@ -0,0 +1,56 @@ +package mysql + +import ( + "testing" + time2 "time" + + "github.com/stretchr/testify/require" +) + +func TestRaw(t *testing.T) { + assertSerialize(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) + + assertSerialize(t, + Int(700).ADD(RawInt("#1 + table.colInt + #2", RawArgs{"#1": 11, "#2": 22})), + "(? + (? + table.colInt + ?))", + int64(700), 11, 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) +} diff --git a/mysql/interval.go b/mysql/interval.go index f325bb8d..c563855e 100644 --- a/mysql/interval.go +++ b/mysql/interval.go @@ -97,7 +97,7 @@ func INTERVAL(value interface{}, unitType unitType) Interval { // INTERVALe creates new temporal interval from expresion and unit type. func INTERVALe(expr Expression, unitType unitType) Interval { return jet.NewInterval(jet.ListSerializer{ - Serializers: []jet.Serializer{expr, jet.Raw(string(unitType))}, + Serializers: []jet.Serializer{expr, jet.RawWithParent(string(unitType))}, Separator: " ", }) } diff --git a/mysql/select_statement_test.go b/mysql/select_statement_test.go index 2312a60c..630f8006 100644 --- a/mysql/select_statement_test.go +++ b/mysql/select_statement_test.go @@ -136,15 +136,15 @@ 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), - ), - ))), ` + 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 ( diff --git a/postgres/expressions.go b/postgres/expressions.go index 57a33359..ca6223b1 100644 --- a/postgres/expressions.go +++ b/postgres/expressions.go @@ -81,9 +81,24 @@ var TimestampExp = jet.TimestampExp // Does not add sql cast to generated sql builder output. var TimestampzExp = jet.TimestampzExp -// Raw can be used for any unsupported functions, operators or expressions. -// For example: Raw("current_database()") -var Raw = jet.Raw +// RawArgs is type used to pass optional arguments to Raw method +type RawArgs = map[string]interface{} + +var ( + // Raw can be used for any unsupported functions, operators or expressions. + // For example: Raw("current_database()") + Raw = jet.Raw + + // Raw helper methods for each of the postgres type + 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 diff --git a/postgres/expressions_test.go b/postgres/expressions_test.go new file mode 100644 index 00000000..1e7f3c6e --- /dev/null +++ b/postgres/expressions_test.go @@ -0,0 +1,64 @@ +package postgres + +import ( + "testing" + "time" + + "github.com/stretchr/testify/require" +) + +func TestRaw(t *testing.T) { + assertSerialize(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) + + 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) +} + +func TestDuplicateArguments(t *testing.T) { + + assertSerialize(t, Raw(":arg + table.colInt + :arg", RawArgs{":arg": 11}), + "($1 + table.colInt + $1)", 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) + + 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, 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) +} diff --git a/postgres/interval_expression.go b/postgres/interval_expression.go index df2ed606..b8468cf7 100644 --- a/postgres/interval_expression.go +++ b/postgres/interval_expression.go @@ -128,7 +128,7 @@ func INTERVAL(quantityAndUnit ...quantityAndUnit) IntervalExpression { newInterval := &intervalExpression{} - newInterval.Expression = jet.Raw(intervalStr, newInterval) + newInterval.Expression = jet.RawWithParent(intervalStr, newInterval) newInterval.intervalInterfaceImpl.parent = newInterval return newInterval diff --git a/tests/mysql/alltypes_test.go b/tests/mysql/alltypes_test.go index 70ca2771..a85d0b4a 100644 --- a/tests/mysql/alltypes_test.go +++ b/tests/mysql/alltypes_test.go @@ -89,14 +89,17 @@ func TestExpressionOperators(t *testing.T) { AllTypes.DatePtr.IS_NOT_NULL().AS("result.is_not_null"), AllTypes.SmallIntPtr.IN(Int(11), Int(22)).AS("result.in"), 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(AllTypes.SELECT(AllTypes.Integer)).AS("result.not_in_select"), - - Raw("DATABASE()"), ).LIMIT(2) - //fmt.Println(query.Sql()) - testutils.AssertStatementSql(t, query, strings.Replace(` SELECT all_types.'integer' IS NULL AS "result.is_null", all_types.date_ptr IS NOT NULL AS "result.is_not_null", @@ -105,15 +108,17 @@ SELECT all_types.'integer' IS NULL AS "result.is_null", SELECT all_types.'integer' AS "all_types.integer" FROM test_sample.all_types ))) 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 (( SELECT all_types.'integer' AS "all_types.integer" FROM test_sample.all_types - ))) AS "result.not_in_select", - DATABASE() + ))) AS "result.not_in_select" FROM test_sample.all_types 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 { common.ExpressionTestResult `alias:"result.*"` @@ -132,6 +137,9 @@ LIMIT ?; "IsNotNull": true, "In": false, "InSelect": false, + "Raw": "jet@localhost", + "RawArg": 148, + "RawArg2": -1479, "NotIn": null, "NotInSelect": true }, @@ -140,6 +148,9 @@ LIMIT ?; "IsNotNull": false, "In": null, "InSelect": null, + "Raw": "jet@localhost", + "RawArg": 134, + "RawArg2": -1479, "NotIn": null, "NotInSelect": null } diff --git a/tests/postgres/alltypes_test.go b/tests/postgres/alltypes_test.go index 4379e678..8bd16820 100644 --- a/tests/postgres/alltypes_test.go +++ b/tests/postgres/alltypes_test.go @@ -1,17 +1,19 @@ package postgres import ( - "github.com/stretchr/testify/require" "testing" "time" + "github.com/stretchr/testify/require" + + "github.com/google/uuid" + "github.com/go-jet/jet/v2/internal/testutils" . "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/table" "github.com/go-jet/jet/v2/tests/.gentestdata/jetdb/test_sample/view" "github.com/go-jet/jet/v2/tests/testdata/results/common" - "github.com/google/uuid" ) func TestAllTypesSelect(t *testing.T) { @@ -221,12 +223,16 @@ func TestExpressionOperators(t *testing.T) { AllTypes.DatePtr.IS_NOT_NULL().AS("result.is_not_null"), AllTypes.SmallIntPtr.IN(Int(11), Int(22)).AS("result.in"), 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(AllTypes.SELECT(AllTypes.Integer)).AS("result.not_in_select"), ).LIMIT(2) - //fmt.Println(query.Sql()) - testutils.AssertStatementSql(t, query, ` SELECT all_types.integer IS NULL AS "result.is_null", all_types.date_ptr IS NOT NULL AS "result.is_not_null", @@ -235,14 +241,17 @@ SELECT all_types.integer IS NULL AS "result.is_null", SELECT all_types.integer AS "all_types.integer" FROM test_sample.all_types ))) 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 (( SELECT all_types.integer AS "all_types.integer" FROM test_sample.all_types ))) AS "result.not_in_select" FROM test_sample.all_types -LIMIT $5; -`, int64(11), int64(22), int64(11), int64(22), int64(2)) +LIMIT $11; +`, int64(11), int64(22), 78, 56, 11, 22, 33, 44, int64(11), int64(22), int64(2)) var dest []struct { common.ExpressionTestResult `alias:"result.*"` @@ -261,6 +270,9 @@ LIMIT $5; "IsNotNull": true, "In": false, "InSelect": false, + "Raw": "jet", + "RawArg": 148, + "RawArg2": 421, "NotIn": null, "NotInSelect": true }, @@ -269,6 +281,9 @@ LIMIT $5; "IsNotNull": false, "In": null, "InSelect": null, + "Raw": "jet", + "RawArg": 134, + "RawArg2": 421, "NotIn": null, "NotInSelect": null } diff --git a/tests/testdata b/tests/testdata index 1c977643..0d52780c 160000 --- a/tests/testdata +++ b/tests/testdata @@ -1 +1 @@ -Subproject commit 1c977643ceb0df149fc953ad617e2a86c6ecdd65 +Subproject commit 0d52780c6510d4b1e560081a82648b85c555ce43 From e95a2385ee183602bf2fe40f00014711caad2b95 Mon Sep 17 00:00:00 2001 From: go-jet Date: Fri, 14 May 2021 14:13:42 +0200 Subject: [PATCH 32/41] Run postgres tests with pgx driver --- go.mod | 1 + go.sum | 461 +++++++++++++++++++++++++++++++- tests/postgres/alltypes_test.go | 14 + tests/postgres/main_test.go | 46 +++- 4 files changed, 507 insertions(+), 15 deletions(-) diff --git a/go.mod b/go.mod index b6a39511..9dd3e02a 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,7 @@ require ( github.com/go-sql-driver/mysql v1.5.0 github.com/google/go-cmp v0.5.0 //tests 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/pkg/profile v1.5.0 //tests github.com/shopspring/decimal v1.2.0 // tests diff --git a/go.sum b/go.sum index e4afd349..47af4791 100644 --- a/go.sum +++ b/go.sum @@ -1,25 +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/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/go-jet/jet v1.0.0 h1:ENxUe/6lH82qLykIGAdZIlskZrpTeNfxjHz4VHtkVmA= -github.com/go-jet/jet v2.3.0+incompatible h1:Yg7JSERDC0f9x3dHUBMA2cxe9/qC6qlozDDO/s38USU= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/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/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/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/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/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/go.mod h1:qBsxPvzyUincmltOk6iyRVxHYg4adc0OFOv72ZdLa18= 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/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.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/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= +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 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/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= diff --git a/tests/postgres/alltypes_test.go b/tests/postgres/alltypes_test.go index 8bd16820..4a1ab9fb 100644 --- a/tests/postgres/alltypes_test.go +++ b/tests/postgres/alltypes_test.go @@ -17,6 +17,8 @@ import ( ) func TestAllTypesSelect(t *testing.T) { + skipForPgxDriver(t) // pgx driver returns time with time zone as string + dest := []model.AllTypes{} err := AllTypes.SELECT(AllTypes.AllColumns).Query(db, &dest) @@ -27,6 +29,8 @@ func TestAllTypesSelect(t *testing.T) { } func TestAllTypesViewSelect(t *testing.T) { + skipForPgxDriver(t) // pgx driver returns time with time zone as string + type AllTypesView model.AllTypes dest := []AllTypesView{} @@ -39,6 +43,8 @@ func TestAllTypesViewSelect(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). MODEL(allTypesRow0). MODEL(&allTypesRow1). @@ -54,6 +60,8 @@ func TestAllTypesInsertModel(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. @@ -293,6 +301,8 @@ LIMIT $11; func TestExpressionCast(t *testing.T) { + skipForPgxDriver(t) // for some reason, pgx driver, 150:char(12) returns as int value + query := AllTypes.SELECT( CAST(Int(150)).AS_CHAR(12).AS("char12"), CAST(String("TRUE")).AS_BOOL(), @@ -338,6 +348,8 @@ func TestExpressionCast(t *testing.T) { } func TestStringOperators(t *testing.T) { + skipForPgxDriver(t) // pgx driver returns text column as int value + query := AllTypes.SELECT( AllTypes.Text.EQ(AllTypes.Char), AllTypes.Text.EQ(String("Text")), @@ -853,6 +865,7 @@ func TestInterval(t *testing.T) { } func TestSubQueryColumnReference(t *testing.T) { + skipForPgxDriver(t) // pgx driver returns time with time zone as string value type expected struct { sql string @@ -1030,6 +1043,7 @@ FROM` } func TestTimeLiterals(t *testing.T) { + skipForPgxDriver(t) // pgx driver returns time with time zone as string loc, err := time.LoadLocation("Europe/Berlin") require.NoError(t, err) diff --git a/tests/postgres/main_test.go b/tests/postgres/main_test.go index 56d0363f..ef9337c7 100644 --- a/tests/postgres/main_test.go +++ b/tests/postgres/main_test.go @@ -3,17 +3,23 @@ package postgres import ( "context" "database/sql" - "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" + "fmt" "math/rand" "os" "os/exec" "strings" "testing" "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 @@ -25,16 +31,23 @@ func TestMain(m *testing.M) { setTestRoot() - var err error - db, err = sql.Open("postgres", dbconfig.PostgresConnectString) - if err != nil { - panic("Failed to connect to test db") - } - defer db.Close() + for _, driverName := range []string{"postgres", "pgx"} { + func() { + var err error + db, err = sql.Open(driverName, dbconfig.PostgresConnectString) + if err != nil { + fmt.Println(err.Error()) + panic("Failed to connect to test db") + } + defer db.Close() - ret := m.Run() + ret := m.Run() - os.Exit(ret) + if ret != 0 { + os.Exit(ret) + } + }() + } } func setTestRoot() { @@ -64,3 +77,10 @@ func requireLogged(t *testing.T, statement postgres.Statement) { require.Equal(t, loggedSQLArgs, args) require.Equal(t, loggedDebugSQL, statement.DebugSql()) } + +func skipForPgxDriver(t *testing.T) { + switch db.Driver().(type) { + case *stdlib.Driver: + t.SkipNow() + } +} From a5b77695894d6216c4d80153b9d54c671f06d4cb Mon Sep 17 00:00:00 2001 From: go-jet Date: Sat, 15 May 2021 11:54:41 +0200 Subject: [PATCH 33/41] Add RawStatement support RawStatement method creates new sql statements from raw query and optional map of named arguments. --- internal/jet/literal_expression.go | 66 +----------- internal/jet/raw_statement.go | 47 +++++++++ internal/jet/sql_builder.go | 68 +++++++++++++ internal/testutils/test_utils.go | 24 ++--- mysql/expressions_test.go | 6 ++ mysql/statement.go | 8 ++ postgres/expressions_test.go | 16 ++- postgres/statement.go | 8 ++ postgres/utils_test.go | 4 + tests/mysql/raw_statement_test.go | 82 +++++++++++++++ tests/postgres/raw_statements_test.go | 138 ++++++++++++++++++++++++++ 11 files changed, 391 insertions(+), 76 deletions(-) create mode 100644 internal/jet/raw_statement.go create mode 100644 mysql/statement.go create mode 100644 postgres/statement.go create mode 100644 tests/mysql/raw_statement_test.go create mode 100644 tests/postgres/raw_statements_test.go diff --git a/internal/jet/literal_expression.go b/internal/jet/literal_expression.go index b5ac9f01..9ce9a813 100644 --- a/internal/jet/literal_expression.go +++ b/internal/jet/literal_expression.go @@ -2,8 +2,6 @@ package jet import ( "fmt" - "sort" - "strings" "time" ) @@ -402,71 +400,15 @@ type rawExpression struct { } func (n *rawExpression) serialize(statement StatementType, out *SQLBuilder, options ...SerializeOption) { - raw := n.Raw - - type namedArgumentPosition struct { - Name string - Value interface{} - Position int - } - - var namedArgumentPositions []namedArgumentPosition - - for namedArg, value := range n.NamedArgument { - rawCopy := n.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") - } + if !n.noWrap && !contains(options, NoWrap) { + out.WriteByte('(') } - 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 - } - out.Args = append(out.Args, namedArgumentPos.Value) - currentArgNum := len(out.Args) - - dialectPlaceholder := out.Dialect.ArgumentPlaceholder()(currentArgNum) - // if placeholder is not unique identifier ($1, $2, etc..), we will replace just one occurence of the argument - toReplace := -1 // all occurrences - if dialectPlaceholder == "?" { - toReplace = 1 // just one occurrence - } - raw = strings.Replace(raw, namedArgumentPos.Name, dialectPlaceholder, toReplace) - } + out.insertRawQuery(n.Raw, n.NamedArgument) if !n.noWrap && !contains(options, NoWrap) { - raw = "(" + raw + ")" + out.WriteByte(')') } - - out.WriteString(raw) } // Raw can be used for any unsupported functions, operators or expressions. diff --git a/internal/jet/raw_statement.go b/internal/jet/raw_statement.go new file mode 100644 index 00000000..191c7b47 --- /dev/null +++ b/internal/jet/raw_statement.go @@ -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(")") + } +} diff --git a/internal/jet/sql_builder.go b/internal/jet/sql_builder.go index bca078d9..6241feec 100644 --- a/internal/jet/sql_builder.go +++ b/internal/jet/sql_builder.go @@ -7,6 +7,7 @@ import ( "github.com/go-jet/jet/v2/internal/utils" "github.com/google/uuid" "reflect" + "sort" "strconv" "strings" "time" @@ -135,6 +136,73 @@ func (s *SQLBuilder) insertParametrizedArgument(arg interface{}) { 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 { if utils.IsNil(value) { return "NULL" diff --git a/internal/testutils/test_utils.go b/internal/testutils/test_utils.go index f8492193..dd5e7906 100644 --- a/internal/testutils/test_utils.go +++ b/internal/testutils/test_utils.go @@ -116,8 +116,8 @@ func AssertDebugStatementSql(t *testing.T, query jet.Statement, expectedQuery st AssertDeepEqual(t, args, expectedArgs, "arguments are not equal") } - debuqSql := query.DebugSql() - assertQueryString(t, debuqSql, expectedQuery) + debugSql := query.DebugSql() + assertQueryString(t, debugSql, expectedQuery) } // AssertSerialize checks if clause serialize produces expected query and args @@ -134,24 +134,24 @@ 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) +// 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{}) { + out := jet.SQLBuilder{Dialect: dialect, Debug: true} + jet.Serialize(clause, jet.SelectStatementType, &out) - require.Equal(t, out.Buff.String(), query) + AssertDeepEqual(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 -func AssertDebugSerialize(t *testing.T, dialect jet.Dialect, clause jet.Serializer, query string, args ...interface{}) { - out := jet.SQLBuilder{Dialect: dialect, Debug: true} - jet.Serialize(clause, jet.SelectStatementType, &out) +// 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) - AssertDeepEqual(t, out.Buff.String(), query) + require.Equal(t, out.Buff.String(), query) if len(args) > 0 { AssertDeepEqual(t, out.Args, args) diff --git a/mysql/expressions_test.go b/mysql/expressions_test.go index 127fccdc..2826c08d 100644 --- a/mysql/expressions_test.go +++ b/mysql/expressions_test.go @@ -9,14 +9,20 @@ import ( 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) { diff --git a/mysql/statement.go b/mysql/statement.go new file mode 100644 index 00000000..073adce6 --- /dev/null +++ b/mysql/statement.go @@ -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...) +} diff --git a/postgres/expressions_test.go b/postgres/expressions_test.go index 1e7f3c6e..77c3dee4 100644 --- a/postgres/expressions_test.go +++ b/postgres/expressions_test.go @@ -9,27 +9,39 @@ import ( 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, 11, 22, 33, 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) { diff --git a/postgres/statement.go b/postgres/statement.go new file mode 100644 index 00000000..d10bd656 --- /dev/null +++ b/postgres/statement.go @@ -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...) +} diff --git a/postgres/utils_test.go b/postgres/utils_test.go index bd59b432..292d7e4e 100644 --- a/postgres/utils_test.go +++ b/postgres/utils_test.go @@ -58,6 +58,10 @@ func assertSerialize(t *testing.T, serializer jet.Serializer, query string, 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{}) { testutils.AssertClauseSerialize(t, Dialect, clause, query, args...) } diff --git a/tests/mysql/raw_statement_test.go b/tests/mysql/raw_statement_test.go new file mode 100644 index 00000000..9af4c467 --- /dev/null +++ b/tests/mysql/raw_statement_test.go @@ -0,0 +1,82 @@ +package mysql + +import ( + "testing" + + "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), + }) +} diff --git a/tests/postgres/raw_statements_test.go b/tests/postgres/raw_statements_test.go new file mode 100644 index 00000000..61c32288 --- /dev/null +++ b/tests/postgres/raw_statements_test.go @@ -0,0 +1,138 @@ +package postgres + +import ( + "testing" + + "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) +} From 3021a6a0fd381184ef1d8d941bd856377679292f Mon Sep 17 00:00:00 2001 From: go-jet Date: Sun, 16 May 2021 18:46:50 +0200 Subject: [PATCH 34/41] Add support to retrieve Rows from statement Rows statement method executes statements over db connection/transaction and returns Rows. --- internal/jet/statement.go | 35 ++++++++++++++++--- qrm/db.go | 3 +- qrm/qrm.go | 50 ++++++++++++++++++++++++++- tests/mysql/raw_statement_test.go | 41 ++++++++++++++++++++++ tests/mysql/select_test.go | 40 +++++++++++++++++++++ tests/postgres/raw_statements_test.go | 41 ++++++++++++++++++++++ tests/postgres/scan_test.go | 48 +++++++++++++++++++++++-- 7 files changed, 248 insertions(+), 10 deletions(-) diff --git a/internal/jet/statement.go b/internal/jet/statement.go index 6e87da86..da3650db 100644 --- a/internal/jet/statement.go +++ b/internal/jet/statement.go @@ -13,19 +13,30 @@ type Statement interface { // 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. 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. // If destination is pointer to struct and query result set is empty, method returns qrm.ErrNoRows. 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. // 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 - - //Exec executes statement over db connection without returning any rows. + //Exec executes statement over db connection/transaction without returning any rows. 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) + // 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 @@ -99,6 +110,20 @@ func (s *serializerStatementInterfaceImpl) ExecContext(ctx context.Context, db q 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) { if logger != nil { logger(ctx, statement) diff --git a/qrm/db.go b/qrm/db.go index 564819a4..6b319eb3 100644 --- a/qrm/db.go +++ b/qrm/db.go @@ -5,7 +5,8 @@ import ( "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 { Exec(query string, args ...interface{}) (sql.Result, error) ExecContext(ctx context.Context, query string, args ...interface{}) (sql.Result, error) diff --git a/qrm/qrm.go b/qrm/qrm.go index 7477569b..52c1a280 100644 --- a/qrm/qrm.go +++ b/qrm/qrm.go @@ -2,9 +2,12 @@ package qrm import ( "context" + "database/sql" "errors" - "github.com/go-jet/jet/v2/internal/utils" + "fmt" "reflect" + + "github.com/go-jet/jet/v2/internal/utils" ) // ErrNoRows is returned by Query when query result set is empty @@ -56,6 +59,51 @@ func Query(ctx context.Context, db DB, query string, args []interface{}, destPtr } } +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) { if ctx == nil { ctx = context.Background() diff --git a/tests/mysql/raw_statement_test.go b/tests/mysql/raw_statement_test.go index 9af4c467..fd4531ff 100644 --- a/tests/mysql/raw_statement_test.go +++ b/tests/mysql/raw_statement_test.go @@ -1,7 +1,9 @@ package mysql import ( + "context" "testing" + "time" "github.com/stretchr/testify/require" @@ -80,3 +82,42 @@ func TestRawStatementSelectWithArguments(t *testing.T) { 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) +} diff --git a/tests/mysql/select_test.go b/tests/mysql/select_test.go index 1a60a429..6bbc2117 100644 --- a/tests/mysql/select_test.go +++ b/tests/mysql/select_test.go @@ -1,8 +1,10 @@ package mysql import ( + "context" "strings" "testing" + "time" "github.com/go-jet/jet/v2/internal/testutils" . "github.com/go-jet/jet/v2/mysql" @@ -887,3 +889,41 @@ LIMIT 1; 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) +} diff --git a/tests/postgres/raw_statements_test.go b/tests/postgres/raw_statements_test.go index 61c32288..a193258c 100644 --- a/tests/postgres/raw_statements_test.go +++ b/tests/postgres/raw_statements_test.go @@ -1,7 +1,9 @@ package postgres import ( + "context" "testing" + "time" "github.com/stretchr/testify/require" @@ -136,3 +138,42 @@ RETURNING link.id AS "link.id", 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) +} diff --git a/tests/postgres/scan_test.go b/tests/postgres/scan_test.go index def30979..dacdf880 100644 --- a/tests/postgres/scan_test.go +++ b/tests/postgres/scan_test.go @@ -1,14 +1,18 @@ package postgres import ( + "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/postgres" "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/table" - "github.com/google/uuid" - "github.com/stretchr/testify/require" - "testing" ) var oneInventoryQuery = Inventory. @@ -722,6 +726,44 @@ 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) +} + var address256 = model.Address{ AddressID: 256, Address: "1497 Yuzhou Drive", From 38541522e66895e6820c1e9c454e4c1b7a4af77d Mon Sep 17 00:00:00 2001 From: go-jet Date: Sun, 16 May 2021 19:10:43 +0200 Subject: [PATCH 35/41] Fix linter errors --- internal/jet/literal_expression.go | 16 ++++++++-------- internal/jet/select_table.go | 1 + mysql/expressions.go | 6 +++--- mysql/lateral.go | 1 + postgres/expressions.go | 6 +++--- postgres/lateral.go | 1 + qrm/qrm.go | 1 + 7 files changed, 18 insertions(+), 14 deletions(-) diff --git a/internal/jet/literal_expression.go b/internal/jet/literal_expression.go index 9ce9a813..d7cf47a9 100644 --- a/internal/jet/literal_expression.go +++ b/internal/jet/literal_expression.go @@ -440,42 +440,42 @@ func RawWithParent(raw string, parent ...Expression) Expression { return rawExp } -// Raw helper that for integer expressions +// RawInt helper that for integer expressions func RawInt(raw string, namedArgs ...map[string]interface{}) IntegerExpression { return IntExp(Raw(raw, namedArgs...)) } -// Raw helper that for float expressions +// RawFloat helper that for float expressions func RawFloat(raw string, namedArgs ...map[string]interface{}) FloatExpression { return FloatExp(Raw(raw, namedArgs...)) } -// Raw helper that for string expressions +// RawString helper that for string expressions func RawString(raw string, namedArgs ...map[string]interface{}) StringExpression { return StringExp(Raw(raw, namedArgs...)) } -// Raw helper that for time expressions +// RawTime helper that for time expressions func RawTime(raw string, namedArgs ...map[string]interface{}) TimeExpression { return TimeExp(Raw(raw, namedArgs...)) } -// Raw helper that for time with time zone expressions +// RawTimez helper that for time with time zone expressions func RawTimez(raw string, namedArgs ...map[string]interface{}) TimezExpression { return TimezExp(Raw(raw, namedArgs...)) } -// Raw helper that for timestamp expressions +// RawTimestamp helper that for timestamp expressions func RawTimestamp(raw string, namedArgs ...map[string]interface{}) TimestampExpression { return TimestampExp(Raw(raw, namedArgs...)) } -// Raw helper that for timestamp with time zone expressions +// RawTimestampz helper that for timestamp with time zone expressions func RawTimestampz(raw string, namedArgs ...map[string]interface{}) TimestampzExpression { return TimestampzExp(Raw(raw, namedArgs...)) } -// Raw helper that for date expressions +// RawDate helper that for date expressions func RawDate(raw string, namedArgs ...map[string]interface{}) DateExpression { return DateExp(Raw(raw, namedArgs...)) } diff --git a/internal/jet/select_table.go b/internal/jet/select_table.go index 1421b14e..541992f9 100644 --- a/internal/jet/select_table.go +++ b/internal/jet/select_table.go @@ -45,6 +45,7 @@ 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)} } diff --git a/mysql/expressions.go b/mysql/expressions.go index 7c13939b..b5857197 100644 --- a/mysql/expressions.go +++ b/mysql/expressions.go @@ -73,12 +73,12 @@ 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. +// For example: Raw("current_database()") +// Raw helper methods for each of the mysql types var ( - // Raw can be used for any unsupported functions, operators or expressions. - // For example: Raw("current_database()") Raw = jet.Raw - // Raw helper methods for each of the mysql type RawInt = jet.RawInt RawFloat = jet.RawFloat RawString = jet.RawString diff --git a/mysql/lateral.go b/mysql/lateral.go index 8ba974b1..30ebb49f 100644 --- a/mysql/lateral.go +++ b/mysql/lateral.go @@ -2,6 +2,7 @@ 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, diff --git a/postgres/expressions.go b/postgres/expressions.go index ca6223b1..8d0be889 100644 --- a/postgres/expressions.go +++ b/postgres/expressions.go @@ -84,12 +84,12 @@ 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. +// For example: Raw("current_database()") +// Raw helper methods for each of the postgres types var ( - // Raw can be used for any unsupported functions, operators or expressions. - // For example: Raw("current_database()") Raw = jet.Raw - // Raw helper methods for each of the postgres type RawInt = jet.RawInt RawFloat = jet.RawFloat RawString = jet.RawString diff --git a/postgres/lateral.go b/postgres/lateral.go index 8d2d4f8a..7b0ded66 100644 --- a/postgres/lateral.go +++ b/postgres/lateral.go @@ -2,6 +2,7 @@ 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, diff --git a/qrm/qrm.go b/qrm/qrm.go index 52c1a280..51bbffa2 100644 --- a/qrm/qrm.go +++ b/qrm/qrm.go @@ -59,6 +59,7 @@ 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") From 8a283bea251953ecfd04688d96669ca543b411d1 Mon Sep 17 00:00:00 2001 From: go-jet Date: Sun, 16 May 2021 19:19:27 +0200 Subject: [PATCH 36/41] Update jet generator version --- cmd/jet/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/jet/main.go b/cmd/jet/main.go index 8e0a6067..136e58b1 100644 --- a/cmd/jet/main.go +++ b/cmd/jet/main.go @@ -47,7 +47,7 @@ func main() { flag.Usage = func() { _, _ = fmt.Fprint(os.Stdout, ` -Jet generator 2.3.0 +Jet generator 2.5.0 Usage: -source string From 87373f14258717d20efd684a00fa3b8b51a6d891 Mon Sep 17 00:00:00 2001 From: go-jet Date: Mon, 17 May 2021 14:53:53 +0200 Subject: [PATCH 37/41] Test fix --- internal/jet/literal_expression_test.go | 2 +- tests/testdata | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/jet/literal_expression_test.go b/internal/jet/literal_expression_test.go index 5142f27b..182aa25e 100644 --- a/internal/jet/literal_expression_test.go +++ b/internal/jet/literal_expression_test.go @@ -6,7 +6,7 @@ import ( ) 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) diff --git a/tests/testdata b/tests/testdata index 0d52780c..a6c1975a 160000 --- a/tests/testdata +++ b/tests/testdata @@ -1 +1 @@ -Subproject commit 0d52780c6510d4b1e560081a82648b85c555ce43 +Subproject commit a6c1975a167645f913496131ae81d4cabc070046 From 3e9eda28c10391fefb8e37809518498425c7b00c Mon Sep 17 00:00:00 2001 From: go-jet Date: Mon, 17 May 2021 14:54:26 +0200 Subject: [PATCH 38/41] Update README.md --- README.md | 43 +++++++++++++++-------------- examples/quick-start/quick-start.go | 3 +- 2 files changed, 24 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index cb3499f5..e79e86c4 100644 --- a/README.md +++ b/README.md @@ -62,24 +62,33 @@ To install Jet package, you need to install Go and set your Go workspace first. ### 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 $ 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: +1) Install jet generator to GOPATH/bin folder: ```sh -$ go get -u github.com/go-jet/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.* -Install jet generator to GOPATH bin folder. This will allow generating jet files from the command line. +2) Install jet generator to specific folder: ```sh -cd $GOPATH/src/ && GO111MODULE=off go get -u github.com/go-jet/jet/cmd/jet +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.* -*Make sure GOPATH bin 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 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 -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. ##### Speed of execution 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. - -Common web and database server usually are not on the same physical machine, and there is some latency between them. -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). +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. -With Jet you can even join the whole database and store the whole structured result in one database call. +With Jet it is even possible to 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). 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/stretchr/testify` - `github.com/google/go-cmp` +- `github.com/jackc/pgx/v4` ## Versioning @@ -577,5 +578,5 @@ To run the tests, additional dependencies are required: ## License -Copyright 2019-2020 Goran Bjelanovic +Copyright 2019-2021 Goran Bjelanovic Licensed under the Apache License, Version 2.0. diff --git a/examples/quick-start/quick-start.go b/examples/quick-start/quick-start.go index ba5a0d84..5bdc424c 100644 --- a/examples/quick-start/quick-start.go +++ b/examples/quick-start/quick-start.go @@ -4,9 +4,10 @@ import ( "database/sql" "encoding/json" "fmt" - _ "github.com/lib/pq" "io/ioutil" + _ "github.com/lib/pq" + // dot import so that jet go code would resemble as much as native SQL // dot import is not mandatory . "github.com/go-jet/jet/v2/examples/quick-start/.gen/jetdb/dvds/table" From cecdab1c67f59e66fee5caf6466182e8499f1893 Mon Sep 17 00:00:00 2001 From: go-jet Date: Mon, 17 May 2021 15:13:54 +0200 Subject: [PATCH 39/41] Test fix. --- tests/mysql/alltypes_test.go | 38 ++++++++------------------------- tests/postgres/alltypes_test.go | 1 + 2 files changed, 10 insertions(+), 29 deletions(-) diff --git a/tests/mysql/alltypes_test.go b/tests/mysql/alltypes_test.go index a85d0b4a..d96c1d3b 100644 --- a/tests/mysql/alltypes_test.go +++ b/tests/mysql/alltypes_test.go @@ -125,37 +125,17 @@ LIMIT ?; } err := query.Query(db, &dest) - require.NoError(t, err) - //testutils.PrintJson(dest) - - testutils.AssertJSON(t, dest, ` -[ - { - "IsNull": false, - "IsNotNull": true, - "In": false, - "InSelect": false, - "Raw": "jet@localhost", - "RawArg": 148, - "RawArg2": -1479, - "NotIn": null, - "NotInSelect": true - }, - { - "IsNull": false, - "IsNotNull": false, - "In": null, - "InSelect": null, - "Raw": "jet@localhost", - "RawArg": 134, - "RawArg2": -1479, - "NotIn": null, - "NotInSelect": null - } -] -`) + require.Equal(t, *dest[0].IsNull, false) + require.Equal(t, *dest[0].IsNotNull, true) + require.Equal(t, *dest[0].In, false) + require.Equal(t, *dest[0].InSelect, false) + require.True(t, strings.Contains(*dest[0].Raw, "jet")) + require.Equal(t, *dest[0].RawArg, int32(148)) + require.Equal(t, *dest[0].RawArg2, int32(-1479)) + require.Nil(t, dest[0].NotIn) + require.Equal(t, *dest[0].NotInSelect, true) } func TestBoolOperators(t *testing.T) { diff --git a/tests/postgres/alltypes_test.go b/tests/postgres/alltypes_test.go index 4a1ab9fb..29986daf 100644 --- a/tests/postgres/alltypes_test.go +++ b/tests/postgres/alltypes_test.go @@ -80,6 +80,7 @@ func TestAllTypesInsertQuery(t *testing.T) { } func TestAllTypesFromSubQuery(t *testing.T) { + skipForPgxDriver(t) subQuery := SELECT(AllTypes.AllColumns). FROM(AllTypes). From 17e5e34111e4e144aa4382c7fc6d281afd6a1353 Mon Sep 17 00:00:00 2001 From: go-jet Date: Fri, 21 May 2021 16:09:29 +0200 Subject: [PATCH 40/41] Allow NUMERIC value scan into any number type --- qrm/utill.go | 16 ++++++++++--- tests/mysql/select_test.go | 45 +++++++++++++++++++++++++++++++++++++ tests/postgres/scan_test.go | 45 +++++++++++++++++++++++++++++++++++++ 3 files changed, 103 insertions(+), 3 deletions(-) diff --git a/qrm/utill.go b/qrm/utill.go index 15745992..f4857974 100644 --- a/qrm/utill.go +++ b/qrm/utill.go @@ -183,6 +183,10 @@ func isIntegerType(value reflect.Type) bool { return false } +func isNumber(valueType reflect.Type) bool { + return isIntegerType(valueType) || valueType == float64Type || valueType == float32Type +} + func tryAssign(source, destination reflect.Value) bool { switch { @@ -196,13 +200,18 @@ func tryAssign(source, destination reflect.Value) bool { } else if intValue == 0 { source = reflect.ValueOf(false) } - case source.Type() == stringType && destination.Type() == float64Type: - strValue := source.String() - f, err := strconv.ParseFloat(strValue, 64) + 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()) { @@ -289,6 +298,7 @@ var int32Type = reflect.TypeOf(int32(1)) var uint32Type = reflect.TypeOf(uint32(1)) var int64Type = reflect.TypeOf(int64(1)) var uint64Type = reflect.TypeOf(uint64(1)) +var float32Type = reflect.TypeOf(float32(1)) var float64Type = reflect.TypeOf(float64(1)) var stringType = reflect.TypeOf("") diff --git a/tests/mysql/select_test.go b/tests/mysql/select_test.go index 6bbc2117..5a88acf0 100644 --- a/tests/mysql/select_test.go +++ b/tests/mysql/select_test.go @@ -927,3 +927,48 @@ func TestRowsScan(t *testing.T) { 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)) +} diff --git a/tests/postgres/scan_test.go b/tests/postgres/scan_test.go index dacdf880..4a80ba07 100644 --- a/tests/postgres/scan_test.go +++ b/tests/postgres/scan_test.go @@ -764,6 +764,51 @@ func TestRowsScan(t *testing.T) { 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{ AddressID: 256, Address: "1497 Yuzhou Drive", From caa81930dc5f5999a6ef4fe54fcaa68fb2f7dae6 Mon Sep 17 00:00:00 2001 From: go-jet Date: Fri, 21 May 2021 16:25:26 +0200 Subject: [PATCH 41/41] Reset FROM clause list before new values are set. --- mysql/select_statement.go | 1 + postgres/select_statement.go | 1 + 2 files changed, 2 insertions(+) diff --git a/mysql/select_statement.go b/mysql/select_statement.go index fa6dd9cc..8ebab036 100644 --- a/mysql/select_statement.go +++ b/mysql/select_statement.go @@ -106,6 +106,7 @@ func (s *selectStatementImpl) DISTINCT() SelectStatement { } func (s *selectStatementImpl) FROM(tables ...ReadableTable) SelectStatement { + s.From.Tables = nil for _, table := range tables { s.From.Tables = append(s.From.Tables, table) } diff --git a/postgres/select_statement.go b/postgres/select_statement.go index a0d3e275..516ae25b 100644 --- a/postgres/select_statement.go +++ b/postgres/select_statement.go @@ -109,6 +109,7 @@ func (s *selectStatementImpl) DISTINCT() SelectStatement { } func (s *selectStatementImpl) FROM(tables ...ReadableTable) SelectStatement { + s.From.Tables = nil for _, table := range tables { s.From.Tables = append(s.From.Tables, table) }