sql_prepare.cc 82.7 KB
Newer Older
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 */
16 17

/**********************************************************************
18
This file contains the implementation of prepared statements.
19

20
When one prepares a statement:
21

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

38
When one executes a statement:
39

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

57
When one supplies long data for a placeholder:
58

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

70 71 72
***********************************************************************/

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

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

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

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

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

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

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

kent@mysql.com's avatar
kent@mysql.com committed
147

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


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

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

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

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

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

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

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

195

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

  SYNOPSIS
    send_prep_stmt()

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

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

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

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

238
  thd->client_stmt_id= stmt->id;
hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
239
  thd->client_param_count= stmt->param_count;
240
  thd->clear_error();
hf@deer.(none)'s avatar
hf@deer.(none) committed
241

hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
242
  return 0;
243
}
konstantin@oak.local's avatar
konstantin@oak.local committed
244
#endif /*!EMBEDDED_LIBRARY*/
245

246 247

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

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

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

  RETURN VALUE
    Length of data piece.
262 263
*/

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

venu@myvenu.com's avatar
venu@myvenu.com committed
305
 /*
306 307
   Data conversion routines.

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

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

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

322 323
  RETURN VALUE
    none
324 325
*/

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

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

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

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

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

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

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

417
#ifndef EMBEDDED_LIBRARY
418 419 420 421 422 423 424

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

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

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

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

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

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

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

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

488

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

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

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

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

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

}

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

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

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

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

549 550

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

557

konstantin@mysql.com's avatar
konstantin@mysql.com committed
558
#undef get_param_length
559 560 561

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

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

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

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

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

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

  RETURN VALUE
   0 if success, 1 otherwise
693 694
*/

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

707
  if (query->copy(stmt->query, stmt->query_length, default_charset_info))
708
    DBUG_RETURN(1);
konstantin@mysql.com's avatar
konstantin@mysql.com committed
709

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

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

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

736

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

konstantin@mysql.com's avatar
konstantin@mysql.com committed
744
  DBUG_ENTER("insert_params");
745

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

766

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

773
  DBUG_ENTER("setup_conversion_functions");
774

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

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

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

802 803
#else

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

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

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

822
  DBUG_ENTER("emb_insert_params");
823

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

848

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

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

860
  DBUG_ENTER("emb_insert_params_withlog");
861

862
  if (query->copy(stmt->query, stmt->query_length, default_charset_info))
863
    DBUG_RETURN(1);
konstantin@mysql.com's avatar
konstantin@mysql.com committed
864

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

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

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

hf@deer.(none)'s avatar
hf@deer.(none) committed
895 896
#endif /*!EMBEDDED_LIBRARY*/

bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
897

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

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

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

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

934

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

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

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

  DBUG_ENTER("insert_params_from_vars");

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

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

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

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

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

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

1007
/*
1008
  Validate INSERT statement.
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
1009

1010
  SYNOPSIS
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
1011
    mysql_test_insert()
1012 1013
      stmt               prepared statement
      tables             global/local table list
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
1014

1015
  RETURN VALUE
1016 1017
    FALSE                success
    TRUE                 error, error message is set in THD
1018
*/
monty@mysql.com's avatar
monty@mysql.com committed
1019

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

1034 1035
  if (insert_precheck(thd, table_list))
    goto error;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
1036

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

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

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

monty@mysql.com's avatar
monty@mysql.com committed
1060 1061 1062
    if (mysql_prepare_insert(thd, table_list, table_list->table,
                             fields, values, update_fields, update_values,
                             duplic, &unused_conds, FALSE))
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
1063
      goto error;
1064

1065 1066
    value_count= values->elements;
    its.rewind();
1067

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

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


/*
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
1097 1098
  Validate UPDATE statement

1099
  SYNOPSIS
1100
    mysql_test_update()
1101 1102
      stmt               prepared statement
      tables             list of tables used in this query
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
1103

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

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

bell@sanja.is.com.ua's avatar
merge  
bell@sanja.is.com.ua committed
1123
  if (update_precheck(thd, table_list))
1124 1125
    goto error;

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

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

    if (!lock_tables(thd, table_list, table_count, &need_reopen))
      break;
    if (!need_reopen)
      goto error;
1145
    close_tables_for_reopen(thd, &table_list);
1146
  }
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
1147

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

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

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

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


/*
1192
  Validate DELETE statement.
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
1193

1194
  SYNOPSIS
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
1195
    mysql_test_delete()
1196 1197
      stmt               prepared statement
      tables             list of tables used in this query
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
1198

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

static bool mysql_test_delete(Prepared_statement *stmt,
                              TABLE_LIST *table_list)
1206
{
1207
  THD *thd= stmt->thd;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
1208 1209
  LEX *lex= stmt->lex;
  DBUG_ENTER("mysql_test_delete");
1210

1211 1212 1213
  if (delete_precheck(thd, table_list) ||
      open_and_lock_tables(thd, table_list))
    goto error;
1214

1215
  if (!table_list->table)
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
1216
  {
1217 1218 1219
    my_error(ER_VIEW_DELETE_MERGE_VIEW, MYF(0),
             table_list->view_db.str, table_list->view_name.str);
    goto error;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
1220
  }
1221 1222 1223

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

bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
1227

1228
/*
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
1229 1230
  Validate SELECT statement.

1231
  SYNOPSIS
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
1232
    mysql_test_select()
1233 1234 1235 1236 1237 1238
      stmt               prepared statement
      tables             list of tables used in the query

  DESCRIPTION
    In case of success, if this query is not EXPLAIN, send column list info
    back to the client.
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
1239

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

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

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

hf@deer.(none)'s avatar
hf@deer.(none) committed
1256
#ifndef NO_EMBEDDED_ACCESS_CHECKS
1257 1258 1259
  ulong privilege= lex->exchange ? SELECT_ACL | FILE_ACL : SELECT_ACL;
  if (tables)
  {
hf@deer.(none)'s avatar
hf@deer.(none) committed
1260
    if (check_table_access(thd, privilege, tables,0))
1261
      goto error;
1262
  }
1263
  else if (check_access(thd, privilege, any_db,0,0,0,0))
1264
    goto error;
hf@deer.(none)'s avatar
hf@deer.(none) committed
1265
#endif
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
1266

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

1273
  if (open_and_lock_tables(thd, tables))
1274
    goto error;
1275

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

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

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

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

1309

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

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

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

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

  DBUG_ENTER("mysql_test_do_fields");
1331 1332
  if (tables && check_table_access(thd, SELECT_ACL, tables, 0))
    DBUG_RETURN(TRUE);
1333

1334
  if (open_and_lock_tables(thd, tables))
1335
    DBUG_RETURN(TRUE);
1336
  DBUG_RETURN(setup_fields(thd, 0, *values, 0, 0, 0));
1337 1338 1339 1340 1341 1342 1343 1344
}


/*
  Validate and prepare for execution SET statement expressions

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

  RETURN VALUE
1350 1351
    FALSE                success
    TRUE                 error, error message is set in THD
1352
*/
1353

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

