diff --git a/go/vt/sqlparser/ast_funcs.go b/go/vt/sqlparser/ast_funcs.go index b6616030532..05fceb15ba4 100644 --- a/go/vt/sqlparser/ast_funcs.go +++ b/go/vt/sqlparser/ast_funcs.go @@ -1478,3 +1478,16 @@ func GetAllSelects(selStmt SelectStatement) []*Select { } panic("[BUG]: unknown type for SelectStatement") } + +// RemoveKeyspaceFromColName removes the Qualifier.Qualifier on all ColNames in the expression tree +func RemoveKeyspaceFromColName(expr Expr) Expr { + return Rewrite(expr, nil, func(cursor *Cursor) bool { + switch col := cursor.Node().(type) { + case *ColName: + if !col.Qualifier.Qualifier.IsEmpty() { + col.Qualifier.Qualifier = NewTableIdent("") + } + } + return true + }).(Expr) // This hard cast is safe because we do not change the type the input +} diff --git a/go/vt/vtgate/planbuilder/abstract/operator.go b/go/vt/vtgate/planbuilder/abstract/operator.go index e56cb9f6dc3..173e5967001 100644 --- a/go/vt/vtgate/planbuilder/abstract/operator.go +++ b/go/vt/vtgate/planbuilder/abstract/operator.go @@ -99,7 +99,7 @@ func getOperatorFromTableExpr(tableExpr sqlparser.TableExpr, semTable *semantics } op := createJoin(lhs, rhs) if tableExpr.Condition.On != nil { - err = op.PushPredicate(tableExpr.Condition.On, semTable) + err = op.PushPredicate(sqlparser.RemoveKeyspaceFromColName(tableExpr.Condition.On), semTable) if err != nil { return nil, err } @@ -117,7 +117,7 @@ func getOperatorFromTableExpr(tableExpr sqlparser.TableExpr, semTable *semantics if tableExpr.Join == sqlparser.RightJoinType { lhs, rhs = rhs, lhs } - return &Join{LHS: lhs, RHS: rhs, LeftJoin: true, Predicate: tableExpr.Condition.On}, nil + return &Join{LHS: lhs, RHS: rhs, LeftJoin: true, Predicate: sqlparser.RemoveKeyspaceFromColName(tableExpr.Condition.On)}, nil case sqlparser.StraightJoinType: return nil, semantics.Gen4NotSupportedF(tableExpr.Join.ToString()) default: @@ -227,7 +227,7 @@ func createOperatorFromSelect(sel *sqlparser.Select, semTable *semantics.SemTabl if sel.Where != nil { exprs := sqlparser.SplitAndExpression(nil, sel.Where.Expr) for _, expr := range exprs { - err := op.PushPredicate(expr, semTable) + err := op.PushPredicate(sqlparser.RemoveKeyspaceFromColName(expr), semTable) if err != nil { return nil, err } diff --git a/go/vt/vtgate/planbuilder/abstract/operator_test.go b/go/vt/vtgate/planbuilder/abstract/operator_test.go index 9e1a7c90ddb..5055cd69638 100644 --- a/go/vt/vtgate/planbuilder/abstract/operator_test.go +++ b/go/vt/vtgate/planbuilder/abstract/operator_test.go @@ -48,7 +48,7 @@ func (lcr *lineCountingReader) nextLine() (string, error) { func readTestCase(lcr *lineCountingReader) (testCase, error) { query := "" var err error - for query == "" || query == "\n" { + for query == "" || query == "\n" || strings.HasPrefix(query, "#") { query, err = lcr.nextLine() if err != nil { return testCase{}, err @@ -61,9 +61,9 @@ func readTestCase(lcr *lineCountingReader) (testCase, error) { jsonPart, err := lcr.nextLine() if err != nil { if err == io.EOF { - return testCase{}, fmt.Errorf("test data is bad. expectation not finished") + return tc, fmt.Errorf("test data is bad. expectation not finished") } - return testCase{}, err + return tc, err } if jsonPart == "}\n" { tc.expected += "}" @@ -93,6 +93,7 @@ func TestOperator(t *testing.T) { break } t.Run(fmt.Sprintf("%d:%s", tc.line, tc.query), func(t *testing.T) { + require.NoError(t, err) tree, err := sqlparser.Parse(tc.query) require.NoError(t, err) stmt := tree.(sqlparser.SelectStatement) diff --git a/go/vt/vtgate/planbuilder/abstract/operator_test_data.txt b/go/vt/vtgate/planbuilder/abstract/operator_test_data.txt index 2970ce7863a..7d8eb29d5d5 100644 --- a/go/vt/vtgate/planbuilder/abstract/operator_test_data.txt +++ b/go/vt/vtgate/planbuilder/abstract/operator_test_data.txt @@ -399,3 +399,51 @@ Concatenate(distinct) { }, order by id asc } + +select id from user where exists(select user_id from user_extra where user_id = 3 and user_id < user.id) +SubQuery: { + SubQueries: [ + { + Type: PulloutExists + Query: QueryGraph: { + Tables: + TableSet{1}:user_extra where user_id = 3 + JoinPredicates: + TableSet{0,1} - user_id < `user`.id + } + }] + Outer: QueryGraph: { + Tables: + TableSet{0}:`user` + JoinPredicates: + TableSet{0,1} - exists (select user_id from user_extra where user_id = 3 and user_id < `user`.id) + } +} +# we should remove the keyspace from predicates +select ks.tbl.col from ks.tbl where ks.tbl.id = 1 +QueryGraph: { +Tables: + TableSet{0}:ks.tbl where tbl.id = 1 +} + +select 1 from ks.t join ks.y on ks.t.id = ks.y.t_id +QueryGraph: { +Tables: + TableSet{0}:ks.t + TableSet{1}:ks.y +JoinPredicates: + TableSet{0,1} - t.id = y.t_id +} + +select 1 from ks.t left join ks.y on ks.t.id = ks.y.t_id +OuterJoin: { + Inner: QueryGraph: { + Tables: + TableSet{0}:ks.t + } + Outer: QueryGraph: { + Tables: + TableSet{1}:ks.y + } + Predicate: t.id = y.t_id +} diff --git a/go/vt/vtgate/planbuilder/horizon_planning.go b/go/vt/vtgate/planbuilder/horizon_planning.go index 63d8d452e56..f04e085a920 100644 --- a/go/vt/vtgate/planbuilder/horizon_planning.go +++ b/go/vt/vtgate/planbuilder/horizon_planning.go @@ -174,7 +174,7 @@ func pushProjection(expr *sqlparser.AliasedExpr, plan logicalPlan, semTable *sem return i, false, nil } } - expr = removeKeyspaceFromColName(expr) + expr.Expr = sqlparser.RemoveKeyspaceFromColName(expr.Expr) sel, isSel := node.Select.(*sqlparser.Select) if !isSel { return 0, false, vterrors.NewErrorf(vtrpcpb.Code_INVALID_ARGUMENT, vterrors.BadFieldError, "Unknown column '%s' in 'order clause'", sqlparser.String(expr)) @@ -265,15 +265,6 @@ func pushProjection(expr *sqlparser.AliasedExpr, plan logicalPlan, semTable *sem } } -func removeKeyspaceFromColName(expr *sqlparser.AliasedExpr) *sqlparser.AliasedExpr { - if _, ok := expr.Expr.(*sqlparser.ColName); ok { - expr = sqlparser.CloneRefOfAliasedExpr(expr) - col := expr.Expr.(*sqlparser.ColName) - col.Qualifier.Qualifier = sqlparser.NewTableIdent("") - } - return expr -} - func checkIfAlreadyExists(expr *sqlparser.AliasedExpr, node sqlparser.SelectStatement, semTable *semantics.SemTable) int { exprDep := semTable.RecursiveDeps(expr.Expr) // Here to find if the expr already exists in the SelectStatement, we have 3 cases diff --git a/go/vt/vtgate/planbuilder/route_planning.go b/go/vt/vtgate/planbuilder/route_planning.go index 4cdfe5aa6cf..8d96ed99aad 100644 --- a/go/vt/vtgate/planbuilder/route_planning.go +++ b/go/vt/vtgate/planbuilder/route_planning.go @@ -333,18 +333,18 @@ func planSingleShardRoutePlan(sel sqlparser.SelectStatement, rb *route) error { return err } sqlparser.Rewrite(rb.Select, func(cursor *sqlparser.Cursor) bool { - if aliasedExpr, ok := cursor.Node().(*sqlparser.AliasedExpr); ok { - cursor.Replace(removeKeyspaceFromColName(aliasedExpr)) + if aliasedExpr, ok := cursor.Node().(sqlparser.SelectExpr); ok { + removeKeyspaceFromSelectExpr(aliasedExpr) } return true }, nil) return nil } -func removeKeyspaceFromSelectExpr(expr sqlparser.SelectExpr, ast *sqlparser.Select, i int) { +func removeKeyspaceFromSelectExpr(expr sqlparser.SelectExpr) { switch expr := expr.(type) { case *sqlparser.AliasedExpr: - ast.SelectExprs[i] = removeKeyspaceFromColName(expr) + expr.Expr = sqlparser.RemoveKeyspaceFromColName(expr.Expr) case *sqlparser.StarExpr: expr.TableName.Qualifier = sqlparser.NewTableIdent("") } @@ -365,8 +365,8 @@ func stripDownQuery(from, to sqlparser.SelectStatement) error { toNode.OrderBy = node.OrderBy toNode.Comments = node.Comments toNode.SelectExprs = node.SelectExprs - for i, expr := range toNode.SelectExprs { - removeKeyspaceFromSelectExpr(expr, toNode, i) + for _, expr := range toNode.SelectExprs { + removeKeyspaceFromSelectExpr(expr) } case *sqlparser.Union: toNode, ok := to.(*sqlparser.Union) diff --git a/go/vt/vtgate/planbuilder/testdata/from_cases.txt b/go/vt/vtgate/planbuilder/testdata/from_cases.txt index 467f9b7582a..0fa28a1439e 100644 --- a/go/vt/vtgate/planbuilder/testdata/from_cases.txt +++ b/go/vt/vtgate/planbuilder/testdata/from_cases.txt @@ -369,6 +369,123 @@ Gen4 plan same as above "table disabled has been disabled" Gen4 plan same as above +"select second_user.foo.col from second_user.foo join user on second_user.foo.id = user.id where second_user.foo.col = 42" +{ + "QueryType": "SELECT", + "Original": "select second_user.foo.col from second_user.foo join user on second_user.foo.id = user.id where second_user.foo.col = 42", + "Instructions": { + "OperatorType": "Route", + "Variant": "SelectScatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select foo.col from `user` as foo join `user` on foo.id = `user`.id where 1 != 1", + "Query": "select foo.col from `user` as foo join `user` on foo.id = `user`.id where foo.col = 42", + "Table": "`user`" + } +} +{ + "QueryType": "SELECT", + "Original": "select second_user.foo.col from second_user.foo join user on second_user.foo.id = user.id where second_user.foo.col = 42", + "Instructions": { + "OperatorType": "Route", + "Variant": "SelectScatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select foo.col from `user` as foo, `user` where 1 != 1", + "Query": "select foo.col from `user` as foo, `user` where foo.col = 42 and foo.id = `user`.id", + "Table": "`user`" + } +} + +"select user.music.foo from user.music join user on user.music.id = user.id where user.music.col = 42" +{ + "QueryType": "SELECT", + "Original": "select user.music.foo from user.music join user on user.music.id = user.id where user.music.col = 42", + "Instructions": { + "OperatorType": "Join", + "Variant": "Join", + "JoinColumnIndexes": "-1", + "JoinVars": { + "music_id": 1 + }, + "TableName": "music_`user`", + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "SelectScatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select music.foo, music.id from music where 1 != 1", + "Query": "select music.foo, music.id from music where music.col = 42", + "Table": "music" + }, + { + "OperatorType": "Route", + "Variant": "SelectEqualUnique", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select 1 from `user` where 1 != 1", + "Query": "select 1 from `user` where `user`.id = :music_id", + "Table": "`user`", + "Values": [ + ":music_id" + ], + "Vindex": "user_index" + } + ] + } +} +{ + "QueryType": "SELECT", + "Original": "select user.music.foo from user.music join user on user.music.id = user.id where user.music.col = 42", + "Instructions": { + "OperatorType": "Join", + "Variant": "Join", + "JoinColumnIndexes": "-2", + "JoinVars": { + "music_id": 0 + }, + "TableName": "music_`user`", + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "SelectScatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select music.id, music.foo from music where 1 != 1", + "Query": "select music.id, music.foo from music where music.col = 42", + "Table": "music" + }, + { + "OperatorType": "Route", + "Variant": "SelectEqualUnique", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select 1 from `user` where 1 != 1", + "Query": "select 1 from `user` where `user`.id = :music_id", + "Table": "`user`", + "Values": [ + ":music_id" + ], + "Vindex": "user_index" + } + ] + } +} + + # ',' join "select music.col from user, music" { diff --git a/go/vt/vtgate/planbuilder/testdata/schema_test.json b/go/vt/vtgate/planbuilder/testdata/schema_test.json index b7e1fcdc0f3..68b3b9eb827 100644 --- a/go/vt/vtgate/planbuilder/testdata/schema_test.json +++ b/go/vt/vtgate/planbuilder/testdata/schema_test.json @@ -9,6 +9,9 @@ }, { "from_table": "second_user.user", "to_tables": ["user.user"] + }, { + "from_table": "second_user.foo", + "to_tables": ["user.user"] }, { "from_table": "primary_redirect@primary", "to_tables": ["user.user"] diff --git a/go/vt/vtgate/planbuilder/testdata/systemtables_cases.txt b/go/vt/vtgate/planbuilder/testdata/systemtables_cases.txt index ad1c60c15c5..868c31685b9 100644 --- a/go/vt/vtgate/planbuilder/testdata/systemtables_cases.txt +++ b/go/vt/vtgate/planbuilder/testdata/systemtables_cases.txt @@ -124,7 +124,21 @@ Gen4 plan same as above "Table": "information_schema.a" } } -Gen4 plan same as above +{ + "QueryType": "SELECT", + "Original": "select * from information_schema.a where information_schema.a.b=10", + "Instructions": { + "OperatorType": "Route", + "Variant": "SelectDBA", + "Keyspace": { + "Name": "main", + "Sharded": false + }, + "FieldQuery": "select * from information_schema.a where 1 != 1", + "Query": "select * from information_schema.a where a.b = 10", + "Table": "information_schema.a" + } +} # union of information_schema "select * from information_schema.a union select * from information_schema.b"