@@ -20,7 +20,6 @@ func replaceIdxSort(ctx *sql.Context, a *Analyzer, n sql.Node, scope *plan.Scope
2020func replaceIdxSortHelper (ctx * sql.Context , scope * plan.Scope , node sql.Node , sortNode * plan.Sort ) (sql.Node , transform.TreeIdentity , error ) {
2121 switch n := node .(type ) {
2222 case * plan.Sort :
23- // TODO: are there problems when there are multiple ORDER BYs?
2423 if isValidSortFieldOrder (n .SortFields ) {
2524 sortNode = n // lowest parent sort node
2625 }
@@ -59,12 +58,10 @@ func replaceIdxSortHelper(ctx *sql.Context, scope *plan.Scope, node sql.Node, so
5958 }
6059
6160 // TODO: need to check for indexOrder = None?
62-
6361 // if the index is not reversible, do nothing
6462 if oi , ok := lookup .Index .(sql.OrderedIndex ); ok && ! oi .Reversible () {
6563 return n , transform .SameTree , nil
6664 }
67-
6865 lookup = sql .NewIndexLookup (
6966 lookup .Index ,
7067 mysqlRanges ,
@@ -73,11 +70,11 @@ func replaceIdxSortHelper(ctx *sql.Context, scope *plan.Scope, node sql.Node, so
7370 lookup .IsSpatialLookup ,
7471 true ,
7572 )
76- nn , err := plan .NewStaticIndexedAccessForTableNode (ctx , n .TableNode , lookup )
73+ newIdxTbl , err := plan .NewStaticIndexedAccessForTableNode (ctx , n .TableNode , lookup )
7774 if err != nil {
7875 return nil , transform .SameTree , err
7976 }
80- return nn , transform .NewTree , err
77+ return newIdxTbl , transform .NewTree , err
8178 case * plan.ResolvedTable :
8279 if sortNode == nil {
8380 return n , transform .SameTree , nil
@@ -154,9 +151,8 @@ func replaceIdxSortHelper(ctx *sql.Context, scope *plan.Scope, node sql.Node, so
154151 var err error
155152 same := transform .SameTree
156153 switch c := child .(type ) {
157- case * plan.Sort , * plan.IndexedTableAccess , * plan.ResolvedTable :
158- newChildren [i ], same , err = replaceIdxSortHelper (ctx , scope , child , sortNode )
159- case * plan.Project , * plan.Filter , * plan.Limit , * plan.Offset , * plan.Distinct , * plan.TableAlias :
154+ case * plan.Sort , * plan.IndexedTableAccess , * plan.ResolvedTable ,
155+ * plan.Project , * plan.Filter , * plan.Limit , * plan.Offset , * plan.Distinct , * plan.TableAlias :
160156 newChildren [i ], same , err = replaceIdxSortHelper (ctx , scope , child , sortNode )
161157 case * plan.JoinNode :
162158 // TODO: is this applicable to other types of joins?
@@ -169,10 +165,6 @@ func replaceIdxSortHelper(ctx *sql.Context, scope *plan.Scope, node sql.Node, so
169165 if sortNode == nil {
170166 continue
171167 }
172- // TODO: allow for reversed indexes; for some reason this breaks
173- if sortNode .SortFields [0 ].Order == sql .Descending {
174- continue
175- }
176168 newLeft , sameLeft , errLeft := replaceIdxSortHelper (ctx , scope , c .Left (), sortNode )
177169 if errLeft != nil {
178170 return nil , transform .SameTree , errLeft
@@ -184,6 +176,25 @@ func replaceIdxSortHelper(ctx *sql.Context, scope *plan.Scope, node sql.Node, so
184176 if sameLeft && sameRight {
185177 continue
186178 }
179+ // No need to check all SortField orders because of isValidSortFieldOrder
180+ isReversed := sortNode .SortFields [0 ].Order == sql .Descending
181+ // either left or right has been reversed
182+ if (sameLeft != sameRight ) && isReversed {
183+ // If descending, then both Indexes must be reversed
184+ if sameLeft {
185+ newLeft , same , err = buildReverseIndexedTable (ctx , newLeft )
186+ } else if sameRight {
187+ newRight , same , err = buildReverseIndexedTable (ctx , newRight )
188+ }
189+ if err != nil {
190+ return nil , transform .SameTree , err
191+ }
192+ // If we could not replace the IndexedTableAccess with a reversed one, result is same, so abandon
193+ if same {
194+ continue
195+ }
196+ c .IsReversed = true
197+ }
187198 newChildren [i ], err = c .WithChildren (newLeft , newRight )
188199 if err != nil {
189200 return nil , transform .SameTree , err
@@ -213,31 +224,34 @@ func replaceIdxSortHelper(ctx *sql.Context, scope *plan.Scope, node sql.Node, so
213224
214225// buildReverseIndexedTable will attempt to take the lookup from an IndexedTableAccess, and return a new
215226// IndexedTableAccess with the lookup reversed.
216- func buildReverseIndexedTable (ctx * sql.Context , node sql.Node ) (* plan.IndexedTableAccess , bool , error ) {
217- idxTbl , isIdxTbl := node .(* plan.IndexedTableAccess )
218- if ! isIdxTbl {
219- return nil , false , nil
220- }
221- lookup , err := idxTbl .GetLookup (ctx , nil )
222- if err != nil {
223- return nil , false , err
224- }
225- if oi , isOrderedIdx := lookup .Index .(sql.OrderedIndex ); isOrderedIdx && (! oi .Reversible () || oi .Order () == sql .IndexOrderNone ) {
226- return nil , false , nil
227- }
228- lookup = sql .NewIndexLookup (
229- lookup .Index ,
230- lookup .Ranges .(sql.MySQLRangeCollection ),
231- lookup .IsPointLookup ,
232- lookup .IsEmptyRange ,
233- lookup .IsSpatialLookup ,
234- true ,
235- )
236- newNode , err := plan .NewStaticIndexedAccessForTableNode (ctx , idxTbl .TableNode , lookup )
237- if err != nil {
238- return nil , false , err
239- }
240- return newNode , true , nil
227+ func buildReverseIndexedTable (ctx * sql.Context , node sql.Node ) (sql.Node , transform.TreeIdentity , error ) {
228+ return transform .Node (node , func (n sql.Node ) (sql.Node , transform.TreeIdentity , error ) {
229+ switch idxTbl := n .(type ) {
230+ case * plan.IndexedTableAccess :
231+ lookup , err := idxTbl .GetLookup (ctx , nil )
232+ if err != nil {
233+ return nil , transform .SameTree , err
234+ }
235+ if oi , isOrderedIdx := lookup .Index .(sql.OrderedIndex ); isOrderedIdx && (! oi .Reversible () || oi .Order () == sql .IndexOrderNone ) {
236+ return n , transform .SameTree , nil
237+ }
238+ lookup = sql .NewIndexLookup (
239+ lookup .Index ,
240+ lookup .Ranges .(sql.MySQLRangeCollection ),
241+ lookup .IsPointLookup ,
242+ lookup .IsEmptyRange ,
243+ lookup .IsSpatialLookup ,
244+ true ,
245+ )
246+ newIdxTbl , err := plan .NewStaticIndexedAccessForTableNode (ctx , idxTbl .TableNode , lookup )
247+ if err != nil {
248+ return nil , transform .SameTree , err
249+ }
250+ return newIdxTbl , transform .NewTree , nil
251+ default :
252+ return n , transform .SameTree , nil
253+ }
254+ })
241255}
242256
243257// replaceAgg converts aggregate functions to order by + limit 1 when possible
0 commit comments