1363 1364
  if (tables && check_table_access(thd, SELECT_ACL, tables, 0) ||
      open_and_lock_tables(thd, tables))
1365
    goto error;
1366

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


/*
  Check internal SELECT of the prepared command

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

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

bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
1393
  RETURN VALUE
1394 1395
    FALSE                success
    TRUE                 error, error message is set in THD
1396
*/
1397 1398 1399 1400

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

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

1408 1409
  if (specific_prepare && (*specific_prepare)(thd))
    DBUG_RETURN(TRUE);
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
1410

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

1413
  /* Calls JOIN::prepare */
1414
  DBUG_RETURN(lex->unit.prepare(thd, 0, setup_tables_done_option));
1415 1416
}

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

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

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

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

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

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


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

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

  RETURN VALUE
1464 1465
    FALSE                success
    TRUE                 error, error message is set in THD
1466
*/
monty@mysql.com's avatar
monty@mysql.com committed
1467

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

1480 1481 1482 1483
  if (create_table_precheck(thd, tables, create_table))
    DBUG_RETURN(TRUE);

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

bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
1489
  /* put tables back for PS rexecuting */
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
1490
  lex->link_first_table_back(create_table, link_to_local);
1491 1492 1493
  DBUG_RETURN(res);
}

bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
1494

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

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

  RETURN VALUE
1505 1506
    FALSE                success
    TRUE                 error, error message is set in THD
1507
*/
1508 1509

static bool mysql_test_multiupdate(Prepared_statement *stmt,
konstantin@mysql.com's avatar
konstantin@mysql.com committed
1510
                                  TABLE_LIST *tables,
1511
                                  bool converted)
1512
{
1513
  /* if we switched from normal update, rights are checked */
bell@sanja.is.com.ua's avatar
merge  
bell@sanja.is.com.ua committed
1514
  if (!converted && multi_update_precheck(stmt->thd, tables))
1515
    return TRUE;
1516 1517 1518

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


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

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

  RETURN VALUE
1531 1532
    FALSE                success
    TRUE                 error, error message in THD is set.
1533
*/
1534 1535

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

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


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

  SYNOPSIS
    mysql_insert_select_prepare_tester()
1568
      thd                thread handle
1569

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

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


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

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

  RETURN VALUE
1604 1605
    FALSE                success
    TRUE                 error, error message is set in THD
1606
*/
monty@mysql.com's avatar
monty@mysql.com committed
1607

1608 1609
static bool mysql_test_insert_select(Prepared_statement *stmt,
                                     TABLE_LIST *tables)
1610 1611 1612
{
  int res;
  LEX *lex= stmt->lex;
monty@mysql.com's avatar
monty@mysql.com committed
1613 1614
  TABLE_LIST *first_local_table;

1615 1616 1617 1618 1619
  if (tables->table)
  {
    // don't allocate insert_values
    tables->table->insert_values=(byte *)1;
  }
monty@mysql.com's avatar
monty@mysql.com committed
1620

1621 1622
  if (insert_precheck(stmt->thd, tables))
    return 1;
1623 1624 1625 1626 1627

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

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


1638
/*
1639 1640 1641
  Perform semantic analysis of the parsed tree and send a response packet
  to the client.

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

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

1652
  RETURN VALUE
1653 1654
    FALSE                success, statement metadata is sent to client
    TRUE                 error, error message is set in THD (but not sent)
1655
*/
1656

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

bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
1670 1671
  lex->first_lists_tables_same();
  tables= lex->query_tables;
1672

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

1677
  switch (sql_command) {
1678
  case SQLCOM_REPLACE:
1679
  case SQLCOM_INSERT:
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
1680
    res= mysql_test_insert(stmt, tables, lex->field_list,
konstantin@mysql.com's avatar
konstantin@mysql.com committed
1681 1682 1683
                           lex->many_values,
                           select_lex->item_list, lex->value_list,
                           lex->duplicates);
1684 1685 1686
    break;

  case SQLCOM_UPDATE:
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
1687
    res= mysql_test_update(stmt, tables);
1688
    /* mysql_test_update returns 2 if we need to switch to multi-update */
1689 1690 1691 1692 1693
    if (res != 2)
      break;

  case SQLCOM_UPDATE_MULTI:
    res= mysql_test_multiupdate(stmt, tables, res == 2);
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
1694 1695
    break;

1696
  case SQLCOM_DELETE:
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
1697
    res= mysql_test_delete(stmt, tables);
1698 1699 1700
    break;

  case SQLCOM_SELECT:
1701 1702 1703 1704 1705 1706 1707
    res= mysql_test_select(stmt, tables, text_protocol);
    if (res == 2)
    {
      /* Statement and field info has already been sent */
      DBUG_RETURN(FALSE);
    }
    break;
1708
  case SQLCOM_CREATE_TABLE:
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
1709
    res= mysql_test_create_table(stmt);
1710
    break;
konstantin@mysql.com's avatar
konstantin@mysql.com committed
1711

1712
  case SQLCOM_DO:
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
1713 1714
    res= mysql_test_do_fields(stmt, tables, lex->insert_list);
    break;
1715 1716

  case SQLCOM_SET_OPTION:
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
1717
    res= mysql_test_set_fields(stmt, tables, &lex->var_list);
1718 1719 1720
    break;

  case SQLCOM_DELETE_MULTI:
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
1721
    res= mysql_test_multidelete(stmt, tables);
1722 1723 1724
    break;

  case SQLCOM_INSERT_SELECT:
1725
  case SQLCOM_REPLACE_SELECT:
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
1726
    res= mysql_test_insert_select(stmt, tables);
1727 1728 1729 1730 1731 1732 1733 1734 1735
    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:
1736 1737 1738
  case SQLCOM_SHOW_ENGINE_LOGS:
  case SQLCOM_SHOW_ENGINE_STATUS:
  case SQLCOM_SHOW_ENGINE_MUTEX:
1739 1740 1741 1742 1743 1744 1745 1746 1747 1748
  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:
1749 1750 1751 1752 1753 1754
  case SQLCOM_ALTER_TABLE:
  case SQLCOM_COMMIT:
  case SQLCOM_CREATE_INDEX:
  case SQLCOM_DROP_INDEX:
  case SQLCOM_ROLLBACK:
  case SQLCOM_TRUNCATE:
1755
  case SQLCOM_CALL:
1756 1757
  case SQLCOM_CREATE_VIEW:
  case SQLCOM_DROP_VIEW:
1758 1759
    break;

1760
  default:
1761
    /* All other statements are not supported yet. */
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
1762
    my_message(ER_UNSUPPORTED_PS, ER(ER_UNSUPPORTED_PS), MYF(0));
1763
    goto error;
1764
  }
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
1765
  if (res == 0)
1766 1767
    DBUG_RETURN(text_protocol? FALSE : (send_prep_stmt(stmt, 0) ||
                                        thd->protocol->flush()));
1768
error:
1769
  DBUG_RETURN(TRUE);
1770 1771
}

venu@myvenu.com's avatar
venu@myvenu.com committed
1772
/*
1773 1774
  Initialize array of parameters in statement from LEX.
  (We need to have quick access to items by number in mysql_stmt_get_longdata).
1775
  This is to avoid using malloc/realloc in the parser.
venu@myvenu.com's avatar
venu@myvenu.com committed
1776
*/
1777

1778
static bool init_param_array(Prepared_statement *stmt)
venu@myvenu.com's avatar
venu@myvenu.com committed
1779
{
1780 1781 1782
  LEX *lex= stmt->lex;
  if ((stmt->param_count= lex->param_list.elements))
  {
1783 1784 1785
    if (stmt->param_count > (uint) UINT_MAX16)
    {
      /* Error code to be defined in 5.0 */
1786 1787
      my_message(ER_PS_MANY_PARAM, ER(ER_PS_MANY_PARAM), MYF(0));
      return TRUE;
1788
    }
1789 1790 1791 1792
    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 **)
1793
                       alloc_root(stmt->thd->mem_root,
1794 1795
                                  sizeof(Item_param*) * stmt->param_count);
    if (!stmt->param_array)
1796
      return TRUE;
1797 1798 1799 1800 1801 1802 1803
    for (to= stmt->param_array;
         to < stmt->param_array + stmt->param_count;
         ++to)
    {
      *to= param_iterator++;
    }
  }
1804
  return FALSE;
venu@myvenu.com's avatar
venu@myvenu.com committed
1805
}
1806

