1 | /* -*- C++ -*- */ |
2 | /* Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved. |
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 _SP_RCONTEXT_H_ |
18 | #define _SP_RCONTEXT_H_ |
19 | |
20 | #ifdef USE_PRAGMA_INTERFACE |
21 | #pragma interface /* gcc class implementation */ |
22 | #endif |
23 | |
24 | #include "sql_class.h" // select_result_interceptor |
25 | #include "sp_pcontext.h" // sp_condition_value |
26 | |
27 | /////////////////////////////////////////////////////////////////////////// |
28 | // sp_rcontext declaration. |
29 | /////////////////////////////////////////////////////////////////////////// |
30 | |
31 | class sp_cursor; |
32 | class sp_lex_keeper; |
33 | class sp_instr_cpush; |
34 | class Query_arena; |
35 | class sp_head; |
36 | class Item_cache; |
37 | class Virtual_tmp_table; |
38 | |
39 | |
40 | /* |
41 | This class is a runtime context of a Stored Routine. It is used in an |
42 | execution and is intended to contain all dynamic objects (i.e. objects, which |
43 | can be changed during execution), such as: |
44 | - stored routine variables; |
45 | - cursors; |
46 | - handlers; |
47 | |
48 | Runtime context is used with sp_head class. sp_head class is intended to |
49 | contain all static things, related to the stored routines (code, for example). |
50 | sp_head instance creates runtime context for the execution of a stored |
51 | routine. |
52 | |
53 | There is a parsing context (an instance of sp_pcontext class), which is used |
54 | on parsing stage. However, now it contains some necessary for an execution |
55 | things, such as definition of used stored routine variables. That's why |
56 | runtime context needs a reference to the parsing context. |
57 | */ |
58 | |
59 | class sp_rcontext : public Sql_alloc |
60 | { |
61 | public: |
62 | /// Construct and properly initialize a new sp_rcontext instance. The static |
63 | /// create-function is needed because we need a way to return an error from |
64 | /// the constructor. |
65 | /// |
66 | /// @param thd Thread handle. |
67 | /// @param root_parsing_ctx Top-level parsing context for this stored program. |
68 | /// @param return_value_fld Field object to store the return value |
69 | /// (for stored functions only). |
70 | /// |
71 | /// @return valid sp_rcontext object or NULL in case of OOM-error. |
72 | static sp_rcontext *create(THD *thd, |
73 | const sp_head *owner, |
74 | const sp_pcontext *root_parsing_ctx, |
75 | Field *return_value_fld, |
76 | Row_definition_list &defs); |
77 | |
78 | ~sp_rcontext(); |
79 | |
80 | private: |
81 | sp_rcontext(const sp_head *owner, |
82 | const sp_pcontext *root_parsing_ctx, |
83 | Field *return_value_fld, |
84 | bool in_sub_stmt); |
85 | |
86 | // Prevent use of copying constructor and operator. |
87 | sp_rcontext(const sp_rcontext &); |
88 | void operator=(sp_rcontext &); |
89 | |
90 | private: |
91 | /// This is an auxillary class to store entering instruction pointer for an |
92 | /// SQL-handler. |
93 | class sp_handler_entry : public Sql_alloc |
94 | { |
95 | public: |
96 | /// Handler definition (from parsing context). |
97 | const sp_handler *handler; |
98 | |
99 | /// Instruction pointer to the first instruction. |
100 | uint first_ip; |
101 | |
102 | /// The constructor. |
103 | /// |
104 | /// @param _handler sp_handler object. |
105 | /// @param _first_ip first instruction pointer. |
106 | sp_handler_entry(const sp_handler *_handler, uint _first_ip) |
107 | :handler(_handler), first_ip(_first_ip) |
108 | { } |
109 | }; |
110 | |
111 | public: |
112 | /// This class stores basic information about SQL-condition, such as: |
113 | /// - SQL error code; |
114 | /// - error level; |
115 | /// - SQLSTATE; |
116 | /// - text message. |
117 | /// |
118 | /// It's used to organize runtime SQL-handler call stack. |
119 | /// |
120 | /// Standard Sql_condition class can not be used, because we don't always have |
121 | /// an Sql_condition object for an SQL-condition in Diagnostics_area. |
122 | /// |
123 | /// Eventually, this class should be moved to sql_error.h, and be a part of |
124 | /// standard SQL-condition processing (Diagnostics_area should contain an |
125 | /// object for active SQL-condition, not just information stored in DA's |
126 | /// fields). |
127 | class Sql_condition_info : public Sql_alloc, |
128 | public Sql_condition_identity |
129 | { |
130 | public: |
131 | /// Text message. |
132 | char *message; |
133 | |
134 | /// The constructor. |
135 | /// |
136 | /// @param _sql_condition The SQL condition. |
137 | /// @param arena Query arena for SP |
138 | Sql_condition_info(const Sql_condition *_sql_condition, |
139 | Query_arena *arena) |
140 | :Sql_condition_identity(*_sql_condition) |
141 | { |
142 | message= strdup_root(arena->mem_root, _sql_condition->get_message_text()); |
143 | } |
144 | }; |
145 | |
146 | private: |
147 | /// This class represents a call frame of SQL-handler (one invocation of a |
148 | /// handler). Basically, it's needed to store continue instruction pointer for |
149 | /// CONTINUE SQL-handlers. |
150 | class Handler_call_frame : public Sql_alloc |
151 | { |
152 | public: |
153 | /// SQL-condition, triggered handler activation. |
154 | const Sql_condition_info *sql_condition; |
155 | |
156 | /// Continue-instruction-pointer for CONTINUE-handlers. |
157 | /// The attribute contains 0 for EXIT-handlers. |
158 | uint continue_ip; |
159 | |
160 | /// The constructor. |
161 | /// |
162 | /// @param _sql_condition SQL-condition, triggered handler activation. |
163 | /// @param _continue_ip Continue instruction pointer. |
164 | Handler_call_frame(const Sql_condition_info *_sql_condition, |
165 | uint _continue_ip) |
166 | :sql_condition(_sql_condition), |
167 | continue_ip(_continue_ip) |
168 | { } |
169 | }; |
170 | |
171 | public: |
172 | /// Arena used to (re) allocate items on. E.g. reallocate INOUT/OUT |
173 | /// SP-variables when they don't fit into prealloced items. This is common |
174 | /// situation with String items. It is used mainly in sp_eval_func_item(). |
175 | Query_arena *callers_arena; |
176 | |
177 | /// Flag to end an open result set before start executing an SQL-handler |
178 | /// (if one is found). Otherwise the client will hang due to a violation |
179 | /// of the client/server protocol. |
180 | bool end_partial_result_set; |
181 | bool pause_state; |
182 | bool quit_func; |
183 | uint instr_ptr; |
184 | |
185 | /// The stored program for which this runtime context is created. Used for |
186 | /// checking if correct runtime context is used for variable handling, |
187 | /// and to access the package run-time context. |
188 | /// Also used by slow log. |
189 | const sp_head *m_sp; |
190 | |
191 | ///////////////////////////////////////////////////////////////////////// |
192 | // SP-variables. |
193 | ///////////////////////////////////////////////////////////////////////// |
194 | |
195 | uint argument_count() const |
196 | { |
197 | return m_root_parsing_ctx->context_var_count(); |
198 | } |
199 | |
200 | int set_variable(THD *thd, uint var_idx, Item **value); |
201 | int set_variable_row_field(THD *thd, uint var_idx, uint field_idx, |
202 | Item **value); |
203 | int set_variable_row_field_by_name(THD *thd, uint var_idx, |
204 | const LEX_CSTRING &field_name, |
205 | Item **value); |
206 | int set_variable_row(THD *thd, uint var_idx, List<Item> &items); |
207 | |
208 | int set_parameter(THD *thd, uint var_idx, Item **value) |
209 | { |
210 | DBUG_ASSERT(var_idx < argument_count()); |
211 | return set_variable(thd, var_idx, value); |
212 | } |
213 | |
214 | Item_field *get_variable(uint var_idx) const |
215 | { return m_var_items[var_idx]; } |
216 | |
217 | Item **get_variable_addr(uint var_idx) const |
218 | { return ((Item **) m_var_items.array()) + var_idx; } |
219 | |
220 | Item_field *get_parameter(uint var_idx) const |
221 | { |
222 | DBUG_ASSERT(var_idx < argument_count()); |
223 | return get_variable(var_idx); |
224 | } |
225 | |
226 | bool find_row_field_by_name_or_error(uint *field_idx, uint var_idx, |
227 | const LEX_CSTRING &field_name); |
228 | |
229 | bool set_return_value(THD *thd, Item **return_value_item); |
230 | |
231 | bool is_return_value_set() const |
232 | { return m_return_value_set; } |
233 | |
234 | ///////////////////////////////////////////////////////////////////////// |
235 | // SQL-handlers. |
236 | ///////////////////////////////////////////////////////////////////////// |
237 | |
238 | /// Create a new sp_handler_entry instance and push it to the handler call |
239 | /// stack. |
240 | /// |
241 | /// @param handler SQL-handler object. |
242 | /// @param first_ip First instruction pointer of the handler. |
243 | /// |
244 | /// @return error flag. |
245 | /// @retval false on success. |
246 | /// @retval true on error. |
247 | bool push_handler(sp_handler *handler, uint first_ip); |
248 | |
249 | /// Pop and delete given number of sp_handler_entry instances from the handler |
250 | /// call stack. |
251 | /// |
252 | /// @param count Number of handler entries to pop & delete. |
253 | void pop_handlers(size_t count); |
254 | |
255 | const Sql_condition_info *raised_condition() const |
256 | { |
257 | return m_handler_call_stack.elements() ? |
258 | (*m_handler_call_stack.back())->sql_condition : NULL; |
259 | } |
260 | |
261 | /// Handle current SQL condition (if any). |
262 | /// |
263 | /// This is the public-interface function to handle SQL conditions in |
264 | /// stored routines. |
265 | /// |
266 | /// @param thd Thread handle. |
267 | /// @param ip[out] Instruction pointer to the first handler |
268 | /// instruction. |
269 | /// @param cur_spi Current SP instruction. |
270 | /// |
271 | /// @retval true if an SQL-handler has been activated. That means, all of |
272 | /// the following conditions are satisfied: |
273 | /// - the SP-instruction raised SQL-condition(s), |
274 | /// - and there is an SQL-handler to process at least one of those |
275 | /// SQL-conditions, |
276 | /// - and that SQL-handler has been activated. |
277 | /// Note, that the return value has nothing to do with "error flag" |
278 | /// semantics. |
279 | /// |
280 | /// @retval false otherwise. |
281 | bool handle_sql_condition(THD *thd, |
282 | uint *ip, |
283 | const sp_instr *cur_spi); |
284 | |
285 | /// Remove latest call frame from the handler call stack. |
286 | /// |
287 | /// @param da Diagnostics area containing handled conditions. |
288 | /// |
289 | /// @return continue instruction pointer of the removed handler. |
290 | uint exit_handler(Diagnostics_area *da); |
291 | |
292 | ///////////////////////////////////////////////////////////////////////// |
293 | // Cursors. |
294 | ///////////////////////////////////////////////////////////////////////// |
295 | |
296 | /// Create a new sp_cursor instance and push it to the cursor stack. |
297 | /// |
298 | /// @param lex_keeper SP-instruction execution helper. |
299 | /// @param i Cursor-push instruction. |
300 | /// |
301 | /// @return error flag. |
302 | /// @retval false on success. |
303 | /// @retval true on error. |
304 | bool push_cursor(THD *thd, sp_lex_keeper *lex_keeper); |
305 | |
306 | /// Pop and delete given number of sp_cursor instance from the cursor stack. |
307 | /// |
308 | /// @param count Number of cursors to pop & delete. |
309 | void pop_cursors(size_t count); |
310 | |
311 | void pop_all_cursors() |
312 | { pop_cursors(m_ccount); } |
313 | |
314 | sp_cursor *get_cursor(uint i) const |
315 | { return m_cstack[i]; } |
316 | |
317 | ///////////////////////////////////////////////////////////////////////// |
318 | // CASE expressions. |
319 | ///////////////////////////////////////////////////////////////////////// |
320 | |
321 | /// Set CASE expression to the specified value. |
322 | /// |
323 | /// @param thd Thread handler. |
324 | /// @param case_expr_id The CASE expression identifier. |
325 | /// @param case_expr_item The CASE expression value |
326 | /// |
327 | /// @return error flag. |
328 | /// @retval false on success. |
329 | /// @retval true on error. |
330 | /// |
331 | /// @note The idea is to reuse Item_cache for the expression of the one |
332 | /// CASE statement. This optimization takes place when there is CASE |
333 | /// statement inside of a loop. So, in other words, we will use the same |
334 | /// object on each iteration instead of creating a new one for each |
335 | /// iteration. |
336 | /// |
337 | /// TODO |
338 | /// Hypothetically, a type of CASE expression can be different for each |
339 | /// iteration. For instance, this can happen if the expression contains |
340 | /// a session variable (something like @@VAR) and its type is changed |
341 | /// from one iteration to another. |
342 | /// |
343 | /// In order to cope with this problem, we check type each time, when we |
344 | /// use already created object. If the type does not match, we re-create |
345 | /// Item. This also can (should?) be optimized. |
346 | bool set_case_expr(THD *thd, int case_expr_id, Item **case_expr_item_ptr); |
347 | |
348 | Item *get_case_expr(int case_expr_id) const |
349 | { return m_case_expr_holders[case_expr_id]; } |
350 | |
351 | Item ** get_case_expr_addr(int case_expr_id) const |
352 | { return (Item**) m_case_expr_holders.array() + case_expr_id; } |
353 | |
354 | private: |
355 | /// Internal function to allocate memory for arrays. |
356 | /// |
357 | /// @param thd Thread handle. |
358 | /// |
359 | /// @return error flag: false on success, true in case of failure. |
360 | bool alloc_arrays(THD *thd); |
361 | |
362 | /// Create and initialize a table to store SP-variables. |
363 | /// |
364 | /// param thd Thread handle. |
365 | /// |
366 | /// @return error flag. |
367 | /// @retval false on success. |
368 | /// @retval true on error. |
369 | bool init_var_table(THD *thd, List<Spvar_definition> &defs); |
370 | |
371 | /// Create and initialize an Item-adapter (Item_field) for each SP-var field. |
372 | /// |
373 | /// param thd Thread handle. |
374 | /// |
375 | /// @return error flag. |
376 | /// @retval false on success. |
377 | /// @retval true on error. |
378 | bool init_var_items(THD *thd, List<Spvar_definition> &defs); |
379 | |
380 | /// Create an instance of appropriate Item_cache class depending on the |
381 | /// specified type in the callers arena. |
382 | /// |
383 | /// @note We should create cache items in the callers arena, as they are |
384 | /// used between in several instructions. |
385 | /// |
386 | /// @param thd Thread handler. |
387 | /// @param item Item to get the expression type. |
388 | /// |
389 | /// @return Pointer to valid object on success, or NULL in case of error. |
390 | Item_cache *create_case_expr_holder(THD *thd, const Item *item) const; |
391 | |
392 | Virtual_tmp_table *virtual_tmp_table_for_row(uint idx); |
393 | |
394 | private: |
395 | /// Top-level (root) parsing context for this runtime context. |
396 | const sp_pcontext *m_root_parsing_ctx; |
397 | |
398 | /// Virtual table for storing SP-variables. |
399 | Virtual_tmp_table *m_var_table; |
400 | |
401 | /// Collection of Item_field proxies, each of them points to the |
402 | /// corresponding field in m_var_table. |
403 | Bounds_checked_array<Item_field *> m_var_items; |
404 | |
405 | /// This is a pointer to a field, which should contain return value for |
406 | /// stored functions (only). For stored procedures, this pointer is NULL. |
407 | Field *m_return_value_fld; |
408 | |
409 | /// Indicates whether the return value (in m_return_value_fld) has been |
410 | /// set during execution. |
411 | bool m_return_value_set; |
412 | |
413 | /// Flag to tell if the runtime context is created for a sub-statement. |
414 | bool m_in_sub_stmt; |
415 | |
416 | /// Stack of visible handlers. |
417 | Dynamic_array<sp_handler_entry *> m_handlers; |
418 | |
419 | /// Stack of caught SQL conditions. |
420 | Dynamic_array<Handler_call_frame *> m_handler_call_stack; |
421 | |
422 | /// Stack of cursors. |
423 | Bounds_checked_array<sp_cursor *> m_cstack; |
424 | |
425 | /// Current number of cursors in m_cstack. |
426 | uint m_ccount; |
427 | |
428 | /// Array of CASE expression holders. |
429 | Bounds_checked_array<Item_cache *> m_case_expr_holders; |
430 | }; // class sp_rcontext : public Sql_alloc |
431 | |
432 | /////////////////////////////////////////////////////////////////////////// |
433 | // sp_cursor declaration. |
434 | /////////////////////////////////////////////////////////////////////////// |
435 | |
436 | class Server_side_cursor; |
437 | typedef class st_select_lex_unit SELECT_LEX_UNIT; |
438 | |
439 | /* A mediator between stored procedures and server side cursors */ |
440 | |
441 | class sp_cursor : public Sql_alloc |
442 | { |
443 | private: |
444 | /// An interceptor of cursor result set used to implement |
445 | /// FETCH <cname> INTO <varlist>. |
446 | class Select_fetch_into_spvars: public select_result_interceptor |
447 | { |
448 | List<sp_variable> *spvar_list; |
449 | uint field_count; |
450 | bool send_data_to_variable_list(List<sp_variable> &vars, List<Item> &items); |
451 | public: |
452 | Select_fetch_into_spvars(THD *thd_arg): select_result_interceptor(thd_arg) {} |
453 | uint get_field_count() { return field_count; } |
454 | void set_spvar_list(List<sp_variable> *vars) { spvar_list= vars; } |
455 | |
456 | virtual bool send_eof() { return FALSE; } |
457 | virtual int send_data(List<Item> &items); |
458 | virtual int prepare(List<Item> &list, SELECT_LEX_UNIT *u); |
459 | }; |
460 | |
461 | public: |
462 | sp_cursor(THD *thd_arg, sp_lex_keeper *lex_keeper); |
463 | |
464 | virtual ~sp_cursor() |
465 | { destroy(); } |
466 | |
467 | sp_lex_keeper *get_lex_keeper() { return m_lex_keeper; } |
468 | |
469 | int open(THD *thd); |
470 | |
471 | int open_view_structure_only(THD *thd); |
472 | |
473 | int close(THD *thd); |
474 | |
475 | my_bool is_open() |
476 | { return MY_TEST(server_side_cursor); } |
477 | |
478 | bool found() const |
479 | { return m_found; } |
480 | |
481 | ulonglong row_count() const |
482 | { return m_row_count; } |
483 | |
484 | ulonglong fetch_count() const |
485 | { return m_fetch_count; } |
486 | |
487 | int fetch(THD *, List<sp_variable> *vars, bool error_on_no_data); |
488 | |
489 | bool export_structure(THD *thd, Row_definition_list *list); |
490 | |
491 | private: |
492 | Select_fetch_into_spvars result; |
493 | sp_lex_keeper *m_lex_keeper; |
494 | Server_side_cursor *server_side_cursor; |
495 | ulonglong m_fetch_count; // Number of FETCH commands since last OPEN |
496 | ulonglong m_row_count; // Number of successful FETCH since last OPEN |
497 | bool m_found; // If last FETCH fetched a row |
498 | void destroy(); |
499 | |
500 | }; // class sp_cursor : public Sql_alloc |
501 | |
502 | #endif /* _SP_RCONTEXT_H_ */ |
503 | |