diff options
Diffstat (limited to 'sql')
-rw-r--r-- | sql/handler.h | 1 | ||||
-rw-r--r-- | sql/sql_parse.cc | 120 | ||||
-rw-r--r-- | sql/sql_parse.h | 3 | ||||
-rw-r--r-- | sql/sql_table.cc | 12 |
4 files changed, 135 insertions, 1 deletions
diff --git a/sql/handler.h b/sql/handler.h index 5eb2d6b440a..17306fe7dd4 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -868,6 +868,7 @@ struct handlerton #define HTON_TEMPORARY_NOT_SUPPORTED (1 << 6) //Having temporary tables not supported #define HTON_SUPPORT_LOG_TABLES (1 << 7) //Engine supports log tables #define HTON_NO_PARTITION (1 << 8) //You can not partition these tables +#define HTON_SUPPORTS_FOREIGN_KEYS (1 << 9) //Foreign key constraint supported. class Ha_trx_info; diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index ea63d23d182..f52f56447f4 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -5251,6 +5251,121 @@ bool check_global_access(THD *thd, ulong want_access) #endif } + +/** + Checks foreign key's parent table access. + + @param thd [in] Thread handler + @param create_info [in] Create information (like MAX_ROWS, ENGINE or + temporary table flag) + @param alter_info [in] Initial list of columns and indexes for the + table to be created + + @retval + false ok. + @retval + true error or access denied. Error is sent to client in this case. +*/ +bool check_fk_parent_table_access(THD *thd, + HA_CREATE_INFO *create_info, + Alter_info *alter_info) +{ + Key *key; + List_iterator<Key> key_iterator(alter_info->key_list); + handlerton *db_type= create_info->db_type ? create_info->db_type : + ha_default_handlerton(thd); + + // Return if engine does not support Foreign key Constraint. + if (!ha_check_storage_engine_flag(db_type, HTON_SUPPORTS_FOREIGN_KEYS)) + return false; + + while ((key= key_iterator++)) + { + if (key->type == Key::FOREIGN_KEY) + { + TABLE_LIST parent_table; + bool is_qualified_table_name; + Foreign_key *fk_key= (Foreign_key *)key; + LEX_STRING db_name; + LEX_STRING table_name= { fk_key->ref_table->table.str, + fk_key->ref_table->table.length }; + const ulong privileges= (SELECT_ACL | INSERT_ACL | UPDATE_ACL | + DELETE_ACL | REFERENCES_ACL); + + // Check if tablename is valid or not. + DBUG_ASSERT(table_name.str != NULL); + if (check_table_name(table_name.str, table_name.length, false)) + { + my_error(ER_WRONG_TABLE_NAME, MYF(0), table_name.str); + return true; + } + + if (fk_key->ref_table->db.str) + { + is_qualified_table_name= true; + db_name.str= (char *) thd->memdup(fk_key->ref_table->db.str, + fk_key->ref_table->db.length+1); + db_name.length= fk_key->ref_table->db.length; + + // Check if database name is valid or not. + if (fk_key->ref_table->db.str && check_db_name(&db_name)) + { + my_error(ER_WRONG_DB_NAME, MYF(0), db_name.str); + return true; + } + } + else if (thd->lex->copy_db_to(&db_name.str, &db_name.length)) + return true; + else + is_qualified_table_name= false; + + // if lower_case_table_names is set then convert tablename to lower case. + if (lower_case_table_names) + { + table_name.str= (char *) thd->memdup(fk_key->ref_table->table.str, + fk_key->ref_table->table.length+1); + table_name.length= my_casedn_str(files_charset_info, table_name.str); + } + + parent_table.init_one_table(db_name.str, db_name.length, + table_name.str, table_name.length, + table_name.str, TL_IGNORE); + + /* + Check if user has any of the "privileges" at table level on + "parent_table". + Having privilege on any of the parent_table column is not + enough so checking whether user has any of the "privileges" + at table level only here. + */ + if (check_some_access(thd, privileges, &parent_table) || + parent_table.grant.want_privilege) + { + if (is_qualified_table_name) + { + const size_t qualified_table_name_len= NAME_LEN + 1 + NAME_LEN + 1; + char *qualified_table_name= (char *) thd->alloc(qualified_table_name_len); + + my_snprintf(qualified_table_name, qualified_table_name_len, "%s.%s", + db_name.str, table_name.str); + table_name.str= qualified_table_name; + } + + my_error(ER_TABLEACCESS_DENIED_ERROR, MYF(0), + "REFERENCES", + thd->security_ctx->priv_user, + thd->security_ctx->host_or_ip, + table_name.str); + + return true; + } + } + } + + return false; +} + + /**************************************************************************** Check stack size; Send error if there isn't enough stack to continue ****************************************************************************/ @@ -7072,8 +7187,11 @@ bool create_table_precheck(THD *thd, TABLE_LIST *tables, if (check_table_access(thd, SELECT_ACL, tables, FALSE, UINT_MAX, FALSE)) goto err; } - error= FALSE; + if (check_fk_parent_table_access(thd, &lex->create_info, &lex->alter_info)) + goto err; + + error= FALSE; err: DBUG_RETURN(error); } diff --git a/sql/sql_parse.h b/sql/sql_parse.h index 9a55174b0fb..f7b4d530543 100644 --- a/sql/sql_parse.h +++ b/sql/sql_parse.h @@ -45,6 +45,9 @@ bool delete_precheck(THD *thd, TABLE_LIST *tables); bool insert_precheck(THD *thd, TABLE_LIST *tables); bool create_table_precheck(THD *thd, TABLE_LIST *tables, TABLE_LIST *create_table); +bool check_fk_parent_table_access(THD *thd, + HA_CREATE_INFO *create_info, + Alter_info *alter_info); bool parse_sql(THD *thd, Parser_state *parser_state, diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 8b4873cb834..e8de6a6928a 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -6005,6 +6005,18 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, } /* + If foreign key is added then check permission to access parent table. + + In function "check_fk_parent_table_access", create_info->db_type is used + to identify whether engine supports FK constraint or not. Since + create_info->db_type is set here, check to parent table access is delayed + till this point for the alter operation. + */ + if ((alter_info->flags & ALTER_FOREIGN_KEY) && + check_fk_parent_table_access(thd, create_info, alter_info)) + goto err; + + /* If this is an ALTER TABLE and no explicit row type specified reuse the table's row type. Note : this is the same as if the row type was specified explicitly. |