1807

1808
/*
1809
  COM_STMT_PREPARE handler.
konstantin@mysql.com's avatar
konstantin@mysql.com committed
1810

1811 1812
  SYNOPSIS
    mysql_stmt_prepare()
1813 1814 1815 1816 1817 1818 1819
      packet             query to be prepared
      packet_length      query string length, including ignored
                         trailing NULL or quote char.

  DESCRIPTION
    Given a query string with parameter markers, create a prepared
    statement from it and send PS info back to the client.
1820

1821
  NOTES
konstantin@mysql.com's avatar
konstantin@mysql.com committed
1822 1823 1824 1825
    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.
1826 1827

    If parameter markers are found in the query, then store the information
konstantin@mysql.com's avatar
konstantin@mysql.com committed
1828 1829
    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
1830
    field items.
konstantin@mysql.com's avatar
konstantin@mysql.com committed
1831

1832 1833 1834
  RETURN VALUE
    none: in case of success a new statement id and metadata is sent
    to the client, otherwise an error message is set in THD.
1835 1836
*/

1837
void mysql_stmt_prepare(THD *thd, const char *packet, uint packet_length)
1838
{
1839
  Prepared_statement *stmt;
1840
  bool error;
1841
  DBUG_ENTER("mysql_stmt_prepare");
1842

bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
1843
  DBUG_PRINT("prep_query", ("%s", packet));
1844

1845 1846 1847 1848
  /* First of all clear possible warnings from the previous command */
  mysql_reset_thd_for_next_command(thd);

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

1851
  if (thd->stmt_map.insert(stmt))
1852 1853
  {
    delete stmt;
1854
    DBUG_VOID_RETURN;                           /* out of memory */
1855
  }
1856

1857
  /* Reset warnings from previous command */
monty@mysql.com's avatar
monty@mysql.com committed
1858
  mysql_reset_errors(thd, 0);
1859 1860 1861
  sp_cache_flush_obsolete(&thd->sp_proc_cache);
  sp_cache_flush_obsolete(&thd->sp_func_cache);

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

1865
  error= stmt->prepare(packet, packet_length);
1866 1867

  if (!(specialflag & SPECIAL_NO_PRIOR))
venu@myvenu.com's avatar
venu@myvenu.com committed
1868
    my_pthread_setprio(pthread_self(),WAIT_PRIOR);
1869

1870
  if (error)
1871
  {
1872 1873
    /* Statement map deletes statement on erase */
    thd->stmt_map.erase(stmt);
1874 1875
  }
  else
1876
    general_log_print(thd, COM_STMT_PREPARE, "[%lu] %s", stmt->id, packet);
1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911

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

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

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

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

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

  if (lex->prepared_stmt_code_is_varref)
  {
    /* This is PREPARE stmt FROM or EXECUTE IMMEDIATE @var. */
    String str;
    CHARSET_INFO *to_cs= thd->variables.collation_connection;
    bool needs_conversion;
    user_var_entry *entry;
1912
    String *var_value= &str;
1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925
    uint32 unused, len;
    /*
      Convert @var contents to string in connection character set. Although
      it is known that int/real/NULL value cannot be a valid query we still
      convert it for error messages to be uniform.
    */
    if ((entry=
         (user_var_entry*)hash_search(&thd->user_vars,
                                      (byte*)lex->prepared_stmt_code.str,
                                      lex->prepared_stmt_code.length))
        && entry->value)
    {
      my_bool is_var_null;
1926
      var_value= entry->val_str(&is_var_null, &str, NOT_FIXED_DEC);
1927 1928 1929 1930 1931
      /*
        NULL value of variable checked early as entry->value so here
        we can't get NULL in normal conditions
      */
      DBUG_ASSERT(!is_var_null);
1932
      if (!var_value)
1933 1934 1935 1936 1937 1938 1939 1940
        goto end;
    }
    else
    {
      /*
        variable absent or equal to NULL, so we need to set variable to
        something reasonable to get a readable error message during parsing
      */
1941
      str.set(STRING_WITH_LEN("NULL"), &my_charset_latin1);
1942 1943
    }

1944 1945 1946
    needs_conversion= String::needs_conversion(var_value->length(),
                                               var_value->charset(), to_cs,
                                               &unused);
1947

1948 1949
    len= (needs_conversion ? var_value->length() * to_cs->mbmaxlen :
          var_value->length());
1950 1951 1952 1953 1954 1955
    if (!(query_str= alloc_root(thd->mem_root, len+1)))
      goto end;

    if (needs_conversion)
    {
      uint dummy_errors;
1956 1957 1958
      len= copy_and_convert(query_str, len, to_cs, var_value->ptr(),
                            var_value->length(), var_value->charset(),
                            &dummy_errors);
1959 1960
    }
    else
1961 1962
      memcpy(query_str, var_value->ptr(), var_value->length());
    query_str[len]= '\0';                       // Safety (mostly for debug)
1963
    *query_len= len;
1964
  }
1965
  else
monty@mysql.com's avatar
monty@mysql.com committed
1966
  {
1967 1968
    query_str= lex->prepared_stmt_code.str;
    *query_len= lex->prepared_stmt_code.length;
monty@mysql.com's avatar
monty@mysql.com committed
1969
  }
1970 1971
end:
  return query_str;
1972 1973
}

