[MDEV-4839] MySQL Bug# 69638 Created: 2013-08-02  Updated: 2013-10-10  Resolved: 2013-10-10

Status: Closed
Project: MariaDB Server
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: Bug Priority: Major
Reporter: Sergei Petrunia Assignee: Sergei Petrunia
Resolution: Fixed Votes: 0
Labels: None


 Description   

This is to track MySQL Bug# 69638.

The bug is reproducible in MySQL 5.6.13. MariaDB 5.5 is not affected.

The query plan in mysql-5.6 is:

MySQL [bug_having]> explain SELECT tid, (rand()*100) AS r FROM bad group by tid HAVING r < 20 ORDER BY r DESC LIMIT 15;
+----+-------------+-------+-------+---------------+---------+---------+------+------+-----------------------------------------------------------+
| id | select_type | table | type  | possible_keys | key     | key_len | ref  | rows | Extra                                                     |
+----+-------------+-------+-------+---------------+---------+---------+------+------+-----------------------------------------------------------+
|  1 | SIMPLE      | bad   | range | bad_tid       | bad_tid | 8       | NULL |  113 | Using index for group-by; Using temporary; Using filesort |
+----+-------------+-------+-------+---------------+---------+---------+------+------+-----------------------------------------------------------+

In MariaDB 5.5, it is:

MariaDB [bug_having]> explain SELECT tid, (rand()*100) AS r FROM bad group by tid HAVING r < 20 ORDER BY r DESC LIMIT 15;
+------+-------------+-------+-------+---------------+---------+---------+------+------+----------------------------------------------+
| id   | select_type | table | type  | possible_keys | key     | key_len | ref  | rows | Extra                                        |
+------+-------------+-------+-------+---------------+---------+---------+------+------+----------------------------------------------+
|    1 | SIMPLE      | bad   | index | NULL          | bad_tid | 8       | NULL |  343 | Using index; Using temporary; Using filesort |
+------+-------------+-------+-------+---------------+---------+---------+------+------+----------------------------------------------+

The bug is not related to "Using index for group-by" optimization, however. If I disable it (in gdb), MySQL 5.6 produces the same EXPLAIN as MariaDB 5.5 does, but the bug is still there.



 Comments   
Comment by Sergei Petrunia [ 2013-08-02 ]

If I trace execution in MariaDB, I see that

  • rand()*100 is evaluated when writing to the temporary table
  • "r<20" is checked after reading from the temporary table. The value of r is obtained from the temporary table field.

Code-wise, it is Item_func_lt::val_int(), which has an argument of Item_ref, which points to Item_field, which is a field in the temporary table.

Comment by Sergei Petrunia [ 2013-08-02 ]

If I trace execution in MySQL 5.6, I see that "r < 20" is checked before writing to the temporary table. It seems, the problem is that rand() is evaluated two times for each group:
1. first time when preparing column for the temporary table
2. second time when evaluating HAVING.

In other words, the following happens:
S1. rand() returns a value that doesn't satisfy HAVING. The value is put into temp. table row buffer.

S2. The second call to rand() call returns value that does satisfy HAVING. We write tem. table row buffer into the temp. table

Comment by Sergei Petrunia [ 2013-08-02 ]

Possible solutions:

== Evaluate the "late" form of HAVING clause ==
The "early" form of HAVING uses "rand() * 100"
The "late" form of HAVING uses "temptable.r"

if we used "late" form, we would have avoided re-calculation of rand().

== Change rand() to have next_value() method ==

Make rand() return the same value until somebody calls next_value() or something like that. This is a rather big change, because we will need to define when it's time to use the "next value" for all possible cases.

== Don't use early-HAVING-evaluation for rand() ==

end_write_group() has the code that checks HAVING clause (join->having) in MariaDB 5.5

However, when tracing the query from the bug, I see that join->having==NULL.
Apparently, MariaDB 5.5

  • generally, supports early HAVING evaluation
  • but does not use it in this case.

Perhaps, we should make MySQL 5.6 not to use early HAVING evaluation for conditions with RAND_TABLE_BIT?

Comment by Sergei Petrunia [ 2013-08-02 ]

The optimizer switches between "early" and "late" form of WHERE by changing "ref array".

Here is how the changes are made:
== mariadb 5.5 ==
Hardware watchpoint 27: **$a
Old value = (Item_func_mul *) 0x7fff94006718
New value = (Item_field *) 0x7fff94013bc8

curr_join->set_items_ref_array(items1);

== mysql 5.6 ==

Hardware watchpoint 16: **$a
Old value = (Item_func_mul *) 0x7fffc40071c8
New value = (Item_field *) 0x7fffc400f898

In JOIN::make_tmp_tables_info():

// Need to set them now for correct group_fields setup, reset at the end.
set_items_ref_array(items1);

Hardware watchpoint 16: **$a
Old value = (Item_field *) 0x7fffc400f898
New value = (Item_func_mul *) 0x7fffc40071c8
in JOIN::make_tmp_tables_info:

// Reset before execution
set_items_ref_array(items0);

JOIN::exec() ...
end_write_group()
if (!join_tab->having || join_tab->having->val_int())

^^^^^^^^ this is where the problem happens

....
Hardware watchpoint 16: **$a
Old value = (Item_func_mul *) 0x7fffc40071c8
New value = (Item_field *) 0x7fffc400f898

in QEP_tmp_table::end_send ():

// Update ref array
join_tab->join->set_items_ref_array(*join_tab->ref_array);
table->reginfo.lock_type= TL_UNLOCK;

Hardware watchpoint 16: **$a
Old value = (Item_field *) 0x7fffc400f898
New value = (Item_func_mul *) 0x7fffc40071c8

in JOIN::cleanup (this=0x7fffc4007f30, full=true):

/* Restore ref array to original state */
if (current_ref_ptrs != items0)

{ set_items_ref_array(items0); set_group_rpa= false; }
Comment by Sergei Petrunia [ 2013-10-10 ]

The fix is in the last comment.

Generated at Thu Feb 08 06:59:35 UTC 2024 using Jira 8.20.16#820016-sha1:9d11dbea5f4be3d4cc21f03a88dd11d8c8687422.