Skip to content

Commit 7d4ba2d

Browse files
committed
fix duplicate foreign key constraint on inline column references
1 parent 22a1e1a commit 7d4ba2d

4 files changed

Lines changed: 45 additions & 14 deletions

File tree

server/ast/alter_table.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,10 @@ func nodeAlterTableAddColumn(ctx *Context, node *tree.AlterTableAddColumn, table
245245
if err != nil {
246246
return nil, err
247247
}
248-
tableSpec.AddConstraint(&vitess.ConstraintDefinition{Details: constraintDef})
248+
tableSpec.AddConstraint(&vitess.ConstraintDefinition{
249+
Name: string(node.ColumnDef.References.ConstraintName),
250+
Details: constraintDef,
251+
})
249252
}
250253

251254
return &vitess.DDL{

server/ast/column_table_def.go

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -78,22 +78,12 @@ func nodeColumnTableDef(ctx *Context, node *tree.ColumnTableDef) (*vitess.Column
7878
defaultExpr = &vitess.ParenExpr{Expr: defaultExpr}
7979
}
8080

81-
var fkDef *vitess.ForeignKeyDefinition
8281
if node.References.Table != nil {
8382
if len(node.References.Col) == 0 {
8483
return nil, errors.Errorf("implicit primary key matching on column foreign key is not yet supported")
8584
}
86-
fkDef, err = nodeForeignKeyConstraintTableDef(ctx, &tree.ForeignKeyConstraintTableDef{
87-
Name: node.References.ConstraintName,
88-
Table: *node.References.Table,
89-
FromCols: tree.NameList{node.Name},
90-
ToCols: tree.NameList{node.References.Col},
91-
Actions: node.References.Actions,
92-
Match: node.References.Match,
93-
})
94-
if err != nil {
95-
return nil, err
96-
}
85+
// Callers register the foreign key via the table constraint list, so ForeignKeyDef
86+
// is left unset here to avoid creating the same constraint twice.
9787
}
9888

9989
if len(node.Computed.Options) > 0 {
@@ -164,7 +154,6 @@ func nodeColumnTableDef(ctx *Context, node *tree.ColumnTableDef) (*vitess.Column
164154
Length: convertType.Length,
165155
Scale: convertType.Scale,
166156
KeyOpt: keyOpt,
167-
ForeignKeyDef: fkDef,
168157
GeneratedExpr: generated,
169158
Stored: generated != nil, // postgres generated columns are always stored, never virtual
170159
},

server/ast/table_def.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ func assignTableDef(ctx *Context, node tree.TableDef, target *vitess.DDL) error
6161
return err
6262
}
6363
target.TableSpec.Constraints = append(target.TableSpec.Constraints, &vitess.ConstraintDefinition{
64+
Name: string(node.References.ConstraintName),
6465
Details: fkDef,
6566
})
6667
}

testing/go/foreign_keys_test.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,44 @@ func TestForeignKeys(t *testing.T) {
8888
},
8989
},
9090
},
91+
{
92+
// See https://www.postgresql.org/docs/current/ddl-constraints.html#DDL-CONSTRAINTS-FK
93+
Name: "inline column REFERENCES enforces a foreign key constraint",
94+
SetUpScript: []string{
95+
`CREATE TABLE parent (a INT PRIMARY KEY)`,
96+
`CREATE TABLE child (id INT PRIMARY KEY, pid INT REFERENCES parent(a))`,
97+
`INSERT INTO parent VALUES (1)`,
98+
},
99+
Assertions: []ScriptTestAssertion{
100+
{
101+
Query: "INSERT INTO child VALUES (1, 1)",
102+
},
103+
{
104+
Query: "INSERT INTO child VALUES (2, 999)",
105+
ExpectedErr: "Foreign key violation",
106+
},
107+
},
108+
},
109+
{
110+
// See https://github.com/morenoh149/postgresDBSamples/blob/master/french-towns-communes-francaises/french-towns-communes-francaises.sql
111+
Name: "inline column REFERENCES enforces constraints across a chain of related tables",
112+
SetUpScript: []string{
113+
`CREATE TABLE regions (id SERIAL UNIQUE NOT NULL, code VARCHAR(4) UNIQUE NOT NULL, name TEXT UNIQUE NOT NULL)`,
114+
`CREATE TABLE departments (id SERIAL UNIQUE NOT NULL, code VARCHAR(4) UNIQUE NOT NULL, region VARCHAR(4) NOT NULL REFERENCES regions(code), name TEXT UNIQUE NOT NULL)`,
115+
`CREATE TABLE towns (id SERIAL UNIQUE NOT NULL, code VARCHAR(10) NOT NULL, name TEXT NOT NULL, department VARCHAR(4) NOT NULL REFERENCES departments(code), UNIQUE (code, department))`,
116+
`INSERT INTO regions VALUES (1, '01', 'Region1')`,
117+
`INSERT INTO departments VALUES (1, 'D01', '01', 'Department1')`,
118+
},
119+
Assertions: []ScriptTestAssertion{
120+
{
121+
Query: "INSERT INTO towns VALUES (1, 'T01', 'Town1', 'D01')",
122+
},
123+
{
124+
Query: "INSERT INTO towns VALUES (2, 'T02', 'Town2', 'NOPE')",
125+
ExpectedErr: "Foreign key violation",
126+
},
127+
},
128+
},
91129
{
92130
Name: "text foreign key",
93131
SetUpScript: []string{

0 commit comments

Comments
 (0)