monty@mysql.com's avatar
monty@mysql.com committed
1974

1975
/* Init PS/SP specific parse tree members.  */
1976

1977
static void init_stmt_after_parse(LEX *lex)
1978 1979
{
  SELECT_LEX *sl= lex->all_selects_list;
1980 1981 1982 1983
  /*
    Switch off a temporary flag that prevents evaluation of
    subqueries in statement prepare.
  */
1984 1985
  for (; sl; sl= sl->next_select_in_list())
   sl->uncacheable&= ~UNCACHEABLE_PREPARE;
1986 1987
}

1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013
/*
  SQLCOM_PREPARE implementation.

  SYNOPSIS
    mysql_sql_stmt_prepare()
      thd     thread handle

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

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

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

2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046
  if ((stmt= (Prepared_statement*) thd->stmt_map.find_by_name(name)))
  {
    /*
      If there is a statement with the same name, remove it. It is ok to
      remove old and fail to insert a new one at the same time.
    */
    if (stmt->deallocate())
      DBUG_VOID_RETURN;
  }

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

  if (stmt->set_name(name) || thd->stmt_map.insert(stmt))
  {
    delete stmt;
    DBUG_VOID_RETURN;
  }

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

  DBUG_VOID_RETURN;
}
2047 2048

/* Reinit prepared statement/stored procedure before execution */
2049

2050
void reinit_stmt_before_use(THD *thd, LEX *lex)
2051
{
2052
  SELECT_LEX *sl= lex->all_selects_list;
2053
  DBUG_ENTER("reinit_stmt_before_use");
2054

2055 2056 2057 2058 2059 2060 2061
  /*
    We have to update "thd" pointer in LEX, all its units and in LEX::result,
    since statements which belong to trigger body are associated with TABLE
    object and because of this can be used in different threads.
  */
  lex->thd= thd;

bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
2062 2063 2064
  if (lex->empty_field_list_on_rset)
  {
    lex->empty_field_list_on_rset= 0;
2065
    lex->field_list.empty();
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
2066
  }
2067
  for (; sl; sl= sl->next_select_in_list())
2068
  {
2069 2070
    if (!sl->first_execution)
    {
monty@mysql.com's avatar
monty@mysql.com committed
2071 2072 2073
      /* remove option which was put by mysql_explain_union() */
      sl->options&= ~SELECT_DESCRIBE;

2074 2075 2076
      /* see unique_table() */
      sl->exclude_from_table_unique_test= FALSE;

2077
      /*
2078
        Copy WHERE, HAVING clause pointers to avoid damaging them by optimisation
2079
      */
2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090
     if (sl->prep_where)
     {
       sl->where= sl->prep_where->copy_andor_structure(thd);
       sl->where->cleanup();
     }
     if (sl->prep_having)
     {
       sl->having= sl->prep_having->copy_andor_structure(thd);
       sl->having->cleanup();
     }
     DBUG_ASSERT(sl->join == 0);
2091 2092 2093 2094 2095 2096 2097 2098
      ORDER *order;
      /* Fix GROUP list */
      for (order= (ORDER *)sl->group_list.first; order; order= order->next)
        order->item= &order->item_ptr;
      /* Fix ORDER list */
      for (order= (ORDER *)sl->order_list.first; order; order= order->next)
        order->item= &order->item_ptr;
    }
2099 2100 2101 2102
    {
      SELECT_LEX_UNIT *unit= sl->master_unit();
      unit->unclean();
      unit->types.empty();
2103
      /* for derived tables & PS (which can't be reset by Item_subquery) */
2104
      unit->reinit_exec_mechanism();
2105
      unit->set_thd(thd);
2106
    }
2107
  }
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
2108 2109

  /*
konstantin@mysql.com's avatar
konstantin@mysql.com committed
2110 2111
    TODO: When the new table structure is ready, then have a status bit
    to indicate the table is altered, and re-do the setup_*
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
2112 2113
    and open the tables back.
  */
2114 2115 2116 2117 2118
  /*
    NOTE: We should reset whole table list here including all tables added
    by prelocking algorithm (it is not a problem for substatements since
    they have their own table list).
  */
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
2119
  for (TABLE_LIST *tables= lex->query_tables;
konstantin@mysql.com's avatar
konstantin@mysql.com committed
2120 2121
         tables;
         tables= tables->next_global)
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
2122 2123 2124 2125 2126 2127
  {
    /*
      Reset old pointers to TABLEs: they are not valid since the tables
      were closed in the end of previous prepare or execute call.
    */
    tables->table= 0;
2128 2129
    /* Reset is_schema_table_processed value(needed for I_S tables */
    tables->is_schema_table_processed= FALSE;
2130 2131 2132 2133 2134 2135

    if (tables->prep_on_expr)
    {
      tables->on_expr= tables->prep_on_expr->copy_andor_structure(thd);
      tables->on_expr->cleanup();
    }
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
2136
  }
2137
  lex->current_select= &lex->select_lex;
2138 2139 2140 2141 2142

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

2143
  if (lex->result)
2144
  {
2145
    lex->result->cleanup();
2146 2147
    lex->result->set_thd(thd);
  }
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
2148 2149
  lex->allow_sum_func= 0;
  lex->in_sum_func= NULL;
evgen@moonbone.local's avatar
evgen@moonbone.local committed
2150
  DBUG_VOID_RETURN;  
2151 2152
}

