summaryrefslogtreecommitdiff
path: root/sql/sql_delete.cc
diff options
context:
space:
mode:
Diffstat (limited to 'sql/sql_delete.cc')
-rw-r--r--sql/sql_delete.cc204
1 files changed, 150 insertions, 54 deletions
diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc
index 381d1a71e31..659695e8e73 100644
--- a/sql/sql_delete.cc
+++ b/sql/sql_delete.cc
@@ -21,7 +21,9 @@
*/
#include "mysql_priv.h"
+#ifdef WITH_INNOBASE_STORAGE_ENGINE
#include "ha_innodb.h"
+#endif
#include "sql_select.h"
#include "sp_head.h"
#include "sql_trigger.h"
@@ -30,13 +32,14 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
SQL_LIST *order, ha_rows limit, ulonglong options,
bool reset_auto_increment)
{
- int error;
+ bool will_batch;
+ int error, loc_error;
TABLE *table;
SQL_SELECT *select=0;
READ_RECORD info;
bool using_limit=limit != HA_POS_ERROR;
bool transactional_table, safe_update, const_cond;
- ha_rows deleted;
+ ha_rows deleted= 0;
uint usable_index= MAX_KEY;
SELECT_LEX *select_lex= &thd->lex->select_lex;
DBUG_ENTER("mysql_delete");
@@ -49,7 +52,6 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
table_list->view_db.str, table_list->view_name.str);
DBUG_RETURN(TRUE);
}
- table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK);
thd->proc_info="init";
table->map=1;
@@ -71,15 +73,23 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
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.
+
+ If row-based replication is used, we also delete the table row by
+ row.
*/
if (!using_limit && const_cond && (!conds || conds->val_int()) &&
!(specialflag & (SPECIAL_NO_NEW_FUNC | SPECIAL_SAFE_MODE)) &&
- !(table->triggers && table->triggers->has_delete_triggers()))
+ !(table->triggers && table->triggers->has_delete_triggers()) &&
+ !thd->current_stmt_binlog_row_based)
{
- deleted= table->file->records;
+ /* Update the table->file->stats.records number */
+ table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK);
+ ha_rows const maybe_deleted= table->file->stats.records;
+ DBUG_PRINT("debug", ("Trying to use delete_all_rows()"));
if (!(error=table->file->delete_all_rows()))
{
error= -1; // ok
+ deleted= maybe_deleted;
goto cleanup;
}
if (error != HA_ERR_WRONG_COMMAND)
@@ -91,6 +101,18 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
/* Handler didn't support fast delete; Delete rows one by one */
}
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+ if (prune_partitions(thd, table, conds))
+ {
+ free_underlaid_joins(thd, select_lex);
+ thd->row_count_func= 0;
+ send_ok(thd); // No matching records
+ DBUG_RETURN(0);
+ }
+#endif
+ /* Update the table->file->stats.records number */
+ table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK);
+
table->used_keys.clear_all();
table->quick_keys.clear_all(); // Can't use 'only index'
select=make_select(table, 0, 0, conds, 0, &error);
@@ -102,13 +124,11 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
free_underlaid_joins(thd, select_lex);
thd->row_count_func= 0;
send_ok(thd,0L);
-
/*
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.
*/
-
DBUG_RETURN(0); // Nothing to delete
}
@@ -161,7 +181,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
if (!(sortorder= make_unireg_sortorder((ORDER*) order->first,
&length)) ||
(table->sort.found_records = filesort(thd, table, sortorder, length,
- select, HA_POS_ERROR,
+ select, HA_POS_ERROR, 1,
&examined_rows))
== HA_POS_ERROR)
{
@@ -191,12 +211,12 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
else
init_read_record_idx(&info, thd, table, 1, usable_index);
- deleted=0L;
init_ftfuncs(thd, select_lex, 1);
thd->proc_info="updating";
+ will_batch= !table->file->start_bulk_delete();
- if (table->triggers)
- table->triggers->mark_fields_used(thd, TRG_EVENT_DELETE);
+
+ table->mark_columns_needed_for_delete();
while (!(error=info.read_record(&info)) && !thd->killed &&
!thd->net.report_error)
@@ -213,7 +233,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
break;
}
- if (!(error=table->file->delete_row(table->record[0])))
+ if (!(error= table->file->ha_delete_row(table->record[0])))
{
deleted++;
if (table->triggers &&
@@ -249,9 +269,14 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
}
if (thd->killed && !error)
error= 1; // Aborted
- thd->proc_info="end";
+ if (will_batch && (loc_error= table->file->end_bulk_delete()))
+ {
+ if (error != 1)
+ table->file->print_error(loc_error,MYF(0));
+ error=1;
+ }
+ thd->proc_info= "end";
end_read_record(&info);
- free_io_cache(table); // Will not do any harm
if (options & OPTION_QUICK)
(void) table->file->extra(HA_EXTRA_NORMAL);
@@ -282,6 +307,7 @@ cleanup:
delete select;
transactional_table= table->file->has_transactions();
+
/* See similar binlogging code in sql_update.cc, for comments */
if ((error < 0) || (deleted && !transactional_table))
{
@@ -289,10 +315,21 @@ cleanup:
{
if (error < 0)
thd->clear_error();
- Query_log_event qinfo(thd, thd->query, thd->query_length,
- transactional_table, FALSE);
- if (mysql_bin_log.write(&qinfo) && transactional_table)
+
+ /*
+ [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.
+ */
+ int log_result= thd->binlog_query(THD::ROW_QUERY_TYPE,
+ thd->query, thd->query_length,
+ transactional_table, FALSE);
+
+ if (log_result && transactional_table)
+ {
error=1;
+ }
}
if (!transactional_table)
thd->options|=OPTION_STATUS_NO_TRANS_UPDATE;
@@ -340,7 +377,7 @@ bool mysql_prepare_delete(THD *thd, TABLE_LIST *table_list, Item **conds)
thd->lex->allow_sum_func= 0;
if (setup_tables_and_check_access(thd, &thd->lex->select_lex.context,
&thd->lex->select_lex.top_join_list,
- table_list, conds,
+ table_list,
&select_lex->leaf_tables, FALSE,
DELETE_ACL) ||
setup_conds(thd, table_list, select_lex->leaf_tables, conds) ||
@@ -403,7 +440,7 @@ bool mysql_multi_delete_prepare(THD *thd)
*/
if (setup_tables_and_check_access(thd, &thd->lex->select_lex.context,
&thd->lex->select_lex.top_join_list,
- lex->query_tables, &lex->select_lex.where,
+ lex->query_tables,
&lex->select_lex.leaf_tables, FALSE,
DELETE_ACL))
DBUG_RETURN(TRUE);
@@ -511,8 +548,8 @@ multi_delete::initialize_tables(JOIN *join)
transactional_tables= 1;
else
normal_tables= 1;
- if (tbl->triggers)
- tbl->triggers->mark_fields_used(thd, TRG_EVENT_DELETE);
+ tbl->prepare_for_position();
+ tbl->mark_columns_needed_for_delete();
}
else if ((tab->type != JT_SYSTEM && tab->type != JT_CONST) &&
walk == delete_tables)
@@ -552,7 +589,6 @@ multi_delete::~multi_delete()
table_being_deleted= table_being_deleted->next_local)
{
TABLE *table= table_being_deleted->table;
- free_io_cache(table); // Alloced by unique
table->no_keyread=0;
}
@@ -592,7 +628,7 @@ bool multi_delete::send_data(List<Item> &values)
TRG_ACTION_BEFORE, FALSE))
DBUG_RETURN(1);
table->status|= STATUS_DELETED;
- if (!(error=table->file->delete_row(table->record[0])))
+ if (!(error=table->file->ha_delete_row(table->record[0])))
{
deleted++;
if (table->triggers &&
@@ -666,7 +702,8 @@ void multi_delete::send_error(uint errcode,const char *err)
int multi_delete::do_deletes()
{
- int local_error= 0, counter= 0;
+ int local_error= 0, counter= 0, error;
+ bool will_batch;
DBUG_ENTER("do_deletes");
DBUG_ASSERT(do_delete);
@@ -694,6 +731,7 @@ int multi_delete::do_deletes()
been deleted by foreign key handling
*/
info.ignore_not_found_rows= 1;
+ will_batch= !table->file->start_bulk_delete();
while (!(local_error=info.read_record(&info)) && !thd->killed)
{
if (table->triggers &&
@@ -703,7 +741,7 @@ int multi_delete::do_deletes()
local_error= 1;
break;
}
- if ((local_error=table->file->delete_row(table->record[0])))
+ if ((local_error=table->file->ha_delete_row(table->record[0])))
{
table->file->print_error(local_error,MYF(0));
break;
@@ -717,6 +755,14 @@ int multi_delete::do_deletes()
break;
}
}
+ if (will_batch && (error= table->file->end_bulk_delete()))
+ {
+ if (!local_error)
+ {
+ local_error= error;
+ table->file->print_error(local_error,MYF(0));
+ }
+ }
end_read_record(&info);
if (thd->killed && !local_error)
local_error= 1;
@@ -762,10 +808,13 @@ bool multi_delete::send_eof()
{
if (local_error == 0)
thd->clear_error();
- Query_log_event qinfo(thd, thd->query, thd->query_length,
- transactional_tables, FALSE);
- if (mysql_bin_log.write(&qinfo) && !normal_tables)
+ if (thd->binlog_query(THD::ROW_QUERY_TYPE,
+ thd->query, thd->query_length,
+ transactional_tables, FALSE) &&
+ !normal_tables)
+ {
local_error=1; // Log write failed: roll back the SQL statement
+ }
}
if (!transactional_tables)
thd->options|=OPTION_STATUS_NO_TRANS_UPDATE;
@@ -804,31 +853,33 @@ bool mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok)
{
HA_CREATE_INFO create_info;
char path[FN_REFLEN];
- TABLE **table_ptr;
+ TABLE *table;
bool error;
+ uint closed_log_tables= 0, lock_logger= 0;
+ uint path_length;
DBUG_ENTER("mysql_truncate");
bzero((char*) &create_info,sizeof(create_info));
/* If it is a temporary table, close and regenerate it */
- if (!dont_send_ok && (table_ptr=find_temporary_table(thd,table_list->db,
- table_list->table_name)))
+ if (!dont_send_ok && (table= find_temporary_table(thd, table_list)))
{
- TABLE *table= *table_ptr;
- table->file->info(HA_STATUS_AUTO | HA_STATUS_NO_LOCK);
- db_type table_type= table->s->db_type;
+ handlerton *table_type= table->s->db_type;
+ TABLE_SHARE *share= table->s;
if (!ha_check_storage_engine_flag(table_type, HTON_CAN_RECREATE))
goto trunc_by_del;
- strmov(path, table->s->path);
- *table_ptr= table->next; // Unlink table from list
- close_temporary(table,0);
- if (thd->slave_thread)
- --slave_open_temp_tables;
- *fn_ext(path)=0; // Remove the .frm extension
- ha_create_table(path, &create_info,1);
+
+ 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);
// We don't need to call invalidate() because this table is not in cache
- if ((error= (int) !(open_temporary_table(thd, path, table_list->db,
- table_list->table_name, 1))))
+ if ((error= (int) !(open_temporary_table(thd, share->path.str,
+ share->db.str,
+ share->table_name.str, 1))))
(void) rm_temporary_table(table_type, path);
+ free_table_share(share);
+ my_free((char*) table,MYF(0));
/*
If we return here we will not have logged the truncation to the bin log
and we will not send_ok() to the client.
@@ -836,13 +887,12 @@ bool mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok)
goto end;
}
- (void) sprintf(path,"%s/%s/%s%s",mysql_data_home,table_list->db,
- table_list->table_name,reg_ext);
- fn_format(path, path, "", "", MY_UNPACK_FILENAME);
+ path_length= build_table_filename(path, sizeof(path), table_list->db,
+ table_list->table_name, reg_ext);
if (!dont_send_ok)
{
- db_type table_type;
+ enum legacy_db_type table_type;
mysql_frm_type(thd, path, &table_type);
if (table_type == DB_TYPE_UNKNOWN)
{
@@ -850,14 +900,46 @@ bool mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok)
table_list->db, table_list->table_name);
DBUG_RETURN(TRUE);
}
- if (!ha_check_storage_engine_flag(table_type, HTON_CAN_RECREATE))
+ if (!ha_check_storage_engine_flag(ha_resolve_by_legacy_type(thd, table_type),
+ HTON_CAN_RECREATE))
goto trunc_by_del;
+
if (lock_and_wait_for_table_name(thd, table_list))
DBUG_RETURN(TRUE);
}
- *fn_ext(path)=0; // Remove the .frm extension
- error= ha_create_table(path,&create_info,1);
+ /* close log tables in use */
+ if (!my_strcasecmp(system_charset_info, table_list->db, "mysql"))
+ {
+ if (opt_log &&
+ !my_strcasecmp(system_charset_info, table_list->table_name,
+ "general_log"))
+ {
+ lock_logger= 1;
+ logger.lock();
+ logger.close_log_table(QUERY_LOG_GENERAL, FALSE);
+ closed_log_tables= closed_log_tables | QUERY_LOG_GENERAL;
+ }
+ else
+ if (opt_slow_log &&
+ !my_strcasecmp(system_charset_info, table_list->table_name,
+ "slow_log"))
+ {
+ lock_logger= 1;
+ logger.lock();
+ logger.close_log_table(QUERY_LOG_SLOW, FALSE);
+ closed_log_tables= closed_log_tables | QUERY_LOG_SLOW;
+ }
+ }
+
+ // Remove the .frm extension AIX 5.2 64-bit compiler bug (BUG#16155): this
+ // crashes, replacement works. *(path + path_length - reg_ext_length)=
+ // '\0';
+ path[path_length - reg_ext_length] = 0;
+ VOID(pthread_mutex_lock(&LOCK_open));
+ error= ha_create_table(thd, path, table_list->db, table_list->table_name,
+ &create_info, 1);
+ VOID(pthread_mutex_unlock(&LOCK_open));
query_cache_invalidate3(thd, table_list, 0);
end:
@@ -867,16 +949,27 @@ end:
{
if (mysql_bin_log.is_open())
{
+ /*
+ TRUNCATE must always be statement-based binlogged (not row-based) so
+ we don't test current_stmt_binlog_row_based.
+ */
thd->clear_error();
- Query_log_event qinfo(thd, thd->query, thd->query_length,
- 0, FALSE);
- mysql_bin_log.write(&qinfo);
+ thd->binlog_query(THD::STMT_QUERY_TYPE,
+ thd->query, thd->query_length, FALSE, FALSE);
}
send_ok(thd); // This should return record count
}
VOID(pthread_mutex_lock(&LOCK_open));
unlock_table_name(thd, table_list);
VOID(pthread_mutex_unlock(&LOCK_open));
+
+ if (opt_slow_log && (closed_log_tables & QUERY_LOG_SLOW))
+ logger.reopen_log_table(QUERY_LOG_SLOW);
+
+ if (opt_log && (closed_log_tables & QUERY_LOG_GENERAL))
+ logger.reopen_log_table(QUERY_LOG_GENERAL);
+ if (lock_logger)
+ logger.unlock();
}
else if (error)
{
@@ -886,16 +979,19 @@ end:
}
DBUG_RETURN(error);
- trunc_by_del:
+trunc_by_del:
/* Probably InnoDB table */
ulong save_options= thd->options;
table_list->lock_type= TL_WRITE;
thd->options&= ~(ulong) (OPTION_BEGIN | OPTION_NOT_AUTOCOMMIT);
ha_enable_transaction(thd, FALSE);
mysql_init_select(thd->lex);
+ bool save_binlog_row_based= thd->current_stmt_binlog_row_based;
+ thd->clear_current_stmt_binlog_row_based();
error= mysql_delete(thd, table_list, (COND*) 0, (SQL_LIST*) 0,
HA_POS_ERROR, LL(0), TRUE);
ha_enable_transaction(thd, TRUE);
thd->options= save_options;
+ thd->current_stmt_binlog_row_based= save_binlog_row_based;
DBUG_RETURN(error);
}