Skip to content

Commit

Permalink
duplicate indexes on duplicated columns
Browse files Browse the repository at this point in the history
  • Loading branch information
kvch committed Jan 7, 2025
1 parent 5c44365 commit b47f0b3
Show file tree
Hide file tree
Showing 5 changed files with 79 additions and 16 deletions.
1 change: 1 addition & 0 deletions examples/.ledger
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,4 @@
46_alter_column_drop_default.json
47_add_table_foreign_key_constraint.json
48_drop_tickets_check.json
49_set_not_null_on_indexed_column.json
14 changes: 14 additions & 0 deletions examples/49_set_not_null_on_indexed_column.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"name": "49_unset_not_null_on_indexed_column",
"operations": [
{
"alter_column": {
"table": "fruits",
"column": "name",
"nullable": true,
"up": "SELECT CASE WHEN name IS NULL THEN 'unknown fruit' ELSE name END",
"down": "name"
}
}
]
}
45 changes: 45 additions & 0 deletions pkg/migrations/duplicate.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,13 @@ func (d *Duplicator) Duplicate(ctx context.Context) error {
}
}

// Generate SQL to duplicate any indexes on the columns.
for _, sql := range d.stmtBuilder.duplicateIndexes(d.withoutConstraint, colNames...) {
if _, err := d.conn.ExecContext(ctx, sql); err != nil {
return err
}
}

return nil
}

Expand Down Expand Up @@ -204,6 +211,44 @@ func (d *duplicatorStmtBuilder) duplicateForeignKeyConstraints(withoutConstraint
return stmts
}

func (d *duplicatorStmtBuilder) duplicateIndexes(withoutConstraint []string, colNames ...string) []string {
stmts := make([]string, 0, len(d.table.Indexes))
for _, idx := range d.table.Indexes {
if slices.Contains(withoutConstraint, idx.Name) {
continue
}
if _, ok := d.table.UniqueConstraints[idx.Name]; ok && idx.Unique {
// unique constraints are duplicated as unique indexes
continue
}

if duplicatedMember, columns := d.allConstraintColumns(idx.Columns, colNames...); duplicatedMember {
stmtFmt := "CREATE INDEX %s ON %s"
if idx.Unique {
stmtFmt = "CREATE UNIQUE INDEX %s ON %s"
}
stmt := fmt.Sprintf(stmtFmt, pq.QuoteIdentifier(DuplicationName(idx.Name)), pq.QuoteIdentifier(d.table.Name))
if idx.Method != "" {
stmt += fmt.Sprintf(" USING %s", string(idx.Method))
}

stmt += fmt.Sprintf(" (%s)", strings.Join(quoteColumnNames(columns), ", "))

if idx.StorageParameters != "" {
stmt += fmt.Sprintf(" WITH (%s)", idx.StorageParameters)
}

if idx.Predicate != nil {
pred := strings.Replace(*idx.Predicate, strings.Join(idx.Columns, ", "), strings.Join(quoteColumnNames(columns), ", "), 1)
stmt += fmt.Sprintf(" WHERE %s", pred)
}

stmts = append(stmts, stmt)
}
}
return stmts
}

// duplicatedConstraintColumns returns a new slice of constraint columns with
// the columns that are duplicated replaced with temporary names.
func (d *duplicatorStmtBuilder) duplicatedConstraintColumns(constraintColumns []string, duplicatedColumns ...string) []string {
Expand Down
32 changes: 16 additions & 16 deletions pkg/migrations/rename.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,42 +120,42 @@ func RenameDuplicatedColumn(ctx context.Context, conn db.DB, table *schema.Table
}
}

// Rename any `UNIQUE` indexes on the duplicated column and use them to
// Rename any indexes on the duplicated column and use unique indexes to
// create `UNIQUE` constraints.
for _, ui := range table.Indexes {
if !IsDuplicatedName(ui.Name) {
continue
}
if !ui.Unique {
for _, idx := range table.Indexes {
if !IsDuplicatedName(idx.Name) {
continue
}

if slices.Contains(ui.Columns, TemporaryName(column.Name)) {
// Rename the unique index to its original name
if slices.Contains(idx.Columns, TemporaryName(column.Name)) {
// Rename the index to its original name
renameIndexSQL := fmt.Sprintf(cRenameIndexSQL,
pq.QuoteIdentifier(ui.Name),
pq.QuoteIdentifier(StripDuplicationPrefix(ui.Name)),
pq.QuoteIdentifier(idx.Name),
pq.QuoteIdentifier(StripDuplicationPrefix(idx.Name)),
)

_, err = conn.ExecContext(ctx, renameIndexSQL)
if err != nil {
return fmt.Errorf("failed to rename unique index %q: %w", ui.Name, err)
return fmt.Errorf("failed to rename index %q: %w", idx.Name, err)
}
}

if idx.Unique {
// Create a unique constraint using the unique index
createUniqueConstraintSQL := fmt.Sprintf(cCreateUniqueConstraintSQL,
pq.QuoteIdentifier(table.Name),
pq.QuoteIdentifier(StripDuplicationPrefix(ui.Name)),
pq.QuoteIdentifier(StripDuplicationPrefix(ui.Name)),
pq.QuoteIdentifier(StripDuplicationPrefix(idx.Name)),
pq.QuoteIdentifier(StripDuplicationPrefix(idx.Name)),
)

_, err = conn.ExecContext(ctx, createUniqueConstraintSQL)
if err != nil {
return fmt.Errorf("failed to create unique constraint from index %q: %w", ui.Name, err)
return fmt.Errorf("failed to create unique constraint from index %q: %w", idx.Name, err)
}
// Index no longer exists, remove it from the table
delete(table.Indexes, ui.Name)
}

// Index no longer exists, remove it from the table
delete(table.Indexes, idx.Name)
}

return nil
Expand Down
3 changes: 3 additions & 0 deletions pkg/schema/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,9 @@ type Index struct {
// Method is the method for the index
Method string `json:"method,omitempty"`

// StorageParameters is the storage parameters for the index
StorageParameters string `json:"storageParameters,omitempty"`

// Definition is statement to construct the index
Definition string `json:"definition"`
}
Expand Down

0 comments on commit b47f0b3

Please sign in to comment.