sp_rcontext.cc 17 KB
Newer Older
1 2 3 4
/* Copyright (C) 2002 MySQL AB

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
unknown's avatar
unknown committed
5
   the Free Software Foundation; version 2 of the License.
6 7 8 9 10 11 12 13 14 15

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */

16
#include "mysql_priv.h"
17
#ifdef USE_PRAGMA_IMPLEMENTATION
18 19 20 21 22 23 24
#pragma implementation
#endif

#if defined(WIN32) || defined(__WIN__)
#undef SAFEMALLOC				/* Problems with threads */
#endif

25 26
#include "mysql.h"
#include "sp_head.h"
27
#include "sql_cursor.h"
28 29 30
#include "sp_rcontext.h"
#include "sp_pcontext.h"

31 32 33 34 35 36 37 38 39

sp_rcontext::sp_rcontext(sp_pcontext *root_parsing_ctx,
                         Field *return_value_fld,
                         sp_rcontext *prev_runtime_ctx)
  :m_root_parsing_ctx(root_parsing_ctx),
   m_var_table(0),
   m_var_items(0),
   m_return_value_fld(return_value_fld),
   m_return_value_set(FALSE),
40
   in_sub_stmt(FALSE),
41 42 43 44 45 46 47
   m_hcount(0),
   m_hsp(0),
   m_ihsp(0),
   m_hfound(-1),
   m_ccount(0),
   m_case_expr_holders(0),
   m_prev_runtime_ctx(prev_runtime_ctx)
48 49 50
{
}

51

52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
sp_rcontext::~sp_rcontext()
{
  if (m_var_table)
    free_blobs(m_var_table);
}


/*
  Initialize sp_rcontext instance.

  SYNOPSIS
    thd   Thread handle
  RETURN
    FALSE   on success
    TRUE    on error
*/

bool sp_rcontext::init(THD *thd)
{
71 72
  in_sub_stmt= thd->in_sub_stmt;

73 74 75 76 77
  if (init_var_table(thd) || init_var_items())
    return TRUE;

  return
    !(m_handler=
78
      (sp_handler_t*)thd->alloc(m_root_parsing_ctx->max_handler_index() *
79 80
                                sizeof(sp_handler_t))) ||
    !(m_hstack=
81
      (uint*)thd->alloc(m_root_parsing_ctx->max_handler_index() *
82 83
                        sizeof(uint))) ||
    !(m_in_handler=
84
      (uint*)thd->alloc(m_root_parsing_ctx->max_handler_index() *
85 86
                        sizeof(uint))) ||
    !(m_cstack=
87
      (sp_cursor**)thd->alloc(m_root_parsing_ctx->max_cursor_index() *
88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
                              sizeof(sp_cursor*))) ||
    !(m_case_expr_holders=
      (Item_cache**)thd->calloc(m_root_parsing_ctx->get_num_case_exprs() *
                               sizeof (Item_cache*)));
}


/*
  Create and initialize a table to store SP-vars.

  SYNOPSIS
    thd   Thread handler.
  RETURN
    FALSE   on success
    TRUE    on error
*/

bool
sp_rcontext::init_var_table(THD *thd)
107
{
108 109
  List<create_field> field_def_lst;

110
  if (!m_root_parsing_ctx->max_var_index())
111 112 113 114
    return FALSE;

  m_root_parsing_ctx->retrieve_field_definitions(&field_def_lst);

115
  DBUG_ASSERT(field_def_lst.elements == m_root_parsing_ctx->max_var_index());
116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138
  
  if (!(m_var_table= create_virtual_tmp_table(thd, field_def_lst)))
    return TRUE;

  m_var_table->copy_blobs= TRUE;
  m_var_table->alias= "";

  return FALSE;
}


/*
  Create and initialize an Item-adapter (Item_field) for each SP-var field.

  RETURN
    FALSE   on success
    TRUE    on error
*/

bool
sp_rcontext::init_var_items()
{
  uint idx;
139
  uint num_vars= m_root_parsing_ctx->max_var_index();
140 141 142 143 144

  if (!(m_var_items= (Item**) sql_alloc(num_vars * sizeof (Item *))))
    return TRUE;

  for (idx = 0; idx < num_vars; ++idx)
145
  {
146 147
    if (!(m_var_items[idx]= new Item_field(m_var_table->field[idx])))
      return TRUE;
148
  }
149

150
  return FALSE;
151 152
}

153 154

bool
155
sp_rcontext::set_return_value(THD *thd, Item **return_value_item)
156 157 158 159 160 161 162 163 164
{
  DBUG_ASSERT(m_return_value_fld);

  m_return_value_set = TRUE;

  return sp_eval_expr(thd, m_return_value_fld, return_value_item);
}


165 166 167 168
#define IS_WARNING_CONDITION(S)   ((S)[0] == '0' && (S)[1] == '1')
#define IS_NOT_FOUND_CONDITION(S) ((S)[0] == '0' && (S)[1] == '2')
#define IS_EXCEPTION_CONDITION(S) ((S)[0] != '0' || (S)[1] > '2')

unknown's avatar
unknown committed
169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195
/*
  Find a handler for the given errno.
  This is called from all error message functions (e.g. push_warning,
  net_send_error, et al) when a sp_rcontext is in effect. If a handler
  is found, no error is sent, and the the SP execution loop will instead
  invoke the found handler.
  This might be called several times before we get back to the execution
  loop, so m_hfound can be >= 0 if a handler has already been found.
  (In which case we don't search again - the first found handler will
   be used.)
  Handlers are pushed on the stack m_handler, with the latest/innermost
  one on the top; we then search for matching handlers from the top and
  down.
  We search through all the handlers, looking for the most specific one
  (sql_errno more specific than sqlstate more specific than the rest).
  Note that mysql error code handlers is a MySQL extension, not part of
  the standard.

  SYNOPSIS
    sql_errno     The error code
    level         Warning level

  RETURN
    1             if a handler was found, m_hfound is set to its index (>= 0)
    0             if not found, m_hfound is -1
*/

196
bool
197
sp_rcontext::find_handler(THD *thd, uint sql_errno,
unknown's avatar
unknown committed
198
                          MYSQL_ERROR::enum_warning_level level)
199 200 201 202 203
{
  if (m_hfound >= 0)
    return 1;			// Already got one

  const char *sqlstate= mysql_errno_to_sqlstate(sql_errno);
204
  int i= m_hcount, found= -1;
205

206 207 208 209 210 211 212 213 214
  /*
    If this is a fatal sub-statement error, and this runtime
    context corresponds to a sub-statement, no CONTINUE/EXIT
    handlers from this context are applicable: try to locate one
    in the outer scope.
  */
  if (thd->is_fatal_sub_stmt_error && in_sub_stmt)
    i= 0;

unknown's avatar
unknown committed
215
  /* Search handlers from the latest (innermost) to the oldest (outermost) */
216
  while (i--)
217 218
  {
    sp_cond_type_t *cond= m_handler[i].cond;
219 220
    int j= m_ihsp;

unknown's avatar
unknown committed
221
    /* Check active handlers, to avoid invoking one recursively */
222 223 224 225 226
    while (j--)
      if (m_in_handler[j] == m_handler[i].handler)
	break;
    if (j >= 0)
      continue;                 // Already executing this handler
227 228 229 230

    switch (cond->type)
    {
    case sp_cond_type_t::number:
231 232
      if (sql_errno == cond->mysqlerr &&
          (found < 0 || m_handler[found].cond->type > sp_cond_type_t::number))
233
	found= i;		// Always the most specific
234 235
      break;
    case sp_cond_type_t::state:
236
      if (strcmp(sqlstate, cond->sqlstate) == 0 &&
237
	  (found < 0 || m_handler[found].cond->type > sp_cond_type_t::state))
238
	found= i;
239 240
      break;
    case sp_cond_type_t::warning:
241 242 243
      if ((IS_WARNING_CONDITION(sqlstate) ||
           level == MYSQL_ERROR::WARN_LEVEL_WARN) &&
          found < 0)
244
	found= i;
245 246
      break;
    case sp_cond_type_t::notfound:
247
      if (IS_NOT_FOUND_CONDITION(sqlstate) && found < 0)
248
	found= i;
249 250
      break;
    case sp_cond_type_t::exception:
251
      if (IS_EXCEPTION_CONDITION(sqlstate) &&
252
	  level == MYSQL_ERROR::WARN_LEVEL_ERROR &&
253
	  found < 0)
254
	found= i;
255 256 257
      break;
    }
  }
258
  if (found < 0)
259
  {
260 261 262 263
    /*
      Only "exception conditions" are propagated to handlers in calling
      contexts. If no handler is found locally for a "completion condition"
      (warning or "not found") we will simply resume execution.
unknown's avatar
unknown committed
264
    */
265 266
    if (m_prev_runtime_ctx && IS_EXCEPTION_CONDITION(sqlstate) &&
        level == MYSQL_ERROR::WARN_LEVEL_ERROR)
267
      return m_prev_runtime_ctx->find_handler(thd, sql_errno, level);
268
    return FALSE;
269
  }
270 271
  m_hfound= found;
  return TRUE;
272 273
}

