Analysis:
The reason for the inefficent plan was that Item_subselect::is_expensive()
didn't detect the special case when a subquery was optimized, but had no
join plan because it either has no table, or its tables have been optimized
away, or the optimizer detected that the result set is empty.
Solution:
Identify the special cases above in the Item_subselect::is_expensive(),
and consider such degenerate subqueries inexpensive.
The MIN/MAX optimizer code from the function opt_sum_query erroneously
did not take into account conjunctive conditions that did not depend on
any table, yet were not identified as constant items. These could be
items containing rand() or PS/SP parameters. These items are supposed
to be evaluated at the execution phase. That's why if such conditions
can be extracted from the WHERE condition the MIN/MAX optimization is
not applied as currently it is always done at the optimization phase.
(In 5.3 expensive subqueries are also evaluated only at the execution
phase. So, if a constant condition with such subquery can be extracted
from the WHERE clause the MIN/MAX optimization should not be applied
in 5.3.)
IF an IN/ALL/SOME predicate with a constant left part is transformed
into an EXISTS subquery the resulting subquery should not be considered
uncacheable if the right part of the predicate is not uncacheable.
Backported the function dbug_print_item() from 5.3. The function is used
only for debugging.
- Make elimination work with aggregate functions. The problem was that aggregate functions
reported all table bits in used_tables(), and that prevented table elimination. Fixed by
making aggregate functions return more correct value from used_tables().
mysql-test/r/ps_11bugs.result:
MWL#17: Table elimination
- Update test results. The difference is because of Item_ref change: outer references to constants
are now recognized as constants, too.
mysql-test/r/subselect.result:
- Update test results. The difference is because of Item_ref change: outer references to constants
are now recognized as constants, too.
mysql-test/r/table_elim.result:
MWL#17: Table elimination
- Check that elimination works in presense of aggreagate functions
mysql-test/t/table_elim.test:
MWL#17: Table elimination
- Check that elimination works in presense of aggreagate functions
sql/item.h:
MWL#17: Table elimination
- Add Item_ref::const_item() which calls (*ref)->const_item(). Before this diff Item_ref used the
default implementation of const_item(){ return used_tables()==0; }. This is no longer true, as
COUNT(*) now has used_tables()==0 but const_item()==FALSE.
sql/item_sum.cc:
MWL#17: Table elimination
- Make Item_sum() and it descendants not to return all bits in used_tables(). This is needed
because otherwise table elimination can't work in presense of aggregate functions
- COUNT(*) now has used_tables()==0 and const_item()==FALSE. Had to change
Item_ref::const_item() to account for this.
sql/item_sum.h:
MWL#17: Table elimination
- Add comments
The code to get read the value of a system variable was extracting its value
on PREPARE stage and was substituting the value (as a constant) into the parse tree.
Note that this must be a reversible transformation, i.e. it must be reversed before
each re-execution.
Unfortunately this cannot be reliably done using the current code, because there are
other non-reversible source tree transformations that can interfere with this
reversible transformation.
Fixed by not resolving the value at PREPARE, but at EXECUTE (as the rest of the
functions operate). Added a cache of the value (so that it's constant throughout
the execution of the query). Note that the cache also caches NULL values.
Updated an obsolete related test suite (variables-big) and the code to test the
result type of system variables (as per bug 74).
mysql-test/extra/rpl_tests/rpl_insert_id.test:
Bug #32124: removed ambiguous testcase
mysql-test/r/innodb_data_home_dir_basic.result:
Bug #32124: fixed wrong test case
mysql-test/r/innodb_flush_method_basic.result:
Bug #32124: fixed wrong test case
mysql-test/r/ps_11bugs.result:
Bug #32124: test case
mysql-test/r/ssl_capath_basic.result:
Bug #32124: fixed wrong test case
mysql-test/r/ssl_cipher_basic.result:
Bug #32124: fixed wrong test case
mysql-test/r/variables.result:
Bug #32124: system vars are shown as such in EXPLAIN EXTENDED, not as constants.
mysql-test/suite/rpl/r/rpl_insert_id.result:
Bug #32124: removed ambiguous testcase
mysql-test/t/ps_11bugs.test:
Bug #32124: test case
sql/item.cc:
Bug #32124: placed the code to convert string to longlong or double
to a function (so that it can be reused)
sql/item.h:
Bug #32124: placed the code to convert string to longlong or double
to a function (so that it can be reused)
sql/item_func.cc:
Bug #32124: moved the evaluation of system variables at runtime (val_xxx).
sql/item_func.h:
Bug #32124: moved the evaluation of system variables at runtime (val_xxx).
sql/set_var.cc:
Bug #32124: removed the code that calculated the system variable's value
at PREPARE
sql/set_var.h:
Bug #32124: removed the code that calculated the system variable's value
at PREPARE
tests/mysql_client_test.c:
Bug #32124 : removed the reading of the system variable, because its max
length is depended on the system charset and client charset and can't be
easily calculated.
The executing code had a safety assertion so that it refused to free Items
that it didn't create. However, there is a case, undefined user variables,
which would put Items into the list to be freed.
Instead, do something that is more risky in expectation that the code will
be refactored soon, as Kostja wants to do: Remove the assertions from
prepare() and execute(). Put one assertion at a higher level, before
stmt->set_params_from_vars(), which may then create new to-be-freed Items .
mysql-test/r/ps_11bugs.result:
Create tests to prove that undefined variables work, as keys and not, and
that variables explicitly assigned to Null work.
mysql-test/t/ps_11bugs.test:
Create tests to prove that undefined variables work, as keys and not, and
that variables explicitly assigned to Null work.
sql/sql_prepare.cc:
Move a safety assertion up one level and higher, because there is
legitimately a case where thd->free_list is not NULL going into
Prepared_statement::{prepare,execute} methods.
Kostja plans to refactor this code so that it is both safe and works.
(Now it works, but isn't very safe.)
In the code that converts IN predicates to EXISTS predicates it is changing
the select list elements to constant 1. Example :
SELECT ... FROM ... WHERE a IN (SELECT c FROM ...)
is transformed to :
SELECT ... FROM ... WHERE EXISTS (SELECT 1 FROM ... HAVING a = c)
However there can be no FROM clause in the IN subquery and it may not be
a simple select : SELECT ... FROM ... WHERE a IN (SELECT f(..) AS
c UNION SELECT ...) This query is transformed to : SELECT ... FROM ...
WHERE EXISTS (SELECT 1 FROM (SELECT f(..) AS c UNION SELECT ...)
x HAVING a = c) In the above query c in the HAVING clause is made to be
an Item_null_helper (a subclass of Item_ref) pointing to the real
Item_field (which is not referenced anywhere else in the query anymore).
This is done because Item_ref_null_helper collects information whether
there are NULL values in the result. This is OK for directly executed
statements, because the Item_field pointed by the Item_null_helper is
already fixed when the transformation is done. But when executed as
a prepared statement all the Item instances are "un-fixed" before the
recompilation of the prepared statement. So when the Item_null_helper
gets fixed it discovers that the Item_field it points to is not fixed
and issues an error. The remedy is to keep the original select list
references when there are no tables in the FROM clause. So the above
becomes : SELECT ... FROM ... WHERE EXISTS (SELECT c FROM (SELECT f(..)
AS c UNION SELECT ...) x HAVING a = c) In this way c is referenced
directly in the select list as well as by reference in the HAVING
clause. So it gets correctly fixed even with prepared statements. And
since the Item_null_helper subclass of Item_ref_null_helper is not used
anywhere else it's taken out.
mysql-test/r/ps_11bugs.result:
Test case for the bug
mysql-test/r/subselect.result:
Explain updated because of the tranformation
mysql-test/t/ps_11bugs.test:
Testcase for the bug
sql/item.cc:
Taking out Item_null_helper as it's no longer needed
sql/item.h:
Taking out Item_null_helper as it's no longer needed
sql/item_subselect.cc:
The described change to the IN->EXISTS transformation
test for bug#1180 changed to table naming scheme 't#'.
mysql-test/r/ps_11bugs.result:
Expected results of new tests for bug#1644 and bug#1676,
test for bug#1180 changed to table naming scheme 't#'.
mysql-test/t/ps_11bugs.test:
New tests to check bug#1644 and bug#1676,
test for bug#1180 changed to table naming scheme 't#'.
mysql-test/r/ps_11bugs.result:
Expected results of the added tests for bug#1644 and bug#1676.
mysql-test/t/ps_11bugs.test:
Added tests for bug#1644 and bug#1676,
also minor comments.
- 'ps_10nestset' uses a "nested set" approach for an employee
hierarchy, then does arithmetic on the "salary" field;
(soon) to be extended by inserts / deletes which imply
mass updates on the "l"/"r" fields showing the set inclusion,
- 'ps_11bugs' will get (some of ?) those bug DB entries which
refer to prepared statements, but whose number does not appear
in a test file comment - so it will also be extended.