Skip to content

Commit 754f4b7

Browse files
authored
fix cascade for foreign keys with multiple references (#2704)
1 parent 54bd6d6 commit 754f4b7

File tree

3 files changed

+110
-38
lines changed

3 files changed

+110
-38
lines changed

enginetest/queries/foreign_key_queries.go

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2616,6 +2616,74 @@ var ForeignKeyTests = []ScriptTest{
26162616
},
26172617
},
26182618
},
2619+
{
2620+
Name: "multiple foreign key refs",
2621+
SetUpScript: []string{
2622+
"create table parent1 (i int primary key);",
2623+
"create table child1 (j int, k int, foreign key (j) references parent1(i) on delete cascade on update cascade, foreign key (k) references parent1 (i) on delete cascade on update cascade);",
2624+
"insert into parent1 values (1), (2), (3);",
2625+
"insert into child1 values (1, 2), (2, 3), (3, 1);",
2626+
},
2627+
Assertions: []ScriptTestAssertion{
2628+
{
2629+
Query: "select * from parent1;",
2630+
Expected: []sql.Row{
2631+
{1},
2632+
{2},
2633+
{3},
2634+
},
2635+
},
2636+
{
2637+
Query: "select * from child1 order by j, k;",
2638+
Expected: []sql.Row{
2639+
{1, 2},
2640+
{2, 3},
2641+
{3, 1},
2642+
},
2643+
},
2644+
{
2645+
Query: "update parent1 set i = 20 where i = 2;",
2646+
Expected: []sql.Row{
2647+
{types.OkResult{RowsAffected: 1, Info: plan.UpdateInfo{Matched: 1, Updated: 1}}},
2648+
},
2649+
},
2650+
{
2651+
Query: "select * from parent1 order by i;",
2652+
Expected: []sql.Row{
2653+
{1},
2654+
{3},
2655+
{20},
2656+
},
2657+
},
2658+
{
2659+
Query: "select * from child1 order by j, k;",
2660+
Expected: []sql.Row{
2661+
{1, 20},
2662+
{3, 1},
2663+
{20, 3},
2664+
},
2665+
},
2666+
{
2667+
Query: "delete from parent1 where i = 1;",
2668+
Expected: []sql.Row{
2669+
{types.OkResult{RowsAffected: 1}},
2670+
},
2671+
},
2672+
{
2673+
Query: "select * from parent1;",
2674+
Expected: []sql.Row{
2675+
{3},
2676+
{20},
2677+
},
2678+
},
2679+
{
2680+
Query: "select * from child1 order by j, k;",
2681+
Expected: []sql.Row{
2682+
{20, 3},
2683+
},
2684+
},
2685+
},
2686+
},
26192687
}
26202688

