sql_prepare.cc 85.1 KB
Newer Older
unknown's avatar
unknown committed
1 2 3 4
/* 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
unknown's avatar
unknown committed
5
   the Free Software Foundation; version 2 of the License.
unknown's avatar
unknown committed
6 7 8 9 10 11 12 13

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

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

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

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

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

39
  - Server gets the command 'COM_STMT_EXECUTE' to execute the
40 41
    previously prepared query. If there are any parameter markers, then the
    client will send the data in the following format:
42
    [COM_STMT_EXECUTE:1]
unknown's avatar
unknown committed
43 44 45 46
    [STMT_ID:4]
    [NULL_BITS:(param_count+7)/8)]
    [TYPES_SUPPLIED_BY_CLIENT(0/1):1]
    [[length]data]
unknown's avatar
unknown committed
47 48
    [[length]data] .. [[length]data].
    (Note: Except for string/binary types; all other types will not be
unknown's avatar
unknown committed
49
    supplied with length field)
50 51 52
  - 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
53
  - Execute the query without re-parsing and send back the results
unknown's avatar
unknown committed
54 55
    to client

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

58 59
  - Server gets the long data in pieces with command type
    'COM_STMT_SEND_LONG_DATA'.
unknown's avatar
unknown committed
60
  - The packet recieved will have the format as:
61
    [COM_STMT_SEND_LONG_DATA:1][STMT_ID:4][parameter_number:2][data]
62
  - data from the packet is appended to the long data value buffer for this
63
    placeholder.
64 65 66 67
  - 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.
68

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

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

84 85
/* A result class used to send cursor rows using the binary protocol. */

unknown's avatar
unknown committed
86
class Select_fetch_protocol_binary: public select_send
87
{
unknown's avatar
unknown committed
88
  Protocol_binary protocol;
89
public:
unknown's avatar
unknown committed
90
  Select_fetch_protocol_binary(THD *thd);
91 92 93
  virtual bool send_fields(List<Item> &list, uint flags);
  virtual bool send_data(List<Item> &items);
  virtual bool send_eof();
94 95 96 97 98 99
#ifdef EMBEDDED_LIBRARY
  void begin_dataset()
  {
    protocol.begin_dataset();
  }
#endif
100 101
};

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

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

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

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

unknown's avatar
unknown committed
146

147 148 149
/******************************************************************************
  Implementation
******************************************************************************/
150 151


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

/*
158 159 160 161 162 163 164 165 166 167 168 169 170 171
  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.
172 173
*/

174
static Prepared_statement *
unknown's avatar
unknown committed
175
find_prepared_statement(THD *thd, ulong id, const char *where)
176
{
177 178 179 180 181
  /*
    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.
  */
182 183
  Statement *stmt= thd->stmt_map.find(id);

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

194

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

  SYNOPSIS
    send_prep_stmt()

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

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

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

221 222 223 224
  /*
    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
225
  DBUG_RETURN(my_net_write(net, buff, sizeof(buff)) ||
unknown's avatar
unknown committed
226
              (stmt->param_count &&
unknown's avatar
unknown committed
227 228 229
               stmt->thd->protocol_text.send_fields((List<Item> *)
                                                    &stmt->lex->param_list,
                                                    Protocol::SEND_EOF)));
unknown's avatar
unknown committed
230
}
231
#else
232 233
static bool send_prep_stmt(Prepared_statement *stmt,
                           uint columns __attribute__((unused)))
unknown's avatar
unknown committed
234
{
unknown's avatar
SCRUM  
unknown committed
235 236
  THD *thd= stmt->thd;

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

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

unknown's avatar
unknown committed
245 246

/*
247 248 249 250 251 252 253 254 255 256 257 258 259 260
  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
261 262
*/

unknown's avatar
unknown committed
263
#ifndef EMBEDDED_LIBRARY
264
static ulong get_param_length(uchar **packet, ulong len)
unknown's avatar
unknown committed
265 266
{
  reg1 uchar *pos= *packet;
267 268
  if (len < 1)
    return 0;
unknown's avatar
unknown committed
269 270 271 272 273
  if (*pos < 251)
  {
    (*packet)++;
    return (ulong) *pos;
  }
274 275
  if (len < 3)
    return 0;
unknown's avatar
unknown committed
276 277 278 279 280
  if (*pos == 252)
  {
    (*packet)+=3;
    return (ulong) uint2korr(pos+1);
  }
281 282
  if (len < 4)
    return 0;
unknown's avatar
unknown committed
283 284 285 286 287
  if (*pos == 253)
  {
    (*packet)+=4;
    return (ulong) uint3korr(pos+1);
  }
288 289
  if (len < 5)
    return 0;
unknown's avatar
unknown committed
290
  (*packet)+=9; // Must be 254 when here
291 292 293 294 295 296 297
  /*
    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
298 299
  return (ulong) uint4korr(pos+1);
}
unknown's avatar
unknown committed
300
#else
301
#define get_param_length(packet, len) len
unknown's avatar
unknown committed
302 303
#endif /*!EMBEDDED_LIBRARY*/

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

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

313 314 315
  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
316

317 318 319
    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
320

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

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

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

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

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

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

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

unknown's avatar
unknown committed
409 410 411 412
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);
413
  *pos+= length;
unknown's avatar
unknown committed
414 415
}

