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

unknown's avatar
unknown 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
unknown's avatar
unknown committed
5
   the Free Software Foundation; version 2 of the License.
unknown's avatar
unknown committed
6

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

unknown's avatar
unknown 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
unknown's avatar
unknown committed
20 21
*/

22 23
#include "mysql_priv.h"
#include "sql_select.h"
24 25
#include "sp_head.h"
#include "sql_trigger.h"
26
#include "sql_handler.h"
unknown's avatar
unknown committed
27

28 29 30 31 32 33 34 35
/**
  Implement DELETE SQL word.

  @note Like implementations of other DDL/DML in MySQL, this function
  relies on the caller to close the thread tables. This is done in the
  end of dispatch_command().
*/

unknown's avatar
unknown committed
36
bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
37
                  SQL_I_List<ORDER> *order, ha_rows limit, ulonglong options,
unknown's avatar
unknown committed
38
                  bool reset_auto_increment)
unknown's avatar
unknown committed
39
{
unknown's avatar
unknown committed
40 41
  bool          will_batch;
  int		error, loc_error;
unknown's avatar
unknown committed
42
  TABLE		*table;
43
  SQL_SELECT	*select=0;
unknown's avatar
unknown committed
44
  READ_RECORD	info;
unknown's avatar
unknown committed
45 46
  bool          using_limit=limit != HA_POS_ERROR;
  bool		transactional_table, safe_update, const_cond;
47
  bool          const_cond_result;
48
  ha_rows	deleted= 0;
49
  bool          triggers_applicable;
50
  uint usable_index= MAX_KEY;
51
  SELECT_LEX   *select_lex= &thd->lex->select_lex;
52
  THD::killed_state killed_status= THD::NOT_KILLED;
unknown's avatar
unknown committed
53
  DBUG_ENTER("mysql_delete");
54
  bool save_binlog_row_based;
unknown's avatar
unknown committed
55

Mats Kindahl's avatar
Mats Kindahl committed
56 57 58 59 60
  THD::enum_binlog_query_type query_type=
    thd->lex->sql_command == SQLCOM_TRUNCATE ?
    THD::STMT_QUERY_TYPE :
    THD::ROW_QUERY_TYPE;

unknown's avatar
unknown committed
61 62
  if (open_and_lock_tables(thd, table_list))
    DBUG_RETURN(TRUE);
63 64 65

  if (mysql_handle_list_of_derived(thd->lex, table_list, DT_MERGE_FOR_INSERT) ||
      mysql_handle_list_of_derived(thd->lex, table_list, DT_PREPARE))
66
    DBUG_RETURN(TRUE);
67

68 69 70 71 72
  if (!table_list->updatable)
  {
     my_error(ER_NON_UPDATABLE_TABLE, MYF(0), table_list->alias, "DELETE");
     DBUG_RETURN(TRUE);
  }
73
  if (!(table= table_list->table) || !table->created)
74
  {
75 76
      my_error(ER_VIEW_DELETE_MERGE_VIEW, MYF(0),
	       table_list->view_db.str, table_list->view_name.str);
unknown's avatar
unknown committed
77
    DBUG_RETURN(TRUE);
78
  }
79
  thd_proc_info(thd, "init");
unknown's avatar
unknown committed
80
  table->map=1;
unknown's avatar
unknown committed
81

unknown's avatar
unknown committed
82 83
  if (mysql_prepare_delete(thd, table_list, &conds))
    DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
84

85 86 87 88 89
  if (thd->lex->current_select->first_cond_optimization)
  {
    thd->lex->current_select->save_leaf_tables(thd);
    thd->lex->current_select->first_cond_optimization= 0;
  }
unknown's avatar
unknown committed
90 91 92 93 94 95 96 97 98 99 100 101 102
  /* 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,
103
                    fields, all_fields, order->first))
unknown's avatar
unknown committed
104 105 106 107 108 109 110
    {
      delete select;
      free_underlaid_joins(thd, &thd->lex->select_lex);
      DBUG_RETURN(TRUE);
    }
  }

111 112 113 114
  /* Apply the IN=>EXISTS transformation to all subqueries and optimize them. */
  if (select_lex->optimize_unflattened_subqueries())
    DBUG_RETURN(TRUE);

115 116 117 118
  const_cond= (!conds || conds->const_item());
  safe_update=test(thd->options & OPTION_SAFE_UPDATES);
  if (safe_update && const_cond)
  {
unknown's avatar
unknown committed
119 120
    my_message(ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE,
               ER(ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE), MYF(0));
unknown's avatar
unknown committed
121
    DBUG_RETURN(TRUE);
122 123
  }

124
  select_lex->no_error= thd->lex->ignore;
125

126 127 128 129 130 131
  const_cond_result= const_cond && (!conds || conds->val_int());
  if (thd->is_error())
  {
    /* Error evaluating val_int(). */
    DBUG_RETURN(TRUE);
  }
132

133 134 135 136
  /*
    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.
137 138 139 140 141 142 143 144 145 146 147 148 149 150

    We implement fast TRUNCATE for InnoDB even if triggers are
    present.  TRUNCATE ignores triggers.

    We can use delete_all_rows() if and only if:
    - We allow new functions (not using option --skip-new), and are
      not in safe mode (not using option --safe-mode)
    - There is no limit clause
    - The condition is constant
    - If there is a condition, then it it produces a non-zero value
    - If the current command is DELETE FROM with no where clause
      (i.e., not TRUNCATE) then:
      - We should not be binlogging this statement row-based, and
      - there should be no delete triggers associated with the table.
151
  */
152
  if (!using_limit && const_cond_result &&
153
      (thd->lex->sql_command == SQLCOM_TRUNCATE ||
154 155
       (!thd->current_stmt_binlog_row_based &&
        !(table->triggers && table->triggers->has_delete_triggers()))))
156
  {
157
    /* Update the table->file->stats.records number */
158
    table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK);
159
    ha_rows const maybe_deleted= table->file->stats.records;
160
    DBUG_PRINT("debug", ("Trying to use delete_all_rows()"));
161
    if (!(error=table->file->ha_delete_all_rows()))
162
    {
Mats Kindahl's avatar
Mats Kindahl committed
163 164 165 166 167
      /*
        If delete_all_rows() is used, it is not possible to log the
        query in row format, so we have to log it in statement format.
      */
      query_type= THD::STMT_QUERY_TYPE;
168
      error= -1;				// ok
169
      deleted= maybe_deleted;
170
      save_binlog_row_based= thd->current_stmt_binlog_row_based;
171 172 173 174 175 176
      goto cleanup;
    }
    if (error != HA_ERR_WRONG_COMMAND)
    {
      table->file->print_error(error,MYF(0));
      error=0;
177
      save_binlog_row_based= thd->current_stmt_binlog_row_based;
178 179 180 181
      goto cleanup;
    }
    /* Handler didn't support fast delete; Delete rows one by one */
  }
