sql_delete.cc 19.1 KB
Newer Older
1
/* Copyright (C) 2000 MySQL AB
unknown's avatar
unknown committed
2

unknown's avatar
unknown 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.
unknown's avatar
unknown committed
7

unknown's avatar
unknown 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.
unknown's avatar
unknown committed
12

unknown's avatar
unknown committed
13 14 15 16
   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 */

17

unknown's avatar
unknown committed
18
/*
19
  Delete of records and truncate of tables.
20

21
  Multi-table deletes were introduced by Monty and Sinisa
unknown's avatar
unknown committed
22 23
*/

24 25


26
#include "mysql_priv.h"
27
#include "ha_innodb.h"
28
#include "sql_select.h"
unknown's avatar
unknown committed
29

30
int mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, SQL_LIST *order,
unknown's avatar
unknown committed
31
                 ha_rows limit, ulong options)
unknown's avatar
unknown committed
32 33 34
{
  int		error;
  TABLE		*table;
35
  SQL_SELECT	*select=0;
unknown's avatar
unknown committed
36 37
  READ_RECORD	info;
  bool 		using_limit=limit != HA_POS_ERROR;
unknown's avatar
unknown committed
38
  bool		transactional_table, log_delayed, safe_update, const_cond; 
39
  ha_rows	deleted;
unknown's avatar
unknown committed
40 41
  DBUG_ENTER("mysql_delete");

unknown's avatar
unknown committed
42
  if ((open_and_lock_tables(thd, table_list)))
unknown's avatar
unknown committed
43
    DBUG_RETURN(-1);
unknown's avatar
unknown committed
44
  table= table_list->table;
45
  table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK);
unknown's avatar
unknown committed
46 47
  thd->proc_info="init";
  table->map=1;
unknown's avatar
unknown committed
48 49 50

  if ((error= mysql_prepare_delete(thd, table_list, &conds)))
    DBUG_RETURN(error);
unknown's avatar
unknown committed
51

52 53 54 55 56 57 58 59
  const_cond= (!conds || conds->const_item());
  safe_update=test(thd->options & OPTION_SAFE_UPDATES);
  if (safe_update && const_cond)
  {
    send_error(thd,ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE);
    DBUG_RETURN(1);
  }

60
  thd->lex->select_lex.no_error= thd->lex->ignore;
61

62
  /* Test if the user wants to delete all rows */
unknown's avatar
unknown committed
63
  if (!using_limit && const_cond && (!conds || conds->val_int()) &&
64
      !(specialflag & (SPECIAL_NO_NEW_FUNC | SPECIAL_SAFE_MODE)))
65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
  {
    deleted= table->file->records;
    if (!(error=table->file->delete_all_rows()))
    {
      error= -1;				// ok
      goto cleanup;
    }
    if (error != HA_ERR_WRONG_COMMAND)
    {
      table->file->print_error(error,MYF(0));
      error=0;
      goto cleanup;
    }
    /* Handler didn't support fast delete; Delete rows one by one */
  }

unknown's avatar
unknown committed
81 82
  table->used_keys.clear_all();
  table->quick_keys.clear_all();		// Can't use 'only index'
unknown's avatar
unknown committed
83 84 85
  select=make_select(table,0,0,conds,&error);
  if (error)
    DBUG_RETURN(-1);
86
  if ((select && select->check_quick(thd, safe_update, limit)) || !limit)
unknown's avatar
unknown committed
87 88
  {
    delete select;
89
    free_underlaid_joins(thd, &thd->lex->select_lex);
90
    send_ok(thd,0L);
unknown's avatar
unknown committed
91
    DBUG_RETURN(0);				// Nothing to delete
unknown's avatar
unknown committed
92 93 94
  }

  /* If running in safe sql mode, don't allow updates without keys */
unknown's avatar
unknown committed
95
  if (table->quick_keys.is_clear_all())
unknown's avatar
unknown committed
96
  {
97
    thd->server_status|=SERVER_QUERY_NO_INDEX_USED;
unknown's avatar
unknown committed
98
    if (safe_update && !using_limit)
99 100
    {
      delete select;
101
      free_underlaid_joins(thd, &thd->lex->select_lex);
102
      send_error(thd,ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE);
103 104
      DBUG_RETURN(1);
    }
unknown's avatar
unknown committed
105
  }
