sql_delete.cc 19.2 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 31
int mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
                 SQL_LIST *order, 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 */
  }

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 */
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));
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
  thd->allow_sum_func= 0;
unknown's avatar
unknown committed
270 271 272 273 274 275 276 277 278
  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
279
  DBUG_RETURN(0);
unknown's avatar
unknown committed
280 281 282
}


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

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

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

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


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

unknown's avatar
unknown committed
315

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

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

326 327 328
  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
329

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

367

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

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

unknown's avatar
unknown committed
386

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

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

    /* 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]);
403
    found++;
unknown's avatar
unknown committed
404

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

430

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

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

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

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

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

  /*
450 451
    If rows from the first table only has been deleted and it is
    transactional, just do rollback.
unknown's avatar
unknown committed
452 453 454 455
    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() &&
456
       table_being_deleted == delete_tables) || !normal_tables)
457
    ha_rollback_stmt(thd);
458
  else if (do_delete)
unknown's avatar
unknown committed
459 460 461 462
  {
    VOID(do_deletes(1));
  }
  DBUG_VOID_RETURN;
463 464
}

unknown's avatar
unknown committed
465

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

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

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

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

    READ_RECORD	info;
504 505 506 507 508 509
    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
510
    while (!(local_error=info.read_record(&info)) && !thd->killed)
unknown's avatar
unknown committed
511
    {
512
      if ((local_error=table->file->delete_row(table->record[0])))
513
      {
514
	table->file->print_error(local_error,MYF(0));
unknown's avatar
unknown committed
515
	break;
516
      }
unknown's avatar
unknown committed
517
      deleted++;
518
    }
unknown's avatar
unknown committed
519
    end_read_record(&info);
520 521
    if (thd->killed && !local_error)
      local_error= 1;
522 523
    if (local_error == -1)				// End of file
      local_error = 0;
524
  }
525
  DBUG_RETURN(local_error);
526 527
}

unknown's avatar
unknown committed
528

529
/*
530 531
  Send ok to the client

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

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

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

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

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

553 554 555 556 557
  /*
    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.
558 559
    Note that if we deleted nothing we don't write to the binlog (TODO:
    fix this).
560
  */
unknown's avatar
unknown committed
561
  if (deleted && (error <= 0 || normal_tables))
562 563
  {
    mysql_update_log.write(thd,thd->query,thd->query_length);
unknown's avatar
unknown committed
564 565
    if (mysql_bin_log.is_open())
    {
unknown's avatar
unknown committed
566 567
      if (error <= 0)
        thd->clear_error();
568
      Query_log_event qinfo(thd, thd->query, thd->query_length,
569
			    log_delayed, FALSE);
570
      if (mysql_bin_log.write(&qinfo) && !normal_tables)
571
	local_error=1;  // Log write failed: roll back the SQL statement
unknown's avatar
unknown committed
572
    }
unknown's avatar
unknown committed
573 574
    if (!log_delayed)
      thd->options|=OPTION_STATUS_NO_TRANS_UPDATE;
575
  }
unknown's avatar
unknown committed
576 577 578 579 580
  /* 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
581
  if (local_error)
unknown's avatar
unknown committed
582
    ::send_error(thd);
583
  else
unknown's avatar
unknown committed
584
    ::send_ok(thd, deleted);
585 586
  return 0;
}
587 588 589


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

/*
  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
602
  - If we want to have a name lock on the table on exit without errors.
603 604 605 606 607 608 609 610 611 612
*/

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

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

  (void) sprintf(path,"%s/%s/%s%s",mysql_data_home,table_list->db,
		 table_list->real_name,reg_ext);
641
  fn_format(path, path, "", "", MY_UNPACK_FILENAME);
642 643 644 645 646 647

  if (!dont_send_ok)
  {
    db_type table_type;
    if ((table_type=get_table_type(path)) == DB_TYPE_UNKNOWN)
    {
648 649
      my_error(ER_NO_SUCH_TABLE, MYF(0), table_list->db,
	       table_list->real_name);
650 651 652
      DBUG_RETURN(-1);
    }
    if (!ha_supports_generate(table_type))
653
      goto trunc_by_del;
654 655 656 657 658 659
    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
660
  query_cache_invalidate3(thd, table_list, 0); 
661

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

 trunc_by_del:
  /* Probably InnoDB table */
  ulong save_options= thd->options;
  table_list->lock_type= TL_WRITE;
  thd->options&= ~(ulong) (OPTION_BEGIN | OPTION_NOT_AUTOCOMMIT);
  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);
  thd->options= save_options;
  DBUG_RETURN(error);
700
}