sql_prepare.cc 47.8 KB
Newer Older
unknown's avatar
unknown committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14
/* Copyright (C) 1995-2002 MySQL AB

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.

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

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
15
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA */
unknown's avatar
unknown committed
16 17 18 19 20 21

/**********************************************************************
This file contains the implementation of prepare and executes. 

Prepare:

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

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

Long data handling:
unknown's avatar
unknown committed
57

unknown's avatar
unknown committed
58 59
  - Server gets the long data in pieces with command type 'COM_LONG_DATA'.
  - The packet recieved will have the format as:
60 61 62
    [COM_LONG_DATA:1][STMT_ID:4][parameter_number:2][data]
  - data from the packet is appended to long data value buffer for this
    placeholder.
63
  - It's up to the client to check for read data ended. The server doesn't
unknown's avatar
unknown committed
64 65 66
    care; and also server doesn't notify to the client that it got the 
    data or not; if there is any error; then during execute; the error 
    will be returned
67

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

#include "mysql_priv.h"
#include "sql_acl.h"
unknown's avatar
unknown committed
72
#include "sql_select.h" // for JOIN
73
#include <m_ctype.h>  // for isspace()
74
#include "sp_head.h"
75 76 77 78
#ifdef EMBEDDED_LIBRARY
/* include MYSQL_BIND headers */
#include <mysql.h>
#endif
unknown's avatar
unknown committed
79

80 81 82
/******************************************************************************
  Prepared_statement: statement which can contain placeholders
******************************************************************************/
83

84 85 86 87
class Prepared_statement: public Statement
{
public:
  THD *thd;
88
  Item_param **param_array;
89 90 91
  uint param_count;
  uint last_errno;
  char last_error[MYSQL_ERRMSG_SIZE];
92
  bool get_longdata_error;
93
#ifndef EMBEDDED_LIBRARY
94 95
  bool (*set_params)(Prepared_statement *st, uchar *data, uchar *data_end,
                     uchar *read_pos);
unknown's avatar
unknown committed
96
#else
97
  bool (*set_params_data)(Prepared_statement *st);
unknown's avatar
unknown committed
98
#endif
99 100 101 102 103
public:
  Prepared_statement(THD *thd_arg);
  virtual ~Prepared_statement();
  virtual Statement::Type type() const;
};
unknown's avatar
unknown committed
104

105

106 107 108
/******************************************************************************
  Implementation
******************************************************************************/
109 110


111
inline bool is_param_null(const uchar *pos, ulong param_no)
112
{
113
  return pos[param_no/8] & (1 << (param_no & 7));
114 115
}

116
enum { STMT_QUERY_LOG_LENGTH= 8192 };
117

118
enum enum_send_error { DONT_SEND_ERROR= 0, SEND_ERROR };
119 120

/*
121 122
  Seek prepared statement in statement map by id: returns zero if statement
  was not found, pointer otherwise.
123 124
*/

125
static Prepared_statement *
126 127
find_prepared_statement(THD *thd, ulong id, const char *where,
                        enum enum_send_error se)
128 129 130 131 132 133
{
  Statement *stmt= thd->stmt_map.find(id);

  if (stmt == 0 || stmt->type() != Statement::PREPARED_STATEMENT)
  {
    my_error(ER_UNKNOWN_STMT_HANDLER, MYF(0), id, where);
134 135
    if (se == SEND_ERROR)
      send_error(thd);
136 137 138
    return 0;
  }
  return (Prepared_statement *) stmt;
139 140
}

141

142 143 144 145
/*
  Send prepared stmt info to client after prepare
*/

unknown's avatar
unknown committed
146
#ifndef EMBEDDED_LIBRARY
147
static bool send_prep_stmt(Prepared_statement *stmt, uint columns)
148
{
149
  NET *net= &stmt->thd->net;
150
  char buff[9];
151
  buff[0]= 0;                                   /* OK packet indicator */
152
  int4store(buff+1, stmt->id);
153 154
  int2store(buff+5, columns);
  int2store(buff+7, stmt->param_count);
155 156 157 158 159 160 161 162 163 164
  /*
    Send types and names of placeholders to the client
    XXX: fix this nasty upcast from List<Item_param> to List<Item>
  */
  return my_net_write(net, buff, sizeof(buff)) || 
         (stmt->param_count &&
          stmt->thd->protocol_simple.send_fields((List<Item> *)
                                                 &stmt->lex->param_list, 0)) ||
         net_flush(net);
  return 0;
unknown's avatar
unknown committed
165
}
166
#else
167 168
static bool send_prep_stmt(Prepared_statement *stmt,
                           uint columns __attribute__((unused)))
unknown's avatar
unknown committed
169
{
unknown's avatar
SCRUM  
unknown committed
170 171
  THD *thd= stmt->thd;

172
  thd->client_stmt_id= stmt->id;
unknown's avatar
SCRUM  
unknown committed
173
  thd->client_param_count= stmt->param_count;
unknown's avatar
unknown committed
174
  thd->net.last_errno= 0;
unknown's avatar
unknown committed
175

unknown's avatar
SCRUM  
unknown committed
176
  return 0;
177
}
unknown's avatar
unknown committed
178
#endif /*!EMBEDDED_LIBRARY*/
179

unknown's avatar
unknown committed
180 181

/*
182 183
  Read the length of the parameter data and return back to
  caller by positing the pointer to param data.
unknown's avatar
unknown committed
184 185
*/

unknown's avatar
unknown committed
186
#ifndef EMBEDDED_LIBRARY
187
static ulong get_param_length(uchar **packet, ulong len)
unknown's avatar
unknown committed
188 189
{
  reg1 uchar *pos= *packet;
190 191
  if (len < 1)
    return 0;
unknown's avatar
unknown committed
192 193 194 195 196
  if (*pos < 251)
  {
    (*packet)++;
    return (ulong) *pos;
  }
197 198
  if (len < 3)
    return 0;
unknown's avatar
unknown committed
199 200 201 202 203
  if (*pos == 252)
  {
    (*packet)+=3;
    return (ulong) uint2korr(pos+1);
  }
204 205
  if (len < 4)
    return 0;
unknown's avatar
unknown committed
206 207 208 209 210
  if (*pos == 253)
  {
    (*packet)+=4;
    return (ulong) uint3korr(pos+1);
  }
211 212
  if (len < 5)
    return 0;
unknown's avatar
unknown committed
213
  (*packet)+=9; // Must be 254 when here 
214
  /* TODO: why uint4korr here? (should be uint8korr) */
unknown's avatar
unknown committed
215 216
  return (ulong) uint4korr(pos+1);
}
unknown's avatar
unknown committed
217
#else
218
#define get_param_length(packet, len) len
unknown's avatar
unknown committed
219 220
#endif /*!EMBEDDED_LIBRARY*/

unknown's avatar
unknown committed
221
 /*
222 223 224 225 226 227
   Data conversion routines
   SYNOPSIS
   set_param_xx()
    param   parameter item
    pos     input data buffer
    len     length of data in the buffer
unknown's avatar
unknown committed
228

229 230
  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
231 232 233 234 235

  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.

236 237
  RETURN VALUE
    none
unknown's avatar
unknown committed
238 239
*/

240
static void set_param_tiny(Item_param *param, uchar **pos, ulong len)
unknown's avatar
unknown committed
241
{
242 243 244 245
#ifndef EMBEDDED_LIBRARY
  if (len < 1)
    return;
#endif
246 247
  int8 value= (int8) **pos;
  param->set_int(param->unsigned_flag ? (longlong) ((uint8) value) : 
248
                                        (longlong) value, 4);
unknown's avatar
unknown committed
249 250 251
  *pos+= 1;
}

252
static void set_param_short(Item_param *param, uchar **pos, ulong len)
unknown's avatar
unknown committed
253
{
254
  int16 value;
255 256 257
#ifndef EMBEDDED_LIBRARY
  if (len < 2)
    return;
258
  value= sint2korr(*pos);
259 260
#else
  shortget(value, *pos);
261
#endif
262
  param->set_int(param->unsigned_flag ? (longlong) ((uint16) value) :
263
                                        (longlong) value, 6);
unknown's avatar
unknown committed
264 265 266
  *pos+= 2;
}

267
static void set_param_int32(Item_param *param, uchar **pos, ulong len)
unknown's avatar
unknown committed
268
{
269
  int32 value;
270 271 272
#ifndef EMBEDDED_LIBRARY
  if (len < 4)
    return;
273
  value= sint4korr(*pos);
274 275
#else
  longget(value, *pos);
276
#endif
277
  param->set_int(param->unsigned_flag ? (longlong) ((uint32) value) :
278
                                        (longlong) value, 11);
unknown's avatar
unknown committed
279 280 281
  *pos+= 4;
}

