From 10870f9e8d2a3c7add9f330a1f440736a288b3f7 Mon Sep 17 00:00:00 2001 From: Harshit Gangal Date: Mon, 12 Jul 2021 20:03:31 +0530 Subject: [PATCH] added support for group by with weight string needed. Add truncation if required after horizon planning at top most plan. all select columns to be available irrepective of if they are same expression (for correctness) Signed-off-by: Harshit Gangal --- go/test/endtoend/vtgate/gen4/gen4_test.go | 21 ++++ go/test/endtoend/vtgate/gen4/main_test.go | 23 +++- .../planbuilder/abstract/queryprojection.go | 16 ++- go/vt/vtgate/planbuilder/route_planning.go | 26 +++- go/vt/vtgate/planbuilder/selectGen4.go | 116 +++++++++--------- go/vt/vtgate/planbuilder/testdata/onecase.txt | 89 +++++++++++++- .../testdata/postprocess_cases.txt | 37 +++++- .../testdata/unsupported_cases.txt | 45 ++++++- 8 files changed, 301 insertions(+), 72 deletions(-) diff --git a/go/test/endtoend/vtgate/gen4/gen4_test.go b/go/test/endtoend/vtgate/gen4/gen4_test.go index aa42b8aa3a2..4358e6d9ede 100644 --- a/go/test/endtoend/vtgate/gen4/gen4_test.go +++ b/go/test/endtoend/vtgate/gen4/gen4_test.go @@ -48,6 +48,27 @@ func TestOrderBy(t *testing.T) { require.Error(t, err) } +func TestGroupBy(t *testing.T) { + ctx := context.Background() + conn, err := mysql.Connect(ctx, &vtParams) + require.NoError(t, err) + defer conn.Close() + + // insert some data. + checkedExec(t, conn, `insert into t1(id, col) values (1, 123),(2, 12),(3, 13),(4, 1234)`) + checkedExec(t, conn, `insert into t2(id, tcol1, tcol2) values (1, 'A', 'A'),(2, 'B', 'C'),(3, 'A', 'C'),(4, 'C', 'A'),(5, 'A', 'A'),(6, 'B', 'C'),(7, 'B', 'A'),(8, 'C', 'B')`) + + // Gen4 only supported query. + assertMatches(t, conn, `select tcol2, tcol1, count(id) from t2 group by tcol2, tcol1`, + `[[VARCHAR("A") VARCHAR("A") INT64(2)] [VARCHAR("A") VARCHAR("B") INT64(1)] [VARCHAR("A") VARCHAR("C") INT64(1)] [VARCHAR("B") VARCHAR("C") INT64(1)] [VARCHAR("C") VARCHAR("A") INT64(1)] [VARCHAR("C") VARCHAR("B") INT64(2)]]`) + + assertMatches(t, conn, `select tcol1, tcol1 from t2 order by tcol1`, + `[[VARCHAR("A") VARCHAR("A")] [VARCHAR("A") VARCHAR("A")] [VARCHAR("A") VARCHAR("A")] [VARCHAR("B") VARCHAR("B")] [VARCHAR("B") VARCHAR("B")] [VARCHAR("B") VARCHAR("B")] [VARCHAR("C") VARCHAR("C")] [VARCHAR("C") VARCHAR("C")]]`) + + assertMatches(t, conn, `select tcol1, tcol1 from t1 join t2 on t1.id = t2.id order by tcol1`, + `[[VARCHAR("A") VARCHAR("A")] [VARCHAR("A") VARCHAR("A")] [VARCHAR("B") VARCHAR("B")] [VARCHAR("C") VARCHAR("C")]]`) +} + func assertMatches(t *testing.T, conn *mysql.Conn, query, expected string) { t.Helper() qr := checkedExec(t, conn, query) diff --git a/go/test/endtoend/vtgate/gen4/main_test.go b/go/test/endtoend/vtgate/gen4/main_test.go index 03a9bd25a98..d892042ae18 100644 --- a/go/test/endtoend/vtgate/gen4/main_test.go +++ b/go/test/endtoend/vtgate/gen4/main_test.go @@ -35,6 +35,13 @@ var ( col bigint, primary key(id) ) Engine=InnoDB; + +create table t2( + id bigint, + tcol1 varchar(50), + tcol2 varchar(50), + primary key(id) +) Engine=InnoDB; ` VSchema = ` @@ -53,6 +60,20 @@ var ( "name": "xxhash" } ] + }, + "t2": { + "column_vindexes": [ + { + "column": "id", + "name": "xxhash" + } + ], + "columns": [ + { + "name": "tcol1", + "type": "VARCHAR" + } + ] } } }` @@ -84,7 +105,7 @@ func TestMain(m *testing.M) { } // Start vtgate - clusterInstance.VtGateExtraArgs = []string{"-planner_version", "Gen4Fallback"} // enable Gen4 planner. + clusterInstance.VtGateExtraArgs = []string{"-planner_version", "Gen4"} // enable Gen4 planner. err = clusterInstance.StartVtgate() if err != nil { return 1 diff --git a/go/vt/vtgate/planbuilder/abstract/queryprojection.go b/go/vt/vtgate/planbuilder/abstract/queryprojection.go index bf0cc62ee25..14d99199bb3 100644 --- a/go/vt/vtgate/planbuilder/abstract/queryprojection.go +++ b/go/vt/vtgate/planbuilder/abstract/queryprojection.go @@ -37,7 +37,7 @@ type ( QueryProjection struct { SelectExprs []SelectExpr HasAggr bool - GroupByExprs sqlparser.Exprs + GroupByExprs []GroupBy OrderExprs []OrderBy } @@ -46,6 +46,12 @@ type ( Inner *sqlparser.Order WeightStrExpr sqlparser.Expr } + + // GroupBy contains the expression to used in group by and also if grouping is needed at VTGate level then what the weight_string function expression to be sent down for evaluation. + GroupBy struct { + Inner sqlparser.Expr + WeightStrExpr sqlparser.Expr + } ) // CreateQPFromSelect created the QueryProjection for the input *sqlparser.Select @@ -85,10 +91,10 @@ func CreateQPFromSelect(sel *sqlparser.Select) (*QueryProjection, error) { return nil, vterrors.NewErrorf(vtrpcpb.Code_INVALID_ARGUMENT, vterrors.MixOfGroupFuncAndFields, "Mixing of aggregation and non-aggregation columns is not allowed if there is no GROUP BY clause") } - for _, expr := range sel.GroupBy { + for _, group := range sel.GroupBy { // todo dont ignore weightstringexpr - e, _ := qp.getSimplifiedExpr(expr) - qp.GroupByExprs = append(qp.GroupByExprs, e) + expr, weightStrExpr := qp.getSimplifiedExpr(group) + qp.GroupByExprs = append(qp.GroupByExprs, GroupBy{Inner: expr, WeightStrExpr: weightStrExpr}) } for _, order := range sel.OrderBy { @@ -176,7 +182,7 @@ func (qp *QueryProjection) toString() string { } for _, expr := range qp.GroupByExprs { - out.Grouping = append(out.Grouping, sqlparser.String(expr)) + out.Grouping = append(out.Grouping, sqlparser.String(expr.Inner)) } for _, expr := range qp.OrderExprs { out.OrderBy = append(out.OrderBy, sqlparser.String(expr.Inner)) diff --git a/go/vt/vtgate/planbuilder/route_planning.go b/go/vt/vtgate/planbuilder/route_planning.go index 01572427615..3fda413996a 100644 --- a/go/vt/vtgate/planbuilder/route_planning.go +++ b/go/vt/vtgate/planbuilder/route_planning.go @@ -174,31 +174,49 @@ func planHorizon(sel *sqlparser.Select, plan logicalPlan, semTable *semantics.Se return nil, err } + var needsTruncation bool if qp.HasAggr { - plan, err = planAggregations(qp, plan, semTable) + plan, needsTruncation, err = planAggregations(qp, plan, semTable) if err != nil { return nil, err } } else { for _, e := range qp.SelectExprs { - if _, _, err := pushProjection(e.Col, plan, semTable, true); err != nil { + if _, _, err := pushProjection(e.Col, plan, semTable, true, false); err != nil { return nil, err } } } if len(qp.OrderExprs) > 0 { - plan, err = planOrderBy(qp, qp.OrderExprs, plan, semTable) + var colAdded bool + plan, colAdded, err = planOrderBy(qp, qp.OrderExprs, plan, semTable) if err != nil { return nil, err } + needsTruncation = needsTruncation || colAdded } if qp.HasAggr { - plan, err = planOrderByUsingGroupBy(qp, plan, semTable) + var colAdded bool + plan, colAdded, err = planOrderByUsingGroupBy(qp, plan, semTable) if err != nil { return nil, err } + needsTruncation = needsTruncation || colAdded + } + + if needsTruncation { + switch p := plan.(type) { + case *route: + p.eroute.SetTruncateColumnCount(sel.GetColumnCount()) + case *orderedAggregate: + p.eaggr.SetTruncateColumnCount(sel.GetColumnCount()) + case *memorySort: + p.truncater.SetTruncateColumnCount(sel.GetColumnCount()) + default: + return nil, vterrors.Errorf(vtrpcpb.Code_INTERNAL, "plan type not known for column truncation: %T", plan) + } } return plan, nil diff --git a/go/vt/vtgate/planbuilder/selectGen4.go b/go/vt/vtgate/planbuilder/selectGen4.go index 7edea301953..4edd6cf6d1c 100644 --- a/go/vt/vtgate/planbuilder/selectGen4.go +++ b/go/vt/vtgate/planbuilder/selectGen4.go @@ -29,7 +29,7 @@ import ( "vitess.io/vitess/go/vt/vtgate/engine" ) -func pushProjection(expr *sqlparser.AliasedExpr, plan logicalPlan, semTable *semantics.SemTable, inner bool) (int, bool, error) { +func pushProjection(expr *sqlparser.AliasedExpr, plan logicalPlan, semTable *semantics.SemTable, inner bool, reuseCol bool) (int, bool, error) { switch node := plan.(type) { case *route: value, err := makePlanValue(expr.Expr) @@ -42,9 +42,10 @@ func pushProjection(expr *sqlparser.AliasedExpr, plan logicalPlan, semTable *sem return 0, false, vterrors.New(vtrpcpb.Code_UNIMPLEMENTED, "unsupported: cross-shard left join and column expressions") } sel := node.Select.(*sqlparser.Select) - i := checkIfAlreadyExists(expr, sel) - if i != -1 { - return i, false, nil + if reuseCol { + if i := checkIfAlreadyExists(expr, sel); i != -1 { + return i, false, nil + } } expr = removeQualifierFromColName(expr) @@ -59,14 +60,14 @@ func pushProjection(expr *sqlparser.AliasedExpr, plan logicalPlan, semTable *sem var appended bool switch { case deps.IsSolvedBy(lhsSolves): - offset, added, err := pushProjection(expr, node.Left, semTable, inner) + offset, added, err := pushProjection(expr, node.Left, semTable, inner, true) if err != nil { return 0, false, err } column = -(offset + 1) appended = added case deps.IsSolvedBy(rhsSolves): - offset, added, err := pushProjection(expr, node.Right, semTable, inner && node.Opcode != engine.LeftJoin) + offset, added, err := pushProjection(expr, node.Right, semTable, inner && node.Opcode != engine.LeftJoin, true) if err != nil { return 0, false, err } @@ -75,7 +76,7 @@ func pushProjection(expr *sqlparser.AliasedExpr, plan logicalPlan, semTable *sem default: return 0, false, vterrors.Errorf(vtrpcpb.Code_INTERNAL, "unknown dependencies for %s", sqlparser.String(expr)) } - if !appended { + if reuseCol && !appended { for idx, col := range node.Cols { if column == col { return idx, false, nil @@ -120,7 +121,7 @@ func checkIfAlreadyExists(expr *sqlparser.AliasedExpr, sel *sqlparser.Select) in return -1 } -func planAggregations(qp *abstract.QueryProjection, plan logicalPlan, semTable *semantics.SemTable) (logicalPlan, error) { +func planAggregations(qp *abstract.QueryProjection, plan logicalPlan, semTable *semantics.SemTable) (logicalPlan, bool, error) { eaggr := &engine.OrderedAggregate{} oa := &orderedAggregate{ resultsBuilder: resultsBuilder{ @@ -131,9 +132,9 @@ func planAggregations(qp *abstract.QueryProjection, plan logicalPlan, semTable * eaggr: eaggr, } for _, e := range qp.SelectExprs { - offset, _, err := pushProjection(e.Col, plan, semTable, true) + offset, _, err := pushProjection(e.Col, plan, semTable, true, false) if err != nil { - return nil, err + return nil, false, err } if e.Aggr { fExpr := e.Col.Expr.(*sqlparser.FuncExpr) @@ -145,86 +146,91 @@ func planAggregations(qp *abstract.QueryProjection, plan logicalPlan, semTable * } } + var colAdded bool for _, groupExpr := range qp.GroupByExprs { - if err := planGroupByGen4(groupExpr, oa, semTable); err != nil { - return nil, err + added, err := planGroupByGen4(groupExpr, oa, semTable) + if err != nil { + return nil, false, err } + colAdded = colAdded || added } - return oa, nil + return oa, colAdded, nil } -func planGroupByGen4(groupExpr sqlparser.Expr, plan logicalPlan, semTable *semantics.SemTable) error { +func planGroupByGen4(groupExpr abstract.GroupBy, plan logicalPlan, semTable *semantics.SemTable) (bool, error) { switch node := plan.(type) { case *route: sel := node.Select.(*sqlparser.Select) - sel.GroupBy = append(sel.GroupBy, groupExpr) - return nil + sel.GroupBy = append(sel.GroupBy, groupExpr.Inner) + return false, nil case *orderedAggregate: - offset, weightStringOffset, err := funcName(groupExpr, groupExpr, node.input, semTable) + offset, weightStringOffset, colAdded, err := funcName(groupExpr.Inner, groupExpr.WeightStrExpr, node.input, semTable) if err != nil { - return err + return false, err } if weightStringOffset == -1 { node.eaggr.Keys = append(node.eaggr.Keys, offset) } else { node.eaggr.Keys = append(node.eaggr.Keys, weightStringOffset) } - err = planGroupByGen4(groupExpr, node.input, semTable) + colAddedRecursively, err := planGroupByGen4(groupExpr, node.input, semTable) if err != nil { - return err + return false, err } - return nil + return colAdded || colAddedRecursively, nil default: - return semantics.Gen4NotSupportedF("group by on: %T", plan) + return false, semantics.Gen4NotSupportedF("group by on: %T", plan) } } -func planOrderByUsingGroupBy(qp *abstract.QueryProjection, plan logicalPlan, semTable *semantics.SemTable) (logicalPlan, error) { +func planOrderByUsingGroupBy(qp *abstract.QueryProjection, plan logicalPlan, semTable *semantics.SemTable) (logicalPlan, bool, error) { var orderExprs []abstract.OrderBy for _, groupExpr := range qp.GroupByExprs { addExpr := true for _, orderExpr := range qp.OrderExprs { - if sqlparser.EqualsExpr(groupExpr, orderExpr.Inner.Expr) { + if sqlparser.EqualsExpr(groupExpr.Inner, orderExpr.Inner.Expr) { addExpr = false break } } if addExpr { - // TODO: add weight string expr - orderExprs = append(orderExprs, abstract.OrderBy{Inner: &sqlparser.Order{Expr: groupExpr}}) + orderExprs = append(orderExprs, abstract.OrderBy{ + Inner: &sqlparser.Order{Expr: groupExpr.Inner}, + WeightStrExpr: groupExpr.WeightStrExpr}, + ) } } if len(orderExprs) > 0 { return planOrderBy(qp, orderExprs, plan, semTable) } - return plan, nil + return plan, false, nil } -func planOrderBy(qp *abstract.QueryProjection, orderExprs []abstract.OrderBy, plan logicalPlan, semTable *semantics.SemTable) (logicalPlan, error) { +func planOrderBy(qp *abstract.QueryProjection, orderExprs []abstract.OrderBy, plan logicalPlan, semTable *semantics.SemTable) (logicalPlan, bool, error) { switch plan := plan.(type) { case *route: return planOrderByForRoute(orderExprs, plan, semTable) case *joinGen4: return planOrderByForJoin(qp, orderExprs, plan, semTable) case *orderedAggregate: - newInput, err := planOrderBy(qp, orderExprs, plan.input, semTable) + newInput, colAdded, err := planOrderBy(qp, orderExprs, plan.input, semTable) if err != nil { - return nil, err + return nil, false, err } plan.input = newInput - return plan, nil + return plan, colAdded, nil default: - return nil, semantics.Gen4NotSupportedF("ordering on complex query") + return nil, false, semantics.Gen4NotSupportedF("ordering on complex query") } } -func planOrderByForRoute(orderExprs []abstract.OrderBy, plan *route, semTable *semantics.SemTable) (logicalPlan, error) { +func planOrderByForRoute(orderExprs []abstract.OrderBy, plan *route, semTable *semantics.SemTable) (logicalPlan, bool, error) { origColCount := plan.Select.GetColumnCount() for _, order := range orderExprs { - offset, weightStringOffset, err := funcName(order.Inner.Expr, order.WeightStrExpr, plan, semTable) + offset, weightStringOffset, _, err := funcName(order.Inner.Expr, order.WeightStrExpr, plan, semTable) if err != nil { - return nil, err + return nil, false, err } plan.eroute.OrderBy = append(plan.eroute.OrderBy, engine.OrderbyParams{ @@ -234,38 +240,35 @@ func planOrderByForRoute(orderExprs []abstract.OrderBy, plan *route, semTable *s }) plan.Select.AddOrder(order.Inner) } - if origColCount != plan.Select.GetColumnCount() { - plan.eroute.TruncateColumnCount = origColCount - } - - return plan, nil + return plan, origColCount != plan.Select.GetColumnCount(), nil } -func funcName(expr sqlparser.Expr, weightStrExpr sqlparser.Expr, plan logicalPlan, semTable *semantics.SemTable) (int, int, error) { - offset, _, err := pushProjection(&sqlparser.AliasedExpr{Expr: expr}, plan, semTable, true) +func funcName(expr sqlparser.Expr, weightStrExpr sqlparser.Expr, plan logicalPlan, semTable *semantics.SemTable) (int, int, bool, error) { + offset, added, err := pushProjection(&sqlparser.AliasedExpr{Expr: expr}, plan, semTable, true, true) if err != nil { - return 0, 0, err + return 0, 0, false, err } colName, ok := expr.(*sqlparser.ColName) if !ok { - return 0, 0, semantics.Gen4NotSupportedF("group by/order by non-column expression") + return 0, 0, false, semantics.Gen4NotSupportedF("group by/order by non-column expression") } table := semTable.Dependencies(colName) tbl, err := semTable.TableInfoFor(table) if err != nil { - return 0, 0, err + return 0, 0, false, err } weightStringNeeded := needsWeightString(tbl, colName) weightStringOffset := -1 + var wAdded bool if weightStringNeeded { - weightStringOffset, _, err = pushProjection(&sqlparser.AliasedExpr{Expr: weightStringFor(weightStrExpr)}, plan, semTable, true) + weightStringOffset, wAdded, err = pushProjection(&sqlparser.AliasedExpr{Expr: weightStringFor(weightStrExpr)}, plan, semTable, true, true) if err != nil { - return 0, 0, err + return 0, 0, false, err } } - return offset, weightStringOffset, nil + return offset, weightStringOffset, added || wAdded, nil } func weightStringFor(expr sqlparser.Expr) sqlparser.Expr { @@ -289,14 +292,14 @@ func needsWeightString(tbl semantics.TableInfo, colName *sqlparser.ColName) bool return true // we didn't find the column. better to add just to be safe1 } -func planOrderByForJoin(qp *abstract.QueryProjection, orderExprs []abstract.OrderBy, plan *joinGen4, semTable *semantics.SemTable) (logicalPlan, error) { +func planOrderByForJoin(qp *abstract.QueryProjection, orderExprs []abstract.OrderBy, plan *joinGen4, semTable *semantics.SemTable) (logicalPlan, bool, error) { if allLeft(orderExprs, semTable, plan.Left.ContainsTables()) { - newLeft, err := planOrderBy(qp, orderExprs, plan.Left, semTable) + newLeft, _, err := planOrderBy(qp, orderExprs, plan.Left, semTable) if err != nil { - return nil, err + return nil, false, err } plan.Left = newLeft - return plan, nil + return plan, false, nil } primitive := &engine.MemorySort{} @@ -309,12 +312,13 @@ func planOrderByForJoin(qp *abstract.QueryProjection, orderExprs []abstract.Orde eMemorySort: primitive, } + var colAdded bool for _, order := range orderExprs { - offset, weightStringOffset, err := funcName(order.Inner.Expr, order.WeightStrExpr, plan, semTable) + offset, weightStringOffset, added, err := funcName(order.Inner.Expr, order.WeightStrExpr, plan, semTable) if err != nil { - return nil, err + return nil, false, err } - + colAdded = colAdded || added ms.eMemorySort.OrderBy = append(ms.eMemorySort.OrderBy, engine.OrderbyParams{ Col: offset, WeightStringCol: weightStringOffset, @@ -323,7 +327,7 @@ func planOrderByForJoin(qp *abstract.QueryProjection, orderExprs []abstract.Orde }) } - return ms, nil + return ms, colAdded, nil } diff --git a/go/vt/vtgate/planbuilder/testdata/onecase.txt b/go/vt/vtgate/planbuilder/testdata/onecase.txt index e819513f354..97313ef4be0 100644 --- a/go/vt/vtgate/planbuilder/testdata/onecase.txt +++ b/go/vt/vtgate/planbuilder/testdata/onecase.txt @@ -1 +1,88 @@ -# Add your test case here for debugging and run go test -run=One. +# ORDER BY non-key column for implicit join +"select user.col1 as a, user.col2, music.col3 from user, music where user.id = music.id and user.id = 1 order by a" +{ + "QueryType": "SELECT", + "Original": "select user.col1 as a, user.col2, music.col3 from user, music where user.id = music.id and user.id = 1 order by a", + "Instructions": { + "OperatorType": "Join", + "Variant": "Join", + "JoinColumnIndexes": "-1,-2,1", + "TableName": "`user`_music", + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "SelectEqualUnique", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select `user`.col1 as a, `user`.col2, `user`.id from `user` where 1 != 1", + "Query": "select `user`.col1 as a, `user`.col2, `user`.id from `user` where `user`.id = 1 order by a asc", + "Table": "`user`", + "Values": [ + 1 + ], + "Vindex": "user_index" + }, + { + "OperatorType": "Route", + "Variant": "SelectEqualUnique", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select music.col3 from music where 1 != 1", + "Query": "select music.col3 from music where music.id = :user_id", + "Table": "music", + "Values": [ + ":user_id" + ], + "Vindex": "music_user_map" + } + ] + } +} +{ + "QueryType": "SELECT", + "Original": "select user.col1 as a, user.col2, music.col3 from user, music where user.id = music.id and user.id = 1 order by a", + "Instructions": { + "OperatorType": "Join", + "Variant": "Join", + "JoinColumnIndexes": "-2,-3,1", + "TableName": "`user`_music", + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "SelectEqualUnique", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select `user`.id, `user`.col1 as a, `user`.col2, weight_string(`user`.col1) from `user` where 1 != 1", + "OrderBy": "1 ASC", + "Query": "select `user`.id, `user`.col1 as a, `user`.col2, weight_string(`user`.col1) from `user` where `user`.id = 1 order by a asc", + "ResultColumns": 3, + "Table": "`user`", + "Values": [ + 1 + ], + "Vindex": "user_index" + }, + { + "OperatorType": "Route", + "Variant": "SelectEqualUnique", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select music.col3 from music where 1 != 1", + "Query": "select music.col3 from music where music.id = :user_id", + "Table": "music", + "Values": [ + ":user_id" + ], + "Vindex": "music_user_map" + } + ] + } +} diff --git a/go/vt/vtgate/planbuilder/testdata/postprocess_cases.txt b/go/vt/vtgate/planbuilder/testdata/postprocess_cases.txt index 6bee6600ffa..c94f2c9a73e 100644 --- a/go/vt/vtgate/planbuilder/testdata/postprocess_cases.txt +++ b/go/vt/vtgate/planbuilder/testdata/postprocess_cases.txt @@ -683,7 +683,6 @@ Gen4 plan same as above "FieldQuery": "select `user`.id, `user`.col1 as a, `user`.col2, weight_string(`user`.col1) from `user` where 1 != 1", "OrderBy": "1 ASC", "Query": "select `user`.id, `user`.col1 as a, `user`.col2, weight_string(`user`.col1) from `user` where `user`.id = 1 order by a asc", - "ResultColumns": 3, "Table": "`user`", "Values": [ 1 @@ -1390,7 +1389,41 @@ Gen4 plan same as above ] } } -Gen4 plan same as above +{ + "QueryType": "SELECT", + "Original": "select name from user, music order by name", + "Instructions": { + "OperatorType": "Join", + "Variant": "Join", + "JoinColumnIndexes": "-1", + "TableName": "`user`_music", + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "SelectScatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select `name`, weight_string(`name`) from `user` where 1 != 1", + "OrderBy": "0 ASC", + "Query": "select `name`, weight_string(`name`) from `user` order by `name` asc", + "Table": "`user`" + }, + { + "OperatorType": "Route", + "Variant": "SelectScatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select 1 from music where 1 != 1", + "Query": "select 1 from music", + "Table": "music" + } + ] + } +} # aggregation and non-aggregations column without group by "select count(id), num from user" diff --git a/go/vt/vtgate/planbuilder/testdata/unsupported_cases.txt b/go/vt/vtgate/planbuilder/testdata/unsupported_cases.txt index 041215c4dff..ae35d217c20 100644 --- a/go/vt/vtgate/planbuilder/testdata/unsupported_cases.txt +++ b/go/vt/vtgate/planbuilder/testdata/unsupported_cases.txt @@ -131,14 +131,53 @@ Gen4 plan same as above "Name": "user", "Sharded": true }, - "FieldQuery": "select id, weight_string(id) from `user` where 1 != 1", + "FieldQuery": "select id, id, weight_string(id) from `user` where 1 != 1", "OrderBy": "0 ASC", - "Query": "select id, weight_string(id) from `user` order by id asc", - "ResultColumns": 1, + "Query": "select id, id, weight_string(id) from `user` order by id asc", + "ResultColumns": 2, "Table": "`user`" } } +# join order by with ambiguous column reference ; valid in MySQL +"select name, name from user, music order by name" +"ambiguous symbol reference: `name`" +{ + "QueryType": "SELECT", + "Original": "select name, name from user, music order by name", + "Instructions": { + "OperatorType": "Join", + "Variant": "Join", + "JoinColumnIndexes": "-1,-1", + "TableName": "`user`_music", + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "SelectScatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select `name`, weight_string(`name`) from `user` where 1 != 1", + "OrderBy": "0 ASC", + "Query": "select `name`, weight_string(`name`) from `user` order by `name` asc", + "Table": "`user`" + }, + { + "OperatorType": "Route", + "Variant": "SelectScatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select 1 from music where 1 != 1", + "Query": "select 1 from music", + "Table": "music" + } + ] + } +} + # scatter aggregate with ambiguous aliases "select distinct a, b as a from user" "generating order by clause: ambiguous symbol reference: a"