182 183 184 185 186 187 188
  if (conds)
  {
    Item::cond_result result;
    conds= remove_eq_conds(thd, conds, &result);
    if (result == Item::COND_FALSE)             // Impossible where
      limit= 0;
  }
189

190 191 192 193 194
#ifdef WITH_PARTITION_STORAGE_ENGINE
  if (prune_partitions(thd, table, conds))
  {
    free_underlaid_joins(thd, select_lex);
    thd->row_count_func= 0;
195
    my_ok(thd, (ha_rows) thd->row_count_func);  // No matching records
196 197 198
    DBUG_RETURN(0);
  }
#endif
199
  /* Update the table->file->stats.records number */
200
  table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK);
201

202
  table->covering_keys.clear_all();
203
  table->quick_keys.clear_all();		// Can't use 'only index'
unknown's avatar
unknown committed
204
  select=make_select(table, 0, 0, conds, 0, &error);
unknown's avatar
unknown committed
205
  if (error)
unknown's avatar
unknown committed
206
    DBUG_RETURN(TRUE);
207
  if ((select && select->check_quick(thd, safe_update, limit)) || !limit)
unknown's avatar
unknown committed
208 209
  {
    delete select;
210
    free_underlaid_joins(thd, select_lex);
211
    thd->row_count_func= 0;
212 213 214 215 216 217
    /* 
      Error was already created by quick select evaluation (check_quick()).
      TODO: Add error code output parameter to Item::val_xxx() methods.
      Currently they rely on the user checking DA for
      errors when unwinding the stack after calling Item::val_xxx().
    */
218
    if (thd->is_error())
219
      DBUG_RETURN(TRUE);
220
    my_ok(thd, (ha_rows) thd->row_count_func);
221 222 223 224 225
    /*
      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.
    */
unknown's avatar
unknown committed
226
    DBUG_RETURN(0);				// Nothing to delete
unknown's avatar
unknown committed
227 228 229
  }

  /* If running in safe sql mode, don't allow updates without keys */
230
  if (table->quick_keys.is_clear_all())
unknown's avatar
unknown committed
231
  {
232
    thd->server_status|=SERVER_QUERY_NO_INDEX_USED;
unknown's avatar
unknown committed
233
    if (safe_update && !using_limit)
234 235
    {
      delete select;
236
      free_underlaid_joins(thd, select_lex);
unknown's avatar
unknown committed
237 238
      my_message(ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE,
                 ER(ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE), MYF(0));
unknown's avatar
unknown committed
239
      DBUG_RETURN(TRUE);
240
    }
unknown's avatar
unknown committed
241
  }
242
  if (options & OPTION_QUICK)
243 244
    (void) table->file->extra(HA_EXTRA_QUICK);

245
  if (order && order->elements)
246
  {
247
    uint         length= 0;
248
    SORT_FIELD  *sortorder;
249
    ha_rows examined_rows;
250
    
251
    if ((!select || table->quick_keys.is_clear_all()) && limit != HA_POS_ERROR)
252
      usable_index= get_index_for_order(table, order->first, limit);
253 254 255 256 257 258

    if (usable_index == MAX_KEY)
    {
      table->sort.io_cache= (IO_CACHE *) my_malloc(sizeof(IO_CACHE),
                                                   MYF(MY_FAE | MY_ZEROFILL));
    
259
      if (!(sortorder= make_unireg_sortorder(order->first,
260
                                             &length, NULL)) ||
unknown's avatar
unknown committed
261
	  (table->sort.found_records = filesort(thd, table, sortorder, length,
262
                                                select, HA_POS_ERROR, 1,
unknown's avatar
unknown committed
263
                                                &examined_rows))
264
	  == HA_POS_ERROR)
265 266 267
      {
        delete select;
        free_underlaid_joins(thd, &thd->lex->select_lex);
unknown's avatar
unknown committed
268
        DBUG_RETURN(TRUE);
269
      }
270
      thd->examined_row_count+= examined_rows;
271 272 273 274
      /*
        Filesort has already found and selected the rows we want to delete,
        so we don't need the where clause
      */
275
      delete select;
276
      free_underlaid_joins(thd, select_lex);
277
      select= 0;
278 279 280
    }
  }

unknown's avatar
unknown committed
281 282 283 284
  /* If quick select is used, initialize it before retrieving rows. */
  if (select && select->quick && select->quick->reset())
  {
    delete select;
285
    free_underlaid_joins(thd, select_lex);
unknown's avatar
unknown committed
286
    DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
287
  }
Michael Widenius's avatar
Michael Widenius committed
288
  if (usable_index == MAX_KEY || (select && select->quick))
289 290 291 292 293 294 295 296
  {
    if (init_read_record(&info, thd, table, select, 1, 1, FALSE))
    {
      delete select;
      free_underlaid_joins(thd, select_lex);
      DBUG_RETURN(TRUE);
    }
  }
297 298 299
  else
    init_read_record_idx(&info, thd, table, 1, usable_index);

300
  init_ftfuncs(thd, select_lex, 1);
301
  thd_proc_info(thd, "updating");
unknown's avatar
unknown committed
302 303 304 305 306 307

  /* NOTE: TRUNCATE must not invoke triggers. */

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

308
  if (triggers_applicable &&
309 310
      table->triggers->has_triggers(TRG_EVENT_DELETE,
                                    TRG_ACTION_AFTER))
