sql_prepare.cc 84.8 KB
Newer Older
unknown's avatar
unknown committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14
/* Copyright (C) 1995-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
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.

   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
15
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA */
unknown's avatar
unknown committed
16 17

/**********************************************************************
18
This file contains the implementation of prepared statements.
unknown's avatar
unknown committed
19

20
When one prepares a statement:
unknown's avatar
unknown committed
21

22
  - Server gets the query from client with command 'COM_STMT_PREPARE';
unknown's avatar
unknown committed
23
    in the following format:
24
    [COM_STMT_PREPARE:1] [query]
unknown's avatar
unknown committed
25
  - Parse the query and recognize any parameter markers '?' and
unknown's avatar
unknown committed
26
    store its information list in lex->param_list
unknown's avatar
unknown committed
27
  - Allocate a new statement for this prepare; and keep this in
28
    'thd->stmt_map'.
unknown's avatar
unknown committed
29
  - Without executing the query, return back to client the total
unknown's avatar
unknown committed
30
    number of parameters along with result-set metadata information
unknown's avatar
unknown committed
31
    (if any) in the following format:
32 33 34
    [STMT_ID:4]
    [Column_count:2]
    [Param_count:2]
35
    [Params meta info (stubs only for now)]  (if Param_count > 0)
36
    [Columns meta info] (if Column_count > 0)
unknown's avatar
unknown committed
37

38
When one executes a statement:
unknown's avatar
unknown committed
39

40
  - Server gets the command 'COM_STMT_EXECUTE' to execute the
41 42
    previously prepared query. If there are any parameter markers, then the
    client will send the data in the following format:
43
    [COM_STMT_EXECUTE:1]
unknown's avatar
unknown committed
44 45 46 47
    [STMT_ID:4]
    [NULL_BITS:(param_count+7)/8)]
    [TYPES_SUPPLIED_BY_CLIENT(0/1):1]
    [[length]data]
unknown's avatar
unknown committed
48 49
    [[length]data] .. [[length]data].
    (Note: Except for string/binary types; all other types will not be
unknown's avatar
unknown committed
50
    supplied with length field)
51 52 53
  - If it is a first execute or types of parameters were altered by client,
    then setup the conversion routines.
  - Assign parameter items from the supplied data.
unknown's avatar
unknown committed
54
  - Execute the query without re-parsing and send back the results
unknown's avatar
unknown committed
55 56
    to client

57
When one supplies long data for a placeholder:
unknown's avatar
unknown committed
58

59 60
  - Server gets the long data in pieces with command type
    'COM_STMT_SEND_LONG_DATA'.
unknown's avatar
unknown committed
61
  - The packet recieved will have the format as:
62
    [COM_STMT_SEND_LONG_DATA:1][STMT_ID:4][parameter_number:2][data]
63
  - data from the packet is appended to the long data value buffer for this
64
    placeholder.
65 66 67 68
  - It's up to the client to stop supplying data chunks at any point. The
    server doesn't care; also, the server doesn't notify the client whether
    it got the data or not; if there is any error, then it will be returned
    at statement execute.
69

unknown's avatar
unknown committed
70 71 72
***********************************************************************/

#include "mysql_priv.h"
unknown's avatar
unknown committed
73
#include "sql_select.h" // for JOIN
74
#include "sql_cursor.h"
75
#include "sp_head.h"
76
#include "sp.h"
unknown's avatar
unknown committed
77
#include "sp_cache.h"
78 79 80
#ifdef EMBEDDED_LIBRARY
/* include MYSQL_BIND headers */
#include <mysql.h>
81 82
#else
#include <mysql_com.h>
83
#endif
unknown's avatar
unknown committed
84

85 86 87 88 89 90 91 92 93 94
/* A result class used to send cursor rows using the binary protocol. */

class Select_fetch_protocol_prep: public select_send
{
  Protocol_prep protocol;
public:
  Select_fetch_protocol_prep(THD *thd);
  virtual bool send_fields(List<Item> &list, uint flags);
  virtual bool send_data(List<Item> &items);
  virtual bool send_eof();
95 96 97 98 99 100
#ifdef EMBEDDED_LIBRARY
  void begin_dataset()
  {
    protocol.begin_dataset();
  }
#endif
101 102
};

103
/******************************************************************************
104
  Prepared_statement: a statement that can contain placeholders
105
******************************************************************************/
106

107 108 109
class Prepared_statement: public Statement
{
public:
110 111 112 113 114
  enum flag_values
  {
    IS_IN_USE= 1
  };

115
  THD *thd;
116
  Select_fetch_protocol_prep result;
117
  Protocol *protocol;
118
  Item_param **param_array;
119 120
  uint param_count;
  uint last_errno;
121
  uint flags;
122 123
  char last_error[MYSQL_ERRMSG_SIZE];
#ifndef EMBEDDED_LIBRARY
124
  bool (*set_params)(Prepared_statement *st, uchar *data, uchar *data_end,
125
                     uchar *read_pos, String *expanded_query);
unknown's avatar
unknown committed
126
#else
127
  bool (*set_params_data)(Prepared_statement *st, String *expanded_query);
unknown's avatar
unknown committed
128
#endif
unknown's avatar
unknown committed
129
  bool (*set_params_from_vars)(Prepared_statement *stmt,
130 131
                               List<LEX_STRING>& varnames,
                               String *expanded_query);
132
public:
133
  Prepared_statement(THD *thd_arg, Protocol *protocol_arg);
134
  virtual ~Prepared_statement();
135
  void setup_set_params();
unknown's avatar
unknown committed
136
  virtual Query_arena::Type type() const;
137
  virtual void cleanup_stmt();
138
  bool set_name(LEX_STRING *name);
139
  inline void close_cursor() { delete cursor; cursor= 0; }
140 141 142 143 144 145

  bool prepare(const char *packet, uint packet_length);
  bool execute(String *expanded_query, bool open_cursor);
  /* Destroy this statement */
  bool deallocate();
};
146

unknown's avatar
unknown committed
147

148 149 150
/******************************************************************************
  Implementation
******************************************************************************/
151 152


153
inline bool is_param_null(const uchar *pos, ulong param_no)
154
{
155
  return pos[param_no/8] & (1 << (param_no & 7));
156 157 158
}

/*
159 160 161 162 163 164 165 166 167 168 169 170 171 172
  Find a prepared statement in the statement map by id.

  SYNOPSIS
    find_prepared_statement()
      thd                thread handle
      id                 statement id
      where              the place from which this function is called (for
                         error reporting).

  DESCRIPTION
    Try to find a prepared statement and set THD error if it's not found.

  RETURN VALUE
    0 if the statement was not found, a pointer otherwise.
173 174
*/

175
static Prepared_statement *
unknown's avatar
unknown committed
176
find_prepared_statement(THD *thd, ulong id, const char *where)
177
{
178 179 180 181 182
  /*
    To strictly separate namespaces of SQL prepared statements and C API
    prepared statements find() will return 0 if there is a named prepared
    statement with such id.
  */
183 184
  Statement *stmt= thd->stmt_map.find(id);

unknown's avatar
unknown committed
185
  if (stmt == 0 || stmt->type() != Query_arena::PREPARED_STATEMENT)
186
  {
187
    char llbuf[22];
188 189
    my_error(ER_UNKNOWN_STMT_HANDLER, MYF(0), sizeof(llbuf), llstr(id, llbuf),
             where);
190 191 192
    return 0;
  }
  return (Prepared_statement *) stmt;
193 194
}

195

196
/*
197 198 199 200 201 202 203
  Send prepared statement id and metadata to the client after prepare.

  SYNOPSIS
    send_prep_stmt()

  RETURN VALUE
    0 in case of success, 1 otherwise
204 205
*/

unknown's avatar
unknown committed
206
#ifndef EMBEDDED_LIBRARY
207
static bool send_prep_stmt(Prepared_statement *stmt, uint columns)
208
{
209
  NET *net= &stmt->thd->net;
unknown's avatar
unknown committed
210 211
  char buff[12];
  uint tmp;
unknown's avatar
unknown committed
212 213
  DBUG_ENTER("send_prep_stmt");

214
  buff[0]= 0;                                   /* OK packet indicator */
215
  int4store(buff+1, stmt->id);
216 217
  int2store(buff+5, columns);
  int2store(buff+7, stmt->param_count);
unknown's avatar
unknown committed
218 219 220 221
  buff[9]= 0;                                   // Guard against a 4.1 client
  tmp= min(stmt->thd->total_warn_count, 65535);
  int2store(buff+10, tmp);

222 223 224 225
  /*
    Send types and names of placeholders to the client
    XXX: fix this nasty upcast from List<Item_param> to List<Item>
  */
unknown's avatar
unknown committed
226
  DBUG_RETURN(my_net_write(net, buff, sizeof(buff)) ||
unknown's avatar
unknown committed
227 228 229
              (stmt->param_count &&
               stmt->thd->protocol_simple.send_fields((List<Item> *)
                                                      &stmt->lex->param_list,
230
                                                      Protocol::SEND_EOF)));
unknown's avatar
unknown committed
231
}
232
#else
233 234
static bool send_prep_stmt(Prepared_statement *stmt,
                           uint columns __attribute__((unused)))
unknown's avatar
unknown committed
235
{
unknown's avatar
SCRUM  
unknown committed
236 237
  THD *thd= stmt->thd;

238
  thd->client_stmt_id= stmt->id;
unknown's avatar
SCRUM  
unknown committed
239
  thd->client_param_count= stmt->param_count;
unknown's avatar
unknown committed
240
  thd->clear_error();
unknown's avatar
unknown committed
241

unknown's avatar
SCRUM  
unknown committed
242
  return 0;
243
}
unknown's avatar
unknown committed
244
#endif /*!EMBEDDED_LIBRARY*/
245

unknown's avatar
unknown committed
246 247

/*
248 249 250 251 252 253 254 255 256 257 258 259 260 261
  Read the length of the parameter data and return it back to
  the caller.

  SYNOPSIS
    get_param_length()
      packet             a pointer to the data
      len                remaining packet length

  DESCRIPTION
    Read data length, position the packet to the first byte after it,
    and return the length to the caller.

  RETURN VALUE
    Length of data piece.
unknown's avatar
unknown committed
262 263
*/

unknown's avatar
unknown committed
264
#ifndef EMBEDDED_LIBRARY
265
static ulong get_param_length(uchar **packet, ulong len)
unknown's avatar
unknown committed
266 267
{
  reg1 uchar *pos= *packet;
268 269
  if (len < 1)
    return 0;
unknown's avatar
unknown committed
270 271 272 273 274
  if (*pos < 251)
  {
    (*packet)++;
    return (ulong) *pos;
  }
275 276
  if (len < 3)
    return 0;
unknown's avatar
unknown committed
277 278 279 280 281
  if (*pos == 252)
  {
    (*packet)+=3;
    return (ulong) uint2korr(pos+1);
  }
282 283
  if (len < 4)
    return 0;
unknown's avatar
unknown committed
284 285 286 287 288
  if (*pos == 253)
  {
    (*packet)+=4;
    return (ulong) uint3korr(pos+1);
  }
289 290
  if (len < 5)
    return 0;
unknown's avatar
unknown committed
291
  (*packet)+=9; // Must be 254 when here
292 293 294 295 296 297 298
  /*
    In our client-server protocol all numbers bigger than 2^24
    stored as 8 bytes with uint8korr. Here we always know that
    parameter length is less than 2^4 so don't look at the second
    4 bytes. But still we need to obey the protocol hence 9 in the
    assignment above.
  */
unknown's avatar
unknown committed
299 300
  return (ulong) uint4korr(pos+1);
}
unknown's avatar
unknown committed
301
#else
302
#define get_param_length(packet, len) len
unknown's avatar
unknown committed
303 304
#endif /*!EMBEDDED_LIBRARY*/

unknown's avatar
unknown committed
305
 /*
306 307
   Data conversion routines.

308
   SYNOPSIS
309 310 311 312
     set_param_xx()
       param             parameter item
       pos               input data buffer
       len               length of data in the buffer
unknown's avatar
unknown committed
313

314 315 316
  DESCRIPTION
    All these functions read the data from pos, convert it to requested
    type and assign to param; pos is advanced to predefined length.
unknown's avatar
unknown committed
317

318 319 320
    Make a note that the NULL handling is examined at first execution
    (i.e. when input types altered) and for all subsequent executions
    we don't read any values for this.
unknown's avatar
unknown committed
321

322 323
  RETURN VALUE
    none
unknown's avatar
unknown committed
324 325
*/

326
static void set_param_tiny(Item_param *param, uchar **pos, ulong len)
unknown's avatar
unknown committed
327
{
328 329 330 331
#ifndef EMBEDDED_LIBRARY
  if (len < 1)
    return;
#endif
332
  int8 value= (int8) **pos;
unknown's avatar
unknown committed
333
  param->set_int(param->unsigned_flag ? (longlong) ((uint8) value) :
334
                                        (longlong) value, 4);
unknown's avatar
unknown committed
335 336 337
  *pos+= 1;
}

338
static void set_param_short(Item_param *param, uchar **pos, ulong len)
unknown's avatar
unknown committed
339
{
340
  int16 value;
341 342 343
#ifndef EMBEDDED_LIBRARY
  if (len < 2)
    return;
344
  value= sint2korr(*pos);
345 346
#else
  shortget(value, *pos);
347
#endif
348
  param->set_int(param->unsigned_flag ? (longlong) ((uint16) value) :
349
                                        (longlong) value, 6);
unknown's avatar
unknown committed
350 351 352
  *pos+= 2;
}

353
static void set_param_int32(Item_param *param, uchar **pos, ulong len)
unknown's avatar
unknown committed
354
{
355
  int32 value;
356 357 358
#ifndef EMBEDDED_LIBRARY
  if (len < 4)
    return;
359
  value= sint4korr(*pos);
360 361
#else
  longget(value, *pos);
362
#endif
363
  param->set_int(param->unsigned_flag ? (longlong) ((uint32) value) :
364
                                        (longlong) value, 11);
unknown's avatar
unknown committed
365 366 367
  *pos+= 4;
}

368
static void set_param_int64(Item_param *param, uchar **pos, ulong len)
unknown's avatar
unknown committed
369
{
370
  longlong value;
371 372 373
#ifndef EMBEDDED_LIBRARY
  if (len < 8)
    return;
374
  value= (longlong) sint8korr(*pos);
375 376
#else
  longlongget(value, *pos);
377
#endif
378
  param->set_int(value, 21);
unknown's avatar
unknown committed
379 380 381
  *pos+= 8;
}

382
static void set_param_float(Item_param *param, uchar **pos, ulong len)
unknown's avatar
unknown committed
383
{
unknown's avatar
unknown committed
384
  float data;
385 386 387
#ifndef EMBEDDED_LIBRARY
  if (len < 4)
    return;
unknown's avatar
unknown committed
388
  float4get(data,*pos);
unknown's avatar
unknown committed
389
#else
390
  floatget(data, *pos);
unknown's avatar
unknown committed
391
#endif
unknown's avatar
unknown committed
392 393 394 395
  param->set_double((double) data);
  *pos+= 4;
}

