sql_prepare.cc 57.9 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
***********************************************************************/

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

78 79 80
/******************************************************************************
  Prepared_statement: statement which can contain placeholders
******************************************************************************/
81

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

106 107
static void execute_stmt(THD *thd, Prepared_statement *stmt,
                         String *expanded_query, bool set_context);
108

109 110 111
/******************************************************************************
  Implementation
******************************************************************************/
112 113


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

119
enum { STMT_QUERY_LOG_LENGTH= 8192 };
120

121
enum enum_send_error { DONT_SEND_ERROR= 0, SEND_ERROR };
122 123

/*
124 125
  Seek prepared statement in statement map by id: returns zero if statement
  was not found, pointer otherwise.
126 127
*/

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

unknown's avatar
unknown committed
134
  if (stmt == 0 || stmt->type() != Item_arena::PREPARED_STATEMENT)
135
  {
136 137
    char llbuf[22];
    my_error(ER_UNKNOWN_STMT_HANDLER, MYF(0), 22, llstr(id, llbuf), where);
138 139
    if (se == SEND_ERROR)
      send_error(thd);
140 141 142
    return 0;
  }
  return (Prepared_statement *) stmt;
143 144
}

145

146 147 148 149
/*
  Send prepared stmt info to client after prepare
*/

unknown's avatar
unknown committed
150
#ifndef EMBEDDED_LIBRARY
151
static bool send_prep_stmt(Prepared_statement *stmt, uint columns)
152
{
153
  NET *net= &stmt->thd->net;
154
  char buff[9];
155 156
  DBUG_ENTER("send_prep_stmt");

157
  buff[0]= 0;                                   /* OK packet indicator */
158
  int4store(buff+1, stmt->id);
159 160
  int2store(buff+5, columns);
  int2store(buff+7, stmt->param_count);
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>
  */
165 166 167 168 169
  DBUG_RETURN(my_net_write(net, buff, sizeof(buff)) || 
              (stmt->param_count &&
               stmt->thd->protocol_simple.send_fields((List<Item> *)
                                                      &stmt->lex->param_list,
                                                      0)));
unknown's avatar
unknown committed
170
}
171
#else
172 173
static bool send_prep_stmt(Prepared_statement *stmt,
                           uint columns __attribute__((unused)))
unknown's avatar
unknown committed
174
{
unknown's avatar
SCRUM  
unknown committed
175 176
  THD *thd= stmt->thd;

177
  thd->client_stmt_id= stmt->id;
unknown's avatar
SCRUM  
unknown committed
178
  thd->client_param_count= stmt->param_count;
unknown's avatar
unknown committed
179
  thd->net.last_errno= 0;
unknown's avatar
unknown committed
180

unknown's avatar
SCRUM  
unknown committed
181
  return 0;
182
}
183
#endif /*!EMBEDDED_LIBRARY*/
184

unknown's avatar
unknown committed
185 186

/*
187 188
  Read the length of the parameter data and return back to
  caller by positing the pointer to param data.
unknown's avatar
unknown committed
189 190
*/

unknown's avatar
unknown committed
191
#ifndef EMBEDDED_LIBRARY
192
static ulong get_param_length(uchar **packet, ulong len)
unknown's avatar
unknown committed
193 194
{
  reg1 uchar *pos= *packet;
195 196
  if (len < 1)
    return 0;
unknown's avatar
unknown committed
197 198 199 200 201
  if (*pos < 251)
  {
    (*packet)++;
    return (ulong) *pos;
  }
202 203
  if (len < 3)
    return 0;
unknown's avatar
unknown committed
204 205 206 207 208
  if (*pos == 252)
  {
    (*packet)+=3;
    return (ulong) uint2korr(pos+1);
  }
209 210
  if (len < 4)
    return 0;
unknown's avatar
unknown committed
211 212 213 214 215
  if (*pos == 253)
  {
    (*packet)+=4;
    return (ulong) uint3korr(pos+1);
  }
216 217
  if (len < 5)
    return 0;
unknown's avatar
unknown committed
218
  (*packet)+=9; // Must be 254 when here 
219 220 221 222 223 224 225
  /*
    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
226 227
  return (ulong) uint4korr(pos+1);
}
unknown's avatar
unknown committed
228
#else
229
#define get_param_length(packet, len) len
unknown's avatar
unknown committed
230 231
#endif /*!EMBEDDED_LIBRARY*/

unknown's avatar
unknown committed
232
 /*
233 234 235 236 237 238
   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
239

240 241
  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
242 243 244 245 246

  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.

247 248
  RETURN VALUE
    none
unknown's avatar
unknown committed
249 250
*/

251
static void set_param_tiny(Item_param *param, uchar **pos, ulong len)
unknown's avatar
unknown committed
252
{
253 254 255 256
#ifndef EMBEDDED_LIBRARY
  if (len < 1)
    return;
#endif
257 258
  int8 value= (int8) **pos;
  param->set_int(param->unsigned_flag ? (longlong) ((uint8) value) : 
259
                                        (longlong) value, 4);
unknown's avatar
unknown committed
260 261 262
  *pos+= 1;
}

263
static void set_param_short(Item_param *param, uchar **pos, ulong len)
unknown's avatar
unknown committed
264
{
265
  int16 value;
266 267 268
#ifndef EMBEDDED_LIBRARY
  if (len < 2)
    return;
269
  value= sint2korr(*pos);
270 271 272
#else
  shortget(value, *pos);
#endif
273
  param->set_int(param->unsigned_flag ? (longlong) ((uint16) value) :
274
                                        (longlong) value, 6);
unknown's avatar
unknown committed
275 276 277
  *pos+= 2;
}

278
static void set_param_int32(Item_param *param, uchar **pos, ulong len)
unknown's avatar
unknown committed
279
{
280
  int32 value;
281 282 283
#ifndef EMBEDDED_LIBRARY
  if (len < 4)
    return;
284
  value= sint4korr(*pos);
285 286 287
#else
  longget(value, *pos);
#endif
288
  param->set_int(param->unsigned_flag ? (longlong) ((uint32) value) :
289
                                        (longlong) value, 11);
unknown's avatar
unknown committed
290 291 292
  *pos+= 4;
}

293
static void set_param_int64(Item_param *param, uchar **pos, ulong len)
unknown's avatar
unknown committed
294
{
295
  longlong value;
296 297 298
#ifndef EMBEDDED_LIBRARY
  if (len < 8)
    return;
299
  value= (longlong) sint8korr(*pos);
300 301 302
#else
  longlongget(value, *pos);
#endif
303 304
  param->set_int(value, 21);
  *pos+= 8;
unknown's avatar
unknown committed
305 306
}

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

321
static void set_param_double(Item_param *param, uchar **pos, ulong len)
unknown's avatar
unknown committed
322
{
unknown's avatar
unknown committed
323
  double data;
324 325 326
#ifndef EMBEDDED_LIBRARY
  if (len < 8)
    return;
unknown's avatar
unknown committed
327
  float8get(data,*pos);
unknown's avatar
unknown committed
328
#else
329
  doubleget(data, *pos);
unknown's avatar
unknown committed
330
#endif
unknown's avatar
unknown committed
331 332 333 334
  param->set_double((double) data);
  *pos+= 8;
}

335
#ifndef EMBEDDED_LIBRARY
336 337 338 339 340 341 342

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

343
static void set_param_time(Item_param *param, uchar **pos, ulong len)
344
{
345 346
  MYSQL_TIME tm;
  ulong length= get_param_length(pos, len);
347

348
  if (length >= 8)
349 350
  {
    uchar *to= *pos;
351
    uint day;
352

353 354
    tm.neg= (bool) to[0];
    day= (uint) sint4korr(to+1);
355
    tm.hour=   (uint) to[5] + day * 24;
356 357
    tm.minute= (uint) to[6];
    tm.second= (uint) to[7];
358
    tm.second_part= (length > 8) ? (ulong) sint4korr(to+8) : 0;
359 360 361 362 363 364 365 366
    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;
367
  }
368
  else
369
    set_zero_time(&tm, MYSQL_TIMESTAMP_TIME);
370 371
  param->set_time(&tm, MYSQL_TIMESTAMP_TIME,
                  MAX_TIME_WIDTH * MY_CHARSET_BIN_MB_MAXLEN);
372 373 374
  *pos+= length;
}

375
static void set_param_datetime(Item_param *param, uchar **pos, ulong len)
376
{
377 378
  MYSQL_TIME tm;
  ulong length= get_param_length(pos, len);
379

380
  if (length >= 4)
381 382
  {
    uchar *to= *pos;
383 384 385 386 387

    tm.neg=    0;
    tm.year=   (uint) sint2korr(to);
    tm.month=  (uint) to[2];
    tm.day=    (uint) to[3];
388 389 390 391 392 393 394 395
    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;
396 397

    tm.second_part= (length > 7) ? (ulong) sint4korr(to+7) : 0;
398
  }
399
  else
400
    set_zero_time(&tm, MYSQL_TIMESTAMP_DATETIME);
401 402
  param->set_time(&tm, MYSQL_TIMESTAMP_DATETIME,
                  MAX_DATETIME_WIDTH * MY_CHARSET_BIN_MB_MAXLEN);
403 404 405
  *pos+= length;
}

406
static void set_param_date(Item_param *param, uchar **pos, ulong len)
407
{
408 409 410 411
  MYSQL_TIME tm;
  ulong length= get_param_length(pos, len);

  if (length >= 4)
412 413
  {
    uchar *to= *pos;
414

415
    tm.year=  (uint) sint2korr(to);
416 417 418 419 420 421 422
    tm.month=  (uint) to[2];
    tm.day= (uint) to[3];

    tm.hour= tm.minute= tm.second= 0;
    tm.second_part= 0;
    tm.neg= 0;
  }
423
  else
424
    set_zero_time(&tm, MYSQL_TIMESTAMP_DATE);
425 426
  param->set_time(&tm, MYSQL_TIMESTAMP_DATE,
                  MAX_DATE_WIDTH * MY_CHARSET_BIN_MB_MAXLEN);
427 428 429
  *pos+= length;
}

430 431 432
#else/*!EMBEDDED_LIBRARY*/
void set_param_time(Item_param *param, uchar **pos, ulong len)
{
433 434 435 436 437 438 439 440 441 442 443
  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,
444
                  MAX_TIME_WIDTH * MY_CHARSET_BIN_MB_MAXLEN);
445 446 447 448 449 450 451

}

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

