Commit 99b18a03 authored by unknown's avatar unknown

Bug#11815557 60269: MYSQL SHOULD REJECT ATTEMPTS TO CREATE SYSTEM

                    TABLES IN INCORRECT ENGINE

PROBLEM:
  CREATE/ALTER TABLE currently can move system tables like
mysql.db, user, host etc, to engines other than MyISAM. This is not
completely supported as of now, by mysqld. When some of system tables
like plugin, servers, event, func, *_priv, time_zone* are moved
to innodb, mysqld restart crashes. Currently system tables
can be moved to BLACKHOLE also!!!.

ANALYSIS:
  The problem is that there is no check before creating or moving
a system table to some particular engine.

  System tables are suppose to be residing in MyISAM. We can think
of restricting system tables to exist only in MyISAM. But, there could
be future needs of these system tables to be part of other engines
by design. For eg, NDB cluster expects some tables to be on innodb
or ndb engine. This calls for a solution, by which system
tables can be supported by any desired engine, with minimal effort.

FIX:
  The solution provides a handlerton interface using which,
mysqld server can query particular storage engine handlerton for
system tables that it supports. This way each storage engine
layer can define their own system database and system tables.

  The check_engine() function uses the new handlerton function
ha_check_if_supported_system_table() to check if db.tablename
provided in the DDL is supported by the SE.

