mirror of
https://github.com/MariaDB/server.git
synced 2025-08-08 11:22:35 +03:00
mdev-9864: cleanup, re-factoring.
Added comments.
This commit is contained in:
@@ -128,6 +128,11 @@ public:
|
|||||||
/* TRUE <=> The underlying SELECT is correlated w.r.t some ancestor select */
|
/* TRUE <=> The underlying SELECT is correlated w.r.t some ancestor select */
|
||||||
bool is_correlated;
|
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;
|
bool with_recursive_reference;
|
||||||
|
|
||||||
enum subs_type {UNKNOWN_SUBS, SINGLEROW_SUBS,
|
enum subs_type {UNKNOWN_SUBS, SINGLEROW_SUBS,
|
||||||
|
@@ -7157,7 +7157,7 @@ ER_RECURSIVE_WITHOUT_ANCHORS
|
|||||||
ER_UNACCEPTABLE_MUTUAL_RECURSION
|
ER_UNACCEPTABLE_MUTUAL_RECURSION
|
||||||
eng "Unacceptable mutual recursion with anchored table '%s'"
|
eng "Unacceptable mutual recursion with anchored table '%s'"
|
||||||
ER_REF_TO_RECURSIVE_WITH_TABLE_IN_DERIVED
|
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
|
ER_NOT_STANDARDS_COMPLIANT_RECURSIVE
|
||||||
eng "Restrictions imposed on recursive definitions are violated for table '%s'"
|
eng "Restrictions imposed on recursive definitions are violated for table '%s'"
|
||||||
#
|
#
|
||||||
|
@@ -4682,10 +4682,12 @@ class select_union_recursive :public select_union
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
TABLE *incr_table;
|
TABLE *incr_table;
|
||||||
|
TABLE *first_rec_table_to_update;
|
||||||
List<TABLE> rec_tables;
|
List<TABLE> rec_tables;
|
||||||
|
|
||||||
select_union_recursive(THD *thd_arg):
|
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);
|
int send_data(List<Item> &items);
|
||||||
bool create_result_table(THD *thd, List<Item> *column_types,
|
bool create_result_table(THD *thd, List<Item> *column_types,
|
||||||
|
571
sql/sql_cte.cc
571
sql/sql_cte.cc
@@ -14,21 +14,25 @@
|
|||||||
with_clauses_list Pointer to the first clause in the list
|
with_clauses_list Pointer to the first clause in the list
|
||||||
|
|
||||||
@details
|
@details
|
||||||
The procedure just calls the method With_clause::check_dependencies
|
For each with clause from the given list the procedure finds all
|
||||||
for each member of the given list.
|
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
|
@retval
|
||||||
false on success
|
false on success
|
||||||
true on failure
|
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;
|
for (With_clause *with_clause= with_clauses_list;
|
||||||
with_clause;
|
with_clause;
|
||||||
with_clause= with_clause->next_with_clause)
|
with_clause= with_clause->next_with_clause)
|
||||||
{
|
{
|
||||||
if (with_clause->check_dependencies(thd))
|
if (with_clause->check_dependencies())
|
||||||
return true;
|
return true;
|
||||||
if (with_clause->check_anchors())
|
if (with_clause->check_anchors())
|
||||||
return true;
|
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
|
Check dependencies between tables defined in this with clause
|
||||||
|
|
||||||
@details
|
@details
|
||||||
The method performs the following actions for this with clause:
|
The method performs the following for this with clause:
|
||||||
|
- checks that there are no definitions of the tables with the same name
|
||||||
1. Test for definitions of the tables with the same name.
|
- for each table T defined in this with clause looks for the tables
|
||||||
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
|
||||||
from the same with clause that are used in the query that
|
and set the dependencies of T on these tables in a bitmap.
|
||||||
specifies T and set the dependencies of T on these tables
|
- builds the transitive closure of the above direct dependencies
|
||||||
in dependency_map.
|
to find out all recursive definitions.
|
||||||
3. Build 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
|
@retval
|
||||||
true if an error is reported
|
true if an error is reported
|
||||||
false otherwise
|
false otherwise
|
||||||
*/
|
*/
|
||||||
|
|
||||||
bool With_clause::check_dependencies(THD *thd)
|
bool With_clause::check_dependencies()
|
||||||
{
|
{
|
||||||
if (dependencies_are_checked)
|
if (dependencies_are_checked)
|
||||||
return false;
|
return false;
|
||||||
/*
|
/*
|
||||||
Look for for definitions with the same query name.
|
Look for for definitions with the same query name.
|
||||||
When found report an error and return true immediately.
|
When found report an error and return true immediately.
|
||||||
For each table T defined in this with clause look for all other tables from
|
For each table T defined in this with clause look for all other tables
|
||||||
the same with with clause that are used in the specification of T.
|
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
|
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;
|
for (With_element *with_elem= with_list.first;
|
||||||
with_elem != NULL;
|
with_elem;
|
||||||
with_elem= with_elem->next_elem)
|
with_elem= with_elem->next)
|
||||||
{
|
{
|
||||||
for (With_element *elem= first_elem;
|
for (With_element *elem= with_list.first;
|
||||||
elem != with_elem;
|
elem != with_elem;
|
||||||
elem= elem->next_elem)
|
elem= elem->next)
|
||||||
{
|
{
|
||||||
if (my_strcasecmp(system_charset_info, with_elem->query_name->str,
|
if (my_strcasecmp(system_charset_info, with_elem->query_name->str,
|
||||||
elem->query_name->str) == 0)
|
elem->query_name->str) == 0)
|
||||||
@@ -88,20 +87,20 @@ bool With_clause::check_dependencies(THD *thd)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (with_elem->check_dependencies_in_spec(thd))
|
if (with_elem->check_dependencies_in_spec())
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
/* Build the transitive closure of the direct dependencies found above */
|
/* Build the transitive closure of the direct dependencies found above */
|
||||||
for (With_element *with_elem= first_elem;
|
for (With_element *with_elem= with_list.first;
|
||||||
with_elem != NULL;
|
with_elem;
|
||||||
with_elem= with_elem->next_elem)
|
with_elem= with_elem->next)
|
||||||
with_elem->derived_dep_map= with_elem->base_dep_map;
|
with_elem->derived_dep_map= with_elem->base_dep_map;
|
||||||
for (With_element *with_elem= first_elem;
|
for (With_element *with_elem= with_list.first;
|
||||||
with_elem != NULL;
|
with_elem;
|
||||||
with_elem= with_elem->next_elem)
|
with_elem= with_elem->next)
|
||||||
{
|
{
|
||||||
table_map with_elem_map= with_elem->get_elem_map();
|
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)
|
if (elem->derived_dep_map & with_elem_map)
|
||||||
elem->derived_dep_map |= with_elem->derived_dep_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.
|
Mark those elements where tables are defined with direct or indirect
|
||||||
Report an error when recursion (direct or indirect) is used to define a table.
|
make recursion.
|
||||||
*/
|
*/
|
||||||
for (With_element *with_elem= first_elem;
|
for (With_element *with_elem= with_list.first;
|
||||||
with_elem != NULL;
|
with_elem;
|
||||||
with_elem= with_elem->next_elem)
|
with_elem= with_elem->next)
|
||||||
{
|
{
|
||||||
if (with_elem->derived_dep_map & with_elem->get_elem_map())
|
if (with_elem->derived_dep_map & with_elem->get_elem_map())
|
||||||
with_elem->is_recursive= true;
|
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
|
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;
|
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())
|
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,
|
With_element *find_table_def_in_with_clauses(TABLE_LIST *tbl,
|
||||||
st_unit_ctxt_elem *ctxt)
|
st_unit_ctxt_elem *ctxt)
|
||||||
{
|
{
|
||||||
@@ -159,12 +236,41 @@ With_element *find_table_def_in_with_clauses(TABLE_LIST *tbl,
|
|||||||
return tbl->with;
|
return tbl->with;
|
||||||
barrier= NULL;
|
barrier= NULL;
|
||||||
if (unit->with_element && !unit->with_element->get_owner()->with_recursive)
|
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;
|
barrier= unit->with_element;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return NULL;
|
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,
|
void With_element::check_dependencies_in_select(st_select_lex *sl,
|
||||||
st_unit_ctxt_elem *ctxt,
|
st_unit_ctxt_elem *ctxt,
|
||||||
bool in_subq,
|
bool in_subq,
|
||||||
@@ -176,36 +282,62 @@ void With_element::check_dependencies_in_select(st_select_lex *sl,
|
|||||||
if (tbl->derived || tbl->nested_join)
|
if (tbl->derived || tbl->nested_join)
|
||||||
continue;
|
continue;
|
||||||
tbl->with_internal_reference_map= 0;
|
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)
|
if (with_clause && !tbl->with)
|
||||||
tbl->with= with_clause->find_table_def(tbl, NULL);
|
tbl->with= with_clause->find_table_def(tbl, NULL);
|
||||||
if (!tbl->with)
|
if (!tbl->with)
|
||||||
tbl->with= find_table_def_in_with_clauses(tbl, ctxt);
|
tbl->with= find_table_def_in_with_clauses(tbl, ctxt);
|
||||||
|
|
||||||
if (tbl->with && tbl->with->owner== this->owner)
|
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();
|
*dep_map|= tbl->with->get_elem_map();
|
||||||
tbl->with_internal_reference_map= get_elem_map();
|
tbl->with_internal_reference_map= get_elem_map();
|
||||||
if (in_subq)
|
if (in_subq)
|
||||||
sq_dep_map|= tbl->with->get_elem_map();
|
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();
|
st_select_lex_unit *inner_unit= sl->first_inner_unit();
|
||||||
for (; inner_unit; inner_unit= inner_unit->next_unit())
|
for (; inner_unit; inner_unit= inner_unit->next_unit())
|
||||||
check_dependencies_in_unit(inner_unit, ctxt, in_subq, dep_map);
|
check_dependencies_in_unit(inner_unit, ctxt, in_subq, dep_map);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@brief
|
@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
|
@details
|
||||||
The method recursively looks through all from lists encountered
|
This method searches in the unit 'unit' for the the references in FROM
|
||||||
the given unit. If it finds a reference to a table that is
|
lists of all selects contained in this unit and in the with clause
|
||||||
defined in the same with clause to which this element belongs
|
attached to this unit that refer to definitions of tables from the
|
||||||
the method set the bit of dependency on this table in the
|
same with clause as this element.
|
||||||
dependency_map of 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,
|
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
|
void
|
||||||
With_element::check_dependencies_in_with_clause(With_clause *with_clause,
|
With_element::check_dependencies_in_with_clause(With_clause *with_clause,
|
||||||
st_unit_ctxt_elem *ctxt,
|
st_unit_ctxt_elem *ctxt,
|
||||||
bool in_subq,
|
bool in_subq,
|
||||||
table_map *dep_map)
|
table_map *dep_map)
|
||||||
{
|
{
|
||||||
for (With_element *with_elem= with_clause->first_elem;
|
for (With_element *with_elem= with_clause->with_list.first;
|
||||||
with_elem != NULL;
|
with_elem;
|
||||||
with_elem= with_elem->next_elem)
|
with_elem= with_elem->next)
|
||||||
{
|
{
|
||||||
check_dependencies_in_unit(with_elem->spec, ctxt, in_subq, dep_map);
|
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()
|
bool With_clause::check_anchors()
|
||||||
{
|
{
|
||||||
/* Find mutually recursive with elements */
|
for (With_element *with_elem= with_list.first;
|
||||||
for (With_element *with_elem= first_elem;
|
with_elem;
|
||||||
with_elem != NULL;
|
with_elem= with_elem->next)
|
||||||
with_elem= with_elem->next_elem)
|
|
||||||
{
|
{
|
||||||
if (!with_elem->is_recursive)
|
if (!with_elem->is_recursive)
|
||||||
continue;
|
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)
|
if (!with_elem->next_mutually_recursive)
|
||||||
{
|
{
|
||||||
With_element *last_mutually_recursive= with_elem;
|
With_element *last_mutually_recursive= with_elem;
|
||||||
table_map with_elem_dep= with_elem->derived_dep_map;
|
table_map with_elem_dep= with_elem->derived_dep_map;
|
||||||
table_map with_elem_map= with_elem->get_elem_map();
|
table_map with_elem_map= with_elem->get_elem_map();
|
||||||
for (With_element *elem= with_elem;
|
for (With_element *elem= with_elem; elem; elem= elem->next)
|
||||||
elem != NULL;
|
|
||||||
elem= elem->next_elem)
|
|
||||||
{
|
{
|
||||||
if (!elem->is_recursive)
|
if (!elem->is_recursive)
|
||||||
continue;
|
continue;
|
||||||
@@ -277,11 +453,16 @@ bool With_clause::check_anchors()
|
|||||||
elem->mutually_recursive= with_elem->mutually_recursive;
|
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();
|
for (st_select_lex *sl= with_elem->spec->first_select();
|
||||||
sl;
|
sl;
|
||||||
sl= sl->next_select())
|
sl= sl->next_select())
|
||||||
{
|
{
|
||||||
if (!(with_elem->mutually_recursive & sl->with_dep))
|
if (with_elem->is_anchor(sl))
|
||||||
{
|
{
|
||||||
with_elem->with_anchor= true;
|
with_elem->with_anchor= true;
|
||||||
break;
|
break;
|
||||||
@@ -289,15 +470,25 @@ bool With_clause::check_anchors()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (With_element *with_elem= first_elem;
|
/*
|
||||||
with_elem != NULL;
|
Check that for any group of mutually recursive with elements
|
||||||
with_elem= with_elem->next_elem)
|
- 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)
|
if (!with_elem->is_recursive)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (!with_elem->with_anchor)
|
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;
|
With_element *elem= with_elem;
|
||||||
while ((elem= elem->get_next_mutually_recursive()) != with_elem)
|
while ((elem= elem->get_next_mutually_recursive()) != with_elem)
|
||||||
{
|
{
|
||||||
@@ -313,7 +504,13 @@ bool With_clause::check_anchors()
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
/* 'with_elem' is a with element with an anchor */
|
||||||
With_element *elem= with_elem;
|
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)
|
while ((elem= elem->get_next_mutually_recursive()) != with_elem)
|
||||||
elem->work_dep_map= elem->base_dep_map & elem->mutually_recursive;
|
elem->work_dep_map= elem->base_dep_map & elem->mutually_recursive;
|
||||||
elem= with_elem;
|
elem= with_elem;
|
||||||
@@ -327,6 +524,7 @@ bool With_clause::check_anchors()
|
|||||||
el->work_dep_map|= elem->work_dep_map;
|
el->work_dep_map|= elem->work_dep_map;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/* If the transitive closure displays any cycle report an arror */
|
||||||
elem= with_elem;
|
elem= with_elem;
|
||||||
while ((elem= elem->get_next_mutually_recursive()) != with_elem)
|
while ((elem= elem->get_next_mutually_recursive()) != with_elem)
|
||||||
{
|
{
|
||||||
@@ -346,36 +544,53 @@ bool With_clause::check_anchors()
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
@brief
|
@brief
|
||||||
Search for the definition of a table among the elements of this with clause
|
Move anchors at the beginning of the specifications for with elements
|
||||||
|
|
||||||
@param table The reference to the table that is looked for
|
|
||||||
|
|
||||||
@details
|
@details
|
||||||
The function looks through the elements of this with clause trying to find
|
This method moves anchors at the beginning of the specifications for
|
||||||
the definition of the given table. When it encounters the element with
|
all recursive with elements.
|
||||||
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,
|
void With_clause::move_anchors_ahead()
|
||||||
With_element *barrier)
|
|
||||||
{
|
{
|
||||||
for (With_element *with_elem= first_elem;
|
for (With_element *with_elem= with_list.first;
|
||||||
with_elem != barrier;
|
with_elem;
|
||||||
with_elem= with_elem->next_elem)
|
with_elem= with_elem->next)
|
||||||
{
|
{
|
||||||
if (my_strcasecmp(system_charset_info, with_elem->query_name->str,
|
if (with_elem->is_recursive)
|
||||||
table->table_name) == 0)
|
with_elem->move_anchors_ahead();
|
||||||
{
|
|
||||||
table->set_derived();
|
|
||||||
return with_elem;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return NULL;
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
@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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -397,9 +612,9 @@ With_element *With_clause::find_table_def(TABLE_LIST *table,
|
|||||||
|
|
||||||
bool With_clause::prepare_unreferenced_elements(THD *thd)
|
bool With_clause::prepare_unreferenced_elements(THD *thd)
|
||||||
{
|
{
|
||||||
for (With_element *with_elem= first_elem;
|
for (With_element *with_elem= with_list.first;
|
||||||
with_elem != NULL;
|
with_elem;
|
||||||
with_elem= with_elem->next_elem)
|
with_elem= with_elem->next)
|
||||||
{
|
{
|
||||||
if (!with_elem->is_referenced() && with_elem->prepare_unreferenced(thd))
|
if (!with_elem->is_referenced() && with_elem->prepare_unreferenced(thd))
|
||||||
return true;
|
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
|
@param spec_end The end of the specification in the input string
|
||||||
|
|
||||||
@details
|
@details
|
||||||
The method creates for a string copy of the specification used in this element.
|
The method creates for a string copy of the specification used in this
|
||||||
The method is called when the element is parsed. The copy may be used to
|
element. The method is called when the element is parsed. The copy may be
|
||||||
create clones of the specification whenever they are needed.
|
used to create clones of the specification whenever they are needed.
|
||||||
|
|
||||||
@retval
|
@retval
|
||||||
false on success
|
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)
|
bool With_element::is_anchor(st_select_lex *sel)
|
||||||
{
|
{
|
||||||
return !(mutually_recursive & sel->with_dep);
|
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
|
@param table reference to the table whose definition is searched for
|
||||||
|
|
||||||
@details
|
@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
|
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
|
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
|
@retval
|
||||||
pointer to the found definition if the search has been successful
|
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()
|
bool TABLE_LIST::is_with_table_recursive_reference()
|
||||||
{
|
{
|
||||||
return (with_internal_reference_map &&
|
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)
|
bool st_select_lex::check_unrestricted_recursive(bool only_standards_compliant)
|
||||||
{
|
{
|
||||||
With_element *with_elem= get_with_element();
|
With_element *with_elem= get_with_element();
|
||||||
if (!with_elem ||!with_elem->is_recursive)
|
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;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check conditions 1-2 for restricted specification*/
|
||||||
table_map unrestricted= 0;
|
table_map unrestricted= 0;
|
||||||
table_map encountered= 0;
|
table_map encountered= 0;
|
||||||
if (with_elem->check_unrestricted_recursive(this,
|
if (with_elem->check_unrestricted_recursive(this,
|
||||||
@@ -790,10 +1031,15 @@ bool st_select_lex::check_unrestricted_recursive(bool only_standards_compliant)
|
|||||||
encountered))
|
encountered))
|
||||||
return true;
|
return true;
|
||||||
with_elem->get_owner()->add_unrestricted(unrestricted);
|
with_elem->get_owner()->add_unrestricted(unrestricted);
|
||||||
|
|
||||||
|
|
||||||
|
/* Check conditions 3-4 for restricted specification*/
|
||||||
if (with_sum_func ||
|
if (with_sum_func ||
|
||||||
(with_elem->contains_sq_with_recursive_reference()))
|
(with_elem->contains_sq_with_recursive_reference()))
|
||||||
with_elem->get_owner()->add_unrestricted(
|
with_elem->get_owner()->add_unrestricted(
|
||||||
with_elem->get_mutually_recursive());
|
with_elem->get_mutually_recursive());
|
||||||
|
|
||||||
|
/* Report an error on unrestricted specification if this is required */
|
||||||
if (only_standards_compliant && with_elem->is_unrestricted())
|
if (only_standards_compliant && with_elem->is_unrestricted())
|
||||||
{
|
{
|
||||||
my_error(ER_NOT_STANDARDS_COMPLIANT_RECURSIVE,
|
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,
|
bool With_element::check_unrestricted_recursive(st_select_lex *sel,
|
||||||
table_map &unrestricted,
|
table_map &unrestricted,
|
||||||
table_map &encountered)
|
table_map &encountered)
|
||||||
{
|
{
|
||||||
|
/* Check conditions 1-for restricted specification*/
|
||||||
List_iterator<TABLE_LIST> ti(sel->leaf_tables);
|
List_iterator<TABLE_LIST> ti(sel->leaf_tables);
|
||||||
TABLE_LIST *tbl;
|
TABLE_LIST *tbl;
|
||||||
while ((tbl= ti++))
|
while ((tbl= ti++))
|
||||||
@@ -843,9 +1109,9 @@ bool With_element::check_unrestricted_recursive(st_select_lex *sel,
|
|||||||
encountered|= with_elem->get_elem_map();
|
encountered|= with_elem->get_elem_map();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (With_element *with_elem= sel->get_with_element()->owner->first_elem;
|
for (With_element *with_elem= sel->get_with_element()->owner->with_list.first;
|
||||||
with_elem != NULL;
|
with_elem;
|
||||||
with_elem= with_elem->next_elem)
|
with_elem= with_elem->next)
|
||||||
{
|
{
|
||||||
if (!with_elem->is_recursive && (unrestricted & with_elem->get_elem_map()))
|
if (!with_elem->is_recursive && (unrestricted & with_elem->get_elem_map()))
|
||||||
continue;
|
continue;
|
||||||
@@ -870,6 +1136,9 @@ bool With_element::check_unrestricted_recursive(st_select_lex *sel,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Check conditions 2 for restricted specification*/
|
||||||
ti.rewind();
|
ti.rewind();
|
||||||
while ((tbl= ti++))
|
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();
|
st_select_lex_unit *sl_master= master_unit();
|
||||||
List_iterator<TABLE_LIST> ti(leaf_tables);
|
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))
|
if (!(tbl->is_with_table_recursive_reference() && sl_master->item))
|
||||||
continue;
|
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())
|
for (st_select_lex *sl= this; sl; sl= sl_master->outer_select())
|
||||||
{
|
{
|
||||||
sl_master= sl->master_unit();
|
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)
|
if (!sl_master->item)
|
||||||
continue;
|
continue;
|
||||||
Item_subselect *subq= (Item_subselect *) sl_master->item;
|
Item_subselect *subq= (Item_subselect *) sl_master->item;
|
||||||
subq->with_recursive_reference= true;
|
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 "));
|
str->append(STRING_WITH_LEN("with "));
|
||||||
if (with_recursive)
|
if (with_recursive)
|
||||||
str->append(STRING_WITH_LEN("recursive "));
|
str->append(STRING_WITH_LEN("recursive "));
|
||||||
for (With_element *with_elem= first_elem;
|
for (With_element *with_elem= with_list.first;
|
||||||
with_elem != NULL;
|
with_elem;
|
||||||
with_elem= with_elem->next_elem)
|
with_elem= with_elem->next)
|
||||||
{
|
{
|
||||||
with_elem->print(str, query_type);
|
with_elem->print(str, query_type);
|
||||||
if (with_elem != first_elem)
|
if (with_elem != with_list.first)
|
||||||
str->append(", ");
|
str->append(", ");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
110
sql/sql_cte.h
110
sql/sql_cte.h
@@ -21,7 +21,7 @@ class With_element : public Sql_alloc
|
|||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
With_clause *owner; // with clause this object belongs to
|
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)
|
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
|
table_map elem_map; // The map where with only one 1 set in this->number
|
||||||
/*
|
/*
|
||||||
@@ -36,10 +36,22 @@ private:
|
|||||||
directly or indirectly from the i-th with element.
|
directly or indirectly from the i-th with element.
|
||||||
*/
|
*/
|
||||||
table_map derived_dep_map;
|
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 sq_dep_map;
|
||||||
table_map work_dep_map; // dependency map used for work
|
table_map work_dep_map; // dependency map used for work
|
||||||
/* Dependency map of with elements mutually recursive with this with element */
|
/* Dependency map of with elements mutually recursive with this with element */
|
||||||
table_map mutually_recursive;
|
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;
|
With_element *next_mutually_recursive;
|
||||||
/*
|
/*
|
||||||
Total number of references to this element in the FROM lists of
|
Total number of references to this element in the FROM lists of
|
||||||
@@ -56,8 +68,6 @@ private:
|
|||||||
/* Return the map where 1 is set only in the position for this element */
|
/* Return the map where 1 is set only in the position for this element */
|
||||||
table_map get_elem_map() { return 1 << number; }
|
table_map get_elem_map() { return 1 << number; }
|
||||||
|
|
||||||
TABLE *table;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/*
|
/*
|
||||||
The name of the table introduced by this with elememt. The name
|
The name of the table introduced by this with elememt. The name
|
||||||
@@ -79,34 +89,48 @@ public:
|
|||||||
*/
|
*/
|
||||||
bool is_recursive;
|
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;
|
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;
|
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;
|
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;
|
select_union_recursive *rec_result;
|
||||||
|
|
||||||
TABLE *result_table;
|
|
||||||
|
|
||||||
TABLE *first_rec_table_to_update;
|
|
||||||
|
|
||||||
|
|
||||||
With_element(LEX_STRING *name,
|
With_element(LEX_STRING *name,
|
||||||
List <LEX_STRING> list,
|
List <LEX_STRING> list,
|
||||||
st_select_lex_unit *unit)
|
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),
|
sq_dep_map(0), work_dep_map(0), mutually_recursive(0),
|
||||||
next_mutually_recursive(NULL),
|
next_mutually_recursive(NULL), references(0),
|
||||||
references(0), table(NULL),
|
|
||||||
query_name(name), column_list(list), spec(unit),
|
query_name(name), column_list(list), spec(unit),
|
||||||
is_recursive(false), with_anchor(false),
|
is_recursive(false), with_anchor(false),
|
||||||
level(0), rec_result(NULL), result_table(NULL),
|
level(0), rec_result(NULL)
|
||||||
first_rec_table_to_update(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,
|
void check_dependencies_in_select(st_select_lex *sl, st_unit_ctxt_elem *ctxt,
|
||||||
bool in_subq, table_map *dep_map);
|
bool in_subq, table_map *dep_map);
|
||||||
@@ -155,10 +179,6 @@ public:
|
|||||||
With_element *get_next_mutually_recursive()
|
With_element *get_next_mutually_recursive()
|
||||||
{ return 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);
|
bool is_anchor(st_select_lex *sel);
|
||||||
|
|
||||||
void move_anchors_ahead();
|
void move_anchors_ahead();
|
||||||
@@ -173,7 +193,7 @@ public:
|
|||||||
|
|
||||||
void mark_as_cleaned();
|
void mark_as_cleaned();
|
||||||
|
|
||||||
void reset_for_exec();
|
void reset_recursive_for_exec();
|
||||||
|
|
||||||
void cleanup_stabilized();
|
void cleanup_stabilized();
|
||||||
|
|
||||||
@@ -183,8 +203,6 @@ public:
|
|||||||
|
|
||||||
bool all_are_stabilized();
|
bool all_are_stabilized();
|
||||||
|
|
||||||
void set_result_table(TABLE *tab) { result_table= tab; }
|
|
||||||
|
|
||||||
bool instantiate_tmp_tables();
|
bool instantiate_tmp_tables();
|
||||||
|
|
||||||
void prepare_for_next_iteration();
|
void prepare_for_next_iteration();
|
||||||
@@ -206,9 +224,9 @@ class With_clause : public Sql_alloc
|
|||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
st_select_lex_unit *owner; // the unit this with clause attached to
|
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
|
/* The list of all with elements from this with clause */
|
||||||
uint elements; // number of the elements/defintions in this with clauses
|
SQL_I_List<With_element> with_list;
|
||||||
/*
|
/*
|
||||||
The with clause immediately containing this with clause if there is any,
|
The with clause immediately containing this with clause if there is any,
|
||||||
otherwise NULL. Now used only at parsing.
|
otherwise NULL. Now used only at parsing.
|
||||||
@@ -222,9 +240,22 @@ private:
|
|||||||
/* Set to true if dependencies between with elements have been checked */
|
/* Set to true if dependencies between with elements have been checked */
|
||||||
bool dependencies_are_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;
|
table_map unrestricted;
|
||||||
|
/*
|
||||||
|
The bitmap of all recursive with elements whose anchors
|
||||||
|
has been already prepared.
|
||||||
|
*/
|
||||||
table_map with_prepared_anchor;
|
table_map with_prepared_anchor;
|
||||||
table_map cleaned;
|
table_map cleaned;
|
||||||
|
/*
|
||||||
|
The bitmap of all recursive with elements that
|
||||||
|
has been already materialized
|
||||||
|
*/
|
||||||
table_map stabilized;
|
table_map stabilized;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@@ -232,23 +263,20 @@ public:
|
|||||||
bool with_recursive;
|
bool with_recursive;
|
||||||
|
|
||||||
With_clause(bool recursive_fl, With_clause *emb_with_clause)
|
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),
|
embedding_with_clause(emb_with_clause), next_with_clause(NULL),
|
||||||
dependencies_are_checked(false),
|
dependencies_are_checked(false), unrestricted(0),
|
||||||
unrestricted(0), with_prepared_anchor(0), cleaned(0),
|
with_prepared_anchor(0), cleaned(0), stabilized(0),
|
||||||
stabilized(0),
|
|
||||||
with_recursive(recursive_fl)
|
with_recursive(recursive_fl)
|
||||||
{ last_next= &first_elem; }
|
{ }
|
||||||
|
|
||||||
/* Add a new element to the current with clause */
|
/* Add a new element to the current with clause */
|
||||||
bool add_with_element(With_element *elem)
|
bool add_with_element(With_element *elem)
|
||||||
{
|
{
|
||||||
elem->owner= this;
|
elem->owner= this;
|
||||||
elem->number= elements;
|
elem->number= with_list.elements;
|
||||||
elem->spec->with_element= elem;
|
elem->spec->with_element= elem;
|
||||||
*last_next= elem;
|
with_list.link_in_list(elem, &elem->next);
|
||||||
last_next= &elem->next_elem;
|
|
||||||
elements++;
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -263,7 +291,7 @@ public:
|
|||||||
|
|
||||||
With_clause *pop() { return embedding_with_clause; }
|
With_clause *pop() { return embedding_with_clause; }
|
||||||
|
|
||||||
bool check_dependencies(THD *thd);
|
bool check_dependencies();
|
||||||
|
|
||||||
bool check_anchors();
|
bool check_anchors();
|
||||||
|
|
||||||
@@ -283,7 +311,7 @@ public:
|
|||||||
|
|
||||||
friend
|
friend
|
||||||
bool
|
bool
|
||||||
check_dependencies_in_with_clauses(THD *thd, With_clause *with_clauses_list);
|
check_dependencies_in_with_clauses(With_clause *with_clauses_list);
|
||||||
};
|
};
|
||||||
|
|
||||||
inline
|
inline
|
||||||
@@ -321,16 +349,18 @@ void With_element::mark_as_cleaned()
|
|||||||
|
|
||||||
|
|
||||||
inline
|
inline
|
||||||
void With_element::reset_for_exec()
|
void With_element::reset_recursive_for_exec()
|
||||||
{
|
{
|
||||||
|
DBUG_ASSERT(is_recursive);
|
||||||
level= 0;
|
level= 0;
|
||||||
owner->with_prepared_anchor&= ~mutually_recursive;
|
owner->with_prepared_anchor&= ~mutually_recursive;
|
||||||
owner->cleaned&= ~get_elem_map();
|
owner->cleaned&= ~get_elem_map();
|
||||||
first_rec_table_to_update= NULL;
|
|
||||||
cleanup_stabilized();
|
cleanup_stabilized();
|
||||||
|
rec_result->first_rec_table_to_update= 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
inline
|
inline
|
||||||
void With_element::cleanup_stabilized()
|
void With_element::cleanup_stabilized()
|
||||||
{
|
{
|
||||||
@@ -365,7 +395,7 @@ void With_element::prepare_for_next_iteration()
|
|||||||
With_element *with_elem= this;
|
With_element *with_elem= this;
|
||||||
while ((with_elem= with_elem->get_next_mutually_recursive()) != 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)
|
if (rec_table)
|
||||||
rec_table->reginfo.join_tab->preread_init_done= false;
|
rec_table->reginfo.join_tab->preread_init_done= false;
|
||||||
}
|
}
|
||||||
|
@@ -1109,7 +1109,7 @@ public:
|
|||||||
}
|
}
|
||||||
With_element *find_table_def_in_with_clauses(TABLE_LIST *table);
|
With_element *find_table_def_in_with_clauses(TABLE_LIST *table);
|
||||||
bool check_unrestricted_recursive(bool only_standards_compliant);
|
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;
|
List<Window_spec> window_specs;
|
||||||
void prepare_add_window_spec(THD *thd);
|
void prepare_add_window_spec(THD *thd);
|
||||||
@@ -2475,7 +2475,7 @@ struct LEX: public Query_tables_list
|
|||||||
SELECT_LEX *all_selects_list;
|
SELECT_LEX *all_selects_list;
|
||||||
/* current with clause in parsing if any, otherwise 0*/
|
/* current with clause in parsing if any, otherwise 0*/
|
||||||
With_clause *curr_with_clause;
|
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_clause *with_clauses_list;
|
||||||
/*
|
/*
|
||||||
(*with_clauses_list_last_next) contains a pointer to the last
|
(*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,
|
new (thd->mem_root) Item_int(thd,
|
||||||
(ulonglong) thd->variables.select_limit);
|
(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;
|
return 1;
|
||||||
|
|
||||||
if (!(res= open_and_lock_tables(thd, all_tables, TRUE, 0)))
|
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;
|
lex->select_lex.context.resolve_in_select_list= TRUE;
|
||||||
|
|
||||||
ulong privilege= lex->exchange ? SELECT_ACL | FILE_ACL : SELECT_ACL;
|
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;
|
goto error;
|
||||||
if (tables)
|
if (tables)
|
||||||
{
|
{
|
||||||
|
@@ -1231,8 +1231,8 @@ bool st_select_lex_unit::exec_recursive()
|
|||||||
{
|
{
|
||||||
saved_error=
|
saved_error=
|
||||||
incr_table->insert_all_rows_into(thd, rec_table, !is_unrestricted);
|
incr_table->insert_all_rows_into(thd, rec_table, !is_unrestricted);
|
||||||
if (!with_element->first_rec_table_to_update)
|
if (!with_element->rec_result->first_rec_table_to_update)
|
||||||
with_element->first_rec_table_to_update= rec_table;
|
with_element->rec_result->first_rec_table_to_update= rec_table;
|
||||||
if (with_element->level == 1)
|
if (with_element->level == 1)
|
||||||
rec_table->reginfo.join_tab->preread_init_done= true;
|
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())
|
for (SELECT_LEX *sl= first_select(); sl; sl= sl->next_select())
|
||||||
error|= sl->cleanup();
|
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)
|
if (fake_select_lex)
|
||||||
{
|
{
|
||||||
error|= fake_select_lex->cleanup();
|
error|= fake_select_lex->cleanup();
|
||||||
@@ -1289,15 +1282,25 @@ bool st_select_lex_unit::cleanup()
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (with_element && with_element->is_recursive)
|
if (with_element && with_element->is_recursive)
|
||||||
with_element->mark_as_cleaned();
|
|
||||||
|
|
||||||
if (union_result && !(with_element &&with_element->is_recursive))
|
|
||||||
{
|
{
|
||||||
delete union_result;
|
if (union_result )
|
||||||
union_result=0; // Safety
|
{
|
||||||
if (table)
|
((select_union_recursive *) union_result)->cleanup();
|
||||||
free_tmp_table(thd, table);
|
delete union_result;
|
||||||
table= 0; // Safety
|
union_result= 0;
|
||||||
|
}
|
||||||
|
with_element->mark_as_cleaned();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (union_result)
|
||||||
|
{
|
||||||
|
delete union_result;
|
||||||
|
union_result=0; // Safety
|
||||||
|
if (table)
|
||||||
|
free_tmp_table(thd, table);
|
||||||
|
table= 0; // Safety
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DBUG_RETURN(error);
|
DBUG_RETURN(error);
|
||||||
@@ -1325,7 +1328,7 @@ void st_select_lex_unit::reinit_exec_mechanism()
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
if (with_element && with_element->is_recursive)
|
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);
|
lex->link_first_table_back(view, link_to_local);
|
||||||
view->open_type= OT_BASE_ONLY;
|
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;
|
res= TRUE;
|
||||||
goto err;
|
goto err;
|
||||||
@@ -1390,7 +1390,7 @@ bool mysql_make_view(THD *thd, TABLE_SHARE *share, TABLE_LIST *table,
|
|||||||
TABLE_LIST *tbl;
|
TABLE_LIST *tbl;
|
||||||
Security_context *security_ctx= 0;
|
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;
|
goto err;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@@ -1857,7 +1857,8 @@ struct TABLE_LIST
|
|||||||
derived tables. Use TABLE_LIST::is_anonymous_derived_table().
|
derived tables. Use TABLE_LIST::is_anonymous_derived_table().
|
||||||
*/
|
*/
|
||||||
st_select_lex_unit *derived; /* SELECT_LEX_UNIT of 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;
|
table_map with_internal_reference_map;
|
||||||
bool block_handle_derived;
|
bool block_handle_derived;
|
||||||
ST_SCHEMA_TABLE *schema_table; /* Information_schema table */
|
ST_SCHEMA_TABLE *schema_table; /* Information_schema table */
|
||||||
|
Reference in New Issue
Block a user