452
  param->set_time(to, MYSQL_TIMESTAMP_DATETIME,
453
                  MAX_DATETIME_WIDTH * MY_CHARSET_BIN_MB_MAXLEN);
454 455 456 457 458
}

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

  param->set_time(to, MYSQL_TIMESTAMP_DATE,
461
                  MAX_DATE_WIDTH * MY_CHARSET_BIN_MB_MAXLEN);
462 463 464
}
#endif /*!EMBEDDED_LIBRARY*/

465 466

static void set_param_str(Item_param *param, uchar **pos, ulong len)
unknown's avatar
unknown committed
467
{
468
  ulong length= get_param_length(pos, len);
469
  param->set_str((const char *)*pos, length);
470
  *pos+= length;
unknown's avatar
unknown committed
471 472
}

473 474 475 476 477

#undef get_param_length 

static void setup_one_conversion_function(THD *thd, Item_param *param,
                                          uchar param_type)
unknown's avatar
unknown committed
478
{
unknown's avatar
unknown committed
479
  switch (param_type) {
480
  case MYSQL_TYPE_TINY:
481
    param->set_param_func= set_param_tiny;
482
    param->item_type= Item::INT_ITEM;
483
    param->item_result_type= INT_RESULT;
unknown's avatar
unknown committed
484
    break;
485
  case MYSQL_TYPE_SHORT:
486
    param->set_param_func= set_param_short;
487
    param->item_type= Item::INT_ITEM;
488
    param->item_result_type= INT_RESULT;
unknown's avatar
unknown committed
489
    break;
490
  case MYSQL_TYPE_LONG:
491
    param->set_param_func= set_param_int32;
492
    param->item_type= Item::INT_ITEM;
493
    param->item_result_type= INT_RESULT;
unknown's avatar
unknown committed
494
    break;
495
  case MYSQL_TYPE_LONGLONG:
496
    param->set_param_func= set_param_int64;
497
    param->item_type= Item::INT_ITEM;
498
    param->item_result_type= INT_RESULT;
unknown's avatar
unknown committed
499
    break;
500
  case MYSQL_TYPE_FLOAT:
501
    param->set_param_func= set_param_float;
502
    param->item_type= Item::REAL_ITEM;
503
    param->item_result_type= REAL_RESULT;
unknown's avatar
unknown committed
504
    break;
505
  case MYSQL_TYPE_DOUBLE:
506
    param->set_param_func= set_param_double;
507
    param->item_type= Item::REAL_ITEM;
508
    param->item_result_type= REAL_RESULT;
unknown's avatar
unknown committed
509
    break;
510
  case MYSQL_TYPE_TIME:
511
    param->set_param_func= set_param_time;
512
    param->item_type= Item::STRING_ITEM;
513
    param->item_result_type= STRING_RESULT;
514
    break;
515
  case MYSQL_TYPE_DATE:
516
    param->set_param_func= set_param_date;
517
    param->item_type= Item::STRING_ITEM;
518
    param->item_result_type= STRING_RESULT;
519
    break;
520 521
  case MYSQL_TYPE_DATETIME:
  case MYSQL_TYPE_TIMESTAMP:
522
    param->set_param_func= set_param_datetime;
523
    param->item_type= Item::STRING_ITEM;
524
    param->item_result_type= STRING_RESULT;
525
    break;
526 527 528 529
  case MYSQL_TYPE_TINY_BLOB:
  case MYSQL_TYPE_MEDIUM_BLOB:
  case MYSQL_TYPE_LONG_BLOB:
  case MYSQL_TYPE_BLOB:
530
    param->set_param_func= set_param_str;
531 532 533
    param->value.cs_info.character_set_of_placeholder= &my_charset_bin;
    param->value.cs_info.character_set_client=
      thd->variables.character_set_client;
534 535
    param->value.cs_info.final_character_set_of_str_value= &my_charset_bin;
    param->item_type= Item::STRING_ITEM;
536
    param->item_result_type= STRING_RESULT;
537 538 539 540 541 542 543 544 545 546 547 548
    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;

549
      param->value.cs_info.character_set_of_placeholder= fromcs;
550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567
      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
568
  }
569
  param->param_type= (enum enum_field_types) param_type;
unknown's avatar
unknown committed
570 571
}

unknown's avatar
unknown committed
572
#ifndef EMBEDDED_LIBRARY
unknown's avatar
unknown committed
573
/*
574 575
  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
576 577
*/

578
static bool insert_params_withlog(Prepared_statement *stmt, uchar *null_array,
579 580
                                  uchar *read_pos, uchar *data_end, 
                                  String *query)
581
{
582 583 584 585
  THD  *thd= stmt->thd;
  Item_param **begin= stmt->param_array;
  Item_param **end= begin + stmt->param_count;
  uint32 length= 0;
586
  String str; 
587 588
  const String *res;
  DBUG_ENTER("insert_params_withlog"); 
589

590
  if (query->copy(stmt->query, stmt->query_length, default_charset_info))
591
    DBUG_RETURN(1);
592
  
593
  for (Item_param **it= begin; it < end; ++it)
594
  {
595
    Item_param *param= *it;
596
    if (param->state != Item_param::LONG_DATA_VALUE)
597
    {
598
      if (is_param_null(null_array, it - begin))
599
        param->set_null();
600 601
      else
      {
602 603 604
        if (read_pos >= data_end)
          DBUG_RETURN(1);
        param->set_param_func(param, &read_pos, data_end - read_pos);
605 606
      }
    }
607
    res= param->query_val_str(&str);
608 609 610
    if (param->convert_str_value(thd))
      DBUG_RETURN(1);                           /* out of memory */

611
    if (query->replace(param->pos_in_query+length, 1, *res))
612 613 614 615 616 617 618
      DBUG_RETURN(1);
    
    length+= res->length()-1;
  }
  DBUG_RETURN(0);
}

619

620
static bool insert_params(Prepared_statement *stmt, uchar *null_array,
621 622
                          uchar *read_pos, uchar *data_end, 
                          String *expanded_query)
623
{
624 625
  Item_param **begin= stmt->param_array;
  Item_param **end= begin + stmt->param_count;
626 627 628

  DBUG_ENTER("insert_params"); 

629
  for (Item_param **it= begin; it < end; ++it)
630
  {
631
    Item_param *param= *it;
632
    if (param->state != Item_param::LONG_DATA_VALUE)
633
    {
634
      if (is_param_null(null_array, it - begin))
635
        param->set_null();
636 637
      else
      {
638 639 640
        if (read_pos >= data_end)
          DBUG_RETURN(1);
        param->set_param_func(param, &read_pos, data_end - read_pos);
641 642
      }
    }
643 644
    if (param->convert_str_value(stmt->thd))
      DBUG_RETURN(1);                           /* out of memory */
645 646 647 648
  }
  DBUG_RETURN(0);
}

649

650
static bool setup_conversion_functions(Prepared_statement *stmt,
651
                                       uchar **data, uchar *data_end)
652 653 654
{
  /* skip null bits */
  uchar *read_pos= *data + (stmt->param_count+7) / 8;
unknown's avatar
unknown committed
655

656
  DBUG_ENTER("setup_conversion_functions");
657

unknown's avatar
unknown committed
658
  if (*read_pos++) //types supplied / first execute
659
  {
unknown's avatar
unknown committed
660 661 662 663
    /*
      First execute or types altered by the client, setup the 
      conversion routines for all parameters (one time)
    */
664 665
    Item_param **it= stmt->param_array;
    Item_param **end= it + stmt->param_count;
666
    THD *thd= stmt->thd;
667 668
    for (; it < end; ++it)
    {
669 670 671
      ushort typecode;
      const uint signed_bit= 1 << 15;

672 673
      if (read_pos >= data_end)
        DBUG_RETURN(1);
674 675

      typecode= sint2korr(read_pos);
unknown's avatar
unknown committed
676
      read_pos+= 2;
677
      (**it).unsigned_flag= test(typecode & signed_bit);
678
      setup_one_conversion_function(thd, *it, (uchar) (typecode & ~signed_bit));
unknown's avatar
unknown committed
679
    }
680 681
  }
  *data= read_pos;
unknown's avatar
unknown committed
682 683 684
  DBUG_RETURN(0);
}

685 686
#else

687
static bool emb_insert_params(Prepared_statement *stmt, String *expanded_query)
688
{
689
  THD *thd= stmt->thd;
690 691
  Item_param **it= stmt->param_array;
  Item_param **end= it + stmt->param_count;
692 693
  MYSQL_BIND *client_param= stmt->thd->client_params;

694
  DBUG_ENTER("emb_insert_params");
695

696 697 698
  for (; it < end; ++it, ++client_param)
  {
    Item_param *param= *it;
699 700
    setup_one_conversion_function(thd, param, client_param->buffer_type);
    if (param->state != Item_param::LONG_DATA_VALUE)
701 702
    {
      if (*client_param->is_null)
703
        param->set_null();
704 705
      else
      {
706
        uchar *buff= (uchar*) client_param->buffer;
unknown's avatar
unknown committed
707
        param->unsigned_flag= client_param->is_unsigned;
708 709 710 711
        param->set_param_func(param, &buff,
                              client_param->length ? 
                              *client_param->length : 
                              client_param->buffer_length);
712 713
      }
    }
714 715
    if (param->convert_str_value(thd))
      DBUG_RETURN(1);                           /* out of memory */
716 717 718 719
  }
  DBUG_RETURN(0);
}

720

721
static bool emb_insert_params_withlog(Prepared_statement *stmt, String *query)
722
{
723
  THD *thd= stmt->thd;
724 725
  Item_param **it= stmt->param_array;
  Item_param **end= it + stmt->param_count;
726 727
  MYSQL_BIND *client_param= thd->client_params;

728
  String str;
729
  const String *res;
730
  uint32 length= 0;
731

732
  DBUG_ENTER("emb_insert_params_withlog");
733

734
  if (query->copy(stmt->query, stmt->query_length, default_charset_info))
735 736
    DBUG_RETURN(1);
  
737 738 739
  for (; it < end; ++it, ++client_param)
  {
    Item_param *param= *it;
740 741
    setup_one_conversion_function(thd, param, client_param->buffer_type);
    if (param->state != Item_param::LONG_DATA_VALUE)
742 743
    {
      if (*client_param->is_null)
744
        param->set_null();
745 746
      else
      {
747
        uchar *buff= (uchar*)client_param->buffer;
748
	param->unsigned_flag= client_param->is_unsigned;
749 750 751 752
        param->set_param_func(param, &buff,
                              client_param->length ? 
                              *client_param->length : 
                              client_param->buffer_length);
753 754
      }
    }
755
    res= param->query_val_str(&str);
756 757 758
    if (param->convert_str_value(thd))
      DBUG_RETURN(1);                           /* out of memory */

759
    if (query->replace(param->pos_in_query+length, 1, *res))
760
      DBUG_RETURN(1);
761

762 763 764 765 766
    length+= res->length()-1;
  }
  DBUG_RETURN(0);
}

unknown's avatar
unknown committed
767 768
#endif /*!EMBEDDED_LIBRARY*/

unknown's avatar
unknown committed
769

770
/*
unknown's avatar
unknown committed
771 772 773 774 775 776
  Set prepared statement parameters from user variables.
  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
777
      query     Ignored
unknown's avatar
unknown committed
778 779
*/

780 781
static bool insert_params_from_vars(Prepared_statement *stmt,
                                    List<LEX_STRING>& varnames,
782
                                    String *query __attribute__((unused)))
unknown's avatar
unknown committed
783 784 785 786 787 788
{
  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);
789 790
  DBUG_ENTER("insert_params_from_vars");

unknown's avatar
unknown committed
791 792 793 794
  for (Item_param **it= begin; it < end; ++it)
  {
    Item_param *param= *it;
    varname= var_it++;
795 796 797 798 799 800
    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
801 802 803 804
  }
  DBUG_RETURN(0);
}

805

806
/*
807 808 809 810 811 812 813 814 815 816
  Do the same as insert_params_from_vars but also construct query text for
  binary log.
  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
      query     The query with parameter markers replaced with their values
*/

unknown's avatar
unknown committed
817
static bool insert_params_from_vars_with_log(Prepared_statement *stmt,
818
                                             List<LEX_STRING>& varnames,
819
                                             String *query)
unknown's avatar
unknown committed
820 821 822 823 824
{
  Item_param **begin= stmt->param_array;
  Item_param **end= begin + stmt->param_count;
  user_var_entry *entry;
  LEX_STRING *varname;
825
  DBUG_ENTER("insert_params_from_vars");
unknown's avatar
unknown committed
826 827

  List_iterator<LEX_STRING> var_it(varnames);
828 829
  String buf;
  const String *val;
unknown's avatar
unknown committed
830
  uint32 length= 0;
831
  if (query->copy(stmt->query, stmt->query_length, default_charset_info))
832
    DBUG_RETURN(1);
unknown's avatar
unknown committed
833 834 835 836 837

  for (Item_param **it= begin; it < end; ++it)
  {
    Item_param *param= *it;
    varname= var_it++;
838 839
    if (get_var_with_binlog(stmt->thd, *varname, &entry))
        DBUG_RETURN(1);
unknown's avatar
unknown committed
840

841 842 843
    if (param->set_from_user_var(stmt->thd, entry))
      DBUG_RETURN(1);
    /* Insert @'escaped-varname' instead of parameter in the query */
844 845 846 847 848 849
    if (entry)
    {
      char *begin, *ptr;
      buf.length(0);
      if (buf.reserve(entry->name.length*2+3))
        DBUG_RETURN(1);
850

851 852 853 854 855 856 857 858 859 860 861
      begin= ptr= buf.c_ptr_quick();
      *ptr++= '@';
      *ptr++= '\'';
      ptr+= escape_string_for_mysql(&my_charset_utf8_general_ci,
                                    ptr, entry->name.str, entry->name.length);
      *ptr++= '\'';
      buf.length(ptr - begin);
      val= &buf;
    }
    else
      val= &my_null_string;
862 863 864

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

866
    if (query->replace(param->pos_in_query+length, 1, *val))
867
      DBUG_RETURN(1);
868
    length+= val->length()-1;
unknown's avatar
unknown committed
869 870 871 872
  }
  DBUG_RETURN(0);
}

