diff options
Diffstat (limited to 'sql/sql_delete.cc')
-rw-r--r-- | sql/sql_delete.cc | 204 |
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); } |