Skip to content

Commit 3922f45

Browse files
authored
Add missing attributes to column info in virtual schema (#789)
When a table was added, `pgroll` did not store all required column metadata. Default values and comments were missing from columns. So when `pgroll` tried to duplicate the column, these got lost. Example to reproduce the issue: ```json { "operations": [ { "create_table": { "name": "useres", "comment": "my_comment", "columns": [ { "name": "id", "type": "uuid", "pk": true, "nullable": false, "default": "gen_random_uuid()", "comment": "my_comment" } ] } }, { "create_constraint": { "table": "users", "columns": ["id"], "type": "unique", "name": "my_unique", "up": { "id": "id" }, "down": { "id": "id" } } } ] } ```
1 parent 08c7e01 commit 3922f45

File tree

3 files changed

+131
-0
lines changed

3 files changed

+131
-0
lines changed

pkg/migrations/op_common_test.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,13 @@ func ColumnMustNotHaveComment(t *testing.T, db *sql.DB, schema, table, column st
190190
}
191191
}
192192

193+
func ColumnMustHaveDefault(t *testing.T, db *sql.DB, schema, table, column, expectedDefault string) {
194+
t.Helper()
195+
if !columnHasDefault(t, db, schema, table, column, &expectedDefault) {
196+
t.Fatalf("Expected column %q to have default value %q", column, expectedDefault)
197+
}
198+
}
199+
193200
func ColumnMustBePK(t *testing.T, db *sql.DB, schema, table, column string) {
194201
t.Helper()
195202
if !columnMustBePK(t, db, schema, table, column) {
@@ -710,6 +717,27 @@ func columnHasComment(t *testing.T, db *sql.DB, schema, table, column string, ex
710717
return actualComment != nil && *expectedComment == *actualComment
711718
}
712719

720+
func columnHasDefault(t *testing.T, db *sql.DB, schema, table, column string, expectedDefault *string) bool {
721+
t.Helper()
722+
723+
var actualDefault *string
724+
err := db.QueryRow(`
725+
SELECT column_default
726+
FROM information_schema.columns
727+
WHERE table_schema = $1
728+
AND table_name = $2
729+
AND column_name = $3
730+
`,
731+
schema, table, column).Scan(&actualDefault)
732+
if err != nil {
733+
t.Fatal(err)
734+
}
735+
if expectedDefault == nil {
736+
return actualDefault == nil
737+
}
738+
return actualDefault != nil && *expectedDefault == *actualDefault
739+
}
740+
713741
func columnMustBePK(t *testing.T, db *sql.DB, schema, table, column string) bool {
714742
t.Helper()
715743

pkg/migrations/op_create_constraint_test.go

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1225,6 +1225,105 @@ func TestCreateConstraintInMultiOperationMigrations(t *testing.T) {
12251225
TableMustBeCleanedUp(t, db, schema, "products", "name")
12261226
},
12271227
},
1228+
{
1229+
name: "create table with comment and default on column, add unique constraint",
1230+
migrations: []migrations.Migration{
1231+
{
1232+
Name: "01_create_table",
1233+
Operations: migrations.Operations{
1234+
&migrations.OpCreateTable{
1235+
Name: "items",
1236+
Columns: []migrations.Column{
1237+
{
1238+
Name: "id",
1239+
Type: "int",
1240+
Pk: true,
1241+
},
1242+
{
1243+
Name: "name",
1244+
Type: "text",
1245+
Default: ptr("'Pixel'"),
1246+
Comment: ptr("my important comment"),
1247+
},
1248+
},
1249+
},
1250+
&migrations.OpCreateConstraint{
1251+
Table: "items",
1252+
Type: migrations.OpCreateConstraintTypeUnique,
1253+
Name: "unique_item_name",
1254+
Columns: []string{"name"},
1255+
Up: map[string]string{
1256+
"name": "name",
1257+
},
1258+
Down: map[string]string{
1259+
"name": "name",
1260+
},
1261+
},
1262+
},
1263+
},
1264+
},
1265+
afterStart: func(t *testing.T, db *sql.DB, schema string) {
1266+
// Column must have comment set
1267+
ColumnMustHaveComment(t, db, schema, "items", "name", "my important comment")
1268+
// Column must have default value
1269+
ColumnMustHaveDefault(t, db, schema, "items", "name", "'Pixel'::text")
1270+
// Can insert a row into the new schema
1271+
MustInsert(t, db, schema, "01_create_table", "items", map[string]string{
1272+
"id": "1",
1273+
"name": "apple",
1274+
})
1275+
1276+
// Can insert a row into the new schema that meets the constraint
1277+
MustInsert(t, db, schema, "01_create_table", "items", map[string]string{
1278+
"id": "2",
1279+
"name": "banana",
1280+
})
1281+
1282+
// Can't insert a row into the new schema that violates the constraint
1283+
MustNotInsert(t, db, schema, "01_create_table", "items", map[string]string{
1284+
"id": "3",
1285+
"name": "apple",
1286+
}, testutils.UniqueViolationErrorCode)
1287+
1288+
// The new view has the expected rows
1289+
rows := MustSelect(t, db, schema, "01_create_table", "items")
1290+
assert.Equal(t, []map[string]any{
1291+
{"id": 1, "name": "apple"},
1292+
{"id": 2, "name": "banana"},
1293+
}, rows)
1294+
1295+
// The old view has the expected rows
1296+
rows = MustSelect(t, db, schema, "01_create_table", "items")
1297+
assert.Equal(t, []map[string]any{
1298+
{"id": 1, "name": "apple"},
1299+
{"id": 2, "name": "banana"},
1300+
}, rows)
1301+
},
1302+
afterRollback: func(t *testing.T, db *sql.DB, schema string) {},
1303+
afterComplete: func(t *testing.T, db *sql.DB, schema string) {
1304+
// Column comment must be preserved
1305+
ColumnMustHaveComment(t, db, schema, "items", "name", "my important comment")
1306+
// Column must have default value
1307+
ColumnMustHaveDefault(t, db, schema, "items", "name", "'Pixel'::text")
1308+
// Can insert a row into the new schema that meets the constraint
1309+
MustInsert(t, db, schema, "01_create_table", "items", map[string]string{
1310+
"id": "3",
1311+
"name": "carrot",
1312+
})
1313+
1314+
// Can't insert a row into the new schema that violates the constraint
1315+
MustNotInsert(t, db, schema, "01_create_table", "items", map[string]string{
1316+
"id": "4",
1317+
"name": "carrot",
1318+
}, testutils.UniqueViolationErrorCode)
1319+
1320+
// The new view has the expected rows
1321+
rows := MustSelect(t, db, schema, "01_create_table", "items")
1322+
assert.Equal(t, []map[string]any{
1323+
{"id": 3, "name": "carrot"},
1324+
}, rows)
1325+
},
1326+
},
12281327
})
12291328
}
12301329

pkg/migrations/op_create_table.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,10 +207,14 @@ func (o *OpCreateTable) updateSchema(s *schema.Schema) *schema.Schema {
207207
Unique: col.Unique,
208208
Nullable: col.Nullable,
209209
Type: col.Type,
210+
Default: col.Default,
210211
}
211212
if col.Pk {
212213
primaryKeys = append(primaryKeys, col.Name)
213214
}
215+
if col.Comment != nil {
216+
columns[col.Name].Comment = *col.Comment
217+
}
214218
}
215219

216220
uniqueConstraints := make(map[string]*schema.UniqueConstraint, 0)

0 commit comments

Comments
 (0)