416
#ifndef EMBEDDED_LIBRARY
417 418 419 420 421 422 423

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

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

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

434 435
    tm.neg= (bool) to[0];
    day= (uint) sint4korr(to+1);
436
    tm.hour=   (uint) to[5] + day * 24;
437 438
    tm.minute= (uint) to[6];
    tm.second= (uint) to[7];
439
    tm.second_part= (length > 8) ? (ulong) sint4korr(to+8) : 0;
440 441 442 443 444 445 446 447
    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;
448
  }
449
  else
450
    set_zero_time(&tm, MYSQL_TIMESTAMP_TIME);
451 452
  param->set_time(&tm, MYSQL_TIMESTAMP_TIME,
                  MAX_TIME_WIDTH * MY_CHARSET_BIN_MB_MAXLEN);
453 454 455
  *pos+= length;
}

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

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

    tm.neg=    0;
    tm.year=   (uint) sint2korr(to);
    tm.month=  (uint) to[2];
    tm.day=    (uint) to[3];
469 470 471 472 473 474 475 476 477
    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;

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

487

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

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

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

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

512 513 514
#else/*!EMBEDDED_LIBRARY*/
void set_param_time(Item_param *param, uchar **pos, ulong len)
{
515 516 517 518 519 520 521 522 523 524 525
  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,
526
                  MAX_TIME_WIDTH * MY_CHARSET_BIN_MB_MAXLEN);
527 528 529 530 531

}

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

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

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

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

548 549

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

556

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

static void setup_one_conversion_function(THD *thd, Item_param *param,
                                          uchar param_type)
unknown's avatar
unknown committed
561
{
unknown's avatar
unknown committed
562
  switch (param_type) {
563
  case MYSQL_TYPE_TINY:
564
    param->set_param_func= set_param_tiny;
565
    param->item_type= Item::INT_ITEM;
566
    param->item_result_type= INT_RESULT;
unknown's avatar
unknown committed
567
    break;
568
  case MYSQL_TYPE_SHORT:
569
    param->set_param_func= set_param_short;
570
    param->item_type= Item::INT_ITEM;
571
    param->item_result_type= INT_RESULT;
unknown's avatar
unknown committed
572
    break;
573
  case MYSQL_TYPE_LONG:
574
    param->set_param_func= set_param_int32;
575
    param->item_type= Item::INT_ITEM;
576
    param->item_result_type= INT_RESULT;
unknown's avatar
unknown committed
577
    break;
578
  case MYSQL_TYPE_LONGLONG:
579
    param->set_param_func= set_param_int64;
580
    param->item_type= Item::INT_ITEM;
581
    param->item_result_type= INT_RESULT;
unknown's avatar
unknown committed
582
    break;
583
  case MYSQL_TYPE_FLOAT:
584
    param->set_param_func= set_param_float;
585
    param->item_type= Item::REAL_ITEM;
586
    param->item_result_type= REAL_RESULT;
unknown's avatar
unknown committed
587
    break;
588
  case MYSQL_TYPE_DOUBLE:
589
    param->set_param_func= set_param_double;
590
    param->item_type= Item::REAL_ITEM;
591
    param->item_result_type= REAL_RESULT;
unknown's avatar
unknown committed
592
    break;
unknown's avatar
unknown committed
593 594 595 596 597 598
  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;
599
  case MYSQL_TYPE_TIME:
600
    param->set_param_func= set_param_time;
601
    param->item_type= Item::STRING_ITEM;
602
    param->item_result_type= STRING_RESULT;
603
    break;
604
  case MYSQL_TYPE_DATE:
605
    param->set_param_func= set_param_date;
606
    param->item_type= Item::STRING_ITEM;
607
    param->item_result_type= STRING_RESULT;
608
    break;
609 610
  case MYSQL_TYPE_DATETIME:
  case MYSQL_TYPE_TIMESTAMP:
611
    param->set_param_func= set_param_datetime;
612
    param->item_type= Item::STRING_ITEM;
613
    param->item_result_type= STRING_RESULT;
614
    break;
615 616 617 618
  case MYSQL_TYPE_TINY_BLOB:
  case MYSQL_TYPE_MEDIUM_BLOB:
  case MYSQL_TYPE_LONG_BLOB:
  case MYSQL_TYPE_BLOB:
619
    param->set_param_func= set_param_str;
620 621 622
    param->value.cs_info.character_set_of_placeholder= &my_charset_bin;
    param->value.cs_info.character_set_client=
      thd->variables.character_set_client;
623
    DBUG_ASSERT(thd->variables.character_set_client);
624 625
    param->value.cs_info.final_character_set_of_str_value= &my_charset_bin;
    param->item_type= Item::STRING_ITEM;
626
    param->item_result_type= STRING_RESULT;
627 628 629 630 631 632 633 634 635 636 637 638
    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;

639
      param->value.cs_info.character_set_of_placeholder= fromcs;
640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657
      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
658
  }
659
  param->param_type= (enum enum_field_types) param_type;
unknown's avatar
unknown committed
660 661
}