2153

2154 2155
/*
  Clears parameters from data left from previous execution or long data
konstantin@mysql.com's avatar
konstantin@mysql.com committed
2156

2157 2158
  SYNOPSIS
    reset_stmt_params()
2159 2160
      stmt               prepared statement for which parameters should
                         be reset
2161 2162 2163 2164 2165 2166 2167 2168 2169 2170 2171
*/

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


2172
/*
2173
  COM_STMT_EXECUTE handler: execute a previously prepared statement.
2174

2175
  SYNOPSIS
2176
    mysql_stmt_execute()
2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 2189
      thd                current thread
      packet             parameter types and data, if any
      packet_length      packet length, including the terminator character.

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

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

2192
void mysql_stmt_execute(THD *thd, char *packet_arg, uint packet_length)
2193
{
2194
  uchar *packet= (uchar*)packet_arg; // GCC 4.0.1 workaround
2195
  ulong stmt_id= uint4korr(packet);
2196
  ulong flags= (ulong) ((uchar) packet[4]);
2197
  /* Query text for binary, general or slow log, if any of them is open */
2198
  String expanded_query;
2199
#ifndef EMBEDDED_LIBRARY
2200
  uchar *packet_end= (uchar *) packet + packet_length - 1;
2201
#endif
2202
  Prepared_statement *stmt;
2203
  bool error;
2204
  DBUG_ENTER("mysql_stmt_execute");
2205 2206

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

2208 2209 2210
  /* First of all clear possible warnings from the previous command */
  mysql_reset_thd_for_next_command(thd);

2211
  if (!(stmt= find_prepared_statement(thd, stmt_id, "mysql_stmt_execute")))
2212 2213
    DBUG_VOID_RETURN;

2214 2215
  DBUG_PRINT("exec_query", ("%s", stmt->query));
  DBUG_PRINT("info",("stmt: %p", stmt));
2216

2217 2218 2219
  sp_cache_flush_obsolete(&thd->sp_proc_cache);
  sp_cache_flush_obsolete(&thd->sp_func_cache);

hf@deer.(none)'s avatar
hf@deer.(none) committed
2220
#ifndef EMBEDDED_LIBRARY
2221
  if (stmt->param_count)
2222
  {
2223
    uchar *null_array= (uchar *) packet;
2224
    if (setup_conversion_functions(stmt, (uchar **) &packet, packet_end) ||
2225
        stmt->set_params(stmt, null_array, (uchar *) packet, packet_end,
2226
                         &expanded_query))
2227
      goto set_params_data_err;
2228
  }
hf@deer.(none)'s avatar
hf@deer.(none) committed
2229
#else
2230
  /*
konstantin@mysql.com's avatar
konstantin@mysql.com committed
2231 2232
    In embedded library we re-install conversion routines each time
    we set params, and also we don't need to parse packet.
2233
    So we do it in one function.
2234
  */
2235
  if (stmt->param_count && stmt->set_params_data(stmt, &expanded_query))
2236
    goto set_params_data_err;
hf@deer.(none)'s avatar
hf@deer.(none) committed
2237
#endif
2238 2239
  if (!(specialflag & SPECIAL_NO_PRIOR))
    my_pthread_setprio(pthread_self(),QUERY_PRIOR);
2240
  error= stmt->execute(&expanded_query,
2241
                       test(flags & (ulong) CURSOR_TYPE_READ_ONLY));
2242 2243
  if (!(specialflag & SPECIAL_NO_PRIOR))
    my_pthread_setprio(pthread_self(), WAIT_PRIOR);
2244
  if (error == 0)
2245
    general_log_print(thd, COM_STMT_EXECUTE, "[%lu] %s", stmt->id, thd->query);
2246

2247
  DBUG_VOID_RETURN;
venu@myvenu.com's avatar
venu@myvenu.com committed
2248

2249
set_params_data_err:
2250
  my_error(ER_WRONG_ARGUMENTS, MYF(0), "mysql_stmt_execute");
2251
  reset_stmt_params(stmt);
2252 2253 2254
  DBUG_VOID_RETURN;
}

2255

sergefp@mysql.com's avatar
sergefp@mysql.com committed
2256
/*
2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273
  SQLCOM_EXECUTE implementation.

  SYNOPSIS
    mysql_sql_stmt_execute()
      thd                thread handle

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

  RETURN
    none: in case of success, OK (or result set) packet is sent to the
    client, otherwise an error is set in THD
sergefp@mysql.com's avatar
sergefp@mysql.com committed
2274 2275
*/

2276
void mysql_sql_stmt_execute(THD *thd)
sergefp@mysql.com's avatar
sergefp@mysql.com committed
2277
{
2278
  LEX *lex= thd->lex;
2279
  Prepared_statement *stmt;
2280 2281
  LEX_STRING *name= &lex->prepared_stmt_name;
  /* Query text for binary, general or slow log, if any of them is open */
2282
  String expanded_query;
2283
  DBUG_ENTER("mysql_sql_stmt_execute");
2284
  DBUG_PRINT("info", ("EXECUTE: %.*s\n", name->length, name->str));
2285

2286
  if (!(stmt= (Prepared_statement*) thd->stmt_map.find_by_name(name)))
2287
  {
2288 2289
    my_error(ER_UNKNOWN_STMT_HANDLER, MYF(0),
             name->length, name->str, "EXECUTE");
2290
    DBUG_VOID_RETURN;
2291 2292
  }

2293
  if (stmt->param_count != lex->prepared_stmt_params.elements)
sergefp@mysql.com's avatar
sergefp@mysql.com committed
2294
  {
2295
    my_error(ER_WRONG_ARGUMENTS, MYF(0), "EXECUTE");
sergefp@mysql.com's avatar
sergefp@mysql.com committed
2296 2297 2298
    DBUG_VOID_RETURN;
  }

2299 2300
  DBUG_PRINT("info",("stmt: %p", stmt));

2301
  if (stmt->set_params_from_vars(stmt, lex->prepared_stmt_params,
2302
                                 &expanded_query))
2303
    goto set_params_data_err;
2304

2305
  (void) stmt->execute(&expanded_query, FALSE);
2306

2307
  DBUG_VOID_RETURN;
2308 2309 2310 2311 2312

set_params_data_err:
  my_error(ER_WRONG_ARGUMENTS, MYF(0), "EXECUTE");
  reset_stmt_params(stmt);
  DBUG_VOID_RETURN;
2313 2314
}

2315

