sql_class.cc 57.9 KB
Newer Older
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1
/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
2

bk@work.mysql.com's avatar
bk@work.mysql.com committed
3 4 5 6
   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.
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
7

bk@work.mysql.com's avatar
bk@work.mysql.com committed
8 9 10 11
   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.
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
12

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


/*****************************************************************************
**
** This file implements classes defined in sql_class.h
** Especially the classes to handle a result from a select
**
*****************************************************************************/

25
#ifdef USE_PRAGMA_IMPLEMENTATION
bk@work.mysql.com's avatar
bk@work.mysql.com committed
26 27 28 29 30 31
#pragma implementation				// gcc: Class implementation
#endif

#include "mysql_priv.h"
#include <m_ctype.h>
#include <sys/stat.h>
32
#include <thr_alarm.h>
bk@work.mysql.com's avatar
bk@work.mysql.com committed
33 34 35
#ifdef	__WIN__
#include <io.h>
#endif
36
#include <mysys_err.h>
bk@work.mysql.com's avatar
bk@work.mysql.com committed
37

monty@mysql.com's avatar
monty@mysql.com committed
38 39
#include "sp_rcontext.h"
#include "sp_cache.h"
40

41 42 43 44 45 46
/*
  The following is used to initialise Table_ident with a internal
  table name
*/
char internal_table_name[2]= "*";

47 48
const char * const THD::DEFAULT_WHERE= "field list";

49

bk@work.mysql.com's avatar
bk@work.mysql.com committed
50 51 52 53
/*****************************************************************************
** Instansiate templates
*****************************************************************************/

54
#ifdef HAVE_EXPLICIT_TEMPLATE_INSTANTIATION
bk@work.mysql.com's avatar
bk@work.mysql.com committed
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
/* Used templates */
template class List<Key>;
template class List_iterator<Key>;
template class List<key_part_spec>;
template class List_iterator<key_part_spec>;
template class List<Alter_drop>;
template class List_iterator<Alter_drop>;
template class List<Alter_column>;
template class List_iterator<Alter_column>;
#endif

/****************************************************************************
** User variables
****************************************************************************/

70 71
extern "C" byte *get_var_key(user_var_entry *entry, uint *length,
			     my_bool not_used __attribute__((unused)))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
72 73 74 75 76
{
  *length=(uint) entry->name.length;
  return (byte*) entry->name.str;
}

77
extern "C" void free_user_var(user_var_entry *entry)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
78 79 80 81 82 83 84
{
  char *pos= (char*) entry+ALIGN_SIZE(sizeof(*entry));
  if (entry->value && entry->value != pos)
    my_free(entry->value, MYF(0));
  my_free((char*) entry,MYF(0));
}

85 86 87 88 89
bool key_part_spec::operator==(const key_part_spec& other) const
{
  return length == other.length && !strcmp(field_name, other.field_name);
}

90 91

/*
92
  Test if a foreign key (= generated key) is a prefix of the given key
93 94 95 96 97 98 99 100 101 102 103 104 105 106
  (ignoring key name, key type and order of columns)

  NOTES:
    This is only used to test if an index for a FOREIGN KEY exists

  IMPLEMENTATION
    We only compare field names

  RETURN
    0	Generated key is a prefix of other key
    1	Not equal
*/

bool foreign_key_prefix(Key *a, Key *b)
107
{
108 109 110 111
  /* Ensure that 'a' is the generated key */
  if (a->generated)
  {
    if (b->generated && a->columns.elements > b->columns.elements)
112
      swap_variables(Key*, a, b);               // Put shorter key in 'a'
113 114
  }
  else
115
  {
116 117
    if (!b->generated)
      return TRUE;                              // No foreign key
118
    swap_variables(Key*, a, b);                 // Put generated key in 'a'
119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134
  }

  /* Test if 'a' is a prefix of 'b' */
  if (a->columns.elements > b->columns.elements)
    return TRUE;                                // Can't be prefix

  List_iterator<key_part_spec> col_it1(a->columns);
  List_iterator<key_part_spec> col_it2(b->columns);
  const key_part_spec *col1, *col2;

#ifdef ENABLE_WHEN_INNODB_CAN_HANDLE_SWAPED_FOREIGN_KEY_COLUMNS
  while ((col1= col_it1++))
  {
    bool found= 0;
    col_it2.rewind();
    while ((col2= col_it2++))
135
    {
136 137 138 139 140
      if (*col1 == *col2)
      {
        found= TRUE;
	break;
      }
141
    }
142 143 144 145 146 147 148 149 150 151
    if (!found)
      return TRUE;                              // Error
  }
  return FALSE;                                 // Is prefix
#else
  while ((col1= col_it1++))
  {
    col2= col_it2++;
    if (!(*col1 == *col2))
      return TRUE;
152
  }
153 154
  return FALSE;                                 // Is prefix
#endif
155 156 157
}


bk@work.mysql.com's avatar
bk@work.mysql.com committed
158 159 160
/****************************************************************************
** Thread specific functions
****************************************************************************/
161

162 163
Open_tables_state::Open_tables_state(ulong version_arg)
  :version(version_arg)
164 165 166 167 168
{
  reset_open_tables_state();
}


169 170 171 172 173
/*
  Pass nominal parameters to Statement constructor only to ensure that
  the destructor works OK in case of error. The main_mem_root will be
  re-initialized in init().
*/
bk@work.mysql.com's avatar
bk@work.mysql.com committed
174

175
THD::THD()
176
  :Statement(CONVENTIONAL_EXECUTION, 0, ALLOC_ROOT_MIN_BLOCK_SIZE, 0),
177
   Open_tables_state(refresh_version),
178
   lock_id(&main_lock_id),
179
   user_time(0), in_sub_stmt(0), global_read_lock(0), is_fatal_error(0),
180 181
   rand_used(0), time_zone_used(0),
   last_insert_id_used(0), insert_id_used(0), clear_next_insert_id(0),
182
   in_lock_tables(0), bootstrap(0), derived_tables_processing(FALSE),
183
   spcont(NULL)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
184
{
konstantin@mysql.com's avatar
konstantin@mysql.com committed
185
  stmt_arena= this;
186
  thread_stack= 0;
187
  db= 0;
188
  catalog= (char*)"std"; // the only catalog we have for now
189 190
  main_security_ctx.init();
  security_ctx= &main_security_ctx;
pem@mysql.com's avatar
pem@mysql.com committed
191
  locked=some_tables_deleted=no_errors=password= 0;
konstantin@oak.local's avatar
konstantin@oak.local committed
192
  query_start_used= 0;
193
  count_cuted_fields= CHECK_FIELD_IGNORE;
hf@genie.(none)'s avatar
SCRUM  
hf@genie.(none) committed
194
  killed= NOT_KILLED;
195
  db_length= col_access=0;
196
  query_error= tmp_table_used= 0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
197
  next_insert_id=last_insert_id=0;
198
  hash_clear(&handler_tables_hash);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
199
  tmp_table=0;
200
  used_tables=0;
201
  cuted_fields= sent_row_count= 0L;
202
  limit_found_rows= 0;
203
  statement_id_counter= 0UL;
204
  // Must be reset to handle error with THD's created for init of mysqld
konstantin@oak.local's avatar
konstantin@oak.local committed
205
  lex->current_select= 0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
206
  start_time=(time_t) 0;
207
  current_linfo =  0;
208
  slave_thread = 0;
209
  variables.pseudo_thread_id= 0;
210
  one_shot_set= 0;
211
  file_id = 0;
212
  query_id= 0;
213
  warn_id= 0;
214
  db_charset= global_system_variables.collation_database;
215
  bzero(ha_data, sizeof(ha_data));
monty@tik.mysql.fi's avatar
monty@tik.mysql.fi committed
216
  mysys_var=0;
217
  binlog_evt_union.do_union= FALSE;
218 219
#ifndef DBUG_OFF
  dbug_sentry=THD_SENTRY_MAGIC;
220
#endif
221
#ifndef EMBEDDED_LIBRARY
monty@tik.mysql.fi's avatar
monty@tik.mysql.fi committed
222
  net.vio=0;
223
#endif
224
  client_capabilities= 0;                       // minimalistic client
225 226
  net.last_error[0]=0;                          // If error on boot
  net.query_cache_query=0;                      // If error on boot
monty@tik.mysql.fi's avatar
monty@tik.mysql.fi committed
227
  ull=0;
228
  system_thread= cleanup_done= abort_on_warning= no_warnings_for_error= 0;
229
  peer_port= 0;					// For SHOW PROCESSLIST
monty@tik.mysql.fi's avatar
monty@tik.mysql.fi committed
230 231 232 233 234
#ifdef	__WIN__
  real_id = 0;
#endif
#ifdef SIGNAL_WITH_VIO_CLOSE
  active_vio = 0;
235
#endif
236
  pthread_mutex_init(&LOCK_delete, MY_MUTEX_INIT_FAST);
monty@tik.mysql.fi's avatar
monty@tik.mysql.fi committed
237 238 239

  /* Variables with default values */
  proc_info="login";
240
  where= THD::DEFAULT_WHERE;
monty@tik.mysql.fi's avatar
monty@tik.mysql.fi committed
241
  server_id = ::server_id;
242
  slave_net = 0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
243
  command=COM_CONNECT;
244
  *scramble= '\0';
monty@tik.mysql.fi's avatar
monty@tik.mysql.fi committed
245

246
  init();
monty@tik.mysql.fi's avatar
monty@tik.mysql.fi committed
247
  /* Initialize sub structures */
248
  init_sql_alloc(&warn_root, WARN_ALLOC_BLOCK_SIZE, WARN_ALLOC_PREALLOC_SIZE);
249
  user_connect=(USER_CONN *)0;
250
  hash_init(&user_vars, system_charset_info, USER_VARS_HASH_SIZE, 0, 0,
bk@work.mysql.com's avatar
bk@work.mysql.com committed
251
	    (hash_get_key) get_var_key,
252
	    (hash_free_key) free_user_var, 0);
253

254 255
  sp_proc_cache= NULL;
  sp_func_cache= NULL;
256

257 258 259
  /* For user vars replication*/
  if (opt_bin_log)
    my_init_dynamic_array(&user_var_events,
260
			  sizeof(BINLOG_USER_VAR_EVENT *), 16, 16);
261 262 263
  else
    bzero((char*) &user_var_events, sizeof(user_var_events));

264 265 266 267 268
  /* Protocol */
  protocol= &protocol_simple;			// Default protocol
  protocol_simple.init(this);
  protocol_prep.init(this);

heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
269
  tablespace_op=FALSE;
270 271
  ulong tmp=sql_rnd_with_mutex();
  randominit(&rand, tmp + (ulong) &rand, tmp + (ulong) ::query_id);
272 273
  thr_lock_info_init(&lock_info); /* safety: will be reset after start */
  thr_lock_owner_init(&main_lock_id, &lock_info);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
274 275
}

