Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a54594e49b |
@@ -295,6 +295,31 @@ func (w *Writer) GenerateSchemaStatements(schema *models.Schema) ([]string, erro
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Phase 5.5: Unique constraints
|
||||||
|
for _, table := range schema.Tables {
|
||||||
|
for _, constraint := range table.Constraints {
|
||||||
|
if constraint.Type != models.UniqueConstraint {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wrap in DO block to check for existing constraint
|
||||||
|
stmt := fmt.Sprintf("DO $$\nBEGIN\n"+
|
||||||
|
" IF NOT EXISTS (\n"+
|
||||||
|
" SELECT 1 FROM information_schema.table_constraints\n"+
|
||||||
|
" WHERE table_schema = '%s'\n"+
|
||||||
|
" AND table_name = '%s'\n"+
|
||||||
|
" AND constraint_name = '%s'\n"+
|
||||||
|
" ) THEN\n"+
|
||||||
|
" ALTER TABLE %s.%s ADD CONSTRAINT %s UNIQUE (%s);\n"+
|
||||||
|
" END IF;\n"+
|
||||||
|
"END;\n$$",
|
||||||
|
schema.Name, table.Name, constraint.Name,
|
||||||
|
schema.SQLName(), table.SQLName(), constraint.Name,
|
||||||
|
strings.Join(constraint.Columns, ", "))
|
||||||
|
statements = append(statements, stmt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Phase 6: Foreign keys
|
// Phase 6: Foreign keys
|
||||||
for _, table := range schema.Tables {
|
for _, table := range schema.Tables {
|
||||||
for _, constraint := range table.Constraints {
|
for _, constraint := range table.Constraints {
|
||||||
@@ -542,6 +567,11 @@ func (w *Writer) WriteSchema(schema *models.Schema) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Phase 5.5: Create unique constraints (priority 185)
|
||||||
|
if err := w.writeUniqueConstraints(schema); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// Phase 6: Create foreign key constraints (priority 195)
|
// Phase 6: Create foreign key constraints (priority 195)
|
||||||
if err := w.writeForeignKeys(schema); err != nil {
|
if err := w.writeForeignKeys(schema); err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -865,6 +895,55 @@ func (w *Writer) writeIndexes(schema *models.Schema) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// writeUniqueConstraints generates ALTER TABLE statements for unique constraints
|
||||||
|
func (w *Writer) writeUniqueConstraints(schema *models.Schema) error {
|
||||||
|
fmt.Fprintf(w.writer, "-- Unique constraints for schema: %s\n", schema.Name)
|
||||||
|
|
||||||
|
for _, table := range schema.Tables {
|
||||||
|
// Sort constraints by name for consistent output
|
||||||
|
constraintNames := make([]string, 0, len(table.Constraints))
|
||||||
|
for name, constraint := range table.Constraints {
|
||||||
|
if constraint.Type == models.UniqueConstraint {
|
||||||
|
constraintNames = append(constraintNames, name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sort.Strings(constraintNames)
|
||||||
|
|
||||||
|
for _, name := range constraintNames {
|
||||||
|
constraint := table.Constraints[name]
|
||||||
|
|
||||||
|
// Build column list
|
||||||
|
columnExprs := make([]string, 0, len(constraint.Columns))
|
||||||
|
for _, colName := range constraint.Columns {
|
||||||
|
if col, ok := table.Columns[colName]; ok {
|
||||||
|
columnExprs = append(columnExprs, col.SQLName())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(columnExprs) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wrap in DO block to check for existing constraint
|
||||||
|
fmt.Fprintf(w.writer, "DO $$\n")
|
||||||
|
fmt.Fprintf(w.writer, "BEGIN\n")
|
||||||
|
fmt.Fprintf(w.writer, " IF NOT EXISTS (\n")
|
||||||
|
fmt.Fprintf(w.writer, " SELECT 1 FROM information_schema.table_constraints\n")
|
||||||
|
fmt.Fprintf(w.writer, " WHERE table_schema = '%s'\n", schema.Name)
|
||||||
|
fmt.Fprintf(w.writer, " AND table_name = '%s'\n", table.Name)
|
||||||
|
fmt.Fprintf(w.writer, " AND constraint_name = '%s'\n", constraint.Name)
|
||||||
|
fmt.Fprintf(w.writer, " ) THEN\n")
|
||||||
|
fmt.Fprintf(w.writer, " ALTER TABLE %s.%s ADD CONSTRAINT %s UNIQUE (%s);\n",
|
||||||
|
schema.SQLName(), table.SQLName(), constraint.Name, strings.Join(columnExprs, ", "))
|
||||||
|
fmt.Fprintf(w.writer, " END IF;\n")
|
||||||
|
fmt.Fprintf(w.writer, "END;\n")
|
||||||
|
fmt.Fprintf(w.writer, "$$;\n\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// writeForeignKeys generates ALTER TABLE statements for foreign keys
|
// writeForeignKeys generates ALTER TABLE statements for foreign keys
|
||||||
func (w *Writer) writeForeignKeys(schema *models.Schema) error {
|
func (w *Writer) writeForeignKeys(schema *models.Schema) error {
|
||||||
fmt.Fprintf(w.writer, "-- Foreign keys for schema: %s\n", schema.Name)
|
fmt.Fprintf(w.writer, "-- Foreign keys for schema: %s\n", schema.Name)
|
||||||
|
|||||||
@@ -164,6 +164,76 @@ func TestWriteForeignKeys(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestWriteUniqueConstraints(t *testing.T) {
|
||||||
|
// Create a test database with unique constraints
|
||||||
|
db := models.InitDatabase("testdb")
|
||||||
|
schema := models.InitSchema("public")
|
||||||
|
|
||||||
|
// Create table with unique constraints
|
||||||
|
table := models.InitTable("users", "public")
|
||||||
|
|
||||||
|
// Add columns
|
||||||
|
emailCol := models.InitColumn("email", "users", "public")
|
||||||
|
emailCol.Type = "varchar(255)"
|
||||||
|
emailCol.NotNull = true
|
||||||
|
table.Columns["email"] = emailCol
|
||||||
|
|
||||||
|
guidCol := models.InitColumn("guid", "users", "public")
|
||||||
|
guidCol.Type = "uuid"
|
||||||
|
guidCol.NotNull = true
|
||||||
|
table.Columns["guid"] = guidCol
|
||||||
|
|
||||||
|
// Add unique constraints
|
||||||
|
emailConstraint := &models.Constraint{
|
||||||
|
Name: "uq_email",
|
||||||
|
Type: models.UniqueConstraint,
|
||||||
|
Schema: "public",
|
||||||
|
Table: "users",
|
||||||
|
Columns: []string{"email"},
|
||||||
|
}
|
||||||
|
table.Constraints["uq_email"] = emailConstraint
|
||||||
|
|
||||||
|
guidConstraint := &models.Constraint{
|
||||||
|
Name: "uq_guid",
|
||||||
|
Type: models.UniqueConstraint,
|
||||||
|
Schema: "public",
|
||||||
|
Table: "users",
|
||||||
|
Columns: []string{"guid"},
|
||||||
|
}
|
||||||
|
table.Constraints["uq_guid"] = guidConstraint
|
||||||
|
|
||||||
|
schema.Tables = append(schema.Tables, table)
|
||||||
|
db.Schemas = append(db.Schemas, schema)
|
||||||
|
|
||||||
|
// Create writer with output to buffer
|
||||||
|
var buf bytes.Buffer
|
||||||
|
options := &writers.WriterOptions{}
|
||||||
|
writer := NewWriter(options)
|
||||||
|
writer.writer = &buf
|
||||||
|
|
||||||
|
// Write the database
|
||||||
|
err := writer.WriteDatabase(db)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("WriteDatabase failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
output := buf.String()
|
||||||
|
|
||||||
|
// Print output for debugging
|
||||||
|
t.Logf("Generated SQL:\n%s", output)
|
||||||
|
|
||||||
|
// Verify unique constraints are present
|
||||||
|
if !strings.Contains(output, "-- Unique constraints for schema: public") {
|
||||||
|
t.Errorf("Output missing unique constraints header")
|
||||||
|
}
|
||||||
|
if !strings.Contains(output, "ADD CONSTRAINT uq_email UNIQUE (email)") {
|
||||||
|
t.Errorf("Output missing uq_email unique constraint\nFull output:\n%s", output)
|
||||||
|
}
|
||||||
|
if !strings.Contains(output, "ADD CONSTRAINT uq_guid UNIQUE (guid)") {
|
||||||
|
t.Errorf("Output missing uq_guid unique constraint\nFull output:\n%s", output)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestWriteTable(t *testing.T) {
|
func TestWriteTable(t *testing.T) {
|
||||||
// Create a single table
|
// Create a single table
|
||||||
table := models.InitTable("products", "public")
|
table := models.InitTable("products", "public")
|
||||||
|
|||||||
Reference in New Issue
Block a user