106
  if (options & OPTION_QUICK)
107 108
    (void) table->file->extra(HA_EXTRA_QUICK);

109
  if (order && order->elements)
110 111 112 113 114 115
  {
    uint         length;
    SORT_FIELD  *sortorder;
    TABLE_LIST   tables;
    List<Item>   fields;
    List<Item>   all_fields;
116
    ha_rows examined_rows;
117 118 119

    bzero((char*) &tables,sizeof(tables));
    tables.table = table;
unknown's avatar
unknown committed
120
    tables.alias = table_list->alias;
121

unknown's avatar
unknown committed
122
    table->sort.io_cache = (IO_CACHE *) my_malloc(sizeof(IO_CACHE),
123
                                             MYF(MY_FAE | MY_ZEROFILL));
unknown's avatar
unknown committed
124
      if (thd->lex->select_lex.setup_ref_array(thd, order->elements) ||
125
	  setup_order(thd, thd->lex->select_lex.ref_pointer_array, &tables,
126 127
		      fields, all_fields, (ORDER*) order->first) ||
	  !(sortorder=make_unireg_sortorder((ORDER*) order->first, &length)) ||
unknown's avatar
unknown committed
128
	  (table->sort.found_records = filesort(thd, table, sortorder, length,
unknown's avatar
unknown committed
129
					   select, HA_POS_ERROR,
130 131
					   &examined_rows))
	  == HA_POS_ERROR)
132 133
    {
      delete select;
134
      free_underlaid_joins(thd, &thd->lex->select_lex);
135
      DBUG_RETURN(-1);			// This will force out message
136
    }
137 138 139 140 141 142
    /*
      Filesort has already found and selected the rows we want to delete,
      so we don't need the where clause
    */
    delete select;
    select= 0;
143 144 145
  }

  init_read_record(&info,thd,table,select,1,1);
146
  deleted=0L;
147
  init_ftfuncs(thd, &thd->lex->select_lex, 1);
unknown's avatar
unknown committed
148
  thd->proc_info="updating";
unknown's avatar
unknown committed
149 150
  while (!(error=info.read_record(&info)) && !thd->killed &&
	 !thd->net.report_error)
unknown's avatar
unknown committed
151
  {
unknown's avatar
unknown committed
152
    // thd->net.report_error is tested to disallow delete row on error
153
    if (!(select && select->skip_record())&& !thd->net.report_error )
unknown's avatar
unknown committed
154 155 156 157 158 159 160 161 162 163 164 165 166
    {
      if (!(error=table->file->delete_row(table->record[0])))
      {
	deleted++;
	if (!--limit && using_limit)
	{
	  error= -1;
	  break;
	}
      }
      else
      {
	table->file->print_error(error,MYF(0));
unknown's avatar
unknown committed
167 168 169 170 171 172 173 174 175
	/*
	  In < 4.0.14 we set the error number to 0 here, but that
	  was not sensible, because then MySQL would not roll back the
	  failed DELETE, and also wrote it to the binlog. For MyISAM
	  tables a DELETE probably never should fail (?), but for
	  InnoDB it can fail in a FOREIGN KEY error or an
	  out-of-tablespace error.
	*/
 	error= 1;
unknown's avatar
unknown committed
176 177 178
	break;
      }
    }
unknown's avatar
unknown committed
179 180
    else
      table->file->unlock_row();  // Row failed selection, release lock on it
unknown's avatar
unknown committed
181
  }
182 183
  if (thd->killed && !error)
    error= 1;					// Aborted
unknown's avatar
unknown committed
184 185
  thd->proc_info="end";
  end_read_record(&info);
186
  free_io_cache(table);				// Will not do any harm
187
  if (options & OPTION_QUICK)
188
    (void) table->file->extra(HA_EXTRA_NORMAL);
189 190

cleanup:
unknown's avatar
unknown committed
191 192 193 194 195 196 197 198 199
  /*
    Invalidate the table in the query cache if something changed. This must
    be before binlog writing and ha_autocommit_...
  */
  if (deleted)
  {
    query_cache_invalidate3(thd, table_list, 1);
  }

unknown's avatar
unknown committed
200
  delete select;
201 202
  transactional_table= table->file->has_transactions();
  log_delayed= (transactional_table || table->tmp_table);