276 277 278 279 280 281 282

/*
  Init common variables that has to be reset on start and on change_user
*/

void THD::init(void)
{
283 284
  pthread_mutex_lock(&LOCK_global_system_variables);
  variables= global_system_variables;
285 286 287 288 289 290
  variables.time_format= date_time_format_copy((THD*) 0,
					       variables.time_format);
  variables.date_format= date_time_format_copy((THD*) 0,
					       variables.date_format);
  variables.datetime_format= date_time_format_copy((THD*) 0,
						   variables.datetime_format);
291 292 293
#ifdef HAVE_NDBCLUSTER_DB
  variables.ndb_use_transactions= 1;
#endif
294
  pthread_mutex_unlock(&LOCK_global_system_variables);
295
  server_status= SERVER_STATUS_AUTOCOMMIT;
296 297
  if (variables.sql_mode & MODE_NO_BACKSLASH_ESCAPES)
    server_status|= SERVER_STATUS_NO_BACKSLASH_ESCAPES;
298 299
  options= thd_startup_options;
  open_options=ha_open_options;
300 301 302
  update_lock_default= (variables.low_priority_updates ?
			TL_WRITE_LOW_PRIORITY :
			TL_WRITE);
303
  session_tx_isolation= (enum_tx_isolation) variables.tx_isolation;
304 305 306
  warn_list.empty();
  bzero((char*) warn_count, sizeof(warn_count));
  total_warn_count= 0;
307
  update_charset();
308
  bzero((char *) &status_var, sizeof(status_var));
309 310
}

311

312 313 314 315 316 317 318 319
/*
  Init THD for query processing.
  This has to be called once before we call mysql_parse.
  See also comments in sql_class.h.
*/

void THD::init_for_queries()
{
320
  ha_enable_transaction(this,TRUE);
321

322
  reset_root_defaults(mem_root, variables.query_alloc_block_size,
323
                      variables.query_prealloc_size);
324
#ifdef USING_TRANSACTIONS
325 326 327
  reset_root_defaults(&transaction.mem_root,
                      variables.trans_alloc_block_size,
                      variables.trans_prealloc_size);
328
#endif
329 330
  transaction.xid_state.xid.null();
  transaction.xid_state.in_thd=1;
331 332 333
}


334 335 336 337 338 339 340 341 342 343 344 345 346 347
/*
  Do what's needed when one invokes change user

  SYNOPSIS
    change_user()

  IMPLEMENTATION
    Reset all resources that are connection specific
*/


void THD::change_user(void)
{
  cleanup();
348
  cleanup_done= 0;
349
  init();
350
  stmt_map.reset();
351
  hash_init(&user_vars, system_charset_info, USER_VARS_HASH_SIZE, 0, 0,
352
	    (hash_get_key) get_var_key,
353
	    (hash_free_key) free_user_var, 0);
354 355
  sp_cache_clear(&sp_proc_cache);
  sp_cache_clear(&sp_func_cache);
356 357 358
}


monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
359 360 361
/* Do operations that may take a long time */

void THD::cleanup(void)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
362
{
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
363
  DBUG_ENTER("THD::cleanup");
364
#ifdef ENABLE_WHEN_BINLOG_WILL_BE_ABLE_TO_PREPARE
365 366 367 368
  if (transaction.xid_state.xa_state == XA_PREPARED)
  {
#error xid_state in the cache should be replaced by the allocated value
  }
369
#endif
370
  {
serg@serg.mylan's avatar
serg@serg.mylan committed
371
    ha_rollback(this);
372 373
    xid_cache_delete(&transaction.xid_state);
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
374 375 376 377 378
  if (locked_tables)
  {
    lock=locked_tables; locked_tables=0;
    close_thread_tables(this);
  }
379
  mysql_ha_flush(this, (TABLE_LIST*) 0,
380
                 MYSQL_HA_CLOSE_FINAL | MYSQL_HA_FLUSH_ALL, FALSE);
381
  hash_free(&handler_tables_hash);
382 383
  delete_dynamic(&user_var_events);
  hash_free(&user_vars);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
384
  close_temporary_tables(this);
385 386 387
  my_free((char*) variables.time_format, MYF(MY_ALLOW_ZERO_PTR));
  my_free((char*) variables.date_format, MYF(MY_ALLOW_ZERO_PTR));
  my_free((char*) variables.datetime_format, MYF(MY_ALLOW_ZERO_PTR));
388
  
389
  sp_cache_clear(&sp_proc_cache);
390 391
  sp_cache_clear(&sp_func_cache);

392 393 394
  if (global_read_lock)
    unlock_global_read_lock(this);
  if (ull)
395
  {
396 397 398 399
    pthread_mutex_lock(&LOCK_user_locks);
    item_user_lock_release(ull);
    pthread_mutex_unlock(&LOCK_user_locks);
    ull= 0;
400
  }
401

monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
402 403 404 405
  cleanup_done=1;
  DBUG_VOID_RETURN;
}

406

monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
407 408
THD::~THD()
{
409
  THD_CHECK_SENTRY(this);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
410
  DBUG_ENTER("~THD()");
411 412 413
  /* Ensure that no one is using THD */
  pthread_mutex_lock(&LOCK_delete);
  pthread_mutex_unlock(&LOCK_delete);
414
  add_to_status(&global_status_var, &status_var);
415

monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
416
  /* Close connection */
serg@serg.mylan's avatar
serg@serg.mylan committed
417
#ifndef EMBEDDED_LIBRARY
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
418 419 420
  if (net.vio)
  {
    vio_delete(net.vio);
serg@serg.mylan's avatar
serg@serg.mylan committed
421
    net_end(&net);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
422
  }
423
#endif
424
  stmt_map.reset();                     /* close all prepared statements */
425
  DBUG_ASSERT(lock_info.n_cursors == 0);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
426 427
  if (!cleanup_done)
    cleanup();
428

serg@serg.mylan's avatar
serg@serg.mylan committed
429
  ha_close_connection(this);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
430

431 432
  DBUG_PRINT("info", ("freeing security context"));
  main_security_ctx.destroy();
433
  safeFree(db);
434
  free_root(&warn_root,MYF(0));
435
#ifdef USING_TRANSACTIONS
436
  free_root(&transaction.mem_root,MYF(0));
437
#endif
bk@work.mysql.com's avatar
bk@work.mysql.com committed
438
  mysys_var=0;					// Safety (shouldn't be needed)
439
  pthread_mutex_destroy(&LOCK_delete);
440
#ifndef DBUG_OFF
konstantin@oak.local's avatar
konstantin@oak.local committed
441
  dbug_sentry= THD_SENTRY_GONE;
442
#endif  
bk@work.mysql.com's avatar
bk@work.mysql.com committed
443 444 445
  DBUG_VOID_RETURN;
}

446

447
/*
448 449 450 451 452 453
  Add all status variables to another status variable array

  SYNOPSIS
   add_to_status()
   to_var       add to this array
   from_var     from this array
454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469

  NOTES
    This function assumes that all variables are long/ulong.
    If this assumption will change, then we have to explictely add
    the other variables after the while loop
*/

void add_to_status(STATUS_VAR *to_var, STATUS_VAR *from_var)
{
  ulong *end= (ulong*) ((byte*) to_var + offsetof(STATUS_VAR,
						  last_system_status_var) +
			sizeof(ulong));
  ulong *to= (ulong*) to_var, *from= (ulong*) from_var;

  while (to != end)
    *(to++)+= *(from++);
470
  /* it doesn't make sense to add last_query_cost values */
471 472 473
}


hf@genie.(none)'s avatar
SCRUM  
hf@genie.(none) committed
474
void THD::awake(THD::killed_state state_to_set)
475
{
476
  THD_CHECK_SENTRY(this);
477 478
  safe_mutex_assert_owner(&LOCK_delete); 

hf@genie.(none)'s avatar
SCRUM  
hf@genie.(none) committed
479 480
  killed= state_to_set;
  if (state_to_set != THD::KILL_QUERY)
481
  {
hf@genie.(none)'s avatar
SCRUM  
hf@genie.(none) committed
482
    thr_alarm_kill(real_id);
483
#ifdef SIGNAL_WITH_VIO_CLOSE
484
    close_active_vio();
485
#endif    
486
  }
487
  if (mysys_var)
488 489 490 491 492 493 494 495 496
  {
    pthread_mutex_lock(&mysys_var->mutex);
    if (!system_thread)		// Don't abort locks
      mysys_var->abort=1;
    /*
      This broadcast could be up in the air if the victim thread
      exits the cond in the time between read and broadcast, but that is
      ok since all we want to do is to make the victim thread get out
      of waiting on current_cond.
497 498 499 500 501
      If we see a non-zero current_cond: it cannot be an old value (because
      then exit_cond() should have run and it can't because we have mutex); so
      it is the true value but maybe current_mutex is not yet non-zero (we're
      in the middle of enter_cond() and there is a "memory order
      inversion"). So we test the mutex too to not lock 0.
502

503
      Note that there is a small chance we fail to kill. If victim has locked
504 505 506 507 508
      current_mutex, but hasn't yet entered enter_cond() (which means that
      current_cond and current_mutex are 0), then the victim will not get
      a signal and it may wait "forever" on the cond (until
      we issue a second KILL or the status it's waiting for happens).
      It's true that we have set its thd->killed but it may not
509
      see it immediately and so may have time to reach the cond_wait().
510
    */
511
    if (mysys_var->current_cond && mysys_var->current_mutex)
512
    {
513 514 515
      pthread_mutex_lock(mysys_var->current_mutex);
      pthread_cond_broadcast(mysys_var->current_cond);
      pthread_mutex_unlock(mysys_var->current_mutex);
516
    }
517 518
    pthread_mutex_unlock(&mysys_var->mutex);
  }
519 520
}

521 522 523 524
/*
  Remember the location of thread info, the structure needed for
  sql_alloc() and the structure for the net buffer
*/
bk@work.mysql.com's avatar
bk@work.mysql.com committed
525 526 527

bool THD::store_globals()
{
528 529 530 531 532 533
  /*
    Assert that thread_stack is initialized: it's necessary to be able
    to track stack overrun.
  */
  DBUG_ASSERT(this->thread_stack);

534
  if (my_pthread_setspecific_ptr(THR_THD,  this) ||
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
535
      my_pthread_setspecific_ptr(THR_MALLOC, &mem_root))
536 537 538
    return 1;
  mysys_var=my_thread_var;
  dbug_thread_id=my_thread_id();
guilhem@mysql.com's avatar
guilhem@mysql.com committed
539 540 541 542
  /*
    By default 'slave_proxy_id' is 'thread_id'. They may later become different
    if this is the slave SQL thread.
  */
543
  variables.pseudo_thread_id= thread_id;
544 545 546 547
  /*
    We have to call thr_lock_info_init() again here as THD may have been
    created in another thread
  */
548
  thr_lock_info_init(&lock_info);
549
  return 0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
550 551
}

552

553 554 555 556 557 558 559 560 561 562
/* Cleanup after a query */

void THD::cleanup_after_query()
{
  if (clear_next_insert_id)
  {
    clear_next_insert_id= 0;
    next_insert_id= 0;
  }
  /* Free Items that were created during this execution */
563
  free_items();
564 565
  /* Reset where. */
  where= THD::DEFAULT_WHERE;
566 567
}

monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
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
/*
  Convert a string to another character set

  SYNOPSIS
    convert_string()
    to				Store new allocated string here
    to_cs			New character set for allocated string
    from			String to convert
    from_length			Length of string to convert
    from_cs			Original character set

  NOTES
    to will be 0-terminated to make it easy to pass to system funcs

  RETURN
    0	ok
    1	End of memory.
        In this case to->str will point to 0 and to->length will be 0.
*/

bool THD::convert_string(LEX_STRING *to, CHARSET_INFO *to_cs,
			 const char *from, uint from_length,
			 CHARSET_INFO *from_cs)
{
  DBUG_ENTER("convert_string");
  size_s new_length= to_cs->mbmaxlen * from_length;
594
  uint dummy_errors;
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
595 596 597 598 599 600
  if (!(to->str= alloc(new_length+1)))
  {
    to->length= 0;				// Safety fix
    DBUG_RETURN(1);				// EOM
  }
  to->length= copy_and_convert((char*) to->str, new_length, to_cs,
601
			       from, from_length, from_cs, &dummy_errors);
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
602 603 604 605 606
  to->str[to->length]=0;			// Safety
  DBUG_RETURN(0);
}


607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623
/*
  Convert string from source character set to target character set inplace.

  SYNOPSIS
    THD::convert_string

  DESCRIPTION
    Convert string using convert_buffer - buffer for character set 
    conversion shared between all protocols.

  RETURN
    0   ok
   !0   out of memory
*/

bool THD::convert_string(String *s, CHARSET_INFO *from_cs, CHARSET_INFO *to_cs)
{
624 625
  uint dummy_errors;
  if (convert_buffer.copy(s->ptr(), s->length(), from_cs, to_cs, &dummy_errors))
626 627 628 629 630 631 632 633 634 635 636
    return TRUE;
  /* If convert_buffer >> s copying is more efficient long term */
  if (convert_buffer.alloced_length() >= convert_buffer.length() * 2 ||
      !s->is_alloced())
  {
    return s->copy(convert_buffer);
  }
  s->swap(convert_buffer);
  return FALSE;
}

637

monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
638 639 640 641 642 643
/*
  Update some cache variables when character set changes
*/

void THD::update_charset()
{
644 645 646 647 648 649 650
  uint32 not_used;
  charset_is_system_charset= !String::needs_conversion(0,charset(),
                                                       system_charset_info,
                                                       &not_used);
  charset_is_collation_connection= 
    !String::needs_conversion(0,charset(),variables.collation_connection,
                              &not_used);
bar@mysql.com's avatar
bar@mysql.com committed
651 652 653
  charset_is_character_set_filesystem= 
    !String::needs_conversion(0, charset(),
                              variables.character_set_filesystem, &not_used);
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
654 655 656
}


657 658 659 660 661 662 663 664 665 666 667 668 669 670
/* routings to adding tables to list of changed in transaction tables */

inline static void list_include(CHANGED_TABLE_LIST** prev,
				CHANGED_TABLE_LIST* curr,
				CHANGED_TABLE_LIST* new_table)
{
  if (new_table)
  {
    *prev = new_table;
    (*prev)->next = curr;
  }
}

/* add table to list of changed in transaction tables */
671

672 673
void THD::add_changed_table(TABLE *table)
{
674
  DBUG_ENTER("THD::add_changed_table(table)");
675

676
  DBUG_ASSERT((options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)) &&
677
	      table->file->has_transactions());
678
  add_changed_table(table->s->table_cache_key, table->s->key_length);
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
679
  DBUG_VOID_RETURN;
680
}
681