274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312
/*
   Handle the error for a given errno.
   The severity of the error is adjusted depending of the current sql_mode.
   If an handler is present for the error (see find_handler()),
   this function will return true.
   If a handler is found and if the severity of the error indicate
   that the current instruction executed should abort,
   the flag thd->net.report_error is also set.
   This will cause the execution of the current instruction in a
   sp_instr* to fail, and give control to the handler code itself
   in the sp_head::execute() loop.

  SYNOPSIS
    sql_errno     The error code
    level         Warning level
    thd           The current thread
                  - thd->net.report_error is an optional output.

  RETURN
    TRUE       if a handler was found.
    FALSE      if no handler was found.
*/
bool
sp_rcontext::handle_error(uint sql_errno,
                          MYSQL_ERROR::enum_warning_level level,
                          THD *thd)
{
  bool handled= FALSE;
  MYSQL_ERROR::enum_warning_level elevated_level= level;


  /* Depending on the sql_mode of execution,
     warnings may be considered errors */
  if ((level == MYSQL_ERROR::WARN_LEVEL_WARN) &&
      thd->really_abort_on_warning())
  {
    elevated_level= MYSQL_ERROR::WARN_LEVEL_ERROR;
  }

313
  if (find_handler(thd, sql_errno, elevated_level))
314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332
  {
    if (elevated_level == MYSQL_ERROR::WARN_LEVEL_ERROR)
    {
      /*
         Forces to abort the current instruction execution.
         NOTE: This code is altering the original meaning of
         the net.report_error flag (send an error to the client).
         In the context of stored procedures with error handlers,
         the flag is reused to cause error propagation,
         until the error handler is reached.
         No messages will be sent to the client in that context.
      */
      thd->net.report_error= 1;
    }
    handled= TRUE;
  }

  return handled;
}
333 334

void
335
sp_rcontext::push_cursor(sp_lex_keeper *lex_keeper, sp_instr_cpush *i)
336
{
unknown's avatar
unknown committed
337 338
  DBUG_ENTER("sp_rcontext::push_cursor");
  DBUG_ASSERT(m_ccount < m_root_parsing_ctx->max_cursor_index());
339
  m_cstack[m_ccount++]= new sp_cursor(lex_keeper, i);
unknown's avatar
unknown committed
340 341
  DBUG_PRINT("info", ("m_ccount: %d", m_ccount));
  DBUG_VOID_RETURN;
342 343 344 345 346
}

void
sp_rcontext::pop_cursors(uint count)
{
unknown's avatar
unknown committed
347 348
  DBUG_ENTER("sp_rcontext::pop_cursors");
  DBUG_ASSERT(m_ccount >= count);
349 350 351 352
  while (count--)
  {
    delete m_cstack[--m_ccount];
  }
unknown's avatar
unknown committed
353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421
  DBUG_PRINT("info", ("m_ccount: %d", m_ccount));
  DBUG_VOID_RETURN;
}

void
sp_rcontext::push_handler(struct sp_cond_type *cond, uint h, int type, uint f)
{
  DBUG_ENTER("sp_rcontext::push_handler");
  DBUG_ASSERT(m_hcount < m_root_parsing_ctx->max_handler_index());

  m_handler[m_hcount].cond= cond;
  m_handler[m_hcount].handler= h;
  m_handler[m_hcount].type= type;
  m_handler[m_hcount].foffset= f;
  m_hcount+= 1;

  DBUG_PRINT("info", ("m_hcount: %d", m_hcount));
  DBUG_VOID_RETURN;
}

void
sp_rcontext::pop_handlers(uint count)
{
  DBUG_ENTER("sp_rcontext::pop_handlers");
  DBUG_ASSERT(m_hcount >= count);
  m_hcount-= count;
  DBUG_PRINT("info", ("m_hcount: %d", m_hcount));
  DBUG_VOID_RETURN;
}

void
sp_rcontext::push_hstack(uint h)
{
  DBUG_ENTER("sp_rcontext::push_hstack");
  DBUG_ASSERT(m_hsp < m_root_parsing_ctx->max_handler_index());
  m_hstack[m_hsp++]= h;
  DBUG_PRINT("info", ("m_hsp: %d", m_hsp));
  DBUG_VOID_RETURN;
}

