sql_delete.cc 28.4 KB
Newer Older
1
/* Copyright (C) 2000 MySQL 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
   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
5
   the Free Software Foundation; version 2 of the License.
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
6

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

bk@work.mysql.com's avatar
bk@work.mysql.com committed
12 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
  Delete of records and truncate of tables.
18

19
  Multi-table deletes were introduced by Monty and Sinisa
bk@work.mysql.com's avatar
bk@work.mysql.com committed
20 21
*/

22
#include "mysql_priv.h"
23
#include "ha_innodb.h"
24
#include "sql_select.h"
25 26
#include "sp_head.h"
#include "sql_trigger.h"
bk@work.mysql.com's avatar
bk@work.mysql.com committed
27

28
bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
osku@127.(none)'s avatar
osku@127.(none) committed
29 30
                  SQL_LIST *order, ha_rows limit, ulonglong options,
                  bool reset_auto_increment)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
31 32 33
{
  int		error;
  TABLE		*table;
34
  SQL_SELECT	*select=0;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
35
  READ_RECORD	info;
36 37
  bool          using_limit=limit != HA_POS_ERROR;
  bool		transactional_table, safe_update, const_cond;
38
  bool          triggers_applicable;
39
  ha_rows	deleted;
40
  uint usable_index= MAX_KEY;
41
  SELECT_LEX   *select_lex= &thd->lex->select_lex;
42
  THD::killed_state killed_status= THD::NOT_KILLED;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
43 44
  DBUG_ENTER("mysql_delete");

45 46
  if (open_and_lock_tables(thd, table_list))
    DBUG_RETURN(TRUE);
47 48 49 50
  if (!(table= table_list->table))
  {
    my_error(ER_VIEW_DELETE_MERGE_VIEW, MYF(0),
	     table_list->view_db.str, table_list->view_name.str);
monty@mysql.com's avatar
monty@mysql.com committed
51
    DBUG_RETURN(TRUE);
52
  }
53 54 55 56 57 58
  error= table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK);
  if (error)
  {
    table->file->print_error(error, MYF(0));
    DBUG_RETURN(error);
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
59 60
  thd->proc_info="init";
  table->map=1;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
61

62 63
  if (mysql_prepare_delete(thd, table_list, &conds))
    DBUG_RETURN(TRUE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
64

65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85
  /* check ORDER BY even if it can be ignored */
  if (order && order->elements)
  {
    TABLE_LIST   tables;
    List<Item>   fields;
    List<Item>   all_fields;

    bzero((char*) &tables,sizeof(tables));
    tables.table = table;
    tables.alias = table_list->alias;

      if (select_lex->setup_ref_array(thd, order->elements) ||
	  setup_order(thd, select_lex->ref_pointer_array, &tables,
                    fields, all_fields, (ORDER*) order->first))
    {
      delete select;
      free_underlaid_joins(thd, &thd->lex->select_lex);
      DBUG_RETURN(TRUE);
    }
  }

86 87 88 89
  const_cond= (!conds || conds->const_item());
  safe_update=test(thd->options & OPTION_SAFE_UPDATES);
  if (safe_update && const_cond)
  {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
90 91
    my_message(ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE,
               ER(ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE), MYF(0));
92
    DBUG_RETURN(TRUE);
93 94
  }

95
  select_lex->no_error= thd->lex->ignore;
96

97 98 99 100 101
  /* NOTE: TRUNCATE must not invoke triggers. */

  triggers_applicable= table->triggers &&
                       thd->lex->sql_command != SQLCOM_TRUNCATE;

102 103 104 105
  /*
    Test if the user wants to delete all rows and deletion doesn't have
    any side-effects (because of triggers), so we can use optimized
    handler::delete_all_rows() method.
106 107
    We implement fast TRUNCATE for InnoDB even if triggers are present. 
    TRUNCATE ignores triggers.
108
  */
monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
109
  if (!using_limit && const_cond && (!conds || conds->val_int()) &&
110
      !(specialflag & (SPECIAL_NO_NEW_FUNC | SPECIAL_SAFE_MODE)) &&
111
      !(triggers_applicable && table->triggers->has_delete_triggers())
112
     )
113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128
  {
    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 */
  }

129 130 131 132 133 134 135 136
  if (conds)
  {
    Item::cond_result result;
    conds= remove_eq_conds(thd, conds, &result);
    if (result == Item::COND_FALSE)             // Impossible where
      limit= 0;
  }

137 138
  table->used_keys.clear_all();
  table->quick_keys.clear_all();		// Can't use 'only index'
monty@mysql.com's avatar
monty@mysql.com committed
139
  select=make_select(table, 0, 0, conds, 0, &error);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
140
  if (error)
141
    DBUG_RETURN(TRUE);
142
  if ((select && select->check_quick(thd, safe_update, limit)) || !limit)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
143 144
  {
    delete select;
145
    free_underlaid_joins(thd, select_lex);
146
    thd->row_count_func= 0;
147
    send_ok(thd,0L);
148 149 150 151 152 153 154

    /*
      We don't need to call reset_auto_increment in this case, because
      mysql_truncate always gives a NULL conds argument, hence we never
      get here.
    */

monty@tik.mysql.fi's avatar
monty@tik.mysql.fi committed
155
    DBUG_RETURN(0);				// Nothing to delete
bk@work.mysql.com's avatar
bk@work.mysql.com committed
156 157 158
  }

  /* If running in safe sql mode, don't allow updates without keys */
159
  if (table->quick_keys.is_clear_all())
bk@work.mysql.com's avatar
bk@work.mysql.com committed
160
  {
161
    thd->server_status|=SERVER_QUERY_NO_INDEX_USED;
162
    if (safe_update && !using_limit)
163 164
    {
      delete select;
165
      free_underlaid_joins(thd, select_lex);
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
166 167
      my_message(ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE,
                 ER(ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE), MYF(0));
168
      DBUG_RETURN(TRUE);
169
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
170
  }
171
  if (options & OPTION_QUICK)
172 173
    (void) table->file->extra(HA_EXTRA_QUICK);

174
  if (order && order->elements)
175
  {
176
    uint         length= 0;
177
    SORT_FIELD  *sortorder;
178
    ha_rows examined_rows;
179
    
180
    if ((!select || table->quick_keys.is_clear_all()) && limit != HA_POS_ERROR)
181 182 183 184 185 186 187
      usable_index= get_index_for_order(table, (ORDER*)(order->first), limit);

    if (usable_index == MAX_KEY)
    {
      table->sort.io_cache= (IO_CACHE *) my_malloc(sizeof(IO_CACHE),
                                                   MYF(MY_FAE | MY_ZEROFILL));
    
188
      if (!(sortorder= make_unireg_sortorder((ORDER*) order->first,
189
                                             &length, NULL)) ||
190
	  (table->sort.found_records = filesort(thd, table, sortorder, length,
191 192
                                                select, HA_POS_ERROR,
                                                &examined_rows))
193
	  == HA_POS_ERROR)
194 195 196
      {
        delete select;
        free_underlaid_joins(thd, &thd->lex->select_lex);
197
        DBUG_RETURN(TRUE);
198 199 200 201 202
      }
      /*
        Filesort has already found and selected the rows we want to delete,
        so we don't need the where clause
      */
203
      delete select;
204
      free_underlaid_joins(thd, select_lex);
205
      select= 0;
206 207 208
    }
  }

sergefp@mysql.com's avatar
sergefp@mysql.com committed
209 210 211 212
  /* If quick select is used, initialize it before retrieving rows. */
  if (select && select->quick && select->quick->reset())
  {
    delete select;
213
    free_underlaid_joins(thd, select_lex);
214
    DBUG_RETURN(TRUE);
sergefp@mysql.com's avatar
sergefp@mysql.com committed
215
  }
216 217 218 219 220
  if (usable_index==MAX_KEY)
    init_read_record(&info,thd,table,select,1,1);
  else
    init_read_record_idx(&info, thd, table, 1, usable_index);

221
  deleted=0L;
222
  init_ftfuncs(thd, select_lex, 1);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
223
  thd->proc_info="updating";
224

225
  if (triggers_applicable)
226
  {
227
    table->triggers->mark_fields_used(thd, TRG_EVENT_DELETE);
228 229 230 231 232 233 234 235 236 237 238
    if (table->triggers->has_triggers(TRG_EVENT_DELETE,
                                      TRG_ACTION_AFTER))
    {
      /*
	The table has AFTER DELETE triggers that might access to subject table
	and therefore might need delete to be done immediately. So we turn-off
	the batching.
      */
      (void) table->file->extra(HA_EXTRA_DELETE_CANNOT_BATCH);
    }
  }
239

bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
240 241
  while (!(error=info.read_record(&info)) && !thd->killed &&
	 !thd->net.report_error)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
242
  {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
243
    // thd->net.report_error is tested to disallow delete row on error
244
    if (!(select && select->skip_record())&& !thd->net.report_error )
bk@work.mysql.com's avatar
bk@work.mysql.com committed
245
    {
246

247
      if (triggers_applicable &&
248 249 250 251 252 253
          table->triggers->process_triggers(thd, TRG_EVENT_DELETE,
                                            TRG_ACTION_BEFORE, FALSE))
      {
        error= 1;
        break;
      }
254

bk@work.mysql.com's avatar
bk@work.mysql.com committed
255 256 257
      if (!(error=table->file->delete_row(table->record[0])))
      {
	deleted++;
258
        if (triggers_applicable &&
259 260 261 262 263 264
            table->triggers->process_triggers(thd, TRG_EVENT_DELETE,
                                              TRG_ACTION_AFTER, FALSE))
        {
          error= 1;
          break;
        }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
265 266 267 268 269 270 271 272 273
	if (!--limit && using_limit)
	{
	  error= -1;
	  break;
	}
      }
      else
      {
	table->file->print_error(error,MYF(0));
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
274 275 276 277 278 279 280 281 282
	/*
	  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;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
283 284 285
	break;
      }
    }
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
286 287
    else
      table->file->unlock_row();  // Row failed selection, release lock on it
bk@work.mysql.com's avatar
bk@work.mysql.com committed
288
  }
289 290
  killed_status= thd->killed;
  error= (killed_status == THD::NOT_KILLED)?  error : 1;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
291 292
  thd->proc_info="end";
  end_read_record(&info);
293
  free_io_cache(table);				// Will not do any harm
294
  if (options & OPTION_QUICK)
295
    (void) table->file->extra(HA_EXTRA_NORMAL);
296

osku@127.(none)'s avatar
osku@127.(none) committed
297
  if (reset_auto_increment && (error < 0))
298 299 300 301 302
  {
    /*
      We're really doing a truncate and need to reset the table's
      auto-increment counter.
    */
osku@127.(none)'s avatar
osku@127.(none) committed
303
    int error2= table->file->reset_auto_increment(0);
304 305 306 307

    if (error2 && (error2 != HA_ERR_WRONG_COMMAND))
    {
      table->file->print_error(error2, MYF(0));
osku@127.(none)'s avatar
osku@127.(none) committed
308
      error= 1;
309 310 311
    }
  }

312
cleanup:
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
313 314 315 316 317 318 319 320 321
  /*
    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);
  }

322
  delete select;
323
  transactional_table= table->file->has_transactions();
324 325 326
  if (!transactional_table && deleted > 0)
    thd->transaction.stmt.modified_non_trans_table= TRUE;
  
327
  /* See similar binlogging code in sql_update.cc, for comments */
328
  if ((error < 0) || thd->transaction.stmt.modified_non_trans_table)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
329
  {
330 331
    if (mysql_bin_log.is_open())
    {
332
      if (error < 0)
guilhem@mysql.com's avatar
guilhem@mysql.com committed
333
        thd->clear_error();
334
      Query_log_event qinfo(thd, thd->query, thd->query_length,
335
			    transactional_table, FALSE, killed_status);
336
      if (mysql_bin_log.write(&qinfo) && transactional_table)
337
	error=1;
338
    }
339 340
    if (thd->transaction.stmt.modified_non_trans_table)
      thd->transaction.all.modified_non_trans_table= TRUE;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
341
  }
342
  DBUG_ASSERT(transactional_table || !deleted || thd->transaction.stmt.modified_non_trans_table);
343
  free_underlaid_joins(thd, select_lex);
344 345 346 347 348
  if (transactional_table)
  {
    if (ha_autocommit_or_rollback(thd,error >= 0))
      error=1;
  }
349

bk@work.mysql.com's avatar
bk@work.mysql.com committed
350 351 352 353 354
  if (thd->lock)
  {
    mysql_unlock_tables(thd, thd->lock);
    thd->lock=0;
  }
355
  if (error < 0 || (thd->lex->ignore && !thd->is_fatal_error))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
356
  {
357
    thd->row_count_func= deleted;
358
    send_ok(thd,deleted);
359
    DBUG_PRINT("info",("%ld records deleted",(long) deleted));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
360
  }
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
361
  DBUG_RETURN(error >= 0 || thd->net.report_error);
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
362 363 364 365 366 367 368 369 370
}


/*
  Prepare items in DELETE statement

  SYNOPSIS
    mysql_prepare_delete()
    thd			- thread handler
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
371
    table_list		- global/local table list
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
372 373 374
    conds		- conditions

  RETURN VALUE
375 376
    FALSE OK
    TRUE  error
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
377
*/
378
bool mysql_prepare_delete(THD *thd, TABLE_LIST *table_list, Item **conds)
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
379
{
380
  Item *fake_conds= 0;
381
  SELECT_LEX *select_lex= &thd->lex->select_lex;
382
  DBUG_ENTER("mysql_prepare_delete");
383
  List<Item> all_fields;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
384

igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
385
  thd->lex->allow_sum_func= 0;
386 387 388 389
  if (setup_tables_and_check_access(thd, &thd->lex->select_lex.context,
                                    &thd->lex->select_lex.top_join_list,
                                    table_list, conds, 
                                    &select_lex->leaf_tables, FALSE, 
390
                                    DELETE_ACL, SELECT_ACL) ||
391
      setup_conds(thd, table_list, select_lex->leaf_tables, conds) ||
392
      setup_ftfuncs(select_lex))
393
    DBUG_RETURN(TRUE);
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
394
  if (!table_list->updatable || check_key_in_view(thd, table_list))
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
395
  {
396
    my_error(ER_NON_UPDATABLE_TABLE, MYF(0), table_list->alias, "DELETE");
397
    DBUG_RETURN(TRUE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
398
  }
399
  {
400
    TABLE_LIST *duplicate;
401
    if ((duplicate= unique_table(thd, table_list, table_list->next_global, 0)))
402
    {
403
      update_non_unique_table_error(table_list, "DELETE", duplicate);
404 405
      DBUG_RETURN(TRUE);
    }
406
  }
407 408 409 410 411

  if (select_lex->inner_refs_list.elements &&
    fix_inner_refs(thd, all_fields, select_lex, select_lex->ref_pointer_array))
    DBUG_RETURN(-1);

412
  select_lex->fix_prepare_information(thd, conds, &fake_conds);
413
  DBUG_RETURN(FALSE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
414 415 416
}


417
/***************************************************************************
418
  Delete multiple tables from join 
419 420
***************************************************************************/

421
#define MEM_STRIP_BUF_SIZE current_thd->variables.sortbuff_size
422

423
extern "C" int refpos_order_cmp(void* arg, const void *a,const void *b)
424
{
425 426
  handler *file= (handler*)arg;
  return file->cmp_ref((const byte*)a, (const byte*)b);
427
}
428

bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
429 430 431 432 433 434 435 436
/*
  make delete specific preparation and checks after opening tables

  SYNOPSIS
    mysql_multi_delete_prepare()
    thd         thread handler

  RETURN
437 438
    FALSE OK
    TRUE  Error
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
439 440
*/

441
bool mysql_multi_delete_prepare(THD *thd)
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
442 443
{
  LEX *lex= thd->lex;
444
  TABLE_LIST *aux_tables= (TABLE_LIST *)lex->auxiliary_table_list.first;
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
445 446 447 448 449 450 451 452 453
  TABLE_LIST *target_tbl;
  DBUG_ENTER("mysql_multi_delete_prepare");

  /*
    setup_tables() need for VIEWs. JOIN::prepare() will not do it second
    time.

    lex->query_tables also point on local list of DELETE SELECT_LEX
  */
454 455 456 457
  if (setup_tables_and_check_access(thd, &thd->lex->select_lex.context,
                                    &thd->lex->select_lex.top_join_list,
                                    lex->query_tables, &lex->select_lex.where,
                                    &lex->select_lex.leaf_tables, FALSE, 
458
                                    DELETE_ACL, SELECT_ACL))
459
    DBUG_RETURN(TRUE);
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
460

461 462 463 464 465 466

  /*
    Multi-delete can't be constructed over-union => we always have
    single SELECT on top and have to check underlying SELECTs of it
  */
  lex->select_lex.exclude_from_table_unique_test= TRUE;
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
467 468 469 470 471
  /* Fix tables-to-be-deleted-from list to point at opened tables */
  for (target_tbl= (TABLE_LIST*) aux_tables;
       target_tbl;
       target_tbl= target_tbl->next_local)
  {
472 473 474
    if (!(target_tbl->table= target_tbl->correspondent_table->table))
    {
      DBUG_ASSERT(target_tbl->correspondent_table->view &&
475 476 477
                  target_tbl->correspondent_table->merge_underlying_list &&
                  target_tbl->correspondent_table->merge_underlying_list->
                  next_local);
478 479 480 481 482 483
      my_error(ER_VIEW_DELETE_MERGE_VIEW, MYF(0),
               target_tbl->correspondent_table->view_db.str,
               target_tbl->correspondent_table->view_name.str);
      DBUG_RETURN(TRUE);
    }

bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
484 485 486
    if (!target_tbl->correspondent_table->updatable ||
        check_key_in_view(thd, target_tbl->correspondent_table))
    {
487
      my_error(ER_NON_UPDATABLE_TABLE, MYF(0),
488
               target_tbl->table_name, "DELETE");
489
      DBUG_RETURN(TRUE);
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
490 491
    }
    /*
492 493
      Check that table from which we delete is not used somewhere
      inside subqueries/view.
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
494 495
    */
    {
496
      TABLE_LIST *duplicate;
497
      if ((duplicate= unique_table(thd, target_tbl->correspondent_table,
498
                                   lex->query_tables, 0)))
499 500 501 502 503
      {
        update_non_unique_table_error(target_tbl->correspondent_table,
                                      "DELETE", duplicate);
        DBUG_RETURN(TRUE);
      }
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
504 505
    }
  }
506
  DBUG_RETURN(FALSE);
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
507 508 509
}


510 511
multi_delete::multi_delete(TABLE_LIST *dt, uint num_of_tables_arg)
  : delete_tables(dt), deleted(0), found(0),
512
    num_of_tables(num_of_tables_arg), error(0),
513
    do_delete(0), transactional_tables(0), normal_tables(0), error_handled(0)
514
{
515
  tempfiles= (Unique **) sql_calloc(sizeof(Unique *) * num_of_tables);
516 517 518 519
}


int
520
multi_delete::prepare(List<Item> &values, SELECT_LEX_UNIT *u)
521 522
{
  DBUG_ENTER("multi_delete::prepare");
523
  unit= u;
524
  do_delete= 1;
525
  thd->proc_info="deleting from main table";
526 527 528
  DBUG_RETURN(0);
}

529

530
bool
531 532
multi_delete::initialize_tables(JOIN *join)
{
533
  TABLE_LIST *walk;
534 535 536 537 538 539
  Unique **tempfiles_ptr;
  DBUG_ENTER("initialize_tables");

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

540
  table_map tables_to_delete_from=0;
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
541
  for (walk= delete_tables; walk; walk= walk->next_local)
542
    tables_to_delete_from|= walk->table->map;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
543

544
  walk= delete_tables;
545
  delete_while_scanning= 1;
546 547 548 549
  for (JOIN_TAB *tab=join->join_tab, *end=join->join_tab+join->tables;
       tab < end;
       tab++)
  {
550
    if (tab->table->map & tables_to_delete_from)
551
    {
552
      /* We are going to delete from this table */
553
      TABLE *tbl=walk->table=tab->table;
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
554
      walk= walk->next_local;
555
      /* Don't use KEYREAD optimization on this table */
556
      tbl->no_keyread=1;
557 558
      /* Don't use record cache */
      tbl->no_cache= 1;
559
      tbl->used_keys.clear_all();
560
      if (tbl->file->has_transactions())
561
	transactional_tables= 1;
562 563
      else
	normal_tables= 1;
564
      if (tbl->triggers)
565
      {
566
        tbl->triggers->mark_fields_used(thd, TRG_EVENT_DELETE);
567 568 569 570 571 572 573 574 575 576 577
	if (tbl->triggers->has_triggers(TRG_EVENT_DELETE,
                                      TRG_ACTION_AFTER))
	{
	  /*
	    The table has AFTER DELETE triggers that might access to subject 
	    table and therefore might need delete to be done immediately. 
	    So we turn-off the batching.
	  */
	  (void) tbl->file->extra(HA_EXTRA_DELETE_CANNOT_BATCH);
	}
      }
578
    }
579 580 581 582 583 584 585 586 587 588
    else if ((tab->type != JT_SYSTEM && tab->type != JT_CONST) &&
             walk == delete_tables)
    {
      /*
        We are not deleting from the table we are scanning. In this
        case send_data() shouldn't delete any rows a we may touch
        the rows in the deleted table many times
      */
      delete_while_scanning= 0;
    }
589
  }
590
  walk= delete_tables;
591
  tempfiles_ptr= tempfiles;
592 593 594 595 596 597
  if (delete_while_scanning)
  {
    table_being_deleted= delete_tables;
    walk= walk->next_local;
  }
  for (;walk ;walk= walk->next_local)
598 599
  {
    TABLE *table=walk->table;
600 601
    *tempfiles_ptr++= new Unique (refpos_order_cmp,
				  (void *) table->file,
602 603
				  table->file->ref_length,
				  MEM_STRIP_BUF_SIZE);
604
  }
pem@mysql.com's avatar
pem@mysql.com committed
605
  init_ftfuncs(thd, thd->lex->current_select, 1);
606
  DBUG_RETURN(thd->is_fatal_error != 0);
607
}
608

609

610 611
multi_delete::~multi_delete()
{
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
612 613 614
  for (table_being_deleted= delete_tables;
       table_being_deleted;
       table_being_deleted= table_being_deleted->next_local)
615
  {
616 617 618
    TABLE *table= table_being_deleted->table;
    free_io_cache(table);                       // Alloced by unique
    table->no_keyread=0;
619
  }
620

621
  for (uint counter= 0; counter < num_of_tables; counter++)
622
  {
623
    if (tempfiles[counter])
624 625
      delete tempfiles[counter];
  }
626 627
}

628

629 630
bool multi_delete::send_data(List<Item> &values)
{
631 632
  int secure_counter= delete_while_scanning ? -1 : 0;
  TABLE_LIST *del_table;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
633 634
  DBUG_ENTER("multi_delete::send_data");

635 636 637
  for (del_table= delete_tables;
       del_table;
       del_table= del_table->next_local, secure_counter++)
638
  {
639
    TABLE *table= del_table->table;
640 641 642 643 644 645

    /* 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]);
646
    found++;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
647

648
    if (secure_counter < 0)
649
    {
650 651
      /* We are scanning the current table */
      DBUG_ASSERT(del_table == table_being_deleted);
652 653 654
      if (table->triggers &&
          table->triggers->process_triggers(thd, TRG_EVENT_DELETE,
                                            TRG_ACTION_BEFORE, FALSE))
655
        DBUG_RETURN(1);
656 657
      table->status|= STATUS_DELETED;
      if (!(error=table->file->delete_row(table->record[0])))
658
      {
659 660 661
        deleted++;
        if (!table->file->has_transactions())
          thd->transaction.stmt.modified_non_trans_table= TRUE;
662 663 664
        if (table->triggers &&
            table->triggers->process_triggers(thd, TRG_EVENT_DELETE,
                                              TRG_ACTION_AFTER, FALSE))
665
          DBUG_RETURN(1);
666
      }
667
      else
668
      {
669 670
        table->file->print_error(error,MYF(0));
        DBUG_RETURN(1);
671 672 673 674
      }
    }
    else
    {
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
675
      error=tempfiles[secure_counter]->unique_add((char*) table->file->ref);
676 677
      if (error)
      {
678
	error= 1;                               // Fatal error
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
679
	DBUG_RETURN(1);
680
      }
681 682
    }
  }
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
683
  DBUG_RETURN(0);
684 685
}

686

687 688
void multi_delete::send_error(uint errcode,const char *err)
{
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
689 690
  DBUG_ENTER("multi_delete::send_error");

691
  /* First send error what ever it is ... */
692
  my_message(errcode, err, MYF(0));
693

694 695 696
  /* the error was handled or nothing deleted and no side effects return */
  if (error_handled ||
      !thd->transaction.stmt.modified_non_trans_table && !deleted)
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
697
    DBUG_VOID_RETURN;
698

699
  /* Something already deleted so we have to invalidate cache */
700 701
  if (deleted)
    query_cache_invalidate3(thd, delete_tables, 1);
702

703
  /*
704 705
    If rows from the first table only has been deleted and it is
    transactional, just do rollback.
706 707 708
    The same if all tables are transactional, regardless of where we are.
    In all other cases do attempt deletes ...
  */
709 710 711
  if ((table_being_deleted == delete_tables &&
       table_being_deleted->table->file->has_transactions()) ||
      !normal_tables)
712
    ha_rollback_stmt(thd);
713
  else if (do_delete)
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
714
  {
715 716 717 718 719 720
    /*
      We have to execute the recorded do_deletes() and write info into the
      error log
    */
    error= 1;
    send_eof();
721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736
    DBUG_ASSERT(error_handled);
    DBUG_VOID_RETURN;
  }
  
  if (thd->transaction.stmt.modified_non_trans_table)
  {
    /* 
       there is only side effects; to binlog with the error
    */
    if (mysql_bin_log.is_open())
    {
      Query_log_event qinfo(thd, thd->query, thd->query_length,
                            transactional_tables, FALSE);
      mysql_bin_log.write(&qinfo);
    }
    thd->transaction.all.modified_non_trans_table= true;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
737 738
  }
  DBUG_VOID_RETURN;
739 740
}

741

742

743 744 745 746 747 748 749
/*
  Do delete from other tables.
  Returns values:
	0 ok
	1 error
*/

750
int multi_delete::do_deletes()
751
{
752
  int local_error= 0, counter= 0;
753
  DBUG_ENTER("do_deletes");
754
  DBUG_ASSERT(do_delete);
755

756
  do_delete= 0;                                 // Mark called
757
  if (!found)
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
758
    DBUG_RETURN(0);
759 760 761 762 763

  table_being_deleted= (delete_while_scanning ? delete_tables->next_local :
                        delete_tables);
 
  for (; table_being_deleted;
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
764
       table_being_deleted= table_being_deleted->next_local, counter++)
765
  { 
766
    ha_rows last_deleted= deleted;
767 768 769
    TABLE *table = table_being_deleted->table;
    if (tempfiles[counter]->get(table))
    {
770
      local_error=1;
771 772 773 774
      break;
    }

    READ_RECORD	info;
775 776 777 778 779 780
    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;
monty@butch's avatar
merge  
monty@butch committed
781
    while (!(local_error=info.read_record(&info)) && !thd->killed)
782
    {
783 784 785 786 787 788 789
      if (table->triggers &&
          table->triggers->process_triggers(thd, TRG_EVENT_DELETE,
                                            TRG_ACTION_BEFORE, FALSE))
      {
        local_error= 1;
        break;
      }
790
      if ((local_error=table->file->delete_row(table->record[0])))
791
      {
792
	table->file->print_error(local_error,MYF(0));
793
	break;
794
      }
795
      deleted++;
796 797 798 799 800 801 802
      if (table->triggers &&
          table->triggers->process_triggers(thd, TRG_EVENT_DELETE,
                                            TRG_ACTION_AFTER, FALSE))
      {
        local_error= 1;
        break;
      }
803
    }
804 805
    if (last_deleted != deleted && !table->file->has_transactions())
      thd->transaction.stmt.modified_non_trans_table= TRUE;
806
    end_read_record(&info);
807 808
    if (thd->killed && !local_error)
      local_error= 1;
809 810
    if (local_error == -1)				// End of file
      local_error = 0;
811
  }
812
  DBUG_RETURN(local_error);
813 814
}

815

816
/*
817 818
  Send ok to the client

819 820 821 822
  return:  0 sucess
	   1 error
*/

823 824
bool multi_delete::send_eof()
{
825
  THD::killed_state killed_status= THD::NOT_KILLED;
826
  thd->proc_info="deleting from reference tables";
827 828

  /* Does deletes for the last n - 1 tables, returns 0 if ok */
829
  int local_error= do_deletes();		// returns 0 if success
830

831 832
  /* compute a total error to know if something failed */
  local_error= local_error || error;
833
  killed_status= (local_error == 0)? THD::NOT_KILLED : thd->killed;
834
  /* reset used flags */
835
  thd->proc_info="end";
836

837 838 839 840
  /*
    We must invalidate the query cache before binlog writing and
    ha_autocommit_...
  */
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
841
  if (deleted)
monty@mysql.com's avatar
monty@mysql.com committed
842
  {
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
843
    query_cache_invalidate3(thd, delete_tables, 1);
monty@mysql.com's avatar
monty@mysql.com committed
844
  }
845
  if ((local_error == 0) || thd->transaction.stmt.modified_non_trans_table)
846
  {
847 848
    if (mysql_bin_log.is_open())
    {
849
      if (local_error == 0)
guilhem@mysql.com's avatar
guilhem@mysql.com committed
850
        thd->clear_error();
851
      Query_log_event qinfo(thd, thd->query, thd->query_length,
852
			    transactional_tables, FALSE, killed_status);
853
      if (mysql_bin_log.write(&qinfo) && !normal_tables)
854
	local_error=1;  // Log write failed: roll back the SQL statement
855
    }
856 857
    if (thd->transaction.stmt.modified_non_trans_table)
      thd->transaction.all.modified_non_trans_table= TRUE;
858
  }
859 860
  if (local_error != 0)
    error_handled= TRUE; // to force early leave from ::send_error()
861

862
  /* Commit or rollback the current SQL statement */
863 864 865 866
  if (transactional_tables)
    if (ha_autocommit_or_rollback(thd,local_error > 0))
      local_error=1;

867
  if (!local_error)
868 869
  {
    thd->row_count_func= deleted;
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
870
    ::send_ok(thd, deleted);
871
  }
872 873
  return 0;
}
874 875 876


/***************************************************************************
877
  TRUNCATE TABLE
878 879 880 881 882 883 884 885 886 887 888
****************************************************************************/

/*
  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
889
  - If we want to have a name lock on the table on exit without errors.
890 891
*/

892
bool mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok)
893 894 895 896
{
  HA_CREATE_INFO create_info;
  char path[FN_REFLEN];
  TABLE **table_ptr;
897
  bool error;
898 899
  DBUG_ENTER("mysql_truncate");

900
  bzero((char*) &create_info,sizeof(create_info));
901
  /* If it is a temporary table, close and regenerate it */
902
  if (!dont_send_ok && (table_ptr=find_temporary_table(thd,table_list->db,
903
						       table_list->table_name)))
904 905 906
  {
    TABLE *table= *table_ptr;
    table->file->info(HA_STATUS_AUTO | HA_STATUS_NO_LOCK);
907
    db_type table_type= table->s->db_type;
908
    if (!ha_check_storage_engine_flag(table_type, HTON_CAN_RECREATE))
909
      goto trunc_by_del;
910
    strmov(path, table->s->path);
911 912
    *table_ptr= table->next;			// Unlink table from list
    close_temporary(table,0);
913 914
    if (thd->slave_thread)
      --slave_open_temp_tables;
915 916
    *fn_ext(path)=0;				// Remove the .frm extension
    ha_create_table(path, &create_info,1);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
917
    // We don't need to call invalidate() because this table is not in cache
918
    if ((error= (int) !(open_temporary_table(thd, path, table_list->db,
919
					     table_list->table_name, 1))))
920
      (void) rm_temporary_table(table_type, path);
921
    /*
monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
922 923
      If we return here we will not have logged the truncation to the bin log
      and we will not send_ok() to the client.
924
    */
serg@serg.mylan's avatar
serg@serg.mylan committed
925
    goto end;
926 927 928
  }

  (void) sprintf(path,"%s/%s/%s%s",mysql_data_home,table_list->db,
929
		 table_list->table_name,reg_ext);
930
  fn_format(path, path, "", "", MY_UNPACK_FILENAME);
931 932 933 934

  if (!dont_send_ok)
  {
    db_type table_type;
935
    mysql_frm_type(thd, path, &table_type);
936
    if (table_type == DB_TYPE_UNKNOWN)
937
    {
938
      my_error(ER_NO_SUCH_TABLE, MYF(0),
939
               table_list->db, table_list->table_name);
940
      DBUG_RETURN(TRUE);
941
    }
942
    if (!ha_check_storage_engine_flag(table_type, HTON_CAN_RECREATE))
943
      goto trunc_by_del;
944
    if (lock_and_wait_for_table_name(thd, table_list))
945
      DBUG_RETURN(TRUE);
946 947 948
  }

  *fn_ext(path)=0;				// Remove the .frm extension
949
  error= ha_create_table(path,&create_info,1);
950
  query_cache_invalidate3(thd, table_list, 0);
951

952
end:
953
  if (!dont_send_ok)
954
  {
955
    if (!error)
956
    {
957 958
      if (mysql_bin_log.is_open())
      {
guilhem@mysql.com's avatar
guilhem@mysql.com committed
959
        thd->clear_error();
960
	Query_log_event qinfo(thd, thd->query, thd->query_length,
961
			      0, FALSE);
962 963
	mysql_bin_log.write(&qinfo);
      }
964
      send_ok(thd);		// This should return record count
965
    }
966
    VOID(pthread_mutex_lock(&LOCK_open));
967
    unlock_table_name(thd, table_list);
968
    VOID(pthread_mutex_unlock(&LOCK_open));
969
  }
970
  else if (error)
971 972
  {
    VOID(pthread_mutex_lock(&LOCK_open));
973
    unlock_table_name(thd, table_list);
974 975
    VOID(pthread_mutex_unlock(&LOCK_open));
  }
976
  DBUG_RETURN(error);
977 978 979

 trunc_by_del:
  /* Probably InnoDB table */
980
  ulonglong save_options= thd->options;
981 982 983
  table_list->lock_type= TL_WRITE;
  thd->options&= ~(ulong) (OPTION_BEGIN | OPTION_NOT_AUTOCOMMIT);
  ha_enable_transaction(thd, FALSE);
984
  mysql_init_select(thd->lex);
985
  error= mysql_delete(thd, table_list, (COND*) 0, (SQL_LIST*) 0,
986
                      HA_POS_ERROR, LL(0), TRUE);
987 988 989
  ha_enable_transaction(thd, TRUE);
  thd->options= save_options;
  DBUG_RETURN(error);
990
}