diff --git a/enginetest/enginetests.go b/enginetest/enginetests.go index 04f8994c78..7997aff395 100644 --- a/enginetest/enginetests.go +++ b/enginetest/enginetests.go @@ -4901,10 +4901,6 @@ func TestSessionSelectLimit(t *testing.T, harness Harness) { Query: "SELECT i FROM (SELECT i FROM mytable ORDER BY i DESC) t ORDER BY i LIMIT 3", Expected: []sql.Row{{1}, {2}, {3}}, }, - { - Query: "SELECT i FROM (SELECT i FROM mytable ORDER BY i DESC) t ORDER BY i LIMIT 3", - Expected: []sql.Row{{1}, {2}, {3}}, - }, { Query: "select count(*), y from a group by y;", Expected: []sql.Row{{2, 1}, {3, 2}}, diff --git a/enginetest/queries/query_plans.go b/enginetest/queries/query_plans.go index 72da79345d..91cab5ffea 100644 --- a/enginetest/queries/query_plans.go +++ b/enginetest/queries/query_plans.go @@ -3568,44 +3568,88 @@ Select * from ( Query: `SELECT a FROM (select i,s FROM mytable) mt (a,b) order by 1;`, ExpectedPlan: "Project\n" + " ├─ columns: [mt.a:0!null]\n" + - " └─ Sort(mt.a:0!null ASC nullsFirst)\n" + - " └─ SubqueryAlias\n" + - " ├─ name: mt\n" + - " ├─ outerVisibility: false\n" + - " ├─ isLateral: false\n" + - " ├─ cacheable: true\n" + - " ├─ colSet: (3,4)\n" + - " ├─ tableId: 2\n" + + " └─ SubqueryAlias\n" + + " ├─ name: mt\n" + + " ├─ outerVisibility: false\n" + + " ├─ isLateral: false\n" + + " ├─ cacheable: true\n" + + " ├─ colSet: (3,4)\n" + + " ├─ tableId: 2\n" + + " └─ IndexedTableAccess(mytable)\n" + + " ├─ index: [mytable.i]\n" + + " ├─ static: [{[NULL, ∞)}]\n" + + " ├─ colSet: (1,2)\n" + + " ├─ tableId: 1\n" + " └─ Table\n" + " ├─ name: mytable\n" + - " ├─ columns: [i s]\n" + - " ├─ colSet: (1,2)\n" + - " └─ tableId: 1\n" + + " └─ columns: [i s]\n" + "", ExpectedEstimates: "Project\n" + " ├─ columns: [mt.a]\n" + - " └─ Sort(mt.a ASC)\n" + - " └─ SubqueryAlias\n" + - " ├─ name: mt\n" + - " ├─ outerVisibility: false\n" + - " ├─ isLateral: false\n" + - " ├─ cacheable: true\n" + - " └─ Table\n" + - " ├─ name: mytable\n" + - " └─ columns: [i s]\n" + + " └─ SubqueryAlias\n" + + " ├─ name: mt\n" + + " ├─ outerVisibility: false\n" + + " ├─ isLateral: false\n" + + " ├─ cacheable: true\n" + + " └─ IndexedTableAccess(mytable)\n" + + " ├─ index: [mytable.i]\n" + + " ├─ filters: [{[NULL, ∞)}]\n" + + " └─ columns: [i s]\n" + "", ExpectedAnalysis: "Project\n" + " ├─ columns: [mt.a]\n" + - " └─ Sort(mt.a ASC)\n" + - " └─ SubqueryAlias\n" + - " ├─ name: mt\n" + - " ├─ outerVisibility: false\n" + - " ├─ isLateral: false\n" + - " ├─ cacheable: true\n" + + " └─ SubqueryAlias\n" + + " ├─ name: mt\n" + + " ├─ outerVisibility: false\n" + + " ├─ isLateral: false\n" + + " ├─ cacheable: true\n" + + " └─ IndexedTableAccess(mytable)\n" + + " ├─ index: [mytable.i]\n" + + " ├─ filters: [{[NULL, ∞)}]\n" + + " └─ columns: [i s]\n" + + "", + }, + { + Query: `SELECT * FROM (select t.i,t.s FROM mytable t) mt order by i;`, + ExpectedPlan: "SubqueryAlias\n" + + " ├─ name: mt\n" + + " ├─ outerVisibility: false\n" + + " ├─ isLateral: false\n" + + " ├─ cacheable: true\n" + + " ├─ colSet: (3,4)\n" + + " ├─ tableId: 2\n" + + " └─ TableAlias(t)\n" + + " └─ IndexedTableAccess(mytable)\n" + + " ├─ index: [mytable.i]\n" + + " ├─ static: [{[NULL, ∞)}]\n" + + " ├─ colSet: (1,2)\n" + + " ├─ tableId: 1\n" + " └─ Table\n" + " ├─ name: mytable\n" + " └─ columns: [i s]\n" + "", + ExpectedEstimates: "SubqueryAlias\n" + + " ├─ name: mt\n" + + " ├─ outerVisibility: false\n" + + " ├─ isLateral: false\n" + + " ├─ cacheable: true\n" + + " └─ TableAlias(t)\n" + + " └─ IndexedTableAccess(mytable)\n" + + " ├─ index: [mytable.i]\n" + + " ├─ filters: [{[NULL, ∞)}]\n" + + " └─ columns: [i s]\n" + + "", + ExpectedAnalysis: "SubqueryAlias\n" + + " ├─ name: mt\n" + + " ├─ outerVisibility: false\n" + + " ├─ isLateral: false\n" + + " ├─ cacheable: true\n" + + " └─ TableAlias(t)\n" + + " └─ IndexedTableAccess(mytable)\n" + + " ├─ index: [mytable.i]\n" + + " ├─ filters: [{[NULL, ∞)}]\n" + + " └─ columns: [i s]\n" + + "", }, { Query: ` diff --git a/enginetest/queries/tpch_plans.go b/enginetest/queries/tpch_plans.go index 29e7aa5ea4..e4c64805dd 100644 --- a/enginetest/queries/tpch_plans.go +++ b/enginetest/queries/tpch_plans.go @@ -3457,7 +3457,7 @@ order by " │ ├─ columns: [avg(customer.c_acctbal):8->avg(c_acctbal):0]\n" + " │ └─ GroupBy\n" + " │ ├─ select: AVG(customer.c_acctbal:9!null)\n" + - " │ ├─ group: \n" + // printer adds space sep always + " │ ├─ group: \n" + " │ └─ Filter\n" + " │ ├─ AND\n" + " │ │ ├─ GreaterThan\n" + diff --git a/sql/analyzer/replace_sort.go b/sql/analyzer/replace_sort.go index 8aad71c692..7a2e4b3ff7 100644 --- a/sql/analyzer/replace_sort.go +++ b/sql/analyzer/replace_sort.go @@ -52,8 +52,10 @@ func replaceIdxSortHelper(ctx *sql.Context, scope *plan.Scope, node sql.Node, so if hasOverlapping(sfExprs, mysqlRanges) { return n, transform.SameTree, nil } + + isReverse := sortNode.SortFields[0].Order == sql.Descending // if the lookup does not need any reversing, do nothing - if sortNode.SortFields[0].Order != sql.Descending { + if (isReverse && lookup.IsReverse) || (!isReverse && !lookup.IsReverse) { return n, transform.NewTree, nil } @@ -67,7 +69,7 @@ func replaceIdxSortHelper(ctx *sql.Context, scope *plan.Scope, node sql.Node, so lookup.IsPointLookup, lookup.IsEmptyRange, lookup.IsSpatialLookup, - true, + isReverse, ) newIdxTbl, err := plan.NewStaticIndexedAccessForTableNode(ctx, n.TableNode, lookup) if err != nil { @@ -154,6 +156,40 @@ func replaceIdxSortHelper(ctx *sql.Context, scope *plan.Scope, node sql.Node, so case *plan.Sort, *plan.IndexedTableAccess, *plan.ResolvedTable, *plan.Project, *plan.Filter, *plan.Limit, *plan.Offset, *plan.Distinct, *plan.TableAlias: newChildren[i], same, err = replaceIdxSortHelper(ctx, scope, child, sortNode) + case *plan.SubqueryAlias: + if sortNode == nil { + continue + } + sortFields := make([]sql.SortField, len(sortNode.SortFields)) + sameSortFields := true + for i, sortField := range sortNode.SortFields { + col, sameExpr, _ := transform.Expr(sortField.Column, func(e sql.Expression) (sql.Expression, transform.TreeIdentity, error) { + if gt, ok := e.(*expression.GetField); ok { + if gf, ok := c.ScopeMapping[gt.Id()]; ok { + return gf, transform.NewTree, nil + } + } + return e, transform.SameTree, nil + }) + if sameExpr { + sortFields[i] = sortField + } else { + sameSortFields = false + col2, _ := col.(sql.Expression2) + sortFields[i] = sql.SortField{ + Column: col, + Column2: col2, + NullOrdering: sortField.NullOrdering, + Order: sortField.Order, + } + } + } + if !sameSortFields { + // The Sort node is used to find table aliases, but table aliases can't be found inside SubqueryAlias + // nodes, so we need to construct a new Sort node with the SubqueryAlias's child + newSort := plan.NewSort(sortFields, c.Child) + newChildren[i], same, err = replaceIdxSortHelper(ctx, scope, child, newSort) + } case *plan.JoinNode: // It's (probably) not possible to have Sort as child of Join without Subquery/SubqueryAlias, // and in the case where there is a Subq/SQA it's taken care of through finalizeSubqueries