feat(pgsql): unique constraints on apply, matching unique index names, FK deferral
- migration writer now emits unique constraints (direct apply and live diff previously dropped them); idempotent via the existing DO block - unique constraint adopts a same-named model unique index with ADD CONSTRAINT ... UNIQUE USING INDEX instead of building a second index - dbml reader names unnamed unique indexes ukey_<table>_<cols> to match the unique constraint naming - foreign key deferral: Constraint.Deferrable/InitiallyDeferred are *bool (nil = unspecified, never diffed); writers emit DEFERRABLE, DEFERRABLE INITIALLY DEFERRED and NOT DEFERRABLE; live reader reads condeferrable and condeferred; dbml reader/writer support deferrable and initially deferred - tests and README/naming docs updated
This commit is contained in:
@@ -848,6 +848,13 @@ func (r *Reader) parseColumn(line, tableName, schemaName string) (*models.Column
|
||||
}
|
||||
}
|
||||
|
||||
// "deferrable" / "initially deferred" column settings apply to the inline foreign key
|
||||
if constraint != nil && constraint.Type == models.ForeignKeyConstraint && attrs != "" {
|
||||
for _, attr := range splitColumnAttrs(attrs) {
|
||||
applyDeferralSetting(constraint, attr)
|
||||
}
|
||||
}
|
||||
|
||||
// Parse inline comment
|
||||
if inlineComment != "" {
|
||||
column.Comment = inlineComment
|
||||
@@ -1081,11 +1088,13 @@ func (r *Reader) parseIndex(line, tableName, schemaName string) *models.Index {
|
||||
|
||||
// Generate name if not provided
|
||||
if index.Name == "" {
|
||||
prefix := "idx"
|
||||
// Unique indexes share the ukey_ naming of unique constraints, so an index and a
|
||||
// constraint on the same columns get the same name and can be merged on write.
|
||||
prefix, name := "idx", tableName
|
||||
if index.Unique {
|
||||
prefix = "uidx"
|
||||
prefix, name = "ukey", strings.TrimLeft(tableName, "_")
|
||||
}
|
||||
index.Name = fmt.Sprintf("%s_%s_%s", prefix, tableName, strings.Join(columns, "_"))
|
||||
index.Name = fmt.Sprintf("%s_%s_%s", prefix, name, strings.Join(columns, "_"))
|
||||
}
|
||||
|
||||
return index
|
||||
@@ -1182,6 +1191,8 @@ func (r *Reader) parseRef(refStr string) *models.Constraint {
|
||||
constraint.OnDelete = strings.TrimSpace(strings.TrimPrefix(action, "ondelete:"))
|
||||
} else if strings.HasPrefix(action, "onupdate:") {
|
||||
constraint.OnUpdate = strings.TrimSpace(strings.TrimPrefix(action, "onupdate:"))
|
||||
} else {
|
||||
applyDeferralSetting(constraint, action)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1190,6 +1201,18 @@ func (r *Reader) parseRef(refStr string) *models.Constraint {
|
||||
return constraint
|
||||
}
|
||||
|
||||
// applyDeferralSetting applies a "deferrable" or "initially deferred" Ref setting to a
|
||||
// foreign key constraint. Other settings are ignored.
|
||||
func applyDeferralSetting(constraint *models.Constraint, setting string) {
|
||||
yes := true
|
||||
switch strings.Join(strings.Fields(strings.ToLower(setting)), " ") {
|
||||
case "deferrable":
|
||||
constraint.Deferrable = &yes
|
||||
case "initially deferred", "initially_deferred":
|
||||
constraint.InitiallyDeferred = &yes
|
||||
}
|
||||
}
|
||||
|
||||
// parseTableRef parses a table reference like "schema.table.(column1, column2)" or "schema"."table"."column"
|
||||
func (r *Reader) parseTableRef(ref string) (schema, table string, columns []string) {
|
||||
// Extract columns if present in parentheses format
|
||||
|
||||
Reference in New Issue
Block a user