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"