diff options
Diffstat (limited to 'sql/handler.cc')
-rw-r--r-- | sql/handler.cc | 645 |
1 files changed, 388 insertions, 257 deletions
diff --git a/sql/handler.cc b/sql/handler.cc index 04f10586484..50db743a8ff 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -24,16 +24,34 @@ #pragma implementation // gcc: Class implementation #endif -#include "mysql_priv.h" +#include "sql_priv.h" +#include "unireg.h" +#include "rpl_handler.h" +#include "sql_cache.h" // query_cache, query_cache_* +#include "sql_connect.h" // global_table_stats +#include "key.h" // key_copy, key_unpack, key_cmp_if_same, key_cmp +#include "sql_table.h" // build_table_filename +#include "sql_parse.h" // check_stack_overrun +#include "sql_acl.h" // SUPER_ACL +#include "sql_base.h" // free_io_cache +#include "discover.h" // writefrm +#include "log_event.h" // *_rows_log_event #include "create_options.h" #include "rpl_filter.h" #include <myisampack.h> +#include "transaction.h" #include "myisam.h" +#include "probes_mysql.h" +#include "debug_sync.h" // DEBUG_SYNC #ifdef WITH_PARTITION_STORAGE_ENGINE #include "ha_partition.h" #endif +#ifdef WITH_ARIA_STORAGE_ENGINE +#include "../storage/maria/ha_maria.h" +#endif + /* While we have legacy_db_type, we have this array to check for dups and to find handlerton from legacy_db_type. @@ -45,7 +63,8 @@ static handlerton *installed_htons[128]; #define BITMAP_STACKBUF_SIZE (128/8) -KEY_CREATE_INFO default_key_create_info= { HA_KEY_ALG_UNDEF, 0, {NullS,0} }; +KEY_CREATE_INFO default_key_create_info= + { HA_KEY_ALG_UNDEF, 0, {NullS, 0}, {NullS, 0} }; /* number of entries in handlertons[] */ ulong total_ha= 0; @@ -82,7 +101,6 @@ 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) @@ -175,15 +193,6 @@ plugin_ref ha_lock_engine(THD *thd, const handlerton *hton) } -#ifdef NOT_USED -static handler *create_default(TABLE_SHARE *table, MEM_ROOT *mem_root) -{ - handlerton *hton= ha_default_handlerton(current_thd); - return (hton && hton->create) ? hton->create(hton, table, mem_root) : NULL; -} -#endif - - handlerton *ha_resolve_by_legacy_type(THD *thd, enum legacy_db_type db_type) { plugin_ref plugin; @@ -221,11 +230,9 @@ handlerton *ha_checktype(THD *thd, enum legacy_db_type database_type, return NULL; } + RUN_HOOK(transaction, after_rollback, (thd, FALSE)); + switch (database_type) { -#ifndef NO_HASH - case DB_TYPE_HASH: - return ha_resolve_by_legacy_type(thd, DB_TYPE_HASH); -#endif case DB_TYPE_MRG_ISAM: return ha_resolve_by_legacy_type(thd, DB_TYPE_MRG_MYISAM); default: @@ -282,6 +289,16 @@ handler *get_ha_partition(partition_info *part_info) #endif +static const char **handler_errmsgs; + +C_MODE_START +static const char **get_handler_errmsgs() +{ + return handler_errmsgs; +} +C_MODE_END + + /** Register handler error messages for use with my_error(). @@ -293,62 +310,63 @@ handler *get_ha_partition(partition_info *part_info) int ha_init_errors(void) { -#define SETMSG(nr, msg) errmsgs[(nr) - HA_ERR_FIRST]= (msg) - const char **errmsgs; +#define SETMSG(nr, msg) handler_errmsgs[(nr) - HA_ERR_FIRST]= (msg) /* Allocate a pointer array for the error message strings. */ /* Zerofill it to avoid uninitialized gaps. */ - if (! (errmsgs= (const char**) my_malloc(HA_ERR_ERRORS * sizeof(char*), - MYF(MY_WME | MY_ZEROFILL)))) + if (! (handler_errmsgs= (const char**) my_malloc(HA_ERR_ERRORS * sizeof(char*), + MYF(MY_WME | MY_ZEROFILL)))) return 1; /* Set the dedicated error messages. */ - SETMSG(HA_ERR_KEY_NOT_FOUND, ER(ER_KEY_NOT_FOUND)); - SETMSG(HA_ERR_FOUND_DUPP_KEY, ER(ER_DUP_KEY)); + SETMSG(HA_ERR_KEY_NOT_FOUND, ER_DEFAULT(ER_KEY_NOT_FOUND)); + SETMSG(HA_ERR_FOUND_DUPP_KEY, ER_DEFAULT(ER_DUP_KEY)); SETMSG(HA_ERR_RECORD_CHANGED, "Update wich is recoverable"); SETMSG(HA_ERR_WRONG_INDEX, "Wrong index given to function"); - SETMSG(HA_ERR_CRASHED, ER(ER_NOT_KEYFILE)); - SETMSG(HA_ERR_WRONG_IN_RECORD, ER(ER_CRASHED_ON_USAGE)); + SETMSG(HA_ERR_CRASHED, ER_DEFAULT(ER_NOT_KEYFILE)); + SETMSG(HA_ERR_WRONG_IN_RECORD, ER_DEFAULT(ER_CRASHED_ON_USAGE)); SETMSG(HA_ERR_OUT_OF_MEM, "Table handler out of memory"); SETMSG(HA_ERR_NOT_A_TABLE, "Incorrect file format '%.64s'"); SETMSG(HA_ERR_WRONG_COMMAND, "Command not supported"); - SETMSG(HA_ERR_OLD_FILE, ER(ER_OLD_KEYFILE)); + SETMSG(HA_ERR_OLD_FILE, ER_DEFAULT(ER_OLD_KEYFILE)); SETMSG(HA_ERR_NO_ACTIVE_RECORD, "No record read in update"); SETMSG(HA_ERR_RECORD_DELETED, "Intern record deleted"); - SETMSG(HA_ERR_RECORD_FILE_FULL, ER(ER_RECORD_FILE_FULL)); + SETMSG(HA_ERR_RECORD_FILE_FULL, ER_DEFAULT(ER_RECORD_FILE_FULL)); SETMSG(HA_ERR_INDEX_FILE_FULL, "No more room in index file '%.64s'"); SETMSG(HA_ERR_END_OF_FILE, "End in next/prev/first/last"); - SETMSG(HA_ERR_UNSUPPORTED, ER(ER_ILLEGAL_HA)); + SETMSG(HA_ERR_UNSUPPORTED, ER_DEFAULT(ER_ILLEGAL_HA)); SETMSG(HA_ERR_TO_BIG_ROW, "Too big row"); SETMSG(HA_WRONG_CREATE_OPTION, "Wrong create option"); - SETMSG(HA_ERR_FOUND_DUPP_UNIQUE, ER(ER_DUP_UNIQUE)); + SETMSG(HA_ERR_FOUND_DUPP_UNIQUE, ER_DEFAULT(ER_DUP_UNIQUE)); SETMSG(HA_ERR_UNKNOWN_CHARSET, "Can't open charset"); - SETMSG(HA_ERR_WRONG_MRG_TABLE_DEF, ER(ER_WRONG_MRG_TABLE)); - SETMSG(HA_ERR_CRASHED_ON_REPAIR, ER(ER_CRASHED_ON_REPAIR)); - SETMSG(HA_ERR_CRASHED_ON_USAGE, ER(ER_CRASHED_ON_USAGE)); - SETMSG(HA_ERR_LOCK_WAIT_TIMEOUT, ER(ER_LOCK_WAIT_TIMEOUT)); - SETMSG(HA_ERR_LOCK_TABLE_FULL, ER(ER_LOCK_TABLE_FULL)); - SETMSG(HA_ERR_READ_ONLY_TRANSACTION, ER(ER_READ_ONLY_TRANSACTION)); - SETMSG(HA_ERR_LOCK_DEADLOCK, ER(ER_LOCK_DEADLOCK)); - SETMSG(HA_ERR_CANNOT_ADD_FOREIGN, ER(ER_CANNOT_ADD_FOREIGN)); - SETMSG(HA_ERR_NO_REFERENCED_ROW, ER(ER_NO_REFERENCED_ROW_2)); - SETMSG(HA_ERR_ROW_IS_REFERENCED, ER(ER_ROW_IS_REFERENCED_2)); + SETMSG(HA_ERR_WRONG_MRG_TABLE_DEF, ER_DEFAULT(ER_WRONG_MRG_TABLE)); + SETMSG(HA_ERR_CRASHED_ON_REPAIR, ER_DEFAULT(ER_CRASHED_ON_REPAIR)); + SETMSG(HA_ERR_CRASHED_ON_USAGE, ER_DEFAULT(ER_CRASHED_ON_USAGE)); + SETMSG(HA_ERR_LOCK_WAIT_TIMEOUT, ER_DEFAULT(ER_LOCK_WAIT_TIMEOUT)); + SETMSG(HA_ERR_LOCK_TABLE_FULL, ER_DEFAULT(ER_LOCK_TABLE_FULL)); + SETMSG(HA_ERR_READ_ONLY_TRANSACTION, ER_DEFAULT(ER_READ_ONLY_TRANSACTION)); + SETMSG(HA_ERR_LOCK_DEADLOCK, ER_DEFAULT(ER_LOCK_DEADLOCK)); + SETMSG(HA_ERR_CANNOT_ADD_FOREIGN, ER_DEFAULT(ER_CANNOT_ADD_FOREIGN)); + SETMSG(HA_ERR_NO_REFERENCED_ROW, ER_DEFAULT(ER_NO_REFERENCED_ROW_2)); + SETMSG(HA_ERR_ROW_IS_REFERENCED, ER_DEFAULT(ER_ROW_IS_REFERENCED_2)); SETMSG(HA_ERR_NO_SAVEPOINT, "No savepoint with that name"); SETMSG(HA_ERR_NON_UNIQUE_BLOCK_SIZE, "Non unique key block size"); SETMSG(HA_ERR_NO_SUCH_TABLE, "No such table: '%.64s'"); - SETMSG(HA_ERR_TABLE_EXIST, ER(ER_TABLE_EXISTS_ERROR)); + SETMSG(HA_ERR_TABLE_EXIST, ER_DEFAULT(ER_TABLE_EXISTS_ERROR)); SETMSG(HA_ERR_NO_CONNECTION, "Could not connect to storage engine"); - SETMSG(HA_ERR_TABLE_DEF_CHANGED, ER(ER_TABLE_DEF_CHANGED)); + SETMSG(HA_ERR_TABLE_DEF_CHANGED, ER_DEFAULT(ER_TABLE_DEF_CHANGED)); SETMSG(HA_ERR_FOREIGN_DUPLICATE_KEY, "FK constraint would lead to duplicate key"); - SETMSG(HA_ERR_TABLE_NEEDS_UPGRADE, ER(ER_TABLE_NEEDS_UPGRADE)); - SETMSG(HA_ERR_TABLE_READONLY, ER(ER_OPEN_AS_READONLY)); - SETMSG(HA_ERR_AUTOINC_READ_FAILED, ER(ER_AUTOINC_READ_FAILED)); - SETMSG(HA_ERR_AUTOINC_ERANGE, ER(ER_WARN_DATA_OUT_OF_RANGE)); - SETMSG(HA_ERR_TOO_MANY_CONCURRENT_TRXS, ER(ER_TOO_MANY_CONCURRENT_TRXS)); - SETMSG(HA_ERR_DISK_FULL, ER(ER_DISK_FULL)); + SETMSG(HA_ERR_TABLE_NEEDS_UPGRADE, ER_DEFAULT(ER_TABLE_NEEDS_UPGRADE)); + SETMSG(HA_ERR_TABLE_READONLY, ER_DEFAULT(ER_OPEN_AS_READONLY)); + SETMSG(HA_ERR_AUTOINC_READ_FAILED, ER_DEFAULT(ER_AUTOINC_READ_FAILED)); + 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_INDEX_CORRUPT, ER_DEFAULT(ER_INDEX_CORRUPT)); + SETMSG(HA_ERR_DISK_FULL, ER_DEFAULT(ER_DISK_FULL)); /* Register the error messages for use with my_error(). */ - return my_error_register(errmsgs, HA_ERR_FIRST, HA_ERR_LAST); + return my_error_register(get_handler_errmsgs, HA_ERR_FIRST, HA_ERR_LAST); } @@ -367,7 +385,7 @@ static int ha_finish_errors(void) /* Allocate a pointer array for the error message strings. */ if (! (errmsgs= my_error_unregister(HA_ERR_FIRST, HA_ERR_LAST))) return 1; - my_free((uchar*) errmsgs, MYF(0)); + my_free(errmsgs); return 0; } @@ -413,9 +431,15 @@ int ha_finalize_handlerton(st_plugin_int *plugin) reuse an array slot. Otherwise the number of uninstall/install cycles would be limited. */ - hton2plugin[hton->slot]= NULL; + if (hton->slot != HA_SLOT_UNDEF) + { + /* Make sure we are not unpluging another plugin */ + DBUG_ASSERT(hton2plugin[hton->slot] == plugin); + DBUG_ASSERT(hton->slot < MAX_HA); + hton2plugin[hton->slot]= NULL; + } - my_free((uchar*)hton, MYF(0)); + my_free(hton); end: DBUG_RETURN(0); @@ -430,6 +454,15 @@ int ha_initialize_handlerton(st_plugin_int *plugin) hton= (handlerton *)my_malloc(sizeof(handlerton), MYF(MY_WME | MY_ZEROFILL)); + + if (hton == NULL) + { + sql_print_error("Unable to allocate memory for plugin '%s' handlerton.", + plugin->name.str); + goto err_no_hton_memory; + } + + hton->slot= HA_SLOT_UNDEF; /* Historical Requirement */ plugin->data= hton; // shortcut for the future if (plugin->plugin->init && plugin->plugin->init(hton)) @@ -463,8 +496,6 @@ int ha_initialize_handlerton(st_plugin_int *plugin) if (idx == (int) DB_TYPE_DEFAULT) { sql_print_warning("Too many storage engines!"); - my_free(hton, MYF(0)); - plugin->data= 0; goto err_deinit; } if (hton->db_type != DB_TYPE_UNKNOWN) @@ -545,7 +576,8 @@ err_deinit: (void) plugin->plugin->deinit(NULL); err: - my_free((uchar*) hton, MYF(0)); + my_free(hton); +err_no_hton_memory: plugin->data= NULL; DBUG_RETURN(1); } @@ -886,16 +918,16 @@ void ha_close_connection(THD* thd) a transaction in a given engine is read-write and will not involve the two-phase commit protocol! - At the end of a statement, server call - ha_autocommit_or_rollback() is invoked. This call in turn - invokes handlerton::prepare() for every involved engine. - Prepare is followed by a call to handlerton::commit_one_phase() - If a one-phase commit will suffice, handlerton::prepare() is not - invoked and the server only calls handlerton::commit_one_phase(). - At statement commit, the statement-related read-write engine - flag is propagated to the corresponding flag in the normal - transaction. When the commit is complete, the list of registered - engines is cleared. + At the end of a statement, server call trans_commit_stmt is + invoked. This call in turn invokes handlerton::prepare() + for every involved engine. Prepare is followed by a call + to handlerton::commit_one_phase() If a one-phase commit + will suffice, handlerton::prepare() is not invoked and + the server only calls handlerton::commit_one_phase(). + At statement commit, the statement-related read-write + engine flag is propagated to the corresponding flag in the + normal transaction. When the commit is complete, the list + of registered engines is cleared. Rollback is handled in a similar fashion. @@ -906,7 +938,7 @@ void ha_close_connection(THD* thd) do not "register" in thd->transaction lists, and thus do not modify the transaction state. Besides, each DDL in MySQL is prefixed with an implicit normal transaction commit - (a call to end_active_trans()), and thus leaves nothing + (a call to trans_commit_implicit()), and thus leaves nothing to modify. However, as it has been pointed out with CREATE TABLE .. SELECT, some DDL statements can start a *new* transaction. @@ -990,7 +1022,7 @@ int ha_prepare(THD *thd) THD_TRANS *trans=all ? &thd->transaction.all : &thd->transaction.stmt; Ha_trx_info *ha_info= trans->ha_list; DBUG_ENTER("ha_prepare"); -#ifdef USING_TRANSACTIONS + if (ha_info) { for (; ha_info; ha_info= ha_info->next()) @@ -1016,7 +1048,7 @@ int ha_prepare(THD *thd) } } } -#endif /* USING_TRANSACTIONS */ + DBUG_RETURN(error); } @@ -1133,6 +1165,7 @@ int ha_commit_trans(THD *thd, bool all) if (thd->in_sub_stmt) { + DBUG_ASSERT(0); /* Since we don't support nested statement transactions in 5.0, we can't commit or rollback stmt transactions while we are inside @@ -1147,11 +1180,14 @@ int ha_commit_trans(THD *thd, bool all) bail out with error even before ha_commit_trans() call. To be 100% safe let us throw error in non-debug builds. */ - DBUG_ASSERT(0); my_error(ER_COMMIT_NOT_ALLOWED_IN_SF_OR_TRG, MYF(0)); DBUG_RETURN(2); } -#ifdef USING_TRANSACTIONS + +#ifdef WITH_ARIA_STORAGE_ENGINE + ha_maria::implicit_commit(thd, FALSE); +#endif + if (!ha_info) { /* Free resources and perform other cleanup even for 'empty' transactions. */ @@ -1169,12 +1205,29 @@ int ha_commit_trans(THD *thd, bool all) 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 (rw_trans && - wait_if_global_read_lock(thd, 0, 0)) + if (rw_trans) { - ha_rollback_trans(thd, all); - DBUG_RETURN(1); + /* + 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). + + 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 (thd->mdl_context.acquire_lock(&mdl_request, + thd->variables.lock_wait_timeout)) + { + ha_rollback_trans(thd, all); + DBUG_RETURN(1); + } + + DEBUG_SYNC(thd, "ha_commit_trans_after_acquire_commit_lock"); } if (rw_trans && @@ -1189,8 +1242,7 @@ int ha_commit_trans(THD *thd, bool all) if (trans->no_2pc || (rw_ha_count <= 1)) { error= ha_commit_one_phase(thd, all); - DBUG_EXECUTE_IF("crash_commit_after", DBUG_SUICIDE();); - goto end; + goto done; } need_prepare_ordered= FALSE; @@ -1228,8 +1280,7 @@ int ha_commit_trans(THD *thd, bool all) if (!is_real_trans) { error= commit_one_phase_2(thd, all, trans, is_real_trans); - DBUG_EXECUTE_IF("crash_commit_after", DBUG_SUICIDE();); - goto end; + goto done; } cookie= tc_log->log_and_order(thd, xid, all, need_prepare_ordered, @@ -1240,7 +1291,6 @@ int ha_commit_trans(THD *thd, bool all) 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_after", DBUG_SUICIDE();); DBUG_EXECUTE_IF("crash_commit_before_unlog", DBUG_SUICIDE();); if (tc_log->unlog(cookie, xid)) @@ -1249,7 +1299,9 @@ int ha_commit_trans(THD *thd, bool all) 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. */ @@ -1258,38 +1310,59 @@ err: ha_rollback_trans(thd, all); end: - if (rw_trans) - start_waiting_global_read_lock(thd); -#endif /* USING_TRANSACTIONS */ + 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); + } DBUG_RETURN(error); } /** @note This function does not care about global read lock. A caller should. + + @param[in] all Is set in case of explicit commit + (COMMIT statement), or implicit commit + issued by DDL. Is not set when called + at the end of statement, even if + autocommit=1. */ + int ha_commit_one_phase(THD *thd, bool all) { THD_TRANS *trans=all ? &thd->transaction.all : &thd->transaction.stmt; /* "real" is a nick name for a transaction for which a commit will make persistent changes. E.g. a 'stmt' transaction inside a 'all' - transation is not 'real': even though it's possible to commit it, + transaction is not 'real': even though it's possible to commit it, the changes are not durable as they might be rolled back if the enclosing 'all' transaction is rolled back. + We establish the value of 'is_real_trans' by checking + if it's an explicit COMMIT/BEGIN statement, or implicit + commit issued by DDL (all == TRUE), or if we're running + in autocommit mode (it's only in the autocommit mode + ha_commit_one_phase() can be called with an empty + transaction.all.ha_list, see why in trans_register_ha()). */ bool is_real_trans=all || thd->transaction.all.ha_list == 0; DBUG_ENTER("ha_commit_one_phase"); - DBUG_RETURN(commit_one_phase_2(thd, all, trans, is_real_trans)); + 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"); -#ifdef USING_TRANSACTIONS if (ha_info) { for (; ha_info; ha_info= ha_info_next) @@ -1314,13 +1387,12 @@ commit_one_phase_2(THD *thd, bool all, THD_TRANS *trans, bool is_real_trans) if (thd->transaction.changed_tables) query_cache.invalidate(thd, thd->transaction.changed_tables); #endif - thd->variables.tx_isolation=thd->session_tx_isolation; } } /* Free resources and perform other cleanup even for 'empty' transactions. */ if (is_real_trans) thd->transaction.cleanup(); -#endif /* USING_TRANSACTIONS */ + DBUG_RETURN(error); } @@ -1333,9 +1405,15 @@ int ha_rollback_trans(THD *thd, bool all) /* "real" is a nick name for a transaction for which a commit will make persistent changes. E.g. a 'stmt' transaction inside a 'all' - transation is not 'real': even though it's possible to commit it, + transaction is not 'real': even though it's possible to commit it, the changes are not durable as they might be rolled back if the enclosing 'all' transaction is rolled back. + We establish the value of 'is_real_trans' by checking + if it's an explicit COMMIT or BEGIN statement, or implicit + commit issued by DDL (in these cases all == TRUE), + or if we're running in autocommit mode (it's only in the autocommit mode + ha_commit_one_phase() is called with an empty + transaction.all.ha_list, see why in trans_register_ha()). */ bool is_real_trans=all || thd->transaction.all.ha_list == 0; DBUG_ENTER("ha_rollback_trans"); @@ -1349,6 +1427,7 @@ int ha_rollback_trans(THD *thd, bool all) if (thd->in_sub_stmt) { + DBUG_ASSERT(0); /* If we are inside stored function or trigger we should not commit or rollback current statement transaction. See comment in ha_commit_trans() @@ -1356,11 +1435,10 @@ int ha_rollback_trans(THD *thd, bool all) */ if (!all) DBUG_RETURN(0); - DBUG_ASSERT(0); my_error(ER_COMMIT_NOT_ALLOWED_IN_SF_OR_TRG, MYF(0)); DBUG_RETURN(1); } -#ifdef USING_TRANSACTIONS + if (ha_info) { /* Close all cursors that can not survive ROLLBACK */ @@ -1384,14 +1462,11 @@ int ha_rollback_trans(THD *thd, bool all) trans->no_2pc=0; if (is_real_trans && thd->transaction_rollback_request && thd->transaction.xid_state.xa_state != XA_NOTR) - thd->transaction.xid_state.rm_error= thd->main_da.sql_errno(); - if (all) - thd->variables.tx_isolation=thd->session_tx_isolation; + thd->transaction.xid_state.rm_error= thd->stmt_da->sql_errno(); } - /* Always cleanup. Even if there nht==0. There may be savepoints. */ + /* Always cleanup. Even if nht==0. There may be savepoints. */ if (is_real_trans) thd->transaction.cleanup(); -#endif /* USING_TRANSACTIONS */ if (all) thd->transaction_rollback_request= FALSE; @@ -1413,41 +1488,7 @@ int ha_rollback_trans(THD *thd, bool all) push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARNING_NOT_COMPLETE_ROLLBACK, ER(ER_WARNING_NOT_COMPLETE_ROLLBACK)); - DBUG_RETURN(error); -} - -/** - This is used to commit or rollback a single statement depending on - the value of error. - - @note - Note that if the autocommit is on, then the following call inside - InnoDB will commit or rollback the whole transaction (= the statement). The - autocommit mechanism built into InnoDB is based on counting locks, but if - the user has used LOCK TABLES then that mechanism does not know to do the - commit. -*/ -int ha_autocommit_or_rollback(THD *thd, int error) -{ - DBUG_ENTER("ha_autocommit_or_rollback"); -#ifdef USING_TRANSACTIONS - if (thd->transaction.stmt.ha_list) - { - if (!error) - { - if (ha_commit_trans(thd, 0)) - error=1; - } - else - { - (void) ha_rollback_trans(thd, 0); - if (thd->transaction_rollback_request && !thd->in_sub_stmt) - (void) ha_rollback(thd); - } - - thd->variables.tx_isolation=thd->session_tx_isolation; - } -#endif + RUN_HOOK(transaction, after_rollback, (thd, FALSE)); DBUG_RETURN(error); } @@ -1608,7 +1649,7 @@ static my_bool xarecover_handlerton(THD *unused, plugin_ref plugin, } // recovery mode if (info->commit_list ? - hash_search(info->commit_list, (uchar *)&x, sizeof(x)) != 0 : + my_hash_search(info->commit_list, (uchar *)&x, sizeof(x)) != 0 : tc_heuristic_recover == TC_HEURISTIC_RECOVER_COMMIT) { #ifndef DBUG_OFF @@ -1669,7 +1710,7 @@ int ha_recover(HASH *commit_list) plugin_foreach(NULL, xarecover_handlerton, MYSQL_STORAGE_ENGINE_PLUGIN, &info); - my_free((uchar*)info.list, MYF(0)); + my_free(info.list); if (info.found_foreign_xids) sql_print_warning("Found %d prepared XA transactions", info.found_foreign_xids); @@ -1710,12 +1751,12 @@ bool mysql_xa_recover(THD *thd) field_list.push_back(new Item_int("bqual_length", 0, MY_INT32_NUM_DECIMAL_DIGITS)); field_list.push_back(new Item_empty_string("data",XIDDATASIZE)); - if (protocol->send_fields(&field_list, + if (protocol->send_result_set_metadata(&field_list, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) DBUG_RETURN(1); - pthread_mutex_lock(&LOCK_xid_cache); - while ((xs= (XID_STATE*)hash_element(&xid_cache, i++))) + mysql_mutex_lock(&LOCK_xid_cache); + while ((xs= (XID_STATE*) my_hash_element(&xid_cache, i++))) { if (xs->xa_state==XA_PREPARED) { @@ -1727,13 +1768,13 @@ bool mysql_xa_recover(THD *thd) &my_charset_bin); if (protocol->write()) { - pthread_mutex_unlock(&LOCK_xid_cache); + mysql_mutex_unlock(&LOCK_xid_cache); DBUG_RETURN(1); } } } - pthread_mutex_unlock(&LOCK_xid_cache); + mysql_mutex_unlock(&LOCK_xid_cache); my_eof(thd); DBUG_RETURN(0); } @@ -1840,7 +1881,7 @@ int ha_savepoint(THD *thd, SAVEPOINT *sv) &thd->transaction.all); Ha_trx_info *ha_info= trans->ha_list; DBUG_ENTER("ha_savepoint"); -#ifdef USING_TRANSACTIONS + for (; ha_info; ha_info= ha_info->next()) { int err; @@ -1864,7 +1905,7 @@ int ha_savepoint(THD *thd, SAVEPOINT *sv) engines are prepended to the beginning of the list. */ sv->ha_list= trans->ha_list; -#endif /* USING_TRANSACTIONS */ + DBUG_RETURN(error); } @@ -1917,9 +1958,9 @@ int ha_start_consistent_snapshot(THD *thd) START TRANSACTION WITH CONSISTENT SNAPSHOT and have a consistent binlog position. */ - pthread_mutex_lock(&LOCK_commit_ordered); + mysql_mutex_lock(&LOCK_commit_ordered); plugin_foreach(thd, snapshot_handlerton, MYSQL_STORAGE_ENGINE_PLUGIN, &warn); - pthread_mutex_unlock(&LOCK_commit_ordered); + mysql_mutex_unlock(&LOCK_commit_ordered); /* Same idea as when one wants to CREATE TABLE in one engine which does not @@ -2019,23 +2060,28 @@ const char *get_canonical_filename(handler *file, const char *path, struct Ha_delete_table_error_handler: public Internal_error_handler { public: - virtual bool handle_error(uint sql_errno, - const char *message, - MYSQL_ERROR::enum_warning_level level, - THD *thd); + virtual bool handle_condition(THD *thd, + uint sql_errno, + const char* sqlstate, + MYSQL_ERROR::enum_warning_level level, + const char* msg, + MYSQL_ERROR ** cond_hdl); char buff[MYSQL_ERRMSG_SIZE]; }; bool Ha_delete_table_error_handler:: -handle_error(uint sql_errno, - const char *message, - MYSQL_ERROR::enum_warning_level level, - THD *thd) -{ +handle_condition(THD *, + uint, + const char*, + MYSQL_ERROR::enum_warning_level, + const char* msg, + MYSQL_ERROR ** cond_hdl) +{ + *cond_hdl= NULL; /* Grab the error message */ - strmake(buff, message, sizeof(buff)-1); + strmake(buff, msg, sizeof(buff)-1); return TRUE; } @@ -2095,7 +2141,7 @@ int ha_delete_table(THD *thd, handlerton *table_type, const char *path, XXX: should we convert *all* errors to warnings here? What if the error is fatal? */ - push_warning(thd, MYSQL_ERROR::WARN_LEVEL_ERROR, error, + push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, error, ha_delete_table_error_handler.buff); } delete file; @@ -2132,7 +2178,6 @@ handler *handler::clone(const char *name, MEM_ROOT *mem_root) HA_OPEN_IGNORE_IF_LOCKED)) return NULL; - new_handler->cloned= 1; // Marker for debugging return new_handler; } @@ -2164,6 +2209,10 @@ THD *handler::ha_thd(void) const return (table && table->in_use) ? table->in_use : current_thd; } +PSI_table_share *handler::ha_table_share_psi(const TABLE_SHARE *share) const +{ + return share->m_psi; +} /** @brief Open database-handler. @@ -2585,7 +2634,7 @@ int handler::update_auto_increment() variables->auto_increment_increment); auto_inc_intervals_count++; /* Row-based replication does not need to store intervals in binlog */ - if (mysql_bin_log.is_open() && !thd->current_stmt_binlog_row_based) + if (mysql_bin_log.is_open() && !thd->is_current_stmt_binlog_format_row()) thd->auto_inc_intervals_in_cur_stmt_for_binlog.append(auto_inc_interval_for_cur_row.minimum(), auto_inc_interval_for_cur_row.values(), variables->auto_increment_increment); @@ -2786,6 +2835,7 @@ void handler::print_error(int error, myf errflag) break; case HA_ERR_KEY_NOT_FOUND: case HA_ERR_NO_ACTIVE_RECORD: + case HA_ERR_RECORD_DELETED: case HA_ERR_END_OF_FILE: /* This errors is not not normally fatal (for example for reads). However @@ -2963,6 +3013,9 @@ void handler::print_error(int error, myf errflag) case HA_ERR_TABLE_NEEDS_UPGRADE: textno=ER_TABLE_NEEDS_UPGRADE; break; + case HA_ERR_NO_PARTITION_FOUND: + textno=ER_WRONG_PARTITION_NAME; + break; case HA_ERR_TABLE_READONLY: textno= ER_OPEN_AS_READONLY; break; @@ -2975,6 +3028,15 @@ void handler::print_error(int error, myf errflag) case HA_ERR_TOO_MANY_CONCURRENT_TRXS: textno= ER_TOO_MANY_CONCURRENT_TRXS; break; + case HA_ERR_INDEX_COL_TOO_LONG: + textno= ER_INDEX_COLUMN_TOO_LONG; + break; + case HA_ERR_INDEX_CORRUPT: + textno= ER_INDEX_CORRUPT; + break; + case HA_ERR_UNDO_REC_TOO_BIG: + textno= ER_UNDO_RECORD_TOO_BIG; + break; default: { /* The error was "unknown" to this function. @@ -3013,7 +3075,6 @@ void handler::print_error(int error, myf errflag) } } my_error(textno, errflag, table_share->table_name.str, error); - DBUG_ASSERT(!fatal_error || !debug_assert_if_crashed_table); DBUG_VOID_RETURN; } @@ -3160,27 +3221,21 @@ static bool update_frm_version(TABLE *table) strxmov(path, table->s->normalized_path.str, reg_ext, NullS); - if ((file= my_open(path, O_RDWR|O_BINARY, MYF(MY_WME))) >= 0) + if ((file= mysql_file_open(key_file_frm, + path, O_RDWR|O_BINARY, MYF(MY_WME))) >= 0) { uchar version[4]; - char *key= table->s->table_cache_key.str; - uint key_length= table->s->table_cache_key.length; - TABLE *entry; - HASH_SEARCH_STATE state; int4store(version, MYSQL_VERSION_ID); - if ((result= my_pwrite(file,(uchar*) version,4,51L,MYF_RW))) + if ((result= mysql_file_pwrite(file, (uchar*) version, 4, 51L, MYF_RW))) goto err; - for (entry=(TABLE*) hash_first(&open_cache,(uchar*) key,key_length, &state); - entry; - entry= (TABLE*) hash_next(&open_cache,(uchar*) key,key_length, &state)) - entry->s->mysql_version= MYSQL_VERSION_ID; + table->s->mysql_version= MYSQL_VERSION_ID; } err: if (file >= 0) - VOID(my_close(file,MYF(MY_WME))); + (void) mysql_file_close(file, MYF(MY_WME)); DBUG_RETURN(result); } @@ -3227,7 +3282,7 @@ int handler::delete_table(const char *name) for (const char **ext=bas_ext(); *ext ; ext++) { fn_format(buff, name, "", *ext, MY_UNPACK_FILENAME|MY_APPEND_EXT); - if (my_delete_with_symlink(buff, MYF(0))) + if (mysql_file_delete_with_symlink(key_file_misc, buff, MYF(0))) { if (my_errno != ENOENT) { @@ -3363,9 +3418,13 @@ int handler::ha_repair(THD* thd, HA_CHECK_OPT* check_opt) mark_trx_read_write(); - if ((result= repair(thd, check_opt))) - return result; - return update_frm_version(table); + result= repair(thd, check_opt); + DBUG_ASSERT(result == HA_ADMIN_NOT_IMPLEMENTED || + ha_table_flags() & HA_CAN_REPAIR); + + if (result == HA_ADMIN_OK) + result= update_frm_version(table); + return result; } @@ -3401,47 +3460,32 @@ handler::ha_delete_all_rows() /** - Reset auto increment: public interface. - - @sa handler::reset_auto_increment() -*/ - -int -handler::ha_reset_auto_increment(ulonglong value) -{ - mark_trx_read_write(); - - return reset_auto_increment(value); -} - - -/** - Backup table: public interface. + Truncate table: public interface. - @sa handler::backup() + @sa handler::truncate() */ int -handler::ha_backup(THD* thd, HA_CHECK_OPT* check_opt) +handler::ha_truncate() { mark_trx_read_write(); - return backup(thd, check_opt); + return truncate(); } /** - Restore table: public interface. + Reset auto increment: public interface. - @sa handler::restore() + @sa handler::reset_auto_increment() */ int -handler::ha_restore(THD* thd, HA_CHECK_OPT* check_opt) +handler::ha_reset_auto_increment(ulonglong value) { mark_trx_read_write(); - return restore(thd, check_opt); + return reset_auto_increment(value); } @@ -3705,7 +3749,7 @@ int ha_enable_transaction(THD *thd, bool on) So, let's commit an open transaction (if any) now. */ if (!(error= ha_commit_trans(thd, 0))) - error= end_trans(thd, COMMIT); + error= trans_commit_implicit(thd); } DBUG_RETURN(error); } @@ -3763,7 +3807,7 @@ int handler::index_next_same(uchar *buf, const uchar *key, uint keylen) } -void handler::get_dynamic_partition_info(PARTITION_INFO *stat_info, +void handler::get_dynamic_partition_info(PARTITION_STATS *stat_info, uint part_id) { info(HA_STATUS_CONST | HA_STATUS_TIME | HA_STATUS_VARIABLE | @@ -3806,10 +3850,10 @@ void handler::update_global_table_stats() DBUG_ASSERT(table->s && table->s->table_cache_key.str); - pthread_mutex_lock(&LOCK_global_table_stats); + mysql_mutex_lock(&LOCK_global_table_stats); /* Gets the global table stats, creating one if necessary. */ if (!(table_stats= (TABLE_STATS*) - hash_search(&global_table_stats, + my_hash_search(&global_table_stats, (uchar*) table->s->table_cache_key.str, table->s->table_cache_key.length))) { @@ -3829,7 +3873,7 @@ void handler::update_global_table_stats() if (my_hash_insert(&global_table_stats, (uchar*) table_stats)) { /* Out of memory error is already given */ - my_free(table_stats, 0); + my_free(table_stats); goto end; } } @@ -3841,7 +3885,7 @@ void handler::update_global_table_stats() 1)); rows_read= rows_changed= 0; end: - pthread_mutex_unlock(&LOCK_global_table_stats); + mysql_mutex_unlock(&LOCK_global_table_stats); } @@ -3872,9 +3916,9 @@ void handler::update_global_index_stats() if (!key_info->cache_name) continue; key_length= table->s->table_cache_key.length + key_info->name_length + 1; - pthread_mutex_lock(&LOCK_global_index_stats); + mysql_mutex_lock(&LOCK_global_index_stats); // Gets the global index stats, creating one if necessary. - if (!(index_stats= (INDEX_STATS*) hash_search(&global_index_stats, + if (!(index_stats= (INDEX_STATS*) my_hash_search(&global_index_stats, key_info->cache_name, key_length))) { @@ -3887,7 +3931,7 @@ void handler::update_global_index_stats() index_stats->index_name_length= key_length; if (my_hash_insert(&global_index_stats, (uchar*) index_stats)) { - my_free(index_stats, 0); + my_free(index_stats); goto end; } } @@ -3895,7 +3939,7 @@ void handler::update_global_index_stats() index_stats->rows_read+= index_rows_read[index]; index_rows_read[index]= 0; end: - pthread_mutex_unlock(&LOCK_global_index_stats); + mysql_mutex_unlock(&LOCK_global_index_stats); } } } @@ -3937,8 +3981,7 @@ int ha_create_table(THD *thd, const char *path, name= get_canonical_filename(table.file, share.path.str, name_buff); error= table.file->ha_create(name, &table, create_info); - - VOID(closefrm(&table, 0)); + (void) closefrm(&table, 0); if (error) { strxmov(name_buff, db, ".", table_name, NullS); @@ -3989,7 +4032,7 @@ int ha_create_table_from_engine(THD* thd, const char *db, const char *name) build_table_filename(path, sizeof(path) - 1, db, name, "", 0); // Save the frm file error= writefrm(path, frmblob, frmlen); - my_free(frmblob, MYF(0)); + my_free(frmblob); if (error) DBUG_RETURN(2); @@ -4009,11 +4052,39 @@ int ha_create_table_from_engine(THD* thd, const char *db, const char *name) get_canonical_filename(table.file, path, path); error=table.file->ha_create(path, &table, &create_info); - VOID(closefrm(&table, 1)); + (void) closefrm(&table, 1); DBUG_RETURN(error != 0); } + +/** + Try to find a table in a storage engine. + + @param db Normalized table schema name + @param name Normalized table name. + @param[out] exists Only valid if the function succeeded. + + @retval TRUE An error is found + @retval FALSE Success, check *exists +*/ + +bool +ha_check_if_table_exists(THD* thd, const char *db, const char *name, + bool *exists) +{ + uchar *frmblob= NULL; + size_t frmlen; + DBUG_ENTER("ha_check_if_table_exists"); + + *exists= ! ha_discover(thd, db, name, &frmblob, &frmlen); + if (*exists) + my_free(frmblob); + + DBUG_RETURN(FALSE); +} + + void st_ha_check_opt::init() { flags= sql_flags= 0; @@ -4034,19 +4105,20 @@ void st_ha_check_opt::init() /** Init a key cache if it has not been initied before. */ -int ha_init_key_cache(const char *name, KEY_CACHE *key_cache) +int ha_init_key_cache(const char *name, KEY_CACHE *key_cache, void *unused + __attribute__((unused))) { DBUG_ENTER("ha_init_key_cache"); if (!key_cache->key_cache_inited) { - pthread_mutex_lock(&LOCK_global_system_variables); + mysql_mutex_lock(&LOCK_global_system_variables); size_t tmp_buff_size= (size_t) key_cache->param_buff_size; uint tmp_block_size= (uint) key_cache->param_block_size; - uint division_limit= key_cache->param_division_limit; - uint age_threshold= key_cache->param_age_threshold; - uint partitions= key_cache->param_partitions; - pthread_mutex_unlock(&LOCK_global_system_variables); + uint division_limit= (uint)key_cache->param_division_limit; + uint age_threshold= (uint)key_cache->param_age_threshold; + uint partitions= (uint)key_cache->param_partitions; + mysql_mutex_unlock(&LOCK_global_system_variables); DBUG_RETURN(!init_key_cache(key_cache, tmp_block_size, tmp_buff_size, @@ -4066,12 +4138,12 @@ int ha_resize_key_cache(KEY_CACHE *key_cache) if (key_cache->key_cache_inited) { - pthread_mutex_lock(&LOCK_global_system_variables); + mysql_mutex_lock(&LOCK_global_system_variables); size_t tmp_buff_size= (size_t) key_cache->param_buff_size; long tmp_block_size= (long) key_cache->param_block_size; - uint division_limit= key_cache->param_division_limit; - uint age_threshold= key_cache->param_age_threshold; - pthread_mutex_unlock(&LOCK_global_system_variables); + uint division_limit= (uint)key_cache->param_division_limit; + uint age_threshold= (uint)key_cache->param_age_threshold; + mysql_mutex_unlock(&LOCK_global_system_variables); DBUG_RETURN(!resize_key_cache(key_cache, tmp_block_size, tmp_buff_size, division_limit, age_threshold)); @@ -4089,10 +4161,10 @@ int ha_change_key_cache_param(KEY_CACHE *key_cache) if (key_cache->key_cache_inited) { - pthread_mutex_lock(&LOCK_global_system_variables); - uint division_limit= key_cache->param_division_limit; - uint age_threshold= key_cache->param_age_threshold; - pthread_mutex_unlock(&LOCK_global_system_variables); + mysql_mutex_lock(&LOCK_global_system_variables); + uint division_limit= (uint)key_cache->param_division_limit; + uint age_threshold= (uint)key_cache->param_age_threshold; + mysql_mutex_unlock(&LOCK_global_system_variables); change_key_cache_param(key_cache, division_limit, age_threshold); } DBUG_RETURN(0); @@ -4108,13 +4180,13 @@ int ha_repartition_key_cache(KEY_CACHE *key_cache) if (key_cache->key_cache_inited) { - pthread_mutex_lock(&LOCK_global_system_variables); + mysql_mutex_lock(&LOCK_global_system_variables); size_t tmp_buff_size= (size_t) key_cache->param_buff_size; long tmp_block_size= (long) key_cache->param_block_size; - uint division_limit= key_cache->param_division_limit; - uint age_threshold= key_cache->param_age_threshold; - uint partitions= key_cache->param_partitions; - pthread_mutex_unlock(&LOCK_global_system_variables); + uint division_limit= (uint)key_cache->param_division_limit; + uint age_threshold= (uint)key_cache->param_age_threshold; + uint partitions= (uint)key_cache->param_partitions; + mysql_mutex_unlock(&LOCK_global_system_variables); DBUG_RETURN(!repartition_key_cache(key_cache, tmp_block_size, tmp_buff_size, division_limit, age_threshold, @@ -4125,15 +4197,6 @@ int ha_repartition_key_cache(KEY_CACHE *key_cache) /** - Free memory allocated by a key cache. -*/ -int ha_end_key_cache(KEY_CACHE *key_cache) -{ - end_key_cache(key_cache, 1); // Can never fail - return 0; -} - -/** Move all tables from one key cache to another one. */ int ha_change_key_cache(KEY_CACHE *old_key_cache, @@ -4233,8 +4296,7 @@ ha_find_files(THD *thd,const char *db,const char *path, int error= 0; DBUG_ENTER("ha_find_files"); DBUG_PRINT("enter", ("db: '%s' path: '%s' wild: '%s' dir: %d", - val_or_null(db), val_or_null(path), - val_or_null(wild), dir)); + db, path, wild, dir)); st_find_files_args args= {db, path, wild, dir, files}; plugin_foreach(thd, find_files_handlerton, @@ -4699,7 +4761,7 @@ bool ha_show_status(THD *thd, handlerton *db_type, enum ha_stat_type stat) field_list.push_back(new Item_empty_string("Name",FN_REFLEN)); field_list.push_back(new Item_empty_string("Status",10)); - if (protocol->send_fields(&field_list, + if (protocol->send_result_set_metadata(&field_list, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) return TRUE; @@ -4753,9 +4815,9 @@ static bool check_table_binlog_row_based(THD *thd, TABLE *table) DBUG_ASSERT(table->s->cached_row_logging_check == 0 || table->s->cached_row_logging_check == 1); - return (thd->current_stmt_binlog_row_based && + return (thd->is_current_stmt_binlog_format_row() && table->s->cached_row_logging_check && - (thd->options & OPTION_BIN_LOG) && + (thd->variables.option_bits & OPTION_BIN_LOG) && mysql_bin_log.is_open()); } @@ -4771,9 +4833,7 @@ static bool check_table_binlog_row_based(THD *thd, TABLE *table) DESCRIPTION This function will generate and write table maps for all tables - that are locked by the thread 'thd'. Either manually locked - (stored in THD::locked_tables) and automatically locked (stored - in THD::lock) are considered. + that are locked by the thread 'thd'. RETURN VALUE 0 All OK @@ -4781,25 +4841,22 @@ static bool check_table_binlog_row_based(THD *thd, TABLE *table) SEE ALSO THD::lock - THD::locked_tables */ static int write_locked_table_maps(THD *thd) { DBUG_ENTER("write_locked_table_maps"); - DBUG_PRINT("enter", ("thd: 0x%lx thd->lock: 0x%lx thd->locked_tables: 0x%lx " + DBUG_PRINT("enter", ("thd: 0x%lx thd->lock: 0x%lx " "thd->extra_lock: 0x%lx", - (long) thd, (long) thd->lock, - (long) thd->locked_tables, (long) thd->extra_lock)); + (long) thd, (long) thd->lock, (long) thd->extra_lock)); DBUG_PRINT("debug", ("get_binlog_table_maps(): %d", thd->get_binlog_table_maps())); if (thd->get_binlog_table_maps() == 0) { - MYSQL_LOCK *locks[3]; + MYSQL_LOCK *locks[2]; locks[0]= thd->extra_lock; locks[1]= thd->lock; - locks[2]= thd->locked_tables; my_bool with_annotate= thd->variables.binlog_annotate_row_events && thd->query() && thd->query_length(); @@ -4819,7 +4876,21 @@ static int write_locked_table_maps(THD *thd) if (table->current_lock == F_WRLCK && check_table_binlog_row_based(thd, table)) { - int const has_trans= table->file->has_transactions(); + /* + We need to have a transactional behavior for SQLCOM_CREATE_TABLE + (e.g. CREATE TABLE... SELECT * FROM TABLE) in order to keep a + compatible behavior with the STMT based replication even when + the table is not transactional. In other words, if the operation + fails while executing the insert phase nothing is written to the + binlog. + + Note that at this point, we check the type of a set of tables to + create the table map events. In the function binlog_log_row(), + which calls the current function, we check the type of the table + of the current row. + */ + 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, &with_annotate); /* @@ -4869,10 +4940,20 @@ static int binlog_log_row(TABLE* table, { bitmap_set_all(&cols); if (likely(!(error= write_locked_table_maps(thd)))) - error= (*log_func)(thd, table, table->file->has_transactions(), - &cols, table->s->fields, + { + /* + We need to have a transactional behavior for SQLCOM_CREATE_TABLE + (i.e. CREATE TABLE... SELECT * FROM TABLE) in order to keep a + compatible behavior with the STMT based replication even when + the table is not transactional. In other words, if the operation + fails while executing the insert phase nothing is written to the + binlog. + */ + bool const has_trans= thd->lex->sql_command == SQLCOM_CREATE_TABLE || + table->file->has_transactions(); + error= (*log_func)(thd, table, has_trans, &cols, table->s->fields, before_record, after_record); - + } if (!use_bitbuf) bitmap_free(&cols); } @@ -4890,13 +4971,53 @@ int handler::ha_external_lock(THD *thd, int lock_type) */ DBUG_ASSERT(next_insert_id == 0); + if (MYSQL_HANDLER_RDLOCK_START_ENABLED() || + MYSQL_HANDLER_WRLOCK_START_ENABLED() || + MYSQL_HANDLER_UNLOCK_START_ENABLED()) + { + if (lock_type == F_RDLCK) + { + MYSQL_HANDLER_RDLOCK_START(table_share->db.str, + table_share->table_name.str); + } + else if (lock_type == F_WRLCK) + { + MYSQL_HANDLER_WRLOCK_START(table_share->db.str, + table_share->table_name.str); + } + else if (lock_type == F_UNLCK) + { + MYSQL_HANDLER_UNLOCK_START(table_share->db.str, + table_share->table_name.str); + } + } + /* We cache the table flags if the locking succeeded. Otherwise, we keep them as they were when they were fetched in ha_open(). */ int error= external_lock(thd, lock_type); + if (error == 0) cached_table_flags= table_flags(); + + if (MYSQL_HANDLER_RDLOCK_DONE_ENABLED() || + MYSQL_HANDLER_WRLOCK_DONE_ENABLED() || + MYSQL_HANDLER_UNLOCK_DONE_ENABLED()) + { + if (lock_type == F_RDLCK) + { + MYSQL_HANDLER_RDLOCK_DONE(error); + } + else if (lock_type == F_WRLCK) + { + MYSQL_HANDLER_WRLOCK_DONE(error); + } + else if (lock_type == F_UNLCK) + { + MYSQL_HANDLER_UNLOCK_DONE(error); + } + } DBUG_RETURN(error); } @@ -4933,10 +5054,13 @@ int handler::ha_write_row(uchar *buf) Log_func *log_func= Write_rows_log_event::binlog_row_logging_function; DBUG_ENTER("handler::ha_write_row"); + MYSQL_INSERT_ROW_START(table_share->db.str, table_share->table_name.str); mark_trx_read_write(); increment_statistics(&SSV::ha_write_count); - if (unlikely(error= write_row(buf))) + error= write_row(buf); + MYSQL_INSERT_ROW_DONE(error); + if (unlikely(error)) DBUG_RETURN(error); rows_changed++; if (unlikely(error= binlog_log_row(table, 0, buf, log_func))) @@ -4956,10 +5080,13 @@ int handler::ha_update_row(const uchar *old_data, uchar *new_data) */ DBUG_ASSERT(new_data == table->record[0]); + MYSQL_UPDATE_ROW_START(table_share->db.str, table_share->table_name.str); mark_trx_read_write(); increment_statistics(&SSV::ha_update_count); - if (unlikely(error= update_row(old_data, new_data))) + error= update_row(old_data, new_data); + MYSQL_UPDATE_ROW_DONE(error); + if (unlikely(error)) return error; rows_changed++; if (unlikely(error= binlog_log_row(table, old_data, new_data, log_func))) @@ -4972,10 +5099,13 @@ int handler::ha_delete_row(const uchar *buf) int error; Log_func *log_func= Delete_rows_log_event::binlog_row_logging_function; + MYSQL_DELETE_ROW_START(table_share->db.str, table_share->table_name.str); mark_trx_read_write(); increment_statistics(&SSV::ha_delete_count); - if (unlikely(error= delete_row(buf))) + error= delete_row(buf); + MYSQL_DELETE_ROW_DONE(error); + if (unlikely(error)) return error; rows_changed++; if (unlikely(error= binlog_log_row(table, buf, 0, log_func))) @@ -5036,7 +5166,8 @@ int example_of_iterator_using_for_logs_cleanup(handlerton *hton) { printf("%s\n", data.filename.str); if (data.status == HA_LOG_STATUS_FREE && - my_delete(data.filename.str, MYF(MY_WME))) + mysql_file_delete(INSTRUMENT_ME, + data.filename.str, MYF(MY_WME))) goto err; } res= 0; @@ -5064,7 +5195,7 @@ err: enum log_status fl_get_log_status(char *log) { MY_STAT stat_buff; - if (my_stat(log, &stat_buff, MYF(0))) + if (mysql_file_stat(INSTRUMENT_ME, log, &stat_buff, MYF(0))) return HA_LOG_STATUS_INUSE; return HA_LOG_STATUS_NOSUCHLOG; } @@ -5096,7 +5227,7 @@ int fl_log_iterator_next(struct handler_iterator *iterator, void fl_log_iterator_destroy(struct handler_iterator *iterator) { - my_free((uchar*)iterator->buffer, MYF(MY_ALLOW_ZERO_PTR)); + my_free(iterator->buffer); } |