This the 4-th patch in scope of CS patch (BUG#11986).
The patch contains the following changes: - Introduce auxilary functions to convenient work with character sets: - resolve_charset(); - resolve_collation(); - get_default_db_collation(); - Introduce lex_string_set(); - Refactor Table_trigger_list::process_triggers() & sp_head::execute_trigger() to be consistent with other code; - Move reusable code from add_table_for_trigger() into build_trn_path(), check_trn_exists() and load_table_name_for_trigger() to be used in the following patch. - Rename triggers_file_ext and trigname_file_ext into TRN_EXT and TRG_EXT respectively.
This commit is contained in:
parent
f8be48bb4f
commit
62e3e46275
@ -904,6 +904,14 @@ extern CHARSET_INFO *get_charset(uint cs_number, myf flags);
|
|||||||
extern CHARSET_INFO *get_charset_by_name(const char *cs_name, myf flags);
|
extern CHARSET_INFO *get_charset_by_name(const char *cs_name, myf flags);
|
||||||
extern CHARSET_INFO *get_charset_by_csname(const char *cs_name,
|
extern CHARSET_INFO *get_charset_by_csname(const char *cs_name,
|
||||||
uint cs_flags, myf my_flags);
|
uint cs_flags, myf my_flags);
|
||||||
|
|
||||||
|
extern bool resolve_charset(CHARSET_INFO **cs,
|
||||||
|
const char *cs_name,
|
||||||
|
CHARSET_INFO *default_cs);
|
||||||
|
extern bool resolve_collation(CHARSET_INFO **cl,
|
||||||
|
const char *cl_name,
|
||||||
|
CHARSET_INFO *default_cl);
|
||||||
|
|
||||||
extern void free_charsets(void);
|
extern void free_charsets(void);
|
||||||
extern char *get_charsets_dir(char *buf);
|
extern char *get_charsets_dir(char *buf);
|
||||||
extern my_bool my_charset_same(CHARSET_INFO *cs1, CHARSET_INFO *cs2);
|
extern my_bool my_charset_same(CHARSET_INFO *cs1, CHARSET_INFO *cs2);
|
||||||
|
@ -573,6 +573,70 @@ CHARSET_INFO *get_charset_by_csname(const char *cs_name,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
Resolve character set by the character set name (utf8, latin1, ...).
|
||||||
|
|
||||||
|
The function tries to resolve character set by the specified name. If
|
||||||
|
there is character set with the given name, it is assigned to the "cs"
|
||||||
|
parameter and FALSE is returned. If there is no such character set,
|
||||||
|
"default_cs" is assigned to the "cs" and TRUE is returned.
|
||||||
|
|
||||||
|
@param[out] cs Variable to store character set.
|
||||||
|
@param[in] cs_name Character set name.
|
||||||
|
@param[in] default_cs Default character set.
|
||||||
|
|
||||||
|
@return FALSE if character set was resolved successfully; TRUE if there
|
||||||
|
is no character set with given name.
|
||||||
|
*/
|
||||||
|
|
||||||
|
bool resolve_charset(CHARSET_INFO **cs,
|
||||||
|
const char *cs_name,
|
||||||
|
CHARSET_INFO *default_cs)
|
||||||
|
{
|
||||||
|
*cs= get_charset_by_csname(cs_name, MY_CS_PRIMARY, MYF(0));
|
||||||
|
|
||||||
|
if (*cs == NULL)
|
||||||
|
{
|
||||||
|
*cs= default_cs;
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
Resolve collation by the collation name (utf8_general_ci, ...).
|
||||||
|
|
||||||
|
The function tries to resolve collation by the specified name. If there
|
||||||
|
is collation with the given name, it is assigned to the "cl" parameter
|
||||||
|
and FALSE is returned. If there is no such collation, "default_cl" is
|
||||||
|
assigned to the "cl" and TRUE is returned.
|
||||||
|
|
||||||
|
@param[out] cl Variable to store collation.
|
||||||
|
@param[in] cl_name Collation name.
|
||||||
|
@param[in] default_cl Default collation.
|
||||||
|
|
||||||
|
@return FALSE if collation was resolved successfully; TRUE if there is no
|
||||||
|
collation with given name.
|
||||||
|
*/
|
||||||
|
|
||||||
|
bool resolve_collation(CHARSET_INFO **cl,
|
||||||
|
const char *cl_name,
|
||||||
|
CHARSET_INFO *default_cl)
|
||||||
|
{
|
||||||
|
*cl= get_charset_by_name(cl_name, MYF(0));
|
||||||
|
|
||||||
|
if (*cl == NULL)
|
||||||
|
{
|
||||||
|
*cl= default_cl;
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Escape string with backslashes (\)
|
Escape string with backslashes (\)
|
||||||
|
|
||||||
|
@ -3376,8 +3376,8 @@ TYPELIB *ha_known_exts(void)
|
|||||||
const char **ext, *old_ext;
|
const char **ext, *old_ext;
|
||||||
|
|
||||||
known_extensions_id= mysys_usage_id;
|
known_extensions_id= mysys_usage_id;
|
||||||
found_exts.push_back((char*) triggers_file_ext);
|
found_exts.push_back((char*) TRG_EXT);
|
||||||
found_exts.push_back((char*) trigname_file_ext);
|
found_exts.push_back((char*) TRN_EXT);
|
||||||
|
|
||||||
plugin_foreach(NULL, exts_handlerton,
|
plugin_foreach(NULL, exts_handlerton,
|
||||||
MYSQL_STORAGE_ENGINE_PLUGIN, &found_exts);
|
MYSQL_STORAGE_ENGINE_PLUGIN, &found_exts);
|
||||||
|
@ -101,7 +101,7 @@ char* query_table_status(THD *thd,const char *db,const char *table_name);
|
|||||||
ER_WARN_DEPRECATED_SYNTAX, ER(ER_WARN_DEPRECATED_SYNTAX), \
|
ER_WARN_DEPRECATED_SYNTAX, ER(ER_WARN_DEPRECATED_SYNTAX), \
|
||||||
(Old), (Ver), (New)); \
|
(Old), (Ver), (New)); \
|
||||||
else \
|
else \
|
||||||
sql_print_warning("The syntax %s is deprecated and will be removed " \
|
sql_print_warning("The syntax '%s' is deprecated and will be removed " \
|
||||||
"in MySQL %s. Please use %s instead.", (Old), (Ver), (New)); \
|
"in MySQL %s. Please use %s instead.", (Old), (Ver), (New)); \
|
||||||
} while(0)
|
} while(0)
|
||||||
|
|
||||||
@ -1590,6 +1590,7 @@ bool check_db_dir_existence(const char *db_name);
|
|||||||
bool load_db_opt(THD *thd, const char *path, HA_CREATE_INFO *create);
|
bool load_db_opt(THD *thd, const char *path, HA_CREATE_INFO *create);
|
||||||
bool load_db_opt_by_name(THD *thd, const char *db_name,
|
bool load_db_opt_by_name(THD *thd, const char *db_name,
|
||||||
HA_CREATE_INFO *db_create_info);
|
HA_CREATE_INFO *db_create_info);
|
||||||
|
CHARSET_INFO *get_default_db_collation(THD *thd, const char *db_name);
|
||||||
bool my_dbopt_init(void);
|
bool my_dbopt_init(void);
|
||||||
void my_dbopt_cleanup(void);
|
void my_dbopt_cleanup(void);
|
||||||
extern int creating_database; // How many database locks are made
|
extern int creating_database; // How many database locks are made
|
||||||
@ -1610,8 +1611,8 @@ extern const char *first_keyword, *my_localhost, *delayed_user, *binary_keyword;
|
|||||||
extern const char **errmesg; /* Error messages */
|
extern const char **errmesg; /* Error messages */
|
||||||
extern const char *myisam_recover_options_str;
|
extern const char *myisam_recover_options_str;
|
||||||
extern const char *in_left_expr_name, *in_additional_cond, *in_having_cond;
|
extern const char *in_left_expr_name, *in_additional_cond, *in_having_cond;
|
||||||
extern const char * const triggers_file_ext;
|
extern const char * const TRG_EXT;
|
||||||
extern const char * const trigname_file_ext;
|
extern const char * const TRN_EXT;
|
||||||
extern Eq_creator eq_creator;
|
extern Eq_creator eq_creator;
|
||||||
extern Ne_creator ne_creator;
|
extern Ne_creator ne_creator;
|
||||||
extern Gt_creator gt_creator;
|
extern Gt_creator gt_creator;
|
||||||
@ -2146,6 +2147,12 @@ bool schema_table_store_record(THD *thd, TABLE *table);
|
|||||||
int item_create_init();
|
int item_create_init();
|
||||||
void item_create_cleanup();
|
void item_create_cleanup();
|
||||||
|
|
||||||
|
inline void lex_string_set(LEX_STRING *lex_str, const char *c_str)
|
||||||
|
{
|
||||||
|
lex_str->str= (char *) c_str;
|
||||||
|
lex_str->length= strlen(c_str);
|
||||||
|
}
|
||||||
|
|
||||||
#endif /* MYSQL_SERVER */
|
#endif /* MYSQL_SERVER */
|
||||||
#endif /* MYSQL_CLIENT */
|
#endif /* MYSQL_CLIENT */
|
||||||
|
|
||||||
|
@ -1268,30 +1268,31 @@ set_routine_security_ctx(THD *thd, sp_head *sp, bool is_proc,
|
|||||||
#endif // ! NO_EMBEDDED_ACCESS_CHECKS
|
#endif // ! NO_EMBEDDED_ACCESS_CHECKS
|
||||||
|
|
||||||
|
|
||||||
/*
|
/**
|
||||||
Execute a trigger:
|
Execute trigger stored program.
|
||||||
- changes security context for triggers
|
|
||||||
- switch to new memroot
|
|
||||||
- call sp_head::execute
|
|
||||||
- restore old memroot
|
|
||||||
- restores security context
|
|
||||||
|
|
||||||
SYNOPSIS
|
Execute a trigger:
|
||||||
sp_head::execute_trigger()
|
- changes security context for triggers;
|
||||||
thd Thread handle
|
- switch to new memroot;
|
||||||
db database name
|
- call sp_head::execute;
|
||||||
table table name
|
- restore old memroot;
|
||||||
grant_info GRANT_INFO structure to be filled with
|
- restores security context.
|
||||||
information about definer's privileges
|
|
||||||
on subject table
|
@param thd Thread context.
|
||||||
|
@param db_name Database name.
|
||||||
RETURN
|
@param table_name Table name.
|
||||||
FALSE on success
|
@param grant_info GRANT_INFO structure to be filled with information
|
||||||
TRUE on error
|
about definer's privileges on subject table.
|
||||||
|
|
||||||
|
@return Error status.
|
||||||
|
@retval FALSE on success.
|
||||||
|
@retval TRUE on error.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
bool
|
bool
|
||||||
sp_head::execute_trigger(THD *thd, const char *db, const char *table,
|
sp_head::execute_trigger(THD *thd,
|
||||||
|
const LEX_STRING *db_name,
|
||||||
|
const LEX_STRING *table_name,
|
||||||
GRANT_INFO *grant_info)
|
GRANT_INFO *grant_info)
|
||||||
{
|
{
|
||||||
sp_rcontext *octx = thd->spcont;
|
sp_rcontext *octx = thd->spcont;
|
||||||
@ -1304,6 +1305,46 @@ sp_head::execute_trigger(THD *thd, const char *db, const char *table,
|
|||||||
DBUG_ENTER("sp_head::execute_trigger");
|
DBUG_ENTER("sp_head::execute_trigger");
|
||||||
DBUG_PRINT("info", ("trigger %s", m_name.str));
|
DBUG_PRINT("info", ("trigger %s", m_name.str));
|
||||||
|
|
||||||
|
#ifndef NO_EMBEDDED_ACCESS_CHECKS
|
||||||
|
Security_context *save_ctx= NULL;
|
||||||
|
|
||||||
|
|
||||||
|
if (m_chistics->suid != SP_IS_NOT_SUID &&
|
||||||
|
m_security_ctx.change_security_context(thd,
|
||||||
|
&m_definer_user,
|
||||||
|
&m_definer_host,
|
||||||
|
&m_db,
|
||||||
|
&save_ctx))
|
||||||
|
DBUG_RETURN(TRUE);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Fetch information about table-level privileges for subject table into
|
||||||
|
GRANT_INFO instance. The access check itself will happen in
|
||||||
|
Item_trigger_field, where this information will be used along with
|
||||||
|
information about column-level privileges.
|
||||||
|
*/
|
||||||
|
|
||||||
|
fill_effective_table_privileges(thd,
|
||||||
|
grant_info,
|
||||||
|
db_name->str,
|
||||||
|
table_name->str);
|
||||||
|
|
||||||
|
/* Check that the definer has TRIGGER privilege on the subject table. */
|
||||||
|
|
||||||
|
if (!(grant_info->privilege & TRIGGER_ACL))
|
||||||
|
{
|
||||||
|
char priv_desc[128];
|
||||||
|
get_privilege_desc(priv_desc, sizeof(priv_desc), TRIGGER_ACL);
|
||||||
|
|
||||||
|
my_error(ER_TABLEACCESS_DENIED_ERROR, MYF(0), priv_desc,
|
||||||
|
thd->security_ctx->priv_user, thd->security_ctx->host_or_ip,
|
||||||
|
table_name->str);
|
||||||
|
|
||||||
|
m_security_ctx.restore_security_context(thd, save_ctx);
|
||||||
|
DBUG_RETURN(TRUE);
|
||||||
|
}
|
||||||
|
#endif // NO_EMBEDDED_ACCESS_CHECKS
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Prepare arena and memroot for objects which lifetime is whole
|
Prepare arena and memroot for objects which lifetime is whole
|
||||||
duration of trigger call (sp_rcontext, it's tables and items,
|
duration of trigger call (sp_rcontext, it's tables and items,
|
||||||
@ -1336,6 +1377,11 @@ sp_head::execute_trigger(THD *thd, const char *db, const char *table,
|
|||||||
|
|
||||||
err_with_cleanup:
|
err_with_cleanup:
|
||||||
thd->restore_active_arena(&call_arena, &backup_arena);
|
thd->restore_active_arena(&call_arena, &backup_arena);
|
||||||
|
|
||||||
|
#ifndef NO_EMBEDDED_ACCESS_CHECKS
|
||||||
|
m_security_ctx.restore_security_context(thd, save_ctx);
|
||||||
|
#endif // NO_EMBEDDED_ACCESS_CHECKS
|
||||||
|
|
||||||
delete nctx;
|
delete nctx;
|
||||||
call_arena.free_items();
|
call_arena.free_items();
|
||||||
free_root(&call_mem_root, MYF(0));
|
free_root(&call_mem_root, MYF(0));
|
||||||
|
@ -216,8 +216,10 @@ public:
|
|||||||
destroy();
|
destroy();
|
||||||
|
|
||||||
bool
|
bool
|
||||||
execute_trigger(THD *thd, const char *db, const char *table,
|
execute_trigger(THD *thd,
|
||||||
GRANT_INFO *grant_onfo);
|
const LEX_STRING *db_name,
|
||||||
|
const LEX_STRING *table_name,
|
||||||
|
GRANT_INFO *grant_info);
|
||||||
|
|
||||||
bool
|
bool
|
||||||
execute_function(THD *thd, Item **args, uint argcount, Field *return_fld);
|
execute_function(THD *thd, Item **args, uint argcount, Field *return_fld);
|
||||||
|
@ -538,6 +538,37 @@ bool load_db_opt_by_name(THD *thd, const char *db_name,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
Return default database collation.
|
||||||
|
|
||||||
|
@param thd Thread context.
|
||||||
|
@param db_name Database name.
|
||||||
|
|
||||||
|
@return CHARSET_INFO object. The operation always return valid character
|
||||||
|
set, even if the database does not exist.
|
||||||
|
*/
|
||||||
|
|
||||||
|
CHARSET_INFO *get_default_db_collation(THD *thd, const char *db_name)
|
||||||
|
{
|
||||||
|
HA_CREATE_INFO db_info;
|
||||||
|
|
||||||
|
if (thd->db != NULL && strcmp(db_name, thd->db) == 0)
|
||||||
|
return thd->db_charset;
|
||||||
|
|
||||||
|
load_db_opt_by_name(thd, db_name, &db_info);
|
||||||
|
|
||||||
|
/*
|
||||||
|
NOTE: even if load_db_opt_by_name() fails,
|
||||||
|
db_info.default_table_charset contains valid character set
|
||||||
|
(collation_server). We should not fail if load_db_opt_by_name() fails,
|
||||||
|
because it is valid case. If a database has been created just by
|
||||||
|
"mkdir", it does not contain db.opt file, but it is valid database.
|
||||||
|
*/
|
||||||
|
|
||||||
|
return db_info.default_table_charset;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Create a database
|
Create a database
|
||||||
|
|
||||||
@ -751,10 +782,8 @@ bool mysql_alter_db(THD *thd, const char *db, HA_CREATE_INFO *create_info)
|
|||||||
if ((error=write_db_opt(thd, path, create_info)))
|
if ((error=write_db_opt(thd, path, create_info)))
|
||||||
goto exit;
|
goto exit;
|
||||||
|
|
||||||
/*
|
/* Change options if current database is being altered. */
|
||||||
Change options if current database is being altered
|
|
||||||
TODO: Delete this code
|
|
||||||
*/
|
|
||||||
if (thd->db && !strcmp(thd->db,db))
|
if (thd->db && !strcmp(thd->db,db))
|
||||||
{
|
{
|
||||||
thd->db_charset= create_info->default_table_charset ?
|
thd->db_charset= create_info->default_table_charset ?
|
||||||
@ -1358,6 +1387,7 @@ bool mysql_change_db(THD *thd, const LEX_STRING *new_db_name, bool force_switch)
|
|||||||
|
|
||||||
Security_context *sctx= thd->security_ctx;
|
Security_context *sctx= thd->security_ctx;
|
||||||
ulong db_access= sctx->db_access;
|
ulong db_access= sctx->db_access;
|
||||||
|
CHARSET_INFO *db_default_cl;
|
||||||
|
|
||||||
DBUG_ENTER("mysql_change_db");
|
DBUG_ENTER("mysql_change_db");
|
||||||
DBUG_PRINT("enter",("name: '%s'", new_db_name->str));
|
DBUG_PRINT("enter",("name: '%s'", new_db_name->str));
|
||||||
@ -1487,16 +1517,9 @@ bool mysql_change_db(THD *thd, const LEX_STRING *new_db_name, bool force_switch)
|
|||||||
attributes and will be freed in THD::~THD().
|
attributes and will be freed in THD::~THD().
|
||||||
*/
|
*/
|
||||||
|
|
||||||
{
|
db_default_cl= get_default_db_collation(thd, new_db_file_name.str);
|
||||||
HA_CREATE_INFO db_options;
|
|
||||||
|
|
||||||
load_db_opt_by_name(thd, new_db_name->str, &db_options);
|
mysql_change_db_impl(thd, &new_db_file_name, db_access, db_default_cl);
|
||||||
|
|
||||||
mysql_change_db_impl(thd, &new_db_file_name, db_access,
|
|
||||||
db_options.default_table_charset ?
|
|
||||||
db_options.default_table_charset :
|
|
||||||
thd->variables.collation_server);
|
|
||||||
}
|
|
||||||
|
|
||||||
DBUG_RETURN(FALSE);
|
DBUG_RETURN(FALSE);
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,7 @@
|
|||||||
static const LEX_STRING triggers_file_type=
|
static const LEX_STRING triggers_file_type=
|
||||||
{ C_STRING_WITH_LEN("TRIGGERS") };
|
{ C_STRING_WITH_LEN("TRIGGERS") };
|
||||||
|
|
||||||
const char * const triggers_file_ext= ".TRG";
|
const char * const TRG_EXT= ".TRG";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Table of .TRG file field descriptors.
|
Table of .TRG file field descriptors.
|
||||||
@ -79,7 +79,7 @@ struct st_trigname
|
|||||||
static const LEX_STRING trigname_file_type=
|
static const LEX_STRING trigname_file_type=
|
||||||
{ C_STRING_WITH_LEN("TRIGGERNAME") };
|
{ C_STRING_WITH_LEN("TRIGGERNAME") };
|
||||||
|
|
||||||
const char * const trigname_file_ext= ".TRN";
|
const char * const TRN_EXT= ".TRN";
|
||||||
|
|
||||||
static File_option trigname_file_parameters[]=
|
static File_option trigname_file_parameters[]=
|
||||||
{
|
{
|
||||||
@ -132,6 +132,7 @@ private:
|
|||||||
LEX_STRING *trigger_table_value;
|
LEX_STRING *trigger_table_value;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Create or drop trigger for table.
|
Create or drop trigger for table.
|
||||||
|
|
||||||
@ -463,14 +464,14 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables,
|
|||||||
sql_create_definition_file() files handles renaming and backup of older
|
sql_create_definition_file() files handles renaming and backup of older
|
||||||
versions
|
versions
|
||||||
*/
|
*/
|
||||||
file.length= build_table_filename(file_buff, FN_REFLEN-1,
|
file.length= build_table_filename(file_buff, FN_REFLEN - 1,
|
||||||
tables->db, tables->table_name,
|
tables->db, tables->table_name,
|
||||||
triggers_file_ext, 0);
|
TRG_EXT, 0);
|
||||||
file.str= file_buff;
|
file.str= file_buff;
|
||||||
trigname_file.length= build_table_filename(trigname_buff, FN_REFLEN-1,
|
trigname_file.length= build_table_filename(trigname_buff, FN_REFLEN-1,
|
||||||
tables->db,
|
tables->db,
|
||||||
lex->spname->m_name.str,
|
lex->spname->m_name.str,
|
||||||
trigname_file_ext, 0);
|
TRN_EXT, 0);
|
||||||
trigname_file.str= trigname_buff;
|
trigname_file.str= trigname_buff;
|
||||||
|
|
||||||
/* Use the filesystem to enforce trigger namespace constraints. */
|
/* Use the filesystem to enforce trigger namespace constraints. */
|
||||||
@ -604,7 +605,7 @@ err_with_cleanup:
|
|||||||
static bool rm_trigger_file(char *path, const char *db,
|
static bool rm_trigger_file(char *path, const char *db,
|
||||||
const char *table_name)
|
const char *table_name)
|
||||||
{
|
{
|
||||||
build_table_filename(path, FN_REFLEN-1, db, table_name, triggers_file_ext, 0);
|
build_table_filename(path, FN_REFLEN-1, db, table_name, TRG_EXT, 0);
|
||||||
return my_delete(path, MYF(MY_WME));
|
return my_delete(path, MYF(MY_WME));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -627,8 +628,7 @@ static bool rm_trigger_file(char *path, const char *db,
|
|||||||
static bool rm_trigname_file(char *path, const char *db,
|
static bool rm_trigname_file(char *path, const char *db,
|
||||||
const char *trigger_name)
|
const char *trigger_name)
|
||||||
{
|
{
|
||||||
build_table_filename(path, FN_REFLEN-1,
|
build_table_filename(path, FN_REFLEN - 1, db, trigger_name, TRN_EXT, 0);
|
||||||
db, trigger_name, trigname_file_ext, 0);
|
|
||||||
return my_delete(path, MYF(MY_WME));
|
return my_delete(path, MYF(MY_WME));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -653,8 +653,8 @@ static bool save_trigger_file(Table_triggers_list *triggers, const char *db,
|
|||||||
char file_buff[FN_REFLEN];
|
char file_buff[FN_REFLEN];
|
||||||
LEX_STRING file;
|
LEX_STRING file;
|
||||||
|
|
||||||
file.length= build_table_filename(file_buff, FN_REFLEN-1, db, table_name,
|
file.length= build_table_filename(file_buff, FN_REFLEN - 1, db, table_name,
|
||||||
triggers_file_ext, 0);
|
TRG_EXT, 0);
|
||||||
file.str= file_buff;
|
file.str= file_buff;
|
||||||
return sql_create_definition_file(NULL, &file, &triggers_file_type,
|
return sql_create_definition_file(NULL, &file, &triggers_file_type,
|
||||||
(uchar*)triggers, triggers_file_parameters,
|
(uchar*)triggers, triggers_file_parameters,
|
||||||
@ -834,8 +834,8 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db,
|
|||||||
|
|
||||||
DBUG_ENTER("Table_triggers_list::check_n_load");
|
DBUG_ENTER("Table_triggers_list::check_n_load");
|
||||||
|
|
||||||
path.length= build_table_filename(path_buff, FN_REFLEN-1,
|
path.length= build_table_filename(path_buff, FN_REFLEN - 1,
|
||||||
db, table_name, triggers_file_ext, 0);
|
db, table_name, TRG_EXT, 0);
|
||||||
path.str= path_buff;
|
path.str= path_buff;
|
||||||
|
|
||||||
// QQ: should we analyze errno somehow ?
|
// QQ: should we analyze errno somehow ?
|
||||||
@ -1106,7 +1106,7 @@ err_with_lex_cleanup:
|
|||||||
be merged into .FRM anyway.
|
be merged into .FRM anyway.
|
||||||
*/
|
*/
|
||||||
my_error(ER_WRONG_OBJECT, MYF(0),
|
my_error(ER_WRONG_OBJECT, MYF(0),
|
||||||
table_name, triggers_file_ext+1, "TRIGGER");
|
table_name, TRG_EXT + 1, "TRIGGER");
|
||||||
DBUG_RETURN(1);
|
DBUG_RETURN(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1166,83 +1166,66 @@ bool Table_triggers_list::get_trigger_info(THD *thd, trg_event_type event,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/**
|
||||||
Find trigger's table from trigger identifier and add it to
|
Find trigger's table from trigger identifier and add it to
|
||||||
the statement table list.
|
the statement table list.
|
||||||
|
|
||||||
SYNOPSIS
|
@param[in] thd Thread context.
|
||||||
mysql_table_for_trigger()
|
@param[in] trg_name Trigger name.
|
||||||
thd - current thread context
|
@param[in] if_exists TRUE if SQL statement contains "IF EXISTS" clause.
|
||||||
trig - identifier for trigger
|
That means a warning instead of error should be
|
||||||
if_exists - treat a not existing trigger as a warning if TRUE
|
thrown if trigger with given name does not exist.
|
||||||
table - pointer to TABLE_LIST object for the table trigger (output)
|
@param[out] table Pointer to TABLE_LIST object for the
|
||||||
|
table trigger.
|
||||||
|
|
||||||
RETURN VALUE
|
@return Operation status
|
||||||
0 Success
|
@retval FALSE On success.
|
||||||
1 Error
|
@retval TRUE Otherwise.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
int
|
bool add_table_for_trigger(THD *thd,
|
||||||
add_table_for_trigger(THD *thd, sp_name *trig, bool if_exists,
|
sp_name *trg_name,
|
||||||
TABLE_LIST **table)
|
bool if_exists,
|
||||||
|
TABLE_LIST **table)
|
||||||
{
|
{
|
||||||
LEX *lex= thd->lex;
|
LEX *lex= thd->lex;
|
||||||
char path_buff[FN_REFLEN];
|
char trn_path_buff[FN_REFLEN];
|
||||||
LEX_STRING path;
|
LEX_STRING trn_path= { trn_path_buff, 0 };
|
||||||
File_parser *parser;
|
LEX_STRING tbl_name;
|
||||||
struct st_trigname trigname;
|
|
||||||
Handle_old_incorrect_trigger_table_hook trigger_table_hook(
|
|
||||||
path_buff, &trigname.trigger_table);
|
|
||||||
|
|
||||||
DBUG_ENTER("add_table_for_trigger");
|
DBUG_ENTER("add_table_for_trigger");
|
||||||
DBUG_ASSERT(table != NULL);
|
|
||||||
|
|
||||||
path.length= build_table_filename(path_buff, FN_REFLEN-1,
|
build_trn_path(thd, trg_name, &trn_path);
|
||||||
trig->m_db.str, trig->m_name.str,
|
|
||||||
trigname_file_ext, 0);
|
|
||||||
path.str= path_buff;
|
|
||||||
|
|
||||||
if (access(path_buff, F_OK))
|
if (check_trn_exists(&trn_path))
|
||||||
{
|
{
|
||||||
if (if_exists)
|
if (if_exists)
|
||||||
{
|
{
|
||||||
push_warning_printf(thd,
|
push_warning_printf(thd,
|
||||||
MYSQL_ERROR::WARN_LEVEL_NOTE,
|
MYSQL_ERROR::WARN_LEVEL_NOTE,
|
||||||
ER_TRG_DOES_NOT_EXIST,
|
ER_TRG_DOES_NOT_EXIST,
|
||||||
ER(ER_TRG_DOES_NOT_EXIST));
|
ER(ER_TRG_DOES_NOT_EXIST));
|
||||||
|
|
||||||
*table= NULL;
|
*table= NULL;
|
||||||
DBUG_RETURN(0);
|
|
||||||
|
DBUG_RETURN(FALSE);
|
||||||
}
|
}
|
||||||
|
|
||||||
my_error(ER_TRG_DOES_NOT_EXIST, MYF(0));
|
my_error(ER_TRG_DOES_NOT_EXIST, MYF(0));
|
||||||
DBUG_RETURN(1);
|
DBUG_RETURN(TRUE);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(parser= sql_parse_prepare(&path, thd->mem_root, 1)))
|
if (load_table_name_for_trigger(thd, trg_name, &trn_path, &tbl_name))
|
||||||
DBUG_RETURN(1);
|
DBUG_RETURN(TRUE);
|
||||||
|
|
||||||
if (!is_equal(&trigname_file_type, parser->type()))
|
|
||||||
{
|
|
||||||
my_error(ER_WRONG_OBJECT, MYF(0), trig->m_name.str, trigname_file_ext+1,
|
|
||||||
"TRIGGERNAME");
|
|
||||||
DBUG_RETURN(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (parser->parse((uchar*)&trigname, thd->mem_root,
|
|
||||||
trigname_file_parameters, 1,
|
|
||||||
&trigger_table_hook))
|
|
||||||
DBUG_RETURN(1);
|
|
||||||
|
|
||||||
/* We need to reset statement table list to be PS/SP friendly. */
|
/* We need to reset statement table list to be PS/SP friendly. */
|
||||||
lex->query_tables= 0;
|
lex->query_tables= 0;
|
||||||
lex->query_tables_last= &lex->query_tables;
|
lex->query_tables_last= &lex->query_tables;
|
||||||
*table= sp_add_to_query_tables(thd, lex, trig->m_db.str,
|
|
||||||
trigname.trigger_table.str, TL_IGNORE);
|
|
||||||
|
|
||||||
if (! *table)
|
*table= sp_add_to_query_tables(thd, lex, trg_name->m_db.str,
|
||||||
DBUG_RETURN(1);
|
tbl_name.str, TL_IGNORE);
|
||||||
|
|
||||||
DBUG_RETURN(0);
|
DBUG_RETURN(*table ? FALSE : TRUE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1426,7 +1409,7 @@ Table_triggers_list::change_table_name_in_trignames(const char *db_name,
|
|||||||
{
|
{
|
||||||
trigname_file.length= build_table_filename(trigname_buff, FN_REFLEN-1,
|
trigname_file.length= build_table_filename(trigname_buff, FN_REFLEN-1,
|
||||||
db_name, trigger->str,
|
db_name, trigger->str,
|
||||||
trigname_file_ext, 0);
|
TRN_EXT, 0);
|
||||||
trigname_file.str= trigname_buff;
|
trigname_file.str= trigname_buff;
|
||||||
|
|
||||||
trigname.trigger_table= *new_table_name;
|
trigname.trigger_table= *new_table_name;
|
||||||
@ -1535,77 +1518,54 @@ end:
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool Table_triggers_list::process_triggers(THD *thd, trg_event_type event,
|
/**
|
||||||
|
Execute trigger for given (event, time) pair.
|
||||||
|
|
||||||
|
The operation executes trigger for the specified event (insert, update,
|
||||||
|
delete) and time (after, before) if it is set.
|
||||||
|
|
||||||
|
@param thd
|
||||||
|
@param event
|
||||||
|
@param time_type,
|
||||||
|
@param old_row_is_record1
|
||||||
|
|
||||||
|
@return Error status.
|
||||||
|
@retval FALSE on success.
|
||||||
|
@retval TRUE on error.
|
||||||
|
*/
|
||||||
|
|
||||||
|
bool Table_triggers_list::process_triggers(THD *thd,
|
||||||
|
trg_event_type event,
|
||||||
trg_action_time_type time_type,
|
trg_action_time_type time_type,
|
||||||
bool old_row_is_record1)
|
bool old_row_is_record1)
|
||||||
{
|
{
|
||||||
bool err_status= FALSE;
|
bool err_status;
|
||||||
sp_head *sp_trigger= bodies[event][time_type];
|
Sub_statement_state statement_state;
|
||||||
|
|
||||||
if (sp_trigger)
|
if (!bodies[event][time_type])
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
if (old_row_is_record1)
|
||||||
{
|
{
|
||||||
Sub_statement_state statement_state;
|
old_field= record1_field;
|
||||||
|
new_field= trigger_table->field;
|
||||||
if (old_row_is_record1)
|
|
||||||
{
|
|
||||||
old_field= record1_field;
|
|
||||||
new_field= trigger_table->field;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
new_field= record1_field;
|
|
||||||
old_field= trigger_table->field;
|
|
||||||
}
|
|
||||||
#ifndef NO_EMBEDDED_ACCESS_CHECKS
|
|
||||||
Security_context *sctx= &sp_trigger->m_security_ctx;
|
|
||||||
Security_context *save_ctx= NULL;
|
|
||||||
|
|
||||||
|
|
||||||
if (sp_trigger->m_chistics->suid != SP_IS_NOT_SUID &&
|
|
||||||
sctx->change_security_context(thd,
|
|
||||||
&sp_trigger->m_definer_user,
|
|
||||||
&sp_trigger->m_definer_host,
|
|
||||||
&sp_trigger->m_db,
|
|
||||||
&save_ctx))
|
|
||||||
return TRUE;
|
|
||||||
|
|
||||||
/*
|
|
||||||
Fetch information about table-level privileges to GRANT_INFO structure for
|
|
||||||
subject table. Check of privileges that will use it and information about
|
|
||||||
column-level privileges will happen in Item_trigger_field::fix_fields().
|
|
||||||
*/
|
|
||||||
|
|
||||||
fill_effective_table_privileges(thd,
|
|
||||||
&subject_table_grants[event][time_type],
|
|
||||||
trigger_table->s->db.str,
|
|
||||||
trigger_table->s->table_name.str);
|
|
||||||
|
|
||||||
/* Check that the definer has TRIGGER privilege on the subject table. */
|
|
||||||
|
|
||||||
if (!(subject_table_grants[event][time_type].privilege & TRIGGER_ACL))
|
|
||||||
{
|
|
||||||
char priv_desc[128];
|
|
||||||
get_privilege_desc(priv_desc, sizeof(priv_desc), TRIGGER_ACL);
|
|
||||||
|
|
||||||
my_error(ER_TABLEACCESS_DENIED_ERROR, MYF(0), priv_desc,
|
|
||||||
thd->security_ctx->priv_user, thd->security_ctx->host_or_ip,
|
|
||||||
trigger_table->s->table_name.str);
|
|
||||||
|
|
||||||
sctx->restore_security_context(thd, save_ctx);
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
#endif // NO_EMBEDDED_ACCESS_CHECKS
|
|
||||||
|
|
||||||
thd->reset_sub_statement_state(&statement_state, SUB_STMT_TRIGGER);
|
|
||||||
err_status= sp_trigger->execute_trigger
|
|
||||||
(thd, trigger_table->s->db.str, trigger_table->s->table_name.str,
|
|
||||||
&subject_table_grants[event][time_type]);
|
|
||||||
thd->restore_sub_statement_state(&statement_state);
|
|
||||||
|
|
||||||
#ifndef NO_EMBEDDED_ACCESS_CHECKS
|
|
||||||
sctx->restore_security_context(thd, save_ctx);
|
|
||||||
#endif // NO_EMBEDDED_ACCESS_CHECKS
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
new_field= record1_field;
|
||||||
|
old_field= trigger_table->field;
|
||||||
|
}
|
||||||
|
|
||||||
|
thd->reset_sub_statement_state(&statement_state, SUB_STMT_TRIGGER);
|
||||||
|
|
||||||
|
err_status=
|
||||||
|
bodies[event][time_type]->execute_trigger(
|
||||||
|
thd,
|
||||||
|
&trigger_table->s->db,
|
||||||
|
&trigger_table->s->table_name,
|
||||||
|
&subject_table_grants[event][time_type]);
|
||||||
|
|
||||||
|
thd->restore_sub_statement_state(&statement_state);
|
||||||
|
|
||||||
return err_status;
|
return err_status;
|
||||||
}
|
}
|
||||||
@ -1747,3 +1707,95 @@ process_unknown_string(char *&unknown_key, uchar* base, MEM_ROOT *mem_root,
|
|||||||
}
|
}
|
||||||
DBUG_RETURN(FALSE);
|
DBUG_RETURN(FALSE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
Contruct path to TRN-file.
|
||||||
|
|
||||||
|
@param thd[in] Thread context.
|
||||||
|
@param trg_name[in] Trigger name.
|
||||||
|
@param trn_path[out] Variable to store constructed path
|
||||||
|
*/
|
||||||
|
|
||||||
|
void build_trn_path(THD *thd, const sp_name *trg_name, LEX_STRING *trn_path)
|
||||||
|
{
|
||||||
|
/* Construct path to the TRN-file. */
|
||||||
|
|
||||||
|
trn_path->length= build_table_filename(trn_path->str,
|
||||||
|
FN_REFLEN - 1,
|
||||||
|
trg_name->m_db.str,
|
||||||
|
trg_name->m_name.str,
|
||||||
|
TRN_EXT,
|
||||||
|
0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
Check if TRN-file exists.
|
||||||
|
|
||||||
|
@return
|
||||||
|
@retval TRUE if TRN-file does not exist.
|
||||||
|
@retval FALSE if TRN-file exists.
|
||||||
|
*/
|
||||||
|
|
||||||
|
bool check_trn_exists(const LEX_STRING *trn_path)
|
||||||
|
{
|
||||||
|
return access(trn_path->str, F_OK) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
Retrieve table name for given trigger.
|
||||||
|
|
||||||
|
@param thd[in] Thread context.
|
||||||
|
@param trg_name[in] Trigger name.
|
||||||
|
@param trn_path[in] Path to the corresponding TRN-file.
|
||||||
|
@param tbl_name[out] Variable to store retrieved table name.
|
||||||
|
|
||||||
|
@return Error status.
|
||||||
|
@retval FALSE on success.
|
||||||
|
@retval TRUE if table name could not be retrieved.
|
||||||
|
*/
|
||||||
|
|
||||||
|
bool load_table_name_for_trigger(THD *thd,
|
||||||
|
const sp_name *trg_name,
|
||||||
|
const LEX_STRING *trn_path,
|
||||||
|
LEX_STRING *tbl_name)
|
||||||
|
{
|
||||||
|
File_parser *parser;
|
||||||
|
struct st_trigname trn_data;
|
||||||
|
|
||||||
|
Handle_old_incorrect_trigger_table_hook trigger_table_hook(
|
||||||
|
trn_path->str,
|
||||||
|
&trn_data.trigger_table);
|
||||||
|
|
||||||
|
DBUG_ENTER("load_table_name_for_trigger");
|
||||||
|
|
||||||
|
/* Parse the TRN-file. */
|
||||||
|
|
||||||
|
if (!(parser= sql_parse_prepare(trn_path, thd->mem_root, TRUE)))
|
||||||
|
DBUG_RETURN(TRUE);
|
||||||
|
|
||||||
|
if (!is_equal(&trigname_file_type, parser->type()))
|
||||||
|
{
|
||||||
|
my_error(ER_WRONG_OBJECT, MYF(0),
|
||||||
|
trg_name->m_name.str,
|
||||||
|
TRN_EXT + 1,
|
||||||
|
"TRIGGERNAME");
|
||||||
|
|
||||||
|
DBUG_RETURN(TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parser->parse((uchar*) &trn_data, thd->mem_root,
|
||||||
|
trigname_file_parameters, 1,
|
||||||
|
&trigger_table_hook))
|
||||||
|
DBUG_RETURN(TRUE);
|
||||||
|
|
||||||
|
/* Copy trigger table name. */
|
||||||
|
|
||||||
|
*tbl_name= trn_data.trigger_table;
|
||||||
|
|
||||||
|
/* That's all. */
|
||||||
|
|
||||||
|
DBUG_RETURN(FALSE);
|
||||||
|
}
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
/*
|
/*
|
||||||
This class holds all information about triggers of table.
|
This class holds all information about triggers of table.
|
||||||
|
|
||||||
QQ: Will it be merged into TABLE in future ?
|
QQ: Will it be merged into TABLE in the future ?
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class Table_triggers_list: public Sql_alloc
|
class Table_triggers_list: public Sql_alloc
|
||||||
@ -143,6 +143,17 @@ private:
|
|||||||
extern const LEX_STRING trg_action_time_type_names[];
|
extern const LEX_STRING trg_action_time_type_names[];
|
||||||
extern const LEX_STRING trg_event_type_names[];
|
extern const LEX_STRING trg_event_type_names[];
|
||||||
|
|
||||||
int
|
bool add_table_for_trigger(THD *thd,
|
||||||
add_table_for_trigger(THD *thd, sp_name *trig, bool if_exists,
|
sp_name *trg_name,
|
||||||
TABLE_LIST **table);
|
bool continue_if_not_exist,
|
||||||
|
TABLE_LIST **table);
|
||||||
|
|
||||||
|
void build_trn_path(THD *thd, const sp_name *trg_name, LEX_STRING *trn_path);
|
||||||
|
|
||||||
|
bool check_trn_exists(const LEX_STRING *trn_path);
|
||||||
|
|
||||||
|
bool load_table_name_for_trigger(THD *thd,
|
||||||
|
const sp_name *trg_name,
|
||||||
|
const LEX_STRING *trn_path,
|
||||||
|
LEX_STRING *tbl_name);
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user