unknown's avatar
unknown committed
873
/*
unknown's avatar
unknown committed
874 875
  Validate INSERT statement: 

876
  SYNOPSIS
unknown's avatar
unknown committed
877 878 879 880
    mysql_test_insert()
    stmt	prepared statemen handler
    tables	list of tables queries  

881 882 883 884
  RETURN VALUE
    0   ok
    1   error, sent to the client
   -1   error, not sent to client
unknown's avatar
unknown committed
885
*/
unknown's avatar
unknown committed
886 887 888 889 890 891 892
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
893
{
894
  THD *thd= stmt->thd;
895
  LEX *lex= stmt->lex;
unknown's avatar
unknown committed
896 897
  List_iterator_fast<List_item> its(values_list);
  List_item *values;
unknown's avatar
unknown committed
898
  int res= -1;
899 900
  TABLE_LIST *insert_table_list=
    (TABLE_LIST*) lex->select_lex.table_list.first;
901
  DBUG_ENTER("mysql_test_insert");
unknown's avatar
unknown committed
902

903
  if ((res= insert_precheck(thd, table_list)))
unknown's avatar
unknown committed
904
    DBUG_RETURN(res);
unknown's avatar
unknown committed
905

906
  /*
unknown's avatar
unknown committed
907 908
     open temporary memory pool for temporary data allocated by derived
     tables & preparation procedure
909 910 911 912
     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
913
  */
914
  if (open_normal_and_derived_tables(thd, table_list))
915
  {
916
    DBUG_RETURN(-1);
917 918
  }

unknown's avatar
unknown committed
919 920 921
  if ((values= its++))
  {
    uint value_count;
922
    ulong counter= 0;
unknown's avatar
unknown committed
923

924
    table_list->table->insert_values=(byte *)1; // don't allocate insert_values
unknown's avatar
unknown committed
925
    if ((res= mysql_prepare_insert(thd, table_list, insert_table_list,
unknown's avatar
unknown committed
926
                                   insert_table_list,
unknown's avatar
unknown committed
927 928 929
				   table_list->table, fields, values,
				   update_fields, update_values, duplic)))
      goto error;
930

unknown's avatar
unknown committed
931 932
    value_count= values->elements;
    its.rewind();
933

934
    while ((values= its++))
unknown's avatar
unknown committed
935 936 937 938 939 940
    {
      counter++;
      if (values->elements != value_count)
      {
        my_printf_error(ER_WRONG_VALUE_COUNT_ON_ROW,
			ER(ER_WRONG_VALUE_COUNT_ON_ROW),
941
			MYF(0), counter);
942
        goto error;
unknown's avatar
unknown committed
943
      }
944 945
      if (setup_fields(thd, 0, insert_table_list, *values, 0, 0, 0))
	goto error;
unknown's avatar
unknown committed
946 947
    }
  }
948

unknown's avatar
unknown committed
949
  res= 0;
950 951
error:
  lex->unit.cleanup();
952
  table_list->table->insert_values=0;
unknown's avatar
unknown committed
953
  DBUG_RETURN(res);
unknown's avatar
unknown committed
954 955 956 957
}


/*
unknown's avatar
unknown committed
958 959
  Validate UPDATE statement

960
  SYNOPSIS
961
    mysql_test_update()
unknown's avatar
unknown committed
962 963 964
    stmt	prepared statemen handler
    tables	list of tables queries

965 966 967 968
  RETURN VALUE
    0   success
    1   error, sent to client
   -1   error, not sent to client
unknown's avatar
unknown committed
969
*/
unknown's avatar
unknown committed
970 971
static int mysql_test_update(Prepared_statement *stmt,
			     TABLE_LIST *table_list)
unknown's avatar
unknown committed
972
{
unknown's avatar
unknown committed
973
  int res;
974
  THD *thd= stmt->thd;
unknown's avatar
unknown committed
975 976 977 978 979
  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
980

unknown's avatar
unknown committed
981
  if (open_and_lock_tables(thd, table_list))
unknown's avatar
unknown committed
982 983
    res= -1;
  else
984
  {
unknown's avatar
unknown committed
985 986 987 988 989 990 991 992 993 994 995 996 997
    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;
    }
998 999
    stmt->lex->unit.cleanup();
  }
unknown's avatar
unknown committed
1000 1001 1002
  /* TODO: here we should send types of placeholders to the client. */ 
  DBUG_RETURN(res);
}
unknown's avatar
unknown committed
1003

unknown's avatar
unknown committed
1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037

/*
  Validate DELETE statement

  SYNOPSIS
    mysql_test_delete()
    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_delete(Prepared_statement *stmt,
			     TABLE_LIST *table_list)
{
  int res;
  THD *thd= stmt->thd;
  LEX *lex= stmt->lex;
  DBUG_ENTER("mysql_test_delete");

  if ((res= delete_precheck(thd, table_list)))
    DBUG_RETURN(res);

  if (open_and_lock_tables(thd, table_list))
    res= -1;
  else
  {
    res= mysql_prepare_delete(thd, table_list, &lex->select_lex.where);
    lex->unit.cleanup();
  }
  /* TODO: here we should send types of placeholders to the client. */ 
  DBUG_RETURN(res);
unknown's avatar
unknown committed
1038 1039
}

unknown's avatar
unknown committed
1040

unknown's avatar
unknown committed
1041
/*
unknown's avatar
unknown committed
1042
  Validate SELECT statement.
1043 1044
  In case of success, if this query is not EXPLAIN, send column list info
  back to client. 
unknown's avatar
unknown committed
1045

1046
  SYNOPSIS
unknown's avatar
unknown committed
1047 1048 1049 1050
    mysql_test_select()
    stmt	prepared statemen handler
    tables	list of tables queries

1051 1052 1053 1054
  RETURN VALUE
    0   success
    1   error, sent to client
   -1   error, not sent to client
unknown's avatar
unknown committed
1055
*/
1056

unknown's avatar
unknown committed
1057
static int mysql_test_select(Prepared_statement *stmt,
unknown's avatar
unknown committed
1058
			     TABLE_LIST *tables, bool text_protocol)