unknown's avatar
unknown committed
662
#ifndef EMBEDDED_LIBRARY
unknown's avatar
unknown committed
663
/*
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
  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
693 694
*/

695
static bool insert_params_withlog(Prepared_statement *stmt, uchar *null_array,
unknown's avatar
unknown committed
696
                                  uchar *read_pos, uchar *data_end,
697
                                  String *query)
698
{
699 700 701 702
  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
703
  String str;
704
  const String *res;
unknown's avatar
unknown committed
705
  DBUG_ENTER("insert_params_withlog");
706

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

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

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

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

736

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

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

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

766

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

773
  DBUG_ENTER("setup_conversion_functions");
774

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

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

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

802 803
#else

804 805 806 807 808 809 810 811 812 813 814
/*
  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).
*/

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

822
  DBUG_ENTER("emb_insert_params");
823

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

848

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

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

860
  DBUG_ENTER("emb_insert_params_withlog");
861

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

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

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

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

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

unknown's avatar
unknown committed
897

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

unknown's avatar
unknown committed
901 902 903 904 905
  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
906
      query     Ignored
unknown's avatar
unknown committed
907 908
*/

909 910
static bool insert_params_from_vars(Prepared_statement *stmt,
                                    List<LEX_STRING>& varnames,
911
                                    String *query __attribute__((unused)))
unknown's avatar
unknown committed
912 913 914 915 916 917
{
  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);
918 919
  DBUG_ENTER("insert_params_from_vars");

unknown's avatar
unknown committed
920 921 922 923
  for (Item_param **it= begin; it < end; ++it)
  {
    Item_param *param= *it;
    varname= var_it++;
924 925 926 927 928 929
    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
930 931 932 933
  }
  DBUG_RETURN(0);
}

934

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

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

unknown's avatar
unknown committed
948
static bool insert_params_from_vars_with_log(Prepared_statement *stmt,
949
                                             List<LEX_STRING>& varnames,
950
                                             String *query)
unknown's avatar
unknown committed
951 952 953 954 955 956
{
  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);
957 958
  String buf;
  const String *val;
unknown's avatar
unknown committed
959
  uint32 length= 0;
960 961 962

  DBUG_ENTER("insert_params_from_vars");

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

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

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

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

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

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

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

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

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

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

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

1037
  /*
unknown's avatar
unknown committed
1038 1039 1040 1041 1042 1043
    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
1044
  */
1045
  if (open_normal_and_derived_tables(thd, table_list, 0))
unknown's avatar
unknown committed
1046
    goto error;
unknown's avatar
unknown committed
1047

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

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

unknown's avatar
unknown committed
1060 1061 1062
    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
1063
      goto error;
1064

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

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

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


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

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

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

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

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

1126
  for ( ; ; )
