From d5e7e4fff4b451f3242d2c44e85d5543cd9ef0a4 Mon Sep 17 00:00:00 2001 From: Andres Taylor Date: Thu, 19 Oct 2023 08:51:39 +0200 Subject: [PATCH 01/18] make field public Signed-off-by: Andres Taylor --- go/vt/sqlparser/ast.go | 2 +- go/vt/sqlparser/ast_clone.go | 2 +- go/vt/sqlparser/ast_copy_on_rewrite.go | 14 +++++++------- go/vt/sqlparser/ast_equals.go | 2 +- go/vt/sqlparser/ast_format.go | 6 +++--- go/vt/sqlparser/ast_format_fast.go | 6 +++--- go/vt/sqlparser/ast_rewrite.go | 4 ++-- go/vt/sqlparser/ast_visit.go | 2 +- go/vt/sqlparser/cached_size.go | 6 +++--- go/vt/sqlparser/sql.go | 4 ++-- go/vt/sqlparser/sql.y | 4 ++-- 11 files changed, 26 insertions(+), 26 deletions(-) diff --git a/go/vt/sqlparser/ast.go b/go/vt/sqlparser/ast.go index c167c9971c8..e5c41aca17c 100644 --- a/go/vt/sqlparser/ast.go +++ b/go/vt/sqlparser/ast.go @@ -148,7 +148,7 @@ type ( // With contains the lists of common table expression and specifies if it is recursive or not With struct { - ctes []*CommonTableExpr + CTEs []*CommonTableExpr Recursive bool } diff --git a/go/vt/sqlparser/ast_clone.go b/go/vt/sqlparser/ast_clone.go index 2921d43ec2b..dd5f939a2fc 100644 --- a/go/vt/sqlparser/ast_clone.go +++ b/go/vt/sqlparser/ast_clone.go @@ -3395,7 +3395,7 @@ func CloneRefOfWith(n *With) *With { return nil } out := *n - out.ctes = CloneSliceOfRefOfCommonTableExpr(n.ctes) + out.CTEs = CloneSliceOfRefOfCommonTableExpr(n.CTEs) return &out } diff --git a/go/vt/sqlparser/ast_copy_on_rewrite.go b/go/vt/sqlparser/ast_copy_on_rewrite.go index 8933648f772..64855669c50 100644 --- a/go/vt/sqlparser/ast_copy_on_rewrite.go +++ b/go/vt/sqlparser/ast_copy_on_rewrite.go @@ -6570,18 +6570,18 @@ func (c *cow) copyOnRewriteRefOfWith(n *With, parent SQLNode) (out SQLNode, chan } out = n if c.pre == nil || c.pre(n, parent) { - var changedctes bool - _ctes := make([]*CommonTableExpr, len(n.ctes)) - for x, el := range n.ctes { + var changedCTEs bool + _CTEs := make([]*CommonTableExpr, len(n.CTEs)) + for x, el := range n.CTEs { this, changed := c.copyOnRewriteRefOfCommonTableExpr(el, n) - _ctes[x] = this.(*CommonTableExpr) + _CTEs[x] = this.(*CommonTableExpr) if changed { - changedctes = true + changedCTEs = true } } - if changedctes { + if changedCTEs { res := *n - res.ctes = _ctes + res.CTEs = _CTEs out = &res if c.cloned != nil { c.cloned(n, out) diff --git a/go/vt/sqlparser/ast_equals.go b/go/vt/sqlparser/ast_equals.go index 7348d204c59..5a5f50d5338 100644 --- a/go/vt/sqlparser/ast_equals.go +++ b/go/vt/sqlparser/ast_equals.go @@ -4901,7 +4901,7 @@ func (cmp *Comparator) RefOfWith(a, b *With) bool { return false } return a.Recursive == b.Recursive && - cmp.SliceOfRefOfCommonTableExpr(a.ctes, b.ctes) + cmp.SliceOfRefOfCommonTableExpr(a.CTEs, b.CTEs) } // RefOfXorExpr does deep equals between the two objects. diff --git a/go/vt/sqlparser/ast_format.go b/go/vt/sqlparser/ast_format.go index fed519ec58b..1f82d4e3357 100644 --- a/go/vt/sqlparser/ast_format.go +++ b/go/vt/sqlparser/ast_format.go @@ -138,11 +138,11 @@ func (node *With) Format(buf *TrackedBuffer) { if node.Recursive { buf.astPrintf(node, "recursive ") } - ctesLength := len(node.ctes) + ctesLength := len(node.CTEs) for i := 0; i < ctesLength-1; i++ { - buf.astPrintf(node, "%v, ", node.ctes[i]) + buf.astPrintf(node, "%v, ", node.CTEs[i]) } - buf.astPrintf(node, "%v", node.ctes[ctesLength-1]) + buf.astPrintf(node, "%v", node.CTEs[ctesLength-1]) } // Format formats the node. diff --git a/go/vt/sqlparser/ast_format_fast.go b/go/vt/sqlparser/ast_format_fast.go index 70baca7a46b..378d545865a 100644 --- a/go/vt/sqlparser/ast_format_fast.go +++ b/go/vt/sqlparser/ast_format_fast.go @@ -206,12 +206,12 @@ func (node *With) formatFast(buf *TrackedBuffer) { if node.Recursive { buf.WriteString("recursive ") } - ctesLength := len(node.ctes) + ctesLength := len(node.CTEs) for i := 0; i < ctesLength-1; i++ { - node.ctes[i].formatFast(buf) + node.CTEs[i].formatFast(buf) buf.WriteString(", ") } - node.ctes[ctesLength-1].formatFast(buf) + node.CTEs[ctesLength-1].formatFast(buf) } // formatFast formats the node. diff --git a/go/vt/sqlparser/ast_rewrite.go b/go/vt/sqlparser/ast_rewrite.go index be35b8e0f78..09ff28cba31 100644 --- a/go/vt/sqlparser/ast_rewrite.go +++ b/go/vt/sqlparser/ast_rewrite.go @@ -9474,10 +9474,10 @@ func (a *application) rewriteRefOfWith(parent SQLNode, node *With, replacer repl return true } } - for x, el := range node.ctes { + for x, el := range node.CTEs { if !a.rewriteRefOfCommonTableExpr(node, el, func(idx int) replacerFunc { return func(newNode, parent SQLNode) { - parent.(*With).ctes[idx] = newNode.(*CommonTableExpr) + parent.(*With).CTEs[idx] = newNode.(*CommonTableExpr) } }(x)) { return false diff --git a/go/vt/sqlparser/ast_visit.go b/go/vt/sqlparser/ast_visit.go index e701899628e..802b05ba3ee 100644 --- a/go/vt/sqlparser/ast_visit.go +++ b/go/vt/sqlparser/ast_visit.go @@ -4354,7 +4354,7 @@ func VisitRefOfWith(in *With, f Visit) error { if cont, err := f(in); err != nil || !cont { return err } - for _, el := range in.ctes { + for _, el := range in.CTEs { if err := VisitRefOfCommonTableExpr(el, f); err != nil { return err } diff --git a/go/vt/sqlparser/cached_size.go b/go/vt/sqlparser/cached_size.go index 8459b63a2c9..d888ac5c747 100644 --- a/go/vt/sqlparser/cached_size.go +++ b/go/vt/sqlparser/cached_size.go @@ -4554,10 +4554,10 @@ func (cached *With) CachedSize(alloc bool) int64 { if alloc { size += int64(32) } - // field ctes []*vitess.io/vitess/go/vt/sqlparser.CommonTableExpr + // field CTEs []*vitess.io/vitess/go/vt/sqlparser.CommonTableExpr { - size += hack.RuntimeAllocSize(int64(cap(cached.ctes)) * int64(8)) - for _, elem := range cached.ctes { + size += hack.RuntimeAllocSize(int64(cap(cached.CTEs)) * int64(8)) + for _, elem := range cached.CTEs { size += elem.CachedSize(true) } } diff --git a/go/vt/sqlparser/sql.go b/go/vt/sqlparser/sql.go index 3407d718287..ffa4e05703c 100644 --- a/go/vt/sqlparser/sql.go +++ b/go/vt/sqlparser/sql.go @@ -10177,7 +10177,7 @@ yydefault: var yyLOCAL *With //line sql.y:705 { - yyLOCAL = &With{ctes: yyDollar[2].ctesUnion(), Recursive: false} + yyLOCAL = &With{CTEs: yyDollar[2].ctesUnion(), Recursive: false} } yyVAL.union = yyLOCAL case 50: @@ -10185,7 +10185,7 @@ yydefault: var yyLOCAL *With //line sql.y:709 { - yyLOCAL = &With{ctes: yyDollar[3].ctesUnion(), Recursive: true} + yyLOCAL = &With{CTEs: yyDollar[3].ctesUnion(), Recursive: true} } yyVAL.union = yyLOCAL case 51: diff --git a/go/vt/sqlparser/sql.y b/go/vt/sqlparser/sql.y index 2f416f971ce..394dbe7abaa 100644 --- a/go/vt/sqlparser/sql.y +++ b/go/vt/sqlparser/sql.y @@ -703,11 +703,11 @@ load_statement: with_clause: WITH with_list { - $$ = &With{ctes: $2, Recursive: false} + $$ = &With{CTEs: $2, Recursive: false} } | WITH RECURSIVE with_list { - $$ = &With{ctes: $3, Recursive: true} + $$ = &With{CTEs: $3, Recursive: true} } with_clause_opt: From 148ebbf24639681c3e4cb3e5d2468cc456dc98cb Mon Sep 17 00:00:00 2001 From: Andres Taylor Date: Thu, 19 Oct 2023 09:09:10 +0200 Subject: [PATCH 02/18] feat: stop recursive CTEs in the semantic analaysis Signed-off-by: Andres Taylor --- go/vt/vtgate/semantics/analyzer_test.go | 3 +++ go/vt/vtgate/semantics/check_invalid.go | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/go/vt/vtgate/semantics/analyzer_test.go b/go/vt/vtgate/semantics/analyzer_test.go index dfcc143d073..818fc65f3e5 100644 --- a/go/vt/vtgate/semantics/analyzer_test.go +++ b/go/vt/vtgate/semantics/analyzer_test.go @@ -835,6 +835,9 @@ func TestInvalidQueries(t *testing.T) { }, { sql: "select 1 from t1 where (id, id) in (select 1, 2, 3)", serr: "Operand should contain 2 column(s)", + }, { + sql: "WITH RECURSIVE cte (n) AS (SELECT 1 UNION ALL SELECT n + 1 FROM cte WHERE n < 5) SELECT * FROM cte", + serr: "VT12001: unsupported: recursive common table expression", }} for _, tc := range tcases { diff --git a/go/vt/vtgate/semantics/check_invalid.go b/go/vt/vtgate/semantics/check_invalid.go index 0cc7f9c15b2..54b2de5c36f 100644 --- a/go/vt/vtgate/semantics/check_invalid.go +++ b/go/vt/vtgate/semantics/check_invalid.go @@ -44,6 +44,10 @@ func (a *analyzer) checkForInvalidConstructs(cursor *sqlparser.Cursor) error { return vterrors.VT12001("Assignment expression") case *sqlparser.Subquery: return a.checkSubqueryColumns(cursor.Parent(), node) + case *sqlparser.With: + if node.Recursive { + return vterrors.VT12001("recursive common table expression") + } case *sqlparser.Insert: if node.Action == sqlparser.ReplaceAct { return ShardedError{Inner: &UnsupportedConstruct{errString: "REPLACE INTO with sharded keyspace"}} From d5ed0957233d7aecee60e2550f32c4cc25ecf68f Mon Sep 17 00:00:00 2001 From: Andres Taylor Date: Thu, 19 Oct 2023 12:47:53 +0200 Subject: [PATCH 03/18] stop using table qualifier for aliased tables Signed-off-by: Andres Taylor --- .../planbuilder/operators/query_planning.go | 3 +- .../planbuilder/testdata/ddl_cases.json | 2 +- .../ddl_cases_no_default_keyspace.json | 2 +- .../planbuilder/testdata/from_cases.json | 12 ++++---- .../testdata/info_schema57_cases.json | 12 ++++---- .../testdata/info_schema80_cases.json | 12 ++++---- .../planbuilder/testdata/select_cases.json | 30 +++++++++---------- .../testdata/sysschema_default.json | 4 +-- .../planbuilder/testdata/union_cases.json | 12 ++++---- go/vt/vtgate/semantics/binder.go | 21 ++++--------- go/vt/vtgate/semantics/early_rewriter.go | 3 +- go/vt/vtgate/semantics/early_rewriter_test.go | 4 +-- 12 files changed, 52 insertions(+), 65 deletions(-) diff --git a/go/vt/vtgate/planbuilder/operators/query_planning.go b/go/vt/vtgate/planbuilder/operators/query_planning.go index 8f36107c46f..e66abd02feb 100644 --- a/go/vt/vtgate/planbuilder/operators/query_planning.go +++ b/go/vt/vtgate/planbuilder/operators/query_planning.go @@ -424,8 +424,7 @@ func exposeColumnsThroughDerivedTable(ctx *plancontext.PlanningContext, p *Proje if err != nil { return err } - tblExpr := tbl.GetExpr() - tblName, err := tblExpr.TableName() + tblName, err := tbl.Name() if err != nil { return err } diff --git a/go/vt/vtgate/planbuilder/testdata/ddl_cases.json b/go/vt/vtgate/planbuilder/testdata/ddl_cases.json index eec1a0ce101..c6dad1ab946 100644 --- a/go/vt/vtgate/planbuilder/testdata/ddl_cases.json +++ b/go/vt/vtgate/planbuilder/testdata/ddl_cases.json @@ -260,7 +260,7 @@ "Name": "main", "Sharded": false }, - "Query": "create view view_a as select a.col1, a.col2 from (select col1, col2 from unsharded where id = 1 union select col1, col2 from unsharded where id = 3) as a" + "Query": "create view view_a as select col1, col2 from (select col1, col2 from unsharded where id = 1 union select col1, col2 from unsharded where id = 3) as a" }, "TablesUsed": [ "main.view_a" diff --git a/go/vt/vtgate/planbuilder/testdata/ddl_cases_no_default_keyspace.json b/go/vt/vtgate/planbuilder/testdata/ddl_cases_no_default_keyspace.json index d05631cbff5..e813b8bdbdd 100644 --- a/go/vt/vtgate/planbuilder/testdata/ddl_cases_no_default_keyspace.json +++ b/go/vt/vtgate/planbuilder/testdata/ddl_cases_no_default_keyspace.json @@ -163,7 +163,7 @@ "Name": "user", "Sharded": true }, - "Query": "create view view_a as select a.user_id, a.col1, a.col2 from authoritative as a" + "Query": "create view view_a as select user_id, col1, col2 from authoritative as a" }, "TablesUsed": [ "user.view_a" diff --git a/go/vt/vtgate/planbuilder/testdata/from_cases.json b/go/vt/vtgate/planbuilder/testdata/from_cases.json index 155a8042fe9..b6a73f4c318 100644 --- a/go/vt/vtgate/planbuilder/testdata/from_cases.json +++ b/go/vt/vtgate/planbuilder/testdata/from_cases.json @@ -3796,8 +3796,8 @@ "Name": "user", "Sharded": true }, - "FieldQuery": "select t3.push_it from (select bar as push_it from (select foo as bar from (select id as foo from `user` where 1 != 1) as t1 where 1 != 1) as t2 where 1 != 1) as t3 where 1 != 1", - "Query": "select t3.push_it from (select bar as push_it from (select foo as bar from (select id as foo from `user` where id = 12) as t1) as t2) as t3", + "FieldQuery": "select push_it from (select bar as push_it from (select foo as bar from (select id as foo from `user` where 1 != 1) as t1 where 1 != 1) as t2 where 1 != 1) as t3 where 1 != 1", + "Query": "select push_it from (select bar as push_it from (select foo as bar from (select id as foo from `user` where id = 12) as t1) as t2) as t3", "Table": "`user`", "Values": [ "INT64(12)" @@ -3822,8 +3822,8 @@ "Name": "user", "Sharded": true }, - "FieldQuery": "select user_details_view.id, user_details_view.col from (select `user`.id, user_extra.col from `user`, user_extra where 1 != 1) as user_details_view where 1 != 1", - "Query": "select user_details_view.id, user_details_view.col from (select `user`.id, user_extra.col from `user`, user_extra where `user`.id = user_extra.user_id) as user_details_view", + "FieldQuery": "select id, col from (select `user`.id, user_extra.col from `user`, user_extra where 1 != 1) as user_details_view where 1 != 1", + "Query": "select id, col from (select `user`.id, user_extra.col from `user`, user_extra where `user`.id = user_extra.user_id) as user_details_view", "Table": "`user`, user_extra" }, "TablesUsed": [ @@ -3845,8 +3845,8 @@ "Name": "user", "Sharded": true }, - "FieldQuery": "select user_details_view.id, user_details_view.col from (select `user`.id, user_extra.col from `user`, user_extra where 1 != 1) as user_details_view where 1 != 1", - "Query": "select user_details_view.id, user_details_view.col from (select `user`.id, user_extra.col from `user`, user_extra where `user`.id = user_extra.user_id) as user_details_view", + "FieldQuery": "select id, col from (select `user`.id, user_extra.col from `user`, user_extra where 1 != 1) as user_details_view where 1 != 1", + "Query": "select id, col from (select `user`.id, user_extra.col from `user`, user_extra where `user`.id = user_extra.user_id) as user_details_view", "Table": "`user`, user_extra" }, "TablesUsed": [ diff --git a/go/vt/vtgate/planbuilder/testdata/info_schema57_cases.json b/go/vt/vtgate/planbuilder/testdata/info_schema57_cases.json index aec230c8b3a..4e3ab52ece5 100644 --- a/go/vt/vtgate/planbuilder/testdata/info_schema57_cases.json +++ b/go/vt/vtgate/planbuilder/testdata/info_schema57_cases.json @@ -683,8 +683,8 @@ "Name": "main", "Sharded": false }, - "FieldQuery": "select x.table_name from (select a.CONSTRAINT_CATALOG, a.CONSTRAINT_SCHEMA, a.CONSTRAINT_NAME, a.TABLE_CATALOG, a.TABLE_SCHEMA, a.TABLE_NAME, a.COLUMN_NAME, a.ORDINAL_POSITION, a.POSITION_IN_UNIQUE_CONSTRAINT, a.REFERENCED_TABLE_SCHEMA, a.REFERENCED_TABLE_NAME, a.REFERENCED_COLUMN_NAME from information_schema.key_column_usage as a where 1 != 1) as x where 1 != 1", - "Query": "select x.table_name from (select a.CONSTRAINT_CATALOG, a.CONSTRAINT_SCHEMA, a.CONSTRAINT_NAME, a.TABLE_CATALOG, a.TABLE_SCHEMA, a.TABLE_NAME, a.COLUMN_NAME, a.ORDINAL_POSITION, a.POSITION_IN_UNIQUE_CONSTRAINT, a.REFERENCED_TABLE_SCHEMA, a.REFERENCED_TABLE_NAME, a.REFERENCED_COLUMN_NAME from information_schema.key_column_usage as a) as x", + "FieldQuery": "select x.table_name from (select CONSTRAINT_CATALOG, CONSTRAINT_SCHEMA, CONSTRAINT_NAME, TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, ORDINAL_POSITION, POSITION_IN_UNIQUE_CONSTRAINT, REFERENCED_TABLE_SCHEMA, REFERENCED_TABLE_NAME, REFERENCED_COLUMN_NAME from information_schema.key_column_usage as a where 1 != 1) as x where 1 != 1", + "Query": "select x.table_name from (select CONSTRAINT_CATALOG, CONSTRAINT_SCHEMA, CONSTRAINT_NAME, TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, ORDINAL_POSITION, POSITION_IN_UNIQUE_CONSTRAINT, REFERENCED_TABLE_SCHEMA, REFERENCED_TABLE_NAME, REFERENCED_COLUMN_NAME from information_schema.key_column_usage as a) as x", "Table": "information_schema.key_column_usage" } } @@ -711,8 +711,8 @@ "Name": "main", "Sharded": false }, - "FieldQuery": "select x.table_name, x.COLUMN_NAME from (select a.CONSTRAINT_CATALOG, a.CONSTRAINT_SCHEMA, a.CONSTRAINT_NAME, a.TABLE_CATALOG, a.TABLE_SCHEMA, a.TABLE_NAME, a.COLUMN_NAME, a.ORDINAL_POSITION, a.POSITION_IN_UNIQUE_CONSTRAINT, a.REFERENCED_TABLE_SCHEMA, a.REFERENCED_TABLE_NAME, a.REFERENCED_COLUMN_NAME from information_schema.key_column_usage as a where 1 != 1) as x where 1 != 1", - "Query": "select x.table_name, x.COLUMN_NAME from (select a.CONSTRAINT_CATALOG, a.CONSTRAINT_SCHEMA, a.CONSTRAINT_NAME, a.TABLE_CATALOG, a.TABLE_SCHEMA, a.TABLE_NAME, a.COLUMN_NAME, a.ORDINAL_POSITION, a.POSITION_IN_UNIQUE_CONSTRAINT, a.REFERENCED_TABLE_SCHEMA, a.REFERENCED_TABLE_NAME, a.REFERENCED_COLUMN_NAME from information_schema.key_column_usage as a) as x", + "FieldQuery": "select x.table_name, x.COLUMN_NAME from (select CONSTRAINT_CATALOG, CONSTRAINT_SCHEMA, CONSTRAINT_NAME, TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, ORDINAL_POSITION, POSITION_IN_UNIQUE_CONSTRAINT, REFERENCED_TABLE_SCHEMA, REFERENCED_TABLE_NAME, REFERENCED_COLUMN_NAME from information_schema.key_column_usage as a where 1 != 1) as x where 1 != 1", + "Query": "select x.table_name, x.COLUMN_NAME from (select CONSTRAINT_CATALOG, CONSTRAINT_SCHEMA, CONSTRAINT_NAME, TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, ORDINAL_POSITION, POSITION_IN_UNIQUE_CONSTRAINT, REFERENCED_TABLE_SCHEMA, REFERENCED_TABLE_NAME, REFERENCED_COLUMN_NAME from information_schema.key_column_usage as a) as x", "Table": "information_schema.key_column_usage" }, { @@ -769,8 +769,8 @@ "Name": "main", "Sharded": false }, - "FieldQuery": "select a.table_name from (select a.CONSTRAINT_CATALOG, a.CONSTRAINT_SCHEMA, a.CONSTRAINT_NAME, a.TABLE_CATALOG, a.TABLE_SCHEMA, a.TABLE_NAME, a.COLUMN_NAME, a.ORDINAL_POSITION, a.POSITION_IN_UNIQUE_CONSTRAINT, a.REFERENCED_TABLE_SCHEMA, a.REFERENCED_TABLE_NAME, a.REFERENCED_COLUMN_NAME from information_schema.key_column_usage as a where 1 != 1) as a, (select CONSTRAINT_CATALOG, CONSTRAINT_SCHEMA, CONSTRAINT_NAME, UNIQUE_CONSTRAINT_CATALOG, UNIQUE_CONSTRAINT_SCHEMA, UNIQUE_CONSTRAINT_NAME, MATCH_OPTION, UPDATE_RULE, DELETE_RULE, TABLE_NAME, REFERENCED_TABLE_NAME from information_schema.referential_constraints where 1 != 1) as b where 1 != 1", - "Query": "select a.table_name from (select a.CONSTRAINT_CATALOG, a.CONSTRAINT_SCHEMA, a.CONSTRAINT_NAME, a.TABLE_CATALOG, a.TABLE_SCHEMA, a.TABLE_NAME, a.COLUMN_NAME, a.ORDINAL_POSITION, a.POSITION_IN_UNIQUE_CONSTRAINT, a.REFERENCED_TABLE_SCHEMA, a.REFERENCED_TABLE_NAME, a.REFERENCED_COLUMN_NAME from information_schema.key_column_usage as a where a.table_name = :a_table_name /* VARCHAR */) as a, (select CONSTRAINT_CATALOG, CONSTRAINT_SCHEMA, CONSTRAINT_NAME, UNIQUE_CONSTRAINT_CATALOG, UNIQUE_CONSTRAINT_SCHEMA, UNIQUE_CONSTRAINT_NAME, MATCH_OPTION, UPDATE_RULE, DELETE_RULE, TABLE_NAME, REFERENCED_TABLE_NAME from information_schema.referential_constraints where table_name = :table_name /* VARCHAR */) as b", + "FieldQuery": "select a.table_name from (select CONSTRAINT_CATALOG, CONSTRAINT_SCHEMA, CONSTRAINT_NAME, TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, ORDINAL_POSITION, POSITION_IN_UNIQUE_CONSTRAINT, REFERENCED_TABLE_SCHEMA, REFERENCED_TABLE_NAME, REFERENCED_COLUMN_NAME from information_schema.key_column_usage as a where 1 != 1) as a, (select CONSTRAINT_CATALOG, CONSTRAINT_SCHEMA, CONSTRAINT_NAME, UNIQUE_CONSTRAINT_CATALOG, UNIQUE_CONSTRAINT_SCHEMA, UNIQUE_CONSTRAINT_NAME, MATCH_OPTION, UPDATE_RULE, DELETE_RULE, TABLE_NAME, REFERENCED_TABLE_NAME from information_schema.referential_constraints where 1 != 1) as b where 1 != 1", + "Query": "select a.table_name from (select CONSTRAINT_CATALOG, CONSTRAINT_SCHEMA, CONSTRAINT_NAME, TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, ORDINAL_POSITION, POSITION_IN_UNIQUE_CONSTRAINT, REFERENCED_TABLE_SCHEMA, REFERENCED_TABLE_NAME, REFERENCED_COLUMN_NAME from information_schema.key_column_usage as a where a.table_name = :a_table_name /* VARCHAR */) as a, (select CONSTRAINT_CATALOG, CONSTRAINT_SCHEMA, CONSTRAINT_NAME, UNIQUE_CONSTRAINT_CATALOG, UNIQUE_CONSTRAINT_SCHEMA, UNIQUE_CONSTRAINT_NAME, MATCH_OPTION, UPDATE_RULE, DELETE_RULE, TABLE_NAME, REFERENCED_TABLE_NAME from information_schema.referential_constraints where table_name = :table_name /* VARCHAR */) as b", "SysTableTableName": "[a_table_name:VARCHAR(\"users\"), table_name:VARCHAR(\"users\")]", "Table": "information_schema.key_column_usage, information_schema.referential_constraints" } diff --git a/go/vt/vtgate/planbuilder/testdata/info_schema80_cases.json b/go/vt/vtgate/planbuilder/testdata/info_schema80_cases.json index 13a503a4eb8..40fd07b1dc4 100644 --- a/go/vt/vtgate/planbuilder/testdata/info_schema80_cases.json +++ b/go/vt/vtgate/planbuilder/testdata/info_schema80_cases.json @@ -748,8 +748,8 @@ "Name": "main", "Sharded": false }, - "FieldQuery": "select x.table_name from (select a.CONSTRAINT_CATALOG, a.CONSTRAINT_SCHEMA, a.CONSTRAINT_NAME, a.TABLE_CATALOG, a.TABLE_SCHEMA, a.TABLE_NAME, a.COLUMN_NAME, a.ORDINAL_POSITION, a.POSITION_IN_UNIQUE_CONSTRAINT, a.REFERENCED_TABLE_SCHEMA, a.REFERENCED_TABLE_NAME, a.REFERENCED_COLUMN_NAME from information_schema.key_column_usage as a where 1 != 1) as x where 1 != 1", - "Query": "select x.table_name from (select a.CONSTRAINT_CATALOG, a.CONSTRAINT_SCHEMA, a.CONSTRAINT_NAME, a.TABLE_CATALOG, a.TABLE_SCHEMA, a.TABLE_NAME, a.COLUMN_NAME, a.ORDINAL_POSITION, a.POSITION_IN_UNIQUE_CONSTRAINT, a.REFERENCED_TABLE_SCHEMA, a.REFERENCED_TABLE_NAME, a.REFERENCED_COLUMN_NAME from information_schema.key_column_usage as a) as x", + "FieldQuery": "select x.table_name from (select CONSTRAINT_CATALOG, CONSTRAINT_SCHEMA, CONSTRAINT_NAME, TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, ORDINAL_POSITION, POSITION_IN_UNIQUE_CONSTRAINT, REFERENCED_TABLE_SCHEMA, REFERENCED_TABLE_NAME, REFERENCED_COLUMN_NAME from information_schema.key_column_usage as a where 1 != 1) as x where 1 != 1", + "Query": "select x.table_name from (select CONSTRAINT_CATALOG, CONSTRAINT_SCHEMA, CONSTRAINT_NAME, TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, ORDINAL_POSITION, POSITION_IN_UNIQUE_CONSTRAINT, REFERENCED_TABLE_SCHEMA, REFERENCED_TABLE_NAME, REFERENCED_COLUMN_NAME from information_schema.key_column_usage as a) as x", "Table": "information_schema.key_column_usage" } } @@ -776,8 +776,8 @@ "Name": "main", "Sharded": false }, - "FieldQuery": "select x.table_name, x.COLUMN_NAME from (select a.CONSTRAINT_CATALOG, a.CONSTRAINT_SCHEMA, a.CONSTRAINT_NAME, a.TABLE_CATALOG, a.TABLE_SCHEMA, a.TABLE_NAME, a.COLUMN_NAME, a.ORDINAL_POSITION, a.POSITION_IN_UNIQUE_CONSTRAINT, a.REFERENCED_TABLE_SCHEMA, a.REFERENCED_TABLE_NAME, a.REFERENCED_COLUMN_NAME from information_schema.key_column_usage as a where 1 != 1) as x where 1 != 1", - "Query": "select x.table_name, x.COLUMN_NAME from (select a.CONSTRAINT_CATALOG, a.CONSTRAINT_SCHEMA, a.CONSTRAINT_NAME, a.TABLE_CATALOG, a.TABLE_SCHEMA, a.TABLE_NAME, a.COLUMN_NAME, a.ORDINAL_POSITION, a.POSITION_IN_UNIQUE_CONSTRAINT, a.REFERENCED_TABLE_SCHEMA, a.REFERENCED_TABLE_NAME, a.REFERENCED_COLUMN_NAME from information_schema.key_column_usage as a) as x", + "FieldQuery": "select x.table_name, x.COLUMN_NAME from (select CONSTRAINT_CATALOG, CONSTRAINT_SCHEMA, CONSTRAINT_NAME, TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, ORDINAL_POSITION, POSITION_IN_UNIQUE_CONSTRAINT, REFERENCED_TABLE_SCHEMA, REFERENCED_TABLE_NAME, REFERENCED_COLUMN_NAME from information_schema.key_column_usage as a where 1 != 1) as x where 1 != 1", + "Query": "select x.table_name, x.COLUMN_NAME from (select CONSTRAINT_CATALOG, CONSTRAINT_SCHEMA, CONSTRAINT_NAME, TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, ORDINAL_POSITION, POSITION_IN_UNIQUE_CONSTRAINT, REFERENCED_TABLE_SCHEMA, REFERENCED_TABLE_NAME, REFERENCED_COLUMN_NAME from information_schema.key_column_usage as a) as x", "Table": "information_schema.key_column_usage" }, { @@ -834,8 +834,8 @@ "Name": "main", "Sharded": false }, - "FieldQuery": "select a.table_name from (select a.CONSTRAINT_CATALOG, a.CONSTRAINT_SCHEMA, a.CONSTRAINT_NAME, a.TABLE_CATALOG, a.TABLE_SCHEMA, a.TABLE_NAME, a.COLUMN_NAME, a.ORDINAL_POSITION, a.POSITION_IN_UNIQUE_CONSTRAINT, a.REFERENCED_TABLE_SCHEMA, a.REFERENCED_TABLE_NAME, a.REFERENCED_COLUMN_NAME from information_schema.key_column_usage as a where 1 != 1) as a, (select CONSTRAINT_CATALOG, CONSTRAINT_SCHEMA, CONSTRAINT_NAME, UNIQUE_CONSTRAINT_CATALOG, UNIQUE_CONSTRAINT_SCHEMA, UNIQUE_CONSTRAINT_NAME, MATCH_OPTION, UPDATE_RULE, DELETE_RULE, TABLE_NAME, REFERENCED_TABLE_NAME from information_schema.referential_constraints where 1 != 1) as b where 1 != 1", - "Query": "select a.table_name from (select a.CONSTRAINT_CATALOG, a.CONSTRAINT_SCHEMA, a.CONSTRAINT_NAME, a.TABLE_CATALOG, a.TABLE_SCHEMA, a.TABLE_NAME, a.COLUMN_NAME, a.ORDINAL_POSITION, a.POSITION_IN_UNIQUE_CONSTRAINT, a.REFERENCED_TABLE_SCHEMA, a.REFERENCED_TABLE_NAME, a.REFERENCED_COLUMN_NAME from information_schema.key_column_usage as a where a.table_name = :a_table_name /* VARCHAR */) as a, (select CONSTRAINT_CATALOG, CONSTRAINT_SCHEMA, CONSTRAINT_NAME, UNIQUE_CONSTRAINT_CATALOG, UNIQUE_CONSTRAINT_SCHEMA, UNIQUE_CONSTRAINT_NAME, MATCH_OPTION, UPDATE_RULE, DELETE_RULE, TABLE_NAME, REFERENCED_TABLE_NAME from information_schema.referential_constraints where table_name = :table_name /* VARCHAR */) as b", + "FieldQuery": "select a.table_name from (select CONSTRAINT_CATALOG, CONSTRAINT_SCHEMA, CONSTRAINT_NAME, TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, ORDINAL_POSITION, POSITION_IN_UNIQUE_CONSTRAINT, REFERENCED_TABLE_SCHEMA, REFERENCED_TABLE_NAME, REFERENCED_COLUMN_NAME from information_schema.key_column_usage as a where 1 != 1) as a, (select CONSTRAINT_CATALOG, CONSTRAINT_SCHEMA, CONSTRAINT_NAME, UNIQUE_CONSTRAINT_CATALOG, UNIQUE_CONSTRAINT_SCHEMA, UNIQUE_CONSTRAINT_NAME, MATCH_OPTION, UPDATE_RULE, DELETE_RULE, TABLE_NAME, REFERENCED_TABLE_NAME from information_schema.referential_constraints where 1 != 1) as b where 1 != 1", + "Query": "select a.table_name from (select CONSTRAINT_CATALOG, CONSTRAINT_SCHEMA, CONSTRAINT_NAME, TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, ORDINAL_POSITION, POSITION_IN_UNIQUE_CONSTRAINT, REFERENCED_TABLE_SCHEMA, REFERENCED_TABLE_NAME, REFERENCED_COLUMN_NAME from information_schema.key_column_usage as a where a.table_name = :a_table_name /* VARCHAR */) as a, (select CONSTRAINT_CATALOG, CONSTRAINT_SCHEMA, CONSTRAINT_NAME, UNIQUE_CONSTRAINT_CATALOG, UNIQUE_CONSTRAINT_SCHEMA, UNIQUE_CONSTRAINT_NAME, MATCH_OPTION, UPDATE_RULE, DELETE_RULE, TABLE_NAME, REFERENCED_TABLE_NAME from information_schema.referential_constraints where table_name = :table_name /* VARCHAR */) as b", "SysTableTableName": "[a_table_name:VARCHAR(\"users\"), table_name:VARCHAR(\"users\")]", "Table": "information_schema.key_column_usage, information_schema.referential_constraints" } diff --git a/go/vt/vtgate/planbuilder/testdata/select_cases.json b/go/vt/vtgate/planbuilder/testdata/select_cases.json index 80d9cdf6d23..5dbb6de149b 100644 --- a/go/vt/vtgate/planbuilder/testdata/select_cases.json +++ b/go/vt/vtgate/planbuilder/testdata/select_cases.json @@ -388,8 +388,8 @@ "Name": "user", "Sharded": true }, - "FieldQuery": "select a.user_id, a.col1, a.col2 from authoritative as a where 1 != 1", - "Query": "select a.user_id, a.col1, a.col2 from authoritative as a", + "FieldQuery": "select user_id, col1, col2 from authoritative as a where 1 != 1", + "Query": "select user_id, col1, col2 from authoritative as a", "Table": "authoritative" }, "TablesUsed": [ @@ -1328,8 +1328,8 @@ "Name": "user", "Sharded": true }, - "FieldQuery": "select t.id1 from (select `user`.id as id1 from `user` where 1 != 1) as t where 1 != 1", - "Query": "select t.id1 from (select `user`.id as id1 from `user`) as t", + "FieldQuery": "select id1 from (select `user`.id as id1 from `user` where 1 != 1) as t where 1 != 1", + "Query": "select id1 from (select `user`.id as id1 from `user`) as t", "Table": "`user`" }, { @@ -1339,8 +1339,8 @@ "Name": "user", "Sharded": true }, - "FieldQuery": "select t.id2 from (select user_extra.id as id2 from user_extra where 1 != 1) as t where 1 != 1", - "Query": "select t.id2 from (select user_extra.id as id2 from user_extra) as t", + "FieldQuery": "select id2 from (select user_extra.id as id2 from user_extra where 1 != 1) as t where 1 != 1", + "Query": "select id2 from (select user_extra.id as id2 from user_extra) as t", "Table": "user_extra" } ] @@ -1428,8 +1428,8 @@ "Name": "main", "Sharded": false }, - "FieldQuery": "select a.col1, a.col2 from (select col1, col2 from unsharded where 1 != 1 union select col1, col2 from unsharded where 1 != 1) as a where 1 != 1", - "Query": "select a.col1, a.col2 from (select col1, col2 from unsharded where id = 1 union select col1, col2 from unsharded where id = 3) as a", + "FieldQuery": "select col1, col2 from (select col1, col2 from unsharded where 1 != 1 union select col1, col2 from unsharded where 1 != 1) as a where 1 != 1", + "Query": "select col1, col2 from (select col1, col2 from unsharded where id = 1 union select col1, col2 from unsharded where id = 3) as a", "Table": "unsharded" }, "TablesUsed": [ @@ -3381,7 +3381,7 @@ "Sharded": true }, "FieldQuery": "select music.id from music where 1 != 1", - "Query": "select music.id from music where music.id in (select _inner.id from (select music.id from music where music.user_id in (1, 2, 3)) as _inner)", + "Query": "select music.id from music where music.id in (select id from (select music.id from music where music.user_id in (1, 2, 3)) as _inner)", "Table": "music", "Values": [ "(INT64(1), INT64(2), INT64(3))" @@ -3901,7 +3901,7 @@ "Sharded": true }, "FieldQuery": "select music.id from music where 1 != 1", - "Query": "select music.id from music where music.id in (select subquery_for_limit.id from (select subquery_for_limit.id from (select music.id from music where music.user_id = 5 limit 10) as subquery_for_limit) as subquery_for_limit)", + "Query": "select music.id from music where music.id in (select id from (select id from (select music.id from music where music.user_id = 5 limit 10) as subquery_for_limit) as subquery_for_limit)", "Table": "music", "Values": [ "INT64(5)" @@ -3927,7 +3927,7 @@ "Sharded": true }, "FieldQuery": "select music.id from music where 1 != 1", - "Query": "select music.id from music where music.id in (select subquery_for_limit.id from (select subquery_for_limit.id from (select music.id from music where music.user_id in (5) limit 10) as subquery_for_limit) as subquery_for_limit)", + "Query": "select music.id from music where music.id in (select id from (select id from (select music.id from music where music.user_id in (5) limit 10) as subquery_for_limit) as subquery_for_limit)", "Table": "music", "Values": [ "INT64(5)" @@ -3965,8 +3965,8 @@ "Name": "user", "Sharded": true }, - "FieldQuery": "select subquery_for_limit.id from (select subquery_for_limit.id from (select music.id from music where 1 != 1) as subquery_for_limit where 1 != 1) as subquery_for_limit where 1 != 1", - "Query": "select subquery_for_limit.id from (select subquery_for_limit.id from (select music.id from music where music.user_id in ::__vals) as subquery_for_limit limit :__upper_limit) as subquery_for_limit limit :__upper_limit", + "FieldQuery": "select id from (select id from (select music.id from music where 1 != 1) as subquery_for_limit where 1 != 1) as subquery_for_limit where 1 != 1", + "Query": "select id from (select id from (select music.id from music where music.user_id in ::__vals) as subquery_for_limit limit :__upper_limit) as subquery_for_limit limit :__upper_limit", "Table": "music", "Values": [ "(INT64(5), INT64(6))" @@ -4024,8 +4024,8 @@ "Name": "user", "Sharded": true }, - "FieldQuery": "select subquery_for_limit.id from (select subquery_for_limit.id from (select music.id from music where 1 != 1) as subquery_for_limit where 1 != 1) as subquery_for_limit where 1 != 1", - "Query": "select subquery_for_limit.id from (select subquery_for_limit.id from (select music.id from music) as subquery_for_limit limit :__upper_limit) as subquery_for_limit limit :__upper_limit", + "FieldQuery": "select id from (select id from (select music.id from music where 1 != 1) as subquery_for_limit where 1 != 1) as subquery_for_limit where 1 != 1", + "Query": "select id from (select id from (select music.id from music) as subquery_for_limit limit :__upper_limit) as subquery_for_limit limit :__upper_limit", "Table": "music" } ] diff --git a/go/vt/vtgate/planbuilder/testdata/sysschema_default.json b/go/vt/vtgate/planbuilder/testdata/sysschema_default.json index 1d25f0f60af..cb633955f22 100644 --- a/go/vt/vtgate/planbuilder/testdata/sysschema_default.json +++ b/go/vt/vtgate/planbuilder/testdata/sysschema_default.json @@ -77,8 +77,8 @@ "Name": "main", "Sharded": false }, - "FieldQuery": "select x.`1` from (select 1 from information_schema.schemata where 1 != 1) as x where 1 != 1", - "Query": "select x.`1` from (select 1 from information_schema.schemata where schema_name = :__vtschemaname /* VARCHAR */ limit 1) as x", + "FieldQuery": "select `1` from (select 1 from information_schema.schemata where 1 != 1) as x where 1 != 1", + "Query": "select `1` from (select 1 from information_schema.schemata where schema_name = :__vtschemaname /* VARCHAR */ limit 1) as x", "SysTableTableSchema": "[VARCHAR(\"MyDatabase\")]", "Table": "information_schema.schemata" } diff --git a/go/vt/vtgate/planbuilder/testdata/union_cases.json b/go/vt/vtgate/planbuilder/testdata/union_cases.json index baea31d84bd..d9d60015efb 100644 --- a/go/vt/vtgate/planbuilder/testdata/union_cases.json +++ b/go/vt/vtgate/planbuilder/testdata/union_cases.json @@ -1212,8 +1212,8 @@ "Name": "user", "Sharded": true }, - "FieldQuery": "select X.`name`, X.foo from (select `name`, id as foo from `user` where 1 != 1 union select 'extra', user_id from user_extra where 1 != 1) as X where 1 != 1", - "Query": "select X.`name`, X.foo from (select `name`, id as foo from `user` where id = 3 union select 'extra', user_id from user_extra where user_id = 3) as X", + "FieldQuery": "select `name`, foo from (select `name`, id as foo from `user` where 1 != 1 union select 'extra', user_id from user_extra where 1 != 1) as X where 1 != 1", + "Query": "select `name`, foo from (select `name`, id as foo from `user` where id = 3 union select 'extra', user_id from user_extra where user_id = 3) as X", "Table": "`user`, user_extra", "Values": [ "INT64(3)" @@ -1263,8 +1263,8 @@ "Name": "main", "Sharded": false }, - "FieldQuery": "select kcu.CONSTRAINT_CATALOG, kcu.CONSTRAINT_SCHEMA, kcu.CONSTRAINT_NAME, kcu.TABLE_CATALOG, kcu.TABLE_SCHEMA, kcu.TABLE_NAME, kcu.COLUMN_NAME, kcu.ORDINAL_POSITION, kcu.POSITION_IN_UNIQUE_CONSTRAINT, kcu.REFERENCED_TABLE_SCHEMA, kcu.REFERENCED_TABLE_NAME, kcu.REFERENCED_COLUMN_NAME from information_schema.key_column_usage as kcu where 1 != 1", - "Query": "select distinct kcu.CONSTRAINT_CATALOG, kcu.CONSTRAINT_SCHEMA, kcu.CONSTRAINT_NAME, kcu.TABLE_CATALOG, kcu.TABLE_SCHEMA, kcu.TABLE_NAME, kcu.COLUMN_NAME, kcu.ORDINAL_POSITION, kcu.POSITION_IN_UNIQUE_CONSTRAINT, kcu.REFERENCED_TABLE_SCHEMA, kcu.REFERENCED_TABLE_NAME, kcu.REFERENCED_COLUMN_NAME from information_schema.key_column_usage as kcu where kcu.table_schema = :__vtschemaname /* VARCHAR */ and kcu.table_name = :kcu_table_name /* VARCHAR */", + "FieldQuery": "select CONSTRAINT_CATALOG, CONSTRAINT_SCHEMA, CONSTRAINT_NAME, TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, ORDINAL_POSITION, POSITION_IN_UNIQUE_CONSTRAINT, REFERENCED_TABLE_SCHEMA, REFERENCED_TABLE_NAME, REFERENCED_COLUMN_NAME from information_schema.key_column_usage as kcu where 1 != 1", + "Query": "select distinct CONSTRAINT_CATALOG, CONSTRAINT_SCHEMA, CONSTRAINT_NAME, TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, ORDINAL_POSITION, POSITION_IN_UNIQUE_CONSTRAINT, REFERENCED_TABLE_SCHEMA, REFERENCED_TABLE_NAME, REFERENCED_COLUMN_NAME from information_schema.key_column_usage as kcu where kcu.table_schema = :__vtschemaname /* VARCHAR */ and kcu.table_name = :kcu_table_name /* VARCHAR */", "SysTableTableName": "[kcu_table_name:VARCHAR(\"user_extra\")]", "SysTableTableSchema": "[VARCHAR(\"user\")]", "Table": "information_schema.key_column_usage" @@ -1276,8 +1276,8 @@ "Name": "main", "Sharded": false }, - "FieldQuery": "select kcu.CONSTRAINT_CATALOG, kcu.CONSTRAINT_SCHEMA, kcu.CONSTRAINT_NAME, kcu.TABLE_CATALOG, kcu.TABLE_SCHEMA, kcu.TABLE_NAME, kcu.COLUMN_NAME, kcu.ORDINAL_POSITION, kcu.POSITION_IN_UNIQUE_CONSTRAINT, kcu.REFERENCED_TABLE_SCHEMA, kcu.REFERENCED_TABLE_NAME, kcu.REFERENCED_COLUMN_NAME from information_schema.key_column_usage as kcu where 1 != 1", - "Query": "select distinct kcu.CONSTRAINT_CATALOG, kcu.CONSTRAINT_SCHEMA, kcu.CONSTRAINT_NAME, kcu.TABLE_CATALOG, kcu.TABLE_SCHEMA, kcu.TABLE_NAME, kcu.COLUMN_NAME, kcu.ORDINAL_POSITION, kcu.POSITION_IN_UNIQUE_CONSTRAINT, kcu.REFERENCED_TABLE_SCHEMA, kcu.REFERENCED_TABLE_NAME, kcu.REFERENCED_COLUMN_NAME from information_schema.key_column_usage as kcu where kcu.table_schema = :__vtschemaname /* VARCHAR */ and kcu.table_name = :kcu_table_name1 /* VARCHAR */", + "FieldQuery": "select CONSTRAINT_CATALOG, CONSTRAINT_SCHEMA, CONSTRAINT_NAME, TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, ORDINAL_POSITION, POSITION_IN_UNIQUE_CONSTRAINT, REFERENCED_TABLE_SCHEMA, REFERENCED_TABLE_NAME, REFERENCED_COLUMN_NAME from information_schema.key_column_usage as kcu where 1 != 1", + "Query": "select distinct CONSTRAINT_CATALOG, CONSTRAINT_SCHEMA, CONSTRAINT_NAME, TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, ORDINAL_POSITION, POSITION_IN_UNIQUE_CONSTRAINT, REFERENCED_TABLE_SCHEMA, REFERENCED_TABLE_NAME, REFERENCED_COLUMN_NAME from information_schema.key_column_usage as kcu where kcu.table_schema = :__vtschemaname /* VARCHAR */ and kcu.table_name = :kcu_table_name1 /* VARCHAR */", "SysTableTableName": "[kcu_table_name1:VARCHAR(\"music\")]", "SysTableTableSchema": "[VARCHAR(\"user\")]", "Table": "information_schema.key_column_usage" diff --git a/go/vt/vtgate/semantics/binder.go b/go/vt/vtgate/semantics/binder.go index 0d70816488a..f7fc5d64c1a 100644 --- a/go/vt/vtgate/semantics/binder.go +++ b/go/vt/vtgate/semantics/binder.go @@ -123,11 +123,7 @@ func (b *binder) bindCountStar(node *sqlparser.CountStar) { } } default: - expr := tbl.GetExpr() - if expr != nil { - setFor := b.tc.tableSetFor(expr) - ts = ts.Merge(setFor) - } + ts = ts.Merge(tbl.getTableSet(b.org)) } } b.recursive[node] = ts @@ -144,18 +140,11 @@ func (b *binder) rewriteJoinUsingColName(deps dependency, node *sqlparser.ColNam if err != nil { return dependency{}, err } - alias := infoFor.GetExpr().As - if alias.IsEmpty() { - name, err := infoFor.Name() - if err != nil { - return dependency{}, err - } - node.Qualifier = name - } else { - node.Qualifier = sqlparser.TableName{ - Name: sqlparser.NewIdentifierCS(alias.String()), - } + name, err := infoFor.Name() + if err != nil { + return dependency{}, err } + node.Qualifier = name deps, err = b.resolveColumn(node, currentScope, false) if err != nil { return dependency{}, err diff --git a/go/vt/vtgate/semantics/early_rewriter.go b/go/vt/vtgate/semantics/early_rewriter.go index d11d12023c4..5f22ef5947e 100644 --- a/go/vt/vtgate/semantics/early_rewriter.go +++ b/go/vt/vtgate/semantics/early_rewriter.go @@ -610,8 +610,7 @@ type expanderState struct { // addColumn adds columns to the expander state. If we have vschema info about the query, // we also store which columns were expanded func (e *expanderState) addColumn(col ColumnInfo, tbl TableInfo, tblName sqlparser.TableName) { - tableAliased := !tbl.GetExpr().As.IsEmpty() - withQualifier := e.needsQualifier || tableAliased + withQualifier := e.needsQualifier var colName *sqlparser.ColName var alias sqlparser.IdentifierCI if withQualifier { diff --git a/go/vt/vtgate/semantics/early_rewriter_test.go b/go/vt/vtgate/semantics/early_rewriter_test.go index bd919fe9201..a8e84da6ddb 100644 --- a/go/vt/vtgate/semantics/early_rewriter_test.go +++ b/go/vt/vtgate/semantics/early_rewriter_test.go @@ -172,14 +172,14 @@ func TestExpandStar(t *testing.T) { expSQL: "select 1 from t1 join t5 on t1.b = t5.b having t1.b = 12", }, { sql: "select * from (select 12) as t", - expSQL: "select t.`12` from (select 12 from dual) as t", + expSQL: "select `12` from (select 12 from dual) as t", }, { sql: "SELECT * FROM (SELECT *, 12 AS foo FROM t3) as results", expSQL: "select * from (select *, 12 as foo from t3) as results", }, { // if we are only star-expanding authoritative tables, we don't need to stop the expansion sql: "SELECT * FROM (SELECT t2.*, 12 AS foo FROM t3, t2) as results", - expSQL: "select results.c1, results.c2, results.foo from (select t2.c1 as c1, t2.c2 as c2, 12 as foo from t3, t2) as results", + expSQL: "select c1, c2, foo from (select t2.c1 as c1, t2.c2 as c2, 12 as foo from t3, t2) as results", }} for _, tcase := range tcases { t.Run(tcase.sql, func(t *testing.T) { From 9f43aaa20f055fdafc4b91668b5c262418d4d80a Mon Sep 17 00:00:00 2001 From: Andres Taylor Date: Fri, 20 Oct 2023 08:36:38 +0200 Subject: [PATCH 04/18] refactor: rework SingleUnshardedKeyspace to not use the AliasedTableExpr Signed-off-by: Andres Taylor --- go/vt/vtgate/semantics/derived_table.go | 6 +- go/vt/vtgate/semantics/real_table.go | 25 ++++++- go/vt/vtgate/semantics/semantic_state.go | 81 ++++++++++++----------- go/vt/vtgate/semantics/table_collector.go | 2 +- go/vt/vtgate/semantics/vindex_table.go | 8 ++- go/vt/vtgate/semantics/vtable.go | 6 +- 6 files changed, 83 insertions(+), 45 deletions(-) diff --git a/go/vt/vtgate/semantics/derived_table.go b/go/vt/vtgate/semantics/derived_table.go index 0498a26a429..c72b998f378 100644 --- a/go/vt/vtgate/semantics/derived_table.go +++ b/go/vt/vtgate/semantics/derived_table.go @@ -137,10 +137,14 @@ func (dt *DerivedTable) Name() (sqlparser.TableName, error) { return dt.ASTNode.TableName() } -func (dt *DerivedTable) GetExpr() *sqlparser.AliasedTableExpr { +func (dt *DerivedTable) getAliasedTableExpr() *sqlparser.AliasedTableExpr { return dt.ASTNode } +func (dt *DerivedTable) canShortCut() *bool { + panic(vterrors.VT12001("should not be called")) +} + // GetVindexTable implements the TableInfo interface func (dt *DerivedTable) GetVindexTable() *vindexes.Table { return nil diff --git a/go/vt/vtgate/semantics/real_table.go b/go/vt/vtgate/semantics/real_table.go index 9952e041378..dae32531e17 100644 --- a/go/vt/vtgate/semantics/real_table.go +++ b/go/vt/vtgate/semantics/real_table.go @@ -74,10 +74,33 @@ func (r *RealTable) getColumns() []ColumnInfo { } // GetExpr implements the TableInfo interface -func (r *RealTable) GetExpr() *sqlparser.AliasedTableExpr { +func (r *RealTable) getAliasedTableExpr() *sqlparser.AliasedTableExpr { return r.ASTNode } +var f = false +var t = true + +func (r *RealTable) canShortCut() *bool { + if r.Table == nil { + return &f + } + if r.Table.Type != "" { + // A reference table is not an issue when seeing if a query is going to an unsharded keyspace + if r.Table.Type == vindexes.TypeReference { + return &t + } + return &f + } + + name, ok := r.ASTNode.Expr.(sqlparser.TableName) + if !ok || name.Name.String() != r.Table.Name.String() { + return &f + } + + return nil +} + // GetVindexTable implements the TableInfo interface func (r *RealTable) GetVindexTable() *vindexes.Table { return r.Table diff --git a/go/vt/vtgate/semantics/semantic_state.go b/go/vt/vtgate/semantics/semantic_state.go index af37a9b34d1..e66c38c9bb2 100644 --- a/go/vt/vtgate/semantics/semantic_state.go +++ b/go/vt/vtgate/semantics/semantic_state.go @@ -49,8 +49,12 @@ type ( // authoritative is true if we have exhaustive column information authoritative() bool - // GetExpr returns the AST struct behind this table - GetExpr() *sqlparser.AliasedTableExpr + // getAliasedTableExpr returns the AST struct behind this table + getAliasedTableExpr() *sqlparser.AliasedTableExpr + + // canShortCut will return nil when the keyspace needs to be checked, + // and a true/false if the decision has been made already + canShortCut() *bool // getColumns returns the known column information for this table getColumns() []ColumnInfo @@ -406,7 +410,7 @@ func EmptySemTable() *SemTable { // TableSetFor returns the bitmask for this particular table func (st *SemTable) TableSetFor(t *sqlparser.AliasedTableExpr) TableSet { for idx, t2 := range st.Tables { - if t == t2.GetExpr() { + if t == t2.getAliasedTableExpr() { return SingleTableSet(idx) } } @@ -607,49 +611,48 @@ func (st *SemTable) ColumnLookup(col *sqlparser.ColName) (int, error) { } // SingleUnshardedKeyspace returns the single keyspace if all tables in the query are in the same, unsharded keyspace -func (st *SemTable) SingleUnshardedKeyspace() (*vindexes.Keyspace, []*vindexes.Table) { - var ks *vindexes.Keyspace - var tables []*vindexes.Table - for _, table := range st.Tables { - vindexTable := table.GetVindexTable() - - if vindexTable == nil { - _, isDT := table.GetExpr().Expr.(*sqlparser.DerivedTable) - if isDT { - // derived tables are ok, as long as all real tables are from the same unsharded keyspace - // we check the real tables inside the derived table as well for same unsharded keyspace. - continue - } - return nil, nil - } - if vindexTable.Type != "" { - // A reference table is not an issue when seeing if a query is going to an unsharded keyspace - if vindexTable.Type == vindexes.TypeReference { - tables = append(tables, vindexTable) - continue - } - return nil, nil - } - name, ok := table.GetExpr().Expr.(sqlparser.TableName) - if !ok { - return nil, nil - } - if name.Name.String() != vindexTable.Name.String() { - // this points to a table alias. safer to not shortcut - return nil, nil - } - this := vindexTable.Keyspace +func (st *SemTable) SingleUnshardedKeyspace() (ks *vindexes.Keyspace, tables []*vindexes.Table) { + validKS := func(this *vindexes.Keyspace) bool { if this == nil || this.Sharded { - return nil, nil + return false } if ks == nil { + // first keyspace we see ks = this - } else { - if ks != this { + } else if ks != this { + // even if both are unsharded, we only allow a single keyspace for these queries + return false + } + return true + } + + for _, table := range st.Tables { + if _, isDT := table.(*DerivedTable); isDT { + continue + } + + sc := table.canShortCut() + var vtbl *vindexes.Table + + switch { + case sc == nil: + // we have to check the KS if the table doesn't know if it can be shortcut or not + vtbl = table.GetVindexTable() + if !validKS(vtbl.Keyspace) { return nil, nil } + case *sc: + // the table knows that it's safe to shortcut + vtbl = table.GetVindexTable() + if vtbl == nil { + continue + } + case !*sc: + // the table knows that we can't shortcut + return nil, nil } - tables = append(tables, vindexTable) + + tables = append(tables, vtbl) } return ks, tables } diff --git a/go/vt/vtgate/semantics/table_collector.go b/go/vt/vtgate/semantics/table_collector.go index 3aa46c87bfa..9acffe3abe3 100644 --- a/go/vt/vtgate/semantics/table_collector.go +++ b/go/vt/vtgate/semantics/table_collector.go @@ -192,7 +192,7 @@ func newVindexTable(t sqlparser.IdentifierCS) *vindexes.Table { // The code lives in this file since it is only touching tableCollector data func (tc *tableCollector) tableSetFor(t *sqlparser.AliasedTableExpr) TableSet { for i, t2 := range tc.Tables { - if t == t2.GetExpr() { + if t == t2.getAliasedTableExpr() { return SingleTableSet(i) } } diff --git a/go/vt/vtgate/semantics/vindex_table.go b/go/vt/vtgate/semantics/vindex_table.go index 46e5c2133ac..6a2df4639d8 100644 --- a/go/vt/vtgate/semantics/vindex_table.go +++ b/go/vt/vtgate/semantics/vindex_table.go @@ -67,8 +67,12 @@ func (v *VindexTable) Name() (sqlparser.TableName, error) { } // GetExpr implements the TableInfo interface -func (v *VindexTable) GetExpr() *sqlparser.AliasedTableExpr { - return v.Table.GetExpr() +func (v *VindexTable) getAliasedTableExpr() *sqlparser.AliasedTableExpr { + return v.Table.getAliasedTableExpr() +} + +func (v *VindexTable) canShortCut() *bool { + return &f } // GetColumns implements the TableInfo interface diff --git a/go/vt/vtgate/semantics/vtable.go b/go/vt/vtgate/semantics/vtable.go index ce7efe22371..e4d9f8b3649 100644 --- a/go/vt/vtgate/semantics/vtable.go +++ b/go/vt/vtgate/semantics/vtable.go @@ -70,10 +70,14 @@ func (v *vTableInfo) Name() (sqlparser.TableName, error) { return sqlparser.TableName{}, vterrors.Errorf(vtrpcpb.Code_INTERNAL, "oh noes") } -func (v *vTableInfo) GetExpr() *sqlparser.AliasedTableExpr { +func (v *vTableInfo) getAliasedTableExpr() *sqlparser.AliasedTableExpr { return nil } +func (v *vTableInfo) canShortCut() *bool { + return &t +} + // GetVindexTable implements the TableInfo interface func (v *vTableInfo) GetVindexTable() *vindexes.Table { return nil From 3ecc839435f5d515d8d43f86f6b2029044f42cdf Mon Sep 17 00:00:00 2001 From: Andres Taylor Date: Fri, 20 Oct 2023 09:11:24 +0200 Subject: [PATCH 05/18] refactor: make it easier to add CTEs Signed-off-by: Andres Taylor --- go/vt/vtgate/semantics/table_collector.go | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/go/vt/vtgate/semantics/table_collector.go b/go/vt/vtgate/semantics/table_collector.go index 9acffe3abe3..f154e4fe749 100644 --- a/go/vt/vtgate/semantics/table_collector.go +++ b/go/vt/vtgate/semantics/table_collector.go @@ -91,10 +91,10 @@ func (tc *tableCollector) visitAliasedTableExpr(node *sqlparser.AliasedTableExpr case *sqlparser.DerivedTable: switch sel := t.Select.(type) { case *sqlparser.Select: - return tc.addSelectDerivedTable(sel, node) + return tc.addSelectDerivedTable(sel, node, node.Columns, node.As) case *sqlparser.Union: - return tc.addUnionDerivedTable(sel, node) + return tc.addUnionDerivedTable(sel, node, node.Columns, node.As) default: return vterrors.Errorf(vtrpcpb.Code_INTERNAL, "[BUG] %T in a derived table", sel) @@ -123,7 +123,12 @@ func (tc *tableCollector) visitAliasedTableExpr(node *sqlparser.AliasedTableExpr return nil } -func (tc *tableCollector) addSelectDerivedTable(sel *sqlparser.Select, node *sqlparser.AliasedTableExpr) error { +func (tc *tableCollector) addSelectDerivedTable( + sel *sqlparser.Select, + tableExpr *sqlparser.AliasedTableExpr, + columns sqlparser.Columns, + alias sqlparser.IdentifierCS, +) error { tables := tc.scoper.wScope[sel] size := len(sel.SelectExprs) deps := make([]TableSet, size) @@ -138,20 +143,20 @@ func (tc *tableCollector) addSelectDerivedTable(sel *sqlparser.Select, node *sql _, deps[i], types[i] = tc.org.depsForExpr(ae.Expr) } - tableInfo := createDerivedTableForExpressions(sel.SelectExprs, node.Columns, tables.tables, tc.org, expanded, deps, types) + tableInfo := createDerivedTableForExpressions(sel.SelectExprs, columns, tables.tables, tc.org, expanded, deps, types) if err := tableInfo.checkForDuplicates(); err != nil { return err } - tableInfo.ASTNode = node - tableInfo.tableName = node.As.String() + tableInfo.ASTNode = tableExpr + tableInfo.tableName = alias.String() tc.Tables = append(tc.Tables, tableInfo) scope := tc.scoper.currentScope() return scope.addTable(tableInfo) } -func (tc *tableCollector) addUnionDerivedTable(union *sqlparser.Union, node *sqlparser.AliasedTableExpr) error { +func (tc *tableCollector) addUnionDerivedTable(union *sqlparser.Union, node *sqlparser.AliasedTableExpr, columns sqlparser.Columns, alias sqlparser.IdentifierCS) error { firstSelect := sqlparser.GetFirstSelect(union) tables := tc.scoper.wScope[firstSelect] info, found := tc.unionInfo[union] @@ -159,12 +164,12 @@ func (tc *tableCollector) addUnionDerivedTable(union *sqlparser.Union, node *sql return vterrors.VT13001("information about union is not available") } - tableInfo := createDerivedTableForExpressions(info.exprs, node.Columns, tables.tables, tc.org, info.isAuthoritative, info.recursive, info.types) + tableInfo := createDerivedTableForExpressions(info.exprs, columns, tables.tables, tc.org, info.isAuthoritative, info.recursive, info.types) if err := tableInfo.checkForDuplicates(); err != nil { return err } tableInfo.ASTNode = node - tableInfo.tableName = node.As.String() + tableInfo.tableName = alias.String() tc.Tables = append(tc.Tables, tableInfo) scope := tc.scoper.currentScope() From 03e247471af2370170e8028162a6c64d963ec803 Mon Sep 17 00:00:00 2001 From: Andres Taylor Date: Fri, 20 Oct 2023 09:35:11 +0200 Subject: [PATCH 06/18] refactor: make sure we see the WITH clause before the FROM Signed-off-by: Andres Taylor --- go/vt/sqlparser/ast.go | 4 ++-- go/vt/sqlparser/ast_clone.go | 2 +- go/vt/sqlparser/ast_copy_on_rewrite.go | 6 +++--- go/vt/sqlparser/ast_equals.go | 2 +- go/vt/sqlparser/ast_rewrite.go | 10 +++++----- go/vt/sqlparser/ast_visit.go | 6 +++--- go/vt/sqlparser/cached_size.go | 4 ++-- 7 files changed, 17 insertions(+), 17 deletions(-) diff --git a/go/vt/sqlparser/ast.go b/go/vt/sqlparser/ast.go index e5c41aca17c..6cc921e51be 100644 --- a/go/vt/sqlparser/ast.go +++ b/go/vt/sqlparser/ast.go @@ -259,12 +259,12 @@ type ( Distinct bool StraightJoinHint bool SQLCalcFoundRows bool - // The From field must be the first AST element of this struct so the rewriter sees it first + // The With field needs to come before the FROM clause, so any CTEs have been handled before we analyze it + With *With From []TableExpr Comments *ParsedComments SelectExprs SelectExprs Where *Where - With *With GroupBy GroupBy Having *Where Windows NamedWindows diff --git a/go/vt/sqlparser/ast_clone.go b/go/vt/sqlparser/ast_clone.go index dd5f939a2fc..39860a678dd 100644 --- a/go/vt/sqlparser/ast_clone.go +++ b/go/vt/sqlparser/ast_clone.go @@ -2711,11 +2711,11 @@ func CloneRefOfSelect(n *Select) *Select { } out := *n out.Cache = CloneRefOfBool(n.Cache) + out.With = CloneRefOfWith(n.With) out.From = CloneSliceOfTableExpr(n.From) out.Comments = CloneRefOfParsedComments(n.Comments) out.SelectExprs = CloneSelectExprs(n.SelectExprs) out.Where = CloneRefOfWhere(n.Where) - out.With = CloneRefOfWith(n.With) out.GroupBy = CloneGroupBy(n.GroupBy) out.Having = CloneRefOfWhere(n.Having) out.Windows = CloneNamedWindows(n.Windows) diff --git a/go/vt/sqlparser/ast_copy_on_rewrite.go b/go/vt/sqlparser/ast_copy_on_rewrite.go index 64855669c50..8b731f1c0e2 100644 --- a/go/vt/sqlparser/ast_copy_on_rewrite.go +++ b/go/vt/sqlparser/ast_copy_on_rewrite.go @@ -5128,6 +5128,7 @@ func (c *cow) copyOnRewriteRefOfSelect(n *Select, parent SQLNode) (out SQLNode, } out = n if c.pre == nil || c.pre(n, parent) { + _With, changedWith := c.copyOnRewriteRefOfWith(n.With, n) var changedFrom bool _From := make([]TableExpr, len(n.From)) for x, el := range n.From { @@ -5140,20 +5141,19 @@ func (c *cow) copyOnRewriteRefOfSelect(n *Select, parent SQLNode) (out SQLNode, _Comments, changedComments := c.copyOnRewriteRefOfParsedComments(n.Comments, n) _SelectExprs, changedSelectExprs := c.copyOnRewriteSelectExprs(n.SelectExprs, n) _Where, changedWhere := c.copyOnRewriteRefOfWhere(n.Where, n) - _With, changedWith := c.copyOnRewriteRefOfWith(n.With, n) _GroupBy, changedGroupBy := c.copyOnRewriteGroupBy(n.GroupBy, n) _Having, changedHaving := c.copyOnRewriteRefOfWhere(n.Having, n) _Windows, changedWindows := c.copyOnRewriteNamedWindows(n.Windows, n) _OrderBy, changedOrderBy := c.copyOnRewriteOrderBy(n.OrderBy, n) _Limit, changedLimit := c.copyOnRewriteRefOfLimit(n.Limit, n) _Into, changedInto := c.copyOnRewriteRefOfSelectInto(n.Into, n) - if changedFrom || changedComments || changedSelectExprs || changedWhere || changedWith || changedGroupBy || changedHaving || changedWindows || changedOrderBy || changedLimit || changedInto { + if changedWith || changedFrom || changedComments || changedSelectExprs || changedWhere || changedGroupBy || changedHaving || changedWindows || changedOrderBy || changedLimit || changedInto { res := *n + res.With, _ = _With.(*With) res.From = _From res.Comments, _ = _Comments.(*ParsedComments) res.SelectExprs, _ = _SelectExprs.(SelectExprs) res.Where, _ = _Where.(*Where) - res.With, _ = _With.(*With) res.GroupBy, _ = _GroupBy.(GroupBy) res.Having, _ = _Having.(*Where) res.Windows, _ = _Windows.(NamedWindows) diff --git a/go/vt/sqlparser/ast_equals.go b/go/vt/sqlparser/ast_equals.go index 5a5f50d5338..bb263c65e47 100644 --- a/go/vt/sqlparser/ast_equals.go +++ b/go/vt/sqlparser/ast_equals.go @@ -4122,11 +4122,11 @@ func (cmp *Comparator) RefOfSelect(a, b *Select) bool { a.StraightJoinHint == b.StraightJoinHint && a.SQLCalcFoundRows == b.SQLCalcFoundRows && cmp.RefOfBool(a.Cache, b.Cache) && + cmp.RefOfWith(a.With, b.With) && cmp.SliceOfTableExpr(a.From, b.From) && cmp.RefOfParsedComments(a.Comments, b.Comments) && cmp.SelectExprs(a.SelectExprs, b.SelectExprs) && cmp.RefOfWhere(a.Where, b.Where) && - cmp.RefOfWith(a.With, b.With) && cmp.GroupBy(a.GroupBy, b.GroupBy) && cmp.RefOfWhere(a.Having, b.Having) && cmp.NamedWindows(a.Windows, b.Windows) && diff --git a/go/vt/sqlparser/ast_rewrite.go b/go/vt/sqlparser/ast_rewrite.go index 09ff28cba31..e1c5cb60e59 100644 --- a/go/vt/sqlparser/ast_rewrite.go +++ b/go/vt/sqlparser/ast_rewrite.go @@ -7344,6 +7344,11 @@ func (a *application) rewriteRefOfSelect(parent SQLNode, node *Select, replacer return true } } + if !a.rewriteRefOfWith(node, node.With, func(newNode, parent SQLNode) { + parent.(*Select).With = newNode.(*With) + }) { + return false + } for x, el := range node.From { if !a.rewriteTableExpr(node, el, func(idx int) replacerFunc { return func(newNode, parent SQLNode) { @@ -7368,11 +7373,6 @@ func (a *application) rewriteRefOfSelect(parent SQLNode, node *Select, replacer }) { return false } - if !a.rewriteRefOfWith(node, node.With, func(newNode, parent SQLNode) { - parent.(*Select).With = newNode.(*With) - }) { - return false - } if !a.rewriteGroupBy(node, node.GroupBy, func(newNode, parent SQLNode) { parent.(*Select).GroupBy = newNode.(GroupBy) }) { diff --git a/go/vt/sqlparser/ast_visit.go b/go/vt/sqlparser/ast_visit.go index 802b05ba3ee..7eb418acf46 100644 --- a/go/vt/sqlparser/ast_visit.go +++ b/go/vt/sqlparser/ast_visit.go @@ -3446,6 +3446,9 @@ func VisitRefOfSelect(in *Select, f Visit) error { if cont, err := f(in); err != nil || !cont { return err } + if err := VisitRefOfWith(in.With, f); err != nil { + return err + } for _, el := range in.From { if err := VisitTableExpr(el, f); err != nil { return err @@ -3460,9 +3463,6 @@ func VisitRefOfSelect(in *Select, f Visit) error { if err := VisitRefOfWhere(in.Where, f); err != nil { return err } - if err := VisitRefOfWith(in.With, f); err != nil { - return err - } if err := VisitGroupBy(in.GroupBy, f); err != nil { return err } diff --git a/go/vt/sqlparser/cached_size.go b/go/vt/sqlparser/cached_size.go index d888ac5c747..aa343e9bb9b 100644 --- a/go/vt/sqlparser/cached_size.go +++ b/go/vt/sqlparser/cached_size.go @@ -3595,6 +3595,8 @@ func (cached *Select) CachedSize(alloc bool) int64 { } // field Cache *bool size += hack.RuntimeAllocSize(int64(1)) + // field With *vitess.io/vitess/go/vt/sqlparser.With + size += cached.With.CachedSize(true) // field From []vitess.io/vitess/go/vt/sqlparser.TableExpr { size += hack.RuntimeAllocSize(int64(cap(cached.From)) * int64(16)) @@ -3617,8 +3619,6 @@ func (cached *Select) CachedSize(alloc bool) int64 { } // field Where *vitess.io/vitess/go/vt/sqlparser.Where size += cached.Where.CachedSize(true) - // field With *vitess.io/vitess/go/vt/sqlparser.With - size += cached.With.CachedSize(true) // field GroupBy vitess.io/vitess/go/vt/sqlparser.GroupBy { size += hack.RuntimeAllocSize(int64(cap(cached.GroupBy)) * int64(16)) From 02812c86dc5221ac68430fdd5ae47a9d98897e86 Mon Sep 17 00:00:00 2001 From: Andres Taylor Date: Fri, 20 Oct 2023 11:45:40 +0200 Subject: [PATCH 07/18] refactor: clean up code Signed-off-by: Andres Taylor --- go/vt/vtgate/semantics/table_collector.go | 123 ++++++++++++---------- 1 file changed, 67 insertions(+), 56 deletions(-) diff --git a/go/vt/vtgate/semantics/table_collector.go b/go/vt/vtgate/semantics/table_collector.go index f154e4fe749..a1779fad8cb 100644 --- a/go/vt/vtgate/semantics/table_collector.go +++ b/go/vt/vtgate/semantics/table_collector.go @@ -50,77 +50,88 @@ func (tc *tableCollector) up(cursor *sqlparser.Cursor) error { case *sqlparser.AliasedTableExpr: return tc.visitAliasedTableExpr(node) case *sqlparser.Union: - firstSelect := sqlparser.GetFirstSelect(node) - expanded, selectExprs := getColumnNames(firstSelect.SelectExprs) - info := unionInfo{ - isAuthoritative: expanded, - exprs: selectExprs, - } - tc.unionInfo[node] = info - if !expanded { - return nil - } + return tc.visitUnion(node) + default: + return nil + } +} - size := len(firstSelect.SelectExprs) - info.recursive = make([]TableSet, size) - info.types = make([]evalengine.Type, size) - - _ = sqlparser.VisitAllSelects(node, func(s *sqlparser.Select, idx int) error { - for i, expr := range s.SelectExprs { - ae, ok := expr.(*sqlparser.AliasedExpr) - if !ok { - continue - } - _, recursiveDeps, qt := tc.org.depsForExpr(ae.Expr) - info.recursive[i] = info.recursive[i].Merge(recursiveDeps) - if idx == 0 { - // TODO: we probably should coerce these types together somehow, but I'm not sure how - info.types[i] = qt - } - } - return nil - }) - tc.unionInfo[node] = info +func (tc *tableCollector) visitUnion(union *sqlparser.Union) error { + firstSelect := sqlparser.GetFirstSelect(union) + expanded, selectExprs := getColumnNames(firstSelect.SelectExprs) + info := unionInfo{ + isAuthoritative: expanded, + exprs: selectExprs, + } + tc.unionInfo[union] = info + if !expanded { + return nil } + size := len(firstSelect.SelectExprs) + info.recursive = make([]TableSet, size) + info.types = make([]evalengine.Type, size) + + _ = sqlparser.VisitAllSelects(union, func(s *sqlparser.Select, idx int) error { + for i, expr := range s.SelectExprs { + ae, ok := expr.(*sqlparser.AliasedExpr) + if !ok { + continue + } + _, recursiveDeps, qt := tc.org.depsForExpr(ae.Expr) + info.recursive[i] = info.recursive[i].Merge(recursiveDeps) + if idx == 0 { + // TODO: we probably should coerce these types together somehow, but I'm not sure how + info.types[i] = qt + } + } + return nil + }) + tc.unionInfo[union] = info return nil } func (tc *tableCollector) visitAliasedTableExpr(node *sqlparser.AliasedTableExpr) error { switch t := node.Expr.(type) { case *sqlparser.DerivedTable: - switch sel := t.Select.(type) { - case *sqlparser.Select: - return tc.addSelectDerivedTable(sel, node, node.Columns, node.As) + return tc.handleDerivedTable(node, t) - case *sqlparser.Union: - return tc.addUnionDerivedTable(sel, node, node.Columns, node.As) + case sqlparser.TableName: + return tc.handleTableName(node, t) + } + return nil +} - default: - return vterrors.Errorf(vtrpcpb.Code_INTERNAL, "[BUG] %T in a derived table", sel) - } +func (tc *tableCollector) handleTableName(node *sqlparser.AliasedTableExpr, t sqlparser.TableName) error { + var tbl *vindexes.Table + var vindex vindexes.Vindex + isInfSchema := sqlparser.SystemSchema(t.Qualifier.String()) + var err error + tbl, vindex, _, _, _, err = tc.si.FindTableOrVindex(t) + if err != nil && !isInfSchema { + // if we are dealing with a system table, it might not be available in the vschema, but that is OK + return err + } + if tbl == nil && vindex != nil { + tbl = newVindexTable(t.Name) + } - case sqlparser.TableName: - var tbl *vindexes.Table - var vindex vindexes.Vindex - isInfSchema := sqlparser.SystemSchema(t.Qualifier.String()) - var err error - tbl, vindex, _, _, _, err = tc.si.FindTableOrVindex(t) - if err != nil && !isInfSchema { - // if we are dealing with a system table, it might not be available in the vschema, but that is OK - return err - } - if tbl == nil && vindex != nil { - tbl = newVindexTable(t.Name) - } + scope := tc.scoper.currentScope() + tableInfo := tc.createTable(t, node, tbl, isInfSchema, vindex) - scope := tc.scoper.currentScope() - tableInfo := tc.createTable(t, node, tbl, isInfSchema, vindex) + tc.Tables = append(tc.Tables, tableInfo) + return scope.addTable(tableInfo) +} - tc.Tables = append(tc.Tables, tableInfo) - return scope.addTable(tableInfo) +func (tc *tableCollector) handleDerivedTable(node *sqlparser.AliasedTableExpr, t *sqlparser.DerivedTable) error { + switch sel := t.Select.(type) { + case *sqlparser.Select: + return tc.addSelectDerivedTable(sel, node, node.Columns, node.As) + case *sqlparser.Union: + return tc.addUnionDerivedTable(sel, node, node.Columns, node.As) + default: + return vterrors.Errorf(vtrpcpb.Code_INTERNAL, "[BUG] %T in a derived table", sel) } - return nil } func (tc *tableCollector) addSelectDerivedTable( From f4f57790d2d5abe0c3aaafd15f0a9e48ae182b10 Mon Sep 17 00:00:00 2001 From: Andres Taylor Date: Fri, 20 Oct 2023 12:01:24 +0200 Subject: [PATCH 08/18] feat: fail CTEs that clash on name Signed-off-by: Andres Taylor --- go/vt/vtgate/semantics/analyzer_test.go | 6 ++++++ go/vt/vtgate/semantics/scoper.go | 12 ++++++++++++ go/vt/vtgate/semantics/table_collector.go | 3 +++ 3 files changed, 21 insertions(+) diff --git a/go/vt/vtgate/semantics/analyzer_test.go b/go/vt/vtgate/semantics/analyzer_test.go index 818fc65f3e5..ffd7fd31f23 100644 --- a/go/vt/vtgate/semantics/analyzer_test.go +++ b/go/vt/vtgate/semantics/analyzer_test.go @@ -838,6 +838,12 @@ func TestInvalidQueries(t *testing.T) { }, { sql: "WITH RECURSIVE cte (n) AS (SELECT 1 UNION ALL SELECT n + 1 FROM cte WHERE n < 5) SELECT * FROM cte", serr: "VT12001: unsupported: recursive common table expression", + }, { + sql: "with x as (select 1), x as (select 1) select * from x", + serr: "VT03013: not unique table/alias: 'x'", + }, { + // should not fail, same name is valid as long as it's not in the same scope + sql: "with x as (with x as (select 1) select * from x) select * from x", }} for _, tc := range tcases { diff --git a/go/vt/vtgate/semantics/scoper.go b/go/vt/vtgate/semantics/scoper.go index 5d27b31b84e..25bda53a256 100644 --- a/go/vt/vtgate/semantics/scoper.go +++ b/go/vt/vtgate/semantics/scoper.go @@ -46,6 +46,7 @@ type ( isUnion bool joinUsing map[string]TableSet stmtScope bool + ctes map[string]*sqlparser.CommonTableExpr } ) @@ -283,9 +284,20 @@ func newScope(parent *scope) *scope { return &scope{ parent: parent, joinUsing: map[string]TableSet{}, + ctes: map[string]*sqlparser.CommonTableExpr{}, } } +func (s *scope) addCTE(cte *sqlparser.CommonTableExpr) error { + name := cte.ID.String() + _, exists := s.ctes[name] + if exists { + return vterrors.VT03013(name) + } + s.ctes[name] = cte + return nil +} + func (s *scope) addTable(info TableInfo) error { name, err := info.Name() if err != nil { diff --git a/go/vt/vtgate/semantics/table_collector.go b/go/vt/vtgate/semantics/table_collector.go index a1779fad8cb..19b08fbb08c 100644 --- a/go/vt/vtgate/semantics/table_collector.go +++ b/go/vt/vtgate/semantics/table_collector.go @@ -51,6 +51,9 @@ func (tc *tableCollector) up(cursor *sqlparser.Cursor) error { return tc.visitAliasedTableExpr(node) case *sqlparser.Union: return tc.visitUnion(node) + case *sqlparser.CommonTableExpr: + scope := tc.scoper.currentScope() + return scope.addCTE(node) default: return nil } From 9fc8865045cd97a2cefb5ec196a44edc923932a6 Mon Sep 17 00:00:00 2001 From: Andres Taylor Date: Mon, 23 Oct 2023 15:21:34 +0200 Subject: [PATCH 09/18] feat: allow WITH on SELECT and UNION Signed-off-by: Andres Taylor --- go/vt/sqlparser/ast_format.go | 3 ++ go/vt/sqlparser/ast_format_fast.go | 3 ++ go/vt/vtgate/planbuilder/select.go | 11 ------ go/vt/vtgate/semantics/early_rewriter.go | 35 ++++++++++++++++++- go/vt/vtgate/semantics/early_rewriter_test.go | 27 ++++++++++++++ go/vt/vtgate/semantics/scoper.go | 19 +++++++++- go/vt/vtgate/semantics/table_collector.go | 3 -- 7 files changed, 85 insertions(+), 16 deletions(-) diff --git a/go/vt/sqlparser/ast_format.go b/go/vt/sqlparser/ast_format.go index 1f82d4e3357..f4b6a68ab48 100644 --- a/go/vt/sqlparser/ast_format.go +++ b/go/vt/sqlparser/ast_format.go @@ -133,6 +133,9 @@ func (node *Insert) Format(buf *TrackedBuffer) { // Format formats the node. func (node *With) Format(buf *TrackedBuffer) { + if len(node.CTEs) == 0 { + return + } buf.astPrintf(node, "with ") if node.Recursive { diff --git a/go/vt/sqlparser/ast_format_fast.go b/go/vt/sqlparser/ast_format_fast.go index 378d545865a..e95470f8fa5 100644 --- a/go/vt/sqlparser/ast_format_fast.go +++ b/go/vt/sqlparser/ast_format_fast.go @@ -201,6 +201,9 @@ func (node *Insert) formatFast(buf *TrackedBuffer) { // formatFast formats the node. func (node *With) formatFast(buf *TrackedBuffer) { + if len(node.CTEs) == 0 { + return + } buf.WriteString("with ") if node.Recursive { diff --git a/go/vt/vtgate/planbuilder/select.go b/go/vt/vtgate/planbuilder/select.go index 20b4c914e63..44976815bd2 100644 --- a/go/vt/vtgate/planbuilder/select.go +++ b/go/vt/vtgate/planbuilder/select.go @@ -38,17 +38,6 @@ func gen4SelectStmtPlanner( reservedVars *sqlparser.ReservedVars, vschema plancontext.VSchema, ) (*planResult, error) { - switch node := stmt.(type) { - case *sqlparser.Select: - if node.With != nil { - return nil, vterrors.VT12001("WITH expression in SELECT statement") - } - case *sqlparser.Union: - if node.With != nil { - return nil, vterrors.VT12001("WITH expression in UNION statement") - } - } - sel, isSel := stmt.(*sqlparser.Select) if isSel { // handle dual table for processing at vtgate. diff --git a/go/vt/vtgate/semantics/early_rewriter.go b/go/vt/vtgate/semantics/early_rewriter.go index 5f22ef5947e..9464276a699 100644 --- a/go/vt/vtgate/semantics/early_rewriter.go +++ b/go/vt/vtgate/semantics/early_rewriter.go @@ -57,7 +57,40 @@ func (r *earlyRewriter) down(cursor *sqlparser.Cursor) error { return handleCollateExpr(r, node) case *sqlparser.ComparisonExpr: return handleComparisonExpr(cursor, node) + case *sqlparser.With: + return r.handleWith(node) + case *sqlparser.AliasedTableExpr: + tbl, ok := node.Expr.(sqlparser.TableName) + if !ok || !tbl.Qualifier.IsEmpty() { + return nil + } + scope := r.scoper.currentScope() + cte := scope.findCTE(tbl.Name.String()) + if cte == nil { + return nil + } + if node.As.IsEmpty() { + node.As = tbl.Name + } + node.Expr = &sqlparser.DerivedTable{ + Select: cte.Subquery.Select, + } + if len(cte.Columns) > 0 { + node.Columns = cte.Columns + } + } + return nil +} + +func (r *earlyRewriter) handleWith(node *sqlparser.With) error { + scope := r.scoper.currentScope() + for _, cte := range node.CTEs { + err := scope.addCTE(cte) + if err != nil { + return err + } } + node.CTEs = nil return nil } @@ -84,7 +117,7 @@ func (r *earlyRewriter) up(cursor *sqlparser.Cursor) error { return err } - // since the binder has already been over the join, we need to invoke it again so it + // since the binder has already been over the join, we need to invoke it again, so it // can bind columns to the right tables sqlparser.Rewrite(node.Condition.On, nil, func(cursor *sqlparser.Cursor) bool { innerErr := r.binder.up(cursor) diff --git a/go/vt/vtgate/semantics/early_rewriter_test.go b/go/vt/vtgate/semantics/early_rewriter_test.go index a8e84da6ddb..daaef4ad214 100644 --- a/go/vt/vtgate/semantics/early_rewriter_test.go +++ b/go/vt/vtgate/semantics/early_rewriter_test.go @@ -542,3 +542,30 @@ func TestConstantFolding(t *testing.T) { }) } } + +// TestCTEToDerivedTableRewrite checks that CTEs are correctly rewritten to derived tables +func TestCTEToDerivedTableRewrite(t *testing.T) { + cDB := "db" + tcases := []struct { + sql string + expSQL string + }{{ + sql: "with x as (select 1 as id) select * from x", + expSQL: "select id from (select 1 as id from dual) as x", + }, { + sql: "with x as (select 1 as id), z as (select id + 1 from x) select * from z", + expSQL: "select `id + 1` from (select id + 1 from (select 1 as id from dual) as x) as z", + }, { + sql: "with x(id) as (select 1) select * from x", + expSQL: "select id from (select 1 as id from dual) as x(id``)", + }} + for _, tcase := range tcases { + t.Run(tcase.sql, func(t *testing.T) { + ast, err := sqlparser.Parse(tcase.sql) + require.NoError(t, err) + _, err = Analyze(ast, cDB, fakeSchemaInfo()) + require.NoError(t, err) + require.Equal(t, tcase.expSQL, sqlparser.String(ast)) + }) + } +} diff --git a/go/vt/vtgate/semantics/scoper.go b/go/vt/vtgate/semantics/scoper.go index 25bda53a256..f07f836abb8 100644 --- a/go/vt/vtgate/semantics/scoper.go +++ b/go/vt/vtgate/semantics/scoper.go @@ -134,7 +134,13 @@ func (s *scoper) enterJoinScope(cursor *sqlparser.Cursor) { // can only see the two tables involved in the JOIN, and no other tables of that select statement. // They are allowed to see the tables of the outer select query. // To create this special context, we will find the parent scope of the select statement involved. - nScope := newScope(s.currentScope().findParentScopeOfStatement()) + currScope := s.currentScope() + stmtScope := currScope.findParentScopeOfStatement() + nScope := newScope(stmtScope) + if stmtScope == nil { + // TODO: this feels hacky. revisit with a better plan + nScope.ctes = currScope.ctes + } nScope.stmt = cursor.Parent().(*sqlparser.Select) s.push(nScope) } @@ -343,3 +349,14 @@ func (s *scope) findParentScopeOfStatement() *scope { } return s.parent.findParentScopeOfStatement() } + +// findCTE will search in this scope, and then recursively search the parents +func (s *scope) findCTE(name string) *sqlparser.CommonTableExpr { + cte, found := s.ctes[name] + if found || s.parent == nil { + // if we don't have a parent, we'll return + // whatever we have, even if it happens to be nil + return cte + } + return s.parent.findCTE(name) +} diff --git a/go/vt/vtgate/semantics/table_collector.go b/go/vt/vtgate/semantics/table_collector.go index 19b08fbb08c..a1779fad8cb 100644 --- a/go/vt/vtgate/semantics/table_collector.go +++ b/go/vt/vtgate/semantics/table_collector.go @@ -51,9 +51,6 @@ func (tc *tableCollector) up(cursor *sqlparser.Cursor) error { return tc.visitAliasedTableExpr(node) case *sqlparser.Union: return tc.visitUnion(node) - case *sqlparser.CommonTableExpr: - scope := tc.scoper.currentScope() - return scope.addCTE(node) default: return nil } From 93812bdd5693abb97b60d28dc5f417a7c0d5d3f9 Mon Sep 17 00:00:00 2001 From: Andres Taylor Date: Mon, 23 Oct 2023 15:52:52 +0200 Subject: [PATCH 10/18] stop inifite loops Signed-off-by: Andres Taylor --- go/vt/vtgate/semantics/early_rewriter_test.go | 2 +- go/vt/vtgate/semantics/scoper.go | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/go/vt/vtgate/semantics/early_rewriter_test.go b/go/vt/vtgate/semantics/early_rewriter_test.go index daaef4ad214..ce8a014d685 100644 --- a/go/vt/vtgate/semantics/early_rewriter_test.go +++ b/go/vt/vtgate/semantics/early_rewriter_test.go @@ -557,7 +557,7 @@ func TestCTEToDerivedTableRewrite(t *testing.T) { expSQL: "select `id + 1` from (select id + 1 from (select 1 as id from dual) as x) as z", }, { sql: "with x(id) as (select 1) select * from x", - expSQL: "select id from (select 1 as id from dual) as x(id``)", + expSQL: "select id from (select 1 from dual) as x(id)", }} for _, tcase := range tcases { t.Run(tcase.sql, func(t *testing.T) { diff --git a/go/vt/vtgate/semantics/scoper.go b/go/vt/vtgate/semantics/scoper.go index f07f836abb8..2827f171b85 100644 --- a/go/vt/vtgate/semantics/scoper.go +++ b/go/vt/vtgate/semantics/scoper.go @@ -300,10 +300,29 @@ func (s *scope) addCTE(cte *sqlparser.CommonTableExpr) error { if exists { return vterrors.VT03013(name) } + if err := checkForInvalidAliasUse(cte, name); err != nil { + return err + } s.ctes[name] = cte return nil } +func checkForInvalidAliasUse(cte *sqlparser.CommonTableExpr, name string) (err error) { + // TODO I'm sure there is a better. way, but we need to do this to stop infinite loops from occurring + down := func(node sqlparser.SQLNode, parent sqlparser.SQLNode) bool { + tbl, ok := node.(sqlparser.TableName) + if !ok || !tbl.Qualifier.IsEmpty() { + return err == nil + } + if tbl.Name.String() == name { + err = vterrors.VT12001("do not support CTE that use the CTE alias inside the CTE query") + } + return err == nil + } + _ = sqlparser.CopyOnRewrite(cte.Subquery.Select, down, nil, nil) + return err +} + func (s *scope) addTable(info TableInfo) error { name, err := info.Name() if err != nil { From 99fd17e6b627b6974d5321126aaabafa29f05a03 Mon Sep 17 00:00:00 2001 From: Andres Taylor Date: Tue, 24 Oct 2023 10:10:34 +0200 Subject: [PATCH 11/18] test: add scoping WITH tests Signed-off-by: Andres Taylor --- go/vt/vtgate/semantics/analyzer_test.go | 110 ++++++++++++++++++++++-- 1 file changed, 102 insertions(+), 8 deletions(-) diff --git a/go/vt/vtgate/semantics/analyzer_test.go b/go/vt/vtgate/semantics/analyzer_test.go index ffd7fd31f23..61dd1ee4a9d 100644 --- a/go/vt/vtgate/semantics/analyzer_test.go +++ b/go/vt/vtgate/semantics/analyzer_test.go @@ -180,14 +180,6 @@ func TestBindingMultiTablePositive(t *testing.T) { query: "select case t.col when s.col then r.col else u.col end from t, s, r, w, u", deps: MergeTableSets(TS0, TS1, TS2, TS4), numberOfTables: 4, - // }, { - // TODO: move to subquery - // make sure that we don't let sub-query dependencies leak out by mistake - // query: "select t.col + (select 42 from s) from t", - // deps: TS0, - // }, { - // query: "select (select 42 from s where r.id = s.id) from r", - // deps: TS0 | TS1, }, { query: "select u1.a + u2.a from u1, u2", deps: MergeTableSets(TS0, TS1), @@ -990,6 +982,108 @@ func TestScopingWDerivedTables(t *testing.T) { } } +func TestScopingWithWITH(t *testing.T) { + queries := []struct { + query string + errorMessage string + recursive, direct TableSet + }{ + { + query: "with t as (select x as id from user) select id from t", + recursive: TS0, + direct: TS1, + }, { + query: "with t as (select foo as id from user) select id from t", + recursive: TS0, + direct: TS1, + }, { + query: "with c as (select x as foo from user), t as (select foo as id from c) select id from t", + recursive: TS0, + direct: TS2, + }, { + query: "with t as (select foo as id from user) select t.id from t", + recursive: TS0, + direct: TS1, + }, { + query: "select t.id2 from (select foo as id from user) as t", + errorMessage: "column 't.id2' not found", + }, { + query: "with t as (select 42 as id) select id from t", + recursive: T0, + direct: TS1, + }, { + query: "with t as (select 42 as id) select t.id from t", + recursive: T0, + direct: TS1, + }, { + query: "with t as (select 42 as id) select ks.t.id from t", + errorMessage: "column 'ks.t.id' not found", + }, { + query: "with t as (select id, id from user) select * from t", + errorMessage: "Duplicate column name 'id'", + }, { + query: "with t as (select id as baz from user) select t.baz = 1 from t", + direct: TS1, + recursive: TS0, + }, { + query: "with t as (select * from user, music) select t.id from t", + direct: TS2, + recursive: MergeTableSets(TS0, TS1), + }, { + query: "with t as (select * from user, music) select t.id from t order by t.id", + direct: TS2, + recursive: MergeTableSets(TS0, TS1), + }, { + query: "with t as (select * from user) select t.id from t join user as u on t.id = u.id", + direct: TS1, + recursive: TS0, + }, { + query: "with t as (select t1.id, t1.col1 from t1 join t2) select t.col1 from t3 ua join t", + direct: TS3, + recursive: TS1, + }, { + query: "with uu as (select id from t1) select uu.test from uu", + errorMessage: "column 'uu.test' not found", + }, { + query: "with uu as (select id as col from t1) select uu.id from uu", + errorMessage: "column 'uu.id' not found", + }, { + query: "select uu.id from (select id as col from t1) uu", + errorMessage: "column 'uu.id' not found", + }, { + query: "select uu.id from (select id from t1) as uu where exists (select * from t2 as uu where uu.id = uu.uid)", + direct: TS1, + recursive: TS0, + }, { + query: "select 1 from user uu where exists (select 1 from user where exists (select 1 from (select 1 from t1) uu where uu.user_id = uu.id))", + direct: T0, + recursive: T0, + }} + for _, query := range queries { + t.Run(query.query, func(t *testing.T) { + parse, err := sqlparser.Parse(query.query) + require.NoError(t, err) + st, err := Analyze(parse, "user", &FakeSI{ + Tables: map[string]*vindexes.Table{ + "t": {Name: sqlparser.NewIdentifierCS("t")}, + }, + }) + + switch { + case query.errorMessage != "" && err != nil: + require.EqualError(t, err, query.errorMessage) + case query.errorMessage != "": + require.EqualError(t, st.NotUnshardedErr, query.errorMessage) + default: + require.NoError(t, err) + sel := parse.(*sqlparser.Select) + assert.Equal(t, query.recursive, st.RecursiveDeps(extract(sel, 0)), "RecursiveDeps") + assert.Equal(t, query.direct, st.DirectDeps(extract(sel, 0)), "DirectDeps") + } + }) + } +} + func TestJoinPredicateDependencies(t *testing.T) { // create table t() // create table t1(id bigint) From 4b740a493e0d898883857d66bee25f8610937967 Mon Sep 17 00:00:00 2001 From: Andres Taylor Date: Tue, 24 Oct 2023 10:35:20 +0200 Subject: [PATCH 12/18] feat: support WITH with UNION Signed-off-by: Andres Taylor --- go/vt/sqlparser/ast.go | 2 +- go/vt/sqlparser/ast_clone.go | 2 +- go/vt/sqlparser/ast_copy_on_rewrite.go | 6 +-- go/vt/sqlparser/ast_equals.go | 2 +- go/vt/sqlparser/ast_rewrite.go | 10 ++-- go/vt/sqlparser/ast_visit.go | 6 +-- .../planbuilder/testdata/from_cases.json | 54 +++++++++++++++++++ .../testdata/unsupported_cases.json | 10 ---- go/vt/vtgate/semantics/early_rewriter.go | 41 +++++++------- go/vt/vtgate/semantics/scoper.go | 15 ++++-- 10 files changed, 103 insertions(+), 45 deletions(-) diff --git a/go/vt/sqlparser/ast.go b/go/vt/sqlparser/ast.go index 6cc921e51be..03a1b90397e 100644 --- a/go/vt/sqlparser/ast.go +++ b/go/vt/sqlparser/ast.go @@ -293,11 +293,11 @@ type ( // Union represents a UNION statement. Union struct { + With *With Left SelectStatement Right SelectStatement Distinct bool OrderBy OrderBy - With *With Limit *Limit Lock Lock Into *SelectInto diff --git a/go/vt/sqlparser/ast_clone.go b/go/vt/sqlparser/ast_clone.go index 39860a678dd..b29b4c90047 100644 --- a/go/vt/sqlparser/ast_clone.go +++ b/go/vt/sqlparser/ast_clone.go @@ -3117,10 +3117,10 @@ func CloneRefOfUnion(n *Union) *Union { return nil } out := *n + out.With = CloneRefOfWith(n.With) out.Left = CloneSelectStatement(n.Left) out.Right = CloneSelectStatement(n.Right) out.OrderBy = CloneOrderBy(n.OrderBy) - out.With = CloneRefOfWith(n.With) out.Limit = CloneRefOfLimit(n.Limit) out.Into = CloneRefOfSelectInto(n.Into) return &out diff --git a/go/vt/sqlparser/ast_copy_on_rewrite.go b/go/vt/sqlparser/ast_copy_on_rewrite.go index 8b731f1c0e2..86dda29ebcf 100644 --- a/go/vt/sqlparser/ast_copy_on_rewrite.go +++ b/go/vt/sqlparser/ast_copy_on_rewrite.go @@ -5977,18 +5977,18 @@ func (c *cow) copyOnRewriteRefOfUnion(n *Union, parent SQLNode) (out SQLNode, ch } out = n if c.pre == nil || c.pre(n, parent) { + _With, changedWith := c.copyOnRewriteRefOfWith(n.With, n) _Left, changedLeft := c.copyOnRewriteSelectStatement(n.Left, n) _Right, changedRight := c.copyOnRewriteSelectStatement(n.Right, n) _OrderBy, changedOrderBy := c.copyOnRewriteOrderBy(n.OrderBy, n) - _With, changedWith := c.copyOnRewriteRefOfWith(n.With, n) _Limit, changedLimit := c.copyOnRewriteRefOfLimit(n.Limit, n) _Into, changedInto := c.copyOnRewriteRefOfSelectInto(n.Into, n) - if changedLeft || changedRight || changedOrderBy || changedWith || changedLimit || changedInto { + if changedWith || changedLeft || changedRight || changedOrderBy || changedLimit || changedInto { res := *n + res.With, _ = _With.(*With) res.Left, _ = _Left.(SelectStatement) res.Right, _ = _Right.(SelectStatement) res.OrderBy, _ = _OrderBy.(OrderBy) - res.With, _ = _With.(*With) res.Limit, _ = _Limit.(*Limit) res.Into, _ = _Into.(*SelectInto) out = &res diff --git a/go/vt/sqlparser/ast_equals.go b/go/vt/sqlparser/ast_equals.go index bb263c65e47..9beed3a8242 100644 --- a/go/vt/sqlparser/ast_equals.go +++ b/go/vt/sqlparser/ast_equals.go @@ -4590,10 +4590,10 @@ func (cmp *Comparator) RefOfUnion(a, b *Union) bool { return false } return a.Distinct == b.Distinct && + cmp.RefOfWith(a.With, b.With) && cmp.SelectStatement(a.Left, b.Left) && cmp.SelectStatement(a.Right, b.Right) && cmp.OrderBy(a.OrderBy, b.OrderBy) && - cmp.RefOfWith(a.With, b.With) && cmp.RefOfLimit(a.Limit, b.Limit) && a.Lock == b.Lock && cmp.RefOfSelectInto(a.Into, b.Into) diff --git a/go/vt/sqlparser/ast_rewrite.go b/go/vt/sqlparser/ast_rewrite.go index e1c5cb60e59..0121695fe8c 100644 --- a/go/vt/sqlparser/ast_rewrite.go +++ b/go/vt/sqlparser/ast_rewrite.go @@ -8594,6 +8594,11 @@ func (a *application) rewriteRefOfUnion(parent SQLNode, node *Union, replacer re return true } } + if !a.rewriteRefOfWith(node, node.With, func(newNode, parent SQLNode) { + parent.(*Union).With = newNode.(*With) + }) { + return false + } if !a.rewriteSelectStatement(node, node.Left, func(newNode, parent SQLNode) { parent.(*Union).Left = newNode.(SelectStatement) }) { @@ -8609,11 +8614,6 @@ func (a *application) rewriteRefOfUnion(parent SQLNode, node *Union, replacer re }) { return false } - if !a.rewriteRefOfWith(node, node.With, func(newNode, parent SQLNode) { - parent.(*Union).With = newNode.(*With) - }) { - return false - } if !a.rewriteRefOfLimit(node, node.Limit, func(newNode, parent SQLNode) { parent.(*Union).Limit = newNode.(*Limit) }) { diff --git a/go/vt/sqlparser/ast_visit.go b/go/vt/sqlparser/ast_visit.go index 7eb418acf46..a88d689f102 100644 --- a/go/vt/sqlparser/ast_visit.go +++ b/go/vt/sqlparser/ast_visit.go @@ -3972,6 +3972,9 @@ func VisitRefOfUnion(in *Union, f Visit) error { if cont, err := f(in); err != nil || !cont { return err } + if err := VisitRefOfWith(in.With, f); err != nil { + return err + } if err := VisitSelectStatement(in.Left, f); err != nil { return err } @@ -3981,9 +3984,6 @@ func VisitRefOfUnion(in *Union, f Visit) error { if err := VisitOrderBy(in.OrderBy, f); err != nil { return err } - if err := VisitRefOfWith(in.With, f); err != nil { - return err - } if err := VisitRefOfLimit(in.Limit, f); err != nil { return err } diff --git a/go/vt/vtgate/planbuilder/testdata/from_cases.json b/go/vt/vtgate/planbuilder/testdata/from_cases.json index b6a73f4c318..714efff23c8 100644 --- a/go/vt/vtgate/planbuilder/testdata/from_cases.json +++ b/go/vt/vtgate/planbuilder/testdata/from_cases.json @@ -4050,5 +4050,59 @@ "comment": "select with a target destination", "query": "select * from `user[-]`.user_metadata", "plan": "VT09017: SELECT with a target destination is not allowed" + }, + { + "comment": "simple WITH query", + "query": "with x as (select * from user) select * from x", + "plan": { + "QueryType": "SELECT", + "Original": "with x as (select * from user) select * from x", + "Instructions": { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select * from (select * from `user` where 1 != 1) as x where 1 != 1", + "Query": "select * from (select * from `user`) as x", + "Table": "`user`" + }, + "TablesUsed": [ + "user.user" + ] + } + }, + { + "comment": "UNION with WITH clause", + "query": "with x as (select id, foo from user) select * from x union select * from x", + "plan": { + "QueryType": "SELECT", + "Original": "with x as (select id, foo from user) select * from x union select * from x", + "Instructions": { + "OperatorType": "Distinct", + "Collations": [ + "(0:2)", + "(1:3)" + ], + "ResultColumns": 2, + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select id, foo, weight_string(id), weight_string(foo) from (select id, foo from (select id, foo from `user` where 1 != 1) as x where 1 != 1 union select id, foo from (select id, foo from `user` where 1 != 1) as x where 1 != 1) as dt where 1 != 1", + "Query": "select id, foo, weight_string(id), weight_string(foo) from (select id, foo from (select id, foo from `user`) as x union select id, foo from (select id, foo from `user`) as x) as dt", + "Table": "`user`" + } + ] + }, + "TablesUsed": [ + "user.user" + ] + } } ] diff --git a/go/vt/vtgate/planbuilder/testdata/unsupported_cases.json b/go/vt/vtgate/planbuilder/testdata/unsupported_cases.json index ea4383db911..fe51b0a1678 100644 --- a/go/vt/vtgate/planbuilder/testdata/unsupported_cases.json +++ b/go/vt/vtgate/planbuilder/testdata/unsupported_cases.json @@ -254,16 +254,6 @@ "query": "with x as (select * from user) update x set name = 'f'", "plan": "VT12001: unsupported: WITH expression in UPDATE statement" }, - { - "comment": "unsupported with clause in select statement", - "query": "with x as (select * from user) select * from x", - "plan": "VT12001: unsupported: WITH expression in SELECT statement" - }, - { - "comment": "unsupported with clause in union statement", - "query": "with x as (select * from user) select * from x union select * from x", - "plan": "VT12001: unsupported: WITH expression in UNION statement" - }, { "comment": "insert having subquery in row values", "query": "insert into user(id, name) values ((select 1 from user where id = 1), 'A')", diff --git a/go/vt/vtgate/semantics/early_rewriter.go b/go/vt/vtgate/semantics/early_rewriter.go index 9464276a699..c7452f3b5ba 100644 --- a/go/vt/vtgate/semantics/early_rewriter.go +++ b/go/vt/vtgate/semantics/early_rewriter.go @@ -60,24 +60,29 @@ func (r *earlyRewriter) down(cursor *sqlparser.Cursor) error { case *sqlparser.With: return r.handleWith(node) case *sqlparser.AliasedTableExpr: - tbl, ok := node.Expr.(sqlparser.TableName) - if !ok || !tbl.Qualifier.IsEmpty() { - return nil - } - scope := r.scoper.currentScope() - cte := scope.findCTE(tbl.Name.String()) - if cte == nil { - return nil - } - if node.As.IsEmpty() { - node.As = tbl.Name - } - node.Expr = &sqlparser.DerivedTable{ - Select: cte.Subquery.Select, - } - if len(cte.Columns) > 0 { - node.Columns = cte.Columns - } + return r.handleAliasedTable(node) + } + return nil +} + +func (r *earlyRewriter) handleAliasedTable(node *sqlparser.AliasedTableExpr) error { + tbl, ok := node.Expr.(sqlparser.TableName) + if !ok || !tbl.Qualifier.IsEmpty() { + return nil + } + scope := r.scoper.currentScope() + cte := scope.findCTE(tbl.Name.String()) + if cte == nil { + return nil + } + if node.As.IsEmpty() { + node.As = tbl.Name + } + node.Expr = &sqlparser.DerivedTable{ + Select: cte.Subquery.Select, + } + if len(cte.Columns) > 0 { + node.Columns = cte.Columns } return nil } diff --git a/go/vt/vtgate/semantics/scoper.go b/go/vt/vtgate/semantics/scoper.go index 2827f171b85..c3685913376 100644 --- a/go/vt/vtgate/semantics/scoper.go +++ b/go/vt/vtgate/semantics/scoper.go @@ -66,6 +66,8 @@ func (s *scoper) down(cursor *sqlparser.Cursor) error { s.pushDMLScope(node) case *sqlparser.Select: s.pushSelectScope(node) + case *sqlparser.Union: + s.pushUnionScope(node) case sqlparser.TableExpr: s.enterJoinScope(cursor) case sqlparser.SelectExprs: @@ -75,14 +77,21 @@ func (s *scoper) down(cursor *sqlparser.Cursor) error { case sqlparser.GroupBy: return s.addColumnInfoForGroupBy(cursor, node) case *sqlparser.Where: - if node.Type != sqlparser.HavingClause { - break + if node.Type == sqlparser.HavingClause { + return s.createSpecialScopePostProjection(cursor.Parent()) } - return s.createSpecialScopePostProjection(cursor.Parent()) } return nil } +func (s *scoper) pushUnionScope(union *sqlparser.Union) { + currentScope := s.currentScope() + currScope := newScope(currentScope) + currScope.stmtScope = true + currScope.stmt = union + s.push(currScope) +} + func (s *scoper) addColumnInfoForGroupBy(cursor *sqlparser.Cursor, node sqlparser.GroupBy) error { err := s.createSpecialScopePostProjection(cursor.Parent()) if err != nil { From 1103e2fd2b09d9fbc29c791e340aa559944c1973 Mon Sep 17 00:00:00 2001 From: Andres Taylor Date: Tue, 24 Oct 2023 13:26:10 +0200 Subject: [PATCH 13/18] test: add CTE plan tests Signed-off-by: Andres Taylor --- go/vt/vtgate/planbuilder/plan_test.go | 1 + .../planbuilder/testdata/cte_cases.json | 1825 +++++++++++++++++ .../testdata/unsupported_cases.json | 5 + 3 files changed, 1831 insertions(+) create mode 100644 go/vt/vtgate/planbuilder/testdata/cte_cases.json diff --git a/go/vt/vtgate/planbuilder/plan_test.go b/go/vt/vtgate/planbuilder/plan_test.go index 96d957d8ed4..472648828ef 100644 --- a/go/vt/vtgate/planbuilder/plan_test.go +++ b/go/vt/vtgate/planbuilder/plan_test.go @@ -96,6 +96,7 @@ func TestPlan(t *testing.T) { testFile(t, "reference_cases.json", testOutputTempDir, vschemaWrapper, false) testFile(t, "vexplain_cases.json", testOutputTempDir, vschemaWrapper, false) testFile(t, "misc_cases.json", testOutputTempDir, vschemaWrapper, false) + testFile(t, "cte_cases.json", testOutputTempDir, vschemaWrapper, false) } // TestForeignKeyPlanning tests the planning of foreign keys in a managed mode by Vitess. diff --git a/go/vt/vtgate/planbuilder/testdata/cte_cases.json b/go/vt/vtgate/planbuilder/testdata/cte_cases.json new file mode 100644 index 00000000000..5ff06a07eae --- /dev/null +++ b/go/vt/vtgate/planbuilder/testdata/cte_cases.json @@ -0,0 +1,1825 @@ +[ + { + "comment": "with t as (select count(*) as a from user) select a from t", + "query": "with t as (select count(*) as a from user) select a from t", + "plan": { + "QueryType": "SELECT", + "Original": "with t as (select count(*) as a from user) select a from t", + "Instructions": { + "OperatorType": "Aggregate", + "Variant": "Scalar", + "Aggregates": "sum_count_star(0) AS a", + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select count(*) as a from `user` where 1 != 1", + "Query": "select count(*) as a from `user`", + "Table": "`user`" + } + ] + }, + "TablesUsed": [ + "user.user" + ] + } + }, + { + "comment": "with a as (select user.col, user_extra.extra from user join user_extra on user.id = user_extra.user_id order by user_extra.extra) select count(*) from a", + "query": "with a as (select user.col, user_extra.extra from user join user_extra on user.id = user_extra.user_id order by user_extra.extra) select count(*) from a", + "plan": { + "QueryType": "SELECT", + "Original": "with a as (select user.col, user_extra.extra from user join user_extra on user.id = user_extra.user_id order by user_extra.extra) select count(*) from a", + "Instructions": { + "OperatorType": "Aggregate", + "Variant": "Scalar", + "Aggregates": "sum_count_star(0) AS count(*)", + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select count(*) from (select `user`.col, user_extra.extra from `user`, user_extra where 1 != 1) as a where 1 != 1", + "Query": "select count(*) from (select `user`.col, user_extra.extra from `user`, user_extra where `user`.id = user_extra.user_id) as a", + "Table": "`user`, user_extra" + } + ] + }, + "TablesUsed": [ + "user.user", + "user.user_extra" + ] + } + }, + { + "comment": "with a as (select user.col, user_extra.extra from user join user_extra on user.id = user_extra.user_id order by user_extra.extra) select col from a", + "query": "with a as (select user.col, user_extra.extra from user join user_extra on user.id = user_extra.user_id order by user_extra.extra) select col from a", + "plan": { + "QueryType": "SELECT", + "Original": "with a as (select user.col, user_extra.extra from user join user_extra on user.id = user_extra.user_id order by user_extra.extra) select col from a", + "Instructions": { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select col from (select `user`.col, user_extra.extra from `user`, user_extra where 1 != 1) as a where 1 != 1", + "Query": "select col from (select `user`.col, user_extra.extra from `user`, user_extra where `user`.id = user_extra.user_id) as a", + "Table": "`user`, user_extra" + }, + "TablesUsed": [ + "user.user", + "user.user_extra" + ] + } + }, + { + "comment": "with a as (select user.col, user_extra.extra from user join user_extra on user.id = user_extra.user_id order by user_extra.extra) select col, count(*) from a group by col", + "query": "with a as (select user.col, user_extra.extra from user join user_extra on user.id = user_extra.user_id order by user_extra.extra) select col, count(*) from a group by col", + "plan": { + "QueryType": "SELECT", + "Original": "with a as (select user.col, user_extra.extra from user join user_extra on user.id = user_extra.user_id order by user_extra.extra) select col, count(*) from a group by col", + "Instructions": { + "OperatorType": "Aggregate", + "Variant": "Ordered", + "Aggregates": "sum_count_star(1) AS count(*)", + "GroupBy": "0", + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select col, count(*) from (select `user`.col, user_extra.extra from `user`, user_extra where 1 != 1) as a where 1 != 1 group by col", + "OrderBy": "0 ASC", + "Query": "select col, count(*) from (select `user`.col, user_extra.extra from `user`, user_extra where `user`.id = user_extra.user_id) as a group by col order by col asc", + "Table": "`user`, user_extra" + } + ] + }, + "TablesUsed": [ + "user.user", + "user.user_extra" + ] + } + }, + { + "comment": "with t as (select user.col as col, 32 from user join user_extra) select sum(col) from t", + "query": "with t as (select user.col as col, 32 from user join user_extra) select sum(col) from t", + "plan": { + "QueryType": "SELECT", + "Original": "with t as (select user.col as col, 32 from user join user_extra) select sum(col) from t", + "Instructions": { + "OperatorType": "Aggregate", + "Variant": "Scalar", + "Aggregates": "sum(0) AS sum(col)", + "Inputs": [ + { + "OperatorType": "Projection", + "Expressions": [ + "[COLUMN 0] * [COLUMN 1] as sum(col)" + ], + "Inputs": [ + { + "OperatorType": "Join", + "Variant": "Join", + "JoinColumnIndexes": "L:0,R:0", + "TableName": "`user`_user_extra", + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select sum(col), 32 from (select `user`.col as col, 32 from `user` where 1 != 1) as t where 1 != 1", + "Query": "select sum(col), 32 from (select `user`.col as col, 32 from `user`) as t", + "Table": "`user`" + }, + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select count(*) from user_extra where 1 != 1 group by .0", + "Query": "select count(*) from user_extra group by .0", + "Table": "user_extra" + } + ] + } + ] + } + ] + }, + "TablesUsed": [ + "user.user", + "user.user_extra" + ] + } + }, + { + "comment": "with x as (select phone, id, city from user where id > 12 limit 10) select count(city) from x", + "query": "with x as (select phone, id, city from user where id > 12 limit 10) select count(city) from x", + "plan": { + "QueryType": "SELECT", + "Original": "with x as (select phone, id, city from user where id > 12 limit 10) select count(city) from x", + "Instructions": { + "OperatorType": "Aggregate", + "Variant": "Scalar", + "Aggregates": "count(0) AS count(city)", + "Inputs": [ + { + "OperatorType": "SimpleProjection", + "Columns": [ + 2 + ], + "Inputs": [ + { + "OperatorType": "Limit", + "Count": "INT64(10)", + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select phone, id, city from (select phone, id, city from `user` where 1 != 1) as x where 1 != 1", + "Query": "select phone, id, city from (select phone, id, city from `user` where id > 12) as x limit :__upper_limit", + "Table": "`user`" + } + ] + } + ] + } + ] + }, + "TablesUsed": [ + "user.user" + ] + } + }, + { + "comment": "with x as (select phone, id, city from user where id > 12 limit 10) select count(*) from x", + "query": "with x as (select phone, id, city from user where id > 12 limit 10) select count(*) from x", + "plan": { + "QueryType": "SELECT", + "Original": "with x as (select phone, id, city from user where id > 12 limit 10) select count(*) from x", + "Instructions": { + "OperatorType": "Aggregate", + "Variant": "Scalar", + "Aggregates": "count_star(0) AS count(*)", + "Inputs": [ + { + "OperatorType": "SimpleProjection", + "Columns": [ + 3 + ], + "Inputs": [ + { + "OperatorType": "Limit", + "Count": "INT64(10)", + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select phone, id, city, 1 from (select phone, id, city from `user` where 1 != 1) as x where 1 != 1", + "Query": "select phone, id, city, 1 from (select phone, id, city from `user` where id > 12) as x limit :__upper_limit", + "Table": "`user`" + } + ] + } + ] + } + ] + }, + "TablesUsed": [ + "user.user" + ] + } + }, + { + "comment": "with x as (select user_extra.col as col from user left join user_extra on user.id = user_extra.id limit 10) select count(col) from x", + "query": "with x as (select user_extra.col as col from user left join user_extra on user.id = user_extra.id limit 10) select count(col) from x", + "plan": { + "QueryType": "SELECT", + "Original": "with x as (select user_extra.col as col from user left join user_extra on user.id = user_extra.id limit 10) select count(col) from x", + "Instructions": { + "OperatorType": "Aggregate", + "Variant": "Scalar", + "Aggregates": "count(0) AS count(col)", + "Inputs": [ + { + "OperatorType": "Limit", + "Count": "INT64(10)", + "Inputs": [ + { + "OperatorType": "Join", + "Variant": "LeftJoin", + "JoinColumnIndexes": "R:0", + "JoinVars": { + "user_id": 0 + }, + "TableName": "`user`_user_extra", + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select `user`.id from `user` where 1 != 1", + "Query": "select `user`.id from `user`", + "Table": "`user`" + }, + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select user_extra.col from user_extra where 1 != 1", + "Query": "select user_extra.col from user_extra where user_extra.id = :user_id", + "Table": "user_extra" + } + ] + } + ] + } + ] + }, + "TablesUsed": [ + "user.user", + "user.user_extra" + ] + } + }, + { + "comment": "with x as (select id, val1 from user where val2 < 4 order by val1 limit 2) select val1, count(*) from x group by val1", + "query": "with x as (select id, val1 from user where val2 < 4 order by val1 limit 2) select val1, count(*) from x group by val1", + "plan": { + "QueryType": "SELECT", + "Original": "with x as (select id, val1 from user where val2 < 4 order by val1 limit 2) select val1, count(*) from x group by val1", + "Instructions": { + "OperatorType": "Aggregate", + "Variant": "Ordered", + "Aggregates": "count_star(1) AS count(*)", + "GroupBy": "(0|2)", + "ResultColumns": 2, + "Inputs": [ + { + "OperatorType": "SimpleProjection", + "Columns": [ + 1, + 2, + 3 + ], + "Inputs": [ + { + "OperatorType": "Limit", + "Count": "INT64(2)", + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select id, val1, 1, weight_string(val1) from (select id, val1 from `user` where 1 != 1) as x where 1 != 1", + "OrderBy": "(1|3) ASC", + "Query": "select id, val1, 1, weight_string(val1) from (select id, val1 from `user` where val2 < 4) as x order by val1 asc limit :__upper_limit", + "Table": "`user`" + } + ] + } + ] + } + ] + }, + "TablesUsed": [ + "user.user" + ] + } + }, + { + "comment": "with s as (select id from user having count(*) = 1) select * from s", + "query": "with s as (select id from user having count(*) = 1) select * from s", + "plan": { + "QueryType": "SELECT", + "Original": "with s as (select id from user having count(*) = 1) select * from s", + "Instructions": { + "OperatorType": "Filter", + "Predicate": "count(*) = 1", + "ResultColumns": 1, + "Inputs": [ + { + "OperatorType": "Aggregate", + "Variant": "Scalar", + "Aggregates": "any_value(0) AS id, sum_count_star(1) AS count(*)", + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select id, count(*) from `user` where 1 != 1", + "Query": "select id, count(*) from `user`", + "Table": "`user`" + } + ] + } + ] + }, + "TablesUsed": [ + "user.user" + ] + } + }, + { + "comment": "with A as (select sum(a) as a, sum(b) as b from user) select A.a, A.b, (A.a / A.b) as d from A", + "query": "with A as (select sum(a) as a, sum(b) as b from user) select A.a, A.b, (A.a / A.b) as d from A", + "plan": { + "QueryType": "SELECT", + "Original": "with A as (select sum(a) as a, sum(b) as b from user) select A.a, A.b, (A.a / A.b) as d from A", + "Instructions": { + "OperatorType": "Projection", + "Expressions": [ + "[COLUMN 0] as a", + "[COLUMN 1] as b", + "[COLUMN 0] / [COLUMN 1] as d" + ], + "Inputs": [ + { + "OperatorType": "Aggregate", + "Variant": "Scalar", + "Aggregates": "sum(0) AS a, sum(1) AS b", + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select sum(a) as a, sum(b) as b from `user` where 1 != 1", + "Query": "select sum(a) as a, sum(b) as b from `user`", + "Table": "`user`" + } + ] + } + ] + }, + "TablesUsed": [ + "user.user" + ] + } + }, + { + "comment": "with t1 as (select portalId, flowId, count(*) as count from user_extra where localDate > :v1 group by user_id, flowId order by null) select t1.portalId, t1.flowId from t1 where count >= :v2", + "query": "with t1 as (select portalId, flowId, count(*) as count from user_extra where localDate > :v1 group by user_id, flowId order by null) select t1.portalId, t1.flowId from t1 where count >= :v2", + "plan": { + "QueryType": "SELECT", + "Original": "with t1 as (select portalId, flowId, count(*) as count from user_extra where localDate > :v1 group by user_id, flowId order by null) select t1.portalId, t1.flowId from t1 where count >= :v2", + "Instructions": { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select t1.portalId, t1.flowId from (select portalId, flowId, count(*) as `count` from user_extra where 1 != 1 group by user_id, flowId) as t1 where 1 != 1", + "Query": "select t1.portalId, t1.flowId from (select portalId, flowId, count(*) as `count` from user_extra where localDate > :v1 group by user_id, flowId) as t1 where `count` >= :v2", + "Table": "user_extra" + }, + "TablesUsed": [ + "user.user_extra" + ] + } + }, + { + "comment": "with tt as (SELECT foo, max(baz) as bazo FROM (SELECT foo, baz FROM user) f GROUP BY foo) SELECT foo FROM tt WHERE bazo BETWEEN 100 AND 200", + "query": "with tt as (SELECT foo, max(baz) as bazo FROM (SELECT foo, baz FROM user) f GROUP BY foo) SELECT foo FROM tt WHERE bazo BETWEEN 100 AND 200", + "plan": { + "QueryType": "SELECT", + "Original": "with tt as (SELECT foo, max(baz) as bazo FROM (SELECT foo, baz FROM user) f GROUP BY foo) SELECT foo FROM tt WHERE bazo BETWEEN 100 AND 200", + "Instructions": { + "OperatorType": "Filter", + "Predicate": "bazo between 100 and 200", + "ResultColumns": 1, + "Inputs": [ + { + "OperatorType": "Aggregate", + "Variant": "Ordered", + "Aggregates": "max(1|3) AS bazo", + "GroupBy": "(0|2)", + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select foo, max(baz) as bazo, weight_string(foo), weight_string(baz) from (select foo, baz from `user` where 1 != 1) as f where 1 != 1 group by foo, weight_string(foo), weight_string(baz)", + "OrderBy": "(0|2) ASC", + "Query": "select foo, max(baz) as bazo, weight_string(foo), weight_string(baz) from (select foo, baz from `user`) as f group by foo, weight_string(foo), weight_string(baz) order by foo asc", + "Table": "`user`" + } + ] + } + ] + }, + "TablesUsed": [ + "user.user" + ] + } + }, + { + "comment": "with tt as (SELECT foo, count(baz) as bazo FROM (SELECT foo, baz FROM user) f GROUP BY foo) SELECT foo FROM tt WHERE bazo BETWEEN 100 AND 200", + "query": "with tt as (SELECT foo, count(baz) as bazo FROM (SELECT foo, baz FROM user) f GROUP BY foo) SELECT foo FROM tt WHERE bazo BETWEEN 100 AND 200", + "plan": { + "QueryType": "SELECT", + "Original": "with tt as (SELECT foo, count(baz) as bazo FROM (SELECT foo, baz FROM user) f GROUP BY foo) SELECT foo FROM tt WHERE bazo BETWEEN 100 AND 200", + "Instructions": { + "OperatorType": "Filter", + "Predicate": "bazo between 100 and 200", + "ResultColumns": 1, + "Inputs": [ + { + "OperatorType": "Aggregate", + "Variant": "Ordered", + "Aggregates": "sum_count(1) AS bazo", + "GroupBy": "(0|2)", + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select foo, count(baz) as bazo, weight_string(foo) from (select foo, baz from `user` where 1 != 1) as f where 1 != 1 group by foo, weight_string(foo)", + "OrderBy": "(0|2) ASC", + "Query": "select foo, count(baz) as bazo, weight_string(foo) from (select foo, baz from `user`) as f group by foo, weight_string(foo) order by foo asc", + "Table": "`user`" + } + ] + } + ] + }, + "TablesUsed": [ + "user.user" + ] + } + }, + { + "comment": "with d as (select id, count(*) as a from user) select d.a from music join d on music.user_id = d.id group by 1", + "query": "with d as (select id, count(*) as a from user) select d.a from music join d on music.user_id = d.id group by 1", + "plan": { + "QueryType": "SELECT", + "Original": "with d as (select id, count(*) as a from user) select d.a from music join d on music.user_id = d.id group by 1", + "Instructions": { + "OperatorType": "Aggregate", + "Variant": "Ordered", + "GroupBy": "0", + "Inputs": [ + { + "OperatorType": "Join", + "Variant": "Join", + "JoinColumnIndexes": "L:0", + "JoinVars": { + "d_id": 1 + }, + "TableName": "`user`_music", + "Inputs": [ + { + "OperatorType": "Aggregate", + "Variant": "Ordered", + "GroupBy": "0, (1|2)", + "Inputs": [ + { + "OperatorType": "SimpleProjection", + "Columns": [ + 1, + 0, + 2 + ], + "Inputs": [ + { + "OperatorType": "Aggregate", + "Variant": "Scalar", + "Aggregates": "any_value(0) AS id, sum_count_star(1) AS a, any_value(2)", + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select id, count(*) as a, weight_string(id) from `user` where 1 != 1", + "OrderBy": "1 ASC, (0|2) ASC", + "Query": "select id, count(*) as a, weight_string(id) from `user` order by count(*) asc, id asc", + "Table": "`user`" + } + ] + } + ] + } + ] + }, + { + "OperatorType": "Route", + "Variant": "EqualUnique", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select 1 from music where 1 != 1 group by .0", + "Query": "select 1 from music where music.user_id = :d_id group by .0", + "Table": "music", + "Values": [ + ":d_id" + ], + "Vindex": "user_index" + } + ] + } + ] + }, + "TablesUsed": [ + "user.music", + "user.user" + ] + } + }, + { + "comment": "with t as (select col from user union all select col from unsharded) select sum(col) from t", + "query": "with t as (select col from user union all select col from unsharded) select sum(col) from t", + "plan": { + "QueryType": "SELECT", + "Original": "with t as (select col from user union all select col from unsharded) select sum(col) from t", + "Instructions": { + "OperatorType": "Aggregate", + "Variant": "Scalar", + "Aggregates": "sum(0) AS sum(col)", + "Inputs": [ + { + "OperatorType": "Concatenate", + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select col from `user` where 1 != 1", + "Query": "select col from `user`", + "Table": "`user`" + }, + { + "OperatorType": "Route", + "Variant": "Unsharded", + "Keyspace": { + "Name": "main", + "Sharded": false + }, + "FieldQuery": "select col from unsharded where 1 != 1", + "Query": "select col from unsharded", + "Table": "unsharded" + } + ] + } + ] + }, + "TablesUsed": [ + "main.unsharded", + "user.user" + ] + } + }, + { + "comment": "with x as (select id, val2 from user where val2 is null limit 2) select count(val2), sum(val2) from x", + "query": "with x as (select id, val2 from user where val2 is null limit 2) select count(val2), sum(val2) from x", + "plan": { + "QueryType": "SELECT", + "Original": "with x as (select id, val2 from user where val2 is null limit 2) select count(val2), sum(val2) from x", + "Instructions": { + "OperatorType": "Aggregate", + "Variant": "Scalar", + "Aggregates": "count(0) AS count(val2), sum(1) AS sum(val2)", + "Inputs": [ + { + "OperatorType": "SimpleProjection", + "Columns": [ + 1, + 1 + ], + "Inputs": [ + { + "OperatorType": "Limit", + "Count": "INT64(2)", + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select id, val2 from (select id, val2 from `user` where 1 != 1) as x where 1 != 1", + "Query": "select id, val2 from (select id, val2 from `user` where val2 is null) as x limit :__upper_limit", + "Table": "`user`" + } + ] + } + ] + } + ] + }, + "TablesUsed": [ + "user.user" + ] + } + }, + { + "comment": "with X as (select distinct count(*) from user) select distinct count(*) from X", + "query": "with X as (select distinct count(*) from user) select distinct count(*) from X", + "plan": { + "QueryType": "SELECT", + "Original": "with X as (select distinct count(*) from user) select distinct count(*) from X", + "Instructions": { + "OperatorType": "Aggregate", + "Variant": "Scalar", + "Aggregates": "count_star(0) AS count(*)", + "Inputs": [ + { + "OperatorType": "SimpleProjection", + "Columns": [ + 1 + ], + "Inputs": [ + { + "OperatorType": "Aggregate", + "Variant": "Scalar", + "Aggregates": "sum_count_star(0) AS count(*), any_value(1)", + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select count(*), 1 from `user` where 1 != 1", + "Query": "select count(*), 1 from `user`", + "Table": "`user`" + } + ] + } + ] + } + ] + }, + "TablesUsed": [ + "user.user" + ] + } + }, + { + "comment": "with keepers as (select id from unsharded where col is not null order by col desc limit 10) update unsharded as foo left join keepers on foo.id = keepers.id set col1 = 'asdf' where keepers.id is null and foo.col is not null and foo.col < 1000", + "query": "with keepers as (select id from unsharded where col is not null order by col desc limit 10) update unsharded as foo left join keepers on foo.id = keepers.id set col1 = 'asdf' where keepers.id is null and foo.col is not null and foo.col < 1000", + "plan": "VT12001: unsupported: WITH expression in UPDATE statement" + }, + { + "comment": "with keepers as (select id from unsharded where col is not null order by col desc limit 10) delete foo from unsharded as foo left join keepers on foo.id = keepers.id where keepers.id is null and foo.col is not null and foo.col < 1000", + "query": "with keepers as (select id from unsharded where col is not null order by col desc limit 10) delete foo from unsharded as foo left join keepers on foo.id = keepers.id where keepers.id is null and foo.col is not null and foo.col < 1000", + "plan": "VT12001: unsupported: WITH expression in DELETE statement" + }, + { + "comment": "with music as (select * from user) delete music from music where id = 1", + "query": "with music as (select * from user) delete music from music where id = 1", + "plan": "VT12001: unsupported: WITH expression in DELETE statement" + }, + { + "comment": "with music as (select * from user) delete user from music where id = 1", + "query": "with music as (select * from user) delete user from music where id = 1", + "plan": "VT12001: unsupported: WITH expression in DELETE statement" + }, + { + "comment": "with keepers as (select id from unsharded a join unsharded_b b on a.user_id = b.user_id) delete foo from unsharded as foo join keepers on foo.id = keepers.id where keepers.id is null and foo.col is not null and foo.col < 1000", + "query": "with keepers as (select id from unsharded a join unsharded_b b on a.user_id = b.user_id) delete foo from unsharded as foo join keepers on foo.id = keepers.id where keepers.id is null and foo.col is not null and foo.col < 1000", + "plan": "VT12001: unsupported: WITH expression in DELETE statement" + }, + { + "comment": "with t as (select id, col from user where id = 5) select id from t", + "query": "with t as (select id, col from user where id = 5) select id from t", + "plan": { + "QueryType": "SELECT", + "Original": "with t as (select id, col from user where id = 5) select id from t", + "Instructions": { + "OperatorType": "Route", + "Variant": "EqualUnique", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select id from (select id, col from `user` where 1 != 1) as t where 1 != 1", + "Query": "select id from (select id, col from `user` where id = 5) as t", + "Table": "`user`", + "Values": [ + "INT64(5)" + ], + "Vindex": "user_index" + }, + "TablesUsed": [ + "user.user" + ] + } + }, + { + "comment": "with t as (select id from user where id = 5) select t.id from t join user_extra on t.id = user_extra.user_id", + "query": "with t as (select id from user where id = 5) select t.id from t join user_extra on t.id = user_extra.user_id", + "plan": { + "QueryType": "SELECT", + "Original": "with t as (select id from user where id = 5) select t.id from t join user_extra on t.id = user_extra.user_id", + "Instructions": { + "OperatorType": "Route", + "Variant": "EqualUnique", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select t.id from (select id from `user` where 1 != 1) as t, user_extra where 1 != 1", + "Query": "select t.id from (select id from `user` where id = 5) as t, user_extra where t.id = user_extra.user_id", + "Table": "`user`, user_extra", + "Values": [ + "INT64(5)" + ], + "Vindex": "user_index" + }, + "TablesUsed": [ + "user.user", + "user.user_extra" + ] + } + }, + { + "comment": "with t as (select user.id from user where user.id = 5) select t.id from t join user_extra on t.id = user_extra.user_id", + "query": "with t as (select user.id from user where user.id = 5) select t.id from t join user_extra on t.id = user_extra.user_id", + "plan": { + "QueryType": "SELECT", + "Original": "with t as (select user.id from user where user.id = 5) select t.id from t join user_extra on t.id = user_extra.user_id", + "Instructions": { + "OperatorType": "Route", + "Variant": "EqualUnique", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select t.id from (select `user`.id from `user` where 1 != 1) as t, user_extra where 1 != 1", + "Query": "select t.id from (select `user`.id from `user` where `user`.id = 5) as t, user_extra where t.id = user_extra.user_id", + "Table": "`user`, user_extra", + "Values": [ + "INT64(5)" + ], + "Vindex": "user_index" + }, + "TablesUsed": [ + "user.user", + "user.user_extra" + ] + } + }, + { + "comment": "with t as (select user.id, id from user where user.id = 5) select t.id from t join user_extra on t.id = user_extra.user_id", + "query": "with t as (select user.id, id from user where user.id = 5) select t.id from t join user_extra on t.id = user_extra.user_id", + "plan": "Duplicate column name 'id'" + }, + { + "comment": "with t as (select id from user where id = 5) select t.id from user_extra join t on t.id = user_extra.user_id", + "query": "with t as (select id from user where id = 5) select t.id from user_extra join t on t.id = user_extra.user_id", + "plan": { + "QueryType": "SELECT", + "Original": "with t as (select id from user where id = 5) select t.id from user_extra join t on t.id = user_extra.user_id", + "Instructions": { + "OperatorType": "Route", + "Variant": "EqualUnique", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select t.id from (select id from `user` where 1 != 1) as t, user_extra where 1 != 1", + "Query": "select t.id from (select id from `user` where id = 5) as t, user_extra where t.id = user_extra.user_id", + "Table": "`user`, user_extra", + "Values": [ + "INT64(5)" + ], + "Vindex": "user_index" + }, + "TablesUsed": [ + "user.user", + "user.user_extra" + ] + } + }, + { + "comment": "with t as (select id from user where id = 5) select t.id from t join user_extra on t.id = user_extra.col", + "query": "with t as (select id from user where id = 5) select t.id from t join user_extra on t.id = user_extra.col", + "plan": { + "QueryType": "SELECT", + "Original": "with t as (select id from user where id = 5) select t.id from t join user_extra on t.id = user_extra.col", + "Instructions": { + "OperatorType": "Join", + "Variant": "Join", + "JoinColumnIndexes": "L:0", + "JoinVars": { + "t_id": 0 + }, + "TableName": "`user`_user_extra", + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "EqualUnique", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select t.id from (select id from `user` where 1 != 1) as t where 1 != 1", + "Query": "select t.id from (select id from `user` where id = 5) as t", + "Table": "`user`", + "Values": [ + "INT64(5)" + ], + "Vindex": "user_index" + }, + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select 1 from user_extra where 1 != 1", + "Query": "select 1 from user_extra where user_extra.col = :t_id", + "Table": "user_extra" + } + ] + }, + "TablesUsed": [ + "user.user", + "user.user_extra" + ] + } + }, + { + "comment": "with t as (select id, col from route1 where id = 5) select id from t", + "query": "with t as (select id, col from route1 where id = 5) select id from t", + "plan": { + "QueryType": "SELECT", + "Original": "with t as (select id, col from route1 where id = 5) select id from t", + "Instructions": { + "OperatorType": "Route", + "Variant": "EqualUnique", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select id from (select id, col from `user` as route1 where 1 != 1) as t where 1 != 1", + "Query": "select id from (select id, col from `user` as route1 where id = 5) as t", + "Table": "`user`", + "Values": [ + "INT64(5)" + ], + "Vindex": "user_index" + }, + "TablesUsed": [ + "user.user" + ] + } + }, + { + "comment": "with t as (select id from user) select t.id from t join user_extra on t.id = user_extra.user_id where t.col = 42", + "query": "with t as (select id from user) select t.id from t join user_extra on t.id = user_extra.user_id where t.col = 42", + "plan": "column 't.col' not found" + }, + { + "comment": "with t as (select id, col from route1) select id from t where id = 5", + "query": "with t as (select id, col from route1) select id from t where id = 5", + "plan": { + "QueryType": "SELECT", + "Original": "with t as (select id, col from route1) select id from t where id = 5", + "Instructions": { + "OperatorType": "Route", + "Variant": "EqualUnique", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select id from (select id, col from `user` as route1 where 1 != 1) as t where 1 != 1", + "Query": "select id from (select id, col from `user` as route1 where id = 5) as t", + "Table": "`user`", + "Values": [ + "INT64(5)" + ], + "Vindex": "user_index" + }, + "TablesUsed": [ + "user.user" + ] + } + }, + { + "comment": "with t as (select id+col as foo from route1) select id from t where foo = 5", + "query": "with t as (select id+col as foo from route1) select id from t where foo = 5", + "plan": "column 'id' not found in table 't'" + }, + { + "comment": "with t as (select id, textcol1 as baz from route1), s as (select id, textcol1+textcol1 as baz from user) select t.id from t join s ON t.id = s.id WHERE t.baz = '3' AND s.baz = '3'", + "query": "with t as (select id, textcol1 as baz from route1), s as (select id, textcol1+textcol1 as baz from user) select t.id from t join s ON t.id = s.id WHERE t.baz = '3' AND s.baz = '3'", + "plan": { + "QueryType": "SELECT", + "Original": "with t as (select id, textcol1 as baz from route1), s as (select id, textcol1+textcol1 as baz from user) select t.id from t join s ON t.id = s.id WHERE t.baz = '3' AND s.baz = '3'", + "Instructions": { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select t.id from (select id, textcol1 as baz from `user` as route1 where 1 != 1) as t, (select id, textcol1 + textcol1 as baz from `user` where 1 != 1) as s where 1 != 1", + "Query": "select t.id from (select id, textcol1 as baz from `user` as route1 where textcol1 = '3') as t, (select id, textcol1 + textcol1 as baz from `user` where textcol1 + textcol1 = '3') as s where t.id = s.id", + "Table": "`user`" + }, + "TablesUsed": [ + "user.user" + ] + } + }, + { + "comment": "with u as (select colA+colB as foo from user), t as (select foo+4 as bar from u) select bar from t where bar = 5", + "query": "with u as (select colA+colB as foo from user), t as (select foo+4 as bar from u) select bar from t where bar = 5", + "plan": { + "QueryType": "SELECT", + "Original": "with u as (select colA+colB as foo from user), t as (select foo+4 as bar from u) select bar from t where bar = 5", + "Instructions": { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select bar from (select foo + 4 as bar from (select colA + colB as foo from `user` where 1 != 1) as u where 1 != 1) as t where 1 != 1", + "Query": "select bar from (select foo + 4 as bar from (select colA + colB as foo from `user` where colA + colB + 4 = 5) as u) as t", + "Table": "`user`" + }, + "TablesUsed": [ + "user.user" + ] + } + }, + { + "comment": "with u as (select col from user where id = 5), e as (select col from user_extra where user_id = 5) select u.col, e.col from u join e", + "query": "with u as (select col from user where id = 5), e as (select col from user_extra where user_id = 5) select u.col, e.col from u join e", + "plan": { + "QueryType": "SELECT", + "Original": "with u as (select col from user where id = 5), e as (select col from user_extra where user_id = 5) select u.col, e.col from u join e", + "Instructions": { + "OperatorType": "Route", + "Variant": "EqualUnique", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select u.col, e.col from (select col from `user` where 1 != 1) as u, (select col from user_extra where 1 != 1) as e where 1 != 1", + "Query": "select u.col, e.col from (select col from `user` where id = 5) as u, (select col from user_extra where user_id = 5) as e", + "Table": "`user`, user_extra", + "Values": [ + "INT64(5)" + ], + "Vindex": "user_index" + }, + "TablesUsed": [ + "user.user", + "user.user_extra" + ] + } + }, + { + "comment": "with t as (select user.id, user.col1 from user join user_extra) select t.col1 from t join unsharded on unsharded.col1 = t.col1 and unsharded.id = t.id", + "query": "with t as (select user.id, user.col1 from user join user_extra) select t.col1 from t join unsharded on unsharded.col1 = t.col1 and unsharded.id = t.id", + "plan": { + "QueryType": "SELECT", + "Original": "with t as (select user.id, user.col1 from user join user_extra) select t.col1 from t join unsharded on unsharded.col1 = t.col1 and unsharded.id = t.id", + "Instructions": { + "OperatorType": "Join", + "Variant": "Join", + "JoinColumnIndexes": "L:0", + "JoinVars": { + "t_col1": 0, + "t_id": 1 + }, + "TableName": "`user`_user_extra_unsharded", + "Inputs": [ + { + "OperatorType": "Join", + "Variant": "Join", + "JoinColumnIndexes": "L:0,L:1", + "TableName": "`user`_user_extra", + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select t.col1, t.id from (select `user`.id, `user`.col1 from `user` where 1 != 1) as t where 1 != 1", + "Query": "select t.col1, t.id from (select `user`.id, `user`.col1 from `user`) as t", + "Table": "`user`" + }, + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select 1 from user_extra where 1 != 1", + "Query": "select 1 from user_extra", + "Table": "user_extra" + } + ] + }, + { + "OperatorType": "Route", + "Variant": "Unsharded", + "Keyspace": { + "Name": "main", + "Sharded": false + }, + "FieldQuery": "select 1 from unsharded where 1 != 1", + "Query": "select 1 from unsharded where unsharded.id = :t_id and unsharded.col1 = :t_col1", + "Table": "unsharded" + } + ] + }, + "TablesUsed": [ + "main.unsharded", + "user.user", + "user.user_extra" + ] + } + }, + { + "comment": "with t as (select user.id, user.col1 from user join user_extra on user_extra.col = user.col) select t.id from t", + "query": "with t as (select user.id, user.col1 from user join user_extra on user_extra.col = user.col) select t.id from t", + "plan": { + "QueryType": "SELECT", + "Original": "with t as (select user.id, user.col1 from user join user_extra on user_extra.col = user.col) select t.id from t", + "Instructions": { + "OperatorType": "Join", + "Variant": "Join", + "JoinColumnIndexes": "L:0", + "JoinVars": { + "user_col": 1 + }, + "TableName": "`user`_user_extra", + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select t.id, t.`user.col` from (select `user`.id, `user`.col1, `user`.col as `user.col` from `user` where 1 != 1) as t where 1 != 1", + "Query": "select t.id, t.`user.col` from (select `user`.id, `user`.col1, `user`.col as `user.col` from `user`) as t", + "Table": "`user`" + }, + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select 1 from user_extra where 1 != 1", + "Query": "select 1 from user_extra where user_extra.col = :user_col", + "Table": "user_extra" + } + ] + }, + "TablesUsed": [ + "user.user", + "user.user_extra" + ] + } + }, + { + "comment": "with t as (select user.id, user.col1 from user join user_extra) select t.col1 from unsharded_a ua join t", + "query": "with t as (select user.id, user.col1 from user join user_extra) select t.col1 from unsharded_a ua join t", + "plan": { + "QueryType": "SELECT", + "Original": "with t as (select user.id, user.col1 from user join user_extra) select t.col1 from unsharded_a ua join t", + "Instructions": { + "OperatorType": "Join", + "Variant": "Join", + "JoinColumnIndexes": "R:0", + "TableName": "unsharded_a_`user`_user_extra", + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "Unsharded", + "Keyspace": { + "Name": "main", + "Sharded": false + }, + "FieldQuery": "select 1 from unsharded_a as ua where 1 != 1", + "Query": "select 1 from unsharded_a as ua", + "Table": "unsharded_a" + }, + { + "OperatorType": "Join", + "Variant": "Join", + "JoinColumnIndexes": "L:0", + "TableName": "`user`_user_extra", + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select t.col1 from (select `user`.id, `user`.col1 from `user` where 1 != 1) as t where 1 != 1", + "Query": "select t.col1 from (select `user`.id, `user`.col1 from `user`) as t", + "Table": "`user`" + }, + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select 1 from user_extra where 1 != 1", + "Query": "select 1 from user_extra", + "Table": "user_extra" + } + ] + } + ] + }, + "TablesUsed": [ + "main.unsharded_a", + "user.user", + "user.user_extra" + ] + } + }, + { + "comment": "with t as (select user.id, user.col1 from user join user_extra) select t.col1 from unsharded_a ua join t on t.id = ua.id", + "query": "with t as (select user.id, user.col1 from user join user_extra) select t.col1 from unsharded_a ua join t on t.id = ua.id", + "plan": { + "QueryType": "SELECT", + "Original": "with t as (select user.id, user.col1 from user join user_extra) select t.col1 from unsharded_a ua join t on t.id = ua.id", + "Instructions": { + "OperatorType": "Join", + "Variant": "Join", + "JoinColumnIndexes": "R:0", + "JoinVars": { + "ua_id": 0 + }, + "TableName": "unsharded_a_`user`_user_extra", + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "Unsharded", + "Keyspace": { + "Name": "main", + "Sharded": false + }, + "FieldQuery": "select ua.id from unsharded_a as ua where 1 != 1", + "Query": "select ua.id from unsharded_a as ua", + "Table": "unsharded_a" + }, + { + "OperatorType": "Join", + "Variant": "Join", + "JoinColumnIndexes": "L:0", + "TableName": "`user`_user_extra", + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "EqualUnique", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select t.col1 from (select `user`.id, `user`.col1 from `user` where 1 != 1) as t where 1 != 1", + "Query": "select t.col1 from (select `user`.id, `user`.col1 from `user` where `user`.id = :ua_id) as t", + "Table": "`user`", + "Values": [ + ":ua_id" + ], + "Vindex": "user_index" + }, + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select 1 from user_extra where 1 != 1", + "Query": "select 1 from user_extra", + "Table": "user_extra" + } + ] + } + ] + }, + "TablesUsed": [ + "main.unsharded_a", + "user.user", + "user.user_extra" + ] + } + }, + { + "comment": "with t as (select user.id from user join user_extra) select id, t.id from t", + "query": "with t as (select user.id from user join user_extra) select id, t.id from t", + "plan": { + "QueryType": "SELECT", + "Original": "with t as (select user.id from user join user_extra) select id, t.id from t", + "Instructions": { + "OperatorType": "SimpleProjection", + "Columns": [ + 0, + 0 + ], + "Inputs": [ + { + "OperatorType": "Join", + "Variant": "Join", + "JoinColumnIndexes": "L:0", + "TableName": "`user`_user_extra", + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select id from (select `user`.id from `user` where 1 != 1) as t where 1 != 1", + "Query": "select id from (select `user`.id from `user`) as t", + "Table": "`user`" + }, + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select 1 from user_extra where 1 != 1", + "Query": "select 1 from user_extra", + "Table": "user_extra" + } + ] + } + ] + }, + "TablesUsed": [ + "user.user", + "user.user_extra" + ] + } + }, + { + "comment": "with t as (select count(*) as a from user) select a as k from t", + "query": "with t as (select count(*) as a from user) select a as k from t", + "plan": { + "QueryType": "SELECT", + "Original": "with t as (select count(*) as a from user) select a as k from t", + "Instructions": { + "OperatorType": "Aggregate", + "Variant": "Scalar", + "Aggregates": "sum_count_star(0) AS a", + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select count(*) as a from `user` where 1 != 1", + "Query": "select count(*) as a from `user`", + "Table": "`user`" + } + ] + }, + "TablesUsed": [ + "user.user" + ] + } + }, + { + "comment": "with u as (select * from unsharded) select u.* from u", + "query": "with u as (select * from unsharded) select u.* from u", + "plan": { + "QueryType": "SELECT", + "Original": "with u as (select * from unsharded) select u.* from u", + "Instructions": { + "OperatorType": "Route", + "Variant": "Unsharded", + "Keyspace": { + "Name": "main", + "Sharded": false + }, + "FieldQuery": "select u.* from (select * from unsharded where 1 != 1) as u where 1 != 1", + "Query": "select u.* from (select * from unsharded) as u", + "Table": "unsharded" + }, + "TablesUsed": [ + "main.unsharded" + ] + } + }, + { + "comment": "with t as (select user.id, user.col from user join user_extra) select id from t where id=5", + "query": "with t as (select user.id, user.col from user join user_extra) select id from t where id=5", + "plan": { + "QueryType": "SELECT", + "Original": "with t as (select user.id, user.col from user join user_extra) select id from t where id=5", + "Instructions": { + "OperatorType": "Join", + "Variant": "Join", + "JoinColumnIndexes": "L:0", + "TableName": "`user`_user_extra", + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "EqualUnique", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select id from (select `user`.id, `user`.col from `user` where 1 != 1) as t where 1 != 1", + "Query": "select id from (select `user`.id, `user`.col from `user` where `user`.id = 5) as t", + "Table": "`user`", + "Values": [ + "INT64(5)" + ], + "Vindex": "user_index" + }, + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select 1 from user_extra where 1 != 1", + "Query": "select 1 from user_extra", + "Table": "user_extra" + } + ] + }, + "TablesUsed": [ + "user.user", + "user.user_extra" + ] + } + }, + { + "comment": "with t as (select user.id, user.col from user join user_extra) select id+1 from t", + "query": "with t as (select user.id, user.col from user join user_extra) select id+1 from t", + "plan": { + "QueryType": "SELECT", + "Original": "with t as (select user.id, user.col from user join user_extra) select id+1 from t", + "Instructions": { + "OperatorType": "Join", + "Variant": "Join", + "JoinColumnIndexes": "L:0", + "TableName": "`user`_user_extra", + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select id + 1 from (select `user`.id, `user`.col from `user` where 1 != 1) as t where 1 != 1", + "Query": "select id + 1 from (select `user`.id, `user`.col from `user`) as t", + "Table": "`user`" + }, + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select 1 from user_extra where 1 != 1", + "Query": "select 1 from user_extra", + "Table": "user_extra" + } + ] + }, + "TablesUsed": [ + "user.user", + "user.user_extra" + ] + } + }, + { + "comment": "with u(a,n) as (select id as b, name from user) select u.a from u where u.n = 1", + "query": "with u(a,n) as (select id as b, name from user) select u.a from u where u.n = 1", + "plan": { + "QueryType": "SELECT", + "Original": "with u(a,n) as (select id as b, name from user) select u.a from u where u.n = 1", + "Instructions": { + "OperatorType": "VindexLookup", + "Variant": "Equal", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "Values": [ + "INT64(1)" + ], + "Vindex": "name_user_map", + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "IN", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select `name`, keyspace_id from name_user_vdx where 1 != 1", + "Query": "select `name`, keyspace_id from name_user_vdx where `name` in ::__vals", + "Table": "name_user_vdx", + "Values": [ + "::name" + ], + "Vindex": "user_index" + }, + { + "OperatorType": "Route", + "Variant": "ByDestination", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select u.a from (select id as b, `name` from `user` where 1 != 1) as u(a, n) where 1 != 1", + "Query": "select u.a from (select id as b, `name` from `user` where `name` = 1) as u(a, n)", + "Table": "`user`" + } + ] + }, + "TablesUsed": [ + "user.user" + ] + } + }, + { + "comment": "with u(a, n) as (select id as b, name from user where b = 1) select u.a from u where u.n = 1", + "query": "with u(a, n) as (select id as b, name from user where b = 1) select u.a from u where u.n = 1", + "plan": { + "QueryType": "SELECT", + "Original": "with u(a, n) as (select id as b, name from user where b = 1) select u.a from u where u.n = 1", + "Instructions": { + "OperatorType": "VindexLookup", + "Variant": "Equal", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "Values": [ + "INT64(1)" + ], + "Vindex": "name_user_map", + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "IN", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select `name`, keyspace_id from name_user_vdx where 1 != 1", + "Query": "select `name`, keyspace_id from name_user_vdx where `name` in ::__vals", + "Table": "name_user_vdx", + "Values": [ + "::name" + ], + "Vindex": "user_index" + }, + { + "OperatorType": "Route", + "Variant": "ByDestination", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select u.a from (select id as b, `name` from `user` where 1 != 1) as u(a, n) where 1 != 1", + "Query": "select u.a from (select id as b, `name` from `user` where b = 1 and `name` = 1) as u(a, n)", + "Table": "`user`" + } + ] + }, + "TablesUsed": [ + "user.user" + ] + } + }, + { + "comment": "with t(i) as (select user.id from user join user_extra) select i+1 from t", + "query": "with t(i) as (select user.id from user join user_extra) select i+1 from t", + "plan": { + "QueryType": "SELECT", + "Original": "with t(i) as (select user.id from user join user_extra) select i+1 from t", + "Instructions": { + "OperatorType": "Join", + "Variant": "Join", + "JoinColumnIndexes": "L:0", + "TableName": "`user`_user_extra", + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select i + 1 from (select `user`.id from `user` where 1 != 1) as t(i) where 1 != 1", + "Query": "select i + 1 from (select `user`.id from `user`) as t(i)", + "Table": "`user`" + }, + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select 1 from user_extra where 1 != 1", + "Query": "select 1 from user_extra", + "Table": "user_extra" + } + ] + }, + "TablesUsed": [ + "user.user", + "user.user_extra" + ] + } + }, + { + "comment": "with t as (select `user`.col1 from `user` join unsharded) select 0 from t join unsharded on unsharded.col1 = t.col1 and unsharded.a = t.col1", + "query": "with t as (select `user`.col1 from `user` join unsharded) select 0 from t join unsharded on unsharded.col1 = t.col1 and unsharded.a = t.col1", + "plan": { + "QueryType": "SELECT", + "Original": "with t as (select `user`.col1 from `user` join unsharded) select 0 from t join unsharded on unsharded.col1 = t.col1 and unsharded.a = t.col1", + "Instructions": { + "OperatorType": "Join", + "Variant": "Join", + "JoinColumnIndexes": "L:0", + "JoinVars": { + "t_col1": 1 + }, + "TableName": "`user`_unsharded_unsharded", + "Inputs": [ + { + "OperatorType": "Join", + "Variant": "Join", + "JoinColumnIndexes": "L:0,L:1", + "TableName": "`user`_unsharded", + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select 0, t.col1 from (select `user`.col1 from `user` where 1 != 1) as t where 1 != 1", + "Query": "select 0, t.col1 from (select `user`.col1 from `user`) as t", + "Table": "`user`" + }, + { + "OperatorType": "Route", + "Variant": "Unsharded", + "Keyspace": { + "Name": "main", + "Sharded": false + }, + "FieldQuery": "select 1 from unsharded where 1 != 1", + "Query": "select 1 from unsharded", + "Table": "unsharded" + } + ] + }, + { + "OperatorType": "Route", + "Variant": "Unsharded", + "Keyspace": { + "Name": "main", + "Sharded": false + }, + "FieldQuery": "select 1 from unsharded where 1 != 1", + "Query": "select 1 from unsharded where unsharded.a = :t_col1 and unsharded.col1 = :t_col1", + "Table": "unsharded" + } + ] + }, + "TablesUsed": [ + "main.unsharded", + "user.user" + ] + } + }, + { + "comment": "with x(id2) as (select id from user) select id2 from x", + "query": "with x(id2) as (select id from user) select id2 from x", + "plan": { + "QueryType": "SELECT", + "Original": "with x(id2) as (select id from user) select id2 from x", + "Instructions": { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select id2 from (select id from `user` where 1 != 1) as x(id2) where 1 != 1", + "Query": "select id2 from (select id from `user`) as x(id2)", + "Table": "`user`" + }, + "TablesUsed": [ + "user.user" + ] + } + }, + { + "comment": "with u as (select col from unsharded join unsharded_b) select col from u join unsharded_a ua limit 1", + "query": "with u as (select col from unsharded join unsharded_b) select col from u join unsharded_a ua limit 1", + "plan": { + "QueryType": "SELECT", + "Original": "with u as (select col from unsharded join unsharded_b) select col from u join unsharded_a ua limit 1", + "Instructions": { + "OperatorType": "Route", + "Variant": "Unsharded", + "Keyspace": { + "Name": "main", + "Sharded": false + }, + "FieldQuery": "select col from (select col from unsharded join unsharded_b where 1 != 1) as u join unsharded_a as ua where 1 != 1", + "Query": "select col from (select col from unsharded join unsharded_b) as u join unsharded_a as ua limit 1", + "Table": "unsharded, unsharded_a, unsharded_b" + }, + "TablesUsed": [ + "main.unsharded", + "main.unsharded_a", + "main.unsharded_b" + ] + } + }, + { + "comment": "with u as (select user.col from user join user_extra) select u.col from u join user_extra ue limit 1", + "query": "with u as (select user.col from user join user_extra) select u.col from u join user_extra ue limit 1", + "plan": { + "QueryType": "SELECT", + "Original": "with u as (select user.col from user join user_extra) select u.col from u join user_extra ue limit 1", + "Instructions": { + "OperatorType": "Limit", + "Count": "INT64(1)", + "Inputs": [ + { + "OperatorType": "Join", + "Variant": "Join", + "JoinColumnIndexes": "L:0", + "TableName": "`user`_user_extra_user_extra", + "Inputs": [ + { + "OperatorType": "Join", + "Variant": "Join", + "JoinColumnIndexes": "L:0", + "TableName": "`user`_user_extra", + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select u.col from (select `user`.col from `user` where 1 != 1) as u where 1 != 1", + "Query": "select u.col from (select `user`.col from `user`) as u", + "Table": "`user`" + }, + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select 1 from user_extra where 1 != 1", + "Query": "select 1 from user_extra", + "Table": "user_extra" + } + ] + }, + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select 1 from user_extra as ue where 1 != 1", + "Query": "select 1 from user_extra as ue", + "Table": "user_extra" + } + ] + } + ] + }, + "TablesUsed": [ + "user.user", + "user.user_extra" + ] + } + } +] diff --git a/go/vt/vtgate/planbuilder/testdata/unsupported_cases.json b/go/vt/vtgate/planbuilder/testdata/unsupported_cases.json index fe51b0a1678..b6a7caeb9bd 100644 --- a/go/vt/vtgate/planbuilder/testdata/unsupported_cases.json +++ b/go/vt/vtgate/planbuilder/testdata/unsupported_cases.json @@ -373,5 +373,10 @@ "comment": "select (select 1 from user u having count(ue.col) > 10) from user_extra ue", "query": "select (select 1 from user u having count(ue.col) > 10) from user_extra ue", "plan": "VT12001: unsupported: correlated subquery is only supported for EXISTS" + }, + { + "comment": "CTEs cant use a table with the same name as the CTE alias", + "query": "with user as (select aa from user where user.id=1) select ref.col from ref join user", + "plan": "VT12001: unsupported: do not support CTE that use the CTE alias inside the CTE query" } ] From c83d5f1c02865fb56448af3cb9397a0b8a7839c2 Mon Sep 17 00:00:00 2001 From: Andres Taylor Date: Tue, 24 Oct 2023 14:26:13 +0200 Subject: [PATCH 14/18] test: update expectations Signed-off-by: Andres Taylor --- go/vt/vtgate/executor_select_test.go | 8 ++--- .../planbuilder/testdata/cte_cases.json | 35 ------------------- 2 files changed, 4 insertions(+), 39 deletions(-) diff --git a/go/vt/vtgate/executor_select_test.go b/go/vt/vtgate/executor_select_test.go index 9c8ae647d39..9c1ce203dc2 100644 --- a/go/vt/vtgate/executor_select_test.go +++ b/go/vt/vtgate/executor_select_test.go @@ -1027,7 +1027,7 @@ func TestLastInsertIDInVirtualTable(t *testing.T) { _, err := executorExec(ctx, executor, session, "select * from (select last_insert_id()) as t", nil) require.NoError(t, err) wantQueries := []*querypb.BoundQuery{{ - Sql: "select t.`last_insert_id()` from (select :__lastInsertId as `last_insert_id()` from dual) as t", + Sql: "select `last_insert_id()` from (select :__lastInsertId as `last_insert_id()` from dual) as t", BindVariables: map[string]*querypb.BindVariable{"__lastInsertId": sqltypes.Uint64BindVariable(0)}, }} @@ -4121,7 +4121,7 @@ func TestSelectView(t *testing.T) { _, err = executor.Execute(context.Background(), nil, "TestSelectView", session, "select * from user_details_view", nil) require.NoError(t, err) wantQueries := []*querypb.BoundQuery{{ - Sql: "select user_details_view.id, user_details_view.col from (select `user`.id, user_extra.col from `user`, user_extra where `user`.id = user_extra.user_id) as user_details_view", + Sql: "select id, col from (select `user`.id, user_extra.col from `user`, user_extra where `user`.id = user_extra.user_id) as user_details_view", BindVariables: map[string]*querypb.BindVariable{}, }} utils.MustMatch(t, wantQueries, sbc.Queries) @@ -4130,7 +4130,7 @@ func TestSelectView(t *testing.T) { _, err = executor.Execute(context.Background(), nil, "TestSelectView", session, "select * from user_details_view where id = 2", nil) require.NoError(t, err) wantQueries = []*querypb.BoundQuery{{ - Sql: "select user_details_view.id, user_details_view.col from (select `user`.id, user_extra.col from `user`, user_extra where `user`.id = :id /* INT64 */ and `user`.id = user_extra.user_id) as user_details_view", + Sql: "select id, col from (select `user`.id, user_extra.col from `user`, user_extra where `user`.id = :id /* INT64 */ and `user`.id = user_extra.user_id) as user_details_view", BindVariables: map[string]*querypb.BindVariable{ "id": sqltypes.Int64BindVariable(2), }, @@ -4143,7 +4143,7 @@ func TestSelectView(t *testing.T) { bvtg1, _ := sqltypes.BuildBindVariable([]int64{1, 2, 3, 4, 5}) bvals, _ := sqltypes.BuildBindVariable([]int64{1, 2}) wantQueries = []*querypb.BoundQuery{{ - Sql: "select user_details_view.id, user_details_view.col from (select `user`.id, user_extra.col from `user`, user_extra where `user`.id in ::__vals and `user`.id = user_extra.user_id) as user_details_view", + Sql: "select id, col from (select `user`.id, user_extra.col from `user`, user_extra where `user`.id in ::__vals and `user`.id = user_extra.user_id) as user_details_view", BindVariables: map[string]*querypb.BindVariable{ "vtg1": bvtg1, "__vals": bvals, diff --git a/go/vt/vtgate/planbuilder/testdata/cte_cases.json b/go/vt/vtgate/planbuilder/testdata/cte_cases.json index 5ff06a07eae..0b9690a1e9e 100644 --- a/go/vt/vtgate/planbuilder/testdata/cte_cases.json +++ b/go/vt/vtgate/planbuilder/testdata/cte_cases.json @@ -750,31 +750,6 @@ ] } }, - { - "comment": "with keepers as (select id from unsharded where col is not null order by col desc limit 10) update unsharded as foo left join keepers on foo.id = keepers.id set col1 = 'asdf' where keepers.id is null and foo.col is not null and foo.col < 1000", - "query": "with keepers as (select id from unsharded where col is not null order by col desc limit 10) update unsharded as foo left join keepers on foo.id = keepers.id set col1 = 'asdf' where keepers.id is null and foo.col is not null and foo.col < 1000", - "plan": "VT12001: unsupported: WITH expression in UPDATE statement" - }, - { - "comment": "with keepers as (select id from unsharded where col is not null order by col desc limit 10) delete foo from unsharded as foo left join keepers on foo.id = keepers.id where keepers.id is null and foo.col is not null and foo.col < 1000", - "query": "with keepers as (select id from unsharded where col is not null order by col desc limit 10) delete foo from unsharded as foo left join keepers on foo.id = keepers.id where keepers.id is null and foo.col is not null and foo.col < 1000", - "plan": "VT12001: unsupported: WITH expression in DELETE statement" - }, - { - "comment": "with music as (select * from user) delete music from music where id = 1", - "query": "with music as (select * from user) delete music from music where id = 1", - "plan": "VT12001: unsupported: WITH expression in DELETE statement" - }, - { - "comment": "with music as (select * from user) delete user from music where id = 1", - "query": "with music as (select * from user) delete user from music where id = 1", - "plan": "VT12001: unsupported: WITH expression in DELETE statement" - }, - { - "comment": "with keepers as (select id from unsharded a join unsharded_b b on a.user_id = b.user_id) delete foo from unsharded as foo join keepers on foo.id = keepers.id where keepers.id is null and foo.col is not null and foo.col < 1000", - "query": "with keepers as (select id from unsharded a join unsharded_b b on a.user_id = b.user_id) delete foo from unsharded as foo join keepers on foo.id = keepers.id where keepers.id is null and foo.col is not null and foo.col < 1000", - "plan": "VT12001: unsupported: WITH expression in DELETE statement" - }, { "comment": "with t as (select id, col from user where id = 5) select id from t", "query": "with t as (select id, col from user where id = 5) select id from t", @@ -855,11 +830,6 @@ ] } }, - { - "comment": "with t as (select user.id, id from user where user.id = 5) select t.id from t join user_extra on t.id = user_extra.user_id", - "query": "with t as (select user.id, id from user where user.id = 5) select t.id from t join user_extra on t.id = user_extra.user_id", - "plan": "Duplicate column name 'id'" - }, { "comment": "with t as (select id from user where id = 5) select t.id from user_extra join t on t.id = user_extra.user_id", "query": "with t as (select id from user where id = 5) select t.id from user_extra join t on t.id = user_extra.user_id", @@ -962,11 +932,6 @@ ] } }, - { - "comment": "with t as (select id from user) select t.id from t join user_extra on t.id = user_extra.user_id where t.col = 42", - "query": "with t as (select id from user) select t.id from t join user_extra on t.id = user_extra.user_id where t.col = 42", - "plan": "column 't.col' not found" - }, { "comment": "with t as (select id, col from route1) select id from t where id = 5", "query": "with t as (select id, col from route1) select id from t where id = 5", From 4b9e018757afc0c87a4efe15b0db512e8968cf15 Mon Sep 17 00:00:00 2001 From: Andres Taylor Date: Tue, 24 Oct 2023 15:32:59 +0200 Subject: [PATCH 15/18] test: add end to end tests using CTEs Signed-off-by: Andres Taylor --- .../vtgate/queries/derived/cte_test.go | 74 +++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 go/test/endtoend/vtgate/queries/derived/cte_test.go diff --git a/go/test/endtoend/vtgate/queries/derived/cte_test.go b/go/test/endtoend/vtgate/queries/derived/cte_test.go new file mode 100644 index 00000000000..466ec0fa730 --- /dev/null +++ b/go/test/endtoend/vtgate/queries/derived/cte_test.go @@ -0,0 +1,74 @@ +/* +Copyright 2022 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package misc + +import ( + "testing" +) + +func TestCTEWithOrderByLimit(t *testing.T) { + mcmp, closer := start(t) + defer closer() + + mcmp.Exec("insert into music(id, user_id) values(1,1), (2,5), (3,1), (4,2), (5,3), (6,4), (7,5)") + mcmp.Exec("insert into user(id, name) values(1,'toto'), (2,'tata'), (3,'titi'), (4,'tete'), (5,'foo')") + + mcmp.Exec("with d as (select id,name from user order by id limit 2) select music.id from music join d on music.user_id = d.id") +} + +func TestCTEAggregationOnRHS(t *testing.T) { + mcmp, closer := start(t) + defer closer() + + mcmp.Exec("insert into music(id, user_id) values(1,1), (2,5), (3,1), (4,2), (5,3), (6,4), (7,5)") + mcmp.Exec("insert into user(id, name) values(1,'toto'), (2,'tata'), (3,'titi'), (4,'tete'), (5,'foo')") + + mcmp.Exec("set sql_mode = ''") + mcmp.Exec("with d as (select id, count(*) as a from user) select d.a from music join d on music.user_id = d.id group by 1") +} + +func TestCTERemoveInnerOrderBy(t *testing.T) { + mcmp, closer := start(t) + defer closer() + + mcmp.Exec("insert into music(id, user_id) values(1,1), (2,5), (3,1), (4,2), (5,3), (6,4), (7,5)") + mcmp.Exec("insert into user(id, name) values(1,'toto'), (2,'tata'), (3,'titi'), (4,'tete'), (5,'foo')") + + mcmp.Exec("with toto as (select user.id as oui, music.id as non from user join music on user.id = music.user_id order by user.name) select count(*) from toto") +} + +func TestCTEWithHaving(t *testing.T) { + mcmp, closer := start(t) + defer closer() + + mcmp.Exec("insert into music(id, user_id) values(1,1), (2,5), (3,1), (4,2), (5,3), (6,4), (7,5)") + mcmp.Exec("insert into user(id, name) values(1,'toto'), (2,'tata'), (3,'titi'), (4,'tete'), (5,'foo')") + + mcmp.Exec("set sql_mode = ''") + // For the given query, we can get any id back, because we aren't grouping by it. + mcmp.AssertMatchesAnyNoCompare("with s as (select id from user having count(*) >= 1) select * from s", + "[[INT64(1)]]", "[[INT64(2)]]", "[[INT64(3)]]", "[[INT64(4)]]", "[[INT64(5)]]") +} + +func TestCTEColumns(t *testing.T) { + mcmp, closer := start(t) + defer closer() + + mcmp.Exec("insert into user(id, name) values(1,'toto'), (2,'tata'), (3,'titi'), (4,'tete'), (5,'foo')") + mcmp.AssertMatches(`with t(id) as (SELECT id FROM user) SELECT t.id FROM t ORDER BY t.id DESC`, + `[[INT64(5)] [INT64(4)] [INT64(3)] [INT64(2)] [INT64(1)]]`) +} From 812da102f2123298bc1c376d23f49af7cf48e665 Mon Sep 17 00:00:00 2001 From: Andres Taylor Date: Wed, 25 Oct 2023 11:08:55 +0200 Subject: [PATCH 16/18] test: simplify test setup Signed-off-by: Andres Taylor --- .../vtgate/queries/derived/cte_test.go | 15 +------------ .../vtgate/queries/derived/derived_test.go | 22 ++++++------------- 2 files changed, 8 insertions(+), 29 deletions(-) diff --git a/go/test/endtoend/vtgate/queries/derived/cte_test.go b/go/test/endtoend/vtgate/queries/derived/cte_test.go index 466ec0fa730..677a5dba653 100644 --- a/go/test/endtoend/vtgate/queries/derived/cte_test.go +++ b/go/test/endtoend/vtgate/queries/derived/cte_test.go @@ -1,5 +1,5 @@ /* -Copyright 2022 The Vitess Authors. +Copyright 2023 The Vitess Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -24,9 +24,6 @@ func TestCTEWithOrderByLimit(t *testing.T) { mcmp, closer := start(t) defer closer() - mcmp.Exec("insert into music(id, user_id) values(1,1), (2,5), (3,1), (4,2), (5,3), (6,4), (7,5)") - mcmp.Exec("insert into user(id, name) values(1,'toto'), (2,'tata'), (3,'titi'), (4,'tete'), (5,'foo')") - mcmp.Exec("with d as (select id,name from user order by id limit 2) select music.id from music join d on music.user_id = d.id") } @@ -34,9 +31,6 @@ func TestCTEAggregationOnRHS(t *testing.T) { mcmp, closer := start(t) defer closer() - mcmp.Exec("insert into music(id, user_id) values(1,1), (2,5), (3,1), (4,2), (5,3), (6,4), (7,5)") - mcmp.Exec("insert into user(id, name) values(1,'toto'), (2,'tata'), (3,'titi'), (4,'tete'), (5,'foo')") - mcmp.Exec("set sql_mode = ''") mcmp.Exec("with d as (select id, count(*) as a from user) select d.a from music join d on music.user_id = d.id group by 1") } @@ -45,9 +39,6 @@ func TestCTERemoveInnerOrderBy(t *testing.T) { mcmp, closer := start(t) defer closer() - mcmp.Exec("insert into music(id, user_id) values(1,1), (2,5), (3,1), (4,2), (5,3), (6,4), (7,5)") - mcmp.Exec("insert into user(id, name) values(1,'toto'), (2,'tata'), (3,'titi'), (4,'tete'), (5,'foo')") - mcmp.Exec("with toto as (select user.id as oui, music.id as non from user join music on user.id = music.user_id order by user.name) select count(*) from toto") } @@ -55,9 +46,6 @@ func TestCTEWithHaving(t *testing.T) { mcmp, closer := start(t) defer closer() - mcmp.Exec("insert into music(id, user_id) values(1,1), (2,5), (3,1), (4,2), (5,3), (6,4), (7,5)") - mcmp.Exec("insert into user(id, name) values(1,'toto'), (2,'tata'), (3,'titi'), (4,'tete'), (5,'foo')") - mcmp.Exec("set sql_mode = ''") // For the given query, we can get any id back, because we aren't grouping by it. mcmp.AssertMatchesAnyNoCompare("with s as (select id from user having count(*) >= 1) select * from s", @@ -68,7 +56,6 @@ func TestCTEColumns(t *testing.T) { mcmp, closer := start(t) defer closer() - mcmp.Exec("insert into user(id, name) values(1,'toto'), (2,'tata'), (3,'titi'), (4,'tete'), (5,'foo')") mcmp.AssertMatches(`with t(id) as (SELECT id FROM user) SELECT t.id FROM t ORDER BY t.id DESC`, `[[INT64(5)] [INT64(4)] [INT64(3)] [INT64(2)] [INT64(1)]]`) } diff --git a/go/test/endtoend/vtgate/queries/derived/derived_test.go b/go/test/endtoend/vtgate/queries/derived/derived_test.go index ac9bea1b154..c3360ee4135 100644 --- a/go/test/endtoend/vtgate/queries/derived/derived_test.go +++ b/go/test/endtoend/vtgate/queries/derived/derived_test.go @@ -38,6 +38,9 @@ func start(t *testing.T) (utils.MySQLCompare, func()) { deleteAll() + mcmp.Exec("insert into music(id, user_id) values(1,1), (2,5), (3,1), (4,2), (5,3), (6,4), (7,5)") + mcmp.Exec("insert into user(id, name) values(1,'toto'), (2,'tata'), (3,'titi'), (4,'tete'), (5,'foo')") + return mcmp, func() { deleteAll() mcmp.Close() @@ -49,9 +52,6 @@ func TestDerivedTableWithOrderByLimit(t *testing.T) { mcmp, closer := start(t) defer closer() - mcmp.Exec("insert into music(id, user_id) values(1,1), (2,5), (3,1), (4,2), (5,3), (6,4), (7,5)") - mcmp.Exec("insert into user(id, name) values(1,'toto'), (2,'tata'), (3,'titi'), (4,'tete'), (5,'foo')") - mcmp.Exec("select /*vt+ PLANNER=Gen4 */ music.id from music join (select id,name from user order by id limit 2) as d on music.user_id = d.id") } @@ -59,9 +59,6 @@ func TestDerivedAggregationOnRHS(t *testing.T) { mcmp, closer := start(t) defer closer() - mcmp.Exec("insert into music(id, user_id) values(1,1), (2,5), (3,1), (4,2), (5,3), (6,4), (7,5)") - mcmp.Exec("insert into user(id, name) values(1,'toto'), (2,'tata'), (3,'titi'), (4,'tete'), (5,'foo')") - mcmp.Exec("set sql_mode = ''") mcmp.Exec("select /*vt+ PLANNER=Gen4 */ d.a from music join (select id, count(*) as a from user) as d on music.user_id = d.id group by 1") } @@ -70,9 +67,6 @@ func TestDerivedRemoveInnerOrderBy(t *testing.T) { mcmp, closer := start(t) defer closer() - mcmp.Exec("insert into music(id, user_id) values(1,1), (2,5), (3,1), (4,2), (5,3), (6,4), (7,5)") - mcmp.Exec("insert into user(id, name) values(1,'toto'), (2,'tata'), (3,'titi'), (4,'tete'), (5,'foo')") - mcmp.Exec("select /*vt+ PLANNER=Gen4 */ count(*) from (select user.id as oui, music.id as non from user join music on user.id = music.user_id order by user.name) as toto") } @@ -80,18 +74,16 @@ func TestDerivedTableWithHaving(t *testing.T) { mcmp, closer := start(t) defer closer() - mcmp.Exec("insert into music(id, user_id) values(1,1), (2,5), (3,1), (4,2), (5,3), (6,4), (7,5)") - mcmp.Exec("insert into user(id, name) values(1,'toto'), (2,'tata'), (3,'titi'), (4,'tete'), (5,'foo')") - mcmp.Exec("set sql_mode = ''") // For the given query, we can get any id back, because we aren't grouping by it. - mcmp.AssertMatchesAnyNoCompare("select /*vt+ PLANNER=Gen4 */ * from (select id from user having count(*) >= 1) s", "[[INT64(1)]]", "[[INT64(2)]]", "[[INT64(3)]]", "[[INT64(4)]]", "[[INT64(5)]]") + mcmp.AssertMatchesAnyNoCompare("select /*vt+ PLANNER=Gen4 */ * from (select id from user having count(*) >= 1) s", + "[[INT64(1)]]", "[[INT64(2)]]", "[[INT64(3)]]", "[[INT64(4)]]", "[[INT64(5)]]") } func TestDerivedTableColumns(t *testing.T) { mcmp, closer := start(t) defer closer() - mcmp.Exec("insert into user(id, name) values(1,'toto'), (2,'tata'), (3,'titi'), (4,'tete'), (5,'foo')") - mcmp.AssertMatches(`SELECT /*vt+ PLANNER=gen4 */ t.id FROM (SELECT id FROM user) AS t(id) ORDER BY t.id DESC`, `[[INT64(5)] [INT64(4)] [INT64(3)] [INT64(2)] [INT64(1)]]`) + mcmp.AssertMatches(`SELECT /*vt+ PLANNER=gen4 */ t.id FROM (SELECT id FROM user) AS t(id) ORDER BY t.id DESC`, + `[[INT64(5)] [INT64(4)] [INT64(3)] [INT64(2)] [INT64(1)]]`) } From 2ab394981e69b07500dd064c23216be62c09b1d6 Mon Sep 17 00:00:00 2001 From: Andres Taylor Date: Wed, 25 Oct 2023 11:32:11 +0200 Subject: [PATCH 17/18] use enum instead of pointer to bool Signed-off-by: Andres Taylor --- .../planbuilder/testdata/cte_cases.json | 54 +++++++++++++++++++ .../planbuilder/testdata/from_cases.json | 54 ------------------- .../testdata/unsupported_cases.json | 8 ++- go/vt/vtgate/semantics/derived_table.go | 2 +- go/vt/vtgate/semantics/real_table.go | 15 +++--- go/vt/vtgate/semantics/semantic_state.go | 18 +++++-- go/vt/vtgate/semantics/vindex_table.go | 4 +- go/vt/vtgate/semantics/vtable.go | 4 +- 8 files changed, 84 insertions(+), 75 deletions(-) diff --git a/go/vt/vtgate/planbuilder/testdata/cte_cases.json b/go/vt/vtgate/planbuilder/testdata/cte_cases.json index 0b9690a1e9e..161ffe097e7 100644 --- a/go/vt/vtgate/planbuilder/testdata/cte_cases.json +++ b/go/vt/vtgate/planbuilder/testdata/cte_cases.json @@ -1786,5 +1786,59 @@ "user.user_extra" ] } + }, + { + "comment": "simple WITH query", + "query": "with x as (select * from user) select * from x", + "plan": { + "QueryType": "SELECT", + "Original": "with x as (select * from user) select * from x", + "Instructions": { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select * from (select * from `user` where 1 != 1) as x where 1 != 1", + "Query": "select * from (select * from `user`) as x", + "Table": "`user`" + }, + "TablesUsed": [ + "user.user" + ] + } + }, + { + "comment": "UNION with WITH clause", + "query": "with x as (select id, foo from user) select * from x union select * from x", + "plan": { + "QueryType": "SELECT", + "Original": "with x as (select id, foo from user) select * from x union select * from x", + "Instructions": { + "OperatorType": "Distinct", + "Collations": [ + "(0:2)", + "(1:3)" + ], + "ResultColumns": 2, + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select id, foo, weight_string(id), weight_string(foo) from (select id, foo from (select id, foo from `user` where 1 != 1) as x where 1 != 1 union select id, foo from (select id, foo from `user` where 1 != 1) as x where 1 != 1) as dt where 1 != 1", + "Query": "select id, foo, weight_string(id), weight_string(foo) from (select id, foo from (select id, foo from `user`) as x union select id, foo from (select id, foo from `user`) as x) as dt", + "Table": "`user`" + } + ] + }, + "TablesUsed": [ + "user.user" + ] + } } ] diff --git a/go/vt/vtgate/planbuilder/testdata/from_cases.json b/go/vt/vtgate/planbuilder/testdata/from_cases.json index 714efff23c8..b6a73f4c318 100644 --- a/go/vt/vtgate/planbuilder/testdata/from_cases.json +++ b/go/vt/vtgate/planbuilder/testdata/from_cases.json @@ -4050,59 +4050,5 @@ "comment": "select with a target destination", "query": "select * from `user[-]`.user_metadata", "plan": "VT09017: SELECT with a target destination is not allowed" - }, - { - "comment": "simple WITH query", - "query": "with x as (select * from user) select * from x", - "plan": { - "QueryType": "SELECT", - "Original": "with x as (select * from user) select * from x", - "Instructions": { - "OperatorType": "Route", - "Variant": "Scatter", - "Keyspace": { - "Name": "user", - "Sharded": true - }, - "FieldQuery": "select * from (select * from `user` where 1 != 1) as x where 1 != 1", - "Query": "select * from (select * from `user`) as x", - "Table": "`user`" - }, - "TablesUsed": [ - "user.user" - ] - } - }, - { - "comment": "UNION with WITH clause", - "query": "with x as (select id, foo from user) select * from x union select * from x", - "plan": { - "QueryType": "SELECT", - "Original": "with x as (select id, foo from user) select * from x union select * from x", - "Instructions": { - "OperatorType": "Distinct", - "Collations": [ - "(0:2)", - "(1:3)" - ], - "ResultColumns": 2, - "Inputs": [ - { - "OperatorType": "Route", - "Variant": "Scatter", - "Keyspace": { - "Name": "user", - "Sharded": true - }, - "FieldQuery": "select id, foo, weight_string(id), weight_string(foo) from (select id, foo from (select id, foo from `user` where 1 != 1) as x where 1 != 1 union select id, foo from (select id, foo from `user` where 1 != 1) as x where 1 != 1) as dt where 1 != 1", - "Query": "select id, foo, weight_string(id), weight_string(foo) from (select id, foo from (select id, foo from `user`) as x union select id, foo from (select id, foo from `user`) as x) as dt", - "Table": "`user`" - } - ] - }, - "TablesUsed": [ - "user.user" - ] - } } ] diff --git a/go/vt/vtgate/planbuilder/testdata/unsupported_cases.json b/go/vt/vtgate/planbuilder/testdata/unsupported_cases.json index b6a7caeb9bd..bf1d21629a4 100644 --- a/go/vt/vtgate/planbuilder/testdata/unsupported_cases.json +++ b/go/vt/vtgate/planbuilder/testdata/unsupported_cases.json @@ -378,5 +378,9 @@ "comment": "CTEs cant use a table with the same name as the CTE alias", "query": "with user as (select aa from user where user.id=1) select ref.col from ref join user", "plan": "VT12001: unsupported: do not support CTE that use the CTE alias inside the CTE query" - } -] + }, + { + "comment": "Recursive WITH", + "query": "WITH RECURSIVE cte (n) AS (SELECT 1 UNION ALL SELECT n + 1 FROM cte WHERE n < 5) SELECT * FROM cte", + "plan": "VT12001: unsupported: recursive common table expression" + }] diff --git a/go/vt/vtgate/semantics/derived_table.go b/go/vt/vtgate/semantics/derived_table.go index c72b998f378..9001848f6b4 100644 --- a/go/vt/vtgate/semantics/derived_table.go +++ b/go/vt/vtgate/semantics/derived_table.go @@ -141,7 +141,7 @@ func (dt *DerivedTable) getAliasedTableExpr() *sqlparser.AliasedTableExpr { return dt.ASTNode } -func (dt *DerivedTable) canShortCut() *bool { +func (dt *DerivedTable) canShortCut() shortCut { panic(vterrors.VT12001("should not be called")) } diff --git a/go/vt/vtgate/semantics/real_table.go b/go/vt/vtgate/semantics/real_table.go index dae32531e17..cb638abae82 100644 --- a/go/vt/vtgate/semantics/real_table.go +++ b/go/vt/vtgate/semantics/real_table.go @@ -78,27 +78,24 @@ func (r *RealTable) getAliasedTableExpr() *sqlparser.AliasedTableExpr { return r.ASTNode } -var f = false -var t = true - -func (r *RealTable) canShortCut() *bool { +func (r *RealTable) canShortCut() shortCut { if r.Table == nil { - return &f + return cannotShortCut } if r.Table.Type != "" { // A reference table is not an issue when seeing if a query is going to an unsharded keyspace if r.Table.Type == vindexes.TypeReference { - return &t + return canShortCut } - return &f + return cannotShortCut } name, ok := r.ASTNode.Expr.(sqlparser.TableName) if !ok || name.Name.String() != r.Table.Name.String() { - return &f + return cannotShortCut } - return nil + return dependsOnKeyspace } // GetVindexTable implements the TableInfo interface diff --git a/go/vt/vtgate/semantics/semantic_state.go b/go/vt/vtgate/semantics/semantic_state.go index e66c38c9bb2..13dbc2effb8 100644 --- a/go/vt/vtgate/semantics/semantic_state.go +++ b/go/vt/vtgate/semantics/semantic_state.go @@ -54,7 +54,7 @@ type ( // canShortCut will return nil when the keyspace needs to be checked, // and a true/false if the decision has been made already - canShortCut() *bool + canShortCut() shortCut // getColumns returns the known column information for this table getColumns() []ColumnInfo @@ -150,6 +150,14 @@ type ( // ForeignKeyMode returns the foreign_key flag value ForeignKeyMode(keyspace string) (vschemapb.Keyspace_ForeignKeyMode, error) } + + shortCut = int +) + +const ( + canShortCut shortCut = iota + cannotShortCut + dependsOnKeyspace ) var ( @@ -634,20 +642,20 @@ func (st *SemTable) SingleUnshardedKeyspace() (ks *vindexes.Keyspace, tables []* sc := table.canShortCut() var vtbl *vindexes.Table - switch { - case sc == nil: + switch sc { + case dependsOnKeyspace: // we have to check the KS if the table doesn't know if it can be shortcut or not vtbl = table.GetVindexTable() if !validKS(vtbl.Keyspace) { return nil, nil } - case *sc: + case canShortCut: // the table knows that it's safe to shortcut vtbl = table.GetVindexTable() if vtbl == nil { continue } - case !*sc: + case cannotShortCut: // the table knows that we can't shortcut return nil, nil } diff --git a/go/vt/vtgate/semantics/vindex_table.go b/go/vt/vtgate/semantics/vindex_table.go index 6a2df4639d8..f78e68cbd5b 100644 --- a/go/vt/vtgate/semantics/vindex_table.go +++ b/go/vt/vtgate/semantics/vindex_table.go @@ -71,8 +71,8 @@ func (v *VindexTable) getAliasedTableExpr() *sqlparser.AliasedTableExpr { return v.Table.getAliasedTableExpr() } -func (v *VindexTable) canShortCut() *bool { - return &f +func (v *VindexTable) canShortCut() shortCut { + return cannotShortCut } // GetColumns implements the TableInfo interface diff --git a/go/vt/vtgate/semantics/vtable.go b/go/vt/vtgate/semantics/vtable.go index e4d9f8b3649..48439694b47 100644 --- a/go/vt/vtgate/semantics/vtable.go +++ b/go/vt/vtgate/semantics/vtable.go @@ -74,8 +74,8 @@ func (v *vTableInfo) getAliasedTableExpr() *sqlparser.AliasedTableExpr { return nil } -func (v *vTableInfo) canShortCut() *bool { - return &t +func (v *vTableInfo) canShortCut() shortCut { + return canShortCut } // GetVindexTable implements the TableInfo interface From 42b6982ea23c50d55ce3cf7e97243146a451dd35 Mon Sep 17 00:00:00 2001 From: Andres Taylor Date: Wed, 25 Oct 2023 11:35:07 +0200 Subject: [PATCH 18/18] minor clean ups Signed-off-by: Andres Taylor --- go/vt/vtgate/planbuilder/testdata/unsupported_cases.json | 8 +++++++- go/vt/vtgate/semantics/table_collector.go | 3 +-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/go/vt/vtgate/planbuilder/testdata/unsupported_cases.json b/go/vt/vtgate/planbuilder/testdata/unsupported_cases.json index bf1d21629a4..6070ede27b9 100644 --- a/go/vt/vtgate/planbuilder/testdata/unsupported_cases.json +++ b/go/vt/vtgate/planbuilder/testdata/unsupported_cases.json @@ -383,4 +383,10 @@ "comment": "Recursive WITH", "query": "WITH RECURSIVE cte (n) AS (SELECT 1 UNION ALL SELECT n + 1 FROM cte WHERE n < 5) SELECT * FROM cte", "plan": "VT12001: unsupported: recursive common table expression" - }] + }, + { + "comment": "Alias cannot clash with base tables", + "query": "WITH user AS (SELECT col FROM user) SELECT * FROM user", + "plan": "VT12001: unsupported: do not support CTE that use the CTE alias inside the CTE query" + } +] \ No newline at end of file diff --git a/go/vt/vtgate/semantics/table_collector.go b/go/vt/vtgate/semantics/table_collector.go index a1779fad8cb..12fb691874f 100644 --- a/go/vt/vtgate/semantics/table_collector.go +++ b/go/vt/vtgate/semantics/table_collector.go @@ -18,7 +18,6 @@ package semantics import ( querypb "vitess.io/vitess/go/vt/proto/query" - vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/vterrors" "vitess.io/vitess/go/vt/vtgate/evalengine" @@ -130,7 +129,7 @@ func (tc *tableCollector) handleDerivedTable(node *sqlparser.AliasedTableExpr, t case *sqlparser.Union: return tc.addUnionDerivedTable(sel, node, node.Columns, node.As) default: - return vterrors.Errorf(vtrpcpb.Code_INTERNAL, "[BUG] %T in a derived table", sel) + return vterrors.VT13001("[BUG] %T in a derived table", sel) } }