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

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

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

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

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

Prepare:

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

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

Long data handling:
unknown's avatar
unknown committed
57

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

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

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

79
const String my_null_string("NULL", 4, default_charset_info);
80

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

85 86 87 88 89 90 91 92 93 94 95 96
class Prepared_statement: public Statement
{
public:
  THD *thd;
  Item_param **param;                           /* array of all placeholders */
  uint param_count;
  uint last_errno;
  char last_error[MYSQL_ERRMSG_SIZE];
  bool error_in_prepare, long_data_used;
  bool log_full_query;
#ifndef EMBEDDED_LIBRARY
  bool (*setup_params)(Prepared_statement *st, uchar *pos, uchar *read_pos);
unknown's avatar
unknown committed
97
#else
98
  bool (*setup_params_data)(Prepared_statement *st);
unknown's avatar
unknown committed
99
#endif
100 101 102 103 104
public:
  Prepared_statement(THD *thd_arg);
  virtual ~Prepared_statement();
  virtual Statement::Type type() const;
};
unknown's avatar
unknown committed
105

106

107 108 109
/******************************************************************************
  Implementation
******************************************************************************/
110 111


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

117
enum { STMT_QUERY_LOG_LENGTH= 8192 };
118

119 120 121 122 123 124 125
#ifdef EMBEDDED_LIBRARY
#define SETUP_PARAM_FUNCTION(fn_name) \
static void fn_name(Item_param *param, uchar **pos, ulong data_len)
#else
#define SETUP_PARAM_FUNCTION(fn_name) \
static void fn_name(Item_param *param, uchar **pos)
#endif
126 127 128


/*
129 130
  Seek prepared statement in statement map by id: returns zero if statement
  was not found, pointer otherwise.
131 132
*/

133 134 135 136 137 138 139 140 141 142 143 144
static Prepared_statement *
find_prepared_statement(THD *thd, ulong id, const char *where)
{
  Statement *stmt= thd->stmt_map.find(id);

  if (stmt == 0 || stmt->type() != Statement::PREPARED_STATEMENT)
  {
    my_error(ER_UNKNOWN_STMT_HANDLER, MYF(0), id, where);
    send_error(thd);
    return 0;
  }
  return (Prepared_statement *) stmt;
145 146
}

147

148 149 150 151
/*
  Send prepared stmt info to client after prepare
*/

unknown's avatar
unknown committed
152
#ifndef EMBEDDED_LIBRARY
153
static bool send_prep_stmt(Prepared_statement *stmt, uint columns)
154
{
155
  NET *net= &stmt->thd->net;
156 157
  char buff[9];
  buff[0]= 0;
158
  int4store(buff+1, stmt->id);
159 160
  int2store(buff+5, columns);
  int2store(buff+7, stmt->param_count);
161
  /* This should be fixed to work with prepared statements */
unknown's avatar
unknown committed
162
  return (my_net_write(net, buff, sizeof(buff)) || net_flush(net));
unknown's avatar
unknown committed
163
}
164
#else
165 166
static bool send_prep_stmt(Prepared_statement *stmt,
                           uint columns __attribute__((unused)))
