Serve descending ORDER BY from ascending index tags#300
Merged
Conversation
Closes #294. A single-key ORDER BY ... DESC no longer forces an in-memory sort when the column has a usable ascending tag: the planner reverses the index entry list, and re-sorts each run of duplicate keys by record index afterwards, because a stable descending sort keeps ties in their original ascending row order. This applies to pure order scans, to WHERE candidates on the same column (equality seeks and ranges), to TOP/LIMIT after the order, and to the Query<T> builder's OrderByDescending. Tags that are themselves DESCENDING remain excluded from planning: no fixture contains one and their on-disk key semantics (complement encoding versus reversed tree order) are unverified, so searching them could silently miss rows. That remainder is tracked separately. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



Closes #294.
Summary
ORDER BY col DESCis now index-served whenever the column has a usable ascending tag — previously any descending order meant a full scan plus in-memory sort:How
The planner reverses the ascending tag's entry list — and then re-stabilizes each run of duplicate keys by record index, because a stable descending sort keeps tied rows in their original ascending row order, while a naive reversal would flip them. The duplicate-heavy
CONTACT_IDtag (runs of five equal keys) pins this: the reversed index order must match the forced-scan stable sort byte-for-byte, and does. Works for pure order scans, for WHERE candidates on the same column (seeks and ranges), withTOP/LIMITapplied after the order, and throughDbfQuery<T>.OrderByDescending. Multi-key orderings and untagged columns still sort in memory, as before.Deliberate remainder
Tags that are themselves DESCENDING stay excluded from planning. No fixture anywhere contains one, and their on-disk key semantics are unverified (VFP may store complemented key bytes keeping the tree byte-ascending, or store a genuinely reversed tree) — searching them on a guess could silently miss rows, which is the one failure mode this planner never accepts. A follow-up issue tracks that remainder with the verification plan; contributing a VFP file with
INDEX ON … TAG … DESCENDINGwould unblock it immediately.Test plan
8 new tests in
QueryDescendingOrderTests, all differential against forced scans: descending order scans (character and integer tags), the stable-duplicate-run case, descending order satisfied after equality seeks and ranges,LIMITafter descending order (exact sequence asserted), untagged-column fallback, and both builder paths. The phase-6 test that pinnedDESCto a full scan is repurposed to a genuinely unsatisfiable multi-key ordering. Full suite: 440 passed, 1 pre-existing skip, zero warnings on both TFMs.🤖 Generated with Claude Code