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

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

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

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

1089 1090 1091
  thd->used_tables= 0;                        // Updated by setup_fields

  // JOIN::prepare calls
1092
  if (unit->prepare(thd, 0, 0, ""))
1093 1094 1095 1096
  {
    send_error(thd);
    goto err_prep;
  }
1097
  if (!text_protocol)
1098
  {
1099 1100
    if (lex->describe)
    {
1101
      if (send_prep_stmt(stmt, 0) || thd->protocol->flush())
1102 1103 1104
        goto err_prep;
    }
    else
unknown's avatar
unknown committed
1105
    {
1106 1107 1108 1109 1110 1111 1112 1113
      /* 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;

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

err_prep:
  unit->cleanup();
err:
1129
  DBUG_RETURN(result);
unknown's avatar
unknown committed
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 1155 1156
/*
  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);
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 1194 1195
  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);
1196

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

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

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

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


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

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

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

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

unknown's avatar
unknown committed
1292

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

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


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

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


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

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

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

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

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

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

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

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

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

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

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

1498
static bool init_param_array(Prepared_statement *stmt)
unknown's avatar
unknown committed
1499
{
1500
  LEX *lex= stmt->lex;
1501
  THD *thd= stmt->thd;
1502 1503
  if ((stmt->param_count= lex->param_list.elements))
  {
1504 1505 1506 1507 1508 1509 1510
    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;
    }
1511 1512 1513 1514
    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
1515
                       alloc_root(stmt->thd->mem_root,
1516 1517 1518
                                  sizeof(Item_param*) * stmt->param_count);
    if (!stmt->param_array)
    {
1519
      send_error(thd, ER_OUT_OF_RESOURCES);
1520
      return 1;
1521 1522 1523 1524 1525 1526 1527 1528
    }
    for (to= stmt->param_array;
         to < stmt->param_array + stmt->param_count;
         ++to)
    {
      *to= param_iterator++;
    }
  }
unknown's avatar
unknown committed
1529
  return 0;
unknown's avatar
unknown committed
1530
}
1531

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

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

unknown's avatar
unknown committed
1569
  DBUG_PRINT("prep_query", ("%s", packet));
unknown's avatar
unknown committed
1570

1571 1572 1573 1574 1575 1576 1577
  /*
    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);

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

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

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

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

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

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

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

1627
  error= yyparse((void *)thd) || thd->is_fatal_error ||
1628
         thd->net.report_error || init_param_array(stmt);
1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640
  /*
    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
1641

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

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

1684
  DBUG_RETURN(!stmt);
1685
}
1686

1687
/* Reinit statement before execution */
1688

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

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

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

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

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

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


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

1786
void mysql_stmt_execute(THD *thd, char *packet, uint packet_length)
1787 1788
{
  ulong stmt_id= uint4korr(packet);
1789 1790 1791 1792 1793
  /*
    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
1794
#ifndef EMBEDDED_LIBRARY
1795
  uchar *packet_end= (uchar *) packet + packet_length - 1;
unknown's avatar
unknown committed
1796
#endif
1797 1798
  Prepared_statement *stmt;
  DBUG_ENTER("mysql_stmt_execute");
1799 1800

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

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

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

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

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

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

1848

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

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

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

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

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

1897

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

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

  if (expanded_query->length() &&
      alloc_query(thd, (char *)expanded_query->ptr(),
1922 1923 1924 1925 1926
                  expanded_query->length()+1))
  {
    my_error(ER_OUTOFMEMORY, 0, expanded_query->length());
    DBUG_VOID_RETURN;
  }
1927
  mysql_log.write(thd, thd->command, "[%lu] %s", stmt->id, thd->query);
1928 1929 1930 1931 1932
  /*
    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.
  */
1933
  thd->current_arena= stmt;
1934

unknown's avatar
unknown committed
1935
  if (!(specialflag & SPECIAL_NO_PRIOR))
1936
    my_pthread_setprio(pthread_self(),QUERY_PRIOR);
1937
  mysql_execute_command(thd);
1938
  thd->lex->unit.cleanup();
unknown's avatar
unknown committed
1939
  if (!(specialflag & SPECIAL_NO_PRIOR))
1940
    my_pthread_setprio(pthread_self(), WAIT_PRIOR);
1941 1942 1943 1944 1945 1946 1947 1948
  /*
    '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
1949

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

1964

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

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

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

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

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

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

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


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

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

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

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

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

2033 2034

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

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

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

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

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

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

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

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

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

2109 2110 2111 2112

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

2120

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

2145

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


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