396
static void set_param_double(Item_param *param, uchar **pos, ulong len)
unknown's avatar
unknown committed
397
{
unknown's avatar
unknown committed
398
  double data;
399 400 401
#ifndef EMBEDDED_LIBRARY
  if (len < 8)
    return;
unknown's avatar
unknown committed
402
  float8get(data,*pos);
unknown's avatar
unknown committed
403
#else
404
  doubleget(data, *pos);
unknown's avatar
unknown committed
405
#endif
unknown's avatar
unknown committed
406 407 408 409
  param->set_double((double) data);
  *pos+= 8;
}

unknown's avatar
unknown committed
410 411 412 413
static void set_param_decimal(Item_param *param, uchar **pos, ulong len)
{
  ulong length= get_param_length(pos, len);
  param->set_decimal((char*)*pos, length);
414
  *pos+= length;
unknown's avatar
unknown committed
415 416
}

417
#ifndef EMBEDDED_LIBRARY
418 419 420 421 422 423 424

/*
  Read date/time/datetime parameter values from network (binary
  protocol). See writing counterparts of these functions in
  libmysql.c (store_param_{time,date,datetime}).
*/

425
static void set_param_time(Item_param *param, uchar **pos, ulong len)
426
{
427 428
  MYSQL_TIME tm;
  ulong length= get_param_length(pos, len);
429

430
  if (length >= 8)
431 432
  {
    uchar *to= *pos;
433
    uint day;
434

435 436
    tm.neg= (bool) to[0];
    day= (uint) sint4korr(to+1);
437
    tm.hour=   (uint) to[5] + day * 24;
438 439
    tm.minute= (uint) to[6];
    tm.second= (uint) to[7];
440
    tm.second_part= (length > 8) ? (ulong) sint4korr(to+8) : 0;
441 442 443 444 445 446 447 448
    if (tm.hour > 838)
    {
      /* TODO: add warning 'Data truncated' here */
      tm.hour= 838;
      tm.minute= 59;
      tm.second= 59;
    }
    tm.day= tm.year= tm.month= 0;
449
  }
450
  else
451
    set_zero_time(&tm, MYSQL_TIMESTAMP_TIME);
452 453
  param->set_time(&tm, MYSQL_TIMESTAMP_TIME,
                  MAX_TIME_WIDTH * MY_CHARSET_BIN_MB_MAXLEN);
454 455 456
  *pos+= length;
}

457
static void set_param_datetime(Item_param *param, uchar **pos, ulong len)
458
{
459 460
  MYSQL_TIME tm;
  ulong length= get_param_length(pos, len);
461

462
  if (length >= 4)
463 464
  {
    uchar *to= *pos;
465 466 467 468 469

    tm.neg=    0;
    tm.year=   (uint) sint2korr(to);
    tm.month=  (uint) to[2];
    tm.day=    (uint) to[3];
470 471 472 473 474 475 476 477 478
    if (length > 4)
    {
      tm.hour=   (uint) to[4];
      tm.minute= (uint) to[5];
      tm.second= (uint) to[6];
    }
    else
      tm.hour= tm.minute= tm.second= 0;

479
    tm.second_part= (length > 7) ? (ulong) sint4korr(to+7) : 0;
480
  }
481
  else
482
    set_zero_time(&tm, MYSQL_TIMESTAMP_DATETIME);
483 484
  param->set_time(&tm, MYSQL_TIMESTAMP_DATETIME,
                  MAX_DATETIME_WIDTH * MY_CHARSET_BIN_MB_MAXLEN);
485 486 487
  *pos+= length;
}

488

489
static void set_param_date(Item_param *param, uchar **pos, ulong len)
490
{
491 492 493 494
  MYSQL_TIME tm;
  ulong length= get_param_length(pos, len);

  if (length >= 4)
495 496
  {
    uchar *to= *pos;
497

498
    tm.year=  (uint) sint2korr(to);
499 500 501 502 503 504 505
    tm.month=  (uint) to[2];
    tm.day= (uint) to[3];

    tm.hour= tm.minute= tm.second= 0;
    tm.second_part= 0;
    tm.neg= 0;
  }
506
  else
507
    set_zero_time(&tm, MYSQL_TIMESTAMP_DATE);
508 509
  param->set_time(&tm, MYSQL_TIMESTAMP_DATE,
                  MAX_DATE_WIDTH * MY_CHARSET_BIN_MB_MAXLEN);
510 511 512
  *pos+= length;
}

513 514 515
#else/*!EMBEDDED_LIBRARY*/
void set_param_time(Item_param *param, uchar **pos, ulong len)
{
516 517 518 519 520 521 522 523 524 525 526
  MYSQL_TIME tm= *((MYSQL_TIME*)*pos);
  tm.hour+= tm.day * 24;
  tm.day= tm.year= tm.month= 0;
  if (tm.hour > 838)
  {
    /* TODO: add warning 'Data truncated' here */
    tm.hour= 838;
    tm.minute= 59;
    tm.second= 59;
  }
  param->set_time(&tm, MYSQL_TIMESTAMP_TIME,
527
                  MAX_TIME_WIDTH * MY_CHARSET_BIN_MB_MAXLEN);
528 529 530 531 532

}

void set_param_datetime(Item_param *param, uchar **pos, ulong len)
{
533 534
  MYSQL_TIME tm= *((MYSQL_TIME*)*pos);
  tm.neg= 0;
535

536
  param->set_time(&tm, MYSQL_TIMESTAMP_DATETIME,
537
                  MAX_DATETIME_WIDTH * MY_CHARSET_BIN_MB_MAXLEN);
538 539 540 541 542 543
}

void set_param_date(Item_param *param, uchar **pos, ulong len)
{
  MYSQL_TIME *to= (MYSQL_TIME*)*pos;

544
  param->set_time(to, MYSQL_TIMESTAMP_DATE,
545
                  MAX_DATE_WIDTH * MY_CHARSET_BIN_MB_MAXLEN);
546 547 548
}
#endif /*!EMBEDDED_LIBRARY*/

549 550

static void set_param_str(Item_param *param, uchar **pos, ulong len)
unknown's avatar
unknown committed
551
{
552
  ulong length= get_param_length(pos, len);
553
  param->set_str((const char *)*pos, length);
554
  *pos+= length;
unknown's avatar
unknown committed
555 556
}

557

unknown's avatar
unknown committed
558
#undef get_param_length
559 560 561

static void setup_one_conversion_function(THD *thd, Item_param *param,
                                          uchar param_type)
unknown's avatar
unknown committed
562
{
unknown's avatar
unknown committed
563
  switch (param_type) {
564
  case MYSQL_TYPE_TINY:
565
    param->set_param_func= set_param_tiny;
566
    param->item_type= Item::INT_ITEM;
567
    param->item_result_type= INT_RESULT;
unknown's avatar
unknown committed
568
    break;
569
  case MYSQL_TYPE_SHORT:
570
    param->set_param_func= set_param_short;
571
    param->item_type= Item::INT_ITEM;
572
    param->item_result_type= INT_RESULT;
unknown's avatar
unknown committed
573
    break;
574
  case MYSQL_TYPE_LONG:
575
    param->set_param_func= set_param_int32;
576
    param->item_type= Item::INT_ITEM;
577
    param->item_result_type= INT_RESULT;
unknown's avatar
unknown committed
578
    break;
579
  case MYSQL_TYPE_LONGLONG:
580
    param->set_param_func= set_param_int64;
581
    param->item_type= Item::INT_ITEM;
582
    param->item_result_type= INT_RESULT;
unknown's avatar
unknown committed
583
    break;
584
  case MYSQL_TYPE_FLOAT:
585
    param->set_param_func= set_param_float;
586
    param->item_type= Item::REAL_ITEM;
587
    param->item_result_type= REAL_RESULT;
unknown's avatar
unknown committed
588
    break;
589
  case MYSQL_TYPE_DOUBLE:
590
    param->set_param_func= set_param_double;
591
    param->item_type= Item::REAL_ITEM;
592
    param->item_result_type= REAL_RESULT;
unknown's avatar
unknown committed
593
    break;
unknown's avatar
unknown committed
594 595 596 597 598 599
  case MYSQL_TYPE_DECIMAL:
  case MYSQL_TYPE_NEWDECIMAL:
    param->set_param_func= set_param_decimal;
    param->item_type= Item::DECIMAL_ITEM;
    param->item_result_type= DECIMAL_RESULT;
    break;
600
  case MYSQL_TYPE_TIME:
601
    param->set_param_func= set_param_time;
602
    param->item_type= Item::STRING_ITEM;
603
    param->item_result_type= STRING_RESULT;
604
    break;
605
  case MYSQL_TYPE_DATE:
606
    param->set_param_func= set_param_date;
607
    param->item_type= Item::STRING_ITEM;
608
    param->item_result_type= STRING_RESULT;
609
    break;
610 611
  case MYSQL_TYPE_DATETIME:
  case MYSQL_TYPE_TIMESTAMP:
612
    param->set_param_func= set_param_datetime;
613
    param->item_type= Item::STRING_ITEM;
614
    param->item_result_type= STRING_RESULT;
615
    break;
616 617 618 619
  case MYSQL_TYPE_TINY_BLOB:
  case MYSQL_TYPE_MEDIUM_BLOB:
  case MYSQL_TYPE_LONG_BLOB:
  case MYSQL_TYPE_BLOB:
620
    param->set_param_func= set_param_str;
621 622 623
    param->value.cs_info.character_set_of_placeholder= &my_charset_bin;
    param->value.cs_info.character_set_client=
      thd->variables.character_set_client;
624
    DBUG_ASSERT(thd->variables.character_set_client);
625 626
    param->value.cs_info.final_character_set_of_str_value= &my_charset_bin;
    param->item_type= Item::STRING_ITEM;
627
    param->item_result_type= STRING_RESULT;
628 629 630 631 632 633 634 635 636 637 638 639
    break;
  default:
    /*
      The client library ensures that we won't get any other typecodes
      except typecodes above and typecodes for string types. Marking
      label as 'default' lets us to handle malformed packets as well.
    */
    {
      CHARSET_INFO *fromcs= thd->variables.character_set_client;
      CHARSET_INFO *tocs= thd->variables.collation_connection;
      uint32 dummy_offset;

640
      param->value.cs_info.character_set_of_placeholder= fromcs;
641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658
      param->value.cs_info.character_set_client= fromcs;

      /*
        Setup source and destination character sets so that they
        are different only if conversion is necessary: this will
        make later checks easier.
      */
      param->value.cs_info.final_character_set_of_str_value=
        String::needs_conversion(0, fromcs, tocs, &dummy_offset) ?
        tocs : fromcs;
      param->set_param_func= set_param_str;
      /*
        Exact value of max_length is not known unless data is converted to
        charset of connection, so we have to set it later.
      */
      param->item_type= Item::STRING_ITEM;
      param->item_result_type= STRING_RESULT;
    }
unknown's avatar
unknown committed
659
  }
660
  param->param_type= (enum enum_field_types) param_type;
unknown's avatar
unknown committed
661 662
}

unknown's avatar
unknown committed
663
#ifndef EMBEDDED_LIBRARY
unknown's avatar
unknown committed
664
/*
665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693
  Routines to assign parameters from data supplied by the client.

  DESCRIPTION
    Update the parameter markers by reading data from the packet and
    and generate a valid query for logging.

  NOTES
    This function, along with other _withlog functions is called when one of
    binary, slow or general logs is open. Logging of prepared statements in
    all cases is performed by means of conventional queries: if parameter
    data was supplied from C API, each placeholder in the query is
    replaced with its actual value; if we're logging a [Dynamic] SQL
    prepared statement, parameter markers are replaced with variable names.
    Example:
     mysql_stmt_prepare("UPDATE t1 SET a=a*1.25 WHERE a=?")
       --> general logs gets [Prepare] UPDATE t1 SET a*1.25 WHERE a=?"
     mysql_stmt_execute(stmt);
       --> general and binary logs get
                             [Execute] UPDATE t1 SET a*1.25 WHERE a=1"
     If a statement has been prepared using SQL syntax:
     PREPARE stmt FROM "UPDATE t1 SET a=a*1.25 WHERE a=?"
       --> general log gets
                                 [Query]   PREPARE stmt FROM "UPDATE ..."
     EXECUTE stmt USING @a
       --> general log gets
                                 [Query]   EXECUTE stmt USING @a;

  RETURN VALUE
   0 if success, 1 otherwise
unknown's avatar
unknown committed
694 695
*/

696
static bool insert_params_withlog(Prepared_statement *stmt, uchar *null_array,
unknown's avatar
unknown committed
697
                                  uchar *read_pos, uchar *data_end,
698
                                  String *query)
699
{
700 701 702 703
  THD  *thd= stmt->thd;
  Item_param **begin= stmt->param_array;
  Item_param **end= begin + stmt->param_count;
  uint32 length= 0;
unknown's avatar
unknown committed
704
  String str;
705
  const String *res;
unknown's avatar
unknown committed
706
  DBUG_ENTER("insert_params_withlog");
707

708
  if (query->copy(stmt->query, stmt->query_length, default_charset_info))
709
    DBUG_RETURN(1);
unknown's avatar
unknown committed
710

711
  for (Item_param **it= begin; it < end; ++it)
712
  {
713
    Item_param *param= *it;
714
    if (param->state != Item_param::LONG_DATA_VALUE)
715
    {
716
      if (is_param_null(null_array, it - begin))
717
        param->set_null();
718 719
      else
      {
720 721 722
        if (read_pos >= data_end)
          DBUG_RETURN(1);
        param->set_param_func(param, &read_pos, data_end - read_pos);
723 724
      }
    }
725 726 727 728
    res= param->query_val_str(&str);
    if (param->convert_str_value(thd))
      DBUG_RETURN(1);                           /* out of memory */

729
    if (query->replace(param->pos_in_query+length, 1, *res))
730
      DBUG_RETURN(1);
unknown's avatar
unknown committed
731

732 733 734 735 736
    length+= res->length()-1;
  }
  DBUG_RETURN(0);
}

737

738
static bool insert_params(Prepared_statement *stmt, uchar *null_array,
unknown's avatar
unknown committed
739
                          uchar *read_pos, uchar *data_end,
740
                          String *expanded_query)
741
{
742 743
  Item_param **begin= stmt->param_array;
  Item_param **end= begin + stmt->param_count;
744

unknown's avatar
unknown committed
745
  DBUG_ENTER("insert_params");
746

747
  for (Item_param **it= begin; it < end; ++it)
748
  {
749
    Item_param *param= *it;
750
    if (param->state != Item_param::LONG_DATA_VALUE)
751
    {
752
      if (is_param_null(null_array, it - begin))
753
        param->set_null();
754 755
      else
      {
756 757 758
        if (read_pos >= data_end)
          DBUG_RETURN(1);
        param->set_param_func(param, &read_pos, data_end - read_pos);
759 760
      }
    }
761 762
    if (param->convert_str_value(stmt->thd))
      DBUG_RETURN(1);                           /* out of memory */
763 764 765 766
  }
  DBUG_RETURN(0);
}

767

768
static bool setup_conversion_functions(Prepared_statement *stmt,
769
                                       uchar **data, uchar *data_end)