203 204 205 206 207 208 209 210 211
  /*
    We write to the binary log even if we deleted no row, because maybe the
    user is using this command to ensure that a table is clean on master *and
    on slave*. Think of the case of a user having played separately with the
    master's table and slave's table and wanting to take a fresh identical
    start now.
    error < 0 means "really no error". error <= 0 means "maybe some error".
  */
  if ((deleted || (error < 0)) && (error <= 0 || !transactional_table))
unknown's avatar
unknown committed
212
  {
213 214 215
    mysql_update_log.write(thd,thd->query, thd->query_length);
    if (mysql_bin_log.is_open())
    {
unknown's avatar
unknown committed
216 217
      if (error <= 0)
        thd->clear_error();
unknown's avatar
unknown committed
218
      Query_log_event qinfo(thd, thd->query, thd->query_length,
219
			    log_delayed, FALSE);
220
      if (mysql_bin_log.write(&qinfo) && transactional_table)
221
	error=1;
222
    }
223
    if (!log_delayed)
224
      thd->options|=OPTION_STATUS_NO_TRANS_UPDATE;
unknown's avatar
unknown committed
225
  }
unknown's avatar
unknown committed
226 227 228 229 230
  if (transactional_table)
  {
    if (ha_autocommit_or_rollback(thd,error >= 0))
      error=1;
  }
unknown's avatar
unknown committed
231

unknown's avatar
unknown committed
232 233 234 235 236
  if (thd->lock)
  {
    mysql_unlock_tables(thd, thd->lock);
    thd->lock=0;
  }
237
  free_underlaid_joins(thd, &thd->lex->select_lex);
unknown's avatar
unknown committed
238
  if (error >= 0 || thd->net.report_error)
239
    send_error(thd,thd->killed ? ER_SERVER_SHUTDOWN: 0);
unknown's avatar
unknown committed
240 241
  else
  {
242
    send_ok(thd,deleted);
unknown's avatar
unknown committed
243 244
    DBUG_PRINT("info",("%d records deleted",deleted));
  }
unknown's avatar
unknown committed
245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264
       DBUG_RETURN(0);
}


/*
  Prepare items in DELETE statement

  SYNOPSIS
    mysql_prepare_delete()
    thd			- thread handler
    table_list		- global table list
    conds		- conditions

  RETURN VALUE
    0  - OK
    1  - error (message is sent to user)
    -1 - error (message is not sent to user)
*/
int mysql_prepare_delete(THD *thd, TABLE_LIST *table_list, Item **conds)
{
unknown's avatar
unknown committed
265 266 267
  TABLE_LIST *delete_table_list= ((TABLE_LIST*) thd->lex->
				  select_lex.table_list.first);
  DBUG_ENTER("mysql_prepare_delete");
unknown's avatar
unknown committed
268 269 270 271 272 273 274 275 276 277

  if (setup_conds(thd, delete_table_list, conds) || 
      setup_ftfuncs(&thd->lex->select_lex))
    DBUG_RETURN(-1);
  if (find_real_table_in_list(table_list->next, 
			      table_list->db, table_list->real_name))
  {
    my_error(ER_UPDATE_TABLE_USED, MYF(0), table_list->real_name);
    DBUG_RETURN(-1);
  }
unknown's avatar
unknown committed
278
  DBUG_RETURN(0);
unknown's avatar
unknown committed
279 280 281
}


282
/***************************************************************************
283
  Delete multiple tables from join 
284 285
***************************************************************************/

286
#define MEM_STRIP_BUF_SIZE current_thd->variables.sortbuff_size
unknown's avatar
unknown committed
287

288
extern "C" int refposcmp2(void* arg, const void *a,const void *b)
289
{
unknown's avatar
unknown committed
290
  /* arg is a pointer to file->ref_length */
291
  return memcmp(a,b, *(int*) arg);
292
}
293

unknown's avatar
unknown committed
294 295
multi_delete::multi_delete(THD *thd_arg, TABLE_LIST *dt,
			   uint num_of_tables_arg)
296
  : delete_tables(dt), thd(thd_arg), deleted(0), found(0),
unknown's avatar
unknown committed
297
    num_of_tables(num_of_tables_arg), error(0),
298
    do_delete(0), transactional_tables(0), log_delayed(0), normal_tables(0)
299
{
unknown's avatar
unknown committed
300 301 302 303 304
  tempfiles = (Unique **) sql_calloc(sizeof(Unique *) * (num_of_tables-1));
}