1127
  {
1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144
    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;
1145
    close_tables_for_reopen(thd, &table_list);
unknown's avatar
unknown committed
1146
  }
unknown's avatar
unknown committed
1147

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

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

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

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


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

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

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

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

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

unknown's avatar
unknown committed
1215
  if (!table_list->table)
unknown's avatar
unknown committed
1216
  {
unknown's avatar
unknown committed
1217 1218 1219
    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
1220
  }
unknown's avatar
unknown committed
1221 1222 1223

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

unknown's avatar
unknown committed
1227

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

1231
  SYNOPSIS
unknown's avatar
unknown committed
1232
    mysql_test_select()
1233 1234 1235 1236 1237 1238
      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
1239

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

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

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

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

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

1271
  if (open_and_lock_tables(thd, tables))
unknown's avatar
unknown committed
1272
    goto error;
unknown's avatar
unknown committed
1273

1274 1275
  thd->used_tables= 0;                        // Updated by setup_fields

1276 1277 1278 1279 1280
  /*
    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
  */
1281
  if (unit->prepare(thd, 0, 0))
unknown's avatar
unknown committed
1282
    goto error;
1283
  if (!lex->describe && !text_protocol)
1284
  {
1285 1286
    /* Make copy of item list, as change_columns may change it */
    List<Item> fields(lex->select_lex.item_list);
1287

1288 1289 1290
    /* Change columns if a procedure like analyse() */
    if (unit->last_procedure && unit->last_procedure->change_columns(fields))
      goto error;
1291

1292 1293 1294 1295 1296 1297 1298 1299 1300
    /*
      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);
1301
  }
1302
  DBUG_RETURN(0);
unknown's avatar
unknown committed
1303
error:
1304
  DBUG_RETURN(1);
unknown's avatar
unknown committed
1305 1306
}

1307

1308
/*
1309
  Validate and prepare for execution DO statement expressions.
1310 1311 1312

  SYNOPSIS
    mysql_test_do_fields()
1313 1314 1315
      stmt               prepared statement
      tables             list of tables used in this query
      values             list of expressions
1316 1317

  RETURN VALUE
1318 1319
    FALSE                success
    TRUE                 error, error message is set in THD
1320 1321
*/

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

  DBUG_ENTER("mysql_test_do_fields");
unknown's avatar
unknown committed
1329 1330
  if (tables && check_table_access(thd, SELECT_ACL, tables, 0))
    DBUG_RETURN(TRUE);
1331

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


/*
  Validate and prepare for execution SET statement expressions

  SYNOPSIS
    mysql_test_set_fields()
1343 1344 1345
      stmt               prepared statement
      tables             list of tables used in this query
      values             list of expressions
1346 1347

  RETURN VALUE
1348 1349
    FALSE                success
    TRUE                 error, error message is set in THD
1350
*/
unknown's avatar
unknown committed
1351

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

unknown's avatar
unknown committed
1361 1362
  if (tables && check_table_access(thd, SELECT_ACL, tables, 0) ||
      open_and_lock_tables(thd, tables))
1363
    goto error;
unknown's avatar
unknown committed
1364

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


/*
  Check internal SELECT of the prepared command

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

  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).
1390

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

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

1404 1405
  lex->select_lex.context.resolve_in_select_list= TRUE;

unknown's avatar
unknown committed
1406 1407
  if (specific_prepare && (*specific_prepare)(thd))
    DBUG_RETURN(TRUE);
unknown's avatar
VIEW  
unknown committed
1408

1409 1410
  thd->used_tables= 0;                        // Updated by setup_fields

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

1415 1416
/*
  Check internal SELECT of the prepared command (with opening and
1417
  locking of used tables).
1418 1419 1420

  SYNOPSIS
    select_like_stmt_test_with_open_n_lock()
1421 1422 1423 1424 1425
      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()
1426 1427

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

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));
}


1453
/*
1454
  Validate and prepare for execution CREATE TABLE statement
1455 1456 1457

  SYNOPSIS
    mysql_test_create_table()
1458 1459
      stmt               prepared statement
      tables             list of tables used in this query
1460 1461

  RETURN VALUE
1462 1463
    FALSE                success
    TRUE                 error, error message is set in THD
1464
*/
unknown's avatar
unknown committed
1465

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

unknown's avatar
unknown committed
1478 1479 1480 1481
  if (create_table_precheck(thd, tables, create_table))
    DBUG_RETURN(TRUE);

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

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

unknown's avatar
unknown committed
1492

1493
/*
1494
  Validate and prepare for execution a multi update statement.
1495 1496 1497

  SYNOPSIS
    mysql_test_multiupdate()
1498 1499 1500
      stmt               prepared statement
      tables             list of tables used in this query
      converted          converted to multi-update from usual update
1501 1502

  RETURN VALUE
1503 1504
    FALSE                success
    TRUE                 error, error message is set in THD
1505
*/
unknown's avatar
unknown committed
1506 1507

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

  return select_like_stmt_test(stmt, &mysql_multi_update_prepare,
                               OPTION_SETUP_TABLES_DONE);
1517 1518 1519 1520
}