770 771 772
{
  /* skip null bits */
  uchar *read_pos= *data + (stmt->param_count+7) / 8;
unknown's avatar
unknown committed
773

774
  DBUG_ENTER("setup_conversion_functions");
775

unknown's avatar
unknown committed
776
  if (*read_pos++) //types supplied / first execute
777
  {
unknown's avatar
unknown committed
778
    /*
unknown's avatar
unknown committed
779
      First execute or types altered by the client, setup the
unknown's avatar
unknown committed
780 781
      conversion routines for all parameters (one time)
    */
782 783
    Item_param **it= stmt->param_array;
    Item_param **end= it + stmt->param_count;
784
    THD *thd= stmt->thd;
785 786
    for (; it < end; ++it)
    {
787 788 789
      ushort typecode;
      const uint signed_bit= 1 << 15;

790 791
      if (read_pos >= data_end)
        DBUG_RETURN(1);
792 793

      typecode= sint2korr(read_pos);
unknown's avatar
unknown committed
794
      read_pos+= 2;
795
      (**it).unsigned_flag= test(typecode & signed_bit);
796
      setup_one_conversion_function(thd, *it, (uchar) (typecode & ~signed_bit));
unknown's avatar
unknown committed
797
    }
798 799
  }
  *data= read_pos;
unknown's avatar
unknown committed
800 801 802
  DBUG_RETURN(0);
}

803 804
#else

805 806 807 808 809 810 811 812 813 814 815
/*
  Embedded counterparts of parameter assignment routines.

  DESCRIPTION
    The main difference between the embedded library and the server is
    that in embedded case we don't serialize/deserialize parameters data.
    Additionally, for unknown reason, the client-side flag raised for
    changed types of placeholders is ignored and we simply setup conversion
    functions at each execute (TODO: fix).
*/

816
static bool emb_insert_params(Prepared_statement *stmt, String *expanded_query)
817
{
818
  THD *thd= stmt->thd;
819 820
  Item_param **it= stmt->param_array;
  Item_param **end= it + stmt->param_count;
821 822
  MYSQL_BIND *client_param= stmt->thd->client_params;

823
  DBUG_ENTER("emb_insert_params");
824

825 826 827
  for (; it < end; ++it, ++client_param)
  {
    Item_param *param= *it;
828 829
    setup_one_conversion_function(thd, param, client_param->buffer_type);
    if (param->state != Item_param::LONG_DATA_VALUE)
830 831
    {
      if (*client_param->is_null)
832
        param->set_null();
833 834
      else
      {
835
        uchar *buff= (uchar*) client_param->buffer;
unknown's avatar
unknown committed
836
        param->unsigned_flag= client_param->is_unsigned;
837
        param->set_param_func(param, &buff,
unknown's avatar
unknown committed
838 839
                              client_param->length ?
                              *client_param->length :
840
                              client_param->buffer_length);
841 842
      }
    }
843 844
    if (param->convert_str_value(thd))
      DBUG_RETURN(1);                           /* out of memory */
845 846 847 848
  }
  DBUG_RETURN(0);
}

849

850
static bool emb_insert_params_withlog(Prepared_statement *stmt, String *query)
851
{
852
  THD *thd= stmt->thd;
853 854
  Item_param **it= stmt->param_array;
  Item_param **end= it + stmt->param_count;
855 856
  MYSQL_BIND *client_param= thd->client_params;

857
  String str;
858
  const String *res;
859
  uint32 length= 0;
860

861
  DBUG_ENTER("emb_insert_params_withlog");
862

863
  if (query->copy(stmt->query, stmt->query_length, default_charset_info))
864
    DBUG_RETURN(1);
unknown's avatar
unknown committed
865

866 867 868
  for (; it < end; ++it, ++client_param)
  {
    Item_param *param= *it;
869 870
    setup_one_conversion_function(thd, param, client_param->buffer_type);
    if (param->state != Item_param::LONG_DATA_VALUE)
871 872
    {
      if (*client_param->is_null)
873
        param->set_null();
874 875
      else
      {
876
        uchar *buff= (uchar*)client_param->buffer;
unknown's avatar
unknown committed
877
        param->unsigned_flag= client_param->is_unsigned;
878
        param->set_param_func(param, &buff,
unknown's avatar
unknown committed
879 880
                              client_param->length ?
                              *client_param->length :
881
                              client_param->buffer_length);
882 883
      }
    }
884 885 886 887
    res= param->query_val_str(&str);
    if (param->convert_str_value(thd))
      DBUG_RETURN(1);                           /* out of memory */

888
    if (query->replace(param->pos_in_query+length, 1, *res))
889
      DBUG_RETURN(1);
890

891 892 893 894 895
    length+= res->length()-1;
  }
  DBUG_RETURN(0);
}

unknown's avatar
unknown committed
896 897
#endif /*!EMBEDDED_LIBRARY*/

unknown's avatar
unknown committed
898

899
/*
900 901
  Assign prepared statement parameters from user variables.

unknown's avatar
unknown committed
902 903 904 905 906
  SYNOPSIS
    insert_params_from_vars()
      stmt      Statement
      varnames  List of variables. Caller must ensure that number of variables
                in the list is equal to number of statement parameters
907
      query     Ignored
unknown's avatar
unknown committed
908 909
*/

910 911
static bool insert_params_from_vars(Prepared_statement *stmt,
                                    List<LEX_STRING>& varnames,
912
                                    String *query __attribute__((unused)))
unknown's avatar
unknown committed
913 914 915 916 917 918
{
  Item_param **begin= stmt->param_array;
  Item_param **end= begin + stmt->param_count;
  user_var_entry *entry;
  LEX_STRING *varname;
  List_iterator<LEX_STRING> var_it(varnames);
919 920
  DBUG_ENTER("insert_params_from_vars");

unknown's avatar
unknown committed
921 922 923 924
  for (Item_param **it= begin; it < end; ++it)
  {
    Item_param *param= *it;
    varname= var_it++;
925 926 927 928 929 930
    entry= (user_var_entry*)hash_search(&stmt->thd->user_vars,
                                        (byte*) varname->str,
                                         varname->length);
    if (param->set_from_user_var(stmt->thd, entry) ||
        param->convert_str_value(stmt->thd))
      DBUG_RETURN(1);
unknown's avatar
unknown committed
931 932 933 934
  }
  DBUG_RETURN(0);
}

935

936
/*
937 938
  Do the same as insert_params_from_vars but also construct query text for
  binary log.
939

940 941
  SYNOPSIS
    insert_params_from_vars()
942
      stmt      Prepared statement
943 944
      varnames  List of variables. Caller must ensure that number of variables
                in the list is equal to number of statement parameters
945 946
      query     The query with parameter markers replaced with corresponding
                user variables that were used to execute the query.
947 948
*/

unknown's avatar
unknown committed
949
static bool insert_params_from_vars_with_log(Prepared_statement *stmt,
950
                                             List<LEX_STRING>& varnames,
951
                                             String *query)
unknown's avatar
unknown committed
952 953 954 955 956 957
{
  Item_param **begin= stmt->param_array;
  Item_param **end= begin + stmt->param_count;
  user_var_entry *entry;
  LEX_STRING *varname;
  List_iterator<LEX_STRING> var_it(varnames);
958 959
  String buf;
  const String *val;
unknown's avatar
unknown committed
960
  uint32 length= 0;
961 962 963

  DBUG_ENTER("insert_params_from_vars");

964
  if (query->copy(stmt->query, stmt->query_length, default_charset_info))
965
    DBUG_RETURN(1);
unknown's avatar
unknown committed
966 967 968 969 970

  for (Item_param **it= begin; it < end; ++it)
  {
    Item_param *param= *it;
    varname= var_it++;
971 972
    if (get_var_with_binlog(stmt->thd, stmt->lex->sql_command,
                            *varname, &entry))
973
        DBUG_RETURN(1);
unknown's avatar
unknown committed
974

975 976 977
    if (param->set_from_user_var(stmt->thd, entry))
      DBUG_RETURN(1);
    /* Insert @'escaped-varname' instead of parameter in the query */
978 979 980 981 982 983
    if (entry)
    {
      char *begin, *ptr;
      buf.length(0);
      if (buf.reserve(entry->name.length*2+3))
        DBUG_RETURN(1);
984

985 986 987 988
      begin= ptr= buf.c_ptr_quick();
      *ptr++= '@';
      *ptr++= '\'';
      ptr+= escape_string_for_mysql(&my_charset_utf8_general_ci,
unknown's avatar
unknown committed
989 990
                                    ptr, 0, entry->name.str,
                                    entry->name.length);
991 992 993 994 995 996
      *ptr++= '\'';
      buf.length(ptr - begin);
      val= &buf;
    }
    else
      val= &my_null_string;
997 998 999

    if (param->convert_str_value(stmt->thd))
      DBUG_RETURN(1);                           /* out of memory */
1000

1001
    if (query->replace(param->pos_in_query+length, 1, *val))
1002
      DBUG_RETURN(1);
1003
    length+= val->length()-1;
unknown's avatar
unknown committed
1004 1005 1006 1007
  }
  DBUG_RETURN(0);
}

unknown's avatar
unknown committed
1008
/*
1009
  Validate INSERT statement.
unknown's avatar
unknown committed
1010

1011
  SYNOPSIS
unknown's avatar
unknown committed
1012
    mysql_test_insert()
1013 1014
      stmt               prepared statement
      tables             global/local table list
unknown's avatar
unknown committed
1015

1016
  RETURN VALUE
1017 1018
    FALSE                success
    TRUE                 error, error message is set in THD
unknown's avatar
unknown committed
1019
*/
unknown's avatar
unknown committed
1020

unknown's avatar
unknown committed
1021 1022
static bool mysql_test_insert(Prepared_statement *stmt,
                              TABLE_LIST *table_list,
unknown's avatar
unknown committed
1023
                              List<Item> &fields,
unknown's avatar
unknown committed
1024 1025 1026 1027
                              List<List_item> &values_list,
                              List<Item> &update_fields,
                              List<Item> &update_values,
                              enum_duplicates duplic)
unknown's avatar
unknown committed
1028
{
1029
  THD *thd= stmt->thd;
1030
  LEX *lex= stmt->lex;
unknown's avatar
unknown committed
1031 1032
  List_iterator_fast<List_item> its(values_list);
  List_item *values;
1033
  DBUG_ENTER("mysql_test_insert");
unknown's avatar
unknown committed
1034

unknown's avatar
unknown committed
1035 1036
  if (insert_precheck(thd, table_list))
    goto error;
unknown's avatar
unknown committed
1037

1038
  /*
unknown's avatar
unknown committed
1039 1040 1041 1042 1043 1044
    open temporary memory pool for temporary data allocated by derived
    tables & preparation procedure
    Note that this is done without locks (should not be needed as we will not
    access any data here)
    If we would use locks, then we have to ensure we are not using
    TL_WRITE_DELAYED as having two such locks can cause table corruption.
unknown's avatar
unknown committed
1045
  */
1046
  if (open_normal_and_derived_tables(thd, table_list, 0))
unknown's avatar
unknown committed
1047
    goto error;
unknown's avatar
unknown committed
1048

unknown's avatar
unknown committed
1049 1050 1051
  if ((values= its++))
  {
    uint value_count;
1052
    ulong counter= 0;
unknown's avatar
unknown committed
1053
    Item *unused_conds= 0;
unknown's avatar
unknown committed
1054

unknown's avatar
unknown committed
1055
    if (table_list->table)
1056 1057 1058 1059 1060
    {
      // don't allocate insert_values
      table_list->table->insert_values=(byte *)1;
    }

unknown's avatar
unknown committed
1061 1062 1063
    if (mysql_prepare_insert(thd, table_list, table_list->table,
                             fields, values, update_fields, update_values,
                             duplic, &unused_conds, FALSE))
unknown's avatar
unknown committed
1064
      goto error;
1065

unknown's avatar
unknown committed
1066 1067
    value_count= values->elements;
    its.rewind();
1068

unknown's avatar
unknown committed
1069
    if (table_list->lock_type == TL_WRITE_DELAYED &&
1070
        !(table_list->table->file->ha_table_flags() & HA_CAN_INSERT_DELAYED))
unknown's avatar
unknown committed
1071 1072 1073
    {
      my_error(ER_ILLEGAL_HA, MYF(0), (table_list->view ?
                                       table_list->view_name.str :
1074
                                       table_list->table_name));
unknown's avatar
unknown committed
1075 1076
      goto error;
    }
1077
    while ((values= its++))
unknown's avatar
unknown committed
1078 1079 1080 1081
    {
      counter++;
      if (values->elements != value_count)
      {
1082
        my_error(ER_WRONG_VALUE_COUNT_ON_ROW, MYF(0), counter);
1083
        goto error;
unknown's avatar
unknown committed
1084
      }
1085
      if (setup_fields(thd, 0, *values, MARK_COLUMNS_NONE, 0, 0))
unknown's avatar
unknown committed
1086
        goto error;
unknown's avatar
unknown committed
1087 1088
    }
  }
unknown's avatar
unknown committed
1089
  DBUG_RETURN(FALSE);
1090 1091

error:
1092
  /* insert_values is cleared in open_table */
unknown's avatar
unknown committed
1093
  DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
1094 1095 1096 1097
}


/*
unknown's avatar
unknown committed
1098 1099
  Validate UPDATE statement

1100
  SYNOPSIS
1101
    mysql_test_update()
1102 1103
      stmt               prepared statement
      tables             list of tables used in this query
unknown's avatar
unknown committed
1104

1105
  RETURN VALUE
1106 1107 1108
    0                    success
    1                    error, error message is set in THD
    2                    convert to multi_update
unknown's avatar
unknown committed
1109
*/
unknown's avatar
unknown committed
1110

unknown's avatar
unknown committed
1111
static int mysql_test_update(Prepared_statement *stmt,
unknown's avatar
unknown committed
1112
                              TABLE_LIST *table_list)
unknown's avatar
unknown committed
1113
{
unknown's avatar
unknown committed
1114
  int res;
1115
  THD *thd= stmt->thd;
unknown's avatar
unknown committed
1116
  uint table_count= 0;
unknown's avatar
unknown committed
1117
  SELECT_LEX *select= &stmt->lex->select_lex;
1118
#ifndef NO_EMBEDDED_ACCESS_CHECKS
unknown's avatar
unknown committed
1119
  uint          want_privilege;
1120
#endif
1121
  bool need_reopen;
unknown's avatar
unknown committed
1122 1123
  DBUG_ENTER("mysql_test_update");

unknown's avatar
merge  
unknown committed
1124
  if (update_precheck(thd, table_list))
unknown's avatar
unknown committed
1125 1126
    goto error;

1127
  for ( ; ; )
1128
  {
1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145
    if (open_tables(thd, &table_list, &table_count, 0))
      goto error;

    if (table_list->multitable_view)
    {
      DBUG_ASSERT(table_list->view != 0);
      DBUG_PRINT("info", ("Switch to multi-update"));
      /* pass counter value */
      thd->lex->table_count= table_count;
      /* convert to multiupdate */
      DBUG_RETURN(2);
    }

    if (!lock_tables(thd, table_list, table_count, &need_reopen))
      break;
    if (!need_reopen)
      goto error;
1146
    close_tables_for_reopen(thd, &table_list);
unknown's avatar
unknown committed
1147
  }
unknown's avatar
unknown committed
1148

unknown's avatar
unknown committed
1149 1150 1151 1152
  /*
    thd->fill_derived_tables() is false here for sure (because it is
    preparation of PS, so we even do not check it).
  */
1153
  if (mysql_handle_derived(thd->lex, &mysql_derived_prepare))
unknown's avatar
unknown committed
1154
    goto error;
unknown's avatar
unknown committed
1155

1156 1157 1158 1159 1160
#ifndef NO_EMBEDDED_ACCESS_CHECKS
  /* TABLE_LIST contain right privilages request */
  want_privilege= table_list->grant.want_privilege;
#endif

unknown's avatar
unknown committed
1161 1162 1163 1164 1165
  if (mysql_prepare_update(thd, table_list, &select->where,
                           select->order_list.elements,
                           (ORDER *) select->order_list.first))
    goto error;

1166
#ifndef NO_EMBEDDED_ACCESS_CHECKS
unknown's avatar
unknown committed
1167 1168
  table_list->grant.want_privilege= want_privilege;
  table_list->table->grant.want_privilege= want_privilege;
1169
  table_list->register_want_access(want_privilege);
1170
#endif
1171
  thd->lex->select_lex.no_wrap_view_item= TRUE;
1172
  res= setup_fields(thd, 0, select->item_list, MARK_COLUMNS_READ, 0, 0);
1173
  thd->lex->select_lex.no_wrap_view_item= FALSE;
unknown's avatar
unknown committed
1174 1175
  if (res)
    goto error;
1176
#ifndef NO_EMBEDDED_ACCESS_CHECKS
unknown's avatar
unknown committed
1177 1178 1179 1180
  /* Check values */
  table_list->grant.want_privilege=
  table_list->table->grant.want_privilege=
    (SELECT_ACL & ~table_list->table->grant.privilege);
1181
  table_list->register_want_access(SELECT_ACL);
1182
#endif
1183
  if (setup_fields(thd, 0, stmt->lex->value_list, MARK_COLUMNS_NONE, 0, 0))
unknown's avatar
unknown committed
1184
    goto error;
unknown's avatar
unknown committed
1185
  /* TODO: here we should send types of placeholders to the client. */
unknown's avatar
unknown committed
1186 1187 1188
  DBUG_RETURN(0);
error:
  DBUG_RETURN(1);
unknown's avatar
unknown committed
1189 1190 1191 1192
}


