diff options
Diffstat (limited to 'sql/sql_cte.h')
-rw-r--r-- | sql/sql_cte.h | 551 |
1 files changed, 551 insertions, 0 deletions
diff --git a/sql/sql_cte.h b/sql/sql_cte.h new file mode 100644 index 00000000..6a1f67d3 --- /dev/null +++ b/sql/sql_cte.h @@ -0,0 +1,551 @@ +/* + Copyright (c) 2016, 2017 MariaDB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + +#ifndef SQL_CTE_INCLUDED +#define SQL_CTE_INCLUDED +#include "sql_list.h" +#include "sql_lex.h" +#include "sql_select.h" + +class select_unit; +struct st_unit_ctxt_elem; + + +/** + @class With_element_head + @brief Head of the definition of a CTE table + + It contains the name of the CTE and it contains the position of the subchain + of table references used in the definition in the global chain of table + references used in the query where this definition is encountered. +*/ + +class With_element_head : public Sql_alloc +{ + /* The name of the defined CTE */ + LEX_CSTRING *query_name; + +public: + /* + The structure describing the subchain of the table references used in + the specification of the defined CTE in the global chain of table + references used in the query. The structure is fully defined only + after the CTE definition has been parsed. + */ + TABLE_CHAIN tables_pos; + + With_element_head(LEX_CSTRING *name) + : query_name(name) + { + tables_pos.set_start_pos(0); + tables_pos.set_end_pos(0); + } + friend class With_element; +}; + + +/** + @class With_element + @brief Definition of a CTE table + + It contains a reference to the name of the table introduced by this with element, + and a reference to the unit that specificies this table. Also it contains + a reference to the with clause to which this element belongs to. +*/ + +class With_element : public Sql_alloc +{ +private: + With_clause *owner; // with clause this object belongs to + 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 + /* + 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 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; + /* + 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; + /* + Dependency map built only for the top level references i.e. for those that + are encountered in from lists of the selects of the specification unit + */ + table_map top_level_dep_map; + /* + Points to a recursive reference in subqueries. + Used only for specifications without recursive references on the top level. + */ + TABLE_LIST *sq_rec_ref; + /* + 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 + the queries that are in the scope of the element (including + subqueries and specifications of other with elements). + */ + uint references; + + /* + true <=> this With_element is referred in the query in which the + element is defined + */ + bool referenced; + + /* + true <=> this With_element is needed for the execution of the query + in which the element is defined + */ + bool is_used_in_query; + + /* + Unparsed specification of the query that specifies this element. + It's used to build clones of the specification if they are needed. + */ + LEX_CSTRING unparsed_spec; + /* Offset of the specification in the input string */ + my_ptrdiff_t unparsed_spec_offset; + + /* True if the with element is used a prepared statement */ + bool stmt_prepare_mode; + + /* Return the map where 1 is set only in the position for this element */ + table_map get_elem_map() { return (table_map) 1 << number; } + +public: + /* + Contains the name of the defined With element and the position of + the subchain of the tables references used by its definition in the + global chain of TABLE_LIST objects created for the whole query. + */ + With_element_head *head; + + /* + Optional list of column names to name the columns of the table introduced + by this with element. It is used in the case when the names are not + inherited from the query that specified the table. Otherwise the list is + always empty. + */ + List <Lex_ident_sys> column_list; + List <Lex_ident_sys> *cycle_list; + /* The query that specifies the table introduced by this with element */ + st_select_lex_unit *spec; + /* + Set to true is recursion is used (directly or indirectly) + for the definition of this element + */ + bool is_recursive; + /* + For a simple recursive CTE: the number of references to the CTE from + outside of the CTE specification. + For a CTE mutually recursive with other CTEs : the total number of + references to all these CTEs outside of their specification. + Each of these mutually recursive CTEs has the same value in this field. + */ + uint rec_outer_references; + /* + 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 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; + + /* List of Item_subselects containing recursive references to this CTE */ + SQL_I_List<Item_subselect> sq_with_rec_ref; + /* List of derived tables containing recursive references to this CTE */ + SQL_I_List<TABLE_LIST> derived_with_rec_ref; + + With_element(With_element_head *h, + List <Lex_ident_sys> list, + st_select_lex_unit *unit) + : next(NULL), base_dep_map(0), derived_dep_map(0), + sq_dep_map(0), work_dep_map(0), mutually_recursive(0), + top_level_dep_map(0), sq_rec_ref(NULL), + next_mutually_recursive(NULL), references(0), + referenced(false), is_used_in_query(false), + head(h), column_list(list), cycle_list(0), spec(unit), + is_recursive(false), rec_outer_references(0), with_anchor(false), + level(0), rec_result(NULL) + { unit->with_element= this; } + + LEX_CSTRING *get_name() { return head->query_name; } + const char *get_name_str() { return get_name()->str; } + + void set_tables_start_pos(TABLE_LIST **pos) + { head->tables_pos.set_start_pos(pos); } + void set_tables_end_pos(TABLE_LIST **pos) + { head->tables_pos.set_end_pos(pos); } + + 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); + + void check_dependencies_in_unit(st_select_lex_unit *unit, + st_unit_ctxt_elem *ctxt, + bool in_subq, + table_map *dep_map); + + void check_dependencies_in_with_clause(With_clause *with_clause, + st_unit_ctxt_elem *ctxt, + bool in_subq, + table_map *dep_map); + + void set_dependency_on(With_element *with_elem) + { base_dep_map|= with_elem->get_elem_map(); } + + bool check_dependency_on(With_element *with_elem) + { return base_dep_map & with_elem->get_elem_map(); } + + TABLE_LIST *find_first_sq_rec_ref_in_select(st_select_lex *sel); + + bool set_unparsed_spec(THD *thd, const char *spec_start, const char *spec_end, + my_ptrdiff_t spec_offset); + + st_select_lex_unit *clone_parsed_spec(LEX *old_lex, TABLE_LIST *with_table); + + bool is_referenced() { return referenced; } + + bool is_hanging_recursive() { return is_recursive && !rec_outer_references; } + + void inc_references() { references++; } + + bool process_columns_of_derived_unit(THD *thd, st_select_lex_unit *unit); + + bool prepare_unreferenced(THD *thd); + + bool check_unrestricted_recursive(st_select_lex *sel, + table_map &unrestricted, + table_map &encountered); + + void print(THD *thd, String *str, enum_query_type query_type); + + With_clause *get_owner() { return owner; } + + bool contains_sq_with_recursive_reference() + { return sq_dep_map & mutually_recursive; } + + bool no_rec_ref_on_top_level() + { return !(top_level_dep_map & mutually_recursive); } + + table_map get_mutually_recursive() { return mutually_recursive; } + + With_element *get_next_mutually_recursive() + { return next_mutually_recursive; } + + TABLE_LIST *get_sq_rec_ref() { return sq_rec_ref; } + + 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_recursive_for_exec(); + + void cleanup_stabilized(); + + void set_as_stabilized(); + + bool is_stabilized(); + + bool all_are_stabilized(); + + bool instantiate_tmp_tables(); + + void prepare_for_next_iteration(); + + void set_cycle_list(List<Lex_ident_sys> *cycle_list_arg); + + friend class With_clause; + + friend + bool LEX::resolve_references_to_cte(TABLE_LIST *tables, + TABLE_LIST **tables_last); +}; + +const uint max_number_of_elements_in_with_clause= sizeof(table_map)*8; + +/** + @class With_clause + @brief Set of with_elements + + It has a reference to the first with element from this with clause. + This reference allows to navigate through all the elements of the with clause. + It contains a reference to the unit to which this with clause is attached. + It also contains a flag saying whether this with clause was specified as recursive. +*/ + +class With_clause : public Sql_alloc +{ +private: + st_select_lex_unit *owner; // the unit this with clause attached to + + /* 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. + */ + With_clause *embedding_with_clause; + /* + The next with the clause of the chain of with clauses encountered + in the current statement + */ + With_clause *next_with_clause; + /* 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: + /* If true the specifier RECURSIVE is present in the with clause */ + bool with_recursive; + + With_clause(bool recursive_fl, With_clause *emb_with_clause) + : 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), + with_recursive(recursive_fl) + { } + + bool add_with_element(With_element *elem); + + /* Add this with clause to the list of with clauses used in the statement */ + void add_to_list(With_clause **ptr, With_clause ** &last_next) + { + if (embedding_with_clause) + { + /* + An embedded with clause is always placed before the embedding one + in the list of with clauses used in the query. + */ + while (*ptr != embedding_with_clause) + ptr= &(*ptr)->next_with_clause; + *ptr= this; + next_with_clause= embedding_with_clause; + } + else + { + *last_next= this; + last_next= &this->next_with_clause; + } + } + + st_select_lex_unit *get_owner() { return owner; } + + void set_owner(st_select_lex_unit *unit) { owner= unit; } + + void attach_to(st_select_lex *select_lex); + + With_clause *pop() { return embedding_with_clause; } + + bool check_dependencies(); + + bool check_anchors(); + + void move_anchors_ahead(); + + With_element *find_table_def(TABLE_LIST *table, With_element *barrier); + + With_element *find_table_def_in_with_clauses(TABLE_LIST *table); + + bool prepare_unreferenced_elements(THD *thd); + + void add_unrestricted(table_map map) { unrestricted|= map; } + + void print(THD *thd, String *str, enum_query_type query_type); + + friend class With_element; + + friend + bool LEX::check_dependencies_in_with_clauses(); +}; + +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(); +} + + +inline +void With_element::reset_recursive_for_exec() +{ + DBUG_ASSERT(is_recursive); + level= 0; + owner->with_prepared_anchor&= ~mutually_recursive; + owner->cleaned&= ~get_elem_map(); + cleanup_stabilized(); + spec->columns_are_renamed= false; +} + + + +inline +void With_element::cleanup_stabilized() +{ + owner->stabilized&= ~mutually_recursive; +} + + +inline +void With_element::set_as_stabilized() +{ + owner->stabilized|= get_elem_map(); +} + + +inline +bool With_element::is_stabilized() +{ + return owner->stabilized & get_elem_map(); +} + + +inline +bool With_element::all_are_stabilized() +{ + return (owner->stabilized & mutually_recursive) == mutually_recursive; +} + + +inline +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->rec_result->first_rec_table_to_update; + if (rec_table) + rec_table->reginfo.join_tab->preread_init_done= false; + } +} + + +inline +void With_clause::attach_to(st_select_lex *select_lex) +{ + for (With_element *with_elem= with_list.first; + with_elem; + with_elem= with_elem->next) + { + select_lex->register_unit(with_elem->spec, NULL); + } +} + + +inline +void st_select_lex::set_with_clause(With_clause *with_clause) +{ + master_unit()->with_clause= with_clause; + if (with_clause) + with_clause->set_owner(master_unit()); +} + +#endif /* SQL_CTE_INCLUDED */ |