311
  {
312 313 314 315 316 317 318
    /*
      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);
    will_batch= FALSE;
319
  }
320 321
  else
    will_batch= !table->file->start_bulk_delete();
322 323


324 325
  table->mark_columns_needed_for_delete();

326 327 328 329 330
  save_binlog_row_based= thd->current_stmt_binlog_row_based;
  if (thd->lex->sql_command == SQLCOM_TRUNCATE &&
      thd->current_stmt_binlog_row_based)
    thd->clear_current_stmt_binlog_row_based();

unknown's avatar
unknown committed
331
  while (!(error=info.read_record(&info)) && !thd->killed &&
332
	 ! thd->is_error())
unknown's avatar
unknown committed
333
  {
Igor Babaev's avatar
Igor Babaev committed
334
    update_virtual_fields(thd, table);
335
    thd->examined_row_count++;
336
    // thd->is_error() is tested to disallow delete row on error
337
    if (!select || select->skip_record(thd) > 0)
unknown's avatar
unknown committed
338
    {
339
      if (triggers_applicable &&
unknown's avatar
unknown committed
340 341 342 343 344 345
          table->triggers->process_triggers(thd, TRG_EVENT_DELETE,
                                            TRG_ACTION_BEFORE, FALSE))
      {
        error= 1;
        break;
      }
346

347
      if (!(error= table->file->ha_delete_row(table->record[0])))
unknown's avatar
unknown committed
348 349
      {
	deleted++;
350
        if (triggers_applicable &&
unknown's avatar
unknown committed
351 352 353 354 355 356
            table->triggers->process_triggers(thd, TRG_EVENT_DELETE,
                                              TRG_ACTION_AFTER, FALSE))
        {
          error= 1;
          break;
        }
unknown's avatar
unknown committed
357 358 359 360 361 362 363 364 365
	if (!--limit && using_limit)
	{
	  error= -1;
	  break;
	}
      }
      else
      {
	table->file->print_error(error,MYF(0));
unknown's avatar
unknown committed
366 367 368 369 370 371 372 373
	/*
	  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.
	*/
374 375 376 377 378
        if (!select_lex->no_error)
        {
          error= 1;
          break;
        }
unknown's avatar
unknown committed
379 380
      }
    }
unknown's avatar
unknown committed
381 382
    else
      table->file->unlock_row();  // Row failed selection, release lock on it
unknown's avatar
unknown committed
383
  }
384
  killed_status= thd->killed;
unknown's avatar
unknown committed
385
  if (killed_status != THD::NOT_KILLED || thd->is_error())
386
    error= 1;					// Aborted
unknown's avatar
unknown committed
387 388 389 390 391 392
  if (will_batch && (loc_error= table->file->end_bulk_delete()))
  {
    if (error != 1)
      table->file->print_error(loc_error,MYF(0));
    error=1;
  }
393
  thd_proc_info(thd, "end");
unknown's avatar
unknown committed
394
  end_read_record(&info);
395
  if (options & OPTION_QUICK)
396
    (void) table->file->extra(HA_EXTRA_NORMAL);
397

unknown's avatar
unknown committed
398
  if (reset_auto_increment && (error < 0))
399 400 401 402 403
  {
    /*
      We're really doing a truncate and need to reset the table's
      auto-increment counter.
    */
404
    int error2= table->file->ha_reset_auto_increment(0);
405 406 407 408

    if (error2 && (error2 != HA_ERR_WRONG_COMMAND))
    {
      table->file->print_error(error2, MYF(0));
unknown's avatar
unknown committed
409
      error= 1;
410 411 412
    }
  }

413
cleanup:
unknown's avatar
unknown committed
414 415 416 417 418 419 420 421 422
  /*
    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);
  }

423 424 425 426 427 428
  if (thd->lex->current_select->first_cond_optimization)
  {
    thd->lex->current_select->save_leaf_tables(thd);
    thd->lex->current_select->first_cond_optimization= 0;
  }

unknown's avatar
unknown committed
429
  delete select;
430
  transactional_table= table->file->has_transactions();
431

unknown's avatar
unknown committed
432 433 434
  if (!transactional_table && deleted > 0)
    thd->transaction.stmt.modified_non_trans_table= TRUE;
  
435
  /* See similar binlogging code in sql_update.cc, for comments */
436
  if ((error < 0) || thd->transaction.stmt.modified_non_trans_table)
unknown's avatar
unknown committed
437
  {
438 439 440 441
    if (mysql_bin_log.is_open() &&
        !(thd->lex->sql_command == SQLCOM_TRUNCATE &&
          thd->current_stmt_binlog_row_based &&
          find_temporary_table(thd, table_list)))
442
    {
Mats Kindahl's avatar
Mats Kindahl committed
443 444 445 446 447
      bool const is_trans=
        thd->lex->sql_command == SQLCOM_TRUNCATE ?
        FALSE :
        transactional_table;

448
      int errcode= 0;
449
      if (error < 0)
unknown's avatar
unknown committed
450
        thd->clear_error();
451 452 453
      else
        errcode= query_error_code(thd, killed_status == THD::NOT_KILLED);
      
454
      /*
455 456 457 458
        [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.
Mats Kindahl's avatar
Mats Kindahl committed
459 460 461

        Note that TRUNCATE TABLE is not transactional and should
        therefore be treated as a DDL.
462
      */
Mats Kindahl's avatar
Mats Kindahl committed
463
      int log_result= thd->binlog_query(query_type,
464
                                        thd->query(), thd->query_length(),
465
                                        is_trans, FALSE, errcode);
466

467
      if (log_result)
468
      {
469
	error=1;
470
      }
471
    }
unknown's avatar
unknown committed
472 473
    if (thd->transaction.stmt.modified_non_trans_table)
      thd->transaction.all.modified_non_trans_table= TRUE;
unknown's avatar
unknown committed
474
  }
475
  thd->current_stmt_binlog_row_based= save_binlog_row_based;
unknown's avatar
unknown committed
476
  DBUG_ASSERT(transactional_table || !deleted || thd->transaction.stmt.modified_non_trans_table);
477
  free_underlaid_joins(thd, select_lex);
478 479
  if (error < 0 || 
      (thd->lex->ignore && !thd->is_error() && !thd->is_fatal_error))
unknown's avatar
unknown committed
480
  {
481 482 483 484 485
    /*
      If a TRUNCATE TABLE was issued, the number of rows should be reported as
      zero since the exact number is unknown.
    */
    thd->row_count_func= reset_auto_increment ? 0 : deleted;
486
    my_ok(thd, (ha_rows) thd->row_count_func);
unknown's avatar
unknown committed
487
    DBUG_PRINT("info",("%ld records deleted",(long) deleted));
unknown's avatar
unknown committed
488
  }