unknown's avatar
unknown committed
1059
{
1060
  THD *thd= stmt->thd;
1061
  LEX *lex= stmt->lex;
1062
  SELECT_LEX_UNIT *unit= &lex->unit;
1063
  int result= 1;
unknown's avatar
unknown committed
1064
  DBUG_ENTER("mysql_test_select");
unknown's avatar
unknown committed
1065

1066 1067 1068
  ulong privilege= lex->exchange ? SELECT_ACL | FILE_ACL : SELECT_ACL;
  if (tables)
  {
unknown's avatar
unknown committed
1069
    if (check_table_access(thd, privilege, tables,0))
1070 1071
      DBUG_RETURN(1);
  }
unknown's avatar
unknown committed
1072
  else if (check_access(thd, privilege, any_db,0,0,0))
1073
    DBUG_RETURN(1);
unknown's avatar
unknown committed
1074

unknown's avatar
unknown committed
1075
  if (!lex->result && !(lex->result= new (stmt->mem_root) select_send))
1076 1077 1078 1079 1080
  {
    send_error(thd);
    goto err;
  }

unknown's avatar
unknown committed
1081
  if (open_and_lock_tables(thd, tables))
1082 1083
  {
    send_error(thd);
unknown's avatar
unknown committed
1084
    goto err;
1085
  }
unknown's avatar
unknown committed
1086

1087 1088 1089
  thd->used_tables= 0;                        // Updated by setup_fields

  // JOIN::prepare calls
1090
  if (unit->prepare(thd, 0, 0, ""))
1091 1092 1093 1094
  {
    send_error(thd);
    goto err_prep;
  }
1095
  if (!text_protocol)
1096
  {
1097 1098
    if (lex->describe)
    {
1099
      if (send_prep_stmt(stmt, 0) || thd->protocol->flush())
1100 1101 1102
        goto err_prep;
    }
    else
unknown's avatar
unknown committed
1103
    {
1104 1105 1106 1107 1108 1109 1110 1111
      /* Make copy of item list, as change_columns may change it */
      List<Item> fields(lex->select_lex.item_list);

      /* Change columns if a procedure like analyse() */
      if (unit->last_procedure &&
          unit->last_procedure->change_columns(fields))
        goto err_prep;

1112 1113 1114 1115 1116
      /*
        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)) ||
1117 1118
          lex->result->send_fields(fields, 0) ||
          thd->protocol->flush())
unknown's avatar
unknown committed
1119 1120
        goto err_prep;
    }
1121
  }
1122
  result= 0;                                    // ok
1123 1124 1125 1126

err_prep:
  unit->cleanup();
err:
1127
  DBUG_RETURN(result);
unknown's avatar
unknown committed
1128 1129
}

1130

1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154
/*
  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);
1155

1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193
  if (tables && (res= open_and_lock_tables(thd, tables)))
  {
    DBUG_RETURN(res);
  }
  res= setup_fields(thd, 0, 0, *values, 0, 0, 0);
  stmt->lex->unit.cleanup();
  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);
1194

1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206
  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
1207
  stmt->lex->unit.cleanup();
1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219
  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
1220
  RETURN VALUE
1221 1222
    0   success
    1   error, sent to client
unknown's avatar
unknown committed
1223
   -1   error, not sent to client
1224 1225 1226 1227 1228 1229 1230 1231
*/
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;
1232

1233 1234 1235 1236 1237
  if (tables && (res= open_and_lock_tables(thd, tables)))
    goto end;

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

unknown's avatar
unknown committed
1238
  // JOIN::prepare calls
1239
  if (lex->unit.prepare(thd, 0, 0, ""))
1240 1241 1242 1243 1244 1245 1246 1247 1248 1249
  {
    res= thd->net.report_error ? -1 : 1;
  }
end:
  lex->unit.cleanup();
  DBUG_RETURN(res);
}


/*
1250
  Validate and prepare for execution CREATE TABLE statement
1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267

  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;
1268
  SELECT_LEX *select_lex= &lex->select_lex;
1269 1270 1271 1272 1273
  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
1274
				  &create_table_local);
1275

unknown's avatar
unknown committed
1276
  if (!(res= create_table_precheck(thd, tables, create_table)) &&
1277 1278 1279
      select_lex->item_list.elements)
  {
    select_lex->resolve_mode= SELECT_LEX::SELECT_MODE;
1280
    res= select_like_statement_test(stmt, tables);
1281 1282
    select_lex->resolve_mode= SELECT_LEX::NOMATTER_MODE;
  }
1283

unknown's avatar
unknown committed
1284
  /* put tables back for PS rexecuting */
1285 1286 1287 1288 1289
  tables= lex->link_first_table_back(tables, create_table,
				     create_table_local);
  DBUG_RETURN(res);
}

unknown's avatar
unknown committed
1290

1291
/*
1292
  Validate and prepare for execution multi update statement
1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314

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


/*
1315
  Validate and prepare for execution multi delete statement
1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334

  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
1335
  uint fake_counter;
1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359
  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;
unknown's avatar
unknown committed
1360
  if ((res= insert_precheck(stmt->thd, tables)))
1361 1362 1363 1364
    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
1365
  lex->select_lex.table_list.first= (byte*) first_local_table->next;
1366 1367 1368 1369 1370
  /*
    insert/replace from SELECT give its SELECT_LEX for SELECT,
    and item_list belong to SELECT
  */
  lex->select_lex.resolve_mode= SELECT_LEX::SELECT_MODE;
1371 1372
  res= select_like_statement_test(stmt, tables);
  /* revert changes*/
unknown's avatar
unknown committed
1373
  lex->select_lex.table_list.first= (byte*) first_local_table;
1374 1375 1376 1377 1378
  lex->select_lex.resolve_mode= SELECT_LEX::INSERT_MODE;
  return res;
}


unknown's avatar
unknown committed
1379
/*
1380 1381 1382 1383 1384 1385 1386
  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
1387
*/
unknown's avatar
unknown committed
1388
static int send_prepare_results(Prepared_statement *stmt, bool text_protocol)
unknown's avatar
unknown committed
1389
{   
1390
  THD *thd= stmt->thd;
1391
  LEX *lex= stmt->lex;
1392 1393
  SELECT_LEX *select_lex= &lex->select_lex;
  TABLE_LIST *tables=(TABLE_LIST*) select_lex->table_list.first;
1394
  enum enum_sql_command sql_command= lex->sql_command;
unknown's avatar
unknown committed
1395 1396
  int res= 0;
  DBUG_ENTER("send_prepare_results");
1397
  DBUG_PRINT("enter",("command: %d, param_count: %ld",
1398
                      sql_command, stmt->param_count));
1399

1400 1401
  if ((&lex->select_lex != lex->all_selects_list ||
       lex->time_zone_tables_used) &&
1402 1403 1404
      lex->unit.create_total_list(thd, lex, &tables))
    DBUG_RETURN(1);

1405
  switch (sql_command) {
1406
  case SQLCOM_REPLACE:
unknown's avatar
unknown committed
1407
  case SQLCOM_INSERT:
unknown's avatar
unknown committed
1408 1409 1410
    res= mysql_test_insert(stmt, tables, lex->field_list,
			   lex->many_values,
			   select_lex->item_list, lex->value_list,
1411
			   lex->duplicates);
unknown's avatar
unknown committed
1412 1413 1414
    break;

  case SQLCOM_UPDATE:
unknown's avatar
unknown committed
1415 1416 1417
    res= mysql_test_update(stmt, tables);
    break;

unknown's avatar
unknown committed
1418
  case SQLCOM_DELETE:
unknown's avatar
unknown committed
1419
    res= mysql_test_delete(stmt, tables);
unknown's avatar
unknown committed
1420 1421 1422
    break;

  case SQLCOM_SELECT:
unknown's avatar
unknown committed
1423
    if ((res= mysql_test_select(stmt, tables, text_protocol)))
1424 1425 1426
      goto error;
    /* Statement and field info has already been sent */
    DBUG_RETURN(0);
unknown's avatar
unknown committed
1427

1428
  case SQLCOM_CREATE_TABLE:
unknown's avatar
unknown committed
1429
    res= mysql_test_create_table(stmt, tables);
1430 1431 1432
    break;
  
  case SQLCOM_DO:
unknown's avatar
unknown committed
1433 1434
    res= mysql_test_do_fields(stmt, tables, lex->insert_list);
    break;
1435 1436

  case SQLCOM_SET_OPTION:
unknown's avatar
unknown committed
1437
    res= mysql_test_set_fields(stmt, tables, &lex->var_list);
1438 1439 1440
    break;

  case SQLCOM_DELETE_MULTI:
unknown's avatar
unknown committed
1441
    res= mysql_test_multidelete(stmt, tables);
1442 1443 1444
    break;
  
  case SQLCOM_UPDATE_MULTI:
unknown's avatar
unknown committed
1445
    res= mysql_test_multiupdate(stmt, tables);
1446 1447 1448
    break;

  case SQLCOM_INSERT_SELECT:
1449
  case SQLCOM_REPLACE_SELECT:
unknown's avatar
unknown committed
1450
    res= mysql_test_insert_select(stmt, tables);
1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472
    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
1473
  default:
1474 1475
    /*
      All other is not supported yet
1476
    */
1477 1478 1479
    res= -1;
    my_error(ER_UNSUPPORTED_PS, MYF(0));
    goto error;
unknown's avatar
unknown committed
1480
  }
unknown's avatar
unknown committed
1481
  if (res == 0)
1482 1483
    DBUG_RETURN(text_protocol? 0 : (send_prep_stmt(stmt, 0) ||
                                    thd->protocol->flush()));
1484 1485 1486
error:
  if (res < 0)
    send_error(thd, thd->killed ? ER_SERVER_SHUTDOWN : 0);
1487
  DBUG_RETURN(1);
unknown's avatar
unknown committed
1488 1489
}

unknown's avatar
unknown committed
1490
/*
1491 1492
  Initialize array of parameters in statement from LEX.
  (We need to have quick access to items by number in mysql_stmt_get_longdata).
1493
  This is to avoid using malloc/realloc in the parser.
unknown's avatar
unknown committed
1494
*/
unknown's avatar
unknown committed
1495