Note: This fix has modified a test in help.test, which was moving
mysql.help_* to innodb. The primary intention of the test was not
to move them between engines.
parent 26713f5a
......@@ -1433,3 +1433,58 @@ rename table t2 to t1;
execute stmt1;
deallocate prepare stmt1;
drop table t2;
#
# Bug#11815557 60269: MYSQL SHOULD REJECT ATTEMPTS TO CREATE SYSTEM
# TABLES IN INCORRECT ENGINE
#
# Note: This test assumes that only MyISAM supports system tables.
# If other engines are made to support system tables,
# then this test needs to be updated
#
use mysql;
ALTER TABLE db ENGINE=innodb;
ERROR HY000: Storage engine 'InnoDB' does not support system tables. [mysql.db]
ALTER TABLE user ENGINE=memory;
ERROR HY000: Storage engine 'MEMORY' does not support system tables. [mysql.user]
ALTER TABLE proc ENGINE=heap;
ERROR HY000: Storage engine 'MEMORY' does not support system tables. [mysql.proc]
ALTER TABLE func ENGINE=csv;
ERROR HY000: Storage engine 'CSV' does not support system tables. [mysql.func]
ALTER TABLE event ENGINE=merge;
ERROR HY000: Storage engine 'MRG_MYISAM' does not support system tables. [mysql.event]
ALTER TABLE servers ENGINE=innodb;
ERROR HY000: Storage engine 'InnoDB' does not support system tables. [mysql.servers]
ALTER TABLE procs_priv ENGINE=memory;
ERROR HY000: Storage engine 'MEMORY' does not support system tables. [mysql.procs_priv]
ALTER TABLE tables_priv ENGINE=heap;
ERROR HY000: Storage engine 'MEMORY' does not support system tables. [mysql.tables_priv]
ALTER TABLE columns_priv ENGINE=csv;
ERROR HY000: Storage engine 'CSV' does not support system tables. [mysql.columns_priv]
ALTER TABLE time_zone ENGINE=merge;
ERROR HY000: Storage engine 'MRG_MYISAM' does not support system tables. [mysql.time_zone]
ALTER TABLE help_topic ENGINE=innodb;
ERROR HY000: Storage engine 'InnoDB' does not support system tables. [mysql.help_topic]
CREATE TABLE db (dummy int) ENGINE=innodb;
ERROR HY000: Storage engine 'InnoDB' does not support system tables. [mysql.db]
CREATE TABLE user (dummy int) ENGINE=memory;
ERROR HY000: Storage engine 'MEMORY' does not support system tables. [mysql.user]
CREATE TABLE proc (dummy int) ENGINE=heap;
ERROR HY000: Storage engine 'MEMORY' does not support system tables. [mysql.proc]
CREATE TABLE func (dummy int) ENGINE=csv;
ERROR HY000: Storage engine 'CSV' does not support system tables. [mysql.func]
CREATE TABLE event (dummy int) ENGINE=merge;
ERROR HY000: Storage engine 'MRG_MYISAM' does not support system tables. [mysql.event]
CREATE TABLE servers (dummy int) ENGINE=innodb;
ERROR HY000: Storage engine 'InnoDB' does not support system tables. [mysql.servers]
CREATE TABLE procs_priv (dummy int) ENGINE=memory;
ERROR HY000: Storage engine 'MEMORY' does not support system tables. [mysql.procs_priv]
CREATE TABLE tables_priv (dummy int) ENGINE=heap;
ERROR HY000: Storage engine 'MEMORY' does not support system tables. [mysql.tables_priv]
CREATE TABLE columns_priv (dummy int) ENGINE=csv;
ERROR HY000: Storage engine 'CSV' does not support system tables. [mysql.columns_priv]
CREATE TABLE time_zone (dummy int) ENGINE=merge;
ERROR HY000: Storage engine 'MRG_MYISAM' does not support system tables. [mysql.time_zone]
CREATE TABLE help_topic (dummy int) ENGINE=innodb;
ERROR HY000: Storage engine 'InnoDB' does not support system tables. [mysql.help_topic]
use test;
# End of Bug#11815557
......@@ -148,100 +148,6 @@ help 'impossible_category_1';
source_category_name name is_it_category
impossible_category_1 impossible_function_1 N
impossible_category_1 impossible_function_2 N
alter table mysql.help_relation engine=innodb;
alter table mysql.help_keyword engine=innodb;
alter table mysql.help_topic engine=innodb;
alter table mysql.help_category engine=innodb;
help 'function_of_my_dream';
name is_it_category
help '%possible_f%';
name is_it_category
impossible_function_1 N
impossible_function_2 N
impossible_function_3 N
impossible_function_4 N
impossible_function_7 N
help 'impossible_func%';
name is_it_category
impossible_function_1 N
impossible_function_2 N
impossible_function_3 N
impossible_function_4 N
impossible_function_7 N
help 'impossible_category%';
name is_it_category
impossible_category_1 Y
impossible_category_2 Y
impossible_category_3 Y
help 'impossible_%';
name is_it_category
impossible_function_1 N
impossible_function_2 N
impossible_function_3 N
impossible_function_4 N
impossible_function_7 N
impossible_category_1 Y
impossible_category_2 Y
impossible_category_3 Y
help '%function_1';
name description example
impossible_function_1 description of
impossible_function1
example of
impossible_function1
help '%function_2';
name description example
impossible_function_2 description of
impossible_function2
example of
impossible_function2
help '%function_3';
name description example
impossible_function_3 description of
impossible_function3
example of
impossible_function3
help '%function_4';
name description example
impossible_function_4 description of
impossible_function4
example of
impossible_function4
help '%function_5';
name description example
impossible_function_1 description of
impossible_function1
example of
impossible_function1
help '%function_6';
name is_it_category
impossible_function_3 N
impossible_function_4 N
help '%function_7';
name description example
impossible_function_7 description of
impossible_function5
example of
impossible_function7
help '%category_2';
source_category_name name is_it_category
impossible_category_2 impossible_function_3 N
impossible_category_2 impossible_function_4 N
impossible_category_2 impossible_category_3 Y
help 'impossible_function_1';
name description example
impossible_function_1 description of
impossible_function1
example of
impossible_function1
help 'impossible_category_1';
source_category_name name is_it_category
impossible_category_1 impossible_function_1 N
impossible_category_1 impossible_function_2 N
alter table mysql.help_relation engine=myisam;
alter table mysql.help_keyword engine=myisam;
alter table mysql.help_topic engine=myisam;
alter table mysql.help_category engine=myisam;
delete from mysql.help_topic where help_topic_id=@topic1_id;
delete from mysql.help_topic where help_topic_id=@topic2_id;
delete from mysql.help_topic where help_topic_id=@topic3_id;
......
......@@ -1203,3 +1203,61 @@ rename table t2 to t1;
execute stmt1;
deallocate prepare stmt1;
drop table t2;
--echo #
--echo # Bug#11815557 60269: MYSQL SHOULD REJECT ATTEMPTS TO CREATE SYSTEM
--echo # TABLES IN INCORRECT ENGINE
--echo #
--echo # Note: This test assumes that only MyISAM supports system tables.
--echo # If other engines are made to support system tables,
--echo # then this test needs to be updated
--echo #
use mysql;
--error ER_UNSUPPORTED_ENGINE
ALTER TABLE db ENGINE=innodb;
--error ER_UNSUPPORTED_ENGINE
ALTER TABLE user ENGINE=memory;
--error ER_UNSUPPORTED_ENGINE
ALTER TABLE proc ENGINE=heap;
--error ER_UNSUPPORTED_ENGINE
ALTER TABLE func ENGINE=csv;
--error ER_UNSUPPORTED_ENGINE
ALTER TABLE event ENGINE=merge;
--error ER_UNSUPPORTED_ENGINE
ALTER TABLE servers ENGINE=innodb;
--error ER_UNSUPPORTED_ENGINE
ALTER TABLE procs_priv ENGINE=memory;
--error ER_UNSUPPORTED_ENGINE
ALTER TABLE tables_priv ENGINE=heap;
--error ER_UNSUPPORTED_ENGINE
ALTER TABLE columns_priv ENGINE=csv;
--error ER_UNSUPPORTED_ENGINE
ALTER TABLE time_zone ENGINE=merge;
--error ER_UNSUPPORTED_ENGINE
ALTER TABLE help_topic ENGINE=innodb;
--error ER_UNSUPPORTED_ENGINE
CREATE TABLE db (dummy int) ENGINE=innodb;
--error ER_UNSUPPORTED_ENGINE
CREATE TABLE user (dummy int) ENGINE=memory;
--error ER_UNSUPPORTED_ENGINE
CREATE TABLE proc (dummy int) ENGINE=heap;
--error ER_UNSUPPORTED_ENGINE
CREATE TABLE func (dummy int) ENGINE=csv;
--error ER_UNSUPPORTED_ENGINE
CREATE TABLE event (dummy int) ENGINE=merge;
--error ER_UNSUPPORTED_ENGINE
CREATE TABLE servers (dummy int) ENGINE=innodb;
--error ER_UNSUPPORTED_ENGINE
CREATE TABLE procs_priv (dummy int) ENGINE=memory;
--error ER_UNSUPPORTED_ENGINE
CREATE TABLE tables_priv (dummy int) ENGINE=heap;
--error ER_UNSUPPORTED_ENGINE
CREATE TABLE columns_priv (dummy int) ENGINE=csv;
--error ER_UNSUPPORTED_ENGINE
CREATE TABLE time_zone (dummy int) ENGINE=merge;
--error ER_UNSUPPORTED_ENGINE
CREATE TABLE help_topic (dummy int) ENGINE=innodb;
use test;
--echo # End of Bug#11815557
......@@ -63,38 +63,6 @@ help 'impossible_function_1';
help 'impossible_category_1';
##############
--disable_warnings
alter table mysql.help_relation engine=innodb;
alter table mysql.help_keyword engine=innodb;
alter table mysql.help_topic engine=innodb;
alter table mysql.help_category engine=innodb;
--enable_warnings
##############
help 'function_of_my_dream';
help '%possible_f%';
help 'impossible_func%';
help 'impossible_category%';
help 'impossible_%';
help '%function_1';
help '%function_2';
help '%function_3';
help '%function_4';
help '%function_5';
help '%function_6';
help '%function_7';
help '%category_2';
help 'impossible_function_1';
help 'impossible_category_1';
##############
alter table mysql.help_relation engine=myisam;
alter table mysql.help_keyword engine=myisam;
alter table mysql.help_topic engine=myisam;
alter table mysql.help_category engine=myisam;
delete from mysql.help_topic where help_topic_id=@topic1_id;
delete from mysql.help_topic where help_topic_id=@topic2_id;
delete from mysql.help_topic where help_topic_id=@topic3_id;
......
This diff is collapsed.
......@@ -382,6 +382,25 @@ enum enum_binlog_command {
/** Unused. Reserved for future versions. */
#define HA_CREATE_USED_PAGE_CHECKSUM (1L << 21)
/*
This is master database for most of system tables. However there
can be other databases which can hold system tables. Respective
storage engines define their own system database names.
*/
extern const char *mysqld_system_database;
/*
Structure to hold list of system_database.system_table.
This is used at both mysqld and storage engine layer.
*/
struct st_system_tablename
{
const char *db;
const char *tablename;
};
typedef ulonglong my_xid; // this line is the same as in log_event.h
#define MYSQL_XID_PREFIX "MySQLXid"
#define MYSQL_XID_PREFIX_LEN 8 // must be a multiple of 8
......@@ -800,6 +819,39 @@ struct handlerton
const char *wild, bool dir, List<LEX_STRING> *files);
int (*table_exists_in_engine)(handlerton *hton, THD* thd, const char *db,
const char *name);
/**
List of all system tables specific to the SE.
Array element would look like below,
{ "<database_name>", "<system table name>" },
The last element MUST be,
{ (const char*)NULL, (const char*)NULL }
@see ha_example_system_tables in ha_example.cc
This interface is optional, so every SE need not implement it.
*/
const char* (*system_database)();
/**
Check if the given db.tablename is a system table for this SE.
@param db Database name to check.
@param table_name table name to check.
@param is_sql_layer_system_table if the supplied db.table_name is a SQL
layer system table.
@see example_is_supported_system_table in ha_example.cc
is_sql_layer_system_table is supplied to make more efficient
checks possible for SEs that support all SQL layer tables.
This interface is optional, so every SE need not implement it.
*/
bool (*is_supported_system_table)(const char *db,
const char *table_name,
bool is_sql_layer_system_table);
uint32 license; /* Flag for Engine License */
void *data; /* Location for engines to keep personal structures */
};
......@@ -2238,6 +2290,8 @@ int ha_discover(THD* thd, const char* dbname, const char* name,
int ha_find_files(THD *thd,const char *db,const char *path,
const char *wild, bool dir, List<LEX_STRING>* files);
int ha_table_exists_in_engine(THD* thd, const char* db, const char* name);
bool ha_check_if_supported_system_table(handlerton *hton, const char* db,
const char* table_name);
/* key cache */
extern "C" int ha_init_key_cache(const char *name, KEY_CACHE *key_cache);
......
......@@ -6497,6 +6497,9 @@ ER_BINLOG_UNSAFE_CREATE_SELECT_AUTOINC
ER_BINLOG_UNSAFE_INSERT_TWO_KEYS
eng "INSERT... ON DUPLICATE KEY UPDATE on a table with more than one UNIQUE KEY is unsafe"
ER_UNSUPPORTED_ENGINE
eng "Storage engine '%s' does not support system tables. [%s.%s]"
#
# End of 5.5 error messages.
#
......@@ -69,7 +69,10 @@ static int copy_data_between_tables(TABLE *from,TABLE *to,
bool error_if_not_empty);
static bool prepare_blob_field(THD *thd, Create_field *sql_field);
static bool check_engine(THD *, const char *, HA_CREATE_INFO *);
static bool check_engine(THD *thd, const char *db_name,
const char *table_name,
HA_CREATE_INFO *create_info);
static int
mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info,
Alter_info *alter_info,
......@@ -3940,7 +3943,7 @@ bool mysql_create_table_no_lock(THD *thd,
MYF(0));
DBUG_RETURN(TRUE);
}
if (check_engine(thd, table_name, create_info))
if (check_engine(thd, db, table_name, create_info))
DBUG_RETURN(TRUE);
set_table_default_charset(thd, create_info, (char*) db);
......@@ -5923,7 +5926,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
create_info->db_type= old_db_type;
}
if (check_engine(thd, new_name, create_info))
if (check_engine(thd, new_db, new_name, create_info))
goto err;
new_db_type= create_info->db_type;
......@@ -7342,16 +7345,32 @@ bool mysql_checksum_table(THD *thd, TABLE_LIST *tables,
DBUG_RETURN(TRUE);
}
static bool check_engine(THD *thd, const char *table_name,
HA_CREATE_INFO *create_info)
/**
@brief Check if the table can be created in the specified storage engine.
Checks if the storage engine is enabled and supports the given table
type (e.g. normal, temporary, system). May do engine substitution
if the requested engine is disabled.
@param thd Thread descriptor.
@param db_name Database name.
@param table_name Name of table to be created.
@param create_info Create info from parser, including engine.
@retval true Engine not available/supported, error has been reported.
@retval false Engine available/supported.
*/
static bool check_engine(THD *thd, const char *db_name,
const char *table_name, HA_CREATE_INFO *create_info)
{
DBUG_ENTER("check_engine");
handlerton **new_engine= &create_info->db_type;
handlerton *req_engine= *new_engine;
bool no_substitution=
test(thd->variables.sql_mode & MODE_NO_ENGINE_SUBSTITUTION);
if (!(*new_engine= ha_checktype(thd, ha_legacy_type(req_engine),
no_substitution, 1)))
return TRUE;
DBUG_RETURN(true);
if (req_engine && req_engine != *new_engine)
{
......@@ -7369,9 +7388,23 @@ static bool check_engine(THD *thd, const char *table_name,
my_error(ER_ILLEGAL_HA_CREATE_OPTION, MYF(0),
ha_resolve_storage_engine_name(*new_engine), "TEMPORARY");
*new_engine= 0;
return TRUE;
DBUG_RETURN(true);
}
*new_engine= myisam_hton;
}
return FALSE;
/*
Check, if the given table name is system table, and if the storage engine
does supports it.
*/
if ((create_info->used_fields & HA_CREATE_USED_ENGINE) &&
!ha_check_if_supported_system_table(*new_engine, db_name, table_name))
{
my_error(ER_UNSUPPORTED_ENGINE, MYF(0),
ha_resolve_storage_engine_name(*new_engine), db_name, table_name);
*new_engine= NULL;
DBUG_RETURN(true);
}
DBUG_RETURN(false);
}
......@@ -103,6 +103,12 @@ static handler *example_create_handler(handlerton *hton,
handlerton *example_hton;
/* Interface to mysqld, to check system tables supported by SE */
static const char* example_system_database();
static bool example_is_supported_system_table(const char *db,
const char *table_name,
bool is_sql_layer_system_table);
/* Variables for example share methods */
/*
......@@ -165,6 +171,8 @@ static int example_init_func(void *p)
example_hton->state= SHOW_OPTION_YES;
example_hton->create= example_create_handler;
example_hton->flags= HTON_CAN_RECREATE;
example_hton->system_database= example_system_database;
example_hton->is_supported_system_table= example_is_supported_system_table;
DBUG_RETURN(0);
}
......@@ -298,6 +306,65 @@ const char **ha_example::bas_ext() const
return ha_example_exts;
}
/*
Following handler function provides access to
system database specific to SE. This interface
is optional, so every SE need not implement it.
*/
const char* ha_example_system_database= NULL;
const char* example_system_database()
{
return ha_example_system_database;
}
/*
List of all system tables specific to the SE.
Array element would look like below,
{ "<database_name>", "<system table name>" },
The last element MUST be,
{ (const char*)NULL, (const char*)NULL }
This array is optional, so every SE need not implement it.
*/
static st_system_tablename ha_example_system_tables[]= {
{(const char*)NULL, (const char*)NULL}
};
/**
@brief Check if the given db.tablename is a system table for this SE.
@param db Database name to check.
@param table_name table name to check.
@param is_sql_layer_system_table if the supplied db.table_name is a SQL
layer system table.
@return
@retval TRUE Given db.table_name is supported system table.
@retval FALSE Given db.table_name is not a supported system table.
*/
static bool example_is_supported_system_table(const char *db,
const char *table_name,
bool is_sql_layer_system_table)
{
st_system_tablename *systab;
// Does this SE support "ALL" SQL layer system tables ?
if (is_sql_layer_system_table)
return false;
// Check if this is SE layer system tables
systab= ha_example_system_tables;
while (systab && systab->db)
{
if (systab->db == db &&
strcmp(systab->tablename, table_name) == 0)
return true;
systab++;
}
return false;
}
/**
@brief
......
......@@ -37,6 +37,11 @@
ulonglong myisam_recover_options;
static ulong opt_myisam_block_size;
/* Interface to mysqld, to check system tables supported by SE */
static bool myisam_is_supported_system_table(const char *db,
const char *table_name,
bool is_sql_layer_system_table);
/* bits in myisam_recover_options */
const char *myisam_recover_names[] =
{ "DEFAULT", "BACKUP", "FORCE", "QUICK", "OFF", NullS};
......@@ -665,6 +670,42 @@ const char **ha_myisam::bas_ext() const
return ha_myisam_exts;
}
/**
@brief Check if the given db.tablename is a system table for this SE.
@param db Database name to check.
@param table_name table name to check.
@param is_sql_layer_system_table if the supplied db.table_name is a SQL
layer system table.
@note Currently, only MYISAM engine supports all the SQL layer
system tables, and hence it returns true, when
is_sql_layer_system_table is set.
@note In case there is a need to define MYISAM specific system
database, then please see reference implementation in
ha_example.cc.
@return
@retval TRUE Given db.table_name is supported system table.
@retval FALSE Given db.table_name is not a supported system table.
*/
static bool myisam_is_supported_system_table(const char *db,
const char *table_name,
bool is_sql_layer_system_table)
{
// Does MYISAM support "ALL" SQL layer system tables ?
if (is_sql_layer_system_table)
return true;
/*
Currently MYISAM does not support any other SE specific
system tables. If in future it does, please see ha_example.cc
for reference implementation.
*/
return false;
}
const char *ha_myisam::index_type(uint key_number)
{
......@@ -2070,6 +2111,8 @@ static int myisam_init(void *p)
myisam_hton->create= myisam_create_handler;
myisam_hton->panic= myisam_panic;
myisam_hton->flags= HTON_CAN_RECREATE | HTON_SUPPORT_LOG_TABLES;
myisam_hton->is_supported_system_table= myisam_is_supported_system_table;
return 0;
}
......
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