489
  DBUG_RETURN(error >= 0 || thd->is_error());
unknown's avatar
unknown committed
490 491 492 493 494 495 496 497 498
}


/*
  Prepare items in DELETE statement

  SYNOPSIS
    mysql_prepare_delete()
    thd			- thread handler
unknown's avatar
VIEW  
unknown committed
499
    table_list		- global/local table list
unknown's avatar
unknown committed
500 501 502
    conds		- conditions

  RETURN VALUE
unknown's avatar
unknown committed
503 504
    FALSE OK
    TRUE  error
unknown's avatar
unknown committed
505
*/
506
int mysql_prepare_delete(THD *thd, TABLE_LIST *table_list, Item **conds)
unknown's avatar
unknown committed
507
{
508
  Item *fake_conds= 0;
509
  SELECT_LEX *select_lex= &thd->lex->select_lex;
unknown's avatar
unknown committed
510
  DBUG_ENTER("mysql_prepare_delete");
511
  List<Item> all_fields;
unknown's avatar
unknown committed
512

513 514 515 516 517 518 519 520 521 522 523 524 525
  /*
    Statement-based replication of DELETE ... LIMIT is not safe as order of
    rows is not defined, so in mixed mode we go to row-based.

    Note that we may consider a statement as safe if ORDER BY primary_key
    is present. However it may confuse users to see very similiar statements
    replicated differently.
  */
  if (thd->lex->current_select->select_limit)
  {
    thd->lex->set_stmt_unsafe();
    thd->set_current_stmt_binlog_row_based_if_mixed();
  }
unknown's avatar
unknown committed
526
  thd->lex->allow_sum_func= 0;
527 528
  if (setup_tables_and_check_access(thd, &thd->lex->select_lex.context,
                                    &thd->lex->select_lex.top_join_list,
529
                                    table_list, 
530 531
                                    select_lex->leaf_tables, FALSE, 
                                    DELETE_ACL, SELECT_ACL, TRUE) ||
532
      setup_conds(thd, table_list, select_lex->leaf_tables, conds) ||
533
      setup_ftfuncs(select_lex))
unknown's avatar
unknown committed
534
    DBUG_RETURN(TRUE);
unknown's avatar
VIEW  
unknown committed
535
  if (!table_list->updatable || check_key_in_view(thd, table_list))
unknown's avatar
unknown committed
536
  {
unknown's avatar
unknown committed
537
    my_error(ER_NON_UPDATABLE_TABLE, MYF(0), table_list->alias, "DELETE");
unknown's avatar
unknown committed
538
    DBUG_RETURN(TRUE);
unknown's avatar
unknown committed
539
  }
540
  {
541
    TABLE_LIST *duplicate;
542
    if ((duplicate= unique_table(thd, table_list, table_list->next_global, 0)))
543
    {
unknown's avatar
unknown committed
544
      update_non_unique_table_error(table_list, "DELETE", duplicate);
545 546
      DBUG_RETURN(TRUE);
    }
547
  }
548 549 550

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

553
  select_lex->fix_prepare_information(thd, conds, &fake_conds);
unknown's avatar
unknown committed
554
  DBUG_RETURN(FALSE);
unknown's avatar
unknown committed
555 556 557
}


558
/***************************************************************************
559
  Delete multiple tables from join 
560 561
***************************************************************************/

562
#define MEM_STRIP_BUF_SIZE current_thd->variables.sortbuff_size
unknown's avatar
unknown committed
563

564
extern "C" int refpos_order_cmp(void* arg, const void *a,const void *b)
565
{
566
  handler *file= (handler*)arg;
567
  return file->cmp_ref((const uchar*)a, (const uchar*)b);
568
}
569

unknown's avatar
VIEW  
unknown committed
570 571 572 573 574 575 576 577
/*
  make delete specific preparation and checks after opening tables

  SYNOPSIS
    mysql_multi_delete_prepare()
    thd         thread handler

  RETURN
unknown's avatar
unknown committed
578 579
    FALSE OK
    TRUE  Error
unknown's avatar
VIEW  
unknown committed
580 581
*/

582
int mysql_multi_delete_prepare(THD *thd)
unknown's avatar
VIEW  
unknown committed
583 584
{
  LEX *lex= thd->lex;
585
  TABLE_LIST *aux_tables= lex->auxiliary_table_list.first;
unknown's avatar
VIEW  
unknown committed
586 587 588
  TABLE_LIST *target_tbl;
  DBUG_ENTER("mysql_multi_delete_prepare");

589 590 591 592 593
  TABLE_LIST *tables= lex->query_tables;
  if (mysql_handle_derived(lex, DT_INIT) ||
      mysql_handle_list_of_derived(lex, tables, DT_MERGE_FOR_INSERT) ||
      mysql_handle_list_of_derived(lex, tables, DT_PREPARE))
    DBUG_RETURN(TRUE);
unknown's avatar
VIEW  
unknown committed
594 595 596 597 598 599
  /*
    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
  */
600 601
  if (setup_tables_and_check_access(thd, &thd->lex->select_lex.context,
                                    &thd->lex->select_lex.top_join_list,
602
                                    lex->query_tables,
603 604
                                    lex->select_lex.leaf_tables, FALSE, 
                                    DELETE_ACL, SELECT_ACL, TRUE))
unknown's avatar
unknown committed
605
    DBUG_RETURN(TRUE);
unknown's avatar
VIEW  
unknown committed
606

607 608 609 610 611 612

  /*
    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;
unknown's avatar
VIEW  
unknown committed
613 614 615 616 617
  /* 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)
  {
618

619 620
    if (!(target_tbl->table= target_tbl->correspondent_table->table))
    {
621 622 623 624
       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);
625 626
    }

unknown's avatar
VIEW  
unknown committed
627 628 629
    if (!target_tbl->correspondent_table->updatable ||
        check_key_in_view(thd, target_tbl->correspondent_table))
    {
630
      my_error(ER_NON_UPDATABLE_TABLE, MYF(0),
631
               target_tbl->table_name, "DELETE");
unknown's avatar
unknown committed
632
      DBUG_RETURN(TRUE);
unknown's avatar
VIEW  
unknown committed
633 634
    }
    /*
635 636
      Check that table from which we delete is not used somewhere
      inside subqueries/view.
unknown's avatar
VIEW  
unknown committed
637 638
    */
    {
639
      TABLE_LIST *duplicate;
640
      if ((duplicate= unique_table(thd, target_tbl->correspondent_table,
641
                                   lex->query_tables, 0)))
642 643 644 645 646
      {
        update_non_unique_table_error(target_tbl->correspondent_table,
                                      "DELETE", duplicate);
        DBUG_RETURN(TRUE);
      }
unknown's avatar
VIEW  
unknown committed
647 648
    }
  }