uint
sp_rcontext::pop_hstack()
{
  uint handler;
  DBUG_ENTER("sp_rcontext::pop_hstack");
  DBUG_ASSERT(m_hsp);
  handler= m_hstack[--m_hsp];
  DBUG_PRINT("info", ("m_hsp: %d", m_hsp));
  DBUG_RETURN(handler);
}

void
sp_rcontext::enter_handler(int hid)
{
  DBUG_ENTER("sp_rcontext::enter_handler");
  DBUG_ASSERT(m_ihsp < m_root_parsing_ctx->max_handler_index());
  m_in_handler[m_ihsp++]= hid;
  DBUG_PRINT("info", ("m_ihsp: %d", m_ihsp));
  DBUG_VOID_RETURN;
}

void
sp_rcontext::exit_handler()
{
  DBUG_ENTER("sp_rcontext::exit_handler");
  DBUG_ASSERT(m_ihsp);
  m_ihsp-= 1;
  DBUG_PRINT("info", ("m_ihsp: %d", m_ihsp));
  DBUG_VOID_RETURN;
422 423 424
}


425
int
426
sp_rcontext::set_variable(THD *thd, uint var_idx, Item **value)
427 428 429 430 431 432
{
  return set_variable(thd, m_var_table->field[var_idx], value);
}


int
433
sp_rcontext::set_variable(THD *thd, Field *field, Item **value)
434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458
{
  if (!value)
  {
    field->set_null();
    return 0;
  }

  return sp_eval_expr(thd, field, value);
}


Item *
sp_rcontext::get_item(uint var_idx)
{
  return m_var_items[var_idx];
}


Item **
sp_rcontext::get_item_addr(uint var_idx)
{
  return m_var_items + var_idx;
}


459 460 461 462 463 464
/*
 *
 *  sp_cursor
 *
 */

465
sp_cursor::sp_cursor(sp_lex_keeper *lex_keeper, sp_instr_cpush *i)
466 467
  :m_lex_keeper(lex_keeper),
   server_side_cursor(NULL),
468
   m_i(i)
469 470 471 472 473 474 475 476 477
{
  /*
    currsor can't be stored in QC, so we should prevent opening QC for
    try to write results which are absent.
  */
  lex_keeper->disable_query_cache();
}


unknown's avatar
unknown committed
478
/*
479
  Open an SP cursor
unknown's avatar
unknown committed
480 481

  SYNOPSIS
482 483
    open()
    THD		         Thread handler
unknown's avatar
unknown committed
484 485 486


  RETURN
487
   0 in case of success, -1 otherwise
unknown's avatar
unknown committed
488 489
*/

490 491
int
sp_cursor::open(THD *thd)
492
{
493
  if (server_side_cursor)
494
  {
unknown's avatar
unknown committed
495 496
    my_message(ER_SP_CURSOR_ALREADY_OPEN, ER(ER_SP_CURSOR_ALREADY_OPEN),
               MYF(0));
497
    return -1;
498
  }
499 500 501 502
  if (mysql_open_cursor(thd, (uint) ALWAYS_MATERIALIZED_CURSOR, &result,
                        &server_side_cursor))
    return -1;
  return 0;
503 504
}

unknown's avatar
unknown committed
505

506 507 508
int
sp_cursor::close(THD *thd)
{
509
  if (! server_side_cursor)
510
  {
unknown's avatar
unknown committed
511
    my_message(ER_SP_CURSOR_NOT_OPEN, ER(ER_SP_CURSOR_NOT_OPEN), MYF(0));
512 513 514 515 516 517
    return -1;
  }
  destroy();
  return 0;
}

unknown's avatar
unknown committed
518

519 520 521
void
sp_cursor::destroy()
{
522 523
  delete server_side_cursor;
  server_side_cursor= 0;
524 525
}

526

