1 /* Copyright (c) 2002, 2021, Oracle and/or its affiliates. 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" // Query_result_interceptor 27 #include "sp_pcontext.h" // sp_condition_value 28 #include "sql_array.h" 29 #include "prealloced_array.h" 30 31 /////////////////////////////////////////////////////////////////////////// 32 // sp_rcontext declaration. 33 /////////////////////////////////////////////////////////////////////////// 34 35 class sp_cursor; 36 class sp_instr_cpush; 37 class Query_arena; 38 class sp_head; 39 class Item_cache; 40 41 /* 42 This class is a runtime context of a Stored Routine. It is used in an 43 execution and is intended to contain all dynamic objects (i.e. objects, which 44 can be changed during execution), such as: 45 - stored routine variables; 46 - cursors; 47 - handlers; 48 49 Runtime context is used with sp_head class. sp_head class is intended to 50 contain all static things, related to the stored routines (code, for example). 51 sp_head instance creates runtime context for the execution of a stored 52 routine. 53 54 There is a parsing context (an instance of sp_pcontext class), which is used 55 on parsing stage. However, now it contains some necessary for an execution 56 things, such as definition of used stored routine variables. That's why 57 runtime context needs a reference to the parsing context. 58 */ 59 60 class sp_rcontext : public Sql_alloc 61 { 62 public: 63 /// Construct and properly initialize a new sp_rcontext instance. The static 64 /// create-function is needed because we need a way to return an error from 65 /// the constructor. 66 /// 67 /// @param thd Thread handle. 68 /// @param root_parsing_ctx Top-level parsing context for this stored program. 69 /// @param return_value_fld Field object to store the return value 70 /// (for stored functions only). 71 /// 72 /// @return valid sp_rcontext object or NULL in case of OOM-error. 73 static sp_rcontext *create(THD *thd, 74 const sp_pcontext *root_parsing_ctx, 75 Field *return_value_fld); 76 77 ~sp_rcontext(); 78 79 private: 80 sp_rcontext(const sp_pcontext *root_parsing_ctx, 81 Field *return_value_fld, 82 bool in_sub_stmt); 83 84 // Prevent use of copying constructor and operator. 85 sp_rcontext(const sp_rcontext &); 86 void operator=(sp_rcontext &); 87 88 private: 89 /// This is an auxillary class to store entering instruction pointer for an 90 /// SQL-handler. 91 class sp_handler_entry 92 { 93 public: 94 /// Handler definition (from parsing context). 95 const sp_handler *handler; 96 97 /// Instruction pointer to the first instruction. 98 uint first_ip; 99 100 /// The constructor. 101 /// 102 /// @param _handler sp_handler object. 103 /// @param _first_ip first instruction pointer. sp_handler_entry(const sp_handler * _handler,uint _first_ip)104 sp_handler_entry(const sp_handler *_handler, uint _first_ip) 105 :handler(_handler), first_ip(_first_ip) 106 { } 107 }; 108 109 public: 110 /// This class represents a call frame of SQL-handler (one invocation of a 111 /// handler). Basically, it's needed to store continue instruction pointer for 112 /// CONTINUE SQL-handlers. 113 class Handler_call_frame 114 { 115 public: 116 /// Handler definition (from parsing context). 117 const sp_handler *handler; 118 119 /// SQL-condition, triggered handler activation. 120 Sql_condition *sql_condition; 121 122 /// Continue-instruction-pointer for CONTINUE-handlers. 123 /// The attribute contains 0 for EXIT-handlers. 124 uint continue_ip; 125 126 /// The Diagnostics Area which will be pushed when the handler activates 127 /// and popped when the handler completes. 128 Diagnostics_area handler_da; 129 130 /// The constructor. 131 /// 132 /// @param _sql_condition SQL-condition, triggered handler activation. 133 /// @param _continue_ip Continue instruction pointer. Handler_call_frame(const sp_handler * _handler,Sql_condition * _sql_condition,uint _continue_ip)134 Handler_call_frame(const sp_handler *_handler, 135 Sql_condition *_sql_condition, 136 uint _continue_ip) 137 :handler(_handler), 138 sql_condition(_sql_condition), 139 continue_ip(_continue_ip), 140 handler_da(false) 141 { } 142 }; 143 144 public: 145 /// Arena used to (re) allocate items on. E.g. reallocate INOUT/OUT 146 /// SP-variables when they don't fit into prealloced items. This is common 147 /// situation with String items. It is used mainly in sp_eval_func_item(). 148 Query_arena *callers_arena; 149 150 /// Flag to end an open result set before start executing an SQL-handler 151 /// (if one is found). Otherwise the client will hang due to a violation 152 /// of the client/server protocol. 153 bool end_partial_result_set; 154 155 /// The stored program for which this runtime context is created. 156 sp_head *sp; 157 158 ///////////////////////////////////////////////////////////////////////// 159 // SP-variables. 160 ///////////////////////////////////////////////////////////////////////// 161 set_variable(THD * thd,uint var_idx,Item ** value)162 bool set_variable(THD *thd, uint var_idx, Item **value) 163 { return set_variable(thd, m_var_table->field[var_idx], value); } 164 get_item(uint var_idx)165 Item *get_item(uint var_idx) const 166 { return m_var_items[var_idx]; } 167 get_item_addr(uint var_idx)168 Item **get_item_addr(uint var_idx) const 169 { return m_var_items.array() + var_idx; } 170 171 bool set_return_value(THD *thd, Item **return_value_item); 172 is_return_value_set()173 bool is_return_value_set() const 174 { return m_return_value_set; } 175 176 ///////////////////////////////////////////////////////////////////////// 177 // SQL-handlers. 178 ///////////////////////////////////////////////////////////////////////// 179 180 /// Create a new sp_handler_entry instance and push it to the handler call 181 /// stack. 182 /// 183 /// @param handler SQL-handler object. 184 /// @param first_ip First instruction pointer of the handler. 185 /// 186 /// @return error flag. 187 /// @retval false on success. 188 /// @retval true on error. 189 bool push_handler(sp_handler *handler, uint first_ip); 190 191 /// Pop and delete given number of sp_handler_entry instances from the handler 192 /// call stack. 193 /// 194 /// @param current_scope The current BEGIN..END block. 195 void pop_handlers(sp_pcontext *current_scope); 196 197 /// Get the Handler_call_frame representing the currently active handler. current_handler_frame()198 Handler_call_frame *current_handler_frame() const 199 { 200 return m_activated_handlers.size() ? 201 m_activated_handlers.back() : NULL; 202 } 203 204 /// Handle current SQL condition (if any). 205 /// 206 /// This is the public-interface function to handle SQL conditions in 207 /// stored routines. 208 /// 209 /// @param thd Thread handle. 210 /// @param ip[out] Instruction pointer to the first handler 211 /// instruction. 212 /// @param cur_spi Current SP instruction. 213 /// 214 /// @retval true if an SQL-handler has been activated. That means, all of 215 /// the following conditions are satisfied: 216 /// - the SP-instruction raised SQL-condition(s), 217 /// - and there is an SQL-handler to process at least one of those 218 /// SQL-conditions, 219 /// - and that SQL-handler has been activated. 220 /// Note, that the return value has nothing to do with "error flag" 221 /// semantics. 222 /// 223 /// @retval false otherwise. 224 bool handle_sql_condition(THD *thd, 225 uint *ip, 226 const sp_instr *cur_spi); 227 228 /// Handle return from SQL-handler. 229 /// 230 /// @param thd Thread handle. 231 /// @param target_scope The BEGIN..END block, containing 232 /// the target (next) instruction. 233 void exit_handler(THD *thd, 234 sp_pcontext *target_scope); 235 236 /// @return the continue instruction pointer of the last activated CONTINUE 237 /// handler. This function must not be called for the EXIT handlers. get_last_handler_continue_ip()238 uint get_last_handler_continue_ip() const 239 { 240 uint ip= m_activated_handlers.back()->continue_ip; 241 assert(ip != 0); 242 243 return ip; 244 } 245 246 ///////////////////////////////////////////////////////////////////////// 247 // Cursors. 248 ///////////////////////////////////////////////////////////////////////// 249 250 /// Create a new sp_cursor instance and push it to the cursor stack. 251 /// 252 /// @param i Cursor-push instruction. 253 /// 254 /// @return error flag. 255 /// @retval false on success. 256 /// @retval true on error. 257 bool push_cursor(sp_instr_cpush *i); 258 259 /// Pop and delete given number of sp_cursor instance from the cursor stack. 260 /// 261 /// @param count Number of cursors to pop & delete. 262 void pop_cursors(uint count); 263 pop_all_cursors()264 void pop_all_cursors() 265 { pop_cursors(m_ccount); } 266 get_cursor(uint i)267 sp_cursor *get_cursor(uint i) const 268 { return m_cstack[i]; } 269 270 ///////////////////////////////////////////////////////////////////////// 271 // CASE expressions. 272 ///////////////////////////////////////////////////////////////////////// 273 274 /// Set CASE expression to the specified value. 275 /// 276 /// @param thd Thread handler. 277 /// @param case_expr_id The CASE expression identifier. 278 /// @param case_expr_item The CASE expression value 279 /// 280 /// @return error flag. 281 /// @retval false on success. 282 /// @retval true on error. 283 /// 284 /// @note The idea is to reuse Item_cache for the expression of the one 285 /// CASE statement. This optimization takes place when there is CASE 286 /// statement inside of a loop. So, in other words, we will use the same 287 /// object on each iteration instead of creating a new one for each 288 /// iteration. 289 /// 290 /// TODO 291 /// Hypothetically, a type of CASE expression can be different for each 292 /// iteration. For instance, this can happen if the expression contains 293 /// a session variable (something like @@VAR) and its type is changed 294 /// from one iteration to another. 295 /// 296 /// In order to cope with this problem, we check type each time, when we 297 /// use already created object. If the type does not match, we re-create 298 /// Item. This also can (should?) be optimized. 299 bool set_case_expr(THD *thd, int case_expr_id, Item **case_expr_item_ptr); 300 get_case_expr(int case_expr_id)301 Item *get_case_expr(int case_expr_id) const 302 { return m_case_expr_holders[case_expr_id]; } 303 get_case_expr_addr(int case_expr_id)304 Item ** get_case_expr_addr(int case_expr_id) const 305 { return (Item**) m_case_expr_holders.array() + case_expr_id; } 306 307 private: 308 /// Internal function to allocate memory for arrays. 309 /// 310 /// @param thd Thread handle. 311 /// 312 /// @return error flag: false on success, true in case of failure. 313 bool alloc_arrays(THD *thd); 314 315 /// Create and initialize a table to store SP-variables. 316 /// 317 /// param thd Thread handle. 318 /// 319 /// @return error flag. 320 /// @retval false on success. 321 /// @retval true on error. 322 bool init_var_table(THD *thd); 323 324 /// Create and initialize an Item-adapter (Item_field) for each SP-var field. 325 /// 326 /// param thd Thread handle. 327 /// 328 /// @return error flag. 329 /// @retval false on success. 330 /// @retval true on error. 331 bool init_var_items(THD *thd); 332 333 /// Create an instance of appropriate Item_cache class depending on the 334 /// specified type in the callers arena. 335 /// 336 /// @note We should create cache items in the callers arena, as they are 337 /// used between in several instructions. 338 /// 339 /// @param thd Thread handler. 340 /// @param item Item to get the expression type. 341 /// 342 /// @return Pointer to valid object on success, or NULL in case of error. 343 Item_cache *create_case_expr_holder(THD *thd, const Item *item) const; 344 345 bool set_variable(THD *thd, Field *field, Item **value); 346 347 /// Pop the Handler_call_frame on top of the stack of active handlers. 348 /// Also pop the matching Diagnostics Area and transfer conditions. 349 void pop_handler_frame(THD *thd); 350 351 private: 352 /// Top-level (root) parsing context for this runtime context. 353 const sp_pcontext *m_root_parsing_ctx; 354 355 /// Virtual table for storing SP-variables. 356 TABLE *m_var_table; 357 358 /// Collection of Item_field proxies, each of them points to the 359 /// corresponding field in m_var_table. 360 Bounds_checked_array<Item *> m_var_items; 361 362 /// This is a pointer to a field, which should contain return value for 363 /// stored functions (only). For stored procedures, this pointer is NULL. 364 Field *m_return_value_fld; 365 366 /// Indicates whether the return value (in m_return_value_fld) has been 367 /// set during execution. 368 bool m_return_value_set; 369 370 /// Flag to tell if the runtime context is created for a sub-statement. 371 bool m_in_sub_stmt; 372 373 /// Stack of visible handlers. 374 Prealloced_array<sp_handler_entry *, 16> m_visible_handlers; 375 376 /// Stack of caught SQL conditions. 377 Prealloced_array<Handler_call_frame *, 16> m_activated_handlers; 378 379 /// Stack of cursors. 380 Bounds_checked_array<sp_cursor *> m_cstack; 381 382 /// Current number of cursors in m_cstack. 383 uint m_ccount; 384 385 /// Array of CASE expression holders. 386 Bounds_checked_array<Item_cache *> m_case_expr_holders; 387 }; // class sp_rcontext : public Sql_alloc 388 389 /////////////////////////////////////////////////////////////////////////// 390 // sp_cursor declaration. 391 /////////////////////////////////////////////////////////////////////////// 392 393 class Server_side_cursor; 394 typedef class st_select_lex_unit SELECT_LEX_UNIT; 395 396 /* A mediator between stored procedures and server side cursors */ 397 398 class sp_cursor 399 { 400 private: 401 /// An interceptor of cursor result set used to implement 402 /// FETCH <cname> INTO <varlist>. 403 class Query_fetch_into_spvars: public Query_result_interceptor 404 { 405 List<sp_variable> *spvar_list; 406 uint field_count; 407 public: Query_fetch_into_spvars()408 Query_fetch_into_spvars() {} /* Remove gcc warning */ get_field_count()409 uint get_field_count() { return field_count; } set_spvar_list(List<sp_variable> * vars)410 void set_spvar_list(List<sp_variable> *vars) { spvar_list= vars; } 411 send_eof()412 virtual bool send_eof() { return FALSE; } 413 virtual bool send_data(List<Item> &items); 414 virtual int prepare(List<Item> &list, SELECT_LEX_UNIT *u); 415 }; 416 417 public: sp_cursor(sp_instr_cpush * i)418 sp_cursor(sp_instr_cpush *i) 419 :m_server_side_cursor(NULL), 420 m_push_instr(i) 421 { } 422 ~sp_cursor()423 virtual ~sp_cursor() 424 { destroy(); } 425 426 bool open(THD *thd); 427 428 bool close(THD *thd); 429 is_open()430 bool is_open() const 431 { return MY_TEST(m_server_side_cursor); } 432 433 bool fetch(THD *thd, List<sp_variable> *vars); 434 get_push_instr()435 sp_instr_cpush *get_push_instr() 436 { return m_push_instr; } 437 438 private: 439 Query_fetch_into_spvars m_result; 440 441 Server_side_cursor *m_server_side_cursor; 442 sp_instr_cpush *m_push_instr; 443 444 private: 445 void destroy(); 446 }; // class sp_cursor 447 448 #endif /* _SP_RCONTEXT_H_ */ 449