/*
1193
  Validate DELETE statement.
unknown's avatar
unknown committed
1194

1195
  SYNOPSIS
unknown's avatar
unknown committed
1196
    mysql_test_delete()
1197 1198
      stmt               prepared statement
      tables             list of tables used in this query
unknown's avatar
unknown committed
1199

1200
  RETURN VALUE
1201 1202
    FALSE                success
    TRUE                 error, error message is set in THD
unknown's avatar
unknown committed
1203
*/
unknown's avatar
unknown committed
1204 1205 1206

static bool mysql_test_delete(Prepared_statement *stmt,
                              TABLE_LIST *table_list)
unknown's avatar
unknown committed
1207
{
1208
  THD *thd= stmt->thd;
unknown's avatar
unknown committed
1209 1210
  LEX *lex= stmt->lex;
  DBUG_ENTER("mysql_test_delete");
unknown's avatar
unknown committed
1211

unknown's avatar
unknown committed
1212 1213 1214
  if (delete_precheck(thd, table_list) ||
      open_and_lock_tables(thd, table_list))
    goto error;
unknown's avatar
unknown committed
1215

unknown's avatar
unknown committed
1216
  if (!table_list->table)
unknown's avatar
unknown committed
1217
  {
unknown's avatar
unknown committed
1218 1219 1220
    my_error(ER_VIEW_DELETE_MERGE_VIEW, MYF(0),
             table_list->view_db.str, table_list->view_name.str);
    goto error;
unknown's avatar
unknown committed
1221
  }
unknown's avatar
unknown committed
1222 1223 1224

  DBUG_RETURN(mysql_prepare_delete(thd, table_list, &lex->select_lex.where));
error:
unknown's avatar
unknown committed
1225
  DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
1226 1227
}

unknown's avatar
unknown committed
1228

unknown's avatar
unknown committed
1229
/*
unknown's avatar
unknown committed
1230 1231
  Validate SELECT statement.

1232
  SYNOPSIS
unknown's avatar
unknown committed
1233
    mysql_test_select()
1234 1235 1236 1237 1238 1239
      stmt               prepared statement
      tables             list of tables used in the query

  DESCRIPTION
    In case of success, if this query is not EXPLAIN, send column list info
    back to the client.
unknown's avatar
unknown committed
1240

1241
  RETURN VALUE
1242 1243 1244
    0                    success
    1                    error, error message is set in THD
    2                    success, and statement metadata has been sent
unknown's avatar
unknown committed
1245
*/
1246

1247 1248
static int mysql_test_select(Prepared_statement *stmt,
                             TABLE_LIST *tables, bool text_protocol)
unknown's avatar
unknown committed
1249
{
1250
  THD *thd= stmt->thd;
1251
  LEX *lex= stmt->lex;
1252
  SELECT_LEX_UNIT *unit= &lex->unit;
unknown's avatar
unknown committed
1253
  DBUG_ENTER("mysql_test_select");
unknown's avatar
unknown committed
1254

1255 1256
  lex->select_lex.context.resolve_in_select_list= TRUE;

unknown's avatar
unknown committed
1257
#ifndef NO_EMBEDDED_ACCESS_CHECKS
1258 1259 1260
  ulong privilege= lex->exchange ? SELECT_ACL | FILE_ACL : SELECT_ACL;
  if (tables)
  {
unknown's avatar
unknown committed
1261
    if (check_table_access(thd, privilege, tables,0))
unknown's avatar
unknown committed
1262
      goto error;
1263
  }
1264
  else if (check_access(thd, privilege, any_db,0,0,0,0))
unknown's avatar
unknown committed
1265
    goto error;
unknown's avatar
unknown committed
1266
#endif
unknown's avatar
unknown committed
1267

unknown's avatar
unknown committed
1268
  if (!lex->result && !(lex->result= new (stmt->mem_root) select_send))
unknown's avatar
unknown committed
1269 1270 1271 1272
  {
    my_error(ER_OUTOFMEMORY, MYF(0), sizeof(select_send));
    goto error;
  }
1273

1274
  if (open_and_lock_tables(thd, tables))
unknown's avatar
unknown committed
1275
    goto error;
unknown's avatar
unknown committed
1276

1277 1278
  thd->used_tables= 0;                        // Updated by setup_fields

1279 1280 1281 1282 1283
  /*
    JOIN::prepare calls
    It is not SELECT COMMAND for sure, so setup_tables will be called as
    usual, and we pass 0 as setup_tables_done_option
  */
1284
  if (unit->prepare(thd, 0, 0))
unknown's avatar
unknown committed
1285
    goto error;
1286
  if (!lex->describe && !text_protocol)
1287
  {
1288 1289
    /* Make copy of item list, as change_columns may change it */
    List<Item> fields(lex->select_lex.item_list);
1290

1291 1292 1293
    /* Change columns if a procedure like analyse() */
    if (unit->last_procedure && unit->last_procedure->change_columns(fields))
      goto error;
1294

1295 1296 1297 1298 1299 1300 1301 1302 1303
    /*
      We can use lex->result as it should've been prepared in
      unit->prepare call above.
    */
    if (send_prep_stmt(stmt, lex->result->field_count(fields)) ||
        lex->result->send_fields(fields, Protocol::SEND_EOF) ||
        thd->protocol->flush())
      goto error;
    DBUG_RETURN(2);
1304
  }
1305
  DBUG_RETURN(0);
unknown's avatar
unknown committed
1306
error:
1307
  DBUG_RETURN(1);
unknown's avatar
unknown committed
1308 1309
}

1310

1311
/*
1312
  Validate and prepare for execution DO statement expressions.
1313 1314 1315

  SYNOPSIS
    mysql_test_do_fields()
1316 1317 1318
      stmt               prepared statement
      tables             list of tables used in this query
      values             list of expressions
1319 1320

  RETURN VALUE
1321 1322
    FALSE                success
    TRUE                 error, error message is set in THD
1323 1324
*/

unknown's avatar
unknown committed
1325
static bool mysql_test_do_fields(Prepared_statement *stmt,
unknown's avatar
unknown committed
1326 1327
                                TABLE_LIST *tables,
                                List<Item> *values)
1328 1329
{
  THD *thd= stmt->thd;
unknown's avatar
unknown committed
1330 1331

  DBUG_ENTER("mysql_test_do_fields");
unknown's avatar
unknown committed
1332 1333
  if (tables && check_table_access(thd, SELECT_ACL, tables, 0))
    DBUG_RETURN(TRUE);
1334

1335
  if (open_and_lock_tables(thd, tables))
unknown's avatar
unknown committed
1336
    DBUG_RETURN(TRUE);
1337
  DBUG_RETURN(setup_fields(thd, 0, *values, MARK_COLUMNS_NONE, 0, 0));
1338 1339 1340 1341 1342 1343 1344 1345
}


/*
  Validate and prepare for execution SET statement expressions

  SYNOPSIS
    mysql_test_set_fields()
1346 1347 1348
      stmt               prepared statement
      tables             list of tables used in this query
      values             list of expressions
1349 1350

  RETURN VALUE
1351 1352
    FALSE                success
    TRUE                 error, error message is set in THD
1353
*/
unknown's avatar
unknown committed
1354

unknown's avatar
unknown committed
1355 1356 1357
static bool mysql_test_set_fields(Prepared_statement *stmt,
                                  TABLE_LIST *tables,
                                  List<set_var_base> *var_list)
1358 1359 1360 1361 1362
{
  DBUG_ENTER("mysql_test_set_fields");
  List_iterator_fast<set_var_base> it(*var_list);
  THD *thd= stmt->thd;
  set_var_base *var;
1363

unknown's avatar
unknown committed
1364 1365
  if (tables && check_table_access(thd, SELECT_ACL, tables, 0) ||
      open_and_lock_tables(thd, tables))
1366
    goto error;
unknown's avatar
unknown committed
1367

1368 1369 1370 1371 1372
  while ((var= it++))
  {
    if (var->light_check(thd))
      goto error;
  }
unknown's avatar
unknown committed
1373
  DBUG_RETURN(FALSE);
1374
error:
unknown's avatar
unknown committed
1375
  DBUG_RETURN(TRUE);
1376 1377 1378 1379 1380 1381 1382
}


/*
  Check internal SELECT of the prepared command

  SYNOPSIS
1383
    select_like_stmt_test()
1384 1385 1386
      stmt                      prepared statement
      specific_prepare          function of command specific prepare
      setup_tables_done_option  options to be passed to LEX::unit.prepare()
1387 1388 1389 1390 1391 1392

  NOTE
    This function won't directly open tables used in select. They should
    be opened either by calling function (and in this case you probably
    should use select_like_stmt_test_with_open_n_lock()) or by
    "specific_prepare" call (like this happens in case of multi-update).
1393

unknown's avatar
unknown committed
1394
  RETURN VALUE
1395 1396
    FALSE                success
    TRUE                 error, error message is set in THD
1397
*/
1398 1399 1400 1401

static bool select_like_stmt_test(Prepared_statement *stmt,
                                  bool (*specific_prepare)(THD *thd),
                                  ulong setup_tables_done_option)
1402
{
1403
  DBUG_ENTER("select_like_stmt_test");
1404 1405 1406
  THD *thd= stmt->thd;
  LEX *lex= stmt->lex;

1407 1408
  lex->select_lex.context.resolve_in_select_list= TRUE;

unknown's avatar
unknown committed
1409 1410
  if (specific_prepare && (*specific_prepare)(thd))
    DBUG_RETURN(TRUE);
unknown's avatar
VIEW  
unknown committed
1411

1412 1413
  thd->used_tables= 0;                        // Updated by setup_fields

unknown's avatar
unknown committed
1414
  /* Calls JOIN::prepare */
1415
  DBUG_RETURN(lex->unit.prepare(thd, 0, setup_tables_done_option));
1416 1417
}

1418 1419
/*
  Check internal SELECT of the prepared command (with opening and
1420
  locking of used tables).
1421 1422 1423

  SYNOPSIS
    select_like_stmt_test_with_open_n_lock()
1424 1425 1426 1427 1428
      stmt                      prepared statement
      tables                    list of tables to be opened and locked
                                before calling specific_prepare function
      specific_prepare          function of command specific prepare
      setup_tables_done_option  options to be passed to LEX::unit.prepare()
1429 1430

  RETURN VALUE
1431 1432
    FALSE                success
    TRUE                 error
1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455
*/

static bool
select_like_stmt_test_with_open_n_lock(Prepared_statement *stmt,
                                       TABLE_LIST *tables,
                                       bool (*specific_prepare)(THD *thd),
                                       ulong setup_tables_done_option)
{
  DBUG_ENTER("select_like_stmt_test_with_open_n_lock");

  /*
    We should not call LEX::unit.cleanup() after this open_and_lock_tables()
    call because we don't allow prepared EXPLAIN yet so derived tables will
    clean up after themself.
  */
  if (open_and_lock_tables(stmt->thd, tables))
    DBUG_RETURN(TRUE);

  DBUG_RETURN(select_like_stmt_test(stmt, specific_prepare,
                                    setup_tables_done_option));
}


1456
/*
1457
  Validate and prepare for execution CREATE TABLE statement
1458 1459 1460

  SYNOPSIS
    mysql_test_create_table()
1461 1462
      stmt               prepared statement
      tables             list of tables used in this query
1463 1464

  RETURN VALUE
1465 1466
    FALSE                success
    TRUE                 error, error message is set in THD
1467
*/
unknown's avatar
unknown committed
1468

unknown's avatar
unknown committed
1469
static bool mysql_test_create_table(Prepared_statement *stmt)
1470 1471 1472 1473
{
  DBUG_ENTER("mysql_test_create_table");
  THD *thd= stmt->thd;
  LEX *lex= stmt->lex;
1474
  SELECT_LEX *select_lex= &lex->select_lex;
unknown's avatar
unknown committed
1475
  bool res= FALSE;
1476
  /* Skip first table, which is the table we are creating */
unknown's avatar
VIEW  
unknown committed
1477 1478 1479
  bool link_to_local;
  TABLE_LIST *create_table= lex->unlink_first_table(&link_to_local);
  TABLE_LIST *tables= lex->query_tables;
1480

unknown's avatar
unknown committed
1481 1482 1483 1484
  if (create_table_precheck(thd, tables, create_table))
    DBUG_RETURN(TRUE);

  if (select_lex->item_list.elements)
1485
  {
1486
    select_lex->context.resolve_in_select_list= TRUE;
1487
    res= select_like_stmt_test_with_open_n_lock(stmt, tables, 0, 0);
1488
  }
1489

unknown's avatar
unknown committed
1490
  /* put tables back for PS rexecuting */
unknown's avatar
VIEW  
unknown committed
1491
  lex->link_first_table_back(create_table, link_to_local);
1492 1493 1494
  DBUG_RETURN(res);
}

unknown's avatar
unknown committed
1495

1496
/*
1497
  Validate and prepare for execution a multi update statement.
1498 1499 1500

  SYNOPSIS
    mysql_test_multiupdate()
1501 1502 1503
      stmt               prepared statement
      tables             list of tables used in this query
      converted          converted to multi-update from usual update
1504 1505

  RETURN VALUE
1506 1507
    FALSE                success
    TRUE                 error, error message is set in THD
1508
*/
unknown's avatar
unknown committed
1509 1510

static bool mysql_test_multiupdate(Prepared_statement *stmt,
unknown's avatar
unknown committed
1511
                                  TABLE_LIST *tables,
1512
                                  bool converted)
