Skip to content

Commit

Permalink
tables: add flag to include schema in struct names
Browse files Browse the repository at this point in the history
Fixes #4.
  • Loading branch information
bokwoon95 committed Jun 9, 2023
1 parent 68ca354 commit 389d6f5
Show file tree
Hide file tree
Showing 3 changed files with 251 additions and 6 deletions.
23 changes: 23 additions & 0 deletions ddl/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ func TestPostgres(t *testing.T) {
return record[:len(record)-1]
},
})
testTablesCmdSchemaQualifiedStructs(t, dialect, *postgresDSN, "testdata/postgres/schema_qualified_tables.go")
}

func TestMySQL(t *testing.T) {
Expand Down Expand Up @@ -392,6 +393,28 @@ func testLoadDump(t *testing.T, dialect string, dsn string, transforms map[strin
assertCSVsAreIdentical(t, tempDir, "testdata/extended_subset", nil, transforms)
}

func testTablesCmdSchemaQualifiedStructs(t *testing.T, dialect string, dsn string, goldenFile string) {
tablesCmd, err := TablesCommand("-db", dsn, "-schema-qualified-structs")
if err != nil {
t.Fatal(testutil.Callers(), err)
}
var buf bytes.Buffer
tablesCmd.Stdout = &buf
err = tablesCmd.Run()
if err != nil {
t.Fatal(testutil.Callers(), err)
}
b, err := os.ReadFile(goldenFile)
if err != nil {
t.Fatal(testutil.Callers(), err)
}
gotOutput := buf.String()
wantOutput := string(b)
if diff := testutil.Diff(gotOutput, wantOutput); diff != "" {
t.Error(testutil.Callers(), diff)
}
}

func rewriteCSV(filename string, transform func(record []string) []string) error {
file, err := os.Open(filename)
if err != nil {
Expand Down
49 changes: 43 additions & 6 deletions ddl/tables_cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ type TablesCmd struct {
// blank to write to Stdout instead.
Filename string

// SchemaQualifiedStructs controls whether the generated struct names are
// schema qualified e.g. `type SCHEMA_TABLE struct` instead of `type TABLE
// struct`.
SchemaQualifiedStructs bool

// Stdout specifies the command's standard out to write to if no Filename
// is provided. If nil, the command writes to os.Stdout.
Stdout io.Writer
Expand Down Expand Up @@ -58,6 +63,7 @@ func TablesCommand(args ...string) (*TablesCmd, error) {
flagset.StringVar(&cmd.HistoryTable, "history-table", "sqddl_history", "Name of migration history table.")
flagset.StringVar(&cmd.PackageName, "pkg", "_", "Package name used in the generated code.")
flagset.StringVar(&cmd.Filename, "file", "", "Name of the file to write the generated code into. Leave blank to write to stdout.")
flagset.BoolVar(&cmd.SchemaQualifiedStructs, "schema-qualified-structs", false, "Schema-qualify the generated struct names.")
flagset.StringVar(&schemas, "schemas", "", "Comma-separated list of schemas to be included.")
flagset.StringVar(&excludeSchemas, "exclude-schemas", "", "Comma-separated list of schemas to be excluded.")
flagset.StringVar(&tables, "tables", "", "Comma-separated list of tables to be included.")
Expand Down Expand Up @@ -120,15 +126,46 @@ func (cmd *TablesCmd) Run() error {
defer cmd.DB.Close()
}

tableStructs, err := NewTableStructs(cmd.Dialect, cmd.DB, Filter{
Schemas: cmd.Schemas,
ExcludeSchemas: cmd.ExcludeSchemas,
Tables: cmd.Tables,
ExcludeTables: append(cmd.ExcludeTables, cmd.HistoryTable),
})
var tableStructs TableStructs
var catalog Catalog
dbi := &DatabaseIntrospector{
Dialect: cmd.Dialect,
DB: cmd.DB,
Filter: Filter{
Schemas: cmd.Schemas,
ExcludeSchemas: cmd.ExcludeSchemas,
Tables: cmd.Tables,
ExcludeTables: append(cmd.ExcludeTables, cmd.HistoryTable),
},
}
dbi.ObjectTypes = []string{"TABLES"}
err := dbi.WriteCatalog(&catalog)
if err != nil {
return err
}
err = tableStructs.ReadCatalog(&catalog)
if err != nil {
return err
}
if cmd.SchemaQualifiedStructs {
for i := range tableStructs {
tableStruct := &tableStructs[i]
tableName := strings.ToLower(tableStruct.Name)
if tableStruct.Fields[0].NameTag != "" {
tableName = tableStruct.Fields[0].NameTag
}
tableSchema, tableName, _ := strings.Cut(tableName, ".")
if tableName == "" {
tableSchema, tableName = tableName, tableSchema
}
if tableSchema == "" && catalog.CurrentSchema != "" {
tableSchema = catalog.CurrentSchema
}
if tableSchema != "" {
tableStruct.Name = strings.ToUpper(strings.ReplaceAll(tableSchema+"_"+tableName, " ", "_"))
}
}
}
text, err := tableStructs.MarshalText()
if err != nil {
return err
Expand Down
185 changes: 185 additions & 0 deletions ddl/testdata/postgres/schema_qualified_tables.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
package _

import "github.com/bokwoon95/sq"

type PUBLIC_ACTOR struct {
sq.TableStruct
ACTOR_ID sq.NumberField `ddl:"type=int notnull primarykey identity"`
FIRST_NAME sq.StringField `ddl:"type=varchar(45) notnull"`
LAST_NAME sq.StringField `ddl:"type=varchar(45) notnull index"`
FULL_NAME sq.StringField `ddl:"type=text generated"`
FULL_NAME_REVERSED sq.StringField `ddl:"type=text generated"`
LAST_UPDATE sq.TimeField `ddl:"type=timestamptz notnull default=CURRENT_TIMESTAMP"`
}

type PUBLIC_ADDRESS struct {
sq.TableStruct
ADDRESS_ID sq.NumberField `ddl:"type=int notnull primarykey identity"`
ADDRESS sq.StringField `ddl:"type=varchar(50) notnull"`
ADDRESS2 sq.StringField `ddl:"type=varchar(50)"`
DISTRICT sq.StringField `ddl:"type=varchar(20) notnull"`
CITY_ID sq.NumberField `ddl:"type=int notnull references={city onupdate=cascade ondelete=restrict deferrable index}"`
POSTAL_CODE sq.StringField `ddl:"type=varchar(10)"`
PHONE sq.StringField `ddl:"type=text notnull"`
LAST_UPDATE sq.TimeField `ddl:"type=timestamptz notnull default=CURRENT_TIMESTAMP"`
}

type PUBLIC_CATEGORY struct {
sq.TableStruct
CATEGORY_ID sq.NumberField `ddl:"type=int notnull primarykey identity"`
NAME sq.StringField `ddl:"type=varchar(45) notnull"`
LAST_UPDATE sq.TimeField `ddl:"type=timestamptz notnull default=CURRENT_TIMESTAMP"`
}

type PUBLIC_CITY struct {
sq.TableStruct
CITY_ID sq.NumberField `ddl:"type=int notnull primarykey identity"`
CITY sq.StringField `ddl:"type=varchar(50) notnull"`
COUNTRY_ID sq.NumberField `ddl:"type=int notnull references={country onupdate=cascade ondelete=restrict deferrable index}"`
LAST_UPDATE sq.TimeField `ddl:"type=timestamptz notnull default=CURRENT_TIMESTAMP"`
}

type PUBLIC_COUNTRY struct {
sq.TableStruct
COUNTRY_ID sq.NumberField `ddl:"type=int notnull primarykey identity"`
COUNTRY sq.StringField `ddl:"type=varchar(50) notnull"`
LAST_UPDATE sq.TimeField `ddl:"type=timestamptz notnull default=CURRENT_TIMESTAMP"`
}

type PUBLIC_CUSTOMER struct {
sq.TableStruct
CUSTOMER_ID sq.NumberField `ddl:"type=int notnull primarykey identity"`
STORE_ID sq.NumberField `ddl:"type=int notnull references={store onupdate=cascade ondelete=restrict deferrable index}"`
FIRST_NAME sq.StringField `ddl:"type=varchar(45) notnull"`
LAST_NAME sq.StringField `ddl:"type=varchar(45) notnull index"`
EMAIL sq.StringField `ddl:"type=varchar(50) unique"`
ADDRESS_ID sq.NumberField `ddl:"type=int notnull references={address onupdate=cascade ondelete=restrict deferrable index}"`
ACTIVE sq.BooleanField `ddl:"type=boolean notnull default=true"`
CREATE_DATE sq.TimeField `ddl:"type=timestamptz notnull default=CURRENT_TIMESTAMP"`
LAST_UPDATE sq.TimeField `ddl:"type=timestamptz notnull default=CURRENT_TIMESTAMP"`
_ struct{} `ddl:"unique=email,first_name,last_name"`
}

type PUBLIC_DEPARTMENT struct {
sq.TableStruct
DEPARTMENT_ID sq.UUIDField `ddl:"type=uuid notnull primarykey"`
NAME sq.StringField `ddl:"type=varchar(255) notnull"`
}

type PUBLIC_EMPLOYEE struct {
sq.TableStruct
EMPLOYEE_ID sq.UUIDField `ddl:"type=uuid notnull primarykey"`
NAME sq.StringField `ddl:"type=varchar(255) notnull"`
TITLE sq.StringField `ddl:"type=varchar(255) notnull"`
MANAGER_ID sq.UUIDField `ddl:"type=uuid references={employee.employee_id index}"`
}

type PUBLIC_EMPLOYEE_DEPARTMENT struct {
sq.TableStruct `ddl:"primarykey=employee_id,department_id"`
EMPLOYEE_ID sq.UUIDField `ddl:"type=uuid notnull references={employee index}"`
DEPARTMENT_ID sq.UUIDField `ddl:"type=uuid notnull references={department index}"`
}

type PUBLIC_FILM struct {
sq.TableStruct
FILM_ID sq.NumberField `ddl:"type=int notnull primarykey identity"`
TITLE sq.StringField `ddl:"type=text notnull index"`
DESCRIPTION sq.StringField `ddl:"type=text"`
RELEASE_YEAR sq.NumberField `ddl:"type=year"`
LANGUAGE_ID sq.NumberField `ddl:"type=int notnull references={language onupdate=cascade ondelete=restrict deferrable index}"`
ORIGINAL_LANGUAGE_ID sq.NumberField `ddl:"type=int references={language.language_id onupdate=cascade ondelete=restrict deferrable index}"`
RENTAL_DURATION sq.NumberField `ddl:"type=int notnull default=3"`
RENTAL_RATE sq.NumberField `ddl:"type=numeric(4,2) notnull default=4.99"`
LENGTH sq.NumberField `ddl:"type=int"`
REPLACEMENT_COST sq.NumberField `ddl:"type=numeric(5,2) notnull default=19.99"`
RATING sq.EnumField `ddl:"type=mpaa_rating default='G'::mpaa_rating"`
SPECIAL_FEATURES sq.ArrayField `ddl:"type=text[]"`
LAST_UPDATE sq.TimeField `ddl:"type=timestamptz notnull default=CURRENT_TIMESTAMP"`
FULLTEXT sq.AnyField `ddl:"type=tsvector index={. using=gin}"`
}

type PUBLIC_FILM_ACTOR struct {
sq.TableStruct `ddl:"primarykey=actor_id,film_id"`
ACTOR_ID sq.NumberField `ddl:"type=int notnull references={actor onupdate=cascade ondelete=restrict deferrable}"`
FILM_ID sq.NumberField `ddl:"type=int notnull references={film onupdate=cascade ondelete=restrict deferrable index}"`
LAST_UPDATE sq.TimeField `ddl:"type=timestamptz notnull default=CURRENT_TIMESTAMP"`
}

type PUBLIC_FILM_CATEGORY struct {
sq.TableStruct `ddl:"primarykey=film_id,category_id"`
FILM_ID sq.NumberField `ddl:"type=int notnull references={film onupdate=cascade ondelete=restrict deferrable}"`
CATEGORY_ID sq.NumberField `ddl:"type=int notnull references={category onupdate=cascade ondelete=restrict deferrable}"`
LAST_UPDATE sq.TimeField `ddl:"type=timestamptz notnull default=CURRENT_TIMESTAMP"`
}

type PUBLIC_INVENTORY struct {
sq.TableStruct
INVENTORY_ID sq.NumberField `ddl:"type=int notnull primarykey identity"`
FILM_ID sq.NumberField `ddl:"type=int notnull references={film onupdate=cascade ondelete=restrict deferrable index}"`
STORE_ID sq.NumberField `ddl:"type=int notnull references={store onupdate=cascade ondelete=restrict deferrable}"`
LAST_UPDATE sq.TimeField `ddl:"type=timestamptz notnull default=CURRENT_TIMESTAMP"`
_ struct{} `ddl:"index=store_id,film_id"`
}

type PUBLIC_LANGUAGE struct {
sq.TableStruct
LANGUAGE_ID sq.NumberField `ddl:"type=int notnull primarykey identity"`
NAME sq.StringField `ddl:"type=text notnull"`
LAST_UPDATE sq.TimeField `ddl:"type=timestamptz notnull default=CURRENT_TIMESTAMP"`
}

type PUBLIC_PAYMENT struct {
sq.TableStruct
PAYMENT_ID sq.NumberField `ddl:"type=int notnull primarykey identity"`
CUSTOMER_ID sq.NumberField `ddl:"type=int notnull references={customer onupdate=cascade ondelete=restrict deferrable index}"`
STAFF_ID sq.NumberField `ddl:"type=int notnull references={staff onupdate=cascade ondelete=restrict deferrable index}"`
RENTAL_ID sq.NumberField `ddl:"type=int references={rental onupdate=cascade ondelete=setnull deferrable index}"`
AMOUNT sq.NumberField `ddl:"type=numeric(5,2) notnull"`
PAYMENT_DATE sq.TimeField `ddl:"type=timestamptz notnull default=CURRENT_TIMESTAMP"`
LAST_UPDATE sq.TimeField `ddl:"type=timestamptz notnull default=CURRENT_TIMESTAMP"`
}

type PUBLIC_RENTAL struct {
sq.TableStruct
RENTAL_ID sq.NumberField `ddl:"type=int notnull primarykey identity"`
RENTAL_DATE sq.TimeField `ddl:"type=timestamptz notnull default=CURRENT_TIMESTAMP"`
INVENTORY_ID sq.NumberField `ddl:"type=int notnull references={inventory onupdate=cascade ondelete=restrict deferrable index}"`
CUSTOMER_ID sq.NumberField `ddl:"type=int notnull references={customer onupdate=cascade ondelete=restrict deferrable index}"`
RETURN_DATE sq.TimeField `ddl:"type=timestamptz"`
STAFF_ID sq.NumberField `ddl:"type=int notnull references={staff onupdate=cascade ondelete=restrict deferrable index}"`
LAST_UPDATE sq.TimeField `ddl:"type=timestamptz notnull default=CURRENT_TIMESTAMP"`
_ struct{} `ddl:"index={inventory_id,customer_id,staff_id unique}"`
}

type PUBLIC_STAFF struct {
sq.TableStruct
STAFF_ID sq.NumberField `ddl:"type=int notnull primarykey identity"`
FIRST_NAME sq.StringField `ddl:"type=varchar(45) notnull"`
LAST_NAME sq.StringField `ddl:"type=varchar(45) notnull"`
ADDRESS_ID sq.NumberField `ddl:"type=int notnull references={address onupdate=cascade ondelete=restrict deferrable index}"`
PICTURE sq.BinaryField `ddl:"type=bytea"`
EMAIL sq.StringField `ddl:"type=varchar(50) unique"`
STORE_ID sq.NumberField `ddl:"type=int references={store deferrable index}"`
ACTIVE sq.BooleanField `ddl:"type=boolean notnull default=true"`
USERNAME sq.StringField `ddl:"type=varchar(16) notnull"`
PASSWORD sq.StringField `ddl:"type=varchar(40)"`
LAST_UPDATE sq.TimeField `ddl:"type=timestamptz notnull default=CURRENT_TIMESTAMP"`
}

type PUBLIC_STORE struct {
sq.TableStruct
STORE_ID sq.NumberField `ddl:"type=int notnull primarykey identity"`
MANAGER_STAFF_ID sq.NumberField `ddl:"type=int notnull references={staff.staff_id onupdate=cascade ondelete=restrict deferrable index}"`
ADDRESS_ID sq.NumberField `ddl:"type=int notnull references={address onupdate=cascade ondelete=restrict deferrable index}"`
LAST_UPDATE sq.TimeField `ddl:"type=timestamptz notnull default=CURRENT_TIMESTAMP"`
}

type PUBLIC_TASK struct {
sq.TableStruct
TASK_ID sq.UUIDField `ddl:"type=uuid notnull primarykey"`
EMPLOYEE_ID sq.UUIDField `ddl:"type=uuid notnull"`
DEPARTMENT_ID sq.UUIDField `ddl:"type=uuid notnull"`
TASK sq.StringField `ddl:"type=varchar(255) notnull"`
DATA sq.JSONField `ddl:"type=jsonb"`
_ struct{} `ddl:"foreignkey={employee_id,department_id references=employee_department index}"`
}

0 comments on commit 389d6f5

Please sign in to comment.