summaryrefslogtreecommitdiff
path: root/sql/handler.cc
diff options
context:
space:
mode:
authorGeorgi Kodinov <Georgi.Kodinov@Oracle.com>2012-04-12 14:04:12 +0300
committerGeorgi Kodinov <Georgi.Kodinov@Oracle.com>2012-04-12 14:04:12 +0300
commita84ca722247a2aa118c42d8c55d80196660d6795 (patch)
tree670cea5ced9a811e020dff96778fa05250fc9e39 /sql/handler.cc
parent12421762616f217a98002df50538c46f6966b4d9 (diff)
parent64e74d484fcbf6d348397b3ce4f32438754dc8bd (diff)
downloadmariadb-git-a84ca722247a2aa118c42d8c55d80196660d6795.tar.gz
merge mysql-5.5->mysql-5.5-security
Diffstat (limited to 'sql/handler.cc')
-rw-r--r--sql/handler.cc307
1 files changed, 307 insertions, 0 deletions
diff --git a/sql/handler.cc b/sql/handler.cc
index 8ce20c19d2d..b2cb7f1cf9a 100644
--- a/sql/handler.cc
+++ b/sql/handler.cc
@@ -45,6 +45,7 @@
#include "ha_partition.h"
#endif
+
/*
While we have legacy_db_type, we have this array to
check for dups and to find handlerton from legacy_db_type.
@@ -90,6 +91,83 @@ TYPELIB tx_isolation_typelib= {array_elements(tx_isolation_names)-1,"",
static TYPELIB known_extensions= {0,"known_exts", NULL, NULL};
uint known_extensions_id= 0;
+/**
+ Database name that hold most of mysqld system tables.
+ Current code assumes that, there exists only some
+ specific "database name" designated as system database.
+*/
+const char* mysqld_system_database= "mysql";
+
+// System tables that belong to mysqld_system_database.
+st_system_tablename mysqld_system_tables[]= {
+ {mysqld_system_database, "db"},
+ {mysqld_system_database, "user"},
+ {mysqld_system_database, "host"},
+ {mysqld_system_database, "func"},
+ {mysqld_system_database, "proc"},
+ {mysqld_system_database, "event"},
+ {mysqld_system_database, "plugin"},
+ {mysqld_system_database, "servers"},
+ {mysqld_system_database, "procs_priv"},
+ {mysqld_system_database, "tables_priv"},
+ {mysqld_system_database, "proxies_priv"},
+ {mysqld_system_database, "columns_priv"},
+ {mysqld_system_database, "time_zone"},
+ {mysqld_system_database, "time_zone_name"},
+ {mysqld_system_database, "time_zone_leap_second"},
+ {mysqld_system_database, "time_zone_transition"},
+ {mysqld_system_database, "time_zone_transition_type"},
+ {mysqld_system_database, "help_category"},
+ {mysqld_system_database, "help_keyword"},
+ {mysqld_system_database, "help_relation"},
+ {mysqld_system_database, "help_topic"},
+ {(const char *)NULL, (const char *)NULL} /* This must be at the end */
+};
+
+/**
+ This static pointer holds list of system databases from SQL layer and
+ various SE's. The required memory is allocated once, and never freed.
+*/
+static const char **known_system_databases= NULL;
+static const char **ha_known_system_databases();
+
+// Called for each SE to get SE specific system database.
+static my_bool system_databases_handlerton(THD *unused, plugin_ref plugin,
+ void *arg);
+
+// Called for each SE to check if given db.table_name is a system table.
+static my_bool check_engine_system_table_handlerton(THD *unused,
+ plugin_ref plugin,
+ void *arg);
+
+/**
+ Structure used by SE during check for system table.
+ This structure is passed to each SE handlerton and the status (OUT param)
+ is collected.
+*/
+struct st_sys_tbl_chk_params
+{
+ const char *db; // IN param
+ const char *table_name; // IN param
+ bool is_sql_layer_system_table; // IN param
+ legacy_db_type db_type; // IN param
+
+ enum enum_sys_tbl_chk_status
+ {
+ // db.table_name is not a supported system table.
+ NOT_KNOWN_SYSTEM_TABLE,
+ /*
+ db.table_name is a system table,
+ but may not be supported by SE.
+ */
+ KNOWN_SYSTEM_TABLE,
+ /*
+ db.table_name is a system table,
+ and is supported by SE.
+ */
+ SUPPORTED_SYSTEM_TABLE
+ }status; // OUT param
+};
static plugin_ref ha_default_plugin(THD *thd)
@@ -588,6 +666,14 @@ int ha_init()
*/
opt_using_transactions= total_ha>(ulong)opt_bin_log;
savepoint_alloc_size+= sizeof(SAVEPOINT);
+
+ /*
+ Initialize system database name cache.
+ This cache is used to do a quick check if a given
+ db.tablename is a system table.
+ */
+ known_system_databases= ha_known_system_databases();
+
DBUG_RETURN(error);
}
@@ -3788,6 +3874,227 @@ ha_check_if_table_exists(THD* thd, const char *db, const char *name,
DBUG_RETURN(FALSE);
}
+/**
+ @brief Check if a given table is a system table.
+
+ @details The primary purpose of introducing this function is to stop system
+ tables to be created or being moved to undesired storage engines.
+
+ @todo There is another function called is_system_table_name() used by
+ get_table_category(), which is used to set TABLE_SHARE table_category.
+ It checks only a subset of table name like proc, event and time*.
+ We cannot use below function in get_table_category(),
+ as that affects locking mechanism. If we need to
+ unify these functions, we need to fix locking issues generated.
+
+ @param hton Handlerton of new engine.
+ @param db Database name.
+ @param table_name Table name to be checked.
+
+ @return Operation status
+ @retval true If the table name is a valid system table
+ or if its a valid user table.
+
+ @retval false If the table name is a system table name
+ and does not belong to engine specified
+ in the command.
+*/
+bool ha_check_if_supported_system_table(handlerton *hton, const char *db,
+ const char *table_name)
+{
+ DBUG_ENTER("ha_check_if_supported_system_table");
+ st_sys_tbl_chk_params check_params;
+ bool is_system_database= false;
+ const char **names;
+ st_system_tablename *systab;
+
+ // Check if we have a system database name in the command.
+ DBUG_ASSERT(known_system_databases != NULL);
+ names= known_system_databases;
+ while (names && *names)
+ {
+ if (strcmp(*names, db) == 0)
+ {
+ /* Used to compare later, will be faster */
+ check_params.db= *names;
+ is_system_database= true;
+ break;
+ }
+ names++;
+ }
+ if (!is_system_database)
+ DBUG_RETURN(true); // It's a user table name.
+
+ // Check if this is SQL layer system tables.
+ systab= mysqld_system_tables;
+ check_params.is_sql_layer_system_table= false;
+ while (systab && systab->db)
+ {
+ if (systab->db == check_params.db &&
+ strcmp(systab->tablename, table_name) == 0)
+ {
+ check_params.is_sql_layer_system_table= true;
+ break;
+ }
+ systab++;
+ }
+
+ // Check if this is a system table and if some engine supports it.
+ check_params.status= check_params.is_sql_layer_system_table ?
+ st_sys_tbl_chk_params::KNOWN_SYSTEM_TABLE :
+ st_sys_tbl_chk_params::NOT_KNOWN_SYSTEM_TABLE;
+ check_params.db_type= hton->db_type;
+ check_params.table_name= table_name;
+ plugin_foreach(NULL, check_engine_system_table_handlerton,
+ MYSQL_STORAGE_ENGINE_PLUGIN, &check_params);
+
+ // SE does not support this system table.
+ if (check_params.status == st_sys_tbl_chk_params::KNOWN_SYSTEM_TABLE)
+ DBUG_RETURN(false);
+
+ // It's a system table or a valid user table.
+ DBUG_RETURN(true);
+}
+
+/**
+ @brief Called for each SE to check if given db, tablename is a system table.
+
+ @details The primary purpose of introducing this function is to stop system
+ tables to be created or being moved to undesired storage engines.
+
+ @param unused unused THD*
+ @param plugin Points to specific SE.
+ @param arg Is of type struct st_sys_tbl_chk_params.
+
+ @note
+ args->status Indicates OUT param,
+ see struct st_sys_tbl_chk_params definition for more info.
+
+ @return Operation status
+ @retval true There was a match found.
+ This will stop doing checks with other SE's.
+
+ @retval false There was no match found.
+ Other SE's will be checked to find a match.
+*/
+static my_bool check_engine_system_table_handlerton(THD *unused,
+ plugin_ref plugin,
+ void *arg)
+{
+ st_sys_tbl_chk_params *check_params= (st_sys_tbl_chk_params*) arg;
+ handlerton *hton= plugin_data(plugin, handlerton *);
+
+ // Do we already know that the table is a system table?
+ if (check_params->status == st_sys_tbl_chk_params::KNOWN_SYSTEM_TABLE)
+ {
+ /*
+ If this is the same SE specified in the command, we can
+ simply ask the SE if it supports it stop the search regardless.
+ */
+ if (hton->db_type == check_params->db_type)
+ {
+ if (hton->is_supported_system_table &&
+ hton->is_supported_system_table(check_params->db,
+ check_params->table_name,
+ check_params->is_sql_layer_system_table))
+ check_params->status= st_sys_tbl_chk_params::SUPPORTED_SYSTEM_TABLE;
+ return TRUE;
+ }
+ /*
+ If this is a different SE, there is no point in asking the SE
+ since we already know it's a system table and we don't care
+ if it is supported or not.
+ */
+ return FALSE;
+ }
+
+ /*
+ We don't yet know if the table is a system table or not.
+ We therefore must always ask the SE.
+ */
+ if (hton->is_supported_system_table &&
+ hton->is_supported_system_table(check_params->db,
+ check_params->table_name,
+ check_params->is_sql_layer_system_table))
+ {
+ /*
+ If this is the same SE specified in the command, we know it's a
+ supported system table and can stop the search.
+ */
+ if (hton->db_type == check_params->db_type)
+ {
+ check_params->status= st_sys_tbl_chk_params::SUPPORTED_SYSTEM_TABLE;
+ return TRUE;
+ }
+ else
+ check_params->status= st_sys_tbl_chk_params::KNOWN_SYSTEM_TABLE;
+ }
+
+ return FALSE;
+}
+
+/*
+ Prepare list of all known system database names
+ current we just have 'mysql' as system database name.
+
+ Later ndbcluster, innodb SE's can define some new database
+ name which can store system tables specific to SE.
+*/
+const char** ha_known_system_databases(void)
+{
+ I_List<i_string> found_databases;
+ const char **databases, **database;
+
+ // Get mysqld system database name.
+ found_databases.push_back(new i_string(mysqld_system_database));
+
+ // Get system database names from every specific storage engine.
+ plugin_foreach(NULL, system_databases_handlerton,
+ MYSQL_STORAGE_ENGINE_PLUGIN, &found_databases);
+
+ int element_count= 0;
+ I_List_iterator<i_string> iter(found_databases);
+ while (iter++) element_count++;
+ databases= (const char **) my_once_alloc(sizeof(char *)*
+ (element_count+1),
+ MYF(MY_WME | MY_FAE));
+ DBUG_ASSERT(databases != NULL);
+
+ database= databases;
+ i_string *tmp;
+ while ((tmp= found_databases.get()))
+ {
+ *database++= tmp->ptr;
+ delete tmp;
+ }
+ *database= NULL; // Last element.
+
+ return databases;
+}
+
+/**
+ @brief Fetch system database name specific to SE.
+
+ @details This function is invoked by plugin_foreach() from
+ ha_known_system_databases(), for each storage engine.
+*/
+static my_bool system_databases_handlerton(THD *unused, plugin_ref plugin,
+ void *arg)
+{
+ I_List<i_string> *found_databases= (I_List<i_string> *) arg;
+ const char *db;
+
+ handlerton *hton= plugin_data(plugin, handlerton *);
+ if (hton->system_database)
+ {
+ db= hton->system_database();
+ if (db)
+ found_databases->push_back(new i_string(db));
+ }
+
+ return FALSE;
+}
+
void st_ha_check_opt::init()
{