/*
1521
  Validate and prepare for execution a multi delete statement.
1522 1523 1524

  SYNOPSIS
    mysql_test_multidelete()
1525 1526
      stmt               prepared statement
      tables             list of tables used in this query
1527 1528

  RETURN VALUE
1529 1530
    FALSE                success
    TRUE                 error, error message in THD is set.
1531
*/
unknown's avatar
unknown committed
1532 1533

static bool mysql_test_multidelete(Prepared_statement *stmt,
unknown's avatar
unknown committed
1534
                                  TABLE_LIST *tables)
1535 1536 1537
{
  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
1538 1539 1540 1541
  {
    my_error(ER_OUTOFMEMORY, MYF(0), 0);
    goto error;
  }
1542

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


1560 1561 1562 1563 1564 1565
/*
  Wrapper for mysql_insert_select_prepare, to make change of local tables
  after open_and_lock_tables() call.

  SYNOPSIS
    mysql_insert_select_prepare_tester()
1566
      thd                thread handle
1567

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

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


1593
/*
1594
  Validate and prepare for execution INSERT ... SELECT statement.
1595 1596 1597

  SYNOPSIS
    mysql_test_insert_select()
1598 1599
      stmt               prepared statement
      tables             list of tables used in this query
1600 1601

  RETURN VALUE
1602 1603
    FALSE                success
    TRUE                 error, error message is set in THD
1604
*/
unknown's avatar
unknown committed
1605

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

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

unknown's avatar
unknown committed
1619 1620
  if (insert_precheck(stmt->thd, tables))
    return 1;
1621 1622 1623 1624 1625

  /* 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);

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


unknown's avatar
unknown committed
1636
/*
1637 1638 1639
  Perform semantic analysis of the parsed tree and send a response packet
  to the client.

1640
  SYNOPSIS
1641
    check_prepared_statement()
1642
      stmt               prepared statement
1643 1644 1645 1646 1647 1648 1649

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

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

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

unknown's avatar
VIEW  
unknown committed
1668 1669
  lex->first_lists_tables_same();
  tables= lex->query_tables;
1670

1671 1672 1673 1674
  /* set context for commands which do not use setup_tables */
  lex->select_lex.context.resolve_in_table_list_only(select_lex->
                                                     get_table_list());

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

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

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

unknown's avatar
unknown committed
1694
  case SQLCOM_DELETE:
unknown's avatar
unknown committed
1695
    res= mysql_test_delete(stmt, tables);
unknown's avatar
unknown committed
1696 1697 1698
    break;

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

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

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

  case SQLCOM_DELETE_MULTI:
unknown's avatar
unknown committed
1719
    res= mysql_test_multidelete(stmt, tables);
1720 1721 1722
    break;

  case SQLCOM_INSERT_SELECT:
1723
  case SQLCOM_REPLACE_SELECT:
unknown's avatar
unknown committed
1724
    res= mysql_test_insert_select(stmt, tables);
1725 1726
    break;

1727 1728 1729 1730
    /*
      Note that we don't need to have cases in this list if they are
      marked with CF_STATUS_COMMAND in sql_command_flags
    */
1731 1732 1733 1734
  case SQLCOM_SHOW_PROCESSLIST:
  case SQLCOM_SHOW_STORAGE_ENGINES:
  case SQLCOM_SHOW_PRIVILEGES:
  case SQLCOM_SHOW_COLUMN_TYPES:
1735 1736 1737
  case SQLCOM_SHOW_ENGINE_LOGS:
  case SQLCOM_SHOW_ENGINE_STATUS:
  case SQLCOM_SHOW_ENGINE_MUTEX:
1738 1739
  case SQLCOM_SHOW_CREATE_DB:
  case SQLCOM_SHOW_GRANTS:
1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753
  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:
1754 1755
  case SQLCOM_DROP_TABLE:
  case SQLCOM_RENAME_TABLE:
1756 1757 1758 1759 1760 1761
  case SQLCOM_ALTER_TABLE:
  case SQLCOM_COMMIT:
  case SQLCOM_CREATE_INDEX:
  case SQLCOM_DROP_INDEX:
  case SQLCOM_ROLLBACK:
  case SQLCOM_TRUNCATE:
1762
  case SQLCOM_CALL:
1763 1764
  case SQLCOM_CREATE_VIEW:
  case SQLCOM_DROP_VIEW:
1765 1766 1767
  case SQLCOM_REPAIR:
  case SQLCOM_ANALYZE:
  case SQLCOM_OPTIMIZE:
1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786
  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:
1787 1788
    break;

unknown's avatar
unknown committed
1789
  default:
1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800
    /*
      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
1801
  }
unknown's avatar
unknown committed
1802
  if (res == 0)
unknown's avatar
unknown committed
1803 1804
    DBUG_RETURN(text_protocol? FALSE : (send_prep_stmt(stmt, 0) ||
                                        thd->protocol->flush()));
1805
error:
unknown's avatar
unknown committed
1806
  DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
1807 1808
}

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

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

1844

unknown's avatar
unknown committed
1845
/*
1846
  COM_STMT_PREPARE handler.
unknown's avatar
unknown committed
1847

1848 1849
  SYNOPSIS
    mysql_stmt_prepare()
1850 1851 1852 1853 1854 1855 1856
      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
1857

1858
  NOTES
unknown's avatar
unknown committed
1859 1860 1861 1862
    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.
1863 1864

    If parameter markers are found in the query, then store the information
unknown's avatar
unknown committed
1865 1866
    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
1867
    field items.
unknown's avatar
unknown committed
1868

1869 1870 1871
  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
1872 1873
*/

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

unknown's avatar
unknown committed
1880
  DBUG_PRINT("prep_query", ("%s", packet));
unknown's avatar
unknown committed
1881

unknown's avatar
unknown committed
1882 1883 1884
  /* First of all clear possible warnings from the previous command */
  mysql_reset_thd_for_next_command(thd);

unknown's avatar
unknown committed
1885
  if (! (stmt= new Prepared_statement(thd, &thd->protocol_binary)))
1886
    DBUG_VOID_RETURN; /* out of memory: error is set in Sql_alloc */
1887

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

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

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

1905
  error= stmt->prepare(packet, packet_length);
unknown's avatar
unknown committed
1906 1907

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

1910
  if (error)
1911
  {
1912 1913
    /* Statement map deletes statement on erase */
    thd->stmt_map.erase(stmt);
1914 1915
  }
  else
unknown's avatar
unknown committed
1916 1917
  {
    const char *format= "[%lu] %.*b";
unknown's avatar
unknown committed
1918
    general_log_print(thd, COM_STMT_PREPARE, format, stmt->id,
1919
                      stmt->query_length, stmt->query);
1920

unknown's avatar
unknown committed
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
/*
  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");
unknown's avatar
unknown committed
2057
  DBUG_ASSERT(thd->protocol == &thd->protocol_text);
2058

2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069
  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)) ||
unknown's avatar
unknown committed
2070
      ! (stmt= new Prepared_statement(thd, &thd->protocol_text)))
2071 2072 2073 2074
  {
    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
  DBUG_PRINT("exec_query", ("%s", stmt->query));
unknown's avatar
unknown committed
2265
  DBUG_PRINT("info",("stmt: 0x%lx", (long) 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)
unknown's avatar
unknown committed
2303 2304
  {
    const char *format= "[%lu] %.*b";
unknown's avatar
unknown committed
2305
    general_log_print(thd, COM_STMT_EXECUTE, format, stmt->id,
2306
                      thd->query_length, thd->query);
unknown's avatar
unknown committed
2307
  }
2308
  DBUG_VOID_RETURN;
unknown's avatar
unknown committed
2309

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

2316

unknown's avatar
unknown committed
2317
/*
2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334
  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
2335 2336
*/

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

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

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

unknown's avatar
unknown committed
2360
  DBUG_PRINT("info",("stmt: 0x%lx", (long) stmt));
2361

2362 2363 2364 2365 2366 2367 2368
  /*
    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);

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

2373
  (void) stmt->execute(&expanded_query, FALSE);
2374

unknown's avatar
unknown committed
2375
  DBUG_VOID_RETURN;
2376 2377 2378 2379 2380

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

2383

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

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

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);
2398
  ulong num_rows= uint4korr(packet+4);
2399
  Prepared_statement *stmt;
unknown's avatar
unknown committed
2400
  Statement stmt_backup;
2401
  Server_side_cursor *cursor;
2402 2403
  DBUG_ENTER("mysql_stmt_fetch");

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

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

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

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

2423
  cursor->fetch(num_rows);
2424 2425 2426 2427

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

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

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

2438 2439 2440 2441
  DBUG_VOID_RETURN;
}


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

  DESCRIPTION
2450 2451 2452 2453 2454
    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.
2455
     - close an open cursor
2456 2457
    Sends 'OK' packet in case of success (statement was reset)
    or 'ERROR' packet (unrecoverable error/statement not found/etc).
unknown's avatar
unknown committed
2458 2459
*/

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

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

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

2474 2475 2476 2477 2478 2479 2480
  stmt->close_cursor();

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

unknown's avatar
unknown committed
2482
  stmt->state= Query_arena::PREPARED;
2483

2484
  send_ok(thd);
unknown's avatar
unknown committed
2485

2486 2487 2488 2489 2490
  DBUG_VOID_RETURN;
}


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

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

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

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

unknown's avatar
unknown committed
2512 2513 2514
  DBUG_VOID_RETURN;
}

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 2546 2547
  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.
