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