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