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 table t21,t31;
|
||||
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 database other;
|
||||
|
||||
|
||||
#
|
||||
# Test specific triggers including SELECT into var with replication
|
||||
# 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
|
||||
|
@ -531,6 +531,7 @@ bool create_table_precheck(THD *thd, TABLE_LIST *tables,
|
||||
TABLE_LIST *create_table);
|
||||
|
||||
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);
|
||||
|
||||
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
|
||||
and host names satisfy definers requirements.
|
||||
@ -7218,7 +7246,7 @@ bool get_default_definer(THD *thd, LEX_USER *definer)
|
||||
|
||||
RETURN
|
||||
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.
|
||||
*/
|
||||
|
||||
|
@ -264,7 +264,17 @@ end:
|
||||
log_query.set((char *) 0, 0, system_charset_info); /* reset log_query */
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@ -289,17 +299,30 @@ end:
|
||||
LEX)
|
||||
tables - table list containing one open table for which the
|
||||
trigger is created.
|
||||
definer_user - [out] after a call it points to 0-terminated string,
|
||||
which contains user name part of the actual trigger
|
||||
definer. The caller is responsible to provide memory for
|
||||
definer_user - [out] after a call it points to 0-terminated string or
|
||||
contains the NULL-string:
|
||||
- 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.
|
||||
definer_host - [out] after a call it points to 0-terminated string,
|
||||
which contains host name part of the actual trigger
|
||||
definer. The caller is responsible to provide memory for
|
||||
definer_host - [out] after a call it points to 0-terminated string or
|
||||
contains the NULL-string:
|
||||
- 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.
|
||||
|
||||
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
|
||||
False - success
|
||||
@ -336,12 +359,30 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables,
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
Definer attribute of the Lex instance is always set in sql_yacc.yy when
|
||||
trigger is created.
|
||||
*/
|
||||
if (!lex->definer)
|
||||
{
|
||||
/*
|
||||
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
|
||||
@ -349,10 +390,11 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables,
|
||||
under another user one must have SUPER privilege).
|
||||
*/
|
||||
|
||||
if (strcmp(lex->definer->user.str, thd->security_ctx->priv_user) ||
|
||||
my_strcasecmp(system_charset_info,
|
||||
lex->definer->host.str,
|
||||
thd->security_ctx->priv_host))
|
||||
if (lex->definer &&
|
||||
(strcmp(lex->definer->user.str, thd->security_ctx->priv_user) ||
|
||||
my_strcasecmp(system_charset_info,
|
||||
lex->definer->host.str,
|
||||
thd->security_ctx->priv_host)))
|
||||
{
|
||||
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;
|
||||
|
||||
#ifndef NO_EMBEDDED_ACCESS_CHECKS
|
||||
if (!is_acl_user(lex->definer->host.str,
|
||||
lex->definer->user.str))
|
||||
if (lex->definer && !is_acl_user(lex->definer->host.str,
|
||||
lex->definer->user.str))
|
||||
{
|
||||
push_warning_printf(thd,
|
||||
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 */
|
||||
|
||||
*definer_user= lex->definer->user;
|
||||
*definer_host= lex->definer->host;
|
||||
if (lex->definer)
|
||||
{
|
||||
/* SUID trigger. */
|
||||
|
||||
trg_definer->str= trg_definer_holder;
|
||||
trg_definer->length= strxmov(trg_definer->str, definer_user->str, "@",
|
||||
definer_host->str, NullS) - trg_definer->str;
|
||||
*definer_user= lex->definer->user;
|
||||
*definer_host= lex->definer->host;
|
||||
|
||||
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,
|
||||
(gptr)this, triggers_file_parameters, 0))
|
||||
|
@ -208,6 +208,26 @@ bool mysql_create_view(THD *thd,
|
||||
if (mode != VIEW_CREATE_NEW)
|
||||
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
|
||||
/*
|
||||
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 <lex_user> user grant_user get_definer
|
||||
%type <lex_user> user grant_user
|
||||
|
||||
%type <charset>
|
||||
opt_collate
|
||||
@ -8960,41 +8960,29 @@ subselect_end:
|
||||
};
|
||||
|
||||
definer:
|
||||
get_definer
|
||||
/* empty */
|
||||
{
|
||||
THD *thd= YYTHD;
|
||||
|
||||
if (! (thd->lex->definer= create_definer(thd, &$1->user, &$1->host)))
|
||||
YYABORT;
|
||||
/*
|
||||
We have to distinguish missing DEFINER-clause from case when
|
||||
CURRENT_USER specified as definer explicitly in order to properly
|
||||
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;
|
||||
}
|
||||
;
|
||||
|
||||
get_definer:
|
||||
opt_current_definer
|
||||
| DEFINER_SYM EQ CURRENT_USER optional_braces
|
||||
{
|
||||
THD *thd= YYTHD;
|
||||
|
||||
if (!($$=(LEX_USER*) thd->alloc(sizeof(st_lex_user))))
|
||||
YYABORT;
|
||||
|
||||
if (get_default_definer(thd, $$))
|
||||
YYABORT;
|
||||
if (! (YYTHD->lex->definer= create_default_definer(YYTHD)))
|
||||
YYABORT;
|
||||
}
|
||||
| DEFINER_SYM EQ ident_or_text '@' ident_or_text
|
||||
{
|
||||
if (!($$=(LEX_USER*) YYTHD->alloc(sizeof(st_lex_user))))
|
||||
YYABORT;
|
||||
|
||||
$$->user= $3;
|
||||
$$->host= $5;
|
||||
if (!(YYTHD->lex->definer= create_definer(YYTHD, &$3, &$5)))
|
||||
YYABORT;
|
||||
}
|
||||
;
|
||||
|
||||
opt_current_definer:
|
||||
/* empty */
|
||||
| DEFINER_SYM EQ CURRENT_USER optional_braces
|
||||
;
|
||||
|
||||
/**************************************************************************
|
||||
|
||||
CREATE VIEW statement options.
|
||||
|
Loading…
x
Reference in New Issue
Block a user