26212689
var CreateForeignKeyTests = []ScriptTest{

enginetest/queries/update_queries.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -707,9 +707,9 @@ var UpdateIgnoreScripts = []ScriptTest{
707707
Name: "UPDATE IGNORE with foreign keys",
708708
SetUpScript: []string{
709709
"CREATE TABLE colors ( id INT NOT NULL, color VARCHAR(32) NOT NULL, PRIMARY KEY (id), INDEX color_index(color));",
710-
"CREATE TABLE objects (id INT NOT NULL, name VARCHAR(64) NOT NULL,color VARCHAR(32), PRIMARY KEY(id),FOREIGN KEY (color) REFERENCES colors(color))",
711-
"INSERT INTO colors (id,color) VALUES (1,'red'),(2,'green'),(3,'blue'),(4,'purple')",
712-
"INSERT INTO objects (id,name,color) VALUES (1,'truck','red'),(2,'ball','green'),(3,'shoe','blue')",
710+
"CREATE TABLE objects (id INT NOT NULL, name VARCHAR(64) NOT NULL,color VARCHAR(32), PRIMARY KEY(id),FOREIGN KEY (color) REFERENCES colors(color));",
711+
"INSERT INTO colors (id,color) VALUES (1,'red'),(2,'green'),(3,'blue'),(4,'purple');",
712+
"INSERT INTO objects (id,name,color) VALUES (1,'truck','red'),(2,'ball','green'),(3,'shoe','blue');",
713713
},
714714
Assertions: []ScriptTestAssertion{
715715
{

sql/analyzer/apply_foreign_keys.go

Lines changed: 39 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,7 @@ func applyForeignKeys(ctx *sql.Context, a *Analyzer, n sql.Node, scope *plan.Sco
4040
// and caching of table editors.
4141
func applyForeignKeysToNodes(ctx *sql.Context, a *Analyzer, n sql.Node, cache *foreignKeyCache) (sql.Node, transform.TreeIdentity, error) {
4242
var err error
43-
fkChain := foreignKeyChain{
44-
fkUpdate: make(map[foreignKeyTableName]sql.ForeignKeyEditor),
45-
}
43+
fkChain := newForeignKeyChain()
4644

4745
switch n := n.(type) {
4846
case *plan.CreateTable:
@@ -209,7 +207,11 @@ func getForeignKeyEditor(ctx *sql.Context, a *Analyzer, tbl sql.ForeignKeyTable,
209207
if err != nil {
210208
return nil, err
211209
}
212-
return getForeignKeyRefActions(ctx, a, tbl, cache, fkChain, fkEditor, checkRows)
210+
fkEditor, err = getForeignKeyRefActions(ctx, a, tbl, cache, fkChain, fkEditor, checkRows)
211+
if err != nil {
212+
return nil, err
213+
}
214+
return fkEditor, err
213215
}
214216

215217
// getForeignKeyReferences returns an editor containing only the references for the given table.
@@ -238,7 +240,7 @@ func getForeignKeyReferences(ctx *sql.Context, a *Analyzer, tbl sql.ForeignKeyTa
238240
if err != nil {
239241
return nil, err
240242
}
241-
fkChain = fkChain.AddTable(fks[0].ParentDatabase, fks[0].ParentTable).AddTableUpdater(fks[0].ParentDatabase, fks[0].ParentTable, updater)
243+
fkChain = fkChain.AddTable(fks[0].Database, fks[0].Table).AddTableUpdater(fks[0].Database, fks[0].Table, updater)
242244

243245
tblSch := tbl.Schema()
244246
fkEditor := &plan.ForeignKeyEditor{
@@ -426,6 +428,13 @@ type foreignKeyTableName struct {
426428
tblName string
427429
}
428430

431+
func newForeignKeyTableName(dbName, tblName string) foreignKeyTableName {
432+
return foreignKeyTableName{
433+
dbName: strings.ToLower(dbName),
434+
tblName: strings.ToLower(tblName),
435+
}
436+
}
437+
429438
// foreignKeyTableUpdater is a foreign key table along with its updater.
430439
type foreignKeyTableUpdater struct {
431440
tbl sql.ForeignKeyTable
@@ -539,9 +548,17 @@ func (cache *foreignKeyCache) GetEditor(fkEditor *plan.ForeignKeyEditor, dbName
539548
// updaters that are not a part of this chain. In addition, any updaters that cannot be modified (such as those
540549
// belonging to strictly RESTRICT referential actions) will not appear in the chain.
541550
type foreignKeyChain struct {
542-
fkNames map[string]struct{}
543-
fkTables map[foreignKeyTableName]struct{}
544-
fkUpdate map[foreignKeyTableName]sql.ForeignKeyEditor
551+
fkNames map[string]struct{}
552+
fkTables map[foreignKeyTableName]struct{}
553+
fkUpdaters map[foreignKeyTableName]sql.ForeignKeyEditor
554+
}
555+
556+
func newForeignKeyChain() foreignKeyChain {
557+
return foreignKeyChain{
558+
fkNames: make(map[string]struct{}),
559+
fkTables: make(map[foreignKeyTableName]struct{}),
560+
fkUpdaters: make(map[foreignKeyTableName]sql.ForeignKeyEditor),
561+
}
545562
}
546563

547564
// AddTable returns a new chain with the added table.
@@ -554,23 +571,17 @@ func (chain foreignKeyChain) AddTable(dbName string, tblName string) foreignKeyC
554571
for fkTable := range chain.fkTables {
555572
newFkTables[fkTable] = struct{}{}
556573
}
557-
newFkTables[foreignKeyTableName{
558-
dbName: strings.ToLower(dbName),
559-
tblName: strings.ToLower(tblName),
560-
}] = struct{}{}
574+
newFkTables[newForeignKeyTableName(dbName, tblName)] = struct{}{}
561575
return foreignKeyChain{
562-
fkNames: newFkNames,
563-
fkTables: newFkTables,
564-
fkUpdate: chain.fkUpdate,
576+
fkNames: newFkNames,
577+
fkTables: newFkTables,
578+
fkUpdaters: chain.fkUpdaters,
565579
}
566580
}
567581

568582
// AddTableUpdater returns a new chain with the added foreign key updater.
569583
func (chain foreignKeyChain) AddTableUpdater(dbName string, tblName string, fkUpdater sql.ForeignKeyEditor) foreignKeyChain {
570-
chain.fkUpdate[foreignKeyTableName{
571-
dbName: strings.ToLower(dbName),
572-
tblName: strings.ToLower(tblName),
573-
}] = fkUpdater
584+
chain.fkUpdaters[newForeignKeyTableName(dbName, tblName)] = fkUpdater
574585
return chain
575586
}
576587

@@ -586,35 +597,28 @@ func (chain foreignKeyChain) AddForeignKey(fkName string) foreignKeyChain {
586597
}
587598
newFkNames[strings.ToLower(fkName)] = struct{}{}
588599
return foreignKeyChain{
589-
fkNames: newFkNames,
590-
fkTables: newFkTables,
591-
fkUpdate: chain.fkUpdate,
600+
fkNames: newFkNames,
601+
fkTables: newFkTables,
602+
fkUpdaters: chain.fkUpdaters,
592603
}
593604
}
594605

595606
// HasTable returns whether the chain contains the given table. Case-insensitive.
596607
func (chain foreignKeyChain) HasTable(dbName string, tblName string) bool {
597-
if _, ok := chain.fkTables[foreignKeyTableName{
598-
dbName: strings.ToLower(dbName),
599-
tblName: strings.ToLower(tblName),
600-
}]; ok {
601-
return true
602-
}
603-
return false
608+
_, ok := chain.fkTables[newForeignKeyTableName(dbName, tblName)]
609+
return ok
604610
}
605611

606612
// HasForeignKey returns whether the chain contains the given foreign key. Case-insensitive.
607613
func (chain foreignKeyChain) HasForeignKey(fkName string) bool {
608-
if _, ok := chain.fkNames[strings.ToLower(fkName)]; ok {
609-
return true
610-
}
611-
return false
614+
_, ok := chain.fkNames[strings.ToLower(fkName)]
615+
return ok
612616
}
613617

614618
// GetUpdaters returns all foreign key updaters that have been added to the chain.
615619
func (chain foreignKeyChain) GetUpdaters() []sql.ForeignKeyEditor {
616-
updaters := make([]sql.ForeignKeyEditor, 0, len(chain.fkUpdate))
617-
for _, updater := range chain.fkUpdate {
620+
updaters := make([]sql.ForeignKeyEditor, 0, len(chain.fkUpdaters))
621+
for _, updater := range chain.fkUpdaters {
618622
updaters = append(updaters, updater)
619623
}
620624
return updaters

0 commit comments

Comments
 (0)