2316
/*
2317
  COM_STMT_FETCH handler: fetches requested amount of rows from cursor
monty@mysql.com's avatar
monty@mysql.com committed
2318

2319
  SYNOPSIS
monty@mysql.com's avatar
monty@mysql.com committed
2320
    mysql_stmt_fetch()
2321 2322 2323
      thd                Thread handle
      packet             Packet from client (with stmt_id & num_rows)
      packet_length      Length of packet
2324 2325 2326 2327 2328 2329
*/

void mysql_stmt_fetch(THD *thd, char *packet, uint packet_length)
{
  /* assume there is always place for 8-16 bytes */
  ulong stmt_id= uint4korr(packet);
2330
  ulong num_rows= uint4korr(packet+4);
2331
  Prepared_statement *stmt;
2332
  Statement stmt_backup;
2333
  Server_side_cursor *cursor;
2334 2335
  DBUG_ENTER("mysql_stmt_fetch");

2336
  /* First of all clear possible warnings from the previous command */
2337
  mysql_reset_thd_for_next_command(thd);
konstantin@mysql.com's avatar
konstantin@mysql.com committed
2338
  statistic_increment(thd->status_var.com_stmt_fetch, &LOCK_status);
2339 2340 2341
  if (!(stmt= find_prepared_statement(thd, stmt_id, "mysql_stmt_fetch")))
    DBUG_VOID_RETURN;

2342
  cursor= stmt->cursor;
2343
  if (!cursor)
2344
  {
2345
    my_error(ER_STMT_HAS_NO_OPEN_CURSOR, MYF(0), stmt_id);
2346 2347
    DBUG_VOID_RETURN;
  }
2348

konstantin@mysql.com's avatar
konstantin@mysql.com committed
2349
  thd->stmt_arena= stmt;
2350
  thd->set_n_backup_statement(stmt, &stmt_backup);
2351 2352 2353 2354

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

2355
  cursor->fetch(num_rows);
2356 2357 2358 2359

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

2360
  if (!cursor->is_open())
2361
  {
2362 2363
    stmt->close_cursor();
    thd->cursor= 0;
2364 2365 2366
    reset_stmt_params(stmt);
  }

2367
  thd->restore_backup_statement(stmt, &stmt_backup);
konstantin@mysql.com's avatar
konstantin@mysql.com committed
2368
  thd->stmt_arena= thd;
2369

2370 2371 2372 2373
  DBUG_VOID_RETURN;
}


2374
/*
2375
  Reset a prepared statement in case there was a recoverable error.
2376 2377
  SYNOPSIS
    mysql_stmt_reset()
2378 2379
      thd                Thread handle
      packet             Packet with stmt id
2380 2381

  DESCRIPTION
2382 2383 2384 2385 2386
    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.
2387
     - close an open cursor
2388 2389
    Sends 'OK' packet in case of success (statement was reset)
    or 'ERROR' packet (unrecoverable error/statement not found/etc).
2390 2391
*/

2392
void mysql_stmt_reset(THD *thd, char *packet)
2393
{
2394
  /* There is always space for 4 bytes in buffer */
2395
  ulong stmt_id= uint4korr(packet);
2396
  Prepared_statement *stmt;
2397
  DBUG_ENTER("mysql_stmt_reset");
2398

2399 2400 2401
  /* First of all clear possible warnings from the previous command */
  mysql_reset_thd_for_next_command(thd);

konstantin@mysql.com's avatar
konstantin@mysql.com committed
2402
  statistic_increment(thd->status_var.com_stmt_reset, &LOCK_status);
2403
  if (!(stmt= find_prepared_statement(thd, stmt_id, "mysql_stmt_reset")))
2404 2405
    DBUG_VOID_RETURN;

2406 2407 2408 2409 2410 2411 2412
  stmt->close_cursor();

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

serg@serg.mylan's avatar
serg@serg.mylan committed
2414
  stmt->state= Query_arena::PREPARED;
2415

2416
  send_ok(thd);
konstantin@mysql.com's avatar
konstantin@mysql.com committed
2417

2418 2419 2420 2421 2422
  DBUG_VOID_RETURN;
}


/*
2423
  Delete a prepared statement from memory.
2424
  Note: we don't send any reply to this command.
2425 2426
*/

2427
void mysql_stmt_close(THD *thd, char *packet)
2428
{
2429
  /* There is always space for 4 bytes in packet buffer */
2430
  ulong stmt_id= uint4korr(packet);
2431
  Prepared_statement *stmt;
2432
  DBUG_ENTER("mysql_stmt_close");
2433

2434
  if (!(stmt= find_prepared_statement(thd, stmt_id, "mysql_stmt_close")))
2435
    DBUG_VOID_RETURN;
2436

2437 2438 2439 2440
  /*
    The only way currently a statement can be deallocated when it's
    in use is from within Dynamic SQL.
  */
2441
  DBUG_ASSERT(! (stmt->flags & (uint) Prepared_statement::IS_IN_USE));
2442 2443
  (void) stmt->deallocate();

2444 2445 2446
  DBUG_VOID_RETURN;
}

2447 2448

/*
2449 2450 2451 2452 2453 2454 2455 2456 2457 2458 2459 2460 2461 2462 2463 2464 2465 2466 2467 2468 2469 2470 2471 2472 2473 2474 2475 2476 2477 2478 2479
  SQLCOM_DEALLOCATE implementation.

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

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

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

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

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

/*
  Handle long data in pieces from client.
2480 2481 2482

  SYNOPSIS
    mysql_stmt_get_longdata()
2483 2484
      thd                Thread handle
      packet             String to append
2485
      packet_length      Length of string (including end \0)
2486 2487

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

2495
void mysql_stmt_get_longdata(THD *thd, char *packet, ulong packet_length)
2496
{
2497 2498
  ulong stmt_id;
  uint param_number;
2499
  Prepared_statement *stmt;
2500 2501
  Item_param *param;
  char *packet_end= packet + packet_length - 1;
2502 2503
  DBUG_ENTER("mysql_stmt_get_longdata");

konstantin@mysql.com's avatar
konstantin@mysql.com committed
2504
  statistic_increment(thd->status_var.com_stmt_send_long_data, &LOCK_status);
hf@deer.(none)'s avatar
hf@deer.(none) committed
2505
#ifndef EMBEDDED_LIBRARY
2506
  /* Minimal size of long data packet is 6 bytes */
2507
  if (packet_length <= MYSQL_LONG_DATA_HEADER)
2508
  {
2509
    my_error(ER_WRONG_ARGUMENTS, MYF(0), "mysql_stmt_send_long_data");
2510 2511
    DBUG_VOID_RETURN;
  }
