From 3088aee66d6fff001a3e072a5f9ec2bfb8852b91 Mon Sep 17 00:00:00 2001 From: Andres Taylor Date: Sun, 7 Mar 2021 09:55:27 +0100 Subject: [PATCH 1/8] implemented table alias functionality in the semantic analysis Signed-off-by: Andres Taylor --- go/vt/vtgate/planbuilder/querygraph_test.go | 4 +- go/vt/vtgate/planbuilder/route_planning.go | 6 +- .../planbuilder/testdata/from_cases.txt | 2 + .../planbuilder/testdata/select_cases.txt | 2 + .../planbuilder/testdata/wireup_cases.txt | 2 + go/vt/vtgate/semantics/analyzer.go | 61 +++++++++++----- go/vt/vtgate/semantics/analyzer_test.go | 72 ++++++++++++++----- go/vt/vtgate/semantics/semantic_state.go | 31 ++++---- 8 files changed, 128 insertions(+), 52 deletions(-) diff --git a/go/vt/vtgate/planbuilder/querygraph_test.go b/go/vt/vtgate/planbuilder/querygraph_test.go index d353d04aa61..551ec9bb033 100644 --- a/go/vt/vtgate/planbuilder/querygraph_test.go +++ b/go/vt/vtgate/planbuilder/querygraph_test.go @@ -123,7 +123,7 @@ func TestQueryGraph(t *testing.T) { t.Run(fmt.Sprintf("%d %s", i, sql), func(t *testing.T) { tree, err := sqlparser.Parse(sql) require.NoError(t, err) - semTable, err := semantics.Analyse(tree, &fakeSI{}) + semTable, err := semantics.Analyse(tree, "", &fakeSI{}) require.NoError(t, err) qgraph, err := createQGFromSelect(tree.(*sqlparser.Select), semTable) require.NoError(t, err) @@ -136,7 +136,7 @@ func TestQueryGraph(t *testing.T) { func TestString(t *testing.T) { tree, err := sqlparser.Parse("select * from a,b join c on b.id = c.id where a.id = b.id and b.col IN (select 42) and func() = 'foo'") require.NoError(t, err) - semTable, err := semantics.Analyse(tree, &fakeSI{}) + semTable, err := semantics.Analyse(tree, "", &fakeSI{}) require.NoError(t, err) qgraph, err := createQGFromSelect(tree.(*sqlparser.Select), semTable) require.NoError(t, err) diff --git a/go/vt/vtgate/planbuilder/route_planning.go b/go/vt/vtgate/planbuilder/route_planning.go index 5724584651d..23a913dfdf2 100644 --- a/go/vt/vtgate/planbuilder/route_planning.go +++ b/go/vt/vtgate/planbuilder/route_planning.go @@ -46,7 +46,11 @@ func gen4Planner(_ string) func(sqlparser.Statement, sqlparser.BindVars, Context } func newBuildSelectPlan(sel *sqlparser.Select, vschema ContextVSchema) (engine.Primitive, error) { - semTable, err := semantics.Analyse(sel, vschema) + keyspace, err := vschema.DefaultKeyspace() + if err != nil { + return nil, err + } + semTable, err := semantics.Analyse(sel, keyspace.Name, vschema) if err != nil { return nil, err } diff --git a/go/vt/vtgate/planbuilder/testdata/from_cases.txt b/go/vt/vtgate/planbuilder/testdata/from_cases.txt index 50b3be4af10..f63ea1d3e21 100644 --- a/go/vt/vtgate/planbuilder/testdata/from_cases.txt +++ b/go/vt/vtgate/planbuilder/testdata/from_cases.txt @@ -302,6 +302,7 @@ Gen4 plan same as above "Table": "unsharded" } } +Gen4 plan same as above # ',' join information_schema "select a.id,b.id from information_schema.a as a, information_schema.b as b" @@ -337,6 +338,7 @@ Gen4 plan same as above "Table": "unsharded" } } +Gen4 plan same as above # Left join, single chunk "select m1.col from unsharded as m1 left join unsharded as m2 on m1.a=m2.b" diff --git a/go/vt/vtgate/planbuilder/testdata/select_cases.txt b/go/vt/vtgate/planbuilder/testdata/select_cases.txt index 805066d5981..079349df29a 100644 --- a/go/vt/vtgate/planbuilder/testdata/select_cases.txt +++ b/go/vt/vtgate/planbuilder/testdata/select_cases.txt @@ -950,6 +950,7 @@ Gen4 plan same as above "Vindex": "user_index" } } +Gen4 plan same as above # Column Aliasing with Column "select user0_.col as col0_ from user user0_ where id = 1 order by col0_ desc limit 3" @@ -972,6 +973,7 @@ Gen4 plan same as above "Vindex": "user_index" } } +Gen4 plan same as above # Booleans and parenthesis "select * from user where (id = 1) AND name = true limit 5" diff --git a/go/vt/vtgate/planbuilder/testdata/wireup_cases.txt b/go/vt/vtgate/planbuilder/testdata/wireup_cases.txt index 4fc36b8dd1e..d4766864a73 100644 --- a/go/vt/vtgate/planbuilder/testdata/wireup_cases.txt +++ b/go/vt/vtgate/planbuilder/testdata/wireup_cases.txt @@ -633,7 +633,9 @@ # Invalid value in IN clause from LHS of join "select u1.id from user u1 join user u2 where u1.id = 18446744073709551616" "strconv.ParseUint: parsing "18446744073709551616": value out of range" +Gen4 plan same as above # Invalid value in IN clause from RHS of join "select u1.id from user u1 join user u2 where u2.id = 18446744073709551616" "strconv.ParseUint: parsing "18446744073709551616": value out of range" +Gen4 plan same as above diff --git a/go/vt/vtgate/semantics/analyzer.go b/go/vt/vtgate/semantics/analyzer.go index ef81f01d806..80836db27b9 100644 --- a/go/vt/vtgate/semantics/analyzer.go +++ b/go/vt/vtgate/semantics/analyzer.go @@ -29,20 +29,32 @@ type ( analyzer struct { si SchemaInformation - Tables []*TableInfo - scopes []*scope - - exprDeps map[sqlparser.Expr]TableSet - err error + Tables []*TableInfo + scopes []*scope + exprDeps map[sqlparser.Expr]TableSet + err error + currentDb string } ) // newAnalyzer create the semantic analyzer -func newAnalyzer(si SchemaInformation) *analyzer { +func newAnalyzer(dbName string, si SchemaInformation) *analyzer { return &analyzer{ - exprDeps: map[sqlparser.Expr]TableSet{}, - si: si, + exprDeps: map[sqlparser.Expr]TableSet{}, + currentDb: dbName, + si: si, + } +} + +// Analyse analyzes the parsed query. +func Analyse(statement sqlparser.Statement, currentDb string, si SchemaInformation) (*SemTable, error) { + analyzer := newAnalyzer(currentDb, si) + // Initial scope + err := analyzer.analyze(statement) + if err != nil { + return nil, err } + return &SemTable{exprDependencies: analyzer.exprDeps, Tables: analyzer.Tables}, nil } // analyzeDown pushes new scopes when we encounter sub queries, @@ -103,9 +115,6 @@ func (a *analyzer) analyzeTableExprs(tablExprs sqlparser.TableExprs) error { func (a *analyzer) analyzeTableExpr(tableExpr sqlparser.TableExpr) error { switch table := tableExpr.(type) { case *sqlparser.AliasedTableExpr: - if !table.As.IsEmpty() { - return Gen4NotSupportedF("table aliases") - } return a.bindTable(table, table.Expr) case *sqlparser.JoinTableExpr: if table.Join != sqlparser.NormalJoinType { @@ -125,13 +134,28 @@ func (a *analyzer) analyzeTableExpr(tableExpr sqlparser.TableExpr) error { // resolveQualifiedColumn handles `tabl.col` expressions func (a *analyzer) resolveQualifiedColumn(current *scope, expr *sqlparser.ColName) (*TableInfo, error) { - qualifier := expr.Qualifier.Name.String() + id := tableID{ + dbName: expr.Qualifier.Qualifier.String(), + tableName: expr.Qualifier.Name.String(), + } + id2 := tableID{ + dbName: a.currentDb, + tableName: expr.Qualifier.Name.String(), + } + checkCurrentDB := id.dbName == "" + // search up the scope stack until we find a match for current != nil { - tableExpr, found := current.tables[qualifier] + tableExpr, found := current.tables[id] if found { return tableExpr, nil } + if checkCurrentDB { + tableExpr, found := current.tables[id2] + if found { + return tableExpr, nil + } + } current = current.parent } @@ -181,7 +205,8 @@ func (a *analyzer) bindTable(alias *sqlparser.AliasedTableExpr, expr sqlparser.S } a.popScope() scope := a.currentScope() - return scope.addTable(alias.As.String(), &TableInfo{alias, nil}) + dbName := "" // derived tables are always referenced only by their alias - they cannot be found using a fully qualified name + return scope.addTable(dbName, alias.As.String(), &TableInfo{alias, nil}) case sqlparser.TableName: tbl, vdx, _, _, _, err := a.si.FindTableOrVindex(t) if err != nil { @@ -194,9 +219,13 @@ func (a *analyzer) bindTable(alias *sqlparser.AliasedTableExpr, expr sqlparser.S table := &TableInfo{alias, tbl} a.Tables = append(a.Tables, table) if alias.As.IsEmpty() { - return scope.addTable(t.Name.String(), table) + dbName := t.Qualifier.String() + if dbName == "" { + dbName = a.currentDb + } + return scope.addTable(dbName, t.Name.String(), table) } - return scope.addTable(alias.As.String(), table) + return scope.addTable("", alias.As.String(), table) } return nil } diff --git a/go/vt/vtgate/semantics/analyzer_test.go b/go/vt/vtgate/semantics/analyzer_test.go index 02cfffa9ff1..2189abb8f4e 100644 --- a/go/vt/vtgate/semantics/analyzer_test.go +++ b/go/vt/vtgate/semantics/analyzer_test.go @@ -51,7 +51,7 @@ func TestScopeForSubqueries(t *testing.T) { select t.col1, ( select t.col2 from z as t) from x as t` - stmt, semTable := parseAndAnalyze(t, query) + stmt, semTable := parseAndAnalyze(t, query, "") sel, _ := stmt.(*sqlparser.Select) @@ -64,17 +64,56 @@ from x as t` } func TestBindingSingleTable(t *testing.T) { + t.Run("positive tests", func(t *testing.T) { + queries := []string{ + "select col from tabl", + "select tabl.col from tabl", + "select d.tabl.col from tabl", + "select col from d.tabl", + "select tabl.col from d.tabl", + "select d.tabl.col from d.tabl", + } + for _, query := range queries { + t.Run(query, func(t *testing.T) { + stmt, semTable := parseAndAnalyze(t, query, "d") + sel, _ := stmt.(*sqlparser.Select) + t1 := sel.From[0].(*sqlparser.AliasedTableExpr) + ts := semTable.TableSetFor(t1) + assert.EqualValues(t, 1, ts) + + d := semTable.Dependencies(extract(sel, 0)) + require.Equal(t, T0, d, query) + }) + } + }) + t.Run("negative tests", func(t *testing.T) { + queries := []string{ + "select foo.col from tabl", + "select d.tabl.col from X as tabl", + "select tabl.col from d.tabl as X", + "select d.tabl.col from d.tabl as X", + } + for _, query := range queries { + t.Run(query, func(t *testing.T) { + parse, err := sqlparser.Parse(query) + require.NoError(t, err) + _, err = Analyse(parse, "d", &fakeSI{}) + require.Error(t, err) + }) + } + }) +} + +func TestBindingSingleAliasedTable(t *testing.T) { queries := []string{ - "select col from tabl", - "select tabl.col from tabl", - "select d.tabl.col from tabl", - "select col from d.tabl", - "select tabl.col from d.tabl", - "select d.tabl.col from d.tabl", + "select col from tabl as X", + "select tabl.col from X as tabl", + "select col from d.X as tabl", + "select tabl.col from d.X as tabl", } for _, query := range queries { t.Run(query, func(t *testing.T) { - stmt, semTable := parseAndAnalyze(t, query) + stmt, semTable := parseAndAnalyze(t, query, "") sel, _ := stmt.(*sqlparser.Select) t1 := sel.From[0].(*sqlparser.AliasedTableExpr) ts := semTable.TableSetFor(t1) @@ -89,7 +128,7 @@ func TestBindingSingleTable(t *testing.T) { func TestUnion(t *testing.T) { query := "select col1 from tabl1 union select col2 from tabl2" - stmt, semTable := parseAndAnalyze(t, query) + stmt, semTable := parseAndAnalyze(t, query, "") union, _ := stmt.(*sqlparser.Union) sel1 := union.FirstStatement.(*sqlparser.Select) sel2 := union.UnionSelects[0].Statement.(*sqlparser.Select) @@ -137,7 +176,7 @@ func TestBindingMultiTable(t *testing.T) { }} for _, query := range queries { t.Run(query.query, func(t *testing.T) { - stmt, semTable := parseAndAnalyze(t, query.query) + stmt, semTable := parseAndAnalyze(t, query.query, "") sel, _ := stmt.(*sqlparser.Select) assert.Equal(t, query.deps, semTable.Dependencies(extract(sel, 0)), query.query) }) @@ -146,7 +185,7 @@ func TestBindingMultiTable(t *testing.T) { func TestBindingSingleDepPerTable(t *testing.T) { query := "select t.col + t.col2 from t" - stmt, semTable := parseAndAnalyze(t, query) + stmt, semTable := parseAndAnalyze(t, query, "") sel, _ := stmt.(*sqlparser.Select) d := semTable.Dependencies(extract(sel, 0)) @@ -168,7 +207,7 @@ func TestNotUniqueTableName(t *testing.T) { t.Skip("table alias not implemented") } parse, _ := sqlparser.Parse(query) - _, err := Analyse(parse, &fakeSI{}) + _, err := Analyse(parse, "test", &fakeSI{}) require.Error(t, err) require.Contains(t, err.Error(), "Not unique table/alias") }) @@ -183,7 +222,7 @@ func TestMissingTable(t *testing.T) { for _, query := range queries { t.Run(query, func(t *testing.T) { parse, _ := sqlparser.Parse(query) - _, err := Analyse(parse, &fakeSI{}) + _, err := Analyse(parse, "", &fakeSI{}) require.Error(t, err) require.Contains(t, err.Error(), "Unknown table") }) @@ -262,7 +301,7 @@ func TestUnknownColumnMap2(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { si := &fakeSI{tables: test.schema} - _, err := Analyse(parse, si) + _, err := Analyse(parse, "", si) if test.err { require.Error(t, err) } else { @@ -272,10 +311,11 @@ func TestUnknownColumnMap2(t *testing.T) { } } -func parseAndAnalyze(t *testing.T, query string) (sqlparser.Statement, *SemTable) { +func parseAndAnalyze(t *testing.T, query, dbName string) (sqlparser.Statement, *SemTable) { + t.Helper() parse, err := sqlparser.Parse(query) require.NoError(t, err) - semTable, err := Analyse(parse, &fakeSI{ + semTable, err := Analyse(parse, dbName, &fakeSI{ tables: map[string]*vindexes.Table{ "t": {Name: sqlparser.NewTableIdent("t")}, }, diff --git a/go/vt/vtgate/semantics/semantic_state.go b/go/vt/vtgate/semantics/semantic_state.go index 31a187c2769..2e76c12fff1 100644 --- a/go/vt/vtgate/semantics/semantic_state.go +++ b/go/vt/vtgate/semantics/semantic_state.go @@ -44,9 +44,13 @@ type ( exprDependencies map[sqlparser.Expr]TableSet } + tableID struct { + dbName, tableName string + } + scope struct { parent *scope - tables map[string]*TableInfo + tables map[tableID]*TableInfo } // SchemaInformation is used tp provide table information from Vschema. @@ -100,29 +104,22 @@ func (st *SemTable) Dependencies(expr sqlparser.Expr) TableSet { } func newScope(parent *scope) *scope { - return &scope{tables: map[string]*TableInfo{}, parent: parent} + return &scope{tables: map[tableID]*TableInfo{}, parent: parent} } -func (s *scope) addTable(name string, table *TableInfo) error { - _, found := s.tables[name] +func (s *scope) addTable(dbName, tableName string, table *TableInfo) error { + id := tableID{ + dbName: dbName, + tableName: tableName, + } + _, found := s.tables[id] if found { - return vterrors.NewErrorf(vtrpcpb.Code_INVALID_ARGUMENT, vterrors.NonUniqTable, "Not unique table/alias: '%s'", name) + return vterrors.NewErrorf(vtrpcpb.Code_INVALID_ARGUMENT, vterrors.NonUniqTable, "Not unique table/alias: '%s'", tableName) } - s.tables[name] = table + s.tables[id] = table return nil } -// Analyse analyzes the parsed query. -func Analyse(statement sqlparser.Statement, si SchemaInformation) (*SemTable, error) { - analyzer := newAnalyzer(si) - // Initial scope - err := analyzer.analyze(statement) - if err != nil { - return nil, err - } - return &SemTable{exprDependencies: analyzer.exprDeps, Tables: analyzer.Tables}, nil -} - // IsOverlapping returns true if at least one table exists in both sets func (ts TableSet) IsOverlapping(b TableSet) bool { return ts&b != 0 } From 5cc5174db3bd1638934b3b4fd3c4bc4c1ed9d96d Mon Sep 17 00:00:00 2001 From: Andres Taylor Date: Sun, 7 Mar 2021 10:19:13 +0100 Subject: [PATCH 2/8] added more negative tests and made sure to fail early Signed-off-by: Andres Taylor --- go/vt/vtgate/semantics/analyzer.go | 9 +- go/vt/vtgate/semantics/analyzer_test.go | 140 +++++++++++++++--------- 2 files changed, 93 insertions(+), 56 deletions(-) diff --git a/go/vt/vtgate/semantics/analyzer.go b/go/vt/vtgate/semantics/analyzer.go index 80836db27b9..fcd35342f9f 100644 --- a/go/vt/vtgate/semantics/analyzer.go +++ b/go/vt/vtgate/semantics/analyzer.go @@ -83,10 +83,11 @@ func (a *analyzer) analyzeDown(cursor *sqlparser.Cursor) bool { t, err := a.resolveColumn(node, current) if err != nil { a.err = err + } else { + a.exprDeps[node] = t } - a.exprDeps[node] = t } - return a.shouldContinue() + return true // we always return true here, and then return false on the going up phase to abort on error } func (a *analyzer) resolveColumn(colName *sqlparser.ColName, current *scope) (TableSet, error) { @@ -142,7 +143,7 @@ func (a *analyzer) resolveQualifiedColumn(current *scope, expr *sqlparser.ColNam dbName: a.currentDb, tableName: expr.Qualifier.Name.String(), } - checkCurrentDB := id.dbName == "" + checkCurrentDB := id.dbName == "" && id != id2 // search up the scope stack until we find a match for current != nil { @@ -240,7 +241,7 @@ func (a *analyzer) analyzeUp(cursor *sqlparser.Cursor) bool { case *sqlparser.Union, *sqlparser.Select: a.popScope() } - return true + return a.shouldContinue() } func (a *analyzer) shouldContinue() bool { diff --git a/go/vt/vtgate/semantics/analyzer_test.go b/go/vt/vtgate/semantics/analyzer_test.go index 2189abb8f4e..57693e0bc68 100644 --- a/go/vt/vtgate/semantics/analyzer_test.go +++ b/go/vt/vtgate/semantics/analyzer_test.go @@ -105,24 +105,42 @@ func TestBindingSingleTable(t *testing.T) { } func TestBindingSingleAliasedTable(t *testing.T) { - queries := []string{ - "select col from tabl as X", - "select tabl.col from X as tabl", - "select col from d.X as tabl", - "select tabl.col from d.X as tabl", - } - for _, query := range queries { - t.Run(query, func(t *testing.T) { - stmt, semTable := parseAndAnalyze(t, query, "") - sel, _ := stmt.(*sqlparser.Select) - t1 := sel.From[0].(*sqlparser.AliasedTableExpr) - ts := semTable.TableSetFor(t1) - assert.EqualValues(t, 1, ts) - - d := semTable.Dependencies(extract(sel, 0)) - require.Equal(t, T0, d, query) - }) - } + t.Run("positive tests", func(t *testing.T) { + queries := []string{ + "select col from tabl as X", + "select tabl.col from X as tabl", + "select col from d.X as tabl", + "select tabl.col from d.X as tabl", + } + for _, query := range queries { + t.Run(query, func(t *testing.T) { + stmt, semTable := parseAndAnalyze(t, query, "") + sel, _ := stmt.(*sqlparser.Select) + t1 := sel.From[0].(*sqlparser.AliasedTableExpr) + ts := semTable.TableSetFor(t1) + assert.EqualValues(t, 1, ts) + + d := semTable.Dependencies(extract(sel, 0)) + require.Equal(t, T0, d, query) + }) + } + }) + t.Run("negative tests", func(t *testing.T) { + queries := []string{ + "select tabl.col from tabl as X", + "select X.col from X as tabl", + "select d.X.col from d.X as tabl", + "select d.tabl.col from d.X as tabl", + } + for _, query := range queries { + t.Run(query, func(t *testing.T) { + parse, err := sqlparser.Parse(query) + require.NoError(t, err) + _, err = Analyse(parse, "") + require.Error(t, err) + }) + } + }) } func TestUnion(t *testing.T) { @@ -147,40 +165,58 @@ func TestUnion(t *testing.T) { } func TestBindingMultiTable(t *testing.T) { - type testCase struct { - query string - deps TableSet - } - queries := []testCase{{ - query: "select t.col from t, s", - deps: T0, - }, { - query: "select t.col from t join s", - deps: T0, - }, { - query: "select max(t.col+s.col) from t, s", - deps: T0 | T1, - }, { - query: "select max(t.col+s.col) from t join s", - deps: T0 | T1, - }, { - query: "select case t.col when s.col then r.col else u.col end from t, s, r, w, u", - deps: T0 | T1 | T2 | T4, - //}, { - // // 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: T0, - //}, { - // query: "select (select 42 from s where r.id = s.id) from r", - // deps: T0 | T1, - }} - for _, query := range queries { - t.Run(query.query, func(t *testing.T) { - stmt, semTable := parseAndAnalyze(t, query.query, "") - sel, _ := stmt.(*sqlparser.Select) - assert.Equal(t, query.deps, semTable.Dependencies(extract(sel, 0)), query.query) - }) - } + t.Run("positive tests", func(t *testing.T) { + + type testCase struct { + query string + deps TableSet + } + queries := []testCase{{ + query: "select t.col from t, s", + deps: T0, + }, { + query: "select t.col from t join s", + deps: T0, + }, { + query: "select max(t.col+s.col) from t, s", + deps: T0 | T1, + }, { + query: "select max(t.col+s.col) from t join s", + deps: T0 | T1, + }, { + query: "select case t.col when s.col then r.col else u.col end from t, s, r, w, u", + deps: T0 | T1 | T2 | T4, + //}, { + // // 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: T0, + //}, { + // query: "select (select 42 from s where r.id = s.id) from r", + // deps: T0 | T1, + }} + for _, query := range queries { + t.Run(query.query, func(t *testing.T) { + stmt, semTable := parseAndAnalyze(t, query.query, "") + sel, _ := stmt.(*sqlparser.Select) + assert.Equal(t, query.deps, semTable.Dependencies(extract(sel, 0)), query.query) + }) + } + }) + + t.Run("negative tests", func(t *testing.T) { + + queries := []string{ + "select 1 from d.tabl, d.foo as tabl", + } + for _, query := range queries { + t.Run(query, func(t *testing.T) { + parse, err := sqlparser.Parse(query) + require.NoError(t, err) + _, err = Analyse(parse, "") + require.Error(t, err) + }) + } + }) } func TestBindingSingleDepPerTable(t *testing.T) { From 18e61f08f8aca39860ed14152cf2aa6a35f55c7e Mon Sep 17 00:00:00 2001 From: Andres Taylor Date: Mon, 12 Apr 2021 15:13:37 +0200 Subject: [PATCH 3/8] make tests green again Signed-off-by: Andres Taylor --- go/vt/vtgate/semantics/analyzer_test.go | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/go/vt/vtgate/semantics/analyzer_test.go b/go/vt/vtgate/semantics/analyzer_test.go index 57693e0bc68..b662f876965 100644 --- a/go/vt/vtgate/semantics/analyzer_test.go +++ b/go/vt/vtgate/semantics/analyzer_test.go @@ -20,8 +20,9 @@ import ( "strings" "testing" - "vitess.io/vitess/go/vt/key" querypb "vitess.io/vitess/go/vt/proto/query" + + "vitess.io/vitess/go/vt/key" topodatapb "vitess.io/vitess/go/vt/proto/topodata" "vitess.io/vitess/go/vt/vtgate/vindexes" @@ -136,7 +137,11 @@ func TestBindingSingleAliasedTable(t *testing.T) { t.Run(query, func(t *testing.T) { parse, err := sqlparser.Parse(query) require.NoError(t, err) - _, err = Analyse(parse, "") + _, err = Analyse(parse, "", &fakeSI{ + tables: map[string]*vindexes.Table{ + "t": {Name: sqlparser.NewTableIdent("t")}, + }, + }) require.Error(t, err) }) } @@ -196,7 +201,7 @@ func TestBindingMultiTable(t *testing.T) { }} for _, query := range queries { t.Run(query.query, func(t *testing.T) { - stmt, semTable := parseAndAnalyze(t, query.query, "") + stmt, semTable := parseAndAnalyze(t, query.query, "user") sel, _ := stmt.(*sqlparser.Select) assert.Equal(t, query.deps, semTable.Dependencies(extract(sel, 0)), query.query) }) @@ -204,7 +209,7 @@ func TestBindingMultiTable(t *testing.T) { }) t.Run("negative tests", func(t *testing.T) { - + t.Skip("implement me!") queries := []string{ "select 1 from d.tabl, d.foo as tabl", } @@ -212,7 +217,11 @@ func TestBindingMultiTable(t *testing.T) { t.Run(query, func(t *testing.T) { parse, err := sqlparser.Parse(query) require.NoError(t, err) - _, err = Analyse(parse, "") + _, err = Analyse(parse, "", &fakeSI{ + tables: map[string]*vindexes.Table{ + "t": {Name: sqlparser.NewTableIdent("t")}, + }, + }) require.Error(t, err) }) } @@ -239,8 +248,8 @@ func TestNotUniqueTableName(t *testing.T) { for _, query := range queries { t.Run(query, func(t *testing.T) { - if strings.Contains(query, "as") { - t.Skip("table alias not implemented") + if strings.Contains(query, ") as") { + t.Skip("derived tables not implemented") } parse, _ := sqlparser.Parse(query) _, err := Analyse(parse, "test", &fakeSI{}) From 481e317a36fa6f1215aaa70a801c99486de453c1 Mon Sep 17 00:00:00 2001 From: Florent Poinsard Date: Thu, 15 Apr 2021 10:25:30 +0200 Subject: [PATCH 4/8] Implemented table aliases in semantic analysis Signed-off-by: Florent Poinsard --- go/vt/vtgate/planbuilder/querygraph_test.go | 4 +- go/vt/vtgate/planbuilder/route_planning.go | 2 +- go/vt/vtgate/semantics/analyzer.go | 57 ++++++++++----------- go/vt/vtgate/semantics/analyzer_test.go | 26 +++++----- go/vt/vtgate/semantics/semantic_state.go | 31 +++++------ 5 files changed, 56 insertions(+), 64 deletions(-) diff --git a/go/vt/vtgate/planbuilder/querygraph_test.go b/go/vt/vtgate/planbuilder/querygraph_test.go index 551ec9bb033..76a6052756f 100644 --- a/go/vt/vtgate/planbuilder/querygraph_test.go +++ b/go/vt/vtgate/planbuilder/querygraph_test.go @@ -123,7 +123,7 @@ func TestQueryGraph(t *testing.T) { t.Run(fmt.Sprintf("%d %s", i, sql), func(t *testing.T) { tree, err := sqlparser.Parse(sql) require.NoError(t, err) - semTable, err := semantics.Analyse(tree, "", &fakeSI{}) + semTable, err := semantics.Analyze(tree, "", &fakeSI{}) require.NoError(t, err) qgraph, err := createQGFromSelect(tree.(*sqlparser.Select), semTable) require.NoError(t, err) @@ -136,7 +136,7 @@ func TestQueryGraph(t *testing.T) { func TestString(t *testing.T) { tree, err := sqlparser.Parse("select * from a,b join c on b.id = c.id where a.id = b.id and b.col IN (select 42) and func() = 'foo'") require.NoError(t, err) - semTable, err := semantics.Analyse(tree, "", &fakeSI{}) + semTable, err := semantics.Analyze(tree, "", &fakeSI{}) require.NoError(t, err) qgraph, err := createQGFromSelect(tree.(*sqlparser.Select), semTable) require.NoError(t, err) diff --git a/go/vt/vtgate/planbuilder/route_planning.go b/go/vt/vtgate/planbuilder/route_planning.go index 23a913dfdf2..956ec6de241 100644 --- a/go/vt/vtgate/planbuilder/route_planning.go +++ b/go/vt/vtgate/planbuilder/route_planning.go @@ -50,7 +50,7 @@ func newBuildSelectPlan(sel *sqlparser.Select, vschema ContextVSchema) (engine.P if err != nil { return nil, err } - semTable, err := semantics.Analyse(sel, keyspace.Name, vschema) + semTable, err := semantics.Analyze(sel, keyspace.Name, vschema) if err != nil { return nil, err } diff --git a/go/vt/vtgate/semantics/analyzer.go b/go/vt/vtgate/semantics/analyzer.go index fcd35342f9f..6932ac6759a 100644 --- a/go/vt/vtgate/semantics/analyzer.go +++ b/go/vt/vtgate/semantics/analyzer.go @@ -46,8 +46,8 @@ func newAnalyzer(dbName string, si SchemaInformation) *analyzer { } } -// Analyse analyzes the parsed query. -func Analyse(statement sqlparser.Statement, currentDb string, si SchemaInformation) (*SemTable, error) { +// Analyze analyzes the parsed query. +func Analyze(statement sqlparser.Statement, currentDb string, si SchemaInformation) (*SemTable, error) { analyzer := newAnalyzer(currentDb, si) // Initial scope err := analyzer.analyze(statement) @@ -135,31 +135,18 @@ func (a *analyzer) analyzeTableExpr(tableExpr sqlparser.TableExpr) error { // resolveQualifiedColumn handles `tabl.col` expressions func (a *analyzer) resolveQualifiedColumn(current *scope, expr *sqlparser.ColName) (*TableInfo, error) { - id := tableID{ - dbName: expr.Qualifier.Qualifier.String(), - tableName: expr.Qualifier.Name.String(), - } - id2 := tableID{ - dbName: a.currentDb, - tableName: expr.Qualifier.Name.String(), - } - checkCurrentDB := id.dbName == "" && id != id2 - // search up the scope stack until we find a match for current != nil { - tableExpr, found := current.tables[id] - if found { - return tableExpr, nil - } - if checkCurrentDB { - tableExpr, found := current.tables[id2] - if found { - return tableExpr, nil + dbName := expr.Qualifier.Qualifier.String() + tableName := expr.Qualifier.Name.String() + for _, table := range current.tables { + if tableName == table.tableName && + (dbName == table.dbName || (dbName == "" && (table.dbName == a.currentDb || a.currentDb == ""))) { + return table, nil } } current = current.parent } - return nil, vterrors.NewErrorf(vtrpcpb.Code_INVALID_ARGUMENT, vterrors.BadFieldError, "Unknown table referenced by '%s'", sqlparser.String(expr)) } @@ -206,8 +193,8 @@ func (a *analyzer) bindTable(alias *sqlparser.AliasedTableExpr, expr sqlparser.S } a.popScope() scope := a.currentScope() - dbName := "" // derived tables are always referenced only by their alias - they cannot be found using a fully qualified name - return scope.addTable(dbName, alias.As.String(), &TableInfo{alias, nil}) + //dbName := "" // derived tables are always referenced only by their alias - they cannot be found using a fully qualified name + return scope.addTable(&TableInfo{}) case sqlparser.TableName: tbl, vdx, _, _, _, err := a.si.FindTableOrVindex(t) if err != nil { @@ -217,16 +204,24 @@ func (a *analyzer) bindTable(alias *sqlparser.AliasedTableExpr, expr sqlparser.S return Gen4NotSupportedF("vindex in FROM") } scope := a.currentScope() - table := &TableInfo{alias, tbl} - a.Tables = append(a.Tables, table) + dbName := t.Qualifier.String() + if dbName == "" { + dbName = a.currentDb + } + var tableName string if alias.As.IsEmpty() { - dbName := t.Qualifier.String() - if dbName == "" { - dbName = a.currentDb - } - return scope.addTable(dbName, t.Name.String(), table) + tableName = t.Name.String() + } else { + tableName = alias.As.String() + } + table := &TableInfo{ + dbName: dbName, + tableName: tableName, + ASTNode: alias, + Table: tbl, } - return scope.addTable("", alias.As.String(), table) + a.Tables = append(a.Tables, table) + return scope.addTable(table) } return nil } diff --git a/go/vt/vtgate/semantics/analyzer_test.go b/go/vt/vtgate/semantics/analyzer_test.go index b662f876965..cc4d31dcf36 100644 --- a/go/vt/vtgate/semantics/analyzer_test.go +++ b/go/vt/vtgate/semantics/analyzer_test.go @@ -73,6 +73,7 @@ func TestBindingSingleTable(t *testing.T) { "select col from d.tabl", "select tabl.col from d.tabl", "select d.tabl.col from d.tabl", + "select d.tabl.col from X as tabl", } for _, query := range queries { t.Run(query, func(t *testing.T) { @@ -90,7 +91,6 @@ func TestBindingSingleTable(t *testing.T) { t.Run("negative tests", func(t *testing.T) { queries := []string{ "select foo.col from tabl", - "select d.tabl.col from X as tabl", "select tabl.col from d.tabl as X", "select d.tabl.col from d.tabl as X", } @@ -98,7 +98,7 @@ func TestBindingSingleTable(t *testing.T) { t.Run(query, func(t *testing.T) { parse, err := sqlparser.Parse(query) require.NoError(t, err) - _, err = Analyse(parse, "d", &fakeSI{}) + _, err = Analyze(parse, "d", &fakeSI{}) require.Error(t, err) }) } @@ -112,6 +112,7 @@ func TestBindingSingleAliasedTable(t *testing.T) { "select tabl.col from X as tabl", "select col from d.X as tabl", "select tabl.col from d.X as tabl", + "select d.tabl.col from d.X as tabl", } for _, query := range queries { t.Run(query, func(t *testing.T) { @@ -131,13 +132,12 @@ func TestBindingSingleAliasedTable(t *testing.T) { "select tabl.col from tabl as X", "select X.col from X as tabl", "select d.X.col from d.X as tabl", - "select d.tabl.col from d.X as tabl", } for _, query := range queries { t.Run(query, func(t *testing.T) { parse, err := sqlparser.Parse(query) require.NoError(t, err) - _, err = Analyse(parse, "", &fakeSI{ + _, err = Analyze(parse, "", &fakeSI{ tables: map[string]*vindexes.Table{ "t": {Name: sqlparser.NewTableIdent("t")}, }, @@ -191,11 +191,11 @@ func TestBindingMultiTable(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: T0 | T1 | T2 | T4, - //}, { + // }, { // // 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: T0, - //}, { + // }, { // query: "select (select 42 from s where r.id = s.id) from r", // deps: T0 | T1, }} @@ -209,7 +209,6 @@ func TestBindingMultiTable(t *testing.T) { }) t.Run("negative tests", func(t *testing.T) { - t.Skip("implement me!") queries := []string{ "select 1 from d.tabl, d.foo as tabl", } @@ -217,9 +216,10 @@ func TestBindingMultiTable(t *testing.T) { t.Run(query, func(t *testing.T) { parse, err := sqlparser.Parse(query) require.NoError(t, err) - _, err = Analyse(parse, "", &fakeSI{ + _, err = Analyze(parse, "d", &fakeSI{ tables: map[string]*vindexes.Table{ - "t": {Name: sqlparser.NewTableIdent("t")}, + "tabl": {Name: sqlparser.NewTableIdent("tabl")}, + "foo": {Name: sqlparser.NewTableIdent("foo")}, }, }) require.Error(t, err) @@ -252,7 +252,7 @@ func TestNotUniqueTableName(t *testing.T) { t.Skip("derived tables not implemented") } parse, _ := sqlparser.Parse(query) - _, err := Analyse(parse, "test", &fakeSI{}) + _, err := Analyze(parse, "test", &fakeSI{}) require.Error(t, err) require.Contains(t, err.Error(), "Not unique table/alias") }) @@ -267,7 +267,7 @@ func TestMissingTable(t *testing.T) { for _, query := range queries { t.Run(query, func(t *testing.T) { parse, _ := sqlparser.Parse(query) - _, err := Analyse(parse, "", &fakeSI{}) + _, err := Analyze(parse, "", &fakeSI{}) require.Error(t, err) require.Contains(t, err.Error(), "Unknown table") }) @@ -346,7 +346,7 @@ func TestUnknownColumnMap2(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { si := &fakeSI{tables: test.schema} - _, err := Analyse(parse, "", si) + _, err := Analyze(parse, "", si) if test.err { require.Error(t, err) } else { @@ -360,7 +360,7 @@ func parseAndAnalyze(t *testing.T, query, dbName string) (sqlparser.Statement, * t.Helper() parse, err := sqlparser.Parse(query) require.NoError(t, err) - semTable, err := Analyse(parse, dbName, &fakeSI{ + semTable, err := Analyze(parse, dbName, &fakeSI{ tables: map[string]*vindexes.Table{ "t": {Name: sqlparser.NewTableIdent("t")}, }, diff --git a/go/vt/vtgate/semantics/semantic_state.go b/go/vt/vtgate/semantics/semantic_state.go index 2e76c12fff1..3fbfb749ba4 100644 --- a/go/vt/vtgate/semantics/semantic_state.go +++ b/go/vt/vtgate/semantics/semantic_state.go @@ -29,8 +29,9 @@ import ( type ( // TableInfo contains the alias table expr and vindex table TableInfo struct { - ASTNode *sqlparser.AliasedTableExpr - Table *vindexes.Table + dbName, tableName string + ASTNode *sqlparser.AliasedTableExpr + Table *vindexes.Table } // TableSet is how a set of tables is expressed. @@ -44,13 +45,9 @@ type ( exprDependencies map[sqlparser.Expr]TableSet } - tableID struct { - dbName, tableName string - } - scope struct { parent *scope - tables map[tableID]*TableInfo + tables []*TableInfo } // SchemaInformation is used tp provide table information from Vschema. @@ -104,19 +101,19 @@ func (st *SemTable) Dependencies(expr sqlparser.Expr) TableSet { } func newScope(parent *scope) *scope { - return &scope{tables: map[tableID]*TableInfo{}, parent: parent} + return &scope{parent: parent} } -func (s *scope) addTable(dbName, tableName string, table *TableInfo) error { - id := tableID{ - dbName: dbName, - tableName: tableName, - } - _, found := s.tables[id] - if found { - return vterrors.NewErrorf(vtrpcpb.Code_INVALID_ARGUMENT, vterrors.NonUniqTable, "Not unique table/alias: '%s'", tableName) +func (s *scope) addTable(table *TableInfo) error { + for _, scopeTable := range s.tables { + b := scopeTable.tableName == table.tableName + b2 := scopeTable.dbName == table.dbName + if b && b2 { + return vterrors.NewErrorf(vtrpcpb.Code_INVALID_ARGUMENT, vterrors.NonUniqTable, "Not unique table/alias: '%s'", table.tableName) + } } - s.tables[id] = table + + s.tables = append(s.tables, table) return nil } From 8d097296954e010b22f1dc421a986c2d072c0183 Mon Sep 17 00:00:00 2001 From: Florent Poinsard Date: Thu, 15 Apr 2021 11:14:19 +0200 Subject: [PATCH 5/8] Addition of new analyzer unit tests Signed-off-by: Florent Poinsard --- go/vt/vtgate/semantics/analyzer_test.go | 31 ++++++++++++++++++++----- 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/go/vt/vtgate/semantics/analyzer_test.go b/go/vt/vtgate/semantics/analyzer_test.go index cc4d31dcf36..e9d2b95ea27 100644 --- a/go/vt/vtgate/semantics/analyzer_test.go +++ b/go/vt/vtgate/semantics/analyzer_test.go @@ -73,7 +73,6 @@ func TestBindingSingleTable(t *testing.T) { "select col from d.tabl", "select tabl.col from d.tabl", "select d.tabl.col from d.tabl", - "select d.tabl.col from X as tabl", } for _, query := range queries { t.Run(query, func(t *testing.T) { @@ -91,8 +90,10 @@ func TestBindingSingleTable(t *testing.T) { t.Run("negative tests", func(t *testing.T) { queries := []string{ "select foo.col from tabl", - "select tabl.col from d.tabl as X", - "select d.tabl.col from d.tabl as X", + "select ks.tabl.col from tabl", + "select ks.tabl.col from d.tabl", + "select d.tabl.col from ks.tabl", + "select foo.col from d.tabl", } for _, query := range queries { t.Run(query, func(t *testing.T) { @@ -130,8 +131,9 @@ func TestBindingSingleAliasedTable(t *testing.T) { t.Run("negative tests", func(t *testing.T) { queries := []string{ "select tabl.col from tabl as X", - "select X.col from X as tabl", "select d.X.col from d.X as tabl", + "select d.tabl.col from X as tabl", + "select d.tabl.col from ks.X as tabl", } for _, query := range queries { t.Run(query, func(t *testing.T) { @@ -180,8 +182,8 @@ func TestBindingMultiTable(t *testing.T) { query: "select t.col from t, s", deps: T0, }, { - query: "select t.col from t join s", - deps: T0, + query: "select s.col from t join s", + deps: T1, }, { query: "select max(t.col+s.col) from t, s", deps: T0 | T1, @@ -198,6 +200,21 @@ func TestBindingMultiTable(t *testing.T) { // }, { // query: "select (select 42 from s where r.id = s.id) from r", // deps: T0 | T1, + }, { + query: "select X.col from t as X, s as S", + deps: T0, + }, { + query: "select X.col+S.col from t as X, s as S", + deps: T0 | T1, + }, { + query: "select max(X.col+S.col) from t as X, s as S", + deps: T0 | T1, + }, { + query: "select max(X.col+s.col) from t as X, s", + deps: T0 | T1, + }, { + query: "select b.t.col from b.t, t", + deps: T0, }} for _, query := range queries { t.Run(query.query, func(t *testing.T) { @@ -211,6 +228,8 @@ func TestBindingMultiTable(t *testing.T) { t.Run("negative tests", func(t *testing.T) { queries := []string{ "select 1 from d.tabl, d.foo as tabl", + "select 1 from d.tabl, d.tabl", + "select 1 from d.tabl, tabl", } for _, query := range queries { t.Run(query, func(t *testing.T) { From 8c01827ed8b748240f213d9476ee162306ab01eb Mon Sep 17 00:00:00 2001 From: Andres Taylor Date: Fri, 16 Apr 2021 11:18:22 +0200 Subject: [PATCH 6/8] gen4: handle routed tables Signed-off-by: Andres Taylor --- go/vt/vtgate/planbuilder/route_planning.go | 14 +++++++++++++- go/vt/vtgate/planbuilder/testdata/aggr_cases.txt | 1 + go/vt/vtgate/planbuilder/testdata/filter_cases.txt | 1 + go/vt/vtgate/planbuilder/testdata/from_cases.txt | 1 + .../planbuilder/testdata/postprocess_cases.txt | 1 + go/vt/vtgate/planbuilder/testdata/select_cases.txt | 1 + 6 files changed, 18 insertions(+), 1 deletion(-) diff --git a/go/vt/vtgate/planbuilder/route_planning.go b/go/vt/vtgate/planbuilder/route_planning.go index fb7f95ba1c4..b528fcdcdbd 100644 --- a/go/vt/vtgate/planbuilder/route_planning.go +++ b/go/vt/vtgate/planbuilder/route_planning.go @@ -649,7 +649,19 @@ func createRoutePlan(table *queryTable, solves semantics.TableSet, vschema Conte return nil, err } if vschemaTable.Name.String() != table.table.Name.String() { - return nil, semantics.Gen4NotSupportedF("routed tables") + // we are dealing with a routed table + name := table.table.Name + table.table.Name = vschemaTable.Name + astTable, ok := table.alias.Expr.(sqlparser.TableName) + if !ok { + return nil, vterrors.Errorf(vtrpcpb.Code_INTERNAL, "[BUG] a derived table should never be a routed table") + } + realTableName := sqlparser.NewTableIdent(vschemaTable.Name.String()) + astTable.Name = realTableName + if table.alias.As.IsEmpty() { + // if the user hasn't specified an alias, we'll insert one here so the old table name still works + table.alias.As = sqlparser.NewTableIdent(name.String()) + } } plan := &routePlan{ solved: solves, diff --git a/go/vt/vtgate/planbuilder/testdata/aggr_cases.txt b/go/vt/vtgate/planbuilder/testdata/aggr_cases.txt index 59d635dc853..4ac591fd80e 100644 --- a/go/vt/vtgate/planbuilder/testdata/aggr_cases.txt +++ b/go/vt/vtgate/planbuilder/testdata/aggr_cases.txt @@ -1244,6 +1244,7 @@ Gen4 plan same as above "Table": "unsharded" } } +Gen4 plan same as above # order by on a reference table "select col from ref order by col" diff --git a/go/vt/vtgate/planbuilder/testdata/filter_cases.txt b/go/vt/vtgate/planbuilder/testdata/filter_cases.txt index a04993f1627..dc3dc4dc9e4 100644 --- a/go/vt/vtgate/planbuilder/testdata/filter_cases.txt +++ b/go/vt/vtgate/planbuilder/testdata/filter_cases.txt @@ -1034,6 +1034,7 @@ Gen4 plan same as above "Vindex": "user_index" } } +Gen4 plan same as above # subquery of information_schema with itself "select * from information_schema.a where id in (select * from information_schema.b)" diff --git a/go/vt/vtgate/planbuilder/testdata/from_cases.txt b/go/vt/vtgate/planbuilder/testdata/from_cases.txt index f63ea1d3e21..21971d9de08 100644 --- a/go/vt/vtgate/planbuilder/testdata/from_cases.txt +++ b/go/vt/vtgate/planbuilder/testdata/from_cases.txt @@ -1249,6 +1249,7 @@ Gen4 plan same as above ] } } +Gen4 plan same as above # subquery "select id from (select id, col from user where id = 5) as t" diff --git a/go/vt/vtgate/planbuilder/testdata/postprocess_cases.txt b/go/vt/vtgate/planbuilder/testdata/postprocess_cases.txt index e3eb8cd3d10..55fe2c9ff42 100644 --- a/go/vt/vtgate/planbuilder/testdata/postprocess_cases.txt +++ b/go/vt/vtgate/planbuilder/testdata/postprocess_cases.txt @@ -966,6 +966,7 @@ Gen4 plan same as above "Vindex": "user_index" } } +Gen4 plan same as above # LIMIT "select col1 from user where id = 1 limit 1" diff --git a/go/vt/vtgate/planbuilder/testdata/select_cases.txt b/go/vt/vtgate/planbuilder/testdata/select_cases.txt index 611e167ffec..3bbb054f79f 100644 --- a/go/vt/vtgate/planbuilder/testdata/select_cases.txt +++ b/go/vt/vtgate/planbuilder/testdata/select_cases.txt @@ -1352,6 +1352,7 @@ Gen4 plan same as above "Table": "unsharded" } } +Gen4 plan same as above # testing SingleRow Projection "select 42" From 18036f5fb5f58523dbf50726beb741cedac2baf8 Mon Sep 17 00:00:00 2001 From: Andres Taylor Date: Mon, 26 Apr 2021 12:25:52 +0200 Subject: [PATCH 7/8] better code comment Signed-off-by: Andres Taylor --- go/vt/vtgate/semantics/analyzer.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/go/vt/vtgate/semantics/analyzer.go b/go/vt/vtgate/semantics/analyzer.go index 6932ac6759a..000e34ff8d5 100644 --- a/go/vt/vtgate/semantics/analyzer.go +++ b/go/vt/vtgate/semantics/analyzer.go @@ -87,7 +87,10 @@ func (a *analyzer) analyzeDown(cursor *sqlparser.Cursor) bool { a.exprDeps[node] = t } } - return true // we always return true here, and then return false on the going up phase to abort on error + // this is the visitor going down the tree. Returning false here would just not visit the children + // to the current node, but that is not what we want if we have encountered an error. + // In order to abort the whole visitation, we have to return true here and then return false in the `analyzeUp` method + return true } func (a *analyzer) resolveColumn(colName *sqlparser.ColName, current *scope) (TableSet, error) { From 7ad14e3f3d26cb1780cdbf9c22029740e5aebde4 Mon Sep 17 00:00:00 2001 From: Andres Taylor Date: Mon, 26 Apr 2021 13:11:25 +0200 Subject: [PATCH 8/8] fix compilation error Signed-off-by: Andres Taylor --- go/vt/vtgate/planbuilder/fuzz.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/go/vt/vtgate/planbuilder/fuzz.go b/go/vt/vtgate/planbuilder/fuzz.go index 07fbc9bf33e..201abda2671 100644 --- a/go/vt/vtgate/planbuilder/fuzz.go +++ b/go/vt/vtgate/planbuilder/fuzz.go @@ -31,7 +31,7 @@ type fakeFuzzSI struct { tables map[string]*vindexes.Table } -// Helper func: +// FindTableOrVindex is a helper func func (s *fakeFuzzSI) FindTableOrVindex(tablename sqlparser.TableName) (*vindexes.Table, vindexes.Vindex, string, topodatapb.TabletType, key.Destination, error) { return s.tables[sqlparser.String(tablename)], nil, "", 0, nil, nil } @@ -42,7 +42,7 @@ func FuzzAnalyse(data []byte) int { if err != nil { return -1 } - semTable, err := semantics.Analyse(tree, &fakeFuzzSI{}) + semTable, err := semantics.Analyze(tree, "", &fakeFuzzSI{}) if err != nil { return 0 }