sql_delete.cc 35.2 KB
Newer Older
Marc Alff's avatar
Marc Alff committed
1
/* Copyright (C) 2000 MySQL AB, 2008-2009 Sun Microsystems, Inc
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 23
#include "mysql_priv.h"
#include "sql_select.h"
24 25
#include "sp_head.h"
#include "sql_trigger.h"
bk@work.mysql.com's avatar
bk@work.mysql.com committed
26

27 28 29 30 31 32 33 34
/**
  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().
*/

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

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

59 60
  if (open_and_lock_tables(thd, table_list))
    DBUG_RETURN(TRUE);
61 62 63 64
  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
65
    DBUG_RETURN(TRUE);
66
  }
67
  thd_proc_info(thd, "init");
bk@work.mysql.com's avatar
bk@work.mysql.com committed
68
  table->map=1;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
69

70 71
  if (mysql_prepare_delete(thd, table_list, &conds))
    DBUG_RETURN(TRUE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
72

73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
  /* 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);
    }
  }

94
  const_cond= (!conds || conds->const_item());
95
  safe_update=test(thd->variables.option_bits & OPTION_SAFE_UPDATES);
96 97
  if (safe_update && const_cond)
  {
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
98 99
    my_message(ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE,
               ER(ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE), MYF(0));
100
    DBUG_RETURN(TRUE);
101 102
  }

103
  select_lex->no_error= thd->lex->ignore;
104

105 106 107 108 109 110
  const_cond_result= const_cond && (!conds || conds->val_int());
  if (thd->is_error())
  {
    /* Error evaluating val_int(). */
    DBUG_RETURN(TRUE);
  }
111

112 113 114 115
  /*
    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.
116 117 118 119 120 121 122 123 124 125 126 127 128 129

    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.
130
  */
131
  if (!using_limit && const_cond_result &&
132
      !(specialflag & (SPECIAL_NO_NEW_FUNC | SPECIAL_SAFE_MODE)) &&
133
      (thd->lex->sql_command == SQLCOM_TRUNCATE ||
134
       (!thd->is_current_stmt_binlog_format_row() &&
135
        !(table->triggers && table->triggers->has_delete_triggers()))))
136
  {
137
    /* Update the table->file->stats.records number */
138
    table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK);
139
    ha_rows const maybe_deleted= table->file->stats.records;
140
    DBUG_PRINT("debug", ("Trying to use delete_all_rows()"));
141
    if (!(error=table->file->ha_delete_all_rows()))
142
    {
Mats Kindahl's avatar
Mats Kindahl committed
143 144 145 146 147
      /*
        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;
148
      error= -1;				// ok
149
      deleted= maybe_deleted;
150 151 152 153 154 155 156 157 158 159
      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 */
  }
160 161 162 163 164 165 166
  if (conds)
  {
    Item::cond_result result;
    conds= remove_eq_conds(thd, conds, &result);
    if (result == Item::COND_FALSE)             // Impossible where
      limit= 0;
  }
167

168 169 170 171 172
#ifdef WITH_PARTITION_STORAGE_ENGINE
  if (prune_partitions(thd, table, conds))
  {
    free_underlaid_joins(thd, select_lex);
    thd->row_count_func= 0;
173
    my_ok(thd, (ha_rows) thd->row_count_func);  // No matching records
174 175 176
    DBUG_RETURN(0);
  }
#endif
177
  /* Update the table->file->stats.records number */
178
  table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK);
179

180
  table->covering_keys.clear_all();
181
  table->quick_keys.clear_all();		// Can't use 'only index'
monty@mysql.com's avatar
monty@mysql.com committed
182
  select=make_select(table, 0, 0, conds, 0, &error);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
183
  if (error)
184
    DBUG_RETURN(TRUE);
185
  if ((select && select->check_quick(thd, safe_update, limit)) || !limit)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
