summaryrefslogtreecommitdiff
path: root/sql/handler.cc
diff options
context:
space:
mode:
Diffstat (limited to 'sql/handler.cc')
-rw-r--r--sql/handler.cc1819
1 files changed, 1373 insertions, 446 deletions
diff --git a/sql/handler.cc b/sql/handler.cc
index 717d02bc113..f693d74d9c6 100644
--- a/sql/handler.cc
+++ b/sql/handler.cc
@@ -1,5 +1,5 @@
/* Copyright (c) 2000, 2016, Oracle and/or its affiliates.
- Copyright (c) 2009, 2016, MariaDB
+ Copyright (c) 2009, 2018, MariaDB Corporation.
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
@@ -20,10 +20,7 @@
Handler-calling-functions
*/
-#ifdef USE_PRAGMA_IMPLEMENTATION
-#pragma implementation // gcc: Class implementation
-#endif
-
+#include <my_global.h>
#include "sql_priv.h"
#include "unireg.h"
#include "rpl_handler.h"
@@ -34,7 +31,7 @@
#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 "discover.h" // extension_based_table_discovery, etc
#include "log_event.h" // *_rows_log_event
#include "create_options.h"
#include "rpl_filter.h"
@@ -42,9 +39,9 @@
#include "transaction.h"
#include "myisam.h"
#include "probes_mysql.h"
+#include <mysql/psi/mysql_table.h>
#include "debug_sync.h" // DEBUG_SYNC
#include "sql_audit.h"
-#include "../mysys/my_handler_errors.h"
#ifdef WITH_PARTITION_STORAGE_ENGINE
#include "ha_partition.h"
@@ -66,12 +63,20 @@ 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}, {NullS, 0} };
+{ HA_KEY_ALG_UNDEF, 0, {NullS, 0}, {NullS, 0}, true };
/* number of entries in handlertons[] */
ulong total_ha= 0;
/* number of storage engines (from handlertons[]) that support 2pc */
ulong total_ha_2pc= 0;
+#ifndef DBUG_OFF
+/*
+ Number of non-mandatory 2pc handlertons whose initialization failed
+ to estimate total_ha_2pc value under supposition of the failures
+ have not occcured.
+*/
+ulong failed_ha_2pc= 0;
+#endif
/* size of savepoint storage area (see ha_init) */
ulong savepoint_alloc_size= 0;
@@ -101,6 +106,7 @@ 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)
@@ -123,7 +129,7 @@ handlerton *ha_default_handlerton(THD *thd)
{
plugin_ref plugin= ha_default_plugin(thd);
DBUG_ASSERT(plugin);
- handlerton *hton= plugin_data(plugin, handlerton*);
+ handlerton *hton= plugin_hton(plugin);
DBUG_ASSERT(hton);
return hton;
}
@@ -154,7 +160,7 @@ redo:
if ((plugin= my_plugin_lock_by_name(thd, name, MYSQL_STORAGE_ENGINE_PLUGIN)))
{
- handlerton *hton= plugin_data(plugin, handlerton *);
+ handlerton *hton= plugin_hton(plugin);
if (hton && !(hton->flags & HTON_NOT_USER_SELECTABLE))
return plugin;
@@ -202,7 +208,7 @@ handlerton *ha_resolve_by_legacy_type(THD *thd, enum legacy_db_type db_type)
default:
if (db_type > DB_TYPE_UNKNOWN && db_type < DB_TYPE_DEFAULT &&
(plugin= ha_lock_engine(thd, installed_htons[db_type])))
- return plugin_data(plugin, handlerton*);
+ return plugin_hton(plugin);
/* fall through */
case DB_TYPE_UNKNOWN:
return NULL;
@@ -230,14 +236,7 @@ handlerton *ha_checktype(THD *thd, enum legacy_db_type database_type,
return NULL;
}
- RUN_HOOK(transaction, after_rollback, (thd, FALSE));
-
- switch (database_type) {
- case DB_TYPE_MRG_ISAM:
- return ha_resolve_by_legacy_type(thd, DB_TYPE_MRG_MYISAM);
- default:
- break;
- }
+ (void) RUN_HOOK(transaction, after_rollback, (thd, FALSE));
return ha_default_handlerton(thd);
} /* ha_checktype */
@@ -293,7 +292,7 @@ handler *get_ha_partition(partition_info *part_info)
static const char **handler_errmsgs;
C_MODE_START
-static const char **get_handler_errmsgs()
+static const char **get_handler_errmsgs(void)
{
return handler_errmsgs;
}
@@ -322,7 +321,7 @@ int ha_init_errors(void)
/* Set the dedicated error messages. */
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_RECORD_CHANGED, "Update which is recoverable");
SETMSG(HA_ERR_WRONG_INDEX, "Wrong index given to function");
SETMSG(HA_ERR_CRASHED, ER_DEFAULT(ER_NOT_KEYFILE));
SETMSG(HA_ERR_WRONG_IN_RECORD, ER_DEFAULT(ER_CRASHED_ON_USAGE));
@@ -364,8 +363,10 @@ int ha_init_errors(void)
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_FTS_INVALID_DOCID, "Invalid InnoDB FTS Doc ID");
SETMSG(HA_ERR_TABLE_IN_FK_CHECK, ER_DEFAULT(ER_TABLE_IN_FK_CHECK));
SETMSG(HA_ERR_DISK_FULL, ER_DEFAULT(ER_DISK_FULL));
+ SETMSG(HA_ERR_FTS_TOO_MANY_WORDS_IN_PHRASE, "Too many words in a FTS phrase or proximity search");
/* Register the error messages for use with my_error(). */
return my_error_register(get_handler_errmsgs, HA_ERR_FIRST, HA_ERR_LAST);
@@ -391,6 +392,38 @@ static int ha_finish_errors(void)
return 0;
}
+static volatile int32 need_full_discover_for_existence= 0;
+static volatile int32 engines_with_discover_table_names= 0;
+static volatile int32 engines_with_discover= 0;
+
+static int full_discover_for_existence(handlerton *, const char *, const char *)
+{ return 0; }
+
+static int ext_based_existence(handlerton *, const char *, const char *)
+{ return 0; }
+
+static int hton_ext_based_table_discovery(handlerton *hton, LEX_STRING *db,
+ MY_DIR *dir, handlerton::discovered_list *result)
+{
+ /*
+ tablefile_extensions[0] is the metadata file, see
+ the comment above tablefile_extensions declaration
+ */
+ return extension_based_table_discovery(dir, hton->tablefile_extensions[0],
+ result);
+}
+
+static void update_discovery_counters(handlerton *hton, int val)
+{
+ if (hton->discover_table_existence == full_discover_for_existence)
+ my_atomic_add32(&need_full_discover_for_existence, val);
+
+ if (hton->discover_table_names)
+ my_atomic_add32(&engines_with_discover_table_names, val);
+
+ if (hton->discover_table)
+ my_atomic_add32(&engines_with_discover, val);
+}
int ha_finalize_handlerton(st_plugin_int *plugin)
{
@@ -428,6 +461,9 @@ int ha_finalize_handlerton(st_plugin_int *plugin)
}
}
+ free_sysvar_table_options(hton);
+ update_discovery_counters(hton, -1);
+
/*
In case a plugin is uninstalled and re-installed later, it should
reuse an array slot. Otherwise the number of uninstall/install
@@ -451,12 +487,12 @@ int ha_finalize_handlerton(st_plugin_int *plugin)
int ha_initialize_handlerton(st_plugin_int *plugin)
{
handlerton *hton;
+ static const char *no_exts[]= { 0 };
DBUG_ENTER("ha_initialize_handlerton");
DBUG_PRINT("plugin", ("initialize plugin: '%s'", plugin->name.str));
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.",
@@ -464,6 +500,9 @@ int ha_initialize_handlerton(st_plugin_int *plugin)
goto err_no_hton_memory;
}
+ hton->tablefile_extensions= no_exts;
+ hton->discover_table_names= hton_ext_based_table_discovery;
+
hton->slot= HA_SLOT_UNDEF;
/* Historical Requirement */
plugin->data= hton; // shortcut for the future
@@ -474,6 +513,21 @@ int ha_initialize_handlerton(st_plugin_int *plugin)
goto err;
}
+ // hton_ext_based_table_discovery() works only when discovery
+ // is supported and the engine if file-based.
+ if (hton->discover_table_names == hton_ext_based_table_discovery &&
+ (!hton->discover_table || !hton->tablefile_extensions[0]))
+ hton->discover_table_names= NULL;
+
+ // default discover_table_existence implementation
+ if (!hton->discover_table_existence && hton->discover_table)
+ {
+ if (hton->tablefile_extensions[0])
+ hton->discover_table_existence= ext_based_existence;
+ else
+ hton->discover_table_existence= full_discover_for_existence;
+ }
+
/*
the switch below and hton->state should be removed when
command-line options for plugins will be implemented
@@ -547,7 +601,7 @@ int ha_initialize_handlerton(st_plugin_int *plugin)
{
total_ha_2pc--;
hton->prepare= 0;
- push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning_printf(current_thd, Sql_condition::WARN_LEVEL_WARN,
ER_UNKNOWN_ERROR,
"Cannot enable tc-log at run-time. "
"XA features of %s are disabled",
@@ -581,6 +635,9 @@ int ha_initialize_handlerton(st_plugin_int *plugin)
break;
};
+ resolve_sysvar_table_options(hton);
+ update_discovery_counters(hton, 1);
+
DBUG_RETURN(0);
err_deinit:
@@ -592,6 +649,10 @@ err_deinit:
(void) plugin->plugin->deinit(NULL);
err:
+#ifndef DBUG_OFF
+ if (hton->prepare && hton->state == SHOW_OPTION_YES)
+ failed_ha_2pc++;
+#endif
my_free(hton);
err_no_hton_memory:
plugin->data= NULL;
@@ -634,7 +695,7 @@ int ha_end()
static my_bool dropdb_handlerton(THD *unused1, plugin_ref plugin,
void *path)
{
- handlerton *hton= plugin_data(plugin, handlerton *);
+ handlerton *hton= plugin_hton(plugin);
if (hton->state == SHOW_OPTION_YES && hton->drop_database)
hton->drop_database(hton, (char *)path);
return FALSE;
@@ -650,7 +711,7 @@ 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 *);
+ handlerton *hton= plugin_hton(plugin);
if (hton->state == SHOW_OPTION_YES && hton->checkpoint_state)
hton->checkpoint_state(hton, (int) *(bool*) disable);
return FALSE;
@@ -663,11 +724,48 @@ void ha_checkpoint_state(bool disable)
}
+struct st_commit_checkpoint_request {
+ void *cookie;
+ void (*pre_hook)(void *);
+};
+
+static my_bool commit_checkpoint_request_handlerton(THD *unused1, plugin_ref plugin,
+ void *data)
+{
+ st_commit_checkpoint_request *st= (st_commit_checkpoint_request *)data;
+ handlerton *hton= plugin_hton(plugin);
+ if (hton->state == SHOW_OPTION_YES && hton->commit_checkpoint_request)
+ {
+ void *cookie= st->cookie;
+ if (st->pre_hook)
+ (*st->pre_hook)(cookie);
+ (*hton->commit_checkpoint_request)(hton, cookie);
+ }
+ return FALSE;
+}
+
+
+/*
+ Invoke commit_checkpoint_request() in all storage engines that implement it.
+
+ If pre_hook is non-NULL, the hook will be called prior to each invocation.
+*/
+void
+ha_commit_checkpoint_request(void *cookie, void (*pre_hook)(void *))
+{
+ st_commit_checkpoint_request st;
+ st.cookie= cookie;
+ st.pre_hook= pre_hook;
+ plugin_foreach(NULL, commit_checkpoint_request_handlerton,
+ MYSQL_STORAGE_ENGINE_PLUGIN, &st);
+}
+
+
static my_bool closecon_handlerton(THD *thd, plugin_ref plugin,
void *unused)
{
- handlerton *hton= plugin_data(plugin, handlerton *);
+ handlerton *hton= plugin_hton(plugin);
/*
there's no need to rollback here as all transactions must
be rolled back already
@@ -688,13 +786,15 @@ static my_bool closecon_handlerton(THD *thd, plugin_ref plugin,
*/
void ha_close_connection(THD* thd)
{
- plugin_foreach(thd, closecon_handlerton, MYSQL_STORAGE_ENGINE_PLUGIN, 0);
+ plugin_foreach_with_mask(thd, closecon_handlerton,
+ MYSQL_STORAGE_ENGINE_PLUGIN,
+ PLUGIN_IS_DELETED|PLUGIN_IS_READY, 0);
}
static my_bool kill_handlerton(THD *thd, plugin_ref plugin,
void *level)
{
- handlerton *hton= plugin_data(plugin, handlerton *);
+ handlerton *hton= plugin_hton(plugin);
if (hton->state == SHOW_OPTION_YES && hton->kill_query &&
thd_get_ha_data(thd, hton))
@@ -753,7 +853,7 @@ void ha_kill_query(THD* thd, enum thd_kill_levels level)
end. Such nested transaction was internally referred to as
a "statement transaction" and gave birth to the term.
- <Historical note ends>
+ (Historical note ends)
Since then a statement transaction is started for each statement
that accesses transactional tables or uses the binary log. If
@@ -1027,11 +1127,14 @@ void trans_register_ha(THD *thd, bool all, handlerton *ht_arg)
{
trans= &thd->transaction.all;
thd->server_status|= SERVER_STATUS_IN_TRANS;
+ if (thd->tx_read_only)
+ thd->server_status|= SERVER_STATUS_IN_TRANS_READONLY;
+ DBUG_PRINT("info", ("setting SERVER_STATUS_IN_TRANS"));
}
else
trans= &thd->transaction.stmt;
- ha_info= thd->ha_data[ht_arg->slot].ha_info + static_cast<unsigned>(all);
+ ha_info= thd->ha_data[ht_arg->slot].ha_info + (all ? 1 : 0);
if (ha_info->is_started())
DBUG_VOID_RETURN; /* already registered, return */
@@ -1076,9 +1179,11 @@ int ha_prepare(THD *thd)
}
else
{
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
- ER_ILLEGAL_HA, ER(ER_ILLEGAL_HA),
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
+ ER_GET_ERRNO, ER(ER_GET_ERRNO),
+ HA_ERR_WRONG_COMMAND,
ha_resolve_storage_engine_name(ht));
+
}
}
}
@@ -1176,18 +1281,24 @@ int ha_commit_trans(THD *thd, bool all)
the changes are not durable as they might be rolled back if the
enclosing 'all' transaction is rolled back.
*/
- bool is_real_trans= all || thd->transaction.all.ha_list == 0;
+ bool is_real_trans= ((all || thd->transaction.all.ha_list == 0) &&
+ !(thd->variables.option_bits & OPTION_GTID_BEGIN));
Ha_trx_info *ha_info= trans->ha_list;
bool need_prepare_ordered, need_commit_ordered;
my_xid xid;
DBUG_ENTER("ha_commit_trans");
+ DBUG_PRINT("info",("thd: %p option_bits: %lu all: %d",
+ thd, (ulong) thd->variables.option_bits, all));
/* Just a random warning to test warnings pushed during autocommit. */
DBUG_EXECUTE_IF("warn_during_ha_commit_trans",
- push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning(thd, Sql_condition::WARN_LEVEL_WARN,
ER_WARNING_NOT_COMPLETE_ROLLBACK,
ER(ER_WARNING_NOT_COMPLETE_ROLLBACK)););
+ DBUG_PRINT("info",
+ ("all: %d thd->in_sub_stmt: %d ha_info: %p is_real_trans: %d",
+ all, thd->in_sub_stmt, ha_info, is_real_trans));
/*
We must not commit the normal transaction if a statement
transaction is pending. Otherwise statement transaction
@@ -1224,7 +1335,9 @@ int ha_commit_trans(THD *thd, bool all)
if (!ha_info)
{
- /* Free resources and perform other cleanup even for 'empty' transactions. */
+ /*
+ Free resources and perform other cleanup even for 'empty' transactions.
+ */
if (is_real_trans)
thd->transaction.cleanup();
DBUG_RETURN(0);
@@ -1241,6 +1354,8 @@ int ha_commit_trans(THD *thd, bool all)
bool rw_trans= is_real_trans &&
(rw_ha_count > !thd->is_current_stmt_binlog_disabled());
MDL_request mdl_request;
+ DBUG_PRINT("info", ("is_real_trans: %d rw_trans: %d rw_ha_count: %d",
+ is_real_trans, rw_trans, rw_ha_count));
if (rw_trans)
{
@@ -1319,11 +1434,13 @@ int ha_commit_trans(THD *thd, bool all)
goto done;
}
+ DEBUG_SYNC(thd, "ha_commit_trans_before_log_and_order");
cookie= tc_log->log_and_order(thd, xid, all, need_prepare_ordered,
need_commit_ordered);
if (!cookie)
goto err;
+ DEBUG_SYNC(thd, "ha_commit_trans_after_log_and_order");
DBUG_EXECUTE_IF("crash_commit_after_log", DBUG_SUICIDE(););
error= commit_one_phase_2(thd, all, trans, is_real_trans) ? 2 : 0;
@@ -1343,7 +1460,13 @@ done:
/* Come here if error and we need to rollback. */
err:
error= 1; /* Transaction was rolled back */
- ha_rollback_trans(thd, all);
+ /*
+ In parallel replication, rollback is delayed, as there is extra replication
+ book-keeping to be done before rolling back and allowing a conflicting
+ transaction to continue (MDEV-7458).
+ */
+ if (!(thd->rgi_slave && thd->rgi_slave->is_parallel_exec))
+ ha_rollback_trans(thd, all);
end:
if (rw_trans && mdl_request.ticket)
@@ -1386,9 +1509,17 @@ int ha_commit_one_phase(THD *thd, bool all)
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;
+ bool is_real_trans= ((all || thd->transaction.all.ha_list == 0) &&
+ !(thd->variables.option_bits & OPTION_GTID_BEGIN));
+ int res;
DBUG_ENTER("ha_commit_one_phase");
- int res= commit_one_phase_2(thd, all, trans, is_real_trans);
+ if (is_real_trans)
+ {
+ DEBUG_SYNC(thd, "ha_commit_one_phase");
+ if ((res= thd->wait_for_prior_commit()))
+ DBUG_RETURN(res);
+ }
+ res= commit_one_phase_2(thd, all, trans, is_real_trans);
DBUG_RETURN(res);
}
@@ -1399,6 +1530,8 @@ 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 (is_real_trans)
+ DEBUG_SYNC(thd, "commit_one_phase_2");
if (ha_info)
{
for (; ha_info; ha_info= ha_info_next)
@@ -1427,7 +1560,10 @@ commit_one_phase_2(THD *thd, bool all, THD_TRANS *trans, bool is_real_trans)
}
/* Free resources and perform other cleanup even for 'empty' transactions. */
if (is_real_trans)
+ {
+ thd->has_waiter= false;
thd->transaction.cleanup();
+ }
DBUG_RETURN(error);
}
@@ -1461,6 +1597,26 @@ int ha_rollback_trans(THD *thd, bool all)
DBUG_ASSERT(thd->transaction.stmt.ha_list == NULL ||
trans == &thd->transaction.stmt);
+#ifdef HAVE_REPLICATION
+ if (is_real_trans)
+ {
+ /*
+ In parallel replication, if we need to rollback during commit, we must
+ first inform following transactions that we are going to abort our commit
+ attempt. Otherwise those following transactions can run too early, and
+ possibly cause replication to fail. See comments in retry_event_group().
+
+ There were several bugs with this in the past that were very hard to
+ track down (MDEV-7458, MDEV-8302). So we add here an assertion for
+ rollback without signalling following transactions. And in release
+ builds, we explicitly do the signalling before rolling back.
+ */
+ DBUG_ASSERT(!(thd->rgi_slave && thd->rgi_slave->did_mark_start_commit));
+ if (thd->rgi_slave && thd->rgi_slave->did_mark_start_commit)
+ thd->rgi_slave->unmark_start_commit();
+ }
+#endif
+
if (thd->in_sub_stmt)
{
DBUG_ASSERT(0);
@@ -1504,11 +1660,14 @@ int ha_rollback_trans(THD *thd, bool all)
*/
if (is_real_trans && thd->transaction_rollback_request &&
thd->transaction.xid_state.xa_state != XA_NOTR)
- thd->transaction.xid_state.rm_error= thd->stmt_da->sql_errno();
+ thd->transaction.xid_state.rm_error= thd->get_stmt_da()->sql_errno();
/* Always cleanup. Even if nht==0. There may be savepoints. */
if (is_real_trans)
+ {
+ thd->has_waiter= false;
thd->transaction.cleanup();
+ }
if (all)
thd->transaction_rollback_request= FALSE;
@@ -1527,10 +1686,10 @@ int ha_rollback_trans(THD *thd, bool all)
*/
if (is_real_trans && thd->transaction.all.modified_non_trans_table &&
!thd->slave_thread && thd->killed < KILL_CONNECTION)
- push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ push_warning(thd, Sql_condition::WARN_LEVEL_WARN,
ER_WARNING_NOT_COMPLETE_ROLLBACK,
ER(ER_WARNING_NOT_COMPLETE_ROLLBACK));
- RUN_HOOK(transaction, after_rollback, (thd, FALSE));
+ (void) RUN_HOOK(transaction, after_rollback, (thd, FALSE));
DBUG_RETURN(error);
}
@@ -1543,7 +1702,7 @@ struct xahton_st {
static my_bool xacommit_handlerton(THD *unused1, plugin_ref plugin,
void *arg)
{
- handlerton *hton= plugin_data(plugin, handlerton *);
+ handlerton *hton= plugin_hton(plugin);
if (hton->state == SHOW_OPTION_YES && hton->recover)
{
hton->commit_by_xid(hton, ((struct xahton_st *)arg)->xid);
@@ -1555,7 +1714,7 @@ static my_bool xacommit_handlerton(THD *unused1, plugin_ref plugin,
static my_bool xarollback_handlerton(THD *unused1, plugin_ref plugin,
void *arg)
{
- handlerton *hton= plugin_data(plugin, handlerton *);
+ handlerton *hton= plugin_hton(plugin);
if (hton->state == SHOW_OPTION_YES && hton->recover)
{
hton->rollback_by_xid(hton, ((struct xahton_st *)arg)->xid);
@@ -1661,7 +1820,7 @@ struct xarecover_st
static my_bool xarecover_handlerton(THD *unused, plugin_ref plugin,
void *arg)
{
- handlerton *hton= plugin_data(plugin, handlerton *);
+ handlerton *hton= plugin_hton(plugin);
struct xarecover_st *info= (struct xarecover_st *) arg;
int got;
@@ -1678,7 +1837,7 @@ static my_bool xarecover_handlerton(THD *unused, plugin_ref plugin,
{
#ifndef DBUG_OFF
char buf[XIDDATASIZE*4+6]; // see xid_to_str
- sql_print_information("ignore xid %s", xid_to_str(buf, info->list+i));
+ DBUG_PRINT("info", ("ignore xid %s", xid_to_str(buf, info->list+i)));
#endif
xid_cache_insert(info->list+i, XA_PREPARED);
info->found_foreign_xids++;
@@ -1695,19 +1854,31 @@ static my_bool xarecover_handlerton(THD *unused, plugin_ref plugin,
tc_heuristic_recover == TC_HEURISTIC_RECOVER_COMMIT)
{
#ifndef DBUG_OFF
- char buf[XIDDATASIZE*4+6]; // see xid_to_str
- sql_print_information("commit xid %s", xid_to_str(buf, info->list+i));
+ int rc=
+#endif
+ hton->commit_by_xid(hton, info->list+i);
+#ifndef DBUG_OFF
+ if (rc == 0)
+ {
+ char buf[XIDDATASIZE*4+6]; // see xid_to_str
+ DBUG_PRINT("info", ("commit xid %s", xid_to_str(buf, info->list+i)));
+ }
#endif
- hton->commit_by_xid(hton, info->list+i);
}
else
{
#ifndef DBUG_OFF
- char buf[XIDDATASIZE*4+6]; // see xid_to_str
- sql_print_information("rollback xid %s",
- xid_to_str(buf, info->list+i));
+ int rc=
+#endif
+ hton->rollback_by_xid(hton, info->list+i);
+#ifndef DBUG_OFF
+ if (rc == 0)
+ {
+ char buf[XIDDATASIZE*4+6]; // see xid_to_str
+ DBUG_PRINT("info", ("rollback xid %s",
+ xid_to_str(buf, info->list+i)));
+ }
#endif
- hton->rollback_by_xid(hton, info->list+i);
}
}
if (got < info->len)
@@ -1729,7 +1900,8 @@ int ha_recover(HASH *commit_list)
/* commit_list and tc_heuristic_recover cannot be set both */
DBUG_ASSERT(info.commit_list==0 || tc_heuristic_recover==0);
/* if either is set, total_ha_2pc must be set too */
- DBUG_ASSERT(info.dry_run || total_ha_2pc>(ulong)opt_bin_log);
+ DBUG_ASSERT(info.dry_run ||
+ (failed_ha_2pc + total_ha_2pc) > (ulong)opt_bin_log);
if (total_ha_2pc <= (ulong)opt_bin_log)
DBUG_RETURN(0);
@@ -1822,6 +1994,17 @@ bool mysql_xa_recover(THD *thd)
DBUG_RETURN(0);
}
+/*
+ Called by engine to notify TC that a new commit checkpoint has been reached.
+ See comments on handlerton method commit_checkpoint_request() for details.
+*/
+void
+commit_checkpoint_notify_ha(handlerton *hton, void *cookie)
+{
+ tc_log->commit_checkpoint_notify(cookie);
+}
+
+
/**
@details
This function should be called when MySQL sends rows of a SELECT result set
@@ -1860,6 +2043,41 @@ int ha_release_temporary_latches(THD *thd)
return 0;
}
+/**
+ Check if all storage engines used in transaction agree that after
+ rollback to savepoint it is safe to release MDL locks acquired after
+ savepoint creation.
+
+ @param thd The client thread that executes the transaction.
+
+ @return true - It is safe to release MDL locks.
+ false - If it is not.
+*/
+bool ha_rollback_to_savepoint_can_release_mdl(THD *thd)
+{
+ Ha_trx_info *ha_info;
+ THD_TRANS *trans= (thd->in_sub_stmt ? &thd->transaction.stmt :
+ &thd->transaction.all);
+
+ DBUG_ENTER("ha_rollback_to_savepoint_can_release_mdl");
+
+ /**
+ Checking whether it is safe to release metadata locks after rollback to
+ savepoint in all the storage engines that are part of the transaction.
+ */
+ for (ha_info= trans->ha_list; ha_info; ha_info= ha_info->next())
+ {
+ handlerton *ht= ha_info->ht();
+ DBUG_ASSERT(ht);
+
+ if (ht->savepoint_rollback_can_release_mdl == 0 ||
+ ht->savepoint_rollback_can_release_mdl(ht, thd) == false)
+ DBUG_RETURN(false);
+ }
+
+ DBUG_RETURN(true);
+}
+
int ha_rollback_to_savepoint(THD *thd, SAVEPOINT *sv)
{
int error=0;
@@ -1938,7 +2156,7 @@ int ha_savepoint(THD *thd, SAVEPOINT *sv)
}
if ((err= ht->savepoint_set(ht, thd, (uchar *)(sv+1)+ht->savepoint_offset)))
{ // cannot happen
- my_error(ER_GET_ERRNO, MYF(0), err);
+ my_error(ER_GET_ERRNO, MYF(0), err, hton_name(ht)->str);
error=1;
}
status_var_increment(thd->status_var.ha_savepoint_count);
@@ -1969,7 +2187,7 @@ int ha_release_savepoint(THD *thd, SAVEPOINT *sv)
if ((err= ht->savepoint_release(ht, thd,
(uchar *)(sv+1) + ht->savepoint_offset)))
{ // cannot happen
- my_error(ER_GET_ERRNO, MYF(0), err);
+ my_error(ER_GET_ERRNO, MYF(0), err, hton_name(ht)->str);
error=1;
}
}
@@ -1980,7 +2198,7 @@ int ha_release_savepoint(THD *thd, SAVEPOINT *sv)
static my_bool snapshot_handlerton(THD *thd, plugin_ref plugin,
void *arg)
{
- handlerton *hton= plugin_data(plugin, handlerton *);
+ handlerton *hton= plugin_hton(plugin);
if (hton->state == SHOW_OPTION_YES &&
hton->start_consistent_snapshot)
{
@@ -2010,7 +2228,7 @@ int ha_start_consistent_snapshot(THD *thd)
exist:
*/
if (warn)
- push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_UNKNOWN_ERROR,
+ push_warning(thd, Sql_condition::WARN_LEVEL_WARN, ER_UNKNOWN_ERROR,
"This MySQL server does not support any "
"consistent-read capable storage engine");
return 0;
@@ -2020,7 +2238,7 @@ int ha_start_consistent_snapshot(THD *thd)
static my_bool flush_handlerton(THD *thd, plugin_ref plugin,
void *arg)
{
- handlerton *hton= plugin_data(plugin, handlerton *);
+ handlerton *hton= plugin_hton(plugin);
if (hton->state == SHOW_OPTION_YES && hton->flush_logs &&
hton->flush_logs(hton))
return TRUE;
@@ -2106,9 +2324,9 @@ public:
virtual bool handle_condition(THD *thd,
uint sql_errno,
const char* sqlstate,
- MYSQL_ERROR::enum_warning_level level,
+ Sql_condition::enum_warning_level level,
const char* msg,
- MYSQL_ERROR ** cond_hdl);
+ Sql_condition ** cond_hdl);
char buff[MYSQL_ERRMSG_SIZE];
};
@@ -2118,9 +2336,9 @@ Ha_delete_table_error_handler::
handle_condition(THD *,
uint,
const char*,
- MYSQL_ERROR::enum_warning_level,
+ Sql_condition::enum_warning_level,
const char* msg,
- MYSQL_ERROR ** cond_hdl)
+ Sql_condition ** cond_hdl)
{
*cond_hdl= NULL;
/* Grab the error message */
@@ -2129,9 +2347,11 @@ handle_condition(THD *,
}
-/** @brief
- This should return ENOENT if the file doesn't exists.
- The .frm file will be deleted only if we return 0 or ENOENT
+/** delete a table in the engine
+
+ @note
+ ENOENT and HA_ERR_NO_SUCH_TABLE are not considered errors.
+ The .frm file will be deleted only if we return 0.
*/
int ha_delete_table(THD *thd, handlerton *table_type, const char *path,
const char *db, const char *alias, bool generate_warning)
@@ -2143,51 +2363,72 @@ int ha_delete_table(THD *thd, handlerton *table_type, const char *path,
TABLE_SHARE dummy_share;
DBUG_ENTER("ha_delete_table");
+ /* table_type is NULL in ALTER TABLE when renaming only .frm files */
+ if (table_type == NULL || table_type == view_pseudo_hton ||
+ ! (file=get_new_handler((TABLE_SHARE*)0, thd->mem_root, table_type)))
+ DBUG_RETURN(0);
+
bzero((char*) &dummy_table, sizeof(dummy_table));
bzero((char*) &dummy_share, sizeof(dummy_share));
dummy_table.s= &dummy_share;
- /* DB_TYPE_UNKNOWN is used in ALTER TABLE when renaming only .frm files */
- if (table_type == NULL ||
- ! (file=get_new_handler((TABLE_SHARE*)0, thd->mem_root, table_type)))
- DBUG_RETURN(ENOENT);
-
path= get_canonical_filename(file, path, tmp_path);
- if ((error= file->ha_delete_table(path)) && generate_warning)
+ if ((error= file->ha_delete_table(path)))
{
/*
- Because file->print_error() use my_error() to generate the error message
- we use an internal error handler to intercept it and store the text
- in a temporary buffer. Later the message will be presented to user
- as a warning.
+ it's not an error if the table doesn't exist in the engine.
+ warn the user, but still report DROP being a success
*/
- Ha_delete_table_error_handler ha_delete_table_error_handler;
-
- /* Fill up strucutures that print_error may need */
- dummy_share.path.str= (char*) path;
- dummy_share.path.length= strlen(path);
- dummy_share.db.str= (char*) db;
- dummy_share.db.length= strlen(db);
- dummy_share.table_name.str= (char*) alias;
- dummy_share.table_name.length= strlen(alias);
- dummy_table.alias.set(alias, dummy_share.table_name.length,
- table_alias_charset);
-
- file->change_table_ptr(&dummy_table, &dummy_share);
+ bool intercept= error == ENOENT || error == HA_ERR_NO_SUCH_TABLE;
- thd->push_internal_handler(&ha_delete_table_error_handler);
- file->print_error(error, 0);
+ if (!intercept || generate_warning)
+ {
+ /*
+ Because file->print_error() use my_error() to generate the error message
+ we use an internal error handler to intercept it and store the text
+ in a temporary buffer. Later the message will be presented to user
+ as a warning.
+ */
+ Ha_delete_table_error_handler ha_delete_table_error_handler;
+
+ /* Fill up strucutures that print_error may need */
+ dummy_share.path.str= (char*) path;
+ dummy_share.path.length= strlen(path);
+ dummy_share.normalized_path= dummy_share.path;
+ dummy_share.db.str= (char*) db;
+ dummy_share.db.length= strlen(db);
+ dummy_share.table_name.str= (char*) alias;
+ dummy_share.table_name.length= strlen(alias);
+ dummy_table.alias.set(alias, dummy_share.table_name.length,
+ table_alias_charset);
+
+ file->change_table_ptr(&dummy_table, &dummy_share);
+
+#if MYSQL_VERSION_ID > 100105
+ // XXX as an ugly 10.0-only hack we intercept HA_ERR_ROW_IS_REFERENCED,
+ // to report it under the old historical error number.
+#error remove HA_ERR_ROW_IS_REFERENCED, use ME_JUST_WARNING instead of a handler
+#endif
+ if (intercept || error == HA_ERR_ROW_IS_REFERENCED)
+ thd->push_internal_handler(&ha_delete_table_error_handler);
- thd->pop_internal_handler();
+ file->print_error(error, 0);
- /*
- XXX: should we convert *all* errors to warnings here?
- What if the error is fatal?
- */
- push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, error,
- ha_delete_table_error_handler.buff);
+ if (intercept || error == HA_ERR_ROW_IS_REFERENCED)
+ {
+ thd->pop_internal_handler();
+ if (error == HA_ERR_ROW_IS_REFERENCED)
+ my_message(ER_ROW_IS_REFERENCED, ER(ER_ROW_IS_REFERENCED), MYF(0));
+ else
+ push_warning(thd, Sql_condition::WARN_LEVEL_WARN, error,
+ ha_delete_table_error_handler.buff);
+ }
+ }
+ if (intercept)
+ error= 0;
}
delete file;
+
DBUG_RETURN(error);
}
@@ -2197,8 +2438,11 @@ 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)
+
+ if (!new_handler)
return NULL;
+ if (new_handler->set_ha_share_ref(ha_share))
+ goto err;
/*
Allocate handler->ref here because otherwise ha_open will allocate it
@@ -2208,7 +2452,7 @@ handler *handler::clone(const char *name, MEM_ROOT *mem_root)
if (!(new_handler->ref= (uchar*) alloc_root(mem_root,
ALIGN_SIZE(ref_length)*2)))
- return NULL;
+ goto err;
/*
TODO: Implement a more efficient way to have more than one index open for
@@ -2219,9 +2463,13 @@ handler *handler::clone(const char *name, MEM_ROOT *mem_root)
*/
if (new_handler->ha_open(table, name, table->db_stat,
HA_OPEN_IGNORE_IF_LOCKED))
- return NULL;
+ goto err;
return new_handler;
+
+err:
+ delete new_handler;
+ return NULL;
}
@@ -2255,9 +2503,28 @@ 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
+void handler::unbind_psi()
+{
+ /*
+ Notify the instrumentation that this table is not owned
+ by this thread any more.
+ */
+ PSI_CALL_unbind_table(m_psi);
+}
+
+void handler::rebind_psi()
+{
+ /*
+ Notify the instrumentation that this table is now owned
+ by this thread.
+ */
+ m_psi= PSI_CALL_rebind_table(ha_table_share_psi(), this, m_psi);
+}
+
+
+PSI_table_share *handler::ha_table_share_psi() const
{
- return share->m_psi;
+ return table_share->m_psi;
}
/** @brief
@@ -2279,6 +2546,8 @@ int handler::ha_open(TABLE *table_arg, const char *name, int mode,
table= table_arg;
DBUG_ASSERT(table->s == table_share);
+ DBUG_ASSERT(m_lock_type == F_UNLCK);
+ DBUG_PRINT("info", ("old m_lock_type: %d F_UNLCK %d", m_lock_type, F_UNLCK));
DBUG_ASSERT(alloc_root_inited(&table->mem_root));
if ((error=open(name,mode,test_if_locked)))
@@ -2297,6 +2566,18 @@ int handler::ha_open(TABLE *table_arg, const char *name, int mode,
}
else
{
+ DBUG_ASSERT(m_psi == NULL);
+ DBUG_ASSERT(table_share != NULL);
+ /*
+ Do not call this for partitions handlers, since it may take too much
+ resources.
+ So only use the m_psi on table level, not for individual partitions.
+ */
+ if (!(test_if_locked & HA_OPEN_NO_PSI_CALL))
+ {
+ m_psi= PSI_CALL_open_table(ha_table_share_psi(), this);
+ }
+
if (table->s->db_options_in_use & HA_OPTION_READ_ONLY_DATA)
table->db_stat|=HA_READ_ONLY;
(void) extra(HA_EXTRA_NO_READCHECK); // Not needed in SQL
@@ -2305,7 +2586,7 @@ int handler::ha_open(TABLE *table_arg, const char *name, int mode,
if (!ref && !(ref= (uchar*) alloc_root(&table->mem_root,
ALIGN_SIZE(ref_length)*2)))
{
- close();
+ ha_close();
error=HA_ERR_OUT_OF_MEM;
}
else
@@ -2313,11 +2594,11 @@ int handler::ha_open(TABLE *table_arg, const char *name, int mode,
cached_table_flags= table_flags();
}
reset_statistics();
- internal_tmp_table= test(test_if_locked & HA_OPEN_INTERNAL_TABLE);
+ internal_tmp_table= MY_TEST(test_if_locked & HA_OPEN_INTERNAL_TABLE);
DBUG_RETURN(error);
}
-int handler::ha_close()
+int handler::ha_close(void)
{
DBUG_ENTER("ha_close");
/*
@@ -2326,9 +2607,193 @@ int handler::ha_close()
*/
if (table->in_use)
status_var_add(table->in_use->status_var.rows_tmp_read, rows_tmp_read);
+ PSI_CALL_close_table(m_psi);
+ m_psi= NULL; /* instrumentation handle, invalid after close_table() */
+
+ DBUG_ASSERT(m_lock_type == F_UNLCK);
+ DBUG_ASSERT(inited == NONE);
DBUG_RETURN(close());
}
+int handler::ha_rnd_next(uchar *buf)
+{
+ int result;
+ DBUG_ENTER("handler::ha_rnd_next");
+ DBUG_ASSERT(table_share->tmp_table != NO_TMP_TABLE ||
+ m_lock_type != F_UNLCK);
+ DBUG_ASSERT(inited == RND);
+
+ MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_FETCH_ROW, MAX_KEY, 0,
+ { result= rnd_next(buf); })
+ if (!result)
+ {
+ update_rows_read();
+ increment_statistics(&SSV::ha_read_rnd_next_count);
+ }
+ else if (result == HA_ERR_RECORD_DELETED)
+ increment_statistics(&SSV::ha_read_rnd_deleted_count);
+ else
+ increment_statistics(&SSV::ha_read_rnd_next_count);
+
+ table->status=result ? STATUS_NOT_FOUND: 0;
+ DBUG_RETURN(result);
+}
+
+int handler::ha_rnd_pos(uchar *buf, uchar *pos)
+{
+ int result;
+ DBUG_ENTER("handler::ha_rnd_pos");
+ DBUG_ASSERT(table_share->tmp_table != NO_TMP_TABLE ||
+ m_lock_type != F_UNLCK);
+ /* TODO: Find out how to solve ha_rnd_pos when finding duplicate update. */
+ /* DBUG_ASSERT(inited == RND); */
+
+ MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_FETCH_ROW, MAX_KEY, 0,
+ { result= rnd_pos(buf, pos); })
+ increment_statistics(&SSV::ha_read_rnd_count);
+ if (!result)
+ update_rows_read();
+ table->status=result ? STATUS_NOT_FOUND: 0;
+ DBUG_RETURN(result);
+}
+
+int handler::ha_index_read_map(uchar *buf, const uchar *key,
+ key_part_map keypart_map,
+ enum ha_rkey_function find_flag)
+{
+ int result;
+ DBUG_ENTER("handler::ha_index_read_map");
+ DBUG_ASSERT(table_share->tmp_table != NO_TMP_TABLE ||
+ m_lock_type != F_UNLCK);
+ DBUG_ASSERT(inited==INDEX);
+
+ MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_FETCH_ROW, active_index, 0,
+ { result= index_read_map(buf, key, keypart_map, find_flag); })
+ increment_statistics(&SSV::ha_read_key_count);
+ if (!result)
+ update_index_statistics();
+ table->status=result ? STATUS_NOT_FOUND: 0;
+ DBUG_RETURN(result);
+}
+
+/*
+ @note: Other index lookup/navigation functions require prior
+ handler->index_init() call. This function is different, it requires
+ that the scan is not initialized, and accepts "uint index" as an argument.
+*/
+
+int handler::ha_index_read_idx_map(uchar *buf, uint index, const uchar *key,
+ key_part_map keypart_map,
+ enum ha_rkey_function find_flag)
+{
+ int result;
+ DBUG_ASSERT(inited==NONE);
+ DBUG_ASSERT(table_share->tmp_table != NO_TMP_TABLE ||
+ m_lock_type != F_UNLCK);
+ DBUG_ASSERT(end_range == NULL);
+ MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_FETCH_ROW, index, 0,
+ { result= index_read_idx_map(buf, index, key, keypart_map, find_flag); })
+ increment_statistics(&SSV::ha_read_key_count);
+ if (!result)
+ {
+ update_rows_read();
+ index_rows_read[index]++;
+ }
+ table->status=result ? STATUS_NOT_FOUND: 0;
+ return result;
+}
+
+int handler::ha_index_next(uchar * buf)
+{
+ int result;
+ DBUG_ENTER("handler::ha_index_next");
+ DBUG_ASSERT(table_share->tmp_table != NO_TMP_TABLE ||
+ m_lock_type != F_UNLCK);
+ DBUG_ASSERT(inited==INDEX);
+
+ MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_FETCH_ROW, active_index, 0,
+ { result= index_next(buf); })
+ increment_statistics(&SSV::ha_read_next_count);
+ if (!result)
+ update_index_statistics();
+ table->status=result ? STATUS_NOT_FOUND: 0;
+ DBUG_RETURN(result);
+}
+
+int handler::ha_index_prev(uchar * buf)
+{
+ int result;
+ DBUG_ENTER("handler::ha_index_prev");
+ DBUG_ASSERT(table_share->tmp_table != NO_TMP_TABLE ||
+ m_lock_type != F_UNLCK);
+ DBUG_ASSERT(inited==INDEX);
+
+ MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_FETCH_ROW, active_index, 0,
+ { result= index_prev(buf); })
+ increment_statistics(&SSV::ha_read_prev_count);
+ if (!result)
+ update_index_statistics();
+ table->status=result ? STATUS_NOT_FOUND: 0;
+ DBUG_RETURN(result);
+}
+
+int handler::ha_index_first(uchar * buf)
+{
+ int result;
+ DBUG_ASSERT(table_share->tmp_table != NO_TMP_TABLE ||
+ m_lock_type != F_UNLCK);
+ DBUG_ASSERT(inited==INDEX);
+
+ MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_FETCH_ROW, active_index, 0,
+ { result= index_first(buf); })
+ increment_statistics(&SSV::ha_read_first_count);
+ if (!result)
+ update_index_statistics();
+ table->status=result ? STATUS_NOT_FOUND: 0;
+ return result;
+}
+
+int handler::ha_index_last(uchar * buf)
+{
+ int result;
+ DBUG_ASSERT(table_share->tmp_table != NO_TMP_TABLE ||
+ m_lock_type != F_UNLCK);
+ DBUG_ASSERT(inited==INDEX);
+
+ MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_FETCH_ROW, active_index, 0,
+ { result= index_last(buf); })
+ increment_statistics(&SSV::ha_read_last_count);
+ if (!result)
+ update_index_statistics();
+ table->status=result ? STATUS_NOT_FOUND: 0;
+ return result;
+}
+
+int handler::ha_index_next_same(uchar *buf, const uchar *key, uint keylen)
+{
+ int result;
+ DBUG_ASSERT(table_share->tmp_table != NO_TMP_TABLE ||
+ m_lock_type != F_UNLCK);
+ DBUG_ASSERT(inited==INDEX);
+
+ MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_FETCH_ROW, active_index, 0,
+ { result= index_next_same(buf, key, keylen); })
+ increment_statistics(&SSV::ha_read_next_count);
+ if (!result)
+ update_index_statistics();
+ table->status=result ? STATUS_NOT_FOUND: 0;
+ return result;
+}
+
+
+bool handler::ha_was_semi_consistent_read()
+{
+ bool result= was_semi_consistent_read();
+ if (result)
+ increment_statistics(&SSV::ha_read_retry_count);
+ return result;
+}
+
/* Initialize handler for random reading, with error handling */
int handler::ha_rnd_init_with_error(bool scan)
@@ -2673,7 +3138,8 @@ int handler::update_auto_increment()
if (unlikely(nr == ULONGLONG_MAX))
DBUG_RETURN(HA_ERR_AUTOINC_ERANGE);
- DBUG_PRINT("info",("auto_increment: %llu",nr));
+ DBUG_PRINT("info",("auto_increment: %llu nb_reserved_values: %llu",
+ nr, append ? nb_reserved_values : 0));
/* Store field without warning (Warning will be printed by insert) */
save_count_cuted_fields= thd->count_cuted_fields;
@@ -2691,8 +3157,6 @@ int handler::update_auto_increment()
}
if (append)
{
- DBUG_PRINT("info",("nb_reserved_values: %llu",nb_reserved_values));
-
auto_inc_interval_for_cur_row.replace(nr, nb_reserved_values,
variables->auto_increment_increment);
auto_inc_intervals_count++;
@@ -2789,7 +3253,7 @@ void handler::get_auto_increment(ulonglong offset, ulonglong increment,
if (table->s->next_number_keypart == 0)
{ // Autoincrement at key-start
- error=ha_index_last(table->record[1]);
+ error= ha_index_last(table->record[1]);
/*
MySQL implicitely assumes such method does locking (as MySQL decides to
use nr+increment without checking again with the handler, in
@@ -2837,6 +3301,9 @@ void handler::get_auto_increment(ulonglong offset, ulonglong increment,
void handler::ha_release_auto_increment()
{
DBUG_ENTER("ha_release_auto_increment");
+ DBUG_ASSERT(table_share->tmp_table != NO_TMP_TABLE ||
+ m_lock_type != F_UNLCK ||
+ (!next_insert_id && !insert_id_for_cur_row));
release_auto_increment();
insert_id_for_cur_row= 0;
auto_inc_interval_for_cur_row.replace(0, 0, 0);
@@ -2854,33 +3321,58 @@ void handler::ha_release_auto_increment()
}
-void handler::print_keydup_error(uint key_nr, const char *msg, myf errflag)
+/**
+ Construct and emit duplicate key error message using information
+ from table's record buffer.
+
+ @param table TABLE object which record buffer should be used as
+ source for column values.
+ @param key Key description.
+ @param msg Error message template to which key value should be
+ added.
+ @param errflag Flags for my_error() call.
+*/
+
+void print_keydup_error(TABLE *table, KEY *key, const char *msg, myf errflag)
{
/* Write the duplicated key in the error message */
- char key[MAX_KEY_LENGTH];
- String str(key,sizeof(key),system_charset_info);
+ char key_buff[MAX_KEY_LENGTH];
+ String str(key_buff,sizeof(key_buff),system_charset_info);
- if (key_nr == MAX_KEY)
+ if (key == NULL)
{
- /* Key is unknown */
- str.copy("", 0, system_charset_info);
- my_printf_error(ER_DUP_ENTRY, msg, errflag, str.c_ptr(), "*UNKNOWN*");
+ /*
+ Key is unknown. Should only happen if storage engine reports wrong
+ duplicate key number.
+ */
+ my_printf_error(ER_DUP_ENTRY, msg, errflag, "", "*UNKNOWN*");
}
else
{
/* Table is opened and defined at this point */
- key_unpack(&str,table,(uint) key_nr);
+ key_unpack(&str,table, key);
uint max_length=MYSQL_ERRMSG_SIZE-(uint) strlen(msg);
if (str.length() >= max_length)
{
str.length(max_length-4);
str.append(STRING_WITH_LEN("..."));
}
- my_printf_error(ER_DUP_ENTRY, msg,
- errflag, str.c_ptr_safe(), table->key_info[key_nr].name);
+ my_printf_error(ER_DUP_ENTRY, msg, errflag, str.c_ptr_safe(), key->name);
}
}
+/**
+ Construct and emit duplicate key error message using information
+ from table's record buffer.
+
+ @sa print_keydup_error(table, key, msg, errflag).
+*/
+
+void print_keydup_error(TABLE *table, KEY *key, myf errflag)
+{
+ print_keydup_error(table, key, ER(ER_DUP_ENTRY_WITH_KEY_NAME), errflag);
+}
+
/**
Print error that we got from handler function.
@@ -2900,7 +3392,7 @@ void handler::print_error(int error, myf errflag)
DBUG_ENTER("handler::print_error");
DBUG_PRINT("enter",("error: %d",error));
- int textno=ER_GET_ERRNO;
+ int textno= -1; // impossible value
switch (error) {
case EACCES:
textno=ER_OPEN_AS_READONLY;
@@ -2910,6 +3402,7 @@ void handler::print_error(int error, myf errflag)
break;
case ENOENT:
case ENOTDIR:
+ case ELOOP:
textno=ER_FILE_NOT_FOUND;
break;
case ENOSPC:
@@ -2944,9 +3437,9 @@ void handler::print_error(int error, myf errflag)
if (table)
{
uint key_nr=get_dup_key(error);
- if ((int) key_nr >= 0)
+ if ((int) key_nr >= 0 && key_nr < table->s->keys)
{
- print_keydup_error(key_nr, ER(ER_DUP_ENTRY_WITH_KEY_NAME), errflag);
+ print_keydup_error(table, &table->key_info[key_nr], errflag);
DBUG_VOID_RETURN;
}
}
@@ -2955,44 +3448,31 @@ void handler::print_error(int error, myf errflag)
}
case HA_ERR_FOREIGN_DUPLICATE_KEY:
{
- uint key_nr= get_dup_key(error);
- if ((int) key_nr >= 0)
- {
- uint max_length;
- /* Write the key in the error message */
- char key[MAX_KEY_LENGTH];
- String str(key,sizeof(key),system_charset_info);
- /* Table is opened and defined at this point */
+ char rec_buf[MAX_KEY_LENGTH];
+ String rec(rec_buf, sizeof(rec_buf), system_charset_info);
+ /* Table is opened and defined at this point */
- /*
- Use primary_key instead of key_nr because key_nr is a key
- number in the child FK table, not in our 'table'. See
- Bug#12661768 UPDATE IGNORE CRASHES SERVER IF TABLE IS INNODB
- AND IT IS PARENT FOR OTHER ONE This bug gets a better fix in
- MySQL 5.6, but it is too risky to get that in 5.1 and 5.5
- (extending the handler interface and adding new error message
- codes)
- */
- if (table->s->primary_key < MAX_KEY)
- key_unpack(&str,table,table->s->primary_key);
- else
- {
- LEX_CUSTRING tmp= {USTRING_WITH_LEN("Unknown key value")};
- str.set((const char*) tmp.str, tmp.length, system_charset_info);
- }
- max_length= (MYSQL_ERRMSG_SIZE-
- (uint) strlen(ER(ER_FOREIGN_DUPLICATE_KEY)));
- if (str.length() >= max_length)
- {
- str.length(max_length-4);
- str.append(STRING_WITH_LEN("..."));
+ /*
+ Just print the subset of fields that are part of the first index,
+ printing the whole row from there is not easy.
+ */
+ key_unpack(&rec, table, &table->key_info[0]);
+
+ char child_table_name[NAME_LEN + 1];
+ char child_key_name[NAME_LEN + 1];
+ if (get_foreign_dup_key(child_table_name, sizeof(child_table_name),
+ child_key_name, sizeof(child_key_name)))
+ {
+ my_error(ER_FOREIGN_DUPLICATE_KEY_WITH_CHILD_INFO, errflag,
+ table_share->table_name.str, rec.c_ptr_safe(),
+ child_table_name, child_key_name);
}
- my_error(ER_FOREIGN_DUPLICATE_KEY, errflag, table_share->table_name.str,
- str.c_ptr_safe(), key_nr+1);
- DBUG_VOID_RETURN;
+ else
+ {
+ my_error(ER_FOREIGN_DUPLICATE_KEY_WITHOUT_CHILD_INFO, errflag,
+ table_share->table_name.str, rec.c_ptr_safe());
}
- textno= ER_DUP_KEY;
- break;
+ DBUG_VOID_RETURN;
}
case HA_ERR_NULL_IN_SPATIAL:
my_error(ER_CANT_CREATE_GEOMETRY_OBJECT, errflag);
@@ -3030,7 +3510,9 @@ void handler::print_error(int error, myf errflag)
textno=ER_OUT_OF_RESOURCES;
break;
case HA_ERR_WRONG_COMMAND:
- textno=ER_ILLEGAL_HA;
+ my_error(ER_ILLEGAL_HA, MYF(0), table_type(), table_share->db.str,
+ table_share->table_name.str);
+ DBUG_VOID_RETURN;
break;
case HA_ERR_OLD_FILE:
textno=ER_OLD_KEYFILE;
@@ -3111,7 +3593,7 @@ void handler::print_error(int error, myf errflag)
case HA_ERR_AUTOINC_ERANGE:
textno= error;
my_error(textno, errflag, table->next_number_field->field_name,
- table->in_use->warning_info->current_row_for_warning());
+ table->in_use->get_stmt_da()->current_row_for_warning());
DBUG_VOID_RETURN;
break;
case HA_ERR_TOO_MANY_CONCURRENT_TRXS:
@@ -3120,6 +3602,9 @@ void handler::print_error(int error, myf errflag)
case HA_ERR_INDEX_COL_TOO_LONG:
textno= ER_INDEX_COLUMN_TOO_LONG;
break;
+ case HA_ERR_NOT_IN_LOCK_PARTITIONS:
+ textno=ER_ROW_DOES_NOT_MATCH_GIVEN_PARTITION_SET;
+ break;
case HA_ERR_INDEX_CORRUPT:
textno= ER_INDEX_CORRUPT;
break;
@@ -3148,21 +3633,12 @@ void handler::print_error(int error, myf errflag)
my_error(ER_GET_ERRMSG, errflag, error, str.c_ptr(), engine);
}
}
- else if (error >= HA_ERR_FIRST && error <= HA_ERR_LAST)
- {
- const char* engine= table_type();
- const char *errmsg= handler_error_messages[error - HA_ERR_FIRST];
- my_error(ER_GET_ERRMSG, errflag, error, errmsg, engine);
- SET_FATAL_ERROR;
- }
else
- {
- my_error(ER_GET_ERRNO, errflag,error);
- /* SET_FATAL_ERROR; */
- }
+ my_error(ER_GET_ERRNO, errflag, error, table_type());
DBUG_VOID_RETURN;
}
}
+ DBUG_ASSERT(textno > 0);
if (fatal_error)
{
/* Ensure this becomes a true error */
@@ -3176,7 +3652,17 @@ void handler::print_error(int error, myf errflag)
errflag|= ME_NOREFRESH;
}
}
- my_error(textno, errflag, table_share->table_name.str, error);
+
+ /* if we got an OS error from a file-based engine, specify a path of error */
+ if (error < HA_ERR_FIRST && bas_ext()[0])
+ {
+ char buff[FN_REFLEN];
+ strxnmov(buff, sizeof(buff),
+ table_share->normalized_path.str, bas_ext()[0], NULL);
+ my_error(textno, errflag, buff, error);
+ }
+ else
+ my_error(textno, errflag, table_share->table_name.str, error);
DBUG_VOID_RETURN;
}
@@ -3192,10 +3678,11 @@ void handler::print_error(int error, myf errflag)
*/
bool handler::get_error_message(int error, String* buf)
{
+ DBUG_EXECUTE_IF("external_lock_failure",
+ buf->set_ascii(STRING_WITH_LEN("KABOOM!")););
return FALSE;
}
-
/**
Check for incompatible collation changes.
@@ -3216,7 +3703,7 @@ int handler::check_collation_compatibility()
for (; key < key_end; key++)
{
KEY_PART_INFO *key_part= key->key_part;
- KEY_PART_INFO *key_part_end= key_part + key->key_parts;
+ KEY_PART_INFO *key_part_end= key_part + key->user_defined_key_parts;
for (; key_part < key_part_end; key_part++)
{
if (!key_part->fieldnr)
@@ -3236,9 +3723,10 @@ int handler::check_collation_compatibility()
(cs_number == 33 || /* utf8_general_ci - bug #27877 */
cs_number == 35))) /* ucs2_general_ci - bug #27877 */
return HA_ADMIN_NEEDS_UPGRADE;
- }
- }
- }
+ }
+ }
+ }
+
return 0;
}
@@ -3249,6 +3737,9 @@ int handler::ha_check_for_upgrade(HA_CHECK_OPT *check_opt)
KEY *keyinfo, *keyend;
KEY_PART_INFO *keypart, *keypartend;
+ if (table->s->incompatible_version)
+ return HA_ADMIN_NEEDS_ALTER;
+
if (!table->s->mysql_version)
{
/* check for blob-in-key error */
@@ -3257,7 +3748,7 @@ int handler::ha_check_for_upgrade(HA_CHECK_OPT *check_opt)
for (; keyinfo < keyend; keyinfo++)
{
keypart= keyinfo->key_part;
- keypartend= keypart + keyinfo->key_parts;
+ keypartend= keypart + keyinfo->user_defined_key_parts;
for (; keypart < keypartend; keypart++)
{
if (!keypart->fieldnr)
@@ -3349,6 +3840,8 @@ err:
*/
uint handler::get_dup_key(int error)
{
+ DBUG_ASSERT(table_share->tmp_table != NO_TMP_TABLE ||
+ m_lock_type != F_UNLCK);
DBUG_ENTER("handler::get_dup_key");
table->file->errkey = (uint) -1;
if (error == HA_ERR_FOUND_DUPP_KEY || error == HA_ERR_FOREIGN_DUPLICATE_KEY ||
@@ -3378,7 +3871,12 @@ int handler::delete_table(const char *name)
{
int saved_error= 0;
int error= 0;
- int enoent_or_zero= ENOENT; // Error if no file was deleted
+ int enoent_or_zero;
+
+ if (ht->discover_table)
+ enoent_or_zero= 0; // the table may not exist in the engine, it's ok
+ else
+ enoent_or_zero= ENOENT; // the first file of bas_ext() *must* exist
for (const char **ext=bas_ext(); *ext ; ext++)
{
@@ -3452,6 +3950,8 @@ void handler::drop_table(const char *name)
int handler::ha_check(THD *thd, HA_CHECK_OPT *check_opt)
{
int error;
+ DBUG_ASSERT(table_share->tmp_table != NO_TMP_TABLE ||
+ m_lock_type != F_UNLCK);
if ((table->s->mysql_version >= MYSQL_VERSION_ID) &&
(check_opt->sql_flags & TT_FOR_UPGRADE))
@@ -3538,6 +4038,8 @@ int
handler::ha_bulk_update_row(const uchar *old_data, uchar *new_data,
uint *dup_key_found)
{
+ DBUG_ASSERT(table_share->tmp_table != NO_TMP_TABLE ||
+ m_lock_type == F_WRLCK);
mark_trx_read_write();
return bulk_update_row(old_data, new_data, dup_key_found);
@@ -3553,6 +4055,8 @@ handler::ha_bulk_update_row(const uchar *old_data, uchar *new_data,
int
handler::ha_delete_all_rows()
{
+ DBUG_ASSERT(table_share->tmp_table != NO_TMP_TABLE ||
+ m_lock_type == F_WRLCK);
mark_trx_read_write();
return delete_all_rows();
@@ -3568,6 +4072,8 @@ handler::ha_delete_all_rows()
int
handler::ha_truncate()
{
+ DBUG_ASSERT(table_share->tmp_table != NO_TMP_TABLE ||
+ m_lock_type == F_WRLCK);
mark_trx_read_write();
return truncate();
@@ -3583,6 +4089,8 @@ handler::ha_truncate()
int
handler::ha_reset_auto_increment(ulonglong value)
{
+ DBUG_ASSERT(table_share->tmp_table != NO_TMP_TABLE ||
+ m_lock_type == F_WRLCK);
mark_trx_read_write();
return reset_auto_increment(value);
@@ -3598,6 +4106,8 @@ handler::ha_reset_auto_increment(ulonglong value)
int
handler::ha_optimize(THD* thd, HA_CHECK_OPT* check_opt)
{
+ DBUG_ASSERT(table_share->tmp_table != NO_TMP_TABLE ||
+ m_lock_type == F_WRLCK);
mark_trx_read_write();
return optimize(thd, check_opt);
@@ -3613,6 +4123,8 @@ handler::ha_optimize(THD* thd, HA_CHECK_OPT* check_opt)
int
handler::ha_analyze(THD* thd, HA_CHECK_OPT* check_opt)
{
+ DBUG_ASSERT(table_share->tmp_table != NO_TMP_TABLE ||
+ m_lock_type != F_UNLCK);
mark_trx_read_write();
return analyze(thd, check_opt);
@@ -3628,6 +4140,8 @@ handler::ha_analyze(THD* thd, HA_CHECK_OPT* check_opt)
bool
handler::ha_check_and_repair(THD *thd)
{
+ DBUG_ASSERT(table_share->tmp_table != NO_TMP_TABLE ||
+ m_lock_type == F_UNLCK);
mark_trx_read_write();
return check_and_repair(thd);
@@ -3643,6 +4157,8 @@ handler::ha_check_and_repair(THD *thd)
int
handler::ha_disable_indexes(uint mode)
{
+ DBUG_ASSERT(table_share->tmp_table != NO_TMP_TABLE ||
+ m_lock_type != F_UNLCK);
mark_trx_read_write();
return disable_indexes(mode);
@@ -3658,6 +4174,8 @@ handler::ha_disable_indexes(uint mode)
int
handler::ha_enable_indexes(uint mode)
{
+ DBUG_ASSERT(table_share->tmp_table != NO_TMP_TABLE ||
+ m_lock_type != F_UNLCK);
mark_trx_read_write();
return enable_indexes(mode);
@@ -3673,26 +4191,106 @@ handler::ha_enable_indexes(uint mode)
int
handler::ha_discard_or_import_tablespace(my_bool discard)
{
+ DBUG_ASSERT(table_share->tmp_table != NO_TMP_TABLE ||
+ m_lock_type == F_WRLCK);
mark_trx_read_write();
return discard_or_import_tablespace(discard);
}
-/**
- Prepare for alter: public interface.
+bool handler::ha_prepare_inplace_alter_table(TABLE *altered_table,
+ Alter_inplace_info *ha_alter_info)
+{
+ DBUG_ASSERT(table_share->tmp_table != NO_TMP_TABLE ||
+ m_lock_type != F_UNLCK);
+ mark_trx_read_write();
+
+ return prepare_inplace_alter_table(altered_table, ha_alter_info);
+}
- Called to prepare an *online* ALTER.
- @sa handler::prepare_for_alter()
+bool handler::ha_commit_inplace_alter_table(TABLE *altered_table,
+ Alter_inplace_info *ha_alter_info,
+ bool commit)
+{
+ /*
+ At this point we should have an exclusive metadata lock on the table.
+ The exception is if we're about to roll back changes (commit= false).
+ In this case, we might be rolling back after a failed lock upgrade,
+ so we could be holding the same lock level as for inplace_alter_table().
+ */
+ DBUG_ASSERT(ha_thd()->mdl_context.is_lock_owner(MDL_key::TABLE,
+ table->s->db.str,
+ table->s->table_name.str,
+ MDL_EXCLUSIVE) ||
+ !commit);
+
+ return commit_inplace_alter_table(altered_table, ha_alter_info, commit);
+}
+
+
+/*
+ Default implementation to support in-place alter table
+ and old online add/drop index API
*/
-void
-handler::ha_prepare_for_alter()
+enum_alter_inplace_result
+handler::check_if_supported_inplace_alter(TABLE *altered_table,
+ Alter_inplace_info *ha_alter_info)
{
- mark_trx_read_write();
+ DBUG_ENTER("handler::check_if_supported_inplace_alter");
+
+ HA_CREATE_INFO *create_info= ha_alter_info->create_info;
+
+ Alter_inplace_info::HA_ALTER_FLAGS inplace_offline_operations=
+ Alter_inplace_info::ALTER_COLUMN_EQUAL_PACK_LENGTH |
+ Alter_inplace_info::ALTER_COLUMN_NAME |
+ Alter_inplace_info::ALTER_COLUMN_DEFAULT |
+ Alter_inplace_info::ALTER_COLUMN_OPTION |
+ Alter_inplace_info::CHANGE_CREATE_OPTION |
+ Alter_inplace_info::ALTER_PARTITIONED |
+ Alter_inplace_info::ALTER_RENAME;
- prepare_for_alter();
+ /* Is there at least one operation that requires copy algorithm? */
+ if (ha_alter_info->handler_flags & ~inplace_offline_operations)
+ DBUG_RETURN(HA_ALTER_INPLACE_NOT_SUPPORTED);
+
+ /*
+ ALTER TABLE tbl_name CONVERT TO CHARACTER SET .. and
+ ALTER TABLE table_name DEFAULT CHARSET = .. most likely
+ change column charsets and so not supported in-place through
+ old API.
+
+ Changing of PACK_KEYS, MAX_ROWS and ROW_FORMAT options were
+ not supported as in-place operations in old API either.
+ */
+ if (create_info->used_fields & (HA_CREATE_USED_CHARSET |
+ HA_CREATE_USED_DEFAULT_CHARSET |
+ HA_CREATE_USED_PACK_KEYS |
+ HA_CREATE_USED_MAX_ROWS) ||
+ (table->s->row_type != create_info->row_type))
+ DBUG_RETURN(HA_ALTER_INPLACE_NOT_SUPPORTED);
+
+ uint table_changes= (ha_alter_info->handler_flags &
+ Alter_inplace_info::ALTER_COLUMN_EQUAL_PACK_LENGTH) ?
+ IS_EQUAL_PACK_LENGTH : IS_EQUAL_YES;
+ if (table->file->check_if_incompatible_data(create_info, table_changes)
+ == COMPATIBLE_DATA_YES)
+ DBUG_RETURN(HA_ALTER_INPLACE_NO_LOCK);
+
+ DBUG_RETURN(HA_ALTER_INPLACE_NOT_SUPPORTED);
+}
+
+void Alter_inplace_info::report_unsupported_error(const char *not_supported,
+ const char *try_instead)
+{
+ if (unsupported_reason == NULL)
+ my_error(ER_ALTER_OPERATION_NOT_SUPPORTED, MYF(0),
+ not_supported, try_instead);
+ else
+ my_error(ER_ALTER_OPERATION_NOT_SUPPORTED_REASON, MYF(0),
+ not_supported, unsupported_reason, try_instead);
}
@@ -3705,6 +4303,7 @@ handler::ha_prepare_for_alter()
int
handler::ha_rename_table(const char *from, const char *to)
{
+ DBUG_ASSERT(m_lock_type == F_UNLCK);
mark_trx_read_write();
return rename_table(from, to);
@@ -3737,6 +4336,7 @@ handler::ha_delete_table(const char *name)
void
handler::ha_drop_table(const char *name)
{
+ DBUG_ASSERT(m_lock_type == F_UNLCK);
mark_trx_read_write();
return drop_table(name);
@@ -3752,6 +4352,7 @@ handler::ha_drop_table(const char *name)
int
handler::ha_create(const char *name, TABLE *form, HA_CREATE_INFO *info)
{
+ DBUG_ASSERT(m_lock_type == F_UNLCK);
mark_trx_read_write();
int error= create(name, form, info);
if (!error &&
@@ -3764,17 +4365,22 @@ handler::ha_create(const char *name, TABLE *form, HA_CREATE_INFO *info)
/**
Create handler files for CREATE TABLE: public interface.
- @sa handler::create_handler_files()
+ @sa handler::create_partitioning_metadata()
*/
int
-handler::ha_create_handler_files(const char *name, const char *old_name,
- int action_flag, HA_CREATE_INFO *info)
+handler::ha_create_partitioning_metadata(const char *name, const char *old_name,
+ int action_flag)
{
- if (!opt_readonly || !info || !(info->options & HA_LEX_CREATE_TMP_TABLE))
- mark_trx_read_write();
+ /*
+ Normally this is done when unlocked, but in fast_alter_partition_table,
+ it is done on an already locked handler when preparing to alter/rename
+ partitions.
+ */
+ DBUG_ASSERT(m_lock_type == F_UNLCK ||
+ (!old_name && strcmp(name, table_share->path.str)));
- return create_handler_files(name, old_name, action_flag, info);
+ return create_partitioning_metadata(name, old_name, action_flag);
}
@@ -3791,7 +4397,13 @@ handler::ha_change_partitions(HA_CREATE_INFO *create_info,
ulonglong * const deleted,
const uchar *pack_frm_data,
size_t pack_frm_len)
-{
+{ /*
+ Must have at least RDLCK or be a TMP table. Read lock is needed to read
+ from current partitions and write lock will be taken on new partitions.
+ */
+ DBUG_ASSERT(table_share->tmp_table != NO_TMP_TABLE ||
+ m_lock_type != F_UNLCK);
+
mark_trx_read_write();
return change_partitions(create_info, path, copied, deleted,
@@ -3808,6 +4420,8 @@ handler::ha_change_partitions(HA_CREATE_INFO *create_info,
int
handler::ha_drop_partitions(const char *path)
{
+ DBUG_ASSERT(!table->db_stat);
+
mark_trx_read_write();
return drop_partitions(path);
@@ -3823,6 +4437,8 @@ handler::ha_drop_partitions(const char *path)
int
handler::ha_rename_partitions(const char *path)
{
+ DBUG_ASSERT(!table->db_stat);
+
mark_trx_read_write();
return rename_partitions(path);
@@ -3883,7 +4499,7 @@ int handler::index_next_same(uchar *buf, const uchar *key, uint keylen)
table->record[0]= buf;
key_info= table->key_info + active_index;
key_part= key_info->key_part;
- key_part_end= key_part + key_info->key_parts;
+ key_part_end= key_part + key_info->user_defined_key_parts;
for (; key_part < key_part_end; key_part++)
{
DBUG_ASSERT(key_part->field);
@@ -4062,132 +4678,68 @@ end:
*/
int ha_create_table(THD *thd, const char *path,
const char *db, const char *table_name,
- HA_CREATE_INFO *create_info,
- bool update_create_info)
+ HA_CREATE_INFO *create_info, LEX_CUSTRING *frm)
{
int error= 1;
TABLE table;
char name_buff[FN_REFLEN];
const char *name;
TABLE_SHARE share;
+ bool temp_table __attribute__((unused)) =
+ create_info->options & (HA_LEX_CREATE_TMP_TABLE | HA_CREATE_TMP_ALTER);
+
DBUG_ENTER("ha_create_table");
-
+
init_tmp_table_share(thd, &share, db, 0, table_name, path);
- if (open_table_def(thd, &share, 0) ||
- open_table_from_share(thd, &share, "", 0, (uint) READ_ALL, 0, &table,
- TRUE))
- goto err;
- if (update_create_info)
- update_create_info_from_table(create_info, &table);
+ if (frm)
+ {
+ bool write_frm_now= !create_info->db_type->discover_table &&
+ !create_info->tmp_table();
- name= get_canonical_filename(table.file, share.path.str, name_buff);
+ share.frm_image= frm;
- error= table.file->ha_create(name, &table, create_info);
- (void) closefrm(&table, 0);
- if (error)
+ // open an frm image
+ if (share.init_from_binary_frm_image(thd, write_frm_now,
+ frm->str, frm->length))
+ goto err;
+ }
+ else
{
- strxmov(name_buff, db, ".", table_name, NullS);
- my_error(ER_CANT_CREATE_TABLE, MYF(ME_BELL+ME_WAITTANG), name_buff, error);
+ // open an frm file
+ share.db_plugin= ha_lock_engine(thd, create_info->db_type);
+
+ if (open_table_def(thd, &share))
+ goto err;
}
-err:
- free_table_share(&share);
- DBUG_RETURN(error != 0);
-}
-/**
- Try to discover table from engine.
+ share.m_psi= PSI_CALL_get_table_share(temp_table, &share);
- @note
- If found, write the frm file to disk.
+ if (open_table_from_share(thd, &share, "", 0, READ_ALL, 0, &table, true))
+ goto err;
- @retval
- -1 Table did not exists
- @retval
- 0 Table created ok
- @retval
- > 0 Error, table existed but could not be created
-*/
-int ha_create_table_from_engine(THD* thd, const char *db, const char *name)
-{
- int error;
- uchar *frmblob;
- size_t frmlen;
- char path[FN_REFLEN + 1];
- HA_CREATE_INFO create_info;
- TABLE table;
- TABLE_SHARE share;
- DBUG_ENTER("ha_create_table_from_engine");
- DBUG_PRINT("enter", ("name '%s'.'%s'", db, name));
+ update_create_info_from_table(create_info, &table);
- bzero((uchar*) &create_info,sizeof(create_info));
- if ((error= ha_discover(thd, db, name, &frmblob, &frmlen)))
- {
- /* Table could not be discovered and thus not created */
- DBUG_RETURN(error);
- }
+ name= get_canonical_filename(table.file, share.path.str, name_buff);
- /*
- Table exists in handler and could be discovered
- frmblob and frmlen are set, write the frm to disk
- */
+ error= table.file->ha_create(name, &table, create_info);
- build_table_filename(path, sizeof(path) - 1, db, name, "", 0);
- // Save the frm file
- error= writefrm(path, frmblob, frmlen);
- my_free(frmblob);
if (error)
- DBUG_RETURN(2);
-
- init_tmp_table_share(thd, &share, db, 0, name, path);
- if (open_table_def(thd, &share, 0))
- {
- DBUG_RETURN(3);
- }
- if (open_table_from_share(thd, &share, "" ,0, 0, 0, &table, FALSE))
{
- free_table_share(&share);
- DBUG_RETURN(3);
+ if (!thd->is_error())
+ my_error(ER_CANT_CREATE_TABLE, MYF(0), db, table_name, error);
+ table.file->print_error(error, MYF(ME_JUST_WARNING));
+ PSI_CALL_drop_table_share(temp_table, share.db.str, share.db.length,
+ share.table_name.str, share.table_name.length);
}
- update_create_info_from_table(&create_info, &table);
- create_info.table_options|= HA_OPTION_CREATE_FROM_ENGINE;
-
- get_canonical_filename(table.file, path, path);
- error=table.file->ha_create(path, &table, &create_info);
- (void) closefrm(&table, 1);
-
+ (void) closefrm(&table, 0);
+
+err:
+ free_table_share(&share);
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;
@@ -4221,11 +4773,13 @@ int ha_init_key_cache(const char *name, KEY_CACHE *key_cache, void *unused
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;
+ uint changed_blocks_hash_size= (uint)key_cache->changed_blocks_hash_size;
mysql_mutex_unlock(&LOCK_global_system_variables);
DBUG_RETURN(!init_key_cache(key_cache,
tmp_block_size,
tmp_buff_size,
division_limit, age_threshold,
+ changed_blocks_hash_size,
partitions));
}
DBUG_RETURN(0);
@@ -4246,10 +4800,12 @@ int ha_resize_key_cache(KEY_CACHE *key_cache)
long tmp_block_size= (long) key_cache->param_block_size;
uint division_limit= (uint)key_cache->param_division_limit;
uint age_threshold= (uint)key_cache->param_age_threshold;
+ uint changed_blocks_hash_size= (uint)key_cache->changed_blocks_hash_size;
mysql_mutex_unlock(&LOCK_global_system_variables);
DBUG_RETURN(!resize_key_cache(key_cache, tmp_block_size,
tmp_buff_size,
- division_limit, age_threshold));
+ division_limit, age_threshold,
+ changed_blocks_hash_size));
}
DBUG_RETURN(0);
}
@@ -4289,10 +4845,12 @@ int ha_repartition_key_cache(KEY_CACHE *key_cache)
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;
+ uint changed_blocks_hash_size= (uint)key_cache->changed_blocks_hash_size;
mysql_mutex_unlock(&LOCK_global_system_variables);
DBUG_RETURN(!repartition_key_cache(key_cache, tmp_block_size,
tmp_buff_size,
division_limit, age_threshold,
+ changed_blocks_hash_size,
partitions));
}
DBUG_RETURN(0);
@@ -4310,149 +4868,407 @@ int ha_change_key_cache(KEY_CACHE *old_key_cache,
}
-/**
- Try to discover one table from handler(s).
-
- @retval
- -1 Table did not exists
- @retval
- 0 OK. In this case *frmblob and *frmlen are set
- @retval
- >0 error. frmblob and frmlen may not be set
-*/
-struct st_discover_args
-{
- const char *db;
- const char *name;
- uchar **frmblob;
- size_t *frmlen;
-};
-
static my_bool discover_handlerton(THD *thd, plugin_ref plugin,
void *arg)
{
- st_discover_args *vargs= (st_discover_args *)arg;
- handlerton *hton= plugin_data(plugin, handlerton *);
- if (hton->state == SHOW_OPTION_YES && hton->discover &&
- (!(hton->discover(hton, thd, vargs->db, vargs->name,
- vargs->frmblob,
- vargs->frmlen))))
- return TRUE;
+ TABLE_SHARE *share= (TABLE_SHARE *)arg;
+ handlerton *hton= plugin_hton(plugin);
+ if (hton->state == SHOW_OPTION_YES && hton->discover_table)
+ {
+ share->db_plugin= plugin;
+ int error= hton->discover_table(hton, thd, share);
+ if (error != HA_ERR_NO_SUCH_TABLE)
+ {
+ if (error)
+ {
+ DBUG_ASSERT(share->error); // tdc_lock_share needs that
+ /*
+ report an error, unless it is "generic" and a more
+ specific one was already reported
+ */
+ if (error != HA_ERR_GENERIC || !thd->is_error())
+ my_error(ER_GET_ERRNO, MYF(0), error, plugin_name(plugin)->str);
+ share->db_plugin= 0;
+ }
+ else
+ share->error= OPEN_FRM_OK;
- return FALSE;
+ status_var_increment(thd->status_var.ha_discover_count);
+ return TRUE; // abort the search
+ }
+ share->db_plugin= 0;
+ }
+
+ DBUG_ASSERT(share->error == OPEN_FRM_OPEN_ERROR);
+ return FALSE; // continue with the next engine
}
-int ha_discover(THD *thd, const char *db, const char *name,
- uchar **frmblob, size_t *frmlen)
+int ha_discover_table(THD *thd, TABLE_SHARE *share)
{
- int error= -1; // Table does not exist in any handler
- DBUG_ENTER("ha_discover");
- DBUG_PRINT("enter", ("db: %s, name: %s", db, name));
- st_discover_args args= {db, name, frmblob, frmlen};
+ DBUG_ENTER("ha_discover_table");
+ int found;
- if (is_prefix(name,tmp_file_prefix)) /* skip temporary tables */
- DBUG_RETURN(error);
+ DBUG_ASSERT(share->error == OPEN_FRM_OPEN_ERROR); // share is not OK yet
- if (plugin_foreach(thd, discover_handlerton,
- MYSQL_STORAGE_ENGINE_PLUGIN, &args))
- error= 0;
+ if (!engines_with_discover)
+ found= FALSE;
+ else if (share->db_plugin)
+ found= discover_handlerton(thd, share->db_plugin, share);
+ else
+ found= plugin_foreach(thd, discover_handlerton,
+ MYSQL_STORAGE_ENGINE_PLUGIN, share);
+
+ if (!found)
+ open_table_error(share, OPEN_FRM_OPEN_ERROR, ENOENT); // not found
- if (!error)
- status_var_increment(thd->status_var.ha_discover_count);
- DBUG_RETURN(error);
+ DBUG_RETURN(share->error != OPEN_FRM_OK);
}
+static my_bool file_ext_exists(char *path, size_t path_len, const char *ext)
+{
+ strmake(path + path_len, ext, FN_REFLEN - path_len);
+ return !access(path, F_OK);
+}
-/**
- Call this function in order to give the handler the possiblity
- to ask engine if there are any new tables that should be written to disk
- or any dropped tables that need to be removed from disk
-*/
-struct st_find_files_args
+struct st_discover_existence_args
{
- const char *db;
- const char *path;
- const char *wild;
- bool dir;
- List<LEX_STRING> *files;
+ char *path;
+ size_t path_len;
+ const char *db, *table_name;
+ handlerton *hton;
+ bool frm_exists;
};
-static my_bool find_files_handlerton(THD *thd, plugin_ref plugin,
- void *arg)
+static my_bool discover_existence(THD *thd, plugin_ref plugin,
+ void *arg)
{
- st_find_files_args *vargs= (st_find_files_args *)arg;
- handlerton *hton= plugin_data(plugin, handlerton *);
+ st_discover_existence_args *args= (st_discover_existence_args*)arg;
+ handlerton *ht= plugin_hton(plugin);
+ if (ht->state != SHOW_OPTION_YES || !ht->discover_table_existence)
+ return args->frm_exists;
+ args->hton= ht;
- if (hton->state == SHOW_OPTION_YES && hton->find_files)
- if (hton->find_files(hton, thd, vargs->db, vargs->path, vargs->wild,
- vargs->dir, vargs->files))
- return TRUE;
+ if (ht->discover_table_existence == ext_based_existence)
+ return file_ext_exists(args->path, args->path_len,
+ ht->tablefile_extensions[0]);
- return FALSE;
+ return ht->discover_table_existence(ht, args->db, args->table_name);
}
-int
-ha_find_files(THD *thd,const char *db,const char *path,
- const char *wild, bool dir, List<LEX_STRING> *files)
+class Table_exists_error_handler : public Internal_error_handler
{
- int error= 0;
- DBUG_ENTER("ha_find_files");
- DBUG_PRINT("enter", ("db: '%s' path: '%s' wild: '%s' dir: %d",
- db, path, wild, dir));
- st_find_files_args args= {db, path, wild, dir, files};
-
- plugin_foreach(thd, find_files_handlerton,
- MYSQL_STORAGE_ENGINE_PLUGIN, &args);
- /* The return value is not currently used */
- DBUG_RETURN(error);
+public:
+ Table_exists_error_handler()
+ : m_handled_errors(0), m_unhandled_errors(0)
+ {}
+
+ bool handle_condition(THD *thd,
+ uint sql_errno,
+ const char* sqlstate,
+ Sql_condition::enum_warning_level level,
+ const char* msg,
+ Sql_condition ** cond_hdl)
+ {
+ *cond_hdl= NULL;
+ if (sql_errno == ER_NO_SUCH_TABLE ||
+ sql_errno == ER_NO_SUCH_TABLE_IN_ENGINE ||
+ sql_errno == ER_WRONG_OBJECT)
+ {
+ m_handled_errors++;
+ return TRUE;
+ }
+
+ if (level == Sql_condition::WARN_LEVEL_ERROR)
+ m_unhandled_errors++;
+ return FALSE;
+ }
+
+ bool safely_trapped_errors()
+ {
+ return ((m_handled_errors > 0) && (m_unhandled_errors == 0));
+ }
+
+private:
+ int m_handled_errors;
+ int m_unhandled_errors;
+};
+
+/**
+ Check if a given table exists, without doing a full discover, if possible
+
+ If the 'hton' is not NULL, it's set to the handlerton of the storage engine
+ of this table, or to view_pseudo_hton if the frm belongs to a view.
+
+ This function takes discovery correctly into account. If frm is found,
+ it discovers the table to make sure it really exists in the engine.
+ If no frm is found it discovers the table, in case it still exists in
+ the engine.
+
+ While it tries to cut corners (don't open .frm if no discovering engine is
+ enabled, no full discovery if all discovering engines support
+ discover_table_existence, etc), it still *may* be quite expensive
+ and must be used sparingly.
+
+ @retval true Table exists (even if the error occurred, like bad frm)
+ @retval false Table does not exist (one can do CREATE TABLE table_name)
+
+ @note if frm exists and the table in engine doesn't, *hton will be set,
+ but the return value will be false.
+
+ @note if frm file exists, but the table cannot be opened (engine not
+ loaded, frm is invalid), the return value will be true, but
+ *hton will be NULL.
+*/
+bool ha_table_exists(THD *thd, const char *db, const char *table_name,
+ handlerton **hton)
+{
+ handlerton *dummy;
+ DBUG_ENTER("ha_table_exists");
+
+ if (hton)
+ *hton= 0;
+ else if (engines_with_discover)
+ hton= &dummy;
+
+ TABLE_SHARE *share= tdc_lock_share(db, table_name);
+ if (share)
+ {
+ if (hton)
+ *hton= share->db_type();
+ tdc_unlock_share(share);
+ DBUG_RETURN(TRUE);
+ }
+
+ char path[FN_REFLEN + 1];
+ size_t path_len = build_table_filename(path, sizeof(path) - 1,
+ db, table_name, "", 0);
+ st_discover_existence_args args= {path, path_len, db, table_name, 0, true};
+
+ if (file_ext_exists(path, path_len, reg_ext))
+ {
+ bool exists= true;
+ if (hton)
+ {
+ enum legacy_db_type db_type;
+ if (dd_frm_type(thd, path, &db_type) != FRMTYPE_VIEW)
+ {
+ handlerton *ht= ha_resolve_by_legacy_type(thd, db_type);
+ if ((*hton= ht))
+ // verify that the table really exists
+ exists= discover_existence(thd,
+ plugin_int_to_ref(hton2plugin[ht->slot]), &args);
+ }
+ else
+ *hton= view_pseudo_hton;
+ }
+ DBUG_RETURN(exists);
+ }
+
+ args.frm_exists= false;
+ if (plugin_foreach(thd, discover_existence, MYSQL_STORAGE_ENGINE_PLUGIN,
+ &args))
+ {
+ if (hton)
+ *hton= args.hton;
+ DBUG_RETURN(TRUE);
+ }
+
+
+ if (need_full_discover_for_existence)
+ {
+ TABLE_LIST table;
+ uint flags = GTS_TABLE | GTS_VIEW;
+
+ if (!hton)
+ flags|= GTS_NOLOCK;
+
+ Table_exists_error_handler no_such_table_handler;
+ thd->push_internal_handler(&no_such_table_handler);
+ TABLE_SHARE *share= tdc_acquire_share(thd, db, table_name, flags);
+ thd->pop_internal_handler();
+
+ if (hton && share)
+ {
+ *hton= share->db_type();
+ tdc_release_share(share);
+ }
+
+ // the table doesn't exist if we've caught ER_NO_SUCH_TABLE and nothing else
+ DBUG_RETURN(!no_such_table_handler.safely_trapped_errors());
+ }
+
+ DBUG_RETURN(FALSE);
}
/**
- Ask handler if the table exists in engine.
- @retval
- HA_ERR_NO_SUCH_TABLE Table does not exist
- @retval
- HA_ERR_TABLE_EXIST Table exists
- @retval
- \# Error code
+ Discover all table names in a given database
*/
-struct st_table_exists_in_engine_args
+extern "C" {
+
+static int cmp_file_names(const void *a, const void *b)
{
- const char *db;
- const char *name;
- int err;
-};
+ CHARSET_INFO *cs= character_set_filesystem;
+ char *aa= ((FILEINFO *)a)->name;
+ char *bb= ((FILEINFO *)b)->name;
+ return my_strnncoll(cs, (uchar*)aa, strlen(aa), (uchar*)bb, strlen(bb));
+}
-static my_bool table_exists_in_engine_handlerton(THD *thd, plugin_ref plugin,
- void *arg)
+static int cmp_table_names(LEX_STRING * const *a, LEX_STRING * const *b)
{
- st_table_exists_in_engine_args *vargs= (st_table_exists_in_engine_args *)arg;
- handlerton *hton= plugin_data(plugin, handlerton *);
+ return my_strnncoll(&my_charset_bin, (uchar*)((*a)->str), (*a)->length,
+ (uchar*)((*b)->str), (*b)->length);
+}
- int err= HA_ERR_NO_SUCH_TABLE;
+}
- if (hton->state == SHOW_OPTION_YES && hton->table_exists_in_engine)
- err = hton->table_exists_in_engine(hton, thd, vargs->db, vargs->name);
+Discovered_table_list::Discovered_table_list(THD *thd_arg,
+ Dynamic_array<LEX_STRING*> *tables_arg,
+ const LEX_STRING *wild_arg) :
+ thd(thd_arg), with_temps(false), tables(tables_arg)
+{
+ if (wild_arg->str && wild_arg->str[0])
+ {
+ wild= wild_arg->str;
+ wend= wild + wild_arg->length;
+ }
+ else
+ wild= 0;
+}
- vargs->err = err;
- if (vargs->err == HA_ERR_TABLE_EXIST)
- return TRUE;
+bool Discovered_table_list::add_table(const char *tname, size_t tlen)
+{
+ /*
+ TODO Check with_temps and filter out temp tables.
+ Implement the check, when we'll have at least one affected engine (with
+ custom discover_table_names() method, that calls add_table() directly).
+ Note: avoid comparing the same name twice (here and in add_file).
+ */
+ if (wild && my_wildcmp(table_alias_charset, tname, tname + tlen, wild, wend,
+ wild_prefix, wild_one, wild_many))
+ return 0;
- return FALSE;
+ LEX_STRING *name= thd->make_lex_string(tname, tlen);
+ if (!name || tables->append(name))
+ return 1;
+ return 0;
}
-int ha_table_exists_in_engine(THD* thd, const char* db, const char* name)
+bool Discovered_table_list::add_file(const char *fname)
{
- DBUG_ENTER("ha_table_exists_in_engine");
- DBUG_PRINT("enter", ("db: %s, name: %s", db, name));
- st_table_exists_in_engine_args args= {db, name, HA_ERR_NO_SUCH_TABLE};
- plugin_foreach(thd, table_exists_in_engine_handlerton,
- MYSQL_STORAGE_ENGINE_PLUGIN, &args);
- DBUG_PRINT("exit", ("error: %d", args.err));
- DBUG_RETURN(args.err);
+ bool is_temp= strncmp(fname, STRING_WITH_LEN(tmp_file_prefix)) == 0;
+
+ if (is_temp && !with_temps)
+ return 0;
+
+ char tname[SAFE_NAME_LEN + 1];
+ size_t tlen= filename_to_tablename(fname, tname, sizeof(tname), is_temp);
+ return add_table(tname, tlen);
}
+
+void Discovered_table_list::sort()
+{
+ tables->sort(cmp_table_names);
+}
+
+void Discovered_table_list::remove_duplicates()
+{
+ LEX_STRING **src= tables->front();
+ LEX_STRING **dst= src;
+ while (++dst <= tables->back())
+ {
+ LEX_STRING *s= *src, *d= *dst;
+ DBUG_ASSERT(strncmp(s->str, d->str, MY_MIN(s->length, d->length)) <= 0);
+ if ((s->length != d->length || strncmp(s->str, d->str, d->length)))
+ {
+ src++;
+ if (src != dst)
+ *src= *dst;
+ }
+ }
+ tables->elements(src - tables->front() + 1);
+}
+
+struct st_discover_names_args
+{
+ LEX_STRING *db;
+ MY_DIR *dirp;
+ Discovered_table_list *result;
+ uint possible_duplicates;
+};
+
+static my_bool discover_names(THD *thd, plugin_ref plugin,
+ void *arg)
+{
+ st_discover_names_args *args= (st_discover_names_args *)arg;
+ handlerton *ht= plugin_hton(plugin);
+
+ if (ht->state == SHOW_OPTION_YES && ht->discover_table_names)
+ {
+ uint old_elements= args->result->tables->elements();
+ if (ht->discover_table_names(ht, args->db, args->dirp, args->result))
+ return 1;
+
+ /*
+ hton_ext_based_table_discovery never discovers a table that has
+ a corresponding .frm file; but custom engine discover methods might
+ */
+ if (ht->discover_table_names != hton_ext_based_table_discovery)
+ args->possible_duplicates+= args->result->tables->elements() - old_elements;
+ }
+
+ return 0;
+}
+
+/**
+ Return the list of tables
+
+ @param thd
+ @param db database to look into
+ @param dirp list of files in this database (as returned by my_dir())
+ @param result the object to return the list of files in
+ @param reusable if true, on return, 'dirp' will be a valid list of all
+ non-table files. If false, discovery will work much faster,
+ but it will leave 'dirp' corrupted and completely unusable,
+ only good for my_dirend().
+
+ Normally, reusable=false for SHOW and INFORMATION_SCHEMA, and reusable=true
+ for DROP DATABASE (as it needs to know and delete non-table files).
+*/
+
+int ha_discover_table_names(THD *thd, LEX_STRING *db, MY_DIR *dirp,
+ Discovered_table_list *result, bool reusable)
+{
+ int error;
+ DBUG_ENTER("ha_discover_table_names");
+
+ if (engines_with_discover_table_names == 0 && !reusable)
+ {
+ error= ext_table_discovery_simple(dirp, result);
+ result->sort();
+ }
+ else
+ {
+ st_discover_names_args args= {db, dirp, result, 0};
+
+ /* extension_based_table_discovery relies on dirp being sorted */
+ my_qsort(dirp->dir_entry, dirp->number_of_files,
+ sizeof(FILEINFO), cmp_file_names);
+
+ error= extension_based_table_discovery(dirp, reg_ext, result) ||
+ plugin_foreach(thd, discover_names,
+ MYSQL_STORAGE_ENGINE_PLUGIN, &args);
+ result->sort();
+
+ if (args.possible_duplicates > 0)
+ result->remove_duplicates();
+ }
+
+ DBUG_RETURN(error);
+}
+
+
#ifdef HAVE_NDB_BINLOG
/*
TODO: change this into a dynamic struct
@@ -4479,7 +5295,7 @@ struct binlog_func_st
static my_bool binlog_func_list(THD *thd, plugin_ref plugin, void *arg)
{
hton_list_st *hton_list= (hton_list_st *)arg;
- handlerton *hton= plugin_data(plugin, handlerton *);
+ handlerton *hton= plugin_hton(plugin);
if (hton->state == SHOW_OPTION_YES && hton->binlog_func)
{
uint sz= hton_list->sz;
@@ -4569,7 +5385,7 @@ static my_bool binlog_log_query_handlerton(THD *thd,
plugin_ref plugin,
void *args)
{
- return binlog_log_query_handlerton2(thd, plugin_data(plugin, handlerton *), args);
+ return binlog_log_query_handlerton2(thd, plugin_hton(plugin), args);
}
void ha_binlog_log_query(THD *thd, handlerton *hton,
@@ -4619,14 +5435,7 @@ int handler::read_range_first(const key_range *start_key,
DBUG_ENTER("handler::read_range_first");
eq_range= eq_range_arg;
- end_range= 0;
- if (end_key)
- {
- end_range= &save_end_range;
- save_end_range= *end_key;
- key_compare_result_on_equal= ((end_key->flag == HA_READ_BEFORE_KEY) ? 1 :
- (end_key->flag == HA_READ_AFTER_KEY) ? -1 : 0);
- }
+ set_end_range(end_key);
range_key_part= table->key_info[active_index].key_part;
if (!start_key) // Read first record
@@ -4702,12 +5511,26 @@ int handler::read_range_next()
}
+void handler::set_end_range(const key_range *end_key)
+{
+ end_range= 0;
+ if (end_key)
+ {
+ end_range= &save_end_range;
+ save_end_range= *end_key;
+ key_compare_result_on_equal=
+ ((end_key->flag == HA_READ_BEFORE_KEY) ? 1 :
+ (end_key->flag == HA_READ_AFTER_KEY) ? -1 : 0);
+ }
+}
+
+
/**
Compare if found key (in row) is over max-value.
@param range range to compare to row. May be 0 for no range
- @seealso
+ @see also
key.cc::key_cmp()
@return
@@ -4800,27 +5623,21 @@ static my_bool exts_handlerton(THD *unused, plugin_ref plugin,
void *arg)
{
List<char> *found_exts= (List<char> *) arg;
- handlerton *hton= plugin_data(plugin, handlerton *);
- handler *file;
- if (hton->state == SHOW_OPTION_YES && hton->create &&
- (file= hton->create(hton, (TABLE_SHARE*) 0, current_thd->mem_root)))
- {
- List_iterator_fast<char> it(*found_exts);
- const char **ext, *old_ext;
+ handlerton *hton= plugin_hton(plugin);
+ List_iterator_fast<char> it(*found_exts);
+ const char **ext, *old_ext;
- for (ext= file->bas_ext(); *ext; ext++)
+ for (ext= hton->tablefile_extensions; *ext; ext++)
+ {
+ while ((old_ext= it++))
{
- while ((old_ext= it++))
- {
- if (!strcmp(old_ext, *ext))
- break;
- }
- if (!old_ext)
- found_exts->push_back((char *) *ext);
-
- it.rewind();
+ if (!strcmp(old_ext, *ext))
+ break;
}
- delete file;
+ if (!old_ext)
+ found_exts->push_back((char *) *ext);
+
+ it.rewind();
}
return FALSE;
}
@@ -4875,7 +5692,7 @@ static my_bool showstat_handlerton(THD *thd, plugin_ref plugin,
void *arg)
{
enum ha_stat_type stat= *(enum ha_stat_type *) arg;
- handlerton *hton= plugin_data(plugin, handlerton *);
+ handlerton *hton= plugin_hton(plugin);
if (hton->state == SHOW_OPTION_YES && hton->show_status &&
hton->show_status(hton, thd, stat_print, stat))
return TRUE;
@@ -4923,7 +5740,7 @@ bool ha_show_status(THD *thd, handlerton *db_type, enum ha_stat_type stat)
if (!result && !thd->is_error())
my_eof(thd);
else if (!thd->is_error())
- my_error(ER_GET_ERRNO, MYF(0), errno);
+ my_error(ER_GET_ERRNO, MYF(0), errno, hton_name(db_type)->str);
return result;
}
@@ -4945,6 +5762,7 @@ static bool check_table_binlog_row_based(THD *thd, TABLE *table)
if (table->s->cached_row_logging_check == -1)
{
int const check(table->s->tmp_table == NO_TMP_TABLE &&
+ ! table->no_replicate &&
binlog_filter->db_ok(table->s->db.str));
table->s->cached_row_logging_check= check;
}
@@ -5052,8 +5870,6 @@ static int binlog_log_row(TABLE* table,
const uchar *after_record,
Log_func *log_func)
{
- if (table->no_replicate)
- return 0;
bool error= 0;
THD *const thd= table->in_use;
@@ -5070,7 +5886,7 @@ static int binlog_log_row(TABLE* table,
the first row handled in this statement. In that case, we need
to write table maps for all locked tables to the binary log.
*/
- if (likely(!(error= bitmap_init(&cols,
+ if (likely(!(error= my_bitmap_init(&cols,
use_bitbuf ? bitbuf : NULL,
(n_fields + 7) & ~7UL,
FALSE))))
@@ -5092,7 +5908,7 @@ static int binlog_log_row(TABLE* table,
before_record, after_record);
}
if (!use_bitbuf)
- bitmap_free(&cols);
+ my_bitmap_free(&cols);
}
}
return error ? HA_ERR_RBR_LOGGING_FAILED : 0;
@@ -5100,6 +5916,7 @@ static int binlog_log_row(TABLE* table,
int handler::ha_external_lock(THD *thd, int lock_type)
{
+ int error;
DBUG_ENTER("handler::ha_external_lock");
/*
Whether this is lock or unlock, this should be true, and is to verify that
@@ -5107,6 +5924,12 @@ int handler::ha_external_lock(THD *thd, int lock_type)
taken a table lock), ha_release_auto_increment() was too.
*/
DBUG_ASSERT(next_insert_id == 0);
+ /* Consecutive calls for lock without unlocking in between is not allowed */
+ DBUG_ASSERT(table_share->tmp_table != NO_TMP_TABLE ||
+ ((lock_type != F_UNLCK && m_lock_type == F_UNLCK) ||
+ lock_type == F_UNLCK));
+ /* SQL HANDLER call locks/unlock while scanning (RND/INDEX). */
+ DBUG_ASSERT(inited == NONE || table->open_by_handler);
if (MYSQL_HANDLER_RDLOCK_START_ENABLED() ||
MYSQL_HANDLER_WRLOCK_START_ENABLED() ||
@@ -5129,14 +5952,20 @@ int handler::ha_external_lock(THD *thd, int lock_type)
}
}
+ ha_statistic_increment(&SSV::ha_external_lock_count);
+
/*
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);
+ MYSQL_TABLE_LOCK_WAIT(m_psi, PSI_TABLE_EXTERNAL_LOCK, lock_type,
+ { error= external_lock(thd, lock_type); })
+
+ DBUG_EXECUTE_IF("external_lock_failure", error= HA_ERR_GENERIC;);
- if (error == 0)
+ if (error == 0 || lock_type == F_UNLCK)
{
+ m_lock_type= lock_type;
cached_table_flags= table_flags();
if (table_share->tmp_table == NO_TMP_TABLE)
mysql_audit_external_lock(thd, table_share, lock_type);
@@ -5193,16 +6022,18 @@ int handler::ha_write_row(uchar *buf)
{
int error;
Log_func *log_func= Write_rows_log_event::binlog_row_logging_function;
+ DBUG_ASSERT(table_share->tmp_table != NO_TMP_TABLE ||
+ m_lock_type == F_WRLCK);
DBUG_ENTER("handler::ha_write_row");
DEBUG_SYNC_C("ha_write_row_start");
- DBUG_EXECUTE_IF("inject_error_ha_write_row",
- DBUG_RETURN(HA_ERR_INTERNAL_ERROR); );
MYSQL_INSERT_ROW_START(table_share->db.str, table_share->table_name.str);
mark_trx_read_write();
increment_statistics(&SSV::ha_write_count);
- error= write_row(buf);
+ MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0,
+ { error= write_row(buf); })
+
MYSQL_INSERT_ROW_DONE(error);
if (unlikely(error))
DBUG_RETURN(error);
@@ -5219,6 +6050,8 @@ int handler::ha_update_row(const uchar *old_data, uchar *new_data)
{
int error;
Log_func *log_func= Update_rows_log_event::binlog_row_logging_function;
+ DBUG_ASSERT(table_share->tmp_table != NO_TMP_TABLE ||
+ m_lock_type == F_WRLCK);
/*
Some storage engines require that the new record is in record[0]
@@ -5231,7 +6064,9 @@ int handler::ha_update_row(const uchar *old_data, uchar *new_data)
mark_trx_read_write();
increment_statistics(&SSV::ha_update_count);
- error= update_row(old_data, new_data);
+ MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_UPDATE_ROW, active_index, 0,
+ { error= update_row(old_data, new_data);})
+
MYSQL_UPDATE_ROW_DONE(error);
if (unlikely(error))
return error;
@@ -5245,19 +6080,20 @@ int handler::ha_delete_row(const uchar *buf)
{
int error;
Log_func *log_func= Delete_rows_log_event::binlog_row_logging_function;
+ DBUG_ASSERT(table_share->tmp_table != NO_TMP_TABLE ||
+ m_lock_type == F_WRLCK);
/*
Normally table->record[0] is used, but sometimes table->record[1] is used.
*/
DBUG_ASSERT(buf == table->record[0] ||
buf == table->record[1]);
- DBUG_EXECUTE_IF("inject_error_ha_delete_row",
- return HA_ERR_INTERNAL_ERROR; );
MYSQL_DELETE_ROW_START(table_share->db.str, table_share->table_name.str);
mark_trx_read_write();
increment_statistics(&SSV::ha_delete_count);
- error= delete_row(buf);
+ MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_DELETE_ROW, active_index, 0,
+ { error= delete_row(buf);})
MYSQL_DELETE_ROW_DONE(error);
if (unlikely(error))
return error;
@@ -5277,10 +6113,81 @@ int handler::ha_delete_row(const uchar *buf)
void handler::use_hidden_primary_key()
{
/* fallback to use all columns in the table to identify row */
- table->use_all_columns();
+ table->column_bitmaps_set(&table->s->all_set, table->write_set);
}
+/**
+ Get an initialized ha_share.
+
+ @return Initialized ha_share
+ @retval NULL ha_share is not yet initialized.
+ @retval != NULL previous initialized ha_share.
+
+ @note
+ If not a temp table, then LOCK_ha_data must be held.
+*/
+
+Handler_share *handler::get_ha_share_ptr()
+{
+ DBUG_ENTER("handler::get_ha_share_ptr");
+ DBUG_ASSERT(ha_share && table_share);
+
+#ifndef DBUG_OFF
+ if (table_share->tmp_table == NO_TMP_TABLE)
+ mysql_mutex_assert_owner(&table_share->LOCK_ha_data);
+#endif
+
+ DBUG_RETURN(*ha_share);
+}
+
+
+/**
+ Set ha_share to be used by all instances of the same table/partition.
+
+ @param ha_share Handler_share to be shared.
+
+ @note
+ If not a temp table, then LOCK_ha_data must be held.
+*/
+
+void handler::set_ha_share_ptr(Handler_share *arg_ha_share)
+{
+ DBUG_ENTER("handler::set_ha_share_ptr");
+ DBUG_ASSERT(ha_share);
+#ifndef DBUG_OFF
+ if (table_share->tmp_table == NO_TMP_TABLE)
+ mysql_mutex_assert_owner(&table_share->LOCK_ha_data);
+#endif
+
+ *ha_share= arg_ha_share;
+ DBUG_VOID_RETURN;
+}
+
+
+/**
+ Take a lock for protecting shared handler data.
+*/
+
+void handler::lock_shared_ha_data()
+{
+ DBUG_ASSERT(table_share);
+ if (table_share->tmp_table == NO_TMP_TABLE)
+ mysql_mutex_lock(&table_share->LOCK_ha_data);
+}
+
+
+/**
+ Release lock for protecting ha_share.
+*/
+
+void handler::unlock_shared_ha_data()
+{
+ DBUG_ASSERT(table_share);
+ if (table_share->tmp_table == NO_TMP_TABLE)
+ mysql_mutex_unlock(&table_share->LOCK_ha_data);
+}
+
/** @brief
Dummy function which accept information about log files which is not need
by handlers
@@ -5405,7 +6312,7 @@ fl_log_iterator_buffer_init(struct handler_iterator *iterator)
/* to be able to make my_free without crash in case of error */
iterator->buffer= 0;
- if (!(dirp = my_dir(fl_dir, MYF(0))))
+ if (!(dirp = my_dir(fl_dir, MYF(MY_THREAD_SPECIFIC))))
{
return HA_ITERATOR_ERROR;
}
@@ -5414,7 +6321,7 @@ fl_log_iterator_buffer_init(struct handler_iterator *iterator)
sizeof(enum log_status) +
+ FN_REFLEN + 1) *
(uint) dirp->number_off_files),
- MYF(0))) == 0)
+ MYF(MY_THREAD_SPECIFIC))) == 0)
{
return HA_ITERATOR_ERROR;
}
@@ -5448,6 +6355,7 @@ fl_log_iterator_buffer_init(struct handler_iterator *iterator)
iterator->buffer= buff;
iterator->next= &fl_log_iterator_next;
iterator->destroy= &fl_log_iterator_destroy;
+ my_dirend(dirp);
return HA_ITERATOR_OK;
}
@@ -5465,3 +6373,22 @@ fl_create_iterator(enum handler_iterator_type type,
}
}
#endif /*TRANS_LOG_MGM_EXAMPLE_CODE*/
+
+
+bool HA_CREATE_INFO::check_conflicting_charset_declarations(CHARSET_INFO *cs)
+{
+ if ((used_fields & HA_CREATE_USED_DEFAULT_CHARSET) &&
+ /* DEFAULT vs explicit, or explicit vs DEFAULT */
+ (((default_table_charset == NULL) != (cs == NULL)) ||
+ /* Two different explicit character sets */
+ (default_table_charset && cs &&
+ !my_charset_same(default_table_charset, cs))))
+ {
+ my_error(ER_CONFLICTING_DECLARATIONS, MYF(0),
+ "CHARACTER SET ", default_table_charset ?
+ default_table_charset->csname : "DEFAULT",
+ "CHARACTER SET ", cs ? cs->csname : "DEFAULT");
+ return true;
+ }
+ return false;
+}