282
static void set_param_int64(Item_param *param, uchar **pos, ulong len)
unknown's avatar
unknown committed
283
{
284
  longlong value;
285 286 287
#ifndef EMBEDDED_LIBRARY
  if (len < 8)
    return;
288
  value= (longlong) sint8korr(*pos);
289 290
#else
  longlongget(value, *pos);
291
#endif
292
  param->set_int(value, 21);
unknown's avatar
unknown committed
293 294 295
  *pos+= 8;
}

296
static void set_param_float(Item_param *param, uchar **pos, ulong len)
unknown's avatar
unknown committed
297
{
298 299 300 301
#ifndef EMBEDDED_LIBRARY
  if (len < 4)
    return;
#endif
unknown's avatar
unknown committed
302 303 304 305 306 307
  float data;
  float4get(data,*pos);
  param->set_double((double) data);
  *pos+= 4;
}

308
static void set_param_double(Item_param *param, uchar **pos, ulong len)
unknown's avatar
unknown committed
309
{
310 311 312 313
#ifndef EMBEDDED_LIBRARY
  if (len < 8)
    return;
#endif
unknown's avatar
unknown committed
314 315 316 317 318 319
  double data;
  float8get(data,*pos);
  param->set_double((double) data);
  *pos+= 8;
}

320
#ifndef EMBEDDED_LIBRARY
321
static void set_param_time(Item_param *param, uchar **pos, ulong len)
322 323
{
  ulong length;
324
  uint day;
325

326
  if ((length= get_param_length(pos, len)) >= 8)
327 328
  {
    uchar *to= *pos;
329
    TIME  tm;
330
    
331
    /* TODO: why length is compared with 8 here? */
332 333
    tm.second_part= (length > 8 ) ? (ulong) sint4korr(to+7): 0;

334 335 336 337 338 339 340 341
    /*
      Note, that though ranges of hour, minute and second are not checked
      here we rely on them being < 256: otherwise
      we'll get buffer overflow in make_{date,time} functions,
      which are called when time value is converted to string.
    */
    day= (uint) sint4korr(to+1);
    tm.hour=   (uint) to[5] + day * 24;
342 343
    tm.minute= (uint) to[6];
    tm.second= (uint) to[7];
344 345 346 347 348 349 350 351
    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;
352 353
    tm.neg= (bool)to[0];

354 355
    param->set_time(&tm, TIMESTAMP_TIME,
                    MAX_TIME_WIDTH * MY_CHARSET_BIN_MB_MAXLEN);
356 357 358 359
  }
  *pos+= length;
}

360
static void set_param_datetime(Item_param *param, uchar **pos, ulong len)
361
{
362
  uint length;
363
 
364
  if ((length= get_param_length(pos, len)) >= 4)
365 366 367 368 369 370
  {
    uchar *to= *pos;
    TIME  tm;
    
    tm.second_part= (length > 7 ) ? (ulong) sint4korr(to+7): 0;
    
371 372 373 374 375
    /*
      Note, that though ranges of hour, minute and second are not checked
      here we rely on them being < 256: otherwise
      we'll get buffer overflow in make_{date,time} functions.
    */
376 377 378 379 380 381 382 383 384 385 386 387 388 389
    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;
    
    tm.year=   (uint) sint2korr(to);
    tm.month=  (uint) to[2];
    tm.day=    (uint) to[3];
    tm.neg=    0;

390 391
    param->set_time(&tm, TIMESTAMP_DATETIME, 
                    MAX_DATETIME_WIDTH * MY_CHARSET_BIN_MB_MAXLEN);
392 393 394 395
  }
  *pos+= length;
}

396
static void set_param_date(Item_param *param, uchar **pos, ulong len)
397 398 399
{
  ulong length;
 
400
  if ((length= get_param_length(pos, len)) >= 4)
401 402 403
  {
    uchar *to= *pos;
    TIME tm;
404 405 406 407 408
    /*
      Note, that though ranges of hour, minute and second are not checked
      here we rely on them being < 256: otherwise
      we'll get buffer overflow in make_{date,time} functions.
    */
409
    tm.year=  (uint) sint2korr(to);
410 411 412 413 414 415 416
    tm.month=  (uint) to[2];
    tm.day= (uint) to[3];

    tm.hour= tm.minute= tm.second= 0;
    tm.second_part= 0;
    tm.neg= 0;

417 418
    param->set_time(&tm, TIMESTAMP_DATE,
                    MAX_DATE_WIDTH * MY_CHARSET_BIN_MB_MAXLEN);
419 420 421 422
  }
  *pos+= length;
}

423 424 425 426 427 428 429 430 431 432 433 434 435 436 437
#else/*!EMBEDDED_LIBRARY*/
void set_param_time(Item_param *param, uchar **pos, ulong len)
{
  TIME  tm;
  MYSQL_TIME *to= (MYSQL_TIME*)*pos;
    
  tm.second_part= to->second_part;

  tm.day=    to->day;
  tm.hour=   to->hour;
  tm.minute= to->minute;
  tm.second= to->second;

  tm.year= tm.month= 0;
  tm.neg= to->neg;
438 439
  param->set_time(&tm, TIMESTAMP_TIME,
                  MAX_TIME_WIDTH * MY_CHARSET_BIN_MB_MAXLEN);
440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457

}

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

  tm.second_part= to->second_part;

  tm.day=    to->day;
  tm.hour=   to->hour;
  tm.minute= to->minute;
  tm.second= to->second;
  tm.year=   to->year;
  tm.month=  to->month;
  tm.neg=    0;

458 459
  param->set_time(&tm, TIMESTAMP_DATETIME, 
                  MAX_DATETIME_WIDTH * MY_CHARSET_BIN_MB_MAXLEN);
460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476
}

void set_param_date(Item_param *param, uchar **pos, ulong len)
{
  TIME  tm;
  MYSQL_TIME *to= (MYSQL_TIME*)*pos;
    
  tm.second_part= to->second_part;

  tm.day=    to->day;
  tm.year=   to->year;
  tm.month=  to->month;
  tm.neg=    0;
  tm.hour= tm.minute= tm.second= 0;
  tm.second_part= 0;
  tm.neg= 0;

477 478
  param->set_time(&tm, TIMESTAMP_DATE, 
                  MAX_DATE_WIDTH * MY_CHARSET_BIN_MB_MAXLEN);
479 480 481
}
#endif /*!EMBEDDED_LIBRARY*/

482 483

static void set_param_str(Item_param *param, uchar **pos, ulong len)
unknown's avatar
unknown committed
484
{
485
  ulong length= get_param_length(pos, len);
486
  param->set_str((const char *)*pos, length);
487
  *pos+= length;
unknown's avatar
unknown committed
488 489
}

490 491 492 493 494

#undef get_param_length 

static void setup_one_conversion_function(THD *thd, Item_param *param,
                                          uchar param_type)
unknown's avatar
unknown committed
495
{
unknown's avatar
unknown committed
496
  switch (param_type) {
497
  case MYSQL_TYPE_TINY:
498
    param->set_param_func= set_param_tiny;
499
    param->item_type= Item::INT_ITEM;
500
    param->item_result_type= INT_RESULT;
unknown's avatar
unknown committed
501
    break;
502
  case MYSQL_TYPE_SHORT:
503
    param->set_param_func= set_param_short;
504
    param->item_type= Item::INT_ITEM;
505
    param->item_result_type= INT_RESULT;
unknown's avatar
unknown committed
506
    break;
507
  case MYSQL_TYPE_LONG:
508
    param->set_param_func= set_param_int32;
509
    param->item_type= Item::INT_ITEM;
510
    param->item_result_type= INT_RESULT;
unknown's avatar
unknown committed
511
    break;
512
  case MYSQL_TYPE_LONGLONG:
513
    param->set_param_func= set_param_int64;
514
    param->item_type= Item::INT_ITEM;
515
    param->item_result_type= INT_RESULT;
unknown's avatar
unknown committed
516
    break;
517
  case MYSQL_TYPE_FLOAT:
518
    param->set_param_func= set_param_float;
519
    param->item_type= Item::REAL_ITEM;
520
    param->item_result_type= REAL_RESULT;
unknown's avatar
unknown committed
521
    break;
522
  case MYSQL_TYPE_DOUBLE:
523
    param->set_param_func= set_param_double;
524
    param->item_type= Item::REAL_ITEM;
525
    param->item_result_type= REAL_RESULT;
unknown's avatar
unknown committed
526
    break;
527
  case MYSQL_TYPE_TIME:
528
    param->set_param_func= set_param_time;
529
    param->item_type= Item::STRING_ITEM;
530
    param->item_result_type= STRING_RESULT;
531
    break;
532
  case MYSQL_TYPE_DATE:
533
    param->set_param_func= set_param_date;
534
    param->item_type= Item::STRING_ITEM;
535
    param->item_result_type= STRING_RESULT;
536
    break;
537 538
  case MYSQL_TYPE_DATETIME:
  case MYSQL_TYPE_TIMESTAMP:
539
    param->set_param_func= set_param_datetime;
540
    param->item_type= Item::STRING_ITEM;
541
    param->item_result_type= STRING_RESULT;
542
    break;
543 544 545 546
  case MYSQL_TYPE_TINY_BLOB:
  case MYSQL_TYPE_MEDIUM_BLOB:
  case MYSQL_TYPE_LONG_BLOB:
  case MYSQL_TYPE_BLOB:
547
    param->set_param_func= set_param_str;
548 549 550
    param->value.cs_info.character_set_client= &my_charset_bin;
    param->value.cs_info.final_character_set_of_str_value= &my_charset_bin;
    param->item_type= Item::STRING_ITEM;
551
    param->item_result_type= STRING_RESULT;
552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581
    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;

      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
582 583 584
  }
}

unknown's avatar
unknown committed
585
#ifndef EMBEDDED_LIBRARY
unknown's avatar
unknown committed
586
/*
587 588
  Update the parameter markers by reading data from client packet 
  and if binary/update log is set, generate the valid query.
unknown's avatar
unknown committed
589 590
*/

591 592
static bool insert_params_withlog(Prepared_statement *stmt, uchar *null_array,
                                  uchar *read_pos, uchar *data_end)
593
{
594 595 596 597 598
  THD  *thd= stmt->thd;
  Item_param **begin= stmt->param_array;
  Item_param **end= begin + stmt->param_count;
  uint32 length= 0;

599 600
  String str, query;
  const String *res;
601

602
  DBUG_ENTER("insert_params_withlog"); 
603

604
  if (query.copy(stmt->query, stmt->query_length, default_charset_info))
605
    DBUG_RETURN(1);
606
  
607
  for (Item_param **it= begin; it < end; ++it)
608
  {
609
    Item_param *param= *it;
610
    if (param->state != Item_param::LONG_DATA_VALUE)
611
    {
612
      if (is_param_null(null_array, it - begin))
613
        param->set_null();
614 615
      else
      {
616 617 618
        if (read_pos >= data_end)
          DBUG_RETURN(1);
        param->set_param_func(param, &read_pos, data_end - read_pos);
619 620
      }
    }
621 622 623 624
    res= param->query_val_str(&str);
    if (param->convert_str_value(thd))
      DBUG_RETURN(1);                           /* out of memory */

625
    if (query.replace(param->pos_in_query+length, 1, *res))
626 627 628 629
      DBUG_RETURN(1);
    
    length+= res->length()-1;
  }
630
  if (alloc_query(thd, (char *)query.ptr(), query.length()+1))
631
    DBUG_RETURN(1);
632

633 634 635
  DBUG_RETURN(0);
}

636

637 638
static bool insert_params(Prepared_statement *stmt, uchar *null_array,
                          uchar *read_pos, uchar *data_end)
639
{
640 641
  Item_param **begin= stmt->param_array;
  Item_param **end= begin + stmt->param_count;
642 643 644

  DBUG_ENTER("insert_params"); 

645
  for (Item_param **it= begin; it < end; ++it)
646
  {
647
    Item_param *param= *it;
648
    if (param->state != Item_param::LONG_DATA_VALUE)
649
    {
650
      if (is_param_null(null_array, it - begin))
651
        param->set_null();
652 653
      else
      {
654 655 656
        if (read_pos >= data_end)
          DBUG_RETURN(1);
        param->set_param_func(param, &read_pos, data_end - read_pos);
657 658
      }
    }
659 660
    if (param->convert_str_value(stmt->thd))
      DBUG_RETURN(1);                           /* out of memory */
661 662 663 664
  }
  DBUG_RETURN(0);
}

665

666
static bool setup_conversion_functions(Prepared_statement *stmt,
667
                                       uchar **data, uchar *data_end)
668 669 670
{
  /* skip null bits */
  uchar *read_pos= *data + (stmt->param_count+7) / 8;
unknown's avatar
unknown committed
671

672
  DBUG_ENTER("setup_conversion_functions");
673

unknown's avatar
unknown committed
674
  if (*read_pos++) //types supplied / first execute
675
  {
unknown's avatar
unknown committed
676 677 678 679
    /*
      First execute or types altered by the client, setup the 
      conversion routines for all parameters (one time)
    */
680 681
    Item_param **it= stmt->param_array;
    Item_param **end= it + stmt->param_count;
682
    THD *thd= stmt->thd;
683 684
    for (; it < end; ++it)
    {
685 686 687
      ushort typecode;
      const uint signed_bit= 1 << 15;

688 689
      if (read_pos >= data_end)
        DBUG_RETURN(1);
690 691

      typecode= sint2korr(read_pos);
unknown's avatar
unknown committed
692
      read_pos+= 2;
693
      (**it).unsigned_flag= test(typecode & signed_bit);
694
      setup_one_conversion_function(thd, *it, (uchar) (typecode & ~signed_bit));
unknown's avatar
unknown committed
695
    }
696 697
  }
  *data= read_pos;
unknown's avatar
unknown committed
698 699 700
  DBUG_RETURN(0);
}

701 702
#else

703 704
static bool emb_insert_params(Prepared_statement *stmt)
{
705
  THD *thd= stmt->thd;
706 707
  Item_param **it= stmt->param_array;
  Item_param **end= it + stmt->param_count;
708 709
  MYSQL_BIND *client_param= stmt->thd->client_params;

710
  DBUG_ENTER("emb_insert_params");
711

712 713 714
  for (; it < end; ++it, ++client_param)
  {
    Item_param *param= *it;
715 716
    setup_one_conversion_function(thd, param, client_param->buffer_type);
    if (param->state != Item_param::LONG_DATA_VALUE)
717 718
    {
      if (*client_param->is_null)
719
        param->set_null();
720 721
      else
      {
722
        uchar *buff= (uchar*) client_param->buffer;
723 724 725 726
        param->set_param_func(param, &buff,
                              client_param->length ? 
                              *client_param->length : 
                              client_param->buffer_length);
727 728
      }
    }
729 730
    if (param->convert_str_value(thd))
      DBUG_RETURN(1);                           /* out of memory */
731 732 733 734
  }
  DBUG_RETURN(0);
}

735 736 737

static bool emb_insert_params_withlog(Prepared_statement *stmt)
{
738
  THD *thd= stmt->thd;
739 740
  Item_param **it= stmt->param_array;
  Item_param **end= it + stmt->param_count;
741 742 743 744
  MYSQL_BIND *client_param= thd->client_params;

  String str, query;
  const String *res;
745
  uint32 length= 0;
746

747
  DBUG_ENTER("emb_insert_params_withlog");
748 749 750 751

  if (query.copy(stmt->query, stmt->query_length, default_charset_info))
    DBUG_RETURN(1);
  
752 753 754
  for (; it < end; ++it, ++client_param)
  {
    Item_param *param= *it;
755 756
    setup_one_conversion_function(thd, param, client_param->buffer_type);
    if (param->state != Item_param::LONG_DATA_VALUE)
757 758
    {
      if (*client_param->is_null)
759
        param->set_null();
760 761
      else
      {
762
        uchar *buff= (uchar*)client_param->buffer;
763 764 765 766
        param->set_param_func(param, &buff,
                              client_param->length ? 
                              *client_param->length : 
                              client_param->buffer_length);
767
      }
768 769 770
      res= param->query_val_str(&str);
      if (param->convert_str_value(thd))
        DBUG_RETURN(1);                         /* out of memory */
771 772 773 774 775 776 777 778 779 780 781 782
    }
    if (query.replace(param->pos_in_query+length, 1, *res))
      DBUG_RETURN(1);
    length+= res->length()-1;
  }
  
  if (alloc_query(thd, (char *) query.ptr(), query.length()+1))
    DBUG_RETURN(1);

  DBUG_RETURN(0);
}

unknown's avatar
unknown committed
783 784
#endif /*!EMBEDDED_LIBRARY*/