1513
{
1514
  /* if we switched from normal update, rights are checked */
unknown's avatar
merge  
unknown committed
1515
  if (!converted && multi_update_precheck(stmt->thd, tables))
unknown's avatar
unknown committed
1516
    return TRUE;
1517 1518 1519

  return select_like_stmt_test(stmt, &mysql_multi_update_prepare,
                               OPTION_SETUP_TABLES_DONE);
1520 1521 1522 1523
}


/*
1524
  Validate and prepare for execution a multi delete statement.
1525 1526 1527

  SYNOPSIS
    mysql_test_multidelete()
1528 1529
      stmt               prepared statement
      tables             list of tables used in this query
1530 1531

  RETURN VALUE
1532 1533
    FALSE                success
    TRUE                 error, error message in THD is set.
1534
*/
unknown's avatar
unknown committed
1535 1536

static bool mysql_test_multidelete(Prepared_statement *stmt,
unknown's avatar
unknown committed
1537
                                  TABLE_LIST *tables)
1538 1539 1540
{
  stmt->thd->lex->current_select= &stmt->thd->lex->select_lex;
  if (add_item_to_list(stmt->thd, new Item_null()))
unknown's avatar
unknown committed
1541 1542 1543 1544
  {
    my_error(ER_OUTOFMEMORY, MYF(0), 0);
    goto error;
  }
1545

unknown's avatar
unknown committed
1546
  if (multi_delete_precheck(stmt->thd, tables) ||
unknown's avatar
unknown committed
1547 1548 1549 1550
      select_like_stmt_test_with_open_n_lock(stmt, tables,
                                             &mysql_multi_delete_prepare,
                                             OPTION_SETUP_TABLES_DONE))
    goto error;
1551 1552 1553
  if (!tables->table)
  {
    my_error(ER_VIEW_DELETE_MERGE_VIEW, MYF(0),
unknown's avatar
unknown committed
1554
             tables->view_db.str, tables->view_name.str);
unknown's avatar
unknown committed
1555
    goto error;
1556
  }
unknown's avatar
unknown committed
1557 1558 1559
  return FALSE;
error:
  return TRUE;
1560 1561 1562
}


1563 1564 1565 1566 1567 1568
/*
  Wrapper for mysql_insert_select_prepare, to make change of local tables
  after open_and_lock_tables() call.

  SYNOPSIS
    mysql_insert_select_prepare_tester()
1569
      thd                thread handle
1570

1571 1572 1573
  NOTE
    We need to remove the first local table after open_and_lock_tables,
    because mysql_handle_derived uses local tables lists.
1574 1575 1576 1577
*/

static bool mysql_insert_select_prepare_tester(THD *thd)
{
1578 1579
  TABLE_LIST *first;
  bool res;
1580 1581
  SELECT_LEX *first_select= &thd->lex->select_lex;
  /* Skip first table, which is the table we are inserting in */
1582 1583 1584 1585
  first_select->table_list.first= (byte*)(first=
                                          ((TABLE_LIST*)first_select->
                                           table_list.first)->next_local);
  res= mysql_insert_select_prepare(thd);
1586 1587 1588 1589
  /*
    insert/replace from SELECT give its SELECT_LEX for SELECT,
    and item_list belong to SELECT
  */
1590 1591 1592
  thd->lex->select_lex.context.resolve_in_select_list= TRUE;
  thd->lex->select_lex.context.table_list= first;
  return res;
1593 1594 1595
}


1596
/*
1597
  Validate and prepare for execution INSERT ... SELECT statement.
1598 1599 1600

  SYNOPSIS
    mysql_test_insert_select()
1601 1602
      stmt               prepared statement
      tables             list of tables used in this query
1603 1604

  RETURN VALUE
1605 1606
    FALSE                success
    TRUE                 error, error message is set in THD
1607
*/
unknown's avatar
unknown committed
1608

1609 1610
static bool mysql_test_insert_select(Prepared_statement *stmt,
                                     TABLE_LIST *tables)
1611 1612 1613
{
  int res;
  LEX *lex= stmt->lex;
unknown's avatar
unknown committed
1614 1615
  TABLE_LIST *first_local_table;

1616 1617 1618 1619 1620
  if (tables->table)
  {
    // don't allocate insert_values
    tables->table->insert_values=(byte *)1;
  }
unknown's avatar
unknown committed
1621

unknown's avatar
unknown committed
1622 1623
  if (insert_precheck(stmt->thd, tables))
    return 1;
1624 1625 1626 1627 1628

  /* store it, because mysql_insert_select_prepare_tester change it */
  first_local_table= (TABLE_LIST *)lex->select_lex.table_list.first;
  DBUG_ASSERT(first_local_table != 0);

1629 1630 1631 1632
  res=
    select_like_stmt_test_with_open_n_lock(stmt, tables,
                                           &mysql_insert_select_prepare_tester,
                                           OPTION_SETUP_TABLES_DONE);
1633
  /* revert changes  made by mysql_insert_select_prepare_tester */
unknown's avatar
unknown committed
1634
  lex->select_lex.table_list.first= (byte*) first_local_table;
1635 1636 1637 1638
  return res;
}


unknown's avatar
unknown committed
1639
/*
1640 1641 1642
  Perform semantic analysis of the parsed tree and send a response packet
  to the client.

1643
  SYNOPSIS
1644
    check_prepared_statement()
1645
      stmt               prepared statement
1646 1647 1648 1649 1650 1651 1652

  DESCRIPTION
    This function
    - opens all tables and checks access rights
    - validates semantics of statement columns and SQL functions
      by calling fix_fields.

1653
  RETURN VALUE
1654 1655
    FALSE                success, statement metadata is sent to client
    TRUE                 error, error message is set in THD (but not sent)
unknown's avatar
unknown committed
1656
*/
1657

unknown's avatar
unknown committed
1658 1659
static bool check_prepared_statement(Prepared_statement *stmt,
                                     bool text_protocol)
1660
{
1661
  THD *thd= stmt->thd;
1662
  LEX *lex= stmt->lex;
1663
  SELECT_LEX *select_lex= &lex->select_lex;
unknown's avatar
VIEW  
unknown committed
1664
  TABLE_LIST *tables;
1665
  enum enum_sql_command sql_command= lex->sql_command;
unknown's avatar
unknown committed
1666
  int res= 0;
1667
  DBUG_ENTER("check_prepared_statement");
1668
  DBUG_PRINT("enter",("command: %d, param_count: %ld",
1669
                      sql_command, stmt->param_count));
1670

unknown's avatar
VIEW  
unknown committed
1671 1672
  lex->first_lists_tables_same();
  tables= lex->query_tables;
1673

1674 1675 1676 1677
  /* set context for commands which do not use setup_tables */
  lex->select_lex.context.resolve_in_table_list_only(select_lex->
                                                     get_table_list());

1678
  switch (sql_command) {
1679
  case SQLCOM_REPLACE:
unknown's avatar
unknown committed
1680
  case SQLCOM_INSERT:
unknown's avatar
unknown committed
1681
    res= mysql_test_insert(stmt, tables, lex->field_list,
unknown's avatar
unknown committed
1682 1683 1684
                           lex->many_values,
                           select_lex->item_list, lex->value_list,
                           lex->duplicates);
unknown's avatar
unknown committed
1685 1686 1687
    break;

  case SQLCOM_UPDATE:
unknown's avatar
unknown committed
1688
    res= mysql_test_update(stmt, tables);
unknown's avatar
unknown committed
1689
    /* mysql_test_update returns 2 if we need to switch to multi-update */
1690 1691 1692 1693 1694
    if (res != 2)
      break;

  case SQLCOM_UPDATE_MULTI:
    res= mysql_test_multiupdate(stmt, tables, res == 2);
unknown's avatar
unknown committed
1695 1696
    break;

unknown's avatar
unknown committed
1697
  case SQLCOM_DELETE:
unknown's avatar
unknown committed
1698
    res= mysql_test_delete(stmt, tables);
unknown's avatar
unknown committed
1699 1700 1701
    break;

  case SQLCOM_SELECT:
1702 1703 1704 1705 1706 1707 1708
    res= mysql_test_select(stmt, tables, text_protocol);
    if (res == 2)
    {
      /* Statement and field info has already been sent */
      DBUG_RETURN(FALSE);
    }
    break;
1709
  case SQLCOM_CREATE_TABLE:
unknown's avatar
VIEW  
unknown committed
1710
    res= mysql_test_create_table(stmt);
1711
    break;
unknown's avatar
unknown committed
1712

1713
  case SQLCOM_DO:
unknown's avatar
unknown committed
1714 1715
    res= mysql_test_do_fields(stmt, tables, lex->insert_list);
    break;
1716 1717

  case SQLCOM_SET_OPTION:
unknown's avatar
unknown committed
1718
    res= mysql_test_set_fields(stmt, tables, &lex->var_list);
1719 1720 1721
    break;

  case SQLCOM_DELETE_MULTI:
unknown's avatar
unknown committed
1722
    res= mysql_test_multidelete(stmt, tables);
1723 1724 1725
    break;

  case SQLCOM_INSERT_SELECT:
1726
  case SQLCOM_REPLACE_SELECT:
unknown's avatar
unknown committed
1727
    res= mysql_test_insert_select(stmt, tables);
1728 1729
    break;

1730 1731 1732 1733
    /*
      Note that we don't need to have cases in this list if they are
      marked with CF_STATUS_COMMAND in sql_command_flags
    */
1734 1735 1736 1737
  case SQLCOM_SHOW_PROCESSLIST:
  case SQLCOM_SHOW_STORAGE_ENGINES:
  case SQLCOM_SHOW_PRIVILEGES:
  case SQLCOM_SHOW_COLUMN_TYPES:
1738 1739 1740
  case SQLCOM_SHOW_ENGINE_LOGS:
  case SQLCOM_SHOW_ENGINE_STATUS:
  case SQLCOM_SHOW_ENGINE_MUTEX:
1741 1742
  case SQLCOM_SHOW_CREATE_DB:
  case SQLCOM_SHOW_GRANTS:
1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756
  case SQLCOM_SHOW_BINLOG_EVENTS:
  case SQLCOM_SHOW_MASTER_STAT:
  case SQLCOM_SHOW_SLAVE_STAT:
  case SQLCOM_SHOW_CREATE_PROC:
  case SQLCOM_SHOW_CREATE_FUNC:
  case SQLCOM_SHOW_CREATE_EVENT:
  case SQLCOM_SHOW_CREATE:
  case SQLCOM_SHOW_PROC_CODE:
  case SQLCOM_SHOW_FUNC_CODE:
  case SQLCOM_SHOW_AUTHORS:
  case SQLCOM_SHOW_CONTRIBUTORS:
  case SQLCOM_SHOW_WARNS:
  case SQLCOM_SHOW_ERRORS:
  case SQLCOM_SHOW_BINLOGS:
1757 1758
  case SQLCOM_DROP_TABLE:
  case SQLCOM_RENAME_TABLE:
1759 1760 1761 1762 1763 1764
  case SQLCOM_ALTER_TABLE:
  case SQLCOM_COMMIT:
  case SQLCOM_CREATE_INDEX:
  case SQLCOM_DROP_INDEX:
  case SQLCOM_ROLLBACK:
  case SQLCOM_TRUNCATE:
1765
  case SQLCOM_CALL:
1766 1767
  case SQLCOM_CREATE_VIEW:
  case SQLCOM_DROP_VIEW:
1768 1769 1770
  case SQLCOM_REPAIR:
  case SQLCOM_ANALYZE:
  case SQLCOM_OPTIMIZE:
1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789
  case SQLCOM_CHANGE_MASTER:
  case SQLCOM_RESET:
  case SQLCOM_FLUSH:
  case SQLCOM_SLAVE_START:
  case SQLCOM_SLAVE_STOP:
  case SQLCOM_INSTALL_PLUGIN:
  case SQLCOM_UNINSTALL_PLUGIN:
  case SQLCOM_CREATE_DB:
  case SQLCOM_DROP_DB:
  case SQLCOM_RENAME_DB:
  case SQLCOM_CHECKSUM:
  case SQLCOM_CREATE_USER:
  case SQLCOM_RENAME_USER:
  case SQLCOM_DROP_USER:
  case SQLCOM_ASSIGN_TO_KEYCACHE:
  case SQLCOM_PRELOAD_KEYS:
  case SQLCOM_GRANT:
  case SQLCOM_REVOKE:
  case SQLCOM_KILL:
1790 1791
    break;

unknown's avatar
unknown committed
1792
  default:
1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803
    /*
      Trivial check of all status commands. This is easier than having
      things in the above case list, as it's less chance for mistakes.
    */
    if (!(sql_command_flags[sql_command] & CF_STATUS_COMMAND))
    {
      /* All other statements are not supported yet. */
      my_message(ER_UNSUPPORTED_PS, ER(ER_UNSUPPORTED_PS), MYF(0));
      goto error;
    }
    break;
unknown's avatar
unknown committed
1804
  }
unknown's avatar
unknown committed
1805
  if (res == 0)
unknown's avatar
unknown committed
1806 1807
    DBUG_RETURN(text_protocol? FALSE : (send_prep_stmt(stmt, 0) ||
                                        thd->protocol->flush()));
1808
error:
unknown's avatar
unknown committed
1809
  DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
1810 1811
}

unknown's avatar
unknown committed
1812
/*
1813 1814
  Initialize array of parameters in statement from LEX.
  (We need to have quick access to items by number in mysql_stmt_get_longdata).
1815
  This is to avoid using malloc/realloc in the parser.
unknown's avatar
unknown committed
1816
*/
unknown's avatar
unknown committed
1817

1818
static bool init_param_array(Prepared_statement *stmt)
unknown's avatar
unknown committed
1819
{
1820 1821 1822
  LEX *lex= stmt->lex;
  if ((stmt->param_count= lex->param_list.elements))
  {
1823 1824 1825
    if (stmt->param_count > (uint) UINT_MAX16)
    {
      /* Error code to be defined in 5.0 */
1826 1827
      my_message(ER_PS_MANY_PARAM, ER(ER_PS_MANY_PARAM), MYF(0));
      return TRUE;
1828
    }
1829 1830 1831 1832
    Item_param **to;
    List_iterator<Item_param> param_iterator(lex->param_list);
    /* Use thd->mem_root as it points at statement mem_root */
    stmt->param_array= (Item_param **)
unknown's avatar
unknown committed
1833
                       alloc_root(stmt->thd->mem_root,
1834 1835
                                  sizeof(Item_param*) * stmt->param_count);
    if (!stmt->param_array)
1836
      return TRUE;
1837 1838 1839 1840 1841 1842 1843
    for (to= stmt->param_array;
         to < stmt->param_array + stmt->param_count;
         ++to)
    {
      *to= param_iterator++;
    }
  }
1844
  return FALSE;
unknown's avatar
unknown committed
1845
}
1846

1847

unknown's avatar
unknown committed
1848
/*
1849
  COM_STMT_PREPARE handler.
unknown's avatar
unknown committed
1850

1851 1852
  SYNOPSIS
    mysql_stmt_prepare()
1853 1854 1855 1856 1857 1858 1859
      packet             query to be prepared
      packet_length      query string length, including ignored
                         trailing NULL or quote char.

  DESCRIPTION
    Given a query string with parameter markers, create a prepared
    statement from it and send PS info back to the client.
unknown's avatar
unknown committed
1860

1861
  NOTES
unknown's avatar
unknown committed
1862 1863 1864 1865
    This function parses the query and sends the total number of parameters
    and resultset metadata information back to client (if any), without
    executing the query i.e. without any log/disk writes. This allows the
    queries to be re-executed without re-parsing during execute.
1866 1867

    If parameter markers are found in the query, then store the information
unknown's avatar
unknown committed
1868 1869
    using Item_param along with maintaining a list in lex->param_array, so
    that a fast and direct retrieval can be made without going through all
1870
    field items.
unknown's avatar
unknown committed
1871

1872 1873 1874
  RETURN VALUE
    none: in case of success a new statement id and metadata is sent
    to the client, otherwise an error message is set in THD.
unknown's avatar
unknown committed
1875 1876
*/