682

683 684 685
void THD::add_changed_table(const char *key, long key_length)
{
  DBUG_ENTER("THD::add_changed_table(key)");
686 687
  CHANGED_TABLE_LIST **prev_changed = &transaction.changed_tables;
  CHANGED_TABLE_LIST *curr = transaction.changed_tables;
688

689
  for (; curr; prev_changed = &(curr->next), curr = curr->next)
690
  {
691
    int cmp =  (long)curr->key_length - (long)key_length;
692 693
    if (cmp < 0)
    {
694
      list_include(prev_changed, curr, changed_table_dup(key, key_length));
695
      DBUG_PRINT("info", 
696
		 ("key_length %u %u", key_length, (*prev_changed)->key_length));
697 698 699 700
      DBUG_VOID_RETURN;
    }
    else if (cmp == 0)
    {
701
      cmp = memcmp(curr->key, key, curr->key_length);
702 703
      if (cmp < 0)
      {
704
	list_include(prev_changed, curr, changed_table_dup(key, key_length));
705
	DBUG_PRINT("info", 
706
		   ("key_length %u %u", key_length,
707
		    (*prev_changed)->key_length));
708 709 710 711 712 713 714 715 716
	DBUG_VOID_RETURN;
      }
      else if (cmp == 0)
      {
	DBUG_PRINT("info", ("already in list"));
	DBUG_VOID_RETURN;
      }
    }
  }
717
  *prev_changed = changed_table_dup(key, key_length);
718
  DBUG_PRINT("info", ("key_length %u %u", key_length,
719
		      (*prev_changed)->key_length));
720 721 722
  DBUG_VOID_RETURN;
}

723

724
CHANGED_TABLE_LIST* THD::changed_table_dup(const char *key, long key_length)
725 726 727
{
  CHANGED_TABLE_LIST* new_table = 
    (CHANGED_TABLE_LIST*) trans_alloc(ALIGN_SIZE(sizeof(CHANGED_TABLE_LIST))+
728
				      key_length + 1);
729 730
  if (!new_table)
  {
731 732
    my_error(EE_OUTOFMEMORY, MYF(ME_BELL),
             ALIGN_SIZE(sizeof(TABLE_LIST)) + key_length + 1);
hf@genie.(none)'s avatar
SCRUM  
hf@genie.(none) committed
733
    killed= KILL_CONNECTION;
734 735 736 737 738 739
    return 0;
  }

  new_table->key = (char *) (((byte*)new_table)+
			     ALIGN_SIZE(sizeof(CHANGED_TABLE_LIST)));
  new_table->next = 0;
740 741
  new_table->key_length = key_length;
  ::memcpy(new_table->key, key, key_length);
742 743 744
  return new_table;
}

745

bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
746 747 748 749
int THD::send_explain_fields(select_result *result)
{
  List<Item> field_list;
  Item *item;
750
  CHARSET_INFO *cs= system_charset_info;
751
  field_list.push_back(new Item_return_int("id",3, MYSQL_TYPE_LONGLONG));
752
  field_list.push_back(new Item_empty_string("select_type", 19, cs));
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
753 754 755 756
  field_list.push_back(item= new Item_empty_string("table", NAME_LEN, cs));
  item->maybe_null= 1;
  field_list.push_back(item= new Item_empty_string("type", 10, cs));
  item->maybe_null= 1;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
757
  field_list.push_back(item=new Item_empty_string("possible_keys",
758
						  NAME_LEN*MAX_KEY, cs));
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
759
  item->maybe_null=1;
760
  field_list.push_back(item=new Item_empty_string("key", NAME_LEN, cs));
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
761
  item->maybe_null=1;
762 763
  field_list.push_back(item=new Item_empty_string("key_len",
						  NAME_LEN*MAX_KEY));
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
764 765
  item->maybe_null=1;
  field_list.push_back(item=new Item_empty_string("ref",
766
						  NAME_LEN*MAX_REF_PARTS, cs));
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
767
  item->maybe_null=1;
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
768 769 770
  field_list.push_back(item= new Item_return_int("rows", 10,
                                                 MYSQL_TYPE_LONGLONG));
  item->maybe_null= 1;
771
  field_list.push_back(new Item_empty_string("Extra", 255, cs));
772 773
  return (result->send_fields(field_list,
                              Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF));
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
774
}
775

776 777 778
#ifdef SIGNAL_WITH_VIO_CLOSE
void THD::close_active_vio()
{
779
  DBUG_ENTER("close_active_vio");
780
  safe_mutex_assert_owner(&LOCK_delete); 
781
#ifndef EMBEDDED_LIBRARY
782 783 784 785 786
  if (active_vio)
  {
    vio_close(active_vio);
    active_vio = 0;
  }
787
#endif
788
  DBUG_VOID_RETURN;
789 790 791
}
#endif

792

793 794 795 796 797
struct Item_change_record: public ilink
{
  Item **place;
  Item *old_value;
  /* Placement new was hidden by `new' in ilink (TODO: check): */
798
  static void *operator new(size_t size, void *mem) { return mem; }
799 800
  static void operator delete(void *ptr, size_t size) {}
  static void operator delete(void *ptr, void *mem) { /* never called */ }
801 802 803 804 805 806
};


