Commit graph

36 commits

Author SHA1 Message Date
Sergei Golubchik
213f1c76a0 5.3->5.5 merge 2013-02-28 22:47:29 +01:00
Igor Babaev
c9b63e6a49 Fixed bug mdev-3913.
The wrong result set returned by the left join query  from
the bug test case happened due to several inconsistencies 
and bugs of the legacy mysql code.

The bug test case uses an execution plan that employs a scan
of a materialized IN subquery from the WHERE condition.
When materializing such an IN- subquery the optimizer injects
additional equalities  into the WHERE clause. These equalities
express the constraints imposed by the subquery predicate.
The injected equality of the query in the  test case happens
to belong to the same equality class, and a new equality 
imposing a condition on the rows of the materialized subquery
is inferred from this class. Simultaneously the multiple
equality is added to the ON expression of the LEFT JOIN
used in the main query.
  
The inferred equality of the form f1=f2 is taken into account
when optimizing the scan of  the rows the temporary table 
that is the result of the subquery materialization: only the 
values of the field f1 are read from the table into the record 
buffer. Meanwhile the inferred equality is removed from the
WHERE conditions altogether as a constraint on the fields
of the temporary table that has been used when filling this table. 
This equality is supposed to be removed from the ON expression
when the multiple equalities of the ON expression are converted
into an optimal set of equality predicates. It supposed to be
removed from the ON expression as an equality inferred from only
equalities of the WHERE condition. Yet, it did not happened
due to the following bug in the code.

Erroneously the code tried to build multiple equality for ON
expression twice: the first time, when it called optimize_cond()
for the WHERE condition, the second time, when it called
this function for the HAVING condition. When executing
optimize_con() for the WHERE condition  a reference
to the multiple equality of the WHERE condition is set
in the multiple equality of the  ON expression. This reference
would allow later to convert multiple equalities of the
ON expression into equality predicates. However the 
the second call of build_equal_items() for the ON expression
that happened when optimize_cond() was called for the
HAVING condition reset this reference to NULL.

This bug fix blocks calling build_equal_items() for ON
expressions for the second time. In general, it will be
beneficial for many queries as it removes from ON 
expressions any equalities that are to be checked for the
WHERE condition.
The patch also fixes two bugs in the list manipulation
operations and a bug in the function  
substitute_for_best_equal_field() that resulted
in passing wrong reference to the multiple equalities
of where conditions when processing multiple
equalities  of ON expressions.

The code of substitute_for_best_equal_field() and
the code the helper function eliminate_item_equal()
were also streamlined and cleaned up.
Now the conversion of the multiple equalities into
an optimal set of equality predicates first produces
the sequence of the all equalities processing multiple
equalities one by one, and, only after this, it inserts
the equalities at the beginning of the other conditions.

The multiple changes in the output of EXPLAIN
EXTENDED are mainly the result of this streamlining,
but in some cases is the result of the removal of
unneeded equalities from ON expressions. In
some test cases this removal were reflected in the
output of EXPLAIN resulted in disappearance of 
“Using where” in some rows of the execution plans.
2013-02-20 18:01:36 -08:00
unknown
80b3f74705 Fix bug mdev-447: Wrong output from the EXPLAIN command of the test case for lp bug #714999
The fix backports from MWL#182: Explain running statements the logic that
saves the original JOIN_TAB array of a query plan after optimization. This
array is later used during EXPLAIN to iterate over the original JOIN plan
nodes in the cases when this plan could be changed by early subquery
execution during the optimization phase of the outer query.
2012-08-21 15:24:43 +03:00
Sergey Petrunya
55597a4869 MDEV-410: EXPLAIN shows type=range, while SHOW EXPLAIN and userstat show full table scan is used
- Make Item_subselect::fix_fields() ignore UNCACHEABLE_EXPLAIN flag when deciding whether 
  the subquery item should be marked as constant.
2012-07-25 20:41:48 +04:00
Sergey Petrunya
8c762965d3 Merge 5.3 -> 5.5 2012-06-20 15:01:28 +04:00
unknown
f1ab00891a Fixed bug lp:1000649
Analysis:

When the method JOIN::choose_subquery_plan() decided to apply
the IN-TO-EXISTS strategy, it set the unit and select_lex
uncacheable flag to UNCACHEABLE_DEPENDENT_INJECTED unconditionally.
As result, even if IN-TO-EXISTS injected non-correlated predicates,
the subquery was still treated as correlated.