int
305
multi_delete::prepare(List<Item> &values, SELECT_LEX_UNIT *u)
unknown's avatar
unknown committed
306 307
{
  DBUG_ENTER("multi_delete::prepare");
308
  unit= u;
unknown's avatar
unknown committed
309
  do_delete= 1;
unknown's avatar
unknown committed
310
  thd->proc_info="deleting from main table";
311 312 313
  DBUG_RETURN(0);
}

unknown's avatar
unknown committed
314

unknown's avatar
unknown committed
315
bool
unknown's avatar
unknown committed
316 317
multi_delete::initialize_tables(JOIN *join)
{
318
  TABLE_LIST *walk;
unknown's avatar
unknown committed
319 320 321 322 323 324
  Unique **tempfiles_ptr;
  DBUG_ENTER("initialize_tables");

  if ((thd->options & OPTION_SAFE_UPDATES) && error_if_full_join(join))
    DBUG_RETURN(1);

325 326 327
  table_map tables_to_delete_from=0;
  for (walk= delete_tables ; walk ; walk=walk->next)
    tables_to_delete_from|= walk->table->map;
unknown's avatar
unknown committed
328

329
  walk= delete_tables;
unknown's avatar
unknown committed
330 331 332 333
  for (JOIN_TAB *tab=join->join_tab, *end=join->join_tab+join->tables;
       tab < end;
       tab++)
  {
334
    if (tab->table->map & tables_to_delete_from)
unknown's avatar
unknown committed
335
    {
336
      /* We are going to delete from this table */
337
      TABLE *tbl=walk->table=tab->table;
unknown's avatar
unknown committed
338
      walk=walk->next;
unknown's avatar
unknown committed
339
      /* Don't use KEYREAD optimization on this table */
340
      tbl->no_keyread=1;
341 342
      /* Don't use record cache */
      tbl->no_cache= 1;
unknown's avatar
unknown committed
343
      tbl->used_keys.clear_all();
344 345 346 347 348 349
      if (tbl->file->has_transactions())
	log_delayed= transactional_tables= 1;
      else if (tbl->tmp_table != NO_TMP_TABLE)
	log_delayed= 1;
      else
	normal_tables= 1;
unknown's avatar
unknown committed
350 351
    }
  }
352
  walk= delete_tables;
unknown's avatar
unknown committed
353 354
  tempfiles_ptr= tempfiles;
  for (walk=walk->next ; walk ; walk=walk->next)
355 356
  {
    TABLE *table=walk->table;
unknown's avatar
unknown committed
357 358 359 360
    *tempfiles_ptr++= new Unique (refposcmp2,
				  (void *) &table->file->ref_length,
				  table->file->ref_length,
				  MEM_STRIP_BUF_SIZE);
361
  }
362
  init_ftfuncs(thd, thd->lex->current_select, 1);
363
  DBUG_RETURN(thd->is_fatal_error != 0);
unknown's avatar
unknown committed
364
}
unknown's avatar
unknown committed
365

366

367 368
multi_delete::~multi_delete()
{
unknown's avatar
unknown committed
369 370 371
  for (table_being_deleted=delete_tables ;
       table_being_deleted ;
       table_being_deleted=table_being_deleted->next)
372 373
  {
    TABLE *t=table_being_deleted->table;
374
    free_io_cache(t);				// Alloced by unique
375 376
    t->no_keyread=0;
  }
377

unknown's avatar
unknown committed
378
  for (uint counter= 0; counter < num_of_tables-1; counter++)
unknown's avatar
unknown committed
379
  {
380
    if (tempfiles[counter])
unknown's avatar
unknown committed
381 382
      delete tempfiles[counter];
  }
383 384
}

unknown's avatar
unknown committed
385