1877
void mysql_stmt_prepare(THD *thd, const char *packet, uint packet_length)
unknown's avatar
unknown committed
1878
{
unknown's avatar
unknown committed
1879
  Prepared_statement *stmt;
1880
  bool error;
1881
  DBUG_ENTER("mysql_stmt_prepare");
unknown's avatar
unknown committed
1882

unknown's avatar
unknown committed
1883
  DBUG_PRINT("prep_query", ("%s", packet));
unknown's avatar
unknown committed
1884

unknown's avatar
unknown committed
1885 1886 1887 1888
  /* First of all clear possible warnings from the previous command */
  mysql_reset_thd_for_next_command(thd);

  if (! (stmt= new Prepared_statement(thd, &thd->protocol_prep)))
1889
    DBUG_VOID_RETURN; /* out of memory: error is set in Sql_alloc */
1890

1891
  if (thd->stmt_map.insert(thd, stmt))
1892
  {
1893
    /*
1894
      The error is set in the insert. The statement itself
1895 1896
      will be also deleted there (this is how the hash works).
    */
1897
    DBUG_VOID_RETURN;
1898
  }
unknown's avatar
unknown committed
1899

1900
  /* Reset warnings from previous command */
unknown's avatar
unknown committed
1901
  mysql_reset_errors(thd, 0);
1902 1903 1904
  sp_cache_flush_obsolete(&thd->sp_proc_cache);
  sp_cache_flush_obsolete(&thd->sp_func_cache);

1905 1906
  if (!(specialflag & SPECIAL_NO_PRIOR))
    my_pthread_setprio(pthread_self(),QUERY_PRIOR);
1907

1908
  error= stmt->prepare(packet, packet_length);
unknown's avatar
unknown committed
1909 1910

  if (!(specialflag & SPECIAL_NO_PRIOR))
unknown's avatar
unknown committed
1911
    my_pthread_setprio(pthread_self(),WAIT_PRIOR);
unknown's avatar
unknown committed
1912

1913
  if (error)
1914
  {
1915 1916
    /* Statement map deletes statement on erase */
    thd->stmt_map.erase(stmt);
1917 1918
  }
  else
1919 1920
    general_log_print(thd, COM_STMT_PREPARE, "[%lu] %.*b", stmt->id,
                      stmt->query_length, stmt->query);
1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955

  /* check_prepared_statemnt sends the metadata packet in case of success */
  DBUG_VOID_RETURN;
}

/*
  SYNOPSIS
    get_dynamic_sql_string()
      lex       in      main lex
      query_len out     length of the SQL statement (is set only
                        in case of success)

  DESCRIPTION
    Get an SQL statement text from a user variable or from plain
    text. If the statement is plain text, just assign the
    pointers, otherwise allocate memory in thd->mem_root and copy
    the contents of the variable, possibly with character
    set conversion.

  RETURN VALUE
    non-zero success, 0 in case of error (out of memory)
*/

static const char *get_dynamic_sql_string(LEX *lex, uint *query_len)
{
  THD *thd= lex->thd;
  char *query_str= 0;

  if (lex->prepared_stmt_code_is_varref)
  {
    /* This is PREPARE stmt FROM or EXECUTE IMMEDIATE @var. */
    String str;
    CHARSET_INFO *to_cs= thd->variables.collation_connection;
    bool needs_conversion;
    user_var_entry *entry;
1956
    String *var_value= &str;
1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969
    uint32 unused, len;
    /*
      Convert @var contents to string in connection character set. Although
      it is known that int/real/NULL value cannot be a valid query we still
      convert it for error messages to be uniform.
    */
    if ((entry=
         (user_var_entry*)hash_search(&thd->user_vars,
                                      (byte*)lex->prepared_stmt_code.str,
                                      lex->prepared_stmt_code.length))
        && entry->value)
    {
      my_bool is_var_null;
1970
      var_value= entry->val_str(&is_var_null, &str, NOT_FIXED_DEC);
1971 1972 1973 1974 1975
      /*
        NULL value of variable checked early as entry->value so here
        we can't get NULL in normal conditions
      */
      DBUG_ASSERT(!is_var_null);
1976
      if (!var_value)
1977 1978 1979 1980 1981 1982 1983 1984
        goto end;
    }
    else
    {
      /*
        variable absent or equal to NULL, so we need to set variable to
        something reasonable to get a readable error message during parsing
      */
1985
      str.set(STRING_WITH_LEN("NULL"), &my_charset_latin1);
1986 1987
    }

1988 1989 1990
    needs_conversion= String::needs_conversion(var_value->length(),
                                               var_value->charset(), to_cs,
                                               &unused);
1991

1992 1993
    len= (needs_conversion ? var_value->length() * to_cs->mbmaxlen :
          var_value->length());
1994 1995 1996 1997 1998 1999
    if (!(query_str= alloc_root(thd->mem_root, len+1)))
      goto end;

    if (needs_conversion)
    {
      uint dummy_errors;
2000 2001 2002
      len= copy_and_convert(query_str, len, to_cs, var_value->ptr(),
                            var_value->length(), var_value->charset(),
                            &dummy_errors);
2003 2004
    }
    else
2005 2006
      memcpy(query_str, var_value->ptr(), var_value->length());
    query_str[len]= '\0';                       // Safety (mostly for debug)
2007
    *query_len= len;
2008
  }
2009
  else
unknown's avatar
unknown committed
2010
  {
2011 2012
    query_str= lex->prepared_stmt_code.str;
    *query_len= lex->prepared_stmt_code.length;
unknown's avatar
unknown committed
2013
  }
2014 2015
end:
  return query_str;
unknown's avatar
unknown committed
2016 2017
}

unknown's avatar
unknown committed
2018

2019
/* Init PS/SP specific parse tree members.  */
2020

2021
static void init_stmt_after_parse(LEX *lex)
2022 2023
{
  SELECT_LEX *sl= lex->all_selects_list;
2024 2025 2026 2027
  /*
    Switch off a temporary flag that prevents evaluation of
    subqueries in statement prepare.
  */
2028 2029
  for (; sl; sl= sl->next_select_in_list())
   sl->uncacheable&= ~UNCACHEABLE_PREPARE;
2030 2031
}

2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057
/*
  SQLCOM_PREPARE implementation.

  SYNOPSIS
    mysql_sql_stmt_prepare()
      thd     thread handle

  DESCRIPTION
    Prepare an SQL prepared statement. This is called from
    mysql_execute_command and should therefore behave like an
    ordinary query (e.g. should not reset any global THD data).

  RETURN VALUE
    none: in case of success, OK packet is sent to the client,
    otherwise an error message is set in THD
*/

void mysql_sql_stmt_prepare(THD *thd)
{
  LEX *lex= thd->lex;
  LEX_STRING *name= &lex->prepared_stmt_name;
  Prepared_statement *stmt;
  const char *query;
  uint query_len;
  DBUG_ENTER("mysql_sql_stmt_prepare");
  DBUG_ASSERT(thd->protocol == &thd->protocol_simple);
2058

2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074
  if ((stmt= (Prepared_statement*) thd->stmt_map.find_by_name(name)))
  {
    /*
      If there is a statement with the same name, remove it. It is ok to
      remove old and fail to insert a new one at the same time.
    */
    if (stmt->deallocate())
      DBUG_VOID_RETURN;
  }

  if (! (query= get_dynamic_sql_string(lex, &query_len)) ||
      ! (stmt= new Prepared_statement(thd, &thd->protocol_simple)))
  {
    DBUG_VOID_RETURN;                           /* out of memory */
  }

2075 2076
  /* Set the name first, insert should know that this statement has a name */
  if (stmt->set_name(name))
2077 2078 2079 2080
  {
    delete stmt;
    DBUG_VOID_RETURN;
  }
2081
  if (thd->stmt_map.insert(thd, stmt))
2082
  {
2083
    /* The statement is deleted and an error is set if insert fails */
2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096
    DBUG_VOID_RETURN;
  }

  if (stmt->prepare(query, query_len+1))
  {
    /* Statement map deletes the statement on erase */
    thd->stmt_map.erase(stmt);
  }
  else
    send_ok(thd, 0L, 0L, "Statement prepared");

  DBUG_VOID_RETURN;
}
2097 2098

/* Reinit prepared statement/stored procedure before execution */
unknown's avatar
unknown committed
2099

2100
void reinit_stmt_before_use(THD *thd, LEX *lex)
2101
{
2102
  SELECT_LEX *sl= lex->all_selects_list;
2103
  DBUG_ENTER("reinit_stmt_before_use");
unknown's avatar
unknown committed
2104

2105 2106 2107 2108 2109 2110 2111
  /*
    We have to update "thd" pointer in LEX, all its units and in LEX::result,
    since statements which belong to trigger body are associated with TABLE
    object and because of this can be used in different threads.
  */
  lex->thd= thd;

unknown's avatar
VIEW  
unknown committed
2112 2113 2114
  if (lex->empty_field_list_on_rset)
  {
    lex->empty_field_list_on_rset= 0;
2115
    lex->field_list.empty();
unknown's avatar
VIEW  
unknown committed
2116
  }
2117
  for (; sl; sl= sl->next_select_in_list())
2118
  {
2119 2120
    if (!sl->first_execution)
    {
unknown's avatar
unknown committed
2121 2122 2123
      /* remove option which was put by mysql_explain_union() */
      sl->options&= ~SELECT_DESCRIBE;

2124 2125 2126
      /* see unique_table() */
      sl->exclude_from_table_unique_test= FALSE;

2127
      /*
unknown's avatar
unknown committed
2128 2129
        Copy WHERE, HAVING clause pointers to avoid damaging them
        by optimisation
2130
      */
unknown's avatar
unknown committed
2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141
      if (sl->prep_where)
      {
        sl->where= sl->prep_where->copy_andor_structure(thd);
        sl->where->cleanup();
      }
      if (sl->prep_having)
      {
        sl->having= sl->prep_having->copy_andor_structure(thd);
        sl->having->cleanup();
      }
      DBUG_ASSERT(sl->join == 0);
2142 2143 2144 2145 2146 2147 2148 2149
      ORDER *order;
      /* Fix GROUP list */
      for (order= (ORDER *)sl->group_list.first; order; order= order->next)
        order->item= &order->item_ptr;
      /* Fix ORDER list */
      for (order= (ORDER *)sl->order_list.first; order; order= order->next)
        order->item= &order->item_ptr;
    }
2150 2151 2152 2153
    {
      SELECT_LEX_UNIT *unit= sl->master_unit();
      unit->unclean();
      unit->types.empty();
2154
      /* for derived tables & PS (which can't be reset by Item_subquery) */
2155
      unit->reinit_exec_mechanism();
2156
      unit->set_thd(thd);
2157
    }
2158
  }
unknown's avatar
VIEW  
unknown committed
2159 2160

  /*
unknown's avatar
unknown committed
2161 2162
    TODO: When the new table structure is ready, then have a status bit
    to indicate the table is altered, and re-do the setup_*
unknown's avatar
VIEW  
unknown committed
2163 2164
    and open the tables back.
  */
2165 2166 2167 2168 2169
  /*
    NOTE: We should reset whole table list here including all tables added
    by prelocking algorithm (it is not a problem for substatements since
    they have their own table list).
  */
unknown's avatar
VIEW  
unknown committed
2170
  for (TABLE_LIST *tables= lex->query_tables;
2171 2172
       tables;
       tables= tables->next_global)
unknown's avatar
VIEW  
unknown committed
2173
  {
2174 2175
    tables->reinit_before_use(thd);
  }
2176 2177 2178 2179 2180
  /*
    Cleanup of the special case of DELETE t1, t2 FROM t1, t2, t3 ...
    (multi-delete).  We do a full clean up, although at the moment all we
    need to clean in the tables of MULTI-DELETE list is 'table' member.
  */
2181
  for (TABLE_LIST *tables= (TABLE_LIST*) lex->auxiliary_table_list.first;
2182
       tables;
2183
       tables= tables->next_global)
unknown's avatar
VIEW  
unknown committed
2184
  {
2185
    tables->reinit_before_use(thd);
unknown's avatar
VIEW  
unknown committed
2186
  }
2187
  lex->current_select= &lex->select_lex;
2188 2189 2190 2191 2192

  /* restore original list used in INSERT ... SELECT */
  if (lex->leaf_tables_insert)
    lex->select_lex.leaf_tables= lex->leaf_tables_insert;

2193
  if (lex->result)
2194
  {
2195
    lex->result->cleanup();
2196 2197
    lex->result->set_thd(thd);
  }
unknown's avatar
unknown committed
2198 2199
  lex->allow_sum_func= 0;
  lex->in_sum_func= NULL;
2200
  DBUG_VOID_RETURN;
2201 2202
}

2203

2204 2205
/*
  Clears parameters from data left from previous execution or long data
unknown's avatar
unknown committed
2206

2207 2208
  SYNOPSIS
    reset_stmt_params()
2209 2210
      stmt               prepared statement for which parameters should
                         be reset
2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221
*/

static void reset_stmt_params(Prepared_statement *stmt)
{
  Item_param **item= stmt->param_array;
  Item_param **end= item + stmt->param_count;
  for (;item < end ; ++item)
    (**item).reset();
}


2222
/*
2223
  COM_STMT_EXECUTE handler: execute a previously prepared statement.
2224

2225
  SYNOPSIS
2226
    mysql_stmt_execute()
2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239
      thd                current thread
      packet             parameter types and data, if any
      packet_length      packet length, including the terminator character.

  DESCRIPTION
    If there are any parameters, then replace parameter markers with the
    data supplied from the client, and then execute the statement.
    This function uses binary protocol to send a possible result set
    to the client.

  RETURN VALUE
    none: in case of success OK packet or a result set is sent to the
    client, otherwise an error message is set in THD.
unknown's avatar
unknown committed
2240 2241
*/