Solution:
Set the subquery as correlated only if the injected predicate(s) depend
on the outer query.
2012-06-05 17:25:10 +03:00
unknown
941018f8d1 Patch for mdev-287: CHEAP SQ: A query with subquery in SELECT list, EXISTS, inner joins takes hundreds times longer
Analysis:

The fix for lp:944706 introduces early subquery optimization.
While a subquery is being optimized some of its predicates may be
removed. In the test case, the EXISTS subquery is constant, and is
evaluated to TRUE. As a result the whole OR is TRUE, and thus the
correlated condition "b = alias1.b" is optimized away. The subquery
becomes non-correlated.

The subquery cache is designed to work only for correlated subqueries.
If constant subquery optimization is disallowed, then the constant
subquery is not evaluated, the subquery remains correlated, and its
execution is cached. As a result execution is fast.

However, when the constant subquery was optimized away, it was neither
cached by the subquery cache, nor it was cached by the internal subquery
caching. The latter was due to the fact that the subquery still appeared
as correlated to the subselect_XYZ_engine::exec methods, and they
re-executed the subquery on each call to Item_subselect::exec.

Solution:

The solution is to update the correlated status of the subquery after it has
been optimized. This status consists of:
- st_select_lex::is_correlated
- Item_subselect::is_correlated
- SELECT_LEX::uncacheable
- SELECT_LEX_UNIT::uncacheable
The status is updated by st_select_lex::update_correlated_cache(), and its
caller st_select_lex::optimize_unflattened_subqueries. The solution relies
on the fact that the optimizer already called
st_select_lex::update_used_tables() for each subquery. This allows to
efficiently update the correlated status of each subquery without walking
the whole subquery tree.

Notice that his patch is an improvement over MySQL 5.6 and older, where
subqueries are not pre-optimized, and the above analysis is not possible.
2012-05-30 00:18:53 +03:00
unknown
da5214831d Fix for bug lp:944706, task MDEV-193
The patch enables back constant subquery execution during
query optimization after it was disabled during the development
of MWL#89 (cost-based choice of IN-TO-EXISTS vs MATERIALIZATION).

The main idea is that constant subqueries are allowed to be executed
during optimization if their execution is not expensive.

The approach is as follows:
- Constant subqueries are recursively optimized in the beginning of
  JOIN::optimize of the outer query. This is done by the new method
  JOIN::optimize_constant_subqueries(). This is done so that the cost
  of executing these queries can be estimated.
- Optimization of the outer query proceeds normally. During this phase
  the optimizer may request execution of non-expensive constant subqueries.
  Each place where the optimizer may potentially execute an expensive
  expression is guarded with the predicate Item::is_expensive().
- The implementation of Item_subselect::is_expensive has been extended
  to use the number of examined rows (estimated by the optimizer) as a
  way to determine whether the subquery is expensive or not.
- The new system variable "expensive_subquery_limit" controls how many
  examined rows are considered to be not expensive. The default is 100.

In addition, multiple changes were needed to make this solution work
in the light of the changes made by MWL#89. These changes were needed
to fix various crashes and wrong results, and legacy bugs discovered
during development.
2012-05-17 13:46:05 +03:00
Sergei Golubchik
4f435bddfd 5.3 merge 2012-01-13 15:50:02 +01:00
unknown
072073c09e Backport of WL#5953 from MySQL 5.6
The patch differs from the original MySQL patch as follows:
- All test case differences have been reviewed one by one, and
  care has been taken to restore the original plan so that each
  test case executes the code path it was designed for.
- A bug was found and fixed in MariaDB 5.3 in
  Item_allany_subselect::cleanup().
- ORDER BY is not removed because we are unsure of all effects,
  and it would prevent enabling ORDER BY ... LIMIT subqueries.
- ref_pointer_array.m_size is not adjusted because we don't do
  array bounds checking, and because it looks risky.

Original comment by Jorgen Loland:
-------------------------------------------------------------
WL#5953 - Optimize away useless subquery clauses
      
For IN/ALL/ANY/SOME/EXISTS subqueries, the following clauses are 
meaningless:
      