2548 2549 2550

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

  DESCRIPTION
2556 2557 2558 2559 2560
    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).
2561 2562
*/

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

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

2582 2583
  stmt_id= uint4korr(packet);
  packet+= 4;
2584

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

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

2603 2604
  param= stmt->param_array[param_number];

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

2618

2619
/***************************************************************************
unknown's avatar
unknown committed
2620
 Select_fetch_protocol_binary
2621 2622
****************************************************************************/

unknown's avatar
unknown committed
2623
Select_fetch_protocol_binary::Select_fetch_protocol_binary(THD *thd)
2624 2625 2626
  :protocol(thd)
{}

unknown's avatar
unknown committed
2627
bool Select_fetch_protocol_binary::send_fields(List<Item> &list, uint flags)
2628 2629 2630 2631 2632 2633 2634 2635 2636 2637 2638 2639 2640 2641 2642 2643 2644
{
  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;
}

unknown's avatar
unknown committed
2645
bool Select_fetch_protocol_binary::send_eof()
2646 2647 2648 2649 2650 2651 2652 2653 2654 2655 2656
{
  Protocol *save_protocol= thd->protocol;

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


bool
unknown's avatar
unknown committed
2657
Select_fetch_protocol_binary::send_data(List<Item> &fields)
2658 2659 2660 2661 2662 2663 2664 2665 2666 2667 2668 2669 2670 2671
{
  Protocol *save_protocol= thd->protocol;
  bool rc;

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

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

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

2687

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

2712

2713 2714 2715 2716 2717 2718 2719 2720
/*
  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.
*/

2721 2722
Prepared_statement::~Prepared_statement()
{
2723
  DBUG_ENTER("Prepared_statement::~Prepared_statement");
unknown's avatar
unknown committed
2724 2725
  DBUG_PRINT("enter",("stmt: 0x%lx  cursor: 0x%lx",
                      (long) this, (long) cursor));
2726
  delete cursor;
2727 2728 2729 2730 2731
  /*
    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();
2732
  delete lex->result;
2733
  DBUG_VOID_RETURN;
2734 2735 2736
}


unknown's avatar
unknown committed
2737
Query_arena::Type Prepared_statement::type() const
2738 2739 2740
{
  return PREPARED_STATEMENT;
}
2741 2742


2743
void Prepared_statement::cleanup_stmt()
2744
{
2745
  DBUG_ENTER("Prepared_statement::cleanup_stmt");
unknown's avatar
unknown committed
2746
  DBUG_PRINT("enter",("stmt: 0x%lx", (long) this));
2747

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

2755
  DBUG_VOID_RETURN;
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 2797 2798 2799


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)
{
2800
  bool error;
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 2827 2828 2829
  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;

2830
  error= MYSQLparse((void *)thd) || thd->is_fatal_error ||
2831
      thd->net.report_error || init_param_array(this);
2832 2833 2834 2835 2836 2837 2838 2839 2840 2841

  /*
    The first thing we do after parse error is freeing sp_head to
    ensure that we have restored original memroot.
  */
  if (error && lex->sphead)
  {
    delete lex->sphead;
    lex->sphead= NULL;
  }
2842

2843
  lex->safe_to_cache_query= FALSE;
2844

2845 2846 2847 2848 2849 2850 2851 2852 2853 2854 2855 2856 2857 2858 2859
  /*
    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());
2860 2861 2862 2863 2864

  /* 
   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.
2865 2866
  */

2867 2868
  if (error == 0)
    error= check_prepared_statement(this, name.str != 0);
2869

2870
  /* Free sp_head if check_prepared_statement() failed. */
2871
  if (error && lex->sphead)
2872
  {
2873 2874
    delete lex->sphead;
    lex->sphead= NULL;
2875 2876
  }
  lex_end(lex);
2877
  cleanup_stmt();
2878 2879 2880
  thd->restore_backup_statement(this, &stmt_backup);
  thd->stmt_arena= old_stmt_arena;

2881
  if (error == 0)
2882 2883 2884 2885
  {
    setup_set_params();
    init_stmt_after_parse(lex);
    state= Query_arena::PREPARED;
2886
    flags&= ~ (uint) IS_IN_USE;
2887
  }
2888
  DBUG_RETURN(error);
2889 2890 2891 2892 2893 2894 2895 2896 2897 2898 2899 2900 2901 2902 2903 2904 2905 2906 2907 2908 2909
}

/*
  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().
2910 2911 2912 2913

  RETURN
    FALSE		ok
    TRUE		Error
2914 2915 2916 2917 2918 2919
*/

bool Prepared_statement::execute(String *expanded_query, bool open_cursor)
{
  Statement stmt_backup;
  Query_arena *old_stmt_arena;
2920
  bool error= TRUE;
2921 2922 2923 2924 2925 2926 2927

  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));
2928
    return TRUE;
2929
  }