649 650 651 652 653
  /*
    Reset the exclude flag to false so it doesn't interfare
    with further calls to unique_table
  */
  lex->select_lex.exclude_from_table_unique_test= FALSE;
unknown's avatar
unknown committed
654
  DBUG_RETURN(FALSE);
unknown's avatar
VIEW  
unknown committed
655 656 657
}


658 659
multi_delete::multi_delete(TABLE_LIST *dt, uint num_of_tables_arg)
  : delete_tables(dt), deleted(0), found(0),
unknown's avatar
unknown committed
660
    num_of_tables(num_of_tables_arg), error(0),
661
    do_delete(0), transactional_tables(0), normal_tables(0), error_handled(0)
662
{
663
  tempfiles= (Unique **) sql_calloc(sizeof(Unique *) * num_of_tables);
unknown's avatar
unknown committed
664 665 666 667
}


int
668
multi_delete::prepare(List<Item> &values, SELECT_LEX_UNIT *u)
unknown's avatar
unknown committed
669 670
{
  DBUG_ENTER("multi_delete::prepare");
671
  unit= u;
unknown's avatar
unknown committed
672
  do_delete= 1;
673
  thd_proc_info(thd, "deleting from main table");
674 675 676 677 678 679
  SELECT_LEX *select_lex= u->first_select();
  if (select_lex->first_cond_optimization)
  {
    if (select_lex->handle_derived(thd->lex, DT_MERGE))
      DBUG_RETURN(TRUE);
  }
680 681 682
  DBUG_RETURN(0);
}

unknown's avatar
unknown committed
683

