From 1e5e79e3e8853ac606f9c2b69210c68158b9641b Mon Sep 17 00:00:00 2001 From: Manan Gupta Date: Tue, 30 Jan 2024 13:29:26 +0530 Subject: [PATCH 1/4] feat: add support for multi table deletes with foreign keys Signed-off-by: Manan Gupta --- .../vtgate/foreignkey/fk_fuzz_test.go | 23 +- go/test/endtoend/vtgate/foreignkey/fk_test.go | 15 + go/vt/sqlparser/ast.go | 4 +- go/vt/sqlparser/ast_clone.go | 28 +- go/vt/sqlparser/ast_copy_on_rewrite.go | 24 +- go/vt/sqlparser/ast_equals.go | 30 +- go/vt/sqlparser/ast_format.go | 18 +- go/vt/sqlparser/ast_format_fast.go | 20 +- go/vt/sqlparser/ast_funcs.go | 2 +- go/vt/sqlparser/ast_rewrite.go | 24 +- go/vt/sqlparser/ast_visit.go | 12 +- go/vt/vtctl/workflow/vexec/vexec.go | 4 +- go/vt/vtgate/planbuilder/delete.go | 1 + go/vt/vtgate/planbuilder/operators/delete.go | 4 +- .../planbuilder/operators/route_planning.go | 4 +- go/vt/vtgate/planbuilder/operators/update.go | 6 +- go/vt/vtgate/planbuilder/plan_test.go | 5 +- .../testdata/foreignkey_cases.json | 372 ++++++++++++++---- .../testdata/foreignkey_checks_off_cases.json | 12 +- .../testdata/foreignkey_checks_on_cases.json | 120 +++--- .../planbuilder/testdata/vschemas/schema.json | 22 ++ go/vt/vtgate/semantics/analyzer.go | 6 + go/vt/vtgate/semantics/scoper.go | 8 +- go/vt/vtgate/semantics/semantic_state.go | 5 + go/vt/wrangler/vexec_plan.go | 4 +- 25 files changed, 549 insertions(+), 224 deletions(-) diff --git a/go/test/endtoend/vtgate/foreignkey/fk_fuzz_test.go b/go/test/endtoend/vtgate/foreignkey/fk_fuzz_test.go index 096efead683..d738d699811 100644 --- a/go/test/endtoend/vtgate/foreignkey/fk_fuzz_test.go +++ b/go/test/endtoend/vtgate/foreignkey/fk_fuzz_test.go @@ -181,8 +181,8 @@ func (fz *fuzzer) generateUpdateDMLQuery() string { } } -// generateDeleteDMLQuery generates a DELETE query from the parameters for the fuzzer. -func (fz *fuzzer) generateDeleteDMLQuery() string { +// generateDeleteDMLQuery generates a DELETE query using 1 table from the parameters for the fuzzer. +func (fz *fuzzer) generateSingleDeleteDMLQuery() string { tableId := rand.Intn(len(fkTables)) idValue := 1 + rand.Intn(fz.maxValForId) setVarFkChecksVal := fz.getSetVarFkChecksVal() @@ -190,6 +190,25 @@ func (fz *fuzzer) generateDeleteDMLQuery() string { return query } +// generateMultiDeleteDMLQuery generates a DELETE query using 2 tables from the parameters for the fuzzer. +func (fz *fuzzer) generateMultiDeleteDMLQuery() string { + tableId := rand.Intn(len(fkTables)) + 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) + return query +} + +// generateDeleteDMLQuery generates a DELETE query from the parameters for the fuzzer. +func (fz *fuzzer) generateDeleteDMLQuery() string { + multiTableDelete := rand.Intn(2) + 1 + if multiTableDelete == 1 { + return fz.generateSingleDeleteDMLQuery() + } + return fz.generateMultiDeleteDMLQuery() +} + // start starts running the fuzzer. func (fz *fuzzer) start(t *testing.T, sharded bool) { // We mark the fuzzer thread to be running now. diff --git a/go/test/endtoend/vtgate/foreignkey/fk_test.go b/go/test/endtoend/vtgate/foreignkey/fk_test.go index 956c268d895..97b1e7202e0 100644 --- a/go/test/endtoend/vtgate/foreignkey/fk_test.go +++ b/go/test/endtoend/vtgate/foreignkey/fk_test.go @@ -672,6 +672,21 @@ func TestFkScenarios(t *testing.T) { assertionQueries: []string{ "select * from fk_t20 order by id", }, + }, { + name: "Multi Table Delete success", + dataQueries: []string{ + "insert into fk_t15(id, col) values (1, 7), (2, 9)", + "insert into fk_t16(id, col) values (1, 7), (2, 9)", + "insert into fk_t17(id, col) values (1, 7)", + "insert into fk_t19(id, col) values (1, 7)", + }, + dmlQuery: "delete fk_t15 from fk_t15 join fk_t17 using id", + assertionQueries: []string{ + "select * from fk_t15 order by id", + "select * from fk_t16 order by id", + "select * from fk_t17 order by id", + "select * from fk_t19 order by id", + }, }, } diff --git a/go/vt/sqlparser/ast.go b/go/vt/sqlparser/ast.go index b510c81767c..3dd376ff228 100644 --- a/go/vt/sqlparser/ast.go +++ b/go/vt/sqlparser/ast.go @@ -352,7 +352,7 @@ type ( With *With Comments *ParsedComments Ignore Ignore - TableExprs TableExprs + TableExprs []TableExpr Exprs UpdateExprs Where *Where OrderBy OrderBy @@ -365,7 +365,7 @@ type ( With *With Ignore Ignore Comments *ParsedComments - TableExprs TableExprs + TableExprs []TableExpr Targets TableNames Partitions Partitions Where *Where diff --git a/go/vt/sqlparser/ast_clone.go b/go/vt/sqlparser/ast_clone.go index 912cba84e6c..ef46a348df1 100644 --- a/go/vt/sqlparser/ast_clone.go +++ b/go/vt/sqlparser/ast_clone.go @@ -1175,7 +1175,7 @@ func CloneRefOfDelete(n *Delete) *Delete { out := *n out.With = CloneRefOfWith(n.With) out.Comments = CloneRefOfParsedComments(n.Comments) - out.TableExprs = CloneTableExprs(n.TableExprs) + out.TableExprs = CloneSliceOfTableExpr(n.TableExprs) out.Targets = CloneTableNames(n.Targets) out.Partitions = ClonePartitions(n.Partitions) out.Where = CloneRefOfWhere(n.Where) @@ -3143,7 +3143,7 @@ func CloneRefOfUpdate(n *Update) *Update { out := *n out.With = CloneRefOfWith(n.With) out.Comments = CloneRefOfParsedComments(n.Comments) - out.TableExprs = CloneTableExprs(n.TableExprs) + out.TableExprs = CloneSliceOfTableExpr(n.TableExprs) out.Exprs = CloneUpdateExprs(n.Exprs) out.Where = CloneRefOfWhere(n.Where) out.OrderBy = CloneOrderBy(n.OrderBy) @@ -4349,6 +4349,18 @@ func CloneSliceOfString(n []string) []string { return res } +// CloneSliceOfTableExpr creates a deep clone of the input. +func CloneSliceOfTableExpr(n []TableExpr) []TableExpr { + if n == nil { + return nil + } + res := make([]TableExpr, len(n)) + for i, x := range n { + res[i] = CloneTableExpr(x) + } + return res +} + // CloneSliceOfRefOfVariable creates a deep clone of the input. func CloneSliceOfRefOfVariable(n []*Variable) []*Variable { if n == nil { @@ -4553,18 +4565,6 @@ func CloneRefOfRootNode(n *RootNode) *RootNode { return &out } -// CloneSliceOfTableExpr creates a deep clone of the input. -func CloneSliceOfTableExpr(n []TableExpr) []TableExpr { - if n == nil { - return nil - } - res := make([]TableExpr, len(n)) - for i, x := range n { - res[i] = CloneTableExpr(x) - } - return res -} - // CloneRefOfTableName creates a deep clone of the input. func CloneRefOfTableName(n *TableName) *TableName { if n == nil { diff --git a/go/vt/sqlparser/ast_copy_on_rewrite.go b/go/vt/sqlparser/ast_copy_on_rewrite.go index 65fab00c890..fa90169355a 100644 --- a/go/vt/sqlparser/ast_copy_on_rewrite.go +++ b/go/vt/sqlparser/ast_copy_on_rewrite.go @@ -1850,7 +1850,15 @@ func (c *cow) copyOnRewriteRefOfDelete(n *Delete, parent SQLNode) (out SQLNode, if c.pre == nil || c.pre(n, parent) { _With, changedWith := c.copyOnRewriteRefOfWith(n.With, n) _Comments, changedComments := c.copyOnRewriteRefOfParsedComments(n.Comments, n) - _TableExprs, changedTableExprs := c.copyOnRewriteTableExprs(n.TableExprs, n) + var changedTableExprs bool + _TableExprs := make([]TableExpr, len(n.TableExprs)) + for x, el := range n.TableExprs { + this, changed := c.copyOnRewriteTableExpr(el, n) + _TableExprs[x] = this.(TableExpr) + if changed { + changedTableExprs = true + } + } _Targets, changedTargets := c.copyOnRewriteTableNames(n.Targets, n) _Partitions, changedPartitions := c.copyOnRewritePartitions(n.Partitions, n) _Where, changedWhere := c.copyOnRewriteRefOfWhere(n.Where, n) @@ -1860,7 +1868,7 @@ func (c *cow) copyOnRewriteRefOfDelete(n *Delete, parent SQLNode) (out SQLNode, res := *n res.With, _ = _With.(*With) res.Comments, _ = _Comments.(*ParsedComments) - res.TableExprs, _ = _TableExprs.(TableExprs) + res.TableExprs = _TableExprs res.Targets, _ = _Targets.(TableNames) res.Partitions, _ = _Partitions.(Partitions) res.Where, _ = _Where.(*Where) @@ -6023,7 +6031,15 @@ func (c *cow) copyOnRewriteRefOfUpdate(n *Update, parent SQLNode) (out SQLNode, if c.pre == nil || c.pre(n, parent) { _With, changedWith := c.copyOnRewriteRefOfWith(n.With, n) _Comments, changedComments := c.copyOnRewriteRefOfParsedComments(n.Comments, n) - _TableExprs, changedTableExprs := c.copyOnRewriteTableExprs(n.TableExprs, n) + var changedTableExprs bool + _TableExprs := make([]TableExpr, len(n.TableExprs)) + for x, el := range n.TableExprs { + this, changed := c.copyOnRewriteTableExpr(el, n) + _TableExprs[x] = this.(TableExpr) + if changed { + changedTableExprs = true + } + } _Exprs, changedExprs := c.copyOnRewriteUpdateExprs(n.Exprs, n) _Where, changedWhere := c.copyOnRewriteRefOfWhere(n.Where, n) _OrderBy, changedOrderBy := c.copyOnRewriteOrderBy(n.OrderBy, n) @@ -6032,7 +6048,7 @@ func (c *cow) copyOnRewriteRefOfUpdate(n *Update, parent SQLNode) (out SQLNode, res := *n res.With, _ = _With.(*With) res.Comments, _ = _Comments.(*ParsedComments) - res.TableExprs, _ = _TableExprs.(TableExprs) + res.TableExprs = _TableExprs res.Exprs, _ = _Exprs.(UpdateExprs) res.Where, _ = _Where.(*Where) res.OrderBy, _ = _OrderBy.(OrderBy) diff --git a/go/vt/sqlparser/ast_equals.go b/go/vt/sqlparser/ast_equals.go index 0ded1081fc3..c3cc5dad18d 100644 --- a/go/vt/sqlparser/ast_equals.go +++ b/go/vt/sqlparser/ast_equals.go @@ -2362,7 +2362,7 @@ func (cmp *Comparator) RefOfDelete(a, b *Delete) bool { return cmp.RefOfWith(a.With, b.With) && a.Ignore == b.Ignore && cmp.RefOfParsedComments(a.Comments, b.Comments) && - cmp.TableExprs(a.TableExprs, b.TableExprs) && + cmp.SliceOfTableExpr(a.TableExprs, b.TableExprs) && cmp.TableNames(a.Targets, b.Targets) && cmp.Partitions(a.Partitions, b.Partitions) && cmp.RefOfWhere(a.Where, b.Where) && @@ -4621,7 +4621,7 @@ func (cmp *Comparator) RefOfUpdate(a, b *Update) bool { return cmp.RefOfWith(a.With, b.With) && cmp.RefOfParsedComments(a.Comments, b.Comments) && a.Ignore == b.Ignore && - cmp.TableExprs(a.TableExprs, b.TableExprs) && + cmp.SliceOfTableExpr(a.TableExprs, b.TableExprs) && cmp.UpdateExprs(a.Exprs, b.Exprs) && cmp.RefOfWhere(a.Where, b.Where) && cmp.OrderBy(a.OrderBy, b.OrderBy) && @@ -7192,6 +7192,19 @@ func (cmp *Comparator) SliceOfString(a, b []string) bool { return true } +// SliceOfTableExpr does deep equals between the two objects. +func (cmp *Comparator) SliceOfTableExpr(a, b []TableExpr) bool { + if len(a) != len(b) { + return false + } + for i := 0; i < len(a); i++ { + if !cmp.TableExpr(a[i], b[i]) { + return false + } + } + return true +} + // SliceOfRefOfVariable does deep equals between the two objects. func (cmp *Comparator) SliceOfRefOfVariable(a, b []*Variable) bool { if len(a) != len(b) { @@ -7419,19 +7432,6 @@ func (cmp *Comparator) RefOfRootNode(a, b *RootNode) bool { return cmp.SQLNode(a.SQLNode, b.SQLNode) } -// SliceOfTableExpr does deep equals between the two objects. -func (cmp *Comparator) SliceOfTableExpr(a, b []TableExpr) bool { - if len(a) != len(b) { - return false - } - for i := 0; i < len(a); i++ { - if !cmp.TableExpr(a[i], b[i]) { - return false - } - } - return true -} - // RefOfTableName does deep equals between the two objects. func (cmp *Comparator) RefOfTableName(a, b *TableName) bool { if a == b { diff --git a/go/vt/sqlparser/ast_format.go b/go/vt/sqlparser/ast_format.go index a61399ae8ae..9f5f54bfa3d 100644 --- a/go/vt/sqlparser/ast_format.go +++ b/go/vt/sqlparser/ast_format.go @@ -158,9 +158,14 @@ func (node *Update) Format(buf *TrackedBuffer) { if node.With != nil { buf.astPrintf(node, "%v", node.With) } - buf.astPrintf(node, "update %v%s%v set %v%v%v%v", - node.Comments, node.Ignore.ToString(), node.TableExprs, - node.Exprs, node.Where, node.OrderBy, node.Limit) + buf.astPrintf(node, "update %v%s", + node.Comments, node.Ignore.ToString()) + prefix := "" + for _, expr := range node.TableExprs { + buf.astPrintf(node, "%s%v", prefix, expr) + prefix = ", " + } + buf.astPrintf(node, " set %v%v%v%v", node.Exprs, node.Where, node.OrderBy, node.Limit) } // Format formats the node. @@ -175,7 +180,12 @@ func (node *Delete) Format(buf *TrackedBuffer) { if node.Targets != nil && !node.isSingleAliasExpr() { buf.astPrintf(node, "%v ", node.Targets) } - buf.astPrintf(node, "from %v%v%v%v%v", node.TableExprs, node.Partitions, node.Where, node.OrderBy, node.Limit) + prefix := "from " + for _, expr := range node.TableExprs { + buf.astPrintf(node, "%s%v", prefix, expr) + prefix = ", " + } + buf.astPrintf(node, "%v%v%v%v", node.Partitions, node.Where, node.OrderBy, node.Limit) } // Format formats the node. diff --git a/go/vt/sqlparser/ast_format_fast.go b/go/vt/sqlparser/ast_format_fast.go index 37d3ddfa5b8..f6a5d6df570 100644 --- a/go/vt/sqlparser/ast_format_fast.go +++ b/go/vt/sqlparser/ast_format_fast.go @@ -234,17 +234,17 @@ func (node *Update) FormatFast(buf *TrackedBuffer) { buf.WriteString("update ") node.Comments.FormatFast(buf) buf.WriteString(node.Ignore.ToString()) - node.TableExprs.FormatFast(buf) + prefix := "" + for _, expr := range node.TableExprs { + buf.WriteString(prefix) + expr.FormatFast(buf) + prefix = ", " + } buf.WriteString(" set ") - node.Exprs.FormatFast(buf) - node.Where.FormatFast(buf) - node.OrderBy.FormatFast(buf) - node.Limit.FormatFast(buf) - } // FormatFast formats the node. @@ -261,8 +261,12 @@ func (node *Delete) FormatFast(buf *TrackedBuffer) { node.Targets.FormatFast(buf) buf.WriteByte(' ') } - buf.WriteString("from ") - node.TableExprs.FormatFast(buf) + prefix := "from " + for _, expr := range node.TableExprs { + buf.WriteString(prefix) + expr.FormatFast(buf) + prefix = ", " + } node.Partitions.FormatFast(buf) node.Where.FormatFast(buf) node.OrderBy.FormatFast(buf) diff --git a/go/vt/sqlparser/ast_funcs.go b/go/vt/sqlparser/ast_funcs.go index f7e0af8373f..6d5f894a2ac 100644 --- a/go/vt/sqlparser/ast_funcs.go +++ b/go/vt/sqlparser/ast_funcs.go @@ -2566,7 +2566,7 @@ func (node *Delete) isSingleAliasExpr() bool { return isAliasExpr } -func (node TableExprs) MultiTable() bool { +func MultiTable(node []TableExpr) bool { if len(node) > 1 { return true } diff --git a/go/vt/sqlparser/ast_rewrite.go b/go/vt/sqlparser/ast_rewrite.go index 6ec89e9a2ba..87196ee0b2f 100644 --- a/go/vt/sqlparser/ast_rewrite.go +++ b/go/vt/sqlparser/ast_rewrite.go @@ -2455,10 +2455,14 @@ func (a *application) rewriteRefOfDelete(parent SQLNode, node *Delete, replacer }) { return false } - if !a.rewriteTableExprs(node, node.TableExprs, func(newNode, parent SQLNode) { - parent.(*Delete).TableExprs = newNode.(TableExprs) - }) { - return false + for x, el := range node.TableExprs { + if !a.rewriteTableExpr(node, el, func(idx int) replacerFunc { + return func(newNode, parent SQLNode) { + parent.(*Delete).TableExprs[idx] = newNode.(TableExpr) + } + }(x)) { + return false + } } if !a.rewriteTableNames(node, node.Targets, func(newNode, parent SQLNode) { parent.(*Delete).Targets = newNode.(TableNames) @@ -8680,10 +8684,14 @@ func (a *application) rewriteRefOfUpdate(parent SQLNode, node *Update, replacer }) { return false } - if !a.rewriteTableExprs(node, node.TableExprs, func(newNode, parent SQLNode) { - parent.(*Update).TableExprs = newNode.(TableExprs) - }) { - return false + for x, el := range node.TableExprs { + if !a.rewriteTableExpr(node, el, func(idx int) replacerFunc { + return func(newNode, parent SQLNode) { + parent.(*Update).TableExprs[idx] = newNode.(TableExpr) + } + }(x)) { + return false + } } if !a.rewriteUpdateExprs(node, node.Exprs, func(newNode, parent SQLNode) { parent.(*Update).Exprs = newNode.(UpdateExprs) diff --git a/go/vt/sqlparser/ast_visit.go b/go/vt/sqlparser/ast_visit.go index bb2ec7c3500..2133bc05330 100644 --- a/go/vt/sqlparser/ast_visit.go +++ b/go/vt/sqlparser/ast_visit.go @@ -1377,8 +1377,10 @@ func VisitRefOfDelete(in *Delete, f Visit) error { if err := VisitRefOfParsedComments(in.Comments, f); err != nil { return err } - if err := VisitTableExprs(in.TableExprs, f); err != nil { - return err + for _, el := range in.TableExprs { + if err := VisitTableExpr(el, f); err != nil { + return err + } } if err := VisitTableNames(in.Targets, f); err != nil { return err @@ -4014,8 +4016,10 @@ func VisitRefOfUpdate(in *Update, f Visit) error { if err := VisitRefOfParsedComments(in.Comments, f); err != nil { return err } - if err := VisitTableExprs(in.TableExprs, f); err != nil { - return err + for _, el := range in.TableExprs { + if err := VisitTableExpr(el, f); err != nil { + return err + } } if err := VisitUpdateExprs(in.Exprs, f); err != nil { return err diff --git a/go/vt/vtctl/workflow/vexec/vexec.go b/go/vt/vtctl/workflow/vexec/vexec.go index 179af564eca..9e6fbe96940 100644 --- a/go/vt/vtctl/workflow/vexec/vexec.go +++ b/go/vt/vtctl/workflow/vexec/vexec.go @@ -316,9 +316,9 @@ func (vx *VExec) WithWorkflow(workflow string) *VExec { func extractTableName(stmt sqlparser.Statement) (string, error) { switch stmt := stmt.(type) { case *sqlparser.Update: - return sqlparser.String(stmt.TableExprs), nil + return sqlparser.ToString(stmt.TableExprs), nil case *sqlparser.Delete: - return sqlparser.String(stmt.TableExprs), nil + return sqlparser.ToString(stmt.TableExprs), nil case *sqlparser.Insert: return sqlparser.String(stmt.Table), nil case *sqlparser.Select: diff --git a/go/vt/vtgate/planbuilder/delete.go b/go/vt/vtgate/planbuilder/delete.go index 08353383e73..98ea4e7d122 100644 --- a/go/vt/vtgate/planbuilder/delete.go +++ b/go/vt/vtgate/planbuilder/delete.go @@ -140,6 +140,7 @@ func checkIfDeleteSupported(del *sqlparser.Delete, semTable *semantics.SemTable) } // Delete is only supported for single Target. + // TODO: Fix error message based on whether sharded keyspace or foreign keys. if len(del.Targets) > 1 { return vterrors.VT12001("multi-table DELETE statement in a sharded keyspace") } diff --git a/go/vt/vtgate/planbuilder/operators/delete.go b/go/vt/vtgate/planbuilder/operators/delete.go index e074e2685ef..189ae5ff2fc 100644 --- a/go/vt/vtgate/planbuilder/operators/delete.go +++ b/go/vt/vtgate/planbuilder/operators/delete.go @@ -90,7 +90,7 @@ func createOperatorFromDelete(ctx *plancontext.PlanningContext, deleteStmt *sqlp } } - childFks := ctx.SemTable.GetChildForeignKeysList() + childFks := ctx.SemTable.GetChildForeignKeysForTable(deleteStmt.Targets[0]) // If there are no foreign key constraints, then we don't need to do anything. if len(childFks) == 0 { return op @@ -231,7 +231,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) + offsets, selectExprs = addColumns(ctx, fk.ParentColumns, selectExprs, deletedTbl.GetTableName()) fkChildren = append(fkChildren, createFkChildForDelete(ctx, fk, offsets)) diff --git a/go/vt/vtgate/planbuilder/operators/route_planning.go b/go/vt/vtgate/planbuilder/operators/route_planning.go index bae8ed33625..81fece1e596 100644 --- a/go/vt/vtgate/planbuilder/operators/route_planning.go +++ b/go/vt/vtgate/planbuilder/operators/route_planning.go @@ -127,12 +127,12 @@ func buildVindexTableForDML( func generateOwnedVindexQuery(tblExpr sqlparser.TableExpr, del *sqlparser.Delete, table TargetTable, ksidCols []sqlparser.IdentifierCI) *sqlparser.Select { var selExprs sqlparser.SelectExprs for _, col := range ksidCols { - colName := makeColName(col, table, del.TableExprs.MultiTable()) + colName := makeColName(col, table, sqlparser.MultiTable(del.TableExprs)) selExprs = append(selExprs, sqlparser.NewAliasedExpr(colName, "")) } for _, cv := range table.VTable.Owned { for _, col := range cv.Columns { - colName := makeColName(col, table, del.TableExprs.MultiTable()) + colName := makeColName(col, table, sqlparser.MultiTable(del.TableExprs)) selExprs = append(selExprs, sqlparser.NewAliasedExpr(colName, "")) } } diff --git a/go/vt/vtgate/planbuilder/operators/update.go b/go/vt/vtgate/planbuilder/operators/update.go index 6c51418d054..727db448d30 100644 --- a/go/vt/vtgate/planbuilder/operators/update.go +++ b/go/vt/vtgate/planbuilder/operators/update.go @@ -233,7 +233,7 @@ func createFKCascadeOp(ctx *plancontext.PlanningContext, parentOp Operator, updS // We need to select all the parent columns for the foreign key constraint, to use in the update of the child table. var selectOffsets []int - selectOffsets, selectExprs = addColumns(ctx, fk.ParentColumns, selectExprs) + selectOffsets, selectExprs = addColumns(ctx, fk.ParentColumns, selectExprs, updatedTable.GetTableName()) // If we are updating a foreign key column to a non-literal value then, need information about // 1. whether the new value is different from the old value @@ -276,11 +276,11 @@ func hasNonLiteralUpdate(exprs sqlparser.UpdateExprs) bool { // addColumns adds the given set of columns to the select expressions provided. It tries to reuse the columns if already present in it. // It returns the list of offsets for the columns and the updated select expressions. -func addColumns(ctx *plancontext.PlanningContext, columns sqlparser.Columns, exprs []sqlparser.SelectExpr) ([]int, []sqlparser.SelectExpr) { +func addColumns(ctx *plancontext.PlanningContext, columns sqlparser.Columns, exprs []sqlparser.SelectExpr, tableName sqlparser.TableName) ([]int, []sqlparser.SelectExpr) { var offsets []int selectExprs := exprs for _, column := range columns { - ae := aeWrap(sqlparser.NewColName(column.String())) + ae := aeWrap(sqlparser.NewColNameWithQualifier(column.String(), tableName)) exists := false for idx, expr := range exprs { if ctx.SemTable.EqualsExpr(expr.(*sqlparser.AliasedExpr).Expr, ae.Expr) { diff --git a/go/vt/vtgate/planbuilder/plan_test.go b/go/vt/vtgate/planbuilder/plan_test.go index 30730f35db7..1a09aa555ec 100644 --- a/go/vt/vtgate/planbuilder/plan_test.go +++ b/go/vt/vtgate/planbuilder/plan_test.go @@ -209,9 +209,10 @@ func setFks(t *testing.T, vschema *vindexes.VSchema) { _ = vschema.AddForeignKey("unsharded_fk_allow", "u_tbl4", createFkDefinition([]string{"col4"}, "u_tbl3", []string{"col3"}, sqlparser.Restrict, sqlparser.Restrict)) _ = vschema.AddForeignKey("unsharded_fk_allow", "u_tbl6", createFkDefinition([]string{"col6"}, "u_tbl5", []string{"col5"}, sqlparser.DefaultAction, sqlparser.DefaultAction)) _ = vschema.AddForeignKey("unsharded_fk_allow", "u_tbl8", createFkDefinition([]string{"col8"}, "u_tbl9", []string{"col9"}, sqlparser.SetNull, sqlparser.SetNull)) - _ = vschema.AddForeignKey("unsharded_fk_allow", "u_tbl8", createFkDefinition([]string{"col8"}, "u_tbl6", []string{"col6"}, sqlparser.Cascade, sqlparser.CASCADE)) + _ = vschema.AddForeignKey("unsharded_fk_allow", "u_tbl8", createFkDefinition([]string{"col8"}, "u_tbl6", []string{"col6"}, sqlparser.Cascade, sqlparser.Cascade)) _ = vschema.AddForeignKey("unsharded_fk_allow", "u_tbl4", createFkDefinition([]string{"col4"}, "u_tbl7", []string{"col7"}, sqlparser.Cascade, sqlparser.Cascade)) _ = vschema.AddForeignKey("unsharded_fk_allow", "u_tbl9", createFkDefinition([]string{"col9"}, "u_tbl4", []string{"col4"}, sqlparser.Restrict, sqlparser.Restrict)) + _ = vschema.AddForeignKey("unsharded_fk_allow", "u_tbl11", createFkDefinition([]string{"col"}, "u_tbl10", []string{"col"}, sqlparser.Cascade, sqlparser.Cascade)) _ = vschema.AddForeignKey("unsharded_fk_allow", "u_tbl", createFkDefinition([]string{"col"}, "sharded_fk_allow.s_tbl", []string{"col"}, sqlparser.Restrict, sqlparser.Restrict)) _ = vschema.AddForeignKey("unsharded_fk_allow", "u_multicol_tbl2", createFkDefinition([]string{"cola", "colb"}, "u_multicol_tbl1", []string{"cola", "colb"}, sqlparser.SetNull, sqlparser.SetNull)) @@ -224,7 +225,7 @@ func setFks(t *testing.T, vschema *vindexes.VSchema) { _ = vschema.AddUniqueKey("unsharded_fk_allow", "u_tbl9", sqlparser.Exprs{sqlparser.NewColName("bar"), sqlparser.NewColName("col9")}) _ = vschema.AddUniqueKey("unsharded_fk_allow", "u_tbl8", sqlparser.Exprs{sqlparser.NewColName("col8")}) - addPKs(t, vschema, "unsharded_fk_allow", []string{"u_tbl1", "u_tbl2", "u_tbl3", "u_tbl4", "u_tbl5", "u_tbl6", "u_tbl7", "u_tbl8", "u_tbl9", + addPKs(t, vschema, "unsharded_fk_allow", []string{"u_tbl1", "u_tbl2", "u_tbl3", "u_tbl4", "u_tbl5", "u_tbl6", "u_tbl7", "u_tbl8", "u_tbl9", "u_tbl10", "u_tbl11", "u_multicol_tbl1", "u_multicol_tbl2", "u_multicol_tbl3"}) } diff --git a/go/vt/vtgate/planbuilder/testdata/foreignkey_cases.json b/go/vt/vtgate/planbuilder/testdata/foreignkey_cases.json index cc8f5fcbde6..e0b30bb21b5 100644 --- a/go/vt/vtgate/planbuilder/testdata/foreignkey_cases.json +++ b/go/vt/vtgate/planbuilder/testdata/foreignkey_cases.json @@ -81,8 +81,8 @@ "Name": "sharded_fk_allow", "Sharded": true }, - "FieldQuery": "select colb, cola, y, colc, x from multicol_tbl1 where 1 != 1", - "Query": "select colb, cola, y, colc, x from multicol_tbl1 where cola = 1 and colb = 2 and colc = 3 for update", + "FieldQuery": "select multicol_tbl1.colb, multicol_tbl1.cola, multicol_tbl1.y, multicol_tbl1.colc, multicol_tbl1.x from multicol_tbl1 where 1 != 1", + "Query": "select multicol_tbl1.colb, multicol_tbl1.cola, multicol_tbl1.y, multicol_tbl1.colc, multicol_tbl1.x from multicol_tbl1 where cola = 1 and colb = 2 and colc = 3 for update", "Table": "multicol_tbl1", "Values": [ "1", @@ -160,8 +160,8 @@ "Name": "sharded_fk_allow", "Sharded": true }, - "FieldQuery": "select col5, t5col5 from tbl5 where 1 != 1", - "Query": "select col5, t5col5 from tbl5 for update", + "FieldQuery": "select tbl5.col5, tbl5.t5col5 from tbl5 where 1 != 1", + "Query": "select tbl5.col5, tbl5.t5col5 from tbl5 for update", "Table": "tbl5" }, { @@ -242,8 +242,8 @@ "Name": "unsharded_fk_allow", "Sharded": false }, - "FieldQuery": "select col9 from u_tbl9 where 1 != 1", - "Query": "select col9 from u_tbl9 where col9 = 5 for update nowait", + "FieldQuery": "select u_tbl9.col9 from u_tbl9 where 1 != 1", + "Query": "select u_tbl9.col9 from u_tbl9 where col9 = 5 for update nowait", "Table": "u_tbl9" }, { @@ -321,8 +321,8 @@ "Name": "unsharded_fk_allow", "Sharded": false }, - "FieldQuery": "select col2 from u_tbl2 where 1 != 1", - "Query": "select col2 from u_tbl2 where id = 1 for update", + "FieldQuery": "select u_tbl2.col2 from u_tbl2 where 1 != 1", + "Query": "select u_tbl2.col2 from u_tbl2 where id = 1 for update", "Table": "u_tbl2" }, { @@ -432,8 +432,8 @@ "Name": "sharded_fk_allow", "Sharded": true }, - "FieldQuery": "select t5col5 from tbl5 where 1 != 1", - "Query": "select t5col5 from tbl5 for update", + "FieldQuery": "select tbl5.t5col5 from tbl5 where 1 != 1", + "Query": "select tbl5.t5col5 from tbl5 for update", "Table": "tbl5" }, { @@ -601,8 +601,8 @@ "Name": "sharded_fk_allow", "Sharded": true }, - "FieldQuery": "select col9 from tbl9 where 1 != 1", - "Query": "select col9 from tbl9 where col9 = 34 for update", + "FieldQuery": "select tbl9.col9 from tbl9 where 1 != 1", + "Query": "select tbl9.col9 from tbl9 where col9 = 34 for update", "Table": "tbl9", "Values": [ "34" @@ -666,8 +666,8 @@ "Name": "unsharded_fk_allow", "Sharded": false }, - "FieldQuery": "select col1 from u_tbl1 where 1 != 1", - "Query": "select col1 from u_tbl1 for update", + "FieldQuery": "select u_tbl1.col1 from u_tbl1 where 1 != 1", + "Query": "select u_tbl1.col1 from u_tbl1 for update", "Table": "u_tbl1" }, { @@ -686,8 +686,8 @@ "Name": "unsharded_fk_allow", "Sharded": false }, - "FieldQuery": "select col2 from u_tbl2 where 1 != 1", - "Query": "select col2 from u_tbl2 where (col2) in ::fkc_vals for update", + "FieldQuery": "select u_tbl2.col2 from u_tbl2 where 1 != 1", + "Query": "select u_tbl2.col2 from u_tbl2 where (col2) in ::fkc_vals for update", "Table": "u_tbl2" }, { @@ -736,8 +736,8 @@ "Name": "unsharded_fk_allow", "Sharded": false }, - "FieldQuery": "select col9 from u_tbl9 where 1 != 1", - "Query": "select col9 from u_tbl9 where (col9) in ::fkc_vals2 and (col9) not in ((cast('foo' as CHAR))) for update nowait", + "FieldQuery": "select u_tbl9.col9 from u_tbl9 where 1 != 1", + "Query": "select u_tbl9.col9 from u_tbl9 where (col9) in ::fkc_vals2 and (col9) not in ((cast('foo' as CHAR))) for update nowait", "Table": "u_tbl9" }, { @@ -831,8 +831,8 @@ "Name": "unsharded_fk_allow", "Sharded": false }, - "FieldQuery": "select col2, col2 <=> cast(col1 + 'bar' as CHAR), cast(col1 + 'bar' as CHAR) from u_tbl2 where 1 != 1", - "Query": "select col2, col2 <=> cast(col1 + 'bar' as CHAR), cast(col1 + 'bar' as CHAR) from u_tbl2 where id = 1 for update", + "FieldQuery": "select u_tbl2.col2, col2 <=> cast(col1 + 'bar' as CHAR), cast(col1 + 'bar' as CHAR) from u_tbl2 where 1 != 1", + "Query": "select u_tbl2.col2, col2 <=> cast(col1 + 'bar' as CHAR), cast(col1 + 'bar' as CHAR) from u_tbl2 where id = 1 for update", "Table": "u_tbl2" }, { @@ -898,8 +898,8 @@ "Name": "unsharded_fk_allow", "Sharded": false }, - "FieldQuery": "select col1, col1 <=> cast(x + 'bar' as CHAR), cast(x + 'bar' as CHAR) from u_tbl1 where 1 != 1", - "Query": "select col1, col1 <=> cast(x + 'bar' as CHAR), cast(x + 'bar' as CHAR) from u_tbl1 where id = 1 for update", + "FieldQuery": "select u_tbl1.col1, col1 <=> cast(x + 'bar' as CHAR), cast(x + 'bar' as CHAR) from u_tbl1 where 1 != 1", + "Query": "select u_tbl1.col1, col1 <=> cast(x + 'bar' as CHAR), cast(x + 'bar' as CHAR) from u_tbl1 where id = 1 for update", "Table": "u_tbl1" }, { @@ -925,8 +925,8 @@ "Name": "unsharded_fk_allow", "Sharded": false }, - "FieldQuery": "select col2 from u_tbl2 where 1 != 1", - "Query": "select col2 from u_tbl2 where (col2) in ::fkc_vals for update", + "FieldQuery": "select u_tbl2.col2 from u_tbl2 where 1 != 1", + "Query": "select u_tbl2.col2 from u_tbl2 where (col2) in ::fkc_vals for update", "Table": "u_tbl2" }, { @@ -982,8 +982,8 @@ "Name": "unsharded_fk_allow", "Sharded": false }, - "FieldQuery": "select col9 from u_tbl9 where 1 != 1", - "Query": "select col9 from u_tbl9 where (col9) in ::fkc_vals2 and (:fkc_upd1 is null or (col9) not in ((:fkc_upd1))) for update nowait", + "FieldQuery": "select u_tbl9.col9 from u_tbl9 where 1 != 1", + "Query": "select u_tbl9.col9 from u_tbl9 where (col9) in ::fkc_vals2 and (:fkc_upd1 is null or (col9) not in ((:fkc_upd1))) for update nowait", "Table": "u_tbl9" }, { @@ -1056,8 +1056,8 @@ "Name": "unsharded_fk_allow", "Sharded": false }, - "FieldQuery": "select col2 from u_tbl2 where 1 != 1", - "Query": "select col2 from u_tbl2 where id = 1 for update", + "FieldQuery": "select u_tbl2.col2 from u_tbl2 where 1 != 1", + "Query": "select u_tbl2.col2 from u_tbl2 where id = 1 for update", "Table": "u_tbl2" }, { @@ -1113,8 +1113,8 @@ "Name": "unsharded_fk_allow", "Sharded": false }, - "FieldQuery": "select col1 from u_tbl1 where 1 != 1", - "Query": "select col1 from u_tbl1 where id = 1 for update", + "FieldQuery": "select u_tbl1.col1 from u_tbl1 where 1 != 1", + "Query": "select u_tbl1.col1 from u_tbl1 where id = 1 for update", "Table": "u_tbl1" }, { @@ -1133,8 +1133,8 @@ "Name": "unsharded_fk_allow", "Sharded": false }, - "FieldQuery": "select col2 from u_tbl2 where 1 != 1", - "Query": "select col2 from u_tbl2 where (col2) in ::fkc_vals for update", + "FieldQuery": "select u_tbl2.col2 from u_tbl2 where 1 != 1", + "Query": "select u_tbl2.col2 from u_tbl2 where (col2) in ::fkc_vals for update", "Table": "u_tbl2" }, { @@ -1183,8 +1183,8 @@ "Name": "unsharded_fk_allow", "Sharded": false }, - "FieldQuery": "select col9 from u_tbl9 where 1 != 1", - "Query": "select col9 from u_tbl9 where (col9) in ::fkc_vals2 and (col9) not in ((cast(2 as CHAR))) for update nowait", + "FieldQuery": "select u_tbl9.col9 from u_tbl9 where 1 != 1", + "Query": "select u_tbl9.col9 from u_tbl9 where (col9) in ::fkc_vals2 and (col9) not in ((cast(2 as CHAR))) for update nowait", "Table": "u_tbl9" }, { @@ -1430,8 +1430,8 @@ "Name": "unsharded_fk_allow", "Sharded": false }, - "FieldQuery": "select col6 from u_tbl6 where 1 != 1", - "Query": "select col6 from u_tbl6 for update", + "FieldQuery": "select u_tbl6.col6 from u_tbl6 where 1 != 1", + "Query": "select u_tbl6.col6 from u_tbl6 for update", "Table": "u_tbl6" }, { @@ -1506,8 +1506,8 @@ "Name": "unsharded_fk_allow", "Sharded": false }, - "FieldQuery": "select col7 from u_tbl7 where 1 != 1", - "Query": "select col7 from u_tbl7 for update", + "FieldQuery": "select u_tbl7.col7 from u_tbl7 where 1 != 1", + "Query": "select u_tbl7.col7 from u_tbl7 for update", "Table": "u_tbl7" }, { @@ -1595,8 +1595,8 @@ "Name": "unsharded_fk_allow", "Sharded": false }, - "FieldQuery": "select col7 from u_tbl7 where 1 != 1", - "Query": "select col7 from u_tbl7 for update", + "FieldQuery": "select u_tbl7.col7 from u_tbl7 where 1 != 1", + "Query": "select u_tbl7.col7 from u_tbl7 for update", "Table": "u_tbl7" }, { @@ -1702,8 +1702,8 @@ "Name": "unsharded_fk_allow", "Sharded": false }, - "FieldQuery": "select col1 from u_tbl1 where 1 != 1", - "Query": "select col1 from u_tbl1 where id = 1 for update", + "FieldQuery": "select u_tbl1.col1 from u_tbl1 where 1 != 1", + "Query": "select u_tbl1.col1 from u_tbl1 where id = 1 for update", "Table": "u_tbl1" }, { @@ -1722,8 +1722,8 @@ "Name": "unsharded_fk_allow", "Sharded": false }, - "FieldQuery": "select col2 from u_tbl2 where 1 != 1", - "Query": "select col2 from u_tbl2 where (col2) in ::fkc_vals for update", + "FieldQuery": "select u_tbl2.col2 from u_tbl2 where 1 != 1", + "Query": "select u_tbl2.col2 from u_tbl2 where (col2) in ::fkc_vals for update", "Table": "u_tbl2" }, { @@ -1772,8 +1772,8 @@ "Name": "unsharded_fk_allow", "Sharded": false }, - "FieldQuery": "select col9 from u_tbl9 where 1 != 1", - "Query": "select col9 from u_tbl9 where (col9) in ::fkc_vals2 and (col9) not in ((cast(5 as CHAR))) for update nowait", + "FieldQuery": "select u_tbl9.col9 from u_tbl9 where 1 != 1", + "Query": "select u_tbl9.col9 from u_tbl9 where (col9) in ::fkc_vals2 and (col9) not in ((cast(5 as CHAR))) for update nowait", "Table": "u_tbl9" }, { @@ -1878,8 +1878,8 @@ "Name": "unsharded_fk_allow", "Sharded": false }, - "FieldQuery": "select col1 from u_tbl1 where 1 != 1", - "Query": "select col1 from u_tbl1 where (id) in ((1)) for update", + "FieldQuery": "select u_tbl1.col1 from u_tbl1 where 1 != 1", + "Query": "select u_tbl1.col1 from u_tbl1 where (id) in ((1)) for update", "Table": "u_tbl1" }, { @@ -1898,8 +1898,8 @@ "Name": "unsharded_fk_allow", "Sharded": false }, - "FieldQuery": "select col2 from u_tbl2 where 1 != 1", - "Query": "select col2 from u_tbl2 where (col2) in ::fkc_vals for update", + "FieldQuery": "select u_tbl2.col2 from u_tbl2 where 1 != 1", + "Query": "select u_tbl2.col2 from u_tbl2 where (col2) in ::fkc_vals for update", "Table": "u_tbl2" }, { @@ -1984,8 +1984,8 @@ "Name": "unsharded_fk_allow", "Sharded": false }, - "FieldQuery": "select cola, colb from u_multicol_tbl1 where 1 != 1", - "Query": "select cola, colb from u_multicol_tbl1 where id = 3 for update", + "FieldQuery": "select u_multicol_tbl1.cola, u_multicol_tbl1.colb from u_multicol_tbl1 where 1 != 1", + "Query": "select u_multicol_tbl1.cola, u_multicol_tbl1.colb from u_multicol_tbl1 where id = 3 for update", "Table": "u_multicol_tbl1" }, { @@ -2005,8 +2005,8 @@ "Name": "unsharded_fk_allow", "Sharded": false }, - "FieldQuery": "select cola, colb from u_multicol_tbl2 where 1 != 1", - "Query": "select cola, colb from u_multicol_tbl2 where (cola, colb) in ::fkc_vals and (cola, colb) not in ((1, 2)) for update", + "FieldQuery": "select u_multicol_tbl2.cola, u_multicol_tbl2.colb from u_multicol_tbl2 where 1 != 1", + "Query": "select u_multicol_tbl2.cola, u_multicol_tbl2.colb from u_multicol_tbl2 where (cola, colb) in ::fkc_vals and (cola, colb) not in ((1, 2)) for update", "Table": "u_multicol_tbl2" }, { @@ -2078,8 +2078,8 @@ "Name": "unsharded_fk_allow", "Sharded": false }, - "FieldQuery": "select cola, colb from u_multicol_tbl1 where 1 != 1", - "Query": "select cola, colb from u_multicol_tbl1 where id = :v3 for update", + "FieldQuery": "select u_multicol_tbl1.cola, u_multicol_tbl1.colb from u_multicol_tbl1 where 1 != 1", + "Query": "select u_multicol_tbl1.cola, u_multicol_tbl1.colb from u_multicol_tbl1 where id = :v3 for update", "Table": "u_multicol_tbl1" }, { @@ -2099,8 +2099,8 @@ "Name": "unsharded_fk_allow", "Sharded": false }, - "FieldQuery": "select cola, colb from u_multicol_tbl2 where 1 != 1", - "Query": "select cola, colb from u_multicol_tbl2 where (cola, colb) in ::fkc_vals and (:v2 is null or (:v1 is null or (cola, colb) not in ((:v1, :v2)))) for update", + "FieldQuery": "select u_multicol_tbl2.cola, u_multicol_tbl2.colb from u_multicol_tbl2 where 1 != 1", + "Query": "select u_multicol_tbl2.cola, u_multicol_tbl2.colb from u_multicol_tbl2 where (cola, colb) in ::fkc_vals and (:v2 is null or (:v1 is null or (cola, colb) not in ((:v1, :v2)))) for update", "Table": "u_multicol_tbl2" }, { @@ -2178,8 +2178,8 @@ "Name": "sharded_fk_allow", "Sharded": true }, - "FieldQuery": "select col5, t5col5 from tbl5 where 1 != 1", - "Query": "select col5, t5col5 from tbl5 where id = :v1 for update", + "FieldQuery": "select tbl5.col5, tbl5.t5col5 from tbl5 where 1 != 1", + "Query": "select tbl5.col5, tbl5.t5col5 from tbl5 where id = :v1 for update", "Table": "tbl5" }, { @@ -2262,8 +2262,8 @@ "Name": "unsharded_fk_allow", "Sharded": false }, - "FieldQuery": "select col7, col7 <=> cast(baz + 1 + col7 as CHAR), cast(baz + 1 + col7 as CHAR) from u_tbl7 where 1 != 1", - "Query": "select col7, col7 <=> cast(baz + 1 + col7 as CHAR), cast(baz + 1 + col7 as CHAR) from u_tbl7 where bar = 42 for update", + "FieldQuery": "select u_tbl7.col7, col7 <=> cast(baz + 1 + col7 as CHAR), cast(baz + 1 + col7 as CHAR) from u_tbl7 where 1 != 1", + "Query": "select u_tbl7.col7, col7 <=> cast(baz + 1 + col7 as CHAR), cast(baz + 1 + col7 as CHAR) from u_tbl7 where bar = 42 for update", "Table": "u_tbl7" }, { @@ -2358,8 +2358,8 @@ "Name": "unsharded_fk_allow", "Sharded": false }, - "FieldQuery": "select cola, colb, cola <=> cola + 3, cola + 3 from u_multicol_tbl1 where 1 != 1", - "Query": "select cola, colb, cola <=> cola + 3, cola + 3 from u_multicol_tbl1 where id = 3 for update", + "FieldQuery": "select u_multicol_tbl1.cola, u_multicol_tbl1.colb, cola <=> cola + 3, cola + 3 from u_multicol_tbl1 where 1 != 1", + "Query": "select u_multicol_tbl1.cola, u_multicol_tbl1.colb, cola <=> cola + 3, cola + 3 from u_multicol_tbl1 where id = 3 for update", "Table": "u_multicol_tbl1" }, { @@ -2386,8 +2386,8 @@ "Name": "unsharded_fk_allow", "Sharded": false }, - "FieldQuery": "select cola, colb from u_multicol_tbl2 where 1 != 1", - "Query": "select cola, colb from u_multicol_tbl2 where (cola, colb) in ::fkc_vals and (:fkc_upd is null or (cola) not in ((:fkc_upd))) for update", + "FieldQuery": "select u_multicol_tbl2.cola, u_multicol_tbl2.colb from u_multicol_tbl2 where 1 != 1", + "Query": "select u_multicol_tbl2.cola, u_multicol_tbl2.colb from u_multicol_tbl2 where (cola, colb) in ::fkc_vals and (:fkc_upd is null or (cola) not in ((:fkc_upd))) for update", "Table": "u_multicol_tbl2" }, { @@ -2480,8 +2480,8 @@ "Name": "unsharded_fk_allow", "Sharded": false }, - "FieldQuery": "select cola, colb, cola <=> 2, 2, colb <=> colc - 2, colc - 2 from u_multicol_tbl2 where 1 != 1", - "Query": "select cola, colb, cola <=> 2, 2, colb <=> colc - 2, colc - 2 from u_multicol_tbl2 where id = 7 for update", + "FieldQuery": "select u_multicol_tbl2.cola, u_multicol_tbl2.colb, cola <=> 2, 2, colb <=> colc - 2, colc - 2 from u_multicol_tbl2 where 1 != 1", + "Query": "select u_multicol_tbl2.cola, u_multicol_tbl2.colb, cola <=> 2, 2, colb <=> colc - 2, colc - 2 from u_multicol_tbl2 where id = 7 for update", "Table": "u_multicol_tbl2" }, { @@ -2556,8 +2556,8 @@ "Name": "unsharded_fk_allow", "Sharded": false }, - "FieldQuery": "select col9 from u_tbl9 where 1 != 1", - "Query": "select col9 from u_tbl9 where (col9) in ((10), (20), (30)) or (col9 * foo) in ((10 * null), (20 * null), (30 * null)) or (bar, col9) in ((1, 10), (1, 20), (1, 30)) or (id) in ((1), (2), (3)) for update nowait", + "FieldQuery": "select u_tbl9.col9 from u_tbl9 where 1 != 1", + "Query": "select u_tbl9.col9 from u_tbl9 where (col9) in ((10), (20), (30)) or (col9 * foo) in ((10 * null), (20 * null), (30 * null)) or (bar, col9) in ((1, 10), (1, 20), (1, 30)) or (id) in ((1), (2), (3)) for update nowait", "Table": "u_tbl9" }, { @@ -2720,8 +2720,8 @@ "Name": "unsharded_fk_allow", "Sharded": false }, - "FieldQuery": "select col1 from u_tbl1 where 1 != 1", - "Query": "select col1 from u_tbl1 where id = 1 for update", + "FieldQuery": "select u_tbl1.col1 from u_tbl1 where 1 != 1", + "Query": "select u_tbl1.col1 from u_tbl1 where id = 1 for update", "Table": "u_tbl1" }, { @@ -2740,8 +2740,8 @@ "Name": "unsharded_fk_allow", "Sharded": false }, - "FieldQuery": "select col2 from u_tbl2 where 1 != 1", - "Query": "select col2 from u_tbl2 where (col2) in ::fkc_vals for update", + "FieldQuery": "select u_tbl2.col2 from u_tbl2 where 1 != 1", + "Query": "select u_tbl2.col2 from u_tbl2 where (col2) in ::fkc_vals for update", "Table": "u_tbl2" }, { @@ -2790,8 +2790,8 @@ "Name": "unsharded_fk_allow", "Sharded": false }, - "FieldQuery": "select col9 from u_tbl9 where 1 != 1", - "Query": "select col9 from u_tbl9 where (col9) in ::fkc_vals2 and (col9) not in ((cast(3 as CHAR))) for update nowait", + "FieldQuery": "select u_tbl9.col9 from u_tbl9 where 1 != 1", + "Query": "select u_tbl9.col9 from u_tbl9 where (col9) in ::fkc_vals2 and (col9) not in ((cast(3 as CHAR))) for update nowait", "Table": "u_tbl9" }, { @@ -2884,8 +2884,8 @@ "Name": "unsharded_fk_allow", "Sharded": false }, - "FieldQuery": "select col2 from u_tbl2 where 1 != 1", - "Query": "select col2 from u_tbl2 where id = :v1 for update", + "FieldQuery": "select u_tbl2.col2 from u_tbl2 where 1 != 1", + "Query": "select u_tbl2.col2 from u_tbl2 where id = :v1 for update", "Table": "u_tbl2" }, { @@ -2943,8 +2943,8 @@ "Name": "unsharded_fk_allow", "Sharded": false }, - "FieldQuery": "select col2 from u_tbl2 where 1 != 1", - "Query": "select col2 from u_tbl2 where id = :v3 for update", + "FieldQuery": "select u_tbl2.col2 from u_tbl2 where 1 != 1", + "Query": "select u_tbl2.col2 from u_tbl2 where id = :v3 for update", "Table": "u_tbl2" }, { @@ -3002,8 +3002,8 @@ "Name": "unsharded_fk_allow", "Sharded": false }, - "FieldQuery": "select col2 from u_tbl2 where 1 != 1", - "Query": "select col2 from u_tbl2 where id = :v5 for update", + "FieldQuery": "select u_tbl2.col2 from u_tbl2 where 1 != 1", + "Query": "select u_tbl2.col2 from u_tbl2 where id = :v5 for update", "Table": "u_tbl2" }, { @@ -3048,5 +3048,213 @@ "comment": "Unknown update column in foreign keys", "query": "update tbl_auth set unknown_col = 'verified' where id = 1", "plan": "column 'unknown_col' not found in table 'tbl_auth'" + }, + { + "comment": "Unsharded multi-table delete with foreign keys", + "query": "delete u from u_tbl6 u join u_tbl5 m on u.col = m.col where u.col2 = 4 and m.col3 = 6", + "plan": { + "QueryType": "DELETE", + "Original": "delete u from u_tbl6 u join u_tbl5 m on u.col = m.col where u.col2 = 4 and m.col3 = 6", + "Instructions": { + "OperatorType": "FkCascade", + "Inputs": [ + { + "InputName": "Selection", + "OperatorType": "Route", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "FieldQuery": "select u_tbl6.col6 from u_tbl6 as u, u_tbl5 as m where 1 != 1", + "Query": "select u_tbl6.col6 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" + }, + { + "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 u from u_tbl6 as u, u_tbl5 as m where u.col2 = 4 and m.col3 = 6 and u.col = m.col", + "Table": "u_tbl6" + } + ] + }, + "TablesUsed": [ + "unsharded_fk_allow.u_tbl5", + "unsharded_fk_allow.u_tbl6", + "unsharded_fk_allow.u_tbl8" + ] + } + }, + { + "comment": "Multi table delete with using", + "query": "delete u_tbl10 from u_tbl10 join u_tbl11 using (id) where id = 5", + "plan": { + "QueryType": "DELETE", + "Original": "delete u_tbl10 from u_tbl10 join u_tbl11 using (id) where id = 5", + "Instructions": { + "OperatorType": "FkCascade", + "Inputs": [ + { + "InputName": "Selection", + "OperatorType": "Route", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "FieldQuery": "select u_tbl10.col from u_tbl10, u_tbl11 where 1 != 1", + "Query": "select u_tbl10.col from u_tbl10, u_tbl11 where u_tbl10.id = 5 and u_tbl10.id = u_tbl11.id for update", + "Table": "u_tbl10, u_tbl11" + }, + { + "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_tbl11 where (col) in ::fkc_vals", + "Table": "u_tbl11" + }, + { + "InputName": "Parent", + "OperatorType": "Delete", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "Query": "delete u_tbl10 from u_tbl10, u_tbl11 where u_tbl10.id = 5 and u_tbl10.id = u_tbl11.id", + "Table": "u_tbl10" + } + ] + }, + "TablesUsed": [ + "unsharded_fk_allow.u_tbl10", + "unsharded_fk_allow.u_tbl11" + ] + } + }, + { + "comment": "Multi table delete with unrelated tables", + "query": "delete u_tbl1 from u_tbl10 join u_tbl1 on u_tbl10.col = u_tbl1.col", + "plan": { + "QueryType": "DELETE", + "Original": "delete u_tbl1 from u_tbl10 join u_tbl1 on u_tbl10.col = u_tbl1.col", + "Instructions": { + "OperatorType": "FkCascade", + "Inputs": [ + { + "InputName": "Selection", + "OperatorType": "Route", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "FieldQuery": "select u_tbl1.col1 from u_tbl10, u_tbl1 where 1 != 1", + "Query": "select u_tbl1.col1 from u_tbl10, u_tbl1 where u_tbl10.col = u_tbl1.col for update", + "Table": "u_tbl1, u_tbl10" + }, + { + "InputName": "CascadeChild-1", + "OperatorType": "FkCascade", + "BvName": "fkc_vals", + "Cols": [ + 0 + ], + "Inputs": [ + { + "InputName": "Selection", + "OperatorType": "Route", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "FieldQuery": "select u_tbl2.col2 from u_tbl2 where 1 != 1", + "Query": "select u_tbl2.col2 from u_tbl2 where (col2) in ::fkc_vals for update", + "Table": "u_tbl2" + }, + { + "InputName": "CascadeChild-1", + "OperatorType": "Update", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "BvName": "fkc_vals1", + "Cols": [ + 0 + ], + "Query": "update u_tbl3 set col3 = null where (col3) in ::fkc_vals1", + "Table": "u_tbl3" + }, + { + "InputName": "Parent", + "OperatorType": "Delete", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "Query": "delete from u_tbl2 where (col2) in ::fkc_vals", + "Table": "u_tbl2" + } + ] + }, + { + "InputName": "Parent", + "OperatorType": "Delete", + "Variant": "Unsharded", + "Keyspace": { + "Name": "unsharded_fk_allow", + "Sharded": false + }, + "TargetTabletType": "PRIMARY", + "Query": "delete u_tbl1 from u_tbl10, u_tbl1 where u_tbl10.col = u_tbl1.col", + "Table": "u_tbl1" + } + ] + }, + "TablesUsed": [ + "unsharded_fk_allow.u_tbl1", + "unsharded_fk_allow.u_tbl10", + "unsharded_fk_allow.u_tbl2", + "unsharded_fk_allow.u_tbl3" + ] + } } ] diff --git a/go/vt/vtgate/planbuilder/testdata/foreignkey_checks_off_cases.json b/go/vt/vtgate/planbuilder/testdata/foreignkey_checks_off_cases.json index 31ee3e7c554..264311696a3 100644 --- a/go/vt/vtgate/planbuilder/testdata/foreignkey_checks_off_cases.json +++ b/go/vt/vtgate/planbuilder/testdata/foreignkey_checks_off_cases.json @@ -110,8 +110,8 @@ "Name": "sharded_fk_allow", "Sharded": true }, - "FieldQuery": "select colb, cola, y, colc, x from multicol_tbl1 where 1 != 1", - "Query": "select colb, cola, y, colc, x from multicol_tbl1 where cola = 1 and colb = 2 and colc = 3 for update", + "FieldQuery": "select multicol_tbl1.colb, multicol_tbl1.cola, multicol_tbl1.y, multicol_tbl1.colc, multicol_tbl1.x from multicol_tbl1 where 1 != 1", + "Query": "select multicol_tbl1.colb, multicol_tbl1.cola, multicol_tbl1.y, multicol_tbl1.colc, multicol_tbl1.x from multicol_tbl1 where cola = 1 and colb = 2 and colc = 3 for update", "Table": "multicol_tbl1", "Values": [ "1", @@ -263,8 +263,8 @@ "Name": "sharded_fk_allow", "Sharded": true }, - "FieldQuery": "select t5col5 from tbl5 where 1 != 1", - "Query": "select t5col5 from tbl5 for update", + "FieldQuery": "select tbl5.t5col5 from tbl5 where 1 != 1", + "Query": "select tbl5.t5col5 from tbl5 for update", "Table": "tbl5" }, { @@ -371,8 +371,8 @@ "Name": "sharded_fk_allow", "Sharded": true }, - "FieldQuery": "select col9 from tbl9 where 1 != 1", - "Query": "select col9 from tbl9 where col9 = 34 for update", + "FieldQuery": "select tbl9.col9 from tbl9 where 1 != 1", + "Query": "select tbl9.col9 from tbl9 where col9 = 34 for update", "Table": "tbl9", "Values": [ "34" diff --git a/go/vt/vtgate/planbuilder/testdata/foreignkey_checks_on_cases.json b/go/vt/vtgate/planbuilder/testdata/foreignkey_checks_on_cases.json index 76a71986842..0e22d3ed323 100644 --- a/go/vt/vtgate/planbuilder/testdata/foreignkey_checks_on_cases.json +++ b/go/vt/vtgate/planbuilder/testdata/foreignkey_checks_on_cases.json @@ -81,8 +81,8 @@ "Name": "sharded_fk_allow", "Sharded": true }, - "FieldQuery": "select colb, cola, y, colc, x from multicol_tbl1 where 1 != 1", - "Query": "select colb, cola, y, colc, x from multicol_tbl1 where cola = 1 and colb = 2 and colc = 3 for update", + "FieldQuery": "select multicol_tbl1.colb, multicol_tbl1.cola, multicol_tbl1.y, multicol_tbl1.colc, multicol_tbl1.x from multicol_tbl1 where 1 != 1", + "Query": "select multicol_tbl1.colb, multicol_tbl1.cola, multicol_tbl1.y, multicol_tbl1.colc, multicol_tbl1.x from multicol_tbl1 where cola = 1 and colb = 2 and colc = 3 for update", "Table": "multicol_tbl1", "Values": [ "1", @@ -160,8 +160,8 @@ "Name": "sharded_fk_allow", "Sharded": true }, - "FieldQuery": "select col5, t5col5 from tbl5 where 1 != 1", - "Query": "select col5, t5col5 from tbl5 for update", + "FieldQuery": "select tbl5.col5, tbl5.t5col5 from tbl5 where 1 != 1", + "Query": "select tbl5.col5, tbl5.t5col5 from tbl5 for update", "Table": "tbl5" }, { @@ -242,8 +242,8 @@ "Name": "unsharded_fk_allow", "Sharded": false }, - "FieldQuery": "select col9 from u_tbl9 where 1 != 1", - "Query": "select col9 from u_tbl9 where col9 = 5 for update nowait", + "FieldQuery": "select u_tbl9.col9 from u_tbl9 where 1 != 1", + "Query": "select u_tbl9.col9 from u_tbl9 where col9 = 5 for update nowait", "Table": "u_tbl9" }, { @@ -321,8 +321,8 @@ "Name": "unsharded_fk_allow", "Sharded": false }, - "FieldQuery": "select col2 from u_tbl2 where 1 != 1", - "Query": "select col2 from u_tbl2 where id = 1 for update", + "FieldQuery": "select u_tbl2.col2 from u_tbl2 where 1 != 1", + "Query": "select u_tbl2.col2 from u_tbl2 where id = 1 for update", "Table": "u_tbl2" }, { @@ -432,8 +432,8 @@ "Name": "sharded_fk_allow", "Sharded": true }, - "FieldQuery": "select t5col5 from tbl5 where 1 != 1", - "Query": "select t5col5 from tbl5 for update", + "FieldQuery": "select tbl5.t5col5 from tbl5 where 1 != 1", + "Query": "select tbl5.t5col5 from tbl5 for update", "Table": "tbl5" }, { @@ -601,8 +601,8 @@ "Name": "sharded_fk_allow", "Sharded": true }, - "FieldQuery": "select col9 from tbl9 where 1 != 1", - "Query": "select col9 from tbl9 where col9 = 34 for update", + "FieldQuery": "select tbl9.col9 from tbl9 where 1 != 1", + "Query": "select tbl9.col9 from tbl9 where col9 = 34 for update", "Table": "tbl9", "Values": [ "34" @@ -666,8 +666,8 @@ "Name": "unsharded_fk_allow", "Sharded": false }, - "FieldQuery": "select col1 from u_tbl1 where 1 != 1", - "Query": "select col1 from u_tbl1 for update", + "FieldQuery": "select u_tbl1.col1 from u_tbl1 where 1 != 1", + "Query": "select u_tbl1.col1 from u_tbl1 for update", "Table": "u_tbl1" }, { @@ -686,8 +686,8 @@ "Name": "unsharded_fk_allow", "Sharded": false }, - "FieldQuery": "select col2 from u_tbl2 where 1 != 1", - "Query": "select col2 from u_tbl2 where (col2) in ::fkc_vals for update", + "FieldQuery": "select u_tbl2.col2 from u_tbl2 where 1 != 1", + "Query": "select u_tbl2.col2 from u_tbl2 where (col2) in ::fkc_vals for update", "Table": "u_tbl2" }, { @@ -736,8 +736,8 @@ "Name": "unsharded_fk_allow", "Sharded": false }, - "FieldQuery": "select col9 from u_tbl9 where 1 != 1", - "Query": "select col9 from u_tbl9 where (col9) in ::fkc_vals2 and (col9) not in ((cast('foo' as CHAR))) for update nowait", + "FieldQuery": "select u_tbl9.col9 from u_tbl9 where 1 != 1", + "Query": "select u_tbl9.col9 from u_tbl9 where (col9) in ::fkc_vals2 and (col9) not in ((cast('foo' as CHAR))) for update nowait", "Table": "u_tbl9" }, { @@ -831,8 +831,8 @@ "Name": "unsharded_fk_allow", "Sharded": false }, - "FieldQuery": "select col2, col2 <=> cast(col1 + 'bar' as CHAR), cast(col1 + 'bar' as CHAR) from u_tbl2 where 1 != 1", - "Query": "select col2, col2 <=> cast(col1 + 'bar' as CHAR), cast(col1 + 'bar' as CHAR) from u_tbl2 where id = 1 for update", + "FieldQuery": "select u_tbl2.col2, col2 <=> cast(col1 + 'bar' as CHAR), cast(col1 + 'bar' as CHAR) from u_tbl2 where 1 != 1", + "Query": "select u_tbl2.col2, col2 <=> cast(col1 + 'bar' as CHAR), cast(col1 + 'bar' as CHAR) from u_tbl2 where id = 1 for update", "Table": "u_tbl2" }, { @@ -898,8 +898,8 @@ "Name": "unsharded_fk_allow", "Sharded": false }, - "FieldQuery": "select col1, col1 <=> cast(x + 'bar' as CHAR), cast(x + 'bar' as CHAR) from u_tbl1 where 1 != 1", - "Query": "select col1, col1 <=> cast(x + 'bar' as CHAR), cast(x + 'bar' as CHAR) from u_tbl1 where id = 1 for update", + "FieldQuery": "select u_tbl1.col1, col1 <=> cast(x + 'bar' as CHAR), cast(x + 'bar' as CHAR) from u_tbl1 where 1 != 1", + "Query": "select u_tbl1.col1, col1 <=> cast(x + 'bar' as CHAR), cast(x + 'bar' as CHAR) from u_tbl1 where id = 1 for update", "Table": "u_tbl1" }, { @@ -925,8 +925,8 @@ "Name": "unsharded_fk_allow", "Sharded": false }, - "FieldQuery": "select col2 from u_tbl2 where 1 != 1", - "Query": "select col2 from u_tbl2 where (col2) in ::fkc_vals for update", + "FieldQuery": "select u_tbl2.col2 from u_tbl2 where 1 != 1", + "Query": "select u_tbl2.col2 from u_tbl2 where (col2) in ::fkc_vals for update", "Table": "u_tbl2" }, { @@ -982,8 +982,8 @@ "Name": "unsharded_fk_allow", "Sharded": false }, - "FieldQuery": "select col9 from u_tbl9 where 1 != 1", - "Query": "select col9 from u_tbl9 where (col9) in ::fkc_vals2 and (:fkc_upd1 is null or (col9) not in ((:fkc_upd1))) for update nowait", + "FieldQuery": "select u_tbl9.col9 from u_tbl9 where 1 != 1", + "Query": "select u_tbl9.col9 from u_tbl9 where (col9) in ::fkc_vals2 and (:fkc_upd1 is null or (col9) not in ((:fkc_upd1))) for update nowait", "Table": "u_tbl9" }, { @@ -1056,8 +1056,8 @@ "Name": "unsharded_fk_allow", "Sharded": false }, - "FieldQuery": "select col2 from u_tbl2 where 1 != 1", - "Query": "select col2 from u_tbl2 where id = 1 for update", + "FieldQuery": "select u_tbl2.col2 from u_tbl2 where 1 != 1", + "Query": "select u_tbl2.col2 from u_tbl2 where id = 1 for update", "Table": "u_tbl2" }, { @@ -1113,8 +1113,8 @@ "Name": "unsharded_fk_allow", "Sharded": false }, - "FieldQuery": "select col1 from u_tbl1 where 1 != 1", - "Query": "select col1 from u_tbl1 where id = 1 for update", + "FieldQuery": "select u_tbl1.col1 from u_tbl1 where 1 != 1", + "Query": "select u_tbl1.col1 from u_tbl1 where id = 1 for update", "Table": "u_tbl1" }, { @@ -1133,8 +1133,8 @@ "Name": "unsharded_fk_allow", "Sharded": false }, - "FieldQuery": "select col2 from u_tbl2 where 1 != 1", - "Query": "select col2 from u_tbl2 where (col2) in ::fkc_vals for update", + "FieldQuery": "select u_tbl2.col2 from u_tbl2 where 1 != 1", + "Query": "select u_tbl2.col2 from u_tbl2 where (col2) in ::fkc_vals for update", "Table": "u_tbl2" }, { @@ -1183,8 +1183,8 @@ "Name": "unsharded_fk_allow", "Sharded": false }, - "FieldQuery": "select col9 from u_tbl9 where 1 != 1", - "Query": "select col9 from u_tbl9 where (col9) in ::fkc_vals2 and (col9) not in ((cast(2 as CHAR))) for update nowait", + "FieldQuery": "select u_tbl9.col9 from u_tbl9 where 1 != 1", + "Query": "select u_tbl9.col9 from u_tbl9 where (col9) in ::fkc_vals2 and (col9) not in ((cast(2 as CHAR))) for update nowait", "Table": "u_tbl9" }, { @@ -1430,8 +1430,8 @@ "Name": "unsharded_fk_allow", "Sharded": false }, - "FieldQuery": "select col6 from u_tbl6 where 1 != 1", - "Query": "select col6 from u_tbl6 for update", + "FieldQuery": "select u_tbl6.col6 from u_tbl6 where 1 != 1", + "Query": "select u_tbl6.col6 from u_tbl6 for update", "Table": "u_tbl6" }, { @@ -1506,8 +1506,8 @@ "Name": "unsharded_fk_allow", "Sharded": false }, - "FieldQuery": "select col7 from u_tbl7 where 1 != 1", - "Query": "select col7 from u_tbl7 for update", + "FieldQuery": "select u_tbl7.col7 from u_tbl7 where 1 != 1", + "Query": "select u_tbl7.col7 from u_tbl7 for update", "Table": "u_tbl7" }, { @@ -1595,8 +1595,8 @@ "Name": "unsharded_fk_allow", "Sharded": false }, - "FieldQuery": "select col7 from u_tbl7 where 1 != 1", - "Query": "select col7 from u_tbl7 for update", + "FieldQuery": "select u_tbl7.col7 from u_tbl7 where 1 != 1", + "Query": "select u_tbl7.col7 from u_tbl7 for update", "Table": "u_tbl7" }, { @@ -1702,8 +1702,8 @@ "Name": "unsharded_fk_allow", "Sharded": false }, - "FieldQuery": "select col1 from u_tbl1 where 1 != 1", - "Query": "select col1 from u_tbl1 where id = 1 for update", + "FieldQuery": "select u_tbl1.col1 from u_tbl1 where 1 != 1", + "Query": "select u_tbl1.col1 from u_tbl1 where id = 1 for update", "Table": "u_tbl1" }, { @@ -1722,8 +1722,8 @@ "Name": "unsharded_fk_allow", "Sharded": false }, - "FieldQuery": "select col2 from u_tbl2 where 1 != 1", - "Query": "select col2 from u_tbl2 where (col2) in ::fkc_vals for update", + "FieldQuery": "select u_tbl2.col2 from u_tbl2 where 1 != 1", + "Query": "select u_tbl2.col2 from u_tbl2 where (col2) in ::fkc_vals for update", "Table": "u_tbl2" }, { @@ -1772,8 +1772,8 @@ "Name": "unsharded_fk_allow", "Sharded": false }, - "FieldQuery": "select col9 from u_tbl9 where 1 != 1", - "Query": "select col9 from u_tbl9 where (col9) in ::fkc_vals2 and (col9) not in ((cast(5 as CHAR))) for update nowait", + "FieldQuery": "select u_tbl9.col9 from u_tbl9 where 1 != 1", + "Query": "select u_tbl9.col9 from u_tbl9 where (col9) in ::fkc_vals2 and (col9) not in ((cast(5 as CHAR))) for update nowait", "Table": "u_tbl9" }, { @@ -1878,8 +1878,8 @@ "Name": "unsharded_fk_allow", "Sharded": false }, - "FieldQuery": "select col1 from u_tbl1 where 1 != 1", - "Query": "select col1 from u_tbl1 where (id) in ((1)) for update", + "FieldQuery": "select u_tbl1.col1 from u_tbl1 where 1 != 1", + "Query": "select u_tbl1.col1 from u_tbl1 where (id) in ((1)) for update", "Table": "u_tbl1" }, { @@ -1898,8 +1898,8 @@ "Name": "unsharded_fk_allow", "Sharded": false }, - "FieldQuery": "select col2 from u_tbl2 where 1 != 1", - "Query": "select col2 from u_tbl2 where (col2) in ::fkc_vals for update", + "FieldQuery": "select u_tbl2.col2 from u_tbl2 where 1 != 1", + "Query": "select u_tbl2.col2 from u_tbl2 where (col2) in ::fkc_vals for update", "Table": "u_tbl2" }, { @@ -1984,8 +1984,8 @@ "Name": "unsharded_fk_allow", "Sharded": false }, - "FieldQuery": "select cola, colb from u_multicol_tbl1 where 1 != 1", - "Query": "select cola, colb from u_multicol_tbl1 where id = 3 for update", + "FieldQuery": "select u_multicol_tbl1.cola, u_multicol_tbl1.colb from u_multicol_tbl1 where 1 != 1", + "Query": "select u_multicol_tbl1.cola, u_multicol_tbl1.colb from u_multicol_tbl1 where id = 3 for update", "Table": "u_multicol_tbl1" }, { @@ -2005,8 +2005,8 @@ "Name": "unsharded_fk_allow", "Sharded": false }, - "FieldQuery": "select cola, colb from u_multicol_tbl2 where 1 != 1", - "Query": "select cola, colb from u_multicol_tbl2 where (cola, colb) in ::fkc_vals and (cola, colb) not in ((1, 2)) for update", + "FieldQuery": "select u_multicol_tbl2.cola, u_multicol_tbl2.colb from u_multicol_tbl2 where 1 != 1", + "Query": "select u_multicol_tbl2.cola, u_multicol_tbl2.colb from u_multicol_tbl2 where (cola, colb) in ::fkc_vals and (cola, colb) not in ((1, 2)) for update", "Table": "u_multicol_tbl2" }, { @@ -2078,8 +2078,8 @@ "Name": "unsharded_fk_allow", "Sharded": false }, - "FieldQuery": "select cola, colb from u_multicol_tbl1 where 1 != 1", - "Query": "select cola, colb from u_multicol_tbl1 where id = :v3 for update", + "FieldQuery": "select u_multicol_tbl1.cola, u_multicol_tbl1.colb from u_multicol_tbl1 where 1 != 1", + "Query": "select u_multicol_tbl1.cola, u_multicol_tbl1.colb from u_multicol_tbl1 where id = :v3 for update", "Table": "u_multicol_tbl1" }, { @@ -2099,8 +2099,8 @@ "Name": "unsharded_fk_allow", "Sharded": false }, - "FieldQuery": "select cola, colb from u_multicol_tbl2 where 1 != 1", - "Query": "select cola, colb from u_multicol_tbl2 where (cola, colb) in ::fkc_vals and (:v2 is null or (:v1 is null or (cola, colb) not in ((:v1, :v2)))) for update", + "FieldQuery": "select u_multicol_tbl2.cola, u_multicol_tbl2.colb from u_multicol_tbl2 where 1 != 1", + "Query": "select u_multicol_tbl2.cola, u_multicol_tbl2.colb from u_multicol_tbl2 where (cola, colb) in ::fkc_vals and (:v2 is null or (:v1 is null or (cola, colb) not in ((:v1, :v2)))) for update", "Table": "u_multicol_tbl2" }, { @@ -2178,8 +2178,8 @@ "Name": "sharded_fk_allow", "Sharded": true }, - "FieldQuery": "select col5, t5col5 from tbl5 where 1 != 1", - "Query": "select col5, t5col5 from tbl5 where id = :v1 for update", + "FieldQuery": "select tbl5.col5, tbl5.t5col5 from tbl5 where 1 != 1", + "Query": "select tbl5.col5, tbl5.t5col5 from tbl5 where id = :v1 for update", "Table": "tbl5" }, { diff --git a/go/vt/vtgate/planbuilder/testdata/vschemas/schema.json b/go/vt/vtgate/planbuilder/testdata/vschemas/schema.json index 6f274413fbe..3579205ba70 100644 --- a/go/vt/vtgate/planbuilder/testdata/vschemas/schema.json +++ b/go/vt/vtgate/planbuilder/testdata/vschemas/schema.json @@ -862,6 +862,28 @@ {"name": "bar", "default": "1"} ] }, + "u_tbl10": { + "columns": [ + { + "name": "col10", + "type": "VARCHAR" + }, + {"name": "col"}, + {"name": "id"} + ], + "column_list_authoritative": true + }, + "u_tbl11": { + "columns": [ + { + "name": "col11", + "type": "VARCHAR" + }, + {"name": "col"}, + {"name": "id"} + ], + "column_list_authoritative": true + }, "u_tbl": {}, "u_multicol_tbl1": {}, "u_multicol_tbl2": {}, diff --git a/go/vt/vtgate/semantics/analyzer.go b/go/vt/vtgate/semantics/analyzer.go index 44573c1ec1d..546cdf76186 100644 --- a/go/vt/vtgate/semantics/analyzer.go +++ b/go/vt/vtgate/semantics/analyzer.go @@ -268,6 +268,12 @@ func isParentSelect(cursor *sqlparser.Cursor) bool { return isSelect } +func isParentDeleteOrUpdate(cursor *sqlparser.Cursor) bool { + _, isDelete := cursor.Parent().(*sqlparser.Delete) + _, isUpdate := cursor.Parent().(*sqlparser.Update) + return isDelete || isUpdate +} + func isParentSelectStatement(cursor *sqlparser.Cursor) bool { _, isSelect := cursor.Parent().(sqlparser.SelectStatement) return isSelect diff --git a/go/vt/vtgate/semantics/scoper.go b/go/vt/vtgate/semantics/scoper.go index 878ac222911..faef930b488 100644 --- a/go/vt/vtgate/semantics/scoper.go +++ b/go/vt/vtgate/semantics/scoper.go @@ -197,7 +197,7 @@ func (s *scoper) up(cursor *sqlparser.Cursor) error { if isParentSelectStatement(cursor) { s.popScope() } - case *sqlparser.Select, sqlparser.GroupBy, *sqlparser.Update, *sqlparser.Insert, *sqlparser.Union: + case *sqlparser.Select, sqlparser.GroupBy, *sqlparser.Update, *sqlparser.Insert, *sqlparser.Union, *sqlparser.Delete: id := EmptyTableSet() for _, tableInfo := range s.currentScope().tables { set := tableInfo.getTableSet(s.org) @@ -223,6 +223,12 @@ func (s *scoper) up(cursor *sqlparser.Cursor) error { } } } + if isParentDeleteOrUpdate(cursor) { + usingMap := s.currentScope().prepareUsingMap() + for ts, m := range usingMap { + s.binder.usingJoinInfo[ts] = m + } + } } return nil } diff --git a/go/vt/vtgate/semantics/semantic_state.go b/go/vt/vtgate/semantics/semantic_state.go index 5b4ff4f69de..9067e77dd88 100644 --- a/go/vt/vtgate/semantics/semantic_state.go +++ b/go/vt/vtgate/semantics/semantic_state.go @@ -188,6 +188,11 @@ func (st *SemTable) CopyDependencies(from, to sqlparser.Expr) { } } +// GetChildForeignKeysForTable gets the child foreign keys as a list for the specified table. +func (st *SemTable) GetChildForeignKeysForTable(tableName sqlparser.TableName) []vindexes.ChildFKInfo { + return st.childForeignKeysInvolved[st.Targets[tableName.Name]] +} + // GetChildForeignKeysList gets the child foreign keys as a list. func (st *SemTable) GetChildForeignKeysList() []vindexes.ChildFKInfo { var childFkInfos []vindexes.ChildFKInfo diff --git a/go/vt/wrangler/vexec_plan.go b/go/vt/wrangler/vexec_plan.go index 382f8a1a134..e9ea1daf5c3 100644 --- a/go/vt/wrangler/vexec_plan.go +++ b/go/vt/wrangler/vexec_plan.go @@ -120,9 +120,9 @@ const ( func extractTableName(stmt sqlparser.Statement) (string, error) { switch stmt := stmt.(type) { case *sqlparser.Update: - return sqlparser.String(stmt.TableExprs), nil + return sqlparser.ToString(stmt.TableExprs), nil case *sqlparser.Delete: - return sqlparser.String(stmt.TableExprs), nil + return sqlparser.ToString(stmt.TableExprs), nil case *sqlparser.Insert: return sqlparser.String(stmt.Table), nil case *sqlparser.Select: From b2f008557ceff1971e7d5446ba9650727b7a08b0 Mon Sep 17 00:00:00 2001 From: Harshit Gangal Date: Tue, 30 Jan 2024 22:13:30 +0530 Subject: [PATCH 2/4] update sizegen Signed-off-by: Harshit Gangal --- go/vt/sqlparser/cached_size.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/go/vt/sqlparser/cached_size.go b/go/vt/sqlparser/cached_size.go index cecd02ceadc..fe83cdce2ed 100644 --- a/go/vt/sqlparser/cached_size.go +++ b/go/vt/sqlparser/cached_size.go @@ -1106,7 +1106,7 @@ func (cached *Delete) CachedSize(alloc bool) int64 { size += cached.With.CachedSize(true) // field Comments *vitess.io/vitess/go/vt/sqlparser.ParsedComments size += cached.Comments.CachedSize(true) - // field TableExprs vitess.io/vitess/go/vt/sqlparser.TableExprs + // field TableExprs []vitess.io/vitess/go/vt/sqlparser.TableExpr { size += hack.RuntimeAllocSize(int64(cap(cached.TableExprs)) * int64(16)) for _, elem := range cached.TableExprs { @@ -4238,7 +4238,7 @@ func (cached *Update) CachedSize(alloc bool) int64 { size += cached.With.CachedSize(true) // field Comments *vitess.io/vitess/go/vt/sqlparser.ParsedComments size += cached.Comments.CachedSize(true) - // field TableExprs vitess.io/vitess/go/vt/sqlparser.TableExprs + // field TableExprs []vitess.io/vitess/go/vt/sqlparser.TableExpr { size += hack.RuntimeAllocSize(int64(cap(cached.TableExprs)) * int64(16)) for _, elem := range cached.TableExprs { From 673c8e9c78093bcb4b2b2bc29caae5e1fb39169a Mon Sep 17 00:00:00 2001 From: Harshit Gangal Date: Tue, 30 Jan 2024 22:54:49 +0530 Subject: [PATCH 3/4] modified permissions to accomodate the new update and delete tblexprs changes and fix the table extraction logic Signed-off-by: Harshit Gangal --- .../tabletserver/planbuilder/permission.go | 18 +++++---------- .../planbuilder/permission_test.go | 23 +++++++++---------- 2 files changed, 17 insertions(+), 24 deletions(-) diff --git a/go/vt/vttablet/tabletserver/planbuilder/permission.go b/go/vt/vttablet/tabletserver/planbuilder/permission.go index d6069c6adf3..79b2f9eb430 100644 --- a/go/vt/vttablet/tabletserver/planbuilder/permission.go +++ b/go/vt/vttablet/tabletserver/planbuilder/permission.go @@ -76,18 +76,15 @@ func BuildPermissions(stmt sqlparser.Statement) []Permission { func buildSubqueryPermissions(stmt sqlparser.Statement, role tableacl.Role, permissions []Permission) []Permission { _ = sqlparser.Walk(func(node sqlparser.SQLNode) (bool, error) { - switch node := node.(type) { - case *sqlparser.Select: - permissions = buildTableExprsPermissions(node.From, role, permissions) - case sqlparser.TableExprs: - return false, nil + if sel, ok := node.(*sqlparser.Select); ok { + permissions = buildTableExprsPermissions(sel.From, role, permissions) } return true, nil }, stmt) return permissions } -func buildTableExprsPermissions(node sqlparser.TableExprs, role tableacl.Role, permissions []Permission) []Permission { +func buildTableExprsPermissions(node []sqlparser.TableExpr, role tableacl.Role, permissions []Permission) []Permission { for _, node := range node { permissions = buildTableExprPermissions(node, role, permissions) } @@ -97,14 +94,11 @@ func buildTableExprsPermissions(node sqlparser.TableExprs, role tableacl.Role, p func buildTableExprPermissions(node sqlparser.TableExpr, role tableacl.Role, permissions []Permission) []Permission { switch node := node.(type) { case *sqlparser.AliasedTableExpr: - // An AliasedTableExpr can also be a subquery, but we should skip them here + // An AliasedTableExpr can also be a derived table, but we should skip them here // because the buildSubQueryPermissions walker will catch them and extract // the corresponding table names. - switch node := node.Expr.(type) { - case sqlparser.TableName: - permissions = buildTableNamePermissions(node, role, permissions) - case *sqlparser.DerivedTable: - permissions = buildSubqueryPermissions(node.Select, role, permissions) + if tblName, ok := node.Expr.(sqlparser.TableName); ok { + permissions = buildTableNamePermissions(tblName, role, permissions) } case *sqlparser.ParenTableExpr: permissions = buildTableExprsPermissions(node.Exprs, role, permissions) diff --git a/go/vt/vttablet/tabletserver/planbuilder/permission_test.go b/go/vt/vttablet/tabletserver/planbuilder/permission_test.go index aac0ed1f64a..6d42118cb0b 100644 --- a/go/vt/vttablet/tabletserver/planbuilder/permission_test.go +++ b/go/vt/vttablet/tabletserver/planbuilder/permission_test.go @@ -17,9 +17,9 @@ limitations under the License. package planbuilder import ( - "reflect" "testing" + "vitess.io/vitess/go/test/utils" "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/tableacl" ) @@ -169,22 +169,21 @@ func TestBuildPermissions(t *testing.T) { }, { input: "update (select * from t1) as a join t2 on a=b set c=d", output: []Permission{{ - TableName: "t1", - Role: tableacl.WRITER, - }, { TableName: "t2", Role: tableacl.WRITER, + }, { + TableName: "t1", // derived table in update or delete needs reader permission as they cannot be modified. }}, }} for _, tcase := range tcases { - stmt, err := sqlparser.NewTestParser().Parse(tcase.input) - if err != nil { - t.Fatal(err) - } - got := BuildPermissions(stmt) - if !reflect.DeepEqual(got, tcase.output) { - t.Errorf("BuildPermissions(%s): %v, want %v", tcase.input, got, tcase.output) - } + t.Run(tcase.input, func(t *testing.T) { + stmt, err := sqlparser.NewTestParser().Parse(tcase.input) + if err != nil { + t.Fatal(err) + } + got := BuildPermissions(stmt) + utils.MustMatch(t, tcase.output, got) + }) } } From ef565e678d9c5c44c5ffba8174904de55b7e9185 Mon Sep 17 00:00:00 2001 From: Harshit Gangal Date: Tue, 30 Jan 2024 23:45:06 +0530 Subject: [PATCH 4/4] addressed todo Signed-off-by: Harshit Gangal --- go/vt/vtgate/planbuilder/delete.go | 3 +-- go/vt/vtgate/planbuilder/testdata/unsupported_cases.json | 8 ++++---- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/go/vt/vtgate/planbuilder/delete.go b/go/vt/vtgate/planbuilder/delete.go index 98ea4e7d122..3fb4937ff97 100644 --- a/go/vt/vtgate/planbuilder/delete.go +++ b/go/vt/vtgate/planbuilder/delete.go @@ -140,9 +140,8 @@ func checkIfDeleteSupported(del *sqlparser.Delete, semTable *semantics.SemTable) } // Delete is only supported for single Target. - // TODO: Fix error message based on whether sharded keyspace or foreign keys. if len(del.Targets) > 1 { - return vterrors.VT12001("multi-table DELETE statement in a sharded keyspace") + return vterrors.VT12001("multi-table DELETE statement with multi-target") } err := sqlparser.Walk(func(node sqlparser.SQLNode) (kontinue bool, err error) { diff --git a/go/vt/vtgate/planbuilder/testdata/unsupported_cases.json b/go/vt/vtgate/planbuilder/testdata/unsupported_cases.json index 54507d94145..979b50c3d3d 100644 --- a/go/vt/vtgate/planbuilder/testdata/unsupported_cases.json +++ b/go/vt/vtgate/planbuilder/testdata/unsupported_cases.json @@ -147,7 +147,7 @@ { "comment": "delete with multi-table targets", "query": "delete music,user from music inner join user where music.id = user.id", - "plan": "VT12001: unsupported: multi-table DELETE statement in a sharded keyspace" + "plan": "VT12001: unsupported: multi-table DELETE statement with multi-target" }, { "comment": "select get_lock with non-dual table", @@ -382,17 +382,17 @@ { "comment": "multi table delete with 2 sharded tables join on vindex column", "query": "delete u, m from user u join music m on u.id = m.user_id", - "plan": "VT12001: unsupported: multi-table DELETE statement in a sharded keyspace" + "plan": "VT12001: unsupported: multi-table DELETE statement with multi-target" }, { "comment": "multi table delete with 2 sharded tables join on non-vindex column", "query": "delete u, m from user u join music m on u.col = m.col", - "plan": "VT12001: unsupported: multi-table DELETE statement in a sharded keyspace" + "plan": "VT12001: unsupported: multi-table DELETE statement with multi-target" }, { "comment": "multi table delete with 1 sharded and 1 reference table", "query": "delete u, r from user u join ref_with_source r on u.col = r.col", - "plan": "VT12001: unsupported: multi-table DELETE statement in a sharded keyspace" + "plan": "VT12001: unsupported: multi-table DELETE statement with multi-target" }, { "comment": "reference table delete with join",