386 387
bool multi_delete::send_data(List<Item> &values)
{
unknown's avatar
unknown committed
388
  int secure_counter= -1;
unknown's avatar
unknown committed
389 390
  DBUG_ENTER("multi_delete::send_data");

unknown's avatar
unknown committed
391 392 393
  for (table_being_deleted=delete_tables ;
       table_being_deleted ;
       table_being_deleted=table_being_deleted->next, secure_counter++)
394 395
  {
    TABLE *table=table_being_deleted->table;
unknown's avatar
unknown committed
396 397 398 399 400 401

    /* Check if we are using outer join and we didn't find the row */
    if (table->status & (STATUS_NULL_ROW | STATUS_DELETED))
      continue;

    table->file->position(table->record[0]);
402
    found++;
unknown's avatar
unknown committed
403

unknown's avatar
unknown committed
404
    if (secure_counter < 0)
405
    {
unknown's avatar
unknown committed
406
      /* If this is the table we are scanning */
unknown's avatar
unknown committed
407 408 409
      table->status|= STATUS_DELETED;
      if (!(error=table->file->delete_row(table->record[0])))
	deleted++;
unknown's avatar
unknown committed
410
      else if (!table_being_deleted->next || table_being_deleted->table->file->has_transactions())
411
      {
unknown's avatar
unknown committed
412
	table->file->print_error(error,MYF(0));
unknown's avatar
unknown committed
413
	DBUG_RETURN(1);
unknown's avatar
unknown committed
414 415 416 417
      }
    }
    else
    {
unknown's avatar
unknown committed
418
      error=tempfiles[secure_counter]->unique_add((char*) table->file->ref);
unknown's avatar
unknown committed
419 420 421
      if (error)
      {
	error=-1;
unknown's avatar
unknown committed
422
	DBUG_RETURN(1);
unknown's avatar
unknown committed
423
      }
424 425
    }
  }
unknown's avatar
unknown committed
426
  DBUG_RETURN(0);
427 428
}

429

430 431
void multi_delete::send_error(uint errcode,const char *err)
{
unknown's avatar
unknown committed
432 433
  DBUG_ENTER("multi_delete::send_error");

unknown's avatar
unknown committed
434
  /* First send error what ever it is ... */
435
  ::send_error(thd,errcode,err);
unknown's avatar
unknown committed
436

unknown's avatar
unknown committed
437 438
  /* If nothing deleted return */
  if (!deleted)
unknown's avatar
unknown committed
439
    DBUG_VOID_RETURN;
440

441
  /* Something already deleted so we have to invalidate cache */
442 443
  query_cache_invalidate3(thd, delete_tables, 1);

unknown's avatar
unknown committed
444 445 446 447 448
  /* Below can happen when thread is killed early ... */
  if (!table_being_deleted)
    table_being_deleted=delete_tables;

  /*
449 450
    If rows from the first table only has been deleted and it is
    transactional, just do rollback.
unknown's avatar
unknown committed
451 452 453 454
    The same if all tables are transactional, regardless of where we are.
    In all other cases do attempt deletes ...
  */
  if ((table_being_deleted->table->file->has_transactions() &&
455
       table_being_deleted == delete_tables) || !normal_tables)
456
    ha_rollback_stmt(thd);
457
  else if (do_delete)
unknown's avatar
unknown committed
458 459 460 461
  {
    VOID(do_deletes(1));
  }
  DBUG_VOID_RETURN;
462 463
}

unknown's avatar
unknown committed
464

unknown's avatar
unknown committed
465 466 467 468 469 470 471
/*
  Do delete from other tables.
  Returns values:
	0 ok
	1 error
*/

unknown's avatar
unknown committed
472
int multi_delete::do_deletes(bool from_send_error)
473
{
474
  int local_error= 0, counter= 0;
475
  DBUG_ENTER("do_deletes");
unknown's avatar
unknown committed
476

477 478
  if (from_send_error)
  {
479
    /* Found out table number for 'table_being_deleted*/
unknown's avatar
unknown committed
480 481 482
    for (TABLE_LIST *aux=delete_tables;
	 aux != table_being_deleted;
	 aux=aux->next)
483 484 485 486
      counter++;
  }
  else
    table_being_deleted = delete_tables;
unknown's avatar
unknown committed
487

unknown's avatar
unknown committed
488
  do_delete= 0;
489
  if (!found)
unknown's avatar
unknown committed
490
    DBUG_RETURN(0);
unknown's avatar
unknown committed
491 492 493
  for (table_being_deleted=table_being_deleted->next;
       table_being_deleted ;
       table_being_deleted=table_being_deleted->next, counter++)
494
  { 
unknown's avatar
unknown committed
495 496 497
    TABLE *table = table_being_deleted->table;
    if (tempfiles[counter]->get(table))
    {
498
      local_error=1;
unknown's avatar
unknown committed
499 500 501 502
      break;
    }

    READ_RECORD	info;
503 504 505 506 507 508
    init_read_record(&info,thd,table,NULL,0,1);
    /*
      Ignore any rows not found in reference tables as they may already have
      been deleted by foreign key handling
    */
    info.ignore_not_found_rows= 1;
unknown's avatar
merge  
unknown committed
509
    while (!(local_error=info.read_record(&info)) && !thd->killed)
unknown's avatar
unknown committed
510
    {
511
      if ((local_error=table->file->delete_row(table->record[0])))
512
      {
513
	table->file->print_error(local_error,MYF(0));
unknown's avatar
unknown committed
514
	break;
515
      }
unknown's avatar
unknown committed
516
      deleted++;
517
    }
unknown's avatar
unknown committed
518
    end_read_record(&info);
519 520
    if (thd->killed && !local_error)
      local_error= 1;
521 522
    if (local_error == -1)				// End of file
      local_error = 0;
523
  }
524
  DBUG_RETURN(local_error);
525 526
}