unknown's avatar
unknown committed
785

unknown's avatar
unknown committed
786
/*
unknown's avatar
unknown committed
787 788
  Validate INSERT statement: 

789
  SYNOPSIS
unknown's avatar
unknown committed
790 791 792 793
    mysql_test_insert()
    stmt	prepared statemen handler
    tables	list of tables queries  

794 795 796 797
  RETURN VALUE
    0   ok
    1   error, sent to the client
   -1   error, not sent to client
unknown's avatar
unknown committed
798
*/
unknown's avatar
unknown committed
799 800 801 802 803 804 805
static int mysql_test_insert(Prepared_statement *stmt,
			     TABLE_LIST *table_list,
			     List<Item> &fields, 
			     List<List_item> &values_list,
			     List<Item> &update_fields,
			     List<Item> &update_values,
			     enum_duplicates duplic)
unknown's avatar
unknown committed
806
{
807
  THD *thd= stmt->thd;
808
  LEX *lex= stmt->lex;
unknown's avatar
unknown committed
809 810
  List_iterator_fast<List_item> its(values_list);
  List_item *values;
unknown's avatar
unknown committed
811
  int res= -1;
812 813
  TABLE_LIST *insert_table_list=
    (TABLE_LIST*) lex->select_lex.table_list.first;
unknown's avatar
unknown committed
814
  my_bool update= (lex->value_list.elements ? UPDATE_ACL : 0);
815
  DBUG_ENTER("mysql_test_insert");
unknown's avatar
unknown committed
816

unknown's avatar
unknown committed
817 818
  if ((res= insert_precheck(thd, table_list, update)))
    DBUG_RETURN(res);
unknown's avatar
unknown committed
819

820
  /*
unknown's avatar
unknown committed
821 822 823
     open temporary memory pool for temporary data allocated by derived
     tables & preparation procedure
  */
unknown's avatar
unknown committed
824
  thd->allocate_temporary_memory_pool_for_ps_preparing();
unknown's avatar
unknown committed
825
  if (open_and_lock_tables(thd, table_list))
826
  {
unknown's avatar
unknown committed
827
    thd->free_temporary_memory_pool_for_ps_preparing();
828
    DBUG_RETURN(-1);
829 830
  }

unknown's avatar
unknown committed
831 832 833
  if ((values= its++))
  {
    uint value_count;
834
    ulong counter= 0;
unknown's avatar
unknown committed
835

unknown's avatar
unknown committed
836 837 838 839 840
    if ((res= mysql_prepare_insert(thd, table_list, insert_table_list,
				   table_list->table, fields, values,
				   update_fields, update_values, duplic)))
      goto error;
    
unknown's avatar
unknown committed
841 842 843
    value_count= values->elements;
    its.rewind();
   
844
    while ((values= its++))
unknown's avatar
unknown committed
845 846 847 848 849 850
    {
      counter++;
      if (values->elements != value_count)
      {
        my_printf_error(ER_WRONG_VALUE_COUNT_ON_ROW,
			ER(ER_WRONG_VALUE_COUNT_ON_ROW),
851
			MYF(0), counter);
852
        goto error;
unknown's avatar
unknown committed
853
      }
854 855
      if (setup_fields(thd, 0, insert_table_list, *values, 0, 0, 0))
	goto error;
unknown's avatar
unknown committed
856 857
    }
  }
858

unknown's avatar
unknown committed
859
  res= 0;
860 861 862
error:
  lex->unit.cleanup();
  thd->free_temporary_memory_pool_for_ps_preparing();
unknown's avatar
unknown committed
863
  DBUG_RETURN(res);
unknown's avatar
unknown committed
864 865 866 867
}


/*
unknown's avatar
unknown committed
868 869
  Validate UPDATE statement

870
  SYNOPSIS
871
    mysql_test_update()
unknown's avatar
unknown committed
872 873 874
    stmt	prepared statemen handler
    tables	list of tables queries

875 876 877 878
  RETURN VALUE
    0   success
    1   error, sent to client
   -1   error, not sent to client
unknown's avatar
unknown committed
879
*/
unknown's avatar
unknown committed
880 881
static int mysql_test_update(Prepared_statement *stmt,
			     TABLE_LIST *table_list)
unknown's avatar
unknown committed
882
{
unknown's avatar
unknown committed
883
  int res;
884
  THD *thd= stmt->thd;
unknown's avatar
unknown committed
885 886 887 888 889
  SELECT_LEX *select= &stmt->lex->select_lex;
  DBUG_ENTER("mysql_test_update");

  if ((res= update_precheck(thd, table_list)))
    DBUG_RETURN(res);
unknown's avatar
unknown committed
890

891
  /*
unknown's avatar
unknown committed
892 893 894
     open temporary memory pool for temporary data allocated by derived
     tables & preparation procedure
  */
unknown's avatar
unknown committed
895
  thd->allocate_temporary_memory_pool_for_ps_preparing();
896

unknown's avatar
unknown committed
897
  if (open_and_lock_tables(thd, table_list))
unknown's avatar
unknown committed
898
    res= -1;
899 900
  else
  {
unknown's avatar
unknown committed
901 902 903 904 905 906 907 908 909 910 911 912 913
    TABLE_LIST *update_table_list= (TABLE_LIST *)select->table_list.first;
    if (!(res= mysql_prepare_update(thd, table_list,
				    update_table_list,
				    &select->where,
				    select->order_list.elements,
				    (ORDER *) select->order_list.first)))
    {
      if (setup_fields(thd, 0, update_table_list,
		       select->item_list, 1, 0, 0) ||
	  setup_fields(thd, 0, update_table_list,
		       stmt->lex->value_list, 0, 0, 0))
	res= -1;
    }
914
    stmt->lex->unit.cleanup();
915
  }
unknown's avatar
unknown committed
916
  thd->free_temporary_memory_pool_for_ps_preparing();
unknown's avatar
unknown committed
917 918
  /* TODO: here we should send types of placeholders to the client. */ 
  DBUG_RETURN(res);
unknown's avatar
unknown committed
919 920 921 922
}


/*
unknown's avatar
unknown committed
923 924
  Validate DELETE statement

925
  SYNOPSIS
unknown's avatar
unknown committed
926 927 928 929
    mysql_test_delete()
    stmt	prepared statemen handler
    tables	list of tables queries

930 931 932 933
  RETURN VALUE
    0   success
    1   error, sent to client
   -1   error, not sent to client
unknown's avatar
unknown committed
934
*/
unknown's avatar
unknown committed
935 936
static int mysql_test_delete(Prepared_statement *stmt,
			     TABLE_LIST *table_list)
unknown's avatar
unknown committed
937
{
unknown's avatar
unknown committed
938
  int res;
939
  THD *thd= stmt->thd;
unknown's avatar
unknown committed
940 941
  LEX *lex= stmt->lex;
  DBUG_ENTER("mysql_test_delete");
unknown's avatar
unknown committed
942

unknown's avatar
unknown committed
943 944
  if ((res= delete_precheck(thd, table_list)))
    DBUG_RETURN(res);
unknown's avatar
unknown committed
945

unknown's avatar
unknown committed
946
  /*
unknown's avatar
unknown committed
947 948
     open temporary memory pool for temporary data allocated by derived
     tables & preparation procedure
unknown's avatar
unknown committed
949
  */
unknown's avatar
unknown committed
950
  thd->allocate_temporary_memory_pool_for_ps_preparing();
951

unknown's avatar
unknown committed
952
  if (open_and_lock_tables(thd, table_list))
unknown's avatar
unknown committed
953 954 955 956 957 958
    res= -1;
  else
  {
    res= mysql_prepare_delete(thd, table_list, &lex->select_lex.where);
    lex->unit.cleanup();
  }
unknown's avatar
unknown committed
959
  thd->free_temporary_memory_pool_for_ps_preparing();
unknown's avatar
unknown committed
960 961
  /* TODO: here we should send types of placeholders to the client. */ 
  DBUG_RETURN(res);
unknown's avatar
unknown committed
962 963
}

unknown's avatar
unknown committed
964

unknown's avatar
unknown committed
965
/*
unknown's avatar
unknown committed
966
  Validate SELECT statement.
967 968
  In case of success, if this query is not EXPLAIN, send column list info
  back to client. 
unknown's avatar
unknown committed
969

970
  SYNOPSIS
unknown's avatar
unknown committed
971 972 973 974
    mysql_test_select()
    stmt	prepared statemen handler
    tables	list of tables queries

975 976 977 978
  RETURN VALUE
    0   success
    1   error, sent to client
   -1   error, not sent to client
unknown's avatar
unknown committed
979
*/
980

