Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
M
MariaDB
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
nexedi
MariaDB
Commits
c8f85bf2
Commit
c8f85bf2
authored
Aug 26, 2016
by
Igor Babaev
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
mdev-9864: cleanup, re-factoring.
Added comments.
parent
f33c3524
Changes
11
Hide whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
547 additions
and
207 deletions
+547
-207
sql/item_subselect.h
sql/item_subselect.h
+5
-0
sql/share/errmsg-utf8.txt
sql/share/errmsg-utf8.txt
+1
-1
sql/sql_class.h
sql/sql_class.h
+3
-1
sql/sql_cte.cc
sql/sql_cte.cc
+436
-137
sql/sql_cte.h
sql/sql_cte.h
+72
-42
sql/sql_lex.h
sql/sql_lex.h
+2
-2
sql/sql_parse.cc
sql/sql_parse.cc
+1
-1
sql/sql_prepare.cc
sql/sql_prepare.cc
+1
-1
sql/sql_union.cc
sql/sql_union.cc
+21
-18
sql/sql_view.cc
sql/sql_view.cc
+2
-2
sql/table.h
sql/table.h
+3
-2
No files found.
sql/item_subselect.h
View file @
c8f85bf2
...
...
@@ -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
,
...
...
sql/share/errmsg-utf8.txt
View file @
c8f85bf2
...
...
@@ -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 materia
li
zed derived"
ER_NOT_STANDARDS_COMPLIANT_RECURSIVE
eng "Restrictions imposed on recursive definitions are violated for table '%s'"
#
...
...
sql/sql_class.h
View file @
c8f85bf2
...
...
@@ -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
,
...
...
sql/sql_cte.cc
View file @
c8f85bf2
...
...
@@ -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
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.
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.
@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_select_lex_unit
*
unit
;
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
@param unit The unit where the siblings are to be searched for
Find the dependencies of this element on its siblings in a unit
@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
Move anchors at the beginning of the specifications for with elements
@param table The reference to the table that is looked for
@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
();
}
}
With_element
*
With_clause
::
find_table_def
(
TABLE_LIST
*
table
,
With_element
*
barrier
)
/**
@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
()
{
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 the
re
the embedding units are looked through.
definition is not found the
n
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
(
", "
);
}
}
...
...
sql/sql_cte.h
View file @
c8f85bf2
...
...
@@ -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
/*
...
...
@@ -35,11 +35,23 @@ class With_element : public Sql_alloc
The map derived_dep_map has 1 in i-th position if this with element depends
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
work_dep_map
;
// dependency map used for work
/* 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
;
/*
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
;
}
...
...
sql/sql_lex.h
View file @
c8f85bf2
...
...
@@ -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 statem
a
nt */
/* pointer to the first with clause in the current statem
e
nt */
With_clause
*
with_clauses_list
;
/*
(*with_clauses_list_last_next) contains a pointer to the last
...
...
sql/sql_parse.cc
View file @
c8f85bf2
...
...
@@ -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
)))
...
...
sql/sql_prepare.cc
View file @
c8f85bf2
...
...
@@ -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
)
{
...
...
sql/sql_union.cc
View file @
c8f85bf2
...
...
@@ -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
;
}
...
...
@@ -1257,14 +1257,7 @@ 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,15 +1282,25 @@ 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
{
delete
union_result
;
union_result
=
0
;
// Safety
if
(
table
)
free_tmp_table
(
thd
,
table
);
table
=
0
;
// Safety
if
(
union_result
)
{
delete
union_result
;
union_result
=
0
;
// Safety
if
(
table
)
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
();
}
...
...
sql/sql_view.cc
View file @
c8f85bf2
...
...
@@ -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
;
/*
...
...
sql/table.h
View file @
c8f85bf2
...
...
@@ -1857,8 +1857,9 @@ 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 */
table_map
with_internal_reference_map
;
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 */
st_select_lex
*
schema_select_lex
;
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment