Fix for BUG#16266: Definer is not fully qualified error during replication.
The idea of the fix is to extend support of non-SUID triggers for backward compatibility. Formerly non-SUID triggers were appeared when "new" server is being started against "old" database. Now, they are also created when "new" slave receives updates from "old" master. mysql-test/r/rpl_trigger.result: Updated the result file with the results of the test for BUG#16266. mysql-test/t/rpl_trigger.test: Added the test case for BUG#16266. sql/mysql_priv.h: Added an utility operation to be used from sql_yacc.yy. sql/sql_parse.cc: Add a utility operation to be used from sql_yacc.yy. sql/sql_trigger.cc: Extend support of non-SUID triggers. sql/sql_view.cc: Initialize LEX::definer if DEFINER-clause is missing. sql/sql_yacc.yy: Extended support of non-SUID triggers. mysql-test/std_data/bug16266.000001: A new binlog file for testing a patch for BUG#16266.
This commit is contained in:
parent
2efabfd11a
commit
a44a924a40
@ -855,3 +855,44 @@ f3
|
|||||||
drop trigger trg11;
|
drop trigger trg11;
|
||||||
drop table t21,t31;
|
drop table t21,t31;
|
||||||
drop table t11;
|
drop table t11;
|
||||||
|
STOP SLAVE;
|
||||||
|
FLUSH LOGS;
|
||||||
|
RESET SLAVE;
|
||||||
|
START SLAVE;
|
||||||
|
SELECT MASTER_POS_WAIT('master-bin.000001', 513) >= 0;
|
||||||
|
MASTER_POS_WAIT('master-bin.000001', 513) >= 0
|
||||||
|
1
|
||||||
|
SHOW TABLES;
|
||||||
|
Tables_in_test
|
||||||
|
t1
|
||||||
|
t2
|
||||||
|
SHOW TRIGGERS;
|
||||||
|
Trigger Event Table Statement Timing Created sql_mode Definer
|
||||||
|
trg1 INSERT t1 INSERT INTO t2 VALUES(CURRENT_USER()) AFTER NULL
|
||||||
|
SELECT * FROM t1;
|
||||||
|
c
|
||||||
|
1
|
||||||
|
SELECT * FROM t2;
|
||||||
|
s
|
||||||
|
@
|
||||||
|
INSERT INTO t1 VALUES(2);
|
||||||
|
SELECT * FROM t1;
|
||||||
|
c
|
||||||
|
1
|
||||||
|
2
|
||||||
|
SELECT * FROM t2;
|
||||||
|
s
|
||||||
|
@
|
||||||
|
root@localhost
|
||||||
|
DROP TRIGGER trg1;
|
||||||
|
Warnings:
|
||||||
|
Warning 1454 No definer attribute for trigger 'test'.'trg1'. The trigger will be activated under the authorization of the invoker, which may have insufficient privileges. Please recreate the trigger.
|
||||||
|
DROP TABLE t1;
|
||||||
|
DROP TABLE t2;
|
||||||
|
STOP SLAVE;
|
||||||
|
RESET SLAVE;
|
||||||
|
SHOW TABLES;
|
||||||
|
Tables_in_test
|
||||||
|
SHOW TRIGGERS;
|
||||||
|
Trigger Event Table Statement Timing Created sql_mode Definer
|
||||||
|
RESET MASTER;
|
||||||
|
BIN
mysql-test/std_data/bug16266.000001
Normal file
BIN
mysql-test/std_data/bug16266.000001
Normal file
Binary file not shown.
@ -162,6 +162,7 @@ use test;
|
|||||||
drop table t1,t2;
|
drop table t1,t2;
|
||||||
drop database other;
|
drop database other;
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Test specific triggers including SELECT into var with replication
|
# Test specific triggers including SELECT into var with replication
|
||||||
# BUG#13227:
|
# BUG#13227:
|
||||||
@ -257,6 +258,79 @@ while ($rnd)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# BUG#16266: Definer is not fully qualified error during replication.
|
||||||
|
#
|
||||||
|
# The idea of this test is to emulate replication of a trigger from the old
|
||||||
|
# master (master w/o "DEFINER in triggers" support) to the new slave and check
|
||||||
|
# that:
|
||||||
|
# 1. the trigger on the slave will be replicated w/o errors;
|
||||||
|
# 2. the trigger on the slave will be non-SUID (will have no DEFINER);
|
||||||
|
# 3. the trigger can be activated later on the slave w/o errors.
|
||||||
|
#
|
||||||
|
# In order to emulate this kind of replication, we make the slave playing the binlog,
|
||||||
|
# recorded by 5.0.16 master. This binlog contains the following statements:
|
||||||
|
# CREATE TABLE t1(c INT);
|
||||||
|
# CREATE TABLE t2(s CHAR(200));
|
||||||
|
# CREATE TRIGGER trg1 AFTER INSERT ON t1
|
||||||
|
# FOR EACH ROW
|
||||||
|
# INSERT INTO t2 VALUES(CURRENT_USER());
|
||||||
|
# INSERT INTO t1 VALUES(1);
|
||||||
|
#
|
||||||
|
|
||||||
|
# 1. Check that the trigger's replication is succeeded.
|
||||||
|
|
||||||
|
# Stop the slave.
|
||||||
|
|
||||||
|
connection slave;
|
||||||
|
STOP SLAVE;
|
||||||
|
|
||||||
|
# Replace master's binlog.
|
||||||
|
|
||||||
|
connection master;
|
||||||
|
FLUSH LOGS;
|
||||||
|
exec cp $MYSQL_TEST_DIR/std_data/bug16266.000001 $MYSQLTEST_VARDIR/log/master-bin.000001;
|
||||||
|
|
||||||
|
# Make the slave to replay the new binlog.
|
||||||
|
|
||||||
|
connection slave;
|
||||||
|
RESET SLAVE;
|
||||||
|
START SLAVE;
|
||||||
|
|
||||||
|
SELECT MASTER_POS_WAIT('master-bin.000001', 513) >= 0;
|
||||||
|
|
||||||
|
# Check that the replication succeeded.
|
||||||
|
|
||||||
|
SHOW TABLES;
|
||||||
|
SHOW TRIGGERS;
|
||||||
|
SELECT * FROM t1;
|
||||||
|
SELECT * FROM t2;
|
||||||
|
|
||||||
|
# 2. Check that the trigger is non-SUID on the slave;
|
||||||
|
# 3. Check that the trigger can be activated on the slave.
|
||||||
|
|
||||||
|
INSERT INTO t1 VALUES(2);
|
||||||
|
|
||||||
|
SELECT * FROM t1;
|
||||||
|
SELECT * FROM t2;
|
||||||
|
|
||||||
|
# That's all, cleanup.
|
||||||
|
|
||||||
|
DROP TRIGGER trg1;
|
||||||
|
DROP TABLE t1;
|
||||||
|
DROP TABLE t2;
|
||||||
|
|
||||||
|
STOP SLAVE;
|
||||||
|
RESET SLAVE;
|
||||||
|
|
||||||
|
# The master should be clean.
|
||||||
|
|
||||||
|
connection master;
|
||||||
|
SHOW TABLES;
|
||||||
|
SHOW TRIGGERS;
|
||||||
|
|
||||||
|
RESET MASTER;
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# End of tests
|
# End of tests
|
||||||
|
@ -531,6 +531,7 @@ bool create_table_precheck(THD *thd, TABLE_LIST *tables,
|
|||||||
TABLE_LIST *create_table);
|
TABLE_LIST *create_table);
|
||||||
|
|
||||||
bool get_default_definer(THD *thd, LEX_USER *definer);
|
bool get_default_definer(THD *thd, LEX_USER *definer);
|
||||||
|
LEX_USER *create_default_definer(THD *thd);
|
||||||
LEX_USER *create_definer(THD *thd, LEX_STRING *user_name, LEX_STRING *host_name);
|
LEX_USER *create_definer(THD *thd, LEX_STRING *user_name, LEX_STRING *host_name);
|
||||||
|
|
||||||
enum enum_mysql_completiontype {
|
enum enum_mysql_completiontype {
|
||||||
|
@ -7206,6 +7206,34 @@ bool get_default_definer(THD *thd, LEX_USER *definer)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Create default definer for the specified THD. Also check that the current
|
||||||
|
user is conformed to the definers requirements.
|
||||||
|
|
||||||
|
SYNOPSIS
|
||||||
|
create_default_definer()
|
||||||
|
thd [in] thread handler
|
||||||
|
|
||||||
|
RETURN
|
||||||
|
On success, return a valid pointer to the created and initialized
|
||||||
|
LEX_USER, which contains definer information.
|
||||||
|
On error, return 0.
|
||||||
|
*/
|
||||||
|
|
||||||
|
LEX_USER *create_default_definer(THD *thd)
|
||||||
|
{
|
||||||
|
LEX_USER *definer;
|
||||||
|
|
||||||
|
if (! (definer= (LEX_USER*) thd->alloc(sizeof(LEX_USER))))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (get_default_definer(thd, definer))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return definer;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Create definer with the given user and host names. Also check that the user
|
Create definer with the given user and host names. Also check that the user
|
||||||
and host names satisfy definers requirements.
|
and host names satisfy definers requirements.
|
||||||
@ -7218,7 +7246,7 @@ bool get_default_definer(THD *thd, LEX_USER *definer)
|
|||||||
|
|
||||||
RETURN
|
RETURN
|
||||||
On success, return a valid pointer to the created and initialized
|
On success, return a valid pointer to the created and initialized
|
||||||
LEX_STRING, which contains definer information.
|
LEX_USER, which contains definer information.
|
||||||
On error, return 0.
|
On error, return 0.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -264,7 +264,17 @@ end:
|
|||||||
log_query.set((char *) 0, 0, system_charset_info); /* reset log_query */
|
log_query.set((char *) 0, 0, system_charset_info); /* reset log_query */
|
||||||
|
|
||||||
log_query.append(STRING_WITH_LEN("CREATE "));
|
log_query.append(STRING_WITH_LEN("CREATE "));
|
||||||
append_definer(thd, &log_query, &definer_user, &definer_host);
|
|
||||||
|
if (definer_user.str && definer_host.str)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
Append definer-clause if the trigger is SUID (a usual trigger in
|
||||||
|
new MySQL versions).
|
||||||
|
*/
|
||||||
|
|
||||||
|
append_definer(thd, &log_query, &definer_user, &definer_host);
|
||||||
|
}
|
||||||
|
|
||||||
log_query.append(thd->lex->trigger_definition_begin);
|
log_query.append(thd->lex->trigger_definition_begin);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -289,17 +299,30 @@ end:
|
|||||||
LEX)
|
LEX)
|
||||||
tables - table list containing one open table for which the
|
tables - table list containing one open table for which the
|
||||||
trigger is created.
|
trigger is created.
|
||||||
definer_user - [out] after a call it points to 0-terminated string,
|
definer_user - [out] after a call it points to 0-terminated string or
|
||||||
which contains user name part of the actual trigger
|
contains the NULL-string:
|
||||||
definer. The caller is responsible to provide memory for
|
- 0-terminated is returned if the trigger is SUID. The
|
||||||
|
string contains user name part of the actual trigger
|
||||||
|
definer.
|
||||||
|
- NULL-string is returned if the trigger is non-SUID.
|
||||||
|
Anyway, the caller is responsible to provide memory for
|
||||||
storing LEX_STRING object.
|
storing LEX_STRING object.
|
||||||
definer_host - [out] after a call it points to 0-terminated string,
|
definer_host - [out] after a call it points to 0-terminated string or
|
||||||
which contains host name part of the actual trigger
|
contains the NULL-string:
|
||||||
definer. The caller is responsible to provide memory for
|
- 0-terminated string is returned if the trigger is
|
||||||
|
SUID. The string contains host name part of the
|
||||||
|
actual trigger definer.
|
||||||
|
- NULL-string is returned if the trigger is non-SUID.
|
||||||
|
Anyway, the caller is responsible to provide memory for
|
||||||
storing LEX_STRING object.
|
storing LEX_STRING object.
|
||||||
|
|
||||||
NOTE
|
NOTE
|
||||||
Assumes that trigger name is fully qualified.
|
- Assumes that trigger name is fully qualified.
|
||||||
|
- NULL-string means the following LEX_STRING instance:
|
||||||
|
{ str = 0; length = 0 }.
|
||||||
|
- In other words, definer_user and definer_host should contain
|
||||||
|
simultaneously NULL-strings (non-SUID/old trigger) or valid strings
|
||||||
|
(SUID/new trigger).
|
||||||
|
|
||||||
RETURN VALUE
|
RETURN VALUE
|
||||||
False - success
|
False - success
|
||||||
@ -336,12 +359,30 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables,
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
if (!lex->definer)
|
||||||
Definer attribute of the Lex instance is always set in sql_yacc.yy when
|
{
|
||||||
trigger is created.
|
/*
|
||||||
*/
|
DEFINER-clause is missing.
|
||||||
|
|
||||||
DBUG_ASSERT(lex->definer);
|
If we are in slave thread, this means that we received CREATE TRIGGER
|
||||||
|
from the master, that does not support definer in triggers. So, we
|
||||||
|
should mark this trigger as non-SUID. Note that this does not happen
|
||||||
|
when we parse triggers' definitions during opening .TRG file.
|
||||||
|
LEX::definer is ignored in that case.
|
||||||
|
|
||||||
|
Otherwise, we should use CURRENT_USER() as definer.
|
||||||
|
|
||||||
|
NOTE: when CREATE TRIGGER statement is allowed to be executed in PS/SP,
|
||||||
|
it will be required to create the definer below in persistent MEM_ROOT
|
||||||
|
of PS/SP.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (!thd->slave_thread)
|
||||||
|
{
|
||||||
|
if (!(lex->definer= create_default_definer(thd)))
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
If the specified definer differs from the current user, we should check
|
If the specified definer differs from the current user, we should check
|
||||||
@ -349,10 +390,11 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables,
|
|||||||
under another user one must have SUPER privilege).
|
under another user one must have SUPER privilege).
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if (strcmp(lex->definer->user.str, thd->security_ctx->priv_user) ||
|
if (lex->definer &&
|
||||||
my_strcasecmp(system_charset_info,
|
(strcmp(lex->definer->user.str, thd->security_ctx->priv_user) ||
|
||||||
lex->definer->host.str,
|
my_strcasecmp(system_charset_info,
|
||||||
thd->security_ctx->priv_host))
|
lex->definer->host.str,
|
||||||
|
thd->security_ctx->priv_host)))
|
||||||
{
|
{
|
||||||
if (check_global_access(thd, SUPER_ACL))
|
if (check_global_access(thd, SUPER_ACL))
|
||||||
{
|
{
|
||||||
@ -446,8 +488,8 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables,
|
|||||||
*trg_sql_mode= thd->variables.sql_mode;
|
*trg_sql_mode= thd->variables.sql_mode;
|
||||||
|
|
||||||
#ifndef NO_EMBEDDED_ACCESS_CHECKS
|
#ifndef NO_EMBEDDED_ACCESS_CHECKS
|
||||||
if (!is_acl_user(lex->definer->host.str,
|
if (lex->definer && !is_acl_user(lex->definer->host.str,
|
||||||
lex->definer->user.str))
|
lex->definer->user.str))
|
||||||
{
|
{
|
||||||
push_warning_printf(thd,
|
push_warning_printf(thd,
|
||||||
MYSQL_ERROR::WARN_LEVEL_NOTE,
|
MYSQL_ERROR::WARN_LEVEL_NOTE,
|
||||||
@ -458,12 +500,30 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables,
|
|||||||
}
|
}
|
||||||
#endif /* NO_EMBEDDED_ACCESS_CHECKS */
|
#endif /* NO_EMBEDDED_ACCESS_CHECKS */
|
||||||
|
|
||||||
*definer_user= lex->definer->user;
|
if (lex->definer)
|
||||||
*definer_host= lex->definer->host;
|
{
|
||||||
|
/* SUID trigger. */
|
||||||
|
|
||||||
trg_definer->str= trg_definer_holder;
|
*definer_user= lex->definer->user;
|
||||||
trg_definer->length= strxmov(trg_definer->str, definer_user->str, "@",
|
*definer_host= lex->definer->host;
|
||||||
definer_host->str, NullS) - trg_definer->str;
|
|
||||||
|
trg_definer->str= trg_definer_holder;
|
||||||
|
trg_definer->length= strxmov(trg_definer->str, definer_user->str, "@",
|
||||||
|
definer_host->str, NullS) - trg_definer->str;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* non-SUID trigger. */
|
||||||
|
|
||||||
|
definer_user->str= 0;
|
||||||
|
definer_user->length= 0;
|
||||||
|
|
||||||
|
definer_host->str= 0;
|
||||||
|
definer_host->length= 0;
|
||||||
|
|
||||||
|
trg_definer->str= (char*) "";
|
||||||
|
trg_definer->length= 0;
|
||||||
|
}
|
||||||
|
|
||||||
if (!sql_create_definition_file(&dir, &file, &triggers_file_type,
|
if (!sql_create_definition_file(&dir, &file, &triggers_file_type,
|
||||||
(gptr)this, triggers_file_parameters, 0))
|
(gptr)this, triggers_file_parameters, 0))
|
||||||
|
@ -208,6 +208,26 @@ bool mysql_create_view(THD *thd,
|
|||||||
if (mode != VIEW_CREATE_NEW)
|
if (mode != VIEW_CREATE_NEW)
|
||||||
sp_cache_invalidate();
|
sp_cache_invalidate();
|
||||||
|
|
||||||
|
if (!lex->definer)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
DEFINER-clause is missing; we have to create default definer in
|
||||||
|
persistent arena to be PS/SP friendly.
|
||||||
|
*/
|
||||||
|
|
||||||
|
Query_arena original_arena;
|
||||||
|
Query_arena *ps_arena = thd->activate_stmt_arena_if_needed(&original_arena);
|
||||||
|
|
||||||
|
if (!(lex->definer= create_default_definer(thd)))
|
||||||
|
res= TRUE;
|
||||||
|
|
||||||
|
if (ps_arena)
|
||||||
|
thd->restore_active_arena(ps_arena, &original_arena);
|
||||||
|
|
||||||
|
if (res)
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
#ifndef NO_EMBEDDED_ACCESS_CHECKS
|
#ifndef NO_EMBEDDED_ACCESS_CHECKS
|
||||||
/*
|
/*
|
||||||
check definer of view:
|
check definer of view:
|
||||||
|
@ -778,7 +778,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
|
|||||||
|
|
||||||
%type <symbol> FUNC_ARG0 FUNC_ARG1 FUNC_ARG2 FUNC_ARG3 keyword keyword_sp
|
%type <symbol> FUNC_ARG0 FUNC_ARG1 FUNC_ARG2 FUNC_ARG3 keyword keyword_sp
|
||||||
|
|
||||||
%type <lex_user> user grant_user get_definer
|
%type <lex_user> user grant_user
|
||||||
|
|
||||||
%type <charset>
|
%type <charset>
|
||||||
opt_collate
|
opt_collate
|
||||||
@ -8960,41 +8960,29 @@ subselect_end:
|
|||||||
};
|
};
|
||||||
|
|
||||||
definer:
|
definer:
|
||||||
get_definer
|
/* empty */
|
||||||
{
|
{
|
||||||
THD *thd= YYTHD;
|
/*
|
||||||
|
We have to distinguish missing DEFINER-clause from case when
|
||||||
if (! (thd->lex->definer= create_definer(thd, &$1->user, &$1->host)))
|
CURRENT_USER specified as definer explicitly in order to properly
|
||||||
YYABORT;
|
handle CREATE TRIGGER statements which come to replication thread
|
||||||
|
from older master servers (i.e. to create non-suid trigger in this
|
||||||
|
case).
|
||||||
|
*/
|
||||||
|
YYTHD->lex->definer= 0;
|
||||||
}
|
}
|
||||||
;
|
| DEFINER_SYM EQ CURRENT_USER optional_braces
|
||||||
|
|
||||||
get_definer:
|
|
||||||
opt_current_definer
|
|
||||||
{
|
{
|
||||||
THD *thd= YYTHD;
|
if (! (YYTHD->lex->definer= create_default_definer(YYTHD)))
|
||||||
|
YYABORT;
|
||||||
if (!($$=(LEX_USER*) thd->alloc(sizeof(st_lex_user))))
|
|
||||||
YYABORT;
|
|
||||||
|
|
||||||
if (get_default_definer(thd, $$))
|
|
||||||
YYABORT;
|
|
||||||
}
|
}
|
||||||
| DEFINER_SYM EQ ident_or_text '@' ident_or_text
|
| DEFINER_SYM EQ ident_or_text '@' ident_or_text
|
||||||
{
|
{
|
||||||
if (!($$=(LEX_USER*) YYTHD->alloc(sizeof(st_lex_user))))
|
if (!(YYTHD->lex->definer= create_definer(YYTHD, &$3, &$5)))
|
||||||
YYABORT;
|
YYABORT;
|
||||||
|
|
||||||
$$->user= $3;
|
|
||||||
$$->host= $5;
|
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
opt_current_definer:
|
|
||||||
/* empty */
|
|
||||||
| DEFINER_SYM EQ CURRENT_USER optional_braces
|
|
||||||
;
|
|
||||||
|
|
||||||
/**************************************************************************
|
/**************************************************************************
|
||||||
|
|
||||||
CREATE VIEW statement options.
|
CREATE VIEW statement options.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user