unknown's avatar
unknown committed
684
bool
unknown's avatar
unknown committed
685 686
multi_delete::initialize_tables(JOIN *join)
{
687
  TABLE_LIST *walk;
unknown's avatar
unknown committed
688 689 690 691 692 693
  Unique **tempfiles_ptr;
  DBUG_ENTER("initialize_tables");

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

694
  table_map tables_to_delete_from=0;
695
  delete_while_scanning= 1;
unknown's avatar
VIEW  
unknown committed
696
  for (walk= delete_tables; walk; walk= walk->next_local)
697
  {
698
    tables_to_delete_from|= walk->table->map;
699 700 701 702 703 704 705 706 707 708 709 710
    if (delete_while_scanning &&
        unique_table(thd, walk, join->tables_list, false))
    {
      /*
        If the table we are going to delete from appears
        in join, we need to defer delete. So the delete
        doesn't interfers with the scaning of results.
      */
      delete_while_scanning= 0;
    }
  }

unknown's avatar
unknown committed
711

712
  walk= delete_tables;
unknown's avatar
unknown committed
713 714 715 716
  for (JOIN_TAB *tab=join->join_tab, *end=join->join_tab+join->tables;
       tab < end;
       tab++)
  {
717
    if (tab->table->map & tables_to_delete_from)
unknown's avatar
unknown committed
718
    {
719
      /* We are going to delete from this table */
720
      TABLE *tbl=walk->table=tab->table;
unknown's avatar
VIEW  
unknown committed
721
      walk= walk->next_local;
unknown's avatar
unknown committed
722
      /* Don't use KEYREAD optimization on this table */
723
      tbl->no_keyread=1;
724 725
      /* Don't use record cache */
      tbl->no_cache= 1;
726
      tbl->covering_keys.clear_all();
727
      if (tbl->file->has_transactions())
unknown's avatar
unknown committed
728
	transactional_tables= 1;
729 730
      else
	normal_tables= 1;
731 732
      if (tbl->triggers &&
          tbl->triggers->has_triggers(TRG_EVENT_DELETE,
733
                                      TRG_ACTION_AFTER))
734 735 736 737 738 739 740
      {
	/*
          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);
741
      }
742 743
      tbl->prepare_for_position();
      tbl->mark_columns_needed_for_delete();
unknown's avatar
unknown committed
744
    }
745 746 747 748 749 750 751 752 753 754
    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;
    }
unknown's avatar
unknown committed
755
  }
756
  walk= delete_tables;
unknown's avatar
unknown committed
757
  tempfiles_ptr= tempfiles;
758 759 760 761 762 763
  if (delete_while_scanning)
  {
    table_being_deleted= delete_tables;
    walk= walk->next_local;
  }
  for (;walk ;walk= walk->next_local)
764 765
  {
    TABLE *table=walk->table;
766 767
    *tempfiles_ptr++= new Unique (refpos_order_cmp,
				  (void *) table->file,
unknown's avatar
unknown committed
768 769
				  table->file->ref_length,
				  MEM_STRIP_BUF_SIZE);
770
  }
unknown's avatar
unknown committed
771
  init_ftfuncs(thd, thd->lex->current_select, 1);
772
  DBUG_RETURN(thd->is_fatal_error != 0);
unknown's avatar
unknown committed
773
}
unknown's avatar
unknown committed
774

775

776 777
multi_delete::~multi_delete()
{
unknown's avatar
VIEW  
unknown committed
778 779 780
  for (table_being_deleted= delete_tables;
       table_being_deleted;
       table_being_deleted= table_being_deleted->next_local)
781
  {
782 783
    TABLE *table= table_being_deleted->table;
    table->no_keyread=0;
784
  }
785

786
  for (uint counter= 0; counter < num_of_tables; counter++)
unknown's avatar
unknown committed
787
  {
788
    if (tempfiles[counter])
unknown's avatar
unknown committed
789 790
      delete tempfiles[counter];
  }
791 792
}

unknown's avatar
unknown committed
793

794
int multi_delete::send_data(List<Item> &values)
795
{
796 797
  int secure_counter= delete_while_scanning ? -1 : 0;
  TABLE_LIST *del_table;
unknown's avatar
unknown committed
798 799
  DBUG_ENTER("multi_delete::send_data");

800 801
  bool ignore= thd->lex->current_select->no_error;

802 803 804
  for (del_table= delete_tables;
       del_table;
       del_table= del_table->next_local, secure_counter++)
805
  {
806
    TABLE *table= del_table->table;
unknown's avatar
unknown committed
807 808 809 810 811 812

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

unknown's avatar
unknown committed
815
    if (secure_counter < 0)
816
    {
817 818
      /* We are scanning the current table */
      DBUG_ASSERT(del_table == table_being_deleted);
unknown's avatar
unknown committed
819 820 821
      if (table->triggers &&
          table->triggers->process_triggers(thd, TRG_EVENT_DELETE,
                                            TRG_ACTION_BEFORE, FALSE))
unknown's avatar
unknown committed
822
        DBUG_RETURN(1);
unknown's avatar
unknown committed
823
      table->status|= STATUS_DELETED;
824
      if (!(error=table->file->ha_delete_row(table->record[0])))
unknown's avatar
unknown committed
825
      {
unknown's avatar
unknown committed
826 827 828
        deleted++;
        if (!table->file->has_transactions())
          thd->transaction.stmt.modified_non_trans_table= TRUE;
unknown's avatar
unknown committed
829 830 831
        if (table->triggers &&
            table->triggers->process_triggers(thd, TRG_EVENT_DELETE,
                                              TRG_ACTION_AFTER, FALSE))
unknown's avatar
unknown committed
832
          DBUG_RETURN(1);
unknown's avatar
unknown committed
833
      }
834
      else if (!ignore)
835
      {
836 837 838 839
        /*
          If the IGNORE option is used errors caused by ha_delete_row don't
          have to stop the iteration.
        */
unknown's avatar
unknown committed
840 841
        table->file->print_error(error,MYF(0));
        DBUG_RETURN(1);
unknown's avatar
unknown committed
842 843 844 845
      }
    }
    else
    {
unknown's avatar
unknown committed
846
      error=tempfiles[secure_counter]->unique_add((char*) table->file->ref);
unknown's avatar
unknown committed
847 848
      if (error)
      {
849
	error= 1;                               // Fatal error
unknown's avatar
unknown committed
850
	DBUG_RETURN(1);
unknown's avatar
unknown committed
851
      }
852 853
    }
  }
unknown's avatar
unknown committed
854
  DBUG_RETURN(0);
855 856
}

857

858 859
void multi_delete::send_error(uint errcode,const char *err)
{
unknown's avatar
unknown committed
860 861
  DBUG_ENTER("multi_delete::send_error");

unknown's avatar
unknown committed
862
  /* First send error what ever it is ... */
unknown's avatar
unknown committed
863
  my_message(errcode, err, MYF(0));
unknown's avatar
unknown committed
864

865 866 867 868 869 870 871 872
  DBUG_VOID_RETURN;
}


void multi_delete::abort()
{
  DBUG_ENTER("multi_delete::abort");

873 874
  /* the error was handled or nothing deleted and no side effects return */
  if (error_handled ||
875
      (!thd->transaction.stmt.modified_non_trans_table && !deleted))
unknown's avatar
unknown committed
876
    DBUG_VOID_RETURN;
877

878
  /* Something already deleted so we have to invalidate cache */
879 880
  if (deleted)
    query_cache_invalidate3(thd, delete_tables, 1);
881

unknown's avatar
unknown committed
882
  /*
883 884
    If rows from the first table only has been deleted and it is
    transactional, just do rollback.
unknown's avatar
unknown committed
885 886 887
    The same if all tables are transactional, regardless of where we are.
    In all other cases do attempt deletes ...
  */
888 889 890
  if (do_delete && normal_tables &&
      (table_being_deleted != delete_tables ||
       !table_being_deleted->table->file->has_transactions()))
unknown's avatar
unknown committed
891
  {
892 893 894 895 896 897
    /*
      We have to execute the recorded do_deletes() and write info into the
      error log
    */
    error= 1;
    send_eof();
898 899
    DBUG_ASSERT(error_handled);
    DBUG_VOID_RETURN;
unknown's avatar
unknown committed
900
  }
901 902 903 904 905 906 907 908
  
  if (thd->transaction.stmt.modified_non_trans_table)
  {
    /* 
       there is only side effects; to binlog with the error
    */
    if (mysql_bin_log.is_open())
    {
909
      int errcode= query_error_code(thd, thd->killed == THD::NOT_KILLED);
He Zhenxing's avatar
He Zhenxing committed
910 911 912 913
      /* possible error of writing binary log is ignored deliberately */
      (void) thd->binlog_query(THD::ROW_QUERY_TYPE,
                              thd->query(), thd->query_length(),
                              transactional_tables, FALSE, errcode);
914 915
    }
    thd->transaction.all.modified_non_trans_table= true;
unknown's avatar
unknown committed
916 917
  }
  DBUG_VOID_RETURN;
918 919
}

unknown's avatar
unknown committed
920

921

922
/**
unknown's avatar
unknown committed
923
  Do delete from other tables.
924 925 926 927 928 929 930

  @retval 0 ok
  @retval 1 error

  @todo Is there any reason not use the normal nested-loops join? If not, and
  there is no documentation supporting it, this method and callee should be
  removed and there should be hooks within normal execution.
unknown's avatar
unknown committed
931 932
*/

933
int multi_delete::do_deletes()
934
{
935
  DBUG_ENTER("do_deletes");
936
  DBUG_ASSERT(do_delete);
unknown's avatar
unknown committed
937

938
  do_delete= 0;                                 // Mark called
939
  if (!found)
unknown's avatar
unknown committed
940
    DBUG_RETURN(0);
941 942 943 944

  table_being_deleted= (delete_while_scanning ? delete_tables->next_local :
                        delete_tables);
 
945
  for (uint counter= 0; table_being_deleted;
unknown's avatar
VIEW  
unknown committed
946
       table_being_deleted= table_being_deleted->next_local, counter++)
947
  { 
unknown's avatar
unknown committed
948 949
    TABLE *table = table_being_deleted->table;
    if (tempfiles[counter]->get(table))
950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988
      DBUG_RETURN(1);

    int local_error= 
      do_table_deletes(table, thd->lex->current_select->no_error);

    if (thd->killed && !local_error)
      DBUG_RETURN(1);

    if (local_error == -1)				// End of file
      local_error = 0;

    if (local_error)
      DBUG_RETURN(local_error);
  }
  DBUG_RETURN(0);
}


/**
   Implements the inner loop of nested-loops join within multi-DELETE
   execution.

   @param table The table from which to delete.

   @param ignore If used, all non fatal errors will be translated
   to warnings and we should not break the row-by-row iteration.

   @return Status code

   @retval  0 All ok.
   @retval  1 Triggers or handler reported error.
   @retval -1 End of file from handler.
*/
int multi_delete::do_table_deletes(TABLE *table, bool ignore)
{
  int local_error= 0;
  READ_RECORD info;
  ha_rows last_deleted= deleted;
  DBUG_ENTER("do_deletes_for_table");
989 990 991 992

  if (init_read_record(&info, thd, table, NULL, 0, 1, FALSE))
    DBUG_RETURN(1);

993 994 995 996 997 998 999 1000 1001 1002 1003
  /*
    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;
  bool will_batch= !table->file->start_bulk_delete();
  while (!(local_error= info.read_record(&info)) && !thd->killed)
  {
    if (table->triggers &&
        table->triggers->process_triggers(thd, TRG_EVENT_DELETE,
                                          TRG_ACTION_BEFORE, FALSE))
unknown's avatar
unknown committed
1004
    {
1005
      local_error= 1;
unknown's avatar
unknown committed
1006 1007
      break;
    }
1008 1009 1010 1011 1012 1013 1014 1015
      
    local_error= table->file->ha_delete_row(table->record[0]);
    if (local_error && !ignore)
    {
      table->file->print_error(local_error, MYF(0));
      break;
    }
      
1016
    /*
1017 1018 1019
      Increase the reported number of deleted rows only if no error occurred
      during ha_delete_row.
      Also, don't execute the AFTER trigger if the row operation failed.
1020
    */
1021
    if (!local_error)
unknown's avatar
unknown committed
1022
    {
1023
      deleted++;
unknown's avatar
unknown committed
1024 1025
      if (table->triggers &&
          table->triggers->process_triggers(thd, TRG_EVENT_DELETE,
1026
                                            TRG_ACTION_AFTER, FALSE))
unknown's avatar
unknown committed
1027 1028 1029 1030
      {
        local_error= 1;
        break;
      }
1031
    }
1032 1033 1034 1035 1036
  }
  if (will_batch)
  {
    int tmp_error= table->file->end_bulk_delete();
    if (tmp_error && !local_error)
unknown's avatar
unknown committed
1037
    {
1038 1039
      local_error= tmp_error;
      table->file->print_error(local_error, MYF(0));
unknown's avatar
unknown committed
1040
    }
1041
  }
1042 1043 1044 1045 1046
  if (last_deleted != deleted && !table->file->has_transactions())
    thd->transaction.stmt.modified_non_trans_table= TRUE;

  end_read_record(&info);

1047
  DBUG_RETURN(local_error);
1048 1049
}

