diff options
Diffstat (limited to 'sql/handler.cc')
-rw-r--r-- | sql/handler.cc | 375 |
1 files changed, 243 insertions, 132 deletions
diff --git a/sql/handler.cc b/sql/handler.cc index 8e0812f3528..1aeb818b65d 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -1,5 +1,5 @@ -/* Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved. - Copyright (c) 2011 Monty Program Ab +/* Copyright (c) 2000, 2011, Oracle and/or its affiliates. + Copyright (c) 2009-2011 Monty Program Ab 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 @@ -98,6 +98,9 @@ TYPELIB tx_isolation_typelib= {array_elements(tx_isolation_names)-1,"", static TYPELIB known_extensions= {0,"known_exts", NULL, NULL}; uint known_extensions_id= 0; +static int commit_one_phase_2(THD *thd, bool all, THD_TRANS *trans, + bool is_real_trans); + static plugin_ref ha_default_plugin(THD *thd) { if (thd->variables.table_plugin) @@ -359,6 +362,7 @@ int ha_init_errors(void) SETMSG(HA_ERR_AUTOINC_ERANGE, ER_DEFAULT(ER_WARN_DATA_OUT_OF_RANGE)); SETMSG(HA_ERR_TOO_MANY_CONCURRENT_TRXS, ER_DEFAULT(ER_TOO_MANY_CONCURRENT_TRXS)); SETMSG(HA_ERR_INDEX_COL_TOO_LONG, ER_DEFAULT(ER_INDEX_COLUMN_TOO_LONG)); + SETMSG(HA_ERR_DISK_FULL, ER_DEFAULT(ER_DISK_FULL)); /* Register the error messages for use with my_error(). */ return my_error_register(get_handler_errmsgs, HA_ERR_FIRST, HA_ERR_LAST); @@ -626,6 +630,23 @@ void ha_drop_database(char* path) } +static my_bool checkpoint_state_handlerton(THD *unused1, plugin_ref plugin, + void *disable) +{ + handlerton *hton= plugin_data(plugin, handlerton *); + if (hton->state == SHOW_OPTION_YES && hton->checkpoint_state) + hton->checkpoint_state(hton, (int) *(bool*) disable); + return FALSE; +} + + +void ha_checkpoint_state(bool disable) +{ + plugin_foreach(NULL, checkpoint_state_handlerton, MYSQL_STORAGE_ENGINE_PLUGIN, &disable); +} + + + static my_bool closecon_handlerton(THD *thd, plugin_ref plugin, void *unused) { @@ -1107,7 +1128,7 @@ ha_check_and_coalesce_trx_read_only(THD *thd, Ha_trx_info *ha_list, */ int ha_commit_trans(THD *thd, bool all) { - int error= 0, cookie= 0; + int error= 0, cookie; /* 'all' means that this is either an explicit commit issued by user, or an implicit commit issued by a DDL. @@ -1122,7 +1143,8 @@ int ha_commit_trans(THD *thd, bool all) */ bool is_real_trans= all || thd->transaction.all.ha_list == 0; Ha_trx_info *ha_info= trans->ha_list; - my_xid xid= thd->transaction.xid_state.xid.get_my_xid(); + bool need_prepare_ordered, need_commit_ordered; + my_xid xid; DBUG_ENTER("ha_commit_trans"); /* Just a random warning to test warnings pushed during autocommit. */ @@ -1165,115 +1187,138 @@ int ha_commit_trans(THD *thd, bool all) ha_maria::implicit_commit(thd, FALSE); #endif - if (ha_info) + if (!ha_info) { - uint rw_ha_count; - bool rw_trans; - MDL_request mdl_request; - - DBUG_EXECUTE_IF("crash_commit_before", DBUG_SUICIDE();); - - /* Close all cursors that can not survive COMMIT */ - if (is_real_trans) /* not a statement commit */ - thd->stmt_map.close_transient_cursors(); + /* Free resources and perform other cleanup even for 'empty' transactions. */ + if (is_real_trans) + thd->transaction.cleanup(); + DBUG_RETURN(0); + } - rw_ha_count= ha_check_and_coalesce_trx_read_only(thd, ha_info, all); - /* rw_trans is TRUE when we in a transaction changing data */ - rw_trans= is_real_trans && (rw_ha_count > 0); + DBUG_EXECUTE_IF("crash_commit_before", DBUG_SUICIDE();); - if (rw_trans) - { - /* - Acquire a metadata lock which will ensure that COMMIT is blocked - by an active FLUSH TABLES WITH READ LOCK (and vice versa: - COMMIT in progress blocks FTWRL). + /* Close all cursors that can not survive COMMIT */ + if (is_real_trans) /* not a statement commit */ + thd->stmt_map.close_transient_cursors(); - We allow the owner of FTWRL to COMMIT; we assume that it knows - what it does. - */ - mdl_request.init(MDL_key::COMMIT, "", "", MDL_INTENTION_EXCLUSIVE, - MDL_EXPLICIT); + uint rw_ha_count= ha_check_and_coalesce_trx_read_only(thd, ha_info, all); + /* rw_trans is TRUE when we in a transaction changing data */ + bool rw_trans= is_real_trans && (rw_ha_count > 0); + MDL_request mdl_request; - if (thd->mdl_context.acquire_lock(&mdl_request, - thd->variables.lock_wait_timeout)) - { - ha_rollback_trans(thd, all); - DBUG_RETURN(1); - } + if (rw_trans) + { + /* + Acquire a metadata lock which will ensure that COMMIT is blocked + by an active FLUSH TABLES WITH READ LOCK (and vice versa: + COMMIT in progress blocks FTWRL). - DEBUG_SYNC(thd, "ha_commit_trans_after_acquire_commit_lock"); - } + We allow the owner of FTWRL to COMMIT; we assume that it knows + what it does. + */ + mdl_request.init(MDL_key::COMMIT, "", "", MDL_INTENTION_EXCLUSIVE, + MDL_EXPLICIT); - if (rw_trans && - opt_readonly && - !(thd->security_ctx->master_access & SUPER_ACL) && - !thd->slave_thread) + if (thd->mdl_context.acquire_lock(&mdl_request, + thd->variables.lock_wait_timeout)) { - my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--read-only"); ha_rollback_trans(thd, all); - error= 1; - goto end; + DBUG_RETURN(1); } - if (!trans->no_2pc && (rw_ha_count > 1)) - { - for (; ha_info && !error; ha_info= ha_info->next()) - { - int err; - handlerton *ht= ha_info->ht(); - /* - Do not call two-phase commit if this particular - transaction is read-only. This allows for simpler - implementation in engines that are always read-only. - */ - if (! ha_info->is_trx_read_write()) - continue; - /* - Sic: we know that prepare() is not NULL since otherwise - trans->no_2pc would have been set. - */ - if ((err= ht->prepare(ht, thd, all))) - { - my_error(ER_ERROR_DURING_COMMIT, MYF(0), err); - error= 1; - } - status_var_increment(thd->status_var.ha_prepare_count); - } - DBUG_EXECUTE_IF("crash_commit_after_prepare", DBUG_SUICIDE();); - if (error || (is_real_trans && xid && - (error= !(cookie= tc_log->log_xid(thd, xid))))) - { - ha_rollback_trans(thd, all); - error= 1; - goto end; - } - DBUG_EXECUTE_IF("crash_commit_after_log", DBUG_SUICIDE();); - } - error=ha_commit_one_phase(thd, all) ? (cookie ? 2 : 1) : 0; - DBUG_EXECUTE_IF("crash_commit_before_unlog", DBUG_SUICIDE();); - if (cookie) - if(tc_log->unlog(cookie, xid)) - { - error= 2; - goto end; - } - DBUG_EXECUTE_IF("crash_commit_after", DBUG_SUICIDE();); - RUN_HOOK(transaction, after_commit, (thd, FALSE)); + DEBUG_SYNC(thd, "ha_commit_trans_after_acquire_commit_lock"); + } + + if (rw_trans && + opt_readonly && + !(thd->security_ctx->master_access & SUPER_ACL) && + !thd->slave_thread) + { + my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--read-only"); + goto err; + } + + if (trans->no_2pc || (rw_ha_count <= 1)) + { + error= ha_commit_one_phase(thd, all); + goto done; + } + + need_prepare_ordered= FALSE; + need_commit_ordered= FALSE; + xid= thd->transaction.xid_state.xid.get_my_xid(); + + for (Ha_trx_info *hi= ha_info; hi; hi= hi->next()) + { + int err; + handlerton *ht= hi->ht(); + /* + Do not call two-phase commit if this particular + transaction is read-only. This allows for simpler + implementation in engines that are always read-only. + */ + if (! hi->is_trx_read_write()) + continue; + /* + Sic: we know that prepare() is not NULL since otherwise + trans->no_2pc would have been set. + */ + err= ht->prepare(ht, thd, all); + status_var_increment(thd->status_var.ha_prepare_count); + if (err) + my_error(ER_ERROR_DURING_COMMIT, MYF(0), err); + + if (err) + goto err; + + need_prepare_ordered|= (ht->prepare_ordered != NULL); + need_commit_ordered|= (ht->commit_ordered != NULL); + } + DBUG_EXECUTE_IF("crash_commit_after_prepare", DBUG_SUICIDE();); + + if (!is_real_trans) + { + error= commit_one_phase_2(thd, all, trans, is_real_trans); + goto done; + } + + cookie= tc_log->log_and_order(thd, xid, all, need_prepare_ordered, + need_commit_ordered); + if (!cookie) + goto err; + + DBUG_EXECUTE_IF("crash_commit_after_log", DBUG_SUICIDE();); + + error= commit_one_phase_2(thd, all, trans, is_real_trans) ? 2 : 0; + + DBUG_EXECUTE_IF("crash_commit_before_unlog", DBUG_SUICIDE();); + if (tc_log->unlog(cookie, xid)) + { + error= 2; /* Error during commit */ + goto end; + } + +done: + DBUG_EXECUTE_IF("crash_commit_after", DBUG_SUICIDE();); + RUN_HOOK(transaction, after_commit, (thd, FALSE)); + goto end; + + /* Come here if error and we need to rollback. */ +err: + error= 1; /* Transaction was rolled back */ + ha_rollback_trans(thd, all); + end: - if (rw_trans && mdl_request.ticket) - { - /* - We do not always immediately release transactional locks - after ha_commit_trans() (see uses of ha_enable_transaction()), - thus we release the commit blocker lock as soon as it's - not needed. - */ - thd->mdl_context.release_lock(mdl_request.ticket); - } + if (rw_trans && mdl_request.ticket) + { + /* + We do not always immediately release transactional locks + after ha_commit_trans() (see uses of ha_enable_transaction()), + thus we release the commit blocker lock as soon as it's + not needed. + */ + thd->mdl_context.release_lock(mdl_request.ticket); } - /* Free resources and perform other cleanup even for 'empty' transactions. */ - else if (is_real_trans) - thd->transaction.cleanup(); DBUG_RETURN(error); } @@ -1290,7 +1335,6 @@ end: int ha_commit_one_phase(THD *thd, bool all) { - int error=0; THD_TRANS *trans=all ? &thd->transaction.all : &thd->transaction.stmt; /* "real" is a nick name for a transaction for which a commit will @@ -1306,9 +1350,18 @@ int ha_commit_one_phase(THD *thd, bool all) transaction.all.ha_list, see why in trans_register_ha()). */ bool is_real_trans=all || thd->transaction.all.ha_list == 0; - Ha_trx_info *ha_info= trans->ha_list, *ha_info_next; DBUG_ENTER("ha_commit_one_phase"); + int res= commit_one_phase_2(thd, all, trans, is_real_trans); + DBUG_RETURN(res); +} + +static int +commit_one_phase_2(THD *thd, bool all, THD_TRANS *trans, bool is_real_trans) +{ + int error= 0; + Ha_trx_info *ha_info= trans->ha_list, *ha_info_next; + DBUG_ENTER("commit_one_phase_2"); if (ha_info) { for (; ha_info; ha_info= ha_info_next) @@ -1331,7 +1384,7 @@ int ha_commit_one_phase(THD *thd, bool all) { #ifdef HAVE_QUERY_CACHE if (thd->transaction.changed_tables) - query_cache.invalidate(thd->transaction.changed_tables); + query_cache.invalidate(thd, thd->transaction.changed_tables); #endif } } @@ -1893,7 +1946,16 @@ int ha_start_consistent_snapshot(THD *thd) { bool warn= true; + /* + Holding the LOCK_commit_ordered mutex ensures that we get the same + snapshot for all engines (including the binary log). This allows us + among other things to do backups with + START TRANSACTION WITH CONSISTENT SNAPSHOT and + have a consistent binlog position. + */ + mysql_mutex_lock(&LOCK_commit_ordered); plugin_foreach(thd, snapshot_handlerton, MYSQL_STORAGE_ENGINE_PLUGIN, &warn); + mysql_mutex_unlock(&LOCK_commit_ordered); /* Same idea as when one wants to CREATE TABLE in one engine which does not @@ -2060,7 +2122,8 @@ int ha_delete_table(THD *thd, handlerton *table_type, const char *path, dummy_share.db.length= strlen(db); dummy_share.table_name.str= (char*) alias; dummy_share.table_name.length= strlen(alias); - dummy_table.alias= alias; + dummy_table.alias.set(alias, dummy_share.table_name.length, + table_alias_charset); file->change_table_ptr(&dummy_table, &dummy_share); @@ -2086,28 +2149,34 @@ int ha_delete_table(THD *thd, handlerton *table_type, const char *path, handler *handler::clone(const char *name, MEM_ROOT *mem_root) { handler *new_handler= get_new_handler(table->s, mem_root, ht); + if (! new_handler) + return NULL; + /* Allocate handler->ref here because otherwise ha_open will allocate it on this->table->mem_root and we will not be able to reclaim that memory when the clone handler object is destroyed. */ - if (new_handler && - !(new_handler->ref= (uchar*) alloc_root(mem_root, - ALIGN_SIZE(ref_length)*2))) - new_handler= NULL; + + if (!(new_handler->ref= (uchar*) alloc_root(mem_root, + ALIGN_SIZE(ref_length)*2))) + return NULL; + /* TODO: Implement a more efficient way to have more than one index open for the same table instance. The ha_open call is not cachable for clone. + + This is not critical as the engines already have the table open + and should be able to use the original instance of the table. */ - if (new_handler && new_handler->ha_open(table, - name, - table->db_stat, - HA_OPEN_IGNORE_IF_LOCKED)) - new_handler= NULL; + if (new_handler->ha_open(table, name, table->db_stat, + HA_OPEN_IGNORE_IF_LOCKED)) + return NULL; return new_handler; } + double handler::keyread_time(uint index, uint ranges, ha_rows rows) { /* @@ -2148,7 +2217,7 @@ PSI_table_share *handler::ha_table_share_psi(const TABLE_SHARE *share) const Don't wait for locks if not HA_OPEN_WAIT_IF_LOCKED is set */ int handler::ha_open(TABLE *table_arg, const char *name, int mode, - int test_if_locked) + uint test_if_locked) { int error; DBUG_ENTER("handler::ha_open"); @@ -2192,11 +2261,22 @@ int handler::ha_open(TABLE *table_arg, const char *name, int mode, dup_ref=ref+ALIGN_SIZE(ref_length); cached_table_flags= table_flags(); } - rows_read= rows_changed= 0; - memset(index_rows_read, 0, sizeof(index_rows_read)); + reset_statistics(); + internal_tmp_table= test(test_if_locked & HA_OPEN_INTERNAL_TABLE); DBUG_RETURN(error); } +int handler::ha_close() +{ + DBUG_ENTER("ha_close"); + /* + Increment global statistics for temporary tables. + In_use is 0 for tables that was closed from the table cache. + */ + if (table->in_use) + status_var_add(table->in_use->status_var.rows_tmp_read, rows_tmp_read); + DBUG_RETURN(close()); +} /* Initialize handler for random reading, with error handling */ @@ -2589,8 +2669,9 @@ int handler::update_auto_increment() void handler::column_bitmaps_signal() { DBUG_ENTER("column_bitmaps_signal"); - DBUG_PRINT("info", ("read_set: 0x%lx write_set: 0x%lx", (long) table->read_set, - (long) table->write_set)); + if (table) + DBUG_PRINT("info", ("read_set: 0x%lx write_set: 0x%lx", + (long) table->read_set, (long) table->write_set)); DBUG_VOID_RETURN; } @@ -2667,6 +2748,7 @@ void handler::get_auto_increment(ulonglong offset, ulonglong increment, void handler::ha_release_auto_increment() { + DBUG_ENTER("ha_release_auto_increment"); release_auto_increment(); insert_id_for_cur_row= 0; auto_inc_interval_for_cur_row.replace(0, 0, 0); @@ -2680,6 +2762,7 @@ void handler::ha_release_auto_increment() */ table->in_use->auto_inc_intervals_forced.empty(); } + DBUG_VOID_RETURN; } @@ -2721,17 +2804,11 @@ void handler::print_keydup_error(uint key_nr, const char *msg) - table->alias */ -#ifndef DBUG_OFF #define SET_FATAL_ERROR fatal_error=1 -#else -#define SET_FATAL_ERROR -#endif void handler::print_error(int error, myf errflag) { -#ifndef DBUG_OFF bool fatal_error= 0; -#endif DBUG_ENTER("handler::print_error"); DBUG_PRINT("enter",("error: %d",error)); @@ -2746,6 +2823,11 @@ void handler::print_error(int error, myf errflag) case ENOENT: textno=ER_FILE_NOT_FOUND; break; + case ENOSPC: + case HA_ERR_DISK_FULL: + textno= ER_DISK_FULL; + SET_FATAL_ERROR; // Ensure error is logged + break; case HA_ERR_KEY_NOT_FOUND: case HA_ERR_NO_ACTIVE_RECORD: case HA_ERR_RECORD_DELETED: @@ -2759,6 +2841,12 @@ void handler::print_error(int error, myf errflag) SET_FATAL_ERROR; textno=ER_KEY_NOT_FOUND; break; + case HA_ERR_ABORTED_BY_USER: + { + DBUG_ASSERT(table->in_use->killed); + table->in_use->send_kill_message(); + DBUG_VOID_RETURN; + } case HA_ERR_WRONG_MRG_TABLE_DEF: textno=ER_WRONG_MRG_TABLE; break; @@ -2808,7 +2896,10 @@ void handler::print_error(int error, myf errflag) textno=ER_DUP_UNIQUE; break; case HA_ERR_RECORD_CHANGED: - SET_FATAL_ERROR; + /* + This is not fatal error when using HANDLER interface + SET_FATAL_ERROR; + */ textno=ER_CHECKREAD; break; case HA_ERR_CRASHED: @@ -2930,11 +3021,12 @@ void handler::print_error(int error, myf errflag) { const char* engine= table_type(); if (temporary) - my_error(ER_GET_TEMPORARY_ERRMSG, MYF(0), error, str.ptr(), engine); + my_error(ER_GET_TEMPORARY_ERRMSG, MYF(0), error, str.c_ptr(), + engine); else { SET_FATAL_ERROR; - my_error(ER_GET_ERRMSG, MYF(0), error, str.ptr(), engine); + my_error(ER_GET_ERRMSG, MYF(0), error, str.c_ptr(), engine); } } else @@ -2942,6 +3034,15 @@ void handler::print_error(int error, myf errflag) DBUG_VOID_RETURN; } } + if (fatal_error && (debug_assert_if_crashed_table || + global_system_variables.log_warnings > 1)) + { + /* + Log error to log before we crash or if extended warnings are requested + */ + errflag|= ME_NOREFRESH; + } + my_error(textno, errflag, table_share->table_name.str, error); DBUG_VOID_RETURN; } @@ -3197,7 +3298,7 @@ int handler::rename_table(const char * from, const char * to) void handler::drop_table(const char *name) { - close(); + ha_close(); delete_table(name); } @@ -3498,6 +3599,9 @@ handler::ha_delete_table(const char *name) Drop table in the engine: public interface. @sa handler::drop_table() + + The difference between this and delete_table() is that the table is open in + drop_table(). */ void @@ -3702,6 +3806,7 @@ void handler::update_global_table_stats() TABLE_STATS * table_stats; status_var_add(table->in_use->status_var.rows_read, rows_read); + DBUG_ASSERT(rows_tmp_read == 0); if (!table->in_use->userstat_running) { @@ -3952,6 +4057,7 @@ ha_check_if_table_exists(THD* thd, const char *db, const char *name, void st_ha_check_opt::init() { flags= sql_flags= 0; + start_time= my_time(0); } @@ -4487,11 +4593,11 @@ int handler::index_read_idx_map(uchar * buf, uint index, const uchar * key, int error, error1; LINT_INIT(error1); - error= index_init(index, 0); + error= ha_index_init(index, 0); if (!error) { error= index_read_map(buf, key, keypart_map, find_flag); - error1= index_end(); + error1= ha_index_end(); } return error ? error : error1; } @@ -4666,7 +4772,8 @@ static bool check_table_binlog_row_based(THD *thd, TABLE *table) /** @brief Write table maps for all (manually or automatically) locked tables - to the binary log. + to the binary log. Also, if binlog_annotate_rows_events is ON, + write Annotate_rows event before the first table map. SYNOPSIS write_locked_table_maps() @@ -4698,6 +4805,9 @@ static int write_locked_table_maps(THD *thd) MYSQL_LOCK *locks[2]; locks[0]= thd->extra_lock; locks[1]= thd->lock; + my_bool with_annotate= thd->variables.binlog_annotate_rows_events && + thd->query() && thd->query_length(); + for (uint i= 0 ; i < sizeof(locks)/sizeof(*locks) ; ++i ) { MYSQL_LOCK const *const lock= locks[i]; @@ -4729,7 +4839,8 @@ static int write_locked_table_maps(THD *thd) */ bool const has_trans= thd->lex->sql_command == SQLCOM_CREATE_TABLE || table->file->has_transactions(); - int const error= thd->binlog_write_table_map(table, has_trans); + int const error= thd->binlog_write_table_map(table, has_trans, + &with_annotate); /* If an error occurs, it is the responsibility of the caller to roll back the transaction. |