unknown's avatar
unknown committed
981 982
static int mysql_test_select(Prepared_statement *stmt,
			     TABLE_LIST *tables)
unknown's avatar
unknown committed
983
{
984
  THD *thd= stmt->thd;
985
  LEX *lex= stmt->lex;
986
  SELECT_LEX_UNIT *unit= &lex->unit;
987

unknown's avatar
unknown committed
988
  DBUG_ENTER("mysql_test_select");
unknown's avatar
unknown committed
989

unknown's avatar
unknown committed
990
#ifndef NO_EMBEDDED_ACCESS_CHECKS
991 992 993
  ulong privilege= lex->exchange ? SELECT_ACL | FILE_ACL : SELECT_ACL;
  if (tables)
  {
unknown's avatar
unknown committed
994
    if (check_table_access(thd, privilege, tables,0))
995 996
      DBUG_RETURN(1);
  }
unknown's avatar
unknown committed
997
  else if (check_access(thd, privilege, any_db,0,0,0))
998
    DBUG_RETURN(1);
unknown's avatar
unknown committed
999
#endif
unknown's avatar
unknown committed
1000

1001
  /*
unknown's avatar
unknown committed
1002 1003 1004
     open temporary memory pool for temporary data allocated by derived
     tables & preparation procedure
  */
unknown's avatar
unknown committed
1005
  thd->allocate_temporary_memory_pool_for_ps_preparing();
unknown's avatar
unknown committed
1006
  if (open_and_lock_tables(thd, tables))
1007 1008
  {
    send_error(thd);
unknown's avatar
unknown committed
1009
    goto err;
1010
  }
unknown's avatar
unknown committed
1011

1012
  if (lex->describe)
unknown's avatar
unknown committed
1013
  {
1014
    if (send_prep_stmt(stmt, 0))
1015
      goto err;
1016
  }
1017
  else
1018
  {
1019
    thd->used_tables= 0;                        // Updated by setup_fields
unknown's avatar
unknown committed
1020

unknown's avatar
unknown committed
1021
    // JOIN::prepare calls
1022
    if (unit->prepare(thd, 0, 0))
1023 1024
    {
      send_error(thd);
1025
      goto err_prep;
1026
    }
unknown's avatar
unknown committed
1027

unknown's avatar
unknown committed
1028 1029
    if (send_prep_stmt(stmt, lex->select_lex.item_list.elements) ||
        thd->protocol_simple.send_fields(&lex->select_lex.item_list, 0)
unknown's avatar
SCRUM  
unknown committed
1030
#ifndef EMBEDDED_LIBRARY
1031
        || net_flush(&thd->net)
unknown's avatar
SCRUM  
unknown committed
1032
#endif
1033
       )
1034 1035
      goto err_prep;

unknown's avatar
unknown committed
1036
    unit->cleanup();
1037
  }
unknown's avatar
unknown committed
1038
  thd->free_temporary_memory_pool_for_ps_preparing();
1039 1040 1041 1042 1043
  DBUG_RETURN(0);

err_prep:
  unit->cleanup();
err:
unknown's avatar
unknown committed
1044
  thd->free_temporary_memory_pool_for_ps_preparing();
1045
  DBUG_RETURN(1);
unknown's avatar
unknown committed
1046 1047
}

1048

1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134
/*
  Validate and prepare for execution DO statement expressions

  SYNOPSIS
    mysql_test_do_fields()
    stmt	prepared statemen handler
    tables	list of tables queries
    values	list of expressions

  RETURN VALUE
    0   success
    1   error, sent to client
   -1   error, not sent to client
*/

static int mysql_test_do_fields(Prepared_statement *stmt,
				TABLE_LIST *tables,
				List<Item> *values)
{
  DBUG_ENTER("mysql_test_do_fields");
  THD *thd= stmt->thd;
  int res= 0;
  if (tables && (res= check_table_access(thd, SELECT_ACL, tables, 0)))
    DBUG_RETURN(res);
  /*
    open temporary memory pool for temporary data allocated by derived
    tables & preparation procedure
  */
  thd->allocate_temporary_memory_pool_for_ps_preparing();
  if (tables && (res= open_and_lock_tables(thd, tables)))
  {
    thd->free_temporary_memory_pool_for_ps_preparing();
    DBUG_RETURN(res);
  }
  res= setup_fields(thd, 0, 0, *values, 0, 0, 0);
  stmt->lex->unit.cleanup();
  thd->free_temporary_memory_pool_for_ps_preparing();
  if (res)
    DBUG_RETURN(-1);
  DBUG_RETURN(0);
}


/*
  Validate and prepare for execution SET statement expressions

  SYNOPSIS
    mysql_test_set_fields()
    stmt	prepared statemen handler
    tables	list of tables queries
    values	list of expressions

  RETURN VALUE
    0   success
    1   error, sent to client
   -1   error, not sent to client
*/
static int mysql_test_set_fields(Prepared_statement *stmt,
				TABLE_LIST *tables,
				List<set_var_base> *var_list)
{
  DBUG_ENTER("mysql_test_set_fields");
  List_iterator_fast<set_var_base> it(*var_list);
  THD *thd= stmt->thd;
  set_var_base *var;
  int res= 0;

  if (tables && (res= check_table_access(thd, SELECT_ACL, tables, 0)))
    DBUG_RETURN(res);
  /*
    open temporary memory pool for temporary data allocated by derived
    tables & preparation procedure
  */
  thd->allocate_temporary_memory_pool_for_ps_preparing();
  if (tables && (res= open_and_lock_tables(thd, tables)))
    goto error;
  while ((var= it++))
  {
    if (var->light_check(thd))
    {
      stmt->lex->unit.cleanup();
      res= -1;
      goto error;
    }
  }
error:
unknown's avatar
unknown committed
1135
  stmt->lex->unit.cleanup();
1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148
  thd->free_temporary_memory_pool_for_ps_preparing();
  DBUG_RETURN(res);
}


/*
  Check internal SELECT of the prepared command

  SYNOPSIS
    select_like_statement_test()
      stmt	- prepared table handler
      tables	- global list of tables

unknown's avatar
unknown committed
1149
  RETURN VALUE
1150 1151
    0   success
    1   error, sent to client
unknown's avatar
unknown committed
1152
   -1   error, not sent to client
1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170
*/
static int select_like_statement_test(Prepared_statement *stmt,
				      TABLE_LIST *tables)
{
  DBUG_ENTER("select_like_statement_test");
  THD *thd= stmt->thd;
  LEX *lex= stmt->lex;
  int res= 0;
  /*
    open temporary memory pool for temporary data allocated by derived
    tables & preparation procedure
  */
  thd->allocate_temporary_memory_pool_for_ps_preparing();
  if (tables && (res= open_and_lock_tables(thd, tables)))
    goto end;

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

unknown's avatar
unknown committed
1171
  // JOIN::prepare calls
1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183
  if (lex->unit.prepare(thd, 0, 0))
  {
    res= thd->net.report_error ? -1 : 1;
  }
end:
  lex->unit.cleanup();
  thd->free_temporary_memory_pool_for_ps_preparing();
  DBUG_RETURN(res);
}


/*
1184
  Validate and prepare for execution CREATE TABLE statement
1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206

  SYNOPSIS
    mysql_test_create_table()
    stmt	prepared statemen handler
    tables	list of tables queries

  RETURN VALUE
    0   success
    1   error, sent to client
   -1   error, not sent to client
*/
static int mysql_test_create_table(Prepared_statement *stmt,
				   TABLE_LIST *tables)
{
  DBUG_ENTER("mysql_test_create_table");
  THD *thd= stmt->thd;
  LEX *lex= stmt->lex;
  int res= 0;

  /* Skip first table, which is the table we are creating */
  TABLE_LIST *create_table, *create_table_local;
  tables= lex->unlink_first_table(tables, &create_table,
unknown's avatar
unknown committed
1207
				  &create_table_local);
1208

unknown's avatar
unknown committed
1209 1210
  if (!(res= create_table_precheck(thd, tables, create_table)) &&
      lex->select_lex.item_list.elements)
1211 1212
    res= select_like_statement_test(stmt, tables);

unknown's avatar
unknown committed
1213
  /* put tables back for PS rexecuting */
1214 1215 1216 1217 1218
  tables= lex->link_first_table_back(tables, create_table,
				     create_table_local);
  DBUG_RETURN(res);
}

unknown's avatar
unknown committed
1219

