Skip to content

Commit 556ed53

Browse files
authored
Properly Strip Keyspace Table Qualifiers in FK Constraints (#18926)
Signed-off-by: Matt Lord <[email protected]>
1 parent e9fe271 commit 556ed53

File tree

3 files changed

+143
-1
lines changed

3 files changed

+143
-1
lines changed

go/test/endtoend/vtgate/foreignkey/fk_test.go

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,10 @@ import (
3030
"vitess.io/vitess/go/test/endtoend/cluster"
3131
"vitess.io/vitess/go/test/endtoend/utils"
3232
"vitess.io/vitess/go/vt/log"
33+
"vitess.io/vitess/go/vt/vtgate/vtgateconn"
34+
3335
binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata"
3436
topodatapb "vitess.io/vitess/go/vt/proto/topodata"
35-
"vitess.io/vitess/go/vt/vtgate/vtgateconn"
3637
)
3738

3839
// TestInsertWithFK tests that insertions work as expected when foreign key management is enabled in Vitess.
@@ -1505,6 +1506,45 @@ create table temp2(id bigint auto_increment primary key, col varchar(20) not nul
15051506
mcmp.ExecAllowAndCompareError(`insert into temp1(col) values('d') `, utils.CompareOptions{})
15061507
}
15071508

1509+
// TestForeignKeyWithKeyspaceQualifier tests that CREATE TABLE with foreign key references
1510+
// that include keyspace qualifiers work correctly. This addresses bug #18889 where keyspace
1511+
// names were not being stripped before being sent to MySQL, causing failures because MySQL
1512+
// expects database names (vt_<keyspace>) not keyspace names.
1513+
func TestForeignKeyWithKeyspaceQualifier(t *testing.T) {
1514+
mcmp, closer := start(t)
1515+
defer closer()
1516+
1517+
utils.Exec(t, mcmp.VtConn, `use uks`)
1518+
1519+
// Create the parent table.
1520+
utils.Exec(t, mcmp.VtConn, `create table fk_parent(id bigint primary key)`)
1521+
1522+
// Create the child table with keyspace-qualified foreign key reference.
1523+
utils.Exec(t, mcmp.VtConn, `create table fk_child(id bigint primary key, parent_id bigint, foreign key (parent_id) references uks.fk_parent(id))`)
1524+
1525+
// Verify that the foreign key constraint works.
1526+
utils.Exec(t, mcmp.VtConn, `insert into fk_parent(id) values (1), (2)`)
1527+
utils.Exec(t, mcmp.VtConn, `insert into fk_child(id, parent_id) values (100, 1)`)
1528+
1529+
// This should fail due to FK constraint.
1530+
_, err := utils.ExecAllowError(t, mcmp.VtConn, `insert into fk_child(id, parent_id) values (101, 999)`)
1531+
assert.ErrorContains(t, err, "Cannot add or update a child row: a foreign key constraint fails")
1532+
1533+
// Test ALTER TABLE with keyspace-qualified foreign key.
1534+
utils.Exec(t, mcmp.VtConn, `create table fk_child2(id bigint primary key, parent_id bigint)`)
1535+
utils.Exec(t, mcmp.VtConn, `alter table fk_child2 add foreign key (parent_id) references uks.fk_parent(id)`)
1536+
1537+
// Verify the constraint works for the altered table.
1538+
utils.Exec(t, mcmp.VtConn, `insert into fk_child2(id, parent_id) values (200, 2)`)
1539+
_, err = utils.ExecAllowError(t, mcmp.VtConn, `insert into fk_child2(id, parent_id) values (201, 888)`)
1540+
assert.ErrorContains(t, err, "Cannot add or update a child row: a foreign key constraint fails")
1541+
1542+
// Clean up.
1543+
utils.Exec(t, mcmp.VtConn, `drop table fk_child`)
1544+
utils.Exec(t, mcmp.VtConn, `drop table fk_child2`)
1545+
utils.Exec(t, mcmp.VtConn, `drop table fk_parent`)
1546+
}
1547+
15081548
// TestRestrictFkOnNonStandardKey verifies that restrict_fk_on_non_standard_key is set to off
15091549
func TestRestrictFkOnNonStandardKey(t *testing.T) {
15101550
mcmp, closer := start(t)

go/vt/vtgate/planbuilder/ddl.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,8 @@ func buildDDLPlans(ctx context.Context, sql string, ddlStatement sqlparser.DDLSt
109109
if err != nil {
110110
return nil, nil, err
111111
}
112+
// Remove keyspace qualifiers from all table references (including foreign key references).
113+
sqlparser.RemoveSpecificKeyspace(ddlStatement, keyspace.Name)
112114
err = checkFKError(vschema, ddlStatement, keyspace)
113115
case *sqlparser.CreateView:
114116
destination, keyspace, err = buildCreateViewCommon(ctx, vschema, reservedVars, cfg, ddl.Select, ddl)

go/vt/vtgate/planbuilder/testdata/ddl_cases.json

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -683,5 +683,105 @@
683683
"main.function_default"
684684
]
685685
}
686+
},
687+
{
688+
"comment": "create table with foreign key reference without keyspace qualifier",
689+
"query": "create table t1(id bigint, t2_id bigint, primary key(id), foreign key (t2_id) references t2(id))",
690+
"plan": {
691+
"Type": "DirectDDL",
692+
"QueryType": "DDL",
693+
"Original": "create table t1(id bigint, t2_id bigint, primary key(id), foreign key (t2_id) references t2(id))",
694+
"Instructions": {
695+
"OperatorType": "DDL",
696+
"Keyspace": {
697+
"Name": "main",
698+
"Sharded": false
699+
},
700+
"Query": "create table t1 (\n\tid bigint,\n\tt2_id bigint,\n\tprimary key (id),\n\tforeign key (t2_id) references t2 (id)\n)"
701+
},
702+
"TablesUsed": [
703+
"main.t1"
704+
]
705+
}
706+
},
707+
{
708+
"comment": "create table with foreign key reference with keyspace qualifier",
709+
"query": "create table user.t1(id bigint, t2_id bigint, primary key(id), foreign key (t2_id) references user.t2(id))",
710+
"plan": {
711+
"Type": "DirectDDL",
712+
"QueryType": "DDL",
713+
"Original": "create table user.t1(id bigint, t2_id bigint, primary key(id), foreign key (t2_id) references user.t2(id))",
714+
"Instructions": {
715+
"OperatorType": "DDL",
716+
"Keyspace": {
717+
"Name": "user",
718+
"Sharded": true
719+
},
720+
"Query": "create table t1 (\n\tid bigint,\n\tt2_id bigint,\n\tprimary key (id),\n\tforeign key (t2_id) references t2 (id)\n)"
721+
},
722+
"TablesUsed": [
723+
"user.t1"
724+
]
725+
}
726+
},
727+
{
728+
"comment": "create table with multiple foreign keys with keyspace qualifiers",
729+
"query": "create table user.orders(order_id bigint, customer_id bigint, product_id bigint, primary key(order_id), foreign key (customer_id) references user.customers(id), foreign key (product_id) references user.products(id))",
730+
"plan": {
731+
"Type": "DirectDDL",
732+
"QueryType": "DDL",
733+
"Original": "create table user.orders(order_id bigint, customer_id bigint, product_id bigint, primary key(order_id), foreign key (customer_id) references user.customers(id), foreign key (product_id) references user.products(id))",
734+
"Instructions": {
735+
"OperatorType": "DDL",
736+
"Keyspace": {
737+
"Name": "user",
738+
"Sharded": true
739+
},
740+
"Query": "create table orders (\n\torder_id bigint,\n\tcustomer_id bigint,\n\tproduct_id bigint,\n\tprimary key (order_id),\n\tforeign key (customer_id) references customers (id),\n\tforeign key (product_id) references products (id)\n)"
741+
},
742+
"TablesUsed": [
743+
"user.orders"
744+
]
745+
}
746+
},
747+
{
748+
"comment": "alter table add foreign key with keyspace qualifier",
749+
"query": "alter table user.t1 add foreign key (t2_id) references user.t2(id)",
750+
"plan": {
751+
"Type": "DirectDDL",
752+
"QueryType": "DDL",
753+
"Original": "alter table user.t1 add foreign key (t2_id) references user.t2(id)",
754+
"Instructions": {
755+
"OperatorType": "DDL",
756+
"Keyspace": {
757+
"Name": "user",
758+
"Sharded": true
759+
},
760+
"Query": "alter table t1 add foreign key (t2_id) references t2 (id)"
761+
},
762+
"TablesUsed": [
763+
"user.t1"
764+
]
765+
}
766+
},
767+
{
768+
"comment": "create table with foreign key with ON DELETE and ON UPDATE clauses and keyspace qualifier",
769+
"query": "create table user.employees(emp_id bigint, dept_id bigint, primary key(emp_id), foreign key (dept_id) references user.departments(dept_id) on delete set null on update cascade)",
770+
"plan": {
771+
"Type": "DirectDDL",
772+
"QueryType": "DDL",
773+
"Original": "create table user.employees(emp_id bigint, dept_id bigint, primary key(emp_id), foreign key (dept_id) references user.departments(dept_id) on delete set null on update cascade)",
774+
"Instructions": {
775+
"OperatorType": "DDL",
776+
"Keyspace": {
777+
"Name": "user",
778+
"Sharded": true
779+
},
780+
"Query": "create table employees (\n\temp_id bigint,\n\tdept_id bigint,\n\tprimary key (emp_id),\n\tforeign key (dept_id) references departments (dept_id) on delete set null on update cascade\n)"
781+
},
782+
"TablesUsed": [
783+
"user.employees"
784+
]
785+
}
686786
}
687787
]

0 commit comments

Comments
 (0)