diff --git a/.gitignore b/.gitignore index 4d7da45..a627142 100644 --- a/.gitignore +++ b/.gitignore @@ -21,4 +21,6 @@ gen .tests/testdata/ .gen .docker -.env \ No newline at end of file +.env +.tempTestDir +.gentestdata3 \ No newline at end of file diff --git a/generator/mysql/query_set.go b/generator/mysql/query_set.go index 5847be4..8da3f9c 100644 --- a/generator/mysql/query_set.go +++ b/generator/mysql/query_set.go @@ -3,10 +3,11 @@ package mysql import ( "context" "database/sql" + "strings" + "github.com/go-jet/jet/v2/generator/metadata" "github.com/go-jet/jet/v2/internal/utils/throw" "github.com/go-jet/jet/v2/qrm" - "strings" ) // mySqlQuerySet is dialect query set for MySQL @@ -16,7 +17,8 @@ func (m mySqlQuerySet) GetTablesMetaData(db *sql.DB, schemaName string, tableTyp query := ` SELECT table_name as "table.name" FROM INFORMATION_SCHEMA.tables -WHERE table_schema = ? and table_type = ?; +WHERE table_schema = ? and table_type = ? +ORDER BY table_name; ` var tables []metadata.Table diff --git a/generator/postgres/query_set.go b/generator/postgres/query_set.go index 8aa41bf..16c1275 100644 --- a/generator/postgres/query_set.go +++ b/generator/postgres/query_set.go @@ -3,6 +3,7 @@ package postgres import ( "context" "database/sql" + "github.com/go-jet/jet/v2/generator/metadata" "github.com/go-jet/jet/v2/internal/utils/throw" "github.com/go-jet/jet/v2/qrm" @@ -15,7 +16,8 @@ func (p postgresQuerySet) GetTablesMetaData(db *sql.DB, schemaName string, table query := ` SELECT table_name as "table.name" FROM information_schema.tables -WHERE table_schema = $1 and table_type = $2; +WHERE table_schema = $1 and table_type = $2 +ORDER BY table_name; ` var tables []metadata.Table diff --git a/generator/template/file_templates.go b/generator/template/file_templates.go index 35b4dcd..db30ab7 100644 --- a/generator/template/file_templates.go +++ b/generator/template/file_templates.go @@ -97,6 +97,18 @@ func new{{tableTemplate.TypeName}}Impl(schemaName, tableName, alias string) {{st } ` +var tableSqlBuilderSetSchemaTemplate = `package {{package}} + +// UseSchema changes all global tables/views with the value returned +// returned by calling FromSchema on them. Passing an empty string to this function +// will cause queries to be generated without any table/view alias. +func UseSchema(schema string) { +{{- range .}} + {{ .InstanceName }} = {{ .InstanceName }}.FromSchema(schema) +{{- end}} +} +` + var tableModelFileTemplate = `package {{package}} {{ with modelImports }} diff --git a/generator/template/process.go b/generator/template/process.go index 09636dc..417c0b2 100644 --- a/generator/template/process.go +++ b/generator/template/process.go @@ -3,13 +3,14 @@ package template import ( "bytes" "fmt" + "path" + "strings" + "text/template" + "github.com/go-jet/jet/v2/generator/metadata" "github.com/go-jet/jet/v2/internal/jet" "github.com/go-jet/jet/v2/internal/utils" "github.com/go-jet/jet/v2/internal/utils/throw" - "path" - "strings" - "text/template" ) // ProcessSchema will process schema metadata and constructs go files using generator Template @@ -118,6 +119,8 @@ func processTableSQLBuilder(fileTypes, dirPath string, fmt.Printf("Generating %s sql builder files\n", fileTypes) + var generatedBuilders []TableSQLBuilder + for _, tableMetaData := range tablesMetaData { var tableSQLBuilder TableSQLBuilder @@ -169,7 +172,31 @@ func processTableSQLBuilder(fileTypes, dirPath string, err = utils.SaveGoFile(tableSQLBuilderPath, tableSQLBuilder.FileName, text) throw.OnError(err) + + generatedBuilders = append(generatedBuilders, tableSQLBuilder) } + + if len(generatedBuilders) > 0 { + generateUseSchemaFunc(dirPath, fileTypes, generatedBuilders) + } +} + +func generateUseSchemaFunc(dirPath, fileTypes string, builders []TableSQLBuilder) { + + basePath := path.Join(dirPath, builders[0].Path) + + fmt.Printf("Generating global `UseSchema` method for %s\n", fileTypes) + text, err := generateTemplate( + autoGenWarningTemplate+tableSqlBuilderSetSchemaTemplate, + builders, + template.FuncMap{ + "package": func() string { return builders[0].PackageName() }, + }, + ) + throw.OnError(err) + + err = utils.SaveGoFile(basePath, fileTypes, text) + throw.OnError(err) } func insertedRowAlias(dialect jet.Dialect) string { diff --git a/tests/mysql/generator_test.go b/tests/mysql/generator_test.go index acbe04b..9e1403d 100644 --- a/tests/mysql/generator_test.go +++ b/tests/mysql/generator_test.go @@ -7,10 +7,11 @@ import ( "strconv" "testing" + "github.com/stretchr/testify/require" + "github.com/go-jet/jet/v2/generator/mysql" "github.com/go-jet/jet/v2/internal/testutils" "github.com/go-jet/jet/v2/tests/dbconfig" - "github.com/stretchr/testify/require" ) const genTestDirRoot = "./.gentestdata3" @@ -132,12 +133,12 @@ func TestIgnoreTablesViewsEnums(t *testing.T) { tableSQLBuilderFiles, err := ioutil.ReadDir(genTestDir3 + "/dvds/table") require.NoError(t, err) testutils.AssertFileNamesEqual(t, tableSQLBuilderFiles, "customer.go", "film.go", "film_actor.go", - "film_category.go", "film_text.go", "inventory.go", "language.go", "payment.go") + "film_category.go", "film_text.go", "inventory.go", "language.go", "payment.go", "table.go") viewSQLBuilderFiles, err := ioutil.ReadDir(genTestDir3 + "/dvds/view") require.NoError(t, err) testutils.AssertFileNamesEqual(t, viewSQLBuilderFiles, "nicer_but_slower_film_list.go", - "sales_by_film_category.go", "sales_by_store.go", "staff_list.go") + "sales_by_film_category.go", "sales_by_store.go", "staff_list.go", "view.go") enumFiles, err := ioutil.ReadDir(genTestDir3 + "/dvds/enum") require.NoError(t, err) @@ -161,18 +162,20 @@ func assertGeneratedFiles(t *testing.T) { testutils.AssertFileNamesEqual(t, tableSQLBuilderFiles, "actor.go", "address.go", "category.go", "city.go", "country.go", "customer.go", "film.go", "film_actor.go", "film_category.go", "film_text.go", "inventory.go", "language.go", - "payment.go", "rental.go", "staff.go", "store.go") + "payment.go", "rental.go", "staff.go", "store.go", "table.go") testutils.AssertFileContent(t, genTestDir3+"/dvds/table/actor.go", actorSQLBuilderFile) + testutils.AssertFileContent(t, genTestDir3+"/dvds/table/table.go", tableSetSchemaFile) // View SQL Builder files viewSQLBuilderFiles, err := ioutil.ReadDir(genTestDir3 + "/dvds/view") require.NoError(t, err) testutils.AssertFileNamesEqual(t, viewSQLBuilderFiles, "actor_info.go", "film_list.go", "nicer_but_slower_film_list.go", - "sales_by_film_category.go", "customer_list.go", "sales_by_store.go", "staff_list.go") + "sales_by_film_category.go", "customer_list.go", "sales_by_store.go", "staff_list.go", "view.go") testutils.AssertFileContent(t, genTestDir3+"/dvds/view/actor_info.go", actorInfoSQLBuilderFile) + testutils.AssertFileContent(t, genTestDir3+"/dvds/view/view.go", viewSetSchemaFile) // Enums SQL Builder files enumFiles, err := ioutil.ReadDir(genTestDir3 + "/dvds/enum") @@ -309,6 +312,39 @@ func newActorTableImpl(schemaName, tableName, alias string) actorTable { } ` +var tableSetSchemaFile = ` +// +// Code generated by go-jet DO NOT EDIT. +// +// WARNING: Changes to this file may cause incorrect behavior +// and will be lost if the code is regenerated +// + +package table + +// UseSchema changes all global tables/views with the value returned +// returned by calling FromSchema on them. Passing an empty string to this function +// will cause queries to be generated without any table/view alias. +func UseSchema(schema string) { + Actor = Actor.FromSchema(schema) + Address = Address.FromSchema(schema) + Category = Category.FromSchema(schema) + City = City.FromSchema(schema) + Country = Country.FromSchema(schema) + Customer = Customer.FromSchema(schema) + Film = Film.FromSchema(schema) + FilmActor = FilmActor.FromSchema(schema) + FilmCategory = FilmCategory.FromSchema(schema) + FilmText = FilmText.FromSchema(schema) + Inventory = Inventory.FromSchema(schema) + Language = Language.FromSchema(schema) + Payment = Payment.FromSchema(schema) + Rental = Rental.FromSchema(schema) + Staff = Staff.FromSchema(schema) + Store = Store.FromSchema(schema) +} +` + var actorModelFile = ` // // Code generated by go-jet DO NOT EDIT. @@ -417,3 +453,26 @@ func newActorInfoTableImpl(schemaName, tableName, alias string) actorInfoTable { } } ` +var viewSetSchemaFile = ` +// +// Code generated by go-jet DO NOT EDIT. +// +// WARNING: Changes to this file may cause incorrect behavior +// and will be lost if the code is regenerated +// + +package view + +// UseSchema changes all global tables/views with the value returned +// returned by calling FromSchema on them. Passing an empty string to this function +// will cause queries to be generated without any table/view alias. +func UseSchema(schema string) { + ActorInfo = ActorInfo.FromSchema(schema) + CustomerList = CustomerList.FromSchema(schema) + FilmList = FilmList.FromSchema(schema) + NicerButSlowerFilmList = NicerButSlowerFilmList.FromSchema(schema) + SalesByFilmCategory = SalesByFilmCategory.FromSchema(schema) + SalesByStore = SalesByStore.FromSchema(schema) + StaffList = StaffList.FromSchema(schema) +} +` diff --git a/tests/mysql/update_dvds_test.go b/tests/mysql/update_dvds_test.go new file mode 100644 index 0000000..ea9149d --- /dev/null +++ b/tests/mysql/update_dvds_test.go @@ -0,0 +1,25 @@ +package mysql + +import ( + "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/table" +) + +func TestUpdateWithJoin(t *testing.T) { + statement := Staff.INNER_JOIN(Address, Address.AddressID.EQ(Staff.AddressID)). + UPDATE(Staff.LastName). + SET(String("New staff name")). + WHERE(Staff.StaffID.EQ(Int(1))) + + testutils.AssertStatementSql(t, statement, ` +UPDATE dvds.staff +INNER JOIN dvds.address ON (address.address_id = staff.address_id) +SET last_name = ? +WHERE staff.staff_id = ?; +`, "New staff name", int64(1)) + + testutils.AssertExecAndRollback(t, statement, db) +} diff --git a/tests/mysql/update_test.go b/tests/mysql/update_test.go index 76844eb..909d884 100644 --- a/tests/mysql/update_test.go +++ b/tests/mysql/update_test.go @@ -3,14 +3,15 @@ package mysql import ( "context" "database/sql" - "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" - "github.com/go-jet/jet/v2/tests/.gentestdata/mysql/test_sample/model" - . "github.com/go-jet/jet/v2/tests/.gentestdata/mysql/test_sample/table" - "github.com/stretchr/testify/require" "testing" "time" + + "github.com/stretchr/testify/require" + + "github.com/go-jet/jet/v2/internal/testutils" + . "github.com/go-jet/jet/v2/mysql" + "github.com/go-jet/jet/v2/tests/.gentestdata/mysql/test_sample/model" + . "github.com/go-jet/jet/v2/tests/.gentestdata/mysql/test_sample/table" ) func TestUpdateValues(t *testing.T) { @@ -259,22 +260,6 @@ func TestUpdateExecContext(t *testing.T) { require.Error(t, err, "context deadline exceeded") } -func TestUpdateWithJoin(t *testing.T) { - statement := Staff.INNER_JOIN(Address, Address.AddressID.EQ(Staff.AddressID)). - UPDATE(Staff.LastName). - SET(String("New staff name")). - WHERE(Staff.StaffID.EQ(Int(1))) - - testutils.AssertStatementSql(t, statement, ` -UPDATE dvds.staff -INNER JOIN dvds.address ON (address.address_id = staff.address_id) -SET last_name = ? -WHERE staff.staff_id = ?; -`, "New staff name", int64(1)) - - testutils.AssertExecAndRollback(t, statement, db) -} - func TestUpdateOptimizerHints(t *testing.T) { stmt := Link.UPDATE(Link.AllColumns). diff --git a/tests/postgres/generator_test.go b/tests/postgres/generator_test.go index 328de17..fb757ae 100644 --- a/tests/postgres/generator_test.go +++ b/tests/postgres/generator_test.go @@ -10,10 +10,11 @@ import ( "strconv" "testing" + "github.com/stretchr/testify/require" + "github.com/go-jet/jet/v2/generator/postgres" "github.com/go-jet/jet/v2/internal/testutils" "github.com/go-jet/jet/v2/tests/dbconfig" - "github.com/stretchr/testify/require" "github.com/go-jet/jet/v2/tests/.gentestdata/jetdb/dvds/model" ) @@ -152,14 +153,14 @@ func TestGeneratorIgnoreTables(t *testing.T) { testutils.AssertFileNamesEqual(t, tableSQLBuilderFiles, "category.go", "customer.go", "film_actor.go", "film_category.go", "inventory.go", "language.go", - "payment.go", "rental.go", "staff.go", "store.go") + "payment.go", "rental.go", "staff.go", "store.go", "table.go") // View SQL Builder files viewSQLBuilderFiles, err := ioutil.ReadDir("./.gentestdata2/jetdb/dvds/view") require.NoError(t, err) testutils.AssertFileNamesEqual(t, viewSQLBuilderFiles, "nicer_but_slower_film_list.go", - "sales_by_film_category.go", "customer_list.go", "sales_by_store.go") + "sales_by_film_category.go", "customer_list.go", "sales_by_store.go", "view.go") // Enums SQL Builder files _, err = ioutil.ReadDir("./.gentestdata2/jetdb/dvds/enum") @@ -240,18 +241,20 @@ func assertGeneratedFiles(t *testing.T) { testutils.AssertFileNamesEqual(t, tableSQLBuilderFiles, "actor.go", "address.go", "category.go", "city.go", "country.go", "customer.go", "film.go", "film_actor.go", "film_category.go", "inventory.go", "language.go", - "payment.go", "rental.go", "staff.go", "store.go") + "payment.go", "rental.go", "staff.go", "store.go", "table.go") testutils.AssertFileContent(t, "./.gentestdata2/jetdb/dvds/table/actor.go", actorSQLBuilderFile) + testutils.AssertFileContent(t, "./.gentestdata2/jetdb/dvds/table/table.go", actorSQLBuilderTableFile) // View SQL Builder files viewSQLBuilderFiles, err := ioutil.ReadDir("./.gentestdata2/jetdb/dvds/view") require.NoError(t, err) testutils.AssertFileNamesEqual(t, viewSQLBuilderFiles, "actor_info.go", "film_list.go", "nicer_but_slower_film_list.go", - "sales_by_film_category.go", "customer_list.go", "sales_by_store.go", "staff_list.go") + "sales_by_film_category.go", "customer_list.go", "sales_by_store.go", "staff_list.go", "view.go") testutils.AssertFileContent(t, "./.gentestdata2/jetdb/dvds/view/actor_info.go", actorInfoSQLBuilderFile) + testutils.AssertFileContent(t, "./.gentestdata2/jetdb/dvds/view/view.go", actorInfoSQLBuilderViewFile) // Enums SQL Builder files enumFiles, err := ioutil.ReadDir("./.gentestdata2/jetdb/dvds/enum") @@ -387,6 +390,38 @@ func newActorTableImpl(schemaName, tableName, alias string) actorTable { } ` +var actorSQLBuilderTableFile = ` +// +// Code generated by go-jet DO NOT EDIT. +// +// WARNING: Changes to this file may cause incorrect behavior +// and will be lost if the code is regenerated +// + +package table + +// UseSchema changes all global tables/views with the value returned +// returned by calling FromSchema on them. Passing an empty string to this function +// will cause queries to be generated without any table/view alias. +func UseSchema(schema string) { + Actor = Actor.FromSchema(schema) + Address = Address.FromSchema(schema) + Category = Category.FromSchema(schema) + City = City.FromSchema(schema) + Country = Country.FromSchema(schema) + Customer = Customer.FromSchema(schema) + Film = Film.FromSchema(schema) + FilmActor = FilmActor.FromSchema(schema) + FilmCategory = FilmCategory.FromSchema(schema) + Inventory = Inventory.FromSchema(schema) + Language = Language.FromSchema(schema) + Payment = Payment.FromSchema(schema) + Rental = Rental.FromSchema(schema) + Staff = Staff.FromSchema(schema) + Store = Store.FromSchema(schema) +} +` + var actorModelFile = ` // // Code generated by go-jet DO NOT EDIT. @@ -496,6 +531,30 @@ func newActorInfoTableImpl(schemaName, tableName, alias string) actorInfoTable { } ` +var actorInfoSQLBuilderViewFile = ` +// +// Code generated by go-jet DO NOT EDIT. +// +// WARNING: Changes to this file may cause incorrect behavior +// and will be lost if the code is regenerated +// + +package view + +// UseSchema changes all global tables/views with the value returned +// returned by calling FromSchema on them. Passing an empty string to this function +// will cause queries to be generated without any table/view alias. +func UseSchema(schema string) { + ActorInfo = ActorInfo.FromSchema(schema) + CustomerList = CustomerList.FromSchema(schema) + FilmList = FilmList.FromSchema(schema) + NicerButSlowerFilmList = NicerButSlowerFilmList.FromSchema(schema) + SalesByFilmCategory = SalesByFilmCategory.FromSchema(schema) + SalesByStore = SalesByStore.FromSchema(schema) + StaffList = StaffList.FromSchema(schema) +} +` + func TestGeneratedAllTypesSQLBuilderFiles(t *testing.T) { skipForCockroachDB(t) @@ -522,7 +581,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", "floats.go") + "person.go", "person_phone.go", "weird_names_table.go", "user.go", "floats.go", "table.go") testutils.AssertFileContent(t, tableDir+"/all_types.go", allTypesTableContent) } diff --git a/tests/sqlite/generator_test.go b/tests/sqlite/generator_test.go index 9665387..c987fc1 100644 --- a/tests/sqlite/generator_test.go +++ b/tests/sqlite/generator_test.go @@ -7,11 +7,12 @@ import ( "reflect" "testing" + "github.com/stretchr/testify/require" + "github.com/go-jet/jet/v2/generator/sqlite" "github.com/go-jet/jet/v2/internal/testutils" "github.com/go-jet/jet/v2/tests/.gentestdata/sqlite/sakila/model" "github.com/go-jet/jet/v2/tests/internal/utils/repo" - "github.com/stretchr/testify/require" ) func TestGeneratedModel(t *testing.T) { @@ -91,12 +92,12 @@ func TestCmdGeneratorIgnoreTablesViewsEnums(t *testing.T) { require.NoError(t, err) testutils.AssertFileNamesEqual(t, tableSQLBuilderFiles, "country.go", "customer.go", "film_actor.go", "film_category.go", "film_text.go", "inventory.go", "language.go", - "payment.go", "staff.go") + "payment.go", "staff.go", "table.go") viewSQLBuilderFiles, err := ioutil.ReadDir(genDestDir + "/view") require.NoError(t, err) testutils.AssertFileNamesEqual(t, viewSQLBuilderFiles, "sales_by_film_category.go", - "sales_by_store.go") + "sales_by_store.go", "view.go") modelFiles, err := ioutil.ReadDir(genDestDir + "/model") require.NoError(t, err) @@ -113,18 +114,20 @@ func assertGeneratedFiles(t *testing.T) { testutils.AssertFileNamesEqual(t, tableSQLBuilderFiles, "actor.go", "address.go", "category.go", "city.go", "country.go", "customer.go", "film.go", "film_actor.go", "film_category.go", "film_text.go", "inventory.go", "language.go", - "payment.go", "rental.go", "staff.go", "store.go") + "payment.go", "rental.go", "staff.go", "store.go", "table.go") testutils.AssertFileContent(t, genDestDir+"/table/actor.go", actorSQLBuilderFile) + testutils.AssertFileContent(t, genDestDir+"/table/table.go", actorSQLBuilderTableFile) // View SQL Builder files viewSQLBuilderFiles, err := ioutil.ReadDir(genDestDir + "/view") require.NoError(t, err) testutils.AssertFileNamesEqual(t, viewSQLBuilderFiles, "film_list.go", "sales_by_film_category.go", - "customer_list.go", "sales_by_store.go", "staff_list.go") + "customer_list.go", "sales_by_store.go", "staff_list.go", "view.go") testutils.AssertFileContent(t, genDestDir+"/view/film_list.go", filmListSQLBuilderFile) + testutils.AssertFileContent(t, genDestDir+"/view/view.go", filmListSQLBuilderViewFile) // Model files modelFiles, err := ioutil.ReadDir(genDestDir + "/model") @@ -225,6 +228,38 @@ func newActorTableImpl(schemaName, tableName, alias string) actorTable { } } ` +const actorSQLBuilderTableFile = ` +// +// Code generated by go-jet DO NOT EDIT. +// +// WARNING: Changes to this file may cause incorrect behavior +// and will be lost if the code is regenerated +// + +package table + +// UseSchema changes all global tables/views with the value returned +// returned by calling FromSchema on them. Passing an empty string to this function +// will cause queries to be generated without any table/view alias. +func UseSchema(schema string) { + Actor = Actor.FromSchema(schema) + Address = Address.FromSchema(schema) + Category = Category.FromSchema(schema) + City = City.FromSchema(schema) + Country = Country.FromSchema(schema) + Customer = Customer.FromSchema(schema) + Film = Film.FromSchema(schema) + FilmActor = FilmActor.FromSchema(schema) + FilmCategory = FilmCategory.FromSchema(schema) + FilmText = FilmText.FromSchema(schema) + Inventory = Inventory.FromSchema(schema) + Language = Language.FromSchema(schema) + Payment = Payment.FromSchema(schema) + Rental = Rental.FromSchema(schema) + Staff = Staff.FromSchema(schema) + Store = Store.FromSchema(schema) +} +` const filmListSQLBuilderFile = ` // @@ -325,6 +360,28 @@ func newFilmListTableImpl(schemaName, tableName, alias string) filmListTable { } ` +const filmListSQLBuilderViewFile = ` +// +// Code generated by go-jet DO NOT EDIT. +// +// WARNING: Changes to this file may cause incorrect behavior +// and will be lost if the code is regenerated +// + +package view + +// UseSchema changes all global tables/views with the value returned +// returned by calling FromSchema on them. Passing an empty string to this function +// will cause queries to be generated without any table/view alias. +func UseSchema(schema string) { + CustomerList = CustomerList.FromSchema(schema) + FilmList = FilmList.FromSchema(schema) + SalesByFilmCategory = SalesByFilmCategory.FromSchema(schema) + SalesByStore = SalesByStore.FromSchema(schema) + StaffList = StaffList.FromSchema(schema) +} +` + const addressModelFile = ` // // Code generated by go-jet DO NOT EDIT.