1220
/*
1221
  Validate and prepare for execution multi update statement
1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243

  SYNOPSIS
    mysql_test_multiupdate()
    stmt	prepared statemen handler
    tables	list of tables queries

  RETURN VALUE
    0   success
    1   error, sent to client
   -1   error, not sent to client
*/
static int mysql_test_multiupdate(Prepared_statement *stmt,
				  TABLE_LIST *tables)
{
  int res;
  if ((res= multi_update_precheck(stmt->thd, tables)))
    return res;
  return select_like_statement_test(stmt, tables);
}


/*
1244
  Validate and prepare for execution multi delete statement
1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263

  SYNOPSIS
    mysql_test_multidelete()
    stmt	prepared statemen handler
    tables	list of tables queries

  RETURN VALUE
    0   success
    1   error, sent to client
   -1   error, not sent to client
*/
static int mysql_test_multidelete(Prepared_statement *stmt,
				  TABLE_LIST *tables)
{
  int res;
  stmt->thd->lex->current_select= &stmt->thd->lex->select_lex;
  if (add_item_to_list(stmt->thd, new Item_null()))
    return -1;

unknown's avatar
unknown committed
1264
  uint fake_counter;
1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293
  if ((res= multi_delete_precheck(stmt->thd, tables, &fake_counter)))
    return res;
  return select_like_statement_test(stmt, tables);
}


/*
  Validate and prepare for execution INSERT ... SELECT statement

  SYNOPSIS
    mysql_test_insert_select()
    stmt	prepared statemen handler
    tables	list of tables queries

  RETURN VALUE
    0   success
    1   error, sent to client
   -1   error, not sent to client
*/
static int mysql_test_insert_select(Prepared_statement *stmt,
				    TABLE_LIST *tables)
{
  int res;
  LEX *lex= stmt->lex;
  if ((res= insert_select_precheck(stmt->thd, tables)))
    return res;
  TABLE_LIST *first_local_table=
    (TABLE_LIST *)lex->select_lex.table_list.first;
  /* Skip first table, which is the table we are inserting in */
unknown's avatar
unknown committed
1294
  lex->select_lex.table_list.first= (byte*) first_local_table->next;
1295 1296 1297
  lex->select_lex.resolve_mode= SELECT_LEX::NOMATTER_MODE;
  res= select_like_statement_test(stmt, tables);
  /* revert changes*/
unknown's avatar
unknown committed
1298
  lex->select_lex.table_list.first= (byte*) first_local_table;
1299 1300 1301 1302 1303
  lex->select_lex.resolve_mode= SELECT_LEX::INSERT_MODE;
  return res;
}


unknown's avatar
unknown committed
1304
/*
1305 1306 1307 1308 1309 1310 1311
  Send the prepare query results back to client
  SYNOPSIS
  send_prepare_results()
    stmt prepared statement
  RETURN VALUE
    0   success
    1   error, sent to client
unknown's avatar
unknown committed
1312
*/
1313
static int send_prepare_results(Prepared_statement *stmt)
unknown's avatar
unknown committed
1314
{   
1315
  THD *thd= stmt->thd;
1316
  LEX *lex= stmt->lex;
1317 1318
  SELECT_LEX *select_lex= &lex->select_lex;
  TABLE_LIST *tables=(TABLE_LIST*) select_lex->table_list.first;
1319
  enum enum_sql_command sql_command= lex->sql_command;
unknown's avatar
unknown committed
1320
  int res= 0;
1321
  DBUG_ENTER("send_prepare_results");
unknown's avatar
unknown committed
1322

1323
  DBUG_PRINT("enter",("command: %d, param_count: %ld",
1324
                      sql_command, stmt->param_count));
1325

unknown's avatar
unknown committed
1326
  if (select_lex != lex->all_selects_list &&
1327 1328 1329
      lex->unit.create_total_list(thd, lex, &tables))
    DBUG_RETURN(1);

unknown's avatar
unknown committed
1330
  
1331
  switch (sql_command) {
1332
  case SQLCOM_REPLACE:
unknown's avatar
unknown committed
1333
  case SQLCOM_INSERT:
unknown's avatar
unknown committed
1334 1335 1336 1337 1338
    res= mysql_test_insert(stmt, tables, lex->field_list,
			   lex->many_values,
			   select_lex->item_list, lex->value_list,
			   (lex->value_list.elements ?
			    DUP_UPDATE : lex->duplicates));
unknown's avatar
unknown committed
1339 1340 1341
    break;

  case SQLCOM_UPDATE:
unknown's avatar
unknown committed
1342 1343 1344
    res= mysql_test_update(stmt, tables);
    break;

unknown's avatar
unknown committed
1345
  case SQLCOM_DELETE:
unknown's avatar
unknown committed
1346
    res= mysql_test_delete(stmt, tables);
unknown's avatar
unknown committed
1347 1348 1349
    break;

  case SQLCOM_SELECT:
unknown's avatar
unknown committed
1350
    if ((res= mysql_test_select(stmt, tables)))
1351 1352 1353
      goto error;
    /* Statement and field info has already been sent */
    DBUG_RETURN(0);
unknown's avatar
unknown committed
1354

1355
  case SQLCOM_CREATE_TABLE:
unknown's avatar
unknown committed
1356
    res= mysql_test_create_table(stmt, tables);
1357 1358 1359
    break;
  
  case SQLCOM_DO:
unknown's avatar
unknown committed
1360 1361
    res= mysql_test_do_fields(stmt, tables, lex->insert_list);
    break;
1362 1363

  case SQLCOM_SET_OPTION:
unknown's avatar
unknown committed
1364
    res= mysql_test_set_fields(stmt, tables, &lex->var_list);
1365 1366 1367
    break;

  case SQLCOM_DELETE_MULTI:
unknown's avatar
unknown committed
1368
    res= mysql_test_multidelete(stmt, tables);
1369 1370 1371
    break;
  
  case SQLCOM_UPDATE_MULTI:
unknown's avatar
unknown committed
1372
    res= mysql_test_multiupdate(stmt, tables);
1373 1374 1375
    break;

  case SQLCOM_INSERT_SELECT:
unknown's avatar
unknown committed
1376
    res= mysql_test_insert_select(stmt, tables);
1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398
    break;

  case SQLCOM_SHOW_DATABASES:
  case SQLCOM_SHOW_PROCESSLIST:
  case SQLCOM_SHOW_STORAGE_ENGINES:
  case SQLCOM_SHOW_PRIVILEGES:
  case SQLCOM_SHOW_COLUMN_TYPES:
  case SQLCOM_SHOW_STATUS:
  case SQLCOM_SHOW_VARIABLES:
  case SQLCOM_SHOW_LOGS:
  case SQLCOM_SHOW_TABLES:
  case SQLCOM_SHOW_OPEN_TABLES:
  case SQLCOM_SHOW_CHARSETS:
  case SQLCOM_SHOW_COLLATIONS:
  case SQLCOM_SHOW_FIELDS:
  case SQLCOM_SHOW_KEYS:
  case SQLCOM_SHOW_CREATE_DB:
  case SQLCOM_SHOW_GRANTS:
  case SQLCOM_DROP_TABLE:
  case SQLCOM_RENAME_TABLE:
    break;

unknown's avatar
unknown committed
1399
  default:
1400 1401
    /*
      All other is not supported yet
1402
    */
1403 1404 1405
    res= -1;
    my_error(ER_UNSUPPORTED_PS, MYF(0));
    goto error;
unknown's avatar
unknown committed
1406
  }
unknown's avatar
unknown committed
1407 1408
  if (res == 0)
    DBUG_RETURN(send_prep_stmt(stmt, 0));
1409 1410
error:
  if (res < 0)
unknown's avatar
unknown committed
1411
    send_error(thd,thd->killed_errno());
1412
  DBUG_RETURN(1);
unknown's avatar
unknown committed
1413 1414
}

unknown's avatar
unknown committed
1415
/*
1416 1417
  Initialize array of parameters in statement from LEX.
  (We need to have quick access to items by number in mysql_stmt_get_longdata).
1418
  This is to avoid using malloc/realloc in the parser.
unknown's avatar
unknown committed
1419
*/
unknown's avatar
unknown committed
1420

1421
static bool init_param_array(Prepared_statement *stmt)
unknown's avatar
unknown committed
1422
{
1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434
  LEX *lex= stmt->lex;
  if ((stmt->param_count= lex->param_list.elements))
  {
    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 **)
                       alloc_root(&stmt->thd->mem_root,
                                  sizeof(Item_param*) * stmt->param_count);
    if (!stmt->param_array)
    {
      send_error(stmt->thd, ER_OUT_OF_RESOURCES);
1435
      return 1;
1436 1437 1438 1439 1440 1441 1442 1443
    }
    for (to= stmt->param_array;
         to < stmt->param_array + stmt->param_count;
         ++to)
    {
      *to= param_iterator++;
    }
  }