1496
static bool init_param_array(Prepared_statement *stmt)
unknown's avatar
unknown committed
1497
{
1498
  LEX *lex= stmt->lex;
1499
  THD *thd= stmt->thd;
1500 1501
  if ((stmt->param_count= lex->param_list.elements))
  {
1502 1503 1504 1505 1506 1507 1508
    if (stmt->param_count > (uint) UINT_MAX16)
    {
      /* Error code to be defined in 5.0 */
      send_error(thd, ER_UNKNOWN_ERROR,
                 "Prepared statement contains too many placeholders.");
      return 1;
    }
1509 1510 1511 1512
    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
1513
                       alloc_root(stmt->thd->mem_root,
1514 1515 1516
                                  sizeof(Item_param*) * stmt->param_count);
    if (!stmt->param_array)
    {
1517
      send_error(thd, ER_OUT_OF_RESOURCES);
1518
      return 1;
1519 1520 1521 1522 1523 1524 1525 1526
    }
    for (to= stmt->param_array;
         to < stmt->param_array + stmt->param_count;
         ++to)
    {
      *to= param_iterator++;
    }
  }
unknown's avatar
unknown committed
1527
  return 0;
unknown's avatar
unknown committed
1528
}
1529

unknown's avatar
unknown committed
1530
/*
1531 1532 1533
  Given a query string with parameter markers, create a Prepared Statement
  from it and send PS info back to the client.
  
1534 1535
  SYNOPSIS
    mysql_stmt_prepare()
1536 1537 1538
      packet         query to be prepared 
      packet_length  query string length, including ignored trailing NULL or 
                     quote char.
1539
      name           NULL or statement name. For unnamed statements binary PS
1540
                     protocol is used, for named statements text protocol is 
1541
                     used.
1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555
  RETURN 
    0      OK, statement prepared successfully
    other  Error
  
  NOTES
    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. 

    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.
1556
   
unknown's avatar
unknown committed
1557 1558
*/

1559 1560
int mysql_stmt_prepare(THD *thd, char *packet, uint packet_length,
                       LEX_STRING *name)
unknown's avatar
unknown committed
1561
{
1562 1563
  LEX *lex;
  Prepared_statement *stmt= new Prepared_statement(thd);
1564
  int error;
1565
  DBUG_ENTER("mysql_stmt_prepare");
unknown's avatar
unknown committed
1566

unknown's avatar
unknown committed
1567
  DBUG_PRINT("prep_query", ("%s", packet));
unknown's avatar
unknown committed
1568

1569 1570 1571 1572 1573 1574 1575
  /*
    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(com_stmt_prepare, &LOCK_status);

1576
  if (stmt == 0)
1577 1578
  {
    send_error(thd, ER_OUT_OF_RESOURCES);
1579 1580 1581 1582 1583 1584
    DBUG_RETURN(1);
  }

  if (name)
  {
    stmt->name.length= name->length;
unknown's avatar
unknown committed
1585
    if (!(stmt->name.str= memdup_root(stmt->mem_root, (char*)name->str,
1586
                                      name->length)))
1587 1588 1589 1590 1591
    {
      delete stmt;
      send_error(thd, ER_OUT_OF_RESOURCES);
      DBUG_RETURN(1);
    }
1592
  }
1593

1594
  if (thd->stmt_map.insert(thd, stmt))
1595
  {
1596 1597 1598 1599
    /*
      The error is sent in the insert. The statement itself
      will be also deleted there (this is how the hash works).
    */
1600
    DBUG_RETURN(1);
1601
  }
1602

1603 1604
  thd->set_n_backup_statement(stmt, &thd->stmt_backup);
  thd->set_n_backup_item_arena(stmt, &thd->stmt_backup);
unknown's avatar
unknown committed
1605

1606
  if (alloc_query(thd, packet, packet_length))
1607
  {
1608 1609
    thd->restore_backup_statement(stmt, &thd->stmt_backup);
    thd->restore_backup_item_arena(stmt, &thd->stmt_backup);
1610 1611 1612
    /* Statement map deletes statement on erase */
    thd->stmt_map.erase(stmt);
    send_error(thd, ER_OUT_OF_RESOURCES);
1613
    DBUG_RETURN(1);
1614
  }
1615

1616
  mysql_log.write(thd, thd->command, "[%lu] %s", stmt->id, packet);
1617

1618
  thd->current_arena= stmt;
unknown's avatar
unknown committed
1619
  mysql_init_query(thd, (uchar *) thd->query, thd->query_length);
1620 1621
  /* Reset warnings from previous command */
  mysql_reset_errors(thd);
unknown's avatar
unknown committed
1622
  lex= thd->lex;
1623 1624
  lex->safe_to_cache_query= 0;

1625
  error= yyparse((void *)thd) || thd->is_fatal_error ||
1626
         thd->net.report_error || init_param_array(stmt);
1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638
  /*
    While doing context analysis of the query (in send_prepare_results) 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_backup_item_arena(stmt, &thd->stmt_backup);

  if (!error)
    error= send_prepare_results(stmt, test(name));
unknown's avatar
unknown committed
1639

1640
  /* restore to WAIT_PRIOR: QUERY_PRIOR is set inside alloc_query */
unknown's avatar
unknown committed
1641
  if (!(specialflag & SPECIAL_NO_PRIOR))
unknown's avatar
unknown committed
1642
    my_pthread_setprio(pthread_self(),WAIT_PRIOR);
1643
  lex_end(lex);
1644 1645 1646 1647
  thd->restore_backup_statement(stmt, &thd->stmt_backup);
  cleanup_items(stmt->free_list);
  close_thread_tables(thd);
  free_items(thd->free_list);
1648
  thd->rollback_item_tree_changes();
1649 1650
  thd->free_list= 0;
  thd->current_arena= thd;
unknown's avatar
unknown committed
1651

1652
  if (error)
1653
  {
1654 1655
    /* Statement map deletes statement on erase */
    thd->stmt_map.erase(stmt);
unknown's avatar
unknown committed
1656
    stmt= NULL;
1657 1658 1659
    if (thd->net.report_error)
      send_error(thd);
    /* otherwise the error is sent inside yyparse/send_prepare_results */
1660
  }
1661 1662
  else
  {
1663
    stmt->setup_set_params();
1664 1665 1666
    SELECT_LEX *sl= stmt->lex->all_selects_list;
    for (; sl; sl= sl->next_select_in_list())
    {
1667
      /*
unknown's avatar
unknown committed
1668
        Save WHERE, HAVING clause pointers, because they may be changed
1669 1670
        during query optimisation.
      */
1671
      sl->prep_where= sl->where;
unknown's avatar
unknown committed
1672
      sl->prep_having= sl->having;
1673 1674 1675 1676 1677
      /*
        Switch off a temporary flag that prevents evaluation of
        subqueries in statement prepare.
      */
      sl->uncacheable&= ~UNCACHEABLE_PREPARE;
1678
    }
unknown's avatar
unknown committed
1679
    stmt->state= Item_arena::PREPARED;
1680
  }
1681

1682
  DBUG_RETURN(!stmt);
1683
}
1684

1685
/* Reinit statement before execution */
1686

1687 1688 1689
static void reset_stmt_for_execute(Prepared_statement *stmt)
{
  THD *thd= stmt->thd;
1690 1691
  LEX *lex= stmt->lex;
  SELECT_LEX *sl= lex->all_selects_list;
1692

1693
  for (; sl; sl= sl->next_select_in_list())
1694
  {
1695 1696
    /* remove option which was put by mysql_explain_union() */
    sl->options&= ~SELECT_DESCRIBE;
unknown's avatar
unknown committed
1697
    /*
unknown's avatar
unknown committed
1698
      Copy WHERE, HAVING clause pointers to avoid damaging they by optimisation
unknown's avatar
unknown committed
1699
    */
1700
    if (sl->prep_where)
1701
    {
1702
      sl->where= sl->prep_where->copy_andor_structure(thd);
1703 1704
      sl->where->cleanup();
    }
unknown's avatar
unknown committed
1705 1706 1707 1708 1709
    if (sl->prep_having)
    {
      sl->having= sl->prep_having->copy_andor_structure(thd);
      sl->having->cleanup();
    }
1710
    DBUG_ASSERT(sl->join == 0);
unknown's avatar
unknown committed
1711
    ORDER *order;
unknown's avatar
unknown committed
1712
    /* Fix GROUP list */
unknown's avatar
unknown committed
1713 1714
    for (order= (ORDER *)sl->group_list.first; order; order= order->next)
      order->item= &order->item_ptr;
unknown's avatar
unknown committed
1715
    /* Fix ORDER list */
unknown's avatar
unknown committed
1716 1717
    for (order= (ORDER *)sl->order_list.first; order; order= order->next)
      order->item= &order->item_ptr;
unknown's avatar
unknown committed
1718 1719 1720 1721 1722 1723 1724 1725 1726 1727

    /*
      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)
    {
1728
      tables->reinit_before_use(thd);
unknown's avatar
unknown committed
1729
    }
1730

1731 1732 1733 1734
    {
      SELECT_LEX_UNIT *unit= sl->master_unit();
      unit->unclean();
      unit->types.empty();
1735
      /* for derived tables & PS (which can't be reset by Item_subquery) */
1736 1737
      unit->reinit_exec_mechanism();
    }
1738
  }