1050
/*
1051 1052
  Send ok to the client

1053 1054 1055 1056
  return:  0 sucess
	   1 error
*/

1057 1058
bool multi_delete::send_eof()
{
1059
  THD::killed_state killed_status= THD::NOT_KILLED;
1060
  thd_proc_info(thd, "deleting from reference tables");
1061 1062

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

1065 1066
  /* compute a total error to know if something failed */
  local_error= local_error || error;
1067
  killed_status= (local_error == 0)? THD::NOT_KILLED : thd->killed;
unknown's avatar
unknown committed
1068
  /* reset used flags */
1069
  thd_proc_info(thd, "end");
unknown's avatar
unknown committed
1070

1071 1072 1073 1074
  /*
    We must invalidate the query cache before binlog writing and
    ha_autocommit_...
  */
unknown's avatar
unknown committed
1075
  if (deleted)
unknown's avatar
unknown committed
1076
  {
unknown's avatar
unknown committed
1077
    query_cache_invalidate3(thd, delete_tables, 1);
unknown's avatar
unknown committed
1078
  }
1079
  if ((local_error == 0) || thd->transaction.stmt.modified_non_trans_table)
1080
  {
unknown's avatar
unknown committed
1081 1082
    if (mysql_bin_log.is_open())
    {
1083
      int errcode= 0;
1084
      if (local_error == 0)
unknown's avatar
unknown committed
1085
        thd->clear_error();
1086 1087
      else
        errcode= query_error_code(thd, killed_status == THD::NOT_KILLED);
1088
      if (thd->binlog_query(THD::ROW_QUERY_TYPE,
1089
                            thd->query(), thd->query_length(),
1090
                            transactional_tables, FALSE, errcode) &&
1091 1092
          !normal_tables)
      {
1093
	local_error=1;  // Log write failed: roll back the SQL statement
1094
      }
unknown's avatar
unknown committed
1095
    }
unknown's avatar
unknown committed
1096 1097
    if (thd->transaction.stmt.modified_non_trans_table)
      thd->transaction.all.modified_non_trans_table= TRUE;
1098
  }
1099 1100
  if (local_error != 0)
    error_handled= TRUE; // to force early leave from ::send_error()
unknown's avatar
unknown committed
1101

unknown's avatar
unknown committed
1102
  if (!local_error)
1103 1104
  {
    thd->row_count_func= deleted;
1105
    ::my_ok(thd, (ha_rows) thd->row_count_func);
1106
  }
1107 1108
  return 0;
}
1109 1110 1111


/***************************************************************************
1112
  TRUNCATE TABLE
1113 1114
****************************************************************************/

1115 1116 1117 1118 1119 1120 1121
/*
  Row-by-row truncation if the engine does not support table recreation.
  Probably a InnoDB table.
*/

static bool mysql_truncate_by_delete(THD *thd, TABLE_LIST *table_list)
{
1122
  bool error;
1123 1124 1125 1126 1127 1128 1129 1130 1131 1132
  DBUG_ENTER("mysql_truncate_by_delete");
  table_list->lock_type= TL_WRITE;
  mysql_init_select(thd->lex);
  error= mysql_delete(thd, table_list, NULL, NULL, HA_POS_ERROR, LL(0), TRUE);
  ha_autocommit_or_rollback(thd, error);
  end_trans(thd, error ? ROLLBACK : COMMIT);
  DBUG_RETURN(error);
}