2242
void mysql_stmt_execute(THD *thd, char *packet_arg, uint packet_length)
unknown's avatar
unknown committed
2243
{
2244
  uchar *packet= (uchar*)packet_arg; // GCC 4.0.1 workaround
unknown's avatar
unknown committed
2245
  ulong stmt_id= uint4korr(packet);
2246
  ulong flags= (ulong) ((uchar) packet[4]);
2247
  /* Query text for binary, general or slow log, if any of them is open */
2248
  String expanded_query;
unknown's avatar
unknown committed
2249
#ifndef EMBEDDED_LIBRARY
2250
  uchar *packet_end= (uchar *) packet + packet_length - 1;
unknown's avatar
unknown committed
2251
#endif
2252
  Prepared_statement *stmt;
2253
  bool error;
2254
  DBUG_ENTER("mysql_stmt_execute");
2255 2256

  packet+= 9;                               /* stmt_id + 5 bytes of flags */
2257

unknown's avatar
unknown committed
2258 2259 2260
  /* First of all clear possible warnings from the previous command */
  mysql_reset_thd_for_next_command(thd);

unknown's avatar
unknown committed
2261
  if (!(stmt= find_prepared_statement(thd, stmt_id, "mysql_stmt_execute")))
2262 2263
    DBUG_VOID_RETURN;

2264 2265
  DBUG_PRINT("exec_query", ("%s", stmt->query));
  DBUG_PRINT("info",("stmt: %p", stmt));
unknown's avatar
unknown committed
2266

2267 2268 2269
  sp_cache_flush_obsolete(&thd->sp_proc_cache);
  sp_cache_flush_obsolete(&thd->sp_func_cache);

unknown's avatar
unknown committed
2270
#ifndef EMBEDDED_LIBRARY
2271
  if (stmt->param_count)
2272
  {
2273
    uchar *null_array= (uchar *) packet;
2274
    if (setup_conversion_functions(stmt, (uchar **) &packet, packet_end) ||
2275
        stmt->set_params(stmt, null_array, (uchar *) packet, packet_end,
2276
                         &expanded_query))
2277
      goto set_params_data_err;
2278
  }
unknown's avatar
unknown committed
2279
#else
2280
  /*
unknown's avatar
unknown committed
2281 2282
    In embedded library we re-install conversion routines each time
    we set params, and also we don't need to parse packet.
2283
    So we do it in one function.
2284
  */
2285
  if (stmt->param_count && stmt->set_params_data(stmt, &expanded_query))
2286
    goto set_params_data_err;
unknown's avatar
unknown committed
2287
#endif
2288 2289
  if (!(specialflag & SPECIAL_NO_PRIOR))
    my_pthread_setprio(pthread_self(),QUERY_PRIOR);
2290 2291 2292 2293 2294 2295 2296 2297

  /*
    If the free_list is not empty, we'll wrongly free some externally
    allocated items when cleaning up after validation of the prepared
    statement.
  */
  DBUG_ASSERT(thd->free_list == NULL);

2298
  error= stmt->execute(&expanded_query,
2299
                       test(flags & (ulong) CURSOR_TYPE_READ_ONLY));
2300 2301
  if (!(specialflag & SPECIAL_NO_PRIOR))
    my_pthread_setprio(pthread_self(), WAIT_PRIOR);
2302
  if (error == 0)
2303 2304
    general_log_print(thd, COM_STMT_EXECUTE, "[%lu] %.*b", stmt->id,
                      thd->query_length, thd->query);
2305

2306
  DBUG_VOID_RETURN;
unknown's avatar
unknown committed
2307

2308
set_params_data_err:
2309
  my_error(ER_WRONG_ARGUMENTS, MYF(0), "mysql_stmt_execute");
2310
  reset_stmt_params(stmt);
unknown's avatar
unknown committed
2311 2312 2313
  DBUG_VOID_RETURN;
}

2314

unknown's avatar
unknown committed
2315
/*
2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 2332
  SQLCOM_EXECUTE implementation.

  SYNOPSIS
    mysql_sql_stmt_execute()
      thd                thread handle

  DESCRIPTION
    Execute prepared statement using parameter values from
    lex->prepared_stmt_params and send result to the client using
    text protocol. This is called from mysql_execute_command and
    therefore should behave like an ordinary query (e.g. not change
    global THD data, such as warning count, server status, etc).
    This function uses text protocol to send a possible result set.

  RETURN
    none: in case of success, OK (or result set) packet is sent to the
    client, otherwise an error is set in THD
unknown's avatar
unknown committed
2333 2334
*/

2335
void mysql_sql_stmt_execute(THD *thd)
unknown's avatar
unknown committed
2336
{
2337
  LEX *lex= thd->lex;
2338
  Prepared_statement *stmt;
2339 2340
  LEX_STRING *name= &lex->prepared_stmt_name;
  /* Query text for binary, general or slow log, if any of them is open */
2341
  String expanded_query;
2342
  DBUG_ENTER("mysql_sql_stmt_execute");
2343
  DBUG_PRINT("info", ("EXECUTE: %.*s\n", name->length, name->str));
2344

2345
  if (!(stmt= (Prepared_statement*) thd->stmt_map.find_by_name(name)))
2346
  {
2347 2348
    my_error(ER_UNKNOWN_STMT_HANDLER, MYF(0),
             name->length, name->str, "EXECUTE");
2349
    DBUG_VOID_RETURN;
2350 2351
  }

2352
  if (stmt->param_count != lex->prepared_stmt_params.elements)
unknown's avatar
unknown committed
2353
  {
2354
    my_error(ER_WRONG_ARGUMENTS, MYF(0), "EXECUTE");
unknown's avatar
unknown committed
2355 2356 2357
    DBUG_VOID_RETURN;
  }

2358 2359
  DBUG_PRINT("info",("stmt: %p", stmt));

2360 2361 2362 2363 2364 2365 2366
  /*
    If the free_list is not empty, we'll wrongly free some externally
    allocated items when cleaning up after validation of the prepared
    statement.
  */
  DBUG_ASSERT(thd->free_list == NULL);

2367
  if (stmt->set_params_from_vars(stmt, lex->prepared_stmt_params,
2368
                                 &expanded_query))
2369
    goto set_params_data_err;
2370

2371
  (void) stmt->execute(&expanded_query, FALSE);
2372

unknown's avatar
unknown committed
2373
  DBUG_VOID_RETURN;
2374 2375 2376 2377 2378

set_params_data_err:
  my_error(ER_WRONG_ARGUMENTS, MYF(0), "EXECUTE");
  reset_stmt_params(stmt);
  DBUG_VOID_RETURN;
unknown's avatar
unknown committed
2379 2380
}

2381

2382
/*
2383
  COM_STMT_FETCH handler: fetches requested amount of rows from cursor
unknown's avatar
unknown committed
2384

2385
  SYNOPSIS
unknown's avatar
unknown committed
2386
    mysql_stmt_fetch()
2387 2388 2389
      thd                Thread handle
      packet             Packet from client (with stmt_id & num_rows)
      packet_length      Length of packet
2390 2391 2392 2393 2394 2395
*/

void mysql_stmt_fetch(THD *thd, char *packet, uint packet_length)
{
  /* assume there is always place for 8-16 bytes */
  ulong stmt_id= uint4korr(packet);
2396
  ulong num_rows= uint4korr(packet+4);
2397
  Prepared_statement *stmt;
unknown's avatar
unknown committed
2398
  Statement stmt_backup;
2399
  Server_side_cursor *cursor;
2400 2401
  DBUG_ENTER("mysql_stmt_fetch");

unknown's avatar
unknown committed
2402
  /* First of all clear possible warnings from the previous command */
2403
  mysql_reset_thd_for_next_command(thd);
unknown's avatar
unknown committed
2404
  statistic_increment(thd->status_var.com_stmt_fetch, &LOCK_status);
2405 2406 2407
  if (!(stmt= find_prepared_statement(thd, stmt_id, "mysql_stmt_fetch")))
    DBUG_VOID_RETURN;

2408
  cursor= stmt->cursor;
2409
  if (!cursor)
2410
  {
2411
    my_error(ER_STMT_HAS_NO_OPEN_CURSOR, MYF(0), stmt_id);
2412 2413
    DBUG_VOID_RETURN;
  }
2414

unknown's avatar
unknown committed
2415
  thd->stmt_arena= stmt;
unknown's avatar
unknown committed
2416
  thd->set_n_backup_statement(stmt, &stmt_backup);
2417 2418 2419 2420

  if (!(specialflag & SPECIAL_NO_PRIOR))
    my_pthread_setprio(pthread_self(), QUERY_PRIOR);

2421
  cursor->fetch(num_rows);
2422 2423 2424 2425

  if (!(specialflag & SPECIAL_NO_PRIOR))
    my_pthread_setprio(pthread_self(), WAIT_PRIOR);

2426
  if (!cursor->is_open())
2427
  {
2428 2429
    stmt->close_cursor();
    thd->cursor= 0;
2430 2431 2432
    reset_stmt_params(stmt);
  }

2433
  thd->restore_backup_statement(stmt, &stmt_backup);
unknown's avatar
unknown committed
2434
  thd->stmt_arena= thd;
2435

2436 2437 2438 2439
  DBUG_VOID_RETURN;
}


unknown's avatar
unknown committed
2440
/*
2441
  Reset a prepared statement in case there was a recoverable error.
2442 2443
  SYNOPSIS
    mysql_stmt_reset()
2444 2445
      thd                Thread handle
      packet             Packet with stmt id
2446 2447

  DESCRIPTION
2448 2449 2450 2451 2452
    This function resets statement to the state it was right after prepare.
    It can be used to:
     - clear an error happened during mysql_stmt_send_long_data
     - cancel long data stream for all placeholders without
       having to call mysql_stmt_execute.
2453
     - close an open cursor
2454 2455
    Sends 'OK' packet in case of success (statement was reset)
    or 'ERROR' packet (unrecoverable error/statement not found/etc).
unknown's avatar
unknown committed
2456 2457
*/

2458
void mysql_stmt_reset(THD *thd, char *packet)
unknown's avatar
unknown committed
2459
{
2460
  /* There is always space for 4 bytes in buffer */
2461
  ulong stmt_id= uint4korr(packet);
2462
  Prepared_statement *stmt;
2463
  DBUG_ENTER("mysql_stmt_reset");
unknown's avatar
unknown committed
2464

unknown's avatar
unknown committed
2465 2466 2467
  /* First of all clear possible warnings from the previous command */
  mysql_reset_thd_for_next_command(thd);

unknown's avatar
unknown committed
2468
  statistic_increment(thd->status_var.com_stmt_reset, &LOCK_status);
unknown's avatar
unknown committed
2469
  if (!(stmt= find_prepared_statement(thd, stmt_id, "mysql_stmt_reset")))
2470 2471
    DBUG_VOID_RETURN;

2472 2473 2474 2475 2476 2477 2478
  stmt->close_cursor();

  /*
    Clear parameters from data which could be set by
    mysql_stmt_send_long_data() call.
  */
  reset_stmt_params(stmt);
2479

unknown's avatar
unknown committed
2480
  stmt->state= Query_arena::PREPARED;
2481

2482
  send_ok(thd);
unknown's avatar
unknown committed
2483

2484 2485 2486 2487 2488
  DBUG_VOID_RETURN;
}


/*
2489
  Delete a prepared statement from memory.
2490
  Note: we don't send any reply to this command.
2491 2492
*/

2493
void mysql_stmt_close(THD *thd, char *packet)
2494
{
2495
  /* There is always space for 4 bytes in packet buffer */
2496
  ulong stmt_id= uint4korr(packet);
2497
  Prepared_statement *stmt;
2498
  DBUG_ENTER("mysql_stmt_close");
2499

unknown's avatar
unknown committed
2500
  if (!(stmt= find_prepared_statement(thd, stmt_id, "mysql_stmt_close")))
2501
    DBUG_VOID_RETURN;
2502

2503 2504 2505 2506
  /*
    The only way currently a statement can be deallocated when it's
    in use is from within Dynamic SQL.
  */
2507
  DBUG_ASSERT(! (stmt->flags & (uint) Prepared_statement::IS_IN_USE));
2508 2509
  (void) stmt->deallocate();

unknown's avatar
unknown committed
2510 2511 2512
  DBUG_VOID_RETURN;
}

2513 2514

/*
2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528 2529 2530 2531 2532 2533 2534 2535 2536 2537 2538 2539 2540 2541 2542 2543 2544 2545
  SQLCOM_DEALLOCATE implementation.

  DESCRIPTION
    Close an SQL prepared statement. As this can be called from Dynamic
    SQL, we should be careful to not close a statement that is currently
    being executed.

  RETURN VALUE
    none: OK packet is sent in case of success, otherwise an error
    message is set in THD
*/

void mysql_sql_stmt_close(THD *thd)
{
  Prepared_statement* stmt;
  LEX_STRING *name= &thd->lex->prepared_stmt_name;
  DBUG_PRINT("info", ("DEALLOCATE PREPARE: %.*s\n", name->length, name->str));

  if (! (stmt= (Prepared_statement*) thd->stmt_map.find_by_name(name)))
  {
    my_error(ER_UNKNOWN_STMT_HANDLER, MYF(0),
             name->length, name->str, "DEALLOCATE PREPARE");
    return;
  }

  if (stmt->deallocate() == 0)
    send_ok(thd);
}

/*
  Handle long data in pieces from client.
2546 2547 2548

  SYNOPSIS
    mysql_stmt_get_longdata()
2549 2550
      thd                Thread handle
      packet             String to append
2551
      packet_length      Length of string (including end \0)
2552 2553

  DESCRIPTION
2554 2555 2556 2557 2558
    Get a part of a long data. To make the protocol efficient, we are
    not sending any return packets here. If something goes wrong, then
    we will send the error on 'execute' We assume that the client takes
    care of checking that all parts are sent to the server. (No checking
    that we get a 'end of column' in the server is performed).
2559 2560
*/

2561
void mysql_stmt_get_longdata(THD *thd, char *packet, ulong packet_length)
2562
{
2563 2564
  ulong stmt_id;
  uint param_number;
2565
  Prepared_statement *stmt;
2566 2567
  Item_param *param;
  char *packet_end= packet + packet_length - 1;
2568 2569
  DBUG_ENTER("mysql_stmt_get_longdata");

unknown's avatar
unknown committed
2570
  statistic_increment(thd->status_var.com_stmt_send_long_data, &LOCK_status);
unknown's avatar
unknown committed
2571
#ifndef EMBEDDED_LIBRARY
2572
  /* Minimal size of long data packet is 6 bytes */
2573
  if (packet_length <= MYSQL_LONG_DATA_HEADER)
2574
  {
2575
    my_error(ER_WRONG_ARGUMENTS, MYF(0), "mysql_stmt_send_long_data");
2576 2577
    DBUG_VOID_RETURN;
  }
unknown's avatar
unknown committed
2578
#endif
2579

2580 2581
  stmt_id= uint4korr(packet);
  packet+= 4;
2582

unknown's avatar
unknown committed
2583 2584
  if (!(stmt=find_prepared_statement(thd, stmt_id,
                                     "mysql_stmt_send_long_data")))
2585 2586
    DBUG_VOID_RETURN;

2587 2588
  param_number= uint2korr(packet);
  packet+= 2;
unknown's avatar
unknown committed
2589
#ifndef EMBEDDED_LIBRARY
2590 2591
  if (param_number >= stmt->param_count)
  {
unknown's avatar
unknown committed
2592
    /* Error will be sent in execute call */
unknown's avatar
unknown committed
2593
    stmt->state= Query_arena::ERROR;
unknown's avatar
unknown committed
2594
    stmt->last_errno= ER_WRONG_ARGUMENTS;
2595 2596
    sprintf(stmt->last_error, ER(ER_WRONG_ARGUMENTS),
            "mysql_stmt_send_long_data");
2597 2598
    DBUG_VOID_RETURN;
  }
unknown's avatar
unknown committed
2599 2600
#endif

2601 2602
  param= stmt->param_array[param_number];

unknown's avatar
unknown committed
2603
#ifndef EMBEDDED_LIBRARY
2604
  if (param->set_longdata(packet, (ulong) (packet_end - packet)))
unknown's avatar
unknown committed
2605
#else
2606
  if (param->set_longdata(thd->extra_data, thd->extra_length))
unknown's avatar
unknown committed
2607
#endif
2608
  {
unknown's avatar
unknown committed
2609
    stmt->state= Query_arena::ERROR;
2610 2611 2612
    stmt->last_errno= ER_OUTOFMEMORY;
    sprintf(stmt->last_error, ER(ER_OUTOFMEMORY), 0);
  }
2613 2614
  DBUG_VOID_RETURN;
}
unknown's avatar
unknown committed
2615

