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 | |