1
0
mirror of https://github.com/MariaDB/server.git synced 2025-07-27 18:02:13 +03:00

MDEV-33875: ORDER BY DESC causes ROWID Filter slowdown

Rowid Filter cannot be used with reverse-ordered scans, for the
same reason as IndexConditionPushdown cannot be.

test_if_skip_sort_order() already has logic to disable ICP when
setting up a reverse-ordered scan. Added logic to also disable
Rowid Filter in this case, factored out the code into
prepare_for_reverse_ordered_access(), and added a comment describing
the cause of this limitation.
This commit is contained in:
Sergei Petrunia
2024-06-14 12:46:56 +03:00
parent 956bcf8f49
commit b47bd3f8bf
3 changed files with 196 additions and 28 deletions

View File

@ -23957,6 +23957,90 @@ void compute_part_of_sort_key_for_equals(JOIN *join, TABLE *table,
}
/*
@brief
This is called when switching table access to produce records
in reverse order.
@detail
- Disable "Range checked for each record" (Is this strictly necessary
here?)
- Disable Index Condition Pushdown and Rowid Filtering.
IndexConditionPushdownAndReverseScans, RowidFilteringAndReverseScans:
Suppose we're computing
select * from t1
where
key1 between 10 and 20 and extra_condition
order by key1 desc
here the range access uses a reverse-ordered scan on (1 <= key1 <= 10) and
extra_condition is checked by either ICP or Rowid Filtering.
Also suppose that extra_condition happens to be false for rows of t1 that
do not satisfy the "10 <= key1 <= 20" condition.
For forward ordered range scan, the SQL layer will make these calls:
h->read_range_first(RANGE(10 <= key1 <= 20));
while (h->read_range_next()) { ... }
The storage engine sees the end endpoint of "key1<=20" and can stop scanning
as soon as it encounters a row with key1>20.
For backward-ordered range scan, the SQL layer will make these calls:
h->index_read_map(key1=20, HA_READ_PREFIX_LAST_OR_PREV);
while (h->index_prev()) {
if (cmp_key(h->record, "key1=10" )<0)
break; // end of range
...
}
Note that the check whether we've walked beyond the key=10 endpoint is
made at the SQL layer. The storage engine has no information about the left
endpoint of the interval we're scanning. If all rows before that endpoint
do not satisfy ICP condition or do not pass the Rowid Filter, the storage
engine will enumerate the records until the table start.
In MySQL, the API is extended with set_end_range() call so that the storage
engine "knows" when to stop scanning.
*/
static void prepare_for_reverse_ordered_access(JOIN_TAB *tab)
{
/* Cancel "Range checked for each record" */
if (tab->use_quick == 2)
{
tab->use_quick= 1;
tab->read_first_record= join_init_read_record;
}
/*
Cancel Pushed Index Condition, as it doesn't work for reverse scans.
*/
if (tab->select && tab->select->pre_idx_push_select_cond)
{
tab->set_cond(tab->select->pre_idx_push_select_cond);
tab->table->file->cancel_pushed_idx_cond();
}
/*
The same with Rowid Filter: it doesn't work with reverse scans so cancel
it, too.
*/
{
/*
Rowid Filter is initialized at a later stage. It is not pushed to
the storage engine yet:
*/
DBUG_ASSERT(!tab->table->file->pushed_rowid_filter);
tab->range_rowid_filter_info= NULL;
delete tab->rowid_filter;
tab->rowid_filter= NULL;
}
}
/**
Test if we can skip the ORDER BY by using an index.
@ -24409,23 +24493,11 @@ check_reverse_order:
tab->limit= 0;
goto use_filesort; // Reverse sort failed -> filesort
}
/*
Cancel Pushed Index Condition, as it doesn't work for reverse scans.
*/
if (tab->select && tab->select->pre_idx_push_select_cond)
{
tab->set_cond(tab->select->pre_idx_push_select_cond);
tab->table->file->cancel_pushed_idx_cond();
}
prepare_for_reverse_ordered_access(tab);
if (select->quick == save_quick)
save_quick= 0; // make_reverse() consumed it
select->set_quick(tmp);
/* Cancel "Range checked for each record" */
if (tab->use_quick == 2)
{
tab->use_quick= 1;
tab->read_first_record= join_init_read_record;
}
}
else if (tab->type != JT_NEXT && tab->type != JT_REF_OR_NULL &&
tab->ref.key >= 0 && tab->ref.key_parts <= used_key_parts)
@ -24438,20 +24510,7 @@ check_reverse_order:
*/
tab->read_first_record= join_read_last_key;
tab->read_record.read_record_func= join_read_prev_same;
/* Cancel "Range checked for each record" */
if (tab->use_quick == 2)
{
tab->use_quick= 1;
tab->read_first_record= join_init_read_record;
}
/*
Cancel Pushed Index Condition, as it doesn't work for reverse scans.
*/
if (tab->select && tab->select->pre_idx_push_select_cond)
{
tab->set_cond(tab->select->pre_idx_push_select_cond);
tab->table->file->cancel_pushed_idx_cond();
}
prepare_for_reverse_ordered_access(tab);
}
}
else if (select && select->quick)