summaryrefslogtreecommitdiff
path: root/sql/sql_derived.cc
diff options
context:
space:
mode:
Diffstat (limited to 'sql/sql_derived.cc')
-rw-r--r--sql/sql_derived.cc386
1 files changed, 347 insertions, 39 deletions
diff --git a/sql/sql_derived.cc b/sql/sql_derived.cc
index 6b415c675f2..3e84536673a 100644
--- a/sql/sql_derived.cc
+++ b/sql/sql_derived.cc
@@ -30,6 +30,8 @@
#include "sql_base.h"
#include "sql_view.h" // check_duplicate_names
#include "sql_acl.h" // SELECT_ACL
+#include "sql_class.h"
+#include "sql_cte.h"
typedef bool (*dt_processor)(THD *thd, LEX *lex, TABLE_LIST *derived);
@@ -483,10 +485,8 @@ exit_merge:
unconditional_materialization:
derived->change_refs_to_fields();
derived->set_materialized_derived();
- if (!derived->table || !derived->table->created)
+ if (!derived->table || !derived->table->is_created())
res= mysql_derived_create(thd, lex, derived);
- if (!res)
- res= mysql_derived_fill(thd, lex, derived);
goto exit_merge;
}
@@ -579,7 +579,11 @@ bool mysql_derived_init(THD *thd, LEX *lex, TABLE_LIST *derived)
if (!unit || unit->prepared)
DBUG_RETURN(FALSE);
- DBUG_RETURN(derived->init_derived(thd, TRUE));
+ bool res= derived->init_derived(thd, TRUE);
+
+ derived->updatable= derived->updatable && derived->is_view();
+
+ DBUG_RETURN(res);
}
@@ -639,6 +643,7 @@ bool mysql_derived_init(THD *thd, LEX *lex, TABLE_LIST *derived)
true Error
*/
+
bool mysql_derived_prepare(THD *thd, LEX *lex, TABLE_LIST *derived)
{
SELECT_LEX_UNIT *unit= derived->get_unit();
@@ -648,24 +653,87 @@ bool mysql_derived_prepare(THD *thd, LEX *lex, TABLE_LIST *derived)
(derived->alias ? derived->alias : "<NULL>"),
unit));
+ if (!unit)
+ DBUG_RETURN(FALSE);
+
+ SELECT_LEX *first_select= unit->first_select();
+
+ if (derived->is_recursive_with_table() &&
+ !derived->is_with_table_recursive_reference() &&
+ !derived->with->rec_result && derived->with->get_sq_rec_ref())
+ {
+ /*
+ This is a non-recursive reference to a recursive CTE whose
+ specification unit has not been prepared at the regular processing of
+ derived table references. This can happen only in the case when
+ the specification unit has no recursive references at the top level.
+ Force the preparation of the specification unit. Use a recursive
+ table reference from a subquery for this.
+ */
+ DBUG_ASSERT(derived->with->get_sq_rec_ref());
+ if (mysql_derived_prepare(lex->thd, lex, derived->with->get_sq_rec_ref()))
+ DBUG_RETURN(TRUE);
+ }
+
+ if (unit->prepared && derived->is_recursive_with_table() &&
+ !derived->table)
+ {
+ /*
+ Here 'derived' is either a non-recursive table reference to a recursive
+ with table or a recursive table reference to a recursvive table whose
+ specification has been already prepared (a secondary recursive table
+ reference.
+ */
+ if (!(derived->derived_result= new (thd->mem_root) select_union(thd)))
+ DBUG_RETURN(TRUE); // out of memory
+ thd->create_tmp_table_for_derived= TRUE;
+ res= derived->derived_result->create_result_table(
+ thd, &unit->types, FALSE,
+ (first_select->options |
+ thd->variables.option_bits |
+ TMP_TABLE_ALL_COLUMNS),
+ derived->alias, FALSE, FALSE);
+ thd->create_tmp_table_for_derived= FALSE;
+
+ if (!res && !derived->table)
+ {
+ derived->derived_result->set_unit(unit);
+ derived->table= derived->derived_result->table;
+ if (derived->is_with_table_recursive_reference())
+ {
+ /* Here 'derived" is a secondary recursive table reference */
+ unit->with_element->rec_result->rec_tables.push_back(derived->table);
+ }
+ }
+ DBUG_ASSERT(derived->table || res);
+ goto exit;
+ }
+
// Skip already prepared views/DT
- if (!unit || unit->prepared ||
+ if (unit->prepared ||
(derived->merged_for_insert &&
!(derived->is_multitable() &&
(thd->lex->sql_command == SQLCOM_UPDATE_MULTI ||
thd->lex->sql_command == SQLCOM_DELETE_MULTI))))
DBUG_RETURN(FALSE);
- SELECT_LEX *first_select= unit->first_select();
-
/* prevent name resolving out of derived table */
for (SELECT_LEX *sl= first_select; sl; sl= sl->next_select())
{
sl->context.outer_context= 0;
- // Prepare underlying views/DT first.
- if ((res= sl->handle_derived(lex, DT_PREPARE)))
- goto exit;
-
+ if (!derived->is_with_table_recursive_reference() ||
+ (!derived->with->with_anchor &&
+ !derived->with->is_with_prepared_anchor()))
+ {
+ /*
+ Prepare underlying views/DT first unless 'derived' is a recursive
+ table reference and either the anchors from the specification of
+ 'derived' has been already prepared or there no anchor in this
+ specification
+ */
+ if ((res= sl->handle_derived(lex, DT_PREPARE)))
+ goto exit;
+ }
if (derived->outer_join && sl->first_cond_optimization)
{
/* Mark that table is part of OUTER JOIN and fields may be NULL */
@@ -686,8 +754,11 @@ bool mysql_derived_prepare(THD *thd, LEX *lex, TABLE_LIST *derived)
// st_select_lex_unit::prepare correctly work for single select
if ((res= unit->prepare(thd, derived->derived_result, 0)))
goto exit;
+ if (derived->with &&
+ (res= derived->with->rename_columns_of_derived_unit(thd, unit)))
+ goto exit;
lex->context_analysis_only&= ~CONTEXT_ANALYSIS_ONLY_DERIVED;
- if ((res= check_duplicate_names(unit->types, 0)))
+ if ((res= check_duplicate_names(thd, unit->types, 0)))
goto exit;
/*
@@ -709,19 +780,21 @@ bool mysql_derived_prepare(THD *thd, LEX *lex, TABLE_LIST *derived)
SELECT is last SELECT of UNION).
*/
thd->create_tmp_table_for_derived= TRUE;
- if (derived->derived_result->create_result_table(thd, &unit->types, FALSE,
- (first_select->options |
- thd->variables.option_bits |
- TMP_TABLE_ALL_COLUMNS),
- derived->alias,
- FALSE, FALSE))
+ if (!(derived->table) &&
+ derived->derived_result->create_result_table(thd, &unit->types, FALSE,
+ (first_select->options |
+ thd->variables.option_bits |
+ TMP_TABLE_ALL_COLUMNS),
+ derived->alias,
+ FALSE, FALSE, FALSE))
{
thd->create_tmp_table_for_derived= FALSE;
goto exit;
}
thd->create_tmp_table_for_derived= FALSE;
- derived->table= derived->derived_result->table;
+ if (!derived->table)
+ derived->table= derived->derived_result->table;
DBUG_ASSERT(derived->table);
if (derived->is_derived() && derived->is_merged_derived())
first_select->mark_as_belong_to_derived(derived);
@@ -748,9 +821,12 @@ exit:
*/
if (res)
{
- if (derived->table)
- free_tmp_table(thd, derived->table);
- delete derived->derived_result;
+ if (!derived->is_with_table_recursive_reference())
+ {
+ if (derived->table)
+ free_tmp_table(thd, derived->table);
+ delete derived->derived_result;
+ }
}
else
{
@@ -758,18 +834,22 @@ exit:
table->derived_select_number= first_select->select_number;
table->s->tmp_table= INTERNAL_TMP_TABLE;
#ifndef NO_EMBEDDED_ACCESS_CHECKS
- if (derived->referencing_view)
+ if (derived->is_view())
table->grant= derived->grant;
else
{
+ DBUG_ASSERT(derived->is_derived());
+ DBUG_ASSERT(derived->is_anonymous_derived_table());
table->grant.privilege= SELECT_ACL;
- if (derived->is_derived())
- derived->grant.privilege= SELECT_ACL;
+ derived->grant.privilege= SELECT_ACL;
}
#endif
/* Add new temporary table to list of open derived tables */
- table->next= thd->derived_tables;
- thd->derived_tables= table;
+ if (!derived->is_with_table_recursive_reference())
+ {
+ table->next= thd->derived_tables;
+ thd->derived_tables= table;
+ }
/* If table is used by a left join, mark that any column may be null */
if (derived->outer_join)
@@ -877,9 +957,9 @@ bool mysql_derived_create(THD *thd, LEX *lex, TABLE_LIST *derived)
TABLE *table= derived->table;
SELECT_LEX_UNIT *unit= derived->get_unit();
- if (table->created)
+ if (table->is_created())
DBUG_RETURN(FALSE);
- select_union *result= (select_union*)unit->result;
+ select_union *result= derived->derived_result;
if (table->s->db_type() == TMP_ENGINE_HTON)
{
result->tmp_table_param.keyinfo= table->s->key_info;
@@ -898,6 +978,48 @@ bool mysql_derived_create(THD *thd, LEX *lex, TABLE_LIST *derived)
}
+/**
+ @brief
+ Fill the recursive with table
+
+ @param thd The thread handle
+
+ @details
+ The method is called only for recursive with tables.
+ The method executes the recursive part of the specification
+ of this with table until no more rows are added to the table
+ or the number of the performed iteration reaches the allowed
+ maximum.
+
+ @retval
+ false on success
+ true on failure
+*/
+
+bool TABLE_LIST::fill_recursive(THD *thd)
+{
+ bool rc= false;
+ st_select_lex_unit *unit= get_unit();
+ rc= with->instantiate_tmp_tables();
+ while (!rc && !with->all_are_stabilized())
+ {
+ if (with->level > thd->variables.max_recursive_iterations)
+ break;
+ with->prepare_for_next_iteration();
+ rc= unit->exec_recursive();
+ }
+ if (!rc)
+ {
+ TABLE *src= with->rec_result->table;
+ rc =src->insert_all_rows_into_tmp_table(thd,
+ table,
+ &with->rec_result->tmp_table_param,
+ true);
+ }
+ return rc;
+}
+
+
/*
Execute subquery of a materialized derived table/view and fill the result
table.
@@ -908,9 +1030,10 @@ bool mysql_derived_create(THD *thd, LEX *lex, TABLE_LIST *derived)
@details
Execute subquery of given 'derived' table/view and fill the result
- table. After result table is filled, if this is not the EXPLAIN statement,
- the entire unit / node is deleted. unit is deleted if UNION is used
- for derived table and node is deleted is it is a simple SELECT.
+ table. After result table is filled, if this is not the EXPLAIN statement
+ and the table is not specified with a recursion the entire unit / node
+ is deleted. unit is deleted if UNION is used for derived table and node
+ is deleted is it is a simple SELECT.
'lex' is unused and 'thd' is passed as an argument to an underlying function.
@note
@@ -921,36 +1044,53 @@ bool mysql_derived_create(THD *thd, LEX *lex, TABLE_LIST *derived)
@return TRUE Error
*/
+
bool mysql_derived_fill(THD *thd, LEX *lex, TABLE_LIST *derived)
{
Field_iterator_table field_iterator;
SELECT_LEX_UNIT *unit= derived->get_unit();
+ bool derived_is_recursive= derived->is_recursive_with_table();
bool res= FALSE;
DBUG_ENTER("mysql_derived_fill");
DBUG_PRINT("enter", ("Alias: '%s' Unit: %p",
(derived->alias ? derived->alias : "<NULL>"),
derived->get_unit()));
- if (unit->executed && !unit->uncacheable && !unit->describe)
+ if (unit->executed && !unit->uncacheable && !unit->describe &&
+ !derived_is_recursive)
DBUG_RETURN(FALSE);
/*check that table creation passed without problems. */
- DBUG_ASSERT(derived->table && derived->table->created);
- SELECT_LEX *first_select= unit->first_select();
+ DBUG_ASSERT(derived->table && derived->table->is_created());
select_union *derived_result= derived->derived_result;
SELECT_LEX *save_current_select= lex->current_select;
- if (unit->is_union())
+
+ if (derived_is_recursive)
+ {
+ if (derived->is_with_table_recursive_reference())
+ {
+ /* Here only one iteration step is performed */
+ res= unit->exec_recursive();
+ }
+ else
+ {
+ /* In this case all iteration are performed */
+ res= derived->fill_recursive(thd);
+ }
+ }
+ else if (unit->is_union())
{
// execute union without clean up
res= unit->exec();
}
else
{
+ SELECT_LEX *first_select= unit->first_select();
unit->set_limit(unit->global_parameters());
if (unit->select_limit_cnt == HA_POS_ERROR)
first_select->options&= ~OPTION_FOUND_ROWS;
lex->current_select= first_select;
- res= mysql_select(thd, &first_select->ref_pointer_array,
+ res= mysql_select(thd,
first_select->table_list.first,
first_select->with_wild,
first_select->item_list, first_select->where,
@@ -964,7 +1104,7 @@ bool mysql_derived_fill(THD *thd, LEX *lex, TABLE_LIST *derived)
derived_result, unit, first_select);
}
- if (!res)
+ if (!res && !derived_is_recursive)
{
if (derived_result->flush())
res= TRUE;
@@ -990,7 +1130,7 @@ bool mysql_derived_fill(THD *thd, LEX *lex, TABLE_LIST *derived)
}
}
- if (res || !lex->describe)
+ if (res || (!lex->describe && !derived_is_recursive))
unit->cleanup();
lex->current_select= save_current_select;
@@ -1028,6 +1168,174 @@ bool mysql_derived_reinit(THD *thd, LEX *lex, TABLE_LIST *derived)
unit->types.empty();
/* for derived tables & PS (which can't be reset by Item_subselect) */
unit->reinit_exec_mechanism();
+ for (st_select_lex *sl= unit->first_select(); sl; sl= sl->next_select())
+ {
+ sl->cond_pushed_into_where= NULL;
+ sl->cond_pushed_into_having= NULL;
+ }
unit->set_thd(thd);
DBUG_RETURN(FALSE);
}
+
+
+/**
+ @brief
+ Extract the condition depended on derived table/view and pushed it there
+
+ @param thd The thread handle
+ @param cond The condition from which to extract the pushed condition
+ @param derived The reference to the derived table/view
+
+ @details
+ This functiom builds the most restrictive condition depending only on
+ the derived table/view that can be extracted from the condition cond.
+ The built condition is pushed into the having clauses of the
+ selects contained in the query specifying the derived table/view.
+ The function also checks for each select whether any condition depending
+ only on grouping fields can be extracted from the pushed condition.
+ If so, it pushes the condition over grouping fields into the where
+ clause of the select.
+
+ @retval
+ true if an error is reported
+ false otherwise
+*/
+
+bool pushdown_cond_for_derived(THD *thd, Item *cond, TABLE_LIST *derived)
+{
+ DBUG_ENTER("pushdown_cond_for_derived");
+ if (!cond)
+ DBUG_RETURN(false);
+
+ st_select_lex_unit *unit= derived->get_unit();
+ st_select_lex *sl= unit->first_select();
+
+ if (derived->prohibit_cond_pushdown)
+ DBUG_RETURN(false);
+
+ /* Do not push conditions into constant derived */
+ if (unit->executed)
+ DBUG_RETURN(false);
+
+ /* Do not push conditions into recursive with tables */
+ if (derived->is_recursive_with_table())
+ DBUG_RETURN(false);
+
+ /* Do not push conditions into unit with global ORDER BY ... LIMIT */
+ if (unit->fake_select_lex && unit->fake_select_lex->explicit_limit)
+ DBUG_RETURN(false);
+
+ /* Check whether any select of 'unit' allows condition pushdown */
+ bool some_select_allows_cond_pushdown= false;
+ for (; sl; sl= sl->next_select())
+ {
+ if (sl->cond_pushdown_is_allowed())
+ {
+ some_select_allows_cond_pushdown= true;
+ break;
+ }
+ }
+ if (!some_select_allows_cond_pushdown)
+ DBUG_RETURN(false);
+
+ /*
+ Build the most restrictive condition extractable from 'cond'
+ that can be pushed into the derived table 'derived'.
+ All subexpressions of this condition are cloned from the
+ subexpressions of 'cond'.
+ This condition has to be fixed yet.
+ */
+ Item *extracted_cond;
+ derived->check_pushable_cond_for_table(cond);
+ extracted_cond= derived->build_pushable_cond_for_table(thd, cond);
+ if (!extracted_cond)
+ {
+ /* Nothing can be pushed into the derived table */
+ DBUG_RETURN(false);
+ }
+ /* Push extracted_cond into every select of the unit specifying 'derived' */
+ st_select_lex *save_curr_select= thd->lex->current_select;
+ for (; sl; sl= sl->next_select())
+ {
+ if (!sl->cond_pushdown_is_allowed())
+ continue;
+ thd->lex->current_select= sl;
+ /*
+ For each select of the unit except the last one
+ create a clone of extracted_cond
+ */
+ Item *extracted_cond_copy= !sl->next_select() ? extracted_cond :
+ extracted_cond->build_clone(thd, thd->mem_root);
+ if (!extracted_cond_copy)
+ continue;
+
+ if (!sl->join->group_list && !sl->with_sum_func)
+ {
+ /* extracted_cond_copy is pushed into where of sl */
+ extracted_cond_copy= extracted_cond_copy->transform(thd,
+ &Item::derived_field_transformer_for_where,
+ (uchar*) sl);
+ if (extracted_cond_copy)
+ {
+ extracted_cond_copy->walk(
+ &Item::cleanup_excluding_const_fields_processor, 0, 0);
+ sl->cond_pushed_into_where= extracted_cond_copy;
+ }
+
+ continue;
+ }
+
+ /*
+ Figure out what can be extracted from the pushed condition
+ that could be pushed into the where clause of sl
+ */
+ Item *cond_over_grouping_fields;
+ sl->collect_grouping_fields(thd);
+ sl->check_cond_extraction_for_grouping_fields(extracted_cond_copy,
+ derived);
+ cond_over_grouping_fields=
+ sl->build_cond_for_grouping_fields(thd, extracted_cond_copy, true);
+
+ /*
+ Transform the references to the 'derived' columns from the condition
+ pushed into the where clause of sl to make them usable in the new context
+ */
+ if (cond_over_grouping_fields)
+ cond_over_grouping_fields= cond_over_grouping_fields->transform(thd,
+ &Item::derived_grouping_field_transformer_for_where,
+ (uchar*) sl);
+
+ if (cond_over_grouping_fields)
+ {
+ /*
+ In extracted_cond_copy remove top conjuncts that
+ has been pushed into the where clause of sl
+ */
+ extracted_cond_copy= remove_pushed_top_conjuncts(thd, extracted_cond_copy);
+
+ cond_over_grouping_fields->walk(
+ &Item::cleanup_excluding_const_fields_processor, 0, 0);
+ sl->cond_pushed_into_where= cond_over_grouping_fields;
+
+ if (!extracted_cond_copy)
+ continue;
+ }
+
+ /*
+ Transform the references to the 'derived' columns from the condition
+ pushed into the having clause of sl to make them usable in the new context
+ */
+ extracted_cond_copy= extracted_cond_copy->transform(thd,
+ &Item::derived_field_transformer_for_having,
+ (uchar*) sl);
+ if (!extracted_cond_copy)
+ continue;
+
+ extracted_cond_copy->walk(&Item::cleanup_excluding_const_fields_processor,
+ 0, 0);
+ sl->cond_pushed_into_having= extracted_cond_copy;
+ }
+ thd->lex->current_select= save_curr_select;
+ DBUG_RETURN(false);
+}
+