unknown's avatar
unknown committed
1444
  return 0;
unknown's avatar
unknown committed
1445
}
1446

1447

unknown's avatar
unknown committed
1448 1449 1450
/*
  Parse the query and send the total number of parameters 
  and resultset metadata information back to client (if any), 
1451
  without executing the query i.e. without any log/disk 
unknown's avatar
unknown committed
1452
  writes. This will allow the queries to be re-executed 
1453 1454 1455 1456 1457 1458 1459
  without re-parsing during execute. 

  If parameter markers are found in the query, then store 
  the information 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 field
  items.
unknown's avatar
unknown committed
1460 1461
*/

1462
void mysql_stmt_prepare(THD *thd, char *packet, uint packet_length)
unknown's avatar
unknown committed
1463
{
1464 1465
  LEX *lex;
  Prepared_statement *stmt= new Prepared_statement(thd);
1466
  int error;
1467
  DBUG_ENTER("mysql_stmt_prepare");
unknown's avatar
unknown committed
1468

unknown's avatar
unknown committed
1469
  DBUG_PRINT("prep_query", ("%s", packet));
unknown's avatar
unknown committed
1470

1471
  if (stmt == 0)
1472 1473 1474 1475
  {
    send_error(thd, ER_OUT_OF_RESOURCES);
    DBUG_VOID_RETURN;
  }
1476

1477
  if (thd->stmt_map.insert(stmt))
1478 1479 1480 1481 1482
  {
    delete stmt;
    send_error(thd, ER_OUT_OF_RESOURCES);
    DBUG_VOID_RETURN;
  }
unknown's avatar
unknown committed
1483

1484
  thd->stmt_backup.set_statement(thd);
1485
  thd->stmt_backup.set_item_arena(thd);
1486
  thd->set_statement(stmt);
1487
  thd->set_item_arena(stmt);
unknown's avatar
unknown committed
1488

1489
  if (alloc_query(thd, packet, packet_length))
1490 1491 1492 1493 1494 1495 1496 1497 1498 1499
  {
    stmt->set_statement(thd);
    stmt->set_item_arena(thd);
    thd->set_statement(&thd->stmt_backup);
    thd->set_item_arena(&thd->stmt_backup);
    /* Statement map deletes statement on erase */
    thd->stmt_map.erase(stmt);
    send_error(thd, ER_OUT_OF_RESOURCES);
    DBUG_VOID_RETURN;
  }
1500

1501
  mysql_log.write(thd, COM_PREPARE, "%s", packet);
1502

1503
  thd->current_arena= stmt;
1504 1505 1506 1507
  lex= lex_start(thd, (uchar *) thd->query, thd->query_length);
  mysql_init_query(thd);
  lex->safe_to_cache_query= 0;

1508 1509 1510
  error= yyparse((void *)thd) || thd->is_fatal_error ||
         init_param_array(stmt) ||
         send_prepare_results(stmt);
unknown's avatar
unknown committed
1511

1512
  /* restore to WAIT_PRIOR: QUERY_PRIOR is set inside alloc_query */
unknown's avatar
unknown committed
1513
  if (!(specialflag & SPECIAL_NO_PRIOR))
unknown's avatar
unknown committed
1514
    my_pthread_setprio(pthread_self(),WAIT_PRIOR);
unknown's avatar
unknown committed
1515
  if (error && thd->lex->sphead)
unknown's avatar
unknown committed
1516 1517 1518 1519 1520 1521
  {
    if (lex != thd->lex)
      thd->lex->sphead->restore_lex(thd);
    delete thd->lex->sphead;
    thd->lex->sphead= NULL;
  }
1522 1523
  lex_end(lex);
  stmt->set_statement(thd);
1524
  stmt->set_item_arena(thd);
1525
  thd->set_statement(&thd->stmt_backup);
1526
  thd->set_item_arena(&thd->stmt_backup);
1527
  thd->current_arena= 0;
unknown's avatar
unknown committed
1528

1529
  if (error)
1530
  {
1531 1532 1533
    /* Statement map deletes statement on erase */
    thd->stmt_map.erase(stmt);
    /* error is sent inside yyparse/send_prepare_results */
1534
  }
1535
  DBUG_VOID_RETURN;
unknown's avatar
unknown committed
1536 1537
}

1538
/* Reinit statement before execution */
unknown's avatar
unknown committed
1539

1540
void reset_stmt_for_execute(THD *thd, LEX *lex)
1541
{
1542
  SELECT_LEX *sl= lex->all_selects_list;
unknown's avatar
unknown committed
1543

1544
  for (; sl; sl= sl->next_select_in_list())
1545
  {
1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561
    if (!sl->first_execution)
    {
      /*
        Copy WHERE clause pointers to avoid damaging they by optimisation
      */
      if (sl->prep_where)
        sl->where= sl->prep_where->copy_andor_structure(thd);
      DBUG_ASSERT(sl->join == 0);
      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;
    }
unknown's avatar
unknown committed
1562 1563 1564 1565 1566 1567 1568 1569 1570 1571

    /*
      TODO: When the new table structure is ready, then have a status bit 
      to indicate the table is altered, and re-do the setup_* 
      and open the tables back.
    */
    for (TABLE_LIST *tables= (TABLE_LIST*) sl->table_list.first;
	 tables;
	 tables= tables->next)
    {
unknown's avatar
unknown committed
1572 1573 1574 1575 1576
      /*
        Reset old pointers to TABLEs: they are not valid since the tables
        were closed in the end of previous prepare or execute call.
      */
      tables->table= 0;
unknown's avatar
unknown committed
1577 1578
      tables->table_list= 0;
    }
1579 1580 1581 1582
    {
      SELECT_LEX_UNIT *unit= sl->master_unit();
      unit->unclean();
      unit->types.empty();
1583
      /* for derived tables & PS (which can't be reset by Item_subquery) */
1584 1585
      unit->reinit_exec_mechanism();
    }
1586
  }
1587
  lex->current_select= &lex->select_lex;
1588 1589
}

1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607

/* 
    Clears parameters from data left from previous execution or long data
    
  SYNOPSIS
    reset_stmt_params()
      stmt - prepared statement for which parameters should be reset
*/

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


1608 1609 1610 1611 1612 1613
/*
  Executes previously prepared query.
  If there is any parameters, then replace markers with the data supplied
  from client, and then execute the query.
  SYNOPSYS
    mysql_stmt_execute()
unknown's avatar
unknown committed
1614 1615
*/