unknown's avatar
unknown committed
167
{
unknown's avatar
SCRUM  
unknown committed
168 169
  THD *thd= stmt->thd;

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

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

unknown's avatar
unknown committed
178 179 180 181 182 183

/*
  Read the length of the parameter data and retun back to   
  caller by positing the pointer to param data              
*/

unknown's avatar
unknown committed
184
#ifndef EMBEDDED_LIBRARY
unknown's avatar
unknown committed
185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205
static ulong get_param_length(uchar **packet)
{
  reg1 uchar *pos= *packet;
  if (*pos < 251)
  {
    (*packet)++;
    return (ulong) *pos;
  }
  if (*pos == 252)
  {
    (*packet)+=3;
    return (ulong) uint2korr(pos+1);
  }
  if (*pos == 253)
  {
    (*packet)+=4;
    return (ulong) uint3korr(pos+1);
  }
  (*packet)+=9; // Must be 254 when here 
  return (ulong) uint4korr(pos+1);
}
unknown's avatar
unknown committed
206 207 208 209
#else
#define get_param_length(A) data_len
#endif /*!EMBEDDED_LIBRARY*/

unknown's avatar
unknown committed
210 211
 /*
  Setup param conversion routines
unknown's avatar
unknown committed
212

unknown's avatar
unknown committed
213 214 215 216 217 218 219 220 221 222 223 224 225 226
  setup_param_xx()
  param   Parameter Item
  pos     Input data buffer

  All these functions reads the data from pos and sets up that data
  through 'param' and advances the buffer position to predifined
  length position.

  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.

  RETURN VALUES
    
unknown's avatar
unknown committed
227 228
*/

unknown's avatar
unknown committed
229
SETUP_PARAM_FUNCTION(setup_param_tiny)
unknown's avatar
unknown committed
230
{
unknown's avatar
unknown committed
231 232 233 234
  param->set_int((longlong)(**pos));
  *pos+= 1;
}

unknown's avatar
unknown committed
235
SETUP_PARAM_FUNCTION(setup_param_short)
unknown's avatar
unknown committed
236 237 238 239 240
{
  param->set_int((longlong)sint2korr(*pos));
  *pos+= 2;
}

unknown's avatar
unknown committed
241
SETUP_PARAM_FUNCTION(setup_param_int32)
unknown's avatar
unknown committed
242 243 244 245 246
{
  param->set_int((longlong)sint4korr(*pos));
  *pos+= 4;
}

unknown's avatar
unknown committed
247
SETUP_PARAM_FUNCTION(setup_param_int64)
unknown's avatar
unknown committed
248 249 250 251 252
{
  param->set_int((longlong)sint8korr(*pos));
  *pos+= 8;
}

unknown's avatar
unknown committed
253
SETUP_PARAM_FUNCTION(setup_param_float)
unknown's avatar
unknown committed
254 255 256 257 258 259 260
{
  float data;
  float4get(data,*pos);
  param->set_double((double) data);
  *pos+= 4;
}

unknown's avatar
unknown committed
261
SETUP_PARAM_FUNCTION(setup_param_double)
unknown's avatar
unknown committed
262 263 264 265 266 267 268
{
  double data;
  float8get(data,*pos);
  param->set_double((double) data);
  *pos+= 8;
}

unknown's avatar
unknown committed
269
SETUP_PARAM_FUNCTION(setup_param_time)
270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292
{
  ulong length;

  if ((length= get_param_length(pos)))
  {
    uchar *to= *pos;
    TIME  tm;   
    
    tm.second_part= (length > 8 ) ? (ulong) sint4korr(to+7): 0;

    tm.day=    (ulong) sint4korr(to+1);
    tm.hour=   (uint) to[5];
    tm.minute= (uint) to[6];
    tm.second= (uint) to[7];

    tm.year= tm.month= 0;
    tm.neg= (bool)to[0];

    param->set_time(&tm, TIMESTAMP_TIME);
  }
  *pos+= length;
}

unknown's avatar
unknown committed
293
SETUP_PARAM_FUNCTION(setup_param_datetime)
294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317
{
  uint length= get_param_length(pos);
 
  if (length)
  {
    uchar *to= *pos;
    TIME  tm;
    
    tm.second_part= (length > 7 ) ? (ulong) sint4korr(to+7): 0;
    
    if (length > 4)
    {
      tm.hour=   (uint) to[4];
      tm.minute= (uint) to[5];
      tm.second= (uint) to[6];
    }
    else
      tm.hour= tm.minute= tm.second= 0;
    
    tm.year=   (uint) sint2korr(to);
    tm.month=  (uint) to[2];
    tm.day=    (uint) to[3];
    tm.neg=    0;

318
    param->set_time(&tm, TIMESTAMP_DATETIME);
319 320 321 322
  }
  *pos+= length;
}

unknown's avatar
unknown committed
323
SETUP_PARAM_FUNCTION(setup_param_date)
324 325 326 327 328 329 330 331
{
  ulong length;
 
  if ((length= get_param_length(pos)))
  {
    uchar *to= *pos;
    TIME tm;

332
    tm.year=  (uint) sint2korr(to);
333 334 335 336 337 338 339 340 341 342 343 344
    tm.month=  (uint) to[2];
    tm.day= (uint) to[3];

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

    param->set_time(&tm, TIMESTAMP_DATE);
  }
  *pos+= length;
}

unknown's avatar
unknown committed
345
SETUP_PARAM_FUNCTION(setup_param_str)
unknown's avatar
unknown committed
346
{
347
  ulong len= get_param_length(pos);
unknown's avatar
unknown committed
348
  param->set_value((const char *)*pos, len);
349
  *pos+= len;        
unknown's avatar
unknown committed
350 351
}

unknown's avatar
unknown committed
352
void setup_param_functions(Item_param *param, uchar param_type)
unknown's avatar
unknown committed
353
{
unknown's avatar
unknown committed
354
  switch (param_type) {
unknown's avatar
unknown committed
355
  case FIELD_TYPE_TINY:
unknown's avatar
unknown committed
356
    param->setup_param_func= setup_param_tiny;
357
    param->item_result_type= INT_RESULT;
unknown's avatar
unknown committed
358 359
    break;
  case FIELD_TYPE_SHORT:
unknown's avatar
unknown committed
360
    param->setup_param_func= setup_param_short;
361
    param->item_result_type= INT_RESULT;
unknown's avatar
unknown committed
362 363
    break;
  case FIELD_TYPE_LONG:
unknown's avatar
unknown committed
364
    param->setup_param_func= setup_param_int32;
365
    param->item_result_type= INT_RESULT;
unknown's avatar
unknown committed
366 367
    break;
  case FIELD_TYPE_LONGLONG:
unknown's avatar
unknown committed
368
    param->setup_param_func= setup_param_int64;
369
    param->item_result_type= INT_RESULT;
unknown's avatar
unknown committed
370 371
    break;
  case FIELD_TYPE_FLOAT:
unknown's avatar
unknown committed
372
    param->setup_param_func= setup_param_float;
373
    param->item_result_type= REAL_RESULT;
unknown's avatar
unknown committed
374 375
    break;
  case FIELD_TYPE_DOUBLE:
unknown's avatar
unknown committed
376
    param->setup_param_func= setup_param_double;
377
    param->item_result_type= REAL_RESULT;
unknown's avatar
unknown committed
378
    break;
379 380
  case FIELD_TYPE_TIME:
    param->setup_param_func= setup_param_time;
381
    param->item_result_type= STRING_RESULT;
382 383 384
    break;
  case FIELD_TYPE_DATE:
    param->setup_param_func= setup_param_date;
385
    param->item_result_type= STRING_RESULT;
386
    break;
387 388
  case MYSQL_TYPE_DATETIME:
  case MYSQL_TYPE_TIMESTAMP:
389
    param->setup_param_func= setup_param_datetime;
390
    param->item_result_type= STRING_RESULT;
391
    break;
unknown's avatar
unknown committed
392
  default:
unknown's avatar
unknown committed
393
    param->setup_param_func= setup_param_str;
394
    param->item_result_type= STRING_RESULT;
unknown's avatar
unknown committed
395 396 397
  }
}

unknown's avatar
unknown committed
398
#ifndef EMBEDDED_LIBRARY
unknown's avatar
unknown committed
399
/*
400 401
  Update the parameter markers by reading data from client packet 
  and if binary/update log is set, generate the valid query.
unknown's avatar
unknown committed
402 403
*/

404 405
static bool insert_params_withlog(Prepared_statement *stmt, uchar *pos,
                                  uchar *read_pos)
406 407
{
  THD *thd= stmt->thd;
408
  List<Item> &params= stmt->lex->param_list;
409 410 411
  List_iterator<Item> param_iterator(params);
  Item_param *param;
  
412 413 414 415
  String str, query;
  const String *res;

  DBUG_ENTER("insert_params_withlog"); 
416

417
  if (query.copy(stmt->query, stmt->query_length, default_charset_info))
418
    DBUG_RETURN(1);
419 420 421 422 423 424 425 426 427 428
  
  ulong param_no= 0;  
  uint32 length= 0;
  
  while ((param= (Item_param *)param_iterator++))
  {
    if (param->long_data_supplied)
      res= param->query_val_str(&str);       
    else
    {
429
      if (is_param_null(pos,param_no))
430 431
      {
        param->maybe_null= param->null_value= 1;
unknown's avatar
unknown committed
432
        res= &my_null_string;
433 434 435 436 437 438 439 440
      }
      else
      {
        param->maybe_null= param->null_value= 0;
        param->setup_param_func(param,&read_pos);
        res= param->query_val_str(&str);
      }
    }
441
    if (query.replace(param->pos_in_query+length, 1, *res))
442 443 444 445 446
      DBUG_RETURN(1);
    
    length+= res->length()-1;
    param_no++;
  }
447
  if (alloc_query(thd, (char *)query.ptr(), query.length()+1))
448
    DBUG_RETURN(1);
449

450 451 452
  DBUG_RETURN(0);
}

453 454 455

static bool insert_params(Prepared_statement *stmt, uchar *pos,
                          uchar *read_pos)
456
{
457
  List<Item> &params= stmt->lex->param_list;
458 459 460
  List_iterator<Item> param_iterator(params);
  Item_param *param;
  ulong param_no= 0;  
461 462 463

  DBUG_ENTER("insert_params"); 

464 465 466 467
  while ((param= (Item_param *)param_iterator++))
  {
    if (!param->long_data_supplied)   
    {
468
      if (is_param_null(pos,param_no))
469 470 471 472 473 474 475 476 477 478 479 480
        param->maybe_null= param->null_value= 1;
      else
      {
        param->maybe_null= param->null_value= 0;
        param->setup_param_func(param,&read_pos);
      }
    }
    param_no++;
  }
  DBUG_RETURN(0);
}

481
static bool setup_params_data(Prepared_statement *stmt)
unknown's avatar
unknown committed
482
{                                       
483
  List<Item> &params= stmt->lex->param_list;
unknown's avatar
unknown committed
484 485
  List_iterator<Item> param_iterator(params);
  Item_param *param;
486

487 488
  uchar *pos= (uchar*) stmt->thd->net.read_pos + 1 +
              MYSQL_STMT_HEADER;                //skip header
unknown's avatar
unknown committed
489
  uchar *read_pos= pos+(stmt->param_count+7) / 8; //skip null bits   
unknown's avatar
unknown committed
490

491 492
  DBUG_ENTER("setup_params_data");

unknown's avatar
unknown committed
493 494 495 496 497 498 499 500
  if (*read_pos++) //types supplied / first execute
  {              
    /*
      First execute or types altered by the client, setup the 
      conversion routines for all parameters (one time)
    */
    while ((param= (Item_param *)param_iterator++))
    {       
unknown's avatar
unknown committed
501 502
      setup_param_functions(param,*read_pos);
      read_pos+= 2;
unknown's avatar
unknown committed
503
    }
unknown's avatar
unknown committed
504 505
    param_iterator.rewind();
  }    
506
  stmt->setup_params(stmt,pos,read_pos);
unknown's avatar
unknown committed
507 508 509
  DBUG_RETURN(0);
}

510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594
#else

bool setup_params_data(Prepared_statement *stmt)
{                                       
  List<Item> &params= stmt->lex->param_list;
  List_iterator<Item> param_iterator(params);
  Item_param *param;
  MYSQL_BIND *client_param= stmt->thd->client_params;

  DBUG_ENTER("setup_params_data");

  for (;(param= (Item_param *)param_iterator++); client_param++)
  {       
    setup_param_functions(param, client_param->buffer_type);
    if (!param->long_data_supplied)
    {
      if (*client_param->is_null)
        param->maybe_null= param->null_value= 1;
      else
      {
	uchar *buff= (uchar*)client_param->buffer;
        param->maybe_null= param->null_value= 0;
        param->setup_param_func(param,&buff, 
				client_param->length ? 
				*client_param->length : 
				client_param->buffer_length);
      }
    }
  }
  DBUG_RETURN(0);
}

bool setup_params_data_withlog(Prepared_statement *stmt)
{                                       
  THD *thd= stmt->thd;
  List<Item> &params= stmt->lex->param_list;
  List_iterator<Item> param_iterator(params);
  Item_param *param;
  MYSQL_BIND *client_param= thd->client_params;

  String str, query;
  const String *res;

  DBUG_ENTER("setup_params_data_withlog");

  if (query.copy(stmt->query, stmt->query_length, default_charset_info))
    DBUG_RETURN(1);
  
  uint32 length= 0;

  for (;(param= (Item_param *)param_iterator++); client_param++)
  {       
    setup_param_functions(param, client_param->buffer_type);
    if (param->long_data_supplied)
      res= param->query_val_str(&str);       
    else
    {
      if (*client_param->is_null)
      {
        param->maybe_null= param->null_value= 1;
        res= &my_null_string;
      }
      else
      {
	uchar *buff= (uchar*)client_param->buffer;
        param->maybe_null= param->null_value= 0;
        param->setup_param_func(param,&buff,
				client_param->length ? 
				*client_param->length : 
				client_param->buffer_length);
        res= param->query_val_str(&str);
      }
    }
    if (query.replace(param->pos_in_query+length, 1, *res))
      DBUG_RETURN(1);
    
    length+= res->length()-1;
  }
  
  if (alloc_query(thd, (char *) query.ptr(), query.length()+1))
    DBUG_RETURN(1);

  DBUG_RETURN(0);
}

unknown's avatar
unknown committed
595 596
#endif /*!EMBEDDED_LIBRARY*/

unknown's avatar
unknown committed
597 598 599 600 601 602
/*
  Validate the following information for INSERT statement:                         
    - field existance           
    - fields count                          
*/

603
static bool mysql_test_insert_fields(Prepared_statement *stmt,
604
				     TABLE_LIST *table_list,
unknown's avatar
unknown committed
605
				     List<Item> &fields, 
unknown's avatar
unknown committed
606
				     List<List_item> &values_list)
unknown's avatar
unknown committed
607
{
608
  THD *thd= stmt->thd;
unknown's avatar
unknown committed
609 610 611
  TABLE *table;
  List_iterator_fast<List_item> its(values_list);
  List_item *values;
612

unknown's avatar
unknown committed
613 614
  DBUG_ENTER("mysql_test_insert_fields");

unknown's avatar
unknown committed
615
#ifndef NO_EMBEDDED_ACCESS_CHECKS
616 617
  my_bool update=(stmt->lex->value_list.elements ? UPDATE_ACL : 0);
  ulong privilege= (stmt->lex->duplicates == DUP_REPLACE ?
618 619
                    INSERT_ACL | DELETE_ACL : INSERT_ACL | update);
  if (check_access(thd,privilege,table_list->db,
unknown's avatar
unknown committed
620
                   &table_list->grant.privilege,0,0) || 
unknown's avatar
unknown committed
621
      (grant_option && check_grant(thd,privilege,table_list,0,0)))
unknown's avatar
unknown committed
622
    DBUG_RETURN(1); 
623
#endif
unknown's avatar
unknown committed
624
  if (open_and_lock_tables(thd, table_list))
625 626
    DBUG_RETURN(1); 
  table= table_list->table;
unknown's avatar
unknown committed
627 628 629 630

  if ((values= its++))
  {
    uint value_count;
631
    ulong counter= 0;
unknown's avatar
unknown committed
632 633 634 635 636 637 638
    
    if (check_insert_fields(thd,table,fields,*values,1))
      DBUG_RETURN(1);

    value_count= values->elements;
    its.rewind();
   
639
    while ((values= its++))
unknown's avatar
unknown committed
640 641 642 643 644 645
    {
      counter++;
      if (values->elements != value_count)
      {
        my_printf_error(ER_WRONG_VALUE_COUNT_ON_ROW,
			ER(ER_WRONG_VALUE_COUNT_ON_ROW),
646
			MYF(0), counter);
unknown's avatar
unknown committed
647 648 649 650
        DBUG_RETURN(1);
      }
    }
  }
651
  if (send_prep_stmt(stmt, 0))
652
    DBUG_RETURN(1);
unknown's avatar
unknown committed
653 654 655 656 657 658 659 660
  DBUG_RETURN(0);
}


/*
  Validate the following information                         
    UPDATE - set and where clause    DELETE - where clause                                             
                                                             
661 662
  And send update-set clause column list fields info 
  back to client. For DELETE, just validate where clause 
unknown's avatar
unknown committed
663 664 665
  and return no fields information back to client.
*/

666 667
static bool mysql_test_upd_fields(Prepared_statement *stmt,
                                  TABLE_LIST *table_list,
unknown's avatar
unknown committed
668
				  List<Item> &fields, List<Item> &values,
unknown's avatar
unknown committed
669
				  COND *conds)
unknown's avatar
unknown committed
670
{
671
  THD *thd= stmt->thd;
unknown's avatar
unknown committed
672

673
  DBUG_ENTER("mysql_test_upd_fields");
unknown's avatar
unknown committed
674
#ifndef NO_EMBEDDED_ACCESS_CHECKS
675
  if (check_access(thd,UPDATE_ACL,table_list->db,
unknown's avatar
unknown committed
676
                   &table_list->grant.privilege,0,0) || 
unknown's avatar
unknown committed
677
      (grant_option && check_grant(thd,UPDATE_ACL,table_list,0,0)))
unknown's avatar
unknown committed
678 679 680
    DBUG_RETURN(1);
#endif
  if (open_and_lock_tables(thd, table_list))
unknown's avatar
unknown committed
681
    DBUG_RETURN(1);
682 683
  if (setup_tables(table_list) ||
      setup_fields(thd, 0, table_list, fields, 1, 0, 0) || 
unknown's avatar
unknown committed
684
      setup_conds(thd, table_list, &conds) || thd->net.report_error)      
unknown's avatar
unknown committed
685 686 687 688 689 690
    DBUG_RETURN(1);

  /* 
     Currently return only column list info only, and we are not
     sending any info on where clause.
  */
691
  if (send_prep_stmt(stmt, 0))
unknown's avatar
unknown committed
692 693 694 695 696 697 698 699 700
    DBUG_RETURN(1);
  DBUG_RETURN(0);
}

/*
  Validate the following information:                         

    SELECT - column list 
           - where clause
701
           - order clause
unknown's avatar
unknown committed
702 703 704 705 706 707
           - having clause
           - group by clause
           - if no column spec i.e. '*', then setup all fields
                                                           
  And send column list fields info back to client. 
*/
708 709 710

static bool mysql_test_select_fields(Prepared_statement *stmt,
                                     TABLE_LIST *tables,
711
				     uint wild_num,
unknown's avatar
unknown committed
712
                                     List<Item> &fields, COND *conds, 
713
                                     uint og_num, ORDER *order, ORDER *group,
unknown's avatar
unknown committed
714 715 716 717
                                     Item *having, ORDER *proc,
                                     ulong select_options, 
                                     SELECT_LEX_UNIT *unit,
                                     SELECT_LEX *select_lex)
unknown's avatar
unknown committed
718
{
719
  THD *thd= stmt->thd;
720 721 722
  LEX *lex= stmt->lex;
  select_result *result= lex->result;

unknown's avatar
unknown committed
723 724
  DBUG_ENTER("mysql_test_select_fields");

unknown's avatar
unknown committed
725
#ifndef NO_EMBEDDED_ACCESS_CHECKS
726 727 728
  ulong privilege= lex->exchange ? SELECT_ACL | FILE_ACL : SELECT_ACL;
  if (tables)
  {
unknown's avatar
unknown committed
729
    if (check_table_access(thd, privilege, tables,0))
730 731
      DBUG_RETURN(1);
  }
unknown's avatar
unknown committed
732
  else if (check_access(thd, privilege, "*any*",0,0,0))
733
    DBUG_RETURN(1);
unknown's avatar
unknown committed
734
#endif
unknown's avatar
unknown committed
735
  if ((&lex->select_lex != lex->all_selects_list &&
736
       lex->unit.create_total_list(thd, lex, &tables, 0)))
unknown's avatar
unknown committed
737 738
   DBUG_RETURN(1);
    
unknown's avatar
unknown committed
739
  if (open_and_lock_tables(thd, tables))
unknown's avatar
unknown committed
740 741
    DBUG_RETURN(1);

742
  if (lex->describe)
unknown's avatar
unknown committed
743
  {
744
    if (send_prep_stmt(stmt, 0))
745
      DBUG_RETURN(1);      
746
  }
747 748
  else 
  {
749
    fix_tables_pointers(lex->all_selects_list);
750 751 752 753 754
    if (!result && !(result= new select_send()))
    {
      send_error(thd, ER_OUT_OF_RESOURCES);
      DBUG_RETURN(1);
    }
unknown's avatar
unknown committed
755

756 757
    JOIN *join= new JOIN(thd, fields, select_options, result);
    thd->used_tables= 0;	// Updated by setup_fields  
unknown's avatar
unknown committed
758

759 760 761 762
    if (join->prepare(&select_lex->ref_pointer_array, tables, 
                      wild_num, conds, og_num, order, group, having, proc, 
                      select_lex, unit))
      DBUG_RETURN(1);
763
    if (send_prep_stmt(stmt, fields.elements) ||
764
        thd->protocol_simple.send_fields(&fields, 0)
unknown's avatar
SCRUM  
unknown committed
765
#ifndef EMBEDDED_LIBRARY
766
         || net_flush(&thd->net)
unknown's avatar
SCRUM  
unknown committed
767
#endif
768
       )
769
      DBUG_RETURN(1);
770
    join->cleanup();
771
  }
unknown's avatar
unknown committed
772 773 774
  DBUG_RETURN(0);  
}

775

unknown's avatar
unknown committed
776 777 778 779
/*
  Send the prepare query results back to client              
*/
                     
780
static bool send_prepare_results(Prepared_statement *stmt)     
unknown's avatar
unknown committed
781
{   
782
  THD *thd= stmt->thd;
783 784 785
  LEX *lex= stmt->lex;
  enum enum_sql_command sql_command= lex->sql_command;

786 787 788
  DBUG_ENTER("send_prepare_results");
  DBUG_PRINT("enter",("command: %d, param_count: %ld",
                      sql_command, lex->param_count));
unknown's avatar
unknown committed
789
  
790 791 792
  /* Setup prepared stmt */
  stmt->param_count= lex->param_count;

793
  SELECT_LEX *select_lex= &lex->select_lex;
unknown's avatar
unknown committed
794 795
  TABLE_LIST *tables=(TABLE_LIST*) select_lex->table_list.first;
  
796
  switch (sql_command) {
unknown's avatar
unknown committed
797 798

  case SQLCOM_INSERT:
799
    if (mysql_test_insert_fields(stmt, tables, lex->field_list,
unknown's avatar
unknown committed
800
				 lex->many_values))
unknown's avatar
unknown committed
801 802 803 804
      goto abort;    
    break;

  case SQLCOM_UPDATE:
805
    if (mysql_test_upd_fields(stmt, tables, select_lex->item_list,
unknown's avatar
unknown committed
806
			      lex->value_list, select_lex->where))
unknown's avatar
unknown committed
807 808 809 810
      goto abort;
    break;

  case SQLCOM_DELETE:
811
    if (mysql_test_upd_fields(stmt, tables, select_lex->item_list,
unknown's avatar
unknown committed
812
			      lex->value_list, select_lex->where))
unknown's avatar
unknown committed
813 814 815 816
      goto abort;
    break;

  case SQLCOM_SELECT:
817
    if (mysql_test_select_fields(stmt, tables, select_lex->with_wild,
unknown's avatar
unknown committed
818 819
                                 select_lex->item_list,
                                 select_lex->where,
820 821
				 select_lex->order_list.elements +
				 select_lex->group_list.elements,
unknown's avatar
unknown committed
822 823 824 825 826 827
                                 (ORDER*) select_lex->order_list.first,
                                 (ORDER*) select_lex->group_list.first, 
                                 select_lex->having,
                                 (ORDER*)lex->proc_list.first,
                                 select_lex->options | thd->options,
                                 &(lex->unit), select_lex))
unknown's avatar
unknown committed
828 829 830 831 832 833 834 835 836
      goto abort;
    break;

  default:
    {
      /* 
         Rest fall through to default category, no parsing 
         for non-DML statements 
      */
unknown's avatar
unknown committed
837 838
      if (send_prep_stmt(stmt, 0))
        goto abort;
unknown's avatar
unknown committed
839 840
    }
  }
841
  DBUG_RETURN(0);
unknown's avatar
unknown committed
842 843

abort:
844 845
  send_error(thd,thd->killed ? ER_SERVER_SHUTDOWN : 0);
  DBUG_RETURN(1);
unknown's avatar
unknown committed
846 847
}

unknown's avatar
unknown committed
848 849 850
/*
  Initialize parameter items in statement
*/
unknown's avatar
unknown committed
851

852
static bool init_param_items(Prepared_statement *stmt)
unknown's avatar
unknown committed
853 854
{
  Item_param **to;
855 856 857 858
 
  if (!stmt->param_count)
    stmt->param= (Item_param **)0;
  else
859
  {    
860 861 862 863
    if (!(stmt->param= to= (Item_param **)
          my_malloc(sizeof(Item_param *)*(stmt->param_count+1), 
                    MYF(MY_WME))))
      return 1;
864

865
    List_iterator<Item> param_iterator(stmt->lex->param_list);
866
    while ((*(to++)= (Item_param *)param_iterator++));
867
  }  
unknown's avatar
unknown committed
868
  return 0;
unknown's avatar
unknown committed
869
}
870

871

unknown's avatar
unknown committed
872 873 874 875 876 877 878 879 880 881
/*
  Parse the query and send the total number of parameters 
  and resultset metadata information back to client (if any), 
  without executing the query i.e. with out any log/disk 
  writes. This will allow the queries to be re-executed 
  without re-parsing during execute.          
                                                              
  If parameter markers are found in the query, then store    
  the information using Item_param along with maintaining a  
  list in lex->param_list, so that a fast and direct         
882
  retrieval can be made without going through all field     
unknown's avatar
unknown committed
883 884 885
  items.                                                     
*/

886
bool mysql_stmt_prepare(THD *thd, char *packet, uint packet_length)
unknown's avatar
unknown committed
887
{
888 889 890
  LEX *lex;
  Prepared_statement *stmt= new Prepared_statement(thd);

891
  DBUG_ENTER("mysql_stmt_prepare");
unknown's avatar
unknown committed
892

893 894 895 896 897
  if (stmt == 0)
    DBUG_RETURN(0);

  if (thd->stmt_map.insert(stmt))
    goto insert_stmt_err;
898

899 900
  thd->stmt_backup.set_statement(thd);
  thd->set_statement(stmt);
unknown's avatar
unknown committed
901

902 903 904 905 906 907 908 909 910 911 912 913 914 915
  if (alloc_query(thd, packet, packet_length))
    goto alloc_query_err;

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

  lex= lex_start(thd, (uchar *) thd->query, thd->query_length);
  mysql_init_query(thd);
  lex->safe_to_cache_query= 0;
  lex->param_count= 0;

  if (yyparse((void *)thd) || thd->is_fatal_error || send_prepare_results(stmt))
    goto yyparse_err;

  lex_end(lex);
unknown's avatar
unknown committed
916 917

  if (!(specialflag & SPECIAL_NO_PRIOR))
unknown's avatar
unknown committed
918
    my_pthread_setprio(pthread_self(),WAIT_PRIOR);
919 920

  // save WHERE clause pointers to avoid damaging they by optimisation
921
  for (SELECT_LEX *sl= thd->lex->all_selects_list;
922 923 924 925 926 927
       sl;
       sl= sl->next_select_in_list())
  {
    sl->prep_where= sl->where;
  }

928 929 930 931 932 933 934
  stmt->set_statement(thd);
  thd->set_statement(&thd->stmt_backup);

  if (init_param_items(stmt))
    goto init_param_err;

  stmt->command= COM_EXECUTE;                   // set it only once here 
935

936 937
  DBUG_RETURN(0);

938 939 940 941 942 943 944 945 946 947 948
yyparse_err:
  lex_end(lex);
  stmt->set_statement(thd);
  thd->set_statement(&thd->stmt_backup);
init_param_err:
alloc_query_err:
  /* Statement map deletes statement on erase */
  thd->stmt_map.erase(stmt);
  DBUG_RETURN(1);
insert_stmt_err:
  delete stmt;
949
  DBUG_RETURN(1);
unknown's avatar
unknown committed
950 951 952 953 954 955
}


/*
  Executes previously prepared query

956
  If there is any parameters (stmt->param_count), then replace 
unknown's avatar
unknown committed
957 958 959 960
  markers with the data supplied from client, and then       
  execute the query                                            
*/

961
void mysql_stmt_execute(THD *thd, char *packet)
unknown's avatar
unknown committed
962
{
unknown's avatar
unknown committed
963
  ulong stmt_id= uint4korr(packet);
964
  Prepared_statement *stmt;
unknown's avatar
unknown committed
965

966 967 968
  DBUG_ENTER("mysql_stmt_execute");
  
  if (!(stmt= find_prepared_statement(thd, stmt_id, "execute")))
969 970 971 972 973
    DBUG_VOID_RETURN;

  /* Check if we got an error when sending long data */
  if (stmt->error_in_prepare)
  {
unknown's avatar
unknown committed
974
    send_error(thd, stmt->last_errno, stmt->last_error);
975 976 977
    DBUG_VOID_RETURN;
  }

978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999
  /*
    XXX: while thd->query_id is incremented for each command, stmt->query_id
    holds query_id of prepare stage. Keeping old query_id seems to be more
    natural, but differs from the way prepared statements work in 4.1:
  */ 
  /* stmt->query_id= thd->query_id; */
  thd->stmt_backup.set_statement(thd);
  thd->set_statement(stmt);

  /*
    To make sure that all runtime data is stored in its own memory root and 
    does not interfere with data possibly present in thd->mem_root.
    This root is cleaned up in the end of execution.
    FIXME: to be replaced with more efficient approach, and verified why we
    can not use thd->mem_root safely.
  */
  init_sql_alloc(&thd->mem_root,
                 thd->variables.query_alloc_block_size,
                 thd->variables.query_prealloc_size);


  for (SELECT_LEX *sl= stmt->lex->all_selects_list;
1000 1001 1002
       sl;
       sl= sl->next_select_in_list())
  {
unknown's avatar
unknown committed
1003 1004 1005
    /*
      Copy WHERE clause pointers to avoid damaging they by optimisation
    */
1006 1007
    if (sl->prep_where)
      sl->where= sl->prep_where->copy_andor_structure(thd);
1008
    DBUG_ASSERT(sl->join == 0);
1009
  }
1010 1011 1012 1013 1014 1015 1016 1017 1018 1019

  /*
    TODO: When the new table structure is ready, then have a status bit 
    to indicate the table is altered, and re-do the setup_* 
    and open the tables back.
  */
  for (TABLE_LIST *tables= (TABLE_LIST*) stmt->lex->select_lex.table_list.first;
       tables;
       tables= tables->next)
    tables->table= 0; // safety - nasty init
1020

unknown's avatar
unknown committed
1021
#ifndef EMBEDDED_LIBRARY
unknown's avatar
unknown committed
1022
  if (stmt->param_count && setup_params_data(stmt))
unknown's avatar
unknown committed
1023
    DBUG_VOID_RETURN;
unknown's avatar
unknown committed
1024 1025 1026 1027
#else
  if (stmt->param_count && (*stmt->setup_params_data)(stmt))
    DBUG_VOID_RETURN;
#endif
1028

unknown's avatar
unknown committed
1029 1030 1031
  if (!(specialflag & SPECIAL_NO_PRIOR))
    my_pthread_setprio(pthread_self(),QUERY_PRIOR);  
 
1032 1033
  /*
    TODO:
unknown's avatar
unknown committed
1034 1035 1036 1037
    Also, have checks on basic executions such as mysql_insert(), 
    mysql_delete(), mysql_update() and mysql_select() to not to 
    have re-check on setup_* and other things ..
  */  
1038 1039 1040
  thd->protocol= &thd->protocol_prep;		// Switch to binary protocol
  mysql_execute_command(thd);
  thd->protocol= &thd->protocol_simple;	// Use normal protocol
unknown's avatar
unknown committed
1041

unknown's avatar
unknown committed
1042
  if (!(specialflag & SPECIAL_NO_PRIOR))
1043
    my_pthread_setprio(pthread_self(), WAIT_PRIOR);
unknown's avatar
unknown committed
1044

1045 1046
  free_root(&thd->mem_root, MYF(0));
  thd->set_statement(&thd->stmt_backup);
unknown's avatar
unknown committed
1047 1048 1049
  DBUG_VOID_RETURN;
}

1050

unknown's avatar
unknown committed
1051
/*
1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062
  Reset a prepared statement
  
  SYNOPSIS
    mysql_stmt_reset()
    thd		Thread handle
    packet	Packet with stmt handle

  DESCRIPTION
    This function is useful when one gets an error after calling
    mysql_stmt_getlongdata() and one wants to reset the handle
    so that one can call execute again.
unknown's avatar
unknown committed
1063 1064
*/

1065
void mysql_stmt_reset(THD *thd, char *packet)
unknown's avatar
unknown committed
1066
{
1067
  ulong stmt_id= uint4korr(packet);
1068 1069
  Prepared_statement *stmt;
  
1070
  DBUG_ENTER("mysql_stmt_reset");
unknown's avatar
unknown committed
1071

1072
  if (!(stmt= find_prepared_statement(thd, stmt_id, "reset")))
1073 1074
    DBUG_VOID_RETURN;

unknown's avatar
unknown committed
1075 1076
  stmt->error_in_prepare= 0;
  Item_param *item= *stmt->param, *end= item + stmt->param_count;
1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092

  /* Free long data if used */
  if (stmt->long_data_used)
  {
    stmt->long_data_used= 0;
    for (; item < end ; item++)
      item->reset();
  }
  DBUG_VOID_RETURN;
}


/*
  Delete a prepared statement from memory
*/

unknown's avatar
unknown committed
1093
void mysql_stmt_free(THD *thd, char *packet)
1094 1095
{
  ulong stmt_id= uint4korr(packet);
1096 1097
  Prepared_statement *stmt;

unknown's avatar
unknown committed
1098
  DBUG_ENTER("mysql_stmt_free");
1099

1100
  if (!(stmt= find_prepared_statement(thd, stmt_id, "close")))
1101
    DBUG_VOID_RETURN;
1102 1103 1104

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

1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129

/*
  Long data in pieces from client                            

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

  DESCRIPTION
    Get a part of a long data.
    To make the protocol efficient, we are not sending any return packages
    here.
    If something goes wrong, then we will send the error on 'execute'

    We assume that the client takes care of checking that all parts are sent
    to the server. (No checking that we get a 'end of column' in the server)
*/

void mysql_stmt_get_longdata(THD *thd, char *pos, ulong packet_length)
{
1130 1131
  Prepared_statement *stmt;
  
1132 1133
  DBUG_ENTER("mysql_stmt_get_longdata");

unknown's avatar
unknown committed
1134
#ifndef EMBEDDED_LIBRARY
1135
  /* The following should never happen */
unknown's avatar
unknown committed
1136
  if (packet_length < MYSQL_LONG_DATA_HEADER+1)
1137 1138 1139 1140
  {
    my_error(ER_WRONG_ARGUMENTS, MYF(0), "get_longdata");
    DBUG_VOID_RETURN;
  }
unknown's avatar
unknown committed
1141
#endif
1142 1143 1144 1145 1146 1147 1148

  ulong stmt_id=     uint4korr(pos);
  uint param_number= uint2korr(pos+4);

  if (!(stmt=find_prepared_statement(thd, stmt_id, "get_longdata")))
    DBUG_VOID_RETURN;

unknown's avatar
unknown committed
1149
#ifndef EMBEDDED_LIBRARY
1150 1151
  if (param_number >= stmt->param_count)
  {
unknown's avatar
unknown committed
1152 1153 1154
    /* Error will be sent in execute call */
    stmt->error_in_prepare= 1;
    stmt->last_errno= ER_WRONG_ARGUMENTS;
1155 1156 1157
    sprintf(stmt->last_error, ER(ER_WRONG_ARGUMENTS), "get_longdata");
    DBUG_VOID_RETURN;
  }
unknown's avatar
unknown committed
1158 1159 1160
  pos+= MYSQL_LONG_DATA_HEADER;	// Point to data
#endif

unknown's avatar
unknown committed
1161
  Item_param *param= *(stmt->param+param_number);
unknown's avatar
unknown committed
1162
#ifndef EMBEDDED_LIBRARY
unknown's avatar
unknown committed
1163
  param->set_longdata(pos, packet_length-MYSQL_LONG_DATA_HEADER-1);
unknown's avatar
unknown committed
1164 1165 1166
#else
  param->set_longdata(thd->extra_data, thd->extra_length);
#endif
1167 1168 1169
  stmt->long_data_used= 1;
  DBUG_VOID_RETURN;
}
unknown's avatar
unknown committed
1170

1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213

Prepared_statement::Prepared_statement(THD *thd_arg)
  :Statement(thd_arg),
  thd(thd_arg),
  param(0),
  param_count(0),
  last_errno(0),
  error_in_prepare(0),
  long_data_used(0),
  log_full_query(0)
{
  *last_error= '\0';
  if (mysql_bin_log.is_open())
  {
    log_full_query= 1;
#ifndef EMBEDDED_LIBRARY
    setup_params= insert_params_withlog;
#else
    setup_params_data= setup_params_data_withlog;
#endif
  }
  else
#ifndef EMBEDDED_LIBRARY
    setup_params= insert_params; // not fully qualified query
#else
    setup_params_data= setup_params_data;
#endif
}


Prepared_statement::~Prepared_statement()
{
  my_free((char *) param, MYF(MY_ALLOW_ZERO_PTR));
  free_items(free_list);
}


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