Commit 6a839ff4 authored by Sergei Golubchik's avatar Sergei Golubchik

handlerton::discover_table_existence() method

parent 93411518
......@@ -383,6 +383,11 @@ static int ha_finish_errors(void)
return 0;
}
volatile int32 need_full_discover_for_existence= 0;
static int full_discover_for_existence(handlerton *, const char *, const char *)
{ return 1; }
static int ext_based_existence(handlerton *, const char *, const char *)
{ return 1; }
int ha_finalize_handlerton(st_plugin_int *plugin)
{
......@@ -433,6 +438,9 @@ int ha_finalize_handlerton(st_plugin_int *plugin)
hton2plugin[hton->slot]= NULL;
}
if (hton->discover_table_existence == full_discover_for_existence)
my_atomic_add32(&need_full_discover_for_existence, -1);
if (hton->discover_table_names)
my_atomic_add32(&engines_with_discover_table_names, -1);
......@@ -486,6 +494,18 @@ int ha_initialize_handlerton(st_plugin_int *plugin)
hton->discover && hton->tablefile_extensions[0])
hton->discover_table_names= hton_ext_based_table_discovery;
// default discover_table_existence implementation
if (!hton->discover_table_existence && hton->discover)
{
if (hton->tablefile_extensions[0])
hton->discover_table_existence= ext_based_existence;
else
{
hton->discover_table_existence= full_discover_for_existence;
my_atomic_add32(&need_full_discover_for_existence, 1);
}
}
/*
the switch below and hton->state should be removed when
command-line options for plugins will be implemented
......@@ -4377,6 +4397,83 @@ int ha_discover(THD *thd, const char *db, const char *name,
DBUG_RETURN(error);
}
/**
Check if a given table exists, without doing a full discover, if possible
*/
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);
}
struct st_discover_existence_args
{
char *path;
size_t path_len;
const char *db, *table_name;
};
static my_bool discover_existence(THD *thd, plugin_ref plugin,
void *arg)
{
st_discover_existence_args *args= (st_discover_existence_args*)arg;
handlerton *ht= plugin_data(plugin, handlerton *);
if (ht->state != SHOW_OPTION_YES || !ht->discover_table_existence)
return FALSE;
if (ht->discover_table_existence == ext_based_existence)
return file_ext_exists(args->path, args->path_len,
ht->tablefile_extensions[0]);
return ht->discover_table_existence(ht, args->db, args->table_name);
}
bool ha_table_exists(THD *thd, const char *db, const char *table_name)
{
DBUG_ENTER("ha_discover_table_existence");
if (need_full_discover_for_existence)
{
enum open_frm_error err;
TABLE_LIST table;
DBUG_ASSERT(0);
TABLE_SHARE *share= get_table_share(thd, db, table_name,
FRM_READ_TABLE_ONLY, &err);
if (share)
{
mysql_mutex_lock(&LOCK_open);
release_table_share(share);
mysql_mutex_unlock(&LOCK_open);
DBUG_RETURN(TRUE);
}
DBUG_RETURN(FALSE);
}
mysql_mutex_lock(&LOCK_open);
TABLE_SHARE *share= get_cached_table_share(db, table_name);
mysql_mutex_unlock(&LOCK_open);
if (share)
DBUG_RETURN(TRUE);
char path[FN_REFLEN + 1];
size_t path_len = build_table_filename(path, sizeof(path) - 1,
db, table_name, "", 0);
if (file_ext_exists(path, path_len, reg_ext))
DBUG_RETURN(TRUE);
st_discover_existence_args args= {path, path_len, db, table_name};
if (plugin_foreach(thd, discover_existence, MYSQL_STORAGE_ENGINE_PLUGIN,
&args))
DBUG_RETURN(TRUE);
DBUG_RETURN(FALSE);
}
/**
Discover all table names in a given database
......
......@@ -1136,6 +1136,27 @@ struct handlerton
*/
int (*discover_table_names)(handlerton *hton, LEX_STRING *db, MY_DIR *dir,
discovered_list *result);
/*
This is a method that allows to server to check if a table exists without
an overhead of the complete discovery.
By default (if not implemented by the engine, but the discovery_table() is
implemented) it will try to perform a file-based discovery:
- if tablefile_extensions[0] is not null this will look for a file name
with the tablefile_extensions[0] extension.
- if tablefile_extensions[0] is null, this will resort to discover_table().
Note that resorting to discover_table() is slow and the engine
should probably implement its own discover_table_existence() method,
if its tablefile_extensions[0] is null.
Returns 1 if the table exists and 0 if it does not.
*/
int (*discover_table_existence)(handlerton *hton, const char *db,
const char *table_name);
};
......@@ -3072,6 +3093,8 @@ int ha_discover(THD* thd, const char* dbname, const char* name,
uchar** frmblob, size_t* frmlen);
int ha_discover_table_names(THD *thd, LEX_STRING *db, MY_DIR *dirp,
handlerton::discovered_list *result);
bool ha_table_exists(THD *thd, const char *db, const char *table_name);
#ifdef MYSQL_SERVER
extern volatile int32 engines_with_discover_table_names;
#endif
......
......@@ -771,7 +771,7 @@ void release_table_share(TABLE_SHARE *share)
# TABLE_SHARE for table
*/
static TABLE_SHARE *get_cached_table_share(const char *db, const char *table_name)
TABLE_SHARE *get_cached_table_share(const char *db, const char *table_name)
{
char key[SAFE_NAME_LEN*2+2];
uint key_length;
......@@ -2337,58 +2337,6 @@ void drop_open_table(THD *thd, TABLE *table, const char *db_name,
}
/**
Check that table exists in table definition cache, on disk
or in some storage engine.
@param thd Thread context
@param db database name
@param table_name table name
@param path (optional) path to the frm file
@note This function acquires LOCK_open internally.
@note If there is no .FRM file for the table but it exists in one
of engines (e.g. it was created on another node of NDB cluster)
this function will fetch and create proper .FRM file for it.
@retval TRUE Some error occurred
@retval FALSE No error. 'exists' out parameter set accordingly.
*/
bool table_exists(THD *thd, const char *db, const char *table_name,
const char *path)
{
char path_buf[FN_REFLEN + 1];
TABLE_SHARE *share;
DBUG_ENTER("table_exists");
mysql_mutex_lock(&LOCK_open);
share= get_cached_table_share(db, table_name);
mysql_mutex_unlock(&LOCK_open);
if (share)
DBUG_RETURN(TRUE);
if (!path)
{
build_table_filename(path_buf, sizeof(path_buf) - 1,
db, table_name, reg_ext, 0);
path= path_buf;
}
if (!access(path, F_OK))
DBUG_RETURN(TRUE);
/* .FRM file doesn't exist. Try to discover it */
uchar *frmblob= NULL;
size_t frmlen;
bool exists= ! ha_discover(thd, db, table_name, &frmblob, &frmlen);
my_free(frmblob);
DBUG_RETURN(exists);
}
/**
An error handler which converts, if possible, ER_LOCK_DEADLOCK error
that can occur when we are trying to acquire a metadata lock to
......@@ -2926,7 +2874,7 @@ bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
if (table_list->open_strategy == TABLE_LIST::OPEN_IF_EXISTS)
{
if (!table_exists(thd, table_list))
if (!ha_table_exists(thd, table_list->db, table_list->table_name))
DBUG_RETURN(FALSE);
/* Table exists. Let us try to open it. */
......@@ -4705,7 +4653,7 @@ lock_table_names(THD *thd,
We come here in the case of lock timeout when executing CREATE TABLE.
Verify that table does exist (it usually does, as we got a lock conflict)
*/
if (table_exists(thd, tables_start))
if (ha_table_exists(thd, tables_start->db, tables_start->table_name))
{
if (thd->lex->create_info.options & HA_LEX_CREATE_IF_NOT_EXISTS)
{
......
......@@ -113,7 +113,7 @@ TABLE_SHARE *get_table_share(THD *thd, const char *db, const char *table_name,
enum open_frm_error *error,
my_hash_value_type hash_value);
void release_table_share(TABLE_SHARE *share);
TABLE_SHARE *get_cached_table_share(const char *db, const char *table_name);
// convenience helper: call get_table_share() without precomputed hash_value
static inline TABLE_SHARE *get_table_share(THD *thd, const char *db,
......@@ -369,13 +369,6 @@ TABLE *find_table_for_mdl_upgrade(THD *thd, const char *db,
bool no_error);
void mark_tmp_table_for_reuse(TABLE *table);
bool table_exists(THD *thd, const char *db, const char *table_name,
const char *path);
static inline bool table_exists(THD *thd, TABLE_LIST *table)
{
return table_exists(thd, table->db, table->table_name, NULL);
}
int update_virtual_fields(THD *thd, TABLE *table,
enum enum_vcol_update_mode vcol_update_mode= VCOL_UPDATE_FOR_READ);
int dynamic_column_error_message(enum_dyncol_func_result rc);
......
......@@ -937,7 +937,7 @@ update_binlog:
char quoted_name[FN_REFLEN+3];
// Only write drop table to the binlog for tables that no longer exist.
if (table_exists(thd, tbl))
if (ha_table_exists(thd, tbl->db, tbl->table_name))
continue;
my_snprintf(quoted_name, sizeof(quoted_name), "%`s", tbl->table_name);
......
......@@ -2279,8 +2279,9 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists,
}
DEBUG_SYNC(thd, "rm_table_no_locks_before_delete_table");
error= 0;
if (drop_temporary || !table_exists(thd, db, alias, path) ||
(!drop_view && dd_frm_type(thd, path, &frm_db_type) != FRMTYPE_TABLE))
if (!table->internal_tmp_table &&
(drop_temporary || !ha_table_exists(thd, db, alias) ||
(!drop_view && dd_frm_type(thd, path, &frm_db_type) != FRMTYPE_TABLE)))
{
/*
One of the following cases happened:
......@@ -2288,6 +2289,10 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists,
. "DROP" but table was not found on disk and table can't be
created from engine.
. ./sql/datadict.cc +32 /Alfranio - TODO: We need to test this.
Table->internal_tmp_table is set when one of the #sql-xxx files
was left in the datadir after a crash during ALTER TABLE.
See Bug#30152.
*/
if (if_exists)
push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
......@@ -4317,7 +4322,7 @@ bool mysql_create_table_no_lock(THD *thd,
if (!internal_tmp_table && !(create_info->options & HA_LEX_CREATE_TMP_TABLE))
{
if (table_exists(thd, db, table_name, path))
if (ha_table_exists(thd, db, table_name))
{
if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS)
goto warn;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment