diff --git a/gen/aliases.go b/gen/aliases.go index 7168cc86..b9f847e4 100644 --- a/gen/aliases.go +++ b/gen/aliases.go @@ -1,7 +1,6 @@ package gen import ( - "fmt" "strings" "unicode" "unicode/utf8" @@ -10,26 +9,12 @@ import ( "github.com/volatiletech/strmangle" ) -// Aliases defines aliases for the generation run -type Aliases map[string]TableAlias - -// TableAlias defines the spellings for a table name in Go -type TableAlias struct { - UpPlural string `yaml:"up_plural,omitempty" toml:"up_plural,omitempty" json:"up_plural,omitempty"` - UpSingular string `yaml:"up_singular,omitempty" toml:"up_singular,omitempty" json:"up_singular,omitempty"` - DownPlural string `yaml:"down_plural,omitempty" toml:"down_plural,omitempty" json:"down_plural,omitempty"` - DownSingular string `yaml:"down_singular,omitempty" toml:"down_singular,omitempty" json:"down_singular,omitempty"` - - Columns map[string]string `yaml:"columns,omitempty" toml:"columns,omitempty" json:"columns,omitempty"` - Relationships map[string]string `yaml:"relationships,omitempty" toml:"relationships,omitempty" json:"relationships,omitempty"` -} - // initAliases takes the table information from the driver // and fills in aliases where the user has provided none. // // This leaves us with a complete list of Go names for all tables, // columns, and relationships. -func initAliases(a Aliases, tables []drivers.Table, relMap Relationships) { +func initAliases[C, I any](a drivers.Aliases, tables drivers.Tables[C, I], relMap Relationships) { for _, t := range tables { tableAlias := a[t.Key] cleanKey := strings.ReplaceAll(t.Key, ".", "_") @@ -76,33 +61,3 @@ func initAliases(a Aliases, tables []drivers.Table, relMap Relationships) { a[t.Key] = tableAlias } } - -// Table gets a table alias, panics if not found. -func (a Aliases) Table(table string) TableAlias { - t, ok := a[table] - if !ok { - panic("could not find table aliases for: " + table) - } - - return t -} - -// Column get's a column's aliased name, panics if not found. -func (t TableAlias) Column(column string) string { - c, ok := t.Columns[column] - if !ok { - panic(fmt.Sprintf("could not find column alias for: %s.%s", t.UpSingular, column)) - } - - return c -} - -// Relationship looks up a relationship, panics if not found. -func (t TableAlias) Relationship(fkey string) string { - r, ok := t.Relationships[fkey] - if !ok { - panic(fmt.Sprintf("could not find relationship alias for: %s.%s", t.UpSingular, fkey)) - } - - return r -} diff --git a/gen/bobgen-helpers/helpers.go b/gen/bobgen-helpers/helpers.go index daca4c65..064312ba 100644 --- a/gen/bobgen-helpers/helpers.go +++ b/gen/bobgen-helpers/helpers.go @@ -71,9 +71,9 @@ func DefaultOutputs(destination, pkgname string, noFactory bool, templates *Temp return outputs } -func GetConfigFromFile[DriverConfig any](configPath, driverConfigKey string) (gen.Config, DriverConfig, error) { +func GetConfigFromFile[ConstraintExtra, DriverConfig any](configPath, driverConfigKey string) (gen.Config[ConstraintExtra], DriverConfig, error) { var provider koanf.Provider - var config gen.Config + var config gen.Config[ConstraintExtra] var driverConfig DriverConfig _, err := os.Stat(configPath) @@ -85,11 +85,11 @@ func GetConfigFromFile[DriverConfig any](configPath, driverConfigKey string) (ge return config, driverConfig, err } - return GetConfigFromProvider[DriverConfig](provider, driverConfigKey) + return GetConfigFromProvider[ConstraintExtra, DriverConfig](provider, driverConfigKey) } -func GetConfigFromProvider[DriverConfig any](provider koanf.Provider, driverConfigKey string) (gen.Config, DriverConfig, error) { - var config gen.Config +func GetConfigFromProvider[ConstraintExtra, DriverConfig any](provider koanf.Provider, driverConfigKey string) (gen.Config[ConstraintExtra], DriverConfig, error) { + var config gen.Config[ConstraintExtra] var driverConfig DriverConfig k := koanf.New(".") diff --git a/gen/bobgen-mysql/driver/exclude-tables.golden.json b/gen/bobgen-mysql/driver/exclude-tables.golden.json index 253425b2..443e1062 100644 --- a/gen/bobgen-mysql/driver/exclude-tables.golden.json +++ b/gen/bobgen-mysql/driver/exclude-tables.golden.json @@ -23,7 +23,8 @@ "columns": [ "id" ], - "expressions": null + "expressions": null, + "extra": null } ], "constraints": { @@ -31,7 +32,8 @@ "name": "PRIMARY", "columns": [ "id" - ] + ], + "extra": null }, "foreign": null, "uniques": null @@ -60,7 +62,8 @@ "columns": [ "id" ], - "expressions": null + "expressions": null, + "extra": null } ], "constraints": { @@ -68,7 +71,8 @@ "name": "PRIMARY", "columns": [ "id" - ] + ], + "extra": null }, "foreign": null, "uniques": null @@ -97,7 +101,8 @@ "columns": [ "id" ], - "expressions": null + "expressions": null, + "extra": null } ], "constraints": { @@ -105,7 +110,8 @@ "name": "PRIMARY", "columns": [ "id" - ] + ], + "extra": null }, "foreign": null, "uniques": null @@ -201,14 +207,16 @@ "one", "two" ], - "expressions": null + "expressions": null, + "extra": null }, { "name": "PRIMARY", "columns": [ "id" ], - "expressions": null + "expressions": null, + "extra": null }, { "name": "something", @@ -216,14 +224,16 @@ "something", "another" ], - "expressions": null + "expressions": null, + "extra": null }, { "name": "sponsor_id", "columns": [ "sponsor_id" ], - "expressions": null + "expressions": null, + "extra": null } ], "constraints": { @@ -231,7 +241,8 @@ "name": "PRIMARY", "columns": [ "id" - ] + ], + "extra": null }, "foreign": [ { @@ -240,6 +251,7 @@ "one", "two" ], + "extra": null, "foreign_table": "type_monsters", "foreign_columns": [ "int_one", @@ -253,13 +265,15 @@ "columns": [ "something", "another" - ] + ], + "extra": null }, { "name": "sponsor_id", "columns": [ "sponsor_id" - ] + ], + "extra": null } ] } @@ -287,7 +301,8 @@ "columns": [ "id" ], - "expressions": null + "expressions": null, + "extra": null } ], "constraints": { @@ -295,7 +310,8 @@ "name": "PRIMARY", "columns": [ "id" - ] + ], + "extra": null }, "foreign": null, "uniques": null @@ -324,7 +340,8 @@ "columns": [ "id" ], - "expressions": null + "expressions": null, + "extra": null } ], "constraints": { @@ -332,7 +349,8 @@ "name": "PRIMARY", "columns": [ "id" - ] + ], + "extra": null }, "foreign": null, "uniques": null @@ -383,7 +401,8 @@ "columns": null, "expressions": [ "(`col1` + `col2`)" - ] + ], + "extra": null }, { "name": "idx2", @@ -392,7 +411,8 @@ ], "expressions": [ "(`col1` + `col2`)" - ] + ], + "extra": null }, { "name": "idx3", @@ -401,14 +421,16 @@ ], "expressions": [ "(`col2` + `col3`)" - ] + ], + "extra": null }, { "name": "idx4", "columns": [ "col3" ], - "expressions": null + "expressions": null, + "extra": null }, { "name": "idx5", @@ -416,14 +438,16 @@ "col1", "col2" ], - "expressions": null + "expressions": null, + "extra": null }, { "name": "idx6", "columns": null, "expressions": [ "pow(`col3`,2)" - ] + ], + "extra": null } ], "constraints": { @@ -1798,14 +1822,16 @@ "int_one", "int_two" ], - "expressions": null + "expressions": null, + "extra": null }, { "name": "PRIMARY", "columns": [ "id" ], - "expressions": null + "expressions": null, + "extra": null } ], "constraints": { @@ -1813,7 +1839,8 @@ "name": "PRIMARY", "columns": [ "id" - ] + ], + "extra": null }, "foreign": null, "uniques": [ @@ -1822,7 +1849,8 @@ "columns": [ "int_one", "int_two" - ] + ], + "extra": null } ] } @@ -1896,7 +1924,8 @@ "columns": [ "id" ], - "expressions": null + "expressions": null, + "extra": null } ], "constraints": { @@ -1904,7 +1933,8 @@ "name": "PRIMARY", "columns": [ "id" - ] + ], + "extra": null }, "foreign": null, "uniques": null @@ -1945,14 +1975,16 @@ "video_id", "tag_id" ], - "expressions": null + "expressions": null, + "extra": null }, { "name": "tag_id", "columns": [ "tag_id" ], - "expressions": null + "expressions": null, + "extra": null } ], "constraints": { @@ -1961,7 +1993,8 @@ "columns": [ "video_id", "tag_id" - ] + ], + "extra": null }, "foreign": [ { @@ -1969,6 +2002,7 @@ "columns": [ "video_id" ], + "extra": null, "foreign_table": "videos", "foreign_columns": [ "id" @@ -1979,6 +2013,7 @@ "columns": [ "tag_id" ], + "extra": null, "foreign_table": "tags", "foreign_columns": [ "id" @@ -2033,21 +2068,24 @@ "columns": [ "id" ], - "expressions": null + "expressions": null, + "extra": null }, { "name": "sponsor_id", "columns": [ "sponsor_id" ], - "expressions": null + "expressions": null, + "extra": null }, { "name": "user_id", "columns": [ "user_id" ], - "expressions": null + "expressions": null, + "extra": null } ], "constraints": { @@ -2055,7 +2093,8 @@ "name": "PRIMARY", "columns": [ "id" - ] + ], + "extra": null }, "foreign": [ { @@ -2063,6 +2102,7 @@ "columns": [ "user_id" ], + "extra": null, "foreign_table": "users", "foreign_columns": [ "id" @@ -2073,6 +2113,7 @@ "columns": [ "sponsor_id" ], + "extra": null, "foreign_table": "sponsors", "foreign_columns": [ "id" @@ -2084,7 +2125,8 @@ "name": "sponsor_id", "columns": [ "sponsor_id" - ] + ], + "extra": null } ] } diff --git a/gen/bobgen-mysql/driver/include-exclude-tables-mixed.golden.json b/gen/bobgen-mysql/driver/include-exclude-tables-mixed.golden.json index 8b2f8e31..1edc4ccc 100644 --- a/gen/bobgen-mysql/driver/include-exclude-tables-mixed.golden.json +++ b/gen/bobgen-mysql/driver/include-exclude-tables-mixed.golden.json @@ -34,7 +34,8 @@ "columns": [ "id" ], - "expressions": null + "expressions": null, + "extra": null } ], "constraints": { @@ -42,7 +43,8 @@ "name": "PRIMARY", "columns": [ "id" - ] + ], + "extra": null }, "foreign": null, "uniques": null @@ -82,7 +84,8 @@ "columns": [ "id" ], - "expressions": null + "expressions": null, + "extra": null } ], "constraints": { @@ -90,7 +93,8 @@ "name": "PRIMARY", "columns": [ "id" - ] + ], + "extra": null }, "foreign": null, "uniques": null diff --git a/gen/bobgen-mysql/driver/include-exclude-tables-regex.golden.json b/gen/bobgen-mysql/driver/include-exclude-tables-regex.golden.json index f99c25d7..fdf55bc0 100644 --- a/gen/bobgen-mysql/driver/include-exclude-tables-regex.golden.json +++ b/gen/bobgen-mysql/driver/include-exclude-tables-regex.golden.json @@ -34,7 +34,8 @@ "columns": [ "id" ], - "expressions": null + "expressions": null, + "extra": null } ], "constraints": { @@ -42,7 +43,8 @@ "name": "PRIMARY", "columns": [ "id" - ] + ], + "extra": null }, "foreign": null, "uniques": null @@ -82,7 +84,8 @@ "columns": [ "id" ], - "expressions": null + "expressions": null, + "extra": null } ], "constraints": { @@ -90,7 +93,8 @@ "name": "PRIMARY", "columns": [ "id" - ] + ], + "extra": null }, "foreign": null, "uniques": null diff --git a/gen/bobgen-mysql/driver/include-exclude-tables.golden.json b/gen/bobgen-mysql/driver/include-exclude-tables.golden.json index f73cc301..89befa5e 100644 --- a/gen/bobgen-mysql/driver/include-exclude-tables.golden.json +++ b/gen/bobgen-mysql/driver/include-exclude-tables.golden.json @@ -34,7 +34,8 @@ "columns": [ "id" ], - "expressions": null + "expressions": null, + "extra": null } ], "constraints": { @@ -42,7 +43,8 @@ "name": "PRIMARY", "columns": [ "id" - ] + ], + "extra": null }, "foreign": null, "uniques": null diff --git a/gen/bobgen-mysql/driver/include-tables.golden.json b/gen/bobgen-mysql/driver/include-tables.golden.json index 2b12154d..0a4aef31 100644 --- a/gen/bobgen-mysql/driver/include-tables.golden.json +++ b/gen/bobgen-mysql/driver/include-tables.golden.json @@ -34,7 +34,8 @@ "columns": [ "id" ], - "expressions": null + "expressions": null, + "extra": null } ], "constraints": { @@ -42,7 +43,8 @@ "name": "PRIMARY", "columns": [ "id" - ] + ], + "extra": null }, "foreign": null, "uniques": null @@ -82,7 +84,8 @@ "columns": [ "id" ], - "expressions": null + "expressions": null, + "extra": null } ], "constraints": { @@ -90,7 +93,8 @@ "name": "PRIMARY", "columns": [ "id" - ] + ], + "extra": null }, "foreign": null, "uniques": null diff --git a/gen/bobgen-mysql/driver/mysql.go b/gen/bobgen-mysql/driver/mysql.go index 05ec4685..c7772e6a 100644 --- a/gen/bobgen-mysql/driver/mysql.go +++ b/gen/bobgen-mysql/driver/mysql.go @@ -21,8 +21,8 @@ import ( var rgxEnum = regexp.MustCompile(`^enum\([^\)]+\)$`) type ( - Interface = drivers.Interface[any] - DBInfo = drivers.DBInfo[any] + Interface = drivers.Interface[any, any, any] + DBInfo = drivers.DBInfo[any, any, any] ) type Config struct { @@ -69,10 +69,6 @@ func (d *driver) Dialect() string { return "mysql" } -func (d *driver) Capabilities() drivers.Capabilities { - return drivers.Capabilities{} -} - // Assemble all the information we need to provide back to the driver func (d *driver) Assemble(ctx context.Context) (*DBInfo, error) { var dbinfo *DBInfo @@ -100,7 +96,7 @@ func (d *driver) Assemble(ctx context.Context) (*DBInfo, error) { dbinfo = &DBInfo{DriverName: "github.com/go-sql-driver/mysql"} - dbinfo.Tables, err = drivers.BuildDBInfo(ctx, d, d.config.Concurrency, d.config.Only, d.config.Except) + dbinfo.Tables, err = drivers.BuildDBInfo[any](ctx, d, d.config.Concurrency, d.config.Only, d.config.Except) if err != nil { return nil, err } @@ -326,11 +322,11 @@ func (d *driver) Types() drivers.Types { return d.types } -func (d *driver) Constraints(ctx context.Context, _ drivers.ColumnFilter) (drivers.DBConstraints, error) { - ret := drivers.DBConstraints{ - PKs: map[string]*drivers.Constraint{}, - FKs: map[string][]drivers.ForeignKey{}, - Uniques: map[string][]drivers.Constraint{}, +func (d *driver) Constraints(ctx context.Context, _ drivers.ColumnFilter) (drivers.DBConstraints[any], error) { + ret := drivers.DBConstraints[any]{ + PKs: map[string]*drivers.Constraint[any]{}, + FKs: map[string][]drivers.ForeignKey[any]{}, + Uniques: map[string][]drivers.Constraint[any]{}, } query := `SELECT @@ -364,7 +360,7 @@ func (d *driver) Constraints(ctx context.Context, _ drivers.ColumnFilter) (drive // Extra for the loop constraints = append(constraints, constraint{}) - var current drivers.Constraint + var current drivers.Constraint[any] var table, foreignTable, currentTyp string var foreignCols []string for i, c := range constraints { @@ -372,23 +368,25 @@ func (d *driver) Constraints(ctx context.Context, _ drivers.ColumnFilter) (drive switch currentTyp { case "PRIMARY KEY": // Create a new constraint because it is a pointer - ret.PKs[table] = &drivers.Constraint{ + ret.PKs[table] = &drivers.Constraint[any]{ Name: current.Name, Columns: current.Columns, } case "UNIQUE": ret.Uniques[table] = append(ret.Uniques[table], current) case "FOREIGN KEY": - ret.FKs[table] = append(ret.FKs[table], drivers.ForeignKey{ - Name: current.Name, - Columns: current.Columns, + ret.FKs[table] = append(ret.FKs[table], drivers.ForeignKey[any]{ + Constraint: drivers.Constraint[any]{ + Name: current.Name, + Columns: current.Columns, + }, ForeignTable: foreignTable, ForeignColumns: foreignCols, }) } // reset things - current = drivers.Constraint{} + current = drivers.Constraint[any]{} table, foreignTable, currentTyp, foreignCols = "", "", "", nil //nolint:ineffassign } @@ -406,8 +404,8 @@ func (d *driver) Constraints(ctx context.Context, _ drivers.ColumnFilter) (drive return ret, nil } -func (d *driver) Indexes(ctx context.Context) (drivers.DBIndexes, error) { - ret := drivers.DBIndexes{} +func (d *driver) Indexes(ctx context.Context) (drivers.DBIndexes[any], error) { + ret := drivers.DBIndexes[any]{} query := `SELECT s.table_name AS table_name, @@ -430,12 +428,12 @@ func (d *driver) Indexes(ctx context.Context) (drivers.DBIndexes, error) { } indexColumns = append(indexColumns, indexColumn{}) - var current drivers.Index + var current drivers.Index[any] var table string for i, c := range indexColumns { if i != 0 && (c.TableName != table || c.IndexName != current.Name) { ret[table] = append(ret[table], current) - current = drivers.Index{} + current = drivers.Index[any]{} table = "" //nolint:ineffassign } table = c.TableName diff --git a/gen/bobgen-mysql/driver/mysql.golden.json b/gen/bobgen-mysql/driver/mysql.golden.json index 8db2187a..b512acf8 100644 --- a/gen/bobgen-mysql/driver/mysql.golden.json +++ b/gen/bobgen-mysql/driver/mysql.golden.json @@ -34,7 +34,8 @@ "columns": [ "id" ], - "expressions": null + "expressions": null, + "extra": null } ], "constraints": { @@ -42,7 +43,8 @@ "name": "PRIMARY", "columns": [ "id" - ] + ], + "extra": null }, "foreign": null, "uniques": null @@ -82,7 +84,8 @@ "columns": [ "id" ], - "expressions": null + "expressions": null, + "extra": null } ], "constraints": { @@ -90,7 +93,8 @@ "name": "PRIMARY", "columns": [ "id" - ] + ], + "extra": null }, "foreign": null, "uniques": null @@ -130,7 +134,8 @@ "columns": [ "id" ], - "expressions": null + "expressions": null, + "extra": null } ], "constraints": { @@ -138,7 +143,8 @@ "name": "PRIMARY", "columns": [ "id" - ] + ], + "extra": null }, "foreign": null, "uniques": null @@ -178,7 +184,8 @@ "columns": [ "id" ], - "expressions": null + "expressions": null, + "extra": null } ], "constraints": { @@ -186,7 +193,8 @@ "name": "PRIMARY", "columns": [ "id" - ] + ], + "extra": null }, "foreign": null, "uniques": null @@ -226,7 +234,8 @@ "columns": [ "id" ], - "expressions": null + "expressions": null, + "extra": null } ], "constraints": { @@ -234,7 +243,8 @@ "name": "PRIMARY", "columns": [ "id" - ] + ], + "extra": null }, "foreign": null, "uniques": null @@ -330,14 +340,16 @@ "one", "two" ], - "expressions": null + "expressions": null, + "extra": null }, { "name": "PRIMARY", "columns": [ "id" ], - "expressions": null + "expressions": null, + "extra": null }, { "name": "something", @@ -345,14 +357,16 @@ "something", "another" ], - "expressions": null + "expressions": null, + "extra": null }, { "name": "sponsor_id", "columns": [ "sponsor_id" ], - "expressions": null + "expressions": null, + "extra": null } ], "constraints": { @@ -360,7 +374,8 @@ "name": "PRIMARY", "columns": [ "id" - ] + ], + "extra": null }, "foreign": [ { @@ -369,6 +384,7 @@ "one", "two" ], + "extra": null, "foreign_table": "type_monsters", "foreign_columns": [ "int_one", @@ -382,13 +398,15 @@ "columns": [ "something", "another" - ] + ], + "extra": null }, { "name": "sponsor_id", "columns": [ "sponsor_id" - ] + ], + "extra": null } ] } @@ -416,7 +434,8 @@ "columns": [ "id" ], - "expressions": null + "expressions": null, + "extra": null } ], "constraints": { @@ -424,7 +443,8 @@ "name": "PRIMARY", "columns": [ "id" - ] + ], + "extra": null }, "foreign": null, "uniques": null @@ -453,7 +473,8 @@ "columns": [ "id" ], - "expressions": null + "expressions": null, + "extra": null } ], "constraints": { @@ -461,7 +482,8 @@ "name": "PRIMARY", "columns": [ "id" - ] + ], + "extra": null }, "foreign": null, "uniques": null @@ -512,7 +534,8 @@ "columns": null, "expressions": [ "(`col1` + `col2`)" - ] + ], + "extra": null }, { "name": "idx2", @@ -521,7 +544,8 @@ ], "expressions": [ "(`col1` + `col2`)" - ] + ], + "extra": null }, { "name": "idx3", @@ -530,14 +554,16 @@ ], "expressions": [ "(`col2` + `col3`)" - ] + ], + "extra": null }, { "name": "idx4", "columns": [ "col3" ], - "expressions": null + "expressions": null, + "extra": null }, { "name": "idx5", @@ -545,14 +571,16 @@ "col1", "col2" ], - "expressions": null + "expressions": null, + "extra": null }, { "name": "idx6", "columns": null, "expressions": [ "pow(`col3`,2)" - ] + ], + "extra": null } ], "constraints": { @@ -1927,14 +1955,16 @@ "int_one", "int_two" ], - "expressions": null + "expressions": null, + "extra": null }, { "name": "PRIMARY", "columns": [ "id" ], - "expressions": null + "expressions": null, + "extra": null } ], "constraints": { @@ -1942,7 +1972,8 @@ "name": "PRIMARY", "columns": [ "id" - ] + ], + "extra": null }, "foreign": null, "uniques": [ @@ -1951,7 +1982,8 @@ "columns": [ "int_one", "int_two" - ] + ], + "extra": null } ] } @@ -2025,7 +2057,8 @@ "columns": [ "id" ], - "expressions": null + "expressions": null, + "extra": null } ], "constraints": { @@ -2033,7 +2066,8 @@ "name": "PRIMARY", "columns": [ "id" - ] + ], + "extra": null }, "foreign": null, "uniques": null @@ -2074,14 +2108,16 @@ "video_id", "tag_id" ], - "expressions": null + "expressions": null, + "extra": null }, { "name": "tag_id", "columns": [ "tag_id" ], - "expressions": null + "expressions": null, + "extra": null } ], "constraints": { @@ -2090,7 +2126,8 @@ "columns": [ "video_id", "tag_id" - ] + ], + "extra": null }, "foreign": [ { @@ -2098,6 +2135,7 @@ "columns": [ "video_id" ], + "extra": null, "foreign_table": "videos", "foreign_columns": [ "id" @@ -2108,6 +2146,7 @@ "columns": [ "tag_id" ], + "extra": null, "foreign_table": "tags", "foreign_columns": [ "id" @@ -2162,21 +2201,24 @@ "columns": [ "id" ], - "expressions": null + "expressions": null, + "extra": null }, { "name": "sponsor_id", "columns": [ "sponsor_id" ], - "expressions": null + "expressions": null, + "extra": null }, { "name": "user_id", "columns": [ "user_id" ], - "expressions": null + "expressions": null, + "extra": null } ], "constraints": { @@ -2184,7 +2226,8 @@ "name": "PRIMARY", "columns": [ "id" - ] + ], + "extra": null }, "foreign": [ { @@ -2192,6 +2235,7 @@ "columns": [ "user_id" ], + "extra": null, "foreign_table": "users", "foreign_columns": [ "id" @@ -2202,6 +2246,7 @@ "columns": [ "sponsor_id" ], + "extra": null, "foreign_table": "sponsors", "foreign_columns": [ "id" @@ -2213,7 +2258,8 @@ "name": "sponsor_id", "columns": [ "sponsor_id" - ] + ], + "extra": null } ] } diff --git a/gen/bobgen-mysql/driver/mysql_test.go b/gen/bobgen-mysql/driver/mysql_test.go index efd185b4..c5173870 100644 --- a/gen/bobgen-mysql/driver/mysql_test.go +++ b/gen/bobgen-mysql/driver/mysql_test.go @@ -175,9 +175,9 @@ func TestDriver(t *testing.T) { os.RemoveAll(out) }() - testgen.TestDriver(t, testgen.DriverTestConfig[any]{ + testgen.TestDriver(t, testgen.DriverTestConfig[any, any, any]{ Root: out, - GetDriver: func() drivers.Interface[any] { + GetDriver: func() drivers.Interface[any, any, any] { return New(tt.config) }, GoldenFile: tt.goldenJson, diff --git a/gen/bobgen-mysql/main.go b/gen/bobgen-mysql/main.go index d919a1d2..cda0d818 100644 --- a/gen/bobgen-mysql/main.go +++ b/gen/bobgen-mysql/main.go @@ -44,7 +44,7 @@ func main() { } func run(c *cli.Context) error { - config, driverConfig, err := helpers.GetConfigFromFile[driver.Config](c.String("config"), "mysql") + config, driverConfig, err := helpers.GetConfigFromFile[any, driver.Config](c.String("config"), "mysql") if err != nil { return err } @@ -55,7 +55,7 @@ func run(c *cli.Context) error { &helpers.Templates{Models: []fs.FS{gen.MySQLModelTemplates}}, ) - state := &gen.State{ + state := &gen.State[any]{ Config: config, Outputs: outputs, } diff --git a/gen/bobgen-mysql/templates/models/100_blocks.go.tpl b/gen/bobgen-mysql/templates/models/100_blocks.go.tpl index 9cb59874..858c8b7d 100644 --- a/gen/bobgen-mysql/templates/models/100_blocks.go.tpl +++ b/gen/bobgen-mysql/templates/models/100_blocks.go.tpl @@ -8,7 +8,7 @@ type {{$tAlias.UpPlural}}Query = *{{$.Dialect}}.ViewQuery[*{{$tAlias.UpSingular}}, {{$tAlias.UpSingular}}Slice] {{- else -}} // {{$tAlias.UpPlural}} contains methods to work with the {{$table.Name}} table - var {{$tAlias.UpPlural}} = {{$.Dialect}}.NewTablex[*{{$tAlias.UpSingular}}, {{$tAlias.UpSingular}}Slice, *{{$tAlias.UpSingular}}Setter]("{{$table.Name}}", {{uniqueColPairs $table}}) + var {{$tAlias.UpPlural}} = {{$.Dialect}}.NewTablex[*{{$tAlias.UpSingular}}, {{$tAlias.UpSingular}}Slice, *{{$tAlias.UpSingular}}Setter]("{{$table.Name}}", {{$table.UniqueColPairs}}) // {{$tAlias.UpPlural}}Query is a query on the {{$table.Name}} table type {{$tAlias.UpPlural}}Query = *{{$.Dialect}}.ViewQuery[*{{$tAlias.UpSingular}}, {{$tAlias.UpSingular}}Slice] {{- end}} diff --git a/gen/bobgen-psql/driver/exclude-tables.golden.json b/gen/bobgen-psql/driver/exclude-tables.golden.json index 090353da..7b407ba3 100644 --- a/gen/bobgen-psql/driver/exclude-tables.golden.json +++ b/gen/bobgen-psql/driver/exclude-tables.golden.json @@ -23,7 +23,8 @@ "columns": [ "id" ], - "expressions": null + "expressions": null, + "extra": null } ], "constraints": { @@ -31,7 +32,8 @@ "name": "bar_baz_pkey", "columns": [ "id" - ] + ], + "extra": null }, "foreign": null, "uniques": null @@ -60,7 +62,8 @@ "columns": [ "id" ], - "expressions": null + "expressions": null, + "extra": null } ], "constraints": { @@ -68,7 +71,8 @@ "name": "bar_qux_pkey", "columns": [ "id" - ] + ], + "extra": null }, "foreign": null, "uniques": null @@ -97,7 +101,8 @@ "columns": [ "id" ], - "expressions": null + "expressions": null, + "extra": null } ], "constraints": { @@ -105,7 +110,8 @@ "name": "foo_qux_pkey", "columns": [ "id" - ] + ], + "extra": null }, "foreign": null, "uniques": null @@ -134,7 +140,8 @@ "columns": [ "id" ], - "expressions": null + "expressions": null, + "extra": null } ], "constraints": { @@ -142,7 +149,8 @@ "name": "sponsors_pkey", "columns": [ "id" - ] + ], + "extra": null }, "foreign": null, "uniques": null @@ -171,7 +179,8 @@ "columns": [ "id" ], - "expressions": null + "expressions": null, + "extra": null } ], "constraints": { @@ -179,7 +188,8 @@ "name": "tags_pkey", "columns": [ "id" - ] + ], + "extra": null }, "foreign": null, "uniques": null @@ -230,7 +240,8 @@ "columns": null, "expressions": [ "(col1 + col2)" - ] + ], + "extra": null }, { "name": "idx2", @@ -239,7 +250,8 @@ ], "expressions": [ "(col1 + col2)" - ] + ], + "extra": null }, { "name": "idx3", @@ -248,14 +260,16 @@ ], "expressions": [ "(col2 + col3)" - ] + ], + "extra": null }, { "name": "idx4", "columns": [ "col3" ], - "expressions": null + "expressions": null, + "extra": null }, { "name": "idx5", @@ -263,14 +277,16 @@ "col1", "col2" ], - "expressions": null + "expressions": null, + "extra": null }, { "name": "idx6", "columns": null, "expressions": [ "pow(col3::double precision, 2::double precision)" - ] + ], + "extra": null } ], "constraints": { @@ -2062,7 +2078,8 @@ "columns": [ "id" ], - "expressions": null + "expressions": null, + "extra": null } ], "constraints": { @@ -2070,7 +2087,8 @@ "name": "type_monsters_pkey", "columns": [ "id" - ] + ], + "extra": null }, "foreign": null, "uniques": null @@ -3996,21 +4014,24 @@ "columns": [ "party_id" ], - "expressions": null + "expressions": null, + "extra": null }, { "name": "users_pkey", "columns": [ "id" ], - "expressions": null + "expressions": null, + "extra": null }, { "name": "users_primary_email_key", "columns": [ "primary_email" ], - "expressions": null + "expressions": null, + "extra": null } ], "constraints": { @@ -4018,7 +4039,8 @@ "name": "users_pkey", "columns": [ "id" - ] + ], + "extra": null }, "foreign": [ { @@ -4026,6 +4048,7 @@ "columns": [ "parent_id" ], + "extra": null, "foreign_table": "users", "foreign_columns": [ "id" @@ -4036,6 +4059,7 @@ "columns": [ "party_id" ], + "extra": null, "foreign_table": "users", "foreign_columns": [ "id" @@ -4046,6 +4070,7 @@ "columns": [ "referrer" ], + "extra": null, "foreign_table": "users", "foreign_columns": [ "id" @@ -4057,13 +4082,15 @@ "name": "users_party_id_key", "columns": [ "party_id" - ] + ], + "extra": null }, { "name": "users_primary_email_key", "columns": [ "primary_email" - ] + ], + "extra": null } ] } @@ -4103,7 +4130,8 @@ "video_id", "tag_id" ], - "expressions": null + "expressions": null, + "extra": null } ], "constraints": { @@ -4112,7 +4140,8 @@ "columns": [ "video_id", "tag_id" - ] + ], + "extra": null }, "foreign": [ { @@ -4120,6 +4149,7 @@ "columns": [ "tag_id" ], + "extra": null, "foreign_table": "tags", "foreign_columns": [ "id" @@ -4130,6 +4160,7 @@ "columns": [ "video_id" ], + "extra": null, "foreign_table": "videos", "foreign_columns": [ "id" @@ -4184,14 +4215,16 @@ "columns": [ "id" ], - "expressions": null + "expressions": null, + "extra": null }, { "name": "videos_sponsor_id_key", "columns": [ "sponsor_id" ], - "expressions": null + "expressions": null, + "extra": null } ], "constraints": { @@ -4199,7 +4232,8 @@ "name": "videos_pkey", "columns": [ "id" - ] + ], + "extra": null }, "foreign": [ { @@ -4207,6 +4241,7 @@ "columns": [ "sponsor_id" ], + "extra": null, "foreign_table": "sponsors", "foreign_columns": [ "id" @@ -4217,6 +4252,7 @@ "columns": [ "user_id" ], + "extra": null, "foreign_table": "users", "foreign_columns": [ "id" @@ -4228,7 +4264,8 @@ "name": "videos_sponsor_id_key", "columns": [ "sponsor_id" - ] + ], + "extra": null } ] } diff --git a/gen/bobgen-psql/driver/include-exclude-tables-mixed.golden.json b/gen/bobgen-psql/driver/include-exclude-tables-mixed.golden.json index ec4b821b..a8ccf03c 100644 --- a/gen/bobgen-psql/driver/include-exclude-tables-mixed.golden.json +++ b/gen/bobgen-psql/driver/include-exclude-tables-mixed.golden.json @@ -34,7 +34,8 @@ "columns": [ "id" ], - "expressions": null + "expressions": null, + "extra": null } ], "constraints": { @@ -42,7 +43,8 @@ "name": "bar_baz_pkey", "columns": [ "id" - ] + ], + "extra": null }, "foreign": null, "uniques": null @@ -82,7 +84,8 @@ "columns": [ "id" ], - "expressions": null + "expressions": null, + "extra": null } ], "constraints": { @@ -90,7 +93,8 @@ "name": "bar_qux_pkey", "columns": [ "id" - ] + ], + "extra": null }, "foreign": null, "uniques": null diff --git a/gen/bobgen-psql/driver/include-exclude-tables-regex.golden.json b/gen/bobgen-psql/driver/include-exclude-tables-regex.golden.json index 352eb706..3f7627ee 100644 --- a/gen/bobgen-psql/driver/include-exclude-tables-regex.golden.json +++ b/gen/bobgen-psql/driver/include-exclude-tables-regex.golden.json @@ -34,7 +34,8 @@ "columns": [ "id" ], - "expressions": null + "expressions": null, + "extra": null } ], "constraints": { @@ -42,7 +43,8 @@ "name": "bar_qux_pkey", "columns": [ "id" - ] + ], + "extra": null }, "foreign": null, "uniques": null @@ -82,7 +84,8 @@ "columns": [ "id" ], - "expressions": null + "expressions": null, + "extra": null } ], "constraints": { @@ -90,7 +93,8 @@ "name": "foo_qux_pkey", "columns": [ "id" - ] + ], + "extra": null }, "foreign": null, "uniques": null diff --git a/gen/bobgen-psql/driver/include-exclude-tables.golden.json b/gen/bobgen-psql/driver/include-exclude-tables.golden.json index 94107b1f..9a67996d 100644 --- a/gen/bobgen-psql/driver/include-exclude-tables.golden.json +++ b/gen/bobgen-psql/driver/include-exclude-tables.golden.json @@ -34,7 +34,8 @@ "columns": [ "id" ], - "expressions": null + "expressions": null, + "extra": null } ], "constraints": { @@ -42,7 +43,8 @@ "name": "foo_baz_pkey", "columns": [ "id" - ] + ], + "extra": null }, "foreign": null, "uniques": null diff --git a/gen/bobgen-psql/driver/include-tables.golden.json b/gen/bobgen-psql/driver/include-tables.golden.json index 9044b37d..ce15289a 100644 --- a/gen/bobgen-psql/driver/include-tables.golden.json +++ b/gen/bobgen-psql/driver/include-tables.golden.json @@ -34,7 +34,8 @@ "columns": [ "id" ], - "expressions": null + "expressions": null, + "extra": null } ], "constraints": { @@ -42,7 +43,8 @@ "name": "foo_bar_pkey", "columns": [ "id" - ] + ], + "extra": null }, "foreign": null, "uniques": null @@ -82,7 +84,8 @@ "columns": [ "id" ], - "expressions": null + "expressions": null, + "extra": null } ], "constraints": { @@ -90,7 +93,8 @@ "name": "foo_baz_pkey", "columns": [ "id" - ] + ], + "extra": null }, "foreign": null, "uniques": null diff --git a/gen/bobgen-psql/driver/keys.go b/gen/bobgen-psql/driver/keys.go index 0afdcd66..8c4f0169 100644 --- a/gen/bobgen-psql/driver/keys.go +++ b/gen/bobgen-psql/driver/keys.go @@ -10,11 +10,11 @@ import ( "github.com/stephenafamo/scan/stdscan" ) -func (d *driver) Constraints(ctx context.Context, _ drivers.ColumnFilter) (drivers.DBConstraints, error) { - ret := drivers.DBConstraints{ - PKs: map[string]*drivers.Constraint{}, - FKs: map[string][]drivers.ForeignKey{}, - Uniques: map[string][]drivers.Constraint{}, +func (d *driver) Constraints(ctx context.Context, _ drivers.ColumnFilter) (drivers.DBConstraints[any], error) { + ret := drivers.DBConstraints[any]{ + PKs: map[string]*drivers.Constraint[any]{}, + FKs: map[string][]drivers.ForeignKey[any]{}, + Uniques: map[string][]drivers.Constraint[any]{}, } query := `SELECT @@ -87,12 +87,12 @@ func (d *driver) Constraints(ctx context.Context, _ drivers.ColumnFilter) (drive switch c.Type { case "p": - ret.PKs[key] = &drivers.Constraint{ + ret.PKs[key] = &drivers.Constraint[any]{ Name: c.Name, Columns: c.Columns, } case "u": - ret.Uniques[key] = append(ret.Uniques[c.Table], drivers.Constraint{ + ret.Uniques[key] = append(ret.Uniques[c.Table], drivers.Constraint[any]{ Name: c.Name, Columns: c.Columns, }) @@ -101,9 +101,11 @@ func (d *driver) Constraints(ctx context.Context, _ drivers.ColumnFilter) (drive if c.ForeignSchema.Valid && c.ForeignSchema.String != d.config.SharedSchema { fkey = c.ForeignSchema.String + "." + c.ForeignTable.String } - ret.FKs[key] = append(ret.FKs[key], drivers.ForeignKey{ - Name: key + "." + c.Name, - Columns: c.Columns, + ret.FKs[key] = append(ret.FKs[key], drivers.ForeignKey[any]{ + Constraint: drivers.Constraint[any]{ + Name: key + "." + c.Name, + Columns: c.Columns, + }, ForeignTable: fkey, ForeignColumns: c.ForeignColumns, }) diff --git a/gen/bobgen-psql/driver/psql.go b/gen/bobgen-psql/driver/psql.go index b7a52527..f5802556 100644 --- a/gen/bobgen-psql/driver/psql.go +++ b/gen/bobgen-psql/driver/psql.go @@ -20,8 +20,8 @@ import ( var rgxValidColumnName = regexp.MustCompile(`(?i)^[a-z_][a-z0-9_]*$`) type ( - Interface = drivers.Interface[any] - DBInfo = drivers.DBInfo[any] + Interface = drivers.Interface[any, any, any] + DBInfo = drivers.DBInfo[any, any, any] ) type Enum struct { @@ -120,10 +120,6 @@ func (d *driver) Types() drivers.Types { return d.types } -func (d *driver) Capabilities() drivers.Capabilities { - return drivers.Capabilities{} -} - // Assemble all the information we need to provide back to the driver func (d *driver) Assemble(ctx context.Context) (*DBInfo, error) { var dbinfo *DBInfo @@ -146,7 +142,7 @@ func (d *driver) Assemble(ctx context.Context) (*DBInfo, error) { return nil, fmt.Errorf("unable to load enums: %w", err) } - dbinfo.Tables, err = drivers.BuildDBInfo(ctx, d, d.config.Concurrency, d.config.Only, d.config.Except) + dbinfo.Tables, err = drivers.BuildDBInfo[any](ctx, d, d.config.Concurrency, d.config.Only, d.config.Except) if err != nil { return nil, err } @@ -448,8 +444,8 @@ func (d *driver) loadEnums(ctx context.Context) error { return nil } -func (d *driver) Indexes(ctx context.Context) (drivers.DBIndexes, error) { - ret := drivers.DBIndexes{} +func (d *driver) Indexes(ctx context.Context) (drivers.DBIndexes[any], error) { + ret := drivers.DBIndexes[any]{} query := `SELECT n.nspname AS schema_name, @@ -482,7 +478,7 @@ func (d *driver) Indexes(ctx context.Context) (drivers.DBIndexes, error) { if r.SchemaName != "" && r.SchemaName != d.config.SharedSchema { key = r.SchemaName + "." + r.TableName } - var index drivers.Index + var index drivers.Index[any] index.Name = r.IndexName for _, colName := range r.IndexCols { if rgxValidColumnName.MatchString(colName) { diff --git a/gen/bobgen-psql/driver/psql.golden.json b/gen/bobgen-psql/driver/psql.golden.json index 0aa2aeaf..9d64ac0e 100644 --- a/gen/bobgen-psql/driver/psql.golden.json +++ b/gen/bobgen-psql/driver/psql.golden.json @@ -34,7 +34,8 @@ "columns": [ "id" ], - "expressions": null + "expressions": null, + "extra": null } ], "constraints": { @@ -42,7 +43,8 @@ "name": "bar_baz_pkey", "columns": [ "id" - ] + ], + "extra": null }, "foreign": null, "uniques": null @@ -82,7 +84,8 @@ "columns": [ "id" ], - "expressions": null + "expressions": null, + "extra": null } ], "constraints": { @@ -90,7 +93,8 @@ "name": "bar_qux_pkey", "columns": [ "id" - ] + ], + "extra": null }, "foreign": null, "uniques": null @@ -130,7 +134,8 @@ "columns": [ "id" ], - "expressions": null + "expressions": null, + "extra": null } ], "constraints": { @@ -138,7 +143,8 @@ "name": "foo_bar_pkey", "columns": [ "id" - ] + ], + "extra": null }, "foreign": null, "uniques": null @@ -178,7 +184,8 @@ "columns": [ "id" ], - "expressions": null + "expressions": null, + "extra": null } ], "constraints": { @@ -186,7 +193,8 @@ "name": "foo_baz_pkey", "columns": [ "id" - ] + ], + "extra": null }, "foreign": null, "uniques": null @@ -226,7 +234,8 @@ "columns": [ "id" ], - "expressions": null + "expressions": null, + "extra": null } ], "constraints": { @@ -234,7 +243,8 @@ "name": "foo_qux_pkey", "columns": [ "id" - ] + ], + "extra": null }, "foreign": null, "uniques": null @@ -263,7 +273,8 @@ "columns": [ "id" ], - "expressions": null + "expressions": null, + "extra": null } ], "constraints": { @@ -271,7 +282,8 @@ "name": "sponsors_pkey", "columns": [ "id" - ] + ], + "extra": null }, "foreign": null, "uniques": null @@ -300,7 +312,8 @@ "columns": [ "id" ], - "expressions": null + "expressions": null, + "extra": null } ], "constraints": { @@ -308,7 +321,8 @@ "name": "tags_pkey", "columns": [ "id" - ] + ], + "extra": null }, "foreign": null, "uniques": null @@ -359,7 +373,8 @@ "columns": null, "expressions": [ "(col1 + col2)" - ] + ], + "extra": null }, { "name": "idx2", @@ -368,7 +383,8 @@ ], "expressions": [ "(col1 + col2)" - ] + ], + "extra": null }, { "name": "idx3", @@ -377,14 +393,16 @@ ], "expressions": [ "(col2 + col3)" - ] + ], + "extra": null }, { "name": "idx4", "columns": [ "col3" ], - "expressions": null + "expressions": null, + "extra": null }, { "name": "idx5", @@ -392,14 +410,16 @@ "col1", "col2" ], - "expressions": null + "expressions": null, + "extra": null }, { "name": "idx6", "columns": null, "expressions": [ "pow(col3::double precision, 2::double precision)" - ] + ], + "extra": null } ], "constraints": { @@ -2191,7 +2211,8 @@ "columns": [ "id" ], - "expressions": null + "expressions": null, + "extra": null } ], "constraints": { @@ -2199,7 +2220,8 @@ "name": "type_monsters_pkey", "columns": [ "id" - ] + ], + "extra": null }, "foreign": null, "uniques": null @@ -4125,21 +4147,24 @@ "columns": [ "party_id" ], - "expressions": null + "expressions": null, + "extra": null }, { "name": "users_pkey", "columns": [ "id" ], - "expressions": null + "expressions": null, + "extra": null }, { "name": "users_primary_email_key", "columns": [ "primary_email" ], - "expressions": null + "expressions": null, + "extra": null } ], "constraints": { @@ -4147,7 +4172,8 @@ "name": "users_pkey", "columns": [ "id" - ] + ], + "extra": null }, "foreign": [ { @@ -4155,6 +4181,7 @@ "columns": [ "parent_id" ], + "extra": null, "foreign_table": "users", "foreign_columns": [ "id" @@ -4165,6 +4192,7 @@ "columns": [ "party_id" ], + "extra": null, "foreign_table": "users", "foreign_columns": [ "id" @@ -4175,6 +4203,7 @@ "columns": [ "referrer" ], + "extra": null, "foreign_table": "users", "foreign_columns": [ "id" @@ -4186,13 +4215,15 @@ "name": "users_party_id_key", "columns": [ "party_id" - ] + ], + "extra": null }, { "name": "users_primary_email_key", "columns": [ "primary_email" - ] + ], + "extra": null } ] } @@ -4232,7 +4263,8 @@ "video_id", "tag_id" ], - "expressions": null + "expressions": null, + "extra": null } ], "constraints": { @@ -4241,7 +4273,8 @@ "columns": [ "video_id", "tag_id" - ] + ], + "extra": null }, "foreign": [ { @@ -4249,6 +4282,7 @@ "columns": [ "tag_id" ], + "extra": null, "foreign_table": "tags", "foreign_columns": [ "id" @@ -4259,6 +4293,7 @@ "columns": [ "video_id" ], + "extra": null, "foreign_table": "videos", "foreign_columns": [ "id" @@ -4313,14 +4348,16 @@ "columns": [ "id" ], - "expressions": null + "expressions": null, + "extra": null }, { "name": "videos_sponsor_id_key", "columns": [ "sponsor_id" ], - "expressions": null + "expressions": null, + "extra": null } ], "constraints": { @@ -4328,7 +4365,8 @@ "name": "videos_pkey", "columns": [ "id" - ] + ], + "extra": null }, "foreign": [ { @@ -4336,6 +4374,7 @@ "columns": [ "sponsor_id" ], + "extra": null, "foreign_table": "sponsors", "foreign_columns": [ "id" @@ -4346,6 +4385,7 @@ "columns": [ "user_id" ], + "extra": null, "foreign_table": "users", "foreign_columns": [ "id" @@ -4357,7 +4397,8 @@ "name": "videos_sponsor_id_key", "columns": [ "sponsor_id" - ] + ], + "extra": null } ] } diff --git a/gen/bobgen-psql/driver/psql_test.go b/gen/bobgen-psql/driver/psql_test.go index dc1f88a2..96b4e998 100644 --- a/gen/bobgen-psql/driver/psql_test.go +++ b/gen/bobgen-psql/driver/psql_test.go @@ -157,9 +157,9 @@ func TestDriver(t *testing.T) { os.RemoveAll(out) }() - testgen.TestDriver(t, testgen.DriverTestConfig[any]{ + testgen.TestDriver(t, testgen.DriverTestConfig[any, any, any]{ Root: out, - GetDriver: func() drivers.Interface[any] { + GetDriver: func() drivers.Interface[any, any, any] { return New(tt.config) }, GoldenFile: tt.goldenJson, diff --git a/gen/bobgen-psql/main.go b/gen/bobgen-psql/main.go index 0cbd1a4f..712034db 100644 --- a/gen/bobgen-psql/main.go +++ b/gen/bobgen-psql/main.go @@ -44,7 +44,7 @@ func main() { } func run(c *cli.Context) error { - config, driverConfig, err := helpers.GetConfigFromFile[driver.Config](c.String("config"), "psql") + config, driverConfig, err := helpers.GetConfigFromFile[any, driver.Config](c.String("config"), "psql") if err != nil { return err } @@ -55,7 +55,7 @@ func run(c *cli.Context) error { &helpers.Templates{Models: []fs.FS{gen.PSQLModelTemplates}}, ) - state := &gen.State{ + state := &gen.State[any]{ Config: config, Outputs: outputs, } diff --git a/gen/bobgen-sql/driver/sql.go b/gen/bobgen-sql/driver/sql.go index d5699954..2c6cc17d 100644 --- a/gen/bobgen-sql/driver/sql.go +++ b/gen/bobgen-sql/driver/sql.go @@ -47,7 +47,7 @@ type Config struct { fs fs.FS } -func RunPostgres(ctx context.Context, state *gen.State, config Config) error { +func RunPostgres(ctx context.Context, state *gen.State[any], config Config) error { config.fs = os.DirFS(config.Dir) d, err := getPsqlDriver(ctx, config) @@ -109,7 +109,7 @@ func getPsqlDriver(ctx context.Context, config Config) (psqlDriver.Interface, er return d, nil } -func RunSQLite(ctx context.Context, state *gen.State, config Config) error { +func RunSQLite(ctx context.Context, state *gen.State[any], config Config) error { config.fs = os.DirFS(config.Dir) d, err := getSQLiteDriver(ctx, config) @@ -171,17 +171,17 @@ func getSQLiteDriver(ctx context.Context, config Config) (sqliteDriver.Interface return d, nil } -func wrapDriver[T any](ctx context.Context, d drivers.Interface[T]) driver[T] { +func wrapDriver[T, C, I any](ctx context.Context, d drivers.Interface[T, C, I]) driver[T, C, I] { info, err := d.Assemble(ctx) - return driver[T]{d, info, err} + return driver[T, C, I]{d, info, err} } -type driver[T any] struct { - drivers.Interface[T] - info *drivers.DBInfo[T] +type driver[T, C, I any] struct { + drivers.Interface[T, C, I] + info *drivers.DBInfo[T, C, I] err error } -func (d driver[T]) Assemble(context.Context) (*drivers.DBInfo[T], error) { +func (d driver[T, C, I]) Assemble(context.Context) (*drivers.DBInfo[T, C, I], error) { return d.info, d.err } diff --git a/gen/bobgen-sql/driver/sql_test.go b/gen/bobgen-sql/driver/sql_test.go index 923a7244..27b70ae3 100644 --- a/gen/bobgen-sql/driver/sql_test.go +++ b/gen/bobgen-sql/driver/sql_test.go @@ -22,9 +22,9 @@ func TestPostgres(t *testing.T) { fs: testfiles.PostgresSchema, } - testgen.TestDriver(t, testgen.DriverTestConfig[any]{ + testgen.TestDriver(t, testgen.DriverTestConfig[any, any, any]{ Root: out, - GetDriver: func() drivers.Interface[any] { + GetDriver: func() drivers.Interface[any, any, any] { d, err := getPsqlDriver(context.Background(), config) if err != nil { t.Fatalf("getting psql driver: %s", err) @@ -46,9 +46,9 @@ func TestSQLite(t *testing.T) { Schemas: []string{"one"}, } - testgen.TestDriver(t, testgen.DriverTestConfig[any]{ + testgen.TestDriver(t, testgen.DriverTestConfig[any, any, any]{ Root: out, - GetDriver: func() drivers.Interface[any] { + GetDriver: func() drivers.Interface[any, any, any] { d, err := getSQLiteDriver(context.Background(), config) if err != nil { t.Fatalf("getting sqlite driver: %s", err) diff --git a/gen/bobgen-sql/main.go b/gen/bobgen-sql/main.go index 97cca364..bc143316 100644 --- a/gen/bobgen-sql/main.go +++ b/gen/bobgen-sql/main.go @@ -45,7 +45,7 @@ func main() { } func run(c *cli.Context) error { - config, driverConfig, err := helpers.GetConfigFromFile[driver.Config](c.String("config"), "sql") + config, driverConfig, err := helpers.GetConfigFromFile[any, driver.Config](c.String("config"), "sql") if err != nil { return err } @@ -65,7 +65,7 @@ func run(c *cli.Context) error { &helpers.Templates{Models: modelTemplates}, ) - state := &gen.State{ + state := &gen.State[any]{ Config: config, Outputs: outputs, } diff --git a/gen/bobgen-sqlite/driver/exclude-tables.golden.json b/gen/bobgen-sqlite/driver/exclude-tables.golden.json index b7e91a2a..4878262c 100644 --- a/gen/bobgen-sqlite/driver/exclude-tables.golden.json +++ b/gen/bobgen-sqlite/driver/exclude-tables.golden.json @@ -67,7 +67,8 @@ "columns": [ "sponsor_id" ], - "expressions": null + "expressions": null, + "extra": null }, { "name": "sqlite_autoindex_autoinckeywordtest_2", @@ -75,7 +76,8 @@ "something", "another" ], - "expressions": null + "expressions": null, + "extra": null } ], "constraints": { @@ -83,7 +85,8 @@ "name": "pk_main_autoinckeywordtest", "columns": [ "id" - ] + ], + "extra": null }, "foreign": [ { @@ -92,6 +95,7 @@ "user_id", "sponsor_id" ], + "extra": null, "foreign_table": "videos", "foreign_columns": [ "user_id", @@ -105,13 +109,15 @@ "columns": [ "something", "another" - ] + ], + "extra": null }, { "name": "sqlite_autoindex_autoinckeywordtest_1", "columns": [ "sponsor_id" - ] + ], + "extra": null } ] } @@ -139,7 +145,8 @@ "name": "pk_main_autoinctest", "columns": [ "id" - ] + ], + "extra": null }, "foreign": [ { @@ -147,6 +154,7 @@ "columns": [ "id" ], + "extra": null, "foreign_table": "tags", "foreign_columns": [ "id" @@ -179,7 +187,8 @@ "name": "pk_main_bar_baz", "columns": [ "id" - ] + ], + "extra": null }, "foreign": [], "uniques": [] @@ -208,7 +217,8 @@ "name": "pk_main_bar_qux", "columns": [ "id" - ] + ], + "extra": null }, "foreign": [], "uniques": [] @@ -237,7 +247,8 @@ "name": "pk_main_foo_qux", "columns": [ "id" - ] + ], + "extra": null }, "foreign": [], "uniques": [] @@ -310,7 +321,8 @@ "name": "pk_main_has_generated_columns", "columns": [ "a" - ] + ], + "extra": null }, "foreign": [], "uniques": [] @@ -383,7 +395,8 @@ "name": "pk_one_as_generated_columns", "columns": [ "a" - ] + ], + "extra": null }, "foreign": [], "uniques": [] @@ -423,7 +436,8 @@ "columns": [ "sponsor_id" ], - "expressions": null + "expressions": null, + "extra": null }, { "name": "sqlite_autoindex_autoinckeywordtest_2", @@ -431,7 +445,8 @@ "something", "another" ], - "expressions": null + "expressions": null, + "extra": null } ], "constraints": { @@ -439,7 +454,8 @@ "name": "pk_one_autoinckeywordtest", "columns": [ "id" - ] + ], + "extra": null }, "foreign": [], "uniques": [] @@ -468,7 +484,8 @@ "name": "pk_one_autoinctest", "columns": [ "id" - ] + ], + "extra": null }, "foreign": [], "uniques": [] @@ -497,7 +514,8 @@ "name": "pk_one_bar_baz", "columns": [ "id" - ] + ], + "extra": null }, "foreign": [], "uniques": [] @@ -526,7 +544,8 @@ "name": "pk_one_bar_qux", "columns": [ "id" - ] + ], + "extra": null }, "foreign": [], "uniques": [] @@ -555,7 +574,8 @@ "name": "pk_one_foo_qux", "columns": [ "id" - ] + ], + "extra": null }, "foreign": [], "uniques": [] @@ -584,7 +604,8 @@ "columns": [ "id" ], - "expressions": null + "expressions": null, + "extra": null } ], "constraints": { @@ -592,7 +613,8 @@ "name": "pk_one_sponsors", "columns": [ "id" - ] + ], + "extra": null }, "foreign": [], "uniques": [] @@ -621,7 +643,8 @@ "columns": [ "id" ], - "expressions": null + "expressions": null, + "extra": null } ], "constraints": { @@ -629,7 +652,8 @@ "name": "pk_one_tags", "columns": [ "id" - ] + ], + "extra": null }, "foreign": [], "uniques": [] @@ -704,7 +728,8 @@ "columns": [ "id" ], - "expressions": null + "expressions": null, + "extra": null } ], "constraints": { @@ -712,7 +737,8 @@ "name": "pk_one_users", "columns": [ "id" - ] + ], + "extra": null }, "foreign": [], "uniques": [] @@ -753,7 +779,8 @@ "video_id", "tag_id" ], - "expressions": null + "expressions": null, + "extra": null } ], "constraints": { @@ -762,7 +789,8 @@ "columns": [ "video_id", "tag_id" - ] + ], + "extra": null }, "foreign": [ { @@ -770,6 +798,7 @@ "columns": [ "tag_id" ], + "extra": null, "foreign_table": "one.tags", "foreign_columns": [ "id" @@ -780,6 +809,7 @@ "columns": [ "video_id" ], + "extra": null, "foreign_table": "one.videos", "foreign_columns": [ "id" @@ -834,14 +864,16 @@ "columns": [ "id" ], - "expressions": null + "expressions": null, + "extra": null }, { "name": "sqlite_autoindex_videos_2", "columns": [ "sponsor_id" ], - "expressions": null + "expressions": null, + "extra": null } ], "constraints": { @@ -849,7 +881,8 @@ "name": "pk_one_videos", "columns": [ "id" - ] + ], + "extra": null }, "foreign": [ { @@ -857,6 +890,7 @@ "columns": [ "sponsor_id" ], + "extra": null, "foreign_table": "one.sponsors", "foreign_columns": [ "id" @@ -867,6 +901,7 @@ "columns": [ "user_id" ], + "extra": null, "foreign_table": "one.users", "foreign_columns": [ "id" @@ -878,7 +913,8 @@ "name": "sqlite_autoindex_videos_2", "columns": [ "sponsor_id" - ] + ], + "extra": null } ] } @@ -906,7 +942,8 @@ "columns": [ "id" ], - "expressions": null + "expressions": null, + "extra": null } ], "constraints": { @@ -914,7 +951,8 @@ "name": "pk_main_sponsors", "columns": [ "id" - ] + ], + "extra": null }, "foreign": [], "uniques": [] @@ -943,7 +981,8 @@ "columns": [ "id" ], - "expressions": null + "expressions": null, + "extra": null } ], "constraints": { @@ -951,7 +990,8 @@ "name": "pk_main_tags", "columns": [ "id" - ] + ], + "extra": null }, "foreign": [], "uniques": [] @@ -1002,7 +1042,8 @@ "columns": null, "expressions": [ "(col1 + col2)" - ] + ], + "extra": null }, { "name": "idx2", @@ -1011,7 +1052,8 @@ ], "expressions": [ "(col1 + col2)" - ] + ], + "extra": null }, { "name": "idx3", @@ -1020,14 +1062,16 @@ ], "expressions": [ "(col2 + col3)" - ] + ], + "extra": null }, { "name": "idx4", "columns": [ "col3" ], - "expressions": null + "expressions": null, + "extra": null }, { "name": "idx5", @@ -1035,14 +1079,16 @@ "col1", "col2" ], - "expressions": null + "expressions": null, + "extra": null }, { "name": "idx6", "columns": null, "expressions": [ "POW(col3, 2)" - ] + ], + "extra": null } ], "constraints": { @@ -2306,7 +2352,8 @@ "columns": [ "id" ], - "expressions": null + "expressions": null, + "extra": null } ], "constraints": { @@ -2314,7 +2361,8 @@ "name": "pk_main_type_monsters", "columns": [ "id" - ] + ], + "extra": null }, "foreign": [], "uniques": [] @@ -2389,7 +2437,8 @@ "columns": [ "id" ], - "expressions": null + "expressions": null, + "extra": null } ], "constraints": { @@ -2397,7 +2446,8 @@ "name": "pk_main_users", "columns": [ "id" - ] + ], + "extra": null }, "foreign": [], "uniques": [] @@ -2438,7 +2488,8 @@ "video_id", "tag_id" ], - "expressions": null + "expressions": null, + "extra": null } ], "constraints": { @@ -2447,7 +2498,8 @@ "columns": [ "video_id", "tag_id" - ] + ], + "extra": null }, "foreign": [ { @@ -2455,6 +2507,7 @@ "columns": [ "tag_id" ], + "extra": null, "foreign_table": "tags", "foreign_columns": [ "id" @@ -2465,6 +2518,7 @@ "columns": [ "video_id" ], + "extra": null, "foreign_table": "videos", "foreign_columns": [ "id" @@ -2519,14 +2573,16 @@ "columns": [ "id" ], - "expressions": null + "expressions": null, + "extra": null }, { "name": "sqlite_autoindex_videos_2", "columns": [ "sponsor_id" ], - "expressions": null + "expressions": null, + "extra": null } ], "constraints": { @@ -2534,7 +2590,8 @@ "name": "pk_main_videos", "columns": [ "id" - ] + ], + "extra": null }, "foreign": [ { @@ -2542,6 +2599,7 @@ "columns": [ "sponsor_id" ], + "extra": null, "foreign_table": "sponsors", "foreign_columns": [ "id" @@ -2552,6 +2610,7 @@ "columns": [ "user_id" ], + "extra": null, "foreign_table": "users", "foreign_columns": [ "id" @@ -2563,7 +2622,8 @@ "name": "sqlite_autoindex_videos_2", "columns": [ "sponsor_id" - ] + ], + "extra": null } ] } diff --git a/gen/bobgen-sqlite/driver/include-exclude-tables-mixed.golden.json b/gen/bobgen-sqlite/driver/include-exclude-tables-mixed.golden.json index f9832822..8a5ca3d6 100644 --- a/gen/bobgen-sqlite/driver/include-exclude-tables-mixed.golden.json +++ b/gen/bobgen-sqlite/driver/include-exclude-tables-mixed.golden.json @@ -34,7 +34,8 @@ "name": "pk_main_bar_baz", "columns": [ "id" - ] + ], + "extra": null }, "foreign": [], "uniques": [] @@ -74,7 +75,8 @@ "name": "pk_main_bar_qux", "columns": [ "id" - ] + ], + "extra": null }, "foreign": [], "uniques": [] @@ -114,7 +116,8 @@ "name": "pk_one_bar_baz", "columns": [ "id" - ] + ], + "extra": null }, "foreign": [], "uniques": [] @@ -154,7 +157,8 @@ "name": "pk_one_bar_qux", "columns": [ "id" - ] + ], + "extra": null }, "foreign": [], "uniques": [] diff --git a/gen/bobgen-sqlite/driver/include-exclude-tables-regex.golden.json b/gen/bobgen-sqlite/driver/include-exclude-tables-regex.golden.json index fea63555..6982d187 100644 --- a/gen/bobgen-sqlite/driver/include-exclude-tables-regex.golden.json +++ b/gen/bobgen-sqlite/driver/include-exclude-tables-regex.golden.json @@ -34,7 +34,8 @@ "name": "pk_main_bar_qux", "columns": [ "id" - ] + ], + "extra": null }, "foreign": [], "uniques": [] @@ -74,7 +75,8 @@ "name": "pk_main_foo_qux", "columns": [ "id" - ] + ], + "extra": null }, "foreign": [], "uniques": [] @@ -114,7 +116,8 @@ "name": "pk_one_bar_qux", "columns": [ "id" - ] + ], + "extra": null }, "foreign": [], "uniques": [] @@ -154,7 +157,8 @@ "name": "pk_one_foo_qux", "columns": [ "id" - ] + ], + "extra": null }, "foreign": [], "uniques": [] diff --git a/gen/bobgen-sqlite/driver/include-exclude-tables.golden.json b/gen/bobgen-sqlite/driver/include-exclude-tables.golden.json index e5ae14ec..bbbddd26 100644 --- a/gen/bobgen-sqlite/driver/include-exclude-tables.golden.json +++ b/gen/bobgen-sqlite/driver/include-exclude-tables.golden.json @@ -34,7 +34,8 @@ "name": "pk_main_foo_baz", "columns": [ "id" - ] + ], + "extra": null }, "foreign": [], "uniques": [] @@ -74,7 +75,8 @@ "name": "pk_one_foo_baz", "columns": [ "id" - ] + ], + "extra": null }, "foreign": [], "uniques": [] diff --git a/gen/bobgen-sqlite/driver/include-tables.golden.json b/gen/bobgen-sqlite/driver/include-tables.golden.json index a5178771..37339bea 100644 --- a/gen/bobgen-sqlite/driver/include-tables.golden.json +++ b/gen/bobgen-sqlite/driver/include-tables.golden.json @@ -34,7 +34,8 @@ "name": "pk_main_foo_bar", "columns": [ "id" - ] + ], + "extra": null }, "foreign": [], "uniques": [] @@ -74,7 +75,8 @@ "name": "pk_main_foo_baz", "columns": [ "id" - ] + ], + "extra": null }, "foreign": [], "uniques": [] @@ -114,7 +116,8 @@ "name": "pk_one_foo_bar", "columns": [ "id" - ] + ], + "extra": null }, "foreign": [], "uniques": [] @@ -154,7 +157,8 @@ "name": "pk_one_foo_baz", "columns": [ "id" - ] + ], + "extra": null }, "foreign": [], "uniques": [] diff --git a/gen/bobgen-sqlite/driver/sqlite.go b/gen/bobgen-sqlite/driver/sqlite.go index 6612cbc0..64375ad1 100644 --- a/gen/bobgen-sqlite/driver/sqlite.go +++ b/gen/bobgen-sqlite/driver/sqlite.go @@ -17,8 +17,8 @@ import ( ) type ( - Interface = drivers.Interface[any] - DBInfo = drivers.DBInfo[any] + Interface = drivers.Interface[any, any, any] + DBInfo = drivers.DBInfo[any, any, any] ) func New(config Config) Interface { @@ -73,10 +73,6 @@ func (d *driver) PackageName() string { return d.config.Pkgname } -func (d *driver) Capabilities() drivers.Capabilities { - return drivers.Capabilities{} -} - func (d *driver) Types() drivers.Types { return helpers.Types() } @@ -178,7 +174,7 @@ func (d *driver) buildQuery(schema string) (string, []any) { return query, args } -func (d *driver) tables(ctx context.Context) ([]drivers.Table, error) { +func (d *driver) tables(ctx context.Context) (drivers.Tables[any, any], error) { mainQuery, mainArgs := d.buildQuery("main") mainTables, err := stdscan.All(ctx, d.conn, scan.SingleColumnMapper[string], mainQuery, mainArgs...) if err != nil { @@ -186,7 +182,7 @@ func (d *driver) tables(ctx context.Context) ([]drivers.Table, error) { } colFilter := drivers.ParseColumnFilter(mainTables, d.config.Only, d.config.Except) - allTables := make([]drivers.Table, len(mainTables)) + allTables := make(drivers.Tables[any, any], len(mainTables)) for i, name := range mainTables { allTables[i], err = d.getTable(ctx, "main", name, colFilter) if err != nil { @@ -213,10 +209,10 @@ func (d *driver) tables(ctx context.Context) ([]drivers.Table, error) { return allTables, nil } -func (d driver) getTable(ctx context.Context, schema, name string, colFilter drivers.ColumnFilter) (drivers.Table, error) { +func (d driver) getTable(ctx context.Context, schema, name string, colFilter drivers.ColumnFilter) (drivers.Table[any, any], error) { var err error - table := drivers.Table{ + table := drivers.Table[any, any]{ Key: d.key(schema, name), Schema: d.schema(schema), Name: name, @@ -344,7 +340,7 @@ func (s driver) tableInfo(ctx context.Context, schema, tableName string) ([]info } // primaryKey looks up the primary key for a table. -func (s driver) primaryKey(schema, tableName string, tinfo []info) *drivers.PrimaryKey { +func (s driver) primaryKey(schema, tableName string, tinfo []info) *drivers.Constraint[any] { var cols []string for _, c := range tinfo { if c.Pk > 0 { @@ -356,7 +352,7 @@ func (s driver) primaryKey(schema, tableName string, tinfo []info) *drivers.Prim return nil } - return &drivers.PrimaryKey{ + return &drivers.Constraint[any]{ Name: fmt.Sprintf("pk_%s_%s", schema, tableName), Columns: cols, } @@ -404,14 +400,14 @@ func (d driver) skipKey(table, column string) bool { } // foreignKeys retrieves the foreign keys for a given table name. -func (d driver) foreignKeys(ctx context.Context, schema, tableName string) ([]drivers.ForeignKey, error) { +func (d driver) foreignKeys(ctx context.Context, schema, tableName string) ([]drivers.ForeignKey[any], error) { rows, err := d.conn.QueryContext(ctx, fmt.Sprintf("PRAGMA '%s'.foreign_key_list('%s')", schema, tableName)) if err != nil { return nil, err } defer rows.Close() - fkeyMap := make(map[int]drivers.ForeignKey) + fkeyMap := make(map[int]drivers.ForeignKey[any]) for rows.Next() { var id, seq int var ftable, col string @@ -445,9 +441,11 @@ func (d driver) foreignKeys(ctx context.Context, schema, tableName string) ([]dr continue } - fkeyMap[id] = drivers.ForeignKey{ - Name: fmt.Sprintf("fk_%s_%d", tableName, id), - Columns: append(fkeyMap[id].Columns, col), + fkeyMap[id] = drivers.ForeignKey[any]{ + Constraint: drivers.Constraint[any]{ + Name: fmt.Sprintf("fk_%s_%d", tableName, id), + Columns: append(fkeyMap[id].Columns, col), + }, ForeignTable: d.key(schema, ftable), ForeignColumns: append(fkeyMap[id].ForeignColumns, fcol), } @@ -457,7 +455,7 @@ func (d driver) foreignKeys(ctx context.Context, schema, tableName string) ([]dr return nil, err } - fkeys := make([]drivers.ForeignKey, 0, len(fkeyMap)) + fkeys := make([]drivers.ForeignKey[any], 0, len(fkeyMap)) for _, fkey := range fkeyMap { fkeys = append(fkeys, fkey) @@ -471,7 +469,7 @@ func (d driver) foreignKeys(ctx context.Context, schema, tableName string) ([]dr } // uniques retrieves the unique keys for a given table name. -func (d driver) uniques(ctx context.Context, schema, tableName string) ([]drivers.Constraint, error) { +func (d driver) uniques(ctx context.Context, schema, tableName string) ([]drivers.Constraint[any], error) { rows, err := d.conn.QueryContext(ctx, fmt.Sprintf("PRAGMA '%s'.index_list('%s')", schema, tableName)) if err != nil { return nil, err @@ -501,7 +499,7 @@ func (d driver) uniques(ctx context.Context, schema, tableName string) ([]driver return nil, err } - uniques := make([]drivers.Constraint, len(indexes)) + uniques := make([]drivers.Constraint[any], len(indexes)) for i, index := range indexes { uniques[i], err = d.getUniqueIndex(ctx, schema, index) if err != nil { @@ -512,8 +510,8 @@ func (d driver) uniques(ctx context.Context, schema, tableName string) ([]driver return uniques, nil } -func (d driver) getUniqueIndex(ctx context.Context, schema, index string) (drivers.Constraint, error) { - unique := drivers.Constraint{Name: index} +func (d driver) getUniqueIndex(ctx context.Context, schema, index string) (drivers.Constraint[any], error) { + unique := drivers.Constraint[any]{Name: index} rows, err := d.conn.QueryContext(ctx, fmt.Sprintf("PRAGMA '%s'.index_info('%s')", schema, index)) if err != nil { @@ -611,7 +609,7 @@ func (driver) translateColumnType(c drivers.Column) drivers.Column { return c } -func (d *driver) indexes(ctx context.Context, schema, tableName string) ([]drivers.Index, error) { +func (d *driver) indexes(ctx context.Context, schema, tableName string) ([]drivers.Index[any], error) { //nolint:gosec query := fmt.Sprintf("SELECT name FROM '%s'.pragma_index_list('%s') ORDER BY name ASC", schema, tableName) rows, err := d.conn.QueryContext(ctx, query) @@ -634,7 +632,7 @@ func (d *driver) indexes(ctx context.Context, schema, tableName string) ([]drive return nil, err } - indexes := make([]drivers.Index, len(indexNames)) + indexes := make([]drivers.Index[any], len(indexNames)) for i, indexName := range indexNames { indexes[i], err = d.getIndexInformation(ctx, schema, tableName, indexName) if err != nil { @@ -645,8 +643,8 @@ func (d *driver) indexes(ctx context.Context, schema, tableName string) ([]drive return indexes, nil } -func (d *driver) getIndexInformation(ctx context.Context, schema, tableName, indexName string) (drivers.Index, error) { - var index drivers.Index +func (d *driver) getIndexInformation(ctx context.Context, schema, tableName, indexName string) (drivers.Index[any], error) { + var index drivers.Index[any] index.Name = indexName //nolint:gosec diff --git a/gen/bobgen-sqlite/driver/sqlite.golden.json b/gen/bobgen-sqlite/driver/sqlite.golden.json index 12dff48c..e57b3295 100644 --- a/gen/bobgen-sqlite/driver/sqlite.golden.json +++ b/gen/bobgen-sqlite/driver/sqlite.golden.json @@ -67,7 +67,8 @@ "columns": [ "sponsor_id" ], - "expressions": null + "expressions": null, + "extra": null }, { "name": "sqlite_autoindex_autoinckeywordtest_2", @@ -75,7 +76,8 @@ "something", "another" ], - "expressions": null + "expressions": null, + "extra": null } ], "constraints": { @@ -83,7 +85,8 @@ "name": "pk_main_autoinckeywordtest", "columns": [ "id" - ] + ], + "extra": null }, "foreign": [ { @@ -92,6 +95,7 @@ "user_id", "sponsor_id" ], + "extra": null, "foreign_table": "videos", "foreign_columns": [ "user_id", @@ -105,13 +109,15 @@ "columns": [ "something", "another" - ] + ], + "extra": null }, { "name": "sqlite_autoindex_autoinckeywordtest_1", "columns": [ "sponsor_id" - ] + ], + "extra": null } ] } @@ -139,7 +145,8 @@ "name": "pk_main_autoinctest", "columns": [ "id" - ] + ], + "extra": null }, "foreign": [ { @@ -147,6 +154,7 @@ "columns": [ "id" ], + "extra": null, "foreign_table": "tags", "foreign_columns": [ "id" @@ -190,7 +198,8 @@ "name": "pk_main_bar_baz", "columns": [ "id" - ] + ], + "extra": null }, "foreign": [], "uniques": [] @@ -230,7 +239,8 @@ "name": "pk_main_bar_qux", "columns": [ "id" - ] + ], + "extra": null }, "foreign": [], "uniques": [] @@ -270,7 +280,8 @@ "name": "pk_main_foo_bar", "columns": [ "id" - ] + ], + "extra": null }, "foreign": [], "uniques": [] @@ -310,7 +321,8 @@ "name": "pk_main_foo_baz", "columns": [ "id" - ] + ], + "extra": null }, "foreign": [], "uniques": [] @@ -350,7 +362,8 @@ "name": "pk_main_foo_qux", "columns": [ "id" - ] + ], + "extra": null }, "foreign": [], "uniques": [] @@ -423,7 +436,8 @@ "name": "pk_main_has_generated_columns", "columns": [ "a" - ] + ], + "extra": null }, "foreign": [], "uniques": [] @@ -496,7 +510,8 @@ "name": "pk_one_as_generated_columns", "columns": [ "a" - ] + ], + "extra": null }, "foreign": [], "uniques": [] @@ -536,7 +551,8 @@ "columns": [ "sponsor_id" ], - "expressions": null + "expressions": null, + "extra": null }, { "name": "sqlite_autoindex_autoinckeywordtest_2", @@ -544,7 +560,8 @@ "something", "another" ], - "expressions": null + "expressions": null, + "extra": null } ], "constraints": { @@ -552,7 +569,8 @@ "name": "pk_one_autoinckeywordtest", "columns": [ "id" - ] + ], + "extra": null }, "foreign": [], "uniques": [] @@ -581,7 +599,8 @@ "name": "pk_one_autoinctest", "columns": [ "id" - ] + ], + "extra": null }, "foreign": [], "uniques": [] @@ -621,7 +640,8 @@ "name": "pk_one_bar_baz", "columns": [ "id" - ] + ], + "extra": null }, "foreign": [], "uniques": [] @@ -661,7 +681,8 @@ "name": "pk_one_bar_qux", "columns": [ "id" - ] + ], + "extra": null }, "foreign": [], "uniques": [] @@ -701,7 +722,8 @@ "name": "pk_one_foo_bar", "columns": [ "id" - ] + ], + "extra": null }, "foreign": [], "uniques": [] @@ -741,7 +763,8 @@ "name": "pk_one_foo_baz", "columns": [ "id" - ] + ], + "extra": null }, "foreign": [], "uniques": [] @@ -781,7 +804,8 @@ "name": "pk_one_foo_qux", "columns": [ "id" - ] + ], + "extra": null }, "foreign": [], "uniques": [] @@ -810,7 +834,8 @@ "columns": [ "id" ], - "expressions": null + "expressions": null, + "extra": null } ], "constraints": { @@ -818,7 +843,8 @@ "name": "pk_one_sponsors", "columns": [ "id" - ] + ], + "extra": null }, "foreign": [], "uniques": [] @@ -847,7 +873,8 @@ "columns": [ "id" ], - "expressions": null + "expressions": null, + "extra": null } ], "constraints": { @@ -855,7 +882,8 @@ "name": "pk_one_tags", "columns": [ "id" - ] + ], + "extra": null }, "foreign": [], "uniques": [] @@ -930,7 +958,8 @@ "columns": [ "id" ], - "expressions": null + "expressions": null, + "extra": null } ], "constraints": { @@ -938,7 +967,8 @@ "name": "pk_one_users", "columns": [ "id" - ] + ], + "extra": null }, "foreign": [], "uniques": [] @@ -979,7 +1009,8 @@ "video_id", "tag_id" ], - "expressions": null + "expressions": null, + "extra": null } ], "constraints": { @@ -988,7 +1019,8 @@ "columns": [ "video_id", "tag_id" - ] + ], + "extra": null }, "foreign": [ { @@ -996,6 +1028,7 @@ "columns": [ "tag_id" ], + "extra": null, "foreign_table": "one.tags", "foreign_columns": [ "id" @@ -1006,6 +1039,7 @@ "columns": [ "video_id" ], + "extra": null, "foreign_table": "one.videos", "foreign_columns": [ "id" @@ -1060,14 +1094,16 @@ "columns": [ "id" ], - "expressions": null + "expressions": null, + "extra": null }, { "name": "sqlite_autoindex_videos_2", "columns": [ "sponsor_id" ], - "expressions": null + "expressions": null, + "extra": null } ], "constraints": { @@ -1075,7 +1111,8 @@ "name": "pk_one_videos", "columns": [ "id" - ] + ], + "extra": null }, "foreign": [ { @@ -1083,6 +1120,7 @@ "columns": [ "sponsor_id" ], + "extra": null, "foreign_table": "one.sponsors", "foreign_columns": [ "id" @@ -1093,6 +1131,7 @@ "columns": [ "user_id" ], + "extra": null, "foreign_table": "one.users", "foreign_columns": [ "id" @@ -1104,7 +1143,8 @@ "name": "sqlite_autoindex_videos_2", "columns": [ "sponsor_id" - ] + ], + "extra": null } ] } @@ -1132,7 +1172,8 @@ "columns": [ "id" ], - "expressions": null + "expressions": null, + "extra": null } ], "constraints": { @@ -1140,7 +1181,8 @@ "name": "pk_main_sponsors", "columns": [ "id" - ] + ], + "extra": null }, "foreign": [], "uniques": [] @@ -1169,7 +1211,8 @@ "columns": [ "id" ], - "expressions": null + "expressions": null, + "extra": null } ], "constraints": { @@ -1177,7 +1220,8 @@ "name": "pk_main_tags", "columns": [ "id" - ] + ], + "extra": null }, "foreign": [], "uniques": [] @@ -1228,7 +1272,8 @@ "columns": null, "expressions": [ "(col1 + col2)" - ] + ], + "extra": null }, { "name": "idx2", @@ -1237,7 +1282,8 @@ ], "expressions": [ "(col1 + col2)" - ] + ], + "extra": null }, { "name": "idx3", @@ -1246,14 +1292,16 @@ ], "expressions": [ "(col2 + col3)" - ] + ], + "extra": null }, { "name": "idx4", "columns": [ "col3" ], - "expressions": null + "expressions": null, + "extra": null }, { "name": "idx5", @@ -1261,14 +1309,16 @@ "col1", "col2" ], - "expressions": null + "expressions": null, + "extra": null }, { "name": "idx6", "columns": null, "expressions": [ "POW(col3, 2)" - ] + ], + "extra": null } ], "constraints": { @@ -2532,7 +2582,8 @@ "columns": [ "id" ], - "expressions": null + "expressions": null, + "extra": null } ], "constraints": { @@ -2540,7 +2591,8 @@ "name": "pk_main_type_monsters", "columns": [ "id" - ] + ], + "extra": null }, "foreign": [], "uniques": [] @@ -2615,7 +2667,8 @@ "columns": [ "id" ], - "expressions": null + "expressions": null, + "extra": null } ], "constraints": { @@ -2623,7 +2676,8 @@ "name": "pk_main_users", "columns": [ "id" - ] + ], + "extra": null }, "foreign": [], "uniques": [] @@ -2664,7 +2718,8 @@ "video_id", "tag_id" ], - "expressions": null + "expressions": null, + "extra": null } ], "constraints": { @@ -2673,7 +2728,8 @@ "columns": [ "video_id", "tag_id" - ] + ], + "extra": null }, "foreign": [ { @@ -2681,6 +2737,7 @@ "columns": [ "tag_id" ], + "extra": null, "foreign_table": "tags", "foreign_columns": [ "id" @@ -2691,6 +2748,7 @@ "columns": [ "video_id" ], + "extra": null, "foreign_table": "videos", "foreign_columns": [ "id" @@ -2745,14 +2803,16 @@ "columns": [ "id" ], - "expressions": null + "expressions": null, + "extra": null }, { "name": "sqlite_autoindex_videos_2", "columns": [ "sponsor_id" ], - "expressions": null + "expressions": null, + "extra": null } ], "constraints": { @@ -2760,7 +2820,8 @@ "name": "pk_main_videos", "columns": [ "id" - ] + ], + "extra": null }, "foreign": [ { @@ -2768,6 +2829,7 @@ "columns": [ "sponsor_id" ], + "extra": null, "foreign_table": "sponsors", "foreign_columns": [ "id" @@ -2778,6 +2840,7 @@ "columns": [ "user_id" ], + "extra": null, "foreign_table": "users", "foreign_columns": [ "id" @@ -2789,7 +2852,8 @@ "name": "sqlite_autoindex_videos_2", "columns": [ "sponsor_id" - ] + ], + "extra": null } ] } diff --git a/gen/bobgen-sqlite/driver/sqlite_test.go b/gen/bobgen-sqlite/driver/sqlite_test.go index 3300b538..c6bd3aa6 100644 --- a/gen/bobgen-sqlite/driver/sqlite_test.go +++ b/gen/bobgen-sqlite/driver/sqlite_test.go @@ -191,9 +191,9 @@ func TestAssemble(t *testing.T) { os.RemoveAll(out) }() - testgen.TestDriver(t, testgen.DriverTestConfig[any]{ + testgen.TestDriver(t, testgen.DriverTestConfig[any, any, any]{ Root: out, - GetDriver: func() drivers.Interface[any] { + GetDriver: func() drivers.Interface[any, any, any] { return New(tt.config) }, GoldenFile: tt.goldenJson, diff --git a/gen/bobgen-sqlite/main.go b/gen/bobgen-sqlite/main.go index 566c9c8c..4fd2c9b0 100644 --- a/gen/bobgen-sqlite/main.go +++ b/gen/bobgen-sqlite/main.go @@ -44,7 +44,7 @@ func main() { } func run(c *cli.Context) error { - config, driverConfig, err := helpers.GetConfigFromFile[driver.Config](c.String("config"), "sqlite") + config, driverConfig, err := helpers.GetConfigFromFile[any, driver.Config](c.String("config"), "sqlite") if err != nil { return err } @@ -55,7 +55,7 @@ func run(c *cli.Context) error { &helpers.Templates{Models: []fs.FS{gen.SQLiteModelTemplates}}, ) - state := &gen.State{ + state := &gen.State[any]{ Config: config, Outputs: outputs, } diff --git a/gen/config.go b/gen/config.go index 2d4a7505..8c4607d7 100644 --- a/gen/config.go +++ b/gen/config.go @@ -5,7 +5,7 @@ import ( ) // Config for the running of the commands -type Config struct { +type Config[ConstraintExtra any] struct { // Struct tags to generate Tags []string `yaml:"tags"` // Disable generating factories for models @@ -23,10 +23,10 @@ type Config struct { // List of column names that should have tags values set to '-' (ignored during parsing) TagIgnore []string `yaml:"tag_ignore"` - Types drivers.Types `yaml:"types"` // register custom types - Aliases Aliases `yaml:"aliases"` // customize aliases - Constraints Constraints `yaml:"constraints"` // define additional constraints - Relationships Relationships `yaml:"relationships"` // define additional relationships + Types drivers.Types `yaml:"types"` // register custom types + Aliases drivers.Aliases `yaml:"aliases"` // customize aliases + Constraints Constraints[ConstraintExtra] `yaml:"constraints"` // define additional constraints + Relationships Relationships `yaml:"relationships"` // define additional relationships Replacements []Replace `yaml:"replacements"` Inflections Inflections `yaml:"inflections"` diff --git a/gen/constraints.go b/gen/constraints.go index b12b4357..81cea814 100644 --- a/gen/constraints.go +++ b/gen/constraints.go @@ -2,9 +2,9 @@ package gen import "github.com/stephenafamo/bob/gen/drivers" -type Constraints map[string]drivers.Constraints +type Constraints[C any] map[string]drivers.Constraints[C] -func processConstraintConfig(tables []drivers.Table, extras Constraints) { +func processConstraintConfig[C, I any](tables []drivers.Table[C, I], extras Constraints[C]) { if len(tables) == 0 { return } @@ -19,7 +19,7 @@ func processConstraintConfig(tables []drivers.Table, extras Constraints) { } } -func mergeConstraints(srcs, extras drivers.Constraints) drivers.Constraints { +func mergeConstraints[C any](srcs, extras drivers.Constraints[C]) drivers.Constraints[C] { if extras.Primary != nil { srcs.Primary = extras.Primary } diff --git a/gen/drivers/aliases.go b/gen/drivers/aliases.go new file mode 100644 index 00000000..6437f314 --- /dev/null +++ b/gen/drivers/aliases.go @@ -0,0 +1,49 @@ +package drivers + +import ( + "fmt" +) + +// Aliases defines aliases for the generation run +type Aliases map[string]TableAlias + +// TableAlias defines the spellings for a table name in Go +type TableAlias struct { + UpPlural string `yaml:"up_plural,omitempty" toml:"up_plural,omitempty" json:"up_plural,omitempty"` + UpSingular string `yaml:"up_singular,omitempty" toml:"up_singular,omitempty" json:"up_singular,omitempty"` + DownPlural string `yaml:"down_plural,omitempty" toml:"down_plural,omitempty" json:"down_plural,omitempty"` + DownSingular string `yaml:"down_singular,omitempty" toml:"down_singular,omitempty" json:"down_singular,omitempty"` + + Columns map[string]string `yaml:"columns,omitempty" toml:"columns,omitempty" json:"columns,omitempty"` + Relationships map[string]string `yaml:"relationships,omitempty" toml:"relationships,omitempty" json:"relationships,omitempty"` +} + +// Table gets a table alias, panics if not found. +func (a Aliases) Table(table string) TableAlias { + t, ok := a[table] + if !ok { + panic("could not find table aliases for: " + table) + } + + return t +} + +// Column get's a column's aliased name, panics if not found. +func (t TableAlias) Column(column string) string { + c, ok := t.Columns[column] + if !ok { + panic(fmt.Sprintf("could not find column alias for: %s.%s", t.UpSingular, column)) + } + + return c +} + +// Relationship looks up a relationship, panics if not found. +func (t TableAlias) Relationship(fkey string) string { + r, ok := t.Relationships[fkey] + if !ok { + panic(fmt.Sprintf("could not find relationship alias for: %s.%s", t.UpSingular, fkey)) + } + + return r +} diff --git a/gen/drivers/constraints.go b/gen/drivers/constraints.go index 5cb2874b..572a2f23 100644 --- a/gen/drivers/constraints.go +++ b/gen/drivers/constraints.go @@ -1,26 +1,82 @@ package drivers -type DBConstraints struct { - PKs map[string]*PrimaryKey - FKs map[string][]ForeignKey - Uniques map[string][]Constraint +import "encoding/json" + +// Index represents an index in a table +type Index[Extra any] struct { + Name string `yaml:"name" json:"name"` + Columns []string `yaml:"columns" json:"columns"` + Expressions []string `yaml:"expressions" json:"expressions"` + Extra Extra `yaml:"extra" json:"extra"` } -// PrimaryKey represents a primary key constraint in a database -type PrimaryKey = Constraint +// DBIndexes lists all indexes in the database schema keyed by table name +type DBIndexes[Extra any] map[string][]Index[Extra] + +type Constraints[Extra any] struct { + Primary *Constraint[Extra] `yaml:"primary" json:"primary"` + Foreign []ForeignKey[Extra] `yaml:"foreign" json:"foreign"` + Uniques []Constraint[Extra] `yaml:"uniques" json:"uniques"` +} + +type DBConstraints[Extra any] struct { + PKs map[string]*Constraint[Extra] + FKs map[string][]ForeignKey[Extra] + Uniques map[string][]Constraint[Extra] +} // Constraint represents a constraint in a database -type Constraint NamedColumnList +type Constraint[Extra any] NamedColumnList[Extra] -type NamedColumnList struct { +type NamedColumnList[Extra any] struct { Name string `yaml:"name" json:"name"` Columns []string `yaml:"columns" json:"columns"` + Extra Extra `yaml:"extra" json:"extra"` } // ForeignKey represents a foreign key constraint in a database -type ForeignKey struct { - Name string `yaml:"name" json:"name"` - Columns []string `yaml:"columns" json:"columns"` - ForeignTable string `yaml:"foreign_table" json:"foreign_table"` - ForeignColumns []string `yaml:"foreign_columns" json:"foreign_columns"` +type ForeignKey[Extra any] struct { + Constraint[Extra] `yaml:",inline" json:"-"` + ForeignTable string `yaml:"foreign_table" json:"foreign_table"` + ForeignColumns []string `yaml:"foreign_columns" json:"foreign_columns"` +} + +func (f *ForeignKey[E]) UnmarshalJSON(data []byte) error { + var tmp struct { + Name string `json:"name"` + Columns []string `json:"columns"` + Extra E `json:"extra"` + ForeignTable string `json:"foreign_table"` + ForeignColumns []string `json:"foreign_columns"` + } + + if err := json.Unmarshal(data, &tmp); err != nil { + return err + } + + f.Name = tmp.Name + f.Columns = tmp.Columns + f.Extra = tmp.Extra + f.ForeignTable = tmp.ForeignTable + f.ForeignColumns = tmp.ForeignColumns + + return nil +} + +func (f ForeignKey[E]) MarshalJSON() ([]byte, error) { + tmp := struct { + Name string `json:"name"` + Columns []string `json:"columns"` + Extra E `json:"extra"` + ForeignTable string `json:"foreign_table"` + ForeignColumns []string `json:"foreign_columns"` + }{ + Name: f.Name, + Columns: f.Columns, + Extra: f.Extra, + ForeignTable: f.ForeignTable, + ForeignColumns: f.ForeignColumns, + } + + return json.Marshal(tmp) } diff --git a/gen/drivers/filter.go b/gen/drivers/filter.go new file mode 100644 index 00000000..ced4ebe2 --- /dev/null +++ b/gen/drivers/filter.go @@ -0,0 +1,61 @@ +package drivers + +import "strings" + +type Filter struct { + Only []string + Except []string +} + +func (f Filter) ClassifyPatterns(patterns []string) ([]string, []string) { + const regexDelimiter = "/" + var stringPatterns, regexPatterns []string //nolint:prealloc + + for _, pattern := range patterns { + if f.isRegexPattern(pattern, regexDelimiter) { + regexPatterns = append(regexPatterns, strings.Trim(pattern, regexDelimiter)) + continue + } + stringPatterns = append(stringPatterns, pattern) + } + + return stringPatterns, regexPatterns +} + +func (f Filter) isRegexPattern(pattern, delimiter string) bool { + return strings.HasPrefix(pattern, delimiter) && strings.HasSuffix(pattern, delimiter) +} + +type ColumnFilter map[string]Filter + +func ParseTableFilter(only, except map[string][]string) Filter { + var filter Filter + for name := range only { + filter.Only = append(filter.Only, name) + } + + for name, cols := range except { + // If they only want to exclude some columns, then we don't want to exclude the whole table + if len(cols) == 0 { + filter.Except = append(filter.Except, name) + } + } + + return filter +} + +func ParseColumnFilter(tables []string, only, except map[string][]string) ColumnFilter { + global := Filter{ + Only: only["*"], + Except: except["*"], + } + + colFilter := make(ColumnFilter, len(tables)) + for _, t := range tables { + colFilter[t] = Filter{ + Only: append(global.Only, only[t]...), + Except: append(global.Except, except[t]...), + } + } + return colFilter +} diff --git a/gen/drivers/indexes.go b/gen/drivers/indexes.go deleted file mode 100644 index 9f114dbc..00000000 --- a/gen/drivers/indexes.go +++ /dev/null @@ -1,11 +0,0 @@ -package drivers - -// DBIndexes lists all indexes in the database schema keyed by table name -type DBIndexes map[string][]Index - -// Index represents an index in a table -type Index struct { - Name string `yaml:"name" json:"name"` - Columns []string `yaml:"columns" json:"columns"` - Expressions []string `yaml:"expressions" json:"expressions"` -} diff --git a/gen/drivers/interface.go b/gen/drivers/interface.go index 8a74613d..da877bd1 100644 --- a/gen/drivers/interface.go +++ b/gen/drivers/interface.go @@ -13,13 +13,11 @@ import ( // Interface abstracts either a side-effect imported driver or a binary // that is called in order to produce the data required for generation. -type Interface[T any] interface { +type Interface[DBExtra, ConstraintExtra, IndexExtra any] interface { // The dialect Dialect() string - // What the driver is capable of - Capabilities() Capabilities // Assemble the database information into a nice struct - Assemble(ctx context.Context) (*DBInfo[T], error) + Assemble(ctx context.Context) (*DBInfo[DBExtra, ConstraintExtra, IndexExtra], error) // Custom types defined by the driver Types() Types } @@ -54,13 +52,11 @@ type Type struct { type Types map[string]Type -type Capabilities struct{} - // DBInfo is the database's table data and dialect. -type DBInfo[T any] struct { - Tables []Table `json:"tables"` - Enums []Enum `json:"enums"` - ExtraInfo T `json:"extra_info"` +type DBInfo[DBExtra, ConstraintExtra, IndexExtra any] struct { + Tables []Table[ConstraintExtra, IndexExtra] `json:"tables"` + Enums []Enum `json:"enums"` + ExtraInfo DBExtra `json:"extra_info"` // DriverName is the module name of the underlying `database/sql` driver DriverName string `json:"driver_name"` } @@ -89,12 +85,12 @@ func (t TablesInfo) Keys() []string { // Constructor breaks down the functionality required to implement a driver // such that the drivers.Tables method can be used to reduce duplication in driver // implementations. -type Constructor interface { +type Constructor[ConstraintExtra, IndexExtra any] interface { // Load all constraints in the database, keyed by TableInfo.Key - Constraints(context.Context, ColumnFilter) (DBConstraints, error) + Constraints(context.Context, ColumnFilter) (DBConstraints[ConstraintExtra], error) // Load all indexes in the database, keyed by TableInfo.Key - Indexes(ctx context.Context) (DBIndexes, error) + Indexes(ctx context.Context) (DBIndexes[IndexExtra], error) // Load basic info about all tables TablesInfo(context.Context, Filter) (TablesInfo, error) @@ -104,9 +100,12 @@ type Constructor interface { // TablesConcurrently is a concurrent version of BuildDBInfo. It returns the // metadata for all tables, minus the tables specified in the excludes. -func BuildDBInfo(ctx context.Context, c Constructor, concurrency int, only, except map[string][]string) ([]Table, error) { +func BuildDBInfo[DBExtra, ConstraintExtra, IndexExtra any]( + ctx context.Context, c Constructor[ConstraintExtra, IndexExtra], + concurrency int, only, except map[string][]string, +) ([]Table[ConstraintExtra, IndexExtra], error) { var err error - var ret []Table + var ret []Table[ConstraintExtra, IndexExtra] if concurrency < 1 { concurrency = 1 @@ -147,12 +146,12 @@ func BuildDBInfo(ctx context.Context, c Constructor, concurrency int, only, exce return ret, nil } -func tables(ctx context.Context, c Constructor, concurrency int, infos TablesInfo, filter ColumnFilter) ([]Table, error) { +func tables[C, I any](ctx context.Context, c Constructor[C, I], concurrency int, infos TablesInfo, filter ColumnFilter) ([]Table[C, I], error) { sort.Slice(infos, func(i, j int) bool { return infos[i].Key < infos[j].Key }) - ret := make([]Table, len(infos)) + ret := make([]Table[C, I], len(infos)) limiter := newConcurrencyLimiter(concurrency) wg := sync.WaitGroup{} @@ -183,14 +182,14 @@ func tables(ctx context.Context, c Constructor, concurrency int, infos TablesInf } // table returns columns info for a given table -func table(ctx context.Context, c Constructor, info TableInfo, filter ColumnFilter) (Table, error) { +func table[C, I any](ctx context.Context, c Constructor[C, I], info TableInfo, filter ColumnFilter) (Table[C, I], error) { var err error - t := Table{ + t := Table[C, I]{ Key: info.Key, } if t.Schema, t.Name, t.Columns, err = c.TableDetails(ctx, info, filter); err != nil { - return Table{}, fmt.Errorf("unable to fetch table column info (%s): %w", info.Key, err) + return Table[C, I]{}, fmt.Errorf("unable to fetch table column info (%s): %w", info.Key, err) } return t, nil diff --git a/gen/drivers/table.go b/gen/drivers/table.go index 59427906..7c85e061 100644 --- a/gen/drivers/table.go +++ b/gen/drivers/table.go @@ -3,50 +3,43 @@ package drivers import ( "fmt" "strings" + + "github.com/stephenafamo/bob/internal" + "github.com/stephenafamo/bob/orm" ) // Table metadata from the database schema. -type Table struct { +type Table[ConstraintExtra, IndexExtra any] struct { Key string `yaml:"key" json:"key"` // For dbs with real schemas, like Postgres. // Example value: "schema_name"."table_name" - Schema string `yaml:"schema" json:"schema"` - Name string `yaml:"name" json:"name"` - Columns []Column `yaml:"columns" json:"columns"` - Indexes []Index `yaml:"indexes" json:"indexes"` - - Constraints Constraints `yaml:"constraints" json:"constraints"` -} + Schema string `yaml:"schema" json:"schema"` + Name string `yaml:"name" json:"name"` + Columns []Column `yaml:"columns" json:"columns"` + Indexes []Index[IndexExtra] `yaml:"indexes" json:"indexes"` -type Constraints struct { - Primary *PrimaryKey `yaml:"primary" json:"primary"` - Foreign []ForeignKey `yaml:"foreign" json:"foreign"` - Uniques []Constraint `yaml:"uniques" json:"uniques"` + Constraints Constraints[ConstraintExtra] `yaml:"constraints" json:"constraints"` } -// GetTable by name. Panics if not found (for use in templates mostly). -func GetTable(tables []Table, name string) Table { - for _, t := range tables { - if t.Key == name { - return t +func (t Table[C, I]) DBTag(c Column) string { + tag := c.Name + if t.Constraints.Primary != nil { + for _, pkc := range t.Constraints.Primary.Columns { + if pkc == c.Name { + tag += ",pk" + } } } - - panic(fmt.Sprintf("could not find table name: %s", name)) -} - -// GetColumn by name. Panics if not found (for use in templates mostly). -func (t Table) GetColumn(name string) Column { - for _, c := range t.Columns { - if c.Name == name { - return c - } + if c.Generated { + tag += ",generated" } - - panic(fmt.Sprintf("could not find column name: %q.%q in %#v", t.Key, name, t.Columns)) + if c.AutoIncr { + tag += ",autoincr" + } + return tag } -func (t Table) NonGeneratedColumns() []Column { +func (t Table[C, I]) NonGeneratedColumns() []Column { cols := make([]Column, 0, len(t.Columns)) for _, c := range t.Columns { if c.Generated { @@ -58,7 +51,7 @@ func (t Table) NonGeneratedColumns() []Column { return cols } -func (t Table) CanSoftDelete(deleteColumn string) bool { +func (t Table[C, I]) CanSoftDelete(deleteColumn string) bool { if deleteColumn == "" { deleteColumn = "deleted_at" } @@ -71,60 +64,139 @@ func (t Table) CanSoftDelete(deleteColumn string) bool { return false } -type Filter struct { - Only []string - Except []string +// GetColumn by name. Panics if not found (for use in templates mostly). +func (t Table[C, I]) GetColumn(name string) Column { + for _, c := range t.Columns { + if c.Name == name { + return c + } + } + + panic(fmt.Sprintf("could not find column name: %q.%q in %#v", t.Key, name, t.Columns)) } -func (f Filter) ClassifyPatterns(patterns []string) ([]string, []string) { - const regexDelimiter = "/" - var stringPatterns, regexPatterns []string //nolint:prealloc +// Returns true if the table has a unique constraint on exactly these columns +func (t Table[C, I]) HasExactUnique(cols ...string) bool { + if len(cols) == 0 { + fmt.Printf(" false\n") + return false + } - for _, pattern := range patterns { - if f.isRegexPattern(pattern, regexDelimiter) { - regexPatterns = append(regexPatterns, strings.Trim(pattern, regexDelimiter)) - continue + // Primary keys are unique + if t.Constraints.Primary != nil && internal.SliceMatch(t.Constraints.Primary.Columns, cols) { + return true + } + + // Check other unique constrints + for _, u := range t.Constraints.Uniques { + if internal.SliceMatch(u.Columns, cols) { + return true } - stringPatterns = append(stringPatterns, pattern) } - return stringPatterns, regexPatterns + return false } -func (f Filter) isRegexPattern(pattern, delimiter string) bool { - return strings.HasPrefix(pattern, delimiter) && strings.HasSuffix(pattern, delimiter) +func (tables Table[C, I]) RelIsRequired(rel orm.Relationship) bool { + // The relationship is not required, if its not using foreign keys + if rel.NeverRequired { + return false + } + + firstSide := rel.Sides[0] + if firstSide.Modify == "to" { + return false + } + + for _, colName := range firstSide.FromColumns { + if tables.GetColumn(colName).Nullable { + return false + } + } + + return true } -type ColumnFilter map[string]Filter +// Used in templates to know if the given table is a join table for this relationship +func (t Table[C, I]) IsJoinTable() bool { + // Must have exactly 2 foreign keys + if len(t.Constraints.Foreign) != 2 { + return false + } -func ParseTableFilter(only, except map[string][]string) Filter { - var filter Filter - for name := range only { - filter.Only = append(filter.Only, name) + // Extract the columns names + colNames := make([]string, len(t.Columns)) + for i, c := range t.Columns { + colNames[i] = c.Name } - for name, cols := range except { - // If they only want to exclude some columns, then we don't want to exclude the whole table - if len(cols) == 0 { - filter.Except = append(filter.Except, name) - } + // All columns must be contained in the foreign keys + if !internal.AllColsInList(colNames, t.Constraints.Foreign[0].Columns, t.Constraints.Foreign[1].Columns) { + return false } - return filter + // Must have a unique constraint on all columns + return t.HasExactUnique(colNames...) } -func ParseColumnFilter(tables []string, only, except map[string][]string) ColumnFilter { - global := Filter{ - Only: only["*"], - Except: except["*"], +// Used in templates to know if the given table is a join table for this relationship +func (t Table[C, I]) IsJoinTableForRel(r orm.Relationship, position int) bool { + if position == 0 || len(r.Sides) < 2 { + return false } - colFilter := make(ColumnFilter, len(tables)) - for _, t := range tables { - colFilter[t] = Filter{ - Only: append(global.Only, only[t]...), - Except: append(global.Except, except[t]...), - } + if position == len(r.Sides) { + return false } - return colFilter + + if t.Key != r.Sides[position-1].To { + panic(fmt.Sprintf( + "table name does not match relationship position, expected %s got %s", + t.Key, r.Sides[position-1].To, + )) + } + + relevantSides := r.Sides[position-1 : position+1] + + // If the external mappings are not unique, it is not a join table + if !relevantSides[0].FromUnique || !relevantSides[1].ToUnique { + return false + } + + // Extract the columns names + colNames := make([]string, len(t.Columns)) + for i, c := range t.Columns { + colNames[i] = c.Name + } + + if !internal.AllColsInList( + colNames, + relevantSides[0].IgnoredColumns[1], relevantSides[0].ToColumns, + relevantSides[1].IgnoredColumns[0], relevantSides[1].FromColumns, + ) { + return false + } + + // These are the columns actually used in the relationship + // i.e. not ignored + relevantColumns := append( + relevantSides[0].ToColumns, + relevantSides[1].FromColumns..., + ) + + // Must have a unique constraint on all columns + return t.HasExactUnique(internal.RemoveDuplicates(relevantColumns)...) +} + +func (t Table[C, I]) UniqueColPairs() string { + ret := make([]string, 0, len(t.Constraints.Uniques)+1) + if t.Constraints.Primary != nil { + ret = append(ret, fmt.Sprintf("%#v", t.Constraints.Primary.Columns)) + } + + for _, unique := range t.Constraints.Uniques { + ret = append(ret, fmt.Sprintf("%#v", unique.Columns)) + } + + return strings.Join(ret, ", ") } diff --git a/gen/drivers/table_test.go b/gen/drivers/table_test.go index 8b35626e..3839b87e 100644 --- a/gen/drivers/table_test.go +++ b/gen/drivers/table_test.go @@ -7,11 +7,11 @@ import ( func TestGetTable(t *testing.T) { t.Parallel() - tables := []Table{ + tables := Tables[any, any]{ {Key: "one"}, } - tbl := GetTable(tables, "one") + tbl := tables.Get("one") if tbl.Key != "one" { t.Error("didn't get column") @@ -21,7 +21,7 @@ func TestGetTable(t *testing.T) { func TestGetTableMissing(t *testing.T) { t.Parallel() - tables := []Table{ + tables := Tables[any, any]{ {Key: "one"}, } @@ -31,13 +31,13 @@ func TestGetTableMissing(t *testing.T) { } }() - GetTable(tables, "missing") + tables.Get("missing") } func TestGetColumn(t *testing.T) { t.Parallel() - table := Table{ + table := Table[any, any]{ Columns: []Column{ {Name: "one"}, }, @@ -53,7 +53,7 @@ func TestGetColumn(t *testing.T) { func TestGetColumnMissing(t *testing.T) { t.Parallel() - table := Table{ + table := Table[any, any]{ Columns: []Column{ {Name: "one"}, }, @@ -88,7 +88,7 @@ func TestCanSoftDelete(t *testing.T) { } for i, test := range tests { - table := Table{ + table := Table[any, any]{ Columns: test.Columns, } diff --git a/gen/drivers/tables.go b/gen/drivers/tables.go new file mode 100644 index 00000000..0d82e5f8 --- /dev/null +++ b/gen/drivers/tables.go @@ -0,0 +1,347 @@ +package drivers + +import ( + "fmt" + "strings" + + "github.com/stephenafamo/bob/internal" + "github.com/stephenafamo/bob/orm" +) + +type Tables[C, I any] []Table[C, I] + +// GetTable by name. Panics if not found (for use in templates mostly). +func (tables Tables[C, I]) Get(name string) Table[C, I] { + for _, table := range tables { + if table.Key == name { + return table + } + } + + panic(fmt.Sprintf("could not find table name: %s", name)) +} + +func (tables Tables[C, I]) GetColumn(table string, column string) Column { + for _, t := range tables { + if t.Key != table { + continue + } + + return t.GetColumn(column) + } + + panic("unknown table " + table) +} + +func (tables Tables[C, I]) ColumnGetter(alias TableAlias, table, column string) string { + for _, t := range tables { + if t.Key != table { + continue + } + + col := t.GetColumn(column) + colAlias := alias.Column(column) + if !col.Nullable { + return colAlias + } + + return fmt.Sprintf("%s.GetOrZero()", colAlias) + } + + panic("unknown table " + table) +} + +type Importer interface{ Import(...string) string } + +func (tables Tables[C, I]) columnSetter(i Importer, aliases Aliases, fromTName, toTName, fromColName, toColName, varName string, fromOpt, toOpt bool) string { + fromTable := tables.Get(fromTName) + fromCol := fromTable.GetColumn(fromColName) + + toTable := tables.Get(toTName) + toCol := toTable.GetColumn(toColName) + to := fmt.Sprintf("%s.%s", varName, aliases[toTName].Columns[toColName]) + + switch { + case (fromOpt == toOpt) && (toCol.Nullable == fromCol.Nullable): + // If both type match, return it plainly + return to + + case !fromOpt && !fromCol.Nullable: + // if from is concrete, then use MustGet() + return fmt.Sprintf("%s.MustGet()", to) + + case fromOpt && fromCol.Nullable && !toOpt && !toCol.Nullable: + i.Import("github.com/aarondl/opt/omitnull") + return fmt.Sprintf("omitnull.From(%s)", to) + + case fromOpt && fromCol.Nullable && !toOpt && toCol.Nullable: + i.Import("github.com/aarondl/opt/omitnull") + return fmt.Sprintf("omitnull.FromNull(%s)", to) + + case fromOpt && fromCol.Nullable && toOpt && !toCol.Nullable: + i.Import("github.com/aarondl/opt/omitnull") + return fmt.Sprintf("omitnull.FromOmit(%s)", to) + + default: + // from is either omit or null + val := "omit" + if fromCol.Nullable { + val = "null" + } + + i.Import(fmt.Sprintf("github.com/aarondl/opt/%s", val)) + + switch { + case !toOpt && !toCol.Nullable: + return fmt.Sprintf("%s.From(%s)", val, to) + + default: + return fmt.Sprintf("%s.FromCond(%s.GetOrZero(), %s.IsSet())", val, to, to) + } + } +} + +func (tables Tables[C, I]) ColumnSetter(table, column string) bool { + for _, t := range tables { + if t.Key != table { + continue + } + + return t.CanSoftDelete(column) + } + + panic("unknown table " + table) +} + +func (tables Tables[C, I]) NeededBridgeRels(r orm.Relationship) []struct { + Table string + Position int + Many bool +} { + ma := []struct { + Table string + Position int + Many bool + }{} + + for _, side := range r.ValuedSides() { + if side.TableName == r.Local() { + continue + } + if side.TableName == r.Foreign() { + continue + } + if side.TableName == "" { + continue + } + + sideTable := tables.Get(side.TableName) + if sideTable.IsJoinTableForRel(r, side.Position) { + continue + } + + shouldAdd := false + + table := tables.Get(side.TableName) + for _, col := range table.Columns { + if col.Generated { + continue + } + if internal.InList(side.Columns(), col.Name) { + continue + } + + shouldAdd = true + break + } + + if !shouldAdd { + continue + } + + ma = append(ma, struct { + Table string + Position int + Many bool + }{ + Table: side.TableName, + Position: side.Position, + Many: r.NeedsMany(side.Position), + }) + + } + + return ma +} + +func (tables Tables[C, I]) RelArgs(aliases Aliases, r orm.Relationship) string { + ma := []string{} + for _, need := range tables.NeededBridgeRels(r) { + ma = append(ma, fmt.Sprintf( + "%s%d,", aliases[need.Table].DownSingular, need.Position, + )) + } + + return strings.Join(ma, "") +} + +func (tables Tables[C, I]) RelDependencies(aliases Aliases, r orm.Relationship, preSuf ...string) string { + var prefix, suffix string + if len(preSuf) > 0 { + prefix = preSuf[0] + } + if len(preSuf) > 1 { + suffix = preSuf[1] + } + ma := []string{} + for _, need := range tables.NeededBridgeRels(r) { + alias := aliases[need.Table] + ma = append(ma, fmt.Sprintf( + "%s *%s%s%s,", alias.DownSingular, alias.UpSingular, prefix, suffix, + )) + } + + return strings.Join(ma, "") +} + +func (tables Tables[C, I]) RelDependenciesPos(aliases Aliases, r orm.Relationship) string { + needed := tables.NeededBridgeRels(r) + ma := make([]string, len(needed)) + + for i, need := range needed { + alias := aliases[need.Table] + if need.Many { + ma[i] = fmt.Sprintf( + "%s%d %sSlice,", alias.DownPlural, need.Position, alias.UpSingular, + ) + } else { + ma[i] = fmt.Sprintf( + "%s%d *%s,", alias.DownSingular, need.Position, alias.UpSingular, + ) + } + } + + return strings.Join(ma, "") +} + +func (tables Tables[C, I]) RelDependenciesTyp(aliases Aliases, r orm.Relationship) string { + ma := []string{} + + for _, need := range tables.NeededBridgeRels(r) { + alias := aliases[need.Table] + ma = append(ma, fmt.Sprintf("%s *%sTemplate", alias.DownSingular, alias.UpSingular)) + } + + return strings.Join(ma, "\n") +} + +func (tables Tables[C, I]) RelDependenciesTypSet(aliases Aliases, r orm.Relationship) string { + ma := []string{} + + for _, need := range tables.NeededBridgeRels(r) { + alias := aliases[need.Table] + ma = append(ma, fmt.Sprintf("%s: %s,", alias.DownSingular, alias.DownSingular)) + } + + return strings.Join(ma, "\n") +} + +func (tables Tables[C, I]) SetFactoryDeps(i Importer, aliases Aliases, r orm.Relationship, inLoop bool) string { + local := r.Local() + foreign := r.Foreign() + ksides := r.ValuedSides() + + ret := make([]string, 0, len(ksides)) + for _, kside := range ksides { + switch kside.TableName { + case local, foreign: + default: + continue + } + + mret := make([]string, 0, len(kside.Mapped)) + + for _, mapp := range kside.Mapped { + switch mapp.ExternalTable { + case local, foreign: + default: + continue + } + + oalias := aliases[kside.TableName] + objVarName := getVarName(aliases, kside.TableName, kside.Start, kside.End, false) + + if mapp.Value != [2]string{} { + oGetter := tables.ColumnGetter(oalias, kside.TableName, mapp.Column) + + if kside.TableName == r.Local() { + i.Import("github.com/stephenafamo/bob/orm") + mret = append(mret, fmt.Sprintf(`if %s.%s != %s { + return &orm.RelationshipChainError{ + Table1: %q, Column1: %q, Value: %q, + } + }`, + objVarName, oGetter, mapp.Value[1], + kside.TableName, mapp.Column, mapp.Value[1], + )) + continue + } + + mret = append(mret, fmt.Sprintf(`%s.%s = %s`, + objVarName, + oalias.Columns[mapp.Column], + mapp.Value[1], + )) + continue + } + + extObjVarName := getVarName(aliases, mapp.ExternalTable, mapp.ExternalStart, mapp.ExternalEnd, false) + + oSetter := tables.columnSetter(i, aliases, + kside.TableName, mapp.ExternalTable, + mapp.Column, mapp.ExternalColumn, + extObjVarName, false, false) + + mret = append(mret, fmt.Sprintf(`%s.%s = %s`, + objVarName, + oalias.Columns[mapp.Column], + oSetter, + )) + } + + ret = append(ret, strings.Join(mret, "\n")) + } + + return strings.Join(ret, "\n") +} + +func (tables Tables[C, I]) RelIsView(rel orm.Relationship) bool { + for _, s := range rel.Sides { + t := tables.Get(s.To) + if t.Constraints.Primary == nil { + return true + } + } + + return false +} + +func getVarName(aliases Aliases, tableName string, local, foreign, many bool) string { + switch { + case foreign: + if many { + return "rels" + } + return "rel" + + case local: + return "o" + + default: + alias := aliases[tableName] + if many { + return alias.DownPlural + } + return alias.DownSingular + } +} diff --git a/gen/gen.go b/gen/gen.go index 2f20a5ce..28946864 100644 --- a/gen/gen.go +++ b/gen/gen.go @@ -28,22 +28,22 @@ var ( ) // State holds the global data needed by most pieces to run -type State struct { - Config Config +type State[ConstraintExtra any] struct { + Config Config[ConstraintExtra] Outputs []*Output CustomTemplateFuncs template.FuncMap } // Run executes the templates and outputs them to files based on the // state given. -func Run[T any](ctx context.Context, s *State, driver drivers.Interface[T], plugins ...Plugin) error { +func Run[T, C, I any](ctx context.Context, s *State[C], driver drivers.Interface[T, C, I], plugins ...Plugin) error { if driver.Dialect() == "" { return fmt.Errorf("no dialect specified") } // For StatePlugins for _, plugin := range plugins { - if statePlug, ok := plugin.(StatePlugin); ok { + if statePlug, ok := plugin.(StatePlugin[C]); ok { err := statePlug.PlugState(s) if err != nil { return fmt.Errorf("StatePlugin Error [%s]: %w", statePlug.Name(), err) @@ -64,7 +64,7 @@ func Run[T any](ctx context.Context, s *State, driver drivers.Interface[T], plug // For DBInfoPlugins for _, plugin := range plugins { - if dbPlug, ok := plugin.(DBInfoPlugin[T]); ok { + if dbPlug, ok := plugin.(DBInfoPlugin[T, C, I]); ok { err := dbPlug.PlugDBInfo(dbInfo) if err != nil { return fmt.Errorf("StatePlugin Error [%s]: %w", dbPlug.Name(), err) @@ -110,14 +110,14 @@ func Run[T any](ctx context.Context, s *State, driver drivers.Interface[T], plug } if s.Config.Aliases == nil { - s.Config.Aliases = make(map[string]TableAlias) + s.Config.Aliases = make(map[string]drivers.TableAlias) } initAliases(s.Config.Aliases, dbInfo.Tables, relationships) if err = s.initTags(); err != nil { return fmt.Errorf("unable to initialize struct tags: %w", err) } - data := &TemplateData[T]{ + data := &TemplateData[T, C, I]{ Dialect: driver.Dialect(), Tables: dbInfo.Tables, Enums: dbInfo.Enums, @@ -145,7 +145,7 @@ func Run[T any](ctx context.Context, s *State, driver drivers.Interface[T], plug // For TemplateDataPlugins for _, plugin := range plugins { - if tdPlug, ok := plugin.(TemplateDataPlugin[T]); ok { + if tdPlug, ok := plugin.(TemplateDataPlugin[T, C, I]); ok { err = tdPlug.PlugTemplateData(data) if err != nil { return fmt.Errorf("TemplateDataPlugin Error [%s]: %w", tdPlug.Name(), err) @@ -156,7 +156,7 @@ func Run[T any](ctx context.Context, s *State, driver drivers.Interface[T], plug return generate(s, data, version) } -func generate[T any](s *State, data *TemplateData[T], goVersion string) error { +func generate[T, C, I any](s *State[C], data *TemplateData[T, C, I], goVersion string) error { knownKeys := make(map[string]struct{}) templateByteBuffer := &bytes.Buffer{} templateHeaderByteBuffer := &bytes.Buffer{} @@ -260,7 +260,7 @@ func initInflections(i Inflections) { // initTags removes duplicate tags and validates the format // of all user tags are simple strings without quotes: [a-zA-Z_\.]+ -func (s *State) initTags() error { +func (s *State[C]) initTags() error { s.Config.Tags = strmangle.RemoveDuplicates(s.Config.Tags) for _, v := range s.Config.Tags { if !rgxValidTag.MatchString(v) { diff --git a/gen/gen_test.go b/gen/gen_test.go index eaa324e0..03adc125 100644 --- a/gen/gen_test.go +++ b/gen/gen_test.go @@ -7,7 +7,7 @@ import ( ) func TestProcessTypeReplacements(t *testing.T) { - tables := []drivers.Table{ + tables := drivers.Tables[any, any]{ { Columns: []drivers.Column{ { diff --git a/gen/output.go b/gen/output.go index 4829efad..9321288f 100644 --- a/gen/output.go +++ b/gen/output.go @@ -179,9 +179,9 @@ func (o *Output) initTemplates(funcs template.FuncMap, notests bool) ([]lazyTemp return lazyTemplates, nil } -type executeTemplateData[T any] struct { +type executeTemplateData[T, C, I any] struct { output *Output - data *TemplateData[T] + data *TemplateData[T, C, I] templates *templateList dirExtensions dirExtMap @@ -190,8 +190,8 @@ type executeTemplateData[T any] struct { } // generateOutput builds the file output and sends it to outHandler for saving -func generateOutput[T any](o *Output, dirExts dirExtMap, data *TemplateData[T], goVersion string) error { - return executeTemplates(executeTemplateData[T]{ +func generateOutput[T, C, I any](o *Output, dirExts dirExtMap, data *TemplateData[T, C, I], goVersion string) error { + return executeTemplates(executeTemplateData[T, C, I]{ output: o, data: data, templates: o.templates, @@ -200,8 +200,8 @@ func generateOutput[T any](o *Output, dirExts dirExtMap, data *TemplateData[T], } // generateTestOutput builds the test file output and sends it to outHandler for saving -func generateTestOutput[T any](o *Output, dirExts dirExtMap, data *TemplateData[T], goVersion string) error { - return executeTemplates(executeTemplateData[T]{ +func generateTestOutput[T, C, I any](o *Output, dirExts dirExtMap, data *TemplateData[T, C, I], goVersion string) error { + return executeTemplates(executeTemplateData[T, C, I]{ output: o, data: data, templates: o.testTemplates, @@ -212,8 +212,8 @@ func generateTestOutput[T any](o *Output, dirExts dirExtMap, data *TemplateData[ // generateSingletonOutput processes the templates that should only be run // one time. -func generateSingletonOutput[T any](o *Output, data *TemplateData[T], goVersion string) error { - return executeSingletonTemplates(executeTemplateData[T]{ +func generateSingletonOutput[T, C, I any](o *Output, data *TemplateData[T, C, I], goVersion string) error { + return executeSingletonTemplates(executeTemplateData[T, C, I]{ output: o, data: data, templates: o.templates, @@ -222,8 +222,8 @@ func generateSingletonOutput[T any](o *Output, data *TemplateData[T], goVersion // generateSingletonTestOutput processes the templates that should only be run // one time. -func generateSingletonTestOutput[T any](o *Output, data *TemplateData[T], goVersion string) error { - return executeSingletonTemplates(executeTemplateData[T]{ +func generateSingletonTestOutput[T, C, I any](o *Output, data *TemplateData[T, C, I], goVersion string) error { + return executeSingletonTemplates(executeTemplateData[T, C, I]{ output: o, data: data, templates: o.testTemplates, @@ -231,7 +231,7 @@ func generateSingletonTestOutput[T any](o *Output, data *TemplateData[T], goVers }, goVersion) } -func executeTemplates[T any](e executeTemplateData[T], goVersion string) error { +func executeTemplates[T, C, I any](e executeTemplateData[T, C, I], goVersion string) error { for dir, dirExts := range e.dirExtensions { for ext, tplNames := range dirExts { headerOut := e.output.templateHeaderByteBuffer @@ -291,7 +291,7 @@ func executeTemplates[T any](e executeTemplateData[T], goVersion string) error { return nil } -func executeSingletonTemplates[T any](e executeTemplateData[T], goVersion string) error { +func executeSingletonTemplates[T, C, I any](e executeTemplateData[T, C, I], goVersion string) error { headerOut := e.output.templateHeaderByteBuffer out := e.output.templateByteBuffer for _, tplName := range e.templates.Templates() { @@ -392,7 +392,7 @@ func writeFile(outFolder string, fileName string, input io.Reader, goVersion str // executeTemplate takes a template and returns the output of the template // execution. -func executeTemplate[T any](buf io.Writer, t *template.Template, name string, data *TemplateData[T]) (err error) { +func executeTemplate[T, C, I any](buf io.Writer, t *template.Template, name string, data *TemplateData[T, C, I]) (err error) { defer func() { if r := recover(); r != nil { err = fmt.Errorf("failed to execute template: %s\npanic: %+v\n", name, r) diff --git a/gen/plugin.go b/gen/plugin.go index aa3c35e9..806d15a6 100644 --- a/gen/plugin.go +++ b/gen/plugin.go @@ -7,23 +7,23 @@ type Plugin interface { } // This is called at the very beginning if there are any changes to be made to the state -type StatePlugin interface { +type StatePlugin[ConstraintExtra any] interface { Plugin - PlugState(*State) error + PlugState(*State[ConstraintExtra]) error } // DBInfoPlugin is called immediately after the database information // is assembled from the driver -type DBInfoPlugin[T any] interface { +type DBInfoPlugin[T, C, I any] interface { Plugin - PlugDBInfo(*drivers.DBInfo[T]) error + PlugDBInfo(*drivers.DBInfo[T, C, I]) error } // TemplateDataPlugin is called right after assembling the template data, before // generating them for each output. // NOTE: The PkgName field is overwritten for each output, so mofifying it in a plugin // will have no effect. Use a StatePlugin instead -type TemplateDataPlugin[T any] interface { +type TemplateDataPlugin[T, C, I any] interface { Plugin - PlugTemplateData(*TemplateData[T]) error + PlugTemplateData(*TemplateData[T, C, I]) error } diff --git a/gen/relationships.go b/gen/relationships.go index 76b2a8c1..7fc7dddf 100644 --- a/gen/relationships.go +++ b/gen/relationships.go @@ -5,6 +5,7 @@ import ( "strings" "github.com/stephenafamo/bob/gen/drivers" + "github.com/stephenafamo/bob/internal" "github.com/stephenafamo/bob/orm" ) @@ -13,7 +14,7 @@ const selfJoinSuffix = "__self_join_reverse" type Relationships map[string][]orm.Relationship // Set parameters of the relationship (unique, nullables, e.t.c.) -func (r Relationships) init(tables []drivers.Table) (err error) { +func initRelationships[C, I any](r Relationships, tables drivers.Tables[C, I]) (err error) { defer func() { if r := recover(); r != nil { err = fmt.Errorf("%v", r) @@ -23,15 +24,15 @@ func (r Relationships) init(tables []drivers.Table) (err error) { for tName, rels := range r { for i, rel := range rels { for j, side := range rel.Sides { - from := drivers.GetTable(tables, side.From) - to := drivers.GetTable(tables, side.To) + from := tables.Get(side.From) + to := tables.Get(side.To) // Set the uniqueness - r[tName][i].Sides[j].FromUnique = hasExactUnique( - from, side.FromColumns..., + r[tName][i].Sides[j].FromUnique = from.HasExactUnique( + side.FromColumns..., ) - r[tName][i].Sides[j].ToUnique = hasExactUnique( - to, side.ToColumns..., + r[tName][i].Sides[j].ToUnique = to.HasExactUnique( + side.ToColumns..., ) if side.Modify == "" { @@ -64,7 +65,7 @@ func (r Relationships) Get(table string) []orm.Relationship { } // GetInverse returns the Relationship of the other side -func (rs Relationships) GetInverse(tables []drivers.Table, r orm.Relationship) orm.Relationship { +func (rs Relationships) GetInverse(r orm.Relationship) orm.Relationship { frels, ok := rs[r.Foreign()] if !ok { return orm.Relationship{} @@ -88,16 +89,16 @@ func (rs Relationships) GetInverse(tables []drivers.Table, r orm.Relationship) o return orm.Relationship{} } -func buildRelationships(tables []drivers.Table) Relationships { +func buildRelationships[C, I any](tables []drivers.Table[C, I]) Relationships { relationships := map[string][]orm.Relationship{} - tableNameMap := make(map[string]drivers.Table, len(tables)) + tableNameMap := make(map[string]drivers.Table[C, I], len(tables)) for _, t := range tables { tableNameMap[t.Key] = t } for _, t1 := range tables { - isJoinTable := isJoinTable(t1) + isJoinTable := t1.IsJoinTable() // Build BelongsTo, ToOne and ToMany for _, fk := range t1.Constraints.Foreign { @@ -199,7 +200,7 @@ func buildRelationships(tables []drivers.Table) Relationships { return relationships } -func flipRelationships(relMap Relationships, tables []drivers.Table) error { +func flipRelationships[C, I any](relMap Relationships, tables []drivers.Table[C, I]) error { for _, rels := range relMap { RelsLoop: for _, rel := range rels { @@ -229,7 +230,7 @@ func flipRelationships(relMap Relationships, tables []drivers.Table) error { return nil } -func flipRelationship(r orm.Relationship, tables []drivers.Table) (orm.Relationship, error) { +func flipRelationship[C, I any](r orm.Relationship, tables []drivers.Table[C, I]) (orm.Relationship, error) { name := r.Name if r.Local() == r.Foreign() { name += selfJoinSuffix @@ -244,7 +245,7 @@ func flipRelationship(r orm.Relationship, tables []drivers.Table) (orm.Relations } for i, side := range r.Sides { - var from, to drivers.Table + var from, to drivers.Table[C, I] for _, t := range tables { if t.Key == side.From { from = t @@ -289,7 +290,7 @@ func flipRelationship(r orm.Relationship, tables []drivers.Table) (orm.Relations return flipped, nil } -func flipModify(side orm.RelSide, tables []drivers.Table) (string, error) { +func flipModify[C, I any](side orm.RelSide, tables []drivers.Table[C, I]) (string, error) { side.Modify = strings.ToLower(side.Modify) if side.Modify == "" { @@ -343,7 +344,7 @@ func mergeRelationship(src, extra orm.Relationship) orm.Relationship { } // Returns true if the table has a unique constraint on exactly these columns -func allNullable(t drivers.Table, cols ...string) bool { +func allNullable[C, I any](t drivers.Table[C, I], cols ...string) bool { foundNullable := 0 for _, col := range t.Columns { for _, cname := range cols { @@ -359,142 +360,12 @@ func allNullable(t drivers.Table, cols ...string) bool { return false } -// Returns true if the table has a unique constraint on exactly these columns -func hasExactUnique(t drivers.Table, cols ...string) bool { - if len(cols) == 0 { - return false - } - - // Primary keys are unique - if t.Constraints.Primary != nil && sliceMatch(t.Constraints.Primary.Columns, cols) { - return true - } - - // Check other unique constrints - for _, u := range t.Constraints.Uniques { - if sliceMatch(u.Columns, cols) { - return true - } - } - - return false -} - -func sliceMatch[T comparable, Ts ~[]T](a, b Ts) bool { - if len(a) != len(b) { - return false - } +func inferModify[C, I any](side orm.RelSide, tables drivers.Tables[C, I]) string { + t1 := tables.Get(side.From) + t2 := tables.Get(side.To) - if len(a) == 0 { - return false - } - - var matches int - for _, v1 := range a { - for _, v2 := range b { - if v1 == v2 { - matches++ - } - } - } - - return matches == len(a) -} - -// A composite primary key involving two columns -// Both primary key columns are also foreign keys -func isJoinTable(t drivers.Table) bool { - // Must have exactly 2 foreign keys - if len(t.Constraints.Foreign) != 2 { - return false - } - - // Extract the columns names - colNames := make([]string, len(t.Columns)) - for i, c := range t.Columns { - colNames[i] = c.Name - } - - // All columns must be contained in the foreign keys - if !allColsInList(colNames, t.Constraints.Foreign[0].Columns, t.Constraints.Foreign[1].Columns) { - return false - } - - // Must have a unique constraint on all columns - return hasExactUnique(t, colNames...) -} - -// Used in templates to know if the given table is a join table for this relationship -func isJoinTableForRel(t drivers.Table, r orm.Relationship, position int) bool { - if position == 0 || len(r.Sides) < 2 { - return false - } - - if position == len(r.Sides) { - return false - } - - if t.Key != r.Sides[position-1].To { - panic(fmt.Sprintf( - "table name does not match relationship position, expected %s got %s", - t.Key, r.Sides[position-1].To, - )) - } - - relevantSides := r.Sides[position-1 : position+1] - - // If the external mappings are not unique, it is not a join table - if !relevantSides[0].FromUnique || !relevantSides[1].ToUnique { - return false - } - - // Extract the columns names - colNames := make([]string, len(t.Columns)) - for i, c := range t.Columns { - colNames[i] = c.Name - } - - if !allColsInList( - colNames, - relevantSides[0].IgnoredColumns[1], relevantSides[0].ToColumns, - relevantSides[1].IgnoredColumns[0], relevantSides[1].FromColumns, - ) { - return false - } - - // These are the columns actually used in the relationship - // i.e. not ignored - relevantColumns := append( - relevantSides[0].ToColumns, - relevantSides[1].FromColumns..., - ) - - // Must have a unique constraint on all columns - return hasExactUnique(t, removeDuplicates(relevantColumns)...) -} - -func allColsInList(cols []string, lists ...[]string) bool { -ColumnsLoop: - for _, col := range cols { - for _, list := range lists { - for _, sideCol := range list { - if col == sideCol { - continue ColumnsLoop - } - } - } - return false - } - - return true -} - -func inferModify(side orm.RelSide, tables []drivers.Table) string { - t1 := drivers.GetTable(tables, side.From) - t2 := drivers.GetTable(tables, side.To) - - isT1PK := t1.Constraints.Primary != nil && sliceMatch(side.FromColumns, t1.Constraints.Primary.Columns) - isT2PK := t2.Constraints.Primary != nil && sliceMatch(side.ToColumns, t2.Constraints.Primary.Columns) + isT1PK := t1.Constraints.Primary != nil && internal.SliceMatch(side.FromColumns, t1.Constraints.Primary.Columns) + isT2PK := t2.Constraints.Primary != nil && internal.SliceMatch(side.ToColumns, t2.Constraints.Primary.Columns) switch { case isT1PK && !isT2PK: @@ -503,8 +374,8 @@ func inferModify(side orm.RelSide, tables []drivers.Table) string { return "from" } - isT1Unique := hasExactUnique(t1, side.FromColumns...) - isT2Unique := hasExactUnique(t2, side.ToColumns...) + isT1Unique := t1.HasExactUnique(side.FromColumns...) + isT2Unique := t2.HasExactUnique(side.ToColumns...) switch { case isT1Unique && !isT2Unique: @@ -518,7 +389,7 @@ func inferModify(side orm.RelSide, tables []drivers.Table) string { } // processRelationshipConfig checks any user included relationships and adds them to the tables -func processRelationshipConfig(config *Config, tables []drivers.Table, relMap Relationships) error { +func processRelationshipConfig[C, I any](config *Config[C], tables []drivers.Table[C, I], relMap Relationships) error { if len(tables) == 0 { return nil } @@ -537,7 +408,7 @@ func processRelationshipConfig(config *Config, tables []drivers.Table, relMap Re relMap[t.Key] = mergeRelationships(relMap[t.Key], rels) } - return relMap.init(tables) + return initRelationships(relMap, tables) } func validateRelationships(rels Relationships) error { @@ -582,19 +453,3 @@ func setColumns(relMap Relationships) { } } } - -func removeDuplicates[T comparable, Ts ~[]T](slice Ts) Ts { - seen := make(map[T]struct{}, len(slice)) - final := make(Ts, 0, len(slice)) - - for _, v := range slice { - if _, ok := seen[v]; ok { - continue - } - - seen[v] = struct{}{} - final = append(final, v) - } - - return final -} diff --git a/gen/relationships_test.go b/gen/relationships_test.go index 31f1765c..eaa58355 100644 --- a/gen/relationships_test.go +++ b/gen/relationships_test.go @@ -26,20 +26,22 @@ func TestJoinTable(t *testing.T) { } for i, test := range tests { - var table drivers.Table + var table drivers.Table[any, any] - table.Constraints.Primary = &drivers.PrimaryKey{Columns: test.Pkey} + table.Constraints.Primary = &drivers.Constraint[any]{Columns: test.Pkey} for _, col := range strmangle.SetMerge(test.Pkey, test.Fkey) { table.Columns = append(table.Columns, drivers.Column{Name: col}) } for _, k := range test.Fkey { table.Constraints.Foreign = append( table.Constraints.Foreign, - drivers.ForeignKey{Columns: []string{k}}, + drivers.ForeignKey[any]{ + Constraint: drivers.Constraint[any]{Columns: []string{k}}, + }, ) } - if isJoinTable(table) != test.Should { + if table.IsJoinTable() != test.Should { t.Errorf("%d) want: %t, got: %t\nTest: %#v", i, test.Should, !test.Should, test) } } diff --git a/gen/templates.go b/gen/templates.go index 1a5df72d..753eb961 100644 --- a/gen/templates.go +++ b/gen/templates.go @@ -13,7 +13,6 @@ import ( "github.com/Masterminds/sprig/v3" "github.com/stephenafamo/bob/gen/drivers" "github.com/stephenafamo/bob/gen/importers" - "github.com/stephenafamo/bob/orm" "github.com/volatiletech/strmangle" ) @@ -79,14 +78,14 @@ func (i Importer) ToList() importers.List { return list } -type TemplateData[T any] struct { +type TemplateData[T, C, I any] struct { Dialect string Importer Importer - Table drivers.Table - Tables []drivers.Table + Table drivers.Table[C, I] + Tables drivers.Tables[C, I] Enums []drivers.Enum - Aliases Aliases + Aliases drivers.Aliases Types drivers.Types Relationships Relationships @@ -118,7 +117,7 @@ type TemplateData[T any] struct { DriverName string } -func (t *TemplateData[T]) ResetImports() { +func (t *TemplateData[T, C, I]) ResetImports() { t.Importer = make(Importer) } @@ -226,8 +225,6 @@ func (a assetLoader) String() string { // //nolint:gochecknoglobals var templateFunctions = template.FuncMap{ - "getTable": drivers.GetTable, - "isJoinTable": isJoinTableForRel, "titleCase": strmangle.TitleCase, "ignore": strmangle.Ignore, "generateTags": strmangle.GenerateTags, @@ -246,23 +243,6 @@ var templateFunctions = template.FuncMap{ // Title case after doing unicode replacements or they will be stripped return strmangle.TitleCase(newval.String()) }, - "dbTag": func(t drivers.Table, c drivers.Column) string { - tag := c.Name - if t.Constraints.Primary != nil { - for _, pkc := range t.Constraints.Primary.Columns { - if pkc == c.Name { - tag += ",pk" - } - } - } - if c.Generated { - tag += ",generated" - } - if c.AutoIncr { - tag += ",autoincr" - } - return tag - }, "columnTagName": func(casing, name, alias string) string { switch casing { case "camel": @@ -275,8 +255,6 @@ var templateFunctions = template.FuncMap{ return name } }, - "columnGetter": columnGetter, - "getColumn": getColumn, "quoteAndJoin": func(s1, s2 string) string { if s1 == "" && s2 == "" { return "" @@ -292,365 +270,11 @@ var templateFunctions = template.FuncMap{ return fmt.Sprintf("%q, %q", s1, s2) }, - "isPrimitiveType": isPrimitiveType, - "uniqueColPairs": uniqueColPairs, - "neededBridgeRels": neededBridgeRels, - "relArgs": relArgs, - "relDependencies": relDependencies, - "relDependenciesPos": relDependenciesPos, - "relDependenciesTyp": relDependenciesTyp, - "relDependenciesTypSet": relDependenciesTypSet, - "relIsRequired": relIsRequired, - "setFactoryDeps": setFactoryDeps, - "relIsView": relIsView, - "relQueryMethodName": relQueryMethodName, -} - -func getColumn(t []drivers.Table, table string, a TableAlias, column string) drivers.Column { - for _, t := range t { - if t.Key != table { - continue - } - - return t.GetColumn(column) - } - - panic("unknown table " + table) -} - -func columnGetter(tables []drivers.Table, table string, a TableAlias, column string) string { - for _, t := range tables { - if t.Key != table { - continue - } - - col := t.GetColumn(column) - colAlias := a.Column(column) - if !col.Nullable { - return colAlias - } - - return fmt.Sprintf("%s.GetOrZero()", colAlias) - } - - panic("unknown table " + table) -} - -func columnSetter(i Importer, aliases Aliases, tables []drivers.Table, fromTName, toTName, fromColName, toColName, varName string, fromOpt, toOpt bool) string { - fromTable := drivers.GetTable(tables, fromTName) - fromCol := fromTable.GetColumn(fromColName) - - toTable := drivers.GetTable(tables, toTName) - toCol := toTable.GetColumn(toColName) - to := fmt.Sprintf("%s.%s", varName, aliases[toTName].Columns[toColName]) - - switch { - case (fromOpt == toOpt) && (toCol.Nullable == fromCol.Nullable): - // If both type match, return it plainly - return to - - case !fromOpt && !fromCol.Nullable: - // if from is concrete, then use MustGet() - return fmt.Sprintf("%s.MustGet()", to) - - case fromOpt && fromCol.Nullable && !toOpt && !toCol.Nullable: - i.Import("github.com/aarondl/opt/omitnull") - return fmt.Sprintf("omitnull.From(%s)", to) - - case fromOpt && fromCol.Nullable && !toOpt && toCol.Nullable: - i.Import("github.com/aarondl/opt/omitnull") - return fmt.Sprintf("omitnull.FromNull(%s)", to) - - case fromOpt && fromCol.Nullable && toOpt && !toCol.Nullable: - i.Import("github.com/aarondl/opt/omitnull") - return fmt.Sprintf("omitnull.FromOmit(%s)", to) - - default: - // from is either omit or null - val := "omit" - if fromCol.Nullable { - val = "null" - } - - i.Import(fmt.Sprintf("github.com/aarondl/opt/%s", val)) - - switch { - case !toOpt && !toCol.Nullable: - return fmt.Sprintf("%s.From(%s)", val, to) - - default: - return fmt.Sprintf("%s.FromCond(%s.GetOrZero(), %s.IsSet())", val, to, to) - } - } -} - -func relIsRequired(t drivers.Table, r orm.Relationship) bool { - // The relationship is not required, if its not using foreign keys - if r.NeverRequired { - return false - } - - firstSide := r.Sides[0] - if firstSide.Modify == "to" { - return false - } - - for _, colName := range firstSide.FromColumns { - if t.GetColumn(colName).Nullable { - return false - } - } - - return true -} - -func neededBridgeRels(tables []drivers.Table, aliases Aliases, r orm.Relationship) []struct { - Table string - Position int - Many bool -} { - ma := []struct { - Table string - Position int - Many bool - }{} - - for _, side := range r.ValuedSides() { - if side.TableName == r.Local() { - continue - } - if side.TableName == r.Foreign() { - continue - } - if side.TableName == "" { - continue - } - - sideTable := drivers.GetTable(tables, side.TableName) - if isJoinTableForRel(sideTable, r, side.Position) { - continue - } - - shouldAdd := false - - table := drivers.GetTable(tables, side.TableName) - for _, col := range table.Columns { - if col.Generated { - continue - } - if inList(side.Columns(), col.Name) { - continue - } - - shouldAdd = true - break - } - - if !shouldAdd { - continue - } - - ma = append(ma, struct { - Table string - Position int - Many bool - }{ - Table: side.TableName, - Position: side.Position, - Many: r.NeedsMany(side.Position), - }) - - } - - return ma -} - -func relArgs(tables []drivers.Table, aliases Aliases, r orm.Relationship) string { - ma := []string{} - for _, need := range neededBridgeRels(tables, aliases, r) { - ma = append(ma, fmt.Sprintf( - "%s%d,", aliases[need.Table].DownSingular, need.Position, - )) - } - - return strings.Join(ma, "") -} - -func relDependencies(tables []drivers.Table, aliases Aliases, r orm.Relationship, preSuf ...string) string { - var prefix, suffix string - if len(preSuf) > 0 { - prefix = preSuf[0] - } - if len(preSuf) > 1 { - suffix = preSuf[1] - } - ma := []string{} - for _, need := range neededBridgeRels(tables, aliases, r) { - alias := aliases[need.Table] - ma = append(ma, fmt.Sprintf( - "%s *%s%s%s,", alias.DownSingular, alias.UpSingular, prefix, suffix, - )) - } - - return strings.Join(ma, "") -} - -func relDependenciesPos(tables []drivers.Table, aliases Aliases, r orm.Relationship) string { - needed := neededBridgeRels(tables, aliases, r) - ma := make([]string, len(needed)) - - for i, need := range needed { - alias := aliases[need.Table] - if need.Many { - ma[i] = fmt.Sprintf( - "%s%d %sSlice,", alias.DownPlural, need.Position, alias.UpSingular, - ) - } else { - ma[i] = fmt.Sprintf( - "%s%d *%s,", alias.DownSingular, need.Position, alias.UpSingular, - ) - } - } - - return strings.Join(ma, "") -} - -func relDependenciesTyp(tables []drivers.Table, aliases Aliases, r orm.Relationship) string { - ma := []string{} - - for _, need := range neededBridgeRels(tables, aliases, r) { - alias := aliases[need.Table] - ma = append(ma, fmt.Sprintf("%s *%sTemplate", alias.DownSingular, alias.UpSingular)) - } - - return strings.Join(ma, "\n") -} - -func relDependenciesTypSet(tables []drivers.Table, aliases Aliases, r orm.Relationship) string { - ma := []string{} - - for _, need := range neededBridgeRels(tables, aliases, r) { - alias := aliases[need.Table] - ma = append(ma, fmt.Sprintf("%s: %s,", alias.DownSingular, alias.DownSingular)) - } - - return strings.Join(ma, "\n") -} - -func setFactoryDeps(i Importer, tables []drivers.Table, aliases Aliases, r orm.Relationship, inLoop bool) string { - local := r.Local() - foreign := r.Foreign() - ksides := r.ValuedSides() - - ret := make([]string, 0, len(ksides)) - for _, kside := range ksides { - switch kside.TableName { - case local, foreign: - default: - continue - } - - mret := make([]string, 0, len(kside.Mapped)) - - for _, mapp := range kside.Mapped { - switch mapp.ExternalTable { - case local, foreign: - default: - continue - } - - oalias := aliases[kside.TableName] - objVarName := getVarName(aliases, kside.TableName, kside.Start, kside.End, false) - - if mapp.Value != [2]string{} { - oGetter := columnGetter(tables, kside.TableName, oalias, mapp.Column) - - if kside.TableName == r.Local() { - i.Import("github.com/stephenafamo/bob/orm") - mret = append(mret, fmt.Sprintf(`if %s.%s != %s { - return &orm.RelationshipChainError{ - Table1: %q, Column1: %q, Value: %q, - } - }`, - objVarName, oGetter, mapp.Value[1], - kside.TableName, mapp.Column, mapp.Value[1], - )) - continue - } - - mret = append(mret, fmt.Sprintf(`%s.%s = %s`, - objVarName, - oalias.Columns[mapp.Column], - mapp.Value[1], - )) - continue - } - - extObjVarName := getVarName(aliases, mapp.ExternalTable, mapp.ExternalStart, mapp.ExternalEnd, false) - - oSetter := columnSetter(i, aliases, tables, - kside.TableName, mapp.ExternalTable, - mapp.Column, mapp.ExternalColumn, - extObjVarName, false, false) - - mret = append(mret, fmt.Sprintf(`%s.%s = %s`, - objVarName, - oalias.Columns[mapp.Column], - oSetter, - )) - } - - ret = append(ret, strings.Join(mret, "\n")) - } - - return strings.Join(ret, "\n") + "isPrimitiveType": isPrimitiveType, + "relQueryMethodName": relQueryMethodName, } -func getVarName(aliases Aliases, tableName string, local, foreign, many bool) string { - switch { - case foreign: - if many { - return "rels" - } - return "rel" - - case local: - return "o" - - default: - alias := aliases[tableName] - if many { - return alias.DownPlural - } - return alias.DownSingular - } -} - -func uniqueColPairs(t drivers.Table) string { - ret := make([]string, 0, len(t.Constraints.Uniques)+1) - if t.Constraints.Primary != nil { - ret = append(ret, fmt.Sprintf("%#v", t.Constraints.Primary.Columns)) - } - - for _, unique := range t.Constraints.Uniques { - ret = append(ret, fmt.Sprintf("%#v", unique.Columns)) - } - - return strings.Join(ret, ", ") -} - -func relIsView(tables []drivers.Table, rel orm.Relationship) bool { - for _, s := range rel.Sides { - t := drivers.GetTable(tables, s.To) - if t.Constraints.Primary == nil { - return true - } - } - - return false -} - -func relQueryMethodName(tAlias TableAlias, relAlias string) string { +func relQueryMethodName(tAlias drivers.TableAlias, relAlias string) string { for _, colAlias := range tAlias.Columns { // clash with field name if colAlias == relAlias { @@ -661,16 +285,6 @@ func relQueryMethodName(tAlias TableAlias, relAlias string) string { return relAlias } -func inList[T comparable](s []T, val T) bool { - for _, v := range s { - if v == val { - return true - } - } - - return false -} - func NormalizeType(val string) string { return typesReplacer.Replace(val) } diff --git a/gen/templates/factory/01_types.go.tpl b/gen/templates/factory/01_types.go.tpl index 1e0fb42d..25a1f31d 100644 --- a/gen/templates/factory/01_types.go.tpl +++ b/gen/templates/factory/01_types.go.tpl @@ -66,7 +66,7 @@ type {{$tAlias.DownSingular}}R{{$relAlias}}R struct{ number int {{- end}} o *{{$ftable.UpSingular}}Template - {{relDependenciesTyp $.Tables $.Aliases .}} + {{$.Tables.RelDependenciesTyp $.Aliases .}} } {{end}} diff --git a/gen/templates/factory/02_build.go.tpl b/gen/templates/factory/02_build.go.tpl index 53604c9f..19617821 100644 --- a/gen/templates/factory/02_build.go.tpl +++ b/gen/templates/factory/02_build.go.tpl @@ -7,7 +7,7 @@ func (t {{$tAlias.UpSingular}}Template) setModelRels(o *models.{{$tAlias.UpSingular}}) { {{- range $index, $rel := $.Relationships.Get $table.Key -}} {{- $relAlias := $tAlias.Relationship .Name -}} - {{- $invRel := $.Relationships.GetInverse $.Tables . -}} + {{- $invRel := $.Relationships.GetInverse . -}} {{- $ftable := $.Aliases.Table $rel.Foreign -}} {{- $invAlias := "" -}} {{- if and (not $.NoBackReferencing) $invRel.Name -}} @@ -24,12 +24,12 @@ func (t {{$tAlias.UpSingular}}Template) setModelRels(o *models.{{$tAlias.UpSingu rel.R.{{$invAlias}} = append(rel.R.{{$invAlias}}, o) {{- end}} {{- end}} - {{setFactoryDeps $.Importer $.Tables $.Aliases . false}} + {{$.Tables.SetFactoryDeps $.Importer $.Aliases . false}} {{- else -}} rel := models.{{$ftable.UpSingular}}Slice{} for _, r := range t.r.{{$relAlias}} { related := r.o.toModels(r.number) - {{- $setter := setFactoryDeps $.Importer $.Tables $.Aliases . false}} + {{- $setter := $.Tables.SetFactoryDeps $.Importer $.Aliases . false}} {{- if or $setter (and (not $.NoBackReferencing) $invRel.Name) }} for _, rel := range related { {{$setter}} diff --git a/gen/templates/factory/03_create.go.tpl b/gen/templates/factory/03_create.go.tpl index 14f43af5..121de56a 100644 --- a/gen/templates/factory/03_create.go.tpl +++ b/gen/templates/factory/03_create.go.tpl @@ -26,10 +26,10 @@ func ensureCreatable{{$tAlias.UpSingular}}(m *models.{{$tAlias.UpSingular}}Sette func (o *{{$tAlias.UpSingular}}Template) insertOptRels(ctx context.Context, exec bob.Executor, m *models.{{$tAlias.UpSingular}}) (context.Context,error) { var err error - {{range $index, $rel := $.Relationships.Get $table.Key -}}{{if not (relIsView $.Tables $rel) -}} - {{- if (relIsRequired $table $rel)}}{{continue}}{{end -}} + {{range $index, $rel := $.Relationships.Get $table.Key -}}{{if not ($.Tables.RelIsView $rel) -}} + {{- if ($table.RelIsRequired $rel)}}{{continue}}{{end -}} {{- $relAlias := $tAlias.Relationship .Name -}} - {{- $invRel := $.Relationships.GetInverse $.Tables . -}} + {{- $invRel := $.Relationships.GetInverse . -}} {{- $ftable := $.Aliases.Table $rel.Foreign -}} {{- $invAlias := "" -}} {{- if and (not $.NoBackReferencing) $invRel.Name -}} @@ -39,7 +39,7 @@ func (o *{{$tAlias.UpSingular}}Template) insertOptRels(ctx context.Context, exec if o.r.{{$relAlias}} != nil { {{- if .IsToMany -}} for _, r := range o.r.{{$relAlias}} { - {{- range neededBridgeRels $.Tables $.Aliases . -}} + {{- range $.Tables.NeededBridgeRels . -}} {{$alias := $.Aliases.Table .Table -}} {{if not .Many}} var {{$alias.DownSingular}}{{.Position}} *models.{{$alias.UpSingular}} @@ -59,13 +59,13 @@ func (o *{{$tAlias.UpSingular}}Template) insertOptRels(ctx context.Context, exec return ctx, err } - err = m.Attach{{$relAlias}}(ctx, exec, {{relArgs $.Tables $.Aliases $rel}} rel{{$index}}...) + err = m.Attach{{$relAlias}}(ctx, exec, {{$.Tables.RelArgs $.Aliases $rel}} rel{{$index}}...) if err != nil { return ctx, err } } {{- else -}} - {{- range neededBridgeRels $.Tables $.Aliases . -}} + {{- range $.Tables.NeededBridgeRels . -}} {{$alias := $.Aliases.Table .Table -}} {{if not .Many}} var {{$alias.DownSingular}}{{.Position}} *models.{{$alias.UpSingular}} @@ -84,7 +84,7 @@ func (o *{{$tAlias.UpSingular}}Template) insertOptRels(ctx context.Context, exec if err != nil { return ctx, err } - err = m.Attach{{$relAlias}}(ctx, exec, {{relArgs $.Tables $.Aliases $rel}} rel{{$index}}) + err = m.Attach{{$relAlias}}(ctx, exec, {{$.Tables.RelArgs $.Aliases $rel}} rel{{$index}}) if err != nil { return ctx, err } @@ -139,7 +139,7 @@ func (o *{{$tAlias.UpSingular}}Template) create(ctx context.Context, exec bob.Ex ensureCreatable{{$tAlias.UpSingular}}(opt) {{range $index, $rel := $.Relationships.Get $table.Key -}} - {{- if not (relIsRequired $table $rel)}}{{continue}}{{end -}} + {{- if not ($table.RelIsRequired $rel)}}{{continue}}{{end -}} {{- $ftable := $.Aliases.Table .Foreign -}} {{- $relAlias := $tAlias.Relationship .Name -}} var rel{{$index}} *models.{{$ftable.UpSingular}} @@ -176,7 +176,7 @@ func (o *{{$tAlias.UpSingular}}Template) create(ctx context.Context, exec bob.Ex {{range $index, $rel := $.Relationships.Get $table.Key -}} - {{- if not (relIsRequired $table $rel) -}}{{continue}}{{end -}} + {{- if not ($table.RelIsRequired $rel) -}}{{continue}}{{end -}} {{- $ftable := $.Aliases.Table .Foreign -}} {{- $relAlias := $tAlias.Relationship .Name -}} m.R.{{$relAlias}} = rel{{$index}} diff --git a/gen/templates/factory/12_rel_to_one_mods.go.tpl b/gen/templates/factory/12_rel_to_one_mods.go.tpl index fc4f43f1..c1a1faf1 100644 --- a/gen/templates/factory/12_rel_to_one_mods.go.tpl +++ b/gen/templates/factory/12_rel_to_one_mods.go.tpl @@ -6,24 +6,24 @@ {{- $ftable := $.Aliases.Table .Foreign -}} {{- $relAlias := $tAlias.Relationship .Name -}} -func (m {{$tAlias.DownSingular}}Mods) With{{$relAlias}}({{relDependencies $.Tables $.Aliases . "" "Template"}} rel *{{$ftable.UpSingular}}Template) {{$tAlias.UpSingular}}Mod { +func (m {{$tAlias.DownSingular}}Mods) With{{$relAlias}}({{$.Tables.RelDependencies $.Aliases . "" "Template"}} rel *{{$ftable.UpSingular}}Template) {{$tAlias.UpSingular}}Mod { return {{$tAlias.UpSingular}}ModFunc(func(o *{{$tAlias.UpSingular}}Template) { o.r.{{$relAlias}} = &{{$tAlias.DownSingular}}R{{$relAlias}}R{ o: rel, - {{relDependenciesTypSet $.Tables $.Aliases .}} + {{$.Tables.RelDependenciesTypSet $.Aliases .}} } }) } func (m {{$tAlias.DownSingular}}Mods) WithNew{{$relAlias}}(mods ...{{$ftable.UpSingular}}Mod) {{$tAlias.UpSingular}}Mod { return {{$tAlias.UpSingular}}ModFunc(func(o *{{$tAlias.UpSingular}}Template) { - {{range neededBridgeRels $.Tables $.Aliases . -}} + {{range $.Tables.NeededBridgeRels . -}} {{$alias := $.Aliases.Table .Table -}} {{$alias.DownSingular}}{{.Position}} := o.f.New{{$alias.UpSingular}}() {{end}} related := o.f.New{{$ftable.UpSingular}}(mods...) - m.With{{$relAlias}}({{relArgs $.Tables $.Aliases .}} related).Apply(o) + m.With{{$relAlias}}({{$.Tables.RelArgs $.Aliases .}} related).Apply(o) }) } diff --git a/gen/templates/factory/13_rel_to_many_mods.go.tpl b/gen/templates/factory/13_rel_to_many_mods.go.tpl index 49956965..f60c4928 100644 --- a/gen/templates/factory/13_rel_to_many_mods.go.tpl +++ b/gen/templates/factory/13_rel_to_many_mods.go.tpl @@ -7,47 +7,47 @@ {{- $relAlias := $tAlias.Relationship .Name -}} {{- $type := printf "*%sTemplate" $ftable.UpSingular -}} -func (m {{$tAlias.DownSingular}}Mods) With{{$relAlias}}(number int, {{relDependencies $.Tables $.Aliases . "" "Template"}} related {{$type}}) {{$tAlias.UpSingular}}Mod { +func (m {{$tAlias.DownSingular}}Mods) With{{$relAlias}}(number int, {{$.Tables.RelDependencies $.Aliases . "" "Template"}} related {{$type}}) {{$tAlias.UpSingular}}Mod { return {{$tAlias.UpSingular}}ModFunc(func(o *{{$tAlias.UpSingular}}Template) { o.r.{{$relAlias}} = []*{{$tAlias.DownSingular}}R{{$relAlias}}R{ { number: number, o: related, - {{relDependenciesTypSet $.Tables $.Aliases .}} + {{$.Tables.RelDependenciesTypSet $.Aliases .}} }} }) } func (m {{$tAlias.DownSingular}}Mods) WithNew{{$relAlias}}(number int, mods ...{{$ftable.UpSingular}}Mod) {{$tAlias.UpSingular}}Mod { return {{$tAlias.UpSingular}}ModFunc(func(o *{{$tAlias.UpSingular}}Template) { - {{range neededBridgeRels $.Tables $.Aliases . -}} + {{range $.Tables.NeededBridgeRels . -}} {{$alias := $.Aliases.Table .Table -}} {{$alias.DownSingular}}{{.Position}} := o.f.New{{$alias.UpSingular}}() {{end}} related := o.f.New{{$ftable.UpSingular}}(mods...) - m.With{{$relAlias}}(number, {{relArgs $.Tables $.Aliases .}} related).Apply(o) + m.With{{$relAlias}}(number, {{$.Tables.RelArgs $.Aliases .}} related).Apply(o) }) } -func (m {{$tAlias.DownSingular}}Mods) Add{{$relAlias}}(number int, {{relDependencies $.Tables $.Aliases . "" "Template"}} related {{$type}}) {{$tAlias.UpSingular}}Mod { +func (m {{$tAlias.DownSingular}}Mods) Add{{$relAlias}}(number int, {{$.Tables.RelDependencies $.Aliases . "" "Template"}} related {{$type}}) {{$tAlias.UpSingular}}Mod { return {{$tAlias.UpSingular}}ModFunc(func(o *{{$tAlias.UpSingular}}Template) { o.r.{{$relAlias}} = append(o.r.{{$relAlias}}, &{{$tAlias.DownSingular}}R{{$relAlias}}R{ number: number, o: related, - {{relDependenciesTypSet $.Tables $.Aliases .}} + {{$.Tables.RelDependenciesTypSet $.Aliases .}} }) }) } func (m {{$tAlias.DownSingular}}Mods) AddNew{{$relAlias}}(number int, mods ...{{$ftable.UpSingular}}Mod) {{$tAlias.UpSingular}}Mod { return {{$tAlias.UpSingular}}ModFunc(func(o *{{$tAlias.UpSingular}}Template) { - {{range neededBridgeRels $.Tables $.Aliases . -}} + {{range $.Tables.NeededBridgeRels . -}} {{$alias := $.Aliases.Table .Table -}} {{$alias.DownSingular}}{{.Position}} := o.f.New{{$alias.UpSingular}}() {{end}} related := o.f.New{{$ftable.UpSingular}}(mods...) - m.Add{{$relAlias}}(number, {{relArgs $.Tables $.Aliases .}} related).Apply(o) + m.Add{{$relAlias}}(number, {{$.Tables.RelArgs $.Aliases .}} related).Apply(o) }) } diff --git a/gen/templates/models/01_types.go.tpl b/gen/templates/models/01_types.go.tpl index 5d6503b4..274250c6 100644 --- a/gen/templates/models/01_types.go.tpl +++ b/gen/templates/models/01_types.go.tpl @@ -17,9 +17,9 @@ type {{$tAlias.UpSingular}} struct { // {{ . }} {{- end}}{{end -}} {{- if ignore $table.Key $orig_col_name $.TagIgnore}} - {{$colAlias}} {{$colTyp}} `db:"{{dbTag $table $column}}" {{generateIgnoreTags $.Tags | trim}}` + {{$colAlias}} {{$colTyp}} `db:"{{$table.DBTag $column}}" {{generateIgnoreTags $.Tags | trim}}` {{- else}}{{$tagName := columnTagName $.StructTagCasing $column.Name $colAlias}} - {{$colAlias}} {{$colTyp}} `db:"{{dbTag $table $column}}" {{generateTags $.Tags $tagName | trim}}` + {{$colAlias}} {{$colTyp}} `db:"{{$table.DBTag $column}}" {{generateTags $.Tags $tagName | trim}}` {{- end -}} {{- end -}} {{block "model/fields/additional" $}}{{end}} diff --git a/gen/templates/models/02_setter.go.tpl b/gen/templates/models/02_setter.go.tpl index 7345af13..04372252 100644 --- a/gen/templates/models/02_setter.go.tpl +++ b/gen/templates/models/02_setter.go.tpl @@ -20,9 +20,9 @@ type {{$tAlias.UpSingular}}Setter struct { {{- $colTyp = printf "omit.Val[%s]" $colTyp -}} {{- end -}} {{- if ignore $table.Key $orig_col_name $.TagIgnore}} - {{$colAlias}} {{$colTyp}} `db:"{{dbTag $table $column}}" {{generateIgnoreTags $.Tags | trim}}` + {{$colAlias}} {{$colTyp}} `db:"{{$table.DBTag $column}}" {{generateIgnoreTags $.Tags | trim}}` {{- else}}{{$tagName := columnTagName $.StructTagCasing $column.Name $colAlias}} - {{$colAlias}} {{$colTyp}} `db:"{{dbTag $table $column}}" {{generateTags $.Tags $tagName | trim}}` + {{$colAlias}} {{$colTyp}} `db:"{{$table.DBTag $column}}" {{generateTags $.Tags $tagName | trim}}` {{- end -}} {{end -}} } diff --git a/gen/templates/models/09_rel_query.go.tpl b/gen/templates/models/09_rel_query.go.tpl index 68465bec..7fd2768d 100644 --- a/gen/templates/models/09_rel_query.go.tpl +++ b/gen/templates/models/09_rel_query.go.tpl @@ -47,7 +47,7 @@ func {{$tAlias.DownPlural}}Join{{$relAlias}}[Q dialect.Joinable](from {{$tAlias. {{- $fromCols := printf "%sColumns" $from.UpSingular -}} {{- $to := $.Aliases.Table $side.To -}} {{- $toCols := printf "%sColumns" $to.UpSingular -}} - {{- $toTable := getTable $.Tables $side.To -}} + {{- $toTable := $.Tables.Get $side.To -}} { {{if ne $index 0 -}} from := {{$fromCols}}.AliasedAs({{$fromCols}}.Alias() + random) @@ -92,7 +92,7 @@ func (o *{{$tAlias.UpSingular}}) {{relQueryMethodName $tAlias $relAlias}}(mods . {{- $side := index $rel.Sides $index -}} {{- $from := $.Aliases.Table $side.From -}} {{- $to := $.Aliases.Table $side.To -}} - {{- $fromTable := getTable $.Tables $side.From -}} + {{- $fromTable := $.Tables.Get $side.From -}} {{- if gt $index 0 -}} sm.InnerJoin({{$from.UpPlural}}.NameAs()).On( {{end -}} @@ -152,7 +152,7 @@ func (os {{$tAlias.UpSingular}}Slice) {{relQueryMethodName $tAlias $relAlias}}(m {{- $side := index $rel.Sides $index -}} {{- $from := $.Aliases.Table $side.From -}} {{- $to := $.Aliases.Table $side.To -}} - {{- $fromTable := getTable $.Tables $side.From -}} + {{- $fromTable := $.Tables.Get $side.From -}} {{- if gt $index 0 -}} sm.InnerJoin({{$from.UpPlural}}.NameAs()).On( {{range $i, $local := $side.FromColumns -}} diff --git a/gen/templates/models/10_rel_load.go.tpl b/gen/templates/models/10_rel_load.go.tpl index e21ada2c..d3d9ec5f 100644 --- a/gen/templates/models/10_rel_load.go.tpl +++ b/gen/templates/models/10_rel_load.go.tpl @@ -16,7 +16,7 @@ func (o *{{$tAlias.UpSingular}}) Preload(name string, retrieved any) error { {{range $.Relationships.Get $table.Key -}} {{- $fAlias := $.Aliases.Table .Foreign -}} {{- $relAlias := $tAlias.Relationship .Name -}} - {{- $invRel := $.Relationships.GetInverse $.Tables . -}} + {{- $invRel := $.Relationships.GetInverse . -}} case "{{$relAlias}}": {{if .IsToMany -}} rels, ok := retrieved.({{$fAlias.UpSingular}}Slice) @@ -69,10 +69,10 @@ func (o *{{$tAlias.UpSingular}}) Preload(name string, retrieved any) error { {{range $rel := $.Relationships.Get $table.Key -}} -{{- $isToView := relIsView $.Tables $rel -}} +{{- $isToView := $.Tables.RelIsView $rel -}} {{- $fAlias := $.Aliases.Table $rel.Foreign -}} {{- $relAlias := $tAlias.Relationship $rel.Name -}} -{{- $invRel := $.Relationships.GetInverse $.Tables . -}} +{{- $invRel := $.Relationships.GetInverse . -}} {{- if not $rel.IsToMany -}} {{$.Importer.Import "github.com/stephenafamo/bob/orm"}} func Preload{{$tAlias.UpSingular}}{{$relAlias}}(opts ...{{$.Dialect}}.PreloadOption) {{$.Dialect}}.Preloader { @@ -83,8 +83,8 @@ func Preload{{$tAlias.UpSingular}}{{$relAlias}}(opts ...{{$.Dialect}}.PreloadOpt {{range $side := $rel.Sides -}} {{- $from := $.Aliases.Table $side.From -}} {{- $to := $.Aliases.Table $side.To -}} - {{- $fromTable := getTable $.Tables $side.From -}} - {{- $toTable = getTable $.Tables $side.To -}} + {{- $fromTable := $.Tables.Get $side.From -}} + {{- $toTable = $.Tables.Get $side.To -}} { From: TableNames.{{$from.UpPlural}}, To: TableNames.{{$to.UpPlural}}, @@ -221,8 +221,8 @@ func (os {{$tAlias.UpSingular}}Slice) Load{{$tAlias.UpSingular}}{{$relAlias}}(ct for _, rel := range {{$fAlias.DownPlural}} { {{range $index, $local := $side.FromColumns -}} {{- $foreign := index $side.ToColumns $index -}} - {{- $fromColGet := columnGetter $.Tables $side.From $fromAlias $local -}} - {{- $toColGet := columnGetter $.Tables $side.To $toAlias $foreign -}} + {{- $fromColGet := $.Tables.ColumnGetter $fromAlias $side.From $local -}} + {{- $toColGet := $.Tables.ColumnGetter $toAlias $side.To $foreign -}} if o.{{$fromColGet}} != rel.{{$toColGet}} { continue } @@ -279,7 +279,7 @@ func (os {{$tAlias.UpSingular}}Slice) Load{{$tAlias.UpSingular}}{{$relAlias}}(ct {{range $index, $local := $firstSide.FromColumns -}} {{- $fromColAlias := index $firstFrom.Columns $local -}} - {{- $fromCol := getColumn $.Tables $firstSide.From $firstFrom $local -}} + {{- $fromCol := $.Tables.GetColumn $firstSide.From $local -}} {{$fromColAlias}}Slice := []{{$fromCol.Type}}{} {{end}} @@ -288,7 +288,7 @@ func (os {{$tAlias.UpSingular}}Slice) Load{{$tAlias.UpSingular}}{{$relAlias}}(ct return func(row *scan.Row) (any, error) { {{range $index, $local := $firstSide.FromColumns -}} {{- $fromColAlias := index $firstFrom.Columns $local -}} - {{- $fromCol := getColumn $.Tables $firstSide.From $firstFrom $local -}} + {{- $fromCol := $.Tables.GetColumn $firstSide.From $local -}} {{$fromColAlias}}Slice = append({{$fromColAlias}}Slice, *new({{$fromCol.Type}})) row.ScheduleScan("related_{{$firstSide.From}}.{{$fromColAlias}}", &{{$fromColAlias}}Slice[len({{$fromColAlias}}Slice)-1]) {{end}} diff --git a/gen/templates/models/11_rel_ops.go.tpl b/gen/templates/models/11_rel_ops.go.tpl index e27d8a81..d42bb736 100644 --- a/gen/templates/models/11_rel_ops.go.tpl +++ b/gen/templates/models/11_rel_ops.go.tpl @@ -2,10 +2,10 @@ {{$table := .Table}} {{$tAlias := .Aliases.Table $table.Key -}} -{{range $rel := $.Relationships.Get $table.Key -}}{{if not (relIsView $.Tables $rel) -}} +{{range $rel := $.Relationships.Get $table.Key -}}{{if not ($.Tables.RelIsView $rel) -}} {{- $ftable := $.Aliases.Table $rel.Foreign -}} {{- $relAlias := $tAlias.Relationship $rel.Name -}} -{{- $invRel := $.Relationships.GetInverse $.Tables . -}} +{{- $invRel := $.Relationships.GetInverse . -}} {{- $from := printf "%s%d" $tAlias.DownSingular $rel.LocalPosition}} {{- $to := printf "%s%d" $ftable.DownSingular $rel.ForeignPosition}} {{if $rel.NeedsMany $rel.ForeignPosition -}} @@ -15,7 +15,7 @@ {{range $index, $side := reverse $valuedSides -}} - {{$sideTable := getTable $.Tables $side.TableName}} + {{$sideTable := $.Tables.Get $side.TableName}} {{$sideAlias := $.Aliases.Table $side.TableName}} @@ -53,7 +53,7 @@ {{end}} {{- else -}} {{$a := $.Aliases.Table .ExternalTable -}} - {{$t := getTable $.Tables .ExternalTable -}} + {{$t := $.Tables.Get .ExternalTable -}} {{$c := $t.GetColumn .ExternalColumn -}} {{$colVal := printf "%s%d.%s" $a.DownSingular $map.ExtPosition ($a.Column $map.ExternalColumn) -}} {{if $rel.NeedsMany .ExtPosition -}} @@ -91,7 +91,7 @@ func attach{{$tAlias.UpSingular}}{{$relAlias}}{{$index}}(ctx context.Context, exec bob.Executor, count int - {{- if not (isJoinTable $sideTable $rel $side.Position) -}} + {{- if not ($sideTable.IsJoinTableForRel $rel $side.Position) -}} {{- if $rel.NeedsMany $side.Position -}} , {{$sideAlias.DownPlural}}{{$side.Position}} {{$sideAlias.UpSingular}}Slice {{- else -}} @@ -108,7 +108,7 @@ {{- end -}} ) ({{if $rel.NeedsMany $side.Position}}{{$sideAlias.UpSingular}}Slice{{else}}*{{$sideAlias.UpSingular}}{{end}}, error) { {{- $uniqueEnd := and $side.End (not (index $rel.Sides (sub $side.Position 1)).ToUnique) -}} - {{- $needsIndividualUpdate := (and (not $uniqueEnd) ($rel.NeedsMany $side.Position) (not (isJoinTable $sideTable $rel $side.Position))) -}} + {{- $needsIndividualUpdate := (and (not $uniqueEnd) ($rel.NeedsMany $side.Position) (not ($sideTable.IsJoinTableForRel $rel $side.Position))) -}} {{if $needsIndividualUpdate}} for i := range {{$sideAlias.DownPlural}}{{$side.Position}} { setter := &{{$sideAlias.UpSingular}}Setter{ @@ -116,7 +116,7 @@ {{$colName := $sideAlias.Column $map.Column -}} {{$sideColumn := $sideTable.GetColumn .Column -}} {{$tableAlias := $.Aliases.Table .ExternalTable -}} - {{$table := getTable $.Tables .ExternalTable -}} + {{$table := $.Tables.Get .ExternalTable -}} {{$column := $table.GetColumn .ExternalColumn -}} {{if $rel.NeedsMany .ExtPosition -}} {{if .HasValue -}} @@ -181,7 +181,7 @@ {{end}} {{if $needsBulkUpdate -}} - {{if and ($rel.NeedsMany $side.Position) (isJoinTable $sideTable $rel $side.Position) -}} + {{if and ($rel.NeedsMany $side.Position) ($sideTable.IsJoinTableForRel $rel $side.Position) -}} setters := make([]*{{$sideAlias.UpSingular}}Setter, count) for i := 0; i < count; i++ { setters[i] = &{{$sideAlias.UpSingular}}Setter{ @@ -202,7 +202,7 @@ {{end}} {{- else -}} {{$a := $.Aliases.Table .ExternalTable -}} - {{$t := getTable $.Tables .ExternalTable -}} + {{$t := $.Tables.Get .ExternalTable -}} {{$c := $t.GetColumn .ExternalColumn -}} {{$colVal := printf "%s%d.%s" $a.DownSingular $map.ExtPosition ($a.Column $map.ExternalColumn) -}} {{if $rel.NeedsMany .ExtPosition -}} @@ -225,9 +225,9 @@ {{- end}} {{- end}} } - {{if and ($rel.NeedsMany $side.Position) (isJoinTable $sideTable $rel $side.Position) -}}}{{end}} + {{if and ($rel.NeedsMany $side.Position) ($sideTable.IsJoinTableForRel $rel $side.Position) -}}}{{end}} - {{if (isJoinTable $sideTable $rel $side.Position) -}} + {{if ($sideTable.IsJoinTableForRel $rel $side.Position) -}} {{if $rel.NeedsMany $side.Position -}} {{$sideAlias.DownPlural}}{{$side.Position}}, err := {{$sideAlias.UpPlural}}.Insert(bob.ToMods(setters...)).All(ctx, exec) {{- else -}} @@ -256,7 +256,7 @@ {{end}} {{if not $rel.IsToMany -}} - func ({{$from}} *{{$tAlias.UpSingular}}) Insert{{$relAlias}}(ctx context.Context, exec bob.Executor,{{relDependenciesPos $.Tables $.Aliases $rel}} related *{{$ftable.UpSingular}}Setter) error { + func ({{$from}} *{{$tAlias.UpSingular}}) Insert{{$relAlias}}(ctx context.Context, exec bob.Executor,{{$.Tables.RelDependenciesPos $.Aliases $rel}} related *{{$ftable.UpSingular}}Setter) error { {{if $rel.InsertEarly -}} {{$to}}, err := {{$ftable.UpPlural}}.Insert(related).One(ctx, exec) if err != nil { @@ -265,7 +265,7 @@ {{end}} {{range $index, $side := (reverse $valuedSides) -}} - {{$sideTable := getTable $.Tables $side.TableName}} + {{$sideTable := $.Tables.Get $side.TableName}} {{$sideAlias := $.Aliases.Table $side.TableName}} {{if eq $side.Position $rel.ForeignPosition -}} @@ -281,9 +281,9 @@ {{- else -}} _, err {{- end -}} - {{- if and $show (isJoinTable $sideTable $rel $side.Position) -}}:{{- end -}} + {{- if and $show ($sideTable.IsJoinTableForRel $rel $side.Position) -}}:{{- end -}} = attach{{$tAlias.UpSingular}}{{$relAlias}}{{$index}}(ctx, exec, 1 - {{- if not (isJoinTable $sideTable $rel $side.Position) -}} + {{- if not ($sideTable.IsJoinTableForRel $rel $side.Position) -}} , {{$sideAlias.DownSingular}}{{$side.Position}} {{- end -}} {{- end}} @@ -312,11 +312,11 @@ return nil } - func ({{$from}} *{{$tAlias.UpSingular}}) Attach{{$relAlias}}(ctx context.Context, exec bob.Executor,{{relDependenciesPos $.Tables $.Aliases $rel}} {{$to}} *{{$ftable.UpSingular}}) error { + func ({{$from}} *{{$tAlias.UpSingular}}) Attach{{$relAlias}}(ctx context.Context, exec bob.Executor,{{$.Tables.RelDependenciesPos $.Aliases $rel}} {{$to}} *{{$ftable.UpSingular}}) error { var err error {{range $index, $side := (reverse $valuedSides) -}} - {{$sideTable := getTable $.Tables $side.TableName}} + {{$sideTable := $.Tables.Get $side.TableName}} {{$sideAlias := $.Aliases.Table $side.TableName}} {{$show := lt $index (len $valuedSides | add -1)}} {{if $show -}} @@ -328,9 +328,9 @@ {{- else -}} _, err {{- end -}} - {{- if and $show (isJoinTable $sideTable $rel $side.Position) -}}:{{- end -}} + {{- if and $show ($sideTable.IsJoinTableForRel $rel $side.Position) -}}:{{- end -}} = attach{{$tAlias.UpSingular}}{{$relAlias}}{{$index}}(ctx, exec, 1 - {{- if not (isJoinTable $sideTable $rel $side.Position) -}} + {{- if not ($sideTable.IsJoinTableForRel $rel $side.Position) -}} , {{$sideAlias.DownSingular}}{{$side.Position}} {{- end -}} {{- range $map := $side.UniqueExternals -}} @@ -359,7 +359,7 @@ } {{else -}} - func ({{$from}} *{{$tAlias.UpSingular}}) Insert{{$relAlias}}(ctx context.Context, exec bob.Executor,{{relDependenciesPos $.Tables $.Aliases $rel}} related ...*{{$ftable.UpSingular}}Setter) error { + func ({{$from}} *{{$tAlias.UpSingular}}) Insert{{$relAlias}}(ctx context.Context, exec bob.Executor,{{$.Tables.RelDependenciesPos $.Aliases $rel}} related ...*{{$ftable.UpSingular}}Setter) error { if len(related) == 0 { return nil } @@ -375,7 +375,7 @@ {{end}} {{range $index, $side := (reverse $valuedSides) -}} - {{$sideTable := getTable $.Tables $side.TableName}} + {{$sideTable := $.Tables.Get $side.TableName}} {{$sideAlias := $.Aliases.Table $side.TableName}} {{if eq $side.Position $rel.ForeignPosition -}} {{$to}}, err := insert{{$tAlias.UpSingular}}{{$relAlias}}{{$index}}(ctx, exec, related @@ -390,9 +390,9 @@ {{- else -}} _, err {{- end -}} - {{- if and $show (isJoinTable $sideTable $rel $side.Position) -}}:{{- end -}} + {{- if and $show ($sideTable.IsJoinTableForRel $rel $side.Position) -}}:{{- end -}} = attach{{$tAlias.UpSingular}}{{$relAlias}}{{$index}}(ctx, exec, len(related) - {{- if not (isJoinTable $sideTable $rel $side.Position) -}} + {{- if not ($sideTable.IsJoinTableForRel $rel $side.Position) -}} , {{if $rel.NeedsMany $side.Position}}{{$sideAlias.DownPlural}}{{else}}{{$sideAlias.DownSingular}}{{end}}{{$side.Position}} {{- end -}} {{- end}} @@ -423,7 +423,7 @@ } - func ({{$from}} *{{$tAlias.UpSingular}}) Attach{{$relAlias}}(ctx context.Context, exec bob.Executor,{{relDependenciesPos $.Tables $.Aliases $rel}} related ...*{{$ftable.UpSingular}}) error { + func ({{$from}} *{{$tAlias.UpSingular}}) Attach{{$relAlias}}(ctx context.Context, exec bob.Executor,{{$.Tables.RelDependenciesPos $.Aliases $rel}} related ...*{{$ftable.UpSingular}}) error { if len(related) == 0 { return nil } @@ -432,7 +432,7 @@ {{$to}} := {{$ftable.UpSingular}}Slice(related) {{range $index, $side := (reverse $valuedSides) -}} - {{$sideTable := getTable $.Tables $side.TableName}} + {{$sideTable := $.Tables.Get $side.TableName}} {{$sideAlias := $.Aliases.Table $side.TableName}} {{$show := lt $index (len $valuedSides | add -1)}} {{if $show -}} @@ -444,9 +444,9 @@ {{- else -}} _, err {{- end -}} - {{- if and $show (isJoinTable $sideTable $rel $side.Position) -}}:{{- end -}} + {{- if and $show ($sideTable.IsJoinTableForRel $rel $side.Position) -}}:{{- end -}} = attach{{$tAlias.UpSingular}}{{$relAlias}}{{$index}}(ctx, exec, len(related) - {{- if not (isJoinTable $sideTable $rel $side.Position) -}} + {{- if not ($sideTable.IsJoinTableForRel $rel $side.Position) -}} , {{if $rel.NeedsMany $side.Position}}{{$sideAlias.DownPlural}}{{else}}{{$sideAlias.DownSingular}}{{end}}{{$side.Position}} {{- end -}} {{- range $map := $side.UniqueExternals -}} diff --git a/gen/types.go b/gen/types.go index 9d9789ad..2b2debaf 100644 --- a/gen/types.go +++ b/gen/types.go @@ -20,7 +20,7 @@ func isPrimitiveType(name string) bool { // processTypeReplacements checks the config for type replacements // and performs them. -func processTypeReplacements(types map[string]drivers.Type, replacements []Replace, tables []drivers.Table) { +func processTypeReplacements[C, I any](types map[string]drivers.Type, replacements []Replace, tables []drivers.Table[C, I]) { for _, r := range replacements { didMatch := false for i := range tables { @@ -106,7 +106,7 @@ func matchColumn(c, m drivers.Column) bool { // shouldReplaceInTable checks if tables were specified in types.match in the config. // If tables were set, it checks if the given table is among the specified tables. -func shouldReplaceInTable(t drivers.Table, r Replace) bool { +func shouldReplaceInTable[C, I any](t drivers.Table[C, I], r Replace) bool { if len(r.Tables) == 0 { return true } diff --git a/internal/utils.go b/internal/utils.go index 4b8d7fd6..dec46baf 100644 --- a/internal/utils.go +++ b/internal/utils.go @@ -47,3 +47,67 @@ func RandInt() int64 { return out % 10000 } + +func SliceMatch[T comparable, Ts ~[]T](a, b Ts) bool { + if len(a) != len(b) { + return false + } + + if len(a) == 0 { + return false + } + + var matches int + for _, v1 := range a { + for _, v2 := range b { + if v1 == v2 { + matches++ + } + } + } + + return matches == len(a) +} + +// Check if any one of the lists contains all the columns +func AllColsInList(cols []string, lists ...[]string) bool { +ColumnsLoop: + for _, col := range cols { + for _, list := range lists { + for _, sideCol := range list { + if col == sideCol { + continue ColumnsLoop + } + } + } + return false + } + + return true +} + +func RemoveDuplicates[T comparable, Ts ~[]T](slice Ts) Ts { + seen := make(map[T]struct{}, len(slice)) + final := make(Ts, 0, len(slice)) + + for _, v := range slice { + if _, ok := seen[v]; ok { + continue + } + + seen[v] = struct{}{} + final = append(final, v) + } + + return final +} + +func InList[T comparable](s []T, val T) bool { + for _, v := range s { + if v == val { + return true + } + } + + return false +} diff --git a/test/gen/gen.go b/test/gen/gen.go index bbdacb77..4e91addd 100644 --- a/test/gen/gen.go +++ b/test/gen/gen.go @@ -26,15 +26,15 @@ const module = "github.com/stephenafamo/bob/orm/bob-gen-test" var rgxHasSpaces = regexp.MustCompile(`^\s+`) -type driverWrapper[T any] struct { - drivers.Interface[T] - info *drivers.DBInfo[T] +type driverWrapper[T, C, I any] struct { + drivers.Interface[T, C, I] + info *drivers.DBInfo[T, C, I] overwriteGolden bool goldenFile string once sync.Once } -func (d *driverWrapper[T]) Assemble(context.Context) (*drivers.DBInfo[T], error) { +func (d *driverWrapper[T, C, I]) Assemble(context.Context) (*drivers.DBInfo[T, C, I], error) { var err error d.once.Do(func() { @@ -48,7 +48,7 @@ func (d *driverWrapper[T]) Assemble(context.Context) (*drivers.DBInfo[T], error) return d.info, nil } -func (d *driverWrapper[T]) TestAssemble(t *testing.T) { +func (d *driverWrapper[T, C, I]) TestAssemble(t *testing.T) { t.Helper() _, err := d.Assemble(context.Background()) @@ -85,18 +85,18 @@ func (d *driverWrapper[T]) TestAssemble(t *testing.T) { } } -type DriverTestConfig[T any] struct { +type DriverTestConfig[T, C, I any] struct { Root string Templates *helpers.Templates OverwriteGolden bool GoldenFile string - GetDriver func() drivers.Interface[T] + GetDriver func() drivers.Interface[T, C, I] } -func TestDriver[T any](t *testing.T, config DriverTestConfig[T]) { +func TestDriver[T, C, I any](t *testing.T, config DriverTestConfig[T, C, I]) { t.Helper() - var aliases gen.Aliases + var aliases drivers.Aliases defaultFolder := filepath.Join(config.Root, "default") err := os.Mkdir(defaultFolder, os.ModePerm) @@ -104,7 +104,7 @@ func TestDriver[T any](t *testing.T, config DriverTestConfig[T]) { t.Fatalf("unable to create default folder: %s", err) } - d := &driverWrapper[T]{ + d := &driverWrapper[T, C, I]{ Interface: config.GetDriver(), overwriteGolden: config.OverwriteGolden, goldenFile: config.GoldenFile, @@ -131,10 +131,10 @@ func TestDriver[T any](t *testing.T, config DriverTestConfig[T]) { t.Fatalf("go env GOMOD cmd execution failed: %s", "not in a go module") } - aliaser := &aliasPlugin[T]{} + aliaser := &aliasPlugin[T, C, I]{} t.Run("generate", func(t *testing.T) { - testDriver(t, defaultFolder, config.Templates, gen.Config{}, d, goModFilePath, aliaser) + testDriver(t, defaultFolder, config.Templates, gen.Config[C]{}, d, goModFilePath, aliaser) }) aliasesFolder := filepath.Join(config.Root, "aliases") @@ -144,11 +144,11 @@ func TestDriver[T any](t *testing.T, config DriverTestConfig[T]) { } t.Run("generate with aliases", func(t *testing.T) { - testDriver(t, aliasesFolder, config.Templates, gen.Config{Aliases: aliases}, d, goModFilePath, aliaser) + testDriver(t, aliasesFolder, config.Templates, gen.Config[C]{Aliases: aliases}, d, goModFilePath, aliaser) }) } -func testDriver[T any](t *testing.T, dst string, tpls *helpers.Templates, config gen.Config, d drivers.Interface[T], modPath string, plugins ...gen.Plugin) { +func testDriver[T, C, I any](t *testing.T, dst string, tpls *helpers.Templates, config gen.Config[C], d drivers.Interface[T, C, I], modPath string, plugins ...gen.Plugin) { t.Helper() buf := &bytes.Buffer{} @@ -177,7 +177,7 @@ func testDriver[T any](t *testing.T, dst string, tpls *helpers.Templates, config if err := gen.Run( context.Background(), - &gen.State{Config: config, Outputs: outputs}, + &gen.State[C]{Config: config, Outputs: outputs}, d, plugins..., ); err != nil { t.Fatalf("Unable to execute State.Run: %s", err) @@ -282,23 +282,23 @@ func outputCompileErrors(buf *bytes.Buffer, outFolder string) { } } -type aliasPlugin[T any] struct { - tables []drivers.Table +type aliasPlugin[T, C, I any] struct { + tables drivers.Tables[C, I] rels gen.Relationships } -func (a *aliasPlugin[T]) Name() string { +func (a *aliasPlugin[T, C, I]) Name() string { return "aliaser" } -func (a *aliasPlugin[T]) PlugState(s *gen.State) error { +func (a *aliasPlugin[T, C, I]) PlugState(s *gen.State[C]) error { if a.rels == nil || len(a.tables) == 0 { return nil } - aliases := make(map[string]gen.TableAlias, len(a.tables)) + aliases := make(map[string]drivers.TableAlias, len(a.tables)) for i, table := range a.tables { - tableAlias := gen.TableAlias{ + tableAlias := drivers.TableAlias{ UpPlural: fmt.Sprintf("Alias%dThings", i), UpSingular: fmt.Sprintf("Alias%dThing", i), DownPlural: fmt.Sprintf("alias%dThings", i), @@ -323,7 +323,7 @@ func (a *aliasPlugin[T]) PlugState(s *gen.State) error { return nil } -func (a *aliasPlugin[T]) PlugTemplateData(data *gen.TemplateData[any]) error { +func (a *aliasPlugin[T, C, I]) PlugTemplateData(data *gen.TemplateData[T, C, I]) error { a.tables = data.Tables a.rels = data.Relationships return nil