maria-developers team mailing list archive
-
maria-developers team
-
Mailing list archive
-
Message #11834
Re: [Commits] 66349551bf3: MDEV-16214: Incorrect plan taken by the optimizer , uses INDEX instead of ref access with ORDER BY
Hi Varun,
The patch is mostly fine. Please find some input on the comments / coding style
below. Ok to push after it is addressed.
On Fri, May 17, 2019 at 01:44:36PM +0530, Varun wrote:
> revision-id: 66349551bf3a266fbefaebfe044abd05108723c2 (mariadb-10.3.12-205-g66349551bf3)
> parent(s): 3d56adbfac394b2b3ffd22a89fe7c2978ed9a505
> author: Varun Gupta
> committer: Varun Gupta
> timestamp: 2019-05-17 13:44:05 +0530
> message:
>
> MDEV-16214: Incorrect plan taken by the optimizer , uses INDEX instead of ref access with ORDER BY
>
> The issue in this case is that we take in account the estimates from quick keys instead of rec_per_key.
> The estimates for quick keys are better than rec_per_key only if we have ref(const), so we need to check
> that all keyparts in the ref key are of the type ref(const).
>
> ---
> mysql-test/main/order_by.result | 57 +++++++++++++++++++++++++++++++++++++++++
> mysql-test/main/order_by.test | 37 ++++++++++++++++++++++++++
> sql/sql_select.cc | 42 ++++++++++++++++++++----------
> 3 files changed, 122 insertions(+), 14 deletions(-)
...
> diff --git a/sql/sql_select.cc b/sql/sql_select.cc
> index f36a68bc7ae..8cdcf3afc8b 100644
> --- a/sql/sql_select.cc
> +++ b/sql/sql_select.cc
> @@ -9986,31 +9986,35 @@ static bool create_ref_for_key(JOIN *join, JOIN_TAB *j,
> j->ref.null_rejecting|= (key_part_map)1 << i;
> keyuse_uses_no_tables= keyuse_uses_no_tables && !keyuse->used_tables;
> /*
> - Todo: we should remove this check for thd->lex->describe on the next
> - line. With SHOW EXPLAIN code, EXPLAIN printout code no longer depends
> - on it. However, removing the check caused change in lots of query
> - plans! Does the optimizer depend on the contents of
> - table_ref->key_copy ? If yes, do we produce incorrect EXPLAINs?
> + We don't want to compute heavy expressions in EXPLAIN, an example would
> + select * from t1 where t1.key=(select thats very heavy);
> +
> + (select thats very heavy) => is a constant here
> + eg: (select avg(order_cost) from orders) => constant but expensive
> */
> if (!keyuse->val->used_tables() && !thd->lex->describe)
> { // Compare against constant
> - store_key_item tmp(thd,
> + store_key_item tmp(thd,
> keyinfo->key_part[i].field,
> key_buff + maybe_null,
> maybe_null ? key_buff : 0,
> keyinfo->key_part[i].length,
> keyuse->val,
> FALSE);
> - if (unlikely(thd->is_fatal_error))
> - DBUG_RETURN(TRUE);
> - tmp.copy();
> + if (unlikely(thd->is_fatal_error))
> + DBUG_RETURN(TRUE);
> + tmp.copy();
> j->ref.const_ref_part_map |= key_part_map(1) << i ;
> }
> else
> - *ref_key++= get_store_key(thd,
> - keyuse,join->const_table_map,
> - &keyinfo->key_part[i],
> - key_buff, maybe_null);
> + {
> + *ref_key++= get_store_key(thd,
> + keyuse,join->const_table_map,
> + &keyinfo->key_part[i],
> + key_buff, maybe_null);
> + if (!keyuse->val->used_tables())
> + j->ref.const_ref_part_map |= key_part_map(1) << i ;
> + }
> /*
> Remember if we are going to use REF_OR_NULL
> But only if field _really_ can be null i.e. we force JT_REF
> @@ -25256,6 +25260,8 @@ bool JOIN_TAB::save_explain_data(Explain_table_access *eta,
> {
> if (!(eta->ref_list.append_str(thd->mem_root, "const")))
> return 1;
> + if (thd->lex->describe)
> + key_ref++;
This is very cryptic.
Please provide an explanation, something like:
create_ref_for_key() handles keypart=const equalities as follows:
- non-EXPLAIN execution will copy the "const" to lookup tuple immediately
and will not add an element to ref.key_copy
- EXPLAIN will put an element into ref.key_copy. Since we've just printed
"const" for it, we should skip it here.
> }
> else
> {
> @@ -26921,7 +26927,15 @@ test_if_cheaper_ordering(const JOIN_TAB *tab, ORDER *order, TABLE *table,
> */
> if (ref_key >= 0 && ref_key != MAX_KEY && tab->type == JT_REF)
> {
> - if (table->quick_keys.is_set(ref_key))
> + /*
> + For all the parts of the ref key we check if all of them belong
> + to the type ref(const). This is done because if all parts of the ref
> + key are of type ref(const), then we are sure that the estimates
> + provides by quick keys is better than that provide by rec_per_key.
The above is hard to read and has typos. Here's a shorter variant:
If ref access uses keypart=const for all its key parts,
and quick select uses the same # of key parts, then they are equivalent.
Reuse #rows estimate from quick select as it is more precise.
> + */
> + if (tab->ref.const_ref_part_map == make_prev_keypart_map(tab->ref.key_parts) &&
> + table->quick_keys.is_set(ref_key) &&
> + table->quick_key_parts[ref_key] == tab->ref.key_parts)
Please fix indetation;
One of the lines is too long.
> refkey_rows_estimate= table->quick_rows[ref_key];
> else
> {
BR
Sergei
--
Sergei Petrunia, Software Developer
MariaDB Corporation | Skype: sergefp | Blog: http://s.petrunia.net/blog