hf@deer.(none)'s avatar
hf@deer.(none) committed
2512
#endif
2513

2514 2515
  stmt_id= uint4korr(packet);
  packet+= 4;
2516

2517 2518
  if (!(stmt=find_prepared_statement(thd, stmt_id,
                                     "mysql_stmt_send_long_data")))
2519 2520
    DBUG_VOID_RETURN;

2521 2522
  param_number= uint2korr(packet);
  packet+= 2;
hf@deer.(none)'s avatar
hf@deer.(none) committed
2523
#ifndef EMBEDDED_LIBRARY
2524 2525
  if (param_number >= stmt->param_count)
  {
venu@myvenu.com's avatar
venu@myvenu.com committed
2526
    /* Error will be sent in execute call */
serg@serg.mylan's avatar
serg@serg.mylan committed
2527
    stmt->state= Query_arena::ERROR;
venu@myvenu.com's avatar
venu@myvenu.com committed
2528
    stmt->last_errno= ER_WRONG_ARGUMENTS;
2529 2530
    sprintf(stmt->last_error, ER(ER_WRONG_ARGUMENTS),
            "mysql_stmt_send_long_data");
2531 2532
    DBUG_VOID_RETURN;
  }
hf@deer.(none)'s avatar
hf@deer.(none) committed
2533 2534
#endif

2535 2536
  param= stmt->param_array[param_number];

hf@deer.(none)'s avatar
hf@deer.(none) committed
2537
#ifndef EMBEDDED_LIBRARY
2538
  if (param->set_longdata(packet, (ulong) (packet_end - packet)))
hf@deer.(none)'s avatar
hf@deer.(none) committed
2539
#else
2540
  if (param->set_longdata(thd->extra_data, thd->extra_length))
hf@deer.(none)'s avatar
hf@deer.(none) committed
2541
#endif
2542
  {
serg@serg.mylan's avatar
serg@serg.mylan committed
2543
    stmt->state= Query_arena::ERROR;
2544 2545 2546
    stmt->last_errno= ER_OUTOFMEMORY;
    sprintf(stmt->last_error, ER(ER_OUTOFMEMORY), 0);
  }
2547 2548
  DBUG_VOID_RETURN;
}
venu@myvenu.com's avatar
venu@myvenu.com committed
2549

2550

2551 2552 2553 2554 2555 2556 2557 2558 2559 2560 2561 2562 2563 2564 2565 2566 2567 2568 2569 2570 2571 2572 2573 2574 2575 2576 2577 2578 2579 2580 2581 2582 2583 2584 2585 2586 2587 2588 2589 2590 2591 2592 2593 2594 2595 2596 2597 2598 2599 2600 2601 2602 2603
/***************************************************************************
 Select_fetch_protocol_prep
****************************************************************************/

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

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

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

  return rc;
}

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

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


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

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

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

2604
Prepared_statement::Prepared_statement(THD *thd_arg, Protocol *protocol_arg)
2605 2606 2607
  :Statement(INITIALIZED, ++thd_arg->statement_id_counter,
             thd_arg->variables.query_alloc_block_size,
             thd_arg->variables.query_prealloc_size),
2608
  thd(thd_arg),
2609
  result(thd_arg),
2610
  protocol(protocol_arg),
2611
  param_array(0),
2612
  param_count(0),
2613
  last_errno(0),
2614
   flags((uint) IS_IN_USE)
2615 2616 2617 2618
{
  *last_error= '\0';
}

2619

2620 2621 2622
void Prepared_statement::setup_set_params()
{
  /* Setup binary logging */
2623
  if (mysql_bin_log.is_open() && is_update_query(lex->sql_command) ||
2624
      opt_log || opt_slow_log)
2625
  {
2626
    set_params_from_vars= insert_params_from_vars_with_log;
2627
#ifndef EMBEDDED_LIBRARY
2628
    set_params= insert_params_withlog;
2629
#else
2630
    set_params_data= emb_insert_params_withlog;
2631 2632 2633
#endif
  }
  else
2634 2635
  {
    set_params_from_vars= insert_params_from_vars;
2636
#ifndef EMBEDDED_LIBRARY
2637
    set_params= insert_params;
2638
#else
2639
    set_params_data= emb_insert_params;
2640
#endif
2641
  }
2642 2643
}

2644

2645 2646 2647 2648 2649 2650 2651 2652
/*
  DESCRIPTION
    Destroy this prepared statement, cleaning up all used memory
    and resources. This is called from ::deallocate() to
    handle COM_STMT_CLOSE and DEALLOCATE PREPARE or when
    THD ends and all prepared statements are freed.
*/

2653 2654
Prepared_statement::~Prepared_statement()
{
2655 2656
  DBUG_ENTER("Prepared_statement::~Prepared_statement");
  DBUG_PRINT("enter",("stmt: %p  cursor: %p", this, cursor));
2657
  delete cursor;
2658 2659 2660 2661 2662
  /*
    We have to call free on the items even if cleanup is called as some items,
    like Item_param, don't free everything until free_items()
  */
  free_items();
2663
  delete lex->result;
2664
  DBUG_VOID_RETURN;
2665 2666 2667
}


serg@serg.mylan's avatar
serg@serg.mylan committed
2668
Query_arena::Type Prepared_statement::type() const
2669 2670 2671
{
  return PREPARED_STATEMENT;
}
2672 2673


2674
void Prepared_statement::cleanup_stmt()
2675
{
2676
  DBUG_ENTER("Prepared_statement::cleanup_stmt");
2677 2678
  DBUG_PRINT("enter",("stmt: %p", this));

2679 2680 2681 2682 2683 2684 2685
  /* The order is important */
  lex->unit.cleanup();
  cleanup_items(free_list);
  thd->cleanup_after_query();
  close_thread_tables(thd);
  thd->rollback_item_tree_changes();

2686
  DBUG_VOID_RETURN;
2687
}
2688 2689 2690 2691 2692 2693 2694 2695 2696 2697 2698 2699 2700 2701 2702 2703 2704 2705 2706 2707 2708 2709 2710 2711 2712 2713 2714 2715 2716 2717 2718 2719 2720 2721 2722 2723 2724 2725 2726 2727 2728 2729 2730


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

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

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

  SYNOPSIS
    Prepared_statement::prepare()
      packet             statement text
      packet_len

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

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

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

