diff --git a/changelog/20.0/20.0.0/summary.md b/changelog/20.0/20.0.0/summary.md
index b30ec93d400..54179429bd5 100644
--- a/changelog/20.0/20.0.0/summary.md
+++ b/changelog/20.0/20.0.0/summary.md
@@ -7,6 +7,7 @@
- [Vindex Hints](#vindex-hints)
- [Update with Limit Support](#update-limit)
- [Update with Multi Table Support](#multi-table-update)
+ - [Delete with Subquery Support](#delete-subquery)
- **[Flag changes](#flag-changes)**
- [`pprof-http` default change](#pprof-http-default)
- **[Minor Changes](#minor-changes)**
@@ -45,6 +46,13 @@ Example: `update t1 join t2 on t1.id = t2.id join t3 on t1.col = t3.col set t1.b
More details about how it works is available in [MySQL Docs](https://dev.mysql.com/doc/refman/8.0/en/update.html)
+#### Delete with Subquery Support
+
+Support is added for sharded table delete with subquery
+
+Example: `delete from t1 where id in (select col from t2 where foo = 32 and bar = 43)`
+
+
### Flag Changes
#### `pprof-http` Default Change
diff --git a/go/test/endtoend/vtgate/queries/dml/dml_test.go b/go/test/endtoend/vtgate/queries/dml/dml_test.go
index f23320a3fe9..561d73f44d5 100644
--- a/go/test/endtoend/vtgate/queries/dml/dml_test.go
+++ b/go/test/endtoend/vtgate/queries/dml/dml_test.go
@@ -204,3 +204,92 @@ func TestUpdateWithLimit(t *testing.T) {
require.EqualValues(t, 0, qr.RowsAffected)
}
+
+// TestMultiTableUpdate executed multi-table update queries
+func TestMultiTableUpdate(t *testing.T) {
+ utils.SkipIfBinaryIsBelowVersion(t, 20, "vtgate")
+
+ mcmp, closer := start(t)
+ defer closer()
+
+ // initial rows
+ mcmp.Exec("insert into order_tbl(region_id, oid, cust_no) values (1,1,4), (1,2,2), (2,3,5), (2,4,55)")
+ mcmp.Exec("insert into oevent_tbl(oid, ename) values (1,'a'), (2,'b'), (3,'a'), (4,'c')")
+
+ // check rows
+ mcmp.AssertMatches(`select region_id, oid, cust_no from order_tbl order by oid`,
+ `[[INT64(1) INT64(1) INT64(4)] [INT64(1) INT64(2) INT64(2)] [INT64(2) INT64(3) INT64(5)] [INT64(2) INT64(4) INT64(55)]]`)
+ mcmp.AssertMatches(`select oid, ename from oevent_tbl order by oid`,
+ `[[INT64(1) VARCHAR("a")] [INT64(2) VARCHAR("b")] [INT64(3) VARCHAR("a")] [INT64(4) VARCHAR("c")]]`)
+
+ // multi table delete
+ qr := mcmp.Exec(`update order_tbl o join oevent_tbl ev on o.oid = ev.oid set ev.ename = 'a' where ev.oid > 3`)
+ assert.EqualValues(t, 1, qr.RowsAffected)
+
+ // check rows
+ mcmp.AssertMatches(`select region_id, oid, cust_no from order_tbl order by oid`,
+ `[[INT64(1) INT64(1) INT64(4)] [INT64(1) INT64(2) INT64(2)] [INT64(2) INT64(3) INT64(5)] [INT64(2) INT64(4) INT64(55)]]`)
+ mcmp.AssertMatches(`select oid, ename from oevent_tbl order by oid`,
+ `[[INT64(1) VARCHAR("a")] [INT64(2) VARCHAR("b")] [INT64(3) VARCHAR("a")] [INT64(4) VARCHAR("a")]]`)
+
+ qr = mcmp.Exec(`update order_tbl o, oevent_tbl ev set ev.ename = 'xyz' where o.cust_no = ev.oid`)
+ assert.EqualValues(t, 2, qr.RowsAffected)
+
+ // check rows
+ mcmp.AssertMatches(`select region_id, oid, cust_no from order_tbl order by oid`,
+ `[[INT64(1) INT64(1) INT64(4)] [INT64(1) INT64(2) INT64(2)] [INT64(2) INT64(3) INT64(5)] [INT64(2) INT64(4) INT64(55)]]`)
+ mcmp.AssertMatches(`select oid, ename from oevent_tbl order by oid`,
+ `[[INT64(1) VARCHAR("a")] [INT64(2) VARCHAR("xyz")] [INT64(3) VARCHAR("a")] [INT64(4) VARCHAR("xyz")]]`)
+}
+
+// TestDeleteWithSubquery executed delete queries with subqueries
+func TestDeleteWithSubquery(t *testing.T) {
+ utils.SkipIfBinaryIsBelowVersion(t, 20, "vtgate")
+
+ mcmp, closer := start(t)
+ defer closer()
+
+ // initial rows
+ mcmp.Exec("insert into s_tbl(id, num) values (1,10), (2,10), (3,10), (4,20), (5,5), (6,15), (7,17), (8,80)")
+ mcmp.Exec("insert into order_tbl(region_id, oid, cust_no) values (1,1,4), (1,2,2), (2,3,5), (2,4,55)")
+
+ // check rows
+ mcmp.AssertMatches(`select id, num from s_tbl order by id`,
+ `[[INT64(1) INT64(10)] [INT64(2) INT64(10)] [INT64(3) INT64(10)] [INT64(4) INT64(20)] [INT64(5) INT64(5)] [INT64(6) INT64(15)] [INT64(7) INT64(17)] [INT64(8) INT64(80)]]`)
+ mcmp.AssertMatches(`select region_id, oid, cust_no from order_tbl order by oid`,
+ `[[INT64(1) INT64(1) INT64(4)] [INT64(1) INT64(2) INT64(2)] [INT64(2) INT64(3) INT64(5)] [INT64(2) INT64(4) INT64(55)]]`)
+
+ // delete with subquery on s_tbl
+ qr := mcmp.Exec(`delete from s_tbl where id in (select oid from order_tbl)`)
+ require.EqualValues(t, 4, qr.RowsAffected)
+
+ // check rows
+ mcmp.AssertMatches(`select id, num from s_tbl order by id`,
+ `[[INT64(5) INT64(5)] [INT64(6) INT64(15)] [INT64(7) INT64(17)] [INT64(8) INT64(80)]]`)
+ mcmp.AssertMatches(`select region_id, oid, cust_no from order_tbl order by oid`,
+ `[[INT64(1) INT64(1) INT64(4)] [INT64(1) INT64(2) INT64(2)] [INT64(2) INT64(3) INT64(5)] [INT64(2) INT64(4) INT64(55)]]`)
+
+ // delete with subquery on order_tbl
+ qr = mcmp.Exec(`delete from order_tbl where cust_no > (select num from s_tbl where id = 7)`)
+ require.EqualValues(t, 1, qr.RowsAffected)
+
+ // check rows
+ mcmp.AssertMatches(`select id, num from s_tbl order by id`,
+ `[[INT64(5) INT64(5)] [INT64(6) INT64(15)] [INT64(7) INT64(17)] [INT64(8) INT64(80)]]`)
+ mcmp.AssertMatches(`select region_id, oid, cust_no from order_tbl order by oid`,
+ `[[INT64(1) INT64(1) INT64(4)] [INT64(1) INT64(2) INT64(2)] [INT64(2) INT64(3) INT64(5)]]`)
+
+ // delete with subquery from same table (fails on mysql) - subquery get's merged so fails for vitess
+ _, err := mcmp.ExecAllowAndCompareError(`delete from s_tbl where id in (select id from s_tbl)`)
+ require.ErrorContains(t, err, "You can't specify target table 's_tbl' for update in FROM clause (errno 1093) (sqlstate HY000)")
+
+ // delete with subquery from same table (fails on mysql) - subquery not merged so passes for vitess
+ qr = utils.Exec(t, mcmp.VtConn, `delete from order_tbl where region_id in (select cust_no from order_tbl)`)
+ require.EqualValues(t, 1, qr.RowsAffected)
+
+ // check rows
+ utils.AssertMatches(t, mcmp.VtConn, `select id, num from s_tbl order by id`,
+ `[[INT64(5) INT64(5)] [INT64(6) INT64(15)] [INT64(7) INT64(17)] [INT64(8) INT64(80)]]`)
+ utils.AssertMatches(t, mcmp.VtConn, `select region_id, oid, cust_no from order_tbl order by oid`,
+ `[[INT64(1) INT64(1) INT64(4)] [INT64(1) INT64(2) INT64(2)]]`)
+}
diff --git a/go/vt/vtgate/planbuilder/delete.go b/go/vt/vtgate/planbuilder/delete.go
index 3fb4937ff97..a613c158ab7 100644
--- a/go/vt/vtgate/planbuilder/delete.go
+++ b/go/vt/vtgate/planbuilder/delete.go
@@ -144,19 +144,5 @@ func checkIfDeleteSupported(del *sqlparser.Delete, semTable *semantics.SemTable)
return vterrors.VT12001("multi-table DELETE statement with multi-target")
}
- err := sqlparser.Walk(func(node sqlparser.SQLNode) (kontinue bool, err error) {
- switch node.(type) {
- case *sqlparser.Subquery, *sqlparser.DerivedTable:
- // We have a subquery, so we must fail the planning.
- // If this subquery and the table expression were all belonging to the same unsharded keyspace,
- // we would have already created a plan for them before doing these checks.
- return false, vterrors.VT12001("subqueries in DML")
- }
- return true, nil
- }, del)
- if err != nil {
- return err
- }
-
return nil
}
diff --git a/go/vt/vtgate/planbuilder/operator_transformers.go b/go/vt/vtgate/planbuilder/operator_transformers.go
index 674849ffc61..577e585351d 100644
--- a/go/vt/vtgate/planbuilder/operator_transformers.go
+++ b/go/vt/vtgate/planbuilder/operator_transformers.go
@@ -688,6 +688,8 @@ func buildUpdateLogicalPlan(
var vindexes []*vindexes.ColumnVindex
vQuery := ""
if len(upd.ChangedVindexValues) > 0 {
+ upd.OwnedVindexQuery.From = stmt.GetFrom()
+ upd.OwnedVindexQuery.Where = stmt.Where
vQuery = sqlparser.String(upd.OwnedVindexQuery)
vindexes = upd.Target.VTable.ColumnVindexes
if upd.OwnedVindexQuery.Limit != nil && len(upd.OwnedVindexQuery.OrderBy) == 0 {
@@ -709,6 +711,8 @@ func buildDeleteLogicalPlan(ctx *plancontext.PlanningContext, rb *operators.Rout
var vindexes []*vindexes.ColumnVindex
vQuery := ""
if del.OwnedVindexQuery != nil {
+ del.OwnedVindexQuery.From = stmt.GetFrom()
+ del.OwnedVindexQuery.Where = stmt.Where
vQuery = sqlparser.String(del.OwnedVindexQuery)
vindexes = del.Target.VTable.Owned
}
diff --git a/go/vt/vtgate/planbuilder/operators/delete.go b/go/vt/vtgate/planbuilder/operators/delete.go
index 7a497d5511c..c22da14c35f 100644
--- a/go/vt/vtgate/planbuilder/operators/delete.go
+++ b/go/vt/vtgate/planbuilder/operators/delete.go
@@ -74,7 +74,15 @@ func (d *Delete) GetOrdering(*plancontext.PlanningContext) []OrderBy {
func (d *Delete) ShortDescription() string {
ovq := ""
if d.OwnedVindexQuery != nil {
- ovq = " vindexQuery:%s" + sqlparser.String(d.OwnedVindexQuery)
+ var cols, orderby, limit string
+ cols = fmt.Sprintf("COLUMNS: [%s]", sqlparser.String(d.OwnedVindexQuery.SelectExprs))
+ if len(d.OwnedVindexQuery.OrderBy) > 0 {
+ orderby = fmt.Sprintf(" ORDERBY: [%s]", sqlparser.String(d.OwnedVindexQuery.OrderBy))
+ }
+ if d.OwnedVindexQuery.Limit != nil {
+ limit = fmt.Sprintf(" LIMIT: [%s]", sqlparser.String(d.OwnedVindexQuery.Limit))
+ }
+ ovq = fmt.Sprintf(" vindexQuery(%s%s%s)", cols, orderby, limit)
}
return fmt.Sprintf("%s.%s%s", d.Target.VTable.Keyspace.Name, d.Target.VTable.Name.String(), ovq)
}
@@ -90,8 +98,8 @@ func createOperatorFromDelete(ctx *plancontext.PlanningContext, deleteStmt *sqlp
}
delClone := sqlparser.CloneRefOfDelete(deleteStmt)
- delOp := createDeleteOperator(ctx, deleteStmt)
- op = delOp
+ var vTbl *vindexes.Table
+ op, vTbl = createDeleteOperator(ctx, deleteStmt)
if deleteStmt.Comments != nil {
op = &LockAndComment{
@@ -105,7 +113,7 @@ func createOperatorFromDelete(ctx *plancontext.PlanningContext, deleteStmt *sqlp
return op
}
- return createFkCascadeOpForDelete(ctx, op, delClone, childFks, delOp.Target.VTable)
+ return createFkCascadeOpForDelete(ctx, op, delClone, childFks, vTbl)
}
func deleteWithInputPlanningRequired(childFks []vindexes.ChildFKInfo, deleteStmt *sqlparser.Delete) bool {
@@ -170,11 +178,12 @@ func deleteWithInputPlanningForFk(ctx *plancontext.PlanningContext, del *sqlpars
}
}
-func createDeleteOperator(ctx *plancontext.PlanningContext, del *sqlparser.Delete) *Delete {
+func createDeleteOperator(ctx *plancontext.PlanningContext, del *sqlparser.Delete) (Operator, *vindexes.Table) {
op := crossJoin(ctx, del.TableExprs)
+ sqc := &SubQueryBuilder{}
if del.Where != nil {
- op = addWherePredicates(ctx, del.Where.Expr, op)
+ op = addWherePredsToSubQueryBuilder(ctx, del.Where.Expr, op, sqc)
}
target := del.Targets[0]
@@ -207,9 +216,8 @@ func createDeleteOperator(ctx *plancontext.PlanningContext, del *sqlparser.Delet
var ovq *sqlparser.Select
if vTbl.Keyspace.Sharded && vTbl.Type == vindexes.TypeTable {
primaryVindex, _ := getVindexInformation(tblID, vTbl)
- ate := tblInfo.GetAliasedTableExpr()
if len(vTbl.Owned) > 0 {
- ovq = generateOwnedVindexQuery(ate, del, targetTbl, primaryVindex.Columns)
+ ovq = generateOwnedVindexQuery(del, targetTbl, primaryVindex.Columns)
}
}
@@ -222,21 +230,18 @@ func createDeleteOperator(ctx *plancontext.PlanningContext, del *sqlparser.Delet
},
}
- if del.Limit == nil {
- return delOp
- }
-
- addOrdering(ctx, del.OrderBy, delOp)
-
- delOp.Source = &Limit{
- Source: delOp.Source,
- AST: del.Limit,
+ if del.Limit != nil {
+ addOrdering(ctx, del.OrderBy, delOp)
+ delOp.Source = &Limit{
+ Source: delOp.Source,
+ AST: del.Limit,
+ }
}
- return delOp
+ return sqc.getRootOperator(delOp, nil), vTbl
}
-func generateOwnedVindexQuery(tblExpr sqlparser.TableExpr, del *sqlparser.Delete, table TargetTable, ksidCols []sqlparser.IdentifierCI) *sqlparser.Select {
+func generateOwnedVindexQuery(del *sqlparser.Delete, table TargetTable, ksidCols []sqlparser.IdentifierCI) *sqlparser.Select {
var selExprs sqlparser.SelectExprs
for _, col := range ksidCols {
colName := makeColName(col, table, sqlparser.MultiTable(del.TableExprs))
@@ -248,11 +253,8 @@ func generateOwnedVindexQuery(tblExpr sqlparser.TableExpr, del *sqlparser.Delete
selExprs = append(selExprs, aeWrap(colName))
}
}
- sqlparser.RemoveKeyspaceInTables(tblExpr)
return &sqlparser.Select{
SelectExprs: selExprs,
- From: del.TableExprs,
- Where: del.Where,
OrderBy: del.OrderBy,
Limit: del.Limit,
Lock: sqlparser.ForUpdateLock,
diff --git a/go/vt/vtgate/planbuilder/operators/dml_planning.go b/go/vt/vtgate/planbuilder/operators/dml_planning.go
index ed777428381..6f71b41162e 100644
--- a/go/vt/vtgate/planbuilder/operators/dml_planning.go
+++ b/go/vt/vtgate/planbuilder/operators/dml_planning.go
@@ -109,7 +109,6 @@ func buildChangedVindexesValues(
ctx *plancontext.PlanningContext,
update *sqlparser.Update,
table *vindexes.Table,
- ate *sqlparser.AliasedTableExpr,
ksidCols []sqlparser.IdentifierCI,
assignments []SetExpr,
) (vv map[string]*engine.VindexValues, ownedVindexQuery *sqlparser.Select, subQueriesArgOnChangedVindex []string) {
@@ -145,11 +144,8 @@ func buildChangedVindexesValues(
return nil, nil, nil
}
// generate rest of the owned vindex query.
- tblExpr := sqlparser.NewAliasedTableExpr(sqlparser.TableName{Name: table.Name}, ate.As.String())
ovq := &sqlparser.Select{
- From: []sqlparser.TableExpr{tblExpr},
SelectExprs: selExprs,
- Where: update.Where,
OrderBy: update.OrderBy,
Limit: update.Limit,
Lock: sqlparser.ForUpdateLock,
diff --git a/go/vt/vtgate/planbuilder/operators/update.go b/go/vt/vtgate/planbuilder/operators/update.go
index 5fc7dc0d4a7..cf55f91fddd 100644
--- a/go/vt/vtgate/planbuilder/operators/update.go
+++ b/go/vt/vtgate/planbuilder/operators/update.go
@@ -97,7 +97,15 @@ func (u *Update) TablesUsed() []string {
func (u *Update) ShortDescription() string {
ovq := ""
if u.OwnedVindexQuery != nil {
- ovq = " vindexQuery:%s" + sqlparser.String(u.OwnedVindexQuery)
+ var cols, orderby, limit string
+ cols = fmt.Sprintf("COLUMNS: [%s]", sqlparser.String(u.OwnedVindexQuery.SelectExprs))
+ if len(u.OwnedVindexQuery.OrderBy) > 0 {
+ orderby = fmt.Sprintf(" ORDERBY: [%s]", sqlparser.String(u.OwnedVindexQuery.OrderBy))
+ }
+ if u.OwnedVindexQuery.Limit != nil {
+ limit = fmt.Sprintf(" LIMIT: [%s]", sqlparser.String(u.OwnedVindexQuery.Limit))
+ }
+ ovq = fmt.Sprintf(" vindexQuery(%s%s%s)", cols, orderby, limit)
}
return fmt.Sprintf("%s.%s%s", u.Target.VTable.Keyspace.Name, u.Target.VTable.Name.String(), ovq)
}
@@ -217,7 +225,7 @@ func createUpdateOperator(ctx *plancontext.PlanningContext, updStmt *sqlparser.U
Name: name,
}
- _, cvv, ovq, subQueriesArgOnChangedVindex := getUpdateVindexInformation(ctx, updStmt, targetTbl, tblInfo.GetAliasedTableExpr(), assignments)
+ _, cvv, ovq, subQueriesArgOnChangedVindex := getUpdateVindexInformation(ctx, updStmt, targetTbl, assignments)
updOp := &Update{
DMLCommon: &DMLCommon{
@@ -249,7 +257,6 @@ func getUpdateVindexInformation(
ctx *plancontext.PlanningContext,
updStmt *sqlparser.Update,
table TargetTable,
- ate *sqlparser.AliasedTableExpr,
assignments []SetExpr,
) ([]*VindexPlusPredicates, map[string]*engine.VindexValues, *sqlparser.Select, []string) {
if !table.VTable.Keyspace.Sharded {
@@ -257,7 +264,7 @@ func getUpdateVindexInformation(
}
primaryVindex, vindexAndPredicates := getVindexInformation(table.ID, table.VTable)
- changedVindexValues, ownedVindexQuery, subQueriesArgOnChangedVindex := buildChangedVindexesValues(ctx, updStmt, table.VTable, ate, primaryVindex.Columns, assignments)
+ changedVindexValues, ownedVindexQuery, subQueriesArgOnChangedVindex := buildChangedVindexesValues(ctx, updStmt, table.VTable, primaryVindex.Columns, assignments)
return vindexAndPredicates, changedVindexValues, ownedVindexQuery, subQueriesArgOnChangedVindex
}
diff --git a/go/vt/vtgate/planbuilder/plan_test.go b/go/vt/vtgate/planbuilder/plan_test.go
index 8d339eb52ee..e7c18af1d4c 100644
--- a/go/vt/vtgate/planbuilder/plan_test.go
+++ b/go/vt/vtgate/planbuilder/plan_test.go
@@ -279,6 +279,7 @@ func TestOne(t *testing.T) {
lv := loadSchema(t, "vschemas/schema.json", true)
setFks(t, lv)
addPKs(t, lv, "user", []string{"user", "music"})
+ addPKs(t, lv, "main", []string{"unsharded"})
vschema := &vschemawrapper.VSchemaWrapper{
V: lv,
TestBuilder: TestBuilder,
diff --git a/go/vt/vtgate/planbuilder/testdata/dml_cases.json b/go/vt/vtgate/planbuilder/testdata/dml_cases.json
index 18ba14db06e..6fbc31eb84d 100644
--- a/go/vt/vtgate/planbuilder/testdata/dml_cases.json
+++ b/go/vt/vtgate/planbuilder/testdata/dml_cases.json
@@ -5012,7 +5012,7 @@
"TargetTabletType": "PRIMARY",
"KsidLength": 1,
"KsidVindex": "user_index",
- "OwnedVindexQuery": "select u.Id, u.`Name`, u.Costly from `user` as u join ref_with_source as r on u.col = r.col for update",
+ "OwnedVindexQuery": "select u.Id, u.`Name`, u.Costly from `user` as u, ref_with_source as r where u.col = r.col for update",
"Query": "delete u from `user` as u, ref_with_source as r where u.col = r.col",
"Table": "user"
},
@@ -5038,7 +5038,7 @@
"TargetTabletType": "PRIMARY",
"KsidLength": 1,
"KsidVindex": "user_index",
- "OwnedVindexQuery": "select u.Id, u.`Name`, u.Costly from `user` as u join music as m on u.id = m.user_id for update",
+ "OwnedVindexQuery": "select u.Id, u.`Name`, u.Costly from `user` as u, music as m where u.id = m.user_id for update",
"Query": "delete u from `user` as u, music as m where u.id = m.user_id",
"Table": "user"
},
@@ -5284,7 +5284,7 @@
"TargetTabletType": "PRIMARY",
"KsidLength": 1,
"KsidVindex": "user_index",
- "OwnedVindexQuery": "select u.Id, u.`Name`, u.Costly from `user` as u join music as m where u.id = m.user_id and m.foo = 42 for update",
+ "OwnedVindexQuery": "select u.Id, u.`Name`, u.Costly from `user` as u, music as m where m.foo = 42 and u.id = m.user_id for update",
"Query": "delete u from `user` as u, music as m where m.foo = 42 and u.id = m.user_id",
"Table": "user"
},
@@ -5901,5 +5901,292 @@
"user.music"
]
}
+ },
+ {
+ "comment": "sharded subquery in sharded delete",
+ "query": "delete from user where id = (select id from music where user_id = 1)",
+ "plan": {
+ "QueryType": "DELETE",
+ "Original": "delete from user where id = (select id from music where user_id = 1)",
+ "Instructions": {
+ "OperatorType": "UncorrelatedSubquery",
+ "Variant": "PulloutValue",
+ "PulloutVars": [
+ "__sq1"
+ ],
+ "Inputs": [
+ {
+ "InputName": "SubQuery",
+ "OperatorType": "Route",
+ "Variant": "EqualUnique",
+ "Keyspace": {
+ "Name": "user",
+ "Sharded": true
+ },
+ "FieldQuery": "select id from music where 1 != 1",
+ "Query": "select id from music where user_id = 1",
+ "Table": "music",
+ "Values": [
+ "1"
+ ],
+ "Vindex": "user_index"
+ },
+ {
+ "InputName": "Outer",
+ "OperatorType": "Delete",
+ "Variant": "EqualUnique",
+ "Keyspace": {
+ "Name": "user",
+ "Sharded": true
+ },
+ "TargetTabletType": "PRIMARY",
+ "KsidLength": 1,
+ "KsidVindex": "user_index",
+ "OwnedVindexQuery": "select Id, `Name`, Costly from `user` where id = :__sq1 for update",
+ "Query": "delete from `user` where id = :__sq1",
+ "Table": "user",
+ "Values": [
+ ":__sq1"
+ ],
+ "Vindex": "user_index"
+ }
+ ]
+ },
+ "TablesUsed": [
+ "user.music",
+ "user.user"
+ ]
+ }
+ },
+ {
+ "comment": "unsharded subquery in sharded delete",
+ "query": "delete from user where col = (select id from unsharded)",
+ "plan": {
+ "QueryType": "DELETE",
+ "Original": "delete from user where col = (select id from unsharded)",
+ "Instructions": {
+ "OperatorType": "UncorrelatedSubquery",
+ "Variant": "PulloutValue",
+ "PulloutVars": [
+ "__sq1"
+ ],
+ "Inputs": [
+ {
+ "InputName": "SubQuery",
+ "OperatorType": "Route",
+ "Variant": "Unsharded",
+ "Keyspace": {
+ "Name": "main",
+ "Sharded": false
+ },
+ "FieldQuery": "select id from unsharded where 1 != 1",
+ "Query": "select id from unsharded",
+ "Table": "unsharded"
+ },
+ {
+ "InputName": "Outer",
+ "OperatorType": "Delete",
+ "Variant": "Scatter",
+ "Keyspace": {
+ "Name": "user",
+ "Sharded": true
+ },
+ "TargetTabletType": "PRIMARY",
+ "KsidLength": 1,
+ "KsidVindex": "user_index",
+ "OwnedVindexQuery": "select Id, `Name`, Costly from `user` where col = :__sq1 for update",
+ "Query": "delete from `user` where col = :__sq1",
+ "Table": "user"
+ }
+ ]
+ },
+ "TablesUsed": [
+ "main.unsharded",
+ "user.user"
+ ]
+ }
+ },
+ {
+ "comment": "sharded subquery in unsharded delete",
+ "query": "delete from unsharded where col = (select id from user)",
+ "plan": {
+ "QueryType": "DELETE",
+ "Original": "delete from unsharded where col = (select id from user)",
+ "Instructions": {
+ "OperatorType": "UncorrelatedSubquery",
+ "Variant": "PulloutValue",
+ "PulloutVars": [
+ "__sq1"
+ ],
+ "Inputs": [
+ {
+ "InputName": "SubQuery",
+ "OperatorType": "Route",
+ "Variant": "Scatter",
+ "Keyspace": {
+ "Name": "user",
+ "Sharded": true
+ },
+ "FieldQuery": "select id from `user` where 1 != 1",
+ "Query": "select id from `user`",
+ "Table": "`user`"
+ },
+ {
+ "InputName": "Outer",
+ "OperatorType": "Delete",
+ "Variant": "Unsharded",
+ "Keyspace": {
+ "Name": "main",
+ "Sharded": false
+ },
+ "TargetTabletType": "PRIMARY",
+ "Query": "delete from unsharded where col = :__sq1",
+ "Table": "unsharded"
+ }
+ ]
+ },
+ "TablesUsed": [
+ "main.unsharded",
+ "user.user"
+ ]
+ }
+ },
+ {
+ "comment": "sharded subquery in unsharded subquery in unsharded delete",
+ "query": "delete from unsharded where col = (select id from unsharded where id = (select id from user))",
+ "plan": {
+ "QueryType": "DELETE",
+ "Original": "delete from unsharded where col = (select id from unsharded where id = (select id from user))",
+ "Instructions": {
+ "OperatorType": "UncorrelatedSubquery",
+ "Variant": "PulloutValue",
+ "PulloutVars": [
+ "__sq1"
+ ],
+ "Inputs": [
+ {
+ "InputName": "SubQuery",
+ "OperatorType": "UncorrelatedSubquery",
+ "Variant": "PulloutValue",
+ "PulloutVars": [
+ "__sq2"
+ ],
+ "Inputs": [
+ {
+ "InputName": "SubQuery",
+ "OperatorType": "Route",
+ "Variant": "Scatter",
+ "Keyspace": {
+ "Name": "user",
+ "Sharded": true
+ },
+ "FieldQuery": "select id from `user` where 1 != 1",
+ "Query": "select id from `user`",
+ "Table": "`user`"
+ },
+ {
+ "InputName": "Outer",
+ "OperatorType": "Route",
+ "Variant": "Unsharded",
+ "Keyspace": {
+ "Name": "main",
+ "Sharded": false
+ },
+ "FieldQuery": "select id from unsharded where 1 != 1",
+ "Query": "select id from unsharded where id = :__sq2",
+ "Table": "unsharded"
+ }
+ ]
+ },
+ {
+ "InputName": "Outer",
+ "OperatorType": "Delete",
+ "Variant": "Unsharded",
+ "Keyspace": {
+ "Name": "main",
+ "Sharded": false
+ },
+ "TargetTabletType": "PRIMARY",
+ "Query": "delete from unsharded where col = :__sq1",
+ "Table": "unsharded"
+ }
+ ]
+ },
+ "TablesUsed": [
+ "main.unsharded",
+ "user.user"
+ ]
+ }
+ },
+ {
+ "comment": "sharded join unsharded subquery in unsharded delete",
+ "query": "delete from unsharded where col = (select id from unsharded join user on unsharded.id = user.id)",
+ "plan": {
+ "QueryType": "DELETE",
+ "Original": "delete from unsharded where col = (select id from unsharded join user on unsharded.id = user.id)",
+ "Instructions": {
+ "OperatorType": "UncorrelatedSubquery",
+ "Variant": "PulloutValue",
+ "PulloutVars": [
+ "__sq1"
+ ],
+ "Inputs": [
+ {
+ "InputName": "SubQuery",
+ "OperatorType": "Join",
+ "Variant": "Join",
+ "JoinColumnIndexes": "R:0",
+ "JoinVars": {
+ "unsharded_id": 0
+ },
+ "TableName": "unsharded_`user`",
+ "Inputs": [
+ {
+ "OperatorType": "Route",
+ "Variant": "Unsharded",
+ "Keyspace": {
+ "Name": "main",
+ "Sharded": false
+ },
+ "FieldQuery": "select unsharded.id from unsharded where 1 != 1",
+ "Query": "select unsharded.id from unsharded",
+ "Table": "unsharded"
+ },
+ {
+ "OperatorType": "Route",
+ "Variant": "EqualUnique",
+ "Keyspace": {
+ "Name": "user",
+ "Sharded": true
+ },
+ "FieldQuery": "select id from `user` where 1 != 1",
+ "Query": "select id from `user` where `user`.id = :unsharded_id",
+ "Table": "`user`",
+ "Values": [
+ ":unsharded_id"
+ ],
+ "Vindex": "user_index"
+ }
+ ]
+ },
+ {
+ "InputName": "Outer",
+ "OperatorType": "Delete",
+ "Variant": "Unsharded",
+ "Keyspace": {
+ "Name": "main",
+ "Sharded": false
+ },
+ "TargetTabletType": "PRIMARY",
+ "Query": "delete from unsharded where col = :__sq1",
+ "Table": "unsharded"
+ }
+ ]
+ },
+ "TablesUsed": [
+ "main.unsharded",
+ "user.user"
+ ]
+ }
}
]
diff --git a/go/vt/vtgate/planbuilder/testdata/unsupported_cases.json b/go/vt/vtgate/planbuilder/testdata/unsupported_cases.json
index 4214a396499..5c8bcad0c57 100644
--- a/go/vt/vtgate/planbuilder/testdata/unsupported_cases.json
+++ b/go/vt/vtgate/planbuilder/testdata/unsupported_cases.json
@@ -19,26 +19,6 @@
"query": "select id from user group by id, (select id from user_extra)",
"plan": "VT12001: unsupported: subqueries in GROUP BY"
},
- {
- "comment": "subqueries in delete",
- "query": "delete from user where col = (select id from unsharded)",
- "plan": "VT12001: unsupported: subqueries in DML"
- },
- {
- "comment": "sharded subqueries in unsharded delete",
- "query": "delete from unsharded where col = (select id from user)",
- "plan": "VT12001: unsupported: subqueries in DML"
- },
- {
- "comment": "sharded subquery in unsharded subquery in unsharded delete",
- "query": "delete from unsharded where col = (select id from unsharded where id = (select id from user))",
- "plan": "VT12001: unsupported: subqueries in DML"
- },
- {
- "comment": "sharded join unsharded subqueries in unsharded delete",
- "query": "delete from unsharded where col = (select id from unsharded join user on unsharded.id = user.id)",
- "plan": "VT12001: unsupported: subqueries in DML"
- },
{
"comment": "update changes primary vindex column",
"query": "update user set id = 1 where id = 1",