From e03e5222013a0bbfc17adb37f5201b9f0dbc5327 Mon Sep 17 00:00:00 2001 From: "anozdrin@mysql.com" <> Date: Wed, 1 Mar 2006 14:13:07 +0300 Subject: [PATCH 1/7] 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 | 41 +++++++++++ mysql-test/std_data/bug16266.000001 | Bin 0 -> 532 bytes mysql-test/t/rpl_trigger.test | 74 +++++++++++++++++++ sql/mysql_priv.h | 1 + sql/sql_parse.cc | 30 +++++++- sql/sql_trigger.cc | 108 +++++++++++++++++++++------- sql/sql_view.cc | 20 ++++++ sql/sql_yacc.yy | 42 ++++------- 8 files changed, 264 insertions(+), 52 deletions(-) create mode 100644 mysql-test/std_data/bug16266.000001 diff --git a/mysql-test/r/rpl_trigger.result b/mysql-test/r/rpl_trigger.result index ccd880c1e07..185862097bc 100644 --- a/mysql-test/r/rpl_trigger.result +++ b/mysql-test/r/rpl_trigger.result @@ -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; diff --git a/mysql-test/std_data/bug16266.000001 b/mysql-test/std_data/bug16266.000001 new file mode 100644 index 0000000000000000000000000000000000000000..1b24d231511ff1505b232ef852b6473852afd480 GIT binary patch literal 532 zcmeyDl$p2U!XIaTMg|6kI3P{}Vg?2l22(u)Jwr3yl+>isblsf%bg(d90%VM^1up{! zgAfA?5C}0acr&m_Z3PN5flLnt;_YA)Ks*p&VPKE~QVt9ZFcAhe=HikR77m~x91JC? z#U%{RL9UJ=t_mTJPCl**C59Tw3Z8x;ng?NqMg#Q=fpx=-Al*3hK?)^B>4pl9ZXrOXr(dvZP>6!RAJ7{LZvH_Eu8z(g3PJwi zFd3l7{1r-!6v7;RLS2J3oI`_xT>V1gLxI{fG&Nts+!YRV7b6ppfQK3}UWVEZvBMB% UhoPp&#Xru>AYTdtu?W~$0PX--`v3p{ literal 0 HcmV?d00001 diff --git a/mysql-test/t/rpl_trigger.test b/mysql-test/t/rpl_trigger.test index f3db1cb5841..90822e0654c 100644 --- a/mysql-test/t/rpl_trigger.test +++ b/mysql-test/t/rpl_trigger.test @@ -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 diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 2c817ae54c2..9559b0be76a 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -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 { diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index b2066953cf5..0494ccf985f 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -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. */ diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc index 4c3ae5c032d..9ccc70d8ccd 100644 --- a/sql/sql_trigger.cc +++ b/sql/sql_trigger.cc @@ -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)) diff --git a/sql/sql_view.cc b/sql/sql_view.cc index 4f62a80cfd4..2178b5d00a8 100644 --- a/sql/sql_view.cc +++ b/sql/sql_view.cc @@ -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: diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index d6d2939bed3..7e93b9c8e73 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -778,7 +778,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %type FUNC_ARG0 FUNC_ARG1 FUNC_ARG2 FUNC_ARG3 keyword keyword_sp -%type user grant_user get_definer +%type user grant_user %type 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. From 302239f388d0bf28ccbb3a234463f7fdd25da717 Mon Sep 17 00:00:00 2001 From: "anozdrin@mysql.com" <> Date: Thu, 2 Mar 2006 14:17:13 +0300 Subject: [PATCH 2/7] Fix for BUG#16777: Can not create trigger nor view w/o definer if --skip-grant-tables specified. The problem is that there is a check that prevents creating a definer with empty host name. In --skip-grant-tables mode this check prevents the user from creating a trigger/view without explicitly specifying its definer. This happens, because in --skip-grant-tables mode CURRENT_USER is ''@''. According to Sanja this check was implemented intentionally. However, according to the MySQL manual it is possible to specify empty host name (as well as empty user name). Moreover, the behaviour for stored routines is different in this aspect -- we allow them to be created with implicit definer. Based on this, we believe it is OK to change the behaviour for views to be similar with the behaviour for stored routines. --- mysql-test/r/skip_grants.result | 21 ++++++++++--- mysql-test/t/skip_grants.test | 53 ++++++++++++++++++++++++++++----- sql/mysql_priv.h | 2 +- sql/sql_parse.cc | 28 +++-------------- sql/sql_view.cc | 3 +- 5 files changed, 68 insertions(+), 39 deletions(-) diff --git a/mysql-test/r/skip_grants.result b/mysql-test/r/skip_grants.result index 5dc770a7363..d8548d84886 100644 --- a/mysql-test/r/skip_grants.result +++ b/mysql-test/r/skip_grants.result @@ -2,13 +2,26 @@ drop table if exists t1,v1; drop view if exists t1,v1; drop procedure if exists f1; use test; -create table t1 (field1 INT); -CREATE VIEW v1 AS SELECT field1 FROM t1; -ERROR HY000: Definer is not fully qualified -drop table t1; create procedure f1() select 1; drop procedure f1; create table t1 (a int); create definer='user'@'host' sql security definer view v1 as select * from t1; drop view v1; drop table t1; +DROP VIEW IF EXISTS v1; +DROP VIEW IF EXISTS v2; +DROP TABLE IF EXISTS t1; +CREATE TABLE t1(c INT); +CREATE TRIGGER t1_bi BEFORE INSERT ON t1 +FOR EACH ROW +SET @a = 1; +CREATE VIEW v1 AS SELECT * FROM t1; +CREATE DEFINER=a@b TRIGGER ti_ai AFTER INSERT ON t1 +FOR EACH ROW +SET @b = 1; +CREATE DEFINER=a@b VIEW v2 AS SELECT * FROM t1; +DROP TRIGGER t1_bi; +DROP TRIGGER ti_ai; +DROP VIEW v1; +DROP VIEW v2; +DROP TABLE t1; diff --git a/mysql-test/t/skip_grants.test b/mysql-test/t/skip_grants.test index a0164cb0759..ddf2a7e3fd1 100644 --- a/mysql-test/t/skip_grants.test +++ b/mysql-test/t/skip_grants.test @@ -8,14 +8,6 @@ drop procedure if exists f1; --enable_warnings use test; -# -# test that we can create VIEW if privileges check switched off -# -create table t1 (field1 INT); --- error ER_MALFORMED_DEFINER -CREATE VIEW v1 AS SELECT field1 FROM t1; -drop table t1; - # # Test that we can create and drop procedure without warnings # see bug#9993 @@ -30,3 +22,48 @@ create table t1 (a int); create definer='user'@'host' sql security definer view v1 as select * from t1; drop view v1; drop table t1; + +# +# BUG#16777: Can not create trigger nor view w/o definer if --skip-grant-tables +# specified +# +# Also, a test that we can create VIEW if privileges check switched off has +# been moved here. +# + +# Prepare. + +--disable_warnings + +DROP VIEW IF EXISTS v1; +DROP VIEW IF EXISTS v2; + +DROP TABLE IF EXISTS t1; + +--enable_warnings + +# Test case. + +CREATE TABLE t1(c INT); + +CREATE TRIGGER t1_bi BEFORE INSERT ON t1 + FOR EACH ROW + SET @a = 1; + +CREATE VIEW v1 AS SELECT * FROM t1; + +CREATE DEFINER=a@b TRIGGER ti_ai AFTER INSERT ON t1 + FOR EACH ROW + SET @b = 1; + +CREATE DEFINER=a@b VIEW v2 AS SELECT * FROM t1; + +# Cleanup. + +DROP TRIGGER t1_bi; +DROP TRIGGER ti_ai; + +DROP VIEW v1; +DROP VIEW v2; + +DROP TABLE t1; diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 9559b0be76a..a43307edf27 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -530,7 +530,7 @@ bool insert_precheck(THD *thd, TABLE_LIST *tables); bool create_table_precheck(THD *thd, TABLE_LIST *tables, TABLE_LIST *create_table); -bool get_default_definer(THD *thd, LEX_USER *definer); +void 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); diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 0494ccf985f..13d814bdf42 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -7168,47 +7168,28 @@ Item *negate_expression(THD *thd, Item *expr) /* Set the specified definer to the default value, which is the current user in - the thread. Also check that the current user satisfies to the definers - requirements. + the thread. SYNOPSIS get_default_definer() thd [in] thread handler definer [out] definer - - RETURN - error status, that is: - - FALSE -- on success; - - TRUE -- on error (current user can not be a definer). */ -bool get_default_definer(THD *thd, LEX_USER *definer) +void get_default_definer(THD *thd, LEX_USER *definer) { - /* Check that current user has non-empty host name. */ - const Security_context *sctx= thd->security_ctx; - if (sctx->priv_host[0] == 0) - { - my_error(ER_MALFORMED_DEFINER, MYF(0)); - return TRUE; - } - - /* Fill in. */ - definer->user.str= (char *) sctx->priv_user; definer->user.length= strlen(definer->user.str); definer->host.str= (char *) sctx->priv_host; definer->host.length= strlen(definer->host.str); - - return FALSE; } /* - Create default definer for the specified THD. Also check that the current - user is conformed to the definers requirements. + Create default definer for the specified THD. SYNOPSIS create_default_definer() @@ -7227,8 +7208,7 @@ LEX_USER *create_default_definer(THD *thd) if (! (definer= (LEX_USER*) thd->alloc(sizeof(LEX_USER)))) return 0; - if (get_default_definer(thd, definer)) - return 0; + get_default_definer(thd, definer); return definer; } diff --git a/sql/sql_view.cc b/sql/sql_view.cc index 2178b5d00a8..4f2a9a07705 100644 --- a/sql/sql_view.cc +++ b/sql/sql_view.cc @@ -835,8 +835,7 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table) push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_VIEW_FRM_NO_USER, ER(ER_VIEW_FRM_NO_USER), table->db, table->table_name); - if (get_default_definer(thd, &table->definer)) - goto err; + get_default_definer(thd, &table->definer); } /* From fbb59203995255042356ba0fbd681e5afd7ac607 Mon Sep 17 00:00:00 2001 From: "anozdrin@mysql.com" <> Date: Thu, 2 Mar 2006 15:18:49 +0300 Subject: [PATCH 3/7] Implementation of WL#2897: Complete definer support in the stored routines. The idea is to add DEFINER-clause in CREATE PROCEDURE and CREATE FUNCTION statements. Almost all support of definer in stored routines had been already done before this patch. NOTE: this patch changes behaviour of dumping stored routines in mysqldump. Before this patch, mysqldump did not dump DEFINER-clause for stored routines and this was documented behaviour. In order to get full information about stored routines, one should have dumped mysql.proc table. This patch changes this behaviour, so that DEFINER-clause is dumped. Since DEFINER-clause is not supported in CREATE PROCEDURE | FUNCTION statements before this patch, the clause is covered by additional version-specific comments. --- client/mysqldump.c | 48 +++++- mysql-test/r/gis.result | 2 +- mysql-test/r/information_schema.result | 4 +- mysql-test/r/mysqldump.result | 14 +- mysql-test/r/rpl_ddl.result | 4 +- mysql-test/r/rpl_sp.result | 30 ++-- mysql-test/r/rpl_trigger.result | 2 +- mysql-test/r/sp-security.result | 52 +++++++ mysql-test/r/sp.result | 28 ++-- mysql-test/r/sql_mode.result | 8 +- mysql-test/t/sp-security.test | 101 +++++++++++++ sql/sp.cc | 55 ++++++- sql/sp_head.cc | 28 ++-- sql/sp_head.h | 1 + sql/sql_lex.h | 14 +- sql/sql_parse.cc | 84 ++++++++++ sql/sql_trigger.cc | 2 +- sql/sql_yacc.yy | 202 ++++++++++++++----------- 18 files changed, 521 insertions(+), 158 deletions(-) diff --git a/client/mysqldump.c b/client/mysqldump.c index 161197821d6..e0469e00031 100644 --- a/client/mysqldump.c +++ b/client/mysqldump.c @@ -80,6 +80,7 @@ static char *add_load_option(char *ptr, const char *object, const char *statement); static ulong find_set(TYPELIB *lib, const char *x, uint length, char **err_pos, uint *err_len); +static char *alloc_query_str(ulong size); static char *field_escape(char *to,const char *from,uint length); static my_bool verbose=0,tFlag=0,dFlag=0,quick= 1, extended_insert= 1, @@ -1304,19 +1305,64 @@ static uint dump_routines_for_db(char *db) routine_name, row[2], strlen(row[2]))); if (strlen(row[2])) { + char *query_str= NULL; + char *definer_begin; + if (opt_drop) fprintf(sql_file, "/*!50003 DROP %s IF EXISTS %s */;;\n", routine_type[i], routine_name); + + /* + Cover DEFINER-clause in version-specific comments. + + TODO: this is definitely a BAD IDEA to parse SHOW CREATE output. + We should user INFORMATION_SCHEMA instead. The only problem is + that now INFORMATION_SCHEMA does not provide information about + routine parameters. + */ + + definer_begin= strstr(row[2], " DEFINER"); + + if (definer_begin) + { + char *definer_end= strstr(definer_begin, " PROCEDURE"); + + if (!definer_end) + definer_end= strstr(definer_begin, " FUNCTION"); + + if (definer_end) + { + char *query_str_tail; + + /* + Allocate memory for new query string: original string + from SHOW statement and version-specific comments. + */ + query_str= alloc_query_str(strlen(row[2]) + 23); + + query_str_tail= strnmov(query_str, row[2], + definer_begin - row[2]); + query_str_tail= strmov(query_str_tail, "*/ /*!50019"); + query_str_tail= strnmov(query_str_tail, definer_begin, + definer_end - definer_begin); + query_str_tail= strxmov(query_str_tail, "*/ /*!50003", + definer_end, NullS); + } + } + /* we need to change sql_mode only for the CREATE PROCEDURE/FUNCTION otherwise we may need to re-quote routine_name */; fprintf(sql_file, "/*!50003 SET SESSION SQL_MODE=\"%s\"*/;;\n", row[1] /* sql_mode */); - fprintf(sql_file, "/*!50003 %s */;;\n", row[2]); + fprintf(sql_file, "/*!50003 %s */;;\n", + (query_str != NULL ? query_str : row[2])); fprintf(sql_file, "/*!50003 SET SESSION SQL_MODE=@OLD_SQL_MODE*/" ";;\n"); + + my_free(query_str, MYF(MY_ALLOW_ZERO_PTR)); } } /* end of routine printing */ } /* end of list of routines */ diff --git a/mysql-test/r/gis.result b/mysql-test/r/gis.result index 049ba784dc9..13e2d56d83e 100644 --- a/mysql-test/r/gis.result +++ b/mysql-test/r/gis.result @@ -680,7 +680,7 @@ drop procedure if exists fn3; create function fn3 () returns point return GeomFromText("point(1 1)"); show create function fn3; Function sql_mode Create Function -fn3 CREATE FUNCTION `fn3`() RETURNS point +fn3 CREATE DEFINER=`root`@`localhost` FUNCTION `fn3`() RETURNS point return GeomFromText("point(1 1)") select astext(fn3()); astext(fn3()) diff --git a/mysql-test/r/information_schema.result b/mysql-test/r/information_schema.result index d3576e24a44..1c845c73e4b 100644 --- a/mysql-test/r/information_schema.result +++ b/mysql-test/r/information_schema.result @@ -309,7 +309,7 @@ Function sql_mode Create Function sub1 show create function sub2; Function sql_mode Create Function -sub2 CREATE FUNCTION `sub2`(i int) RETURNS int(11) +sub2 CREATE DEFINER=`mysqltest_1`@`localhost` FUNCTION `sub2`(i int) RETURNS int(11) return i+1 show function status like "sub2"; Db Name Type Definer Modified Created Security_type Comment @@ -317,7 +317,7 @@ test sub2 FUNCTION mysqltest_1@localhost # # DEFINER drop function sub2; show create procedure sel2; Procedure sql_mode Create Procedure -sel2 CREATE PROCEDURE `sel2`() +sel2 CREATE DEFINER=`root`@`localhost` PROCEDURE `sel2`() begin select * from t1; select * from t2; diff --git a/mysql-test/r/mysqldump.result b/mysql-test/r/mysqldump.result index d156e711167..e6ac9fbc740 100644 --- a/mysql-test/r/mysqldump.result +++ b/mysql-test/r/mysqldump.result @@ -2210,12 +2210,12 @@ UNLOCK TABLES; DELIMITER ;; /*!50003 DROP FUNCTION IF EXISTS `bug9056_func1` */;; /*!50003 SET SESSION SQL_MODE=""*/;; -/*!50003 CREATE FUNCTION `bug9056_func1`(a INT, b INT) RETURNS int(11) +/*!50003 CREATE*/ /*!50019 DEFINER=`root`@`localhost`*/ /*!50003 FUNCTION `bug9056_func1`(a INT, b INT) RETURNS int(11) RETURN a+b */;; /*!50003 SET SESSION SQL_MODE=@OLD_SQL_MODE*/;; /*!50003 DROP FUNCTION IF EXISTS `bug9056_func2` */;; /*!50003 SET SESSION SQL_MODE=""*/;; -/*!50003 CREATE FUNCTION `bug9056_func2`(f1 char binary) RETURNS char(1) +/*!50003 CREATE*/ /*!50019 DEFINER=`root`@`localhost`*/ /*!50003 FUNCTION `bug9056_func2`(f1 char binary) RETURNS char(1) begin set f1= concat( 'hello', f1 ); return f1; @@ -2223,17 +2223,17 @@ end */;; /*!50003 SET SESSION SQL_MODE=@OLD_SQL_MODE*/;; /*!50003 DROP PROCEDURE IF EXISTS `a'b` */;; /*!50003 SET SESSION SQL_MODE="REAL_AS_FLOAT,PIPES_AS_CONCAT,ANSI_QUOTES,IGNORE_SPACE,ANSI"*/;; -/*!50003 CREATE PROCEDURE "a'b"() +/*!50003 CREATE*/ /*!50019 DEFINER="root"@"localhost"*/ /*!50003 PROCEDURE "a'b"() select 1 */;; /*!50003 SET SESSION SQL_MODE=@OLD_SQL_MODE*/;; /*!50003 DROP PROCEDURE IF EXISTS `bug9056_proc1` */;; /*!50003 SET SESSION SQL_MODE=""*/;; -/*!50003 CREATE PROCEDURE `bug9056_proc1`(IN a INT, IN b INT, OUT c INT) +/*!50003 CREATE*/ /*!50019 DEFINER=`root`@`localhost`*/ /*!50003 PROCEDURE `bug9056_proc1`(IN a INT, IN b INT, OUT c INT) BEGIN SELECT a+b INTO c; end */;; /*!50003 SET SESSION SQL_MODE=@OLD_SQL_MODE*/;; /*!50003 DROP PROCEDURE IF EXISTS `bug9056_proc2` */;; /*!50003 SET SESSION SQL_MODE=""*/;; -/*!50003 CREATE PROCEDURE `bug9056_proc2`(OUT a INT) +/*!50003 CREATE*/ /*!50019 DEFINER=`root`@`localhost`*/ /*!50003 PROCEDURE `bug9056_proc2`(OUT a INT) BEGIN select sum(id) from t1 into a; END */;; @@ -2685,11 +2685,11 @@ return 42 */| select 42 */| show create function f; Function sql_mode Create Function -f CREATE FUNCTION `f`() RETURNS bigint(20) +f CREATE DEFINER=`root`@`localhost` FUNCTION `f`() RETURNS bigint(20) return 42 show create procedure p; Procedure sql_mode Create Procedure -p CREATE PROCEDURE `p`() +p CREATE DEFINER=`root`@`localhost` PROCEDURE `p`() select 42 drop function f; drop procedure p; diff --git a/mysql-test/r/rpl_ddl.result b/mysql-test/r/rpl_ddl.result index c56c9f20cf8..92e91b31459 100644 --- a/mysql-test/r/rpl_ddl.result +++ b/mysql-test/r/rpl_ddl.result @@ -1133,7 +1133,7 @@ SHOW PROCEDURE STATUS LIKE 'p1'; Db mysqltest1 Name p1 Type PROCEDURE -Definer @ +Definer root@localhost Modified # Created # Security_type DEFINER @@ -1199,7 +1199,7 @@ SHOW PROCEDURE STATUS LIKE 'p1'; Db mysqltest1 Name p1 Type PROCEDURE -Definer @ +Definer root@localhost Modified # Created # Security_type DEFINER diff --git a/mysql-test/r/rpl_sp.result b/mysql-test/r/rpl_sp.result index a42c33ce333..5dfda16c763 100644 --- a/mysql-test/r/rpl_sp.result +++ b/mysql-test/r/rpl_sp.result @@ -31,7 +31,7 @@ declare b int; set b = 8; insert into t1 values (b); insert into t1 values (unix_timestamp()); -end @ # # +end root@localhost # # set timestamp=1000000000; call foo(); select * from t1; @@ -118,7 +118,7 @@ select * from mysql.proc where name="foo4" and db='mysqltest1'; db name type specific_name language sql_data_access is_deterministic security_type param_list returns body definer created modified sql_mode comment mysqltest1 foo4 PROCEDURE foo4 SQL CONTAINS_SQL YES DEFINER begin insert into t2 values(20),(20); -end @ # # +end root@localhost # # drop procedure foo4; select * from mysql.proc where name="foo4" and db='mysqltest1'; db name type specific_name language sql_data_access is_deterministic security_type param_list returns body definer created modified sql_mode comment @@ -223,13 +223,13 @@ select * from mysql.proc where db='mysqltest1'; db name type specific_name language sql_data_access is_deterministic security_type param_list returns body definer created modified sql_mode comment mysqltest1 fn1 FUNCTION fn1 SQL NO_SQL NO DEFINER int(11) begin return unix_timestamp(); -end @ # # +end root@localhost # # mysqltest1 fn2 FUNCTION fn2 SQL NO_SQL NO DEFINER int(11) begin return unix_timestamp(); -end @ # # +end zedjzlcsjhd@localhost # # mysqltest1 fn3 FUNCTION fn3 SQL READS_SQL_DATA NO DEFINER int(11) begin return 0; -end @ # # +end root@localhost # # delete from t2; alter table t2 add unique (a); drop function fn1; @@ -274,7 +274,7 @@ Log_name Pos Event_type Server_id End_log_pos Info master-bin.000001 # Query 1 # drop database if exists mysqltest1 master-bin.000001 # Query 1 # create database mysqltest1 master-bin.000001 # Query 1 # use `mysqltest1`; create table t1 (a varchar(100)) -master-bin.000001 # Query 1 # use `mysqltest1`; create procedure foo() +master-bin.000001 # Query 1 # use `mysqltest1`; CREATE DEFINER=`root`@`localhost` procedure foo() begin declare b int; set b = 8; @@ -284,19 +284,19 @@ end master-bin.000001 # Query 1 # use `mysqltest1`; insert into t1 values ( NAME_CONST('b',8)) master-bin.000001 # Query 1 # use `mysqltest1`; insert into t1 values (unix_timestamp()) master-bin.000001 # Query 1 # use `mysqltest1`; delete from t1 -master-bin.000001 # Query 1 # use `mysqltest1`; create procedure foo2() +master-bin.000001 # Query 1 # use `mysqltest1`; CREATE DEFINER=`root`@`localhost` procedure foo2() select * from mysqltest1.t1 master-bin.000001 # Query 1 # use `mysqltest1`; alter procedure foo2 contains sql master-bin.000001 # Query 1 # use `mysqltest1`; drop table t1 master-bin.000001 # Query 1 # use `mysqltest1`; create table t1 (a int) master-bin.000001 # Query 1 # use `mysqltest1`; create table t2 like t1 -master-bin.000001 # Query 1 # use `mysqltest1`; create procedure foo3() +master-bin.000001 # Query 1 # use `mysqltest1`; CREATE DEFINER=`root`@`localhost` procedure foo3() deterministic insert into t1 values (15) master-bin.000001 # Query 1 # use `mysqltest1`; grant CREATE ROUTINE, EXECUTE on mysqltest1.* to "zedjzlcsjhd"@127.0.0.1 master-bin.000001 # Query 1 # use `mysqltest1`; grant SELECT on mysqltest1.t1 to "zedjzlcsjhd"@127.0.0.1 master-bin.000001 # Query 1 # use `mysqltest1`; grant SELECT, INSERT on mysqltest1.t2 to "zedjzlcsjhd"@127.0.0.1 -master-bin.000001 # Query 1 # use `mysqltest1`; create procedure foo4() +master-bin.000001 # Query 1 # use `mysqltest1`; CREATE DEFINER=`zedjzlcsjhd`@`127.0.0.1` procedure foo4() deterministic begin insert into t2 values(3); @@ -311,7 +311,7 @@ master-bin.000001 # Query 1 # use `mysqltest1`; insert into t1 values (5) master-bin.000001 # Query 1 # use `mysqltest1`; delete from t2 master-bin.000001 # Query 1 # use `mysqltest1`; alter table t2 add unique (a) master-bin.000001 # Query 1 # use `mysqltest1`; drop procedure foo4 -master-bin.000001 # Query 1 # use `mysqltest1`; create procedure foo4() +master-bin.000001 # Query 1 # use `mysqltest1`; CREATE DEFINER=`root`@`localhost` procedure foo4() deterministic begin insert into t2 values(20),(20); @@ -321,7 +321,7 @@ master-bin.000001 # Query 1 # use `mysqltest1`; drop procedure foo4 master-bin.000001 # Query 1 # use `mysqltest1`; drop procedure foo master-bin.000001 # Query 1 # use `mysqltest1`; drop procedure foo2 master-bin.000001 # Query 1 # use `mysqltest1`; drop procedure foo3 -master-bin.000001 # Query 1 # use `mysqltest1`; create function fn1(x int) +master-bin.000001 # Query 1 # use `mysqltest1`; CREATE DEFINER=`root`@`localhost` function fn1(x int) returns int deterministic begin @@ -332,7 +332,7 @@ master-bin.000001 # Query 1 # use `mysqltest1`; delete t1,t2 from t1,t2 master-bin.000001 # Query 1 # use `mysqltest1`; SELECT `fn1`(20) master-bin.000001 # Query 1 # use `mysqltest1`; insert into t2 values(fn1(21)) master-bin.000001 # Query 1 # use `mysqltest1`; drop function fn1 -master-bin.000001 # Query 1 # use `mysqltest1`; create function fn1() +master-bin.000001 # Query 1 # use `mysqltest1`; CREATE DEFINER=`root`@`localhost` function fn1() returns int no sql begin @@ -340,13 +340,13 @@ return unix_timestamp(); end master-bin.000001 # Query 1 # use `mysqltest1`; delete from t1 master-bin.000001 # Query 1 # use `mysqltest1`; insert into t1 values(fn1()) -master-bin.000001 # Query 1 # use `mysqltest1`; create function fn2() +master-bin.000001 # Query 1 # use `mysqltest1`; CREATE DEFINER=`zedjzlcsjhd`@`127.0.0.1` function fn2() returns int no sql begin return unix_timestamp(); end -master-bin.000001 # Query 1 # use `mysqltest1`; create function fn3() +master-bin.000001 # Query 1 # use `mysqltest1`; CREATE DEFINER=`root`@`localhost` function fn3() returns int not deterministic reads sql data @@ -356,7 +356,7 @@ end master-bin.000001 # Query 1 # use `mysqltest1`; delete from t2 master-bin.000001 # Query 1 # use `mysqltest1`; alter table t2 add unique (a) master-bin.000001 # Query 1 # use `mysqltest1`; drop function fn1 -master-bin.000001 # Query 1 # use `mysqltest1`; create function fn1(x int) +master-bin.000001 # Query 1 # use `mysqltest1`; CREATE DEFINER=`root`@`localhost` function fn1(x int) returns int begin insert into t2 values(x),(x); diff --git a/mysql-test/r/rpl_trigger.result b/mysql-test/r/rpl_trigger.result index 185862097bc..b9b20b82acd 100644 --- a/mysql-test/r/rpl_trigger.result +++ b/mysql-test/r/rpl_trigger.result @@ -102,7 +102,7 @@ t1_first root@localhost SELECT routine_name, definer FROM information_schema.routines; routine_name definer -bug12480 @ +bug12480 root@localhost SELECT trigger_name, definer FROM information_schema.triggers; trigger_name definer diff --git a/mysql-test/r/sp-security.result b/mysql-test/r/sp-security.result index d8abc387677..90466bfcfc4 100644 --- a/mysql-test/r/sp-security.result +++ b/mysql-test/r/sp-security.result @@ -323,3 +323,55 @@ Warning 1287 'SHOW INNODB STATUS' is deprecated; use 'SHOW ENGINE INNODB STATUS' GRANT EXECUTE ON PROCEDURE p1 TO user_bug7787@localhost; DROP DATABASE db_bug7787; use test; + +---> connection: root +DROP DATABASE IF EXISTS mysqltest; +CREATE DATABASE mysqltest; +CREATE USER mysqltest_1@localhost; +GRANT ALL PRIVILEGES ON mysqltest.* TO mysqltest_1@localhost; +CREATE USER mysqltest_2@localhost; +GRANT SUPER ON *.* TO mysqltest_2@localhost; +GRANT ALL PRIVILEGES ON mysqltest.* TO mysqltest_2@localhost; + +---> connection: mysqltest_2_con +use mysqltest; +CREATE PROCEDURE wl2897_p1() SELECT 1; +CREATE FUNCTION wl2897_f1() RETURNS INT RETURN 1; + +---> connection: mysqltest_1_con +use mysqltest; +CREATE DEFINER=root@localhost PROCEDURE wl2897_p2() SELECT 2; +ERROR 42000: Access denied; you need the SUPER privilege for this operation +CREATE DEFINER=root@localhost FUNCTION wl2897_f2() RETURNS INT RETURN 2; +ERROR 42000: Access denied; you need the SUPER privilege for this operation + +---> connection: mysqltest_2_con +use mysqltest; +CREATE DEFINER='a @ b @ c'@localhost PROCEDURE wl2897_p3() SELECT 3; +Warnings: +Note 1449 There is no 'a @ b @ c'@'localhost' registered +CREATE DEFINER='a @ b @ c'@localhost FUNCTION wl2897_f3() RETURNS INT RETURN 3; +Warnings: +Note 1449 There is no 'a @ b @ c'@'localhost' registered + +---> connection: con1root +use mysqltest; +SHOW CREATE PROCEDURE wl2897_p1; +Procedure sql_mode Create Procedure +wl2897_p1 CREATE DEFINER=`mysqltest_2`@`localhost` PROCEDURE `wl2897_p1`() +SELECT 1 +SHOW CREATE PROCEDURE wl2897_p3; +Procedure sql_mode Create Procedure +wl2897_p3 CREATE DEFINER=`a @ b @ c`@`localhost` PROCEDURE `wl2897_p3`() +SELECT 3 +SHOW CREATE FUNCTION wl2897_f1; +Function sql_mode Create Function +wl2897_f1 CREATE DEFINER=`mysqltest_2`@`localhost` FUNCTION `wl2897_f1`() RETURNS int(11) +RETURN 1 +SHOW CREATE FUNCTION wl2897_f3; +Function sql_mode Create Function +wl2897_f3 CREATE DEFINER=`a @ b @ c`@`localhost` FUNCTION `wl2897_f3`() RETURNS int(11) +RETURN 3 +DROP USER mysqltest_1@localhost; +DROP USER mysqltest_2@localhost; +DROP DATABASE mysqltest; diff --git a/mysql-test/r/sp.result b/mysql-test/r/sp.result index a4c920f8e15..3e7c51e9394 100644 --- a/mysql-test/r/sp.result +++ b/mysql-test/r/sp.result @@ -788,7 +788,7 @@ comment 'Characteristics procedure test' insert into t1 values ("chistics", 1)| show create procedure chistics| Procedure sql_mode Create Procedure -chistics CREATE PROCEDURE `chistics`() +chistics CREATE DEFINER=`root`@`localhost` PROCEDURE `chistics`() MODIFIES SQL DATA COMMENT 'Characteristics procedure test' insert into t1 values ("chistics", 1) @@ -800,7 +800,7 @@ delete from t1| alter procedure chistics sql security invoker| show create procedure chistics| Procedure sql_mode Create Procedure -chistics CREATE PROCEDURE `chistics`() +chistics CREATE DEFINER=`root`@`localhost` PROCEDURE `chistics`() MODIFIES SQL DATA SQL SECURITY INVOKER COMMENT 'Characteristics procedure test' @@ -815,7 +815,7 @@ comment 'Characteristics procedure test' return 42| show create function chistics| Function sql_mode Create Function -chistics CREATE FUNCTION `chistics`() RETURNS int(11) +chistics CREATE DEFINER=`root`@`localhost` FUNCTION `chistics`() RETURNS int(11) DETERMINISTIC SQL SECURITY INVOKER COMMENT 'Characteristics procedure test' @@ -828,7 +828,7 @@ no sql comment 'Characteristics function test'| show create function chistics| Function sql_mode Create Function -chistics CREATE FUNCTION `chistics`() RETURNS int(11) +chistics CREATE DEFINER=`root`@`localhost` FUNCTION `chistics`() RETURNS int(11) NO SQL DETERMINISTIC SQL SECURITY INVOKER @@ -1287,7 +1287,7 @@ end while; end| show create procedure opp| Procedure sql_mode Create Procedure -opp CREATE PROCEDURE `opp`(n bigint unsigned, out pp bool) +opp CREATE DEFINER=`root`@`localhost` PROCEDURE `opp`(n bigint unsigned, out pp bool) begin declare r double; declare b, s bigint unsigned default 0; @@ -1386,7 +1386,7 @@ alter procedure bar comment "3333333333"| alter procedure bar| show create procedure bar| Procedure sql_mode Create Procedure -bar CREATE PROCEDURE `bar`(x char(16), y int) +bar CREATE DEFINER=`root`@`localhost` PROCEDURE `bar`(x char(16), y int) COMMENT '3333333333' insert into test.t1 values (x, y) show procedure status like 'bar'| @@ -1966,13 +1966,13 @@ Db Name Type Definer Modified Created Security_type Comment test bug2267_4 FUNCTION root@localhost 0000-00-00 00:00:00 0000-00-00 00:00:00 DEFINER call bug2267_3()| Procedure sql_mode Create Procedure -bug2267_1 CREATE PROCEDURE `bug2267_1`() +bug2267_1 CREATE DEFINER=`root`@`localhost` PROCEDURE `bug2267_1`() begin show procedure status; end call bug2267_4()| Function sql_mode Create Function -bug2267_4 CREATE FUNCTION `bug2267_4`() RETURNS int(11) +bug2267_4 CREATE DEFINER=`root`@`localhost` FUNCTION `bug2267_4`() RETURNS int(11) return 100 drop procedure bug2267_1| drop procedure bug2267_2| @@ -2333,20 +2333,20 @@ return x || y$ set @@sql_mode = ''| show create procedure bug2564_1| Procedure sql_mode Create Procedure -bug2564_1 CREATE PROCEDURE `bug2564_1`() +bug2564_1 CREATE DEFINER=`root`@`localhost` PROCEDURE `bug2564_1`() COMMENT 'Joe''s procedure' insert into `t1` values ("foo", 1) show create procedure bug2564_2| Procedure sql_mode Create Procedure -bug2564_2 ANSI_QUOTES CREATE PROCEDURE "bug2564_2"() +bug2564_2 ANSI_QUOTES CREATE DEFINER="root"@"localhost" PROCEDURE "bug2564_2"() insert into "t1" values ('foo', 1) show create function bug2564_3| Function sql_mode Create Function -bug2564_3 CREATE FUNCTION `bug2564_3`(x int, y int) RETURNS int(11) +bug2564_3 CREATE DEFINER=`root`@`localhost` FUNCTION `bug2564_3`(x int, y int) RETURNS int(11) return x || y show create function bug2564_4| Function sql_mode Create Function -bug2564_4 REAL_AS_FLOAT,PIPES_AS_CONCAT,ANSI_QUOTES,IGNORE_SPACE,ANSI CREATE FUNCTION "bug2564_4"(x int, y int) RETURNS int(11) +bug2564_4 REAL_AS_FLOAT,PIPES_AS_CONCAT,ANSI_QUOTES,IGNORE_SPACE,ANSI CREATE DEFINER="root"@"localhost" FUNCTION "bug2564_4"(x int, y int) RETURNS int(11) return x || y drop procedure bug2564_1| drop procedure bug2564_2| @@ -4056,7 +4056,7 @@ return 42; end */;; show create function bug14723;; Function sql_mode Create Function -bug14723 CREATE FUNCTION `bug14723`() RETURNS bigint(20) +bug14723 CREATE DEFINER=`root`@`localhost` FUNCTION `bug14723`() RETURNS bigint(20) main_loop: begin return 42; end @@ -4069,7 +4069,7 @@ select 42; end */;; show create procedure bug14723;; Procedure sql_mode Create Procedure -bug14723 CREATE PROCEDURE `bug14723`() +bug14723 CREATE DEFINER=`root`@`localhost` PROCEDURE `bug14723`() main_loop: begin select 42; end diff --git a/mysql-test/r/sql_mode.result b/mysql-test/r/sql_mode.result index 66df424919b..b05680f9c54 100644 --- a/mysql-test/r/sql_mode.result +++ b/mysql-test/r/sql_mode.result @@ -445,23 +445,23 @@ SET @@SQL_MODE=''; create function `foo` () returns int return 5; show create function `foo`; Function sql_mode Create Function -foo CREATE FUNCTION `foo`() RETURNS int(11) +foo CREATE DEFINER=`root`@`localhost` FUNCTION `foo`() RETURNS int(11) return 5 SET @@SQL_MODE='ANSI_QUOTES'; show create function `foo`; Function sql_mode Create Function -foo CREATE FUNCTION `foo`() RETURNS int(11) +foo CREATE DEFINER=`root`@`localhost` FUNCTION `foo`() RETURNS int(11) return 5 drop function `foo`; create function `foo` () returns int return 5; show create function `foo`; Function sql_mode Create Function -foo ANSI_QUOTES CREATE FUNCTION "foo"() RETURNS int(11) +foo ANSI_QUOTES CREATE DEFINER="root"@"localhost" FUNCTION "foo"() RETURNS int(11) return 5 SET @@SQL_MODE=''; show create function `foo`; Function sql_mode Create Function -foo ANSI_QUOTES CREATE FUNCTION "foo"() RETURNS int(11) +foo ANSI_QUOTES CREATE DEFINER="root"@"localhost" FUNCTION "foo"() RETURNS int(11) return 5 drop function `foo`; SET @@SQL_MODE=''; diff --git a/mysql-test/t/sp-security.test b/mysql-test/t/sp-security.test index 19f94a32d9c..b466d2125d4 100644 --- a/mysql-test/t/sp-security.test +++ b/mysql-test/t/sp-security.test @@ -547,4 +547,105 @@ GRANT EXECUTE ON PROCEDURE p1 TO user_bug7787@localhost; DROP DATABASE db_bug7787; use test; + +# +# WL#2897: Complete definer support in the stored routines. +# +# The following cases are tested: +# 1. check that if DEFINER-clause is not explicitly specified, stored routines +# are created with CURRENT_USER privileges; +# 2. check that if DEFINER-clause specifies non-current user, SUPER privilege +# is required to create a stored routine; +# 3. check that if DEFINER-clause specifies non-existent user, a warning is +# emitted. +# 4. check that SHOW CREATE PROCEDURE | FUNCTION works correctly; +# +# The following cases are tested in other test suites: +# - check that mysqldump dumps new attribute correctly; +# - check that slave replicates CREATE-statements with explicitly specified +# DEFINER correctly. +# + +# Setup the environment. + +--echo +--echo ---> connection: root +--connection con1root + +--disable_warnings +DROP DATABASE IF EXISTS mysqltest; +--enable_warnings + +CREATE DATABASE mysqltest; + +CREATE USER mysqltest_1@localhost; +GRANT ALL PRIVILEGES ON mysqltest.* TO mysqltest_1@localhost; + +CREATE USER mysqltest_2@localhost; +GRANT SUPER ON *.* TO mysqltest_2@localhost; +GRANT ALL PRIVILEGES ON mysqltest.* TO mysqltest_2@localhost; + +--connect (mysqltest_2_con,localhost,mysqltest_2,,mysqltest) +--connect (mysqltest_1_con,localhost,mysqltest_1,,mysqltest) + +# test case (1). + +--echo +--echo ---> connection: mysqltest_2_con +--connection mysqltest_2_con + +use mysqltest; + +CREATE PROCEDURE wl2897_p1() SELECT 1; + +CREATE FUNCTION wl2897_f1() RETURNS INT RETURN 1; + +# test case (2). + +--echo +--echo ---> connection: mysqltest_1_con +--connection mysqltest_1_con + +use mysqltest; + +--error ER_SPECIFIC_ACCESS_DENIED_ERROR +CREATE DEFINER=root@localhost PROCEDURE wl2897_p2() SELECT 2; + +--error ER_SPECIFIC_ACCESS_DENIED_ERROR +CREATE DEFINER=root@localhost FUNCTION wl2897_f2() RETURNS INT RETURN 2; + +# test case (3). + +--echo +--echo ---> connection: mysqltest_2_con +--connection mysqltest_2_con + +use mysqltest; + +CREATE DEFINER='a @ b @ c'@localhost PROCEDURE wl2897_p3() SELECT 3; + +CREATE DEFINER='a @ b @ c'@localhost FUNCTION wl2897_f3() RETURNS INT RETURN 3; + +# test case (4). + +--echo +--echo ---> connection: con1root +--connection con1root + +use mysqltest; + +SHOW CREATE PROCEDURE wl2897_p1; +SHOW CREATE PROCEDURE wl2897_p3; + +SHOW CREATE FUNCTION wl2897_f1; +SHOW CREATE FUNCTION wl2897_f3; + +# Cleanup. + +DROP USER mysqltest_1@localhost; +DROP USER mysqltest_2@localhost; + +DROP DATABASE mysqltest; + + # End of 5.0 bugs. diff --git a/sql/sp.cc b/sql/sp.cc index ce0282bf810..e4489af1fdd 100644 --- a/sql/sp.cc +++ b/sql/sp.cc @@ -21,6 +21,8 @@ #include "sp_cache.h" #include "sql_trigger.h" +#include + static bool create_string(THD *thd, String *buf, int sp_type, @@ -28,7 +30,9 @@ create_string(THD *thd, String *buf, const char *params, ulong paramslen, const char *returns, ulong returnslen, const char *body, ulong bodylen, - st_sp_chistics *chistics); + st_sp_chistics *chistics, + const LEX_STRING *definer_user, + const LEX_STRING *definer_host); static int db_load_routine(THD *thd, int type, sp_name *name, sp_head **sphp, ulong sql_mode, const char *params, const char *returns, @@ -406,6 +410,15 @@ db_load_routine(THD *thd, int type, sp_name *name, sp_head **sphp, ulong old_sql_mode= thd->variables.sql_mode; ha_rows old_select_limit= thd->variables.select_limit; sp_rcontext *old_spcont= thd->spcont; + + char definer_user_name_holder[USERNAME_LENGTH + 1]; + LEX_STRING_WITH_INIT definer_user_name(definer_user_name_holder, + USERNAME_LENGTH); + + char definer_host_name_holder[HOSTNAME_LENGTH + 1]; + LEX_STRING_WITH_INIT definer_host_name(definer_host_name_holder, + HOSTNAME_LENGTH); + int ret; thd->variables.sql_mode= sql_mode; @@ -414,14 +427,25 @@ db_load_routine(THD *thd, int type, sp_name *name, sp_head **sphp, thd->lex= &newlex; newlex.current_select= NULL; + parse_user(definer, strlen(definer), + definer_user_name.str, &definer_user_name.length, + definer_host_name.str, &definer_host_name.length); + defstr.set_charset(system_charset_info); + + /* + We have to add DEFINER clause and provide proper routine characterstics in + routine definition statement that we build here to be able to use this + definition for SHOW CREATE PROCEDURE later. + */ + if (!create_string(thd, &defstr, type, name, params, strlen(params), returns, strlen(returns), body, strlen(body), - &chistics)) + &chistics, &definer_user_name, &definer_host_name)) { ret= SP_INTERNAL_ERROR; goto end; @@ -449,7 +473,7 @@ db_load_routine(THD *thd, int type, sp_name *name, sp_head **sphp, if (dbchanged && (ret= mysql_change_db(thd, olddb, 1))) goto end; *sphp= newlex.sphead; - (*sphp)->set_definer((char*) definer, (uint) strlen(definer)); + (*sphp)->set_definer(&definer_user_name, &definer_host_name); (*sphp)->set_info(created, modified, &chistics, sql_mode); (*sphp)->optimize(); } @@ -500,8 +524,10 @@ db_create_routine(THD *thd, int type, sp_head *sp) else { restore_record(table, s->default_values); // Get default values for fields - strxmov(definer, thd->security_ctx->priv_user, "@", - thd->security_ctx->priv_host, NullS); + + /* NOTE: all needed privilege checks have been already done. */ + strxmov(definer, thd->lex->definer->user.str, "@", + thd->lex->definer->host.str, NullS); if (table->s->fields != MYSQL_PROC_FIELD_COUNT) { @@ -592,8 +618,17 @@ db_create_routine(THD *thd, int type, sp_head *sp) else if (mysql_bin_log.is_open()) { thd->clear_error(); + + String log_query; + log_query.set_charset(system_charset_info); + log_query.append(STRING_WITH_LEN("CREATE ")); + append_definer(thd, &log_query, &thd->lex->definer->user, + &thd->lex->definer->host); + log_query.append(thd->lex->stmt_definition_begin); + /* Such a statement can always go directly to binlog, no trans cache */ - Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE); + Query_log_event qinfo(thd, log_query.c_ptr(), log_query.length(), 0, + FALSE); mysql_bin_log.write(&qinfo); } @@ -1723,14 +1758,18 @@ create_string(THD *thd, String *buf, const char *params, ulong paramslen, const char *returns, ulong returnslen, const char *body, ulong bodylen, - st_sp_chistics *chistics) + st_sp_chistics *chistics, + const LEX_STRING *definer_user, + const LEX_STRING *definer_host) { /* Make some room to begin with */ if (buf->alloc(100 + name->m_qname.length + paramslen + returnslen + bodylen + - chistics->comment.length)) + chistics->comment.length + 10 /* length of " DEFINER= "*/ + + USER_HOST_BUFF_SIZE)) return FALSE; buf->append(STRING_WITH_LEN("CREATE ")); + append_definer(thd, buf, definer_user, definer_host); if (type == TYPE_ENUM_FUNCTION) buf->append(STRING_WITH_LEN("FUNCTION ")); else diff --git a/sql/sp_head.cc b/sql/sp_head.cc index f7572a374f1..b8b7ee2f78b 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -1810,19 +1810,27 @@ sp_head::set_info(longlong created, longlong modified, void sp_head::set_definer(const char *definer, uint definerlen) { - uint user_name_len; - char user_name_str[USERNAME_LENGTH + 1]; - uint host_name_len; - char host_name_str[HOSTNAME_LENGTH + 1]; + char user_name_holder[USERNAME_LENGTH + 1]; + LEX_STRING_WITH_INIT user_name(user_name_holder, USERNAME_LENGTH); - parse_user(definer, definerlen, user_name_str, &user_name_len, - host_name_str, &host_name_len); + char host_name_holder[HOSTNAME_LENGTH + 1]; + LEX_STRING_WITH_INIT host_name(host_name_holder, HOSTNAME_LENGTH); - m_definer_user.str= strmake_root(mem_root, user_name_str, user_name_len); - m_definer_user.length= user_name_len; + parse_user(definer, definerlen, user_name.str, &user_name.length, + host_name.str, &host_name.length); - m_definer_host.str= strmake_root(mem_root, host_name_str, host_name_len); - m_definer_host.length= host_name_len; + set_definer(&user_name, &host_name); +} + + +void +sp_head::set_definer(const LEX_STRING *user_name, const LEX_STRING *host_name) +{ + m_definer_user.str= strmake_root(mem_root, user_name->str, user_name->length); + m_definer_user.length= user_name->length; + + m_definer_host.str= strmake_root(mem_root, host_name->str, host_name->length); + m_definer_host.length= host_name->length; } diff --git a/sql/sp_head.h b/sql/sp_head.h index a4dd68ee4a3..64d9167fb17 100644 --- a/sql/sp_head.h +++ b/sql/sp_head.h @@ -302,6 +302,7 @@ public: st_sp_chistics *chistics, ulong sql_mode); void set_definer(const char *definer, uint definerlen); + void set_definer(const LEX_STRING *user_name, const LEX_STRING *host_name); void reset_thd_mem_root(THD *thd); diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 7b2ea359fb2..8db059ae2fa 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -909,12 +909,16 @@ typedef struct st_lex SQL_LIST trg_table_fields; /* - trigger_definition_begin points to the beginning of the word "TRIGGER" in - CREATE TRIGGER statement. This is used to add possibly omitted DEFINER - clause to the trigger definition statement before dumping it to the - binlog. + stmt_definition_begin is intended to point to the next word after + DEFINER-clause in the following statements: + - CREATE TRIGGER (points to "TRIGGER"); + - CREATE PROCEDURE (points to "PROCEDURE"); + - CREATE FUNCTION (points to "FUNCTION" or "AGGREGATE"); + + This pointer is required to add possibly omitted DEFINER-clause to the + DDL-statement before dumping it to the binlog. */ - const char *trigger_definition_begin; + const char *stmt_definition_begin; /* If non-0 then indicates that query requires prelocking and points to diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 13d814bdf42..527a6a67811 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -4145,6 +4145,90 @@ end_with_restore_list: } #endif + /* + If the definer is not specified, this means that CREATE-statement missed + DEFINER-clause. DEFINER-clause can be missed in two cases: + + - The user submitted a statement w/o the clause. This is a normal + case, we should assign CURRENT_USER as definer. + + - Our slave received an updated from the master, that does not + replicate definer for stored rountines. We should also assign + CURRENT_USER as definer here, but also we should mark this routine + as NON-SUID. This is essential for the sake of backward + compatibility. + + The problem is the slave thread is running under "special" user (@), + that actually does not exist. In the older versions we do not fail + execution of a stored routine if its definer does not exist and + continue the execution under the authorization of the invoker + (BUG#13198). And now if we try to switch to slave-current-user (@), + we will fail. + + Actually, this leads to the inconsistent state of master and + slave (different definers, different SUID behaviour), but it seems, + this is the best we can do. + */ + + if (!lex->definer) + { + bool res= FALSE; + 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) + { + /* Error has been already reported. */ + delete lex->sphead; + lex->sphead= 0; + goto error; + } + + if (thd->slave_thread) + lex->sphead->m_chistics->suid= SP_IS_NOT_SUID; + } + + /* + If the specified definer differs from the current user, we should check + that the current user has SUPER privilege (in order to create a stored + routine under another user one must have SUPER privilege). + */ + + else 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 (check_global_access(thd, SUPER_ACL)) + { + my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), "SUPER"); + delete lex->sphead; + lex->sphead= 0; + goto error; + } + } + + /* Check that the specified definer exists. Emit a warning if not. */ + +#ifndef NO_EMBEDDED_ACCESS_CHECKS + if (!is_acl_user(lex->definer->host.str, + lex->definer->user.str)) + { + push_warning_printf(thd, + MYSQL_ERROR::WARN_LEVEL_NOTE, + ER_NO_SUCH_USER, + ER(ER_NO_SUCH_USER), + lex->definer->user.str, + lex->definer->host.str); + } +#endif /* NO_EMBEDDED_ACCESS_CHECKS */ + /* We need to copy name and db in order to use them for check_routine_access which is called after lex->sphead has diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc index 9ccc70d8ccd..1fe5bf63756 100644 --- a/sql/sql_trigger.cc +++ b/sql/sql_trigger.cc @@ -275,7 +275,7 @@ end: append_definer(thd, &log_query, &definer_user, &definer_host); } - log_query.append(thd->lex->trigger_definition_begin); + log_query.append(thd->lex->stmt_definition_begin); } /* Such a statement can always go directly to binlog, no trans cache. */ diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 7e93b9c8e73..36a684f1d4a 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -833,8 +833,9 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); sp_c_chistics sp_a_chistics sp_chistic sp_c_chistic xa load_data opt_field_or_var_spec fields_or_vars opt_load_data_set_spec definer view_replace_or_algorithm view_replace view_algorithm_opt - view_algorithm view_or_trigger_tail view_suid view_tail view_list_opt - view_list view_select view_check_option trigger_tail + view_algorithm view_or_trigger_or_sp view_or_trigger_or_sp_tail + view_suid view_tail view_list_opt view_list view_select + view_check_option trigger_tail sp_tail END_OF_INPUT %type call sp_proc_stmts sp_proc_stmts1 sp_proc_stmt @@ -1189,81 +1190,13 @@ create: lex->name=$4.str; lex->create_info.options=$3; } - | CREATE udf_func_type FUNCTION_SYM sp_name - { - LEX *lex=Lex; - lex->spname= $4; - lex->udf.type= $2; - } - create_function_tail - {} - | CREATE PROCEDURE sp_name - { - LEX *lex= Lex; - sp_head *sp; - - if (lex->sphead) - { - my_error(ER_SP_NO_RECURSIVE_CREATE, MYF(0), "PROCEDURE"); - YYABORT; - } - /* Order is important here: new - reset - init */ - sp= new sp_head(); - sp->reset_thd_mem_root(YYTHD); - sp->init(lex); - - sp->m_type= TYPE_ENUM_PROCEDURE; - lex->sphead= sp; - /* - * We have to turn of CLIENT_MULTI_QUERIES while parsing a - * stored procedure, otherwise yylex will chop it into pieces - * at each ';'. - */ - sp->m_old_cmq= YYTHD->client_capabilities & CLIENT_MULTI_QUERIES; - YYTHD->client_capabilities &= (~CLIENT_MULTI_QUERIES); - } - '(' - { - LEX *lex= Lex; - - lex->sphead->m_param_begin= lex->tok_start+1; - } - sp_pdparam_list - ')' - { - LEX *lex= Lex; - - lex->sphead->m_param_end= lex->tok_start; - bzero((char *)&lex->sp_chistics, sizeof(st_sp_chistics)); - } - sp_c_chistics - { - LEX *lex= Lex; - - lex->sphead->m_chistics= &lex->sp_chistics; - lex->sphead->m_body_begin= lex->tok_start; - } - sp_proc_stmt - { - LEX *lex= Lex; - sp_head *sp= lex->sphead; - - if (sp->check_backpatch(YYTHD)) - YYABORT; - sp->init_strings(YYTHD, lex, $3); - lex->sql_command= SQLCOM_CREATE_PROCEDURE; - /* Restore flag if it was cleared above */ - if (sp->m_old_cmq) - YYTHD->client_capabilities |= CLIENT_MULTI_QUERIES; - sp->restore_thd_mem_root(YYTHD); - } | CREATE { Lex->create_view_mode= VIEW_CREATE_NEW; Lex->create_view_algorithm= VIEW_ALGORITHM_UNDEFINED; Lex->create_view_suid= TRUE; } - view_or_trigger + view_or_trigger_or_sp {} | CREATE USER clear_privileges grant_list { @@ -8959,6 +8892,34 @@ subselect_end: lex->nest_level--; }; +/************************************************************************** + + CREATE VIEW | TRIGGER | PROCEDURE statements. + +**************************************************************************/ + +view_or_trigger_or_sp: + definer view_or_trigger_or_sp_tail + {} + | view_replace_or_algorithm definer view_tail + {} + ; + +view_or_trigger_or_sp_tail: + view_tail + {} + | trigger_tail + {} + | sp_tail + {} + ; + +/************************************************************************** + + DEFINER clause support. + +**************************************************************************/ + definer: /* empty */ { @@ -8985,7 +8946,7 @@ definer: /************************************************************************** - CREATE VIEW statement options. + CREATE VIEW statement parts. **************************************************************************/ @@ -9019,20 +8980,6 @@ view_algorithm_opt: {} ; -view_or_trigger: - definer view_or_trigger_tail - {} - | view_replace_or_algorithm definer view_tail - {} - ; - -view_or_trigger_tail: - view_tail - {} - | trigger_tail - {} - ; - view_suid: /* empty */ { Lex->create_view_suid= TRUE; } @@ -9131,7 +9078,7 @@ trigger_tail: sp->reset_thd_mem_root(YYTHD); sp->init(lex); - lex->trigger_definition_begin= $2; + lex->stmt_definition_begin= $2; lex->ident.str= $7; lex->ident.length= $9 - $7; @@ -9180,6 +9127,87 @@ trigger_tail: } ; +/************************************************************************** + + CREATE FUNCTION | PROCEDURE statements parts. + +**************************************************************************/ + +sp_tail: + udf_func_type remember_name FUNCTION_SYM sp_name + { + LEX *lex=Lex; + lex->udf.type= $1; + lex->stmt_definition_begin= $2; + lex->spname= $4; + } + create_function_tail + {} + | PROCEDURE remember_name sp_name + { + LEX *lex= Lex; + sp_head *sp; + + if (lex->sphead) + { + my_error(ER_SP_NO_RECURSIVE_CREATE, MYF(0), "PROCEDURE"); + YYABORT; + } + + lex->stmt_definition_begin= $2; + + /* Order is important here: new - reset - init */ + sp= new sp_head(); + sp->reset_thd_mem_root(YYTHD); + sp->init(lex); + + sp->m_type= TYPE_ENUM_PROCEDURE; + lex->sphead= sp; + /* + * We have to turn of CLIENT_MULTI_QUERIES while parsing a + * stored procedure, otherwise yylex will chop it into pieces + * at each ';'. + */ + sp->m_old_cmq= YYTHD->client_capabilities & CLIENT_MULTI_QUERIES; + YYTHD->client_capabilities &= (~CLIENT_MULTI_QUERIES); + } + '(' + { + LEX *lex= Lex; + + lex->sphead->m_param_begin= lex->tok_start+1; + } + sp_pdparam_list + ')' + { + LEX *lex= Lex; + + lex->sphead->m_param_end= lex->tok_start; + bzero((char *)&lex->sp_chistics, sizeof(st_sp_chistics)); + } + sp_c_chistics + { + LEX *lex= Lex; + + lex->sphead->m_chistics= &lex->sp_chistics; + lex->sphead->m_body_begin= lex->tok_start; + } + sp_proc_stmt + { + LEX *lex= Lex; + sp_head *sp= lex->sphead; + + if (sp->check_backpatch(YYTHD)) + YYABORT; + sp->init_strings(YYTHD, lex, $3); + lex->sql_command= SQLCOM_CREATE_PROCEDURE; + /* Restore flag if it was cleared above */ + if (sp->m_old_cmq) + YYTHD->client_capabilities |= CLIENT_MULTI_QUERIES; + sp->restore_thd_mem_root(YYTHD); + } + ; + /*************************************************************************/ xa: XA_SYM begin_or_start xid opt_join_or_resume From c4cbe46bc73b680532f8a6cf2dff20f0e7afa341 Mon Sep 17 00:00:00 2001 From: "anozdrin@mysql.com" <> Date: Thu, 2 Mar 2006 16:23:42 +0300 Subject: [PATCH 4/7] Fix for BUG#13198: SP executes if definer does not exist. Basically, this fix contains a test case and removing of a workaround for replication. This fix became possible after pushing WL#2897 (Complete definer support in stored routines). --- mysql-test/r/sp-security.result | 44 +++++++++++++++ mysql-test/t/sp-security.test | 95 +++++++++++++++++++++++++++++++++ sql/sp_head.cc | 15 ------ 3 files changed, 139 insertions(+), 15 deletions(-) diff --git a/mysql-test/r/sp-security.result b/mysql-test/r/sp-security.result index 90466bfcfc4..896b6fa572c 100644 --- a/mysql-test/r/sp-security.result +++ b/mysql-test/r/sp-security.result @@ -375,3 +375,47 @@ RETURN 3 DROP USER mysqltest_1@localhost; DROP USER mysqltest_2@localhost; DROP DATABASE mysqltest; + +---> connection: root +DROP DATABASE IF EXISTS mysqltest; +CREATE DATABASE mysqltest; +CREATE USER mysqltest_1@localhost; +GRANT ALL PRIVILEGES ON mysqltest.* TO mysqltest_1@localhost; +CREATE USER mysqltest_2@localhost; +GRANT ALL PRIVILEGES ON mysqltest.* TO mysqltest_2@localhost; + +---> connection: mysqltest_1_con +use mysqltest; +CREATE PROCEDURE bug13198_p1() +SELECT 1; +CREATE FUNCTION bug13198_f1() RETURNS INT +RETURN 1; +CALL bug13198_p1(); +1 +1 +SELECT bug13198_f1(); +bug13198_f1() +1 + +---> connection: mysqltest_2_con +use mysqltest; +CALL bug13198_p1(); +1 +1 +SELECT bug13198_f1(); +bug13198_f1() +1 + +---> connection: root +DROP USER mysqltest_1@localhost; + +---> connection: mysqltest_2_con +use mysqltest; +CALL bug13198_p1(); +ERROR HY000: There is no 'mysqltest_1'@'localhost' registered +SELECT bug13198_f1(); +ERROR HY000: There is no 'mysqltest_1'@'localhost' registered + +---> connection: root +DROP USER mysqltest_2@localhost; +DROP DATABASE mysqltest; diff --git a/mysql-test/t/sp-security.test b/mysql-test/t/sp-security.test index b466d2125d4..f369dc64b0e 100644 --- a/mysql-test/t/sp-security.test +++ b/mysql-test/t/sp-security.test @@ -647,5 +647,100 @@ DROP USER mysqltest_2@localhost; DROP DATABASE mysqltest; +--disconnect mysqltest_1_con +--disconnect mysqltest_2_con + + +# +# BUG#13198: SP executes if definer does not exist +# + +# Prepare environment. + +--echo +--echo ---> connection: root +--connection con1root + +--disable_warnings +DROP DATABASE IF EXISTS mysqltest; +--enable_warnings + +CREATE DATABASE mysqltest; + +CREATE USER mysqltest_1@localhost; +GRANT ALL PRIVILEGES ON mysqltest.* TO mysqltest_1@localhost; + +CREATE USER mysqltest_2@localhost; +GRANT ALL PRIVILEGES ON mysqltest.* TO mysqltest_2@localhost; + +--connect (mysqltest_1_con,localhost,mysqltest_1,,mysqltest) +--connect (mysqltest_2_con,localhost,mysqltest_2,,mysqltest) + +# Create a procedure/function under u1. + +--echo +--echo ---> connection: mysqltest_1_con +--connection mysqltest_1_con + +use mysqltest; + +CREATE PROCEDURE bug13198_p1() + SELECT 1; + +CREATE FUNCTION bug13198_f1() RETURNS INT + RETURN 1; + +CALL bug13198_p1(); + +SELECT bug13198_f1(); + +# Check that u2 can call the procedure/function. + +--echo +--echo ---> connection: mysqltest_2_con +--connection mysqltest_2_con + +use mysqltest; + +CALL bug13198_p1(); + +SELECT bug13198_f1(); + +# Drop user u1 (definer of the object); + +--echo +--echo ---> connection: root +--connection con1root + +--disconnect mysqltest_1_con + +DROP USER mysqltest_1@localhost; + +# Check that u2 can not call the procedure/function. + +--echo +--echo ---> connection: mysqltest_2_con +--connection mysqltest_2_con + +use mysqltest; + +--error ER_NO_SUCH_USER +CALL bug13198_p1(); + +--error ER_NO_SUCH_USER +SELECT bug13198_f1(); + +# Cleanup. + +--echo +--echo ---> connection: root +--connection con1root + +--disconnect mysqltest_2_con + +DROP USER mysqltest_2@localhost; + +DROP DATABASE mysqltest; + # End of 5.0 bugs. diff --git a/sql/sp_head.cc b/sql/sp_head.cc index b8b7ee2f78b..c0b566f9b9b 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -3171,24 +3171,9 @@ sp_change_security_context(THD *thd, sp_head *sp, Security_context **backup) sp->m_definer_host.str, sp->m_db.str)) { -#ifdef NOT_YET_REPLICATION_SAFE - /* - Until we don't properly replicate information about stored routine - definer with stored routine creation statement all stored routines - on slave are created under ''@'' definer. Therefore we won't be able - to run any routine which was replicated from master on slave server - if we emit error here. This will cause big problems for users - who use slave for fail-over. So until we fully implement WL#2897 - "Complete definer support in the stored routines" we run suid - stored routines for which we were unable to find definer under - invoker security context. - */ my_error(ER_NO_SUCH_USER, MYF(0), sp->m_definer_user.str, sp->m_definer_host.str); return TRUE; -#else - return FALSE; -#endif } *backup= thd->security_ctx; thd->security_ctx= &sp->m_security_ctx; From 615357b96c91e54b2fc9031e16a9182c43b46cf9 Mon Sep 17 00:00:00 2001 From: "anozdrin@mysql.com" <> Date: Wed, 8 Mar 2006 00:13:27 +0300 Subject: [PATCH 5/7] After-merge fix: fix the test case for BUG#16266 to query only its own tables to prevent getting tables from others. --- mysql-test/r/rpl_trigger.result | 8 ++++---- mysql-test/t/rpl_trigger.test | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/mysql-test/r/rpl_trigger.result b/mysql-test/r/rpl_trigger.result index b9b20b82acd..3e4a3349e13 100644 --- a/mysql-test/r/rpl_trigger.result +++ b/mysql-test/r/rpl_trigger.result @@ -862,8 +862,8 @@ 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 +SHOW TABLES LIKE 't_'; +Tables_in_test (t_) t1 t2 SHOW TRIGGERS; @@ -891,8 +891,8 @@ DROP TABLE t1; DROP TABLE t2; STOP SLAVE; RESET SLAVE; -SHOW TABLES; -Tables_in_test +SHOW TABLES LIKE 't_'; +Tables_in_test (t_) SHOW TRIGGERS; Trigger Event Table Statement Timing Created sql_mode Definer RESET MASTER; diff --git a/mysql-test/t/rpl_trigger.test b/mysql-test/t/rpl_trigger.test index 90822e0654c..35f0a0b0a4b 100644 --- a/mysql-test/t/rpl_trigger.test +++ b/mysql-test/t/rpl_trigger.test @@ -301,7 +301,7 @@ SELECT MASTER_POS_WAIT('master-bin.000001', 513) >= 0; # Check that the replication succeeded. -SHOW TABLES; +SHOW TABLES LIKE 't_'; SHOW TRIGGERS; SELECT * FROM t1; SELECT * FROM t2; @@ -326,7 +326,7 @@ RESET SLAVE; # The master should be clean. connection master; -SHOW TABLES; +SHOW TABLES LIKE 't_'; SHOW TRIGGERS; RESET MASTER; From e37f80a1ace70526e29d27e978fdd007891dbb77 Mon Sep 17 00:00:00 2001 From: "msvensson@shellback.(none)" <> Date: Wed, 8 Mar 2006 16:57:42 +0100 Subject: [PATCH 6/7] BUG#17574 Detect cluster start failure and "fail" ndb_* tests - Fix problems that occur when ndbcluster is not supported --- mysql-test/lib/mtr_cases.pl | 7 +++++++ mysql-test/mysql-test-run.pl | 4 ++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/mysql-test/lib/mtr_cases.pl b/mysql-test/lib/mtr_cases.pl index fc32c56536f..51eed62c1ff 100644 --- a/mysql-test/lib/mtr_cases.pl +++ b/mysql-test/lib/mtr_cases.pl @@ -258,6 +258,13 @@ sub collect_one_test_case($$$$$$$) { $tinfo->{'ndb_test'}= 1; if ( $::opt_skip_ndbcluster ) { + # Skip all ndb tests + $tinfo->{'skip'}= 1; + return; + } + if ( ! $::opt_with_ndbcluster ) + { + # Ndb is not supported, skip them $tinfo->{'skip'}= 1; return; } diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index c105ace789f..75491795a60 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -1840,7 +1840,7 @@ sub run_testcase ($) { if ( $tinfo->{'master_restart'} or $master->[0]->{'running_master_is_special'} or # Stop if cluster is started but test cases does not need cluster - ( $tinfo->{'ndb_test'} != $using_ndbcluster_master ) ) + ( $opt_with_ndbcluster && $tinfo->{'ndb_test'} != $using_ndbcluster_master ) ) { stop_masters(); $master->[0]->{'running_master_is_special'}= 0; # Forget why we stopped @@ -1889,7 +1889,7 @@ sub run_testcase ($) { if ( $tinfo->{'component_id'} eq 'mysqld' and ! $opt_local_master ) { - if ( $master->[0]->{'ndbcluster'} ) + if ( $opt_with_ndbcluster and $master->[0]->{'ndbcluster'} ) { # Cluster is not started From be527f3070c67455bc7d7a5e9361ee2b1f09461a Mon Sep 17 00:00:00 2001 From: "anozdrin@mysql.com" <> Date: Thu, 9 Mar 2006 03:10:39 +0300 Subject: [PATCH 7/7] Fix for multiple test failures on some platforms. --- sql/sql_lex.cc | 3 ++- sql/sql_show.cc | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 1ed8887a878..49b0c70ff03 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -223,7 +223,7 @@ static int find_keyword(LEX *lex, uint len, bool function) SYNOPSIS is_keyword() - name checked name + name checked name (must not be empty) len length of checked name RETURN VALUES @@ -233,6 +233,7 @@ static int find_keyword(LEX *lex, uint len, bool function) bool is_keyword(const char *name, uint len) { + DBUG_ASSERT(len != 0); return get_hash_symbol(name,len,0)!=0; } diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 8920efa87ab..5c9d13cac3e 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -713,7 +713,8 @@ append_identifier(THD *thd, String *packet, const char *name, uint length) int get_quote_char_for_identifier(THD *thd, const char *name, uint length) { - if (!is_keyword(name,length) && + if (!length || + !is_keyword(name,length) && !require_quotes(name, length) && !(thd->options & OPTION_QUOTE_SHOW_CREATE)) return EOF;