diff --git a/CHANGELOG.md b/CHANGELOG.md index 53bf845..6eda893 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -59,6 +59,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added the `Case()` starter to all dialects to build `CASE` expressions. (thanks @k4n4ry) - Added `bob.Named()` which is used to add named arguments to the query and bind them later. - Added `bob.BindNamed` which takes an argument (struct, map, or a single value type) to be used to bind named arguments in a query. See changes to `bob.Prepare()` for details of which type can be used. +- Indexes now include more information such as the type, unique and comment fields. +- Constraints now include a comment field. +- Added `Checks` field to DBConstraints so that drivers can also load check constraints. (not yet supported by the SQLite driver). +- Added comments field to Table definitions. ### Changed @@ -87,6 +91,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - A primitive type (int, bool, string, etc) - `time.Time` - Any type that implements `driver.Valuer`. +- `Index` columns are no longer just strings, but are a struct to include more information such as the sort order. ### Removed @@ -100,6 +105,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `context.Context` and `bob.Executor` are no longer passed when creating a Table/ViewQuery. It is now passed at the point of execution with `Exec/One/All/Cursor`. - Remove `Prepare` methods from table and view qureries. Since `bob.Prepare()` now takes a type parameter, it is not possible to prepare from a method since Go does not allow additional type parameters in methods. - Removed the **Prisma** and **Atlas** code generation drivers. It is better for Bob to focus on being able to generate code from the database in the most robust and detailed way and if the user wants, they can use other tools (such as prisma and atlas) to manage migrations before the code generation. +- Removed `Expressions` from Index definitions. It is now merged with the `Columns` field with an `IsExpression` field to indicate if the column is an expression. ### Fixed diff --git a/gen/bobgen-mysql/driver/exclude-tables.golden.json b/gen/bobgen-mysql/driver/exclude-tables.golden.json index 443e106..d8df1fb 100644 --- a/gen/bobgen-mysql/driver/exclude-tables.golden.json +++ b/gen/bobgen-mysql/driver/exclude-tables.golden.json @@ -19,11 +19,17 @@ ], "indexes": [ { + "type": "BTREE", "name": "PRIMARY", "columns": [ - "id" + { + "name": "id", + "desc": false, + "is_expression": false + } ], - "expressions": null, + "unique": true, + "comment": "", "extra": null } ], @@ -33,11 +39,14 @@ "columns": [ "id" ], + "comment": "", "extra": null }, "foreign": null, - "uniques": null - } + "uniques": null, + "check": null + }, + "comment": "" }, { "key": "bar_qux", @@ -58,11 +67,17 @@ ], "indexes": [ { + "type": "BTREE", "name": "PRIMARY", "columns": [ - "id" + { + "name": "id", + "desc": false, + "is_expression": false + } ], - "expressions": null, + "unique": true, + "comment": "", "extra": null } ], @@ -72,11 +87,14 @@ "columns": [ "id" ], + "comment": "", "extra": null }, "foreign": null, - "uniques": null - } + "uniques": null, + "check": null + }, + "comment": "" }, { "key": "foo_qux", @@ -97,11 +115,17 @@ ], "indexes": [ { + "type": "BTREE", "name": "PRIMARY", "columns": [ - "id" + { + "name": "id", + "desc": false, + "is_expression": false + } ], - "expressions": null, + "unique": true, + "comment": "", "extra": null } ], @@ -111,11 +135,14 @@ "columns": [ "id" ], + "comment": "", "extra": null }, "foreign": null, - "uniques": null - } + "uniques": null, + "check": null + }, + "comment": "" }, { "key": "multi_keys", @@ -202,37 +229,69 @@ ], "indexes": [ { + "type": "BTREE", "name": "one", "columns": [ - "one", - "two" + { + "name": "one", + "desc": false, + "is_expression": false + }, + { + "name": "two", + "desc": false, + "is_expression": false + } ], - "expressions": null, + "unique": false, + "comment": "", "extra": null }, { + "type": "BTREE", "name": "PRIMARY", "columns": [ - "id" + { + "name": "id", + "desc": false, + "is_expression": false + } ], - "expressions": null, + "unique": true, + "comment": "", "extra": null }, { + "type": "BTREE", "name": "something", "columns": [ - "something", - "another" + { + "name": "something", + "desc": false, + "is_expression": false + }, + { + "name": "another", + "desc": false, + "is_expression": false + } ], - "expressions": null, + "unique": true, + "comment": "", "extra": null }, { + "type": "BTREE", "name": "sponsor_id", "columns": [ - "sponsor_id" + { + "name": "sponsor_id", + "desc": false, + "is_expression": false + } ], - "expressions": null, + "unique": true, + "comment": "", "extra": null } ], @@ -242,6 +301,7 @@ "columns": [ "id" ], + "comment": "", "extra": null }, "foreign": [ @@ -251,12 +311,13 @@ "one", "two" ], - "extra": null, "foreign_table": "type_monsters", "foreign_columns": [ "int_one", "int_two" - ] + ], + "comment": "", + "extra": null } ], "uniques": [ @@ -266,6 +327,7 @@ "something", "another" ], + "comment": "", "extra": null }, { @@ -273,10 +335,13 @@ "columns": [ "sponsor_id" ], + "comment": "", "extra": null } - ] - } + ], + "check": null + }, + "comment": "" }, { "key": "sponsors", @@ -297,11 +362,17 @@ ], "indexes": [ { + "type": "BTREE", "name": "PRIMARY", "columns": [ - "id" + { + "name": "id", + "desc": false, + "is_expression": false + } ], - "expressions": null, + "unique": true, + "comment": "", "extra": null } ], @@ -311,11 +382,14 @@ "columns": [ "id" ], + "comment": "", "extra": null }, "foreign": null, - "uniques": null - } + "uniques": null, + "check": null + }, + "comment": "" }, { "key": "tags", @@ -336,11 +410,17 @@ ], "indexes": [ { + "type": "BTREE", "name": "PRIMARY", "columns": [ - "id" + { + "name": "id", + "desc": false, + "is_expression": false + } ], - "expressions": null, + "unique": true, + "comment": "", "extra": null } ], @@ -350,11 +430,14 @@ "columns": [ "id" ], + "comment": "", "extra": null }, "foreign": null, - "uniques": null - } + "uniques": null, + "check": null + }, + "comment": "" }, { "key": "test_index_expressions", @@ -397,64 +480,112 @@ ], "indexes": [ { + "type": "BTREE", "name": "idx1", - "columns": null, - "expressions": [ - "(`col1` + `col2`)" + "columns": [ + { + "name": "(`col1` + `col2`)", + "desc": false, + "is_expression": true + } ], + "unique": false, + "comment": "This is an index", "extra": null }, { + "type": "BTREE", "name": "idx2", "columns": [ - "col3" - ], - "expressions": [ - "(`col1` + `col2`)" + { + "name": "(`col1` + `col2`)", + "desc": false, + "is_expression": true + }, + { + "name": "col3", + "desc": false, + "is_expression": false + } ], + "unique": false, + "comment": "", "extra": null }, { + "type": "BTREE", "name": "idx3", "columns": [ - "col1" - ], - "expressions": [ - "(`col2` + `col3`)" + { + "name": "col1", + "desc": false, + "is_expression": false + }, + { + "name": "(`col2` + `col3`)", + "desc": false, + "is_expression": true + } ], + "unique": false, + "comment": "", "extra": null }, { + "type": "BTREE", "name": "idx4", "columns": [ - "col3" + { + "name": "col3", + "desc": false, + "is_expression": false + } ], - "expressions": null, + "unique": false, + "comment": "", "extra": null }, { + "type": "BTREE", "name": "idx5", "columns": [ - "col1", - "col2" + { + "name": "col1", + "desc": true, + "is_expression": false + }, + { + "name": "col2", + "desc": true, + "is_expression": false + } ], - "expressions": null, + "unique": false, + "comment": "", "extra": null }, { + "type": "BTREE", "name": "idx6", - "columns": null, - "expressions": [ - "pow(`col3`,2)" + "columns": [ + { + "name": "pow(`col3`,2)", + "desc": false, + "is_expression": true + } ], + "unique": false, + "comment": "", "extra": null } ], "constraints": { "primary": null, "foreign": null, - "uniques": null - } + "uniques": null, + "check": null + }, + "comment": "" }, { "key": "type_monsters", @@ -465,7 +596,7 @@ "name": "id", "db_type": "int", "default": "AUTO_INCREMENT", - "comment": "comment on ID", + "comment": "This is another column", "nullable": false, "generated": false, "autoincr": true, @@ -1817,20 +1948,36 @@ ], "indexes": [ { + "type": "BTREE", "name": "int_one", "columns": [ - "int_one", - "int_two" + { + "name": "int_one", + "desc": false, + "is_expression": false + }, + { + "name": "int_two", + "desc": false, + "is_expression": false + } ], - "expressions": null, + "unique": true, + "comment": "", "extra": null }, { + "type": "BTREE", "name": "PRIMARY", "columns": [ - "id" + { + "name": "id", + "desc": false, + "is_expression": false + } ], - "expressions": null, + "unique": true, + "comment": "", "extra": null } ], @@ -1840,6 +1987,7 @@ "columns": [ "id" ], + "comment": "", "extra": null }, "foreign": null, @@ -1850,10 +1998,13 @@ "int_one", "int_two" ], + "comment": "", "extra": null } - ] - } + ], + "check": null + }, + "comment": "This is a table" }, { "key": "user_videos", @@ -1898,8 +2049,10 @@ "constraints": { "primary": null, "foreign": null, - "uniques": null - } + "uniques": null, + "check": null + }, + "comment": "" }, { "key": "users", @@ -1920,11 +2073,17 @@ ], "indexes": [ { + "type": "BTREE", "name": "PRIMARY", "columns": [ - "id" + { + "name": "id", + "desc": false, + "is_expression": false + } ], - "expressions": null, + "unique": true, + "comment": "", "extra": null } ], @@ -1934,11 +2093,14 @@ "columns": [ "id" ], + "comment": "", "extra": null }, "foreign": null, - "uniques": null - } + "uniques": null, + "check": null + }, + "comment": "" }, { "key": "video_tags", @@ -1970,20 +2132,36 @@ ], "indexes": [ { + "type": "BTREE", "name": "PRIMARY", "columns": [ - "video_id", - "tag_id" + { + "name": "video_id", + "desc": false, + "is_expression": false + }, + { + "name": "tag_id", + "desc": false, + "is_expression": false + } ], - "expressions": null, + "unique": true, + "comment": "", "extra": null }, { + "type": "BTREE", "name": "tag_id", "columns": [ - "tag_id" + { + "name": "tag_id", + "desc": false, + "is_expression": false + } ], - "expressions": null, + "unique": false, + "comment": "", "extra": null } ], @@ -1994,6 +2172,7 @@ "video_id", "tag_id" ], + "comment": "", "extra": null }, "foreign": [ @@ -2002,26 +2181,30 @@ "columns": [ "video_id" ], - "extra": null, "foreign_table": "videos", "foreign_columns": [ "id" - ] + ], + "comment": "", + "extra": null }, { "name": "video_tags_ibfk_2", "columns": [ "tag_id" ], - "extra": null, "foreign_table": "tags", "foreign_columns": [ "id" - ] + ], + "comment": "", + "extra": null } ], - "uniques": null - } + "uniques": null, + "check": null + }, + "comment": "" }, { "key": "videos", @@ -2043,7 +2226,7 @@ "name": "user_id", "db_type": "int", "default": "", - "comment": "this is a comment", + "comment": "This is a column", "nullable": false, "generated": false, "autoincr": false, @@ -2064,27 +2247,45 @@ ], "indexes": [ { + "type": "BTREE", "name": "PRIMARY", "columns": [ - "id" + { + "name": "id", + "desc": false, + "is_expression": false + } ], - "expressions": null, + "unique": true, + "comment": "", "extra": null }, { + "type": "BTREE", "name": "sponsor_id", "columns": [ - "sponsor_id" + { + "name": "sponsor_id", + "desc": false, + "is_expression": false + } ], - "expressions": null, + "unique": true, + "comment": "", "extra": null }, { + "type": "BTREE", "name": "user_id", "columns": [ - "user_id" + { + "name": "user_id", + "desc": false, + "is_expression": false + } ], - "expressions": null, + "unique": false, + "comment": "", "extra": null } ], @@ -2094,6 +2295,7 @@ "columns": [ "id" ], + "comment": "", "extra": null }, "foreign": [ @@ -2102,22 +2304,24 @@ "columns": [ "user_id" ], - "extra": null, "foreign_table": "users", "foreign_columns": [ "id" - ] + ], + "comment": "", + "extra": null }, { "name": "videos_ibfk_2", "columns": [ "sponsor_id" ], - "extra": null, "foreign_table": "sponsors", "foreign_columns": [ "id" - ] + ], + "comment": "", + "extra": null } ], "uniques": [ @@ -2126,10 +2330,13 @@ "columns": [ "sponsor_id" ], + "comment": "", "extra": null } - ] - } + ], + "check": null + }, + "comment": "" } ], "enums": [ 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 1edc4cc..00db9bf 100644 --- a/gen/bobgen-mysql/driver/include-exclude-tables-mixed.golden.json +++ b/gen/bobgen-mysql/driver/include-exclude-tables-mixed.golden.json @@ -30,11 +30,17 @@ ], "indexes": [ { + "type": "BTREE", "name": "PRIMARY", "columns": [ - "id" + { + "name": "id", + "desc": false, + "is_expression": false + } ], - "expressions": null, + "unique": true, + "comment": "", "extra": null } ], @@ -44,11 +50,14 @@ "columns": [ "id" ], + "comment": "", "extra": null }, "foreign": null, - "uniques": null - } + "uniques": null, + "check": null + }, + "comment": "" }, { "key": "bar_qux", @@ -80,11 +89,17 @@ ], "indexes": [ { + "type": "BTREE", "name": "PRIMARY", "columns": [ - "id" + { + "name": "id", + "desc": false, + "is_expression": false + } ], - "expressions": null, + "unique": true, + "comment": "", "extra": null } ], @@ -94,11 +109,14 @@ "columns": [ "id" ], + "comment": "", "extra": null }, "foreign": null, - "uniques": null - } + "uniques": null, + "check": null + }, + "comment": "" } ], "enums": 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 fdf55bc..eab0389 100644 --- a/gen/bobgen-mysql/driver/include-exclude-tables-regex.golden.json +++ b/gen/bobgen-mysql/driver/include-exclude-tables-regex.golden.json @@ -30,11 +30,17 @@ ], "indexes": [ { + "type": "BTREE", "name": "PRIMARY", "columns": [ - "id" + { + "name": "id", + "desc": false, + "is_expression": false + } ], - "expressions": null, + "unique": true, + "comment": "", "extra": null } ], @@ -44,11 +50,14 @@ "columns": [ "id" ], + "comment": "", "extra": null }, "foreign": null, - "uniques": null - } + "uniques": null, + "check": null + }, + "comment": "" }, { "key": "foo_qux", @@ -80,11 +89,17 @@ ], "indexes": [ { + "type": "BTREE", "name": "PRIMARY", "columns": [ - "id" + { + "name": "id", + "desc": false, + "is_expression": false + } ], - "expressions": null, + "unique": true, + "comment": "", "extra": null } ], @@ -94,11 +109,14 @@ "columns": [ "id" ], + "comment": "", "extra": null }, "foreign": null, - "uniques": null - } + "uniques": null, + "check": null + }, + "comment": "" } ], "enums": null, diff --git a/gen/bobgen-mysql/driver/include-exclude-tables.golden.json b/gen/bobgen-mysql/driver/include-exclude-tables.golden.json index 89befa5..93e9b62 100644 --- a/gen/bobgen-mysql/driver/include-exclude-tables.golden.json +++ b/gen/bobgen-mysql/driver/include-exclude-tables.golden.json @@ -30,11 +30,17 @@ ], "indexes": [ { + "type": "BTREE", "name": "PRIMARY", "columns": [ - "id" + { + "name": "id", + "desc": false, + "is_expression": false + } ], - "expressions": null, + "unique": true, + "comment": "", "extra": null } ], @@ -44,11 +50,14 @@ "columns": [ "id" ], + "comment": "", "extra": null }, "foreign": null, - "uniques": null - } + "uniques": null, + "check": null + }, + "comment": "" } ], "enums": null, diff --git a/gen/bobgen-mysql/driver/include-tables.golden.json b/gen/bobgen-mysql/driver/include-tables.golden.json index 0a4aef3..bd781e1 100644 --- a/gen/bobgen-mysql/driver/include-tables.golden.json +++ b/gen/bobgen-mysql/driver/include-tables.golden.json @@ -30,11 +30,17 @@ ], "indexes": [ { + "type": "BTREE", "name": "PRIMARY", "columns": [ - "id" + { + "name": "id", + "desc": false, + "is_expression": false + } ], - "expressions": null, + "unique": true, + "comment": "", "extra": null } ], @@ -44,11 +50,14 @@ "columns": [ "id" ], + "comment": "", "extra": null }, "foreign": null, - "uniques": null - } + "uniques": null, + "check": null + }, + "comment": "" }, { "key": "foo_baz", @@ -80,11 +89,17 @@ ], "indexes": [ { + "type": "BTREE", "name": "PRIMARY", "columns": [ - "id" + { + "name": "id", + "desc": false, + "is_expression": false + } ], - "expressions": null, + "unique": true, + "comment": "", "extra": null } ], @@ -94,11 +109,14 @@ "columns": [ "id" ], + "comment": "", "extra": null }, "foreign": null, - "uniques": null - } + "uniques": null, + "check": null + }, + "comment": "" } ], "enums": null, diff --git a/gen/bobgen-mysql/driver/mysql.go b/gen/bobgen-mysql/driver/mysql.go index c7772e6..9e58110 100644 --- a/gen/bobgen-mysql/driver/mysql.go +++ b/gen/bobgen-mysql/driver/mysql.go @@ -9,7 +9,6 @@ import ( "strings" "sync" - "github.com/aarondl/opt/null" "github.com/go-sql-driver/mysql" helpers "github.com/stephenafamo/bob/gen/bobgen-helpers" "github.com/stephenafamo/bob/gen/drivers" @@ -165,17 +164,17 @@ func (d *driver) TableDetails(ctx context.Context, info drivers.TableInfo, colFi args := []any{tableName, schema} query := ` - select - c.column_name, - c.column_type, - c.column_comment, - c.data_type, - c.column_default, - c.extra = 'auto_increment' AS autoincr, - c.is_nullable = 'YES' AS nullable, - (c.extra = 'STORED GENERATED' OR c.extra = 'VIRTUAL GENERATED') is_generated - from information_schema.columns as c - where table_name = ? and table_schema = ?` + select + c.column_name, + c.column_type, + c.column_comment, + c.data_type, + c.column_default, + c.extra = 'auto_increment' AS autoincr, + c.is_nullable = 'YES' AS nullable, + (c.extra = 'STORED GENERATED' OR c.extra = 'VIRTUAL GENERATED') is_generated + from information_schema.columns as c + where table_name = ? and table_schema = ?` if len(filter.Only) > 0 { query += fmt.Sprintf(" and c.column_name in (%s)", strings.Repeat(",?", len(filter.Only))[1:]) @@ -330,19 +329,19 @@ func (d *driver) Constraints(ctx context.Context, _ drivers.ColumnFilter) (drive } query := `SELECT - tc.table_name AS table_name, - tc.constraint_name AS name, - tc.constraint_type AS type, - kcu.column_name AS column_name, - referenced_table_name AS foreign_table, - referenced_column_name AS foreign_column - FROM information_schema.table_constraints AS tc - LEFT JOIN information_schema.key_column_usage AS kcu - ON kcu.table_name = tc.table_name - AND kcu.table_schema = tc.table_schema - AND kcu.constraint_name = tc.constraint_name - WHERE tc.constraint_type IN ('PRIMARY KEY', 'UNIQUE', 'FOREIGN KEY') AND tc.table_schema = ? - ORDER BY tc.table_name, tc.constraint_name, tc.constraint_type, kcu.ordinal_position` + tc.table_name AS table_name, + tc.constraint_name AS name, + tc.constraint_type AS type, + kcu.column_name AS column_name, + referenced_table_name AS foreign_table, + referenced_column_name AS foreign_column + FROM information_schema.table_constraints AS tc + LEFT JOIN information_schema.key_column_usage AS kcu + ON kcu.table_name = tc.table_name + AND kcu.table_schema = tc.table_schema + AND kcu.constraint_name = tc.constraint_name + WHERE tc.constraint_type IN ('PRIMARY KEY', 'UNIQUE', 'FOREIGN KEY') AND tc.table_schema = ? + ORDER BY tc.table_name, tc.constraint_name, tc.constraint_type, kcu.ordinal_position` type constraint struct { TableName string @@ -408,19 +407,27 @@ func (d *driver) Indexes(ctx context.Context) (drivers.DBIndexes[any], error) { ret := drivers.DBIndexes[any]{} query := `SELECT - s.table_name AS table_name, - s.index_name AS index_name, - s.column_name AS column_name, - s.expression AS expression - FROM information_schema.statistics s - WHERE s.table_schema = ? - ORDER BY s.table_name,s.index_name,s.seq_in_index` + s.table_name AS table_name, + s.index_name AS index_name, + s.column_name AS column_name, + s.expression AS expression, + NOT s.non_unique AS is_unique, + s.collation = 'D' AS descending, + s.index_type as type, + s.index_comment as comment + FROM information_schema.statistics s + WHERE s.table_schema = ? + ORDER BY s.table_name, s.index_name, s.seq_in_index` type indexColumn struct { TableName string IndexName string - ColumnName null.Val[string] - Expression null.Val[string] + ColumnName sql.NullString + Expression sql.NullString + IsUnique bool + Descending bool + Type string + Comment string } indexColumns, err := stdscan.All(ctx, d.conn, scan.StructMapper[indexColumn](), query, d.dbName) if err != nil { @@ -436,14 +443,51 @@ func (d *driver) Indexes(ctx context.Context) (drivers.DBIndexes[any], error) { current = drivers.Index[any]{} table = "" //nolint:ineffassign } + table = c.TableName current.Name = c.IndexName - if c.ColumnName.IsSet() { - current.Columns = append(current.Columns, c.ColumnName.GetOrZero()) + current.Type = c.Type + current.Unique = c.IsUnique + current.Comment = c.Comment + + col := drivers.IndexColumn{ + Name: c.ColumnName.String, + Desc: c.Descending, + } + + if c.Expression.Valid { + col.Name = c.Expression.String + col.IsExpression = true } - if c.Expression.IsSet() { - current.Expressions = append(current.Expressions, c.Expression.GetOrZero()) + + current.Columns = append(current.Columns, col) + } + + return ret, nil +} + +func (d *driver) Comments(ctx context.Context) (map[string]string, error) { + ret := map[string]string{} + + query := `SELECT + table_name as name, + table_comment as comment + FROM information_schema.tables + WHERE table_comment != table_type AND table_schema = ?` + + type comment struct { + Name string + Comment string + } + + for c, err := range stdscan.Each( + ctx, d.conn, scan.StructMapper[comment](), + query, d.dbName, + ) { + if err != nil { + return nil, err } + ret[c.Name] = c.Comment } return ret, nil diff --git a/gen/bobgen-mysql/driver/mysql.golden.json b/gen/bobgen-mysql/driver/mysql.golden.json index b512acf..b821b40 100644 --- a/gen/bobgen-mysql/driver/mysql.golden.json +++ b/gen/bobgen-mysql/driver/mysql.golden.json @@ -30,11 +30,17 @@ ], "indexes": [ { + "type": "BTREE", "name": "PRIMARY", "columns": [ - "id" + { + "name": "id", + "desc": false, + "is_expression": false + } ], - "expressions": null, + "unique": true, + "comment": "", "extra": null } ], @@ -44,11 +50,14 @@ "columns": [ "id" ], + "comment": "", "extra": null }, "foreign": null, - "uniques": null - } + "uniques": null, + "check": null + }, + "comment": "" }, { "key": "bar_qux", @@ -80,11 +89,17 @@ ], "indexes": [ { + "type": "BTREE", "name": "PRIMARY", "columns": [ - "id" + { + "name": "id", + "desc": false, + "is_expression": false + } ], - "expressions": null, + "unique": true, + "comment": "", "extra": null } ], @@ -94,11 +109,14 @@ "columns": [ "id" ], + "comment": "", "extra": null }, "foreign": null, - "uniques": null - } + "uniques": null, + "check": null + }, + "comment": "" }, { "key": "foo_bar", @@ -130,11 +148,17 @@ ], "indexes": [ { + "type": "BTREE", "name": "PRIMARY", "columns": [ - "id" + { + "name": "id", + "desc": false, + "is_expression": false + } ], - "expressions": null, + "unique": true, + "comment": "", "extra": null } ], @@ -144,11 +168,14 @@ "columns": [ "id" ], + "comment": "", "extra": null }, "foreign": null, - "uniques": null - } + "uniques": null, + "check": null + }, + "comment": "" }, { "key": "foo_baz", @@ -180,11 +207,17 @@ ], "indexes": [ { + "type": "BTREE", "name": "PRIMARY", "columns": [ - "id" + { + "name": "id", + "desc": false, + "is_expression": false + } ], - "expressions": null, + "unique": true, + "comment": "", "extra": null } ], @@ -194,11 +227,14 @@ "columns": [ "id" ], + "comment": "", "extra": null }, "foreign": null, - "uniques": null - } + "uniques": null, + "check": null + }, + "comment": "" }, { "key": "foo_qux", @@ -230,11 +266,17 @@ ], "indexes": [ { + "type": "BTREE", "name": "PRIMARY", "columns": [ - "id" + { + "name": "id", + "desc": false, + "is_expression": false + } ], - "expressions": null, + "unique": true, + "comment": "", "extra": null } ], @@ -244,11 +286,14 @@ "columns": [ "id" ], + "comment": "", "extra": null }, "foreign": null, - "uniques": null - } + "uniques": null, + "check": null + }, + "comment": "" }, { "key": "multi_keys", @@ -335,37 +380,69 @@ ], "indexes": [ { + "type": "BTREE", "name": "one", "columns": [ - "one", - "two" + { + "name": "one", + "desc": false, + "is_expression": false + }, + { + "name": "two", + "desc": false, + "is_expression": false + } ], - "expressions": null, + "unique": false, + "comment": "", "extra": null }, { + "type": "BTREE", "name": "PRIMARY", "columns": [ - "id" + { + "name": "id", + "desc": false, + "is_expression": false + } ], - "expressions": null, + "unique": true, + "comment": "", "extra": null }, { + "type": "BTREE", "name": "something", "columns": [ - "something", - "another" + { + "name": "something", + "desc": false, + "is_expression": false + }, + { + "name": "another", + "desc": false, + "is_expression": false + } ], - "expressions": null, + "unique": true, + "comment": "", "extra": null }, { + "type": "BTREE", "name": "sponsor_id", "columns": [ - "sponsor_id" + { + "name": "sponsor_id", + "desc": false, + "is_expression": false + } ], - "expressions": null, + "unique": true, + "comment": "", "extra": null } ], @@ -375,6 +452,7 @@ "columns": [ "id" ], + "comment": "", "extra": null }, "foreign": [ @@ -384,12 +462,13 @@ "one", "two" ], - "extra": null, "foreign_table": "type_monsters", "foreign_columns": [ "int_one", "int_two" - ] + ], + "comment": "", + "extra": null } ], "uniques": [ @@ -399,6 +478,7 @@ "something", "another" ], + "comment": "", "extra": null }, { @@ -406,10 +486,13 @@ "columns": [ "sponsor_id" ], + "comment": "", "extra": null } - ] - } + ], + "check": null + }, + "comment": "" }, { "key": "sponsors", @@ -430,11 +513,17 @@ ], "indexes": [ { + "type": "BTREE", "name": "PRIMARY", "columns": [ - "id" + { + "name": "id", + "desc": false, + "is_expression": false + } ], - "expressions": null, + "unique": true, + "comment": "", "extra": null } ], @@ -444,11 +533,14 @@ "columns": [ "id" ], + "comment": "", "extra": null }, "foreign": null, - "uniques": null - } + "uniques": null, + "check": null + }, + "comment": "" }, { "key": "tags", @@ -469,11 +561,17 @@ ], "indexes": [ { + "type": "BTREE", "name": "PRIMARY", "columns": [ - "id" + { + "name": "id", + "desc": false, + "is_expression": false + } ], - "expressions": null, + "unique": true, + "comment": "", "extra": null } ], @@ -483,11 +581,14 @@ "columns": [ "id" ], + "comment": "", "extra": null }, "foreign": null, - "uniques": null - } + "uniques": null, + "check": null + }, + "comment": "" }, { "key": "test_index_expressions", @@ -530,64 +631,112 @@ ], "indexes": [ { + "type": "BTREE", "name": "idx1", - "columns": null, - "expressions": [ - "(`col1` + `col2`)" + "columns": [ + { + "name": "(`col1` + `col2`)", + "desc": false, + "is_expression": true + } ], + "unique": false, + "comment": "This is an index", "extra": null }, { + "type": "BTREE", "name": "idx2", "columns": [ - "col3" - ], - "expressions": [ - "(`col1` + `col2`)" + { + "name": "(`col1` + `col2`)", + "desc": false, + "is_expression": true + }, + { + "name": "col3", + "desc": false, + "is_expression": false + } ], + "unique": false, + "comment": "", "extra": null }, { + "type": "BTREE", "name": "idx3", "columns": [ - "col1" - ], - "expressions": [ - "(`col2` + `col3`)" + { + "name": "col1", + "desc": false, + "is_expression": false + }, + { + "name": "(`col2` + `col3`)", + "desc": false, + "is_expression": true + } ], + "unique": false, + "comment": "", "extra": null }, { + "type": "BTREE", "name": "idx4", "columns": [ - "col3" + { + "name": "col3", + "desc": false, + "is_expression": false + } ], - "expressions": null, + "unique": false, + "comment": "", "extra": null }, { + "type": "BTREE", "name": "idx5", "columns": [ - "col1", - "col2" + { + "name": "col1", + "desc": true, + "is_expression": false + }, + { + "name": "col2", + "desc": true, + "is_expression": false + } ], - "expressions": null, + "unique": false, + "comment": "", "extra": null }, { + "type": "BTREE", "name": "idx6", - "columns": null, - "expressions": [ - "pow(`col3`,2)" + "columns": [ + { + "name": "pow(`col3`,2)", + "desc": false, + "is_expression": true + } ], + "unique": false, + "comment": "", "extra": null } ], "constraints": { "primary": null, "foreign": null, - "uniques": null - } + "uniques": null, + "check": null + }, + "comment": "" }, { "key": "type_monsters", @@ -598,7 +747,7 @@ "name": "id", "db_type": "int", "default": "AUTO_INCREMENT", - "comment": "comment on ID", + "comment": "This is another column", "nullable": false, "generated": false, "autoincr": true, @@ -1950,20 +2099,36 @@ ], "indexes": [ { + "type": "BTREE", "name": "int_one", "columns": [ - "int_one", - "int_two" + { + "name": "int_one", + "desc": false, + "is_expression": false + }, + { + "name": "int_two", + "desc": false, + "is_expression": false + } ], - "expressions": null, + "unique": true, + "comment": "", "extra": null }, { + "type": "BTREE", "name": "PRIMARY", "columns": [ - "id" + { + "name": "id", + "desc": false, + "is_expression": false + } ], - "expressions": null, + "unique": true, + "comment": "", "extra": null } ], @@ -1973,6 +2138,7 @@ "columns": [ "id" ], + "comment": "", "extra": null }, "foreign": null, @@ -1983,10 +2149,13 @@ "int_one", "int_two" ], + "comment": "", "extra": null } - ] - } + ], + "check": null + }, + "comment": "This is a table" }, { "key": "user_videos", @@ -2031,8 +2200,10 @@ "constraints": { "primary": null, "foreign": null, - "uniques": null - } + "uniques": null, + "check": null + }, + "comment": "" }, { "key": "users", @@ -2053,11 +2224,17 @@ ], "indexes": [ { + "type": "BTREE", "name": "PRIMARY", "columns": [ - "id" + { + "name": "id", + "desc": false, + "is_expression": false + } ], - "expressions": null, + "unique": true, + "comment": "", "extra": null } ], @@ -2067,11 +2244,14 @@ "columns": [ "id" ], + "comment": "", "extra": null }, "foreign": null, - "uniques": null - } + "uniques": null, + "check": null + }, + "comment": "" }, { "key": "video_tags", @@ -2103,20 +2283,36 @@ ], "indexes": [ { + "type": "BTREE", "name": "PRIMARY", "columns": [ - "video_id", - "tag_id" + { + "name": "video_id", + "desc": false, + "is_expression": false + }, + { + "name": "tag_id", + "desc": false, + "is_expression": false + } ], - "expressions": null, + "unique": true, + "comment": "", "extra": null }, { + "type": "BTREE", "name": "tag_id", "columns": [ - "tag_id" + { + "name": "tag_id", + "desc": false, + "is_expression": false + } ], - "expressions": null, + "unique": false, + "comment": "", "extra": null } ], @@ -2127,6 +2323,7 @@ "video_id", "tag_id" ], + "comment": "", "extra": null }, "foreign": [ @@ -2135,26 +2332,30 @@ "columns": [ "video_id" ], - "extra": null, "foreign_table": "videos", "foreign_columns": [ "id" - ] + ], + "comment": "", + "extra": null }, { "name": "video_tags_ibfk_2", "columns": [ "tag_id" ], - "extra": null, "foreign_table": "tags", "foreign_columns": [ "id" - ] + ], + "comment": "", + "extra": null } ], - "uniques": null - } + "uniques": null, + "check": null + }, + "comment": "" }, { "key": "videos", @@ -2176,7 +2377,7 @@ "name": "user_id", "db_type": "int", "default": "", - "comment": "this is a comment", + "comment": "This is a column", "nullable": false, "generated": false, "autoincr": false, @@ -2197,27 +2398,45 @@ ], "indexes": [ { + "type": "BTREE", "name": "PRIMARY", "columns": [ - "id" + { + "name": "id", + "desc": false, + "is_expression": false + } ], - "expressions": null, + "unique": true, + "comment": "", "extra": null }, { + "type": "BTREE", "name": "sponsor_id", "columns": [ - "sponsor_id" + { + "name": "sponsor_id", + "desc": false, + "is_expression": false + } ], - "expressions": null, + "unique": true, + "comment": "", "extra": null }, { + "type": "BTREE", "name": "user_id", "columns": [ - "user_id" + { + "name": "user_id", + "desc": false, + "is_expression": false + } ], - "expressions": null, + "unique": false, + "comment": "", "extra": null } ], @@ -2227,6 +2446,7 @@ "columns": [ "id" ], + "comment": "", "extra": null }, "foreign": [ @@ -2235,22 +2455,24 @@ "columns": [ "user_id" ], - "extra": null, "foreign_table": "users", "foreign_columns": [ "id" - ] + ], + "comment": "", + "extra": null }, { "name": "videos_ibfk_2", "columns": [ "sponsor_id" ], - "extra": null, "foreign_table": "sponsors", "foreign_columns": [ "id" - ] + ], + "comment": "", + "extra": null } ], "uniques": [ @@ -2259,10 +2481,13 @@ "columns": [ "sponsor_id" ], + "comment": "", "extra": null } - ] - } + ], + "check": null + }, + "comment": "" } ], "enums": [ diff --git a/gen/bobgen-mysql/driver/mysql_test.go b/gen/bobgen-mysql/driver/mysql_test.go index c517387..5a8f178 100644 --- a/gen/bobgen-mysql/driver/mysql_test.go +++ b/gen/bobgen-mysql/driver/mysql_test.go @@ -159,8 +159,19 @@ func TestDriver(t *testing.T) { }, } - for _, tt := range tests { + for i, tt := range tests { t.Run(tt.name, func(t *testing.T) { + if i > 0 { + testgen.TestAssemble(t, testgen.AssembleTestConfig[any, any, any]{ + GetDriver: func() drivers.Interface[any, any, any] { + return New(tt.config) + }, + GoldenFile: tt.goldenJson, + OverwriteGolden: *flagOverwriteGolden, + Templates: &helpers.Templates{Models: []fs.FS{gen.MySQLModelTemplates}}, + }) + return + } out, err := os.MkdirTemp("", "bobgen_mysql_") if err != nil { t.Fatalf("unable to create tempdir: %s", err) diff --git a/gen/bobgen-psql/driver/exclude-tables.golden.json b/gen/bobgen-psql/driver/exclude-tables.golden.json index 7b407ba..d71d1f9 100644 --- a/gen/bobgen-psql/driver/exclude-tables.golden.json +++ b/gen/bobgen-psql/driver/exclude-tables.golden.json @@ -19,12 +19,25 @@ ], "indexes": [ { + "type": "btree", "name": "bar_baz_pkey", "columns": [ - "id" + { + "name": "id", + "desc": false, + "is_expression": false + } ], - "expressions": null, - "extra": null + "unique": true, + "comment": "", + "extra": { + "nulls_first": [ + false + ], + "nulls_not_distinct": false, + "where_clause": "", + "include": [] + } } ], "constraints": { @@ -33,11 +46,14 @@ "columns": [ "id" ], + "comment": "", "extra": null }, "foreign": null, - "uniques": null - } + "uniques": null, + "check": null + }, + "comment": "" }, { "key": "bar_qux", @@ -58,12 +74,25 @@ ], "indexes": [ { + "type": "btree", "name": "bar_qux_pkey", "columns": [ - "id" + { + "name": "id", + "desc": false, + "is_expression": false + } ], - "expressions": null, - "extra": null + "unique": true, + "comment": "", + "extra": { + "nulls_first": [ + false + ], + "nulls_not_distinct": false, + "where_clause": "", + "include": [] + } } ], "constraints": { @@ -72,11 +101,14 @@ "columns": [ "id" ], + "comment": "", "extra": null }, "foreign": null, - "uniques": null - } + "uniques": null, + "check": null + }, + "comment": "" }, { "key": "foo_qux", @@ -97,12 +129,25 @@ ], "indexes": [ { + "type": "btree", "name": "foo_qux_pkey", "columns": [ - "id" + { + "name": "id", + "desc": false, + "is_expression": false + } ], - "expressions": null, - "extra": null + "unique": true, + "comment": "", + "extra": { + "nulls_first": [ + false + ], + "nulls_not_distinct": false, + "where_clause": "", + "include": [] + } } ], "constraints": { @@ -111,11 +156,14 @@ "columns": [ "id" ], + "comment": "", "extra": null }, "foreign": null, - "uniques": null - } + "uniques": null, + "check": null + }, + "comment": "" }, { "key": "sponsors", @@ -136,12 +184,25 @@ ], "indexes": [ { + "type": "btree", "name": "sponsors_pkey", "columns": [ - "id" + { + "name": "id", + "desc": false, + "is_expression": false + } ], - "expressions": null, - "extra": null + "unique": true, + "comment": "", + "extra": { + "nulls_first": [ + false + ], + "nulls_not_distinct": false, + "where_clause": "", + "include": [] + } } ], "constraints": { @@ -150,11 +211,14 @@ "columns": [ "id" ], + "comment": "", "extra": null }, "foreign": null, - "uniques": null - } + "uniques": null, + "check": null + }, + "comment": "" }, { "key": "tags", @@ -175,12 +239,25 @@ ], "indexes": [ { + "type": "btree", "name": "tags_pkey", "columns": [ - "id" + { + "name": "id", + "desc": false, + "is_expression": false + } ], - "expressions": null, - "extra": null + "unique": true, + "comment": "", + "extra": { + "nulls_first": [ + false + ], + "nulls_not_distinct": false, + "where_clause": "", + "include": [] + } } ], "constraints": { @@ -189,11 +266,14 @@ "columns": [ "id" ], + "comment": "", "extra": null }, "foreign": null, - "uniques": null - } + "uniques": null, + "check": null + }, + "comment": "" }, { "key": "test_index_expressions", @@ -236,64 +316,157 @@ ], "indexes": [ { + "type": "btree", "name": "idx1", - "columns": null, - "expressions": [ - "(col1 + col2)" + "columns": [ + { + "name": "(col1 + col2)", + "desc": false, + "is_expression": true + } ], - "extra": null + "unique": false, + "comment": "This is an index", + "extra": { + "nulls_first": [ + false + ], + "nulls_not_distinct": false, + "where_clause": "", + "include": [] + } }, { + "type": "btree", "name": "idx2", "columns": [ - "col3" - ], - "expressions": [ - "(col1 + col2)" + { + "name": "(col1 + col2)", + "desc": false, + "is_expression": true + }, + { + "name": "col3", + "desc": false, + "is_expression": false + } ], - "extra": null + "unique": false, + "comment": "", + "extra": { + "nulls_first": [ + false, + false + ], + "nulls_not_distinct": false, + "where_clause": "", + "include": [] + } }, { + "type": "btree", "name": "idx3", "columns": [ - "col1" + { + "name": "col1", + "desc": false, + "is_expression": false + }, + { + "name": "(col2 + col3)", + "desc": false, + "is_expression": true + } ], - "expressions": [ - "(col2 + col3)" - ], - "extra": null + "unique": false, + "comment": "", + "extra": { + "nulls_first": [ + false, + false + ], + "nulls_not_distinct": false, + "where_clause": "", + "include": [] + } }, { + "type": "btree", "name": "idx4", "columns": [ - "col3" + { + "name": "col3", + "desc": false, + "is_expression": false + } ], - "expressions": null, - "extra": null + "unique": false, + "comment": "", + "extra": { + "nulls_first": [ + false + ], + "nulls_not_distinct": false, + "where_clause": "", + "include": [] + } }, { + "type": "btree", "name": "idx5", "columns": [ - "col1", - "col2" + { + "name": "col1", + "desc": true, + "is_expression": false + }, + { + "name": "col2", + "desc": true, + "is_expression": false + } ], - "expressions": null, - "extra": null + "unique": false, + "comment": "", + "extra": { + "nulls_first": [ + true, + true + ], + "nulls_not_distinct": false, + "where_clause": "", + "include": [] + } }, { + "type": "btree", "name": "idx6", - "columns": null, - "expressions": [ - "pow(col3::double precision, 2::double precision)" + "columns": [ + { + "name": "pow(col3::double precision, 2::double precision)", + "desc": false, + "is_expression": true + } ], - "extra": null + "unique": false, + "comment": "", + "extra": { + "nulls_first": [ + false + ], + "nulls_not_distinct": false, + "where_clause": "", + "include": [] + } } ], "constraints": { "primary": null, "foreign": null, - "uniques": null - } + "uniques": null, + "check": null + }, + "comment": "" }, { "key": "type_monsters", @@ -2074,12 +2247,25 @@ ], "indexes": [ { + "type": "btree", "name": "type_monsters_pkey", "columns": [ - "id" + { + "name": "id", + "desc": false, + "is_expression": false + } ], - "expressions": null, - "extra": null + "unique": true, + "comment": "", + "extra": { + "nulls_first": [ + false + ], + "nulls_not_distinct": false, + "where_clause": "", + "include": [] + } } ], "constraints": { @@ -2088,11 +2274,24 @@ "columns": [ "id" ], + "comment": "", "extra": null }, "foreign": null, - "uniques": null - } + "uniques": null, + "check": [ + { + "name": "check_not_null", + "columns": [ + "bool_two" + ], + "expression": "(bool_two IS NOT NULL)", + "comment": "This is a constraint", + "extra": null + } + ] + }, + "comment": "This is a table" }, { "key": "type_monsters_mv", @@ -2103,8 +2302,10 @@ "constraints": { "primary": null, "foreign": null, - "uniques": null - } + "uniques": null, + "check": null + }, + "comment": "This is a materialized view" }, { "key": "type_monsters_v", @@ -3887,8 +4088,10 @@ "constraints": { "primary": null, "foreign": null, - "uniques": null - } + "uniques": null, + "check": null + }, + "comment": "This is a view" }, { "key": "user_videos", @@ -3933,8 +4136,10 @@ "constraints": { "primary": null, "foreign": null, - "uniques": null - } + "uniques": null, + "check": null + }, + "comment": "" }, { "key": "users", @@ -4010,28 +4215,67 @@ ], "indexes": [ { - "name": "users_party_id_key", + "type": "btree", + "name": "users_pkey", "columns": [ - "party_id" + { + "name": "id", + "desc": false, + "is_expression": false + } ], - "expressions": null, - "extra": null + "unique": true, + "comment": "", + "extra": { + "nulls_first": [ + false + ], + "nulls_not_distinct": false, + "where_clause": "", + "include": [] + } }, { - "name": "users_pkey", + "type": "btree", + "name": "users_party_id_key", "columns": [ - "id" + { + "name": "party_id", + "desc": false, + "is_expression": false + } ], - "expressions": null, - "extra": null + "unique": true, + "comment": "", + "extra": { + "nulls_first": [ + false + ], + "nulls_not_distinct": false, + "where_clause": "", + "include": [] + } }, { + "type": "btree", "name": "users_primary_email_key", "columns": [ - "primary_email" + { + "name": "primary_email", + "desc": false, + "is_expression": false + } ], - "expressions": null, - "extra": null + "unique": true, + "comment": "", + "extra": { + "nulls_first": [ + false + ], + "nulls_not_distinct": false, + "where_clause": "", + "include": [] + } } ], "constraints": { @@ -4040,6 +4284,7 @@ "columns": [ "id" ], + "comment": "", "extra": null }, "foreign": [ @@ -4048,33 +4293,36 @@ "columns": [ "parent_id" ], - "extra": null, "foreign_table": "users", "foreign_columns": [ "id" - ] + ], + "comment": "", + "extra": null }, { "name": "users.users_party_id_fkey", "columns": [ "party_id" ], - "extra": null, "foreign_table": "users", "foreign_columns": [ "id" - ] + ], + "comment": "", + "extra": null }, { "name": "users.users_referrer_fkey", "columns": [ "referrer" ], - "extra": null, "foreign_table": "users", "foreign_columns": [ "id" - ] + ], + "comment": "", + "extra": null } ], "uniques": [ @@ -4083,6 +4331,7 @@ "columns": [ "party_id" ], + "comment": "", "extra": null }, { @@ -4090,10 +4339,13 @@ "columns": [ "primary_email" ], + "comment": "", "extra": null } - ] - } + ], + "check": null + }, + "comment": "" }, { "key": "video_tags", @@ -4125,13 +4377,31 @@ ], "indexes": [ { + "type": "btree", "name": "video_tags_pkey", "columns": [ - "video_id", - "tag_id" + { + "name": "video_id", + "desc": false, + "is_expression": false + }, + { + "name": "tag_id", + "desc": false, + "is_expression": false + } ], - "expressions": null, - "extra": null + "unique": true, + "comment": "", + "extra": { + "nulls_first": [ + false, + false + ], + "nulls_not_distinct": false, + "where_clause": "", + "include": [] + } } ], "constraints": { @@ -4141,6 +4411,7 @@ "video_id", "tag_id" ], + "comment": "", "extra": null }, "foreign": [ @@ -4149,26 +4420,30 @@ "columns": [ "tag_id" ], - "extra": null, "foreign_table": "tags", "foreign_columns": [ "id" - ] + ], + "comment": "", + "extra": null }, { "name": "video_tags.video_tags_video_id_fkey", "columns": [ "video_id" ], - "extra": null, "foreign_table": "videos", "foreign_columns": [ "id" - ] + ], + "comment": "", + "extra": null } ], - "uniques": null - } + "uniques": null, + "check": null + }, + "comment": "" }, { "key": "videos", @@ -4211,20 +4486,46 @@ ], "indexes": [ { + "type": "btree", "name": "videos_pkey", "columns": [ - "id" + { + "name": "id", + "desc": false, + "is_expression": false + } ], - "expressions": null, - "extra": null + "unique": true, + "comment": "", + "extra": { + "nulls_first": [ + false + ], + "nulls_not_distinct": false, + "where_clause": "", + "include": [] + } }, { + "type": "btree", "name": "videos_sponsor_id_key", "columns": [ - "sponsor_id" + { + "name": "sponsor_id", + "desc": false, + "is_expression": false + } ], - "expressions": null, - "extra": null + "unique": true, + "comment": "", + "extra": { + "nulls_first": [ + false + ], + "nulls_not_distinct": false, + "where_clause": "", + "include": [] + } } ], "constraints": { @@ -4233,6 +4534,7 @@ "columns": [ "id" ], + "comment": "", "extra": null }, "foreign": [ @@ -4241,22 +4543,24 @@ "columns": [ "sponsor_id" ], - "extra": null, "foreign_table": "sponsors", "foreign_columns": [ "id" - ] + ], + "comment": "", + "extra": null }, { "name": "videos.videos_user_id_fkey", "columns": [ "user_id" ], - "extra": null, "foreign_table": "users", "foreign_columns": [ "id" - ] + ], + "comment": "", + "extra": null } ], "uniques": [ @@ -4265,10 +4569,13 @@ "columns": [ "sponsor_id" ], + "comment": "", "extra": null } - ] - } + ], + "check": null + }, + "comment": "" } ], "enums": [ 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 a8ccf03..a82f95e 100644 --- a/gen/bobgen-psql/driver/include-exclude-tables-mixed.golden.json +++ b/gen/bobgen-psql/driver/include-exclude-tables-mixed.golden.json @@ -30,12 +30,25 @@ ], "indexes": [ { + "type": "btree", "name": "bar_baz_pkey", "columns": [ - "id" + { + "name": "id", + "desc": false, + "is_expression": false + } ], - "expressions": null, - "extra": null + "unique": true, + "comment": "", + "extra": { + "nulls_first": [ + false + ], + "nulls_not_distinct": false, + "where_clause": "", + "include": [] + } } ], "constraints": { @@ -44,11 +57,14 @@ "columns": [ "id" ], + "comment": "", "extra": null }, "foreign": null, - "uniques": null - } + "uniques": null, + "check": null + }, + "comment": "" }, { "key": "bar_qux", @@ -80,12 +96,25 @@ ], "indexes": [ { + "type": "btree", "name": "bar_qux_pkey", "columns": [ - "id" + { + "name": "id", + "desc": false, + "is_expression": false + } ], - "expressions": null, - "extra": null + "unique": true, + "comment": "", + "extra": { + "nulls_first": [ + false + ], + "nulls_not_distinct": false, + "where_clause": "", + "include": [] + } } ], "constraints": { @@ -94,11 +123,14 @@ "columns": [ "id" ], + "comment": "", "extra": null }, "foreign": null, - "uniques": null - } + "uniques": null, + "check": null + }, + "comment": "" } ], "enums": [ 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 3f7627e..6e24b79 100644 --- a/gen/bobgen-psql/driver/include-exclude-tables-regex.golden.json +++ b/gen/bobgen-psql/driver/include-exclude-tables-regex.golden.json @@ -30,12 +30,25 @@ ], "indexes": [ { + "type": "btree", "name": "bar_qux_pkey", "columns": [ - "id" + { + "name": "id", + "desc": false, + "is_expression": false + } ], - "expressions": null, - "extra": null + "unique": true, + "comment": "", + "extra": { + "nulls_first": [ + false + ], + "nulls_not_distinct": false, + "where_clause": "", + "include": [] + } } ], "constraints": { @@ -44,11 +57,14 @@ "columns": [ "id" ], + "comment": "", "extra": null }, "foreign": null, - "uniques": null - } + "uniques": null, + "check": null + }, + "comment": "" }, { "key": "foo_qux", @@ -80,12 +96,25 @@ ], "indexes": [ { + "type": "btree", "name": "foo_qux_pkey", "columns": [ - "id" + { + "name": "id", + "desc": false, + "is_expression": false + } ], - "expressions": null, - "extra": null + "unique": true, + "comment": "", + "extra": { + "nulls_first": [ + false + ], + "nulls_not_distinct": false, + "where_clause": "", + "include": [] + } } ], "constraints": { @@ -94,11 +123,14 @@ "columns": [ "id" ], + "comment": "", "extra": null }, "foreign": null, - "uniques": null - } + "uniques": null, + "check": null + }, + "comment": "" } ], "enums": [ diff --git a/gen/bobgen-psql/driver/include-exclude-tables.golden.json b/gen/bobgen-psql/driver/include-exclude-tables.golden.json index 9a67996..a122560 100644 --- a/gen/bobgen-psql/driver/include-exclude-tables.golden.json +++ b/gen/bobgen-psql/driver/include-exclude-tables.golden.json @@ -30,12 +30,25 @@ ], "indexes": [ { + "type": "btree", "name": "foo_baz_pkey", "columns": [ - "id" + { + "name": "id", + "desc": false, + "is_expression": false + } ], - "expressions": null, - "extra": null + "unique": true, + "comment": "", + "extra": { + "nulls_first": [ + false + ], + "nulls_not_distinct": false, + "where_clause": "", + "include": [] + } } ], "constraints": { @@ -44,11 +57,14 @@ "columns": [ "id" ], + "comment": "", "extra": null }, "foreign": null, - "uniques": null - } + "uniques": null, + "check": null + }, + "comment": "" } ], "enums": [ diff --git a/gen/bobgen-psql/driver/include-tables.golden.json b/gen/bobgen-psql/driver/include-tables.golden.json index ce15289..2fc32bf 100644 --- a/gen/bobgen-psql/driver/include-tables.golden.json +++ b/gen/bobgen-psql/driver/include-tables.golden.json @@ -30,12 +30,25 @@ ], "indexes": [ { + "type": "btree", "name": "foo_bar_pkey", "columns": [ - "id" + { + "name": "id", + "desc": false, + "is_expression": false + } ], - "expressions": null, - "extra": null + "unique": true, + "comment": "", + "extra": { + "nulls_first": [ + false + ], + "nulls_not_distinct": false, + "where_clause": "", + "include": [] + } } ], "constraints": { @@ -44,11 +57,14 @@ "columns": [ "id" ], + "comment": "", "extra": null }, "foreign": null, - "uniques": null - } + "uniques": null, + "check": null + }, + "comment": "" }, { "key": "foo_baz", @@ -80,12 +96,25 @@ ], "indexes": [ { + "type": "btree", "name": "foo_baz_pkey", "columns": [ - "id" + { + "name": "id", + "desc": false, + "is_expression": false + } ], - "expressions": null, - "extra": null + "unique": true, + "comment": "", + "extra": { + "nulls_first": [ + false + ], + "nulls_not_distinct": false, + "where_clause": "", + "include": [] + } } ], "constraints": { @@ -94,11 +123,14 @@ "columns": [ "id" ], + "comment": "", "extra": null }, "foreign": null, - "uniques": null - } + "uniques": null, + "check": null + }, + "comment": "" } ], "enums": [ diff --git a/gen/bobgen-psql/driver/keys.go b/gen/bobgen-psql/driver/keys.go index 8c4f016..d3f1178 100644 --- a/gen/bobgen-psql/driver/keys.go +++ b/gen/bobgen-psql/driver/keys.go @@ -15,6 +15,7 @@ func (d *driver) Constraints(ctx context.Context, _ drivers.ColumnFilter) (drive PKs: map[string]*drivers.Constraint[any]{}, FKs: map[string][]drivers.ForeignKey[any]{}, Uniques: map[string][]drivers.Constraint[any]{}, + Checks: map[string][]drivers.Check[any]{}, } query := `SELECT @@ -26,6 +27,8 @@ func (d *driver) Constraints(ctx context.Context, _ drivers.ColumnFilter) (drive , max(out.relname) as foreign_table , max(local_cols.columns) as columns , max(foreign_cols.columns) as foreign_columns + , max(pg_get_expr(con.conbin, rel.oid)) AS check_expr + , max(obj_description(con.oid, 'pg_constraint')) AS comment FROM pg_catalog.pg_constraint con INNER JOIN pg_catalog.pg_class rel @@ -61,7 +64,7 @@ func (d *driver) Constraints(ctx context.Context, _ drivers.ColumnFilter) (drive AND foreign_cols.table_name = out.relname WHERE nsp.nspname = ANY($1) - AND con.contype IN ('p', 'f', 'u') + AND con.contype IN ('p', 'f', 'u', 'c') GROUP BY nsp.nspname, rel.relname, name, con.contype ORDER BY nsp.nspname, rel.relname, name, con.contype` @@ -74,6 +77,8 @@ func (d *driver) Constraints(ctx context.Context, _ drivers.ColumnFilter) (drive ForeignSchema sql.NullString ForeignTable sql.NullString ForeignColumns pq.StringArray + CheckExpr sql.NullString + Comment sql.NullString }](), query, d.config.Schemas) if err != nil { return ret, err @@ -90,12 +95,16 @@ func (d *driver) Constraints(ctx context.Context, _ drivers.ColumnFilter) (drive ret.PKs[key] = &drivers.Constraint[any]{ Name: c.Name, Columns: c.Columns, + Comment: c.Comment.String, } + case "u": ret.Uniques[key] = append(ret.Uniques[c.Table], drivers.Constraint[any]{ Name: c.Name, Columns: c.Columns, + Comment: c.Comment.String, }) + case "f": fkey := c.ForeignTable.String if c.ForeignSchema.Valid && c.ForeignSchema.String != d.config.SharedSchema { @@ -105,10 +114,21 @@ func (d *driver) Constraints(ctx context.Context, _ drivers.ColumnFilter) (drive Constraint: drivers.Constraint[any]{ Name: key + "." + c.Name, Columns: c.Columns, + Comment: c.Comment.String, }, ForeignTable: fkey, ForeignColumns: c.ForeignColumns, }) + + case "c": + ret.Checks[key] = append(ret.Checks[key], drivers.Check[any]{ + Constraint: drivers.Constraint[any]{ + Name: c.Name, + Columns: c.Columns, + Comment: c.Comment.String, + }, + Expression: c.CheckExpr.String, + }) } } diff --git a/gen/bobgen-psql/driver/psql.go b/gen/bobgen-psql/driver/psql.go index f580255..78aeb0f 100644 --- a/gen/bobgen-psql/driver/psql.go +++ b/gen/bobgen-psql/driver/psql.go @@ -20,8 +20,14 @@ import ( var rgxValidColumnName = regexp.MustCompile(`(?i)^[a-z_][a-z0-9_]*$`) type ( - Interface = drivers.Interface[any, any, any] - DBInfo = drivers.DBInfo[any, any, any] + Interface = drivers.Interface[any, any, IndexExtra] + DBInfo = drivers.DBInfo[any, any, IndexExtra] + IndexExtra = struct { + NullsFirst []bool `json:"nulls_first"` // same length as Columns + NullsDistinct bool `json:"nulls_not_distinct"` + Where string `json:"where_clause"` + Include []string `json:"include"` + } ) type Enum struct { @@ -444,30 +450,52 @@ func (d *driver) loadEnums(ctx context.Context) error { return nil } -func (d *driver) Indexes(ctx context.Context) (drivers.DBIndexes[any], error) { - ret := drivers.DBIndexes[any]{} - - query := `SELECT - n.nspname AS schema_name, - t.relname AS table_name, - i.relname AS index_name, - ARRAY( - SELECT pg_get_indexdef(x.indexrelid, k + 1, true) - FROM generate_subscripts(x.indkey, 1) as k - ORDER BY k - ) AS index_cols - FROM pg_index x - JOIN pg_class t ON t.oid = x.indrelid - JOIN pg_class i ON i.oid = x.indexrelid - JOIN pg_namespace n ON n.oid = t.relnamespace +func (d *driver) Indexes(ctx context.Context) (drivers.DBIndexes[IndexExtra], error) { + ret := drivers.DBIndexes[IndexExtra]{} + + query := `SELECT + n.nspname AS schema_name, + t.relname AS table_name, + i.relname AS index_name, + a.amname AS type, + cols.cols[:x.indnkeyatts] AS index_cols, + ARRAY(SELECT unnest(x.indoption) & 1 = 1 ) AS descending, + ARRAY(SELECT unnest(x.indoption) & 2 = 2 ) AS nulls_first, + x.indisunique as unique, + x.indnullsnotdistinct as nulls_not_distinct, + pg_get_expr(x.indpred, x.indrelid) AS where_clause, + cols.cols[x.indnkeyatts+1:] AS included_cols, + obj_description(x.indexrelid, 'pg_class') AS comment + FROM pg_index x + JOIN pg_class t ON t.oid = x.indrelid + JOIN pg_class i ON i.oid = x.indexrelid + JOIN pg_am a on i.relam = a.oid + JOIN pg_namespace n ON n.oid = t.relnamespace + JOIN ( + SELECT x.indexrelid, array_agg(cols.cols) cols + FROM pg_index x + LEFT JOIN (SELECT a.attrelid, pg_get_indexdef(a.attrelid, a.attnum, TRUE) AS cols + FROM pg_attribute a) cols ON cols.attrelid = x.indexrelid + WHERE cols IS NOT NULL + GROUP BY x.indexrelid + ) cols ON cols.indexrelid = x.indexrelid WHERE n.nspname = ANY($1) - ORDER BY n.nspname, t.relname, i.relname` + AND x.indisvalid AND x.indislive AND x.indisvalid + ORDER BY n.nspname, t.relname, x.indisprimary DESC, i.relname;` type indexColumns struct { - SchemaName string - TableName string - IndexName string - IndexCols pq.StringArray // a list of column names and/or expressions + SchemaName string + TableName string + IndexName string + Type string + IndexCols pq.StringArray // a list of column names and/or expressions + Descending pq.BoolArray + NullsFirst pq.BoolArray + Unique bool + NullsNotDistinct bool + WhereClause sql.NullString + IncludedCols pq.StringArray + Comment sql.NullString } res, err := stdscan.All(ctx, d.conn, scan.StructMapper[indexColumns](), query, d.config.Schemas) if err != nil { @@ -478,17 +506,63 @@ func (d *driver) Indexes(ctx context.Context) (drivers.DBIndexes[any], error) { if r.SchemaName != "" && r.SchemaName != d.config.SharedSchema { key = r.SchemaName + "." + r.TableName } - var index drivers.Index[any] - index.Name = r.IndexName - for _, colName := range r.IndexCols { - if rgxValidColumnName.MatchString(colName) { - index.Columns = append(index.Columns, colName) - } else { - index.Expressions = append(index.Expressions, colName) - } + index := drivers.Index[IndexExtra]{ + Type: r.Type, + Name: r.IndexName, + Unique: r.Unique, + Comment: r.Comment.String, + Extra: IndexExtra{ + NullsFirst: r.NullsFirst, + NullsDistinct: r.NullsNotDistinct, + Where: r.WhereClause.String, + Include: r.IncludedCols, + }, + } + for i, colName := range r.IndexCols { + isExpression := !rgxValidColumnName.MatchString(colName) + index.Columns = append(index.Columns, drivers.IndexColumn{ + Name: colName, + Desc: r.Descending[i], + IsExpression: isExpression, + }) } ret[key] = append(ret[key], index) } return ret, nil } + +func (d *driver) Comments(ctx context.Context) (map[string]string, error) { + query := fmt.Sprintf(`SELECT + %s AS "key", + obj_description((table_schema||'.'||table_name)::regclass::oid, 'pg_class') AS comment + FROM ( + SELECT + table_name, + table_schema + FROM + information_schema.tables + UNION + SELECT + matviewname AS table_name, + schemaname AS table_schema + FROM + pg_matviews) AS v + WHERE + v.table_schema = ANY ($2)`, keyClause) + args := []any{d.config.SharedSchema, d.config.Schemas} + + comments := make(map[string]string) + + for row, err := range stdscan.Each(ctx, d.conn, scan.StructMapper[struct { + Key string + Comment sql.NullString + }](), query, args...) { + if err != nil { + return nil, err + } + comments[row.Key] = row.Comment.String + } + + return comments, nil +} diff --git a/gen/bobgen-psql/driver/psql.golden.json b/gen/bobgen-psql/driver/psql.golden.json index 9d64ac0..217ac4f 100644 --- a/gen/bobgen-psql/driver/psql.golden.json +++ b/gen/bobgen-psql/driver/psql.golden.json @@ -30,12 +30,25 @@ ], "indexes": [ { + "type": "btree", "name": "bar_baz_pkey", "columns": [ - "id" + { + "name": "id", + "desc": false, + "is_expression": false + } ], - "expressions": null, - "extra": null + "unique": true, + "comment": "", + "extra": { + "nulls_first": [ + false + ], + "nulls_not_distinct": false, + "where_clause": "", + "include": [] + } } ], "constraints": { @@ -44,11 +57,14 @@ "columns": [ "id" ], + "comment": "", "extra": null }, "foreign": null, - "uniques": null - } + "uniques": null, + "check": null + }, + "comment": "" }, { "key": "bar_qux", @@ -80,12 +96,25 @@ ], "indexes": [ { + "type": "btree", "name": "bar_qux_pkey", "columns": [ - "id" + { + "name": "id", + "desc": false, + "is_expression": false + } ], - "expressions": null, - "extra": null + "unique": true, + "comment": "", + "extra": { + "nulls_first": [ + false + ], + "nulls_not_distinct": false, + "where_clause": "", + "include": [] + } } ], "constraints": { @@ -94,11 +123,14 @@ "columns": [ "id" ], + "comment": "", "extra": null }, "foreign": null, - "uniques": null - } + "uniques": null, + "check": null + }, + "comment": "" }, { "key": "foo_bar", @@ -130,12 +162,25 @@ ], "indexes": [ { + "type": "btree", "name": "foo_bar_pkey", "columns": [ - "id" + { + "name": "id", + "desc": false, + "is_expression": false + } ], - "expressions": null, - "extra": null + "unique": true, + "comment": "", + "extra": { + "nulls_first": [ + false + ], + "nulls_not_distinct": false, + "where_clause": "", + "include": [] + } } ], "constraints": { @@ -144,11 +189,14 @@ "columns": [ "id" ], + "comment": "", "extra": null }, "foreign": null, - "uniques": null - } + "uniques": null, + "check": null + }, + "comment": "" }, { "key": "foo_baz", @@ -180,12 +228,25 @@ ], "indexes": [ { + "type": "btree", "name": "foo_baz_pkey", "columns": [ - "id" + { + "name": "id", + "desc": false, + "is_expression": false + } ], - "expressions": null, - "extra": null + "unique": true, + "comment": "", + "extra": { + "nulls_first": [ + false + ], + "nulls_not_distinct": false, + "where_clause": "", + "include": [] + } } ], "constraints": { @@ -194,11 +255,14 @@ "columns": [ "id" ], + "comment": "", "extra": null }, "foreign": null, - "uniques": null - } + "uniques": null, + "check": null + }, + "comment": "" }, { "key": "foo_qux", @@ -230,12 +294,25 @@ ], "indexes": [ { + "type": "btree", "name": "foo_qux_pkey", "columns": [ - "id" + { + "name": "id", + "desc": false, + "is_expression": false + } ], - "expressions": null, - "extra": null + "unique": true, + "comment": "", + "extra": { + "nulls_first": [ + false + ], + "nulls_not_distinct": false, + "where_clause": "", + "include": [] + } } ], "constraints": { @@ -244,11 +321,14 @@ "columns": [ "id" ], + "comment": "", "extra": null }, "foreign": null, - "uniques": null - } + "uniques": null, + "check": null + }, + "comment": "" }, { "key": "sponsors", @@ -269,12 +349,25 @@ ], "indexes": [ { + "type": "btree", "name": "sponsors_pkey", "columns": [ - "id" + { + "name": "id", + "desc": false, + "is_expression": false + } ], - "expressions": null, - "extra": null + "unique": true, + "comment": "", + "extra": { + "nulls_first": [ + false + ], + "nulls_not_distinct": false, + "where_clause": "", + "include": [] + } } ], "constraints": { @@ -283,11 +376,14 @@ "columns": [ "id" ], + "comment": "", "extra": null }, "foreign": null, - "uniques": null - } + "uniques": null, + "check": null + }, + "comment": "" }, { "key": "tags", @@ -308,12 +404,25 @@ ], "indexes": [ { + "type": "btree", "name": "tags_pkey", "columns": [ - "id" + { + "name": "id", + "desc": false, + "is_expression": false + } ], - "expressions": null, - "extra": null + "unique": true, + "comment": "", + "extra": { + "nulls_first": [ + false + ], + "nulls_not_distinct": false, + "where_clause": "", + "include": [] + } } ], "constraints": { @@ -322,11 +431,14 @@ "columns": [ "id" ], + "comment": "", "extra": null }, "foreign": null, - "uniques": null - } + "uniques": null, + "check": null + }, + "comment": "" }, { "key": "test_index_expressions", @@ -369,64 +481,157 @@ ], "indexes": [ { + "type": "btree", "name": "idx1", - "columns": null, - "expressions": [ - "(col1 + col2)" + "columns": [ + { + "name": "(col1 + col2)", + "desc": false, + "is_expression": true + } ], - "extra": null + "unique": false, + "comment": "This is an index", + "extra": { + "nulls_first": [ + false + ], + "nulls_not_distinct": false, + "where_clause": "", + "include": [] + } }, { + "type": "btree", "name": "idx2", "columns": [ - "col3" - ], - "expressions": [ - "(col1 + col2)" + { + "name": "(col1 + col2)", + "desc": false, + "is_expression": true + }, + { + "name": "col3", + "desc": false, + "is_expression": false + } ], - "extra": null + "unique": false, + "comment": "", + "extra": { + "nulls_first": [ + false, + false + ], + "nulls_not_distinct": false, + "where_clause": "", + "include": [] + } }, { + "type": "btree", "name": "idx3", "columns": [ - "col1" + { + "name": "col1", + "desc": false, + "is_expression": false + }, + { + "name": "(col2 + col3)", + "desc": false, + "is_expression": true + } ], - "expressions": [ - "(col2 + col3)" - ], - "extra": null + "unique": false, + "comment": "", + "extra": { + "nulls_first": [ + false, + false + ], + "nulls_not_distinct": false, + "where_clause": "", + "include": [] + } }, { + "type": "btree", "name": "idx4", "columns": [ - "col3" + { + "name": "col3", + "desc": false, + "is_expression": false + } ], - "expressions": null, - "extra": null + "unique": false, + "comment": "", + "extra": { + "nulls_first": [ + false + ], + "nulls_not_distinct": false, + "where_clause": "", + "include": [] + } }, { + "type": "btree", "name": "idx5", "columns": [ - "col1", - "col2" + { + "name": "col1", + "desc": true, + "is_expression": false + }, + { + "name": "col2", + "desc": true, + "is_expression": false + } ], - "expressions": null, - "extra": null + "unique": false, + "comment": "", + "extra": { + "nulls_first": [ + true, + true + ], + "nulls_not_distinct": false, + "where_clause": "", + "include": [] + } }, { + "type": "btree", "name": "idx6", - "columns": null, - "expressions": [ - "pow(col3::double precision, 2::double precision)" + "columns": [ + { + "name": "pow(col3::double precision, 2::double precision)", + "desc": false, + "is_expression": true + } ], - "extra": null + "unique": false, + "comment": "", + "extra": { + "nulls_first": [ + false + ], + "nulls_not_distinct": false, + "where_clause": "", + "include": [] + } } ], "constraints": { "primary": null, "foreign": null, - "uniques": null - } + "uniques": null, + "check": null + }, + "comment": "" }, { "key": "type_monsters", @@ -2207,12 +2412,25 @@ ], "indexes": [ { + "type": "btree", "name": "type_monsters_pkey", "columns": [ - "id" + { + "name": "id", + "desc": false, + "is_expression": false + } ], - "expressions": null, - "extra": null + "unique": true, + "comment": "", + "extra": { + "nulls_first": [ + false + ], + "nulls_not_distinct": false, + "where_clause": "", + "include": [] + } } ], "constraints": { @@ -2221,11 +2439,24 @@ "columns": [ "id" ], + "comment": "", "extra": null }, "foreign": null, - "uniques": null - } + "uniques": null, + "check": [ + { + "name": "check_not_null", + "columns": [ + "bool_two" + ], + "expression": "(bool_two IS NOT NULL)", + "comment": "This is a constraint", + "extra": null + } + ] + }, + "comment": "This is a table" }, { "key": "type_monsters_mv", @@ -2236,8 +2467,10 @@ "constraints": { "primary": null, "foreign": null, - "uniques": null - } + "uniques": null, + "check": null + }, + "comment": "This is a materialized view" }, { "key": "type_monsters_v", @@ -4020,8 +4253,10 @@ "constraints": { "primary": null, "foreign": null, - "uniques": null - } + "uniques": null, + "check": null + }, + "comment": "This is a view" }, { "key": "user_videos", @@ -4066,8 +4301,10 @@ "constraints": { "primary": null, "foreign": null, - "uniques": null - } + "uniques": null, + "check": null + }, + "comment": "" }, { "key": "users", @@ -4143,28 +4380,67 @@ ], "indexes": [ { - "name": "users_party_id_key", + "type": "btree", + "name": "users_pkey", "columns": [ - "party_id" + { + "name": "id", + "desc": false, + "is_expression": false + } ], - "expressions": null, - "extra": null + "unique": true, + "comment": "", + "extra": { + "nulls_first": [ + false + ], + "nulls_not_distinct": false, + "where_clause": "", + "include": [] + } }, { - "name": "users_pkey", + "type": "btree", + "name": "users_party_id_key", "columns": [ - "id" + { + "name": "party_id", + "desc": false, + "is_expression": false + } ], - "expressions": null, - "extra": null + "unique": true, + "comment": "", + "extra": { + "nulls_first": [ + false + ], + "nulls_not_distinct": false, + "where_clause": "", + "include": [] + } }, { + "type": "btree", "name": "users_primary_email_key", "columns": [ - "primary_email" + { + "name": "primary_email", + "desc": false, + "is_expression": false + } ], - "expressions": null, - "extra": null + "unique": true, + "comment": "", + "extra": { + "nulls_first": [ + false + ], + "nulls_not_distinct": false, + "where_clause": "", + "include": [] + } } ], "constraints": { @@ -4173,6 +4449,7 @@ "columns": [ "id" ], + "comment": "", "extra": null }, "foreign": [ @@ -4181,33 +4458,36 @@ "columns": [ "parent_id" ], - "extra": null, "foreign_table": "users", "foreign_columns": [ "id" - ] + ], + "comment": "", + "extra": null }, { "name": "users.users_party_id_fkey", "columns": [ "party_id" ], - "extra": null, "foreign_table": "users", "foreign_columns": [ "id" - ] + ], + "comment": "", + "extra": null }, { "name": "users.users_referrer_fkey", "columns": [ "referrer" ], - "extra": null, "foreign_table": "users", "foreign_columns": [ "id" - ] + ], + "comment": "", + "extra": null } ], "uniques": [ @@ -4216,6 +4496,7 @@ "columns": [ "party_id" ], + "comment": "", "extra": null }, { @@ -4223,10 +4504,13 @@ "columns": [ "primary_email" ], + "comment": "", "extra": null } - ] - } + ], + "check": null + }, + "comment": "" }, { "key": "video_tags", @@ -4258,13 +4542,31 @@ ], "indexes": [ { + "type": "btree", "name": "video_tags_pkey", "columns": [ - "video_id", - "tag_id" + { + "name": "video_id", + "desc": false, + "is_expression": false + }, + { + "name": "tag_id", + "desc": false, + "is_expression": false + } ], - "expressions": null, - "extra": null + "unique": true, + "comment": "", + "extra": { + "nulls_first": [ + false, + false + ], + "nulls_not_distinct": false, + "where_clause": "", + "include": [] + } } ], "constraints": { @@ -4274,6 +4576,7 @@ "video_id", "tag_id" ], + "comment": "", "extra": null }, "foreign": [ @@ -4282,26 +4585,30 @@ "columns": [ "tag_id" ], - "extra": null, "foreign_table": "tags", "foreign_columns": [ "id" - ] + ], + "comment": "", + "extra": null }, { "name": "video_tags.video_tags_video_id_fkey", "columns": [ "video_id" ], - "extra": null, "foreign_table": "videos", "foreign_columns": [ "id" - ] + ], + "comment": "", + "extra": null } ], - "uniques": null - } + "uniques": null, + "check": null + }, + "comment": "" }, { "key": "videos", @@ -4344,20 +4651,46 @@ ], "indexes": [ { + "type": "btree", "name": "videos_pkey", "columns": [ - "id" + { + "name": "id", + "desc": false, + "is_expression": false + } ], - "expressions": null, - "extra": null + "unique": true, + "comment": "", + "extra": { + "nulls_first": [ + false + ], + "nulls_not_distinct": false, + "where_clause": "", + "include": [] + } }, { + "type": "btree", "name": "videos_sponsor_id_key", "columns": [ - "sponsor_id" + { + "name": "sponsor_id", + "desc": false, + "is_expression": false + } ], - "expressions": null, - "extra": null + "unique": true, + "comment": "", + "extra": { + "nulls_first": [ + false + ], + "nulls_not_distinct": false, + "where_clause": "", + "include": [] + } } ], "constraints": { @@ -4366,6 +4699,7 @@ "columns": [ "id" ], + "comment": "", "extra": null }, "foreign": [ @@ -4374,22 +4708,24 @@ "columns": [ "sponsor_id" ], - "extra": null, "foreign_table": "sponsors", "foreign_columns": [ "id" - ] + ], + "comment": "", + "extra": null }, { "name": "videos.videos_user_id_fkey", "columns": [ "user_id" ], - "extra": null, "foreign_table": "users", "foreign_columns": [ "id" - ] + ], + "comment": "", + "extra": null } ], "uniques": [ @@ -4398,10 +4734,13 @@ "columns": [ "sponsor_id" ], + "comment": "", "extra": null } - ] - } + ], + "check": null + }, + "comment": "" } ], "enums": [ diff --git a/gen/bobgen-psql/driver/psql_test.go b/gen/bobgen-psql/driver/psql_test.go index 96b4e99..0aadd1c 100644 --- a/gen/bobgen-psql/driver/psql_test.go +++ b/gen/bobgen-psql/driver/psql_test.go @@ -141,8 +141,19 @@ func TestDriver(t *testing.T) { }, } - for _, tt := range tests { + for i, tt := range tests { t.Run(tt.name, func(t *testing.T) { + if i > 0 { + testgen.TestAssemble(t, testgen.AssembleTestConfig[any, any, IndexExtra]{ + GetDriver: func() drivers.Interface[any, any, IndexExtra] { + return New(tt.config) + }, + GoldenFile: tt.goldenJson, + OverwriteGolden: *flagOverwriteGolden, + }) + return + } + out, err := os.MkdirTemp("", "bobgen_psql_") if err != nil { t.Fatalf("unable to create tempdir: %s", err) @@ -157,9 +168,9 @@ func TestDriver(t *testing.T) { os.RemoveAll(out) }() - testgen.TestDriver(t, testgen.DriverTestConfig[any, any, any]{ + testgen.TestDriver(t, testgen.DriverTestConfig[any, any, IndexExtra]{ Root: out, - GetDriver: func() drivers.Interface[any, any, any] { + GetDriver: func() drivers.Interface[any, any, IndexExtra] { return New(tt.config) }, GoldenFile: tt.goldenJson, diff --git a/gen/bobgen-psql/driver/translate.go b/gen/bobgen-psql/driver/translate.go index 99bae24..2e802c8 100644 --- a/gen/bobgen-psql/driver/translate.go +++ b/gen/bobgen-psql/driver/translate.go @@ -13,9 +13,9 @@ type colInfo struct { // ArrType is the underlying data type of the Postgres // ARRAY type. See here: // https://www.postgresql.org/docs/9.1/static/infoschema-element-types.html - ArrType string `json:"arr_type" yaml:"arr_type" toml:"arr_type"` - UDTName string `json:"udt_name" yaml:"udt_name" toml:"udt_name"` - UDTSchema string `json:"udt_schema" yaml:"udt_schema" toml:"udt_schema"` + ArrType string `json:"arr_type" yaml:"arr_type"` + UDTName string `json:"udt_name" yaml:"udt_name"` + UDTSchema string `json:"udt_schema" yaml:"udt_schema"` } // translateColumnType converts postgres database types to Go types, for example diff --git a/gen/bobgen-sql/driver/sql_test.go b/gen/bobgen-sql/driver/sql_test.go index 27b70ae..becce35 100644 --- a/gen/bobgen-sql/driver/sql_test.go +++ b/gen/bobgen-sql/driver/sql_test.go @@ -8,6 +8,8 @@ import ( "testing" _ "github.com/jackc/pgx/v5/stdlib" + psqlDriver "github.com/stephenafamo/bob/gen/bobgen-psql/driver" + sqliteDriver "github.com/stephenafamo/bob/gen/bobgen-sqlite/driver" "github.com/stephenafamo/bob/gen/drivers" testfiles "github.com/stephenafamo/bob/test/files" testgen "github.com/stephenafamo/bob/test/gen" @@ -22,9 +24,9 @@ func TestPostgres(t *testing.T) { fs: testfiles.PostgresSchema, } - testgen.TestDriver(t, testgen.DriverTestConfig[any, any, any]{ + testgen.TestDriver(t, testgen.DriverTestConfig[any, any, psqlDriver.IndexExtra]{ Root: out, - GetDriver: func() drivers.Interface[any, any, any] { + GetDriver: func() drivers.Interface[any, any, psqlDriver.IndexExtra] { d, err := getPsqlDriver(context.Background(), config) if err != nil { t.Fatalf("getting psql driver: %s", err) @@ -46,9 +48,9 @@ func TestSQLite(t *testing.T) { Schemas: []string{"one"}, } - testgen.TestDriver(t, testgen.DriverTestConfig[any, any, any]{ + testgen.TestDriver(t, testgen.DriverTestConfig[any, any, sqliteDriver.IndexExtra]{ Root: out, - GetDriver: func() drivers.Interface[any, any, any] { + GetDriver: func() drivers.Interface[any, any, sqliteDriver.IndexExtra] { d, err := getSQLiteDriver(context.Background(), config) if err != nil { t.Fatalf("getting sqlite driver: %s", err) diff --git a/gen/bobgen-sqlite/driver/exclude-tables.golden.json b/gen/bobgen-sqlite/driver/exclude-tables.golden.json index 4878262..8087fdb 100644 --- a/gen/bobgen-sqlite/driver/exclude-tables.golden.json +++ b/gen/bobgen-sqlite/driver/exclude-tables.golden.json @@ -63,21 +63,57 @@ ], "indexes": [ { - "name": "sqlite_autoindex_autoinckeywordtest_1", + "type": "pk", + "name": "pk_main_autoinckeywordtest", "columns": [ - "sponsor_id" + { + "name": "id", + "desc": false, + "is_expression": false + } ], - "expressions": null, - "extra": null + "unique": true, + "comment": "", + "extra": { + "partial": false + } }, { + "type": "u", "name": "sqlite_autoindex_autoinckeywordtest_2", "columns": [ - "something", - "another" + { + "name": "something", + "desc": false, + "is_expression": false + }, + { + "name": "another", + "desc": false, + "is_expression": false + } ], - "expressions": null, - "extra": null + "unique": true, + "comment": "", + "extra": { + "partial": false + } + }, + { + "type": "u", + "name": "sqlite_autoindex_autoinckeywordtest_1", + "columns": [ + { + "name": "sponsor_id", + "desc": false, + "is_expression": false + } + ], + "unique": true, + "comment": "", + "extra": { + "partial": false + } } ], "constraints": { @@ -86,6 +122,7 @@ "columns": [ "id" ], + "comment": "", "extra": null }, "foreign": [ @@ -95,12 +132,13 @@ "user_id", "sponsor_id" ], - "extra": null, "foreign_table": "videos", "foreign_columns": [ "user_id", "sponsor_id" - ] + ], + "comment": "", + "extra": null } ], "uniques": [ @@ -110,6 +148,7 @@ "something", "another" ], + "comment": "", "extra": null }, { @@ -117,10 +156,13 @@ "columns": [ "sponsor_id" ], + "comment": "", "extra": null } - ] - } + ], + "check": null + }, + "comment": "" }, { "key": "autoinctest", @@ -139,13 +181,31 @@ "type": "int32" } ], - "indexes": [], + "indexes": [ + { + "type": "pk", + "name": "pk_main_autoinctest", + "columns": [ + { + "name": "id", + "desc": false, + "is_expression": false + } + ], + "unique": true, + "comment": "", + "extra": { + "partial": false + } + } + ], "constraints": { "primary": { "name": "pk_main_autoinctest", "columns": [ "id" ], + "comment": "", "extra": null }, "foreign": [ @@ -154,15 +214,18 @@ "columns": [ "id" ], - "extra": null, "foreign_table": "tags", "foreign_columns": [ "id" - ] + ], + "comment": "", + "extra": null } ], - "uniques": [] - } + "uniques": null, + "check": null + }, + "comment": "" }, { "key": "bar_baz", @@ -181,18 +244,38 @@ "type": "int32" } ], - "indexes": [], + "indexes": [ + { + "type": "pk", + "name": "pk_main_bar_baz", + "columns": [ + { + "name": "id", + "desc": false, + "is_expression": false + } + ], + "unique": true, + "comment": "", + "extra": { + "partial": false + } + } + ], "constraints": { "primary": { "name": "pk_main_bar_baz", "columns": [ "id" ], + "comment": "", "extra": null }, "foreign": [], - "uniques": [] - } + "uniques": null, + "check": null + }, + "comment": "" }, { "key": "bar_qux", @@ -211,18 +294,38 @@ "type": "int32" } ], - "indexes": [], + "indexes": [ + { + "type": "pk", + "name": "pk_main_bar_qux", + "columns": [ + { + "name": "id", + "desc": false, + "is_expression": false + } + ], + "unique": true, + "comment": "", + "extra": { + "partial": false + } + } + ], "constraints": { "primary": { "name": "pk_main_bar_qux", "columns": [ "id" ], + "comment": "", "extra": null }, "foreign": [], - "uniques": [] - } + "uniques": null, + "check": null + }, + "comment": "" }, { "key": "foo_qux", @@ -241,18 +344,38 @@ "type": "int32" } ], - "indexes": [], + "indexes": [ + { + "type": "pk", + "name": "pk_main_foo_qux", + "columns": [ + { + "name": "id", + "desc": false, + "is_expression": false + } + ], + "unique": true, + "comment": "", + "extra": { + "partial": false + } + } + ], "constraints": { "primary": { "name": "pk_main_foo_qux", "columns": [ "id" ], + "comment": "", "extra": null }, "foreign": [], - "uniques": [] - } + "uniques": null, + "check": null + }, + "comment": "" }, { "key": "has_generated_columns", @@ -315,18 +438,38 @@ "type": "string" } ], - "indexes": [], + "indexes": [ + { + "type": "pk", + "name": "pk_main_has_generated_columns", + "columns": [ + { + "name": "a", + "desc": false, + "is_expression": false + } + ], + "unique": true, + "comment": "", + "extra": { + "partial": false + } + } + ], "constraints": { "primary": { "name": "pk_main_has_generated_columns", "columns": [ "a" ], + "comment": "", "extra": null }, "foreign": [], - "uniques": [] - } + "uniques": null, + "check": null + }, + "comment": "" }, { "key": "one.as_generated_columns", @@ -389,18 +532,38 @@ "type": "string" } ], - "indexes": [], + "indexes": [ + { + "type": "pk", + "name": "pk_one_as_generated_columns", + "columns": [ + { + "name": "a", + "desc": false, + "is_expression": false + } + ], + "unique": true, + "comment": "", + "extra": { + "partial": false + } + } + ], "constraints": { "primary": { "name": "pk_one_as_generated_columns", "columns": [ "a" ], + "comment": "", "extra": null }, "foreign": [], - "uniques": [] - } + "uniques": null, + "check": null + }, + "comment": "" }, { "key": "one.autoinckeywordtest", @@ -432,21 +595,20 @@ ], "indexes": [ { - "name": "sqlite_autoindex_autoinckeywordtest_1", - "columns": [ - "sponsor_id" - ], - "expressions": null, - "extra": null - }, - { - "name": "sqlite_autoindex_autoinckeywordtest_2", + "type": "pk", + "name": "pk_one_autoinckeywordtest", "columns": [ - "something", - "another" + { + "name": "id", + "desc": false, + "is_expression": false + } ], - "expressions": null, - "extra": null + "unique": true, + "comment": "", + "extra": { + "partial": false + } } ], "constraints": { @@ -455,11 +617,14 @@ "columns": [ "id" ], + "comment": "", "extra": null }, "foreign": [], - "uniques": [] - } + "uniques": null, + "check": null + }, + "comment": "" }, { "key": "one.autoinctest", @@ -478,18 +643,38 @@ "type": "int32" } ], - "indexes": [], + "indexes": [ + { + "type": "pk", + "name": "pk_one_autoinctest", + "columns": [ + { + "name": "id", + "desc": false, + "is_expression": false + } + ], + "unique": true, + "comment": "", + "extra": { + "partial": false + } + } + ], "constraints": { "primary": { "name": "pk_one_autoinctest", "columns": [ "id" ], + "comment": "", "extra": null }, "foreign": [], - "uniques": [] - } + "uniques": null, + "check": null + }, + "comment": "" }, { "key": "one.bar_baz", @@ -508,18 +693,38 @@ "type": "int32" } ], - "indexes": [], + "indexes": [ + { + "type": "pk", + "name": "pk_one_bar_baz", + "columns": [ + { + "name": "id", + "desc": false, + "is_expression": false + } + ], + "unique": true, + "comment": "", + "extra": { + "partial": false + } + } + ], "constraints": { "primary": { "name": "pk_one_bar_baz", "columns": [ "id" ], + "comment": "", "extra": null }, "foreign": [], - "uniques": [] - } + "uniques": null, + "check": null + }, + "comment": "" }, { "key": "one.bar_qux", @@ -538,18 +743,38 @@ "type": "int32" } ], - "indexes": [], + "indexes": [ + { + "type": "pk", + "name": "pk_one_bar_qux", + "columns": [ + { + "name": "id", + "desc": false, + "is_expression": false + } + ], + "unique": true, + "comment": "", + "extra": { + "partial": false + } + } + ], "constraints": { "primary": { "name": "pk_one_bar_qux", "columns": [ "id" ], + "comment": "", "extra": null }, "foreign": [], - "uniques": [] - } + "uniques": null, + "check": null + }, + "comment": "" }, { "key": "one.foo_qux", @@ -568,18 +793,38 @@ "type": "int32" } ], - "indexes": [], + "indexes": [ + { + "type": "pk", + "name": "pk_one_foo_qux", + "columns": [ + { + "name": "id", + "desc": false, + "is_expression": false + } + ], + "unique": true, + "comment": "", + "extra": { + "partial": false + } + } + ], "constraints": { "primary": { "name": "pk_one_foo_qux", "columns": [ "id" ], + "comment": "", "extra": null }, "foreign": [], - "uniques": [] - } + "uniques": null, + "check": null + }, + "comment": "" }, { "key": "one.sponsors", @@ -600,12 +845,20 @@ ], "indexes": [ { + "type": "pk", "name": "sqlite_autoindex_sponsors_1", "columns": [ - "id" + { + "name": "id", + "desc": false, + "is_expression": false + } ], - "expressions": null, - "extra": null + "unique": true, + "comment": "", + "extra": { + "partial": false + } } ], "constraints": { @@ -614,11 +867,14 @@ "columns": [ "id" ], + "comment": "", "extra": null }, "foreign": [], - "uniques": [] - } + "uniques": null, + "check": null + }, + "comment": "" }, { "key": "one.tags", @@ -639,12 +895,20 @@ ], "indexes": [ { + "type": "pk", "name": "sqlite_autoindex_tags_1", "columns": [ - "id" + { + "name": "id", + "desc": false, + "is_expression": false + } ], - "expressions": null, - "extra": null + "unique": true, + "comment": "", + "extra": { + "partial": false + } } ], "constraints": { @@ -653,11 +917,14 @@ "columns": [ "id" ], + "comment": "", "extra": null }, "foreign": [], - "uniques": [] - } + "uniques": null, + "check": null + }, + "comment": "" }, { "key": "one.user_videos", @@ -702,8 +969,10 @@ "constraints": { "primary": null, "foreign": [], - "uniques": [] - } + "uniques": null, + "check": null + }, + "comment": "" }, { "key": "one.users", @@ -724,12 +993,20 @@ ], "indexes": [ { + "type": "pk", "name": "sqlite_autoindex_users_1", "columns": [ - "id" + { + "name": "id", + "desc": false, + "is_expression": false + } ], - "expressions": null, - "extra": null + "unique": true, + "comment": "", + "extra": { + "partial": false + } } ], "constraints": { @@ -738,11 +1015,14 @@ "columns": [ "id" ], + "comment": "", "extra": null }, "foreign": [], - "uniques": [] - } + "uniques": null, + "check": null + }, + "comment": "" }, { "key": "one.video_tags", @@ -774,13 +1054,25 @@ ], "indexes": [ { + "type": "pk", "name": "sqlite_autoindex_video_tags_1", "columns": [ - "video_id", - "tag_id" + { + "name": "video_id", + "desc": false, + "is_expression": false + }, + { + "name": "tag_id", + "desc": false, + "is_expression": false + } ], - "expressions": null, - "extra": null + "unique": true, + "comment": "", + "extra": { + "partial": false + } } ], "constraints": { @@ -790,6 +1082,7 @@ "video_id", "tag_id" ], + "comment": "", "extra": null }, "foreign": [ @@ -798,26 +1091,30 @@ "columns": [ "tag_id" ], - "extra": null, "foreign_table": "one.tags", "foreign_columns": [ "id" - ] + ], + "comment": "", + "extra": null }, { "name": "fk_video_tags_1", "columns": [ "video_id" ], - "extra": null, "foreign_table": "one.videos", "foreign_columns": [ "id" - ] + ], + "comment": "", + "extra": null } ], - "uniques": [] - } + "uniques": null, + "check": null + }, + "comment": "" }, { "key": "one.videos", @@ -860,20 +1157,36 @@ ], "indexes": [ { - "name": "sqlite_autoindex_videos_1", + "type": "u", + "name": "sqlite_autoindex_videos_2", "columns": [ - "id" + { + "name": "sponsor_id", + "desc": false, + "is_expression": false + } ], - "expressions": null, - "extra": null + "unique": true, + "comment": "", + "extra": { + "partial": false + } }, { - "name": "sqlite_autoindex_videos_2", + "type": "pk", + "name": "sqlite_autoindex_videos_1", "columns": [ - "sponsor_id" + { + "name": "id", + "desc": false, + "is_expression": false + } ], - "expressions": null, - "extra": null + "unique": true, + "comment": "", + "extra": { + "partial": false + } } ], "constraints": { @@ -882,6 +1195,7 @@ "columns": [ "id" ], + "comment": "", "extra": null }, "foreign": [ @@ -890,22 +1204,24 @@ "columns": [ "sponsor_id" ], - "extra": null, "foreign_table": "one.sponsors", "foreign_columns": [ "id" - ] + ], + "comment": "", + "extra": null }, { "name": "fk_videos_1", "columns": [ "user_id" ], - "extra": null, "foreign_table": "one.users", "foreign_columns": [ "id" - ] + ], + "comment": "", + "extra": null } ], "uniques": [ @@ -914,10 +1230,13 @@ "columns": [ "sponsor_id" ], + "comment": "", "extra": null } - ] - } + ], + "check": null + }, + "comment": "" }, { "key": "sponsors", @@ -938,12 +1257,20 @@ ], "indexes": [ { + "type": "pk", "name": "sqlite_autoindex_sponsors_1", "columns": [ - "id" + { + "name": "id", + "desc": false, + "is_expression": false + } ], - "expressions": null, - "extra": null + "unique": true, + "comment": "", + "extra": { + "partial": false + } } ], "constraints": { @@ -952,11 +1279,14 @@ "columns": [ "id" ], + "comment": "", "extra": null }, "foreign": [], - "uniques": [] - } + "uniques": null, + "check": null + }, + "comment": "" }, { "key": "tags", @@ -977,12 +1307,20 @@ ], "indexes": [ { + "type": "pk", "name": "sqlite_autoindex_tags_1", "columns": [ - "id" + { + "name": "id", + "desc": false, + "is_expression": false + } ], - "expressions": null, - "extra": null + "unique": true, + "comment": "", + "extra": { + "partial": false + } } ], "constraints": { @@ -991,11 +1329,14 @@ "columns": [ "id" ], + "comment": "", "extra": null }, "foreign": [], - "uniques": [] - } + "uniques": null, + "check": null + }, + "comment": "" }, { "key": "test_index_expressions", @@ -1038,64 +1379,124 @@ ], "indexes": [ { - "name": "idx1", - "columns": null, - "expressions": [ - "(col1 + col2)" + "type": "c", + "name": "idx6", + "columns": [ + { + "name": "POW(col3, 2)", + "desc": false, + "is_expression": true + } ], - "extra": null + "unique": false, + "comment": "", + "extra": { + "partial": false + } }, { - "name": "idx2", + "type": "c", + "name": "idx5", "columns": [ - "col3" + { + "name": "col1", + "desc": true, + "is_expression": false + }, + { + "name": "col2", + "desc": true, + "is_expression": false + } ], - "expressions": [ - "(col1 + col2)" - ], - "extra": null + "unique": false, + "comment": "", + "extra": { + "partial": false + } }, { - "name": "idx3", + "type": "c", + "name": "idx4", "columns": [ - "col1" + { + "name": "col3", + "desc": false, + "is_expression": false + } ], - "expressions": [ - "(col2 + col3)" - ], - "extra": null + "unique": false, + "comment": "", + "extra": { + "partial": false + } }, { - "name": "idx4", + "type": "c", + "name": "idx3", "columns": [ - "col3" + { + "name": "col1", + "desc": false, + "is_expression": false + }, + { + "name": "(col2 + col3)", + "desc": false, + "is_expression": true + } ], - "expressions": null, - "extra": null + "unique": false, + "comment": "", + "extra": { + "partial": false + } }, { - "name": "idx5", + "type": "c", + "name": "idx2", "columns": [ - "col1", - "col2" + { + "name": "(col1 + col2)", + "desc": false, + "is_expression": true + }, + { + "name": "col3", + "desc": false, + "is_expression": false + } ], - "expressions": null, - "extra": null + "unique": false, + "comment": "", + "extra": { + "partial": false + } }, { - "name": "idx6", - "columns": null, - "expressions": [ - "POW(col3, 2)" + "type": "c", + "name": "idx1", + "columns": [ + { + "name": "(col1 + col2)", + "desc": false, + "is_expression": true + } ], - "extra": null + "unique": false, + "comment": "", + "extra": { + "partial": false + } } ], "constraints": { "primary": null, "foreign": [], - "uniques": [] - } + "uniques": null, + "check": null + }, + "comment": "" }, { "key": "type_monsters", @@ -2348,12 +2749,20 @@ ], "indexes": [ { + "type": "pk", "name": "sqlite_autoindex_type_monsters_1", "columns": [ - "id" + { + "name": "id", + "desc": false, + "is_expression": false + } ], - "expressions": null, - "extra": null + "unique": true, + "comment": "", + "extra": { + "partial": false + } } ], "constraints": { @@ -2362,11 +2771,14 @@ "columns": [ "id" ], + "comment": "", "extra": null }, "foreign": [], - "uniques": [] - } + "uniques": null, + "check": null + }, + "comment": "" }, { "key": "user_videos", @@ -2411,8 +2823,10 @@ "constraints": { "primary": null, "foreign": [], - "uniques": [] - } + "uniques": null, + "check": null + }, + "comment": "" }, { "key": "users", @@ -2433,12 +2847,20 @@ ], "indexes": [ { + "type": "pk", "name": "sqlite_autoindex_users_1", "columns": [ - "id" + { + "name": "id", + "desc": false, + "is_expression": false + } ], - "expressions": null, - "extra": null + "unique": true, + "comment": "", + "extra": { + "partial": false + } } ], "constraints": { @@ -2447,11 +2869,14 @@ "columns": [ "id" ], + "comment": "", "extra": null }, "foreign": [], - "uniques": [] - } + "uniques": null, + "check": null + }, + "comment": "" }, { "key": "video_tags", @@ -2483,13 +2908,25 @@ ], "indexes": [ { + "type": "pk", "name": "sqlite_autoindex_video_tags_1", "columns": [ - "video_id", - "tag_id" + { + "name": "video_id", + "desc": false, + "is_expression": false + }, + { + "name": "tag_id", + "desc": false, + "is_expression": false + } ], - "expressions": null, - "extra": null + "unique": true, + "comment": "", + "extra": { + "partial": false + } } ], "constraints": { @@ -2499,6 +2936,7 @@ "video_id", "tag_id" ], + "comment": "", "extra": null }, "foreign": [ @@ -2507,26 +2945,30 @@ "columns": [ "tag_id" ], - "extra": null, "foreign_table": "tags", "foreign_columns": [ "id" - ] + ], + "comment": "", + "extra": null }, { "name": "fk_video_tags_1", "columns": [ "video_id" ], - "extra": null, "foreign_table": "videos", "foreign_columns": [ "id" - ] + ], + "comment": "", + "extra": null } ], - "uniques": [] - } + "uniques": null, + "check": null + }, + "comment": "" }, { "key": "videos", @@ -2569,20 +3011,36 @@ ], "indexes": [ { - "name": "sqlite_autoindex_videos_1", + "type": "u", + "name": "sqlite_autoindex_videos_2", "columns": [ - "id" + { + "name": "sponsor_id", + "desc": false, + "is_expression": false + } ], - "expressions": null, - "extra": null + "unique": true, + "comment": "", + "extra": { + "partial": false + } }, { - "name": "sqlite_autoindex_videos_2", + "type": "pk", + "name": "sqlite_autoindex_videos_1", "columns": [ - "sponsor_id" + { + "name": "id", + "desc": false, + "is_expression": false + } ], - "expressions": null, - "extra": null + "unique": true, + "comment": "", + "extra": { + "partial": false + } } ], "constraints": { @@ -2591,6 +3049,7 @@ "columns": [ "id" ], + "comment": "", "extra": null }, "foreign": [ @@ -2599,22 +3058,24 @@ "columns": [ "sponsor_id" ], - "extra": null, "foreign_table": "sponsors", "foreign_columns": [ "id" - ] + ], + "comment": "", + "extra": null }, { "name": "fk_videos_1", "columns": [ "user_id" ], - "extra": null, "foreign_table": "users", "foreign_columns": [ "id" - ] + ], + "comment": "", + "extra": null } ], "uniques": [ @@ -2623,10 +3084,13 @@ "columns": [ "sponsor_id" ], + "comment": "", "extra": null } - ] - } + ], + "check": null + }, + "comment": "" } ], "enums": 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 8a5ca3d..e085298 100644 --- a/gen/bobgen-sqlite/driver/include-exclude-tables-mixed.golden.json +++ b/gen/bobgen-sqlite/driver/include-exclude-tables-mixed.golden.json @@ -28,18 +28,38 @@ "type": "string" } ], - "indexes": [], + "indexes": [ + { + "type": "pk", + "name": "pk_main_bar_baz", + "columns": [ + { + "name": "id", + "desc": false, + "is_expression": false + } + ], + "unique": true, + "comment": "", + "extra": { + "partial": false + } + } + ], "constraints": { "primary": { "name": "pk_main_bar_baz", "columns": [ "id" ], + "comment": "", "extra": null }, "foreign": [], - "uniques": [] - } + "uniques": null, + "check": null + }, + "comment": "" }, { "key": "bar_qux", @@ -69,18 +89,38 @@ "type": "string" } ], - "indexes": [], + "indexes": [ + { + "type": "pk", + "name": "pk_main_bar_qux", + "columns": [ + { + "name": "id", + "desc": false, + "is_expression": false + } + ], + "unique": true, + "comment": "", + "extra": { + "partial": false + } + } + ], "constraints": { "primary": { "name": "pk_main_bar_qux", "columns": [ "id" ], + "comment": "", "extra": null }, "foreign": [], - "uniques": [] - } + "uniques": null, + "check": null + }, + "comment": "" }, { "key": "one.bar_baz", @@ -110,18 +150,38 @@ "type": "string" } ], - "indexes": [], + "indexes": [ + { + "type": "pk", + "name": "pk_one_bar_baz", + "columns": [ + { + "name": "id", + "desc": false, + "is_expression": false + } + ], + "unique": true, + "comment": "", + "extra": { + "partial": false + } + } + ], "constraints": { "primary": { "name": "pk_one_bar_baz", "columns": [ "id" ], + "comment": "", "extra": null }, "foreign": [], - "uniques": [] - } + "uniques": null, + "check": null + }, + "comment": "" }, { "key": "one.bar_qux", @@ -151,18 +211,38 @@ "type": "string" } ], - "indexes": [], + "indexes": [ + { + "type": "pk", + "name": "pk_one_bar_qux", + "columns": [ + { + "name": "id", + "desc": false, + "is_expression": false + } + ], + "unique": true, + "comment": "", + "extra": { + "partial": false + } + } + ], "constraints": { "primary": { "name": "pk_one_bar_qux", "columns": [ "id" ], + "comment": "", "extra": null }, "foreign": [], - "uniques": [] - } + "uniques": null, + "check": null + }, + "comment": "" } ], "enums": null, 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 6982d18..e6e4b6b 100644 --- a/gen/bobgen-sqlite/driver/include-exclude-tables-regex.golden.json +++ b/gen/bobgen-sqlite/driver/include-exclude-tables-regex.golden.json @@ -28,18 +28,38 @@ "type": "string" } ], - "indexes": [], + "indexes": [ + { + "type": "pk", + "name": "pk_main_bar_qux", + "columns": [ + { + "name": "id", + "desc": false, + "is_expression": false + } + ], + "unique": true, + "comment": "", + "extra": { + "partial": false + } + } + ], "constraints": { "primary": { "name": "pk_main_bar_qux", "columns": [ "id" ], + "comment": "", "extra": null }, "foreign": [], - "uniques": [] - } + "uniques": null, + "check": null + }, + "comment": "" }, { "key": "foo_qux", @@ -69,18 +89,38 @@ "type": "string" } ], - "indexes": [], + "indexes": [ + { + "type": "pk", + "name": "pk_main_foo_qux", + "columns": [ + { + "name": "id", + "desc": false, + "is_expression": false + } + ], + "unique": true, + "comment": "", + "extra": { + "partial": false + } + } + ], "constraints": { "primary": { "name": "pk_main_foo_qux", "columns": [ "id" ], + "comment": "", "extra": null }, "foreign": [], - "uniques": [] - } + "uniques": null, + "check": null + }, + "comment": "" }, { "key": "one.bar_qux", @@ -110,18 +150,38 @@ "type": "string" } ], - "indexes": [], + "indexes": [ + { + "type": "pk", + "name": "pk_one_bar_qux", + "columns": [ + { + "name": "id", + "desc": false, + "is_expression": false + } + ], + "unique": true, + "comment": "", + "extra": { + "partial": false + } + } + ], "constraints": { "primary": { "name": "pk_one_bar_qux", "columns": [ "id" ], + "comment": "", "extra": null }, "foreign": [], - "uniques": [] - } + "uniques": null, + "check": null + }, + "comment": "" }, { "key": "one.foo_qux", @@ -151,18 +211,38 @@ "type": "string" } ], - "indexes": [], + "indexes": [ + { + "type": "pk", + "name": "pk_one_foo_qux", + "columns": [ + { + "name": "id", + "desc": false, + "is_expression": false + } + ], + "unique": true, + "comment": "", + "extra": { + "partial": false + } + } + ], "constraints": { "primary": { "name": "pk_one_foo_qux", "columns": [ "id" ], + "comment": "", "extra": null }, "foreign": [], - "uniques": [] - } + "uniques": null, + "check": null + }, + "comment": "" } ], "enums": null, diff --git a/gen/bobgen-sqlite/driver/include-exclude-tables.golden.json b/gen/bobgen-sqlite/driver/include-exclude-tables.golden.json index bbbddd2..af3da7e 100644 --- a/gen/bobgen-sqlite/driver/include-exclude-tables.golden.json +++ b/gen/bobgen-sqlite/driver/include-exclude-tables.golden.json @@ -28,18 +28,38 @@ "type": "string" } ], - "indexes": [], + "indexes": [ + { + "type": "pk", + "name": "pk_main_foo_baz", + "columns": [ + { + "name": "id", + "desc": false, + "is_expression": false + } + ], + "unique": true, + "comment": "", + "extra": { + "partial": false + } + } + ], "constraints": { "primary": { "name": "pk_main_foo_baz", "columns": [ "id" ], + "comment": "", "extra": null }, "foreign": [], - "uniques": [] - } + "uniques": null, + "check": null + }, + "comment": "" }, { "key": "one.foo_baz", @@ -69,18 +89,38 @@ "type": "string" } ], - "indexes": [], + "indexes": [ + { + "type": "pk", + "name": "pk_one_foo_baz", + "columns": [ + { + "name": "id", + "desc": false, + "is_expression": false + } + ], + "unique": true, + "comment": "", + "extra": { + "partial": false + } + } + ], "constraints": { "primary": { "name": "pk_one_foo_baz", "columns": [ "id" ], + "comment": "", "extra": null }, "foreign": [], - "uniques": [] - } + "uniques": null, + "check": null + }, + "comment": "" } ], "enums": null, diff --git a/gen/bobgen-sqlite/driver/include-tables.golden.json b/gen/bobgen-sqlite/driver/include-tables.golden.json index 37339be..7825914 100644 --- a/gen/bobgen-sqlite/driver/include-tables.golden.json +++ b/gen/bobgen-sqlite/driver/include-tables.golden.json @@ -28,18 +28,38 @@ "type": "string" } ], - "indexes": [], + "indexes": [ + { + "type": "pk", + "name": "pk_main_foo_bar", + "columns": [ + { + "name": "id", + "desc": false, + "is_expression": false + } + ], + "unique": true, + "comment": "", + "extra": { + "partial": false + } + } + ], "constraints": { "primary": { "name": "pk_main_foo_bar", "columns": [ "id" ], + "comment": "", "extra": null }, "foreign": [], - "uniques": [] - } + "uniques": null, + "check": null + }, + "comment": "" }, { "key": "foo_baz", @@ -69,18 +89,38 @@ "type": "string" } ], - "indexes": [], + "indexes": [ + { + "type": "pk", + "name": "pk_main_foo_baz", + "columns": [ + { + "name": "id", + "desc": false, + "is_expression": false + } + ], + "unique": true, + "comment": "", + "extra": { + "partial": false + } + } + ], "constraints": { "primary": { "name": "pk_main_foo_baz", "columns": [ "id" ], + "comment": "", "extra": null }, "foreign": [], - "uniques": [] - } + "uniques": null, + "check": null + }, + "comment": "" }, { "key": "one.foo_bar", @@ -110,18 +150,38 @@ "type": "string" } ], - "indexes": [], + "indexes": [ + { + "type": "pk", + "name": "pk_one_foo_bar", + "columns": [ + { + "name": "id", + "desc": false, + "is_expression": false + } + ], + "unique": true, + "comment": "", + "extra": { + "partial": false + } + } + ], "constraints": { "primary": { "name": "pk_one_foo_bar", "columns": [ "id" ], + "comment": "", "extra": null }, "foreign": [], - "uniques": [] - } + "uniques": null, + "check": null + }, + "comment": "" }, { "key": "one.foo_baz", @@ -151,18 +211,38 @@ "type": "string" } ], - "indexes": [], + "indexes": [ + { + "type": "pk", + "name": "pk_one_foo_baz", + "columns": [ + { + "name": "id", + "desc": false, + "is_expression": false + } + ], + "unique": true, + "comment": "", + "extra": { + "partial": false + } + } + ], "constraints": { "primary": { "name": "pk_one_foo_baz", "columns": [ "id" ], + "comment": "", "extra": null }, "foreign": [], - "uniques": [] - } + "uniques": null, + "check": null + }, + "comment": "" } ], "enums": null, diff --git a/gen/bobgen-sqlite/driver/sqlite.go b/gen/bobgen-sqlite/driver/sqlite.go index 64375ad..44db243 100644 --- a/gen/bobgen-sqlite/driver/sqlite.go +++ b/gen/bobgen-sqlite/driver/sqlite.go @@ -3,6 +3,7 @@ package driver import ( "context" "database/sql" + "errors" "fmt" "sort" "strings" @@ -17,8 +18,11 @@ import ( ) type ( - Interface = drivers.Interface[any, any, any] - DBInfo = drivers.DBInfo[any, any, any] + Interface = drivers.Interface[any, any, IndexExtra] + DBInfo = drivers.DBInfo[any, any, IndexExtra] + IndexExtra = struct { + Partial bool `json:"partial"` + } ) func New(config Config) Interface { @@ -174,7 +178,7 @@ func (d *driver) buildQuery(schema string) (string, []any) { return query, args } -func (d *driver) tables(ctx context.Context) (drivers.Tables[any, any], error) { +func (d *driver) tables(ctx context.Context) (drivers.Tables[any, IndexExtra], error) { mainQuery, mainArgs := d.buildQuery("main") mainTables, err := stdscan.All(ctx, d.conn, scan.SingleColumnMapper[string], mainQuery, mainArgs...) if err != nil { @@ -182,7 +186,7 @@ func (d *driver) tables(ctx context.Context) (drivers.Tables[any, any], error) { } colFilter := drivers.ParseColumnFilter(mainTables, d.config.Only, d.config.Except) - allTables := make(drivers.Tables[any, any], len(mainTables)) + allTables := make(drivers.Tables[any, IndexExtra], len(mainTables)) for i, name := range mainTables { allTables[i], err = d.getTable(ctx, "main", name, colFilter) if err != nil { @@ -209,10 +213,10 @@ func (d *driver) tables(ctx context.Context) (drivers.Tables[any, any], error) { return allTables, nil } -func (d driver) getTable(ctx context.Context, schema, name string, colFilter drivers.ColumnFilter) (drivers.Table[any, any], error) { +func (d driver) getTable(ctx context.Context, schema, name string, colFilter drivers.ColumnFilter) (drivers.Table[any, IndexExtra], error) { var err error - table := drivers.Table[any, any]{ + table := drivers.Table[any, IndexExtra]{ Key: d.key(schema, name), Schema: d.schema(schema), Name: name, @@ -228,20 +232,53 @@ func (d driver) getTable(ctx context.Context, schema, name string, colFilter dri return table, err } + // We cannot rely on the indexes to get the primary key + // because it is not always included in the indexes table.Constraints.Primary = d.primaryKey(schema, name, tinfo) table.Constraints.Foreign, err = d.foreignKeys(ctx, schema, name) if err != nil { return table, err } - table.Constraints.Uniques, err = d.uniques(ctx, schema, name) + table.Indexes, err = d.indexes(ctx, schema, name) if err != nil { return table, err } - table.Indexes, err = d.indexes(ctx, schema, name) - if err != nil { - return table, err + // Get Unique constraints from indexes + // Also check if the primary key is in the indexes + hasPk := false + for _, index := range table.Indexes { + constraint := drivers.Constraint[any]{ + Name: index.Name, + Columns: index.NonExpressionColumns(), + } + + switch index.Type { + case "pk": + hasPk = true + case "u": + table.Constraints.Uniques = append(table.Constraints.Uniques, constraint) + } + } + + // Add the primary key to the indexes if it is not already there + if !hasPk && table.Constraints.Primary != nil { + pkIndex := drivers.Index[IndexExtra]{ + Type: "pk", + Name: table.Constraints.Primary.Name, + Columns: make([]drivers.IndexColumn, len(table.Constraints.Primary.Columns)), + Unique: true, + } + + for i, col := range table.Constraints.Primary.Columns { + pkIndex.Columns[i] = drivers.IndexColumn{ + Name: col, + } + } + + // List the primary key first + table.Indexes = append([]drivers.Index[IndexExtra]{pkIndex}, table.Indexes...) } return table, nil @@ -430,7 +467,7 @@ func (d driver) foreignKeys(ctx context.Context, schema, tableName string) ([]dr if fcol == "" { fcol, err = stdscan.One( ctx, d.conn, scan.SingleColumnMapper[string], - fmt.Sprintf("SELECT name FROM '%s'.pragma_table_info('%s') WHERE pk = ?", schema, ftable), seq+1, + fmt.Sprintf("SELECT name FROM pragma_table_info('%s', '%s') WHERE pk = ?", ftable, schema), seq+1, ) if err != nil { return nil, fmt.Errorf("could not find column %q in table %q: %w", col, ftable, err) @@ -469,75 +506,6 @@ 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[any], error) { - rows, err := d.conn.QueryContext(ctx, fmt.Sprintf("PRAGMA '%s'.index_list('%s')", schema, tableName)) - if err != nil { - return nil, err - } - defer rows.Close() - - var indexes []string - for rows.Next() { - var seq, unique, partial int - var name, origin string - - err = rows.Scan(&seq, &name, &unique, &origin, &partial) - if err != nil { - return nil, err - } - - // Index must be created by a unique constraint - if origin != "u" { - continue - } - - indexes = append(indexes, name) - } - rows.Close() - - if err = rows.Err(); err != nil { - return nil, err - } - - uniques := make([]drivers.Constraint[any], len(indexes)) - for i, index := range indexes { - uniques[i], err = d.getUniqueIndex(ctx, schema, index) - if err != nil { - return nil, err - } - } - - return uniques, nil -} - -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 { - return unique, err - } - defer rows.Close() - - for rows.Next() { - var seq, cid int - var name sql.NullString - - err = rows.Scan(&seq, &cid, &name) - if err != nil { - return unique, err - } - - // Index must be created by a unique constraint - if !name.Valid { - continue - } - - unique.Columns = append(unique.Columns, name.String) - } - - return unique, nil -} type info struct { Cid int @@ -609,90 +577,97 @@ func (driver) translateColumnType(c drivers.Column) drivers.Column { return c } -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) +func (d *driver) indexes(ctx context.Context, schema, tableName string) ([]drivers.Index[IndexExtra], error) { + query := fmt.Sprintf(` + SELECT name, "unique", origin, partial + FROM pragma_index_list('%s', '%s') ORDER BY seq ASC + `, tableName, schema) + indexNames, err := stdscan.All(ctx, d.conn, scan.StructMapper[struct { + Name string + Unique bool + Origin string + Partial bool + }](), query) if err != nil { return nil, err } - defer rows.Close() - var indexNames []string - for rows.Next() { - var name string - err = rows.Scan(&name) + indexes := make([]drivers.Index[IndexExtra], len(indexNames)) + for i, index := range indexNames { + cols, err := d.getIndexInformation(ctx, schema, tableName, index.Name) if err != nil { return nil, err } - indexNames = append(indexNames, name) - } - - if err = rows.Err(); err != nil { - return nil, err - } - - indexes := make([]drivers.Index[any], len(indexNames)) - for i, indexName := range indexNames { - indexes[i], err = d.getIndexInformation(ctx, schema, tableName, indexName) - if err != nil { - return nil, err + indexes[i] = drivers.Index[IndexExtra]{ + Type: index.Origin, + Name: index.Name, + Unique: index.Unique, + Columns: cols, + Extra: IndexExtra{ + Partial: index.Partial, + }, } + } return indexes, nil } -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 - query := fmt.Sprintf("SELECT name FROM '%s'.pragma_index_info('%s') ORDER BY seqno ASC", schema, indexName) - rows, err := d.conn.QueryContext(ctx, query) +func (d *driver) getIndexInformation(ctx context.Context, schema, tableName, indexName string) ([]drivers.IndexColumn, error) { + colExpressions, err := d.extractIndexExpressions(ctx, schema, tableName, indexName) if err != nil { - return index, err + return nil, err } - defer rows.Close() - exprCols := make(map[int]struct{}) - for seqno := 0; rows.Next(); seqno++ { - var name null.Val[string] - err = rows.Scan(&name) + query := fmt.Sprintf(` + SELECT seqno, name, desc + FROM pragma_index_xinfo('%s', '%s') + WHERE key = 1 + ORDER BY seqno ASC`, + indexName, schema) + + var columns []drivers.IndexColumn //nolint:prealloc + for column, err := range stdscan.Each(ctx, d.conn, scan.StructMapper[struct { + Seqno int + Name sql.NullString + Desc bool + }](), query) { if err != nil { - return index, err + return nil, err } - if name.IsSet() { - index.Columns = append(index.Columns, name.GetOrZero()) - } else { - exprCols[seqno] = struct{}{} + + col := drivers.IndexColumn{ + Name: column.Name.String, + Desc: column.Desc, } - } - if len(exprCols) > 0 { - index.Expressions, err = d.extractIndexExpressions(ctx, schema, tableName, indexName, exprCols) - if err != nil { - return index, err + if !column.Name.Valid { + col.Name = colExpressions[column.Seqno] + col.IsExpression = true } - } - if err = rows.Err(); err != nil { - return index, err + columns = append(columns, col) } - return index, nil + return columns, nil } -func (d driver) extractIndexExpressions(ctx context.Context, schema, tableName, indexName string, exprCols map[int]struct{}) ([]string, error) { - var ddl string - var expressions []string //nolint:prealloc +func (d driver) extractIndexExpressions(ctx context.Context, schema, tableName, indexName string) ([]string, error) { + var nullDDL sql.NullString + //nolint:gosec query := fmt.Sprintf("SELECT sql FROM '%s'.sqlite_master WHERE type = 'index' AND name = ? AND tbl_name = ?", schema) result := d.conn.QueryRowContext(ctx, query, indexName, tableName) - err := result.Scan(&ddl) - if err != nil { - return expressions, fmt.Errorf("failed retrieving index DDL statement: %w", err) + err := result.Scan(&nullDDL) + if err != nil && !errors.Is(err, sql.ErrNoRows) { + return nil, fmt.Errorf("failed retrieving index DDL statement: %w", err) } + + if !nullDDL.Valid { + return nil, nil + } + + ddl := nullDDL.String // We're following the parsing logic from the `intckParseCreateIndex` function in the SQLite source code. // 1. https://github.com/sqlite/sqlite/blob/1d8cde9d56d153767e98595c4b015221864ef0e7/ext/intck/sqlite3intck.c#L363 // 2. https://www.sqlite.org/lang_createindex.html @@ -700,24 +675,21 @@ func (d driver) extractIndexExpressions(ctx context.Context, schema, tableName, // skip forward until the first "(" token i := strings.Index(ddl, "(") if i == -1 { - return expressions, fmt.Errorf("failed locating first column: %w", err) + return nil, fmt.Errorf("failed locating first column: %w", err) } ddl = ddl[i+1:] // discard the WHERE clause fragment (if one exists) i = strings.LastIndex(ddl, ")") if i == -1 { - return expressions, fmt.Errorf("failed locating last column: %w", err) + return nil, fmt.Errorf("failed locating last column: %w", err) } ddl = ddl[:i] // organize column definitions into a list colDefs := d.splitColumnDefinitions(ddl) - for seqno, expression := range colDefs { - if _, ok := exprCols[seqno]; !ok { - // this index column references a regular column rather than an expression, so we skip the extraction. - continue - } - expressions = append(expressions, strings.TrimSpace(expression)) + expressions := make([]string, len(colDefs)) + for seqNo, expression := range colDefs { + expressions[seqNo] = strings.TrimSpace(expression) } return expressions, nil diff --git a/gen/bobgen-sqlite/driver/sqlite.golden.json b/gen/bobgen-sqlite/driver/sqlite.golden.json index e57b329..b755c65 100644 --- a/gen/bobgen-sqlite/driver/sqlite.golden.json +++ b/gen/bobgen-sqlite/driver/sqlite.golden.json @@ -63,21 +63,57 @@ ], "indexes": [ { - "name": "sqlite_autoindex_autoinckeywordtest_1", + "type": "pk", + "name": "pk_main_autoinckeywordtest", "columns": [ - "sponsor_id" + { + "name": "id", + "desc": false, + "is_expression": false + } ], - "expressions": null, - "extra": null + "unique": true, + "comment": "", + "extra": { + "partial": false + } }, { + "type": "u", "name": "sqlite_autoindex_autoinckeywordtest_2", "columns": [ - "something", - "another" + { + "name": "something", + "desc": false, + "is_expression": false + }, + { + "name": "another", + "desc": false, + "is_expression": false + } ], - "expressions": null, - "extra": null + "unique": true, + "comment": "", + "extra": { + "partial": false + } + }, + { + "type": "u", + "name": "sqlite_autoindex_autoinckeywordtest_1", + "columns": [ + { + "name": "sponsor_id", + "desc": false, + "is_expression": false + } + ], + "unique": true, + "comment": "", + "extra": { + "partial": false + } } ], "constraints": { @@ -86,6 +122,7 @@ "columns": [ "id" ], + "comment": "", "extra": null }, "foreign": [ @@ -95,12 +132,13 @@ "user_id", "sponsor_id" ], - "extra": null, "foreign_table": "videos", "foreign_columns": [ "user_id", "sponsor_id" - ] + ], + "comment": "", + "extra": null } ], "uniques": [ @@ -110,6 +148,7 @@ "something", "another" ], + "comment": "", "extra": null }, { @@ -117,10 +156,13 @@ "columns": [ "sponsor_id" ], + "comment": "", "extra": null } - ] - } + ], + "check": null + }, + "comment": "" }, { "key": "autoinctest", @@ -139,13 +181,31 @@ "type": "int32" } ], - "indexes": [], + "indexes": [ + { + "type": "pk", + "name": "pk_main_autoinctest", + "columns": [ + { + "name": "id", + "desc": false, + "is_expression": false + } + ], + "unique": true, + "comment": "", + "extra": { + "partial": false + } + } + ], "constraints": { "primary": { "name": "pk_main_autoinctest", "columns": [ "id" ], + "comment": "", "extra": null }, "foreign": [ @@ -154,15 +214,18 @@ "columns": [ "id" ], - "extra": null, "foreign_table": "tags", "foreign_columns": [ "id" - ] + ], + "comment": "", + "extra": null } ], - "uniques": [] - } + "uniques": null, + "check": null + }, + "comment": "" }, { "key": "bar_baz", @@ -192,18 +255,38 @@ "type": "string" } ], - "indexes": [], + "indexes": [ + { + "type": "pk", + "name": "pk_main_bar_baz", + "columns": [ + { + "name": "id", + "desc": false, + "is_expression": false + } + ], + "unique": true, + "comment": "", + "extra": { + "partial": false + } + } + ], "constraints": { "primary": { "name": "pk_main_bar_baz", "columns": [ "id" ], + "comment": "", "extra": null }, "foreign": [], - "uniques": [] - } + "uniques": null, + "check": null + }, + "comment": "" }, { "key": "bar_qux", @@ -233,18 +316,38 @@ "type": "string" } ], - "indexes": [], + "indexes": [ + { + "type": "pk", + "name": "pk_main_bar_qux", + "columns": [ + { + "name": "id", + "desc": false, + "is_expression": false + } + ], + "unique": true, + "comment": "", + "extra": { + "partial": false + } + } + ], "constraints": { "primary": { "name": "pk_main_bar_qux", "columns": [ "id" ], + "comment": "", "extra": null }, "foreign": [], - "uniques": [] - } + "uniques": null, + "check": null + }, + "comment": "" }, { "key": "foo_bar", @@ -274,18 +377,38 @@ "type": "string" } ], - "indexes": [], + "indexes": [ + { + "type": "pk", + "name": "pk_main_foo_bar", + "columns": [ + { + "name": "id", + "desc": false, + "is_expression": false + } + ], + "unique": true, + "comment": "", + "extra": { + "partial": false + } + } + ], "constraints": { "primary": { "name": "pk_main_foo_bar", "columns": [ "id" ], + "comment": "", "extra": null }, "foreign": [], - "uniques": [] - } + "uniques": null, + "check": null + }, + "comment": "" }, { "key": "foo_baz", @@ -315,18 +438,38 @@ "type": "string" } ], - "indexes": [], + "indexes": [ + { + "type": "pk", + "name": "pk_main_foo_baz", + "columns": [ + { + "name": "id", + "desc": false, + "is_expression": false + } + ], + "unique": true, + "comment": "", + "extra": { + "partial": false + } + } + ], "constraints": { "primary": { "name": "pk_main_foo_baz", "columns": [ "id" ], + "comment": "", "extra": null }, "foreign": [], - "uniques": [] - } + "uniques": null, + "check": null + }, + "comment": "" }, { "key": "foo_qux", @@ -356,18 +499,38 @@ "type": "string" } ], - "indexes": [], + "indexes": [ + { + "type": "pk", + "name": "pk_main_foo_qux", + "columns": [ + { + "name": "id", + "desc": false, + "is_expression": false + } + ], + "unique": true, + "comment": "", + "extra": { + "partial": false + } + } + ], "constraints": { "primary": { "name": "pk_main_foo_qux", "columns": [ "id" ], + "comment": "", "extra": null }, "foreign": [], - "uniques": [] - } + "uniques": null, + "check": null + }, + "comment": "" }, { "key": "has_generated_columns", @@ -430,18 +593,38 @@ "type": "string" } ], - "indexes": [], + "indexes": [ + { + "type": "pk", + "name": "pk_main_has_generated_columns", + "columns": [ + { + "name": "a", + "desc": false, + "is_expression": false + } + ], + "unique": true, + "comment": "", + "extra": { + "partial": false + } + } + ], "constraints": { "primary": { "name": "pk_main_has_generated_columns", "columns": [ "a" ], + "comment": "", "extra": null }, "foreign": [], - "uniques": [] - } + "uniques": null, + "check": null + }, + "comment": "" }, { "key": "one.as_generated_columns", @@ -504,18 +687,38 @@ "type": "string" } ], - "indexes": [], + "indexes": [ + { + "type": "pk", + "name": "pk_one_as_generated_columns", + "columns": [ + { + "name": "a", + "desc": false, + "is_expression": false + } + ], + "unique": true, + "comment": "", + "extra": { + "partial": false + } + } + ], "constraints": { "primary": { "name": "pk_one_as_generated_columns", "columns": [ "a" ], + "comment": "", "extra": null }, "foreign": [], - "uniques": [] - } + "uniques": null, + "check": null + }, + "comment": "" }, { "key": "one.autoinckeywordtest", @@ -547,21 +750,20 @@ ], "indexes": [ { - "name": "sqlite_autoindex_autoinckeywordtest_1", - "columns": [ - "sponsor_id" - ], - "expressions": null, - "extra": null - }, - { - "name": "sqlite_autoindex_autoinckeywordtest_2", + "type": "pk", + "name": "pk_one_autoinckeywordtest", "columns": [ - "something", - "another" + { + "name": "id", + "desc": false, + "is_expression": false + } ], - "expressions": null, - "extra": null + "unique": true, + "comment": "", + "extra": { + "partial": false + } } ], "constraints": { @@ -570,11 +772,14 @@ "columns": [ "id" ], + "comment": "", "extra": null }, "foreign": [], - "uniques": [] - } + "uniques": null, + "check": null + }, + "comment": "" }, { "key": "one.autoinctest", @@ -593,18 +798,38 @@ "type": "int32" } ], - "indexes": [], + "indexes": [ + { + "type": "pk", + "name": "pk_one_autoinctest", + "columns": [ + { + "name": "id", + "desc": false, + "is_expression": false + } + ], + "unique": true, + "comment": "", + "extra": { + "partial": false + } + } + ], "constraints": { "primary": { "name": "pk_one_autoinctest", "columns": [ "id" ], + "comment": "", "extra": null }, "foreign": [], - "uniques": [] - } + "uniques": null, + "check": null + }, + "comment": "" }, { "key": "one.bar_baz", @@ -634,18 +859,38 @@ "type": "string" } ], - "indexes": [], + "indexes": [ + { + "type": "pk", + "name": "pk_one_bar_baz", + "columns": [ + { + "name": "id", + "desc": false, + "is_expression": false + } + ], + "unique": true, + "comment": "", + "extra": { + "partial": false + } + } + ], "constraints": { "primary": { "name": "pk_one_bar_baz", "columns": [ "id" ], + "comment": "", "extra": null }, "foreign": [], - "uniques": [] - } + "uniques": null, + "check": null + }, + "comment": "" }, { "key": "one.bar_qux", @@ -675,18 +920,38 @@ "type": "string" } ], - "indexes": [], + "indexes": [ + { + "type": "pk", + "name": "pk_one_bar_qux", + "columns": [ + { + "name": "id", + "desc": false, + "is_expression": false + } + ], + "unique": true, + "comment": "", + "extra": { + "partial": false + } + } + ], "constraints": { "primary": { "name": "pk_one_bar_qux", "columns": [ "id" ], + "comment": "", "extra": null }, "foreign": [], - "uniques": [] - } + "uniques": null, + "check": null + }, + "comment": "" }, { "key": "one.foo_bar", @@ -716,18 +981,38 @@ "type": "string" } ], - "indexes": [], + "indexes": [ + { + "type": "pk", + "name": "pk_one_foo_bar", + "columns": [ + { + "name": "id", + "desc": false, + "is_expression": false + } + ], + "unique": true, + "comment": "", + "extra": { + "partial": false + } + } + ], "constraints": { "primary": { "name": "pk_one_foo_bar", "columns": [ "id" ], + "comment": "", "extra": null }, "foreign": [], - "uniques": [] - } + "uniques": null, + "check": null + }, + "comment": "" }, { "key": "one.foo_baz", @@ -757,18 +1042,38 @@ "type": "string" } ], - "indexes": [], + "indexes": [ + { + "type": "pk", + "name": "pk_one_foo_baz", + "columns": [ + { + "name": "id", + "desc": false, + "is_expression": false + } + ], + "unique": true, + "comment": "", + "extra": { + "partial": false + } + } + ], "constraints": { "primary": { "name": "pk_one_foo_baz", "columns": [ "id" ], + "comment": "", "extra": null }, "foreign": [], - "uniques": [] - } + "uniques": null, + "check": null + }, + "comment": "" }, { "key": "one.foo_qux", @@ -798,18 +1103,38 @@ "type": "string" } ], - "indexes": [], + "indexes": [ + { + "type": "pk", + "name": "pk_one_foo_qux", + "columns": [ + { + "name": "id", + "desc": false, + "is_expression": false + } + ], + "unique": true, + "comment": "", + "extra": { + "partial": false + } + } + ], "constraints": { "primary": { "name": "pk_one_foo_qux", "columns": [ "id" ], + "comment": "", "extra": null }, "foreign": [], - "uniques": [] - } + "uniques": null, + "check": null + }, + "comment": "" }, { "key": "one.sponsors", @@ -830,12 +1155,20 @@ ], "indexes": [ { + "type": "pk", "name": "sqlite_autoindex_sponsors_1", "columns": [ - "id" + { + "name": "id", + "desc": false, + "is_expression": false + } ], - "expressions": null, - "extra": null + "unique": true, + "comment": "", + "extra": { + "partial": false + } } ], "constraints": { @@ -844,11 +1177,14 @@ "columns": [ "id" ], + "comment": "", "extra": null }, "foreign": [], - "uniques": [] - } + "uniques": null, + "check": null + }, + "comment": "" }, { "key": "one.tags", @@ -869,12 +1205,20 @@ ], "indexes": [ { + "type": "pk", "name": "sqlite_autoindex_tags_1", "columns": [ - "id" + { + "name": "id", + "desc": false, + "is_expression": false + } ], - "expressions": null, - "extra": null + "unique": true, + "comment": "", + "extra": { + "partial": false + } } ], "constraints": { @@ -883,11 +1227,14 @@ "columns": [ "id" ], + "comment": "", "extra": null }, "foreign": [], - "uniques": [] - } + "uniques": null, + "check": null + }, + "comment": "" }, { "key": "one.user_videos", @@ -932,8 +1279,10 @@ "constraints": { "primary": null, "foreign": [], - "uniques": [] - } + "uniques": null, + "check": null + }, + "comment": "" }, { "key": "one.users", @@ -954,12 +1303,20 @@ ], "indexes": [ { + "type": "pk", "name": "sqlite_autoindex_users_1", "columns": [ - "id" + { + "name": "id", + "desc": false, + "is_expression": false + } ], - "expressions": null, - "extra": null + "unique": true, + "comment": "", + "extra": { + "partial": false + } } ], "constraints": { @@ -968,11 +1325,14 @@ "columns": [ "id" ], + "comment": "", "extra": null }, "foreign": [], - "uniques": [] - } + "uniques": null, + "check": null + }, + "comment": "" }, { "key": "one.video_tags", @@ -1004,13 +1364,25 @@ ], "indexes": [ { + "type": "pk", "name": "sqlite_autoindex_video_tags_1", "columns": [ - "video_id", - "tag_id" + { + "name": "video_id", + "desc": false, + "is_expression": false + }, + { + "name": "tag_id", + "desc": false, + "is_expression": false + } ], - "expressions": null, - "extra": null + "unique": true, + "comment": "", + "extra": { + "partial": false + } } ], "constraints": { @@ -1020,6 +1392,7 @@ "video_id", "tag_id" ], + "comment": "", "extra": null }, "foreign": [ @@ -1028,26 +1401,30 @@ "columns": [ "tag_id" ], - "extra": null, "foreign_table": "one.tags", "foreign_columns": [ "id" - ] + ], + "comment": "", + "extra": null }, { "name": "fk_video_tags_1", "columns": [ "video_id" ], - "extra": null, "foreign_table": "one.videos", "foreign_columns": [ "id" - ] + ], + "comment": "", + "extra": null } ], - "uniques": [] - } + "uniques": null, + "check": null + }, + "comment": "" }, { "key": "one.videos", @@ -1090,20 +1467,36 @@ ], "indexes": [ { - "name": "sqlite_autoindex_videos_1", + "type": "u", + "name": "sqlite_autoindex_videos_2", "columns": [ - "id" + { + "name": "sponsor_id", + "desc": false, + "is_expression": false + } ], - "expressions": null, - "extra": null + "unique": true, + "comment": "", + "extra": { + "partial": false + } }, { - "name": "sqlite_autoindex_videos_2", + "type": "pk", + "name": "sqlite_autoindex_videos_1", "columns": [ - "sponsor_id" + { + "name": "id", + "desc": false, + "is_expression": false + } ], - "expressions": null, - "extra": null + "unique": true, + "comment": "", + "extra": { + "partial": false + } } ], "constraints": { @@ -1112,6 +1505,7 @@ "columns": [ "id" ], + "comment": "", "extra": null }, "foreign": [ @@ -1120,22 +1514,24 @@ "columns": [ "sponsor_id" ], - "extra": null, "foreign_table": "one.sponsors", "foreign_columns": [ "id" - ] + ], + "comment": "", + "extra": null }, { "name": "fk_videos_1", "columns": [ "user_id" ], - "extra": null, "foreign_table": "one.users", "foreign_columns": [ "id" - ] + ], + "comment": "", + "extra": null } ], "uniques": [ @@ -1144,10 +1540,13 @@ "columns": [ "sponsor_id" ], + "comment": "", "extra": null } - ] - } + ], + "check": null + }, + "comment": "" }, { "key": "sponsors", @@ -1168,12 +1567,20 @@ ], "indexes": [ { + "type": "pk", "name": "sqlite_autoindex_sponsors_1", "columns": [ - "id" + { + "name": "id", + "desc": false, + "is_expression": false + } ], - "expressions": null, - "extra": null + "unique": true, + "comment": "", + "extra": { + "partial": false + } } ], "constraints": { @@ -1182,11 +1589,14 @@ "columns": [ "id" ], + "comment": "", "extra": null }, "foreign": [], - "uniques": [] - } + "uniques": null, + "check": null + }, + "comment": "" }, { "key": "tags", @@ -1207,12 +1617,20 @@ ], "indexes": [ { + "type": "pk", "name": "sqlite_autoindex_tags_1", "columns": [ - "id" + { + "name": "id", + "desc": false, + "is_expression": false + } ], - "expressions": null, - "extra": null + "unique": true, + "comment": "", + "extra": { + "partial": false + } } ], "constraints": { @@ -1221,11 +1639,14 @@ "columns": [ "id" ], + "comment": "", "extra": null }, "foreign": [], - "uniques": [] - } + "uniques": null, + "check": null + }, + "comment": "" }, { "key": "test_index_expressions", @@ -1268,64 +1689,124 @@ ], "indexes": [ { - "name": "idx1", - "columns": null, - "expressions": [ - "(col1 + col2)" + "type": "c", + "name": "idx6", + "columns": [ + { + "name": "POW(col3, 2)", + "desc": false, + "is_expression": true + } ], - "extra": null + "unique": false, + "comment": "", + "extra": { + "partial": false + } }, { - "name": "idx2", + "type": "c", + "name": "idx5", "columns": [ - "col3" - ], - "expressions": [ - "(col1 + col2)" + { + "name": "col1", + "desc": true, + "is_expression": false + }, + { + "name": "col2", + "desc": true, + "is_expression": false + } ], - "extra": null + "unique": false, + "comment": "", + "extra": { + "partial": false + } }, { - "name": "idx3", + "type": "c", + "name": "idx4", "columns": [ - "col1" + { + "name": "col3", + "desc": false, + "is_expression": false + } ], - "expressions": [ - "(col2 + col3)" - ], - "extra": null + "unique": false, + "comment": "", + "extra": { + "partial": false + } }, { - "name": "idx4", + "type": "c", + "name": "idx3", "columns": [ - "col3" + { + "name": "col1", + "desc": false, + "is_expression": false + }, + { + "name": "(col2 + col3)", + "desc": false, + "is_expression": true + } ], - "expressions": null, - "extra": null + "unique": false, + "comment": "", + "extra": { + "partial": false + } }, { - "name": "idx5", + "type": "c", + "name": "idx2", "columns": [ - "col1", - "col2" + { + "name": "(col1 + col2)", + "desc": false, + "is_expression": true + }, + { + "name": "col3", + "desc": false, + "is_expression": false + } ], - "expressions": null, - "extra": null + "unique": false, + "comment": "", + "extra": { + "partial": false + } }, { - "name": "idx6", - "columns": null, - "expressions": [ - "POW(col3, 2)" + "type": "c", + "name": "idx1", + "columns": [ + { + "name": "(col1 + col2)", + "desc": false, + "is_expression": true + } ], - "extra": null + "unique": false, + "comment": "", + "extra": { + "partial": false + } } ], "constraints": { "primary": null, "foreign": [], - "uniques": [] - } + "uniques": null, + "check": null + }, + "comment": "" }, { "key": "type_monsters", @@ -2578,12 +3059,20 @@ ], "indexes": [ { + "type": "pk", "name": "sqlite_autoindex_type_monsters_1", "columns": [ - "id" + { + "name": "id", + "desc": false, + "is_expression": false + } ], - "expressions": null, - "extra": null + "unique": true, + "comment": "", + "extra": { + "partial": false + } } ], "constraints": { @@ -2592,11 +3081,14 @@ "columns": [ "id" ], + "comment": "", "extra": null }, "foreign": [], - "uniques": [] - } + "uniques": null, + "check": null + }, + "comment": "" }, { "key": "user_videos", @@ -2641,8 +3133,10 @@ "constraints": { "primary": null, "foreign": [], - "uniques": [] - } + "uniques": null, + "check": null + }, + "comment": "" }, { "key": "users", @@ -2663,12 +3157,20 @@ ], "indexes": [ { + "type": "pk", "name": "sqlite_autoindex_users_1", "columns": [ - "id" + { + "name": "id", + "desc": false, + "is_expression": false + } ], - "expressions": null, - "extra": null + "unique": true, + "comment": "", + "extra": { + "partial": false + } } ], "constraints": { @@ -2677,11 +3179,14 @@ "columns": [ "id" ], + "comment": "", "extra": null }, "foreign": [], - "uniques": [] - } + "uniques": null, + "check": null + }, + "comment": "" }, { "key": "video_tags", @@ -2713,13 +3218,25 @@ ], "indexes": [ { + "type": "pk", "name": "sqlite_autoindex_video_tags_1", "columns": [ - "video_id", - "tag_id" + { + "name": "video_id", + "desc": false, + "is_expression": false + }, + { + "name": "tag_id", + "desc": false, + "is_expression": false + } ], - "expressions": null, - "extra": null + "unique": true, + "comment": "", + "extra": { + "partial": false + } } ], "constraints": { @@ -2729,6 +3246,7 @@ "video_id", "tag_id" ], + "comment": "", "extra": null }, "foreign": [ @@ -2737,26 +3255,30 @@ "columns": [ "tag_id" ], - "extra": null, "foreign_table": "tags", "foreign_columns": [ "id" - ] + ], + "comment": "", + "extra": null }, { "name": "fk_video_tags_1", "columns": [ "video_id" ], - "extra": null, "foreign_table": "videos", "foreign_columns": [ "id" - ] + ], + "comment": "", + "extra": null } ], - "uniques": [] - } + "uniques": null, + "check": null + }, + "comment": "" }, { "key": "videos", @@ -2799,20 +3321,36 @@ ], "indexes": [ { - "name": "sqlite_autoindex_videos_1", + "type": "u", + "name": "sqlite_autoindex_videos_2", "columns": [ - "id" + { + "name": "sponsor_id", + "desc": false, + "is_expression": false + } ], - "expressions": null, - "extra": null + "unique": true, + "comment": "", + "extra": { + "partial": false + } }, { - "name": "sqlite_autoindex_videos_2", + "type": "pk", + "name": "sqlite_autoindex_videos_1", "columns": [ - "sponsor_id" + { + "name": "id", + "desc": false, + "is_expression": false + } ], - "expressions": null, - "extra": null + "unique": true, + "comment": "", + "extra": { + "partial": false + } } ], "constraints": { @@ -2821,6 +3359,7 @@ "columns": [ "id" ], + "comment": "", "extra": null }, "foreign": [ @@ -2829,22 +3368,24 @@ "columns": [ "sponsor_id" ], - "extra": null, "foreign_table": "sponsors", "foreign_columns": [ "id" - ] + ], + "comment": "", + "extra": null }, { "name": "fk_videos_1", "columns": [ "user_id" ], - "extra": null, "foreign_table": "users", "foreign_columns": [ "id" - ] + ], + "comment": "", + "extra": null } ], "uniques": [ @@ -2853,10 +3394,13 @@ "columns": [ "sponsor_id" ], + "comment": "", "extra": null } - ] - } + ], + "check": null + }, + "comment": "" } ], "enums": null, diff --git a/gen/bobgen-sqlite/driver/sqlite_test.go b/gen/bobgen-sqlite/driver/sqlite_test.go index c6bd3aa..36365bc 100644 --- a/gen/bobgen-sqlite/driver/sqlite_test.go +++ b/gen/bobgen-sqlite/driver/sqlite_test.go @@ -175,8 +175,20 @@ func TestAssemble(t *testing.T) { }, } - for _, tt := range tests { + for i, tt := range tests { t.Run(tt.name, func(t *testing.T) { + if i > 0 { + testgen.TestAssemble(t, testgen.AssembleTestConfig[any, any, IndexExtra]{ + GetDriver: func() drivers.Interface[any, any, IndexExtra] { + return New(tt.config) + }, + GoldenFile: tt.goldenJson, + OverwriteGolden: *flagOverwriteGolden, + Templates: &helpers.Templates{Models: []fs.FS{gen.SQLiteModelTemplates}}, + }) + return + } + out, err := os.MkdirTemp("", "bobgen_sqlite_") if err != nil { t.Fatalf("unable to create tempdir: %s", err) @@ -191,9 +203,9 @@ func TestAssemble(t *testing.T) { os.RemoveAll(out) }() - testgen.TestDriver(t, testgen.DriverTestConfig[any, any, any]{ + testgen.TestDriver(t, testgen.DriverTestConfig[any, any, IndexExtra]{ Root: out, - GetDriver: func() drivers.Interface[any, any, any] { + GetDriver: func() drivers.Interface[any, any, IndexExtra] { return New(tt.config) }, GoldenFile: tt.goldenJson, diff --git a/gen/constraints.go b/gen/constraints.go index 81cea81..afa8aeb 100644 --- a/gen/constraints.go +++ b/gen/constraints.go @@ -26,6 +26,7 @@ func mergeConstraints[C any](srcs, extras drivers.Constraints[C]) drivers.Constr srcs.Uniques = append(srcs.Uniques, extras.Uniques...) srcs.Foreign = append(srcs.Foreign, extras.Foreign...) + srcs.Checks = append(srcs.Checks, extras.Checks...) return srcs } diff --git a/gen/drivers/aliases.go b/gen/drivers/aliases.go index 6437f31..e54ffff 100644 --- a/gen/drivers/aliases.go +++ b/gen/drivers/aliases.go @@ -9,13 +9,13 @@ 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"` + UpPlural string `yaml:"up_plural,omitempty" json:"up_plural,omitempty"` + UpSingular string `yaml:"up_singular,omitempty" json:"up_singular,omitempty"` + DownPlural string `yaml:"down_plural,omitempty" json:"down_plural,omitempty"` + DownSingular string `yaml:"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"` + Columns map[string]string `yaml:"columns,omitempty" json:"columns,omitempty"` + Relationships map[string]string `yaml:"relationships,omitempty" json:"relationships,omitempty"` } // Table gets a table alias, panics if not found. @@ -32,6 +32,7 @@ func (a Aliases) Table(table string) TableAlias { func (t TableAlias) Column(column string) string { c, ok := t.Columns[column] if !ok { + fmt.Printf("TableAlias: %#v\n", t) panic(fmt.Sprintf("could not find column alias for: %s.%s", t.UpSingular, column)) } diff --git a/gen/drivers/column.go b/gen/drivers/column.go index c3e9eaf..768b8d2 100644 --- a/gen/drivers/column.go +++ b/gen/drivers/column.go @@ -9,19 +9,19 @@ import ( // Column holds information about a database column. // Types are Go types, converted by TranslateColumnType. type Column struct { - Name string `json:"name" yaml:"name" toml:"name"` - DBType string `json:"db_type" yaml:"db_type" toml:"db_type"` - Default string `json:"default" yaml:"default" toml:"default"` - Comment string `json:"comment" yaml:"comment" toml:"comment"` - Nullable bool `json:"nullable" yaml:"nullable" toml:"nullable"` - Generated bool `json:"generated" yaml:"generated" toml:"generated"` - AutoIncr bool `json:"autoincr" yaml:"autoincr" toml:"autoincr"` + Name string `json:"name" yaml:"name"` + DBType string `json:"db_type" yaml:"db_type"` + Default string `json:"default" yaml:"default"` + Comment string `json:"comment" yaml:"comment"` + Nullable bool `json:"nullable" yaml:"nullable"` + Generated bool `json:"generated" yaml:"generated"` + AutoIncr bool `json:"autoincr" yaml:"autoincr"` // DomainName is the domain type name associated to the column. See here: // https://www.postgresql.org/docs/16/extend-type-system.html - DomainName string `json:"domain_name" yaml:"domain_name" toml:"domain_name"` + DomainName string `json:"domain_name" yaml:"domain_name"` - Type string `json:"type" yaml:"type" toml:"type"` + Type string `json:"type" yaml:"type"` } // ColumnNames of the columns. diff --git a/gen/drivers/constraints.go b/gen/drivers/constraints.go index 572a2f2..ac1d381 100644 --- a/gen/drivers/constraints.go +++ b/gen/drivers/constraints.go @@ -2,35 +2,64 @@ package drivers import "encoding/json" +// DBIndexes lists all indexes in the database schema keyed by table name +type DBIndexes[Extra any] map[string][]Index[Extra] + // 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"` + Type string `yaml:"type" json:"type"` + Name string `yaml:"name" json:"name"` + Columns []IndexColumn `yaml:"columns" json:"columns"` + Unique bool `yaml:"unique" json:"unique"` + Comment string `json:"comment" yaml:"comment"` + Extra Extra `yaml:"extra" json:"extra"` } -// DBIndexes lists all indexes in the database schema keyed by table name -type DBIndexes[Extra any] map[string][]Index[Extra] +type IndexColumn struct { + Name string `yaml:"name" json:"name"` + Desc bool `yaml:"desc" json:"desc"` + IsExpression bool `yaml:"is_expression" json:"is_expression"` +} -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"` +func (i Index[E]) HasExpressionColumn() bool { + for _, c := range i.Columns { + if c.IsExpression { + return true + } + } + return false +} + +func (i Index[E]) NonExpressionColumns() []string { + cols := make([]string, 0, len(i.Columns)) + for _, c := range i.Columns { + if !c.IsExpression { + cols = append(cols, c.Name) + } + } + + return cols } type DBConstraints[Extra any] struct { PKs map[string]*Constraint[Extra] FKs map[string][]ForeignKey[Extra] Uniques map[string][]Constraint[Extra] + Checks map[string][]Check[Extra] } -// Constraint represents a constraint in a database -type Constraint[Extra any] NamedColumnList[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"` + Checks []Check[Extra] `yaml:"check" json:"check"` +} -type NamedColumnList[Extra any] struct { +// Constraint represents a constraint in a database +type Constraint[Extra any] struct { Name string `yaml:"name" json:"name"` Columns []string `yaml:"columns" json:"columns"` + Comment string `json:"comment" yaml:"comment"` Extra Extra `yaml:"extra" json:"extra"` } @@ -45,9 +74,10 @@ 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"` + Comment string `json:"comment"` + Extra E `json:"extra"` } if err := json.Unmarshal(data, &tmp); err != nil { @@ -56,9 +86,10 @@ func (f *ForeignKey[E]) UnmarshalJSON(data []byte) error { f.Name = tmp.Name f.Columns = tmp.Columns - f.Extra = tmp.Extra f.ForeignTable = tmp.ForeignTable f.ForeignColumns = tmp.ForeignColumns + f.Comment = tmp.Comment + f.Extra = tmp.Extra return nil } @@ -67,15 +98,63 @@ 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"` + Comment string `json:"comment"` + Extra E `json:"extra"` }{ Name: f.Name, Columns: f.Columns, - Extra: f.Extra, ForeignTable: f.ForeignTable, ForeignColumns: f.ForeignColumns, + Comment: f.Comment, + Extra: f.Extra, + } + + return json.Marshal(tmp) +} + +// Check represents a check constraint in a database +type Check[Extra any] struct { + Constraint[Extra] `yaml:",inline" json:"-"` + Expression string `yaml:"expression" json:"expression"` +} + +func (c *Check[E]) UnmarshalJSON(data []byte) error { + var tmp struct { + Name string `json:"name"` + Columns []string `json:"columns"` + Expression string `json:"expression"` + Comment string `json:"comment"` + Extra E `json:"extra"` + } + + if err := json.Unmarshal(data, &tmp); err != nil { + return err + } + + c.Name = tmp.Name + c.Columns = tmp.Columns + c.Expression = tmp.Expression + c.Comment = tmp.Comment + c.Extra = tmp.Extra + + return nil +} + +func (c Check[E]) MarshalJSON() ([]byte, error) { + tmp := struct { + Name string `json:"name"` + Columns []string `json:"columns"` + Expression string `json:"expression"` + Comment string `json:"comment"` + Extra E `json:"extra"` + }{ + Name: c.Name, + Columns: c.Columns, + Expression: c.Expression, + Comment: c.Comment, + Extra: c.Extra, } return json.Marshal(tmp) diff --git a/gen/drivers/interface.go b/gen/drivers/interface.go index da877bd..608947a 100644 --- a/gen/drivers/interface.go +++ b/gen/drivers/interface.go @@ -69,9 +69,10 @@ type Enum struct { type TablesInfo []TableInfo type TableInfo struct { - Key string - Schema string - Name string + Key string + Schema string + Name string + Comment string } func (t TablesInfo) Keys() []string { @@ -86,20 +87,20 @@ func (t TablesInfo) Keys() []string { // such that the drivers.Tables method can be used to reduce duplication in driver // implementations. type Constructor[ConstraintExtra, IndexExtra any] interface { - // Load all constraints in the database, keyed by TableInfo.Key - Constraints(context.Context, ColumnFilter) (DBConstraints[ConstraintExtra], error) - - // Load all indexes in the database, keyed by TableInfo.Key - Indexes(ctx context.Context) (DBIndexes[IndexExtra], error) - // Load basic info about all tables TablesInfo(context.Context, Filter) (TablesInfo, error) // Load details about a single table TableDetails(ctx context.Context, info TableInfo, filter ColumnFilter) (schema, name string, _ []Column, _ error) + // Load all table comments, keyed by TableInfo.Key + Comments(ctx context.Context) (map[string]string, error) + // Load all constraints in the database, keyed by TableInfo.Key + Constraints(context.Context, ColumnFilter) (DBConstraints[ConstraintExtra], error) + // Load all indexes in the database, keyed by TableInfo.Key + Indexes(ctx context.Context) (DBIndexes[IndexExtra], error) } -// TablesConcurrently is a concurrent version of BuildDBInfo. It returns the -// metadata for all tables, minus the tables specified in the excludes. +// This returns the metadata for all tables, +// minus the tables specified in the excludes. func BuildDBInfo[DBExtra, ConstraintExtra, IndexExtra any]( ctx context.Context, c Constructor[ConstraintExtra, IndexExtra], concurrency int, only, except map[string][]string, @@ -125,6 +126,14 @@ func BuildDBInfo[DBExtra, ConstraintExtra, IndexExtra any]( return nil, fmt.Errorf("unable to load tables: %w", err) } + comments, err := c.Comments(ctx) + if err != nil { + return nil, fmt.Errorf("unable to load comments: %w", err) + } + for i, t := range ret { + ret[i].Comment = comments[t.Key] + } + constraints, err := c.Constraints(ctx, colFilter) if err != nil { return nil, fmt.Errorf("unable to load constraints: %w", err) @@ -133,6 +142,7 @@ func BuildDBInfo[DBExtra, ConstraintExtra, IndexExtra any]( ret[i].Constraints.Primary = constraints.PKs[t.Key] ret[i].Constraints.Foreign = constraints.FKs[t.Key] ret[i].Constraints.Uniques = constraints.Uniques[t.Key] + ret[i].Constraints.Checks = constraints.Checks[t.Key] } indexes, err := c.Indexes(ctx) diff --git a/gen/drivers/table.go b/gen/drivers/table.go index 7c85e06..62b1276 100644 --- a/gen/drivers/table.go +++ b/gen/drivers/table.go @@ -13,12 +13,12 @@ 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[IndexExtra] `yaml:"indexes" json:"indexes"` - + 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"` Constraints Constraints[ConstraintExtra] `yaml:"constraints" json:"constraints"` + Comment string `json:"comment" yaml:"comment"` } func (t Table[C, I]) DBTag(c Column) string { @@ -78,7 +78,6 @@ func (t Table[C, I]) GetColumn(name string) Column { // 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 } @@ -88,8 +87,11 @@ func (t Table[C, I]) HasExactUnique(cols ...string) bool { } // Check other unique constrints - for _, u := range t.Constraints.Uniques { - if internal.SliceMatch(u.Columns, cols) { + for _, u := range t.Indexes { + if !u.Unique || u.HasExpressionColumn() { + continue + } + if internal.SliceMatch(u.NonExpressionColumns(), cols) { return true } } @@ -190,12 +192,12 @@ func (t Table[C, I]) IsJoinTableForRel(r orm.Relationship, position int) bool { 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)) + for _, unique := range t.Indexes { + if !unique.Unique || unique.HasExpressionColumn() { + continue + } + ret = append(ret, fmt.Sprintf("%#v", unique.NonExpressionColumns())) } return strings.Join(ret, ", ") diff --git a/go.mod b/go.mod index e2144ac..183ae11 100644 --- a/go.mod +++ b/go.mod @@ -1,8 +1,6 @@ module github.com/stephenafamo/bob -go 1.22 - -toolchain go1.22.1 +go 1.23 require ( github.com/DATA-DOG/go-txdb v0.1.6 @@ -21,7 +19,7 @@ require ( github.com/lib/pq v1.10.7 github.com/nsf/jsondiff v0.0.0-20210926074059-1e845ec5d249 github.com/qdm12/reprint v0.0.0-20200326205758-722754a53494 - github.com/stephenafamo/scan v0.6.0 + github.com/stephenafamo/scan v0.6.1 github.com/stephenafamo/sqlparser v0.0.0-20241111104950-b04fa8a26c9c github.com/urfave/cli/v2 v2.23.7 github.com/volatiletech/strmangle v0.0.6 diff --git a/go.sum b/go.sum index 1febf51..5f2a47b 100644 --- a/go.sum +++ b/go.sum @@ -111,6 +111,8 @@ github.com/stephenafamo/fakedb v0.0.0-20221230081958-0b86f816ed97 h1:XItoZNmhOih github.com/stephenafamo/fakedb v0.0.0-20221230081958-0b86f816ed97/go.mod h1:bM3Vmw1IakoaXocHmMIGgJFYob0vuK+CFWiJHQvz0jQ= github.com/stephenafamo/scan v0.6.0 h1:N0joyP/wriC9VvP6w9SDxHIuQGatW4c2YW7Z5L4m45s= github.com/stephenafamo/scan v0.6.0/go.mod h1:FhIUJ8pLNyex36xGFiazDJJ5Xry0UkAi+RkWRrEcRMg= +github.com/stephenafamo/scan v0.6.1 h1:nXokGCQwYazMuyvdNAoK0T8Z76FWcpMvDdtengpz6PU= +github.com/stephenafamo/scan v0.6.1/go.mod h1:FhIUJ8pLNyex36xGFiazDJJ5Xry0UkAi+RkWRrEcRMg= github.com/stephenafamo/sqlparser v0.0.0-20241111104950-b04fa8a26c9c h1:JFga++XBnZG2xlnvQyHJkeBWZ9G9mGdtgvLeSRbp/BA= github.com/stephenafamo/sqlparser v0.0.0-20241111104950-b04fa8a26c9c/go.mod h1:4iveRk8mkzQZxDuK/W0MGLrGmu/igyDYWNDD4a6v0r0= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= diff --git a/test/files/mysql/schema.sql b/test/files/mysql/schema.sql index 234b91b..57a5894 100644 --- a/test/files/mysql/schema.sql +++ b/test/files/mysql/schema.sql @@ -18,7 +18,7 @@ create table sponsors ( create table videos ( id int primary key not null auto_increment, - user_id int not null COMMENT 'this is a comment', + user_id int not null COMMENT 'This is a column', sponsor_id int unique, foreign key (user_id) references users (id), @@ -39,7 +39,7 @@ create table video_tags ( ); create table type_monsters ( - id int primary key not null auto_increment COMMENT 'comment on ID', + id int primary key not null auto_increment COMMENT 'This is another column', enum_use enum('monday', 'tuesday', 'wednesday', 'thursday', 'friday') not null, enum_nullable enum('monday', 'tuesday', 'wednesday', 'thursday', 'friday'), @@ -182,7 +182,7 @@ create table type_monsters ( generated_null text GENERATED ALWAYS AS (UPPER(text_null)) STORED, UNIQUE(int_one, int_two) -); +) COMMENT = 'This is a table'; create view user_videos as select u.id user_id, v.id video_id, v.sponsor_id sponsor_id @@ -196,7 +196,7 @@ CREATE TABLE multi_keys ( user_id INT NOT NULL, sponsor_id INT UNIQUE, - something INT, + something INT CHECK (something > 0), another INT, one int NULL, @@ -211,7 +211,7 @@ CREATE TABLE test_index_expressions ( col2 int, col3 int ); -CREATE INDEX idx1 ON test_index_expressions ((col1 + col2)); +CREATE INDEX idx1 ON test_index_expressions ((col1 + col2)) COMMENT 'This is an index'; CREATE INDEX idx2 ON test_index_expressions ((col1 + col2), col3); CREATE INDEX idx3 ON test_index_expressions (col1, (col2 + col3)); CREATE INDEX idx4 ON test_index_expressions (col3); diff --git a/test/files/psql/schema.sql b/test/files/psql/schema.sql index dea5aec..04506ed 100644 --- a/test/files/psql/schema.sql +++ b/test/files/psql/schema.sql @@ -75,7 +75,7 @@ create table type_monsters ( bool_zero bool, bool_one bool null, - bool_two bool not null, + bool_two bool not null CONSTRAINT check_not_null CHECK(bool_two IS NOT NULL), bool_three bool null default false, bool_four bool null default true, bool_five bool not null default false, @@ -279,6 +279,7 @@ CREATE TABLE test_index_expressions ( col2 int, col3 int ); + CREATE INDEX idx1 ON test_index_expressions ((col1 + col2)); CREATE INDEX idx2 ON test_index_expressions ((col1 + col2), col3); CREATE INDEX idx3 ON test_index_expressions (col1, (col2 + col3)); @@ -310,3 +311,10 @@ CREATE TABLE bar_qux ( id SERIAL PRIMARY KEY, secret_col VARCHAR(255) NOT NULL ); + +-- Add comments +COMMENT ON TABLE type_monsters IS 'This is a table'; +COMMENT ON VIEW type_monsters_v IS 'This is a view'; +COMMENT ON MATERIALIZED VIEW type_monsters_mv IS 'This is a materialized view'; +COMMENT ON INDEX idx1 IS 'This is an index'; +COMMENT ON CONSTRAINT check_not_null ON type_monsters IS 'This is a constraint'; diff --git a/test/gen/gen.go b/test/gen/gen.go index 4e91add..f6ec50f 100644 --- a/test/gen/gen.go +++ b/test/gen/gen.go @@ -93,6 +93,27 @@ type DriverTestConfig[T, C, I any] struct { GetDriver func() drivers.Interface[T, C, I] } +type AssembleTestConfig[T, C, I any] struct { + Templates *helpers.Templates + OverwriteGolden bool + GoldenFile string + GetDriver func() drivers.Interface[T, C, I] +} + +func TestAssemble[T, C, I any](t *testing.T, config AssembleTestConfig[T, C, I]) { + t.Helper() + + d := &driverWrapper[T, C, I]{ + Interface: config.GetDriver(), + overwriteGolden: config.OverwriteGolden, + goldenFile: config.GoldenFile, + } + + t.Run("assemble", func(t *testing.T) { + d.TestAssemble(t) + }) +} + func TestDriver[T, C, I any](t *testing.T, config DriverTestConfig[T, C, I]) { t.Helper() diff --git a/website/docs/code-generation/usage.md b/website/docs/code-generation/usage.md index 19159b8..67f70f0 100644 --- a/website/docs/code-generation/usage.md +++ b/website/docs/code-generation/usage.md @@ -33,15 +33,15 @@ A lot of other helpful methods and functions are generated, but let us look at t // Jet is an object representing the database table. // The required methods to satisfy orm.Table are also generated type Jet struct { - ID int `db:"id,pk" json:"id" toml:"id" yaml:"id"` - PilotID int `db:"pilot_id" json:"pilot_id" toml:"pilot_id" yaml:"pilot_id"` - AirportID int `db:"airport_id" json:"airport_id" toml:"airport_id" yaml:"airport_id"` - Name string `db:"name" json:"name" toml:"name" yaml:"name"` - Color null.Val[string] `db:"color" json:"color,omitempty" toml:"color" yaml:"color,omitempty"` - UUID string `db:"uuid" json:"uuid" toml:"uuid" yaml:"uuid"` - Identifier string `db:"identifier" json:"identifier" toml:"identifier" yaml:"identifier"` - Cargo []byte `db:"cargo" json:"cargo" toml:"cargo" yaml:"cargo"` - Manifest []byte `db:"manifest" json:"manifest" toml:"manifest" yaml:"manifest"` + ID int `db:"id,pk" json:"id" yaml:"id"` + PilotID int `db:"pilot_id" json:"pilot_id" yaml:"pilot_id"` + AirportID int `db:"airport_id" json:"airport_id" yaml:"airport_id"` + Name string `db:"name" json:"name" yaml:"name"` + Color null.Val[string] `db:"color" json:"color,omitempty" yaml:"color,omitempty"` + UUID string `db:"uuid" json:"uuid" yaml:"uuid"` + Identifier string `db:"identifier" json:"identifier" yaml:"identifier"` + Cargo []byte `db:"cargo" json:"cargo" yaml:"cargo"` + Manifest []byte `db:"manifest" json:"manifest" yaml:"manifest"` } // JetSetter is used for insert/upsert/update operations