Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions internal/diff/constraint.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import (
)

// generateConstraintSQL generates constraint definition for inline table constraints
func generateConstraintSQL(constraint *ir.Constraint, _ string) string {
func generateConstraintSQL(constraint *ir.Constraint, targetSchema string) string {
// Helper function to get column names from ConstraintColumn array
getColumnNames := func(columns []*ir.ConstraintColumn) []string {
var names []string
Expand All @@ -27,10 +27,12 @@ func generateConstraintSQL(constraint *ir.Constraint, _ string) string {
return fmt.Sprintf("CONSTRAINT %s UNIQUE (%s)", constraint.Name, strings.Join(getColumnNames(constraint.Columns), ", "))
case ir.ConstraintTypeForeignKey:
// Always include CONSTRAINT name to preserve explicit FK names
// Use QualifyEntityNameWithQuotes to add schema qualifier when referencing tables in other schemas
qualifiedRefTable := ir.QualifyEntityNameWithQuotes(constraint.ReferencedSchema, constraint.ReferencedTable, targetSchema)
stmt := fmt.Sprintf("CONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s (%s)",
constraint.Name,
strings.Join(getColumnNames(constraint.Columns), ", "),
ir.QuoteIdentifier(constraint.ReferencedTable), strings.Join(getColumnNames(constraint.ReferencedColumns), ", "))
qualifiedRefTable, strings.Join(getColumnNames(constraint.ReferencedColumns), ", "))
// Only add ON UPDATE/DELETE if they are not the default "NO ACTION"
if constraint.UpdateRule != "" && constraint.UpdateRule != "NO ACTION" {
stmt += fmt.Sprintf(" ON UPDATE %s", constraint.UpdateRule)
Expand Down
25 changes: 24 additions & 1 deletion internal/diff/identifier_quote_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ func TestGenerateConstraintSQL_WithQuoting(t *testing.T) {
Columns: []*ir.ConstraintColumn{
{Name: "userId", Position: 1},
},
ReferencedSchema: "public",
ReferencedTable: "users",
ReferencedColumns: []*ir.ConstraintColumn{
{Name: "id", Position: 1},
Expand All @@ -65,11 +66,33 @@ func TestGenerateConstraintSQL_WithQuoting(t *testing.T) {
},
want: `CONSTRAINT test_unique_lower UNIQUE (email, username)`,
},
{
name: "FOREIGN KEY with cross-schema reference",
constraint: &ir.Constraint{
Name: "test_cross_schema_fk",
Type: ir.ConstraintTypeForeignKey,
Columns: []*ir.ConstraintColumn{
{Name: "category_id", Position: 1},
},
ReferencedSchema: "public",
ReferencedTable: "categories",
ReferencedColumns: []*ir.ConstraintColumn{
{Name: "id", Position: 1},
},
IsValid: true,
},
want: `CONSTRAINT test_cross_schema_fk FOREIGN KEY (category_id) REFERENCES public.categories (id)`,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := generateConstraintSQL(tt.constraint, "public")
// Use "tenant1" as target schema for cross-schema FK test, "public" for others
targetSchema := "public"
if tt.name == "FOREIGN KEY with cross-schema reference" {
targetSchema = "tenant1"
}
got := generateConstraintSQL(tt.constraint, targetSchema)
if got != tt.want {
t.Errorf("generateConstraintSQL() = %q, want %q", got, tt.want)
}
Expand Down
4 changes: 3 additions & 1 deletion testdata/dump/tenant/pgschema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,11 @@ CREATE TABLE IF NOT EXISTS posts (
title varchar(200) NOT NULL,
content text,
author_id integer,
category_id integer NOT NULL,
status public.status DEFAULT 'active',
created_at timestamp DEFAULT now(),
CONSTRAINT posts_pkey PRIMARY KEY (id),
CONSTRAINT posts_author_id_fkey FOREIGN KEY (author_id) REFERENCES users (id)
CONSTRAINT posts_author_id_fkey FOREIGN KEY (author_id) REFERENCES users (id),
CONSTRAINT posts_category_id_fkey FOREIGN KEY (category_id) REFERENCES public.categories (id)
);

9 changes: 9 additions & 0 deletions testdata/dump/tenant/public.sql
Original file line number Diff line number Diff line change
@@ -1,2 +1,11 @@
CREATE TYPE public.user_role AS ENUM ('admin', 'user');
CREATE TYPE public.status AS ENUM ('active', 'inactive');

--
-- Shared table in public schema that tenant schemas will reference
--
CREATE TABLE public.categories (
id SERIAL PRIMARY KEY,
name varchar(100) NOT NULL UNIQUE,
description text
);