| 1 | /* |
| 2 | Copyright (c) 2016, 2017 MariaDB |
| 3 | |
| 4 | This program is free software; you can redistribute it and/or modify |
| 5 | it under the terms of the GNU General Public License as published by |
| 6 | the Free Software Foundation; version 2 of the License. |
| 7 | |
| 8 | This program is distributed in the hope that it will be useful, |
| 9 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 11 | GNU General Public License for more details. |
| 12 | |
| 13 | You should have received a copy of the GNU General Public License |
| 14 | along with this program; if not, write to the Free Software |
| 15 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ |
| 16 | |
| 17 | #ifndef SQL_CTE_INCLUDED |
| 18 | #define SQL_CTE_INCLUDED |
| 19 | #include "sql_list.h" |
| 20 | #include "sql_lex.h" |
| 21 | #include "sql_select.h" |
| 22 | |
| 23 | class select_unit; |
| 24 | struct st_unit_ctxt_elem; |
| 25 | |
| 26 | |
| 27 | /** |
| 28 | @class With_element |
| 29 | @brief Definition of a CTE table |
| 30 | |
| 31 | It contains a reference to the name of the table introduced by this with element, |
| 32 | and a reference to the unit that specificies this table. Also it contains |
| 33 | a reference to the with clause to which this element belongs to. |
| 34 | */ |
| 35 | |
| 36 | class With_element : public Sql_alloc |
| 37 | { |
| 38 | private: |
| 39 | With_clause *owner; // with clause this object belongs to |
| 40 | With_element *next; // next element in the with clause |
| 41 | uint number; // number of the element in the with clause (starting from 0) |
| 42 | table_map elem_map; // The map where with only one 1 set in this->number |
| 43 | /* |
| 44 | The map base_dep_map has 1 in the i-th position if the query that |
| 45 | specifies this with element contains a reference to the with element number i |
| 46 | in the query FROM list. |
| 47 | (In this case this with element depends directly on the i-th with element.) |
| 48 | */ |
| 49 | table_map base_dep_map; |
| 50 | /* |
| 51 | The map derived_dep_map has 1 in i-th position if this with element depends |
| 52 | directly or indirectly from the i-th with element. |
| 53 | */ |
| 54 | table_map derived_dep_map; |
| 55 | /* |
| 56 | The map sq_dep_map has 1 in i-th position if there is a reference to this |
| 57 | with element somewhere in subqueries of the specifications of the tables |
| 58 | defined in the with clause containing this element; |
| 59 | */ |
| 60 | table_map sq_dep_map; |
| 61 | table_map work_dep_map; // dependency map used for work |
| 62 | /* Dependency map of with elements mutually recursive with this with element */ |
| 63 | table_map mutually_recursive; |
| 64 | /* |
| 65 | Dependency map built only for the top level references i.e. for those that |
| 66 | are encountered in from lists of the selects of the specification unit |
| 67 | */ |
| 68 | table_map top_level_dep_map; |
| 69 | /* |
| 70 | Points to a recursive reference in subqueries. |
| 71 | Used only for specifications without recursive references on the top level. |
| 72 | */ |
| 73 | TABLE_LIST *sq_rec_ref; |
| 74 | /* |
| 75 | The next with element from the circular chain of the with elements |
| 76 | mutually recursive with this with element. |
| 77 | (If This element is simply recursive than next_mutually_recursive contains |
| 78 | the pointer to itself. If it's not recursive than next_mutually_recursive |
| 79 | is set to NULL.) |
| 80 | */ |
| 81 | With_element *next_mutually_recursive; |
| 82 | /* |
| 83 | Total number of references to this element in the FROM lists of |
| 84 | the queries that are in the scope of the element (including |
| 85 | subqueries and specifications of other with elements). |
| 86 | */ |
| 87 | uint references; |
| 88 | /* |
| 89 | Unparsed specification of the query that specifies this element. |
| 90 | It used to build clones of the specification if they are needed. |
| 91 | */ |
| 92 | LEX_CSTRING unparsed_spec; |
| 93 | |
| 94 | /* Return the map where 1 is set only in the position for this element */ |
| 95 | table_map get_elem_map() { return (table_map) 1 << number; } |
| 96 | |
| 97 | public: |
| 98 | /* |
| 99 | The name of the table introduced by this with elememt. The name |
| 100 | can be used in FROM lists of the queries in the scope of the element. |
| 101 | */ |
| 102 | LEX_CSTRING *query_name; |
| 103 | /* |
| 104 | Optional list of column names to name the columns of the table introduced |
| 105 | by this with element. It is used in the case when the names are not |
| 106 | inherited from the query that specified the table. Otherwise the list is |
| 107 | always empty. |
| 108 | */ |
| 109 | List <LEX_CSTRING> column_list; |
| 110 | /* The query that specifies the table introduced by this with element */ |
| 111 | st_select_lex_unit *spec; |
| 112 | /* |
| 113 | Set to true is recursion is used (directly or indirectly) |
| 114 | for the definition of this element |
| 115 | */ |
| 116 | bool is_recursive; |
| 117 | |
| 118 | /* |
| 119 | Any non-recursive select in the specification of a recursive |
| 120 | with element is a called anchor. In the case mutually recursive |
| 121 | elements the specification of some them may be without any anchor. |
| 122 | Yet at least one of them must contain an anchor. |
| 123 | All anchors of any recursivespecification are moved ahead before |
| 124 | the prepare stage. |
| 125 | */ |
| 126 | /* Set to true if this is a recursive element with an anchor */ |
| 127 | bool with_anchor; |
| 128 | /* |
| 129 | Set to the first recursive select of the unit specifying the element |
| 130 | after all anchor have been moved to the head of the unit. |
| 131 | */ |
| 132 | st_select_lex *first_recursive; |
| 133 | |
| 134 | /* |
| 135 | The number of the last performed iteration for recursive table |
| 136 | (the number of the initial non-recursive step is 0, the number |
| 137 | of the first iteration is 1). |
| 138 | */ |
| 139 | uint level; |
| 140 | |
| 141 | /* |
| 142 | The pointer to the object used to materialize this with element |
| 143 | if it's recursive. This object is built at the end of prepare |
| 144 | stage and is used at the execution stage. |
| 145 | */ |
| 146 | select_union_recursive *rec_result; |
| 147 | |
| 148 | /* List of Item_subselects containing recursive references to this CTE */ |
| 149 | SQL_I_List<Item_subselect> sq_with_rec_ref; |
| 150 | /* List of derived tables containing recursive references to this CTE */ |
| 151 | SQL_I_List<TABLE_LIST> derived_with_rec_ref; |
| 152 | |
| 153 | With_element(LEX_CSTRING *name, |
| 154 | List <LEX_CSTRING> list, |
| 155 | st_select_lex_unit *unit) |
| 156 | : next(NULL), base_dep_map(0), derived_dep_map(0), |
| 157 | sq_dep_map(0), work_dep_map(0), mutually_recursive(0), |
| 158 | top_level_dep_map(0), sq_rec_ref(NULL), |
| 159 | next_mutually_recursive(NULL), references(0), |
| 160 | query_name(name), column_list(list), spec(unit), |
| 161 | is_recursive(false), with_anchor(false), |
| 162 | level(0), rec_result(NULL) |
| 163 | { unit->with_element= this; } |
| 164 | |
| 165 | bool check_dependencies_in_spec(); |
| 166 | |
| 167 | void check_dependencies_in_select(st_select_lex *sl, st_unit_ctxt_elem *ctxt, |
| 168 | bool in_subq, table_map *dep_map); |
| 169 | |
| 170 | void check_dependencies_in_unit(st_select_lex_unit *unit, |
| 171 | st_unit_ctxt_elem *ctxt, |
| 172 | bool in_subq, |
| 173 | table_map *dep_map); |
| 174 | |
| 175 | void check_dependencies_in_with_clause(With_clause *with_clause, |
| 176 | st_unit_ctxt_elem *ctxt, |
| 177 | bool in_subq, |
| 178 | table_map *dep_map); |
| 179 | |
| 180 | void set_dependency_on(With_element *with_elem) |
| 181 | { base_dep_map|= with_elem->get_elem_map(); } |
| 182 | |
| 183 | bool check_dependency_on(With_element *with_elem) |
| 184 | { return base_dep_map & with_elem->get_elem_map(); } |
| 185 | |
| 186 | TABLE_LIST *find_first_sq_rec_ref_in_select(st_select_lex *sel); |
| 187 | |
| 188 | bool set_unparsed_spec(THD *thd, char *spec_start, char *spec_end); |
| 189 | |
| 190 | st_select_lex_unit *clone_parsed_spec(THD *thd, TABLE_LIST *with_table); |
| 191 | |
| 192 | bool is_referenced() { return references != 0; } |
| 193 | |
| 194 | void inc_references() { references++; } |
| 195 | |
| 196 | bool rename_columns_of_derived_unit(THD *thd, st_select_lex_unit *unit); |
| 197 | |
| 198 | bool prepare_unreferenced(THD *thd); |
| 199 | |
| 200 | bool check_unrestricted_recursive(st_select_lex *sel, |
| 201 | table_map &unrestricted, |
| 202 | table_map &encountered); |
| 203 | |
| 204 | void print(String *str, enum_query_type query_type); |
| 205 | |
| 206 | With_clause *get_owner() { return owner; } |
| 207 | |
| 208 | bool contains_sq_with_recursive_reference() |
| 209 | { return sq_dep_map & mutually_recursive; } |
| 210 | |
| 211 | bool no_rec_ref_on_top_level() |
| 212 | { return !(top_level_dep_map & mutually_recursive); } |
| 213 | |
| 214 | table_map get_mutually_recursive() { return mutually_recursive; } |
| 215 | |
| 216 | With_element *get_next_mutually_recursive() |
| 217 | { return next_mutually_recursive; } |
| 218 | |
| 219 | TABLE_LIST *get_sq_rec_ref() { return sq_rec_ref; } |
| 220 | |
| 221 | bool is_anchor(st_select_lex *sel); |
| 222 | |
| 223 | void move_anchors_ahead(); |
| 224 | |
| 225 | bool is_unrestricted(); |
| 226 | |
| 227 | bool is_with_prepared_anchor(); |
| 228 | |
| 229 | void mark_as_with_prepared_anchor(); |
| 230 | |
| 231 | bool is_cleaned(); |
| 232 | |
| 233 | void mark_as_cleaned(); |
| 234 | |
| 235 | void reset_recursive_for_exec(); |
| 236 | |
| 237 | void cleanup_stabilized(); |
| 238 | |
| 239 | void set_as_stabilized(); |
| 240 | |
| 241 | bool is_stabilized(); |
| 242 | |
| 243 | bool all_are_stabilized(); |
| 244 | |
| 245 | bool instantiate_tmp_tables(); |
| 246 | |
| 247 | void prepare_for_next_iteration(); |
| 248 | |
| 249 | friend class With_clause; |
| 250 | }; |
| 251 | |
| 252 | const uint max_number_of_elements_in_with_clause= sizeof(table_map)*8; |
| 253 | |
| 254 | /** |
| 255 | @class With_clause |
| 256 | @brief Set of with_elements |
| 257 | |
| 258 | It has a reference to the first with element from this with clause. |
| 259 | This reference allows to navigate through all the elements of the with clause. |
| 260 | It contains a reference to the unit to which this with clause is attached. |
| 261 | It also contains a flag saying whether this with clause was specified as recursive. |
| 262 | */ |
| 263 | |
| 264 | class With_clause : public Sql_alloc |
| 265 | { |
| 266 | private: |
| 267 | st_select_lex_unit *owner; // the unit this with clause attached to |
| 268 | |
| 269 | /* The list of all with elements from this with clause */ |
| 270 | SQL_I_List<With_element> with_list; |
| 271 | /* |
| 272 | The with clause immediately containing this with clause if there is any, |
| 273 | otherwise NULL. Now used only at parsing. |
| 274 | */ |
| 275 | With_clause *embedding_with_clause; |
| 276 | /* |
| 277 | The next with the clause of the chain of with clauses encountered |
| 278 | in the current statement |
| 279 | */ |
| 280 | With_clause *next_with_clause; |
| 281 | /* Set to true if dependencies between with elements have been checked */ |
| 282 | bool dependencies_are_checked; |
| 283 | |
| 284 | /* |
| 285 | The bitmap of all recursive with elements whose specifications |
| 286 | are not complied with restrictions imposed by the SQL standards |
| 287 | on recursive specifications. |
| 288 | */ |
| 289 | table_map unrestricted; |
| 290 | /* |
| 291 | The bitmap of all recursive with elements whose anchors |
| 292 | has been already prepared. |
| 293 | */ |
| 294 | table_map with_prepared_anchor; |
| 295 | table_map cleaned; |
| 296 | /* |
| 297 | The bitmap of all recursive with elements that |
| 298 | has been already materialized |
| 299 | */ |
| 300 | table_map stabilized; |
| 301 | |
| 302 | public: |
| 303 | /* If true the specifier RECURSIVE is present in the with clause */ |
| 304 | bool with_recursive; |
| 305 | |
| 306 | With_clause(bool recursive_fl, With_clause *emb_with_clause) |
| 307 | : owner(NULL), |
| 308 | embedding_with_clause(emb_with_clause), next_with_clause(NULL), |
| 309 | dependencies_are_checked(false), unrestricted(0), |
| 310 | with_prepared_anchor(0), cleaned(0), stabilized(0), |
| 311 | with_recursive(recursive_fl) |
| 312 | { } |
| 313 | |
| 314 | bool add_with_element(With_element *elem); |
| 315 | |
| 316 | /* Add this with clause to the list of with clauses used in the statement */ |
| 317 | void add_to_list(With_clause ** &last_next) |
| 318 | { |
| 319 | *last_next= this; |
| 320 | last_next= &this->next_with_clause; |
| 321 | } |
| 322 | |
| 323 | void set_owner(st_select_lex_unit *unit) { owner= unit; } |
| 324 | |
| 325 | With_clause *pop() { return embedding_with_clause; } |
| 326 | |
| 327 | bool check_dependencies(); |
| 328 | |
| 329 | bool check_anchors(); |
| 330 | |
| 331 | void move_anchors_ahead(); |
| 332 | |
| 333 | With_element *find_table_def(TABLE_LIST *table, With_element *barrier); |
| 334 | |
| 335 | With_element *find_table_def_in_with_clauses(TABLE_LIST *table); |
| 336 | |
| 337 | bool prepare_unreferenced_elements(THD *thd); |
| 338 | |
| 339 | void add_unrestricted(table_map map) { unrestricted|= map; } |
| 340 | |
| 341 | void print(String *str, enum_query_type query_type); |
| 342 | |
| 343 | friend class With_element; |
| 344 | |
| 345 | friend |
| 346 | bool |
| 347 | check_dependencies_in_with_clauses(With_clause *with_clauses_list); |
| 348 | }; |
| 349 | |
| 350 | inline |
| 351 | bool With_element::is_unrestricted() |
| 352 | { |
| 353 | return owner->unrestricted & get_elem_map(); |
| 354 | } |
| 355 | |
| 356 | inline |
| 357 | |
| 358 | bool With_element::is_with_prepared_anchor() |
| 359 | { |
| 360 | return owner->with_prepared_anchor & get_elem_map(); |
| 361 | } |
| 362 | |
| 363 | inline |
| 364 | void With_element::mark_as_with_prepared_anchor() |
| 365 | { |
| 366 | owner->with_prepared_anchor|= mutually_recursive; |
| 367 | } |
| 368 | |
| 369 | |
| 370 | inline |
| 371 | bool With_element::is_cleaned() |
| 372 | { |
| 373 | return owner->cleaned & get_elem_map(); |
| 374 | } |
| 375 | |
| 376 | |
| 377 | inline |
| 378 | void With_element::mark_as_cleaned() |
| 379 | { |
| 380 | owner->cleaned|= get_elem_map(); |
| 381 | } |
| 382 | |
| 383 | |
| 384 | inline |
| 385 | void With_element::reset_recursive_for_exec() |
| 386 | { |
| 387 | DBUG_ASSERT(is_recursive); |
| 388 | level= 0; |
| 389 | owner->with_prepared_anchor&= ~mutually_recursive; |
| 390 | owner->cleaned&= ~get_elem_map(); |
| 391 | cleanup_stabilized(); |
| 392 | spec->columns_are_renamed= false; |
| 393 | } |
| 394 | |
| 395 | |
| 396 | |
| 397 | inline |
| 398 | void With_element::cleanup_stabilized() |
| 399 | { |
| 400 | owner->stabilized&= ~mutually_recursive; |
| 401 | } |
| 402 | |
| 403 | |
| 404 | inline |
| 405 | void With_element::set_as_stabilized() |
| 406 | { |
| 407 | owner->stabilized|= get_elem_map(); |
| 408 | } |
| 409 | |
| 410 | |
| 411 | inline |
| 412 | bool With_element::is_stabilized() |
| 413 | { |
| 414 | return owner->stabilized & get_elem_map(); |
| 415 | } |
| 416 | |
| 417 | |
| 418 | inline |
| 419 | bool With_element::all_are_stabilized() |
| 420 | { |
| 421 | return (owner->stabilized & mutually_recursive) == mutually_recursive; |
| 422 | } |
| 423 | |
| 424 | |
| 425 | inline |
| 426 | void With_element::prepare_for_next_iteration() |
| 427 | { |
| 428 | With_element *with_elem= this; |
| 429 | while ((with_elem= with_elem->get_next_mutually_recursive()) != this) |
| 430 | { |
| 431 | TABLE *rec_table= with_elem->rec_result->first_rec_table_to_update; |
| 432 | if (rec_table) |
| 433 | rec_table->reginfo.join_tab->preread_init_done= false; |
| 434 | } |
| 435 | } |
| 436 | |
| 437 | |
| 438 | inline |
| 439 | void st_select_lex_unit::set_with_clause(With_clause *with_cl) |
| 440 | { |
| 441 | with_clause= with_cl; |
| 442 | if (with_clause) |
| 443 | with_clause->set_owner(this); |
| 444 | } |
| 445 | |
| 446 | |
| 447 | inline |
| 448 | void st_select_lex::set_with_clause(With_clause *with_clause) |
| 449 | { |
| 450 | master_unit()->with_clause= with_clause; |
| 451 | if (with_clause) |
| 452 | with_clause->set_owner(master_unit()); |
| 453 | } |
| 454 | |
| 455 | #endif /* SQL_CTE_INCLUDED */ |
| 456 | |