Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Foreign Key: Add support for multi target delete #15504

Merged
merged 5 commits into from
Mar 20, 2024
Merged
Show file tree
Hide file tree
Changes from 3 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: 5 additions & 1 deletion go/test/endtoend/vtgate/foreignkey/fk_fuzz_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,11 @@ func (fz *fuzzer) generateMultiDeleteDMLQuery() string {
tableId2 := rand.Intn(len(fkTables))
idValue := 1 + rand.Intn(fz.maxValForId)
setVarFkChecksVal := fz.getSetVarFkChecksVal()
query := fmt.Sprintf("delete %v%v from %v join %v using (id) where %v.id = %v", setVarFkChecksVal, fkTables[tableId], fkTables[tableId], fkTables[tableId2], fkTables[tableId], idValue)
target := fkTables[tableId]
if rand.Intn(2)%2 == 0 {
harshit-gangal marked this conversation as resolved.
Show resolved Hide resolved
target += ", " + fkTables[tableId2]
}
query := fmt.Sprintf("delete %v%v from %v join %v using (id) where %v.id = %v", setVarFkChecksVal, target, fkTables[tableId], fkTables[tableId2], fkTables[tableId], idValue)
return query
}

Expand Down
16 changes: 16 additions & 0 deletions go/test/endtoend/vtgate/foreignkey/fk_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -704,6 +704,22 @@ func TestFkScenarios(t *testing.T) {
"select * from fk_multicol_t17 order by id",
"select * from fk_multicol_t19 order by id",
},
}, {
name: "Multi Target Delete success",
dataQueries: []string{
"insert into fk_multicol_t15(id, cola, colb) values (1, 7, 1), (2, 9, 1), (3, 11, 1), (4, 13, 1)",
"insert into fk_multicol_t16(id, cola, colb) values (1, 7, 1), (2, 9, 1), (3, 11, 1), (4, 13, 1)",
"insert into fk_multicol_t17(id, cola, colb) values (1, 7, 1), (2, 9, 1), (3, 11, 1)",
"insert into fk_multicol_t18(id, cola, colb) values (1, 7, 1), (3, 11, 1)",
"insert into fk_multicol_t19(id, cola, colb) values (1, 7, 1), (2, 9, 1)",
},
dmlQuery: "delete fk_multicol_t15, fk_multicol_t17 from fk_multicol_t15 join fk_multicol_t17 where fk_multicol_t15.id = fk_multicol_t17.id",
assertionQueries: []string{
"select * from fk_multicol_t15 order by id",
"select * from fk_multicol_t16 order by id",
"select * from fk_multicol_t17 order by id",
"select * from fk_multicol_t19 order by id",
},
}, {
name: "Delete with limit success",
dataQueries: []string{
Expand Down
11 changes: 7 additions & 4 deletions go/vt/vtgate/planbuilder/operators/delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,11 @@ func createOperatorFromDelete(ctx *plancontext.PlanningContext, deleteStmt *sqlp
}
}

var err error
childFks, err = ctx.SemTable.GetChildForeignKeysForTable(deleteStmt.Targets[0])
if err != nil {
panic(err)
}
// If there are no foreign key constraints, then we don't need to do anything special.
if len(childFks) == 0 {
return op
Expand All @@ -94,9 +99,6 @@ func createOperatorFromDelete(ctx *plancontext.PlanningContext, deleteStmt *sqlp

func deleteWithInputPlanningRequired(childFks []vindexes.ChildFKInfo, deleteStmt *sqlparser.Delete) bool {
if len(deleteStmt.Targets) > 1 {
if len(childFks) > 0 {
panic(vterrors.VT12001("multi table delete with foreign keys"))
}
return true
}
// If there are no foreign keys, we don't need to use delete with input.
Expand Down Expand Up @@ -354,6 +356,7 @@ func updateQueryGraphWithSource(ctx *plancontext.PlanningContext, input Operator
func createFkCascadeOpForDelete(ctx *plancontext.PlanningContext, parentOp Operator, delStmt *sqlparser.Delete, childFks []vindexes.ChildFKInfo, deletedTbl *vindexes.Table) Operator {
var fkChildren []*FkChild
var selectExprs []sqlparser.SelectExpr
tblName := delStmt.Targets[0]
for _, fk := range childFks {
// Any RESTRICT type foreign keys that arrive here,
// are cross-shard/cross-keyspace RESTRICT cases, which we don't currently support.
Expand All @@ -363,7 +366,7 @@ func createFkCascadeOpForDelete(ctx *plancontext.PlanningContext, parentOp Opera

// We need to select all the parent columns for the foreign key constraint, to use in the update of the child table.
var offsets []int
offsets, selectExprs = addColumns(ctx, fk.ParentColumns, selectExprs, deletedTbl.GetTableName())
offsets, selectExprs = addColumns(ctx, fk.ParentColumns, selectExprs, tblName)

fkChildren = append(fkChildren,
createFkChildForDelete(ctx, fk, offsets))
Expand Down
91 changes: 88 additions & 3 deletions go/vt/vtgate/planbuilder/testdata/foreignkey_cases.json
Original file line number Diff line number Diff line change
Expand Up @@ -3151,8 +3151,8 @@
"Name": "unsharded_fk_allow",
"Sharded": false
},
"FieldQuery": "select u_tbl6.col6 from u_tbl6 as u where 1 != 1",
"Query": "select u_tbl6.col6 from u_tbl6 as u where u.id in ::dml_vals for update",
"FieldQuery": "select u.col6 from u_tbl6 as u where 1 != 1",
"Query": "select u.col6 from u_tbl6 as u where u.id in ::dml_vals for update",
"Table": "u_tbl6"
},
{
Expand Down Expand Up @@ -3805,7 +3805,92 @@
{
"comment": "multi table delete on foreign key enabled tables",
"query": "delete u, m from u_tbl6 u join u_tbl5 m on u.col = m.col where u.col2 = 4 and m.col3 = 6",
"plan": "VT12001: unsupported: multi table delete with foreign keys"
"plan": {
"QueryType": "DELETE",
"Original": "delete u, m from u_tbl6 u join u_tbl5 m on u.col = m.col where u.col2 = 4 and m.col3 = 6",
"Instructions": {
"OperatorType": "DMLWithInput",
"TargetTabletType": "PRIMARY",
"Offset": [
"0:[0]",
"1:[1]"
],
"Inputs": [
{
"OperatorType": "Route",
"Variant": "Unsharded",
"Keyspace": {
"Name": "unsharded_fk_allow",
"Sharded": false
},
"FieldQuery": "select u.id, m.id from u_tbl6 as u, u_tbl5 as m where 1 != 1",
"Query": "select u.id, m.id from u_tbl6 as u, u_tbl5 as m where u.col2 = 4 and m.col3 = 6 and u.col = m.col for update",
"Table": "u_tbl5, u_tbl6"
},
{
"OperatorType": "FkCascade",
"Inputs": [
{
"InputName": "Selection",
"OperatorType": "Route",
"Variant": "Unsharded",
"Keyspace": {
"Name": "unsharded_fk_allow",
"Sharded": false
},
"FieldQuery": "select u.col6 from u_tbl6 as u where 1 != 1",
"Query": "select u.col6 from u_tbl6 as u where u.id in ::dml_vals for update",
"Table": "u_tbl6"
},
{
"InputName": "CascadeChild-1",
"OperatorType": "Delete",
"Variant": "Unsharded",
"Keyspace": {
"Name": "unsharded_fk_allow",
"Sharded": false
},
"TargetTabletType": "PRIMARY",
"BvName": "fkc_vals",
"Cols": [
0
],
"Query": "delete from u_tbl8 where (col8) in ::fkc_vals",
"Table": "u_tbl8"
},
{
"InputName": "Parent",
"OperatorType": "Delete",
"Variant": "Unsharded",
"Keyspace": {
"Name": "unsharded_fk_allow",
"Sharded": false
},
"TargetTabletType": "PRIMARY",
"Query": "delete from u_tbl6 as u where u.id in ::dml_vals",
"Table": "u_tbl6"
}
]
},
{
"OperatorType": "Delete",
"Variant": "Unsharded",
"Keyspace": {
"Name": "unsharded_fk_allow",
"Sharded": false
},
"TargetTabletType": "PRIMARY",
"Query": "delete from u_tbl5 as m where m.id in ::dml_vals",
"Table": "u_tbl5"
}
]
},
"TablesUsed": [
"unsharded_fk_allow.u_tbl5",
"unsharded_fk_allow.u_tbl6",
"unsharded_fk_allow.u_tbl8"
]
}
},
{
"comment": "update with limit with foreign keys",
Expand Down
10 changes: 7 additions & 3 deletions go/vt/vtgate/semantics/semantic_state.go
Original file line number Diff line number Diff line change
Expand Up @@ -198,9 +198,13 @@ func (st *SemTable) GetChildForeignKeysForTargets() (fks []vindexes.ChildFKInfo)
return fks
}

// GetChildForeignKeysForTableSet gets the child foreign keys as a list for the specified TableSet.
func (st *SemTable) GetChildForeignKeysForTableSet(ts TableSet) []vindexes.ChildFKInfo {
return st.childForeignKeysInvolved[ts]
// GetChildForeignKeysForTable gets the child foreign keys as a list for the specified TableName.
func (st *SemTable) GetChildForeignKeysForTable(tbl sqlparser.TableName) ([]vindexes.ChildFKInfo, error) {
ts, err := st.GetTargetTableSetForTableName(tbl)
if err != nil {
return nil, err
}
return st.childForeignKeysInvolved[ts], nil
}

// GetChildForeignKeysList gets the child foreign keys as a list.
Expand Down
Loading