From a44a924a407ae1f1135a14ee750468e6fc88d9b0 Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 1 Mar 2006 14:13:07 +0300 Subject: [PATCH 01/18] 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. --- 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 fad27ebf573d398e7d725ae95219e2f9a583a828 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 2 Mar 2006 14:17:13 +0300 Subject: [PATCH 02/18] 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: Added a test case for BUG#16777. mysql-test/t/skip_grants.test: Added a test case for BUG#16777. sql/mysql_priv.h: Do not check that strlen(host) > 0 in get_default_definer(). sql/sql_parse.cc: Do not check that strlen(host) > 0 in get_default_definer(). sql/sql_view.cc: Do not check that strlen(host) > 0 in get_default_definer(). --- 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 9a1fed13eec0fec9ac84e70ceade04372a93b64d Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 2 Mar 2006 15:18:49 +0300 Subject: [PATCH 03/18] 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: Updated the code for dumping stored routines: cover DEFINER-clause into version-specific comment. mysql-test/r/gis.result: Updated result file after adding DEFINER-clause. mysql-test/r/information_schema.result: Updated result file after adding DEFINER-clause. mysql-test/r/mysqldump.result: Updated result file after adding DEFINER-clause. mysql-test/r/rpl_ddl.result: Updated result file after adding DEFINER-clause. mysql-test/r/rpl_sp.result: Updated result file after adding DEFINER-clause. mysql-test/r/rpl_trigger.result: Updated result file after adding DEFINER-clause. mysql-test/r/sp-security.result: Updated result file after adding DEFINER-clause. mysql-test/r/sp.result: Updated result file after adding DEFINER-clause. mysql-test/r/sql_mode.result: Updated result file after adding DEFINER-clause. mysql-test/t/sp-security.test: Updated result file after adding DEFINER-clause. sql/sp.cc: Added DEFINER-clause. sql/sp_head.cc: Added a new convenient variant of set_definer() operation. sql/sp_head.h: Updated result file after adding DEFINER-clause. sql/sql_lex.h: Renamed trigger_definition_begin into stmt_definition_begin to be used for triggers and stored routines. sql/sql_parse.cc: Check DEFINER-clause. sql/sql_trigger.cc: Renamed trigger_definition_begin into stmt_definition_begin to be used for triggers and stored routines. sql/sql_yacc.yy: Added DEFINER-clause. --- 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 3dd927cf1cf4f666d55144862e52e9479fe6c4ff Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 2 Mar 2006 16:23:42 +0300 Subject: [PATCH 04/18] 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: Updated the result file to contain results of test for BUG#13198. mysql-test/t/sp-security.test: Added a test case for BUG#13198. sql/sp_head.cc: Removed the workaround for replication, since WL#2897 is pushed and now definer attribute/clause is fully supported 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 cc7a75e6fdf5f4253c550ee3d5d93c46b96f5678 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 3 Mar 2006 16:19:57 +0300 Subject: [PATCH 05/18] Fixed bug#17726: Not checked empty list caused endless loop When the Item_cond::fix_fields() function reduces cond tree, it in loop scans it's own list and when it founds Item_cond with same function (AND or OR) it does next things: 1) replaces that item with item's list. 2) empties item's list. Due to this operation is done twice - for update and for view, at the update phase cond's list of lower view is already empty. Empty list returns ref to itself, thus making endless loop by replacing list with itself, emptying, replacing again and so on. This results in server hung up. To the Item_cond::fix_fields() function added check that ensures that list being replaced with isn't empty. mysql-test/t/view.test: Added test for bug#17726: Not checked empty list caused endless loop mysql-test/r/view.result: Added test for bug#17726: Not checked empty list caused endless loop sql/item_cmpfunc.cc: Fixed bug#17726: Not checked empty list caused endless loop To the Item_cond::fix_fields() function added check that ensures that list being replaced with isn't empty. --- mysql-test/r/view.result | 14 ++++++++++++++ mysql-test/t/view.test | 14 ++++++++++++++ sql/item_cmpfunc.cc | 3 ++- 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/mysql-test/r/view.result b/mysql-test/r/view.result index c9a79e50cc3..7678c70bda2 100644 --- a/mysql-test/r/view.result +++ b/mysql-test/r/view.result @@ -2539,3 +2539,17 @@ drop view v1; // View Create View v1 CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v1` AS select sql_no_cache `test`.`t1`.`id` AS `id` from `t1` +create table t1(f1 int, f2 int); +create view v1 as select ta.f1 as a, tb.f1 as b from t1 ta, t1 tb where ta.f1=tb +.f1 and ta.f2=tb.f2; +insert into t1 values(1,1),(2,2); +create view v2 as select * from v1 where a > 1 with check option; +select * from v2; +a b +2 2 +update v2 set b=3 where a=2; +select * from v2; +a b +3 3 +drop view v2, v1; +drop table t1; diff --git a/mysql-test/t/view.test b/mysql-test/t/view.test index b7e678c6f77..e17e2b98527 100644 --- a/mysql-test/t/view.test +++ b/mysql-test/t/view.test @@ -2385,3 +2385,17 @@ show create view v1; drop view v1; // delimiter ;// + +# +# Bug#17726 Not checked empty list caused endless loop +# +create table t1(f1 int, f2 int); +create view v1 as select ta.f1 as a, tb.f1 as b from t1 ta, t1 tb where ta.f1=tb +.f1 and ta.f2=tb.f2; +insert into t1 values(1,1),(2,2); +create view v2 as select * from v1 where a > 1 with check option; +select * from v2; +update v2 set b=3 where a=2; +select * from v2; +drop view v2, v1; +drop table t1; diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 7ba8a536ac7..4cd460cfeb0 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -2553,7 +2553,8 @@ Item_cond::fix_fields(THD *thd, Item **ref) { table_map tmp_table_map; while (item->type() == Item::COND_ITEM && - ((Item_cond*) item)->functype() == functype()) + ((Item_cond*) item)->functype() == functype() && + !((Item_cond*) item)->list.is_empty()) { // Identical function li.replace(((Item_cond*) item)->list); ((Item_cond*) item)->list.empty(); From a8331c7326dde37ae950ba33ad47257957fc581b Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 8 Mar 2006 00:13:27 +0300 Subject: [PATCH 06/18] 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: Update the result file. mysql-test/t/rpl_trigger.test: Fix the test to query only its own tables to prevent getting tables from other tests. --- 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 acb53e4ec4509d326250ee8e97d3c1404832e64b Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 8 Mar 2006 16:57:42 +0100 Subject: [PATCH 07/18] 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: Marked test to be skipped if it is a ndb test but ndb is not supported mysql-test/mysql-test-run.pl: Check "opt_with_ndbcluster" before deciding to stop masters and when dciding if cluster needs to be started. --- 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 bc6b7859ae61fa7977d8134c0e890e3f82a31848 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 9 Mar 2006 03:10:39 +0300 Subject: [PATCH 08/18] Fix for multiple test failures on some platforms. sql/sql_lex.cc: Added a debug assertion that the passed string is not empty. sql/sql_show.cc: Do not quote empty string. --- 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; From a3b0720bf0f59d2cc9fce9d5c08bcb6ee1867e73 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 9 Mar 2006 03:37:25 +0100 Subject: [PATCH 09/18] New test to satisfy some of the 5.1 CR test plan mysql-test/t/rpl_ndb_dd_advance.test: New test case to test advance use of Cluster Disk Data and replication mysql-test/include/ndb_restore_slave_eoption.inc: New support file to help test advance use of Cluster Disk Data and replication mysql-test/r/rpl_ndb_dd_advance.result: New result file for test of advance use of Cluster Disk Data and replication --- .../include/ndb_restore_slave_eoption.inc | 11 + mysql-test/r/rpl_ndb_dd_advance.result | 421 +++++++++++++ mysql-test/t/rpl_ndb_dd_advance.test | 565 ++++++++++++++++++ 3 files changed, 997 insertions(+) create mode 100644 mysql-test/include/ndb_restore_slave_eoption.inc create mode 100644 mysql-test/r/rpl_ndb_dd_advance.result create mode 100644 mysql-test/t/rpl_ndb_dd_advance.test diff --git a/mysql-test/include/ndb_restore_slave_eoption.inc b/mysql-test/include/ndb_restore_slave_eoption.inc new file mode 100644 index 00000000000..f1f6cf96881 --- /dev/null +++ b/mysql-test/include/ndb_restore_slave_eoption.inc @@ -0,0 +1,11 @@ +###################################################### +# By JBM 2006-03-08 So that the code is not repeated # +# in test cases and can be reused. # +###################################################### + +--exec $NDB_TOOLS_DIR/ndb_restore --no-defaults --ndb-connectstring="localhost:$NDBCLUSTER_PORT_SLAVE" -p 8 -b $the_backup_id -n 1 -m -r --print --print_meta $NDB_BACKUP_DIR/BACKUP/BACKUP-$the_backup_id >> $NDB_TOOLS_OUTPUT + +--exec $NDB_TOOLS_DIR/ndb_restore --no-defaults --ndb-connectstring="localhost:$NDBCLUSTER_PORT_SLAVE" -p 8 -b $the_backup_id -n 2 -r -e --print --print_meta $NDB_BACKUP_DIR/BACKUP/BACKUP-$the_backup_id >> $NDB_TOOLS_OUTPUT + + + diff --git a/mysql-test/r/rpl_ndb_dd_advance.result b/mysql-test/r/rpl_ndb_dd_advance.result new file mode 100644 index 00000000000..64820b6f9e5 --- /dev/null +++ b/mysql-test/r/rpl_ndb_dd_advance.result @@ -0,0 +1,421 @@ +stop slave; +drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9; +reset master; +reset slave; +drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9; +start slave; +DROP TABLE IF EXISTS t1, t2, t3; +***** Test 1 RPL of CDD and Alter ***** +***** Test 1 setup ***** +CREATE LOGFILE GROUP lg1 +ADD UNDOFILE 'undofile.dat' +INITIAL_SIZE 16M +UNDO_BUFFER_SIZE = 1M +ENGINE=NDB; +ALTER LOGFILE GROUP lg1 +ADD UNDOFILE 'undofile02.dat' +INITIAL_SIZE 4M +ENGINE=NDB; +CREATE TABLESPACE ts1 +ADD DATAFILE 'datafile.dat' +USE LOGFILE GROUP lg1 +INITIAL_SIZE 12M +ENGINE=NDB; +ALTER TABLESPACE ts1 +ADD DATAFILE 'datafile02.dat' +INITIAL_SIZE 4M +ENGINE=NDB; +CREATE TABLE t1 +(c1 INT NOT NULL PRIMARY KEY, +c2 INT NOT NULL, +c3 INT NOT NULL) +TABLESPACE ts1 STORAGE DISK +ENGINE=NDB; +***** insert some data ***** +***** Select from Master ***** +SELECT * FROM t1 ORDER BY c1 LIMIT 5; +c1 c2 c3 +1 2 4 +2 4 5 +3 6 6 +4 8 7 +5 10 8 +***** Select from Slave ***** +SELECT * FROM t1 ORDER BY c1 LIMIT 5; +c1 c2 c3 +1 2 4 +2 4 5 +3 6 6 +4 8 7 +5 10 8 +FILE_NAME FILE_TYPE TABLESPACE_NAME LOGFILE_GROUP_NAME +datafile02.dat DATAFILE ts1 lg1 +datafile.dat DATAFILE ts1 lg1 +undofile.dat UNDO LOG ts1 lg1 +undofile02.dat UNDO LOG ts1 lg1 +**** Do First Set of ALTERs in the master table **** +CREATE INDEX t1_i ON t1(c2, c3); +ALTER TABLE t1 ADD c4 TIMESTAMP; +ALTER TABLE t1 ADD c5 DOUBLE; +ALTER TABLE t1 ADD INDEX (c5); +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `c1` int(11) NOT NULL, + `c2` int(11) NOT NULL, + `c3` int(11) NOT NULL, + `c4` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `c5` double DEFAULT NULL, + PRIMARY KEY (`c1`), + KEY `t1_i` (`c2`,`c3`), + KEY `c5` (`c5`) +) ENGINE=ndbcluster DEFAULT CHARSET=latin1 PARTITION BY KEY () +**** Show first set of ALTERs on SLAVE **** +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `c1` int(11) NOT NULL, + `c2` int(11) NOT NULL, + `c3` int(11) NOT NULL, + `c4` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `c5` double DEFAULT NULL, + PRIMARY KEY (`c1`), + KEY `t1_i` (`c2`,`c3`), + KEY `c5` (`c5`) +) ENGINE=ndbcluster DEFAULT CHARSET=latin1 PARTITION BY KEY () +**** Second set of alters test 1 **** +ALTER TABLE t1 RENAME t2; +ALTER TABLE t2 DROP INDEX c5; +CREATE TABLE t1(c1 INT)ENGINE=NDB; +INSERT INTO t1 VALUES(1); +DROP TABLE t1; +ALTER TABLE t2 RENAME t1; +**** Show second set of ALTERs on MASTER **** +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `c1` int(11) NOT NULL, + `c2` int(11) NOT NULL, + `c3` int(11) NOT NULL, + `c4` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `c5` double DEFAULT NULL, + PRIMARY KEY (`c1`), + KEY `t1_i` (`c2`,`c3`) +) ENGINE=ndbcluster DEFAULT CHARSET=latin1 PARTITION BY KEY () +**** Show second set of ALTERs on SLAVE **** +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `c1` int(11) NOT NULL, + `c2` int(11) NOT NULL, + `c3` int(11) NOT NULL, + `c4` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `c5` double DEFAULT NULL, + PRIMARY KEY (`c1`), + KEY `t1_i` (`c2`,`c3`) +) ENGINE=ndbcluster DEFAULT CHARSET=latin1 PARTITION BY KEY () +**** Third and last set of alters for test1 **** +ALTER TABLE t1 CHANGE c1 c1 DOUBLE; +ALTER TABLE t1 CHANGE c2 c2 DECIMAL(10,2); +ALTER TABLE t1 DROP COLUMN c3; +ALTER TABLE t1 CHANGE c4 c4 TEXT CHARACTER SET utf8; +ALTER TABLE t1 CHANGE c4 c4 BLOB; +ALTER TABLE t1 CHANGE c4 c3 BLOB; +set @b1 = 'b1'; +set @b1 = concat(@b1,@b1,@b1,@b1,@b1,@b1,@b1,@b1,@b1,@b1); +UPDATE t1 SET c3=@b1 where c1 = 1; +UPDATE t1 SET c3=@b1 where c1 = 2; +**** Show last set of ALTERs on MASTER **** +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `c1` double NOT NULL DEFAULT '0', + `c2` decimal(10,2) DEFAULT NULL, + `c3` blob, + `c5` double DEFAULT NULL, + PRIMARY KEY (`c1`), + KEY `t1_i` (`c2`) +) ENGINE=ndbcluster DEFAULT CHARSET=latin1 PARTITION BY KEY () +SELECT * FROM t1 ORDER BY c1 LIMIT 5; +c1 c2 c3 c5 +1 2.00 b1b1b1b1b1b1b1b1b1b1 NULL +2 4.00 b1b1b1b1b1b1b1b1b1b1 NULL +3 6.00 0000-00-00 00:00:00 NULL +4 8.00 0000-00-00 00:00:00 NULL +5 10.00 0000-00-00 00:00:00 NULL +**** Show last set of ALTERs on SLAVE **** +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `c1` double NOT NULL DEFAULT '0', + `c2` decimal(10,2) DEFAULT NULL, + `c3` blob, + `c5` double DEFAULT NULL, + PRIMARY KEY (`c1`), + KEY `t1_i` (`c2`) +) ENGINE=ndbcluster DEFAULT CHARSET=latin1 PARTITION BY KEY () +SELECT * FROM t1 where c1 = 1; +c1 c2 c3 c5 +1 2.00 b1b1b1b1b1b1b1b1b1b1 NULL +DROP TABLE t1; +STOP SLAVE; +RESET SLAVE; +RESET MASTER; +START SLAVE; +******** Create additional TABLESPACE test 2 ************** +CREATE TABLESPACE ts2 +ADD DATAFILE 'datafile03.dat' +USE LOGFILE GROUP lg1 +INITIAL_SIZE 12M +ENGINE=NDB; +ALTER TABLESPACE ts2 +ADD DATAFILE 'datafile04.dat' +INITIAL_SIZE 12M +ENGINE=NDB; +DROP DATABASE IF EXISTS tpcb; +Warnings: +Note 1008 Can't drop database 'tpcb'; database doesn't exist +CREATE DATABASE tpcb; +DROP DATABASE IF EXISTS test; +CREATE DATABASE test; +*********** Create TPCB Tables ***************** +CREATE TABLE tpcb.account +(id INT, bid INT, balance DECIMAL(10,2), +filler CHAR(255), PRIMARY KEY(id)) +TABLESPACE ts1 STORAGE DISK +ENGINE=NDB; +CREATE TABLE tpcb.branch +(bid INT, balance DECIMAL(10,2), filler VARCHAR(255), +PRIMARY KEY(bid)) +ENGINE=NDB; +CREATE TABLE tpcb.teller +(tid INT, balance DECIMAL(10,2), filler VARCHAR(255), +PRIMARY KEY(tid)) +TABLESPACE ts2 STORAGE DISK +ENGINE=NDB; +CREATE TABLE tpcb.history +(id MEDIUMINT NOT NULL AUTO_INCREMENT,aid INT, +tid INT, bid INT, amount DECIMAL(10,2), +tdate DATETIME, teller CHAR(20), uuidf LONGBLOB, +filler CHAR(80),PRIMARY KEY (id)) +TABLESPACE ts2 STORAGE DISK +ENGINE=NDB; +********* Create Procedures and Functions ************ +CREATE PROCEDURE tpcb.load() +BEGIN +DECLARE acct INT DEFAULT 1000; +DECLARE brch INT DEFAULT 100; +DECLARE tell INT DEFAULT 1000; +DECLARE tmp INT DEFAULT 100; +WHILE brch > 0 DO +SET tmp = 100; +WHILE tmp > 0 DO +INSERT INTO tpcb.account VALUES (acct, brch, 0.0, "FRESH ACCOUNT"); +SET acct = acct - 1; +SET tmp = tmp -1; +END WHILE; +INSERT INTO tpcb.branch VALUES (brch, 0.0, "FRESH BRANCH"); +SET brch = brch - 1; +END WHILE; +WHILE tell > 0 DO +INSERT INTO tpcb.teller VALUES (tell, 0.0, "FRESH TELLER"); +SET tell = tell - 1; +END WHILE; +END| +CREATE FUNCTION tpcb.account_id () RETURNS INT +BEGIN +DECLARE num INT; +DECLARE ran INT; +SELECT RAND() * 10 INTO ran; +IF (ran < 2) +THEN +SELECT RAND() * 10 INTO num; +ELSEIF (ran < 4) +THEN +SELECT RAND() * 100 INTO num; +ELSE +SELECT RAND() * 1000 INTO num; +END IF; +IF (num < 1) +THEN +RETURN 1; +END IF; +RETURN num; +END| +CREATE FUNCTION tpcb.teller_id () RETURNS INT +BEGIN +DECLARE num INT; +DECLARE ran INT; +SELECT RAND() * 10 INTO ran; +IF (ran < 2) +THEN +SELECT RAND() * 10 INTO num; +ELSEIF (ran < 5) +THEN +SELECT RAND() * 100 INTO num; +ELSE +SELECT RAND() * 1000 INTO num; +END IF; +IF (num < 1) +THEN +RETURN 1; +END IF; +RETURN num; +END| +CREATE PROCEDURE tpcb.trans() +BEGIN +DECLARE acct INT DEFAULT 0; +DECLARE brch INT DEFAULT 0; +DECLARE tell INT DEFAULT 0; +DECLARE bal DECIMAL(10,2) DEFAULT 0.0; +DECLARE amount DECIMAL(10,2) DEFAULT 1.00; +DECLARE test INT DEFAULT 0; +DECLARE bbal DECIMAL(10,2) DEFAULT 0.0; +DECLARE tbal DECIMAL(10,2) DEFAULT 0.0; +DECLARE local_uuid VARCHAR(255); +DECLARE local_user VARCHAR(255); +DECLARE local_time TIMESTAMP; +SELECT RAND() * 10 INTO test; +SELECT tpcb.account_id() INTO acct; +SELECT tpcb.teller_id() INTO tell; +SELECT account.balance INTO bal FROM tpcb.account WHERE id = acct; +SELECT account.bid INTO brch FROM tpcb.account WHERE id = acct; +SELECT teller.balance INTO tbal FROM tpcb.teller WHERE tid = tell; +SELECT branch.balance INTO bbal FROM tpcb.branch WHERE bid = brch; +IF (test < 5) +THEN +SET bal = bal + amount; +SET bbal = bbal + amount; +SET tbal = tbal + amount; +UPDATE tpcb.account SET balance = bal, filler = 'account updated' + WHERE id = acct; +UPDATE tpcb.branch SET balance = bbal, filler = 'branch updated' + WHERE bid = brch; +UPDATE tpcb.teller SET balance = tbal, filler = 'teller updated' + WHERE tid = tell; +ELSE +SET bal = bal - amount; +SET bbal = bbal - amount; +SET tbal = tbal - amount; +UPDATE tpcb.account SET balance = bal, filler = 'account updated' + WHERE id = acct; +UPDATE tpcb.branch SET balance = bbal, filler = 'branch updated' + WHERE bid = brch; +UPDATE tpcb.teller SET balance = tbal, filler = 'teller updated' + WHERE tid = tell; +END IF; +SET local_uuid=UUID(); +SET local_user=USER(); +SET local_time= NOW(); +INSERT INTO tpcb.history VALUES(NULL,acct,tell,brch,amount, local_time,local_user, +local_uuid,'completed trans'); +END| +****** TEST 2 test time ********************************* +USE tpcb; +*********** Load up the database ****************** +CALL tpcb.load(); +********** Check load master and slave ************** +SELECT COUNT(*) FROM account; +COUNT(*) +10000 +USE tpcb; +SELECT COUNT(*) FROM account; +COUNT(*) +10000 +******** Run in some transactions *************** +***** Time to try slave sync *********** +**** Must make sure slave is clean ***** +STOP SLAVE; +RESET SLAVE; +DROP PROCEDURE tpcb.load; +DROP PROCEDURE tpcb.trans; +DROP TABLE tpcb.account; +DROP TABLE tpcb.teller; +DROP TABLE tpcb.branch; +DROP TABLE tpcb.history; +DROP DATABASE tpcb; +ALTER TABLESPACE ts1 +DROP DATAFILE 'datafile.dat' +ENGINE=NDB; +ALTER TABLESPACE ts1 +DROP DATAFILE 'datafile02.dat' +ENGINE=NDB; +DROP TABLESPACE ts1 ENGINE=NDB; +ALTER TABLESPACE ts2 +DROP DATAFILE 'datafile03.dat' +ENGINE=NDB; +ALTER TABLESPACE ts2 +DROP DATAFILE 'datafile04.dat' +ENGINE=NDB; +DROP TABLESPACE ts2 ENGINE=NDB; +DROP LOGFILE GROUP lg1 ENGINE=NDB; +********** Take a backup of the Master ************* +SELECT COUNT(*) FROM history; +COUNT(*) +1000 +SELECT COUNT(*) FROM history; +COUNT(*) +2000 +CREATE TEMPORARY TABLE IF NOT EXISTS cluster.backup_info (id INT, backup_id INT) ENGINE = HEAP; +DELETE FROM cluster.backup_info; +LOAD DATA INFILE '../tmp.dat' INTO TABLE cluster.backup_info FIELDS TERMINATED BY ','; +SELECT @the_backup_id:=backup_id FROM cluster.backup_info; +@the_backup_id:=backup_id + +DROP TABLE IF EXISTS cluster.backup_info; +************ Restore the slave ************************ +CREATE DATABASE tpcb; +***** Check a few slave restore values *************** +USE tpcb; +SELECT COUNT(*) FROM account; +COUNT(*) +10000 +***** Add some more records to master ********* +***** Finsh the slave sync process ******* +* 1. * +@the_epoch:=MAX(epoch) + +* 2. * +@the_pos:=Position @the_file:=SUBSTRING_INDEX(FILE, '/', -1) + master-bin.000001 +* 3. * +* 4. * +* 5. * +START SLAVE; +**** We should be ready to continue on ************* +****** Let's make sure we match ******* +***** MASTER ******* +USE tpcb; +SELECT COUNT(*) FROM history; +COUNT(*) +4050 +****** SLAVE ******** +USE tpcb; +SELECT COUNT(*) FROM history; +COUNT(*) +4050 +*** DUMP MASTER & SLAVE FOR COMPARE ******** +*************** TEST 2 CLEANUP SECTION ******************** +DROP PROCEDURE tpcb.load; +DROP PROCEDURE tpcb.trans; +DROP TABLE tpcb.account; +DROP TABLE tpcb.teller; +DROP TABLE tpcb.branch; +DROP TABLE tpcb.history; +DROP DATABASE tpcb; +ALTER TABLESPACE ts1 +DROP DATAFILE 'datafile.dat' +ENGINE=NDB; +ALTER TABLESPACE ts1 +DROP DATAFILE 'datafile02.dat' +ENGINE=NDB; +DROP TABLESPACE ts1 ENGINE=NDB; +ALTER TABLESPACE ts2 +DROP DATAFILE 'datafile03.dat' +ENGINE=NDB; +ALTER TABLESPACE ts2 +DROP DATAFILE 'datafile04.dat' +ENGINE=NDB; +DROP TABLESPACE ts2 ENGINE=NDB; +DROP LOGFILE GROUP lg1 ENGINE=NDB; +****** Do dumps compare ************ diff --git a/mysql-test/t/rpl_ndb_dd_advance.test b/mysql-test/t/rpl_ndb_dd_advance.test new file mode 100644 index 00000000000..601ea069203 --- /dev/null +++ b/mysql-test/t/rpl_ndb_dd_advance.test @@ -0,0 +1,565 @@ +############################################### +# Author: JBM # +# Date: 2006-03-07 # +# Purpose: To test advance DD and replication # +############################################### + +#### Include Section #### +--source include/have_ndb.inc +--source include/have_binlog_format_row.inc +#--source include/have_ndb_extra.inc +--source include/master-slave.inc + +#### Test start cleanup section ##### +--disable_warnings +DROP TABLE IF EXISTS t1, t2, t3; +--enable_warnings + +###################################################### +# Requirment: Cluster DD and replication must be able# +# to handle ALTER tables and indexes and must rpl # +# to the slave correctly # +###################################################### + +## Test #1 replication of CDD and Alter Tables ##### +--echo ***** Test 1 RPL of CDD and Alter ***** +--echo ***** Test 1 setup ***** + +CREATE LOGFILE GROUP lg1 +ADD UNDOFILE 'undofile.dat' +INITIAL_SIZE 16M +UNDO_BUFFER_SIZE = 1M +ENGINE=NDB; + +ALTER LOGFILE GROUP lg1 +ADD UNDOFILE 'undofile02.dat' +INITIAL_SIZE 4M +ENGINE=NDB; + +CREATE TABLESPACE ts1 +ADD DATAFILE 'datafile.dat' +USE LOGFILE GROUP lg1 +INITIAL_SIZE 12M +ENGINE=NDB; + +ALTER TABLESPACE ts1 +ADD DATAFILE 'datafile02.dat' +INITIAL_SIZE 4M +ENGINE=NDB; + +CREATE TABLE t1 + (c1 INT NOT NULL PRIMARY KEY, + c2 INT NOT NULL, + c3 INT NOT NULL) + TABLESPACE ts1 STORAGE DISK + ENGINE=NDB; + +--echo ***** insert some data ***** + +let $j= 900; +--disable_query_log +while ($j) +{ + eval INSERT INTO t1 VALUES($j,$j*2,$j+3); + dec $j; +} +--enable_query_log + +--echo ***** Select from Master ***** + +SELECT * FROM t1 ORDER BY c1 LIMIT 5; + +--echo ***** Select from Slave ***** +--sync_slave_with_master +connection slave; +SELECT * FROM t1 ORDER BY c1 LIMIT 5; + +--disable_query_log +SELECT FILE_NAME, FILE_TYPE, TABLESPACE_NAME, LOGFILE_GROUP_NAME + FROM INFORMATION_SCHEMA.FILES + WHERE ENGINE="ndbcluster"; +--enable_query_log + +--echo **** Do First Set of ALTERs in the master table **** +connection master; +CREATE INDEX t1_i ON t1(c2, c3); +#Bug 18039 +#CREATE UNIQUE INDEX t1_i2 ON t1(c2); +ALTER TABLE t1 ADD c4 TIMESTAMP; +ALTER TABLE t1 ADD c5 DOUBLE; +ALTER TABLE t1 ADD INDEX (c5); +SHOW CREATE TABLE t1; + +--echo **** Show first set of ALTERs on SLAVE **** +--sync_slave_with_master +connection slave; +SHOW CREATE TABLE t1; + +--echo **** Second set of alters test 1 **** + +connection master; +ALTER TABLE t1 RENAME t2; +ALTER TABLE t2 DROP INDEX c5; +CREATE TABLE t1(c1 INT)ENGINE=NDB; +INSERT INTO t1 VALUES(1); +DROP TABLE t1; +ALTER TABLE t2 RENAME t1; + +--echo **** Show second set of ALTERs on MASTER **** + +SHOW CREATE TABLE t1; + +--echo **** Show second set of ALTERs on SLAVE **** +--sync_slave_with_master +connection slave; +SHOW CREATE TABLE t1; + +--echo **** Third and last set of alters for test1 **** +connection master; +ALTER TABLE t1 CHANGE c1 c1 DOUBLE; +ALTER TABLE t1 CHANGE c2 c2 DECIMAL(10,2); +ALTER TABLE t1 DROP COLUMN c3; +ALTER TABLE t1 CHANGE c4 c4 TEXT CHARACTER SET utf8; +ALTER TABLE t1 CHANGE c4 c4 BLOB; +ALTER TABLE t1 CHANGE c4 c3 BLOB; +set @b1 = 'b1'; +set @b1 = concat(@b1,@b1,@b1,@b1,@b1,@b1,@b1,@b1,@b1,@b1); +UPDATE t1 SET c3=@b1 where c1 = 1; +UPDATE t1 SET c3=@b1 where c1 = 2; + +--echo **** Show last set of ALTERs on MASTER **** + +SHOW CREATE TABLE t1; +SELECT * FROM t1 ORDER BY c1 LIMIT 5; + +--echo **** Show last set of ALTERs on SLAVE **** +--sync_slave_with_master +connection slave; +SHOW CREATE TABLE t1; +# Bug 16498 +#SELECT * FROM t1 ORDER BY c1 LIMIT 5; +SELECT * FROM t1 where c1 = 1; + +connection master; +DROP TABLE t1; +--sync_slave_with_master +connection slave; +STOP SLAVE; +RESET SLAVE; +connection master; +RESET MASTER; +connection slave; +START SLAVE; + +################### TEST 2 TPCB for disk data ########################### +# Requirement: To have Stored Procedures and Functions that are used to # +# populate and post transactions to the data base using CDD that span # +# 2 tables spaces and also use a memory only cluster tables. In addition# +# The slave is to be stopped, cleaned and restored and synced with the # +# Master cluster # +######################################################################### + +--echo ******** Create additional TABLESPACE test 2 ************** + +connection master; +CREATE TABLESPACE ts2 +ADD DATAFILE 'datafile03.dat' +USE LOGFILE GROUP lg1 +INITIAL_SIZE 12M +ENGINE=NDB; + +ALTER TABLESPACE ts2 +ADD DATAFILE 'datafile04.dat' +INITIAL_SIZE 12M +ENGINE=NDB; + +###### CLEAN UP SECTION ############## +DROP DATABASE IF EXISTS tpcb; +CREATE DATABASE tpcb; +DROP DATABASE IF EXISTS test; +CREATE DATABASE test; +######## Creat Table Section ######### +--echo *********** Create TPCB Tables ***************** +CREATE TABLE tpcb.account + (id INT, bid INT, balance DECIMAL(10,2), + filler CHAR(255), PRIMARY KEY(id)) + TABLESPACE ts1 STORAGE DISK + ENGINE=NDB; + +CREATE TABLE tpcb.branch + (bid INT, balance DECIMAL(10,2), filler VARCHAR(255), + PRIMARY KEY(bid)) + ENGINE=NDB; + +CREATE TABLE tpcb.teller + (tid INT, balance DECIMAL(10,2), filler VARCHAR(255), + PRIMARY KEY(tid)) + TABLESPACE ts2 STORAGE DISK + ENGINE=NDB; + +CREATE TABLE tpcb.history + (id MEDIUMINT NOT NULL AUTO_INCREMENT,aid INT, + tid INT, bid INT, amount DECIMAL(10,2), + tdate DATETIME, teller CHAR(20), uuidf LONGBLOB, + filler CHAR(80),PRIMARY KEY (id)) + TABLESPACE ts2 STORAGE DISK + ENGINE=NDB; + +--echo ********* Create Procedures and Functions ************ +delimiter |; +CREATE PROCEDURE tpcb.load() +BEGIN + DECLARE acct INT DEFAULT 1000; + DECLARE brch INT DEFAULT 100; + DECLARE tell INT DEFAULT 1000; + DECLARE tmp INT DEFAULT 100; + WHILE brch > 0 DO + SET tmp = 100; + WHILE tmp > 0 DO + INSERT INTO tpcb.account VALUES (acct, brch, 0.0, "FRESH ACCOUNT"); + SET acct = acct - 1; + SET tmp = tmp -1; + END WHILE; + INSERT INTO tpcb.branch VALUES (brch, 0.0, "FRESH BRANCH"); + SET brch = brch - 1; + END WHILE; + WHILE tell > 0 DO + INSERT INTO tpcb.teller VALUES (tell, 0.0, "FRESH TELLER"); + SET tell = tell - 1; + END WHILE; +END| + +CREATE FUNCTION tpcb.account_id () RETURNS INT +BEGIN + DECLARE num INT; + DECLARE ran INT; + SELECT RAND() * 10 INTO ran; + IF (ran < 2) + THEN + SELECT RAND() * 10 INTO num; + ELSEIF (ran < 4) + THEN + SELECT RAND() * 100 INTO num; + ELSE + SELECT RAND() * 1000 INTO num; + END IF; + IF (num < 1) + THEN + RETURN 1; + END IF; + RETURN num; +END| + +CREATE FUNCTION tpcb.teller_id () RETURNS INT +BEGIN + DECLARE num INT; + DECLARE ran INT; + SELECT RAND() * 10 INTO ran; + IF (ran < 2) + THEN + SELECT RAND() * 10 INTO num; + ELSEIF (ran < 5) + THEN + SELECT RAND() * 100 INTO num; + ELSE + SELECT RAND() * 1000 INTO num; + END IF; + IF (num < 1) + THEN + RETURN 1; + END IF; + RETURN num; +END| + +CREATE PROCEDURE tpcb.trans() +BEGIN + DECLARE acct INT DEFAULT 0; + DECLARE brch INT DEFAULT 0; + DECLARE tell INT DEFAULT 0; + DECLARE bal DECIMAL(10,2) DEFAULT 0.0; + DECLARE amount DECIMAL(10,2) DEFAULT 1.00; + DECLARE test INT DEFAULT 0; + DECLARE bbal DECIMAL(10,2) DEFAULT 0.0; + DECLARE tbal DECIMAL(10,2) DEFAULT 0.0; + DECLARE local_uuid VARCHAR(255); + DECLARE local_user VARCHAR(255); + DECLARE local_time TIMESTAMP; + + SELECT RAND() * 10 INTO test; + SELECT tpcb.account_id() INTO acct; + SELECT tpcb.teller_id() INTO tell; + + SELECT account.balance INTO bal FROM tpcb.account WHERE id = acct; + SELECT account.bid INTO brch FROM tpcb.account WHERE id = acct; + SELECT teller.balance INTO tbal FROM tpcb.teller WHERE tid = tell; + SELECT branch.balance INTO bbal FROM tpcb.branch WHERE bid = brch; + + IF (test < 5) + THEN + SET bal = bal + amount; + SET bbal = bbal + amount; + SET tbal = tbal + amount; + UPDATE tpcb.account SET balance = bal, filler = 'account updated' + WHERE id = acct; + UPDATE tpcb.branch SET balance = bbal, filler = 'branch updated' + WHERE bid = brch; + UPDATE tpcb.teller SET balance = tbal, filler = 'teller updated' + WHERE tid = tell; + ELSE + SET bal = bal - amount; + SET bbal = bbal - amount; + SET tbal = tbal - amount; + UPDATE tpcb.account SET balance = bal, filler = 'account updated' + WHERE id = acct; + UPDATE tpcb.branch SET balance = bbal, filler = 'branch updated' + WHERE bid = brch; + UPDATE tpcb.teller SET balance = tbal, filler = 'teller updated' + WHERE tid = tell; + END IF; + + SET local_uuid=UUID(); + SET local_user=USER(); + SET local_time= NOW(); + INSERT INTO tpcb.history VALUES(NULL,acct,tell,brch,amount, local_time,local_user, + local_uuid,'completed trans'); +END| +delimiter ;| + +--echo ****** TEST 2 test time ********************************* +USE tpcb; + +--echo *********** Load up the database ****************** +CALL tpcb.load(); + +--echo ********** Check load master and slave ************** +SELECT COUNT(*) FROM account; +--sync_slave_with_master +connection slave; +USE tpcb; +SELECT COUNT(*) FROM account; + +--echo ******** Run in some transactions *************** + +connection master; +let $j= 1000; +--disable_query_log +while ($j) +{ + CALL tpcb.trans(); + dec $j; +} +--enable_query_log + +--echo ***** Time to try slave sync *********** +--echo **** Must make sure slave is clean ***** +--connection slave +STOP SLAVE; +RESET SLAVE; +DROP PROCEDURE tpcb.load; +DROP PROCEDURE tpcb.trans; +DROP TABLE tpcb.account; +DROP TABLE tpcb.teller; +DROP TABLE tpcb.branch; +DROP TABLE tpcb.history; +DROP DATABASE tpcb; + +ALTER TABLESPACE ts1 +DROP DATAFILE 'datafile.dat' +ENGINE=NDB; + +ALTER TABLESPACE ts1 +DROP DATAFILE 'datafile02.dat' +ENGINE=NDB; + +DROP TABLESPACE ts1 ENGINE=NDB; + +ALTER TABLESPACE ts2 +DROP DATAFILE 'datafile03.dat' +ENGINE=NDB; + +ALTER TABLESPACE ts2 +DROP DATAFILE 'datafile04.dat' +ENGINE=NDB; + +DROP TABLESPACE ts2 ENGINE=NDB; + +DROP LOGFILE GROUP lg1 ENGINE=NDB; + +--echo ********** Take a backup of the Master ************* +connection master; + +SELECT COUNT(*) FROM history; + +let $j= 1000; +--disable_query_log +while ($j) +{ + CALL tpcb.trans(); + dec $j; +} +--enable_query_log + +SELECT COUNT(*) FROM history; + +#RESET MASTER; +--exec $NDB_MGM --no-defaults --ndb-connectstring="localhost:$NDBCLUSTER_PORT" -e "start backup" >> $NDB_TOOLS_OUTPUT + +--exec $NDB_TOOLS_DIR/ndb_select_all --ndb-connectstring="localhost:$NDBCLUSTER_PORT" -d sys --delimiter=',' SYSTAB_0 | grep 520093696 > $MYSQLTEST_VARDIR/tmp.dat + +CREATE TEMPORARY TABLE IF NOT EXISTS cluster.backup_info (id INT, backup_id INT) ENGINE = HEAP; + +DELETE FROM cluster.backup_info; + +LOAD DATA INFILE '../tmp.dat' INTO TABLE cluster.backup_info FIELDS TERMINATED BY ','; + +--replace_column 1 + +SELECT @the_backup_id:=backup_id FROM cluster.backup_info; + +let the_backup_id=`select @the_backup_id`; + +DROP TABLE IF EXISTS cluster.backup_info; +#RESET MASTER; + +--echo ************ Restore the slave ************************ +connection slave; +CREATE DATABASE tpcb; +--source include/ndb_restore_slave_eoption.inc + +--echo ***** Check a few slave restore values *************** +connection slave; +USE tpcb; +SELECT COUNT(*) FROM account; + +--echo ***** Add some more records to master ********* +connection master; +let $j= 1000; +--disable_query_log +while ($j) +{ + CALL tpcb.trans(); + dec $j; +} +--enable_query_log + +# +# now setup replication to continue from last epoch +# 1. get apply_status epoch from slave +# 2. get corresponding _next_ binlog postition from master +# 3. change master on slave +# 4. add some transaction for slave to process +# 5. start the replication + +--echo ***** Finsh the slave sync process ******* +# 1. +--echo * 1. * +connection slave; +--disable_query_log +--replace_column 1 +SELECT @the_epoch:=MAX(epoch) FROM cluster.apply_status; +--let $the_epoch= `select @the_epoch` + +# 2. +--echo * 2. * +connection master; +--replace_column 1 +eval SELECT @the_pos:=Position,@the_file:=SUBSTRING_INDEX(FILE, '/', -1) + FROM cluster.binlog_index WHERE epoch > $the_epoch ORDER BY epoch ASC LIMIT 1; +--let $the_pos= `SELECT @the_pos` +--let $the_file= `SELECT @the_file` + +# 3. +--echo * 3. * +connection slave; +eval CHANGE MASTER TO + master_log_file = '$the_file', + master_log_pos = $the_pos; +--enable_query_log + +# 4. +--echo * 4. * +connection master; +let $j= 1000; +--disable_query_log +while ($j) +{ + CALL tpcb.trans(); + dec $j; +} +--enable_query_log + +# 5. +--echo * 5. * +connection slave; +START SLAVE; + +--echo **** We should be ready to continue on ************* + +connection master; +let $j= 50; +--disable_query_log +while ($j) +{ + CALL tpcb.trans(); + dec $j; +} +--enable_query_log + +--echo ****** Let's make sure we match ******* +--echo ***** MASTER ******* +USE tpcb; +SELECT COUNT(*) FROM history; + +--echo ****** SLAVE ******** +--sync_slave_with_master +connection slave; +USE tpcb; +SELECT COUNT(*) FROM history; + +--echo *** DUMP MASTER & SLAVE FOR COMPARE ******** + +--exec $MYSQL_DUMP --compact --order-by-primary --skip-extended-insert tpcb account teller branch history > $MYSQLTEST_VARDIR/tmp/RPL_DD_ADV_M.sql + +--exec $MYSQL_DUMP_SLAVE --compact --order-by-primary --skip-extended-insert tpcb account teller branch history > $MYSQLTEST_VARDIR/tmp/RPL_DD_ADV_S.sql + +--echo *************** TEST 2 CLEANUP SECTION ******************** +connection master; +DROP PROCEDURE tpcb.load; +DROP PROCEDURE tpcb.trans; +DROP TABLE tpcb.account; +DROP TABLE tpcb.teller; +DROP TABLE tpcb.branch; +DROP TABLE tpcb.history; +DROP DATABASE tpcb; + +ALTER TABLESPACE ts1 +DROP DATAFILE 'datafile.dat' +ENGINE=NDB; + +ALTER TABLESPACE ts1 +DROP DATAFILE 'datafile02.dat' +ENGINE=NDB; + +DROP TABLESPACE ts1 ENGINE=NDB; + +ALTER TABLESPACE ts2 +DROP DATAFILE 'datafile03.dat' +ENGINE=NDB; + +ALTER TABLESPACE ts2 +DROP DATAFILE 'datafile04.dat' +ENGINE=NDB; + +DROP TABLESPACE ts2 ENGINE=NDB; + +DROP LOGFILE GROUP lg1 ENGINE=NDB; + +--sync_slave_with_master + +connection master; + +--echo ****** Do dumps compare ************ + +--exec diff $MYSQLTEST_VARDIR/tmp/RPL_DD_ADV_M.sql $MYSQLTEST_VARDIR/tmp/RPL_DD_ADV_S.sql + +# End 5.1 test case From 26e765628b21531a743621a90f23d166a7468d30 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 9 Mar 2006 17:14:33 +0100 Subject: [PATCH 10/18] rpl_ndb_dd_advance.result: Updated results rpl_ndb_dd_advance.test: Updated test case from comments in Tomas's review Changes bug number comment mysql-test/t/rpl_ndb_dd_advance.test: Updated test case from comments in Tomas's review mysql-test/r/rpl_ndb_dd_advance.result: Updated results --- mysql-test/r/rpl_ndb_dd_advance.result | 2 +- mysql-test/t/rpl_ndb_dd_advance.test | 43 +++++++++++++++++++++++--- 2 files changed, 40 insertions(+), 5 deletions(-) diff --git a/mysql-test/r/rpl_ndb_dd_advance.result b/mysql-test/r/rpl_ndb_dd_advance.result index 64820b6f9e5..fd4b80d2066 100644 --- a/mysql-test/r/rpl_ndb_dd_advance.result +++ b/mysql-test/r/rpl_ndb_dd_advance.result @@ -49,8 +49,8 @@ c1 c2 c3 4 8 7 5 10 8 FILE_NAME FILE_TYPE TABLESPACE_NAME LOGFILE_GROUP_NAME -datafile02.dat DATAFILE ts1 lg1 datafile.dat DATAFILE ts1 lg1 +datafile02.dat DATAFILE ts1 lg1 undofile.dat UNDO LOG ts1 lg1 undofile02.dat UNDO LOG ts1 lg1 **** Do First Set of ALTERs in the master table **** diff --git a/mysql-test/t/rpl_ndb_dd_advance.test b/mysql-test/t/rpl_ndb_dd_advance.test index 601ea069203..c3ef6a51228 100644 --- a/mysql-test/t/rpl_ndb_dd_advance.test +++ b/mysql-test/t/rpl_ndb_dd_advance.test @@ -74,13 +74,24 @@ SELECT * FROM t1 ORDER BY c1 LIMIT 5; connection slave; SELECT * FROM t1 ORDER BY c1 LIMIT 5; +################################### +# Just to some File Schema check # +################################### + --disable_query_log -SELECT FILE_NAME, FILE_TYPE, TABLESPACE_NAME, LOGFILE_GROUP_NAME +SELECT DISTINCT FILE_NAME, FILE_TYPE, TABLESPACE_NAME, LOGFILE_GROUP_NAME FROM INFORMATION_SCHEMA.FILES - WHERE ENGINE="ndbcluster"; + WHERE ENGINE="ndbcluster" ORDER BY FILE_NAME; --enable_query_log --echo **** Do First Set of ALTERs in the master table **** +################################################### +# On this first set of alters I expect: +# 1. To be able to create and index on 2 columns +# 2. To be able to create a unique index +# 3. To be able to add two columns and have +# it all replicated correctly to the slave cluster. +################################################### connection master; CREATE INDEX t1_i ON t1(c2, c3); #Bug 18039 @@ -96,7 +107,17 @@ connection slave; SHOW CREATE TABLE t1; --echo **** Second set of alters test 1 **** - +############################################ +# With this next set of alters we have had +# Some issues with renames of tables. So this +# test renames our main table, drop and index off +# of it, creates another table with then name +# of the orginal table, inserts a row, drops +# the table and renames the orginal table back. +# I want to make sure that 1) the cluster does +# okay with this and 2) that it is replicated +# correctly. +############################################# connection master; ALTER TABLE t1 RENAME t2; ALTER TABLE t2 DROP INDEX c5; @@ -115,6 +136,15 @@ connection slave; SHOW CREATE TABLE t1; --echo **** Third and last set of alters for test1 **** +######################################################### +# In this last set of alters, we are messing with the +# cluster ability to rebuild indexes, drop a column that make up +# an index with another column and change types several times in +# a row. I have choosen the BLOB as it seems to have had many +# issues in this release. I want to make sure that the cluster +# deals with these radical changes and that the replication to +# the slave cluster is dones correctly. +########################################################### connection master; ALTER TABLE t1 CHANGE c1 c1 DOUBLE; ALTER TABLE t1 CHANGE c2 c2 DECIMAL(10,2); @@ -136,7 +166,7 @@ SELECT * FROM t1 ORDER BY c1 LIMIT 5; --sync_slave_with_master connection slave; SHOW CREATE TABLE t1; -# Bug 16498 +# Bug 18094 #SELECT * FROM t1 ORDER BY c1 LIMIT 5; SELECT * FROM t1 where c1 = 1; @@ -562,4 +592,9 @@ connection master; --exec diff $MYSQLTEST_VARDIR/tmp/RPL_DD_ADV_M.sql $MYSQLTEST_VARDIR/tmp/RPL_DD_ADV_S.sql +## Note: Ths files should only get removed, if the above diff succeeds. + +--exec rm $MYSQLTEST_VARDIR/tmp/RPL_DD_ADV_M.sql +--exec rm $MYSQLTEST_VARDIR/tmp/RPL_DD_ADV_S.sql + # End 5.1 test case From 9f5731821a5a3549c89c7e9e5aa4f8ad6241f153 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 9 Mar 2006 17:16:04 +0100 Subject: [PATCH 11/18] ndb - bug#18067 bug#18075 closes these bugs mysql-test/r/rpl_ndb_blob.result: test case for varsize PK + blob mysql-test/t/rpl_ndb_blob.test: test case for varsize PK + blob storage/ndb/include/ndbapi/NdbBlob.hpp: table PK in blob part key is fixed-sized so keep extra bytes zero if varsize PK storage/ndb/src/ndbapi/NdbBlob.cpp: table PK in blob part key is fixed-sized so keep extra bytes zero if varsize PK --- mysql-test/r/rpl_ndb_blob.result | 101 ++++++++++++++----------- mysql-test/t/rpl_ndb_blob.test | 61 +++++++++++++-- storage/ndb/include/ndbapi/NdbBlob.hpp | 1 + storage/ndb/src/ndbapi/NdbBlob.cpp | 12 +++ 4 files changed, 123 insertions(+), 52 deletions(-) diff --git a/mysql-test/r/rpl_ndb_blob.result b/mysql-test/r/rpl_ndb_blob.result index 1419875c743..c11de8c27e3 100644 --- a/mysql-test/r/rpl_ndb_blob.result +++ b/mysql-test/r/rpl_ndb_blob.result @@ -78,49 +78,58 @@ S 7 a6bae0cfe6b45ff8c3c12d2ce577a1cd3931190f 107 39ee712b4b9e47f2cf3ba7c9790b2bf S 8 e139adcb7b2974ee7ff227fd405709e5cb7c896c 108 ba8073b0e1a281d4111bd2d82c7722b01574c00b NULL S 9 1fc5168fe4be566b17b658d94e7813f0b5032cdb NULL NULL NULL drop table t1; -show binlog events; -Log_name Pos Event_type Server_id End_log_pos Info -master-bin.000001 4 Format_desc 1 102 Server ver: VERSION, Binlog ver: 4 -master-bin.000001 102 Query 1 239 use `test`; create table t1 ( -a int not null primary key, -b text not null -) engine=ndb -master-bin.000001 239 Query 1 303 BEGIN -master-bin.000001 303 Table_map 1 53 cluster.apply_status -master-bin.000001 356 Write_rows 1 95 -master-bin.000001 398 Table_map 1 135 test.t1 -master-bin.000001 438 Write_rows 1 806 -master-bin.000001 1109 Write_rows 1 9841 -master-bin.000001 10144 Query 1 10209 COMMIT -master-bin.000001 10209 Query 1 10273 BEGIN -master-bin.000001 10273 Table_map 1 53 cluster.apply_status -master-bin.000001 10326 Write_rows 1 95 -master-bin.000001 10368 Query 1 10433 COMMIT -master-bin.000001 10433 Query 1 10509 use `test`; drop table t1 -master-bin.000001 10509 Query 1 10684 use `test`; create table t1 ( -a int not null primary key, -b text not null, -c int, -d longblob, -e tinyblob -) engine=ndbcluster -master-bin.000001 10684 Query 1 10748 BEGIN -master-bin.000001 10748 Table_map 1 53 cluster.apply_status -master-bin.000001 10801 Write_rows 1 95 -master-bin.000001 10843 Table_map 1 138 test.t1 -master-bin.000001 10886 Write_rows 1 48922 -master-bin.000001 59670 Write_rows 1 124424 -master-bin.000001 135172 Write_rows 1 124530 -master-bin.000001 135278 Write_rows 1 205949 -master-bin.000001 216697 Write_rows 1 224233 -master-bin.000001 234981 Write_rows 1 227511 -master-bin.000001 238259 Write_rows 1 242379 -master-bin.000001 253127 Write_rows 1 254075 -master-bin.000001 264823 Write_rows 1 304323 -master-bin.000001 315071 Write_rows 1 330415 -master-bin.000001 341163 Query 1 341228 COMMIT -master-bin.000001 341228 Query 1 341292 BEGIN -master-bin.000001 341292 Table_map 1 53 cluster.apply_status -master-bin.000001 341345 Write_rows 1 95 -master-bin.000001 341387 Query 1 341452 COMMIT -master-bin.000001 341452 Query 1 341528 use `test`; drop table t1 +CREATE TABLE IF NOT EXISTS t1 ( +db VARBINARY(63) NOT NULL, +name VARBINARY(63) NOT NULL, +slock BINARY(32) NOT NULL, +query BLOB NOT NULL, +node_id INT UNSIGNED NOT NULL, +epoch BIGINT UNSIGNED NOT NULL, +id INT UNSIGNED NOT NULL, +version INT UNSIGNED NOT NULL, +type INT UNSIGNED NOT NULL, +PRIMARY KEY USING HASH (db,name)) +ENGINE=NDB; +insert into t1 values ('test','t1', +'abc',repeat(@s0,10), 11,12,13,14,15); +insert into t1 values ('test','t2', +'def',repeat(@s1,100), 21,22,23,24,25); +insert into t1 values ('test','t3', +'ghi',repeat(@s2,1000),31,32,33,34,35); +insert into t1 values ('testtttttttttt','t1', +'abc',repeat(@s0,10), 11,12,13,14,15); +insert into t1 values ('testttttttttttt','t1', +'def',repeat(@s1,100), 21,22,23,24,25); +insert into t1 values ('testtttttttttttt','t1', +'ghi',repeat(@s2,1000),31,32,33,34,35); +insert into t1 values ('t','t11111111111', +'abc',repeat(@s0,10), 11,12,13,14,15); +insert into t1 values ('t','t111111111111', +'def',repeat(@s1,100), 21,22,23,24,25); +insert into t1 values ('t','t1111111111111', +'ghi',repeat(@s2,1000),31,32,33,34,35); +select 'M', db, name, sha1(query), node_id, epoch, id, version, type +from t1 order by db, name; +M db name sha1(query) node_id epoch id version type +M t t11111111111 8fc937d303ee7e4795c0b964d4066cedd6d74cfd 11 12 13 14 15 +M t t111111111111 a5229e9f8977bc99afc3b3627c56f083e97e01bd 21 22 23 24 25 +M t t1111111111111 0820e6ad3eeb06ea60e5e04d0bfe36f286b91098 31 32 33 34 35 +M test t1 8fc937d303ee7e4795c0b964d4066cedd6d74cfd 11 12 13 14 15 +M test t2 a5229e9f8977bc99afc3b3627c56f083e97e01bd 21 22 23 24 25 +M test t3 0820e6ad3eeb06ea60e5e04d0bfe36f286b91098 31 32 33 34 35 +M testtttttttttt t1 8fc937d303ee7e4795c0b964d4066cedd6d74cfd 11 12 13 14 15 +M testttttttttttt t1 a5229e9f8977bc99afc3b3627c56f083e97e01bd 21 22 23 24 25 +M testtttttttttttt t1 0820e6ad3eeb06ea60e5e04d0bfe36f286b91098 31 32 33 34 35 +select 'S', db, name, sha1(query), node_id, epoch, id, version, type +from t1 order by db, name; +S db name sha1(query) node_id epoch id version type +S t t11111111111 8fc937d303ee7e4795c0b964d4066cedd6d74cfd 11 12 13 14 15 +S t t111111111111 a5229e9f8977bc99afc3b3627c56f083e97e01bd 21 22 23 24 25 +S t t1111111111111 0820e6ad3eeb06ea60e5e04d0bfe36f286b91098 31 32 33 34 35 +S test t1 8fc937d303ee7e4795c0b964d4066cedd6d74cfd 11 12 13 14 15 +S test t2 a5229e9f8977bc99afc3b3627c56f083e97e01bd 21 22 23 24 25 +S test t3 0820e6ad3eeb06ea60e5e04d0bfe36f286b91098 31 32 33 34 35 +S testtttttttttt t1 8fc937d303ee7e4795c0b964d4066cedd6d74cfd 11 12 13 14 15 +S testttttttttttt t1 a5229e9f8977bc99afc3b3627c56f083e97e01bd 21 22 23 24 25 +S testtttttttttttt t1 0820e6ad3eeb06ea60e5e04d0bfe36f286b91098 31 32 33 34 35 +drop table t1; diff --git a/mysql-test/t/rpl_ndb_blob.test b/mysql-test/t/rpl_ndb_blob.test index c31b629b9f8..94af986b222 100644 --- a/mysql-test/t/rpl_ndb_blob.test +++ b/mysql-test/t/rpl_ndb_blob.test @@ -83,11 +83,60 @@ from t1 order by a; drop table t1; --sync_slave_with_master -# -# view the binlog -# +# table with varsize key (future cluster/schema) + +# sql/ha_ndbcluster_binlog.cc +--connection master +CREATE TABLE IF NOT EXISTS t1 ( + db VARBINARY(63) NOT NULL, + name VARBINARY(63) NOT NULL, + slock BINARY(32) NOT NULL, + query BLOB NOT NULL, + node_id INT UNSIGNED NOT NULL, + epoch BIGINT UNSIGNED NOT NULL, + id INT UNSIGNED NOT NULL, + version INT UNSIGNED NOT NULL, + type INT UNSIGNED NOT NULL, + PRIMARY KEY USING HASH (db,name)) +ENGINE=NDB; + +insert into t1 values ('test','t1', + 'abc',repeat(@s0,10), 11,12,13,14,15); +insert into t1 values ('test','t2', + 'def',repeat(@s1,100), 21,22,23,24,25); +insert into t1 values ('test','t3', + 'ghi',repeat(@s2,1000),31,32,33,34,35); +insert into t1 values ('testtttttttttt','t1', + 'abc',repeat(@s0,10), 11,12,13,14,15); +insert into t1 values ('testttttttttttt','t1', + 'def',repeat(@s1,100), 21,22,23,24,25); +insert into t1 values ('testtttttttttttt','t1', + 'ghi',repeat(@s2,1000),31,32,33,34,35); +insert into t1 values ('t','t11111111111', + 'abc',repeat(@s0,10), 11,12,13,14,15); +insert into t1 values ('t','t111111111111', + 'def',repeat(@s1,100), 21,22,23,24,25); +insert into t1 values ('t','t1111111111111', + 'ghi',repeat(@s2,1000),31,32,33,34,35); + +select 'M', db, name, sha1(query), node_id, epoch, id, version, type +from t1 order by db, name; + +--sync_slave_with_master +--sleep 5 +--connection slave +select 'S', db, name, sha1(query), node_id, epoch, id, version, type +from t1 order by db, name; --connection master -let $VERSION=`select version()`; ---replace_result $VERSION VERSION -show binlog events; +drop table t1; +--sync_slave_with_master + +# +# view the binlog - not deterministic (mats) +# + +#--connection master +#let $VERSION=`select version()`; +#--replace_result $VERSION VERSION +#show binlog events; diff --git a/storage/ndb/include/ndbapi/NdbBlob.hpp b/storage/ndb/include/ndbapi/NdbBlob.hpp index 9ae0ba6f023..13bbfa59e50 100644 --- a/storage/ndb/include/ndbapi/NdbBlob.hpp +++ b/storage/ndb/include/ndbapi/NdbBlob.hpp @@ -288,6 +288,7 @@ private: Buf(); ~Buf(); void alloc(unsigned n); + void zerorest(); void copyfrom(const Buf& src); }; Buf theKeyBuf; diff --git a/storage/ndb/src/ndbapi/NdbBlob.cpp b/storage/ndb/src/ndbapi/NdbBlob.cpp index 680f9e5c70e..5fbfd6f5111 100644 --- a/storage/ndb/src/ndbapi/NdbBlob.cpp +++ b/storage/ndb/src/ndbapi/NdbBlob.cpp @@ -307,6 +307,13 @@ NdbBlob::Buf::alloc(unsigned n) #endif } +void +NdbBlob::Buf::zerorest() +{ + assert(size <= maxsize); + memset(data + size, 0, maxsize - size); +} + void NdbBlob::Buf::copyfrom(const NdbBlob::Buf& src) { @@ -441,6 +448,7 @@ NdbBlob::packKeyValue(const NdbTableImpl* aTable, const Buf& srcBuf) assert(4 * pos == srcBuf.size); assert(4 * pack_pos <= thePackKeyBuf.maxsize); thePackKeyBuf.size = 4 * pack_pos; + thePackKeyBuf.zerorest(); DBUG_RETURN(0); } @@ -1316,6 +1324,7 @@ NdbBlob::atPrepare(NdbTransaction* aCon, NdbOperation* anOp, const NdbColumnImpl DBUG_RETURN(-1); } thePackKeyBuf.size = 4 * size; + thePackKeyBuf.zerorest(); if (unpackKeyValue(theTable, theKeyBuf) == -1) DBUG_RETURN(-1); } @@ -1328,6 +1337,7 @@ NdbBlob::atPrepare(NdbTransaction* aCon, NdbOperation* anOp, const NdbColumnImpl DBUG_RETURN(-1); } thePackKeyBuf.size = 4 * size; + thePackKeyBuf.zerorest(); if (unpackKeyValue(theAccessTable, theAccessKeyBuf) == -1) DBUG_RETURN(-1); } @@ -1634,6 +1644,7 @@ NdbBlob::postExecute(NdbTransaction::ExecType anExecType) // copy key from first blob theKeyBuf.copyfrom(tFirstBlob->theKeyBuf); thePackKeyBuf.copyfrom(tFirstBlob->thePackKeyBuf); + thePackKeyBuf.zerorest(); } } if (isReadOp()) { @@ -1795,6 +1806,7 @@ NdbBlob::atNextResult() DBUG_RETURN(-1); } thePackKeyBuf.size = 4 * size; + thePackKeyBuf.zerorest(); if (unpackKeyValue(theTable, theKeyBuf) == -1) DBUG_RETURN(-1); } From 0f771ef2243d2372a9bc44ebf56c1a62c37af437 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 9 Mar 2006 17:19:39 +0100 Subject: [PATCH 12/18] Cleanup mysql-test/t/disabled.def: Orginize file order --- mysql-test/t/disabled.def | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mysql-test/t/disabled.def b/mysql-test/t/disabled.def index 9bd7a42e8de..0751c07847c 100644 --- a/mysql-test/t/disabled.def +++ b/mysql-test/t/disabled.def @@ -9,10 +9,10 @@ # Do not use any TAB characters for whitespace. # ############################################################################## -#ndb_alter_table_row : sometimes wrong error 1015!=1046 events_bugs : test case unstable (race conditions). andrey will fix events_stress : test case unstable. andrey will fix events : test case unstable. andrey will fix +#ndb_alter_table_row : sometimes wrong error 1015!=1046 ndb_autodiscover : Needs to be fixed w.r.t binlog ndb_autodiscover2 : Needs to be fixed w.r.t binlog ndb_binlog_basic : Results are not deterministic, Tomas will fix @@ -33,9 +33,9 @@ rpl_ndb_insert_ignore : Bugs: #17431: INSERT IGNORE INTO returns failed: 1296 rpl_ndb_myisam2ndb : Bugs#17400: delete & update of rows in table without pk fails rpl_ndb_log : result not deterministic rpl_ndb_relay_space : Bug#16993 +rpl_ndb_multi_update2 : BUG#17738 In progress rpl_ndb_multi_update3 : Bug#17400: delete & update of rows in table without pk fails rpl_ndb_sp007 : Bug #17290 rpl_sp : Bug#16456 rpl_until : Unstable test case, bug#15886 sp-goto : GOTO is currently is disabled - will be fixed in the future -rpl_ndb_multi_update2 : BUG#17738 In progress From fe0b818f9a07a9e56053c8addf53796e106e6c81 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 9 Mar 2006 21:00:45 +0300 Subject: [PATCH 13/18] After-merge fixes. mysql-test/r/trigger-grant.result: Updated the result file. mysql-test/r/view_grant.result: Updated the result file. mysql-test/t/trigger-grant.test: A patch for BUG#13198 has been merged, so need to update the test case. mysql-test/t/view_grant.test: A patch for BUG#16777 has been merged, so need to update the test case. sql/sql_parse.cc: After-merge fix (should have been a part of fix for BUG#16777). sql/sql_show.cc: After-merge fix -- empty identifier should be quoted. --- mysql-test/r/trigger-grant.result | 1 + mysql-test/r/view_grant.result | 11 ++++++++--- mysql-test/t/trigger-grant.test | 5 +---- mysql-test/t/view_grant.test | 5 +++-- sql/sql_parse.cc | 11 +---------- sql/sql_show.cc | 2 +- 6 files changed, 15 insertions(+), 20 deletions(-) diff --git a/mysql-test/r/trigger-grant.result b/mysql-test/r/trigger-grant.result index c3bbc82ef5b..10f1e08eded 100644 --- a/mysql-test/r/trigger-grant.result +++ b/mysql-test/r/trigger-grant.result @@ -135,6 +135,7 @@ SET @new_sum = 0; Warnings: Note 1449 There is no 'mysqltest_nonexs'@'localhost' registered INSERT INTO t1 VALUES(6); +ERROR HY000: There is no 'mysqltest_nonexs'@'localhost' registered SHOW TRIGGERS; Trigger Event Table Statement Timing Created sql_mode Definer trg1 INSERT t1 SET @new_sum = 0 BEFORE NULL mysqltest_inv@localhost diff --git a/mysql-test/r/view_grant.result b/mysql-test/r/view_grant.result index 8da8805b14b..928e3635ec6 100644 --- a/mysql-test/r/view_grant.result +++ b/mysql-test/r/view_grant.result @@ -522,11 +522,16 @@ use test; drop user mysqltest_1@localhost; drop database mysqltest; create definer=some_user@`` sql security invoker view v1 as select 1; -ERROR HY000: Definer is not fully qualified -create definer=some_user@localhost sql security invoker view v1 as select 1; +Warnings: +Note 1449 There is no 'some_user'@'' registered +create definer=some_user@localhost sql security invoker view v2 as select 1; Warnings: Note 1449 There is no 'some_user'@'localhost' registered show create view v1; View Create View -v1 CREATE ALGORITHM=UNDEFINED DEFINER=`some_user`@`localhost` SQL SECURITY INVOKER VIEW `v1` AS select 1 AS `1` +v1 CREATE ALGORITHM=UNDEFINED DEFINER=`some_user`@`` SQL SECURITY INVOKER VIEW `v1` AS select 1 AS `1` +show create view v2; +View Create View +v2 CREATE ALGORITHM=UNDEFINED DEFINER=`some_user`@`localhost` SQL SECURITY INVOKER VIEW `v2` AS select 1 AS `1` drop view v1; +drop view v2; diff --git a/mysql-test/t/trigger-grant.test b/mysql-test/t/trigger-grant.test index e79abf1b826..67aec1496dd 100644 --- a/mysql-test/t/trigger-grant.test +++ b/mysql-test/t/trigger-grant.test @@ -317,10 +317,7 @@ CREATE DEFINER='mysqltest_nonexs'@'localhost' # Check that trg2 will not be activated. -# --error ER_SPECIFIC_ACCESS_DENIED_ERROR -# -# TODO: Due to the BUG#13198(SP executes if definer does not exist) the -# following statement does not fail as it should. +--error ER_NO_SUCH_USER INSERT INTO t1 VALUES(6); # diff --git a/mysql-test/t/view_grant.test b/mysql-test/t/view_grant.test index a3a8aa6a4c3..504c4892808 100644 --- a/mysql-test/t/view_grant.test +++ b/mysql-test/t/view_grant.test @@ -712,10 +712,11 @@ connection default; # # DEFINER information check # --- error ER_MALFORMED_DEFINER create definer=some_user@`` sql security invoker view v1 as select 1; -create definer=some_user@localhost sql security invoker view v1 as select 1; +create definer=some_user@localhost sql security invoker view v2 as select 1; show create view v1; +show create view v2; drop view v1; +drop view v2; diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 4984443a01d..d48a884173f 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -7475,8 +7475,7 @@ LEX_USER *create_default_definer(THD *thd) /* - Create definer with the given user and host names. Also check that the user - and host names satisfy definers requirements. + Create definer with the given user and host names. SYNOPSIS create_definer() @@ -7494,14 +7493,6 @@ LEX_USER *create_definer(THD *thd, LEX_STRING *user_name, LEX_STRING *host_name) { LEX_USER *definer; - /* Check that specified host name is valid. */ - - if (host_name->length == 0) - { - my_error(ER_MALFORMED_DEFINER, MYF(0)); - return 0; - } - /* Create and initialize. */ if (! (definer= (LEX_USER*) thd->alloc(sizeof(LEX_USER)))) diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 4eec1a3175b..43b7c494246 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -831,7 +831,7 @@ 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 (!length || + if (length && !is_keyword(name,length) && !require_quotes(name, length) && !(thd->options & OPTION_QUOTE_SHOW_CREATE)) From 85d705b651cf9057f2d687294044f173880675c3 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 9 Mar 2006 10:09:52 -0800 Subject: [PATCH 14/18] Porting fix that allows others to include compiled code with different parsers. Makes you wonder what I am up to, doesn't? configure.in: Adjusts bison to not create symbols that clash. sql/sql_analyse.cc: Adding define to make sure sql_yacc.h gets included. sql/sql_lex.cc: Fixing it so that sql_yacc.h is included sql/sql_lex.h: Fixing include rules sql/sql_parse.cc: Adding flag to compile sql_yacc.yy --- configure.in | 2 +- sql/sql_analyse.cc | 2 ++ sql/sql_lex.cc | 1 + sql/sql_lex.h | 4 ++++ sql/sql_parse.cc | 1 + 5 files changed, 9 insertions(+), 1 deletion(-) diff --git a/configure.in b/configure.in index 4e630de13d2..4ff90bb39bb 100644 --- a/configure.in +++ b/configure.in @@ -236,7 +236,7 @@ AC_PROG_INSTALL test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL_PROGRAM}' # Not critical since the generated file is distributed -AC_PROG_YACC +AC_CHECK_PROGS(YACC, ['bison -y -p MYSQL']) AC_CHECK_PROG(PDFMANUAL, pdftex, manual.pdf) AC_CHECK_PROG(DVIS, tex, manual.dvi) diff --git a/sql/sql_analyse.cc b/sql/sql_analyse.cc index 0e4198a5114..af9246c673a 100644 --- a/sql/sql_analyse.cc +++ b/sql/sql_analyse.cc @@ -27,6 +27,8 @@ #pragma implementation // gcc: Class implementation #endif +#define MYSQL_LEX 1 + #include "mysql_priv.h" #include "procedure.h" #include "sql_analyse.h" diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 49b0c70ff03..110c82f6926 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -17,6 +17,7 @@ /* A lexical scanner on a temporary buffer with a yacc interface */ +#define MYSQL_LEX 1 #include "mysql_priv.h" #include "item_create.h" #include diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 8db059ae2fa..8bacc60d48d 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -37,8 +37,12 @@ class sp_pcontext; #define LEX_YYSTYPE void * #else #include "lex_symbol.h" +#if MYSQL_LEX #include "sql_yacc.h" #define LEX_YYSTYPE YYSTYPE * +#else +#define LEX_YYSTYPE void * +#endif #endif /* diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 527a6a67811..ea559c70734 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -14,6 +14,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#define MYSQL_LEX 1 #include "mysql_priv.h" #include "sql_repl.h" #include "repl_failsafe.h" From 2fbd17779feb849bbbd7b30ac439196634a1d7e3 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 9 Mar 2006 13:17:19 -0600 Subject: [PATCH 15/18] mysqltest.test: Fix typos. mysql-test/t/mysqltest.test: Fix typos. --- mysql-test/t/mysqltest.test | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/mysql-test/t/mysqltest.test b/mysql-test/t/mysqltest.test index a93aa24e812..3968eb519e1 100644 --- a/mysql-test/t/mysqltest.test +++ b/mysql-test/t/mysqltest.test @@ -27,7 +27,7 @@ # ---------------------------------------------------------------------------- # $mysql_errno contains the return code of the last command -# send to the server. +# sent to the server. # ---------------------------------------------------------------------------- # get $mysql_errno before the first statement # $mysql_errno should be -1 @@ -50,7 +50,7 @@ select otto from (select 1 as otto) as t1; # ---------------------------------------------------------------------------- # Negative case(statement): -# The dervied table t1 does not contain a column named 'friedrich' . +# The derived table t1 does not contain a column named 'friedrich' . # --> ERROR 42S22: Unknown column 'friedrich' in 'field list and # --> 1054: Unknown column 'friedrich' in 'field list' # ---------------------------------------------------------------------------- @@ -68,7 +68,7 @@ select friedrich from (select 1 as otto) as t1; # The following unmasked unsuccessful statement must give # 1. mysqltest gives a 'failed' # 2. does not produce a r/.reject file !!! -# PLEASE uncomment it and check it's effect +# PLEASE uncomment it and check its effect #select friedrich from (select 1 as otto) as t1; @@ -114,7 +114,7 @@ select friedrich from (select 1 as otto) as t1; # test cases for $mysql_errno # # $mysql_errno is a builtin variable of mysqltest and contains the return code -# of the last command send to the server. +# of the last command sent to the server. # # The following test cases often initialize $mysql_errno to 1064 by # a command with wrong syntax. @@ -217,7 +217,7 @@ garbage ; execute stmt; eval select $mysql_errno as "after_successful_execute" ; -# failing execute (table dropped) +# failing execute (table has been dropped) drop table t1; --error 1064 garbage ; @@ -249,8 +249,8 @@ eval select $mysql_errno as "after_failing_deallocate" ; # ---------------------------------------------------------------------------- # test cases for "--disable_abort_on_error" # -# "--disable_abort_on_error" switches the abort of mysqltest -# after "unmasked" failing statements off. +# "--disable_abort_on_error" switches off the abort of mysqltest +# after "unmasked" failing statements. # # The default is "--enable_abort_on_error". # @@ -258,13 +258,13 @@ eval select $mysql_errno as "after_failing_deallocate" ; # --error and --error # in the line before the failing statement. # -# There are some additional test case for $mysql_errno +# There are some additional test cases for $mysql_errno # because "--disable_abort_on_error" enables a new situation. # Example: "unmasked" statement fails + analysis of $mysql_errno # ---------------------------------------------------------------------------- # ---------------------------------------------------------------------------- -# Switch the abort on error off and check the effect on $mysql_errno +# Switch off the abort on error and check the effect on $mysql_errno # ---------------------------------------------------------------------------- --error 1064 garbage ; @@ -345,7 +345,7 @@ select 3 from t1 ; # ---------------------------------------------------------------------------- # Test detect end of line "junk" -# Most likely causes by a missing delimiter +# Most likely caused by a missing delimiter # ---------------------------------------------------------------------------- # Too many parameters to function @@ -1043,7 +1043,7 @@ let $message= . Here comes a very very long message that --source include/show_msg80.inc # -# Test --enable_parsning / disable_parsning +# Test --enable_parsing / disable_parsing # --disable_query_log --disable_parsing @@ -1070,9 +1070,9 @@ select "this will be executed"; # # Test that mysqltest fails when there are no queries executed -# but a result file exist +# but a result file exists # NOTE! This will never happen as long as it's not allowed to have -# test files that does not produce any output +# test files that produce no output #--exec echo "something" > $MYSQLTEST_VARDIR/tmp/result_file.result #--exec echo "let \$i= 1;" > $MYSQLTEST_VARDIR/tmp/query.sql #--error 1 From 3840774309bc4b812db7dd5caef0d598c56b5f3d Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 9 Mar 2006 15:12:43 -0500 Subject: [PATCH 16/18] Added code to mysqldump to dump timed events when instructed to do so, with the '-E' or '--events' flag. (Closes Bug#16853 and Bug#17714.) WARNING: At present, these tests fail due to b*g number 18078. client/mysqldump.c: Added code to dump events, when asked to do so via the --events parameter. Also cleaned up some surrounding code. mysql-test/r/mysqldump.result: Added a test to create an event, dump it, restore it, add more events, dump all of them, and restore all of them. mysql-test/t/mysqldump.test: Added a test to create an event, dump it, restore it, add more events, dump all of them, and restore all of them. sql/event_timed.cc: No longer qualify SHOW CREATE EVENT names with the database name. BitKeeper/etc/ignore: Removing accidentally 'ignored' bogus file entry. --- client/mysqldump.c | 154 ++++++++++++++++++++++++++++++++-- mysql-test/r/mysqldump.result | 37 ++++++++ mysql-test/t/mysqldump.test | 40 +++++++++ sql/event_timed.cc | 2 - 4 files changed, 225 insertions(+), 8 deletions(-) diff --git a/client/mysqldump.c b/client/mysqldump.c index 7dccbf95e20..836b33d2985 100644 --- a/client/mysqldump.c +++ b/client/mysqldump.c @@ -95,6 +95,7 @@ static my_bool verbose=0,tFlag=0,dFlag=0,quick= 1, extended_insert= 1, opt_complete_insert= 0, opt_drop_database= 0, opt_replace_into= 0, opt_dump_triggers= 0, opt_routines=0, opt_tz_utc=1, + opt_events= 0, opt_alltspcs=0; static ulong opt_max_allowed_packet, opt_net_buffer_length; static MYSQL mysql_connection,*sock=0; @@ -231,6 +232,9 @@ static struct my_option my_long_options[] = {"disable-keys", 'K', "'/*!40000 ALTER TABLE tb_name DISABLE KEYS */; and '/*!40000 ALTER TABLE tb_name ENABLE KEYS */; will be put in the output.", (gptr*) &opt_disable_keys, (gptr*) &opt_disable_keys, 0, GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0}, + {"events", 'E', "Dump events.", + (gptr*) &opt_events, (gptr*) &opt_events, 0, GET_BOOL, + NO_ARG, 0, 0, 0, 0, 0, 0}, {"extended-insert", 'e', "Allows utilization of the new, much faster INSERT syntax.", (gptr*) &extended_insert, (gptr*) &extended_insert, 0, GET_BOOL, NO_ARG, @@ -1238,9 +1242,136 @@ static void print_xml_row(FILE *xml_file, const char *row_name, check_io(xml_file); } + +/* + create_delimiter + Generate a new (null-terminated) string that does not exist in query + and is therefore suitable for use as a query delimiter. Store this + delimiter in delimiter_buff . + + This is quite simple in that it doesn't even try to parse statements as an + interpreter would. It merely returns a string that is not in the query, which + is much more than adequate for constructing a delimiter. + + RETURN + ptr to the delimiter on Success + NULL on Failure +*/ +static char *create_delimiter(char *query, char *delimiter_buff, + int delimiter_max_size) +{ + int proposed_length; + char *presence; + + delimiter_buff[0]= ';'; /* start with one semicolon, and */ + + for (proposed_length= 2; proposed_length < delimiter_max_size; + delimiter_max_size++) { + + delimiter_buff[proposed_length-1]= ';'; /* add semicolons, until */ + delimiter_buff[proposed_length]= '\0'; + + presence = strstr(query, delimiter_buff); + if (presence == NULL) { /* the proposed delimiter is not in the query. */ + return delimiter_buff; + } + + } + return NULL; /* but if we run out of space, return nothing at all. */ +} + + +/* + dump_events_for_db + -- retrieves list of events for a given db, and prints out + the CREATE EVENT statement into the output (the dump). + + RETURN + 0 Success + 1 Error +*/ +static uint dump_events_for_db(char *db) +{ + char query_buff[QUERY_LENGTH]; + char db_name_buff[NAME_LEN*2+3], name_buff[NAME_LEN*2+3]; + char *event_name; + char delimiter[QUERY_LENGTH], *delimit_test; + FILE *sql_file= md_result_file; + MYSQL_RES *event_res, *event_list_res; + MYSQL_ROW row, event_list_row; + DBUG_ENTER("dump_events_for_db"); + DBUG_PRINT("enter", ("db: '%s'", db)); + + mysql_real_escape_string(sock, db_name_buff, db, strlen(db)); + + /* nice comments */ + if (opt_comments) + fprintf(sql_file, "\n--\n-- Dumping events for database '%s'\n--\n", db); + + /* + not using "mysql_query_with_error_report" because we may have not + enough privileges to lock mysql.events. + */ + if (lock_tables) + mysql_query(sock, "LOCK TABLES mysql.event READ"); + + if (mysql_query_with_error_report(sock, &event_list_res, "show events")) + { + safe_exit(EX_MYSQLERR); + DBUG_RETURN(0); + } + + strcpy(delimiter, ";"); + if (mysql_num_rows(event_list_res) > 0) + { + while ((event_list_row= mysql_fetch_row(event_list_res)) != NULL) + { + event_name= quote_name(event_list_row[1], name_buff, 0); + DBUG_PRINT("info", ("retrieving CREATE EVENT for %s", name_buff)); + my_snprintf(query_buff, sizeof(query_buff), "SHOW CREATE EVENT %s", + event_name); + + if (mysql_query_with_error_report(sock, &event_res, query_buff)) + DBUG_RETURN(1); + + while ((row= mysql_fetch_row(event_res)) != NULL) + { + /* + if the user has EXECUTE privilege he can see event names, but not the + event body! + */ + if (strlen(row[2]) != 0) + { + if (opt_drop) + fprintf(sql_file, "/*!50106 DROP EVENT IF EXISTS %s */%s\n", + event_name, delimiter); + + delimit_test= create_delimiter(row[2], delimiter, sizeof(delimiter)); + if (delimit_test == NULL) { + fprintf(stderr, "%s: Warning: Can't dump event '%s'\n", + event_name, my_progname); + DBUG_RETURN(1); + } + + fprintf(sql_file, "DELIMITER %s\n", delimiter); + fprintf(sql_file, "/*!50106 %s */ %s\n", row[2], delimiter); + } + } /* end of event printing */ + } /* end of list of events */ + fprintf(sql_file, "DELIMITER ;\n"); + mysql_free_result(event_res); + } + mysql_free_result(event_list_res); + + if (lock_tables) + VOID(mysql_query_with_error_report(sock, 0, "UNLOCK TABLES")); + DBUG_RETURN(0); +} + + /* dump_routines_for_db - -- retrievs list of routines for a given db, and prints out + -- retrieves list of routines for a given db, and prints out the CREATE PROCEDURE definition into the output (the dump). This function has logic to print the appropriate syntax depending on whether @@ -1253,7 +1384,7 @@ static void print_xml_row(FILE *xml_file, const char *row_name, static uint dump_routines_for_db(char *db) { - char query_buff[512]; + char query_buff[QUERY_LENGTH]; const char *routine_type[]= {"FUNCTION", "PROCEDURE"}; char db_name_buff[NAME_LEN*2+3], name_buff[NAME_LEN*2+3]; char *routine_name; @@ -1294,9 +1425,9 @@ static uint dump_routines_for_db(char *db) while ((routine_list_row= mysql_fetch_row(routine_list_res))) { + routine_name= quote_name(routine_list_row[1], name_buff, 0); DBUG_PRINT("info", ("retrieving CREATE %s for %s", routine_type[i], name_buff)); - routine_name= quote_name(routine_list_row[1], name_buff, 0); my_snprintf(query_buff, sizeof(query_buff), "SHOW CREATE %s %s", routine_type[i], routine_name); @@ -1364,7 +1495,7 @@ static uint get_table_structure(char *table, char *db, char *table_type, char *result_table, *opt_quoted_table; const char *insert_option; char name_buff[NAME_LEN+3],table_buff[NAME_LEN*2+3]; - char table_buff2[NAME_LEN*2+3], query_buff[512]; + char table_buff2[NAME_LEN*2+3], query_buff[QUERY_LENGTH]; FILE *sql_file = md_result_file; int len; MYSQL_RES *result; @@ -1840,7 +1971,7 @@ static void dump_triggers_for_table (char *table, char *db) { char *result_table; char name_buff[NAME_LEN*4+3], table_buff[NAME_LEN*2+3]; - char query_buff[512]; + char query_buff[QUERY_LENGTH]; uint old_opt_compatible_mode=opt_compatible_mode; FILE *sql_file = md_result_file; MYSQL_RES *result; @@ -2500,7 +2631,6 @@ static int dump_all_tablespaces() { MYSQL_ROW row; MYSQL_RES *tableres; - int result=0; char buf[FN_REFLEN]; int first; @@ -2798,6 +2928,12 @@ static int dump_all_tables_in_db(char *database) dump_triggers_for_table(table, database); } } + if (opt_events && !opt_xml && + mysql_get_server_version(sock) >= 50106) + { + DBUG_PRINT("info", ("Dumping events for database %s", database)); + dump_events_for_db(database); + } if (opt_routines && !opt_xml && mysql_get_server_version(sock) >= 50009) { @@ -3008,6 +3144,12 @@ static int dump_selected_tables(char *db, char **table_names, int tables) get_view_structure(table_name, db); } } + if (opt_events && !opt_xml && + mysql_get_server_version(sock) >= 50106) + { + DBUG_PRINT("info", ("Dumping events for database %s", db)); + dump_events_for_db(db); + } /* obtain dump of routines (procs/functions) */ if (opt_routines && !opt_xml && mysql_get_server_version(sock) >= 50009) diff --git a/mysql-test/r/mysqldump.result b/mysql-test/r/mysqldump.result index e54dee1fcfd..64cec574678 100644 --- a/mysql-test/r/mysqldump.result +++ b/mysql-test/r/mysqldump.result @@ -2907,3 +2907,40 @@ mysql-import: Error: 1146, Table 'test.words' doesn't exist, when using table: w drop table t1; drop table t2; drop table words2; +create database first; +use first; +set time_zone = 'UTC'; +create event ee1 on schedule at '2035-12-31 20:01:23' do set @a=5; +show events; +Db Name Definer Type Execute at Interval value Interval field Starts Ends Status +first ee1 root@localhost ONE TIME 2035-12-31 20:01:23 NULL NULL NULL NULL ENABLED +show create event ee1; +Event sql_mode Create Event +ee1 CREATE EVENT `ee1` ON SCHEDULE AT '2035-12-31 20:01:23' ON COMPLETION NOT PRESERVE ENABLE DO set @a=5 +drop database first; +create database second; +use second; +show events; +Db Name Definer Type Execute at Interval value Interval field Starts Ends Status +second ee1 root@localhost ONE TIME 2035-12-31 20:01:23 NULL NULL NULL NULL ENABLED +show create event ee1; +Event sql_mode Create Event +ee1 NO_AUTO_VALUE_ON_ZERO CREATE EVENT `ee1` ON SCHEDULE AT '2035-12-31 20:01:23' ON COMPLETION NOT PRESERVE ENABLE DO set @a=5 +create event ee2 on schedule at '2018-12-31 21:01:23' do set @a=5; +create event ee3 on schedule at '2030-12-31 22:01:23' do set @a=5; +show events; +Db Name Definer Type Execute at Interval value Interval field Starts Ends Status +second ee1 root@localhost ONE TIME 2035-12-31 20:01:23 NULL NULL NULL NULL ENABLED +second ee2 root@localhost ONE TIME 2018-12-31 21:01:23 NULL NULL NULL NULL ENABLED +second ee3 root@localhost ONE TIME 2030-12-31 22:01:23 NULL NULL NULL NULL ENABLED +drop database second; +create database third; +use third; +show events; +Db Name Definer Type Execute at Interval value Interval field Starts Ends Status +third ee1 root@localhost ONE TIME 2035-12-31 20:01:23 NULL NULL NULL NULL ENABLED +third ee2 root@localhost ONE TIME 2018-12-31 21:01:23 NULL NULL NULL NULL ENABLED +third ee3 root@localhost ONE TIME 2030-12-31 22:01:23 NULL NULL NULL NULL ENABLED +drop database third; +set time_zone = 'SYSTEM'; +use test; diff --git a/mysql-test/t/mysqldump.test b/mysql-test/t/mysqldump.test index b1ddfc97c70..c883ddf86a9 100644 --- a/mysql-test/t/mysqldump.test +++ b/mysql-test/t/mysqldump.test @@ -1166,3 +1166,43 @@ drop table t1; drop table t2; drop table words2; + +# +# BUG# 16853: mysqldump doesn't show events +# +create database first; +use first; +set time_zone = 'UTC'; + +## prove one works +create event ee1 on schedule at '2035-12-31 20:01:23' do set @a=5; +show events; +show create event ee1; +--exec $MYSQL_DUMP --events first > $MYSQLTEST_VARDIR/tmp/bug16853-1.sql +drop database first; + +create database second; +use second; +--exec $MYSQL second < $MYSQLTEST_VARDIR/tmp/bug16853-1.sql +show events; +show create event ee1; + +## prove three works +# start with one from the previous restore +create event ee2 on schedule at '2018-12-31 21:01:23' do set @a=5; +create event ee3 on schedule at '2030-12-31 22:01:23' do set @a=5; +show events; +--exec $MYSQL_DUMP --events second > $MYSQLTEST_VARDIR/tmp/bug16853-2.sql +drop database second; + +create database third; +use third; +--exec $MYSQL third < $MYSQLTEST_VARDIR/tmp/bug16853-2.sql +show events; +drop database third; + +# revert back to normal settings +set time_zone = 'SYSTEM'; +use test; + +##### diff --git a/sql/event_timed.cc b/sql/event_timed.cc index a8620197668..c1fd433dc5b 100644 --- a/sql/event_timed.cc +++ b/sql/event_timed.cc @@ -1063,8 +1063,6 @@ Event_timed::get_create_event(THD *thd, String *buf) DBUG_RETURN(EVEX_MICROSECOND_UNSUP); buf->append(STRING_WITH_LEN("CREATE EVENT ")); - append_identifier(thd, buf, dbname.str, dbname.length); - buf->append(STRING_WITH_LEN(".")); append_identifier(thd, buf, name.str, name.length); buf->append(STRING_WITH_LEN(" ON SCHEDULE ")); From 01d69c4b966015a7fc5806d337e3006b22baed47 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 9 Mar 2006 16:44:08 -0800 Subject: [PATCH 17/18] This patch does 1) fix my build breakage 2) Complete the removal of all symbols which could clash with another parser. sql/mysql_priv.h: Porting update sql/mysqld.cc: Porting update sql/sp.cc: Porting update sql/sql_lex.cc: Porting update sql/sql_lex.h: Porting update sql/sql_parse.cc: Porting update sql/sql_prepare.cc: Portinng update sql/sql_trigger.cc: Porting update sql/sql_view.cc: Porting update --- sql/mysql_priv.h | 4 ++-- sql/mysqld.cc | 2 +- sql/sp.cc | 4 ++-- sql/sql_lex.cc | 4 ++-- sql/sql_lex.h | 4 ++-- sql/sql_parse.cc | 6 +++--- sql/sql_prepare.cc | 2 +- sql/sql_trigger.cc | 3 ++- sql/sql_view.cc | 3 ++- 9 files changed, 17 insertions(+), 15 deletions(-) diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index a43307edf27..68679cf79dc 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -1093,7 +1093,7 @@ File open_binlog(IO_CACHE *log, const char *log_file_name, const char **errmsg); /* mysqld.cc */ -extern void yyerror(const char*); +extern void MYSQLerror(const char*); /* item_func.cc */ extern bool check_reserved_words(LEX_STRING *name); @@ -1415,7 +1415,7 @@ void free_list(I_List *list); void free_list(I_List *list); /* sql_yacc.cc */ -extern int yyparse(void *thd); +extern int MYSQLparse(void *thd); /* frm_crypt.cc */ #ifdef HAVE_CRYPTED_FRM diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 37a135fa063..bc30a2c981b 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -1524,7 +1524,7 @@ static void network_init(void) #endif /*!EMBEDDED_LIBRARY*/ -void yyerror(const char *s) +void MYSQLerror(const char *s) { THD *thd=current_thd; char *yytext= (char*) thd->lex->tok_start; diff --git a/sql/sp.cc b/sql/sp.cc index e4489af1fdd..0446bf94e53 100644 --- a/sql/sp.cc +++ b/sql/sp.cc @@ -269,7 +269,7 @@ db_find_routine_aux(THD *thd, int type, sp_name *name, TABLE *table) static int db_find_routine(THD *thd, int type, sp_name *name, sp_head **sphp) { - extern int yyparse(void *thd); + extern int MYSQLparse(void *thd); TABLE *table; const char *params, *returns, *body; int ret; @@ -459,7 +459,7 @@ db_load_routine(THD *thd, int type, sp_name *name, sp_head **sphp, lex_start(thd, (uchar*)defstr.c_ptr(), defstr.length()); thd->spcont= 0; - if (yyparse(thd) || thd->is_fatal_error || newlex.sphead == NULL) + if (MYSQLparse(thd) || thd->is_fatal_error || newlex.sphead == NULL) { sp_head *sp= newlex.sphead; diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 110c82f6926..2b31abd6a50 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -507,14 +507,14 @@ static inline uint int_token(const char *str,uint length) } /* - yylex remember the following states from the following yylex() + MYSQLlex remember the following states from the following MYSQLlex() - MY_LEX_EOQ Found end of query - MY_LEX_OPERATOR_OR_IDENT Last state was an ident, text or number (which can't be followed by a signed number) */ -int yylex(void *arg, void *yythd) +int MYSQLlex(void *arg, void *yythd) { reg1 uchar c; int tokval, result_state; diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 8bacc60d48d..8512c075f69 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -716,7 +716,7 @@ typedef struct st_lex uchar *buf; /* The beginning of string, used by SPs */ uchar *ptr,*tok_start,*tok_end,*end_of_query; - /* The values of tok_start/tok_end as they were one call of yylex before */ + /* The values of tok_start/tok_end as they were one call of MYSQLlex before */ uchar *tok_start_prev, *tok_end_prev; char *length,*dec,*change,*name; @@ -1065,7 +1065,7 @@ extern void lex_init(void); extern void lex_free(void); extern void lex_start(THD *thd, uchar *buf,uint length); extern void lex_end(LEX *lex); -extern int yylex(void *arg, void *yythd); +extern int MYSQLlex(void *arg, void *yythd); extern pthread_key(LEX*,THR_LEX); diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index ea559c70734..489afe79b5e 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -4244,7 +4244,7 @@ end_with_restore_list: /* We must cleanup the unit and the lex here because sp_grant_privileges calls (indirectly) db_find_routine, - which in turn may call yyparse with THD::lex. + which in turn may call MYSQLparse with THD::lex. TODO: fix db_find_routine to use a temporary lex. */ lex->unit.cleanup(); @@ -5659,7 +5659,7 @@ void mysql_parse(THD *thd, char *inBuf, uint length) sp_cache_flush_obsolete(&thd->sp_proc_cache); sp_cache_flush_obsolete(&thd->sp_func_cache); - if (!yyparse((void *)thd) && ! thd->is_fatal_error) + if (!MYSQLparse((void *)thd) && ! thd->is_fatal_error) { #ifndef NO_EMBEDDED_ACCESS_CHECKS if (mqh_used && thd->user_connect && @@ -5739,7 +5739,7 @@ bool mysql_test_parse_for_slave(THD *thd, char *inBuf, uint length) DBUG_ENTER("mysql_test_parse_for_slave"); mysql_init_query(thd, (uchar*) inBuf, length); - if (!yyparse((void*) thd) && ! thd->is_fatal_error && + if (!MYSQLparse((void*) thd) && ! thd->is_fatal_error && all_tables_not_ok(thd,(TABLE_LIST*) lex->select_lex.table_list.first)) error= 1; /* Ignore question */ thd->end_statement(); diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index 332f699200e..0f4ae04962b 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -2757,7 +2757,7 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len) lex->safe_to_cache_query= FALSE; lex->stmt_prepare_mode= TRUE; - error= yyparse((void *)thd) || thd->is_fatal_error || + error= MYSQLparse((void *)thd) || thd->is_fatal_error || thd->net.report_error || init_param_array(this); /* While doing context analysis of the query (in check_prepared_statement) diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc index 024c882f8a9..b432961b7e6 100644 --- a/sql/sql_trigger.cc +++ b/sql/sql_trigger.cc @@ -15,6 +15,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#define MYSQL_LEX 1 #include "mysql_priv.h" #include "sp_head.h" #include "sql_trigger.h" @@ -932,7 +933,7 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db, lex_start(thd, (uchar*)trg_create_str->str, trg_create_str->length); thd->spcont= 0; - if (yyparse((void *)thd) || thd->is_fatal_error) + if (MYSQLparse((void *)thd) || thd->is_fatal_error) { /* Free lex associated resources. diff --git a/sql/sql_view.cc b/sql/sql_view.cc index 4f2a9a07705..2832adc1f8f 100644 --- a/sql/sql_view.cc +++ b/sql/sql_view.cc @@ -15,6 +15,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#define MYSQL_LEX 1 #include "mysql_priv.h" #include "sql_select.h" #include "parse_file.h" @@ -889,7 +890,7 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table) MODE_IGNORE_SPACE | MODE_NO_BACKSLASH_ESCAPES); CHARSET_INFO *save_cs= thd->variables.character_set_client; thd->variables.character_set_client= system_charset_info; - res= yyparse((void *)thd); + res= MYSQLparse((void *)thd); thd->variables.character_set_client= save_cs; thd->variables.sql_mode= save_mode; } From 3c7a8c3748b1dd9e2d986395803172144f07ef62 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 9 Mar 2006 17:37:59 -0800 Subject: [PATCH 18/18] Fixed portability issue in 5.1 to include parsers from other applications. sql/event_timed.cc: Added support for new parser function sql/sql_partition.cc: Change calling parser name. --- sql/event_timed.cc | 5 +++-- sql/sql_partition.cc | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/sql/event_timed.cc b/sql/event_timed.cc index a8620197668..40987c9132e 100644 --- a/sql/event_timed.cc +++ b/sql/event_timed.cc @@ -14,13 +14,14 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#define MYSQL_LEX 1 #include "event_priv.h" #include "event.h" #include "sp.h" -extern int yyparse(void *thd); +extern int MYSQLparse(void *thd); /* Init all member variables @@ -1338,7 +1339,7 @@ Event_timed::compile(THD *thd, MEM_ROOT *mem_root) thd->lex= &lex; lex_start(thd, (uchar*)thd->query, thd->query_length); lex.et_compile_phase= TRUE; - if (yyparse((void *)thd) || thd->is_fatal_error) + if (MYSQLparse((void *)thd) || thd->is_fatal_error) { DBUG_PRINT("error", ("error during compile or thd->is_fatal_error=%d", thd->is_fatal_error)); diff --git a/sql/sql_partition.cc b/sql/sql_partition.cc index 257c1988cbd..014d3616d3d 100644 --- a/sql/sql_partition.cc +++ b/sql/sql_partition.cc @@ -33,6 +33,7 @@ /* Some general useful functions */ +#define MYSQL_LEX 1 #include "mysql_priv.h" #include #include @@ -3697,7 +3698,7 @@ bool mysql_unpack_partition(THD *thd, const uchar *part_buf, we then save in the partition info structure. */ thd->free_list= NULL; - lex.part_info= new partition_info();/* Indicates yyparse from this place */ + lex.part_info= new partition_info();/* Indicates MYSQLparse from this place */ if (!lex.part_info) { mem_alloc_error(sizeof(partition_info)); @@ -3706,7 +3707,7 @@ bool mysql_unpack_partition(THD *thd, const uchar *part_buf, lex.part_info->part_state= part_state; lex.part_info->part_state_len= part_state_len; DBUG_PRINT("info", ("Parse: %s", part_buf)); - if (yyparse((void*)thd) || thd->is_fatal_error) + if (MYSQLparse((void*)thd) || thd->is_fatal_error) { free_items(thd->free_list); goto end;