/*
  Register an item tree tree transformation, performed by the query
  optimizer. We need a pointer to runtime_memroot because it may be !=
konstantin@mysql.com's avatar
konstantin@mysql.com committed
807
  thd->mem_root (due to possible set_n_backup_active_arena called for thd).
808 809 810 811 812 813 814 815 816 817 818 819 820 821
*/

void THD::nocheck_register_item_tree_change(Item **place, Item *old_value,
                                            MEM_ROOT *runtime_memroot)
{
  Item_change_record *change;
  /*
    Now we use one node per change, which adds some memory overhead,
    but still is rather fast as we use alloc_root for allocations.
    A list of item tree changes of an average query should be short.
  */
  void *change_mem= alloc_root(runtime_memroot, sizeof(*change));
  if (change_mem == 0)
  {
822 823 824 825
    /*
      OOM, thd->fatal_error() is called by the error handler of the
      memroot. Just return.
    */
826 827 828 829 830
    return;
  }
  change= new (change_mem) Item_change_record;
  change->place= place;
  change->old_value= old_value;
831
  change_list.append(change);
832 833 834 835 836 837 838
}


void THD::rollback_item_tree_changes()
{
  I_List_iterator<Item_change_record> it(change_list);
  Item_change_record *change;
monty@mysql.com's avatar
monty@mysql.com committed
839 840
  DBUG_ENTER("rollback_item_tree_changes");

841 842 843 844
  while ((change= it++))
    *change->place= change->old_value;
  /* We can forget about changes memory: it's allocated in runtime memroot */
  change_list.empty();
monty@mysql.com's avatar
monty@mysql.com committed
845
  DBUG_VOID_RETURN;
846 847 848
}


bk@work.mysql.com's avatar
bk@work.mysql.com committed
849 850 851 852 853 854 855 856 857
/*****************************************************************************
** Functions to provide a interface to select results
*****************************************************************************/

select_result::select_result()
{
  thd=current_thd;
}

858 859
void select_result::send_error(uint errcode,const char *err)
{
860
  my_message(errcode, err, MYF(0));
861 862
}

863 864 865 866 867 868

void select_result::cleanup()
{
  /* do nothing */
}

869 870 871
static String default_line_term("\n",default_charset_info);
static String default_escaped("\\",default_charset_info);
static String default_field_term("\t",default_charset_info);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
872 873 874 875 876

sql_exchange::sql_exchange(char *name,bool flag)
  :file_name(name), opt_enclosed(0), dumpfile(flag), skip_lines(0)
{
  field_term= &default_field_term;
hf@deer.(none)'s avatar
SCRUM  
hf@deer.(none) committed
877
  enclosed=   line_start= &my_empty_string;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
878 879 880 881
  line_term=  &default_line_term;
  escaped=    &default_escaped;
}

882
bool select_send::send_fields(List<Item> &list, uint flags)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
883
{
884 885 886 887
  bool res;
  if (!(res= thd->protocol->send_fields(&list, flags)))
    status= 1;
  return res;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
888 889
}

890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911
void select_send::abort()
{
  DBUG_ENTER("select_send::abort");
  if (status && thd->spcont &&
      thd->spcont->find_handler(thd->net.last_errno,
                                MYSQL_ERROR::WARN_LEVEL_ERROR))
  {
    /*
      Executing stored procedure without a handler.
      Here we should actually send an error to the client,
      but as an error will break a multiple result set, the only thing we
      can do for now is to nicely end the current data set and remembering
      the error so that the calling routine will abort
    */
    thd->net.report_error= 0;
    send_eof();
    thd->net.report_error= 1; // Abort SP
  }
  DBUG_VOID_RETURN;
}


bk@work.mysql.com's avatar
bk@work.mysql.com committed
912 913 914 915
/* Send data to client. Returns 0 if ok */

bool select_send::send_data(List<Item> &items)
{
916
  if (unit->offset_limit_cnt)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
917
  {						// using limit offset,count
918
    unit->offset_limit_cnt--;
919
    return 0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
920
  }
921

monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
922 923 924 925 926
  /*
    We may be passing the control from mysqld to the client: release the
    InnoDB adaptive hash S-latch to avoid thread deadlocks if it was reserved
    by thd
  */
927 928
    ha_release_temporary_latches(thd);

929 930 931
  List_iterator_fast<Item> li(items);
  Protocol *protocol= thd->protocol;
  char buff[MAX_FIELD_WIDTH];
932
  String buffer(buff, sizeof(buff), &my_charset_bin);
933 934 935
  DBUG_ENTER("send_data");

  protocol->prepare_for_resend();
bk@work.mysql.com's avatar
bk@work.mysql.com committed
936 937 938
  Item *item;
  while ((item=li++))
  {
939
    if (item->send(protocol, &buffer))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
940
    {
941
      protocol->free();				// Free used buffer
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
942
      my_message(ER_OUT_OF_RESOURCES, ER(ER_OUT_OF_RESOURCES), MYF(0));
943
      break;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
944 945
    }
  }
946
  thd->sent_row_count++;
947
  if (!thd->vio_ok())
948
    DBUG_RETURN(0);
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
949
  if (!thd->net.report_error)
950
    DBUG_RETURN(protocol->write());
951
  protocol->remove_last_row();
952
  DBUG_RETURN(1);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
953 954 955 956
}

bool select_send::send_eof()
{
957 958 959 960 961
  /* We may be passing the control from mysqld to the client: release the
     InnoDB adaptive hash S-latch to avoid thread deadlocks if it was reserved
     by thd */
    ha_release_temporary_latches(thd);

bk@work.mysql.com's avatar
bk@work.mysql.com committed
962 963 964
  /* Unlock tables before sending packet to gain some speed */
  if (thd->lock)
  {
965 966
    mysql_unlock_tables(thd, thd->lock);
    thd->lock=0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
967
  }
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
968 969
  if (!thd->net.report_error)
  {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
970
    ::send_eof(thd);
971
    status= 0;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
972 973 974 975
    return 0;
  }
  else
    return 1;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
976 977 978
}


979 980 981
/************************************************************************
  Handling writing to file
************************************************************************/
bk@work.mysql.com's avatar
bk@work.mysql.com committed
982

983 984
void select_to_file::send_error(uint errcode,const char *err)
{
985
  my_message(errcode, err, MYF(0));
986 987 988 989 990 991 992 993
  if (file > 0)
  {
    (void) end_io_cache(&cache);
    (void) my_close(file,MYF(0));
    (void) my_delete(path,MYF(0));		// Delete file on error
    file= -1;
  }
}
bk@work.mysql.com's avatar
bk@work.mysql.com committed
994 995


996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021
bool select_to_file::send_eof()
{
  int error= test(end_io_cache(&cache));
  if (my_close(file,MYF(MY_WME)))
    error= 1;
  if (!error)
    ::send_ok(thd,row_count);
  file= -1;
  return error;
}


void select_to_file::cleanup()
{
  /* In case of error send_eof() may be not called: close the file here. */
  if (file >= 0)
  {
    (void) end_io_cache(&cache);
    (void) my_close(file,MYF(0));
    file= -1;
  }
  path[0]= '\0';
  row_count= 0;
}


1022
select_to_file::~select_to_file()
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1023 1024 1025 1026 1027 1028 1029
{
  if (file >= 0)
  {					// This only happens in case of error
    (void) end_io_cache(&cache);
    (void) my_close(file,MYF(0));
    file= -1;
  }
1030 1031 1032 1033 1034 1035 1036 1037
}

/***************************************************************************
** Export of select to textfile
***************************************************************************/

select_export::~select_export()
{
1038
  thd->sent_row_count=row_count;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1039 1040
}

1041

1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059
/*
  Create file with IO cache

  SYNOPSIS
    create_file()
    thd			Thread handle
    path		File name
    exchange		Excange class
    cache		IO cache

  RETURN
    >= 0 	File handle
   -1		Error
*/


static File create_file(THD *thd, char *path, sql_exchange *exchange,
			IO_CACHE *cache)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1060
{
1061 1062
  File file;
  uint option= MY_UNPACK_FILENAME;
1063

bk@work.mysql.com's avatar
bk@work.mysql.com committed
1064
#ifdef DONT_ALLOW_FULL_LOAD_DATA_PATHS
1065
  option|= MY_REPLACE_DIR;			// Force use of db directory
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1066
#endif
1067

hf@deer.(none)'s avatar
hf@deer.(none) committed
1068
  if (!dirname_length(exchange->file_name))
1069 1070 1071 1072 1073 1074 1075
  {
    strxnmov(path, FN_REFLEN, mysql_real_data_home, thd->db ? thd->db : "", NullS);
    (void) fn_format(path, exchange->file_name, path, "", option);
  }
  else
    (void) fn_format(path, exchange->file_name, mysql_real_data_home, "", option);
    
1076
  if (!access(path, F_OK))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1077
  {
1078
    my_error(ER_FILE_EXISTS_ERROR, MYF(0), exchange->file_name);
1079
    return -1;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1080 1081
  }
  /* Create the file world readable */
serg@serg.mylan's avatar
serg@serg.mylan committed
1082
  if ((file= my_create(path, 0666, O_WRONLY|O_EXCL, MYF(MY_WME))) < 0)
1083
    return file;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1084
#ifdef HAVE_FCHMOD
1085
  (void) fchmod(file, 0666);			// Because of umask()
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1086
#else
1087
  (void) chmod(path, 0666);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1088
#endif
1089
  if (init_io_cache(cache, file, 0L, WRITE_CACHE, 0L, 1, MYF(MY_WME)))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1090
  {
1091
    my_close(file, MYF(0));
1092
    my_delete(path, MYF(0));  // Delete file on error, it was just created 
1093
    return -1;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1094
  }
1095
  return file;
1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106
}


int
select_export::prepare(List<Item> &list, SELECT_LEX_UNIT *u)
{
  bool blob_flag=0;
  unit= u;
  if ((uint) strlen(exchange->file_name) + NAME_LEN >= FN_REFLEN)
    strmake(path,exchange->file_name,FN_REFLEN-1);

1107
  if ((file= create_file(thd, path, exchange, &cache)) < 0)
1108
    return 1;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1109 1110
  /* Check if there is any blobs in data */
  {
monty@tik.mysql.fi's avatar
monty@tik.mysql.fi committed
1111
    List_iterator_fast<Item> li(list);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145
    Item *item;
    while ((item=li++))
    {
      if (item->max_length >= MAX_BLOB_WIDTH)
      {
	blob_flag=1;
	break;
      }
    }
  }
  field_term_length=exchange->field_term->length();
  if (!exchange->line_term->length())
    exchange->line_term=exchange->field_term;	// Use this if it exists
  field_sep_char= (exchange->enclosed->length() ? (*exchange->enclosed)[0] :
		   field_term_length ? (*exchange->field_term)[0] : INT_MAX);
  escape_char=	(exchange->escaped->length() ? (*exchange->escaped)[0] : -1);
  line_sep_char= (exchange->line_term->length() ?
		  (*exchange->line_term)[0] : INT_MAX);
  if (!field_term_length)
    exchange->opt_enclosed=0;
  if (!exchange->enclosed->length())
    exchange->opt_enclosed=1;			// A little quicker loop
  fixed_row_size= (!field_term_length && !exchange->enclosed->length() &&
		   !blob_flag);
  return 0;
}


bool select_export::send_data(List<Item> &items)
{

  DBUG_ENTER("send_data");
  char buff[MAX_FIELD_WIDTH],null_buff[2],space[MAX_FIELD_WIDTH];
  bool space_inited=0;
1146
  String tmp(buff,sizeof(buff),&my_charset_bin),*res;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1147 1148
  tmp.length(0);

1149
  if (unit->offset_limit_cnt)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1150
  {						// using limit offset,count
1151
    unit->offset_limit_cnt--;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1152 1153 1154 1155 1156 1157
    DBUG_RETURN(0);
  }
  row_count++;
  Item *item;
  char *buff_ptr=buff;
  uint used_length=0,items_left=items.elements;
monty@tik.mysql.fi's avatar
monty@tik.mysql.fi committed
1158
  List_iterator_fast<Item> li(items);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206

  if (my_b_write(&cache,(byte*) exchange->line_start->ptr(),
		 exchange->line_start->length()))
    goto err;
  while ((item=li++))
  {
    Item_result result_type=item->result_type();
    res=item->str_result(&tmp);
    if (res && (!exchange->opt_enclosed || result_type == STRING_RESULT))
    {
      if (my_b_write(&cache,(byte*) exchange->enclosed->ptr(),
		     exchange->enclosed->length()))
	goto err;
    }
    if (!res)
    {						// NULL
      if (!fixed_row_size)
      {
	if (escape_char != -1)			// Use \N syntax
	{
	  null_buff[0]=escape_char;
	  null_buff[1]='N';
	  if (my_b_write(&cache,(byte*) null_buff,2))
	    goto err;
	}
	else if (my_b_write(&cache,(byte*) "NULL",4))
	  goto err;
      }
      else
      {
	used_length=0;				// Fill with space
      }
    }
    else
    {
      if (fixed_row_size)
	used_length=min(res->length(),item->max_length);
      else
	used_length=res->length();
      if (result_type == STRING_RESULT && escape_char != -1)
      {
	char *pos,*start,*end;

	for (start=pos=(char*) res->ptr(),end=pos+used_length ;
	     pos != end ;
	     pos++)
	{
#ifdef USE_MB
1207 1208
          CHARSET_INFO *res_charset=res->charset();
	  if (use_mb(res_charset))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1209 1210
	  {
	    int l;
1211
	    if ((l=my_ismbchar(res_charset, pos, end)))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246
	    {
	      pos += l-1;
	      continue;
	    }
	  }
#endif
	  if ((int) *pos == escape_char || (int) *pos == field_sep_char ||
	      (int) *pos == line_sep_char || !*pos)
	  {
	    char tmp_buff[2];
	    tmp_buff[0]= escape_char;
	    tmp_buff[1]= *pos ? *pos : '0';
	    if (my_b_write(&cache,(byte*) start,(uint) (pos-start)) ||
		my_b_write(&cache,(byte*) tmp_buff,2))
	      goto err;
	    start=pos+1;
	  }
	}
	if (my_b_write(&cache,(byte*) start,(uint) (pos-start)))
	  goto err;
      }
      else if (my_b_write(&cache,(byte*) res->ptr(),used_length))
	goto err;
    }
    if (fixed_row_size)
    {						// Fill with space
      if (item->max_length > used_length)
      {
	/* QQ:  Fix by adding a my_b_fill() function */
	if (!space_inited)
	{
	  space_inited=1;
	  bfill(space,sizeof(space),' ');
	}
	uint length=item->max_length-used_length;
1247
	for (; length > sizeof(space) ; length-=sizeof(space))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284
	{
	  if (my_b_write(&cache,(byte*) space,sizeof(space)))
	    goto err;
	}
	if (my_b_write(&cache,(byte*) space,length))
	  goto err;
      }
    }
    buff_ptr=buff;				// Place separators here
    if (res && (!exchange->opt_enclosed || result_type == STRING_RESULT))
    {
      memcpy(buff_ptr,exchange->enclosed->ptr(),exchange->enclosed->length());
      buff_ptr+=exchange->enclosed->length();
    }
    if (--items_left)
    {
      memcpy(buff_ptr,exchange->field_term->ptr(),field_term_length);
      buff_ptr+=field_term_length;
    }
    if (my_b_write(&cache,(byte*) buff,(uint) (buff_ptr-buff)))
      goto err;
  }
  if (my_b_write(&cache,(byte*) exchange->line_term->ptr(),
		 exchange->line_term->length()))
    goto err;
  DBUG_RETURN(0);
err:
  DBUG_RETURN(1);
}


/***************************************************************************
** Dump  of select to a binary file
***************************************************************************/


int
1285 1286
select_dump::prepare(List<Item> &list __attribute__((unused)),
		     SELECT_LEX_UNIT *u)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1287
{
1288
  unit= u;
1289
  return (int) ((file= create_file(thd, path, exchange, &cache)) < 0);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1290 1291 1292 1293 1294
}


bool select_dump::send_data(List<Item> &items)
{
monty@tik.mysql.fi's avatar
monty@tik.mysql.fi committed
1295
  List_iterator_fast<Item> li(items);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1296
  char buff[MAX_FIELD_WIDTH];
1297
  String tmp(buff,sizeof(buff),&my_charset_bin),*res;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1298 1299 1300 1301
  tmp.length(0);
  Item *item;
  DBUG_ENTER("send_data");

1302
  if (unit->offset_limit_cnt)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1303
  {						// using limit offset,count
1304
    unit->offset_limit_cnt--;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1305 1306 1307 1308
    DBUG_RETURN(0);
  }
  if (row_count++ > 1) 
  {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
1309
    my_message(ER_TOO_MANY_ROWS, ER(ER_TOO_MANY_ROWS), MYF(0));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1310 1311 1312 1313 1314
    goto err;
  }
  while ((item=li++))
  {
    res=item->str_result(&tmp);
1315
    if (!res)					// If NULL
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1316
    {
1317 1318
      if (my_b_write(&cache,(byte*) "",1))
	goto err;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1319 1320 1321
    }
    else if (my_b_write(&cache,(byte*) res->ptr(),res->length()))
    {
1322
      my_error(ER_ERROR_ON_WRITE, MYF(0), path, my_errno);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
1323 1324 1325 1326 1327 1328 1329 1330 1331
      goto err;
    }
  }
  DBUG_RETURN(0);
err:
  DBUG_RETURN(1);
}


1332
select_subselect::select_subselect(Item_subselect *item_arg)
1333
{
1334
  item= item_arg;
1335 1336
}

1337

1338
bool select_singlerow_subselect::send_data(List<Item> &items)
1339
{
1340 1341
  DBUG_ENTER("select_singlerow_subselect::send_data");
  Item_singlerow_subselect *it= (Item_singlerow_subselect *)item;
Sinisa@sinisa.nasamreza.org's avatar
Sinisa@sinisa.nasamreza.org committed
1342 1343
  if (it->assigned())
  {
1344
    my_message(ER_SUBQUERY_NO_1_ROW, ER(ER_SUBQUERY_NO_1_ROW), MYF(0));
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
1345 1346 1347
    DBUG_RETURN(1);
  }
  if (unit->offset_limit_cnt)
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
1348
  {				          // Using limit offset,count
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
1349 1350
    unit->offset_limit_cnt--;
    DBUG_RETURN(0);
1351
  }
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
1352
  List_iterator_fast<Item> li(items);
1353 1354 1355
  Item *val_item;
  for (uint i= 0; (val_item= li++); i++)
    it->store(i, val_item);
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
1356
  it->assigned(1);
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
1357
  DBUG_RETURN(0);
1358
}
1359

1360

1361 1362 1363 1364 1365 1366 1367 1368
void select_max_min_finder_subselect::cleanup()
{
  DBUG_ENTER("select_max_min_finder_subselect::cleanup");
  cache= 0;
  DBUG_VOID_RETURN;
}


1369 1370 1371
bool select_max_min_finder_subselect::send_data(List<Item> &items)
{
  DBUG_ENTER("select_max_min_finder_subselect::send_data");
1372
  Item_maxmin_subselect *it= (Item_maxmin_subselect *)item;
1373 1374
  List_iterator_fast<Item> li(items);
  Item *val_item= li++;
1375
  it->register_value();
1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397
  if (it->assigned())
  {
    cache->store(val_item);
    if ((this->*op)())
      it->store(0, cache);
  }
  else
  {
    if (!cache)
    {
      cache= Item_cache::get_cache(val_item->result_type());
      switch (val_item->result_type())
      {
      case REAL_RESULT:
	op= &select_max_min_finder_subselect::cmp_real;
	break;
      case INT_RESULT:
	op= &select_max_min_finder_subselect::cmp_int;
	break;
      case STRING_RESULT:
	op= &select_max_min_finder_subselect::cmp_str;
	break;
1398 1399 1400
      case DECIMAL_RESULT:
        op= &select_max_min_finder_subselect::cmp_decimal;
        break;
1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416
      case ROW_RESULT:
        // This case should never be choosen
	DBUG_ASSERT(0);
	op= 0;
      }
    }
    cache->store(val_item);
    it->store(0, cache);
  }
  it->assigned(1);
  DBUG_RETURN(0);
}

bool select_max_min_finder_subselect::cmp_real()
{
  Item *maxmin= ((Item_singlerow_subselect *)item)->el(0);
1417
  double val1= cache->val_real(), val2= maxmin->val_real();
1418 1419 1420 1421
  if (fmax)
    return (cache->null_value && !maxmin->null_value) ||
      (!cache->null_value && !maxmin->null_value &&
       val1 > val2);
1422 1423 1424
  return (maxmin->null_value && !cache->null_value) ||
    (!cache->null_value && !maxmin->null_value &&
     val1 < val2);
1425 1426 1427 1428 1429 1430 1431 1432 1433 1434
}

bool select_max_min_finder_subselect::cmp_int()
{
  Item *maxmin= ((Item_singlerow_subselect *)item)->el(0);
  longlong val1= cache->val_int(), val2= maxmin->val_int();
  if (fmax)
    return (cache->null_value && !maxmin->null_value) ||
      (!cache->null_value && !maxmin->null_value &&
       val1 > val2);
1435 1436 1437
  return (maxmin->null_value && !cache->null_value) ||
    (!cache->null_value && !maxmin->null_value &&
     val1 < val2);
1438 1439
}

1440 1441 1442 1443 1444 1445 1446 1447 1448
bool select_max_min_finder_subselect::cmp_decimal()
{
  Item *maxmin= ((Item_singlerow_subselect *)item)->el(0);
  my_decimal cval, *cvalue= cache->val_decimal(&cval);
  my_decimal mval, *mvalue= maxmin->val_decimal(&mval);
  if (fmax)
    return (cache->null_value && !maxmin->null_value) ||
      (!cache->null_value && !maxmin->null_value &&
       my_decimal_cmp(cvalue, mvalue) > 0) ;
1449 1450 1451
  return (maxmin->null_value && !cache->null_value) ||
    (!cache->null_value && !maxmin->null_value &&
     my_decimal_cmp(cvalue,mvalue) < 0);
1452 1453
}

1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467
bool select_max_min_finder_subselect::cmp_str()
{
  String *val1, *val2, buf1, buf2;
  Item *maxmin= ((Item_singlerow_subselect *)item)->el(0);
  /*
    as far as both operand is Item_cache buf1 & buf2 will not be used,
    but added for safety
  */
  val1= cache->val_str(&buf1);
  val2= maxmin->val_str(&buf1);
  if (fmax)
    return (cache->null_value && !maxmin->null_value) ||
      (!cache->null_value && !maxmin->null_value &&
       sortcmp(val1, val2, cache->collation.collation) > 0) ;
1468 1469 1470
  return (maxmin->null_value && !cache->null_value) ||
    (!cache->null_value && !maxmin->null_value &&
     sortcmp(val1, val2, cache->collation.collation) < 0);
1471 1472
}

1473 1474 1475 1476 1477 1478 1479 1480 1481 1482
bool select_exists_subselect::send_data(List<Item> &items)
{
  DBUG_ENTER("select_exists_subselect::send_data");
  Item_exists_subselect *it= (Item_exists_subselect *)item;
  if (unit->offset_limit_cnt)
  {				          // Using limit offset,count
    unit->offset_limit_cnt--;
    DBUG_RETURN(0);
  }
  it->value= 1;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
1483
  it->assigned(1);
1484 1485 1486
  DBUG_RETURN(0);
}

Sinisa@sinisa.nasamreza.org's avatar
Sinisa@sinisa.nasamreza.org committed
1487 1488

/***************************************************************************
1489
  Dump of select to variables
Sinisa@sinisa.nasamreza.org's avatar
Sinisa@sinisa.nasamreza.org committed
1490
***************************************************************************/
1491

1492
int select_dumpvar::prepare(List<Item> &list, SELECT_LEX_UNIT *u)
Sinisa@sinisa.nasamreza.org's avatar
Sinisa@sinisa.nasamreza.org committed
1493
{
1494
  List_iterator_fast<Item> li(list);
1495
  List_iterator_fast<my_var> gl(var_list);
Sinisa@sinisa.nasamreza.org's avatar
Sinisa@sinisa.nasamreza.org committed
1496
  Item *item;
1497

1498 1499
  local_vars.empty();				// Clear list if SP
  unit= u;
1500
  row_count= 0;
1501

1502
  if (var_list.elements != list.elements)
Sinisa@sinisa.nasamreza.org's avatar
Sinisa@sinisa.nasamreza.org committed
1503
  {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
1504 1505
    my_message(ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT,
               ER(ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT), MYF(0));
1506
    return 1;
Sinisa@sinisa.nasamreza.org's avatar
Sinisa@sinisa.nasamreza.org committed
1507
  }
1508
  while ((item=li++))
Sinisa@sinisa.nasamreza.org's avatar
Sinisa@sinisa.nasamreza.org committed
1509
  {
1510
    my_var *mv= gl++;
1511
    if (mv->local)
1512
    {
1513 1514
      Item_splocal *var= new Item_splocal(mv->s, mv->offset, mv->type);
      (void)local_vars.push_back(var);
1515
#ifndef DBUG_OFF
1516
      var->m_sp= mv->sp;
1517 1518
#endif
    }
1519 1520
    else
    {
1521
      Item_func_set_user_var *var= new Item_func_set_user_var(mv->s, item);
pem@mysql.com's avatar
pem@mysql.com committed
1522 1523 1524
      /*
        Item_func_set_user_var can't substitute something else on its place =>
        0 can be passed as last argument (reference on item)
1525
        Item_func_set_user_var can't be fixed after creation, so we do not
1526
        check var->fixed
pem@mysql.com's avatar
pem@mysql.com committed
1527
      */
1528 1529 1530
      var->fix_fields(thd, 0);
      var->fix_length_and_dec();
      vars.push_back(var);
1531
    }
Sinisa@sinisa.nasamreza.org's avatar
Sinisa@sinisa.nasamreza.org committed
1532
  }
1533 1534
  return 0;
}
1535

1536

1537 1538 1539 1540 1541 1542 1543
void select_dumpvar::cleanup()
{
  vars.empty();
  row_count=0;
}


serg@serg.mylan's avatar
serg@serg.mylan committed
1544
Query_arena::Type Query_arena::type() const
1545
{
monty@mysql.com's avatar
monty@mysql.com committed
1546
  DBUG_ASSERT(0); /* Should never be called */
1547
  return STATEMENT;
1548 1549 1550
}


1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565
void Query_arena::free_items()
{
  Item *next;
  DBUG_ENTER("Query_arena::free_items");
  /* This works because items are allocated with sql_alloc() */
  for (; free_list; free_list= next)
  {
    next= free_list->next;
    free_list->delete_self();
  }
  /* Postcondition: free_list is 0 */
  DBUG_VOID_RETURN;
}


1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578
void Query_arena::set_query_arena(Query_arena *set)
{
  mem_root=  set->mem_root;
  free_list= set->free_list;
  state= set->state;
}


void Query_arena::cleanup_stmt()
{
  DBUG_ASSERT("Query_arena::cleanup_stmt()" == "not implemented");
}

1579 1580 1581 1582
/*
  Statement functions 
*/

1583 1584 1585 1586
Statement::Statement(enum enum_state state_arg, ulong id_arg,
                     ulong alloc_block_size, ulong prealloc_size)
  :Query_arena(&main_mem_root, state_arg),
  id(id_arg),
1587 1588 1589
  set_query_id(1),
  lex(&main_lex),
  query(0),
1590 1591
  query_length(0),
  cursor(0)
1592
{
1593
  name.str= NULL;
1594
  init_sql_alloc(&main_mem_root, alloc_block_size, prealloc_size);
1595 1596 1597
}


serg@serg.mylan's avatar
serg@serg.mylan committed
1598
Query_arena::Type Statement::type() const
1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610
{
  return STATEMENT;
}


void Statement::set_statement(Statement *stmt)
{
  id=             stmt->id;
  set_query_id=   stmt->set_query_id;
  lex=            stmt->lex;
  query=          stmt->query;
  query_length=   stmt->query_length;
1611
  cursor=         stmt->cursor;
1612 1613 1614
}


1615 1616 1617
void
Statement::set_n_backup_statement(Statement *stmt, Statement *backup)
{
1618
  DBUG_ENTER("Statement::set_n_backup_statement");
1619 1620
  backup->set_statement(this);
  set_statement(stmt);
1621
  DBUG_VOID_RETURN;
1622 1623 1624 1625 1626
}


void Statement::restore_backup_statement(Statement *stmt, Statement *backup)
{
1627
  DBUG_ENTER("Statement::restore_backup_statement");
1628 1629
  stmt->set_statement(this);
  set_statement(backup);
1630
  DBUG_VOID_RETURN;
1631 1632 1633
}


1634
void THD::end_statement()
1635 1636 1637 1638 1639
{
  /* Cleanup SQL processing state to resuse this statement in next query. */
  lex_end(lex);
  delete lex->result;
  lex->result= 0;
1640 1641
  /* Note that free_list is freed in cleanup_after_query() */

1642 1643 1644 1645 1646 1647 1648
  /*
    Don't free mem_root, as mem_root is freed in the end of dispatch_command
    (once for any command).
  */
}


konstantin@mysql.com's avatar
konstantin@mysql.com committed
1649
void THD::set_n_backup_active_arena(Query_arena *set, Query_arena *backup)
1650
{
konstantin@mysql.com's avatar
konstantin@mysql.com committed
1651
  DBUG_ENTER("THD::set_n_backup_active_arena");
1652
  DBUG_ASSERT(backup->is_backup_arena == FALSE);
1653

konstantin@mysql.com's avatar
konstantin@mysql.com committed
1654 1655
  backup->set_query_arena(this);
  set_query_arena(set);
monty@mysql.com's avatar
monty@mysql.com committed
1656
#ifndef DBUG_OFF
1657
  backup->is_backup_arena= TRUE;
monty@mysql.com's avatar
monty@mysql.com committed
1658
#endif
1659
  DBUG_VOID_RETURN;
1660 1661 1662
}


konstantin@mysql.com's avatar
konstantin@mysql.com committed
1663
void THD::restore_active_arena(Query_arena *set, Query_arena *backup)
1664
{
konstantin@mysql.com's avatar
konstantin@mysql.com committed
1665
  DBUG_ENTER("THD::restore_active_arena");
1666
  DBUG_ASSERT(backup->is_backup_arena);
konstantin@mysql.com's avatar
konstantin@mysql.com committed
1667 1668
  set->set_query_arena(this);
  set_query_arena(backup);
monty@mysql.com's avatar
monty@mysql.com committed
1669
#ifndef DBUG_OFF
1670
  backup->is_backup_arena= FALSE;
1671
#endif
monty@mysql.com's avatar
monty@mysql.com committed
1672
  DBUG_VOID_RETURN;
1673 1674
}

1675 1676
Statement::~Statement()
{
1677 1678 1679 1680 1681
  /*
    We must free `main_mem_root', not `mem_root' (pointer), to work
    correctly if this statement is used as a backup statement,
    for which `mem_root' may point to some other statement.
  */
1682
  free_root(&main_mem_root, MYF(0));
1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700
}

C_MODE_START

static byte *
get_statement_id_as_hash_key(const byte *record, uint *key_length,
                             my_bool not_used __attribute__((unused)))
{
  const Statement *statement= (const Statement *) record; 
  *key_length= sizeof(statement->id);
  return (byte *) &((const Statement *) statement)->id;
}

static void delete_statement_as_hash_key(void *key)
{
  delete (Statement *) key;
}

1701 1702
static byte *get_stmt_name_hash_key(Statement *entry, uint *length,
                                    my_bool not_used __attribute__((unused)))
1703 1704 1705 1706 1707
{
  *length=(uint) entry->name.length;
  return (byte*) entry->name.str;
}

1708 1709 1710 1711 1712
C_MODE_END

Statement_map::Statement_map() :
  last_found_statement(0)
{
1713 1714 1715 1716 1717
  enum
  {
    START_STMT_HASH_SIZE = 16,
    START_NAME_HASH_SIZE = 16
  };
1718
  hash_init(&st_hash, &my_charset_bin, START_STMT_HASH_SIZE, 0, 0,
1719 1720
            get_statement_id_as_hash_key,
            delete_statement_as_hash_key, MYF(0));
1721
  hash_init(&names_hash, system_charset_info, START_NAME_HASH_SIZE, 0, 0,
1722 1723
            (hash_get_key) get_stmt_name_hash_key,
            NULL,MYF(0));
1724 1725
}

1726

1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748
/*
  Insert a new statement to the thread-local statement map.

  DESCRIPTION
    If there was an old statement with the same name, replace it with the
    new one. Otherwise, check if max_prepared_stmt_count is not reached yet,
    increase prepared_stmt_count, and insert the new statement. It's okay
    to delete an old statement and fail to insert the new one.

  POSTCONDITIONS
    All named prepared statements are also present in names_hash.
    Statement names in names_hash are unique.
    The statement is added only if prepared_stmt_count < max_prepard_stmt_count
    last_found_statement always points to a valid statement or is 0

  RETURN VALUE
    0  success
    1  error: out of resources or max_prepared_stmt_count limit has been
       reached. An error is sent to the client, the statement is deleted.
*/

int Statement_map::insert(THD *thd, Statement *statement)
1749
{
1750 1751 1752 1753 1754 1755 1756 1757 1758 1759
  if (my_hash_insert(&st_hash, (byte*) statement))
  {
    /*
      Delete is needed only in case of an insert failure. In all other
      cases hash_delete will also delete the statement.
    */
    delete statement;
    my_error(ER_OUT_OF_RESOURCES, MYF(0));
    goto err_st_hash;
  }
1760
  if (statement->name.str && my_hash_insert(&names_hash, (byte*) statement))
1761
  {
1762 1763
    my_error(ER_OUT_OF_RESOURCES, MYF(0));
    goto err_names_hash;
1764
  }
1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775
  pthread_mutex_lock(&LOCK_prepared_stmt_count);
  /*
    We don't check that prepared_stmt_count is <= max_prepared_stmt_count
    because we would like to allow to lower the total limit
    of prepared statements below the current count. In that case
    no new statements can be added until prepared_stmt_count drops below
    the limit.
  */
  if (prepared_stmt_count >= max_prepared_stmt_count)
  {
    pthread_mutex_unlock(&LOCK_prepared_stmt_count);
1776 1777
    my_error(ER_MAX_PREPARED_STMT_COUNT_REACHED, MYF(0),
             max_prepared_stmt_count);
1778 1779 1780 1781 1782
    goto err_max;
  }
  prepared_stmt_count++;
  pthread_mutex_unlock(&LOCK_prepared_stmt_count);

1783
  last_found_statement= statement;
1784 1785 1786 1787 1788 1789 1790 1791 1792
  return 0;

err_max:
  if (statement->name.str)
    hash_delete(&names_hash, (byte*) statement);
err_names_hash:
  hash_delete(&st_hash, (byte*) statement);
err_st_hash:
  return 1;
1793 1794
}

1795

1796 1797
void Statement_map::close_transient_cursors()
{
1798
#ifdef TO_BE_IMPLEMENTED
1799 1800 1801
  Statement *stmt;
  while ((stmt= transient_cursor_list.head()))
    stmt->close_cursor();                 /* deletes itself from the list */
1802
#endif
1803 1804 1805
}


1806 1807 1808 1809 1810 1811
void Statement_map::erase(Statement *statement)
{
  if (statement == last_found_statement)
    last_found_statement= 0;
  if (statement->name.str)
    hash_delete(&names_hash, (byte *) statement);
1812

1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831
  hash_delete(&st_hash, (byte *) statement);
  pthread_mutex_lock(&LOCK_prepared_stmt_count);
  DBUG_ASSERT(prepared_stmt_count > 0);
  prepared_stmt_count--;
  pthread_mutex_unlock(&LOCK_prepared_stmt_count);
}


void Statement_map::reset()
{
  /* Must be first, hash_free will reset st_hash.records */
  pthread_mutex_lock(&LOCK_prepared_stmt_count);
  DBUG_ASSERT(prepared_stmt_count >= st_hash.records);
  prepared_stmt_count-= st_hash.records;
  pthread_mutex_unlock(&LOCK_prepared_stmt_count);

  my_hash_reset(&names_hash);
  my_hash_reset(&st_hash);
  last_found_statement= 0;
1832 1833
}

1834

1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846
Statement_map::~Statement_map()
{
  /* Must go first, hash_free will reset st_hash.records */
  pthread_mutex_lock(&LOCK_prepared_stmt_count);
  DBUG_ASSERT(prepared_stmt_count >= st_hash.records);
  prepared_stmt_count-= st_hash.records;
  pthread_mutex_unlock(&LOCK_prepared_stmt_count);

  hash_free(&names_hash);
  hash_free(&st_hash);
}

1847 1848 1849
bool select_dumpvar::send_data(List<Item> &items)
{
  List_iterator_fast<Item_func_set_user_var> li(vars);
1850 1851
  List_iterator_fast<Item_splocal> var_li(local_vars);
  List_iterator_fast<my_var> my_li(var_list);
1852
  List_iterator<Item> it(items);
1853
  Item_func_set_user_var *xx;
1854 1855
  Item_splocal *yy;
  my_var *zz;
1856
  DBUG_ENTER("send_data");
1857 1858 1859 1860 1861
  if (unit->offset_limit_cnt)
  {						// using limit offset,count
    unit->offset_limit_cnt--;
    DBUG_RETURN(0);
  }
1862

1863 1864 1865 1866 1867
  if (unit->offset_limit_cnt)
  {				          // Using limit offset,count
    unit->offset_limit_cnt--;
    DBUG_RETURN(0);
  }
1868 1869
  if (row_count++) 
  {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
1870
    my_message(ER_TOO_MANY_ROWS, ER(ER_TOO_MANY_ROWS), MYF(0));
1871 1872
    DBUG_RETURN(1);
  }
1873
  while ((zz=my_li++) && (it++))
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
1874
  {
1875 1876 1877 1878
    if (zz->local)
    {
      if ((yy=var_li++)) 
      {
1879
	if (thd->spcont->set_variable(current_thd, yy->get_var_idx(),
1880
                                      it.ref()))
1881
	  DBUG_RETURN(1);
1882 1883 1884 1885 1886
      }
    }
    else
    {
      if ((xx=li++))
pem@mysql.com's avatar
pem@mysql.com committed
1887 1888
      {
        xx->check();
1889
	xx->update();
pem@mysql.com's avatar
pem@mysql.com committed
1890
      }
1891
    }
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
1892
  }
Sinisa@sinisa.nasamreza.org's avatar
Sinisa@sinisa.nasamreza.org committed
1893 1894 1895 1896 1897
  DBUG_RETURN(0);
}

bool select_dumpvar::send_eof()
{
1898
  if (! row_count)
serg@serg.mylan's avatar
serg@serg.mylan committed
1899 1900
    push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
                 ER_SP_FETCH_NO_DATA, ER(ER_SP_FETCH_NO_DATA));
1901 1902
  ::send_ok(thd,row_count);
  return 0;
Sinisa@sinisa.nasamreza.org's avatar
Sinisa@sinisa.nasamreza.org committed
1903
}
1904 1905 1906 1907 1908 1909 1910

/****************************************************************************
  TMP_TABLE_PARAM
****************************************************************************/

void TMP_TABLE_PARAM::init()
{
1911 1912
  DBUG_ENTER("TMP_TABLE_PARAM::init");
  DBUG_PRINT("enter", ("this: 0x%lx", (ulong)this));
1913 1914 1915
  field_count= sum_func_count= func_count= hidden_field_count= 0;
  group_parts= group_length= group_null_parts= 0;
  quick_group= 1;
1916
  table_charset= 0;
1917
  precomputed_group_by= 0;
1918
  DBUG_VOID_RETURN;
1919
}
1920 1921 1922 1923


void thd_increment_bytes_sent(ulong length)
{
serg@serg.mylan's avatar
serg@serg.mylan committed
1924
  THD *thd=current_thd;
lars@mysql.com's avatar
lars@mysql.com committed
1925
  if (likely(thd != 0))
serg@serg.mylan's avatar
serg@serg.mylan committed
1926 1927 1928
  { /* current_thd==0 when close_connection() calls net_send_error() */
    thd->status_var.bytes_sent+= length;
  }
1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947
}


