sql_delete.cc 27.8 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 5 6
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
7

bk@work.mysql.com's avatar
bk@work.mysql.com committed
8 9 10 11
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
12

bk@work.mysql.com's avatar
bk@work.mysql.com committed
13 14 15 16 17
   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 */

/*
18
  Delete of records and truncate of tables.
19

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

23
#include "mysql_priv.h"
acurtis@xiphis.org's avatar
acurtis@xiphis.org committed
24
#ifdef WITH_INNOBASE_STORAGE_ENGINE
25
#include "ha_innodb.h"
acurtis@xiphis.org's avatar
acurtis@xiphis.org committed
26
#endif
27
#include "sql_select.h"
28 29
#include "sp_head.h"
#include "sql_trigger.h"
bk@work.mysql.com's avatar
bk@work.mysql.com committed
30

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

47 48
  if (open_and_lock_tables(thd, table_list))
    DBUG_RETURN(TRUE);
49 50 51 52
  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
53
    DBUG_RETURN(TRUE);
54
  }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
55 56
  thd->proc_info="init";
  table->map=1;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
57

58 59
  if (mysql_prepare_delete(thd, table_list, &conds))
    DBUG_RETURN(TRUE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
60

61 62 63 64
  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
65 66
    my_message(ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE,
               ER(ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE), MYF(0));
67
    DBUG_RETURN(TRUE);
68 69
  }

70
  select_lex->no_error= thd->lex->ignore;
71

72 73 74 75
  /*
    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.
76 77 78

    If row-based replication is used, we also delete the table row by
    row.
79
  */
monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
80
  if (!using_limit && const_cond && (!conds || conds->val_int()) &&
81
      !(specialflag & (SPECIAL_NO_NEW_FUNC | SPECIAL_SAFE_MODE)) &&
82 83
      !(table->triggers && table->triggers->has_delete_triggers()) &&
      !thd->current_stmt_binlog_row_based)
84
  {
85 86
    /* Update the table->file->records number */
    table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK);
87
    ha_rows const maybe_deleted= table->file->records;
88
    DBUG_PRINT("debug", ("Trying to use delete_all_rows()"));
89 90 91
    if (!(error=table->file->delete_all_rows()))
    {
      error= -1;				// ok
92
      deleted= maybe_deleted;
93 94 95 96 97 98 99 100 101 102 103
      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 */
  }

104 105 106 107 108 109 110 111 112
#ifdef WITH_PARTITION_STORAGE_ENGINE
  if (prune_partitions(thd, table, conds))
  {
    free_underlaid_joins(thd, select_lex);
    thd->row_count_func= 0;
    send_ok(thd);				// No matching records
    DBUG_RETURN(0);
  }
#endif
113 114
  /* Update the table->file->records number */
  table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK);
115

116 117
  table->used_keys.clear_all();
  table->quick_keys.clear_all();		// Can't use 'only index'
monty@mysql.com's avatar
monty@mysql.com committed
118
  select=make_select(table, 0, 0, conds, 0, &error);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
119
  if (error)
120
    DBUG_RETURN(TRUE);
121
  if ((select && select->check_quick(thd, safe_update, limit)) || !limit)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