unknown's avatar
unknown committed
527

528
/*
529 530
  Send ok to the client

531 532 533 534
  return:  0 sucess
	   1 error
*/

535 536
bool multi_delete::send_eof()
{
537
  thd->proc_info="deleting from reference tables";
538 539

  /* Does deletes for the last n - 1 tables, returns 0 if ok */
540
  int local_error= do_deletes(0);		// returns 0 if success
unknown's avatar
unknown committed
541

unknown's avatar
unknown committed
542
  /* reset used flags */
543
  thd->proc_info="end";
unknown's avatar
unknown committed
544

545 546 547 548
  /*
    We must invalidate the query cache before binlog writing and
    ha_autocommit_...
  */
unknown's avatar
unknown committed
549 550
  if (deleted)
    query_cache_invalidate3(thd, delete_tables, 1);
unknown's avatar
unknown committed
551

552 553 554 555 556
  /*
    Write the SQL statement to the binlog if we deleted
    rows and we succeeded, or also in an error case when there
    was a non-transaction-safe table involved, since
    modifications in it cannot be rolled back.
557 558
    Note that if we deleted nothing we don't write to the binlog (TODO:
    fix this).
559
  */
unknown's avatar
unknown committed
560
  if (deleted && (error <= 0 || normal_tables))
561 562
  {
    mysql_update_log.write(thd,thd->query,thd->query_length);
unknown's avatar
unknown committed
563 564
    if (mysql_bin_log.is_open())
    {
unknown's avatar
unknown committed
565 566
      if (error <= 0)
        thd->clear_error();
567
      Query_log_event qinfo(thd, thd->query, thd->query_length,
568
			    log_delayed, FALSE);
569
      if (mysql_bin_log.write(&qinfo) && !normal_tables)
570
	local_error=1;  // Log write failed: roll back the SQL statement
unknown's avatar
unknown committed
571
    }
unknown's avatar
unknown committed
572 573
    if (!log_delayed)
      thd->options|=OPTION_STATUS_NO_TRANS_UPDATE;
574
  }
unknown's avatar
unknown committed
575 576 577 578 579
  /* Commit or rollback the current SQL statement */ 
  if (transactional_tables)
    if (ha_autocommit_or_rollback(thd,local_error > 0))
      local_error=1;

unknown's avatar
merge  
unknown committed
580
  if (local_error)
unknown's avatar
unknown committed
581
    ::send_error(thd);
582
  else
unknown's avatar
unknown committed
583
    ::send_ok(thd, deleted);
584 585
  return 0;
}
586 587 588


/***************************************************************************
589
  TRUNCATE TABLE
590 591 592 593 594 595 596 597 598 599 600
****************************************************************************/

/*
  Optimize delete of all rows by doing a full generate of the table
  This will work even if the .ISM and .ISD tables are destroyed

  dont_send_ok should be set if:
  - We should always wants to generate the table (even if the table type
    normally can't safely do this.
  - We don't want an ok to be sent to the end user.
  - We don't want to log the truncate command
unknown's avatar
unknown committed
601
  - If we want to have a name lock on the table on exit without errors.
602 603 604 605 606 607 608 609 610 611
*/

int mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok)
{
  HA_CREATE_INFO create_info;
  char path[FN_REFLEN];
  TABLE **table_ptr;
  int error;
  DBUG_ENTER("mysql_truncate");

612
  bzero((char*) &create_info,sizeof(create_info));
613
  /* If it is a temporary table, close and regenerate it */
unknown's avatar
unknown committed
614 615
  if (!dont_send_ok && (table_ptr=find_temporary_table(thd,table_list->db,
						       table_list->real_name)))
616 617 618
  {
    TABLE *table= *table_ptr;
    table->file->info(HA_STATUS_AUTO | HA_STATUS_NO_LOCK);
unknown's avatar
unknown committed
619
    db_type table_type=table->db_type;
620 621 622 623 624
    strmov(path,table->path);
    *table_ptr= table->next;			// Unlink table from list
    close_temporary(table,0);
    *fn_ext(path)=0;				// Remove the .frm extension
    ha_create_table(path, &create_info,1);
unknown's avatar
unknown committed
625
    // We don't need to call invalidate() because this table is not in cache
626 627 628
    if ((error= (int) !(open_temporary_table(thd, path, table_list->db,
					     table_list->real_name, 1))))
      (void) rm_temporary_table(table_type, path);
629
    /*
unknown's avatar
unknown committed
630 631
      If we return here we will not have logged the truncation to the bin log
      and we will not send_ok() to the client.
unknown's avatar
unknown committed
632 633
    */
    goto end; 
634 635 636 637 638 639 640 641 642 643 644
  }

  (void) sprintf(path,"%s/%s/%s%s",mysql_data_home,table_list->db,
		 table_list->real_name,reg_ext);
  fn_format(path,path,"","",4);

  if (!dont_send_ok)
  {
    db_type table_type;
    if ((table_type=get_table_type(path)) == DB_TYPE_UNKNOWN)
    {
645 646
      my_error(ER_NO_SUCH_TABLE, MYF(0), table_list->db,
	       table_list->real_name);
647 648 649 650 651
      DBUG_RETURN(-1);
    }
    if (!ha_supports_generate(table_type))
    {
      /* Probably InnoDB table */
652
      ulong save_options= thd->options;
unknown's avatar
unknown committed
653
      table_list->lock_type= TL_WRITE;
654
      thd->options&= ~(ulong) (OPTION_BEGIN | OPTION_NOT_AUTOCOMMIT);
655 656 657 658
      ha_enable_transaction(thd, FALSE);
      error= mysql_delete(thd, table_list, (COND*) 0, (SQL_LIST*) 0,
			  HA_POS_ERROR, 0);
      ha_enable_transaction(thd, TRUE);
659
      thd->options= save_options;
660
      DBUG_RETURN(error);
661 662 663 664 665 666 667
    }
    if (lock_and_wait_for_table_name(thd, table_list))
      DBUG_RETURN(-1);
  }

  *fn_ext(path)=0;				// Remove the .frm extension
  error= ha_create_table(path,&create_info,1) ? -1 : 0;
unknown's avatar
unknown committed
668
  query_cache_invalidate3(thd, table_list, 0); 
669

unknown's avatar
unknown committed
670
end:
unknown's avatar
unknown committed
671
  if (!dont_send_ok)
672
  {
unknown's avatar
unknown committed
673
    if (!error)
674
    {
unknown's avatar
unknown committed
675 676 677
      mysql_update_log.write(thd,thd->query,thd->query_length);
      if (mysql_bin_log.is_open())
      {
unknown's avatar
unknown committed
678
        thd->clear_error();
679
	Query_log_event qinfo(thd, thd->query, thd->query_length,
680
			      thd->tmp_table, FALSE);
unknown's avatar
unknown committed
681 682
	mysql_bin_log.write(&qinfo);
      }
683
      send_ok(thd);		// This should return record count
684
    }
685
    VOID(pthread_mutex_lock(&LOCK_open));
unknown's avatar
unknown committed
686
    unlock_table_name(thd, table_list);
687
    VOID(pthread_mutex_unlock(&LOCK_open));
688
  }
unknown's avatar
unknown committed
689
  else if (error)
690 691
  {
    VOID(pthread_mutex_lock(&LOCK_open));
unknown's avatar
unknown committed
692
    unlock_table_name(thd, table_list);
693 694
    VOID(pthread_mutex_unlock(&LOCK_open));
  }
695 696
  DBUG_RETURN(error ? -1 : 0);
}