From 1e511654fdd691a9961d04e2849cf354b5f47f3a Mon Sep 17 00:00:00 2001 From: Joonas Haapsaari Date: Tue, 18 Aug 2020 15:47:10 +0300 Subject: [PATCH 1/8] 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 56dba2c..5288ef4 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 45d46e9..68f5962 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 66646b2..47923cd 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 2/8] 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 68f5962..78abb99 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 47923cd..b880784 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 268ae06..43f1f5d 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 3/8] 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 5288ef4..186ade0 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 5630966..0ae7ee8 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 cc8b3c3..d95638a 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 7bf9242..9b7b3d1 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 ac153d3..f90c114 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 ef5f72e..bd59b43 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 356806d..a2f6eb3 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 5728725..6fbb136 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 1e9a51d..c9dcc1a 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) } -func newActorInfoTable() ActorInfoTable { +// Schema creates new ActorInfoTable with assigned schema name +func (a ActorInfoTable) FromSchema(schemaName string) ActorInfoTable { + return newActorInfoTable(schemaName, a.TableName(), a.Alias()) +} + +func newActorInfoTable(schemaName, tableName, alias string) ActorInfoTable { var ( 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 dc6af0d..f447218 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 bef22f9..3b6aa75 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 50f5e60..553f920 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) +} - //spew.Dump(dest) +func Test_SchemaRename(t *testing.T) { + + Artist2 := Artist.FromSchema("chinook2") + Album2 := Album.FromSchema("chinook2") + + first10Artist := Artist2. + SELECT(Artist2.AllColumns). + ORDER_BY(Artist2.ArtistId). + LIMIT(10). + AsTable("first10Artist") + + artistID := Artist2.ArtistId.From(first10Artist) + + first10Albums := Album2. + SELECT(Album2.AllColumns). + ORDER_BY(Album2.AlbumId). + LIMIT(10). + AsTable("first10Albums") + + albumArtistID := Album2.ArtistId.From(first10Albums) + + stmt := SELECT(first10Artist.AllColumns(), first10Albums.AllColumns()). + FROM(first10Artist. + INNER_JOIN(first10Albums, artistID.EQ(albumArtistID))). + ORDER_BY(artistID) + + testutils.AssertDebugStatementSql(t, stmt, ` +SELECT "first10Artist"."Artist.ArtistId" AS "Artist.ArtistId", + "first10Artist"."Artist.Name" AS "Artist.Name", + "first10Albums"."Album.AlbumId" AS "Album.AlbumId", + "first10Albums"."Album.Title" AS "Album.Title", + "first10Albums"."Album.ArtistId" AS "Album.ArtistId" +FROM ( + SELECT "Artist"."ArtistId" AS "Artist.ArtistId", + "Artist"."Name" AS "Artist.Name" + FROM chinook2."Artist" + ORDER BY "Artist"."ArtistId" + LIMIT 10 + ) AS "first10Artist" + INNER JOIN ( + SELECT "Album"."AlbumId" AS "Album.AlbumId", + "Album"."Title" AS "Album.Title", + "Album"."ArtistId" AS "Album.ArtistId" + FROM chinook2."Album" + ORDER BY "Album"."AlbumId" + LIMIT 10 + ) AS "first10Albums" ON ("first10Artist"."Artist.ArtistId" = "first10Albums"."Album.ArtistId") +ORDER BY "first10Artist"."Artist.ArtistId"; +`) + + var dest []struct { + model.Artist + + Album []model.Album + } + + err := stmt.Query(db, &dest) + require.NoError(t, err) + + require.Len(t, dest, 2) + require.Equal(t, *dest[0].Artist.Name, "Apocalyptica") + require.Len(t, dest[0].Album, 1) + require.Equal(t, dest[0].Album[0].Title, "Plays Metallica By Four Cellos") } var album1 = model.Album{ diff --git a/tests/postgres/generator_test.go b/tests/postgres/generator_test.go index 1e23016..833e2a4 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) } -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") @@ -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) } -func newAllTypesTable() *AllTypesTable { +// Schema creates new AllTypesTable with assigned schema name +func (a AllTypesTable) FromSchema(schemaName string) *AllTypesTable { + return newAllTypesTable(schemaName, a.TableName(), a.Alias()) +} + +func newAllTypesTable(schemaName, tableName, alias string) *AllTypesTable { return &AllTypesTable{ - 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 bcb7361..327604f 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 8eb3276..def3097 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 e9dd7d2..8bd6f53 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 ed53a50..391d936 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 4/8] 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 376d73e..bbeb7e8 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 ac2caac..563938d 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 2f8c68e..65db900 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 1f6bb57..30c3ad3 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 41fa6f3..8368177 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 89470c2..5bddeeb 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 65d2560..5bfa25d 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 b4a2c8f..c03a5ef 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 5/8] 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 43eae46..35e0a6d 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 6/8] 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 52689d4..1421b14 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 0000000..fbbe953 --- /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 0000000..a401a1e --- /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 8bd6f53..afa7230 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 7/8] 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 8654547..42dad7e 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 78abb99..c69a1dc 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 556ed6c..272cf5f 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 37ac96b..fa6dd9c 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 b31909f..a0d3e27 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 3b6f498..fa12228 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 8/8] 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 0000000..8ba974b --- /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 fbbe953..8d2d4f8 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 a401a1e..35a0429 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 4f9268c..e2be933 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 f447218..1a60a42 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 afa7230..59fc44a 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) + }) }