122 123
  {
    delete select;
124
    free_underlaid_joins(thd, select_lex);
125
    thd->row_count_func= 0;
126
    send_ok(thd,0L);
127 128 129 130 131
    /*
      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
132
    DBUG_RETURN(0);				// Nothing to delete
bk@work.mysql.com's avatar
bk@work.mysql.com committed
133 134 135
  }

  /* If running in safe sql mode, don't allow updates without keys */
136
  if (table->quick_keys.is_clear_all())
bk@work.mysql.com's avatar
bk@work.mysql.com committed
137
  {
138
    thd->server_status|=SERVER_QUERY_NO_INDEX_USED;
139
    if (safe_update && !using_limit)
140 141
    {
      delete select;
142
      free_underlaid_joins(thd, select_lex);
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
143 144
      my_message(ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE,
                 ER(ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE), MYF(0));
145
      DBUG_RETURN(TRUE);
146
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
147
  }
148
  if (options & OPTION_QUICK)
149 150
    (void) table->file->extra(HA_EXTRA_QUICK);

151
  if (order && order->elements)
152 153 154 155 156 157
  {
    uint         length;
    SORT_FIELD  *sortorder;
    TABLE_LIST   tables;
    List<Item>   fields;
    List<Item>   all_fields;
158
    ha_rows examined_rows;
159 160 161

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

164 165
      if (select_lex->setup_ref_array(thd, order->elements) ||
	  setup_order(thd, select_lex->ref_pointer_array, &tables,
166 167 168 169
                    fields, all_fields, (ORDER*) order->first))
    {
      delete select;
      free_underlaid_joins(thd, &thd->lex->select_lex);
sergefp@mysql.com's avatar
sergefp@mysql.com committed
170
      DBUG_RETURN(TRUE);
171 172 173 174 175 176 177 178 179 180
    }
    
    if (!select && limit != HA_POS_ERROR)
      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));
    
181 182
      if (!(sortorder= make_unireg_sortorder((ORDER*) order->first,
                                             &length)) ||
183
	  (table->sort.found_records = filesort(thd, table, sortorder, length,
184 185
                                                select, HA_POS_ERROR,
                                                &examined_rows))
186
	  == HA_POS_ERROR)
187 188 189
      {
        delete select;
        free_underlaid_joins(thd, &thd->lex->select_lex);
190
        DBUG_RETURN(TRUE);
191 192 193 194 195
      }
      /*
        Filesort has already found and selected the rows we want to delete,
        so we don't need the where clause
      */
196
      delete select;
197
      free_underlaid_joins(thd, select_lex);
198
      select= 0;
199 200 201
    }
  }

sergefp@mysql.com's avatar
sergefp@mysql.com committed
202 203 204 205
  /* If quick select is used, initialize it before retrieving rows. */
  if (select && select->quick && select->quick->reset())
  {
    delete select;
206
    free_underlaid_joins(thd, select_lex);
207
    DBUG_RETURN(TRUE);
sergefp@mysql.com's avatar
sergefp@mysql.com committed
208
  }
209 210 211 212 213
  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);

214
  init_ftfuncs(thd, select_lex, 1);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
215
  thd->proc_info="updating";
mronstrom@mysql.com's avatar
mronstrom@mysql.com committed
216
  will_batch= !table->file->start_bulk_delete();
217

bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
218 219
  while (!(error=info.read_record(&info)) && !thd->killed &&
	 !thd->net.report_error)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
