WL1424 Multiple MySQL Servers: SHOW TABLES etc. should detect new and delete old tables.

parent e6e83d29
......@@ -241,6 +241,7 @@ enum ha_base_keytype {
#define HA_CREATE_TMP_TABLE 4
#define HA_CREATE_CHECKSUM 8
#define HA_CREATE_DELAY_KEY_WRITE 64
#define HA_CREATE_FROM_ENGINE 128
/* Bits in flag to _status */
......@@ -287,7 +288,7 @@ enum ha_base_keytype {
#define HA_ERR_ROW_IS_REFERENCED 152 /* Cannot delete a parent row */
#define HA_ERR_NO_SAVEPOINT 153 /* No savepoint with that name */
#define HA_ERR_NON_UNIQUE_BLOCK_SIZE 154 /* Non unique key block size */
#define HA_ERR_OLD_METADATA 155 /* The frm file on disk is old */
#define HA_ERR_NO_SUCH_TABLE 155 /* The table does not exist in engine */
#define HA_ERR_TABLE_EXIST 156 /* The table existed in storage engine */
#define HA_ERR_NO_CONNECTION 157 /* Could not connect to storage engine */
......
drop table if exists t1,t2,t3,t4,t5,t6,t9;
drop table if exists t1,t2,t3,t4,t5,t6,t7,t9;
flush status;
create table t1(
id int not null primary key,
......@@ -94,8 +94,6 @@ ERROR 42S01: Table 't3' already exists
show status like 'handler_discover%';
Variable_name Value
Handler_discover 1
SHOW TABLES FROM test;
Tables_in_test
create table IF NOT EXISTS t3(
id int not null primary key,
id2 int not null,
......@@ -119,6 +117,40 @@ Variable_name Value
Handler_discover 2
drop table t3;
flush status;
create table t7(
id int not null primary key,
name char(255)
) engine=ndb;
create table t6(
id int not null primary key,
name char(255)
) engine=MyISAM;
insert into t7 values (1, "Explorer");
insert into t6 values (2, "MyISAM table");
select * from t7;
id name
1 Explorer
show status like 'handler_discover%';
Variable_name Value
Handler_discover 0
flush tables;
show tables from test;
Tables_in_test
t6
t7
show status like 'handler_discover%';
Variable_name Value
Handler_discover 1
flush tables;
show table status;
Name Engine Version Row_format Rows Avg_row_length Data_length Max_data_length Index_length Data_free Auto_increment Create_time Update_time Check_time Collation Checksum Create_options Comment
t6 MyISAM 9 Fixed 1 260 # # # 0 NULL # # NULL # NULL
t7 ndbcluster 9 Fixed 100 0 # # # 0 NULL # # NULL # NULL
show status like 'handler_discover%';
Variable_name Value
Handler_discover 2
drop table t6, t7;
flush status;
show status like 'handler_discover%';
Variable_name Value
Handler_discover 0
......
-- source include/have_ndb.inc
--disable_warnings
drop table if exists t1,t2,t3,t4,t5,t6,t9;
drop table if exists t1,t2,t3,t4,t5,t6,t7,t9;
--enable_warnings
################################################
......@@ -122,7 +122,6 @@ create table t3(
# IF NOT EXISTS wasn't specified
show status like 'handler_discover%';
SHOW TABLES FROM test;
# now it should be discovered
create table IF NOT EXISTS t3(
......@@ -145,38 +144,76 @@ show status like 'handler_discover%';
drop table t3;
#######################################################
# Test that a table that already exists as frm file
# but not in NDB can be deleted from disk.
##################################################
# Test that a table that already exists in NDB
# is discovered when SHOW TABLES
# is used
#
# Manual test
#flush status;
#
#create table t4(
# id int not null primary key,
# name char(27)
#) engine=ndb;
#insert into t4 values (1, "Automatic");
#select * from t4;
flush status;
create table t7(
id int not null primary key,
name char(255)
) engine=ndb;
create table t6(
id int not null primary key,
name char(255)
) engine=MyISAM;
insert into t7 values (1, "Explorer");
insert into t6 values (2, "MyISAM table");
select * from t7;
show status like 'handler_discover%';
# Remove the frm file from disk
flush tables;
system rm var/master-data/test/t7.frm ;
show tables from test;
show status like 'handler_discover%';
# Remove the frm file from disk again
flush tables;
system rm var/master-data/test/t7.frm ;
--replace_column 7 # 8 # 9 # 12 # 13 # 15 #
show table status;
show status like 'handler_discover%';
drop table t6, t7;
#######################################################
# Test that a table that has been dropped from NDB
# but still exists on disk, get a consistent error message
# saying "No such table existed"
#
flush status;
create table t4(
id int not null primary key,
name char(27)
) engine=ndb;
insert into t4 values (1, "Automatic");
select * from t4;
# Remove the table from NDB
#system drop_tab -c "$NDB_CONNECTSTRING2" -d test t4 > /dev/null ;
#system drop_tab -c "host=localhost:2200;nodeid=5" -d test t4 > /dev/null ;
#
#--error 1296
#select * from t4;
#
#flush table t4;
#--error 1016
#select * from t4;
#
#show status like 'handler_discover%';
#drop table t4;
#flush tables;
#show tables;
#--error 1146
#select * from t4;
system exec $NDB_TOOLS_DIR/ndb_drop_table -d test t4;
system exec ../ndb/tools/ndb_show_tables > show_tables.log;
# Test that correct error is returned
--error 1146
select * from t4;
--error 1146
select * from t4;
show status like 'handler_discover%';
drop table t4;
show tables;
#########################################################
......@@ -241,7 +278,6 @@ show status like 'handler_discover%';
drop table t6;
######################################################
# Simple test to show use of discover on startup
# Note! This should always be the last step in this
# file, the table t9 will be used and dropped
# by ndb_autodiscover2
......@@ -259,8 +295,7 @@ system rm var/master-data/test/t9.frm ;
# Now leave test case, when ndb_autodiscover2 will run, this
# MySQL Server will have been restarted because it has a
# ndb_autodiscover2-master.opt file. And thus the table should
# have been discovered by the "discover on startup" function.
# ndb_autodiscover2-master.opt file.
#TODO
#SLECT * FROM t1, t2, t4;
......
-- source include/have_ndb.inc
#
# Simple test to show use of discover on startup
# Simple test to show use of discover when the server has been restarted
# The previous step has simply removed the frm file
# from disk, but left the table in NDB
#
--sleep 3;
select * from t9 order by a;
# handler_discover should be zero
# handler_discover should be 1
show status like 'handler_discover%';
drop table t9;
......@@ -125,47 +125,4 @@ int writefrm(const char *name, const void *frmdata, uint len)
/*
Try to discover table from handler and
if found, write the frm file to disk.
RETURN VALUES:
0 : Table existed in handler and created
on disk if so requested
1 : Table does not exist
>1 : error
*/
int create_table_from_handler(const char *db,
const char *name,
bool create_if_found)
{
int error= 0;
const void* frmblob = NULL;
char path[FN_REFLEN];
uint frmlen = 0;
DBUG_ENTER("create_table_from_handler");
DBUG_PRINT("enter", ("create_if_found: %d", create_if_found));
if (ha_discover(db, name, &frmblob, &frmlen))
DBUG_RETURN(1); // Table does not exist
// Table exists in handler
if (create_if_found)
{
(void)strxnmov(path,FN_REFLEN,mysql_data_home,"/",db,"/",name,NullS);
// Save the frm file
error = writefrm(path, frmblob, frmlen);
}
if (frmblob)
my_free((char*) frmblob,MYF(0));
DBUG_RETURN(error);
}
int table_exists_in_handler(const char *db,
const char *name)
{
return (create_table_from_handler(db, name, false) == 0);
}
This diff is collapsed.
......@@ -208,6 +208,8 @@ class ha_ndbcluster: public handler
int ndb_err(NdbConnection*);
bool uses_blob_value(bool all_fields);
int write_ndb_file();
private:
int check_ndb_connection();
......@@ -248,8 +250,11 @@ int ndbcluster_rollback(THD *thd, void* ndb_transaction);
void ndbcluster_close_connection(THD *thd);
int ndbcluster_discover(const char* dbname, const char* name,
int ndbcluster_discover(THD* thd, const char* dbname, const char* name,
const void** frmblob, uint* frmlen);
int ndbcluster_can_discover(THD *thd, const char *name);
int ndbcluster_list_tables(THD* thd, HASH* tables, const char* db);
int ndbcluster_table_exists(THD* thd, const char *db, const char *name);
int ndbcluster_drop_database(const char* path);
void ndbcluster_print_error(int error, const NdbOperation *error_op);
......@@ -1119,6 +1119,16 @@ void handler::print_error(int error, myf errflag)
case HA_ERR_NO_REFERENCED_ROW:
textno=ER_NO_REFERENCED_ROW;
break;
case HA_ERR_NO_SUCH_TABLE:
{
char *db;
char buff[FN_REFLEN];
uint length=dirname_part(buff,table->path);
buff[length-1]=0;
db=buff+dirname_length(buff);
my_error(ER_NO_SUCH_TABLE,MYF(0),db,table->table_name);
break;
}
default:
{
/* The error was "unknown" to this function.
......@@ -1265,6 +1275,71 @@ int ha_create_table(const char *name, HA_CREATE_INFO *create_info,
DBUG_RETURN(error != 0);
}
/*
Try to discover table from engine and
if found, write the frm file to disk.
RETURN VALUES:
0 : Table existed in engine and created
on disk if so requested
1 : Table does not exist
>1 : error
*/
int ha_create_table_from_engine(THD* thd,
const char *db,
const char *name,
bool create_if_found)
{
int error= 0;
const void* frmblob = NULL;
uint frmlen = 0;
char path[FN_REFLEN];
HA_CREATE_INFO create_info;
TABLE table;
DBUG_ENTER("ha_create_table_from_engine");
DBUG_PRINT("enter", ("db: %s, name: %s", db, name));
DBUG_PRINT("enter", ("create_if_found: %d", create_if_found));
bzero((char*) &create_info,sizeof(create_info));
if ((error= ha_discover(thd, db, name, &frmblob, &frmlen)))
DBUG_RETURN(error);
// Table exists in handler
if (create_if_found)
{
(void)strxnmov(path,FN_REFLEN,mysql_data_home,"/",db,"/",name,NullS);
// Save the frm file
if ((error = writefrm(path, frmblob, frmlen)))
goto err_end;
if (openfrm(path,"",0,(uint) READ_ALL, 0, &table))
DBUG_RETURN(1);
update_create_info_from_table(&create_info, &table);
create_info.table_options|= HA_CREATE_FROM_ENGINE;
if (lower_case_table_names == 2 &&
!(table.file->table_flags() & HA_FILE_BASED))
{
/* Ensure that handler gets name in lower case */
strmov(path, name);
my_casedn_str(files_charset_info, path);
name= path;
}
error=table.file->create(path,&table,&create_info);
VOID(closefrm(&table));
}
err_end:
if (frmblob)
my_free((char*) frmblob,MYF(0));
DBUG_RETURN(error);
}
static int NEAR_F delete_file(const char *name,const char *ext,int extflag)
{
char buff[FN_REFLEN];
......@@ -1372,15 +1447,15 @@ int ha_change_key_cache(KEY_CACHE *old_key_cache,
Try to discover one table from handler(s)
*/
int ha_discover(const char* dbname, const char* name,
int ha_discover(THD* thd, const char* db, const char* name,
const void** frmblob, uint* frmlen)
{
int error= 1; // Table does not exist in any handler
DBUG_ENTER("ha_discover");
DBUG_PRINT("enter", ("db: %s, name: %s", dbname, name));
DBUG_PRINT("enter", ("db: %s, name: %s", db, name));
#ifdef HAVE_NDBCLUSTER_DB
if (have_ndbcluster == SHOW_OPTION_YES)
error= ndbcluster_discover(dbname, name, frmblob, frmlen);
error= ndbcluster_discover(thd, db, name, frmblob, frmlen);
#endif
if (!error)
statistic_increment(ha_discover_count,&LOCK_status);
......@@ -1388,6 +1463,65 @@ int ha_discover(const char* dbname, const char* name,
}
/*
Ask handler if it would support discover of a file
with this name
RETURN
0 Does not recognise file
1 File can be discovered
*/
int ha_can_discover(THD* thd, const char* name)
{
int error= 0; // Can't discover this file name
DBUG_ENTER("ha_can_discover");
DBUG_PRINT("enter", ("name: %s", name));
#ifdef HAVE_NDBCLUSTER_DB
if (have_ndbcluster == SHOW_OPTION_YES)
error= ndbcluster_can_discover(thd, name);
#endif
DBUG_RETURN(error);
}
/*
Get a list of tables that exists in handler(s)
*/
int ha_list_tables(THD* thd, HASH *tables, const char* db)
{
int error= 0;
DBUG_ENTER("ha_list_tables");
DBUG_PRINT("enter", ("db: %s", db));
#ifdef HAVE_NDBCLUSTER_DB
if (have_ndbcluster == SHOW_OPTION_YES)
error= ndbcluster_list_tables(thd, tables, db);
#endif
DBUG_RETURN(error);
}
/*
Ask handler if the table exists in engine
RETURN
0 Table does not exist
1 Table exists
# Error code
*/
int ha_table_exists(THD* thd, const char* db, const char* name)
{
int error= 2;
DBUG_ENTER("ha_table_exists");
DBUG_PRINT("enter", ("db: %s, name: %s", db, name));
#ifdef HAVE_NDBCLUSTER_DB
if (have_ndbcluster == SHOW_OPTION_YES)
error= ndbcluster_table_exists(thd, db, name);
#endif
DBUG_RETURN(error);
}
/*
Read first row between two ranges.
Store ranges for future calls to read_range_next
......
......@@ -532,6 +532,8 @@ void ha_close_connection(THD* thd);
enum db_type ha_checktype(enum db_type database_type);
int ha_create_table(const char *name, HA_CREATE_INFO *create_info,
bool update_create_info);
int ha_create_table_from_engine(THD* thd, const char *db, const char *name,
bool create_if_found);
int ha_delete_table(enum db_type db_type, const char *path);
void ha_drop_database(char* path);
int ha_init_key_cache(const char *name, KEY_CACHE *key_cache);
......@@ -553,5 +555,9 @@ bool ha_flush_logs(void);
int ha_enable_transaction(THD *thd, bool on);
int ha_change_key_cache(KEY_CACHE *old_key_cache,
KEY_CACHE *new_key_cache);
int ha_discover(const char* dbname, const char* name,
int ha_discover(THD* thd, const char* dbname, const char* name,
const void** frmblob, uint* frmlen);
int ha_list_tables(THD* thd, HASH *tables, const char* db);
int ha_table_exists(THD* thd, const char* db, const char* name);
int ha_can_discover(THD* thd, const char* name);
......@@ -1000,8 +1000,6 @@ int openfrm(const char *name,const char *alias,uint filestat,uint prgflag,
uint ha_open_flags, TABLE *outparam);
int readfrm(const char *name, const void** data, uint* length);
int writefrm(const char* name, const void* data, uint len);
int create_table_from_handler(const char *db, const char *name,
bool create_if_found);
int closefrm(TABLE *table);
db_type get_table_type(const char *name);
int read_string(File file, gptr *to, uint length);
......
......@@ -1343,7 +1343,7 @@ static int open_unireg_entry(THD *thd, TABLE *entry, const char *db,
*/
if (discover_retry_count++ != 0)
goto err;
if (create_table_from_handler(db, name, true) != 0)
if (ha_create_table_from_engine(thd, db, name, true) != 0)
goto err;
thd->clear_error(); // Clear error message
......
......@@ -358,30 +358,59 @@ int mysqld_show_column_types(THD *thd)
}
/*
Ask all engines if they can provide a list of available tables.
Returns a list of available tables.
*/
int
mysql_discover_tables(THD *thd, HASH *ha_tables, const char *db, bool dir)
{
DBUG_ENTER("mysql_discover_files");
if (dir)
DBUG_RETURN(0); // Discover of directories(databases) not supported yet
// Get list of files in storage engine
if (ha_list_tables(thd, ha_tables, db))
DBUG_RETURN(-1);
DBUG_PRINT("info",("discovered: %d files", ha_tables->records));
DBUG_RETURN(0);
}
/*
List all files or directories in a given location
Returns
files - list of files where wild card has been applied
all_files - list of all files
dsc_files - list of files which are discoverable
*/
int
mysql_find_files(THD *thd,List<char> *files, const char *db,const char *path,
const char *wild, bool dir)
mysql_list_files(THD *thd, const char *db, const char *path, const char *wild,
bool dir, List<char> *files, HASH *all_files, HASH* dsc_files)
{
uint i;
char *ext;
char *ext, **dsc_ext;
MY_DIR *dirp;
FILEINFO *file;
#ifndef NO_EMBEDDED_ACCESS_CHECKS
uint col_access=thd->col_access;
#endif
TABLE_LIST table_list;
DBUG_ENTER("mysql_find_files");
DBUG_ENTER("mysql_list_files");
if (wild && !wild[0])
wild=0;
bzero((char*) &table_list,sizeof(table_list));
if (!(dirp = my_dir(path,MYF(MY_WME | (dir ? MY_WANT_STAT : 0)))))
DBUG_RETURN(-1);
for (i=0 ; i < (uint) dirp->number_off_files ; i++)
for (i= 0; i < (uint)dirp->number_off_files; i++)
{
file=dirp->dir_entry+i;
file= dirp->dir_entry+i;
if (dir)
{ /* Return databases */
#ifdef USE_SYMDIR
......@@ -391,7 +420,7 @@ mysql_find_files(THD *thd,List<char> *files, const char *db,const char *path,
/* Only show the sym file if it points to a directory */
char buff[FN_REFLEN], *end;
MY_STAT status;
*ext=0; /* Remove extension */
*ext= 0; /* Remove extension */
unpack_dirname(buff, file->name);
end= strend(buff);
if (end != buff && end[-1] == FN_LIBCHAR)
......@@ -410,11 +439,36 @@ mysql_find_files(THD *thd,List<char> *files, const char *db,const char *path,
}
else
{
// Return only .frm files which aren't temp files.
if (my_strcasecmp(system_charset_info, ext=fn_ext(file->name),reg_ext) ||
is_prefix(file->name,tmp_file_prefix))
// Don't process temp files
if (is_prefix(file->name, tmp_file_prefix))
continue;
ext= fn_ext(file->name);
// Check for files that indicates the table can be discovered
if (ha_can_discover(thd, file->name))
{
DBUG_PRINT("info", ("Discoverable file found: %s", file->name));
*ext= 0;
if (my_hash_insert(dsc_files, (byte*)thd->strdup(file->name)))
{
my_dirend(dirp);
DBUG_RETURN(-1);
}
continue;
}
// Return only .frm files
if (my_strcasecmp(system_charset_info, ext,reg_ext))
continue;
*ext=0;
// Insert into list of all .frm files
if (my_hash_insert(all_files, (byte*)thd->strdup(file->name)))
{
my_dirend(dirp);
DBUG_RETURN(-1);
}
if (wild)
{
if (lower_case_table_names)
......@@ -443,11 +497,114 @@ mysql_find_files(THD *thd,List<char> *files, const char *db,const char *path,
DBUG_RETURN(-1);
}
}
DBUG_PRINT("info",("found: %d files", files->elements));
my_dirend(dirp);
DBUG_RETURN(0);
}
extern "C" byte* ha_tables_get_key(const char *entry, uint *length,
my_bool not_used __attribute__((unused)))
{
*length= strlen(entry);
return (byte*) entry;
}
int
mysql_find_files(THD *thd,List<char> *files, const char *db,
const char *path, const char *wild, bool dir)
{
int error= -1;
uint i;
bool discovery_performed= false;
DBUG_ENTER("mysql_find_files");
DBUG_PRINT("enter", ("db: %s, path: %s, wild: %s, dir: %d",
db, path, wild, dir));
if (wild && !wild[0])
wild=0;
HASH ha_tables, all_files, dsc_files;
if (hash_init(&ha_tables,system_charset_info,32,0,0,
(hash_get_key) ha_tables_get_key,0,0) ||
hash_init(&all_files,system_charset_info,32,0,0,
(hash_get_key) ha_tables_get_key,0,0) ||
hash_init(&dsc_files,system_charset_info,32,0,0,
(hash_get_key) ha_tables_get_key,0,0))
goto err_end;
if (mysql_discover_tables(thd, &ha_tables, db, dir))
goto err_end;
if (mysql_list_files(thd, db, path, wild, dir,
files, &all_files, &dsc_files))
goto err_end;
/*
Discovery part 1
Loop through handler files and see if any of them should be discovered
*/
for (i= 0; i < ha_tables.records; i++)
{
const char *name = hash_element(&ha_tables, i);
if (hash_search(&all_files, name, strlen(name)))
continue;
// Table was in handler, but not in list of all tables
DBUG_PRINT("info", ("Table to discover[%d]: %s", i, name));
pthread_mutex_lock(&LOCK_open);
ha_create_table_from_engine(thd, db, name, true);
pthread_mutex_unlock(&LOCK_open);
discovery_performed= true;
}
/*
Discovery part2
Loop through dsc files and see if any of them need to be deleted
*/
for (i= 0; i < dsc_files.records; i++)
{
const char *name = hash_element(&dsc_files, i);
if (hash_search(&ha_tables, name, strlen(name)))
continue;
// Table was only on disk and not in handler
DBUG_PRINT("info", ("Table[%d]: %s only exists on disk", i, name));
// Verify that handler agrees table is gone.
if (ha_table_exists(thd, db, name) == 0)
{
// Delete the table and all related files
TABLE_LIST table_list;
bzero((char*) &table_list,sizeof(table_list));
table_list.db= (char*) db;
table_list.real_name=(char*)name;
(void)mysql_rm_table_part2_with_lock(thd, &table_list,
/* if_exists */ true,
/* drop_temporary */ false,
/* dont_log_query*/ true);
discovery_performed= true;
}
}
if (discovery_performed)
{
// Call mysql_list_files one more time to get an updated list
DBUG_PRINT("info", ("Calling mysql_list_files one more time"));
files->empty();
if (mysql_list_files(thd, db, path, wild, dir,
files, &all_files, &dsc_files))
goto err_end;
}
DBUG_PRINT("info",("found: %d files", files->elements));
error = 0;
err_end:
hash_free(&ha_tables);
hash_free(&all_files);
hash_free(&dsc_files);
DBUG_RETURN(error);
}
/***************************************************************************
Extended version of mysqld_show_tables
......
......@@ -216,7 +216,8 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists,
strxmov(path, mysql_data_home, "/", db, "/", alias, reg_ext, NullS);
(void) unpack_filename(path,path);
}
if (drop_temporary || access(path,F_OK))
if (drop_temporary ||
(access(path,F_OK) && ha_create_table_from_engine(thd,db,alias,true)))
{
if (if_exists)
push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
......@@ -1237,7 +1238,7 @@ int mysql_create_table(THD *thd,const char *db, const char *table_name,
{
bool create_if_not_exists =
create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS;
if (!create_table_from_handler(db, table_name,
if (!ha_create_table_from_engine(thd, db, table_name,
create_if_not_exists))
{
DBUG_PRINT("info", ("Table already existed in handler"));
......
......@@ -705,6 +705,14 @@ int openfrm(const char *name, const char *alias, uint db_stat, uint prgflag,
outparam->crashed=((err == HA_ERR_CRASHED_ON_USAGE) &&
outparam->file->auto_repair() &&
!(ha_open_flags & HA_OPEN_FOR_REPAIR));
if (err==HA_ERR_NO_SUCH_TABLE)
{
/* The table did not exists in storage engine, use same error message
as if the .frm file didn't exist */
error= 1;
my_errno= ENOENT;
}
goto err_not_open; /* purecov: inspected */
}
}
......
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