1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749
  /*
    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.
  */
  for (TABLE_LIST *tables= (TABLE_LIST*) lex->auxilliary_table_list.first;
       tables;
       tables= tables->next)
  {
    tables->reinit_before_use(thd);
  }
1750 1751 1752
  lex->current_select= &lex->select_lex;
  if (lex->result)
    lex->result->cleanup();
1753 1754
}

1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772

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


1773 1774 1775 1776
/*
  Executes previously prepared query.
  If there is any parameters, then replace markers with the data supplied
  from client, and then execute the query.
1777
  SYNOPSIS
1778
    mysql_stmt_execute()
1779 1780 1781
      thd            Current thread
      packet         Query string
      packet_length  Query string length, including terminator character.
1782 1783
*/

1784
void mysql_stmt_execute(THD *thd, char *packet_arg, uint packet_length)
1785
{
1786
  uchar* packet= (uchar*)packet_arg; // gcc 4.0 stgrict-aliasing
1787
  ulong stmt_id= uint4korr(packet);
1788 1789 1790 1791 1792
  /*
    Query text for binary log, or empty string if the query is not put into
    binary log.
  */
  String expanded_query;
unknown's avatar
unknown committed
1793
#ifndef EMBEDDED_LIBRARY
1794
  uchar *packet_end= packet + packet_length - 1;
unknown's avatar
unknown committed
1795
#endif
1796 1797
  Prepared_statement *stmt;
  DBUG_ENTER("mysql_stmt_execute");
1798 1799

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

1801
  statistic_increment(com_stmt_execute, &LOCK_status);
1802 1803
  if (!(stmt= find_prepared_statement(thd, stmt_id, "mysql_stmt_execute",
                                      SEND_ERROR)))
1804 1805
    DBUG_VOID_RETURN;

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

1808
  /* Check if we got an error when sending long data */
unknown's avatar
unknown committed
1809
  if (stmt->state == Item_arena::ERROR)
1810 1811 1812 1813 1814
  {
    send_error(thd, stmt->last_errno, stmt->last_error);
    DBUG_VOID_RETURN;
  }

1815 1816
  DBUG_ASSERT(thd->free_list == NULL);
  mysql_reset_thd_for_next_command(thd);
unknown's avatar
unknown committed
1817
#ifndef EMBEDDED_LIBRARY
1818 1819
  if (stmt->param_count)
  {
1820 1821 1822
    uchar *null_array= packet;
    if (setup_conversion_functions(stmt, &packet, packet_end) ||
        stmt->set_params(stmt, null_array, packet, packet_end,
1823
                         &expanded_query))
1824 1825
      goto set_params_data_err;
  }
unknown's avatar
unknown committed
1826
#else
1827 1828 1829 1830 1831
  /*
    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.
  */
1832
  if (stmt->param_count && stmt->set_params_data(stmt, &expanded_query))
1833
    goto set_params_data_err;
unknown's avatar
unknown committed
1834
#endif
1835
  thd->protocol= &thd->protocol_prep;           // Switch to binary protocol
unknown's avatar
unknown committed
1836
  execute_stmt(thd, stmt, &expanded_query, TRUE);
1837 1838
  thd->protocol= &thd->protocol_simple;         // Use normal protocol
  DBUG_VOID_RETURN;
1839

1840
set_params_data_err:
1841
  reset_stmt_params(stmt);
1842
  my_error(ER_WRONG_ARGUMENTS, MYF(0), "mysql_stmt_execute");
1843
  send_error(thd);
unknown's avatar
unknown committed
1844 1845 1846
  DBUG_VOID_RETURN;
}

1847

unknown's avatar
unknown committed
1848
/*
1849
  Execute prepared statement using parameter values from
unknown's avatar
unknown committed
1850 1851 1852
  lex->prepared_stmt_params and send result to the client using text protocol.
*/

1853
void mysql_sql_stmt_execute(THD *thd, LEX_STRING *stmt_name)
unknown's avatar
unknown committed
1854
{
1855
  Prepared_statement *stmt;
1856
  /*
1857 1858
    Query text for binary log, or empty string if the query is not put into
    binary log.
1859
  */
1860
  String expanded_query;
1861
  DBUG_ENTER("mysql_sql_stmt_execute");
1862

1863 1864
  /* See comment for statistics_increment in mysql_stmt_prepare */
  statistic_increment(com_stmt_execute, &LOCK_status);
1865 1866
  if (!(stmt= (Prepared_statement*)thd->stmt_map.find_by_name(stmt_name)))
  {
1867 1868 1869 1870
    my_error(ER_UNKNOWN_STMT_HANDLER, MYF(0), stmt_name->length,
             stmt_name->str, "EXECUTE");
    send_error(thd);
    DBUG_VOID_RETURN;
1871 1872
  }

unknown's avatar
unknown committed
1873 1874
  if (stmt->param_count != thd->lex->prepared_stmt_params.elements)
  {
1875
    my_error(ER_WRONG_ARGUMENTS, MYF(0), "EXECUTE");
unknown's avatar
unknown committed
1876 1877 1878 1879
    send_error(thd);
    DBUG_VOID_RETURN;
  }

1880
  DBUG_ASSERT(thd->free_list == NULL);
1881 1882
  /* Must go before setting variables, as it clears thd->user_var_events */
  mysql_reset_thd_for_next_command(thd);
1883
  thd->set_n_backup_statement(stmt, &thd->stmt_backup);
1884 1885
  if (stmt->set_params_from_vars(stmt,
                                 thd->stmt_backup.lex->prepared_stmt_params,
1886
                                 &expanded_query))
unknown's avatar
unknown committed
1887
  {
1888
    my_error(ER_WRONG_ARGUMENTS, MYF(0), "EXECUTE");
unknown's avatar
unknown committed
1889 1890
    send_error(thd);
  }
1891
  thd->command= COM_EXECUTE; /* For nice messages in general log */
unknown's avatar
unknown committed
1892
  execute_stmt(thd, stmt, &expanded_query, FALSE);
1893 1894 1895
  DBUG_VOID_RETURN;
}

1896

1897 1898
/*
  Execute prepared statement.
1899 1900 1901 1902
  SYNOPSIS
    execute_stmt()
      thd            Current thread
      stmt           Statement to execute
1903
      expanded_query If binary log is enabled, query string with parameter
1904 1905 1906
                     placeholders replaced with actual values. Otherwise empty
                     string.
  NOTES
1907
  Caller must set parameter values and thd::protocol.
unknown's avatar
unknown committed
1908
  thd->free_list is assumed to be garbage.
1909
*/
1910

1911
static void execute_stmt(THD *thd, Prepared_statement *stmt,
1912
                         String *expanded_query, bool set_context)
