fix(dbml): resolve commented cross-file // Ref: lines

Commented refs are collected per file and resolved against the combined
model after all inputs are loaded (directory, --from-list, merge, jobs).
Matched refs become FKs and relationships; duplicates of existing FKs are
skipped; missing targets are skipped with a warning; column type
mismatches warn.

Also keep reused index names within a DBML table instead of overwriting,
give a second FK to the same table a distinct relationship name, and make
the pgsql writer match relationships to FKs by name first.
This commit is contained in:
2026-09-23 18:48:07 +02:00
parent b91985c493
commit 2f69205aa0
13 changed files with 745 additions and 20 deletions
+49
View File
@@ -1549,3 +1549,52 @@ func TestGenerateColumnDefinition_IdentityColumnEmitsIdentityClauseNotDefault(t
t.Fatalf("generateColumnDefinition() = %q, want %q", got, want)
}
}
// Two FKs to the same table: each relationship must emit its own FK columns,
// not whichever FK map iteration returns first.
func TestWriteDatabase_MultipleForeignKeysToSameTable(t *testing.T) {
db := models.InitDatabase("testdb")
schema := models.InitSchema("public")
dept := models.InitTable("department", "public")
id := models.InitColumn("id_department", "department", "public")
id.Type = "bigint"
id.IsPrimaryKey = true
dept.Columns["id_department"] = id
emp := models.InitTable("employee", "public")
for _, name := range []string{"rid_department", "rid_manager"} {
col := models.InitColumn(name, "employee", "public")
col.Type = "bigint"
emp.Columns[name] = col
fkName := "fk_employee_" + name
fk := models.InitConstraint(fkName, models.ForeignKeyConstraint)
fk.Schema, fk.Table, fk.Columns = "public", "employee", []string{name}
fk.ReferencedSchema, fk.ReferencedTable, fk.ReferencedColumns = "public", "department", []string{"id_department"}
emp.Constraints[fkName] = fk
rel := models.InitRelationship("employee_to_department_"+name, models.OneToMany)
rel.FromTable, rel.ToTable, rel.ToSchema, rel.ForeignKey = "employee", "department", "public", fkName
emp.Relationships[rel.Name] = rel
}
schema.Tables = append(schema.Tables, dept, emp)
db.Schemas = append(db.Schemas, schema)
for i := 0; i < 20; i++ {
var buf bytes.Buffer
writer := NewWriter(&writers.WriterOptions{})
writer.writer = &buf
if err := writer.WriteDatabase(db); err != nil {
t.Fatalf("WriteDatabase failed: %v", err)
}
output := buf.String()
for _, name := range []string{"rid_department", "rid_manager"} {
want := "ADD CONSTRAINT fk_employee_" + name + "\n FOREIGN KEY (" + name + ")"
if !strings.Contains(output, want) {
t.Fatalf("run %d: missing %q in output:\n%s", i, want, output)
}
}
}
}