186 187
  {
    delete select;
188
    free_underlaid_joins(thd, select_lex);
189
    thd->row_count_func= 0;
190 191 192 193 194 195
    /* 
      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().
    */
196
    if (thd->is_error())
197
      DBUG_RETURN(TRUE);
198
    my_ok(thd, (ha_rows) thd->row_count_func);
199 200 201 202 203
    /*
      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
204
    DBUG_RETURN(0);				// Nothing to delete
bk@work.mysql.com's avatar
bk@work.mysql.com committed
205 206 207
  }

  /* If running in safe sql mode, don't allow updates without keys */
208
  if (table->quick_keys.is_clear_all())
bk@work.mysql.com's avatar
bk@work.mysql.com committed
209
  {
210
    thd->server_status|=SERVER_QUERY_NO_INDEX_USED;
211
    if (safe_update && !using_limit)
212 213
    {
      delete select;
214
      free_underlaid_joins(thd, select_lex);
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
215 216
      my_message(ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE,
                 ER(ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE), MYF(0));
217
      DBUG_RETURN(TRUE);
218
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
219
  }
220
  if (options & OPTION_QUICK)
221 222
    (void) table->file->extra(HA_EXTRA_QUICK);

223
  if (order && order->elements)
224
  {
225
    uint         length= 0;
226
    SORT_FIELD  *sortorder;
227
    ha_rows examined_rows;
228
    
229
    if ((!select || table->quick_keys.is_clear_all()) && limit != HA_POS_ERROR)
230 231 232 233 234 235 236
      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));
    
237
      if (!(sortorder= make_unireg_sortorder((ORDER*) order->first,
238
                                             &length, NULL)) ||
239
	  (table->sort.found_records = filesort(thd, table, sortorder, length,
240
                                                select, HA_POS_ERROR, 1,
241
                                                &examined_rows))
242
	  == HA_POS_ERROR)
243 244 245
      {
        delete select;
        free_underlaid_joins(thd, &thd->lex->select_lex);
246
        DBUG_RETURN(TRUE);
247 248 249 250 251
      }
      /*
        Filesort has already found and selected the rows we want to delete,
        so we don't need the where clause
      */
252
      delete select;
253
      free_underlaid_joins(thd, select_lex);
254
      select= 0;
255 256 257
    }
  }

sergefp@mysql.com's avatar
sergefp@mysql.com committed
258 259 260 261
  /* If quick select is used, initialize it before retrieving rows. */
  if (select && select->quick && select->quick->reset())
  {
    delete select;
262
    free_underlaid_joins(thd, select_lex);
263
    DBUG_RETURN(TRUE);
sergefp@mysql.com's avatar
sergefp@mysql.com committed
264
  }
265
  if (usable_index==MAX_KEY)
266
    init_read_record(&info, thd, table, select, 1, 1, FALSE);
267 268 269
  else
    init_read_record_idx(&info, thd, table, 1, usable_index);

270
  init_ftfuncs(thd, select_lex, 1);
271
  thd_proc_info(thd, "updating");
anozdrin/alik@quad's avatar
anozdrin/alik@quad committed
272 273 274 275 276 277

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

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

278
  if (triggers_applicable &&
279 280
      table->triggers->has_triggers(TRG_EVENT_DELETE,
                                    TRG_ACTION_AFTER))
281
  {
282 283 284 285 286 287 288
    /*
      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;
289
  }
290 291
  else
    will_batch= !table->file->start_bulk_delete();
292 293


294 295
  table->mark_columns_needed_for_delete();

bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
296
  while (!(error=info.read_record(&info)) && !thd->killed &&
297
	 ! thd->is_error())
bk@work.mysql.com's avatar
bk@work.mysql.com committed
298
  {
299 300
    // thd->is_error() is tested to disallow delete row on error
    if (!(select && select->skip_record())&& ! thd->is_error() )
bk@work.mysql.com's avatar
bk@work.mysql.com committed
301
    {
302

303
      if (triggers_applicable &&
304 305 306 307 308 309
          table->triggers->process_triggers(thd, TRG_EVENT_DELETE,
                                            TRG_ACTION_BEFORE, FALSE))
      {
        error= 1;
        break;
      }
310

311
      if (!(error= table->file->ha_delete_row(table->record[0])))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
312 313
      {
	deleted++;
314
        if (triggers_applicable &&
315 316 317 318 319 320
            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
321 322 323 324 325 326 327 328 329
	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
330 331 332 333 334 335 336 337 338
	/*
	  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
339 340 341
	break;
      }
    }
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
342 343
    else
      table->file->unlock_row();  // Row failed selection, release lock on it
bk@work.mysql.com's avatar
bk@work.mysql.com committed
344
  }
345
  killed_status= thd->killed;
346
  if (killed_status != THD::NOT_KILLED || thd->is_error())
347
    error= 1;					// Aborted
mronstrom@mysql.com[mikron]'s avatar
mronstrom@mysql.com[mikron] committed
348 349 350 351 352 353
  if (will_batch && (loc_error= table->file->end_bulk_delete()))
  {
    if (error != 1)
      table->file->print_error(loc_error,MYF(0));
    error=1;
  }
354
  thd_proc_info(thd, "end");
bk@work.mysql.com's avatar
bk@work.mysql.com committed
355
  end_read_record(&info);
356
  if (options & OPTION_QUICK)
357
    (void) table->file->extra(HA_EXTRA_NORMAL);
358

osku@127.(none)'s avatar
osku@127.(none) committed
359
  if (reset_auto_increment && (error < 0))
360 361 362 363 364
  {
    /*
      We're really doing a truncate and need to reset the table's
      auto-increment counter.
    */
365
    int error2= table->file->ha_reset_auto_increment(0);
366 367 368 369

    if (error2 && (error2 != HA_ERR_WRONG_COMMAND))
    {
      table->file->print_error(error2, MYF(0));
osku@127.(none)'s avatar
osku@127.(none) committed
370
      error= 1;
371 372 373
    }
  }

374
cleanup:
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
375 376 377 378 379 380 381 382 383
  /*
    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);
  }

384
  delete select;
385
  transactional_table= table->file->has_transactions();
386

387
  if (!transactional_table && deleted > 0)
388 389
    thd->transaction.stmt.modified_non_trans_table=
      thd->transaction.all.modified_non_trans_table= TRUE;
390
  
391
  /* See similar binlogging code in sql_update.cc, for comments */
392
  if ((error < 0) || thd->transaction.stmt.modified_non_trans_table)
bk@work.mysql.com's avatar
bk@work.mysql.com committed
393
  {
394 395
    if (mysql_bin_log.is_open())
    {
Mats Kindahl's avatar
Mats Kindahl committed
396 397 398 399 400
      bool const is_trans=
        thd->lex->sql_command == SQLCOM_TRUNCATE ?
        FALSE :
        transactional_table;

401
      int errcode= 0;
402
      if (error < 0)
guilhem@mysql.com's avatar
guilhem@mysql.com committed
403
        thd->clear_error();
404 405 406
      else
        errcode= query_error_code(thd, killed_status == THD::NOT_KILLED);
      
407
      /*
408 409 410 411
        [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
412 413 414

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

420
      if (log_result)
421
      {
422
	error=1;
423
      }
424
    }
bk@work.mysql.com's avatar
bk@work.mysql.com committed
425
  }
426
  DBUG_ASSERT(transactional_table || !deleted || thd->transaction.stmt.modified_non_trans_table);
427
  free_underlaid_joins(thd, select_lex);
428 429
  if (error < 0 || 
      (thd->lex->ignore && !thd->is_error() && !thd->is_fatal_error))
bk@work.mysql.com's avatar
bk@work.mysql.com committed
430
  {
431 432 433 434 435
    /*
      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;
436
    my_ok(thd, (ha_rows) thd->row_count_func);
437
    DBUG_PRINT("info",("%ld records deleted",(long) deleted));
bk@work.mysql.com's avatar
bk@work.mysql.com committed
438
  }
439
  DBUG_RETURN(error >= 0 || thd->is_error());
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
440 441 442 443 444 445 446 447 448
}


/*
  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
449
    table_list		- global/local table list
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
450 451 452
    conds		- conditions

  RETURN VALUE
453 454
    FALSE OK
    TRUE  error
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
455
*/
456
int mysql_prepare_delete(THD *thd, TABLE_LIST *table_list, Item **conds)
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
457
{
458
  Item *fake_conds= 0;
459
  SELECT_LEX *select_lex= &thd->lex->select_lex;
460
  DBUG_ENTER("mysql_prepare_delete");
461
  List<Item> all_fields;
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
462

igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
463
  thd->lex->allow_sum_func= 0;
464 465
  if (setup_tables_and_check_access(thd, &thd->lex->select_lex.context,
                                    &thd->lex->select_lex.top_join_list,
466
                                    table_list, 
467
                                    &select_lex->leaf_tables, FALSE, 
468
                                    DELETE_ACL, SELECT_ACL) ||
469
      setup_conds(thd, table_list, select_lex->leaf_tables, conds) ||
470
      setup_ftfuncs(select_lex))
471
    DBUG_RETURN(TRUE);
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
472
  if (!table_list->updatable || check_key_in_view(thd, table_list))
bell@sanja.is.com.ua's avatar
bell@sanja.is.com.ua committed
473
  {
474
    my_error(ER_NON_UPDATABLE_TABLE, MYF(0), table_list->alias, "DELETE");
475
    DBUG_RETURN(TRUE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
476
  }
477
  {
478
    TABLE_LIST *duplicate;
479
    if ((duplicate= unique_table(thd, table_list, table_list->next_global, 0)))
480
    {
481
      update_non_unique_table_error(table_list, "DELETE", duplicate);
482 483
      DBUG_RETURN(TRUE);
    }
484
  }
485 486 487

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

490
  select_lex->fix_prepare_information(thd, conds, &fake_conds);
491
  DBUG_RETURN(FALSE);
bk@work.mysql.com's avatar
bk@work.mysql.com committed
492 493 494
}


495
/***************************************************************************
496
  Delete multiple tables from join 
497 498
***************************************************************************/

499
#define MEM_STRIP_BUF_SIZE current_thd->variables.sortbuff_size
500

501
extern "C" int refpos_order_cmp(void* arg, const void *a,const void *b)
502
{
503
  handler *file= (handler*)arg;
504
  return file->cmp_ref((const uchar*)a, (const uchar*)b);
505
}
506

bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
507 508 509 510 511 512 513 514
/*
  make delete specific preparation and checks after opening tables

  SYNOPSIS
    mysql_multi_delete_prepare()
    thd         thread handler

  RETURN
515 516
    FALSE OK
    TRUE  Error
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
517 518
*/

519
int mysql_multi_delete_prepare(THD *thd)
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
520 521
{
  LEX *lex= thd->lex;
522
  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
523 524 525 526 527 528 529 530 531
  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
  */
532 533
  if (setup_tables_and_check_access(thd, &thd->lex->select_lex.context,
                                    &thd->lex->select_lex.top_join_list,
534
                                    lex->query_tables,
535
                                    &lex->select_lex.leaf_tables, FALSE, 
536
                                    DELETE_ACL, SELECT_ACL))
537
    DBUG_RETURN(TRUE);
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
538

539 540 541 542 543 544

  /*
    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
545 546 547 548 549
  /* 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)
  {
550 551 552
    if (!(target_tbl->table= target_tbl->correspondent_table->table))
    {
      DBUG_ASSERT(target_tbl->correspondent_table->view &&
553 554 555
                  target_tbl->correspondent_table->merge_underlying_list &&
                  target_tbl->correspondent_table->merge_underlying_list->
                  next_local);
556 557 558 559 560 561
      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
562 563 564
    if (!target_tbl->correspondent_table->updatable ||
        check_key_in_view(thd, target_tbl->correspondent_table))
    {
565
      my_error(ER_NON_UPDATABLE_TABLE, MYF(0),
566
               target_tbl->table_name, "DELETE");
567
      DBUG_RETURN(TRUE);
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
568 569
    }
    /*
570 571
      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
572 573
    */
    {
574
      TABLE_LIST *duplicate;
575
      if ((duplicate= unique_table(thd, target_tbl->correspondent_table,
576
                                   lex->query_tables, 0)))
577 578 579 580 581
      {
        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
582 583
    }
  }
584 585 586 587 588
  /*
    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;
589
  DBUG_RETURN(FALSE);
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
590 591 592
}


593 594
multi_delete::multi_delete(TABLE_LIST *dt, uint num_of_tables_arg)
  : delete_tables(dt), deleted(0), found(0),
595
    num_of_tables(num_of_tables_arg), error(0),
596
    do_delete(0), transactional_tables(0), normal_tables(0), error_handled(0)
597
{
598
  tempfiles= (Unique **) sql_calloc(sizeof(Unique *) * num_of_tables);
599 600 601 602
}


int
603
multi_delete::prepare(List<Item> &values, SELECT_LEX_UNIT *u)
604 605
{
  DBUG_ENTER("multi_delete::prepare");
606
  unit= u;
607
  do_delete= 1;
608
  thd_proc_info(thd, "deleting from main table");
609 610 611
  DBUG_RETURN(0);
}

612

613
bool
614 615
multi_delete::initialize_tables(JOIN *join)
{
616
  TABLE_LIST *walk;
617 618 619
  Unique **tempfiles_ptr;
  DBUG_ENTER("initialize_tables");

620
  if ((thd->variables.option_bits & OPTION_SAFE_UPDATES) && error_if_full_join(join))
621 622
    DBUG_RETURN(1);

623
  table_map tables_to_delete_from=0;
624
  delete_while_scanning= 1;
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
625
  for (walk= delete_tables; walk; walk= walk->next_local)
626
  {
627
    tables_to_delete_from|= walk->table->map;
628 629 630 631 632 633 634 635 636 637 638 639
    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;
    }
  }

monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
640

641
  walk= delete_tables;
642 643 644 645
  for (JOIN_TAB *tab=join->join_tab, *end=join->join_tab+join->tables;
       tab < end;
       tab++)
  {
646
    if (tab->table->map & tables_to_delete_from)
647
    {
648
      /* We are going to delete from this table */
649
      TABLE *tbl=walk->table=tab->table;
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
650
      walk= walk->next_local;
651
      /* Don't use KEYREAD optimization on this table */
652
      tbl->no_keyread=1;
653 654
      /* Don't use record cache */
      tbl->no_cache= 1;
655
      tbl->covering_keys.clear_all();
656
      if (tbl->file->has_transactions())
657
	transactional_tables= 1;
658 659
      else
	normal_tables= 1;
660 661
      if (tbl->triggers &&
          tbl->triggers->has_triggers(TRG_EVENT_DELETE,
662
                                      TRG_ACTION_AFTER))
663 664 665 666 667 668 669
      {
	/*
          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);
670
      }
671 672
      tbl->prepare_for_position();
      tbl->mark_columns_needed_for_delete();
673
    }
674 675 676 677 678 679 680 681 682 683
    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;
    }
684
  }
685
  walk= delete_tables;
686
  tempfiles_ptr= tempfiles;
687 688 689 690 691 692
  if (delete_while_scanning)
  {
    table_being_deleted= delete_tables;
    walk= walk->next_local;
  }
  for (;walk ;walk= walk->next_local)
693 694
  {
    TABLE *table=walk->table;
695 696
    *tempfiles_ptr++= new Unique (refpos_order_cmp,
				  (void *) table->file,
697 698
				  table->file->ref_length,
				  MEM_STRIP_BUF_SIZE);
699
  }
pem@mysql.com's avatar
pem@mysql.com committed
700
  init_ftfuncs(thd, thd->lex->current_select, 1);
701
  DBUG_RETURN(thd->is_fatal_error != 0);
702
}
703

704

705 706
multi_delete::~multi_delete()
{
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
707 708 709
  for (table_being_deleted= delete_tables;
       table_being_deleted;
       table_being_deleted= table_being_deleted->next_local)
710
  {
711 712
    TABLE *table= table_being_deleted->table;
    table->no_keyread=0;
713
  }
714

715
  for (uint counter= 0; counter < num_of_tables; counter++)
716
  {
717
    if (tempfiles[counter])
718 719
      delete tempfiles[counter];
  }
720 721
}

722

723 724
bool multi_delete::send_data(List<Item> &values)
{
725 726
  int secure_counter= delete_while_scanning ? -1 : 0;
  TABLE_LIST *del_table;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
727 728
  DBUG_ENTER("multi_delete::send_data");

729 730
  bool ignore= thd->lex->current_select->no_error;

731 732 733
  for (del_table= delete_tables;
       del_table;
       del_table= del_table->next_local, secure_counter++)
734
  {
735
    TABLE *table= del_table->table;
736 737 738 739 740 741

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

744
    if (secure_counter < 0)
745
    {
746 747
      /* We are scanning the current table */
      DBUG_ASSERT(del_table == table_being_deleted);
748 749 750
      if (table->triggers &&
          table->triggers->process_triggers(thd, TRG_EVENT_DELETE,
                                            TRG_ACTION_BEFORE, FALSE))
751
        DBUG_RETURN(1);
752
      table->status|= STATUS_DELETED;
753
      if (!(error=table->file->ha_delete_row(table->record[0])))
754
      {
755 756 757
        deleted++;
        if (!table->file->has_transactions())
          thd->transaction.stmt.modified_non_trans_table= TRUE;
758 759 760
        if (table->triggers &&
            table->triggers->process_triggers(thd, TRG_EVENT_DELETE,
                                              TRG_ACTION_AFTER, FALSE))
761
          DBUG_RETURN(1);
762
      }
763
      else if (!ignore)
764
      {
765 766 767 768
        /*
          If the IGNORE option is used errors caused by ha_delete_row don't
          have to stop the iteration.
        */
769 770
        table->file->print_error(error,MYF(0));
        DBUG_RETURN(1);
771 772 773 774
      }
    }
    else
    {
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
775
      error=tempfiles[secure_counter]->unique_add((char*) table->file->ref);
776 777
      if (error)
      {
778
	error= 1;                               // Fatal error
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
779
	DBUG_RETURN(1);
780
      }
781 782
    }
  }
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
783
  DBUG_RETURN(0);
784 785
}

786

787 788
void multi_delete::send_error(uint errcode,const char *err)
{
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
789 790
  DBUG_ENTER("multi_delete::send_error");

791
  /* First send error what ever it is ... */
792
  my_message(errcode, err, MYF(0));
793

794 795 796 797 798 799 800 801
  DBUG_VOID_RETURN;
}


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

802 803
  /* the error was handled or nothing deleted and no side effects return */
  if (error_handled ||
804
      (!thd->transaction.stmt.modified_non_trans_table && !deleted))
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
805
    DBUG_VOID_RETURN;
806

807
  /* Something already deleted so we have to invalidate cache */
808 809
  if (deleted)
    query_cache_invalidate3(thd, delete_tables, 1);
810

811 812 813
  if (thd->transaction.stmt.modified_non_trans_table)
    thd->transaction.all.modified_non_trans_table= TRUE;

814
  /*
815 816
    If rows from the first table only has been deleted and it is
    transactional, just do rollback.
817 818 819
    The same if all tables are transactional, regardless of where we are.
    In all other cases do attempt deletes ...
  */
820 821 822
  if (do_delete && normal_tables &&
      (table_being_deleted != delete_tables ||
       !table_being_deleted->table->file->has_transactions()))
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
823
  {
824 825 826 827 828 829
    /*
      We have to execute the recorded do_deletes() and write info into the
      error log
    */
    error= 1;
    send_eof();
830 831
    DBUG_ASSERT(error_handled);
    DBUG_VOID_RETURN;
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
832
  }
833 834 835 836 837 838 839 840
  
  if (thd->transaction.stmt.modified_non_trans_table)
  {
    /* 
       there is only side effects; to binlog with the error
    */
    if (mysql_bin_log.is_open())
    {
841
      int errcode= query_error_code(thd, thd->killed == THD::NOT_KILLED);
842 843
      /* possible error of writing binary log is ignored deliberately */
      (void) thd->binlog_query(THD::ROW_QUERY_TYPE,
844 845
                               thd->query(), thd->query_length(),
                               transactional_tables, FALSE, FALSE, errcode);
846
    }
monty@hundin.mysql.fi's avatar
monty@hundin.mysql.fi committed
847 848
  }
  DBUG_VOID_RETURN;
849 850
}

851

852

853
/**
854
  Do delete from other tables.
855 856 857 858 859 860 861

  @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.
862 863
*/

864
int multi_delete::do_deletes()
865
{
866
  DBUG_ENTER("do_deletes");
867
  DBUG_ASSERT(do_delete);
868

869
  do_delete= 0;                                 // Mark called
870
  if (!found)
igor@rurik.mysql.com's avatar
igor@rurik.mysql.com committed
871
    DBUG_RETURN(0);
872 873 874 875

  table_being_deleted= (delete_while_scanning ? delete_tables->next_local :
                        delete_tables);
 
876
  for (uint counter= 0; table_being_deleted;
bell@sanja.is.com.ua's avatar
VIEW  
bell@sanja.is.com.ua committed
877
       table_being_deleted= table_being_deleted->next_local, counter++)
878
  { 
879 880
    TABLE *table = table_being_deleted->table;
    if (tempfiles[counter]->get(table))
881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931
      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");
  init_read_record(&info, thd, table, NULL, 0, 1, FALSE);
  /*
    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))
932
    {
933
      local_error= 1;
934 935
      break;
    }
936 937 938 939 940 941 942 943
      
    local_error= table->file->ha_delete_row(table->record[0]);
    if (local_error && !ignore)
    {
      table->file->print_error(local_error, MYF(0));
      break;
    }
      
944
    /*
945 946 947
      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.
948
    */
949
    if (!local_error)
950
    {
951
      deleted++;
952 953
      if (table->triggers &&
          table->triggers->process_triggers(thd, TRG_EVENT_DELETE,
954
                                            TRG_ACTION_AFTER, FALSE))
955 956 957 958
      {
        local_error= 1;
        break;
      }
959
    }
960 961 962 963 964
  }
  if (will_batch)
  {
    int tmp_error= table->file->end_bulk_delete();
    if (tmp_error && !local_error)
mronstrom@mysql.com[mikron]'s avatar
mronstrom@mysql.com[mikron] committed
965
    {
966 967
      local_error= tmp_error;
      table->file->print_error(local_error, MYF(0));
mronstrom@mysql.com[mikron]'s avatar
mronstrom@mysql.com[mikron] committed
968
    }
969
  }
970 971 972 973 974
  if (last_deleted != deleted && !table->file->has_transactions())
    thd->transaction.stmt.modified_non_trans_table= TRUE;

  end_read_record(&info);

975
  DBUG_RETURN(local_error);
976 977
}

978
/*
979 980
  Send ok to the client

981 982 983 984
  return:  0 sucess
	   1 error
*/

985 986
bool multi_delete::send_eof()
{
987
  THD::killed_state killed_status= THD::NOT_KILLED;
988
  thd_proc_info(thd, "deleting from reference tables");
989 990

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

993 994
  /* compute a total error to know if something failed */
  local_error= local_error || error;
995
  killed_status= (local_error == 0)? THD::NOT_KILLED : thd->killed;
996
  /* reset used flags */
997
  thd_proc_info(thd, "end");
998

999 1000 1001
  if (thd->transaction.stmt.modified_non_trans_table)
    thd->transaction.all.modified_non_trans_table= TRUE;

1002 1003 1004 1005
  /*
    We must invalidate the query cache before binlog writing and
    ha_autocommit_...
  */
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1006
  if (deleted)
monty@mysql.com's avatar
monty@mysql.com committed
1007
  {
heikki@hundin.mysql.fi's avatar
heikki@hundin.mysql.fi committed
1008
    query_cache_invalidate3(thd, delete_tables, 1);
monty@mysql.com's avatar
monty@mysql.com committed
1009
  }
1010
  if ((local_error == 0) || thd->transaction.stmt.modified_non_trans_table)
1011
  {
1012 1013
    if (mysql_bin_log.is_open())
    {
1014
      int errcode= 0;
1015
      if (local_error == 0)
guilhem@mysql.com's avatar
guilhem@mysql.com committed
1016
        thd->clear_error();
1017 1018
      else
        errcode= query_error_code(thd, killed_status == THD::NOT_KILLED);
1019
      if (thd->binlog_query(THD::ROW_QUERY_TYPE,
1020
                            thd->query(), thd->query_length(),
1021
                            transactional_tables, FALSE, FALSE, errcode) &&
1022 1023
          !normal_tables)
      {
1024
	local_error=1;  // Log write failed: roll back the SQL statement
1025
      }
1026
    }
1027
  }
1028 1029
  if (local_error != 0)
    error_handled= TRUE; // to force early leave from ::send_error()
1030

1031
  if (!local_error)
1032 1033
  {
    thd->row_count_func= deleted;
1034
    ::my_ok(thd, (ha_rows) thd->row_count_func);
1035
  }
1036 1037
  return 0;
}
1038 1039 1040


/***************************************************************************
1041
  TRUNCATE TABLE
1042 1043
****************************************************************************/

1044 1045 1046 1047 1048 1049 1050
/*
  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)
{
1051
  bool error, save_binlog_row_based= thd->is_current_stmt_binlog_format_row();
1052 1053 1054
  DBUG_ENTER("mysql_truncate_by_delete");
  table_list->lock_type= TL_WRITE;
  mysql_init_select(thd->lex);
1055
  thd->clear_current_stmt_binlog_format_row();
1056 1057 1058
  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);
1059
  if (save_binlog_row_based)
1060
    thd->set_current_stmt_binlog_format_row();
1061 1062 1063 1064
  DBUG_RETURN(error);
}


1065 1066 1067 1068 1069 1070 1071 1072 1073
/*
  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
1074
  - If we want to have a name lock on the table on exit without errors.
1075 1076
*/

1077
bool mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok)
1078 1079
{
  HA_CREATE_INFO create_info;
1080
  char path[FN_REFLEN + 1];
1081
  TABLE *table;
1082
  bool error;
1083
  uint path_length;
1084
  bool is_temporary_table= false;
1085 1086
  DBUG_ENTER("mysql_truncate");

1087
  bzero((char*) &create_info,sizeof(create_info));
1088 1089 1090 1091

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

1092
  /* If it is a temporary table, close and regenerate it */
1093
  if (!dont_send_ok && (table= find_temporary_table(thd, table_list)))
1094
  {
1095
    is_temporary_table= true;
antony@ppcg5.local's avatar
antony@ppcg5.local committed
1096
    handlerton *table_type= table->s->db_type();
1097
    TABLE_SHARE *share= table->s;
1098
    /* Note that a temporary table cannot be partitioned */
1099
    if (!ha_check_storage_engine_flag(table_type, HTON_CAN_RECREATE))
1100
      goto trunc_by_del;
1101 1102 1103 1104 1105 1106

    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
1107
    // We don't need to call invalidate() because this table is not in cache
1108 1109 1110
    if ((error= (int) !(open_temporary_table(thd, share->path.str,
                                             share->db.str,
					     share->table_name.str, 1))))
1111
      (void) rm_temporary_table(table_type, path);
1112 1113 1114
    else
      thd->thread_specific_used= TRUE;
    
1115 1116
    free_table_share(share);
    my_free((char*) table,MYF(0));
1117
    /*
monty@narttu.mysql.fi's avatar
monty@narttu.mysql.fi committed
1118
      If we return here we will not have logged the truncation to the bin log
1119
      and we will not my_ok() to the client.
1120
    */
serg@serg.mylan's avatar
serg@serg.mylan committed
1121
    goto end;
1122 1123
  }

1124
  path_length= build_table_filename(path, sizeof(path) - 1, table_list->db,
1125
                                    table_list->table_name, reg_ext, 0);
1126 1127 1128

  if (!dont_send_ok)
  {
1129
    enum legacy_db_type table_type;
1130
    mysql_frm_type(thd, path, &table_type);
1131
    if (table_type == DB_TYPE_UNKNOWN)
1132
    {
1133
      my_error(ER_NO_SUCH_TABLE, MYF(0),
1134
               table_list->db, table_list->table_name);
1135
      DBUG_RETURN(TRUE);
1136
    }
1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152
#ifdef WITH_PARTITION_STORAGE_ENGINE
    /*
      TODO: Add support for TRUNCATE PARTITION for NDB and other engines
      supporting native partitioning
    */
    if (table_type != DB_TYPE_PARTITION_DB &&
        thd->lex->alter_info.flags & ALTER_ADMIN_PARTITION)
    {
      my_error(ER_PARTITION_MGMT_ON_NONPARTITIONED, MYF(0));
      DBUG_RETURN(TRUE);
    }
#endif
    if (!ha_check_storage_engine_flag(ha_resolve_by_legacy_type(thd,
                                                                table_type),
                                      HTON_CAN_RECREATE) ||
        thd->lex->alter_info.flags & ALTER_ADMIN_PARTITION)
1153
      goto trunc_by_del;
1154

1155
    if (lock_and_wait_for_table_name(thd, table_list))
1156
      DBUG_RETURN(TRUE);
1157 1158
  }

1159 1160 1161
  // Remove the .frm extension AIX 5.2 64-bit compiler bug (BUG#16155): this
  // crashes, replacement works.  *(path + path_length - reg_ext_length)=
  // '\0';
1162
  path[path_length - reg_ext_length] = 0;
Marc Alff's avatar
Marc Alff committed
1163
  mysql_mutex_lock(&LOCK_open);
1164 1165
  error= ha_create_table(thd, path, table_list->db, table_list->table_name,
                         &create_info, 1);
Marc Alff's avatar
Marc Alff committed
1166
  mysql_mutex_unlock(&LOCK_open);
1167
  query_cache_invalidate3(thd, table_list, 0);
1168

1169
end:
1170
  if (!dont_send_ok)
1171
  {
1172
    if (!error)
1173
    {
1174
      /* In RBR, the statement is not binlogged if the table is temporary. */
1175
      if (!is_temporary_table || !thd->is_current_stmt_binlog_format_row())
1176
        error= write_bin_log(thd, TRUE, thd->query(), thd->query_length());
1177
      if (!error)
1178
        my_ok(thd);             // This should return record count
1179
    }
Marc Alff's avatar
Marc Alff committed
1180
    mysql_mutex_lock(&LOCK_open);
1181
    unlock_table_name(thd, table_list);
Marc Alff's avatar
Marc Alff committed
1182
    mysql_mutex_unlock(&LOCK_open);
1183
  }
1184
  else if (error)
1185
  {
Marc Alff's avatar
Marc Alff committed
1186
    mysql_mutex_lock(&LOCK_open);
1187
    unlock_table_name(thd, table_list);
Marc Alff's avatar
Marc Alff committed
1188
    mysql_mutex_unlock(&LOCK_open);
1189
  }
1190
  DBUG_RETURN(error);
1191

1192
trunc_by_del:
1193
  error= mysql_truncate_by_delete(thd, table_list);
1194
  DBUG_RETURN(error);
1195
}