220
  {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
221
    // thd->net.report_error is tested to disallow delete row on error
222
    if (!(select && select->skip_record())&& !thd->net.report_error )
bk@work.mysql.com's avatar
bk@work.mysql.com committed
223
    {
224

225 226 227 228 229 230 231
      if (table->triggers &&
          table->triggers->process_triggers(thd, TRG_EVENT_DELETE,
                                            TRG_ACTION_BEFORE, FALSE))
      {
        error= 1;
        break;
      }
232

233
      if (!(error= table->file->ha_delete_row(table->record[0])))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
234 235
      {
	deleted++;
236 237 238 239 240 241 242
        if (table->triggers &&
            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
243 244 245 246 247 248 249 250 251
	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
252 253 254 255 256 257 258 259 260
	/*
	  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
261 262 263
	break;
      }
    }
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
264 265
    else
      table->file->unlock_row();  // Row failed selection, release lock on it
bk@work.mysql.com's avatar
bk@work.mysql.com committed
266
  }
267 268
  if (thd->killed && !error)
    error= 1;					// Aborted
mronstrom@mysql.com[mikron]'s avatar
mronstrom@mysql.com[mikron] committed
269 270 271 272 273 274
  if (will_batch && (loc_error= table->file->end_bulk_delete()))
  {
    if (error != 1)
      table->file->print_error(loc_error,MYF(0));
    error=1;
  }
mronstrom@mysql.com's avatar
mronstrom@mysql.com committed
275
  thd->proc_info= "end";
bk@work.mysql.com's avatar
bk@work.mysql.com committed
276
  end_read_record(&info);
277
  free_io_cache(table);				// Will not do any harm
278
  if (options & OPTION_QUICK)
279
    (void) table->file->extra(HA_EXTRA_NORMAL);
280

osku@127.(none)'s avatar
osku@127.(none) committed
281
  if (reset_auto_increment && (error < 0))
282 283 284 285 286
  {
    /*
      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
287
    int error2= table->file->reset_auto_increment(0);
288 289 290 291

    if (error2 && (error2 != HA_ERR_WRONG_COMMAND))
    {
      table->file->print_error(error2, MYF(0));
osku@127.(none)'s avatar
osku@127.(none) committed
292
      error= 1;
293 294 295
    }
  }

296
cleanup:
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
297 298 299 300 301 302 303 304 305
  /*
    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);
  }

306
  delete select;
307
  transactional_table= table->file->has_transactions();
308

309 310
  /* See similar binlogging code in sql_update.cc, for comments */
  if ((error < 0) || (deleted && !transactional_table))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
311
  {
312 313
    if (mysql_bin_log.is_open())
    {
314
      if (error < 0)
guilhem@mysql.com's avatar
guilhem@mysql.com committed
315
        thd->clear_error();
316 317

      /*
318 319 320 321
        [binlog]: If 'handler::delete_all_rows()' was called and the
        storage engine does not inject the rows itself, we replicate
        statement-based; otherwise, 'ha_delete_row()' was used to
        delete specific rows which we might log row-based.
322
      */
323
      int log_result= thd->binlog_query(THD::ROW_QUERY_TYPE,
324 325 326 327 328
                                        thd->query, thd->query_length,
                                        transactional_table, FALSE);

      if (log_result && transactional_table)
      {
329
	error=1;
330
      }
331
    }
332
    if (!transactional_table)
333
      thd->options|=OPTION_STATUS_NO_TRANS_UPDATE;
bk@work.mysql.com's avatar
bk@work.mysql.com committed
334
  }
335
  free_underlaid_joins(thd, select_lex);
336 337 338 339 340
  if (transactional_table)
  {
    if (ha_autocommit_or_rollback(thd,error >= 0))
      error=1;
  }
341

bk@work.mysql.com's avatar
bk@work.mysql.com committed
342 343 344 345 346
  if (thd->lock)
  {
    mysql_unlock_tables(thd, thd->lock);
    thd->lock=0;
  }
347
  if (error < 0)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
348
  {
349
    thd->row_count_func= deleted;
350
    send_ok(thd,deleted);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
351 352
    DBUG_PRINT("info",("%d records deleted",deleted));
  }
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
353
  DBUG_RETURN(error >= 0 || thd->net.report_error);
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
354 355 356 357 358 359 360 361 362
}


/*
  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
363
    table_list		- global/local table list
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
364 365 366
    conds		- conditions

  RETURN VALUE
367 368
    FALSE OK
    TRUE  error
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
369
*/
370
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
371
{
372
  SELECT_LEX *select_lex= &thd->lex->select_lex;
373
  DBUG_ENTER("mysql_prepare_delete");
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
374

igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
375
  thd->lex->allow_sum_func= 0;
376
  if (setup_tables(thd, &thd->lex->select_lex.context,
377
                   &thd->lex->select_lex.top_join_list,
378 379
                   table_list, conds, &select_lex->leaf_tables,
                   FALSE) ||
380
      setup_conds(thd, table_list, select_lex->leaf_tables, conds) ||
381
      setup_ftfuncs(select_lex))
382
    DBUG_RETURN(TRUE);
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
383
  if (!table_list->updatable || check_key_in_view(thd, table_list))
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
384
  {
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
385
    my_error(ER_NON_UPDATABLE_TABLE, MYF(0), table_list->alias, "DELETE");
386
    DBUG_RETURN(TRUE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
387
  }
388
  {
389
    TABLE_LIST *duplicate;
390
    if ((duplicate= unique_table(thd, table_list, table_list->next_global)))
391 392 393 394
    {
      update_non_unique_table_error(table_list, "DELETE", duplicate);
      DBUG_RETURN(TRUE);
    }
395
  }
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
396
  select_lex->fix_prepare_information(thd, conds);
397
  DBUG_RETURN(FALSE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
398 399 400
}


401
/***************************************************************************
402
  Delete multiple tables from join 
403 404
***************************************************************************/

405
#define MEM_STRIP_BUF_SIZE current_thd->variables.sortbuff_size
406

407
extern "C" int refpos_order_cmp(void* arg, const void *a,const void *b)
408
{
409 410
  handler *file= (handler*)arg;
  return file->cmp_ref((const byte*)a, (const byte*)b);
411
}
412

bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
413 414 415 416 417 418 419 420
/*
  make delete specific preparation and checks after opening tables

  SYNOPSIS
    mysql_multi_delete_prepare()
    thd         thread handler

  RETURN
421 422
    FALSE OK
    TRUE  Error
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
423 424
*/

425
bool mysql_multi_delete_prepare(THD *thd)
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
426 427 428 429 430 431 432 433 434 435 436 437
{
  LEX *lex= thd->lex;
  TABLE_LIST *aux_tables= (TABLE_LIST *)lex->auxilliary_table_list.first;
  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
  */
438
  if (setup_tables(thd, &thd->lex->select_lex.context,
439
                   &thd->lex->select_lex.top_join_list,
440
                   lex->query_tables, &lex->select_lex.where,
441
                   &lex->select_lex.leaf_tables, FALSE))
442
    DBUG_RETURN(TRUE);
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
443

444 445 446 447 448 449

  /*
    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
450 451 452 453 454
  /* 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)
  {
455 456 457
    if (!(target_tbl->table= target_tbl->correspondent_table->table))
    {
      DBUG_ASSERT(target_tbl->correspondent_table->view &&
458 459 460
                  target_tbl->correspondent_table->merge_underlying_list &&
                  target_tbl->correspondent_table->merge_underlying_list->
                  next_local);
461 462 463 464 465 466
      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
467 468 469
    if (!target_tbl->correspondent_table->updatable ||
        check_key_in_view(thd, target_tbl->correspondent_table))
    {
470
      my_error(ER_NON_UPDATABLE_TABLE, MYF(0),
471
               target_tbl->table_name, "DELETE");
472
      DBUG_RETURN(TRUE);
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
473 474
    }
    /*
475 476
      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
477 478
    */
    {
479
      TABLE_LIST *duplicate;
480
      if ((duplicate= unique_table(thd, target_tbl->correspondent_table,
481 482 483 484 485 486
                                   lex->query_tables)))
      {
        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
487 488
    }
  }
489
  DBUG_RETURN(FALSE);
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
490 491 492
}


493 494
multi_delete::multi_delete(TABLE_LIST *dt, uint num_of_tables_arg)
  : delete_tables(dt), deleted(0), found(0),
495
    num_of_tables(num_of_tables_arg), error(0),
496
    do_delete(0), transactional_tables(0), normal_tables(0)
497
{
498
  tempfiles= (Unique **) sql_calloc(sizeof(Unique *) * num_of_tables);
499 500 501 502
}


int
503
multi_delete::prepare(List<Item> &values, SELECT_LEX_UNIT *u)
504 505
{
  DBUG_ENTER("multi_delete::prepare");
506
  unit= u;
507
  do_delete= 1;
508
  thd->proc_info="deleting from main table";
509 510 511
  DBUG_RETURN(0);
}

512

513
bool
514 515
multi_delete::initialize_tables(JOIN *join)
{
516
  TABLE_LIST *walk;
517 518 519 520 521 522
  Unique **tempfiles_ptr;
  DBUG_ENTER("initialize_tables");

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

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

527
  walk= delete_tables;
528
  delete_while_scanning= 1;
529 530 531 532
  for (JOIN_TAB *tab=join->join_tab, *end=join->join_tab+join->tables;
       tab < end;
       tab++)
  {
533
    if (tab->table->map & tables_to_delete_from)
534
    {
535
      /* We are going to delete from this table */
536
      TABLE *tbl=walk->table=tab->table;
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
537
      walk= walk->next_local;
538
      /* Don't use KEYREAD optimization on this table */
539
      tbl->no_keyread=1;
540 541
      /* Don't use record cache */
      tbl->no_cache= 1;
542
      tbl->used_keys.clear_all();
543
      if (tbl->file->has_transactions())
544
	transactional_tables= 1;
545 546
      else
	normal_tables= 1;
547
    }
548 549 550 551 552 553 554 555 556 557
    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;
    }
558
  }
559
  walk= delete_tables;
560
  tempfiles_ptr= tempfiles;
561 562 563 564 565 566
  if (delete_while_scanning)
  {
    table_being_deleted= delete_tables;
    walk= walk->next_local;
  }
  for (;walk ;walk= walk->next_local)
567 568
  {
    TABLE *table=walk->table;
569 570
    *tempfiles_ptr++= new Unique (refpos_order_cmp,
				  (void *) table->file,
571 572
				  table->file->ref_length,
				  MEM_STRIP_BUF_SIZE);
573
  }
pem@mysql.com's avatar
pem@mysql.com committed
574
  init_ftfuncs(thd, thd->lex->current_select, 1);
575
  DBUG_RETURN(thd->is_fatal_error != 0);
576
}
577

578

579 580
multi_delete::~multi_delete()
{
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
581 582 583
  for (table_being_deleted= delete_tables;
       table_being_deleted;
       table_being_deleted= table_being_deleted->next_local)
584
  {
585 586 587
    TABLE *table= table_being_deleted->table;
    free_io_cache(table);                       // Alloced by unique
    table->no_keyread=0;
588
  }
589

590
  for (uint counter= 0; counter < num_of_tables; counter++)
591
  {
592
    if (tempfiles[counter])
593 594
      delete tempfiles[counter];
  }
595 596
}

597

598 599
bool multi_delete::send_data(List<Item> &values)
{
600 601
  int secure_counter= delete_while_scanning ? -1 : 0;
  TABLE_LIST *del_table;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
602 603
  DBUG_ENTER("multi_delete::send_data");

604 605 606
  for (del_table= delete_tables;
       del_table;
       del_table= del_table->next_local, secure_counter++)
607
  {
608
    TABLE *table= del_table->table;
609 610 611 612 613 614

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

617
    if (secure_counter < 0)
618
    {
619 620
      /* We are scanning the current table */
      DBUG_ASSERT(del_table == table_being_deleted);
621 622 623 624
      if (table->triggers &&
          table->triggers->process_triggers(thd, TRG_EVENT_DELETE,
                                            TRG_ACTION_BEFORE, FALSE))
	DBUG_RETURN(1);
625
      table->status|= STATUS_DELETED;
626
      if (!(error=table->file->ha_delete_row(table->record[0])))
627
      {
628
	deleted++;
629 630 631 632 633
        if (table->triggers &&
            table->triggers->process_triggers(thd, TRG_EVENT_DELETE,
                                              TRG_ACTION_AFTER, FALSE))
	  DBUG_RETURN(1);
      }
634
      else
635
      {
636
	table->file->print_error(error,MYF(0));
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
637
	DBUG_RETURN(1);
638 639 640 641
      }
    }
    else
    {
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
642
      error=tempfiles[secure_counter]->unique_add((char*) table->file->ref);
643 644
      if (error)
      {
645
	error= 1;                               // Fatal error
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
646
	DBUG_RETURN(1);
647
      }
648 649
    }
  }
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
650
  DBUG_RETURN(0);
651 652
}

653

654 655
void multi_delete::send_error(uint errcode,const char *err)
{
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
656 657
  DBUG_ENTER("multi_delete::send_error");

658
  /* First send error what ever it is ... */
659
  my_message(errcode, err, MYF(0));
660

661 662
  /* If nothing deleted return */
  if (!deleted)
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
663
    DBUG_VOID_RETURN;
664

665
  /* Something already deleted so we have to invalidate cache */
666 667
  query_cache_invalidate3(thd, delete_tables, 1);

668
  /*
669 670
    If rows from the first table only has been deleted and it is
    transactional, just do rollback.
671 672 673
    The same if all tables are transactional, regardless of where we are.
    In all other cases do attempt deletes ...
  */
674 675 676
  if ((table_being_deleted == delete_tables &&
       table_being_deleted->table->file->has_transactions()) ||
      !normal_tables)
677
    ha_rollback_stmt(thd);
678
  else if (do_delete)
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
679
  {
680 681 682 683 684 685
    /*
      We have to execute the recorded do_deletes() and write info into the
      error log
    */
    error= 1;
    send_eof();
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
686 687
  }
  DBUG_VOID_RETURN;
688 689
}

690

691 692 693 694 695 696 697
/*
  Do delete from other tables.
  Returns values:
	0 ok
	1 error
*/

698
int multi_delete::do_deletes()
699
{
mronstrom@mysql.com[mikron]'s avatar
mronstrom@mysql.com[mikron] committed
700 701
  int local_error= 0, counter= 0, error;
  bool will_batch;
702
  DBUG_ENTER("do_deletes");
703
  DBUG_ASSERT(do_delete);
704

705
  do_delete= 0;                                 // Mark called
706
  if (!found)
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
707
    DBUG_RETURN(0);
708 709 710 711 712

  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
713
       table_being_deleted= table_being_deleted->next_local, counter++)
714
  { 
715 716 717
    TABLE *table = table_being_deleted->table;
    if (tempfiles[counter]->get(table))
    {
718
      local_error=1;
719 720 721 722
      break;
    }

    READ_RECORD	info;
723 724 725 726 727 728
    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;
mronstrom@mysql.com's avatar
mronstrom@mysql.com committed
729
    will_batch= !table->file->start_bulk_delete();
monty@butch's avatar
merge  
monty@butch committed
730
    while (!(local_error=info.read_record(&info)) && !thd->killed)
731
    {
732 733 734 735 736 737 738
      if (table->triggers &&
          table->triggers->process_triggers(thd, TRG_EVENT_DELETE,
                                            TRG_ACTION_BEFORE, FALSE))
      {
        local_error= 1;
        break;
      }
739
      if ((local_error=table->file->ha_delete_row(table->record[0])))
740
      {
741
	table->file->print_error(local_error,MYF(0));
742
	break;
743
      }
744
      deleted++;
745 746 747 748 749 750 751
      if (table->triggers &&
          table->triggers->process_triggers(thd, TRG_EVENT_DELETE,
                                            TRG_ACTION_AFTER, FALSE))
      {
        local_error= 1;
        break;
      }
752
    }
mronstrom@mysql.com's avatar
mronstrom@mysql.com committed
753
    if (will_batch && (error= table->file->end_bulk_delete()))
mronstrom@mysql.com[mikron]'s avatar
mronstrom@mysql.com[mikron] committed
754 755 756 757 758 759 760
    {
      if (!local_error)
      {
        local_error= error;
        table->file->print_error(local_error,MYF(0));
      }
    }
761
    end_read_record(&info);
762 763
    if (thd->killed && !local_error)
      local_error= 1;
764 765
    if (local_error == -1)				// End of file
      local_error = 0;
766
  }
767
  DBUG_RETURN(local_error);
768 769
}

770

771
/*
772 773
  Send ok to the client

774 775 776 777
  return:  0 sucess
	   1 error
*/

778 779
bool multi_delete::send_eof()
{
780
  thd->proc_info="deleting from reference tables";
781 782

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

785 786 787
  /* compute a total error to know if something failed */
  local_error= local_error || error;

788
  /* reset used flags */
789
  thd->proc_info="end";
790

791 792 793 794
  /*
    We must invalidate the query cache before binlog writing and
    ha_autocommit_...
  */
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
795
  if (deleted)
monty@mysql.com's avatar
monty@mysql.com committed
796
  {
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
797
    query_cache_invalidate3(thd, delete_tables, 1);
monty@mysql.com's avatar
monty@mysql.com committed
798
  }
799

800
  if ((local_error == 0) || (deleted && normal_tables))
801
  {
802 803
    if (mysql_bin_log.is_open())
    {
804
      if (local_error == 0)
guilhem@mysql.com's avatar
guilhem@mysql.com committed
805
        thd->clear_error();
806 807 808 809 810
      if (thd->binlog_query(THD::ROW_QUERY_TYPE,
                            thd->query, thd->query_length,
                            transactional_tables, FALSE) &&
          !normal_tables)
      {
811
	local_error=1;  // Log write failed: roll back the SQL statement
812
      }
813
    }
814
    if (!transactional_tables)
815
      thd->options|=OPTION_STATUS_NO_TRANS_UPDATE;
816
  }
817
  /* Commit or rollback the current SQL statement */
818 819 820 821
  if (transactional_tables)
    if (ha_autocommit_or_rollback(thd,local_error > 0))
      local_error=1;

822
  if (!local_error)
823 824
  {
    thd->row_count_func= deleted;
monty@mashka.mysql.fi's avatar
monty@mashka.mysql.fi committed
825
    ::send_ok(thd, deleted);
826
  }
827 828
  return 0;
}
829 830 831


/***************************************************************************
832
  TRUNCATE TABLE
833 834 835 836 837 838 839 840 841 842 843
****************************************************************************/

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

847
bool mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok)
848 849 850
{
  HA_CREATE_INFO create_info;
  char path[FN_REFLEN];
851
  TABLE *table;
852
  bool error;
853
  uint closed_log_tables= 0, lock_logger= 0;
854
  uint path_length;
855 856
  DBUG_ENTER("mysql_truncate");

857
  bzero((char*) &create_info,sizeof(create_info));
858
  /* If it is a temporary table, close and regenerate it */
859
  if (!dont_send_ok && (table= find_temporary_table(thd, table_list)))
860
  {
861
    handlerton *table_type= table->s->db_type;
862
    TABLE_SHARE *share= table->s;
863
    if (!ha_check_storage_engine_flag(table_type, HTON_CAN_RECREATE))
864
      goto trunc_by_del;
865 866 867 868 869 870

    table->file->info(HA_STATUS_AUTO | HA_STATUS_NO_LOCK);
    
    close_temporary_table(thd, table, 0, 0);    // Don't free share
    ha_create_table(thd, share->normalized_path.str,
                    share->db.str, share->table_name.str, &create_info, 1);
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
871
    // We don't need to call invalidate() because this table is not in cache
872 873 874
    if ((error= (int) !(open_temporary_table(thd, share->path.str,
                                             share->db.str,
					     share->table_name.str, 1))))
875
      (void) rm_temporary_table(table_type, path);
876 877
    free_table_share(share);
    my_free((char*) table,MYF(0));
878
    /*
monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
879 880
      If we return here we will not have logged the truncation to the bin log
      and we will not send_ok() to the client.
881
    */
serg@serg.mylan's avatar
serg@serg.mylan committed
882
    goto end;
883 884
  }

885 886
  path_length= build_table_filename(path, sizeof(path), table_list->db,
                                    table_list->table_name, reg_ext);
887 888 889

  if (!dont_send_ok)
  {
890
    enum legacy_db_type table_type;
891 892
    mysql_frm_type(thd, path, &table_type);
    if (table_type == DB_TYPE_UNKNOWN)
893
    {
894
      my_error(ER_NO_SUCH_TABLE, MYF(0),
895
               table_list->db, table_list->table_name);
896
      DBUG_RETURN(TRUE);
897
    }
898
    if (!ha_check_storage_engine_flag(ha_resolve_by_legacy_type(thd, table_type),
899
                                      HTON_CAN_RECREATE))
900
      goto trunc_by_del;
901

902
    if (lock_and_wait_for_table_name(thd, table_list))
903
      DBUG_RETURN(TRUE);
904 905
  }

906 907 908 909 910 911 912 913
  /* close log tables in use */
  if (!my_strcasecmp(system_charset_info, table_list->db, "mysql"))
  {
    if (!my_strcasecmp(system_charset_info, table_list->table_name,
                       "general_log"))
    {
      lock_logger= 1;
      logger.lock();
914 915
      logger.close_log_table(QUERY_LOG_GENERAL, FALSE);
      closed_log_tables= closed_log_tables | QUERY_LOG_GENERAL;
916 917 918 919 920 921 922
    }
    else
      if (!my_strcasecmp(system_charset_info, table_list->table_name,
                         "slow_log"))
      {
        lock_logger= 1;
        logger.lock();
923 924
        logger.close_log_table(QUERY_LOG_SLOW, FALSE);
        closed_log_tables= closed_log_tables | QUERY_LOG_SLOW;
925 926 927 928 929 930
      }
  }

  // Remove the .frm extension AIX 5.2 64-bit compiler bug (BUG#16155): this
  // crashes, replacement works.  *(path + path_length - reg_ext_length)=
  // '\0';
931
  path[path_length - reg_ext_length] = 0;
932 933
  error= ha_create_table(thd, path, table_list->db, table_list->table_name,
                         &create_info, 1);
934
  query_cache_invalidate3(thd, table_list, 0);
935

936
end:
937
  if (!dont_send_ok)
938
  {
939
    if (!error)
940
    {
941 942
      if (mysql_bin_log.is_open())
      {
943 944
        /*
          TRUNCATE must always be statement-based binlogged (not row-based) so
945
          we don't test current_stmt_binlog_row_based.
946
        */
guilhem@mysql.com's avatar
guilhem@mysql.com committed
947
        thd->clear_error();
948 949
        thd->binlog_query(THD::STMT_QUERY_TYPE,
                          thd->query, thd->query_length, FALSE, FALSE);
950
      }
951
      send_ok(thd);		// This should return record count
952
    }
953
    VOID(pthread_mutex_lock(&LOCK_open));
954
    unlock_table_name(thd, table_list);
955
    VOID(pthread_mutex_unlock(&LOCK_open));
956

957 958
    if (closed_log_tables & QUERY_LOG_SLOW)
      logger.reopen_log_table(QUERY_LOG_SLOW);
959

960 961
    if (closed_log_tables & QUERY_LOG_GENERAL)
      logger.reopen_log_table(QUERY_LOG_GENERAL);
962 963
    if (lock_logger)
      logger.unlock();
964
  }
965
  else if (error)
966 967
  {
    VOID(pthread_mutex_lock(&LOCK_open));
968
    unlock_table_name(thd, table_list);
969 970
    VOID(pthread_mutex_unlock(&LOCK_open));
  }
971
  DBUG_RETURN(error);
972

973
trunc_by_del:
974 975 976 977 978
  /* 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);
979
  mysql_init_select(thd->lex);
980
  thd->clear_current_stmt_binlog_row_based();
981
  error= mysql_delete(thd, table_list, (COND*) 0, (SQL_LIST*) 0,
982
                      HA_POS_ERROR, LL(0), TRUE);
983 984 985
  ha_enable_transaction(thd, TRUE);
  thd->options= save_options;
  DBUG_RETURN(error);
986
}