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
42 changes: 41 additions & 1 deletion go/test/endtoend/vtgate/foreignkey/fk_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,10 @@ import (
"vitess.io/vitess/go/test/endtoend/cluster"
"vitess.io/vitess/go/test/endtoend/utils"
"vitess.io/vitess/go/vt/log"
"vitess.io/vitess/go/vt/vtgate/vtgateconn"

binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata"
topodatapb "vitess.io/vitess/go/vt/proto/topodata"
"vitess.io/vitess/go/vt/vtgate/vtgateconn"
)

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

// TestForeignKeyWithKeyspaceQualifier tests that CREATE TABLE with foreign key references
// that include keyspace qualifiers work correctly. This addresses bug #18889 where keyspace
// names were not being stripped before being sent to MySQL, causing failures because MySQL
// expects database names (vt_<keyspace>) not keyspace names.
func TestForeignKeyWithKeyspaceQualifier(t *testing.T) {
mcmp, closer := start(t)
defer closer()

utils.Exec(t, mcmp.VtConn, `use uks`)

// Create the parent table.
utils.Exec(t, mcmp.VtConn, `create table fk_parent(id bigint primary key)`)

// Create the child table with keyspace-qualified foreign key reference.
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))`)

// Verify that the foreign key constraint works.
utils.Exec(t, mcmp.VtConn, `insert into fk_parent(id) values (1), (2)`)
utils.Exec(t, mcmp.VtConn, `insert into fk_child(id, parent_id) values (100, 1)`)

// This should fail due to FK constraint.
_, err := utils.ExecAllowError(t, mcmp.VtConn, `insert into fk_child(id, parent_id) values (101, 999)`)
assert.ErrorContains(t, err, "Cannot add or update a child row: a foreign key constraint fails")

// Test ALTER TABLE with keyspace-qualified foreign key.
utils.Exec(t, mcmp.VtConn, `create table fk_child2(id bigint primary key, parent_id bigint)`)
utils.Exec(t, mcmp.VtConn, `alter table fk_child2 add foreign key (parent_id) references uks.fk_parent(id)`)

// Verify the constraint works for the altered table.
utils.Exec(t, mcmp.VtConn, `insert into fk_child2(id, parent_id) values (200, 2)`)
_, err = utils.ExecAllowError(t, mcmp.VtConn, `insert into fk_child2(id, parent_id) values (201, 888)`)
assert.ErrorContains(t, err, "Cannot add or update a child row: a foreign key constraint fails")

// Clean up.
utils.Exec(t, mcmp.VtConn, `drop table fk_child`)
utils.Exec(t, mcmp.VtConn, `drop table fk_child2`)
utils.Exec(t, mcmp.VtConn, `drop table fk_parent`)
}

// TestRestrictFkOnNonStandardKey verifies that restrict_fk_on_non_standard_key is set to off
func TestRestrictFkOnNonStandardKey(t *testing.T) {
mcmp, closer := start(t)
Expand Down
2 changes: 2 additions & 0 deletions go/vt/vtgate/planbuilder/ddl.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,8 @@ func buildDDLPlans(ctx context.Context, sql string, ddlStatement sqlparser.DDLSt
if err != nil {
return nil, nil, err
}
// Remove keyspace qualifiers from all table references (including foreign key references).
sqlparser.RemoveSpecificKeyspace(ddlStatement, keyspace.Name)
err = checkFKError(vschema, ddlStatement, keyspace)
case *sqlparser.CreateView:
destination, keyspace, err = buildCreateViewCommon(ctx, vschema, reservedVars, cfg, ddl.Select, ddl)
Expand Down
100 changes: 100 additions & 0 deletions go/vt/vtgate/planbuilder/testdata/ddl_cases.json
Original file line number Diff line number Diff line change
Expand Up @@ -683,5 +683,105 @@
"main.function_default"
]
}
},
{
"comment": "create table with foreign key reference without keyspace qualifier",
"query": "create table t1(id bigint, t2_id bigint, primary key(id), foreign key (t2_id) references t2(id))",
"plan": {
"Type": "DirectDDL",
"QueryType": "DDL",
"Original": "create table t1(id bigint, t2_id bigint, primary key(id), foreign key (t2_id) references t2(id))",
"Instructions": {
"OperatorType": "DDL",
"Keyspace": {
"Name": "main",
"Sharded": false
},
"Query": "create table t1 (\n\tid bigint,\n\tt2_id bigint,\n\tprimary key (id),\n\tforeign key (t2_id) references t2 (id)\n)"
},
"TablesUsed": [
"main.t1"
]
}
},
{
"comment": "create table with foreign key reference with keyspace qualifier",
"query": "create table user.t1(id bigint, t2_id bigint, primary key(id), foreign key (t2_id) references user.t2(id))",
"plan": {
"Type": "DirectDDL",
"QueryType": "DDL",
"Original": "create table user.t1(id bigint, t2_id bigint, primary key(id), foreign key (t2_id) references user.t2(id))",
"Instructions": {
"OperatorType": "DDL",
"Keyspace": {
"Name": "user",
"Sharded": true
},
"Query": "create table t1 (\n\tid bigint,\n\tt2_id bigint,\n\tprimary key (id),\n\tforeign key (t2_id) references t2 (id)\n)"
},
"TablesUsed": [
"user.t1"
]
}
},
{
"comment": "create table with multiple foreign keys with keyspace qualifiers",
"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))",
"plan": {
"Type": "DirectDDL",
"QueryType": "DDL",
"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))",
"Instructions": {
"OperatorType": "DDL",
"Keyspace": {
"Name": "user",
"Sharded": true
},
"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)"
},
"TablesUsed": [
"user.orders"
]
}
},
{
"comment": "alter table add foreign key with keyspace qualifier",
"query": "alter table user.t1 add foreign key (t2_id) references user.t2(id)",
"plan": {
"Type": "DirectDDL",
"QueryType": "DDL",
"Original": "alter table user.t1 add foreign key (t2_id) references user.t2(id)",
"Instructions": {
"OperatorType": "DDL",
"Keyspace": {
"Name": "user",
"Sharded": true
},
"Query": "alter table t1 add foreign key (t2_id) references t2 (id)"
},
"TablesUsed": [
"user.t1"
]
}
},
{
"comment": "create table with foreign key with ON DELETE and ON UPDATE clauses and keyspace qualifier",
"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)",
"plan": {
"Type": "DirectDDL",
"QueryType": "DDL",
"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)",
"Instructions": {
"OperatorType": "DDL",
"Keyspace": {
"Name": "user",
"Sharded": true
},
"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)"
},
"TablesUsed": [
"user.employees"
]
}
}
]
Loading