From 5289c4470eaf42981741dcc76f80c93db8b08e53 Mon Sep 17 00:00:00 2001 From: "vitess-bot[bot]" <108069721+vitess-bot[bot]@users.noreply.github.com> Date: Tue, 16 Jul 2024 15:31:34 +0530 Subject: [PATCH] [release-20.0] fix issue with aggregation inside of derived tables (#16366) (#16385) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Andres Taylor Co-authored-by: Andrés Taylor --- .../queries/aggregation/aggregation_test.go | 2 + .../planbuilder/operators/SQL_builder.go | 8 +++ .../operators/horizon_expanding.go | 28 +++++++++- .../planbuilder/operators/queryprojection.go | 5 +- .../planbuilder/testdata/aggr_cases.json | 56 ++++++++++++++++++- .../planbuilder/testdata/cte_cases.json | 2 +- 6 files changed, 93 insertions(+), 8 deletions(-) diff --git a/go/test/endtoend/vtgate/queries/aggregation/aggregation_test.go b/go/test/endtoend/vtgate/queries/aggregation/aggregation_test.go index 4057b1bdee1..eb9da0dba6c 100644 --- a/go/test/endtoend/vtgate/queries/aggregation/aggregation_test.go +++ b/go/test/endtoend/vtgate/queries/aggregation/aggregation_test.go @@ -441,10 +441,12 @@ func TestOrderByCount(t *testing.T) { defer closer() mcmp.Exec("insert into t9(id1, id2, id3) values(1, '1', '1'), (2, '2', '2'), (3, '2', '2'), (4, '3', '3'), (5, '3', '3'), (6, '3', '3')") + mcmp.Exec("insert into t1(t1_id, `name`, `value`, shardkey) values(1,'a1','foo',100), (2,'b1','foo',200), (3,'c1','foo',300), (4,'a1','foo',100), (5,'b1','bar',200)") mcmp.Exec("SELECT t9.id2 FROM t9 GROUP BY t9.id2 ORDER BY COUNT(t9.id2) DESC") if utils.BinaryIsAtLeastAtVersion(20, "vtgate") { mcmp.Exec("select COUNT(*) from (select 1 as one FROM t9 WHERE id3 = 3 ORDER BY id1 DESC LIMIT 3 OFFSET 0) subquery_for_count") + mcmp.Exec("select t.id1, t1.name, t.leCount from (select id1, count(*) as leCount from t9 group by 1 order by 2 desc limit 20) t join t1 on t.id1 = t1.t1_id") } } diff --git a/go/vt/vtgate/planbuilder/operators/SQL_builder.go b/go/vt/vtgate/planbuilder/operators/SQL_builder.go index 10faee5e568..b9a1c931534 100644 --- a/go/vt/vtgate/planbuilder/operators/SQL_builder.go +++ b/go/vt/vtgate/planbuilder/operators/SQL_builder.go @@ -468,6 +468,14 @@ func buildAggregation(op *Aggregator, qb *queryBuilder) { if op.WithRollup { qb.setWithRollup() } + + if op.DT != nil { + sel := qb.asSelectStatement() + qb.stmt = nil + qb.addTableExpr(op.DT.Alias, op.DT.Alias, TableID(op), &sqlparser.DerivedTable{ + Select: sel, + }, nil, op.DT.Columns) + } } func buildOrdering(op *Ordering, qb *queryBuilder) { diff --git a/go/vt/vtgate/planbuilder/operators/horizon_expanding.go b/go/vt/vtgate/planbuilder/operators/horizon_expanding.go index 3b934752a00..dd40dbeb918 100644 --- a/go/vt/vtgate/planbuilder/operators/horizon_expanding.go +++ b/go/vt/vtgate/planbuilder/operators/horizon_expanding.go @@ -82,7 +82,7 @@ func expandSelectHorizon(ctx *plancontext.PlanningContext, horizon *Horizon, sel // if we are dealing with a derived table, we need to make sure that the ordering columns // are available outside the derived table for _, order := range horizon.Query.GetOrderBy() { - qp.addColumn(ctx, order.Expr) + qp.addDerivedColumn(ctx, order.Expr) } } @@ -108,7 +108,7 @@ func expandSelectHorizon(ctx *plancontext.PlanningContext, horizon *Horizon, sel } if len(qp.OrderExprs) > 0 { - op = expandOrderBy(ctx, op, qp) + op = expandOrderBy(ctx, op, qp, horizon.Alias) extracted = append(extracted, "Ordering") } @@ -124,7 +124,7 @@ func expandSelectHorizon(ctx *plancontext.PlanningContext, horizon *Horizon, sel return op, Rewrote(fmt.Sprintf("expand SELECT horizon into (%s)", strings.Join(extracted, ", "))) } -func expandOrderBy(ctx *plancontext.PlanningContext, op Operator, qp *QueryProjection) Operator { +func expandOrderBy(ctx *plancontext.PlanningContext, op Operator, qp *QueryProjection, derived string) Operator { var newOrder []OrderBy sqc := &SubQueryBuilder{} proj, ok := op.(*Projection) @@ -134,6 +134,9 @@ func expandOrderBy(ctx *plancontext.PlanningContext, op Operator, qp *QueryProje newExpr, subqs := sqc.pullOutValueSubqueries(ctx, expr.SimplifiedExpr, TableID(op), false) if newExpr == nil { // If no subqueries are found, retain the original order expression + if derived != "" { + expr = exposeOrderingColumn(ctx, qp, expr, derived) + } newOrder = append(newOrder, expr) continue } @@ -167,6 +170,25 @@ func expandOrderBy(ctx *plancontext.PlanningContext, op Operator, qp *QueryProje } } +// exposeOrderingColumn will expose the ordering column to the outer query +func exposeOrderingColumn(ctx *plancontext.PlanningContext, qp *QueryProjection, orderBy OrderBy, derived string) OrderBy { + for _, se := range qp.SelectExprs { + aliasedExpr, err := se.GetAliasedExpr() + if err != nil { + panic(vterrors.VT13001("unexpected expression in select")) + } + if ctx.SemTable.EqualsExprWithDeps(aliasedExpr.Expr, orderBy.SimplifiedExpr) { + newExpr := sqlparser.NewColNameWithQualifier(aliasedExpr.ColumnName(), sqlparser.NewTableName(derived)) + ctx.SemTable.CopySemanticInfo(orderBy.SimplifiedExpr, newExpr) + orderBy.SimplifiedExpr = newExpr + orderBy.Inner = &sqlparser.Order{Expr: newExpr, Direction: orderBy.Inner.Direction} + break + } + } + + return orderBy +} + func createProjectionFromSelect(ctx *plancontext.PlanningContext, horizon *Horizon) Operator { qp := horizon.getQP(ctx) diff --git a/go/vt/vtgate/planbuilder/operators/queryprojection.go b/go/vt/vtgate/planbuilder/operators/queryprojection.go index 99651258a73..306a245e602 100644 --- a/go/vt/vtgate/planbuilder/operators/queryprojection.go +++ b/go/vt/vtgate/planbuilder/operators/queryprojection.go @@ -709,8 +709,9 @@ func (qp *QueryProjection) useGroupingOverDistinct(ctx *plancontext.PlanningCont return true } -// addColumn adds a column to the QueryProjection if it is not already present -func (qp *QueryProjection) addColumn(ctx *plancontext.PlanningContext, expr sqlparser.Expr) { +// addColumn adds a column to the QueryProjection if it is not already present. +// It will use a column name that is available on the outside of the derived table +func (qp *QueryProjection) addDerivedColumn(ctx *plancontext.PlanningContext, expr sqlparser.Expr) { for _, selectExpr := range qp.SelectExprs { getExpr, err := selectExpr.GetExpr() if err != nil { diff --git a/go/vt/vtgate/planbuilder/testdata/aggr_cases.json b/go/vt/vtgate/planbuilder/testdata/aggr_cases.json index 7d20d065002..5f4671ca51b 100644 --- a/go/vt/vtgate/planbuilder/testdata/aggr_cases.json +++ b/go/vt/vtgate/planbuilder/testdata/aggr_cases.json @@ -760,6 +760,58 @@ ] } }, + { + "comment": "Aggregation with derived table", + "query": "select u.id, u.name, t.num_segments from (select id, count(*) as num_segments from user group by 1 order by 2 desc limit 20) t join unsharded u on u.id = t.id", + "plan": { + "QueryType": "SELECT", + "Original": "select u.id, u.name, t.num_segments from (select id, count(*) as num_segments from user group by 1 order by 2 desc limit 20) t join unsharded u on u.id = t.id", + "Instructions": { + "OperatorType": "Join", + "Variant": "Join", + "JoinColumnIndexes": "R:0,R:1,L:0", + "JoinVars": { + "t_id": 1 + }, + "TableName": "`user`_unsharded", + "Inputs": [ + { + "OperatorType": "Limit", + "Count": "20", + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select t.num_segments, t.id from (select id, count(*) as num_segments from `user` where 1 != 1 group by id) as t where 1 != 1", + "OrderBy": "0 DESC", + "Query": "select t.num_segments, t.id from (select id, count(*) as num_segments from `user` group by id) as t order by t.num_segments desc limit 20", + "Table": "`user`" + } + ] + }, + { + "OperatorType": "Route", + "Variant": "Unsharded", + "Keyspace": { + "Name": "main", + "Sharded": false + }, + "FieldQuery": "select u.id, u.`name` from unsharded as u where 1 != 1", + "Query": "select u.id, u.`name` from unsharded as u where u.id = :t_id", + "Table": "unsharded" + } + ] + }, + "TablesUsed": [ + "main.unsharded", + "user.user" + ] + } + }, { "comment": "scatter aggregate multiple group by (numbers)", "query": "select a, b, count(*) from user group by 2, 1", @@ -3589,7 +3641,7 @@ }, "FieldQuery": "select x.id, x.val1, 1, weight_string(x.val1) from (select id, val1 from `user` where 1 != 1) as x where 1 != 1", "OrderBy": "(1|3) ASC", - "Query": "select x.id, x.val1, 1, weight_string(x.val1) from (select id, val1 from `user` where val2 < 4) as x order by `user`.val1 asc limit 2", + "Query": "select x.id, x.val1, 1, weight_string(x.val1) from (select id, val1 from `user` where val2 < 4) as x order by x.val1 asc limit 2", "Table": "`user`" } ] @@ -7102,7 +7154,7 @@ }, "FieldQuery": "select subquery_for_count.one, subquery_for_count.id, 1, weight_string(subquery_for_count.id) from (select 1 as one, id from `user` where 1 != 1) as subquery_for_count where 1 != 1", "OrderBy": "(1|3) DESC", - "Query": "select subquery_for_count.one, subquery_for_count.id, 1, weight_string(subquery_for_count.id) from (select 1 as one, id from `user` where `user`.is_not_deleted = true) as subquery_for_count order by id desc limit 25", + "Query": "select subquery_for_count.one, subquery_for_count.id, 1, weight_string(subquery_for_count.id) from (select 1 as one, id from `user` where `user`.is_not_deleted = true) as subquery_for_count order by subquery_for_count.id desc limit 25", "Table": "`user`" } ] diff --git a/go/vt/vtgate/planbuilder/testdata/cte_cases.json b/go/vt/vtgate/planbuilder/testdata/cte_cases.json index 8181c13cd0b..ae335ab0347 100644 --- a/go/vt/vtgate/planbuilder/testdata/cte_cases.json +++ b/go/vt/vtgate/planbuilder/testdata/cte_cases.json @@ -352,7 +352,7 @@ }, "FieldQuery": "select x.id, x.val1, 1, weight_string(x.val1) from (select id, val1 from `user` where 1 != 1) as x where 1 != 1", "OrderBy": "(1|3) ASC", - "Query": "select x.id, x.val1, 1, weight_string(x.val1) from (select id, val1 from `user` where val2 < 4) as x order by `user`.val1 asc limit 2", + "Query": "select x.id, x.val1, 1, weight_string(x.val1) from (select id, val1 from `user` where val2 < 4) as x order by x.val1 asc limit 2", "Table": "`user`" } ]