bool Prepared_statement::prepare(const char *packet, uint packet_len)
{
2731
  bool error;
2732 2733 2734 2735 2736 2737 2738 2739 2740 2741 2742 2743 2744 2745 2746 2747 2748 2749 2750 2751 2752 2753 2754 2755 2756 2757 2758 2759 2760
  Statement stmt_backup;
  Query_arena *old_stmt_arena;
  DBUG_ENTER("Prepared_statement::prepare");
  /*
    If this is an SQLCOM_PREPARE, we also increase Com_prepare_sql.
    However, it seems handy if com_stmt_prepare is increased always,
    no matter what kind of prepare is processed.
  */
  statistic_increment(thd->status_var.com_stmt_prepare, &LOCK_status);

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

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

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

2761
  error= MYSQLparse((void *)thd) || thd->is_fatal_error ||
2762
      thd->net.report_error || init_param_array(this);
2763
  lex->safe_to_cache_query= FALSE;
2764 2765 2766 2767 2768 2769 2770 2771 2772 2773 2774 2775 2776 2777 2778 2779 2780 2781 2782 2783 2784 2785
  /*
    While doing context analysis of the query (in check_prepared_statement)
    we allocate a lot of additional memory: for open tables, JOINs, derived
    tables, etc.  Let's save a snapshot of current parse tree to the
    statement and restore original THD. In cases when some tree
    transformation can be reused on execute, we set again thd->mem_root from
    stmt->mem_root (see setup_wild for one place where we do that).
  */
  thd->restore_active_arena(this, &stmt_backup);

  /*
    If called from a stored procedure, ensure that we won't rollback
    external changes when cleaning up after validation.
  */
  DBUG_ASSERT(thd->change_list.is_empty());
  /*
    If the free_list is not empty, we'll wrongly free some externally
    allocated items when cleaning up after validation of the prepared
    statement.
  */
  DBUG_ASSERT(thd->free_list == NULL);

2786 2787
  if (error == 0)
    error= check_prepared_statement(this, name.str != 0);
2788

2789
  if (error && lex->sphead)
2790
  {
2791 2792
    delete lex->sphead;
    lex->sphead= NULL;
2793 2794
  }
  lex_end(lex);
2795
  cleanup_stmt();
2796 2797 2798
  thd->restore_backup_statement(this, &stmt_backup);
  thd->stmt_arena= old_stmt_arena;

2799
  if (error == 0)
2800 2801 2802 2803
  {
    setup_set_params();
    init_stmt_after_parse(lex);
    state= Query_arena::PREPARED;
2804
    flags&= ~ (uint) IS_IN_USE;
2805
  }
2806
  DBUG_RETURN(error);
2807 2808 2809 2810 2811 2812 2813 2814 2815 2816 2817 2818 2819 2820 2821 2822 2823 2824 2825 2826 2827
}

/*
  Execute a prepared statement.

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

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

  NOTES
      Preconditions, postconditions.
      ------------------------------
      See the comment for Prepared_statement::prepare().
2828 2829 2830 2831

  RETURN
    FALSE		ok
    TRUE		Error
2832 2833 2834 2835 2836 2837 2838
*/

bool Prepared_statement::execute(String *expanded_query, bool open_cursor)
{
  Statement stmt_backup;
  Query_arena *old_stmt_arena;
  Item *old_free_list;
2839
  bool error= TRUE;
2840 2841 2842 2843 2844 2845 2846

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

  /* Check if we got an error when sending long data */
  if (state == Query_arena::ERROR)
  {
    my_message(last_errno, last_error, MYF(0));
2847
    return TRUE;
2848
  }
2849
  if (flags & (uint) IS_IN_USE)
2850 2851
  {
    my_error(ER_PS_NO_RECURSION, MYF(0));
2852
    return TRUE;
2853
  }
2854 2855 2856 2857 2858 2859 2860 2861 2862 2863 2864 2865 2866 2867 2868 2869 2870 2871

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

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

2872 2873 2874
  /* In case the command has a call to SP which re-uses this statement name */
  flags|= IS_IN_USE;

2875
  close_cursor();
2876 2877 2878 2879 2880 2881 2882 2883 2884 2885 2886 2887 2888 2889 2890 2891 2892 2893 2894 2895 2896 2897 2898 2899 2900 2901 2902 2903 2904 2905 2906 2907 2908 2909 2910

  /*
    If the free_list is not empty, we'll wrongly free some externally
    allocated items when cleaning up after execution of this statement.
  */
  DBUG_ASSERT(thd->change_list.is_empty());
  DBUG_ASSERT(thd->free_list == NULL);
  thd->set_n_backup_statement(this, &stmt_backup);
  if (expanded_query->length() &&
      alloc_query(thd, (char*) expanded_query->ptr(),
                  expanded_query->length()+1))
  {
    my_error(ER_OUTOFMEMORY, 0, expanded_query->length());
    goto error;
  }
  /*
    Expanded query is needed for slow logging, so we want thd->query
    to point at it even after we restore from backup. This is ok, as
    expanded query was allocated in thd->mem_root.
  */
  stmt_backup.query= thd->query;
  stmt_backup.query_length= thd->query_length;

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

  thd->protocol= protocol;                      /* activate stmt protocol */
2911 2912 2913 2914
  error= (open_cursor ?
          mysql_open_cursor(thd, (uint) ALWAYS_MATERIALIZED_CURSOR,
                            &result, &cursor) :
          mysql_execute_command(thd));
2915 2916
  thd->protocol= &thd->protocol_simple;         /* use normal protocol */

2917
  /* Assert that if an error, no cursor is open */
monty@mysql.com's avatar
monty@mysql.com committed
2918
  DBUG_ASSERT(! (error && cursor));
2919

2920
  if (! cursor)
2921
  {
2922
    cleanup_stmt();
2923 2924 2925 2926 2927 2928 2929 2930 2931 2932
    reset_stmt_params(this);
  }

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

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

error:
2933 2934
  flags&= ~ (uint) IS_IN_USE;
  return error;
2935 2936 2937 2938 2939 2940 2941 2942 2943
}


/* Common part of DEALLOCATE PREPARE and mysql_stmt_close */

bool Prepared_statement::deallocate()
{
  /* We account deallocate in the same manner as mysql_stmt_close */
  statistic_increment(thd->status_var.com_stmt_close, &LOCK_status);
2944
  if (flags & (uint) IS_IN_USE)
2945 2946 2947 2948 2949 2950 2951 2952
  {
    my_error(ER_PS_NO_RECURSION, MYF(0));
    return TRUE;
  }
  /* Statement map calls delete stmt on erase */
  thd->stmt_map.erase(this);
  return FALSE;
}