2930
  if (flags & (uint) IS_IN_USE)
2931 2932
  {
    my_error(ER_PS_NO_RECURSION, MYF(0));
2933
    return TRUE;
2934
  }
2935 2936 2937 2938 2939 2940 2941 2942 2943 2944 2945

  /*
    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.
  */

2946
  if (open_cursor && lex->result && lex->result->check_simple_select())
2947 2948 2949 2950 2951
  {
    DBUG_PRINT("info",("Cursor asked for not SELECT stmt"));
    return TRUE;
  }

2952 2953 2954
  /* In case the command has a call to SP which re-uses this statement name */
  flags|= IS_IN_USE;

2955
  close_cursor();
2956 2957 2958 2959 2960 2961

  /*
    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());
2962 2963 2964 2965 2966 2967 2968

  /* 
   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.
  */

2969 2970 2971 2972 2973 2974 2975 2976 2977 2978 2979 2980 2981 2982 2983 2984 2985 2986 2987 2988 2989 2990 2991 2992 2993 2994 2995 2996
  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 */
2997 2998 2999 3000
  error= (open_cursor ?
          mysql_open_cursor(thd, (uint) ALWAYS_MATERIALIZED_CURSOR,
                            &result, &cursor) :
          mysql_execute_command(thd));
unknown's avatar
unknown committed
3001
  thd->protocol= &thd->protocol_text;         /* use normal protocol */
3002

3003
  /* Assert that if an error, no cursor is open */
unknown's avatar
unknown committed
3004
  DBUG_ASSERT(! (error && cursor));
3005

3006
  if (! cursor)
3007
  {
3008
    cleanup_stmt();
3009 3010 3011 3012 3013 3014 3015 3016 3017 3018
    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:
3019 3020
  flags&= ~ (uint) IS_IN_USE;
  return error;
3021 3022 3023 3024 3025 3026 3027 3028 3029
}


/* 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);
3030
  if (flags & (uint) IS_IN_USE)
3031 3032 3033 3034 3035 3036 3037 3038
  {
    my_error(ER_PS_NO_RECURSION, MYF(0));
    return TRUE;
  }
  /* Statement map calls delete stmt on erase */
  thd->stmt_map.erase(this);
  return FALSE;
}