* ORDER BY (since we don't support LIMIT in these subqueries)
* DISTINCT
* GROUP BY if there is no HAVING clause and no aggregate 
  functions
      
This WL detects and optimizes away these useless parts of the
query during JOIN::prepare()
2011-12-19 23:05:44 +02:00
Sergey Petrunya
255fd6c929 Make subquery Materialization, as well as semi-join Materialization be shown
in EXPLAIN as select_type==MATERIALIZED. 

Before, we had select_type==SUBQUERY and it was difficult to tell materialized
subqueries from uncorrelated scalar-context subqueries.
2011-12-05 01:31:42 +04:00
Sergei Golubchik
effed09bd7 5.3->5.5 merge 2011-11-27 17:46:20 +01:00
Igor Babaev
17b4e4a194 Set new default values for the optimizer switch flags 'derived_merge'
and 'derived_with_keys'. Now they are set on by default.
2011-11-26 14:23:00 -08:00
Sergei Golubchik
d2755a2c9c 5.3->5.5 merge 2011-11-22 18:04:38 +01:00
unknown
eabcd6205c Merge default materialization=on. 2011-11-22 12:06:46 +02:00
Igor Babaev
b2e5a3f603 Fixed LP bug #887496.
This bug in the function Loose_scan_opt::check_ref_access_part1 could lead
to choosing an invalid execution plan employing a loose scan access to a
semi-join table even in the cases when such access could not be used at all.
This could result in wrong answers for some queries with IN subqueries.
2011-11-21 09:06:35 -08:00
unknown
e9a6502f26 Merge enabling materialization=on by default. 2011-11-21 17:48:25 +02:00
Igor Babaev
af3d1da31d Made the optimizer switch for index condition pushdown set to 'on' by default. 2011-11-21 05:16:16 -08:00
unknown
511459bd14 Enable subquery materialization=ON by default. 2011-11-09 15:36:25 +02:00
Sergei Golubchik
76f0b94bb0 merge with 5.3
sql/sql_insert.cc:
  CREATE ... IF NOT EXISTS may do nothing, but
  it is still not a failure. don't forget to my_ok it.
  ******
  CREATE ... IF NOT EXISTS may do nothing, but
  it is still not a failure. don't forget to my_ok it.
sql/sql_table.cc:
  small cleanup
  ******
  small cleanup
2011-10-19 21:45:18 +02:00
unknown
54caeee5d6 Making subquery cache on by default. 2011-10-05 18:18:00 +03:00
Igor Babaev
e1194ad63b Merge. 2011-10-03 15:50:42 -07:00
unknown
ada0850c04 Fix bug lp:858038
Analysis:
The cause of the bug was the changed meaning of
subselect_partial_match_engine::has_covering_null_row.
Previously it meant that there is row with NULLs in
all nullable fields of the materialized subquery table.
Later it was changed to mean a row with NULLs in all
fields of this table.

At the same time there was a shortcut in
subselect_rowid_merge_engine::partial_match() that
detected a special case where:
- there is no match in any of the columns with NULLs, and
- there is no NULL-only row that covers all columns with
  NULLs.
With the change in the meaning of has_covering_null_row,
the condition that detected this special case was incomplete.
This resulted in an incorrect FALSE, when the result was a
partial match.

Solution:
Expand the condition that detected the special case with the
correct test for the existence of a row with NULL values in
all columns that contain NULLs (a kind of parially covering
NULL-row).
2011-10-03 22:48:15 +03:00
Igor Babaev
715dc5f99d Fixed a cost estimation bug introduced into in the function best_access_path
of the 5.3 code line after a merge with 5.2 on 2010-10-28
in order not to allow the cost to access a joined table to be equal
to 0 ever.

Expanded data sets for many test cases to get the same execution plans
as before.
2011-09-30 18:55:02 -07:00
unknown
ea8aa32909 Fix for bug lp:834492
Analysis:
In the test query semi-join merges the inner-most subquery
into the outer subquery, and the optimization of the merged
subquery finds some new index access methods. Later the
IN-EXISTS transformation is applied to the unmerged subquery.
Since the optimizer is instructed to not consider
materialization, it reoptimizes the plan in-place to take into
account the new IN-EXISTS conditions. Just before reoptimization
JOIN::choose_subquery_plan resets the query plan, which also
resets the access methods found during the semi-join merge.
Then reoptimization discovers there are no new access methods,
but it leaves the query plan in its reset state. Later semi-join
crashes because it assumes these access methods are present.

Solution:
When reoptimizing in-place, reset the query plan only after new
access methods were discovered. If no new access methods were
discovered, leave the current plan as it was.
2011-09-01 23:53:12 +03:00
Igor Babaev
2092436457 Merge. 2011-07-21 15:55:08 -07:00
Igor Babaev
63abf00a62 Made the optimizer switches 'derived_merge' and 'derived_with_keys'
off by default.
2011-07-21 14:23:08 -07:00
unknown
ef2b4b14e1 Merge from 5.2 2011-07-21 15:50:25 +03:00
unknown
af284b55f0 Make subquery cache off by default.
mysql-test/r/subselect_scache.result:
  Test with subquery cache on.
mysql-test/t/subselect_scache.test:
  Test with subquery cache on.
2011-07-15 11:36:36 +03:00
Igor Babaev
a853c7dd18 Merge. 2011-06-28 19:56:30 -07:00
unknown
78fa331eaa Fixed bug lp:800679
Analysis:
  The failed assert ensured that the choice of subquery strategy
  is performed only for queries with at least one table. If there
  is a LIMIT 0 clause all tables are removed, and the subquery is
  neither optimized, nor executed during actual optimization. However,
  if the query is EXPLAIN-ed, the EXPLAIN execution path doesn't remove
  the query tables if there is a LIMIT 0 clause. As a result, the
  subquery optimization code is called, which violates the ASSERT
  condition.
  
Solution:
  Transform the assert into a condition, and if the outer query
  has no tables assume that there will be at most one subquery
  execution.
  
  There is potentially a better solution by reengineering the
  EXPLAIN/optimize code, so that subquery optimization is not
  done if not needed. Such a solution would be a lot bigger and
  more complex than a bug fix.
2011-06-28 15:48:44 +03:00
Igor Babaev
f03a3ee54f Merged the code of mwl 106 into the latest 5.3 with mwl 90 pushed.
Resolved all conflicts and failures.
2011-06-04 19:56:06 -07:00
unknown
742dfc92a2 MWL#89: Address review feedback (by Sergey Petrunia)
mysql-test/r/subselect4.result:
  Moved test case for LP BUG#718593 into the correct test file subselect_mat_cost_bugs.test.
mysql-test/t/subselect4.test:
  Moved test case for LP BUG#718593 into the correct test file subselect_mat_cost_bugs.test.
2011-05-23 10:56:05 +03:00
Igor Babaev
704f97035f Merged the code of MWL#106 into 5.3
Resolved all conflicts, bad merges and fixed a few minor bugs in the code.
Commented out the queries from multi_update, view, subselect_sj, func_str,
derived_view, view_grant that failed either with crashes in ps-protocol or
with wrong results.
The failures are clear indications of some bugs in the code and these bugs
are to be fixed.
2011-05-16 22:39:43 -07:00
unknown
0f4236659c Fix LP BUG#718593
Analysis:
Build_equal_items_for_cond() rewrites the WHERE clause in such a way,
that it may merge the list join->cond_equal->current_level with the
list of child Items in an AND condition of the WHERE clause.

The place where this is done is:
static COND *build_equal_items_for_cond(THD *thd, COND *cond,
                                        COND_EQUAL *inherited)
{
  ...
      if (and_level)
    {
      args->concat(&eq_list);
      args->concat((List<Item> *)&cond_equal.current_level);
    }
  ...
}

As a result, later transformations on the WHERE clause may change the
structure of the list join->cond_equal->current_level without knowing this.

Specifically in this bug, Item_in_subselect::inject_in_to_exists_cond
creates a new AND of the old WHERE clause and the IN->EXISTS conditions.
It then calls fix_fields() for the new AND. Among other things, fix_fields
flattens all nested ANDs into one by merging the AND argument lists.

When there is a cond_equal for the JOIN, its list of Item_equal objects
is attached to the end of the original AND. When a lower-level AND is
merged into the top-level one, the argument list of the lower-level AND
is concatenated to the list of multiple equalities in the upper-level AND.

As a result, when substitute_for_best_equal_field processes the 
multiple equalities, it turns out that the multiple equality list contains
the Items from the lower-level AND which were concatenated to the end of
the join->cond_equal->current_level list. This results in a crash because
this list must not contain any other Items except for the previously found
Item_equal ones.

Solution:
When performing IN->EXIST predicate injection, and the where clause is an
AND, detach the list of Item_equal objects before calling fix_fields on
the injected where clause.

After fix_fields is done, reattach back the multiple equalities list to
the end of the argument list of the new AND.
2011-04-28 17:15:05 +03:00
unknown
759d71eba1 MWL#89
Split the tests for MWL#89 into two parts - one for bugs
(currently active), and one for functionality tets
(currently in progress, and thus disabled).

Disable the test for LP BUG#718593.
2011-02-22 12:44:58 +02:00