diff options
Diffstat (limited to 'sql')
-rw-r--r-- | sql/share/errmsg-utf8.txt | 4 | ||||
-rw-r--r-- | sql/sql_class.h | 21 | ||||
-rw-r--r-- | sql/sql_cte.cc | 304 | ||||
-rw-r--r-- | sql/sql_cte.h | 139 | ||||
-rw-r--r-- | sql/sql_derived.cc | 79 | ||||
-rw-r--r-- | sql/sql_lex.cc | 1 | ||||
-rw-r--r-- | sql/sql_lex.h | 13 | ||||
-rw-r--r-- | sql/sql_parse.cc | 2 | ||||
-rw-r--r-- | sql/sql_prepare.cc | 2 | ||||
-rw-r--r-- | sql/sql_select.cc | 8 | ||||
-rw-r--r-- | sql/sql_union.cc | 253 | ||||
-rw-r--r-- | sql/sql_yacc.yy | 17 | ||||
-rw-r--r-- | sql/table.cc | 79 | ||||
-rw-r--r-- | sql/table.h | 7 |
14 files changed, 856 insertions, 73 deletions
diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt index 376c1eb9d0d..f2a5666f1b1 100644 --- a/sql/share/errmsg-utf8.txt +++ b/sql/share/errmsg-utf8.txt @@ -7154,6 +7154,10 @@ ER_WRONG_ORDER_IN_WITH_CLAUSE eng "The definition of the table '%s' refers to the table '%s' defined later in a non-recursive WITH clause" ER_RECURSIVE_QUERY_IN_WITH_CLAUSE eng "Recursive queries in WITH clause are not supported yet" +ER_RECURSIVE_WITHOUT_ANCHORS + eng "No anchors for recursive WITH element '%s'" +ER_REF_TO_RECURSIVE_WITH_TABLE_IN_DERIVED + eng "Reference to recursive WITH table '%s' in materiazed derived" # # Internal errors, not used diff --git a/sql/sql_class.h b/sql/sql_class.h index e0792a4059f..0100a9807f5 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -4196,6 +4196,7 @@ protected: /* Something used only by the parser: */ public: select_result(THD *thd_arg): select_result_sink(thd_arg) {} + void set_unit(SELECT_LEX_UNIT *unit_arg) { unit= unit_arg; } virtual ~select_result() {}; /** Change wrapped select_result. @@ -4637,6 +4638,7 @@ public: } }; + class select_union :public select_result_interceptor { public: @@ -4674,6 +4676,25 @@ public: }; +class select_union_recursive :public select_union +{ + public: + TABLE *incr_table; + List<TABLE> rec_tables; + + select_union_recursive(THD *thd_arg): + select_union(thd_arg), incr_table(0) {}; + + int send_data(List<Item> &items); + bool create_result_table(THD *thd, List<Item> *column_types, + bool is_distinct, ulonglong options, + const char *alias, + bool bit_fields_as_long, + bool create_table, + bool keep_row_order= FALSE); + void cleanup(); +}; + /** UNION result that is passed directly to the receiving select_result without filling a temporary table. diff --git a/sql/sql_cte.cc b/sql/sql_cte.cc index 77f0bcf04ba..04d53495400 100644 --- a/sql/sql_cte.cc +++ b/sql/sql_cte.cc @@ -21,14 +21,17 @@ true on failure */ -bool check_dependencies_in_with_clauses(With_clause *with_clauses_list) +bool check_dependencies_in_with_clauses(THD *thd, 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()) + if (with_clause->check_dependencies(thd)) return true; + if (with_clause->check_anchors()) + return true; + with_clause->move_anchors_ahead(); } return false; } @@ -57,7 +60,7 @@ bool check_dependencies_in_with_clauses(With_clause *with_clauses_list) false otherwise */ -bool With_clause::check_dependencies() +bool With_clause::check_dependencies(THD *thd) { if (dependencies_are_checked) return false; @@ -84,18 +87,23 @@ bool With_clause::check_dependencies() return true; } } - with_elem->check_dependencies_in_unit(with_elem->spec); + if (with_elem->check_dependencies_in_spec(thd)) + 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) + 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) { table_map with_elem_map= with_elem->get_elem_map(); for (With_element *elem= first_elem; elem != NULL; elem= elem->next_elem) { - if (elem->dependency_map & with_elem_map) - elem->dependency_map |= with_elem->dependency_map; + if (elem->derived_dep_map & with_elem_map) + elem->derived_dep_map |= with_elem->derived_dep_map; } } @@ -107,7 +115,7 @@ bool With_clause::check_dependencies() with_elem != NULL; with_elem= with_elem->next_elem) { - if (with_elem->dependency_map & with_elem->get_elem_map()) + if (with_elem->derived_dep_map & with_elem->get_elem_map()) with_elem->is_recursive= true; } for (With_element *with_elem= first_elem; @@ -115,10 +123,12 @@ bool With_clause::check_dependencies() with_elem= with_elem->next_elem) { if (with_elem->is_recursive) - { + { +#if 0 my_error(ER_RECURSIVE_QUERY_IN_WITH_CLAUSE, MYF(0), with_elem->query_name->str); return true; +#endif } } @@ -152,7 +162,39 @@ bool With_clause::check_dependencies() } -/** +bool With_element::check_dependencies_in_spec(THD *thd) +{ + for (st_select_lex *sl= spec->first_select(); sl; sl= sl->next_select()) + { + check_dependencies_in_select(sl, sl->with_dep); + base_dep_map|= sl->with_dep; + } + return false; +} + + +void With_element::check_dependencies_in_select(st_select_lex *sl, table_map &dep_map) +{ + for (TABLE_LIST *tbl= sl->table_list.first; tbl; tbl= tbl->next_local) + { + tbl->with_internal_reference_map= 0; + if (!tbl->with) + tbl->with= owner->find_table_def(tbl); + if (!tbl->with && tbl->select_lex) + tbl->with= tbl->select_lex->find_table_def_in_with_clauses(tbl); + if (tbl->with && tbl->with->owner== this->owner) + { + dep_map|= tbl->with->get_elem_map(); + tbl->with_internal_reference_map= get_elem_map(); + } + } + 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, dep_map); +} + + + /** @brief Check dependencies on the sibling with tables used in the given unit @@ -166,24 +208,102 @@ bool With_clause::check_dependencies() dependency_map of this element. */ -void With_element::check_dependencies_in_unit(st_select_lex_unit *unit) +void With_element::check_dependencies_in_unit(st_select_lex_unit *unit, + table_map &dep_map) { st_select_lex *sl= unit->first_select(); for (; sl; sl= sl->next_select()) { - for (TABLE_LIST *tbl= sl->table_list.first; tbl; tbl= tbl->next_local) + check_dependencies_in_select(sl, dep_map); + } +} + + +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) + { + if (!with_elem->is_recursive) + continue; + + 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) { - if (!tbl->with) - tbl->with= owner->find_table_def(tbl); - if (!tbl->with && tbl->select_lex) - tbl->with= tbl->select_lex->find_table_def_in_with_clauses(tbl); - if (tbl->with && tbl->with->owner== this->owner) - set_dependency_on(tbl->with); + if (!elem->is_recursive) + continue; + + if (elem == with_elem || + ((elem->derived_dep_map & with_elem_map) && + (with_elem_dep & elem->get_elem_map()))) + { + with_elem->mutually_recursive|= elem->get_elem_map(); + elem->mutually_recursive|= with_elem_map; + } } - 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); + + for (st_select_lex *sl= with_elem->spec->first_select(); + sl; + sl= sl->next_select()) + { + if (!(with_elem->mutually_recursive & sl->with_dep)) + { + with_elem->with_anchor= true; + break; + } + } + } + + for (With_element *with_elem= first_elem; + with_elem != NULL; + with_elem= with_elem->next_elem) + { + if (!with_elem->is_recursive || with_elem->with_anchor) + continue; + + table_map anchored= 0; + for (With_element *elem= with_elem; + elem != NULL; + elem= elem->next_elem) + { + if (elem->mutually_recursive && elem->with_anchor) + anchored |= elem->get_elem_map(); + } + table_map non_anchored= with_elem->mutually_recursive & ~anchored; + with_elem->work_dep_map= non_anchored & with_elem->base_dep_map; } + + /*Building transitive clousure on work_dep_map*/ + for (With_element *with_elem= first_elem; + with_elem != NULL; + with_elem= with_elem->next_elem) + { + table_map with_elem_map= with_elem->get_elem_map(); + for (With_element *elem= first_elem; elem != NULL; elem= elem->next_elem) + { + if (elem->work_dep_map & with_elem_map) + elem->work_dep_map|= with_elem->work_dep_map; + } + } + + for (With_element *with_elem= first_elem; + with_elem != NULL; + with_elem= with_elem->next_elem) + { + if (with_elem->work_dep_map & with_elem->get_elem_map()) + { + my_error(ER_RECURSIVE_WITHOUT_ANCHORS, MYF(0), + with_elem->query_name->str); + return true; + } + } + + return false; } @@ -438,8 +558,8 @@ With_element::rename_columns_of_derived_unit(THD *thd, item->is_autogenerated_name= false; } } - - make_valid_column_names(thd, select->item_list); + else + make_valid_column_names(thd, select->item_list); unit->columns_are_renamed= true; @@ -486,6 +606,47 @@ 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); +} + + /** @brief Search for the definition of the given table referred in this select node @@ -540,7 +701,7 @@ With_element *st_select_lex::find_table_def_in_with_clauses(TABLE_LIST *table) bool TABLE_LIST::set_as_with_table(THD *thd, With_element *with_elem) { with= with_elem; - if (!with_elem->is_referenced()) + if (!with_elem->is_referenced() || with_elem->is_recursive) derived= with_elem->spec; else { @@ -553,6 +714,102 @@ bool TABLE_LIST::set_as_with_table(THD *thd, With_element *with_elem) } +bool TABLE_LIST::is_recursive_with_table() +{ + return with && with->is_recursive; +} + + +bool TABLE_LIST::is_with_table_recursive_reference() +{ + return (with_internal_reference_map && + (with->mutually_recursive & with_internal_reference_map)); +} + + + +bool st_select_lex::check_unrestricted_recursive() +{ + With_element *with_elem= get_with_element(); + if (!with_elem) + return false; + table_map unrestricted= 0; + table_map encountered= 0; + if (with_elem->check_unrestricted_recursive(this, + unrestricted, + encountered)) + return true; + with_elem->owner->unrestricted|= unrestricted; + return false; +} + + +bool With_element::check_unrestricted_recursive(st_select_lex *sel, + table_map &unrestricted, + table_map &encountered) +{ + List_iterator<TABLE_LIST> ti(sel->leaf_tables); + TABLE_LIST *tbl; + while ((tbl= ti++)) + { + if (tbl->get_unit() && !tbl->is_with_table()) + { + st_select_lex_unit *unit= tbl->get_unit(); + if (tbl->is_materialized_derived()) + { + table_map dep_map; + check_dependencies_in_unit(unit, dep_map); + if (dep_map & get_elem_map()) + { + my_error(ER_REF_TO_RECURSIVE_WITH_TABLE_IN_DERIVED, + MYF(0), query_name->str); + return true; + } + } + if (check_unrestricted_recursive(unit->first_select(), + unrestricted, + encountered)) + return true; + if (!(tbl->is_recursive_with_table() && unit->with_element->owner == owner)) + continue; + With_element *with_elem= unit->with_element; + if (encountered & with_elem->get_elem_map()) + unrestricted|= with_elem->mutually_recursive; + else + 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) + { + if (!with_elem->is_recursive && (unrestricted & with_elem->get_elem_map())) + continue; + if (encountered & with_elem->get_elem_map()) + { + uint cnt= 0; + table_map mutually_recursive= with_elem->mutually_recursive; + for (table_map map= mutually_recursive >> with_elem->number; + map != 0; + map>>= 1) + { + if (map & 1) + { + if (cnt) + { + unrestricted|= with_elem->mutually_recursive; + break; + } + else + cnt++; + } + } + } + } + return false; +} + + /** @brief Print this with clause @@ -602,3 +859,4 @@ void With_element::print(String *str, enum_query_type query_type) str->append(')'); } + diff --git a/sql/sql_cte.h b/sql/sql_cte.h index 0cbc9247af9..b559be93de5 100644 --- a/sql/sql_cte.h +++ b/sql/sql_cte.h @@ -4,6 +4,7 @@ #include "sql_lex.h" class With_clause; +class select_union; /** @class With_clause @@ -21,13 +22,22 @@ private: With_clause *owner; // with clause this object belongs to With_element *next_elem; // 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 /* - The map dependency_map has 1 in the i-th position if the query that - specifies this element contains a reference to the element number i + The map base_dep_map has 1 in the i-th position if the query that + specifies this with element contains a reference to the with element number i in the query FROM list. + (In this case this with element depends directly on the i-th with element.) */ - table_map elem_map; // The map where with only one 1 set in this->number - table_map dependency_map; + table_map base_dep_map; + /* + 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 work_dep_map; // dependency map used for work + /* Dependency map of with elements mutually recursive with this with element */ + table_map mutually_recursive; /* Total number of references to this element in the FROM lists of the queries that are in the scope of the element (including @@ -43,6 +53,8 @@ private: /* 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 @@ -64,20 +76,40 @@ public: */ bool is_recursive; + bool with_anchor; + + st_select_lex *first_recursive; + + uint level; + + select_union *partial_result; + select_union *final_result; + select_union_recursive *rec_result; + TABLE *result_table; + With_element(LEX_STRING *name, List <LEX_STRING> list, st_select_lex_unit *unit) - : next_elem(NULL), dependency_map(0), references(0), + : next_elem(NULL), base_dep_map(0), derived_dep_map(0), + work_dep_map(0), mutually_recursive(0), + references(0), table(NULL), query_name(name), column_list(list), spec(unit), - is_recursive(false) {} - - void check_dependencies_in_unit(st_select_lex_unit *unit); - + is_recursive(false), with_anchor(false), + partial_result(NULL), final_result(NULL), + rec_result(NULL), result_table(NULL) + { reset();} + + bool check_dependencies_in_spec(THD *thd); + + void check_dependencies_in_select(st_select_lex *sl, table_map &dep_map); + + void check_dependencies_in_unit(st_select_lex_unit *unit, table_map &dep_map); + void set_dependency_on(With_element *with_elem) - { dependency_map|= with_elem->get_elem_map(); } + { base_dep_map|= with_elem->get_elem_map(); } bool check_dependency_on(With_element *with_elem) - { return dependency_map & with_elem->get_elem_map(); } + { return base_dep_map & with_elem->get_elem_map(); } bool set_unparsed_spec(THD *thd, char *spec_start, char *spec_end); @@ -91,9 +123,42 @@ public: bool prepare_unreferenced(THD *thd); - void print(String *str, enum_query_type query_type); + bool check_unrestricted_recursive(st_select_lex *sel, + table_map &unrestricted, + table_map &encountered); + + void print(String *str, enum_query_type query_type); + + void set_table(TABLE *tab) { table= tab; } + + TABLE *get_table() { return table; } + + bool is_anchor(st_select_lex *sel); + + void move_anchors_ahead(); + + bool is_unrestricted(); + + bool is_with_prepared_anchor(); + + void mark_as_with_prepared_anchor(); + + bool is_cleaned(); + + void mark_as_cleaned(); + + void reset() + { + level= 0; + } + + void set_result_table(TABLE *tab) { result_table= tab; } friend class With_clause; + friend + bool st_select_lex::check_unrestricted_recursive(); + friend + bool TABLE_LIST::is_with_table_recursive_reference(); }; @@ -126,6 +191,10 @@ private: /* Set to true if dependencies between with elements have been checked */ bool dependencies_are_checked; + table_map unrestricted; + table_map with_prepared_anchor; + table_map cleaned; + public: /* If true the specifier RECURSIVE is present in the with clause */ bool with_recursive; @@ -133,7 +202,8 @@ public: With_clause(bool recursive_fl, With_clause *emb_with_clause) : owner(NULL), first_elem(NULL), elements(0), embedding_with_clause(emb_with_clause), next_with_clause(NULL), - dependencies_are_checked(false), + dependencies_are_checked(false), + unrestricted(0), with_prepared_anchor(0), cleaned(0), with_recursive(recursive_fl) { last_next= &first_elem; } @@ -159,7 +229,11 @@ public: With_clause *pop() { return embedding_with_clause; } - bool check_dependencies(); + bool check_dependencies(THD *thd); + + bool check_anchors(); + + void move_anchors_ahead(); With_element *find_table_def(TABLE_LIST *table); @@ -169,10 +243,45 @@ public: void print(String *str, enum_query_type query_type); + friend class With_element; + + friend + bool check_dependencies_in_with_clauses(THD *thd, With_clause *with_clauses_list); friend - bool check_dependencies_in_with_clauses(With_clause *with_clauses_list); + bool st_select_lex::check_unrestricted_recursive(); }; +inline +bool With_element::is_unrestricted() +{ + return owner->unrestricted & get_elem_map(); +} + +inline + +bool With_element::is_with_prepared_anchor() +{ + return owner->with_prepared_anchor & get_elem_map(); +} + +inline +void With_element::mark_as_with_prepared_anchor() +{ + owner->with_prepared_anchor|= mutually_recursive; +} + + +inline +bool With_element::is_cleaned() +{ + return owner->cleaned & get_elem_map(); +} + +inline +void With_element::mark_as_cleaned() +{ + owner->cleaned|= get_elem_map(); +} #endif /* SQL_CTE_INCLUDED */ diff --git a/sql/sql_derived.cc b/sql/sql_derived.cc index 79e57cded81..63302c1c6db 100644 --- a/sql/sql_derived.cc +++ b/sql/sql_derived.cc @@ -30,6 +30,7 @@ #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); @@ -627,6 +628,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(); @@ -634,6 +636,34 @@ bool mysql_derived_prepare(THD *thd, LEX *lex, TABLE_LIST *derived) bool res= FALSE; DBUG_PRINT("enter", ("unit 0x%lx", (ulong) unit)); + SELECT_LEX *first_select= unit->first_select(); + + if (unit->prepared && derived->is_recursive_with_table() && + !derived->table) + { + if (!(derived->derived_result= new (thd->mem_root) select_union(thd))) + DBUG_RETURN(TRUE); // out of memory + thd->create_tmp_table_for_derived= TRUE; + if (!derived->table) + 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, TRUE); + 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()) + 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 || (derived->merged_for_insert && @@ -642,16 +672,16 @@ bool mysql_derived_prepare(THD *thd, LEX *lex, TABLE_LIST *derived) 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()) + { + // Prepare underlying views/DT first. + 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 */ @@ -697,19 +727,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); @@ -756,8 +788,11 @@ exit: } #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) @@ -909,6 +944,14 @@ bool mysql_derived_fill(THD *thd, LEX *lex, TABLE_LIST *derived) SELECT_LEX_UNIT *unit= derived->get_unit(); bool res= FALSE; + if (derived->is_recursive_with_table() && unit->executed) + { + TABLE *src= unit->with_element->rec_result->table; + TABLE *dest= derived->table; + res= src->insert_all_rows_into(thd, dest, true); + DBUG_RETURN(res); + } + if (unit->executed && !unit->uncacheable && !unit->describe) DBUG_RETURN(FALSE); /*check that table creation passed without problems. */ @@ -919,6 +962,8 @@ bool mysql_derived_fill(THD *thd, LEX *lex, TABLE_LIST *derived) if (unit->is_union()) { // execute union without clean up + if (derived->is_recursive_with_table()) + unit->with_element->set_result_table(derived->table); res= unit->exec(); } else @@ -948,7 +993,9 @@ bool mysql_derived_fill(THD *thd, LEX *lex, TABLE_LIST *derived) res= TRUE; unit->executed= TRUE; } - if (res || !lex->describe) + if (res || + (!lex->describe && + !(unit->with_element && unit->with_element->is_recursive))) unit->cleanup(); lex->current_select= save_current_select; diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index de345b4dd1c..42058319fc9 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -2172,6 +2172,7 @@ void st_select_lex::init_select() m_non_agg_field_used= false; m_agg_func_used= false; name_visibility_map= 0; + with_dep= 0; join= 0; } diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 10247bd33a2..09463635b94 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -546,6 +546,14 @@ public: LEX_STRING *option= 0); virtual void set_lock_for_tables(thr_lock_type lock_type) {} void set_slave(st_select_lex_node *slave_arg) { slave= slave_arg; } + void move_node(st_select_lex_node *where_to_move) + { + if (where_to_move == this) + return; + *prev= next; + *where_to_move->prev= this; + next= where_to_move; + } st_select_lex_node *insert_chain_before(st_select_lex_node **ptr_pos_to_insert, st_select_lex_node *end_chain_node); friend class st_select_lex_unit; @@ -695,6 +703,7 @@ public: bool prepare(THD *thd, select_result *result, ulong additional_options); bool optimize(); bool exec(); + bool exec_recursive(); bool cleanup(); inline void unclean() { cleaned= 0; } void reinit_exec_mechanism(); @@ -911,6 +920,8 @@ public: /* namp of nesting SELECT visibility (for aggregate functions check) */ nesting_map name_visibility_map; + table_map with_dep; + void init_query(); void init_select(); st_select_lex_unit* master_unit() { return (st_select_lex_unit*) master; } @@ -1097,6 +1108,8 @@ public: return master_unit()->with_element; } With_element *find_table_def_in_with_clauses(TABLE_LIST *table); + bool check_unrestricted_recursive(); + List<Window_spec> window_specs; void prepare_add_window_spec(THD *thd); diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index a6bb89f05df..ecf27bd1239 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -6215,7 +6215,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(lex->with_clauses_list)) + if (check_dependencies_in_with_clauses(thd, lex->with_clauses_list)) return 1; if (!(res= open_and_lock_tables(thd, all_tables, TRUE, 0))) diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index 2d6a7302afc..453ca936a88 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -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(lex->with_clauses_list)) + if (check_dependencies_in_with_clauses(thd,lex->with_clauses_list)) goto error; if (tables) { diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 6c4d2e1fc9c..0a961b4a53a 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -3669,6 +3669,7 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list, s->checked_keys.init(); s->needed_reg.init(); table_vector[i]=s->table=table=tables->table; + s->tab_list= tables; table->pos_in_table_list= tables; error= tables->fetch_number_of_rows(); set_statistics_for_table(join->thd, table); @@ -11423,6 +11424,11 @@ bool error_if_full_join(JOIN *join) void JOIN_TAB::cleanup() { DBUG_ENTER("JOIN_TAB::cleanup"); + + if (tab_list && tab_list->is_with_table_recursive_reference() && + tab_list->with->is_cleaned()) + DBUG_VOID_RETURN; + DBUG_PRINT("enter", ("tab: %p table %s.%s", this, (table ? table->s->db.str : "?"), @@ -11592,7 +11598,7 @@ bool JOIN_TAB::preread_init() } /* Materialize derived table/view. */ - if (!derived->get_unit()->executed && + if ((!derived->get_unit()->executed || derived->is_recursive_with_table()) && mysql_handle_single_derived(join->thd->lex, derived, DT_CREATE | DT_FILL)) return TRUE; diff --git a/sql/sql_union.cc b/sql/sql_union.cc index 87b836f40d9..ac582c115d8 100644 --- a/sql/sql_union.cc +++ b/sql/sql_union.cc @@ -28,6 +28,8 @@ #include "sql_cursor.h" #include "sql_base.h" // fill_record #include "filesort.h" // filesort_free_buffers +#include "sql_view.h" +#include "sql_cte.h" bool mysql_union(THD *thd, LEX *lex, select_result *result, SELECT_LEX_UNIT *unit, ulong setup_tables_done_option) @@ -98,6 +100,26 @@ int select_union::send_data(List<Item> &values) return 0; } +int select_union_recursive::send_data(List<Item> &values) +{ + int rc= select_union::send_data(values); + + if (!write_err) + { + int err; + if ((err= incr_table->file->ha_write_tmp_row(table->record[0]))) + { + bool is_duplicate; + rc= create_internal_tmp_table_from_heap(thd, incr_table, + tmp_table_param.start_recinfo, + &tmp_table_param.recinfo, + err, 1, &is_duplicate); + } + } + + return rc; +} + bool select_union::send_eof() { @@ -171,6 +193,61 @@ select_union::create_result_table(THD *thd_arg, List<Item> *column_types, return FALSE; } +bool +select_union_recursive::create_result_table(THD *thd_arg, + List<Item> *column_types, + bool is_union_distinct, + ulonglong options, + const char *alias, + bool bit_fields_as_long, + bool create_table, + bool keep_row_order) +{ + if (select_union::create_result_table(thd_arg, column_types, + is_union_distinct, options, + alias, bit_fields_as_long, + create_table, keep_row_order)) + return true; + + if (! (incr_table= create_tmp_table(thd_arg, &tmp_table_param, *column_types, + (ORDER*) 0, false, 1, + options, HA_POS_ERROR, alias, + !create_table, keep_row_order))) + return true; + + incr_table->keys_in_use_for_query.clear_all(); + for (uint i=0; i < table->s->fields; i++) + incr_table->field[i]->flags &= ~PART_KEY_FLAG; + + if (create_table) + { + incr_table->file->extra(HA_EXTRA_WRITE_CACHE); + incr_table->file->extra(HA_EXTRA_IGNORE_DUP_KEY); + } + + TABLE *rec_table= 0; + if (! (rec_table= create_tmp_table(thd_arg, &tmp_table_param, *column_types, + (ORDER*) 0, false, 1, + options, HA_POS_ERROR, alias, + !create_table, keep_row_order))) + return true; + + rec_table->keys_in_use_for_query.clear_all(); + for (uint i=0; i < table->s->fields; i++) + rec_table->field[i]->flags &= ~PART_KEY_FLAG; + + if (create_table) + { + rec_table->file->extra(HA_EXTRA_WRITE_CACHE); + rec_table->file->extra(HA_EXTRA_IGNORE_DUP_KEY); + } + + if (rec_tables.push_back(rec_table)) + return true; + + return false; +} + /** Reset and empty the temporary table that stores the materialized query @@ -187,6 +264,29 @@ void select_union::cleanup() } +void select_union_recursive::cleanup() +{ + select_union::cleanup(); + free_tmp_table(thd, table); + + incr_table->file->extra(HA_EXTRA_RESET_STATE); + incr_table->file->ha_delete_all_rows(); + //free_io_cache(incr_table); + //filesort_free_buffers(incr_table,0); + free_tmp_table(thd, incr_table); + + List_iterator<TABLE> it(rec_tables); + TABLE *tab; + while ((tab= it++)) + { + tab->file->extra(HA_EXTRA_RESET_STATE); + tab->file->ha_delete_all_rows(); + //free_io_cache(tab); + //filesort_free_buffers(tab,0); + free_tmp_table(thd, tab); + } +} + /** Replace the current result with new_result and prepare it. @@ -332,11 +432,14 @@ st_select_lex_unit::init_prepare_fake_select_lex(THD *thd_arg, } + + bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, ulong additional_options) { SELECT_LEX *lex_select_save= thd_arg->lex->current_select; SELECT_LEX *sl, *first_sl= first_select(); + bool is_recursive= with_element && with_element->is_recursive; select_result *tmp_result; bool is_union_select; bool instantiate_tmp_table= false; @@ -404,8 +507,15 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, } else { - if (!(tmp_result= union_result= - new (thd_arg->mem_root) select_union(thd_arg))) + if (!is_recursive) + union_result= new (thd_arg->mem_root) select_union(thd_arg); + else + { + with_element->rec_result= + new (thd_arg->mem_root) select_union_recursive(thd_arg); + union_result= with_element->rec_result; + } + if (!(tmp_result= union_result)) goto err; /* purecov: inspected */ instantiate_tmp_table= true; } @@ -414,7 +524,7 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, tmp_result= sel_result; sl->context.resolve_in_select_list= TRUE; - + for (;sl; sl= sl->next_select()) { bool can_skip_order_by; @@ -477,6 +587,13 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, types= first_sl->item_list; else if (sl == first_sl) { + if (is_recursive) + { + if (derived->with->rename_columns_of_derived_unit(thd, this)) + goto err; + if (check_duplicate_names(thd, sl->item_list, 0)) + goto err; + } types.empty(); List_iterator_fast<Item> it(sl->item_list); Item *item_tmp; @@ -489,6 +606,23 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, if (thd_arg->is_fatal_error) goto err; // out of memory + + if (is_recursive) + { + + ulonglong create_options; + create_options= (first_sl->options | thd_arg->variables.option_bits | + TMP_TABLE_ALL_COLUMNS); + if (union_result->create_result_table(thd, &types, + MY_TEST(union_distinct), + create_options, "", false, + instantiate_tmp_table, false)) + goto err; + if (!derived->table) + derived->table= derived->derived_result->table= + with_element->rec_result->rec_tables.head(); + with_element->mark_as_with_prepared_anchor(); + } } else { @@ -507,6 +641,10 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, DBUG_RETURN(TRUE); } } + if (with_element && !with_element->is_anchor(sl)) + { + sl->uncacheable|= UNCACHEABLE_UNITED; + } } /* @@ -580,9 +718,11 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, if (global_parameters()->ftfunc_list->elements) create_options= create_options | TMP_TABLE_FORCE_MYISAM; - if (union_result->create_result_table(thd, &types, MY_TEST(union_distinct), - create_options, "", false, - instantiate_tmp_table)) + + if (!is_recursive && + union_result->create_result_table(thd, &types, MY_TEST(union_distinct), + create_options, "", false, + instantiate_tmp_table, false)) goto err; if (fake_select_lex && !fake_select_lex->first_cond_optimization) { @@ -784,6 +924,12 @@ bool st_select_lex_unit::exec() if (saved_error) DBUG_RETURN(saved_error); + if (with_element && with_element->is_recursive && !describe) + { + saved_error= exec_recursive(); + DBUG_RETURN(saved_error); + } + if (uncacheable || !item || !item->assigned() || describe) { if (!fake_select_lex) @@ -1009,6 +1155,89 @@ err: } + +bool st_select_lex_unit::exec_recursive() +{ + st_select_lex *lex_select_save= thd->lex->current_select; + st_select_lex *first_recursive_sel= with_element->first_recursive; + TABLE *incr_table= with_element->rec_result->incr_table; + TABLE *result_table= with_element->result_table; + ha_rows last_union_records= 0; + ha_rows examined_rows= 0; + bool unrestricted= with_element->is_unrestricted(); + bool is_stabilized= false; + DBUG_ENTER("st_select_lex_unit::exec_recursive"); + bool with_anchor= with_element->with_anchor; + st_select_lex *first_sl= first_select(); + st_select_lex *barrier= with_anchor ? first_recursive_sel : NULL; + List_iterator_fast<TABLE> li(with_element->rec_result->rec_tables); + TABLE *rec_table; + + do + { + if ((saved_error= incr_table->file->ha_delete_all_rows())) + goto err; + + for (st_select_lex *sl= first_sl ; sl != barrier; sl= sl->next_select()) + { + thd->lex->current_select= sl; + sl->join->exec(); + saved_error= sl->join->error; + if (!saved_error) + { + examined_rows+= thd->get_examined_row_count(); + thd->set_examined_row_count(0); + if (union_result->flush()) + { + thd->lex->current_select= lex_select_save; + DBUG_RETURN(1); + } + } + if (saved_error) + { + thd->lex->current_select= lex_select_save; + goto err; + } + } + + if (with_element->level == 0) + { + first_sl= first_recursive_sel; + barrier= NULL; + } + + table->file->info(HA_STATUS_VARIABLE); + if (table->file->stats.records == last_union_records) + { + is_stabilized= true; + } + else + { + last_union_records= table->file->stats.records; + with_element->level++; + } + li.rewind(); + while ((rec_table= li++)) + { + if ((saved_error= incr_table->insert_all_rows_into(thd, rec_table, + !unrestricted))) + goto err; + } + } while (!is_stabilized); + + if ((saved_error= table->insert_all_rows_into(thd, + result_table, + true))) + goto err; + + thd->lex->current_select= lex_select_save; +err: + thd->lex->set_limit_rows_examined(); + DBUG_RETURN(saved_error); + +} + + bool st_select_lex_unit::cleanup() { int error= 0; @@ -1023,6 +1252,13 @@ 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(); @@ -1046,7 +1282,10 @@ bool st_select_lex_unit::cleanup() } } - if (union_result) + if (with_element && with_element->is_recursive) + with_element->mark_as_cleaned(); + + if (union_result && !(with_element->is_recursive)) { delete union_result; union_result=0; // Safety diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 04c1ba7e99a..0d83efcae04 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -978,6 +978,7 @@ bool LEX::set_bincmp(CHARSET_INFO *cs, bool bin) List<Item> *item_list; List<Statement_information_item> *stmt_info_list; List<String> *string_list; + List<LEX_STRING> *lex_str_list; Statement_information_item *stmt_info_item; String *string; TABLE_LIST *table_list; @@ -2053,6 +2054,8 @@ END_OF_INPUT %type <lex_str_ptr> query_name +%type <lex_str_list> opt_with_column_list + %% @@ -14077,13 +14080,18 @@ with_list: with_list_element: query_name opt_with_column_list + { + $2= new List<LEX_STRING> (Lex->with_column_list); + if ($2 == NULL) + MYSQL_YYABORT; + Lex->with_column_list.empty(); + } AS '(' remember_name subselect remember_end ')' { - With_element *elem= new With_element($1, Lex->with_column_list, $6->master_unit()); + With_element *elem= new With_element($1, *$2, $7->master_unit()); if (elem == NULL || Lex->curr_with_clause->add_with_element(elem)) MYSQL_YYABORT; - Lex->with_column_list.empty(); - if (elem->set_unparsed_spec(thd, $5+1, $7)) + if (elem->set_unparsed_spec(thd, $6+1, $8)) MYSQL_YYABORT; } ; @@ -14091,8 +14099,9 @@ with_list_element: opt_with_column_list: /* empty */ - {} + { $$= NULL; } | '(' with_column_list ')' + { $$= NULL; } ; diff --git a/sql/table.cc b/sql/table.cc index dc1730b5b6f..6109c16fb37 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -41,6 +41,7 @@ #include "mdl.h" // MDL_wait_for_graph_visitor #include "sql_view.h" #include "rpl_filter.h" +#include "sql_cte.h" /* INFORMATION_SCHEMA name */ LEX_STRING INFORMATION_SCHEMA_NAME= {C_STRING_WITH_LEN("information_schema")}; @@ -7089,7 +7090,7 @@ bool TABLE::validate_default_values_of_unset_fields(THD *thd) const /* We're here if: - validate_value_in_record_with_warn() failed and - strict mode converted WARN to ERROR + strict mo validate_default_values_of_unset_fieldsde converted WARN to ERROR - or the connection was killed, or closed unexpectedly */ DBUG_RETURN(true); @@ -7100,6 +7101,59 @@ bool TABLE::validate_default_values_of_unset_fields(THD *thd) const } +bool TABLE::insert_all_rows_into(THD *thd, TABLE *dest, bool with_cleanup) +{ + int write_err= 0; + + DBUG_ENTER("TABLE::insert_all_rows_into"); + + if (with_cleanup) + { + if ((write_err= dest->file->ha_delete_all_rows())) + goto err; + } + + if (file->indexes_are_disabled()) + dest->file->ha_disable_indexes(HA_KEY_SWITCH_ALL); + file->ha_index_or_rnd_end(); + + if (file->ha_rnd_init_with_error(1)) + DBUG_RETURN(1); + + if (dest->no_rows) + dest->file->extra(HA_EXTRA_NO_ROWS); + else + { + /* update table->file->stats.records */ + file->info(HA_STATUS_VARIABLE); + dest->file->ha_start_bulk_insert(file->stats.records); + } + + while (!file->ha_rnd_next(dest->record[1])) + { + write_err= dest->file->ha_write_tmp_row(dest->record[1]); + if (write_err) + goto err; + if (thd->check_killed()) + { + thd->send_kill_message(); + goto err_killed; + } + } + if (!dest->no_rows && dest->file->ha_end_bulk_insert()) + goto err; + DBUG_RETURN(0); + +err: + DBUG_PRINT("error",("Got error: %d",write_err)); + file->print_error(write_err, MYF(0)); +err_killed: + (void) file->ha_rnd_end(); + DBUG_RETURN(1); +} + + + /* @brief Reset const_table flag @@ -7140,20 +7194,34 @@ void TABLE_LIST::reset_const_table() bool TABLE_LIST::handle_derived(LEX *lex, uint phases) { - SELECT_LEX_UNIT *unit; + SELECT_LEX_UNIT *unit= get_unit(); DBUG_ENTER("handle_derived"); DBUG_PRINT("enter", ("phases: 0x%x", phases)); - if ((unit= get_unit())) + + if (is_with_table_recursive_reference()) + { + if (!(with->with_anchor || with->is_with_prepared_anchor())) + { + for (SELECT_LEX *sl= unit->first_select(); sl; sl= sl->next_select()) + if (sl->handle_derived(lex, phases)) + DBUG_RETURN(TRUE); + } + else if (mysql_handle_single_derived(lex, this, phases)) + DBUG_RETURN(TRUE); + DBUG_RETURN(FALSE); + } + + if (unit) { for (SELECT_LEX *sl= unit->first_select(); sl; sl= sl->next_select()) if (sl->handle_derived(lex, phases)) DBUG_RETURN(TRUE); - DBUG_RETURN(mysql_handle_single_derived(lex, this, phases)); + if (mysql_handle_single_derived(lex, this, phases)) + DBUG_RETURN(TRUE); } DBUG_RETURN(FALSE); } - /** @brief Return unit of this derived table/view @@ -7430,6 +7498,7 @@ bool TABLE_LIST::is_with_table() return derived && derived->with_element; } + uint TABLE_SHARE::actual_n_key_parts(THD *thd) { return use_ext_keys && diff --git a/sql/table.h b/sql/table.h index a105df31e93..122b036cae5 100644 --- a/sql/table.h +++ b/sql/table.h @@ -1416,6 +1416,8 @@ public: inline Field **field_to_fill(); bool validate_default_values_of_unset_fields(THD *thd) const; + + bool insert_all_rows_into(THD *thd, TABLE *dest, bool with_cleanup); }; @@ -1856,6 +1858,8 @@ struct TABLE_LIST */ 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; + bool block_handle_derived; ST_SCHEMA_TABLE *schema_table; /* Information_schema table */ st_select_lex *schema_select_lex; /* @@ -2227,6 +2231,9 @@ struct TABLE_LIST return (derived_type & DTYPE_TABLE); } bool is_with_table(); + bool is_recursive_with_table(); + bool is_with_table_recursive_reference(); + inline void set_view() { derived_type= DTYPE_VIEW; |