void thd_increment_bytes_received(ulong length)
{
  current_thd->status_var.bytes_received+= length;
}


void thd_increment_net_big_packet_count(ulong length)
{
  current_thd->status_var.net_big_packet_count+= length;
}


void THD::set_status_var_init()
{
  bzero((char*) &status_var, sizeof(status_var));
}
1948

1949

1950
void Security_context::init()
1951 1952 1953 1954 1955 1956 1957 1958 1959
{
  host= user= priv_user= ip= 0;
  host_or_ip= "connecting host";
#ifndef NO_EMBEDDED_ACCESS_CHECKS
  db_access= NO_ACCESS;
#endif
}


1960
void Security_context::destroy()
1961 1962 1963 1964 1965 1966 1967 1968 1969 1970
{
  // If not pointer to constant
  if (host != my_localhost)
    safeFree(host);
  if (user != delayed_user)
    safeFree(user);
  safeFree(ip);
}


1971
void Security_context::skip_grants()
1972 1973 1974 1975 1976 1977 1978 1979 1980
{
  /* privileges for the user are unknown everything is allowed */
  host_or_ip= (char *)"";
  master_access= ~NO_ACCESS;
  priv_user= (char *)"";
  *priv_host= '\0';
}


1981 1982 1983 1984 1985 1986 1987 1988
/****************************************************************************
  Handling of open and locked tables states.

  This is used when we want to open/lock (and then close) some tables when
  we already have a set of tables open and locked. We use these methods for
  access to mysql.proc table to find definitions of stored routines.
****************************************************************************/

1989
void THD::reset_n_backup_open_tables_state(Open_tables_state *backup)
1990
{
1991 1992
  DBUG_ENTER("reset_n_backup_open_tables_state");
  backup->set_open_tables_state(this);
1993
  reset_open_tables_state();
1994
  DBUG_VOID_RETURN;
1995 1996 1997
}


1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009
void THD::restore_backup_open_tables_state(Open_tables_state *backup)
{
  DBUG_ENTER("restore_backup_open_tables_state");
  /*
    Before we will throw away current open tables state we want
    to be sure that it was properly cleaned up.
  */
  DBUG_ASSERT(open_tables == 0 && temporary_tables == 0 &&
              handler_tables == 0 && derived_tables == 0 &&
              lock == 0 && locked_tables == 0 &&
              prelocked_mode == NON_PRELOCKED);
  set_open_tables_state(backup);
2010 2011
  DBUG_VOID_RETURN;
}
2012 2013


2014

2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027
/****************************************************************************
  Handling of statement states in functions and triggers.

  This is used to ensure that the function/trigger gets a clean state
  to work with and does not cause any side effects of the calling statement.

  It also allows most stored functions and triggers to replicate even
  if they are used items that would normally be stored in the binary
  replication (like last_insert_id() etc...)

  The following things is done
  - Disable binary logging for the duration of the statement
  - Disable multi-result-sets for the duration of the statement
2028
  - Value of last_insert_id() is saved and restored
2029 2030 2031 2032
  - Value set by 'SET INSERT_ID=#' is reset and restored
  - Value for found_rows() is reset and restored
  - examined_row_count is added to the total
  - cuted_fields is added to the total
2033
  - new savepoint level is created and destroyed
2034 2035 2036 2037 2038 2039

  NOTES:
    Seed for random() is saved for the first! usage of RAND()
    We reset examined_row_count and cuted_fields and add these to the
    result to ensure that if we have a bug that would reset these within
    a function, we are not loosing any rows from the main statement.
2040 2041

    We do not reset value of last_insert_id().
2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052
****************************************************************************/

void THD::reset_sub_statement_state(Sub_statement_state *backup,
                                    uint new_state)
{
  backup->options=         options;
  backup->in_sub_stmt=     in_sub_stmt;
  backup->no_send_ok=      net.no_send_ok;
  backup->enable_slow_log= enable_slow_log;
  backup->last_insert_id=  last_insert_id;
  backup->next_insert_id=  next_insert_id;
ramil@mysql.com's avatar
ramil@mysql.com committed
2053
  backup->current_insert_id=  current_insert_id;
2054
  backup->insert_id_used=  insert_id_used;
ramil@mysql.com's avatar
ramil@mysql.com committed
2055
  backup->last_insert_id_used=  last_insert_id_used;
2056
  backup->clear_next_insert_id= clear_next_insert_id;
2057 2058 2059 2060 2061
  backup->limit_found_rows= limit_found_rows;
  backup->examined_row_count= examined_row_count;
  backup->sent_row_count=   sent_row_count;
  backup->cuted_fields=     cuted_fields;
  backup->client_capabilities= client_capabilities;
2062
  backup->savepoints= transaction.savepoints;
2063

2064 2065
  if (!lex->requires_prelocking() || is_update_query(lex->sql_command))
    options&= ~OPTION_BIN_LOG;
2066 2067 2068 2069 2070 2071 2072 2073
  /* Disable result sets */
  client_capabilities &= ~CLIENT_MULTI_RESULTS;
  in_sub_stmt|= new_state;
  next_insert_id= 0;
  insert_id_used= 0;
  examined_row_count= 0;
  sent_row_count= 0;
  cuted_fields= 0;
2074
  transaction.savepoints= 0;
2075 2076 2077 2078 2079 2080 2081 2082

  /* Surpress OK packets in case if we will execute statements */
  net.no_send_ok= TRUE;
}


void THD::restore_sub_statement_state(Sub_statement_state *backup)
{
2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097
  /*
    To save resources we want to release savepoints which were created
    during execution of function or trigger before leaving their savepoint
    level. It is enough to release first savepoint set on this level since
    all later savepoints will be released automatically.
  */
  if (transaction.savepoints)
  {
    SAVEPOINT *sv;
    for (sv= transaction.savepoints; sv->prev; sv= sv->prev)
    {}
    /* ha_release_savepoint() never returns error. */
    (void)ha_release_savepoint(this, sv);
  }
  transaction.savepoints= backup->savepoints;
2098 2099 2100 2101 2102 2103
  options=          backup->options;
  in_sub_stmt=      backup->in_sub_stmt;
  net.no_send_ok=   backup->no_send_ok;
  enable_slow_log=  backup->enable_slow_log;
  last_insert_id=   backup->last_insert_id;
  next_insert_id=   backup->next_insert_id;
ramil@mysql.com's avatar
ramil@mysql.com committed
2104
  current_insert_id= backup->current_insert_id;
2105
  insert_id_used=   backup->insert_id_used;
ramil@mysql.com's avatar
ramil@mysql.com committed
2106
  last_insert_id_used= backup->last_insert_id_used;
2107
  clear_next_insert_id= backup->clear_next_insert_id;
2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118
  limit_found_rows= backup->limit_found_rows;
  sent_row_count=   backup->sent_row_count;
  client_capabilities= backup->client_capabilities;

  /*
    The following is added to the old values as we are interested in the
    total complexity of the query
  */
  examined_row_count+= backup->examined_row_count;
  cuted_fields+=       backup->cuted_fields;
}
2119 2120 2121 2122 2123 2124


/***************************************************************************
  Handling of XA id cacheing
***************************************************************************/

2125 2126 2127 2128 2129 2130
pthread_mutex_t LOCK_xid_cache;
HASH xid_cache;

static byte *xid_get_hash_key(const byte *ptr,uint *length,
                                  my_bool not_used __attribute__((unused)))
{
2131 2132
  *length=((XID_STATE*)ptr)->xid.key_length();
  return ((XID_STATE*)ptr)->xid.key();
2133 2134 2135 2136 2137
}

static void xid_free_hash (void *ptr)
{
  if (!((XID_STATE*)ptr)->in_thd)
kent@mysql.com's avatar
kent@mysql.com committed
2138
    my_free((gptr)ptr, MYF(0));
2139 2140 2141 2142 2143
}

bool xid_cache_init()
{
  pthread_mutex_init(&LOCK_xid_cache, MY_MUTEX_INIT_FAST);
serg@sergbook.mysql.com's avatar
serg@sergbook.mysql.com committed
2144 2145
  return hash_init(&xid_cache, &my_charset_bin, 100, 0, 0,
                   xid_get_hash_key, xid_free_hash, 0) != 0;
2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159
}

void xid_cache_free()
{
  if (hash_inited(&xid_cache))
  {
    hash_free(&xid_cache);
    pthread_mutex_destroy(&LOCK_xid_cache);
  }
}

XID_STATE *xid_cache_search(XID *xid)
{
  pthread_mutex_lock(&LOCK_xid_cache);
2160
  XID_STATE *res=(XID_STATE *)hash_search(&xid_cache, xid->key(), xid->key_length());
2161 2162 2163 2164
  pthread_mutex_unlock(&LOCK_xid_cache);
  return res;
}

2165

2166 2167 2168 2169 2170
bool xid_cache_insert(XID *xid, enum xa_states xa_state)
{
  XID_STATE *xs;
  my_bool res;
  pthread_mutex_lock(&LOCK_xid_cache);
2171
  if (hash_search(&xid_cache, xid->key(), xid->key_length()))
2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185
    res=0;
  else if (!(xs=(XID_STATE *)my_malloc(sizeof(*xs), MYF(MY_WME))))
    res=1;
  else
  {
    xs->xa_state=xa_state;
    xs->xid.set(xid);
    xs->in_thd=0;
    res=my_hash_insert(&xid_cache, (byte*)xs);
  }
  pthread_mutex_unlock(&LOCK_xid_cache);
  return res;
}

2186

2187 2188 2189
bool xid_cache_insert(XID_STATE *xid_state)
{
  pthread_mutex_lock(&LOCK_xid_cache);
2190 2191
  DBUG_ASSERT(hash_search(&xid_cache, xid_state->xid.key(),
                          xid_state->xid.key_length())==0);
2192 2193 2194 2195 2196
  my_bool res=my_hash_insert(&xid_cache, (byte*)xid_state);
  pthread_mutex_unlock(&LOCK_xid_cache);
  return res;
}

2197

2198 2199 2200 2201 2202 2203 2204
void xid_cache_delete(XID_STATE *xid_state)
{
  pthread_mutex_lock(&LOCK_xid_cache);
  hash_delete(&xid_cache, (byte *)xid_state);
  pthread_mutex_unlock(&LOCK_xid_cache);
}