527
int
528
sp_cursor::fetch(THD *thd, List<struct sp_variable> *vars)
529
{
530
  if (! server_side_cursor)
531
  {
unknown's avatar
unknown committed
532
    my_message(ER_SP_CURSOR_NOT_OPEN, ER(ER_SP_CURSOR_NOT_OPEN), MYF(0));
533 534
    return -1;
  }
535
  if (vars->elements != result.get_field_count())
536
  {
537 538
    my_message(ER_SP_WRONG_NO_OF_FETCH_ARGS,
               ER(ER_SP_WRONG_NO_OF_FETCH_ARGS), MYF(0));
539 540 541
    return -1;
  }

542
  result.set_spvar_list(vars);
543

544 545 546
  /* Attempt to fetch one row */
  if (server_side_cursor->is_open())
    server_side_cursor->fetch(1);
547

548 549 550 551 552
  /*
    If the cursor was pointing after the last row, the fetch will
    close it instead of sending any rows.
  */
  if (! server_side_cursor->is_open())
553
  {
554
    my_message(ER_SP_FETCH_NO_DATA, ER(ER_SP_FETCH_NO_DATA), MYF(0));
555 556
    return -1;
  }
557

558 559
  return 0;
}
560 561


562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579
/*
  Create an instance of appropriate Item_cache class depending on the
  specified type in the callers arena.

  SYNOPSIS
    thd           thread handler
    result_type   type of the expression

  RETURN
    Pointer to valid object     on success
    NULL                        on error

  NOTE
    We should create cache items in the callers arena, as they are used
    between in several instructions.
*/

Item_cache *
580
sp_rcontext::create_case_expr_holder(THD *thd, const Item *item)
581 582 583 584 585 586
{
  Item_cache *holder;
  Query_arena current_arena;

  thd->set_n_backup_active_arena(thd->spcont->callers_arena, &current_arena);

587
  holder= Item_cache::get_cache(item);
588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624

  thd->restore_active_arena(thd->spcont->callers_arena, &current_arena);

  return holder;
}


/*
  Set CASE expression to the specified value.

  SYNOPSIS
    thd             thread handler
    case_expr_id    identifier of the CASE expression
    case_expr_item  a value of the CASE expression

  RETURN
    FALSE   on success
    TRUE    on error

  NOTE
    The idea is to reuse Item_cache for the expression of the one CASE
    statement. This optimization takes place when there is CASE statement
    inside of a loop. So, in other words, we will use the same object on each
    iteration instead of creating a new one for each iteration.

  TODO
    Hypothetically, a type of CASE expression can be different for each
    iteration. For instance, this can happen if the expression contains a
    session variable (something like @@VAR) and its type is changed from one
    iteration to another.
    
    In order to cope with this problem, we check type each time, when we use
    already created object. If the type does not match, we re-create Item.
    This also can (should?) be optimized.
*/

int
625
sp_rcontext::set_case_expr(THD *thd, int case_expr_id, Item **case_expr_item_ptr)
626
{
627 628
  Item *case_expr_item= sp_prepare_func_item(thd, case_expr_item_ptr);
  if (!case_expr_item)
629 630 631 632 633 634 635
    return TRUE;

  if (!m_case_expr_holders[case_expr_id] ||
      m_case_expr_holders[case_expr_id]->result_type() !=
        case_expr_item->result_type())
  {
    m_case_expr_holders[case_expr_id]=
636
      create_case_expr_holder(thd, case_expr_item);
637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658
  }

  m_case_expr_holders[case_expr_id]->store(case_expr_item);

  return FALSE;
}


Item *
sp_rcontext::get_case_expr(int case_expr_id)
{
  return m_case_expr_holders[case_expr_id];
}


Item **
sp_rcontext::get_case_expr_addr(int case_expr_id)
{
  return (Item**) m_case_expr_holders + case_expr_id;
}


659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675
/***************************************************************************
 Select_fetch_into_spvars
****************************************************************************/

int Select_fetch_into_spvars::prepare(List<Item> &fields, SELECT_LEX_UNIT *u)
{
  /*
    Cache the number of columns in the result set in order to easily
    return an error if column count does not match value count.
  */
  field_count= fields.elements;
  return select_result_interceptor::prepare(fields, u);
}


bool Select_fetch_into_spvars::send_data(List<Item> &items)
{
676
  List_iterator_fast<struct sp_variable> spvar_iter(*spvar_list);
677
  List_iterator_fast<Item> item_iter(items);
678
  sp_variable_t *spvar;
679 680 681 682 683 684 685 686 687
  Item *item;

  /* Must be ensured by the caller */
  DBUG_ASSERT(spvar_list->elements == items.elements);

  /*
    Assign the row fetched from a server side cursor to stored
    procedure variables.
  */
688
  for (; spvar= spvar_iter++, item= item_iter++; )
689
  {
690
    if (thd->spcont->set_variable(thd, spvar->offset, &item))
691
      return TRUE;
692 693 694
  }
  return FALSE;
}