1913 1914
{
  DBUG_ENTER("execute_stmt");
1915
  if (set_context)
1916
    thd->set_n_backup_statement(stmt, &thd->stmt_backup);
1917
  reset_stmt_for_execute(stmt);
1918 1919 1920

  if (expanded_query->length() &&
      alloc_query(thd, (char *)expanded_query->ptr(),
1921 1922 1923 1924 1925
                  expanded_query->length()+1))
  {
    my_error(ER_OUTOFMEMORY, 0, expanded_query->length());
    DBUG_VOID_RETURN;
  }
1926
  mysql_log.write(thd, thd->command, "[%lu] %s", stmt->id, thd->query);
1927 1928 1929 1930 1931
  /*
    At first execution of prepared statement we will perform logical
    transformations of the query tree (i.e. negations elimination).
    This should be done permanently on the parse tree of this statement.
  */
1932
  thd->current_arena= stmt;
1933

unknown's avatar
unknown committed
1934
  if (!(specialflag & SPECIAL_NO_PRIOR))
1935
    my_pthread_setprio(pthread_self(),QUERY_PRIOR);
1936
  mysql_execute_command(thd);
1937
  thd->lex->unit.cleanup();
unknown's avatar
unknown committed
1938
  if (!(specialflag & SPECIAL_NO_PRIOR))
1939
    my_pthread_setprio(pthread_self(), WAIT_PRIOR);
1940 1941 1942 1943 1944 1945 1946 1947
  /*
    'start_time' is set in dispatch_command, but THD::query will
    be freed when we return from this function. So let's log the slow
    query here.
  */
  log_slow_statement(thd);
  /* Prevent from second logging in the end of dispatch_command */
  thd->enable_slow_log= FALSE;
unknown's avatar
unknown committed
1948

1949 1950
  /* Free Items that were created during this execution of the PS. */
  free_items(thd->free_list);
1951
  thd->free_list= 0;
unknown's avatar
unknown committed
1952 1953
  if (stmt->state == Item_arena::PREPARED)
    stmt->state= Item_arena::EXECUTED;
1954
  thd->current_arena= thd;
unknown's avatar
unknown committed
1955
  cleanup_items(stmt->free_list);
1956
  thd->rollback_item_tree_changes();
1957
  reset_stmt_params(stmt);
1958
  close_thread_tables(thd);                    // to close derived tables
1959
  thd->set_statement(&thd->stmt_backup);
unknown's avatar
unknown committed
1960 1961 1962
  DBUG_VOID_RETURN;
}

1963

unknown's avatar
unknown committed
1964
/*
1965
  Reset a prepared statement in case there was a recoverable error.
1966 1967
  SYNOPSIS
    mysql_stmt_reset()
1968 1969
      thd       Thread handle
      packet	Packet with stmt id 
1970 1971

  DESCRIPTION
1972 1973 1974 1975 1976 1977 1978
    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
1979 1980
*/

1981
void mysql_stmt_reset(THD *thd, char *packet)
unknown's avatar
unknown committed
1982
{
1983
  /* There is always space for 4 bytes in buffer */
1984
  ulong stmt_id= uint4korr(packet);
1985 1986
  Prepared_statement *stmt;
  
1987
  DBUG_ENTER("mysql_stmt_reset");
unknown's avatar
unknown committed
1988

1989
  statistic_increment(com_stmt_reset, &LOCK_status);
1990 1991
  if (!(stmt= find_prepared_statement(thd, stmt_id, "mysql_stmt_reset",
                                      SEND_ERROR)))
1992 1993
    DBUG_VOID_RETURN;

unknown's avatar
unknown committed
1994
  stmt->state= Item_arena::PREPARED;
1995

1996 1997 1998 1999 2000
  /* 
    Clear parameters from data which could be set by 
    mysql_stmt_send_long_data() call.
  */
  reset_stmt_params(stmt);
2001

2002
  mysql_reset_thd_for_next_command(thd);
2003
  send_ok(thd);
2004
  
2005 2006 2007 2008 2009
  DBUG_VOID_RETURN;
}


/*
2010 2011
  Delete a prepared statement from memory.
  Note: we don't send any reply to that command. 
2012 2013
*/

unknown's avatar
unknown committed
2014
void mysql_stmt_free(THD *thd, char *packet)
2015
{
2016
  /* There is always space for 4 bytes in packet buffer */
2017
  ulong stmt_id= uint4korr(packet);
2018 2019
  Prepared_statement *stmt;

unknown's avatar
unknown committed
2020
  DBUG_ENTER("mysql_stmt_free");
2021

2022
  statistic_increment(com_stmt_close, &LOCK_status);
2023 2024
  if (!(stmt= find_prepared_statement(thd, stmt_id, "mysql_stmt_close",
                                      DONT_SEND_ERROR)))
2025
    DBUG_VOID_RETURN;
2026 2027 2028

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

2032 2033

/*
2034
  Long data in pieces from client
2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051

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

2052
void mysql_stmt_get_longdata(THD *thd, char *packet, ulong packet_length)
2053
{
2054 2055
  ulong stmt_id;
  uint param_number;
2056
  Prepared_statement *stmt;
2057 2058
  Item_param *param;
  char *packet_end= packet + packet_length - 1;
2059
  
2060 2061
  DBUG_ENTER("mysql_stmt_get_longdata");

2062
  statistic_increment(com_stmt_send_long_data, &LOCK_status);
unknown's avatar
unknown committed
2063
#ifndef EMBEDDED_LIBRARY
2064 2065
  /* Minimal size of long data packet is 6 bytes */
  if ((ulong) (packet_end - packet) < MYSQL_LONG_DATA_HEADER)
2066
  {
2067
    my_error(ER_WRONG_ARGUMENTS, MYF(0), "mysql_stmt_send_long_data");
2068 2069
    DBUG_VOID_RETURN;
  }
unknown's avatar
unknown committed
2070
#endif
2071

2072 2073
  stmt_id= uint4korr(packet);
  packet+= 4;
2074

2075
  if (!(stmt=find_prepared_statement(thd, stmt_id, "mysql_stmt_send_long_data",
2076
                                     DONT_SEND_ERROR)))
2077 2078
    DBUG_VOID_RETURN;

2079 2080
  param_number= uint2korr(packet);
  packet+= 2;
unknown's avatar
unknown committed
2081
#ifndef EMBEDDED_LIBRARY
2082 2083
  if (param_number >= stmt->param_count)
  {
unknown's avatar
unknown committed
2084
    /* Error will be sent in execute call */
unknown's avatar
unknown committed
2085
    stmt->state= Item_arena::ERROR;
unknown's avatar
unknown committed
2086
    stmt->last_errno= ER_WRONG_ARGUMENTS;
2087 2088
    sprintf(stmt->last_error, ER(ER_WRONG_ARGUMENTS),
            "mysql_stmt_send_long_data");
2089 2090
    DBUG_VOID_RETURN;
  }
unknown's avatar
unknown committed
2091 2092
#endif

2093 2094
  param= stmt->param_array[param_number];

unknown's avatar
unknown committed
2095
#ifndef EMBEDDED_LIBRARY
2096
  if (param->set_longdata(packet, (ulong) (packet_end - packet)))
unknown's avatar
unknown committed
2097
#else
2098
  if (param->set_longdata(thd->extra_data, thd->extra_length))
unknown's avatar
unknown committed
2099
#endif
2100
  {
unknown's avatar
unknown committed
2101
    stmt->state= Item_arena::ERROR;
2102 2103 2104
    stmt->last_errno= ER_OUTOFMEMORY;
    sprintf(stmt->last_error, ER(ER_OUTOFMEMORY), 0);
  }
2105 2106
  DBUG_VOID_RETURN;
}
unknown's avatar
unknown committed
2107

2108 2109 2110 2111

Prepared_statement::Prepared_statement(THD *thd_arg)
  :Statement(thd_arg),
  thd(thd_arg),
2112
  param_array(0),
2113
  param_count(0),
2114
  last_errno(0)
2115 2116 2117 2118
{
  *last_error= '\0';
}

2119

2120 2121 2122
void Prepared_statement::setup_set_params()
{
  /* Setup binary logging */
2123 2124
  if (mysql_bin_log.is_open() && is_update_query(lex->sql_command) ||
      mysql_log.is_open() || mysql_slow_log.is_open())
2125
  {
2126
    set_params_from_vars= insert_params_from_vars_with_log;
2127
#ifndef EMBEDDED_LIBRARY
2128
    set_params= insert_params_withlog;
2129
#else
2130
    set_params_data= emb_insert_params_withlog;
2131 2132 2133
#endif
  }
  else
2134 2135
  {
    set_params_from_vars= insert_params_from_vars;
2136
#ifndef EMBEDDED_LIBRARY
2137
    set_params= insert_params;
2138
#else
2139
    set_params_data= emb_insert_params;
2140
#endif
2141
  }
2142 2143
}

2144

2145 2146 2147
Prepared_statement::~Prepared_statement()
{
  free_items(free_list);
2148
  delete lex->result;
2149 2150 2151
}


2152
Item_arena::Type Prepared_statement::type() const
2153 2154 2155 2156
{
  return PREPARED_STATEMENT;
}