1133 1134 1135 1136 1137 1138 1139 1140 1141
/*
  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
1142
  - If we want to have a name lock on the table on exit without errors.
1143 1144
*/

unknown's avatar
unknown committed
1145
bool mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok)
1146 1147
{
  HA_CREATE_INFO create_info;
1148
  char path[FN_REFLEN + 1];
unknown's avatar
unknown committed
1149
  TABLE *table;
1150
  TABLE_LIST *tbl;
unknown's avatar
unknown committed
1151
  bool error;
1152
  uint path_length;
1153
  bool is_temporary_table= false;
1154 1155
  DBUG_ENTER("mysql_truncate");

1156
  bzero((char*) &create_info,sizeof(create_info));
1157

1158 1159 1160
  /* Remove tables from the HANDLER's hash. */
  mysql_ha_rm_tables(thd, table_list, FALSE);

1161
  /* If it is a temporary table, close and regenerate it */
unknown's avatar
unknown committed
1162
  if (!dont_send_ok && (table= find_temporary_table(thd, table_list)))
1163
  {
unknown's avatar
unknown committed
1164
    TABLE_SHARE *share= table->s;
1165
    handlerton *table_type= share->db_type();
Michael Widenius's avatar
Michael Widenius committed
1166 1167
    is_temporary_table= true;

1168
    if (!ha_check_storage_engine_flag(table_type, HTON_CAN_RECREATE))
1169
      goto trunc_by_del;
unknown's avatar
unknown committed
1170

1171 1172 1173
    for (tbl= table_list; tbl; tbl= tbl->next_local)
      tbl->deleting= TRUE; /* to trigger HA_PREPARE_FOR_DROP */

unknown's avatar
unknown committed
1174
    table->file->info(HA_STATUS_AUTO | HA_STATUS_NO_LOCK);
1175

1176
    create_info.options|= HA_LEX_CREATE_TMP_TABLE;
unknown's avatar
unknown committed
1177 1178 1179
    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);
unknown's avatar
unknown committed
1180
    // We don't need to call invalidate() because this table is not in cache
unknown's avatar
unknown committed
1181 1182 1183
    if ((error= (int) !(open_temporary_table(thd, share->path.str,
                                             share->db.str,
					     share->table_name.str, 1))))
1184
      (void) rm_temporary_table(table_type, share->path.str);
1185 1186 1187
    else
      thd->thread_specific_used= TRUE;
    
unknown's avatar
unknown committed
1188 1189
    free_table_share(share);
    my_free((char*) table,MYF(0));
1190
    /*
unknown's avatar
unknown committed
1191
      If we return here we will not have logged the truncation to the bin log
1192
      and we will not my_ok() to the client.
1193
    */
unknown's avatar
unknown committed
1194
    goto end;
1195 1196
  }

1197
  path_length= build_table_filename(path, sizeof(path) - 1, table_list->db,
1198
                                    table_list->table_name, reg_ext, 0);
1199 1200 1201

  if (!dont_send_ok)
  {
unknown's avatar
unknown committed
1202
    enum legacy_db_type table_type;
unknown's avatar
unknown committed
1203
    mysql_frm_type(thd, path, &table_type);
1204
    if (table_type == DB_TYPE_UNKNOWN)
1205
    {
1206
      my_error(ER_NO_SUCH_TABLE, MYF(0),
1207
               table_list->db, table_list->table_name);
unknown's avatar
unknown committed
1208
      DBUG_RETURN(TRUE);
1209
    }
1210 1211
    if (!ha_check_storage_engine_flag(ha_resolve_by_legacy_type(thd,
                                                                table_type),
1212
                                      HTON_CAN_RECREATE))
1213
      goto trunc_by_del;
1214

1215
    if (lock_and_wait_for_table_name(thd, table_list))
unknown's avatar
unknown committed
1216
      DBUG_RETURN(TRUE);
1217 1218
  }

1219 1220 1221 1222 1223
  /*
    Remove the .frm extension AIX 5.2 64-bit compiler bug (BUG#16155): this
    crashes, replacement works.  *(path + path_length - reg_ext_length)=
     '\0';
  */
1224
  path[path_length - reg_ext_length] = 0;
unknown's avatar
unknown committed
1225
  VOID(pthread_mutex_lock(&LOCK_open));
unknown's avatar
unknown committed
1226 1227
  error= ha_create_table(thd, path, table_list->db, table_list->table_name,
                         &create_info, 1);
unknown's avatar
unknown committed
1228
  VOID(pthread_mutex_unlock(&LOCK_open));
unknown's avatar
unknown committed
1229
  query_cache_invalidate3(thd, table_list, 0);
1230

1231
end:
unknown's avatar
unknown committed
1232
  if (!dont_send_ok)
1233
  {
unknown's avatar
unknown committed
1234
    if (!error)
1235
    {
1236 1237
      /* In RBR, the statement is not binlogged if the table is temporary. */
      if (!is_temporary_table || !thd->current_stmt_binlog_row_based)
He Zhenxing's avatar
He Zhenxing committed
1238 1239 1240
        error= write_bin_log(thd, TRUE, thd->query(), thd->query_length());
      if (!error)
        my_ok(thd);		// This should return record count
1241
    }
1242
    VOID(pthread_mutex_lock(&LOCK_open));
unknown's avatar
unknown committed
1243
    unlock_table_name(thd, table_list);
1244
    VOID(pthread_mutex_unlock(&LOCK_open));
1245
  }
unknown's avatar
unknown committed
1246
  else if (error)
1247 1248
  {
    VOID(pthread_mutex_lock(&LOCK_open));
unknown's avatar
unknown committed
1249
    unlock_table_name(thd, table_list);
1250 1251
    VOID(pthread_mutex_unlock(&LOCK_open));
  }
unknown's avatar
unknown committed
1252
  DBUG_RETURN(error);
1253

unknown's avatar
unknown committed
1254
trunc_by_del:
1255
  error= mysql_truncate_by_delete(thd, table_list);
1256
  DBUG_RETURN(error);
1257
}