1616
void mysql_stmt_execute(THD *thd, char *packet, uint packet_length)
unknown's avatar
unknown committed
1617
{
unknown's avatar
unknown committed
1618
  ulong stmt_id= uint4korr(packet);
unknown's avatar
unknown committed
1619
#ifndef EMBEDDED_LIBRARY
1620
  uchar *packet_end= (uchar *) packet + packet_length - 1;
unknown's avatar
unknown committed
1621
#endif
1622 1623
  Prepared_statement *stmt;
  DBUG_ENTER("mysql_stmt_execute");
1624 1625

  packet+= 9;                               /* stmt_id + 5 bytes of flags */
1626
  
1627 1628
  if (!(stmt= find_prepared_statement(thd, stmt_id, "mysql_stmt_execute",
                                      SEND_ERROR)))
1629 1630
    DBUG_VOID_RETURN;

unknown's avatar
unknown committed
1631
  DBUG_PRINT("exec_query:", ("%s", stmt->query));
unknown's avatar
unknown committed
1632

1633
  /* Check if we got an error when sending long data */
1634
  if (stmt->get_longdata_error)
1635
  {
unknown's avatar
unknown committed
1636
    send_error(thd, stmt->last_errno, stmt->last_error);
1637 1638 1639
    DBUG_VOID_RETURN;
  }

1640 1641
  thd->stmt_backup.set_statement(thd);
  thd->set_statement(stmt);
1642
  reset_stmt_for_execute(thd, stmt->lex);
unknown's avatar
unknown committed
1643
#ifndef EMBEDDED_LIBRARY
1644
  if (stmt->param_count)
1645
  {
1646
    uchar *null_array= (uchar *) packet;
1647 1648
    if (setup_conversion_functions(stmt, (uchar **) &packet, packet_end) ||
        stmt->set_params(stmt, null_array, (uchar *) packet, packet_end)) 
1649
      goto set_params_data_err;
1650
  }
unknown's avatar
unknown committed
1651
#else
1652
  /*
1653 1654 1655
    In embedded library we re-install conversion routines each time 
    we set params, and also we don't need to parse packet. 
    So we do it in one function.
1656
  */
1657 1658
  if (stmt->param_count && stmt->set_params_data(stmt))
    goto set_params_data_err;
unknown's avatar
unknown committed
1659
#endif
1660

unknown's avatar
unknown committed
1661
  if (!(specialflag & SPECIAL_NO_PRIOR))
1662
    my_pthread_setprio(pthread_self(),QUERY_PRIOR);
unknown's avatar
unknown committed
1663
 
1664 1665
  /*
    TODO:
unknown's avatar
unknown committed
1666 1667 1668
    Also, have checks on basic executions such as mysql_insert(), 
    mysql_delete(), mysql_update() and mysql_select() to not to 
    have re-check on setup_* and other things ..
1669
  */
1670
  thd->current_arena= stmt;
1671
  thd->protocol= &thd->protocol_prep;           // Switch to binary protocol
1672
  mysql_execute_command(thd);
1673
  thd->lex->unit.cleanup();
1674
  thd->protocol= &thd->protocol_simple;         // Use normal protocol
1675
  thd->current_arena= 0;
unknown's avatar
unknown committed
1676

unknown's avatar
unknown committed
1677
  if (!(specialflag & SPECIAL_NO_PRIOR))
1678
    my_pthread_setprio(pthread_self(), WAIT_PRIOR);
unknown's avatar
unknown committed
1679

unknown's avatar
unknown committed
1680
  cleanup_items(stmt->free_list);
1681
  reset_stmt_params(stmt);
1682
  close_thread_tables(thd); // to close derived tables
1683
  thd->set_statement(&thd->stmt_backup);
1684
  /*
1685 1686 1687 1688
    Free Items that were created during this execution of the PS by query
    optimizer.
  */
  free_items(thd->free_list); 
1689 1690 1691
  DBUG_VOID_RETURN;

set_params_data_err:
1692
  reset_stmt_params(stmt);
1693
  thd->set_statement(&thd->stmt_backup);
1694
  my_error(ER_WRONG_ARGUMENTS, MYF(0), "mysql_stmt_execute");
1695
  send_error(thd);
unknown's avatar
unknown committed
1696 1697 1698
  DBUG_VOID_RETURN;
}

1699

unknown's avatar
unknown committed
1700
/*
1701
    Reset a prepared statement in case there was a recoverable error.
1702 1703 1704
  SYNOPSIS
    mysql_stmt_reset()
    thd		Thread handle
1705
    packet	Packet with stmt id 
1706 1707

  DESCRIPTION
1708 1709 1710 1711 1712 1713 1714
    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.
    Sends 'OK' packet in case of success (statement was reset)
    or 'ERROR' packet (unrecoverable error/statement not found/etc).
unknown's avatar
unknown committed
1715 1716
*/

1717
void mysql_stmt_reset(THD *thd, char *packet)
unknown's avatar
unknown committed
1718
{
1719
  /* There is always space for 4 bytes in buffer */
1720
  ulong stmt_id= uint4korr(packet);
1721 1722
  Prepared_statement *stmt;
  
1723
  DBUG_ENTER("mysql_stmt_reset");
unknown's avatar
unknown committed
1724

1725 1726
  if (!(stmt= find_prepared_statement(thd, stmt_id, "mysql_stmt_reset",
                                      SEND_ERROR)))
1727 1728
    DBUG_VOID_RETURN;

1729
  stmt->get_longdata_error= 0;
1730

1731 1732 1733 1734 1735
  /* 
    Clear parameters from data which could be set by 
    mysql_stmt_send_long_data() call.
  */
  reset_stmt_params(stmt);
1736 1737

  send_ok(thd);
1738
  
1739 1740 1741 1742 1743
  DBUG_VOID_RETURN;
}


/*
1744 1745
  Delete a prepared statement from memory.
  Note: we don't send any reply to that command. 
1746 1747
*/

unknown's avatar
unknown committed
1748
void mysql_stmt_free(THD *thd, char *packet)
1749
{
1750
  /* There is always space for 4 bytes in packet buffer */
1751
  ulong stmt_id= uint4korr(packet);
1752 1753
  Prepared_statement *stmt;

unknown's avatar
unknown committed
1754
  DBUG_ENTER("mysql_stmt_free");
1755

1756 1757
  if (!(stmt= find_prepared_statement(thd, stmt_id, "mysql_stmt_close",
                                      DONT_SEND_ERROR)))
1758
    DBUG_VOID_RETURN;
1759 1760 1761

  /* Statement map deletes statement on erase */
  thd->stmt_map.erase(stmt);
unknown's avatar
unknown committed
1762 1763 1764
  DBUG_VOID_RETURN;
}

1765 1766

/*
1767
  Long data in pieces from client
1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784

  SYNOPSIS
    mysql_stmt_get_longdata()
    thd			Thread handle
    pos			String to append
    packet_length	Length of string

  DESCRIPTION
    Get a part of a long data.
    To make the protocol efficient, we are not sending any return packages
    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)
*/

1785
void mysql_stmt_get_longdata(THD *thd, char *packet, ulong packet_length)
1786
{
1787 1788
  ulong stmt_id;
  uint param_number;
1789
  Prepared_statement *stmt;
1790 1791
  Item_param *param;
  char *packet_end= packet + packet_length - 1;
1792
  
1793 1794
  DBUG_ENTER("mysql_stmt_get_longdata");

unknown's avatar
unknown committed
1795
#ifndef EMBEDDED_LIBRARY
1796 1797
  /* Minimal size of long data packet is 6 bytes */
  if ((ulong) (packet_end - packet) < MYSQL_LONG_DATA_HEADER)
1798
  {
1799
    my_error(ER_WRONG_ARGUMENTS, MYF(0), "mysql_stmt_send_long_data");
1800 1801
    DBUG_VOID_RETURN;
  }
unknown's avatar
unknown committed
1802
#endif
1803

1804 1805
  stmt_id= uint4korr(packet);
  packet+= 4;
1806

1807
  if (!(stmt=find_prepared_statement(thd, stmt_id, "mysql_stmt_send_long_data",
1808
                                     DONT_SEND_ERROR)))
1809 1810
    DBUG_VOID_RETURN;

1811 1812
  param_number= uint2korr(packet);
  packet+= 2;
unknown's avatar
unknown committed
1813
#ifndef EMBEDDED_LIBRARY
1814 1815
  if (param_number >= stmt->param_count)
  {
unknown's avatar
unknown committed
1816
    /* Error will be sent in execute call */
1817
    stmt->get_longdata_error= 1;
unknown's avatar
unknown committed
1818
    stmt->last_errno= ER_WRONG_ARGUMENTS;
1819 1820
    sprintf(stmt->last_error, ER(ER_WRONG_ARGUMENTS),
            "mysql_stmt_send_long_data");
1821 1822
    DBUG_VOID_RETURN;
  }
unknown's avatar
unknown committed
1823 1824
#endif

1825 1826
  param= stmt->param_array[param_number];

unknown's avatar
unknown committed
1827
#ifndef EMBEDDED_LIBRARY
1828
  param->set_longdata(packet, (ulong) (packet_end - packet));
unknown's avatar
unknown committed
1829 1830 1831
#else
  param->set_longdata(thd->extra_data, thd->extra_length);
#endif
1832 1833
  DBUG_VOID_RETURN;
}
unknown's avatar
unknown committed
1834

1835 1836 1837 1838

Prepared_statement::Prepared_statement(THD *thd_arg)
  :Statement(thd_arg),
  thd(thd_arg),
1839
  param_array(0),
1840 1841
  param_count(0),
  last_errno(0),
1842
  get_longdata_error(0)
1843 1844 1845 1846 1847
{
  *last_error= '\0';
  if (mysql_bin_log.is_open())
  {
#ifndef EMBEDDED_LIBRARY
1848
    set_params= insert_params_withlog;
1849
#else
1850
    set_params_data= emb_insert_params_withlog;
1851 1852 1853 1854
#endif
  }
  else
#ifndef EMBEDDED_LIBRARY
1855
    set_params= insert_params;
1856
#else
1857
    set_params_data= emb_insert_params;
1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872
#endif
}


Prepared_statement::~Prepared_statement()
{
  free_items(free_list);
}


Statement::Type Prepared_statement::type() const
{
  return PREPARED_STATEMENT;
}