2616

2617 2618 2619 2620 2621 2622 2623 2624 2625 2626 2627 2628 2629 2630 2631 2632 2633 2634 2635 2636 2637 2638 2639 2640 2641 2642 2643 2644 2645 2646 2647 2648 2649 2650 2651 2652 2653 2654 2655 2656 2657 2658 2659 2660 2661 2662 2663 2664 2665 2666 2667 2668 2669
/***************************************************************************
 Select_fetch_protocol_prep
****************************************************************************/

Select_fetch_protocol_prep::Select_fetch_protocol_prep(THD *thd)
  :protocol(thd)
{}

bool Select_fetch_protocol_prep::send_fields(List<Item> &list, uint flags)
{
  bool rc;
  Protocol *save_protocol= thd->protocol;

  /*
    Protocol::send_fields caches the information about column types:
    this information is later used to send data. Therefore, the same
    dedicated Protocol object must be used for all operations with
    a cursor.
  */
  thd->protocol= &protocol;
  rc= select_send::send_fields(list, flags);
  thd->protocol= save_protocol;

  return rc;
}

bool Select_fetch_protocol_prep::send_eof()
{
  Protocol *save_protocol= thd->protocol;

  thd->protocol= &protocol;
  ::send_eof(thd);
  thd->protocol= save_protocol;
  return FALSE;
}


bool
Select_fetch_protocol_prep::send_data(List<Item> &fields)
{
  Protocol *save_protocol= thd->protocol;
  bool rc;

  thd->protocol= &protocol;
  rc= select_send::send_data(fields);
  thd->protocol= save_protocol;
  return rc;
}

/***************************************************************************
 Prepared_statement
****************************************************************************/

2670
Prepared_statement::Prepared_statement(THD *thd_arg, Protocol *protocol_arg)
unknown's avatar
unknown committed
2671 2672 2673
  :Statement(INITIALIZED, ++thd_arg->statement_id_counter,
             thd_arg->variables.query_alloc_block_size,
             thd_arg->variables.query_prealloc_size),
2674
  thd(thd_arg),
2675
  result(thd_arg),
2676
  protocol(protocol_arg),
2677
  param_array(0),
2678
  param_count(0),
2679
  last_errno(0),
2680
   flags((uint) IS_IN_USE)
2681 2682 2683 2684
{
  *last_error= '\0';
}

2685

2686 2687 2688
void Prepared_statement::setup_set_params()
{
  /* Setup binary logging */
2689
  if (mysql_bin_log.is_open() && is_update_query(lex->sql_command) ||
2690
      opt_log || opt_slow_log)
2691
  {
2692
    set_params_from_vars= insert_params_from_vars_with_log;
2693
#ifndef EMBEDDED_LIBRARY
2694
    set_params= insert_params_withlog;
2695
#else
2696
    set_params_data= emb_insert_params_withlog;
2697 2698 2699
#endif
  }
  else
2700 2701
  {
    set_params_from_vars= insert_params_from_vars;
2702
#ifndef EMBEDDED_LIBRARY
2703
    set_params= insert_params;
2704
#else
2705
    set_params_data= emb_insert_params;
2706
#endif
2707
  }
2708 2709
}

2710

2711 2712 2713 2714 2715 2716 2717 2718
/*
  DESCRIPTION
    Destroy this prepared statement, cleaning up all used memory
    and resources. This is called from ::deallocate() to
    handle COM_STMT_CLOSE and DEALLOCATE PREPARE or when
    THD ends and all prepared statements are freed.
*/

2719 2720
Prepared_statement::~Prepared_statement()
{
2721 2722
  DBUG_ENTER("Prepared_statement::~Prepared_statement");
  DBUG_PRINT("enter",("stmt: %p  cursor: %p", this, cursor));
2723
  delete cursor;
2724 2725 2726 2727 2728
  /*
    We have to call free on the items even if cleanup is called as some items,
    like Item_param, don't free everything until free_items()
  */
  free_items();
2729
  delete lex->result;
2730
  DBUG_VOID_RETURN;
2731 2732 2733
}


unknown's avatar
unknown committed
2734
Query_arena::Type Prepared_statement::type() const
2735 2736 2737
{
  return PREPARED_STATEMENT;
}
2738 2739


2740
void Prepared_statement::cleanup_stmt()
2741
{
2742
  DBUG_ENTER("Prepared_statement::cleanup_stmt");
2743 2744
  DBUG_PRINT("enter",("stmt: %p", this));

2745 2746 2747 2748 2749 2750 2751
  /* The order is important */
  lex->unit.cleanup();
  cleanup_items(free_list);
  thd->cleanup_after_query();
  close_thread_tables(thd);
  thd->rollback_item_tree_changes();

2752
  DBUG_VOID_RETURN;
2753
}
2754 2755 2756 2757 2758 2759 2760 2761 2762 2763 2764 2765 2766 2767 2768 2769 2770 2771 2772 2773 2774 2775 2776 2777 2778 2779 2780 2781 2782 2783 2784 2785 2786 2787 2788 2789 2790 2791 2792 2793 2794 2795 2796


bool Prepared_statement::set_name(LEX_STRING *name_arg)
{
  name.length= name_arg->length;
  name.str= memdup_root(mem_root, (char*) name_arg->str, name_arg->length);
  return name.str == 0;
}

/**************************************************************************
  Common parts of mysql_[sql]_stmt_prepare, mysql_[sql]_stmt_execute.
  Essentially, these functions do all the magic of preparing/executing
  a statement, leaving network communication, input data handling and
  global THD state management to the caller.
***************************************************************************/

/*
  Parse statement text, validate the statement, and prepare it for execution.

  SYNOPSIS
    Prepared_statement::prepare()
      packet             statement text
      packet_len

  DESCRIPTION
    You should not change global THD state in this function, if at all
    possible: it may be called from any context, e.g. when executing
    a COM_* command, and SQLCOM_* command, or a stored procedure.

  NOTES
      Precondition.
      -------------
    The caller must ensure that thd->change_list and thd->free_list
    is empty: this function will not back them up but will free
    in the end of its execution.

      Postcondition.
      --------------
    thd->mem_root contains unused memory allocated during validation.
*/

bool Prepared_statement::prepare(const char *packet, uint packet_len)
{
2797
  bool error;
2798 2799 2800 2801 2802 2803 2804 2805 2806 2807 2808 2809 2810 2811 2812 2813 2814 2815 2816 2817 2818 2819 2820 2821 2822 2823 2824 2825 2826
  Statement stmt_backup;
  Query_arena *old_stmt_arena;
  DBUG_ENTER("Prepared_statement::prepare");
  /*
    If this is an SQLCOM_PREPARE, we also increase Com_prepare_sql.
    However, it seems handy if com_stmt_prepare is increased always,
    no matter what kind of prepare is processed.
  */
  statistic_increment(thd->status_var.com_stmt_prepare, &LOCK_status);

  /*
    alloc_query() uses thd->memroot && thd->query, so we should call
    both of backup_statement() and backup_query_arena() here.
  */
  thd->set_n_backup_statement(this, &stmt_backup);
  thd->set_n_backup_active_arena(this, &stmt_backup);

  if (alloc_query(thd, packet, packet_len))
  {
    thd->restore_backup_statement(this, &stmt_backup);
    thd->restore_active_arena(this, &stmt_backup);
    DBUG_RETURN(TRUE);
  }

  old_stmt_arena= thd->stmt_arena;
  thd->stmt_arena= this;
  lex_start(thd, (uchar*) thd->query, thd->query_length);
  lex->stmt_prepare_mode= TRUE;

2827
  error= MYSQLparse((void *)thd) || thd->is_fatal_error ||
2828
      thd->net.report_error || init_param_array(this);
2829
  lex->safe_to_cache_query= FALSE;
2830 2831 2832 2833 2834 2835 2836 2837 2838 2839 2840 2841 2842 2843 2844
  /*
    While doing context analysis of the query (in check_prepared_statement)
    we allocate a lot of additional memory: for open tables, JOINs, derived
    tables, etc.  Let's save a snapshot of current parse tree to the
    statement and restore original THD. In cases when some tree
    transformation can be reused on execute, we set again thd->mem_root from
    stmt->mem_root (see setup_wild for one place where we do that).
  */
  thd->restore_active_arena(this, &stmt_backup);

  /*
    If called from a stored procedure, ensure that we won't rollback
    external changes when cleaning up after validation.
  */
  DBUG_ASSERT(thd->change_list.is_empty());
2845 2846 2847 2848 2849

  /* 
   The only case where we should have items in the thd->free_list is
   after stmt->set_params_from_vars(), which may in some cases create
   Item_null objects.
2850 2851
  */

2852 2853
  if (error == 0)
    error= check_prepared_statement(this, name.str != 0);
2854

2855
  if (error && lex->sphead)
2856
  {
2857 2858
    delete lex->sphead;
    lex->sphead= NULL;
2859 2860
  }
  lex_end(lex);
2861
  cleanup_stmt();
2862 2863 2864
  thd->restore_backup_statement(this, &stmt_backup);
  thd->stmt_arena= old_stmt_arena;

2865
  if (error == 0)
2866 2867 2868 2869
  {
    setup_set_params();
    init_stmt_after_parse(lex);
    state= Query_arena::PREPARED;
2870
    flags&= ~ (uint) IS_IN_USE;
2871
  }
2872
  DBUG_RETURN(error);
2873 2874 2875 2876 2877 2878 2879 2880 2881 2882 2883 2884 2885 2886 2887 2888 2889 2890 2891 2892 2893
}

/*
  Execute a prepared statement.

  SYNOPSIS
    Prepared_statement::execute()
      expanded_query     A query for binlogging which has all parameter
                         markers ('?') replaced with their actual values.
      open_cursor        True if an attempt to open a cursor should be made.
                         Currenlty used only in the binary protocol.

  DESCRIPTION
    You should not change global THD state in this function, if at all
    possible: it may be called from any context, e.g. when executing
    a COM_* command, and SQLCOM_* command, or a stored procedure.

  NOTES
      Preconditions, postconditions.
      ------------------------------
      See the comment for Prepared_statement::prepare().
2894 2895 2896 2897

  RETURN
    FALSE		ok
    TRUE		Error
2898 2899 2900 2901 2902 2903 2904
*/

bool Prepared_statement::execute(String *expanded_query, bool open_cursor)
{
  Statement stmt_backup;
  Query_arena *old_stmt_arena;
  Item *old_free_list;
2905
  bool error= TRUE;
2906 2907 2908 2909 2910 2911 2912

  statistic_increment(thd->status_var.com_stmt_execute, &LOCK_status);

  /* Check if we got an error when sending long data */
  if (state == Query_arena::ERROR)
  {
    my_message(last_errno, last_error, MYF(0));
2913
    return TRUE;
2914
  }
2915
  if (flags & (uint) IS_IN_USE)
2916 2917
  {
    my_error(ER_PS_NO_RECURSION, MYF(0));
2918
    return TRUE;
2919
  }
2920 2921 2922 2923 2924 2925 2926 2927 2928 2929 2930 2931 2932 2933 2934 2935 2936 2937

  /*
    For SHOW VARIABLES lex->result is NULL, as it's a non-SELECT
    command. For such queries we don't return an error and don't
    open a cursor -- the client library will recognize this case and
    materialize the result set.
    For SELECT statements lex->result is created in
    check_prepared_statement. lex->result->simple_select() is FALSE
    in INSERT ... SELECT and similar commands.
  */

  if (open_cursor && lex->result && !lex->result->simple_select())
  {
    DBUG_PRINT("info",("Cursor asked for not SELECT stmt"));
    my_error(ER_SP_BAD_CURSOR_QUERY, MYF(0));
    return TRUE;
  }

2938 2939 2940
  /* In case the command has a call to SP which re-uses this statement name */
  flags|= IS_IN_USE;

2941
  close_cursor();
2942 2943 2944 2945 2946 2947

  /*
    If the free_list is not empty, we'll wrongly free some externally
    allocated items when cleaning up after execution of this statement.
  */
  DBUG_ASSERT(thd->change_list.is_empty());
2948 2949 2950 2951 2952 2953 2954

  /* 
   The only case where we should have items in the thd->free_list is
   after stmt->set_params_from_vars(), which may in some cases create
   Item_null objects.
  */

2955 2956 2957 2958 2959 2960 2961 2962 2963 2964 2965 2966 2967 2968 2969 2970 2971 2972 2973 2974 2975 2976 2977 2978 2979 2980 2981 2982
  thd->set_n_backup_statement(this, &stmt_backup);
  if (expanded_query->length() &&
      alloc_query(thd, (char*) expanded_query->ptr(),
                  expanded_query->length()+1))
  {
    my_error(ER_OUTOFMEMORY, 0, expanded_query->length());
    goto error;
  }
  /*
    Expanded query is needed for slow logging, so we want thd->query
    to point at it even after we restore from backup. This is ok, as
    expanded query was allocated in thd->mem_root.
  */
  stmt_backup.query= thd->query;
  stmt_backup.query_length= thd->query_length;

  /*
    At first execution of prepared statement we may perform logical
    transformations of the query tree. Such changes should be performed
    on the parse tree of current prepared statement and new items should
    be allocated in its memory root. Set the appropriate pointer in THD
    to the arena of the statement.
  */
  old_stmt_arena= thd->stmt_arena;
  thd->stmt_arena= this;
  reinit_stmt_before_use(thd, lex);

  thd->protocol= protocol;                      /* activate stmt protocol */
2983 2984 2985 2986
  error= (open_cursor ?
          mysql_open_cursor(thd, (uint) ALWAYS_MATERIALIZED_CURSOR,
                            &result, &cursor) :
          mysql_execute_command(thd));
2987 2988
  thd->protocol= &thd->protocol_simple;         /* use normal protocol */

2989
  /* Assert that if an error, no cursor is open */
unknown's avatar
unknown committed
2990
  DBUG_ASSERT(! (error && cursor));
2991

2992
  if (! cursor)
2993
  {
2994
    cleanup_stmt();
2995 2996 2997 2998 2999 3000 3001 3002 3003 3004
    reset_stmt_params(this);
  }

  thd->set_statement(&stmt_backup);
  thd->stmt_arena= old_stmt_arena;

  if (state == Query_arena::PREPARED)
    state= Query_arena::EXECUTED;

error:
3005 3006
  flags&= ~ (uint) IS_IN_USE;
  return error;
3007 3008 3009 3010 3011 3012 3013 3014 3015
}


/* Common part of DEALLOCATE PREPARE and mysql_stmt_close */

bool Prepared_statement::deallocate()
{
  /* We account deallocate in the same manner as mysql_stmt_close */
  statistic_increment(thd->status_var.com_stmt_close, &LOCK_status);
3016
  if (flags & (uint) IS_IN_USE)
3017 3018 3019 3020 3021 3022 3023 3024
  {
    my_error(ER_PS_NO_RECURSION, MYF(0));
    return TRUE;
  }
  /* Statement map calls delete stmt on erase */
  thd->stmt_map.erase(this);
  return FALSE;
}