Commit c8f85bf2 authored by Igor Babaev's avatar Igor Babaev

mdev-9864: cleanup, re-factoring.

Added comments.
parent f33c3524
......@@ -128,6 +128,11 @@ class Item_subselect :public Item_result_field,
/* TRUE <=> The underlying SELECT is correlated w.r.t some ancestor select */
bool is_correlated;
/*
TRUE <=> the subquery contains a recursive reference in the FROM list
of one of its selects. In this case some of subquery optimization
strategies cannot be applied for the subquery;
*/
bool with_recursive_reference;
enum subs_type {UNKNOWN_SUBS, SINGLEROW_SUBS,
......
......@@ -7157,7 +7157,7 @@ ER_RECURSIVE_WITHOUT_ANCHORS
ER_UNACCEPTABLE_MUTUAL_RECURSION
eng "Unacceptable mutual recursion with anchored table '%s'"
ER_REF_TO_RECURSIVE_WITH_TABLE_IN_DERIVED
eng "Reference to recursive WITH table '%s' in materiazed derived"
eng "Reference to recursive WITH table '%s' in materialized derived"
ER_NOT_STANDARDS_COMPLIANT_RECURSIVE
eng "Restrictions imposed on recursive definitions are violated for table '%s'"
#
......
......@@ -4682,10 +4682,12 @@ class select_union_recursive :public select_union
{
public:
TABLE *incr_table;
TABLE *first_rec_table_to_update;
List<TABLE> rec_tables;
select_union_recursive(THD *thd_arg):
select_union(thd_arg), incr_table(0) {};
select_union(thd_arg),
incr_table(0), first_rec_table_to_update(0) {};
int send_data(List<Item> &items);
bool create_result_table(THD *thd, List<Item> *column_types,
......
......@@ -14,21 +14,25 @@
with_clauses_list Pointer to the first clause in the list
@details
The procedure just calls the method With_clause::check_dependencies
for each member of the given list.
For each with clause from the given list the procedure finds all
dependencies between tables defined in the clause by calling the
method With_clause::checked_dependencies.
Additionally, based on the info collected by this method the procedure
finds anchors for each recursive definition and moves them at the head
of the definition.
@retval
false on success
true on failure
*/
bool check_dependencies_in_with_clauses(THD *thd, With_clause *with_clauses_list)
bool check_dependencies_in_with_clauses(With_clause *with_clauses_list)
{
for (With_clause *with_clause= with_clauses_list;
with_clause;
with_clause= with_clause->next_with_clause)
{
if (with_clause->check_dependencies(thd))
if (with_clause->check_dependencies())
return true;
if (with_clause->check_anchors())
return true;
......@@ -43,43 +47,38 @@ bool check_dependencies_in_with_clauses(THD *thd, With_clause *with_clauses_list
Check dependencies between tables defined in this with clause
@details
The method performs the following actions for this with clause:
1. Test for definitions of the tables with the same name.
2. For each table T defined in this with clause look for tables
from the same with clause that are used in the query that
specifies T and set the dependencies of T on these tables
in dependency_map.
3. Build the transitive closure of the above direct dependencies
The method performs the following for this with clause:
- checks that there are no definitions of the tables with the same name
- for each table T defined in this with clause looks for the tables
from the same with clause that are used in the query that specifies T
and set the dependencies of T on these tables in a bitmap.
- builds the transitive closure of the above direct dependencies
to find out all recursive definitions.
4. If this with clause is not specified as recursive then
for each with table T defined in this with clause check whether
it is used in any definition that follows the definition of T.
@retval
true if an error is reported
false otherwise
*/
bool With_clause::check_dependencies(THD *thd)
bool With_clause::check_dependencies()
{
if (dependencies_are_checked)
return false;
/*
Look for for definitions with the same query name.
When found report an error and return true immediately.
For each table T defined in this with clause look for all other tables from
the same with with clause that are used in the specification of T.
For each table T defined in this with clause look for all other tables
from the same with clause that are used in the specification of T.
For each such table set the dependency bit in the dependency map of
with element for T.
the with element for T.
*/
for (With_element *with_elem= first_elem;
with_elem != NULL;
with_elem= with_elem->next_elem)
for (With_element *with_elem= with_list.first;
with_elem;
with_elem= with_elem->next)
{
for (With_element *elem= first_elem;
for (With_element *elem= with_list.first;
elem != with_elem;
elem= elem->next_elem)
elem= elem->next)
{
if (my_strcasecmp(system_charset_info, with_elem->query_name->str,
elem->query_name->str) == 0)
......@@ -88,20 +87,20 @@ bool With_clause::check_dependencies(THD *thd)
return true;
}
}
if (with_elem->check_dependencies_in_spec(thd))
if (with_elem->check_dependencies_in_spec())
return true;
}
/* Build the transitive closure of the direct dependencies found above */
for (With_element *with_elem= first_elem;
with_elem != NULL;
with_elem= with_elem->next_elem)
for (With_element *with_elem= with_list.first;
with_elem;
with_elem= with_elem->next)
with_elem->derived_dep_map= with_elem->base_dep_map;
for (With_element *with_elem= first_elem;
with_elem != NULL;
with_elem= with_elem->next_elem)
for (With_element *with_elem= with_list.first;
with_elem;
with_elem= with_elem->next)
{
table_map with_elem_map= with_elem->get_elem_map();
for (With_element *elem= first_elem; elem != NULL; elem= elem->next_elem)
for (With_element *elem= with_list.first; elem; elem= elem->next)
{
if (elem->derived_dep_map & with_elem_map)
elem->derived_dep_map |= with_elem->derived_dep_map;
......@@ -109,12 +108,12 @@ bool With_clause::check_dependencies(THD *thd)
}
/*
Mark those elements where tables are defined with direct or indirect recursion.
Report an error when recursion (direct or indirect) is used to define a table.
Mark those elements where tables are defined with direct or indirect
make recursion.
*/
for (With_element *with_elem= first_elem;
with_elem != NULL;
with_elem= with_elem->next_elem)
for (With_element *with_elem= with_list.first;
with_elem;
with_elem= with_elem->next)
{
if (with_elem->derived_dep_map & with_elem->get_elem_map())
with_elem->is_recursive= true;
......@@ -125,13 +124,35 @@ bool With_clause::check_dependencies(THD *thd)
}
/*
This structure describes an element of the stack of embedded units.
The stack is used when looking for a definition of a table in
with clauses. The definition can be found only in the scopes
of the with clauses attached to the units from the stack.
The with clauses are looked through from starting from the top
element of the stack.
*/
struct st_unit_ctxt_elem
{
st_unit_ctxt_elem *prev;
st_unit_ctxt_elem *prev; // the previous element of the stack
st_select_lex_unit *unit;
};
bool With_element::check_dependencies_in_spec(THD *thd)
/**
@brief
Find the dependencies of this element on its siblings in its specification
@details
For each table reference ref(T) from the FROM list of every select sl
immediately contained in the specification query of this element this
method searches for the definition of T in the the with clause which
this element belongs to. If such definition is found then the dependency
on it is set in sl->with_dep and in this->base_dep_map.
*/
bool With_element::check_dependencies_in_spec()
{
for (st_select_lex *sl= spec->first_select(); sl; sl= sl->next_select())
{
......@@ -144,6 +165,62 @@ bool With_element::check_dependencies_in_spec(THD *thd)
}
/**
@brief
Search for the definition of a table among the elements of this with clause
@param table The reference to the table that is looked for
@param barrier The barrier with element for the search
@details
The function looks through the elements of this with clause trying to find
the definition of the given table. When it encounters the element with
the same query name as the table's name it returns this element. If no
such definitions are found the function returns NULL.
@retval
found with element if the search succeeded
NULL - otherwise
*/
With_element *With_clause::find_table_def(TABLE_LIST *table,
With_element *barrier)
{
for (With_element *with_elem= with_list.first;
with_elem != barrier;
with_elem= with_elem->next)
{
if (my_strcasecmp(system_charset_info, with_elem->query_name->str,
table->table_name) == 0)
{
table->set_derived();
return with_elem;
}
}
return NULL;
}
/**
@brief
Search for the definition of a table in with clauses
@param tbl The reference to the table that is looked for
@param ctxt The context describing in what with clauses of the upper
levels the table has to be searched for.
@details
The function looks for the definition of the table tbl in the definitions
of the with clauses from the upper levels specified by the parameter ctxt.
When it encounters the element with the same query name as the table's name
it returns this element. If no such definitions are found the function
returns NULL.
@retval
found with element if the search succeeded
NULL - otherwise
*/
With_element *find_table_def_in_with_clauses(TABLE_LIST *tbl,
st_unit_ctxt_elem *ctxt)
{
......@@ -159,12 +236,41 @@ With_element *find_table_def_in_with_clauses(TABLE_LIST *tbl,
return tbl->with;
barrier= NULL;
if (unit->with_element && !unit->with_element->get_owner()->with_recursive)
{
/*
This unit is the specification if the with element unit->with_element.
The with element belongs to a with clause without the specifier RECURSIVE.
So when searching for the matching definition of tbl this with clause must
be looked up to this with element
*/
barrier= unit->with_element;
}
}
return NULL;
}
/**
@brief
Find the dependencies of this element on its siblings in a select
@param sl The select where to look for the dependencies
@param ctxt The structure specifying the scope of the definitions
of the with elements of the upper levels
@param in_sbq if true mark dependencies found in subqueries in
this->sq_dep_map
@param dep_map IN/OUT The bit where to mark the found dependencies
@details
For each table reference ref(T) from the FROM list of the select sl
the method searches in with clauses for the definition of the table T.
If the found definition belongs to the same with clause as this with
element then the method set dependency on T in the in/out parameter
dep_map, add if required - in this->sq_dep_map.
The parameter ctxt describes the proper context for the search
of the definition of T.
*/
void With_element::check_dependencies_in_select(st_select_lex *sl,
st_unit_ctxt_elem *ctxt,
bool in_subq,
......@@ -176,36 +282,62 @@ void With_element::check_dependencies_in_select(st_select_lex *sl,
if (tbl->derived || tbl->nested_join)
continue;
tbl->with_internal_reference_map= 0;
/*
If there is a with clause attached to the unit containing sl
look first for the definition of tbl in this with clause.
If such definition is not found there look in the with
clauses of the upper levels.
If the definition of tbl is found somewhere in with clauses
then tbl->with is set to point to this definition
*/
if (with_clause && !tbl->with)
tbl->with= with_clause->find_table_def(tbl, NULL);
if (!tbl->with)
tbl->with= find_table_def_in_with_clauses(tbl, ctxt);
if (tbl->with && tbl->with->owner== this->owner)
{
/*
The found definition T of tbl belongs to the same
with clause as this with element. In this case:
- set the dependence on T in the bitmap dep_map
- set tbl->with_internal_reference_map with
the bitmap for this definition
- set the dependence on T in the bitmap this->sq_dep_map
if needed
*/
*dep_map|= tbl->with->get_elem_map();
tbl->with_internal_reference_map= get_elem_map();
if (in_subq)
sq_dep_map|= tbl->with->get_elem_map();
}
}
/* Now look for the dependencies in the subqueries of sl */
st_select_lex_unit *inner_unit= sl->first_inner_unit();
for (; inner_unit; inner_unit= inner_unit->next_unit())
check_dependencies_in_unit(inner_unit, ctxt, in_subq, dep_map);
}
/**
/**
@brief
Check dependencies on the sibling with tables used in the given unit
Find the dependencies of this element on its siblings in a unit
@param unit The unit where the siblings are to be searched for
@param unit The unit where to look for the dependencies
@param ctxt The structure specifying the scope of the definitions
of the with elements of the upper levels
@param in_sbq if true mark dependencies found in subqueries in
this->sq_dep_map
@param dep_map IN/OUT The bit where to mark the found dependencies
@details
The method recursively looks through all from lists encountered
the given unit. If it finds a reference to a table that is
defined in the same with clause to which this element belongs
the method set the bit of dependency on this table in the
dependency_map of this element.
This method searches in the unit 'unit' for the the references in FROM
lists of all selects contained in this unit and in the with clause
attached to this unit that refer to definitions of tables from the
same with clause as this element.
If such definitions are found then the dependencies on them are
set in the in/out parameter dep_map and optionally in this->sq_dep_map.
The parameter ctxt describes the proper context for the search.
*/
void With_element::check_dependencies_in_unit(st_select_lex_unit *unit,
......@@ -224,39 +356,83 @@ void With_element::check_dependencies_in_unit(st_select_lex_unit *unit,
}
}
/**
@brief
Find the dependencies of this element on its siblings in a with clause
@param witt_clause The with clause where to look for the dependencies
@param ctxt The structure specifying the scope of the definitions
of the with elements of the upper levels
@param in_sbq if true mark dependencies found in subqueries in
this->sq_dep_map
@param dep_map IN/OUT The bit where to mark the found dependencies
@details
This method searches in the with_clause for the the references in FROM
lists of all selects contained in the specifications of the with elements
from this with_clause that refer to definitions of tables from the
same with clause as this element.
If such definitions are found then the dependencies on them are
set in the in/out parameter dep_map and optionally in this->sq_dep_map.
The parameter ctxt describes the proper context for the search.
*/
void
With_element::check_dependencies_in_with_clause(With_clause *with_clause,
st_unit_ctxt_elem *ctxt,
bool in_subq,
table_map *dep_map)
{
for (With_element *with_elem= with_clause->first_elem;
with_elem != NULL;
with_elem= with_elem->next_elem)
for (With_element *with_elem= with_clause->with_list.first;
with_elem;
with_elem= with_elem->next)
{
check_dependencies_in_unit(with_elem->spec, ctxt, in_subq, dep_map);
}
}
/**
@brief
Find mutually recursive with elements and check that they have ancors
@details
This method performs the following:
- for each recursive with element finds all mutually recursive with it
- links each group of mutually recursive with elements into a ring chain
- checks that every group of mutually recursive with elements contains
at least one anchor
- checks that after removing any with element with anchor the remaining
with elements mutually recursive with the removed one are not recursive
anymore
@retval
true if an error is reported
false otherwise
*/
bool With_clause::check_anchors()
{
/* Find mutually recursive with elements */
for (With_element *with_elem= first_elem;
with_elem != NULL;
with_elem= with_elem->next_elem)
for (With_element *with_elem= with_list.first;
with_elem;
with_elem= with_elem->next)
{
if (!with_elem->is_recursive)
continue;
/*
It with_elem is recursive with element find all elements mutually recursive
with it (any recursive element is mutually recursive with itself). Mark all
these elements in the bitmap->mutually_recursive. Also link all these
elements into a ring chain.
*/
if (!with_elem->next_mutually_recursive)
{
With_element *last_mutually_recursive= with_elem;
table_map with_elem_dep= with_elem->derived_dep_map;
table_map with_elem_map= with_elem->get_elem_map();
for (With_element *elem= with_elem;
elem != NULL;
elem= elem->next_elem)
for (With_element *elem= with_elem; elem; elem= elem->next)
{
if (!elem->is_recursive)
continue;
......@@ -277,11 +453,16 @@ bool With_clause::check_anchors()
elem->mutually_recursive= with_elem->mutually_recursive;
}
/*
For each select from the specification of 'with_elem' check whether
it is an anchor i.e. does not depend on any with elements mutually
recursive with 'with_elem".
*/
for (st_select_lex *sl= with_elem->spec->first_select();
sl;
sl= sl->next_select())
{
if (!(with_elem->mutually_recursive & sl->with_dep))
if (with_elem->is_anchor(sl))
{
with_elem->with_anchor= true;
break;
......@@ -289,15 +470,25 @@ bool With_clause::check_anchors()
}
}
for (With_element *with_elem= first_elem;
with_elem != NULL;
with_elem= with_elem->next_elem)
/*
Check that for any group of mutually recursive with elements
- there is at least one anchor
- after removing any with element with anchor the remaining with elements
mutually recursive with the removed one are not recursive anymore
*/
for (With_element *with_elem= with_list.first;
with_elem;
with_elem= with_elem->next)
{
if (!with_elem->is_recursive)
continue;
if (!with_elem->with_anchor)
{
/*
Check that the other with elements mutually recursive with 'with_elem'
contain at least one anchor.
*/
With_element *elem= with_elem;
while ((elem= elem->get_next_mutually_recursive()) != with_elem)
{
......@@ -313,7 +504,13 @@ bool With_clause::check_anchors()
}
else
{
/* 'with_elem' is a with element with an anchor */
With_element *elem= with_elem;
/*
For the other with elements mutually recursive with 'with_elem'
set dependency bits between those elements in the field work_dep_map
and build transitive closure of these dependencies
*/
while ((elem= elem->get_next_mutually_recursive()) != with_elem)
elem->work_dep_map= elem->base_dep_map & elem->mutually_recursive;
elem= with_elem;
......@@ -327,6 +524,7 @@ bool With_clause::check_anchors()
el->work_dep_map|= elem->work_dep_map;
}
}
/* If the transitive closure displays any cycle report an arror */
elem= with_elem;
while ((elem= elem->get_next_mutually_recursive()) != with_elem)
{
......@@ -346,36 +544,53 @@ bool With_clause::check_anchors()
/**
@brief
Search for the definition of a table among the elements of this with clause
@param table The reference to the table that is looked for
Move anchors at the beginning of the specifications for with elements
@details
The function looks through the elements of this with clause trying to find
the definition of the given table. When it encounters the element with
the same query name as the table's name it returns this element. If no
such definitions are found the function returns NULL.
This method moves anchors at the beginning of the specifications for
all recursive with elements.
*/
@retval
found with element if the search succeeded
NULL - otherwise
void With_clause::move_anchors_ahead()
{
for (With_element *with_elem= with_list.first;
with_elem;
with_elem= with_elem->next)
{
if (with_elem->is_recursive)
with_elem->move_anchors_ahead();
}
}
/**
@brief
Move anchors at the beginning of the specification of this with element
@details
If the specification of this with element contains anchors the method
moves them at the very beginning of the specification.
*/
With_element *With_clause::find_table_def(TABLE_LIST *table,
With_element *barrier)
void With_element::move_anchors_ahead()
{
for (With_element *with_elem= first_elem;
with_elem != barrier;
with_elem= with_elem->next_elem)
st_select_lex *next_sl;
st_select_lex *new_pos= spec->first_select();
st_select_lex *last_sl;
new_pos->linkage= UNION_TYPE;
for (st_select_lex *sl= new_pos; sl; sl= next_sl)
{
if (my_strcasecmp(system_charset_info, with_elem->query_name->str,
table->table_name) == 0)
next_sl= sl->next_select();
if (is_anchor(sl))
{
table->set_derived();
return with_elem;
sl->move_node(new_pos);
new_pos= sl->next_select();
}
last_sl= sl;
}
return NULL;
if (spec->union_distinct)
spec->union_distinct= last_sl;
first_recursive= new_pos;
}
......@@ -397,9 +612,9 @@ With_element *With_clause::find_table_def(TABLE_LIST *table,
bool With_clause::prepare_unreferenced_elements(THD *thd)
{
for (With_element *with_elem= first_elem;
with_elem != NULL;
with_elem= with_elem->next_elem)
for (With_element *with_elem= with_list.first;
with_elem;
with_elem= with_elem->next)
{
if (!with_elem->is_referenced() && with_elem->prepare_unreferenced(thd))
return true;
......@@ -418,9 +633,9 @@ bool With_clause::prepare_unreferenced_elements(THD *thd)
@param spec_end The end of the specification in the input string
@details
The method creates for a string copy of the specification used in this element.
The method is called when the element is parsed. The copy may be used to
create clones of the specification whenever they are needed.
The method creates for a string copy of the specification used in this
element. The method is called when the element is parsed. The copy may be
used to create clones of the specification whenever they are needed.
@retval
false on success
......@@ -646,41 +861,6 @@ bool With_element::prepare_unreferenced(THD *thd)
}
void With_clause::move_anchors_ahead()
{
for (With_element *with_elem= first_elem;
with_elem != NULL;
with_elem= with_elem->next_elem)
{
if (with_elem->is_recursive)
with_elem->move_anchors_ahead();
}
}
void With_element::move_anchors_ahead()
{
st_select_lex *next_sl;
st_select_lex *new_pos= spec->first_select();
st_select_lex *last_sl;
new_pos->linkage= UNION_TYPE;
for (st_select_lex *sl= new_pos; sl; sl= next_sl)
{
next_sl= sl->next_select();
if (is_anchor(sl))
{
sl->move_node(new_pos);
new_pos= sl->next_select();
}
last_sl= sl;
}
if (spec->union_distinct)
spec->union_distinct= last_sl;
first_recursive= new_pos;
}
bool With_element::is_anchor(st_select_lex *sel)
{
return !(mutually_recursive & sel->with_dep);
......@@ -694,10 +874,10 @@ bool With_element::is_anchor(st_select_lex *sel)
@param table reference to the table whose definition is searched for
@details
The method looks for the definition the table whose reference is occurred
The method looks for the definition of the table whose reference is occurred
in the FROM list of this select node. First it searches for it in the
with clause attached to the unit this select node belongs to. If such a
definition is not found there the embedding units are looked through.
definition is not found then the embedding units are looked through.
@retval
pointer to the found definition if the search has been successful
......@@ -770,6 +950,12 @@ bool TABLE_LIST::is_recursive_with_table()
}
/*
A reference to a with table T is recursive if it occurs somewhere
in the query specifying T or in the query specifying one of the tables
mutually recursive with T.
*/
bool TABLE_LIST::is_with_table_recursive_reference()
{
return (with_internal_reference_map &&
......@@ -777,12 +963,67 @@ bool TABLE_LIST::is_with_table_recursive_reference()
}
/*
Specifications of with tables with recursive table references
in non-mergeable derived tables are not allowed in this
implementation.
*/
/*
We say that the specification of a with table T is restricted
if all below is true.
1. Any immediate select of the specification contains at most one
recursive table reference taking into account table references
from mergeable derived tables.
2. Any recursive table reference is not an inner operand of an
outer join operation used in an immediate select of the
specification.
3. Any immediate select from the specification of T does not
contain aggregate functions.
4. The specification of T does not contain recursive table references.
If the specification of T is not restricted we call the corresponding
with element unrestricted.
The SQL standards allows only with elements with restricted specification.
By default we comply with the standards here.
Yet we allow unrestricted specification if the status variable
'standards_compliant_cte' set to 'off'(0).
*/
/**
@brief
Check if this select makes the including specification unrestricted
@param
only_standards_compliant true if the system variable
'standards_compliant_cte' is set to 'on'
@details
This method checks whether the conditions 1-4 (see the comment above)
are satisfied for this select. If not then mark this element as
unrestricted and report an error if 'only_standards_compliant' is true.
@retval
true if an error is reported
false otherwise
*/
bool st_select_lex::check_unrestricted_recursive(bool only_standards_compliant)
{
With_element *with_elem= get_with_element();
if (!with_elem ||!with_elem->is_recursive)
{
/*
If this select is not from the specifiocation of a with elememt or
if this not a recursive with element then there is nothing to check.
*/
return false;
}
/* Check conditions 1-2 for restricted specification*/
table_map unrestricted= 0;
table_map encountered= 0;
if (with_elem->check_unrestricted_recursive(this,
......@@ -790,10 +1031,15 @@ bool st_select_lex::check_unrestricted_recursive(bool only_standards_compliant)
encountered))
return true;
with_elem->get_owner()->add_unrestricted(unrestricted);
/* Check conditions 3-4 for restricted specification*/
if (with_sum_func ||
(with_elem->contains_sq_with_recursive_reference()))
with_elem->get_owner()->add_unrestricted(
with_elem->get_mutually_recursive());
/* Report an error on unrestricted specification if this is required */
if (only_standards_compliant && with_elem->is_unrestricted())
{
my_error(ER_NOT_STANDARDS_COMPLIANT_RECURSIVE,
......@@ -805,10 +1051,30 @@ bool st_select_lex::check_unrestricted_recursive(bool only_standards_compliant)
}
/**
@brief
Check if a select from the spec of this with element is partially restricted
@param
sel select from the specification of this element where to check
whether conditions 1-2 are satisfied
unrestricted IN/OUT bitmap where to mark unrestricted specs
encountered IN/OUT bitmap where to mark encountered recursive references
@details
This method checks whether the conditions 1-2 (see the comment above)
are satisfied for the select sel.
This method is called recursively for derived tables.
@retval
true if an error is reported
false otherwise
*/
bool With_element::check_unrestricted_recursive(st_select_lex *sel,
table_map &unrestricted,
table_map &encountered)
{
/* Check conditions 1-for restricted specification*/
List_iterator<TABLE_LIST> ti(sel->leaf_tables);
TABLE_LIST *tbl;
while ((tbl= ti++))
......@@ -843,9 +1109,9 @@ bool With_element::check_unrestricted_recursive(st_select_lex *sel,
encountered|= with_elem->get_elem_map();
}
}
for (With_element *with_elem= sel->get_with_element()->owner->first_elem;
with_elem != NULL;
with_elem= with_elem->next_elem)
for (With_element *with_elem= sel->get_with_element()->owner->with_list.first;
with_elem;
with_elem= with_elem->next)
{
if (!with_elem->is_recursive && (unrestricted & with_elem->get_elem_map()))
continue;
......@@ -870,6 +1136,9 @@ bool With_element::check_unrestricted_recursive(st_select_lex *sel,
}
}
}
/* Check conditions 2 for restricted specification*/
ti.rewind();
while ((tbl= ti++))
{
......@@ -886,7 +1155,25 @@ bool With_element::check_unrestricted_recursive(st_select_lex *sel,
}
void st_select_lex::check_subqueries_with_recursive_references()
/**
@brief
Check subqueries with recursive table references from FROM list of this select
@details
For each recursive table reference from the FROM list of this select
this method checks:
- whether this reference is within a materialized derived table and
if so it report an error
- whether this reference is within a subquery and if so it set a flag
in this subquery that disallows some optimization strategies for
this subquery.
@retval
true if an error is reported
false otherwise
*/
bool st_select_lex::check_subqueries_with_recursive_references()
{
st_select_lex_unit *sl_master= master_unit();
List_iterator<TABLE_LIST> ti(leaf_tables);
......@@ -895,15 +1182,27 @@ void st_select_lex::check_subqueries_with_recursive_references()
{
if (!(tbl->is_with_table_recursive_reference() && sl_master->item))
continue;
With_element *with_elem= tbl->with;
bool check_embedding_materialized_derived= true;
for (st_select_lex *sl= this; sl; sl= sl_master->outer_select())
{
sl_master= sl->master_unit();
if (with_elem->get_owner() == sl_master->with_clause)
check_embedding_materialized_derived= false;
if (check_embedding_materialized_derived && !sl_master->with_element &&
sl_master->derived && sl_master->derived->is_materialized_derived())
{
my_error(ER_REF_TO_RECURSIVE_WITH_TABLE_IN_DERIVED,
MYF(0), with_elem->query_name->str);
return true;
}
if (!sl_master->item)
continue;
Item_subselect *subq= (Item_subselect *) sl_master->item;
subq->with_recursive_reference= true;
}
}
return false;
}
......@@ -924,12 +1223,12 @@ void With_clause::print(String *str, enum_query_type query_type)
str->append(STRING_WITH_LEN("with "));
if (with_recursive)
str->append(STRING_WITH_LEN("recursive "));
for (With_element *with_elem= first_elem;
with_elem != NULL;
with_elem= with_elem->next_elem)
for (With_element *with_elem= with_list.first;
with_elem;
with_elem= with_elem->next)
{
with_elem->print(str, query_type);
if (with_elem != first_elem)
if (with_elem != with_list.first)
str->append(", ");
}
}
......
......@@ -21,7 +21,7 @@ class With_element : public Sql_alloc
{
private:
With_clause *owner; // with clause this object belongs to
With_element *next_elem; // next element in the with clause
With_element *next; // next element in the with clause
uint number; // number of the element in the with clause (starting from 0)
table_map elem_map; // The map where with only one 1 set in this->number
/*
......@@ -36,10 +36,22 @@ class With_element : public Sql_alloc
directly or indirectly from the i-th with element.
*/
table_map derived_dep_map;
/*
The map sq_dep_map has 1 in i-th position if there is a reference to this
with element somewhere in subqueries of the specifications of the tables
defined in the with clause containing this element;
*/
table_map sq_dep_map;
table_map work_dep_map; // dependency map used for work
/* Dependency map of with elements mutually recursive with this with element */
table_map mutually_recursive;
/*
The next with element from the circular chain of the with elements
mutually recursive with this with element.
(If This element is simply recursive than next_mutually_recursive contains
the pointer to itself. If it's not recursive than next_mutually_recursive
is set to NULL.)
*/
With_element *next_mutually_recursive;
/*
Total number of references to this element in the FROM lists of
......@@ -56,8 +68,6 @@ class With_element : public Sql_alloc
/* Return the map where 1 is set only in the position for this element */
table_map get_elem_map() { return 1 << number; }
TABLE *table;
public:
/*
The name of the table introduced by this with elememt. The name
......@@ -79,34 +89,48 @@ class With_element : public Sql_alloc
*/
bool is_recursive;
/*
Any non-recursive select in the specification of a recursive
with element is a called anchor. In the case mutually recursive
elements the specification of some them may be without any anchor.
Yet at least one of them must contain an anchor.
All anchors of any recursivespecification are moved ahead before
the prepare stage.
*/
/* Set to true if this is a recursive element with an anchor */
bool with_anchor;
/*
Set to the first recursive select of the unit specifying the element
after all anchor have been moved to the head of the unit.
*/
st_select_lex *first_recursive;
/* The number of the last performed iteration for recursive table */
/*
The number of the last performed iteration for recursive table
(the number of the initial non-recursive step is 0, the number
of the first iteration is 1).
*/
uint level;
/*
The pointer to the object used to materialize this with element
if it's recursive. This object is built at the end of prepare
stage and is used at the execution stage.
*/
select_union_recursive *rec_result;
TABLE *result_table;
TABLE *first_rec_table_to_update;
With_element(LEX_STRING *name,
List <LEX_STRING> list,
st_select_lex_unit *unit)
: next_elem(NULL), base_dep_map(0), derived_dep_map(0),
: next(NULL), base_dep_map(0), derived_dep_map(0),
sq_dep_map(0), work_dep_map(0), mutually_recursive(0),
next_mutually_recursive(NULL),
references(0), table(NULL),
next_mutually_recursive(NULL), references(0),
query_name(name), column_list(list), spec(unit),
is_recursive(false), with_anchor(false),
level(0), rec_result(NULL), result_table(NULL),
first_rec_table_to_update(NULL)
level(0), rec_result(NULL)
{}
bool check_dependencies_in_spec(THD *thd);
bool check_dependencies_in_spec();
void check_dependencies_in_select(st_select_lex *sl, st_unit_ctxt_elem *ctxt,
bool in_subq, table_map *dep_map);
......@@ -155,10 +179,6 @@ class With_element : public Sql_alloc
With_element *get_next_mutually_recursive()
{ return next_mutually_recursive; }
void set_table(TABLE *tab) { table= tab; }
TABLE *get_table() { return table; }
bool is_anchor(st_select_lex *sel);
void move_anchors_ahead();
......@@ -173,7 +193,7 @@ class With_element : public Sql_alloc
void mark_as_cleaned();
void reset_for_exec();
void reset_recursive_for_exec();
void cleanup_stabilized();
......@@ -183,8 +203,6 @@ class With_element : public Sql_alloc
bool all_are_stabilized();
void set_result_table(TABLE *tab) { result_table= tab; }
bool instantiate_tmp_tables();
void prepare_for_next_iteration();
......@@ -206,9 +224,9 @@ class With_clause : public Sql_alloc
{
private:
st_select_lex_unit *owner; // the unit this with clause attached to
With_element *first_elem; // the first definition in this with clause
With_element **last_next; // here is set the link for the next added element
uint elements; // number of the elements/defintions in this with clauses
/* The list of all with elements from this with clause */
SQL_I_List<With_element> with_list;
/*
The with clause immediately containing this with clause if there is any,
otherwise NULL. Now used only at parsing.
......@@ -222,9 +240,22 @@ class With_clause : public Sql_alloc
/* Set to true if dependencies between with elements have been checked */
bool dependencies_are_checked;
/*
The bitmap of all recursive with elements whose specifications
are not complied with restrictions imposed by the SQL standards
on recursive specifications.
*/
table_map unrestricted;
/*
The bitmap of all recursive with elements whose anchors
has been already prepared.
*/
table_map with_prepared_anchor;
table_map cleaned;
/*
The bitmap of all recursive with elements that
has been already materialized
*/
table_map stabilized;
public:
......@@ -232,23 +263,20 @@ class With_clause : public Sql_alloc
bool with_recursive;
With_clause(bool recursive_fl, With_clause *emb_with_clause)
: owner(NULL), first_elem(NULL), elements(0),
: owner(NULL),
embedding_with_clause(emb_with_clause), next_with_clause(NULL),
dependencies_are_checked(false),
unrestricted(0), with_prepared_anchor(0), cleaned(0),
stabilized(0),
dependencies_are_checked(false), unrestricted(0),
with_prepared_anchor(0), cleaned(0), stabilized(0),
with_recursive(recursive_fl)
{ last_next= &first_elem; }
{ }
/* Add a new element to the current with clause */
bool add_with_element(With_element *elem)
{
elem->owner= this;
elem->number= elements;
elem->number= with_list.elements;
elem->spec->with_element= elem;
*last_next= elem;
last_next= &elem->next_elem;
elements++;
with_list.link_in_list(elem, &elem->next);
return false;
}
......@@ -263,7 +291,7 @@ class With_clause : public Sql_alloc
With_clause *pop() { return embedding_with_clause; }
bool check_dependencies(THD *thd);
bool check_dependencies();
bool check_anchors();
......@@ -283,7 +311,7 @@ class With_clause : public Sql_alloc
friend
bool
check_dependencies_in_with_clauses(THD *thd, With_clause *with_clauses_list);
check_dependencies_in_with_clauses(With_clause *with_clauses_list);
};
inline
......@@ -321,16 +349,18 @@ void With_element::mark_as_cleaned()
inline
void With_element::reset_for_exec()
void With_element::reset_recursive_for_exec()
{
DBUG_ASSERT(is_recursive);
level= 0;
owner->with_prepared_anchor&= ~mutually_recursive;
owner->cleaned&= ~get_elem_map();
first_rec_table_to_update= NULL;
cleanup_stabilized();
rec_result->first_rec_table_to_update= 0;
}
inline
void With_element::cleanup_stabilized()
{
......@@ -365,7 +395,7 @@ void With_element::prepare_for_next_iteration()
With_element *with_elem= this;
while ((with_elem= with_elem->get_next_mutually_recursive()) != this)
{
TABLE *rec_table= with_elem->first_rec_table_to_update;
TABLE *rec_table= with_elem->rec_result->first_rec_table_to_update;
if (rec_table)
rec_table->reginfo.join_tab->preread_init_done= false;
}
......
......@@ -1109,7 +1109,7 @@ class st_select_lex: public st_select_lex_node
}
With_element *find_table_def_in_with_clauses(TABLE_LIST *table);
bool check_unrestricted_recursive(bool only_standards_compliant);
void check_subqueries_with_recursive_references();
bool check_subqueries_with_recursive_references();
List<Window_spec> window_specs;
void prepare_add_window_spec(THD *thd);
......@@ -2475,7 +2475,7 @@ struct LEX: public Query_tables_list
SELECT_LEX *all_selects_list;
/* current with clause in parsing if any, otherwise 0*/
With_clause *curr_with_clause;
/* pointer to the first with clause in the current statemant */
/* pointer to the first with clause in the current statement */
With_clause *with_clauses_list;
/*
(*with_clauses_list_last_next) contains a pointer to the last
......
......@@ -6233,7 +6233,7 @@ static bool execute_sqlcom_select(THD *thd, TABLE_LIST *all_tables)
new (thd->mem_root) Item_int(thd,
(ulonglong) thd->variables.select_limit);
}
if (check_dependencies_in_with_clauses(thd, lex->with_clauses_list))
if (check_dependencies_in_with_clauses(lex->with_clauses_list))
return 1;
if (!(res= open_and_lock_tables(thd, all_tables, TRUE, 0)))
......
......@@ -1509,7 +1509,7 @@ static int mysql_test_select(Prepared_statement *stmt,
lex->select_lex.context.resolve_in_select_list= TRUE;
ulong privilege= lex->exchange ? SELECT_ACL | FILE_ACL : SELECT_ACL;
if (check_dependencies_in_with_clauses(thd,lex->with_clauses_list))
if (check_dependencies_in_with_clauses(lex->with_clauses_list))
goto error;
if (tables)
{
......
......@@ -1231,8 +1231,8 @@ bool st_select_lex_unit::exec_recursive()
{
saved_error=
incr_table->insert_all_rows_into(thd, rec_table, !is_unrestricted);
if (!with_element->first_rec_table_to_update)
with_element->first_rec_table_to_update= rec_table;
if (!with_element->rec_result->first_rec_table_to_update)
with_element->rec_result->first_rec_table_to_update= rec_table;
if (with_element->level == 1)
rec_table->reginfo.join_tab->preread_init_done= true;
}
......@@ -1258,13 +1258,6 @@ bool st_select_lex_unit::cleanup()
for (SELECT_LEX *sl= first_select(); sl; sl= sl->next_select())
error|= sl->cleanup();
if (union_result && with_element && with_element->is_recursive)
{
((select_union_recursive *) union_result)->cleanup();
delete union_result;
union_result= 0;
}
if (fake_select_lex)
{
error|= fake_select_lex->cleanup();
......@@ -1289,9 +1282,18 @@ bool st_select_lex_unit::cleanup()
}
if (with_element && with_element->is_recursive)
{
if (union_result )
{
((select_union_recursive *) union_result)->cleanup();
delete union_result;
union_result= 0;
}
with_element->mark_as_cleaned();
if (union_result && !(with_element &&with_element->is_recursive))
}
else
{
if (union_result)
{
delete union_result;
union_result=0; // Safety
......@@ -1299,6 +1301,7 @@ bool st_select_lex_unit::cleanup()
free_tmp_table(thd, table);
table= 0; // Safety
}
}
DBUG_RETURN(error);
}
......@@ -1325,7 +1328,7 @@ void st_select_lex_unit::reinit_exec_mechanism()
}
#endif
if (with_element && with_element->is_recursive)
with_element->reset_for_exec();
with_element->reset_recursive_for_exec();
}
......
......@@ -430,7 +430,7 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views,
lex->link_first_table_back(view, link_to_local);
view->open_type= OT_BASE_ONLY;
if (check_dependencies_in_with_clauses(thd, lex->with_clauses_list))
if (check_dependencies_in_with_clauses(lex->with_clauses_list))
{
res= TRUE;
goto err;
......@@ -1390,7 +1390,7 @@ bool mysql_make_view(THD *thd, TABLE_SHARE *share, TABLE_LIST *table,
TABLE_LIST *tbl;
Security_context *security_ctx= 0;
if (check_dependencies_in_with_clauses(thd, thd->lex->with_clauses_list))
if (check_dependencies_in_with_clauses(thd->lex->with_clauses_list))
goto err;
/*
......
......@@ -1857,7 +1857,8 @@ struct TABLE_LIST
derived tables. Use TABLE_LIST::is_anonymous_derived_table().
*/
st_select_lex_unit *derived; /* SELECT_LEX_UNIT of derived table */
With_element *with; /* With element of with_table */
With_element *with; /* With element defining this table (if any) */
/* Bitmap of the defining with element */
table_map with_internal_reference_map;
bool block_handle_derived;
ST_SCHEMA_TABLE *schema_table; /* Information_schema table */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment