diff --git a/client/mysqltest.c b/client/mysqltest.c index ce1a64ec653..0d97eb38c30 100644 --- a/client/mysqltest.c +++ b/client/mysqltest.c @@ -1443,38 +1443,40 @@ int do_system(struct st_query *q) /* Print the content between echo and to result file. - If content is a variable, the variable value will be retrieved + Evaluate all variables in the string before printing, allow + for variable names to be escaped using \ SYNOPSIS do_echo() q called command DESCRIPTION - Usage 1: echo text Print the text after echo until end of command to result file - Usage 2: echo $ Print the content of the variable to result file + echo Some text $ + Print "Some text" plus the content of the variable to + result file + + echo Some text \$ + Print "Some text" plus $ to result file */ -int do_echo(struct st_query *q) +int do_echo(struct st_query *command) { - char *p= q->first_argument; - DYNAMIC_STRING *ds; - VAR v; - var_init(&v,0,0,0,0); + DYNAMIC_STRING *ds, ds_echo; ds= &ds_res; - eval_expr(&v, p, 0); /* NULL terminated */ - if (v.str_val_len) - dynstr_append_mem(ds, v.str_val, v.str_val_len); + init_dynamic_string(&ds_echo, "", 256, 256); + do_eval(&ds_echo, command->first_argument); + dynstr_append_mem(ds, ds_echo.str, ds_echo.length); dynstr_append_mem(ds, "\n", 1); - var_free(&v); - q->last_argument= q->end; + dynstr_free(&ds_echo); + command->last_argument= command->end; return 0; } @@ -3733,7 +3735,7 @@ static void append_stmt_result(DYNAMIC_STRING *ds, MYSQL_STMT *stmt, if (mysql_stmt_fetch(stmt) != MYSQL_NO_DATA) die("fetch didn't end with MYSQL_NO_DATA from statement: %d %s", - mysql_stmt_error(stmt), mysql_stmt_errno(stmt)); + mysql_stmt_errno(stmt), mysql_stmt_error(stmt)); free_replace_column(); @@ -4199,7 +4201,7 @@ static void run_query_stmt(MYSQL *mysql, struct st_query *command, if (mysql_stmt_prepare(stmt, query, query_len)) { handle_error(query, command, mysql_stmt_errno(stmt), - mysql_stmt_error(stmt), mysql_stmt_sqlstate(stmt), ds); + mysql_stmt_error(stmt), mysql_stmt_sqlstate(stmt), ds); goto end; } @@ -4215,29 +4217,34 @@ static void run_query_stmt(MYSQL *mysql, struct st_query *command, parameter markers. */ -#ifdef BUG14013_FIXED - /* - Use cursor when retrieving result - */ if (cursor_protocol_enabled) { + /* + Use cursor when retrieving result + */ ulong type= CURSOR_TYPE_READ_ONLY; if (mysql_stmt_attr_set(stmt, STMT_ATTR_CURSOR_TYPE, (void*) &type)) die("mysql_stmt_attr_set(STMT_ATTR_CURSOR_TYPE) failed': %d %s", - mysql_stmt_errno(stmt), mysql_stmt_error(stmt)); + mysql_stmt_errno(stmt), mysql_stmt_error(stmt)); } -#endif /* Execute the query - */ + */ if (mysql_stmt_execute(stmt)) { handle_error(query, command, mysql_stmt_errno(stmt), - mysql_stmt_error(stmt), mysql_stmt_sqlstate(stmt), ds); + mysql_stmt_error(stmt), mysql_stmt_sqlstate(stmt), ds); goto end; } + /* + When running in cursor_protocol get the warnings from execute here + and keep them in a separate string for later. + */ + if (cursor_protocol_enabled && !disable_warnings) + append_warnings(&ds_execute_warnings, mysql); + /* We instruct that we want to update the "max_length" field in mysql_stmt_store_result(), this is our only way to know how much @@ -4247,7 +4254,7 @@ static void run_query_stmt(MYSQL *mysql, struct st_query *command, my_bool one= 1; if (mysql_stmt_attr_set(stmt, STMT_ATTR_UPDATE_MAX_LENGTH, (void*) &one)) die("mysql_stmt_attr_set(STMT_ATTR_UPDATE_MAX_LENGTH) failed': %d %s", - mysql_stmt_errno(stmt), mysql_stmt_error(stmt)); + mysql_stmt_errno(stmt), mysql_stmt_error(stmt)); } /* @@ -4257,7 +4264,7 @@ static void run_query_stmt(MYSQL *mysql, struct st_query *command, if (mysql_stmt_store_result(stmt)) { handle_error(query, command, mysql_stmt_errno(stmt), - mysql_stmt_error(stmt), mysql_stmt_sqlstate(stmt), ds); + mysql_stmt_error(stmt), mysql_stmt_sqlstate(stmt), ds); goto end; } @@ -4278,10 +4285,10 @@ static void run_query_stmt(MYSQL *mysql, struct st_query *command, uint num_fields= mysql_num_fields(res); if (display_metadata) - append_metadata(ds, fields, num_fields); + append_metadata(ds, fields, num_fields); if (!display_result_vertically) - append_table_headings(ds, fields, num_fields); + append_table_headings(ds, fields, num_fields); append_stmt_result(ds, stmt, fields, num_fields); @@ -4303,10 +4310,11 @@ static void run_query_stmt(MYSQL *mysql, struct st_query *command, /* Append warnings to ds - if there are any */ if (append_warnings(&ds_execute_warnings, mysql) || - ds_prepare_warnings.length || - ds_warnings->length) + ds_execute_warnings.length || + ds_prepare_warnings.length || + ds_warnings->length) { - dynstr_append_mem(ds, "Warnings:\n", 10); + dynstr_append_mem(ds, "Warnings:\n", 10); if (ds_warnings->length) dynstr_append_mem(ds, ds_warnings->str, ds_warnings->length); diff --git a/libmysql/libmysql.c b/libmysql/libmysql.c index d1d6dc02f91..4b7aace549a 100644 --- a/libmysql/libmysql.c +++ b/libmysql/libmysql.c @@ -2002,6 +2002,7 @@ mysql_stmt_init(MYSQL *mysql) stmt->mysql= mysql; stmt->read_row_func= stmt_read_row_no_result_set; stmt->prefetch_rows= DEFAULT_PREFETCH_ROWS; + strmov(stmt->sqlstate, not_error_sqlstate); /* The rest of statement members was bzeroed inside malloc */ DBUG_RETURN(stmt); @@ -4751,12 +4752,39 @@ int STDCALL mysql_stmt_store_result(MYSQL_STMT *stmt) if (!stmt->field_count) DBUG_RETURN(0); - if ((int) stmt->state < (int) MYSQL_STMT_EXECUTE_DONE || - mysql->status != MYSQL_STATUS_GET_RESULT) + + if ((int) stmt->state < (int) MYSQL_STMT_EXECUTE_DONE) { set_stmt_error(stmt, CR_COMMANDS_OUT_OF_SYNC, unknown_sqlstate); DBUG_RETURN(1); } + + if (mysql->status == MYSQL_STATUS_READY && + stmt->server_status & SERVER_STATUS_CURSOR_EXISTS) + { + /* + Server side cursor exist, tell server to start sending the rows + */ + NET *net= &mysql->net; + char buff[4 /* statement id */ + + 4 /* number of rows to fetch */]; + + /* Send row request to the server */ + int4store(buff, stmt->stmt_id); + int4store(buff + 4, (int)~0); /* number of rows to fetch */ + if (cli_advanced_command(mysql, COM_STMT_FETCH, buff, sizeof(buff), + NullS, 0, 1)) + { + set_stmt_errmsg(stmt, net->last_error, net->last_errno, net->sqlstate); + DBUG_RETURN(1); + } + } + else if (mysql->status != MYSQL_STATUS_GET_RESULT) + { + set_stmt_error(stmt, CR_COMMANDS_OUT_OF_SYNC, unknown_sqlstate); + DBUG_RETURN(1); + } + if (result->data) { free_root(&result->alloc, MYF(MY_KEEP_PREALLOC)); @@ -4797,6 +4825,10 @@ int STDCALL mysql_stmt_store_result(MYSQL_STMT *stmt) DBUG_RETURN(1); } + /* Assert that if there was a cursor, all rows have been fetched */ + DBUG_ASSERT(mysql->status != MYSQL_STATUS_READY || + (mysql->server_status & SERVER_STATUS_LAST_ROW_SENT)); + if (stmt->update_max_length) { MYSQL_ROWS *cur= result->data; diff --git a/mysql-test/r/events.result b/mysql-test/r/events.result index 86f98f0da8b..2ab2dd0076c 100644 --- a/mysql-test/r/events.result +++ b/mysql-test/r/events.result @@ -1,5 +1,29 @@ create database if not exists events_test; use events_test; +CREATE USER pauline@localhost; +CREATE DATABASE db_x; +GRANT EVENT ON db_x.* TO pauline@localhost; +USE db_x; +CREATE TABLE x_table(a int); +CREATE EVENT e_x1 ON SCHEDULE EVERY 1 SECOND DO DROP DATABASE db_x; +CREATE EVENT e_x2 ON SCHEDULE EVERY 1 SECOND DO DROP TABLE x_table; +SHOW DATABASES LIKE 'db_x'; +Database (db_x) +db_x +SET GLOBAL event_scheduler=1; +SHOW DATABASES LIKE 'db_x'; +Database (db_x) +db_x +SHOW TABLES FROM db_x; +Tables_in_db_x +x_table +SET GLOBAL event_scheduler=0; +DROP EVENT e_x1; +DROP EVENT e_x2; +DROP DATABASE db_x; +DROP USER pauline@localhost; +USE events_test; +SET GLOBAL event_scheduler=0; drop event if exists event1; Warnings: Note 1305 Event event1 does not exist @@ -20,6 +44,14 @@ select db, name, body, status, interval_field, interval_value from mysql.event; db name body status interval_field interval_value events_test e_43 set @a = 4 ENABLED SECOND 1 drop event e_43; +"Let's check whether we can use non-qualified names" +create table non_qualif(a int); +create event non_qualif_ev on schedule every 10 minute do insert into non_qualif values (800219); +select * from non_qualif; +a +800219 +drop event non_qualif_ev; +drop table non_qualif; set global event_scheduler = 0; create table t_event3 (a int, b float); drop event if exists event3; @@ -331,8 +363,8 @@ create event закачка on schedule every 10 hour do select get_lock("test_l show processlist; Id User Host db Command Time State Info # root localhost events_test Query # NULL show processlist -# event_scheduler NULL Connect # Sleeping NULL -# root events_test Connect # User lock select get_lock("test_lock2", 20) +# event_scheduler connecting host NULL Connect # Sleeping NULL +# root localhost events_test Connect # User lock select get_lock("test_lock2", 20) "Release the mutex, the event worker should finish." select release_lock("test_lock2"); release_lock("test_lock2") @@ -350,8 +382,7 @@ set global event_scheduler=0; show processlist; Id User Host db Command Time State Info # root localhost events_test Query # NULL show processlist -# event_scheduler NULL Connect # Sleeping NULL -# root events_test Connect # User lock select get_lock("test_lock2_1", 20) +# event_scheduler connecting host NULL Connect # Sleeping NULL "Release the lock so the child process should finish. Hence the scheduler also" select release_lock("test_lock2_1"); release_lock("test_lock2_1") diff --git a/mysql-test/r/events_stress.result b/mysql-test/r/events_stress.result new file mode 100644 index 00000000000..9f95cfad75d --- /dev/null +++ b/mysql-test/r/events_stress.result @@ -0,0 +1,46 @@ +CREATE DATABASE IF NOT EXISTS events_test; +CREATE DATABASE events_test2; +USE events_test2; +CREATE EVENT ev_drop1 ON SCHEDULE EVERY 10 MINUTE DISABLE DO SELECT 1; +CREATE EVENT ev_drop2 ON SCHEDULE EVERY 10 MINUTE DISABLE DO SELECT 1; +CREATE EVENT ev_drop3 ON SCHEDULE EVERY 10 MINUTE DISABLE DO SELECT 1; +USE events_test; +SELECT COUNT(*) FROM INFORMATION_SCHEMA.EVENTS WHERE EVENT_SCHEMA='events_test2'; +COUNT(*) +3 +DROP DATABASE events_test2; +SELECT COUNT(*) FROM INFORMATION_SCHEMA.EVENTS WHERE EVENT_SCHEMA='events_test2'; +COUNT(*) +0 +"Now testing stability - dropping db -> events while they are running" +CREATE DATABASE events_test2; +USE events_test2; +SELECT COUNT(*) FROM INFORMATION_SCHEMA.EVENTS WHERE EVENT_SCHEMA='events_test2'; +COUNT(*) +1000 +SET GLOBAL event_scheduler=1; +DROP DATABASE events_test2; +SET GLOBAL event_scheduler=0; +SELECT COUNT(*) FROM INFORMATION_SCHEMA.EVENTS WHERE EVENT_SCHEMA='events_test2'; +COUNT(*) +0 +CREATE DATABASE events_test3; +USE events_test3; +SELECT COUNT(*) FROM INFORMATION_SCHEMA.EVENTS WHERE EVENT_SCHEMA='events_test3'; +COUNT(*) +950 +CREATE DATABASE events_test4; +USE events_test4; +CREATE DATABASE events_test2; +USE events_test2; +SELECT COUNT(*) FROM INFORMATION_SCHEMA.EVENTS WHERE EVENT_SCHEMA='events_test2'; +COUNT(*) +1050 +DROP DATABASE events_test2; +SET GLOBAL event_scheduler=0; +DROP DATABASE events_test3; +SET GLOBAL event_scheduler=1; +DROP DATABASE events_test4; +SET GLOBAL event_scheduler=1; +USE events_test; +DROP DATABASE events_test; diff --git a/mysql-test/r/mysqltest.result b/mysql-test/r/mysqltest.result index 2ef005f4ceb..03acd7a2ce2 100644 --- a/mysql-test/r/mysqltest.result +++ b/mysql-test/r/mysqltest.result @@ -201,8 +201,14 @@ source database - world''s most -- popular open # source database -'$message' -"$message" +'# MySQL: The +- world''s most +-- popular open +# source database' +"# MySQL: The +- world''s most +-- popular open +# source database" hej hej hej @@ -221,6 +227,15 @@ mysqltest: At line 1: Missing arguments to let mysqltest: At line 1: Missing variable name in let mysqltest: At line 1: Missing variable name in let mysqltest: At line 1: Missing assignment operator in let +# Execute: --echo # success: $success +# success: 1 +# Execute: echo # success: $success ; +# success: 1 +# The next two variants work fine and expand the content of $success +# Execute: --echo $success +1 +# Execute: echo $success ; +1 mysqltest: At line 1: Missing file name in source mysqltest: At line 1: Could not open file ./non_existingFile mysqltest: In included file "MYSQLTEST_VARDIR/tmp/recursive.sql": At line 1: Source directives are nesting too deep diff --git a/mysql-test/r/ndb_alter_table_stm.result b/mysql-test/r/ndb_alter_table_stm.result index d05c2ba1c5c..9c1d09a8970 100644 --- a/mysql-test/r/ndb_alter_table_stm.result +++ b/mysql-test/r/ndb_alter_table_stm.result @@ -8,8 +8,6 @@ a b c 2 two two alter table t1 drop index c; select * from t1 where c = 'two'; -ERROR HY000: Table definition has changed, please retry transaction -select * from t1 where c = 'two'; a b c 2 two two drop table t1; diff --git a/mysql-test/r/ndb_multi.result b/mysql-test/r/ndb_multi.result index 5147d052d6e..723712d99ad 100644 --- a/mysql-test/r/ndb_multi.result +++ b/mysql-test/r/ndb_multi.result @@ -30,14 +30,6 @@ drop table t1; create table t1 (a int) engine=ndbcluster; insert into t1 value (2); select * from t1; -ERROR HY000: Table definition has changed, please retry transaction -show warnings; -Level Code Message -Error 1296 Got error 241 'Invalid schema object version' from NDB -Error 1412 Table definition has changed, please retry transaction -Error 1105 Unknown error -flush table t1; -select * from t1; a 2 flush status; @@ -58,15 +50,9 @@ a select * from t3; a b c last_col 1 Hi! 89 Longtext column -show status like 'handler_discover%'; -Variable_name Value -Handler_discover 1 show tables like 't4'; Tables_in_test (t4) t4 -show status like 'handler_discover%'; -Variable_name Value -Handler_discover 2 show tables; Tables_in_test t1 @@ -74,4 +60,3 @@ t2 t3 t4 drop table t1, t2, t3, t4; -drop table t1, t3, t4; diff --git a/mysql-test/r/partition.result b/mysql-test/r/partition.result index 699f448689b..e2601937da6 100644 --- a/mysql-test/r/partition.result +++ b/mysql-test/r/partition.result @@ -300,3 +300,16 @@ select * from t1 where f1 = 10; f1 f2 10 1 drop table t1; +set session storage_engine= 'memory'; +create table t1 (f_int1 int(11) default null) engine = memory +partition by range (f_int1) subpartition by hash (f_int1) +(partition part1 values less than (1000) +(subpartition subpart11 engine = memory)); +drop table t1; +set session storage_engine='myisam'; +create table t1 (f_int1 integer, f_int2 integer, primary key (f_int1)) +partition by hash(f_int1) partitions 2; +insert into t1 values (1,1),(2,2); +replace into t1 values (1,1),(2,2); +drop table t1; +End of 5.1 tests diff --git a/mysql-test/t/disabled.def b/mysql-test/t/disabled.def index 916e63bc7cb..62e667f6b6e 100644 --- a/mysql-test/t/disabled.def +++ b/mysql-test/t/disabled.def @@ -20,7 +20,6 @@ ndb_autodiscover2 : Needs to be fixed w.r.t binlog ndb_binlog_basic : Results are not deterministic, Tomas will fix ndb_binlog_ddl_multi : Bug#17038 [PATCH PENDING] ndb_gis : garbled msgs from corrupt THD* -ndb_load : Bug#17233 partition_03ndb : Bug#16385 ps_7ndb : dbug assert in RBR mode when executing test suite rpl_bit_npk : Bug#13418 diff --git a/mysql-test/t/events.test b/mysql-test/t/events.test index d2a4e877672..6cad9991c44 100644 --- a/mysql-test/t/events.test +++ b/mysql-test/t/events.test @@ -1,5 +1,38 @@ create database if not exists events_test; use events_test; + +# +# START: BUG #17289 Events: missing privilege check for drop database +# +CREATE USER pauline@localhost; +CREATE DATABASE db_x; +GRANT EVENT ON db_x.* TO pauline@localhost; +USE db_x; +CREATE TABLE x_table(a int); +connect (priv_conn,localhost,pauline,,db_x); +CREATE EVENT e_x1 ON SCHEDULE EVERY 1 SECOND DO DROP DATABASE db_x; +CREATE EVENT e_x2 ON SCHEDULE EVERY 1 SECOND DO DROP TABLE x_table; +connection default; +SHOW DATABASES LIKE 'db_x'; +SET GLOBAL event_scheduler=1; +--sleep 2 +SHOW DATABASES LIKE 'db_x'; +SHOW TABLES FROM db_x; +SET GLOBAL event_scheduler=0; +--sleep 1 +connection priv_conn; +DROP EVENT e_x1; +DROP EVENT e_x2; +disconnect priv_conn; +connection default; +DROP DATABASE db_x; +DROP USER pauline@localhost; +USE events_test; +# +# END: BUG #17289 Events: missing privilege check for drop database +# +SET GLOBAL event_scheduler=0; +--sleep 1 drop event if exists event1; create event event1 on schedule every 15 minute starts now() ends date_add(now(), interval 5 hour) DO begin end; alter event event1 rename to event2 enable; @@ -23,6 +56,14 @@ alter event e_43 do alter event e_43 do set @a = 4; select db, name, body, status, interval_field, interval_value from mysql.event; drop event e_43; --sleep 1 + +--echo "Let's check whether we can use non-qualified names" +create table non_qualif(a int); +create event non_qualif_ev on schedule every 10 minute do insert into non_qualif values (800219); +--sleep 2 +select * from non_qualif; +drop event non_qualif_ev; +drop table non_qualif; set global event_scheduler = 0; create table t_event3 (a int, b float); @@ -241,7 +282,6 @@ drop event one_event; --echo "Sleep a bit so the server closes the second connection" --sleep 2 - create event e_26 on schedule at '2017-01-01 00:00:00' disable do set @a = 5; select db, name, body, definer, convert_tz(execute_at, 'UTC', 'SYSTEM'), on_completion from mysql.event; drop event e_26; diff --git a/mysql-test/t/events_stress.test b/mysql-test/t/events_stress.test new file mode 100644 index 00000000000..f6eed79425c --- /dev/null +++ b/mysql-test/t/events_stress.test @@ -0,0 +1,80 @@ +CREATE DATABASE IF NOT EXISTS events_test; +# +# DROP DATABASE test start (bug #16406) +# +CREATE DATABASE events_test2; +USE events_test2; +CREATE EVENT ev_drop1 ON SCHEDULE EVERY 10 MINUTE DISABLE DO SELECT 1; +CREATE EVENT ev_drop2 ON SCHEDULE EVERY 10 MINUTE DISABLE DO SELECT 1; +CREATE EVENT ev_drop3 ON SCHEDULE EVERY 10 MINUTE DISABLE DO SELECT 1; +USE events_test; +SELECT COUNT(*) FROM INFORMATION_SCHEMA.EVENTS WHERE EVENT_SCHEMA='events_test2'; +DROP DATABASE events_test2; +SELECT COUNT(*) FROM INFORMATION_SCHEMA.EVENTS WHERE EVENT_SCHEMA='events_test2'; + +--echo "Now testing stability - dropping db -> events while they are running" +CREATE DATABASE events_test2; +USE events_test2; +--disable_query_log +let $1= 1000; +while ($1) +{ + eval CREATE EVENT ev_drop$1 ON SCHEDULE EVERY 1 SECOND DO SELECT $1; + dec $1; +} +--enable_query_log +SELECT COUNT(*) FROM INFORMATION_SCHEMA.EVENTS WHERE EVENT_SCHEMA='events_test2'; +SET GLOBAL event_scheduler=1; +--sleep 4 +DROP DATABASE events_test2; + +SET GLOBAL event_scheduler=0; +--sleep 2 +SELECT COUNT(*) FROM INFORMATION_SCHEMA.EVENTS WHERE EVENT_SCHEMA='events_test2'; +CREATE DATABASE events_test3; +USE events_test3; +--disable_query_log +let $1= 950; +while ($1) +{ + eval CREATE EVENT ev_drop$1 ON SCHEDULE EVERY 1 SECOND DO SELECT $1; + dec $1; +} +--enable_query_log +SELECT COUNT(*) FROM INFORMATION_SCHEMA.EVENTS WHERE EVENT_SCHEMA='events_test3'; +--sleep 3 +CREATE DATABASE events_test4; +USE events_test4; +--disable_query_log +let $1= 860; +while ($1) +{ + eval CREATE EVENT ev_drop$1 ON SCHEDULE EVERY 1 SECOND DO SELECT $1; + dec $1; +} +--enable_query_log + + +CREATE DATABASE events_test2; +USE events_test2; +--disable_query_log +let $1= 1050; +while ($1) +{ + eval CREATE EVENT ev_drop$1 ON SCHEDULE EVERY 1 SECOND DO SELECT $1; + dec $1; +} +--enable_query_log +SELECT COUNT(*) FROM INFORMATION_SCHEMA.EVENTS WHERE EVENT_SCHEMA='events_test2'; +--sleep 6 +DROP DATABASE events_test2; +SET GLOBAL event_scheduler=0; +DROP DATABASE events_test3; +SET GLOBAL event_scheduler=1; +DROP DATABASE events_test4; +SET GLOBAL event_scheduler=1; +USE events_test; +# +# DROP DATABASE test end (bug #16406) +# +DROP DATABASE events_test; diff --git a/mysql-test/t/mysqltest.test b/mysql-test/t/mysqltest.test index 271d6fb783e..5bdad986b12 100644 --- a/mysql-test/t/mysqltest.test +++ b/mysql-test/t/mysqltest.test @@ -538,6 +538,19 @@ echo $novar1; --error 1 --exec echo "let hi;" | $MYSQL_TEST 2>&1 +# More advanced test for bug#17280 +let $success= 1; +--echo # Execute: --echo # success: \$success +--echo # success: $success +--echo # Execute: echo # success: \$success ; +echo # success: $success ; + +--echo # The next two variants work fine and expand the content of \$success +--echo # Execute: --echo \$success +--echo $success +--echo # Execute: echo \$success ; +echo $success ; + # ---------------------------------------------------------------------------- # Test to assign let from query # let $=``; diff --git a/mysql-test/t/ndb_alter_table_stm.test b/mysql-test/t/ndb_alter_table_stm.test index dfe36487c1c..2c52b542b12 100644 --- a/mysql-test/t/ndb_alter_table_stm.test +++ b/mysql-test/t/ndb_alter_table_stm.test @@ -17,8 +17,6 @@ select * from t1 where c = 'two'; connection server1; alter table t1 drop index c; connection server2; --- error 1412 -select * from t1 where c = 'two'; select * from t1 where c = 'two'; connection server1; drop table t1; diff --git a/mysql-test/t/ndb_multi.test b/mysql-test/t/ndb_multi.test index 0cc8f57e4b5..d2dc0561955 100644 --- a/mysql-test/t/ndb_multi.test +++ b/mysql-test/t/ndb_multi.test @@ -41,11 +41,12 @@ drop table t1; create table t1 (a int) engine=ndbcluster; insert into t1 value (2); connection server1; -# Currently a retry is required remotely ---error 1412 -select * from t1; -show warnings; -flush table t1; +## Currently a retry is required remotely +#--error 1412 +#select * from t1; +#show warnings; +#flush table t1; +# Table definition change should be propagated automatically select * from t1; # Connect to server2 and use the tables from there @@ -65,13 +66,9 @@ create table t4 (pk int primary key, b int) engine=ndb; connection server1; select * from t1; select * from t3; -show status like 'handler_discover%'; show tables like 't4'; -show status like 'handler_discover%'; show tables; drop table t1, t2, t3, t4; -connection server2; -drop table t1, t3, t4; # End of 4.1 tests diff --git a/mysql-test/t/partition.test b/mysql-test/t/partition.test index fd7e3bf7b70..ded54ad28b4 100644 --- a/mysql-test/t/partition.test +++ b/mysql-test/t/partition.test @@ -380,3 +380,25 @@ partition partf values less than (10000)); insert into t1 values(10,1); select * from t1 where f1 = 10; drop table t1; + +# +# Bug #16775: Wrong engine type stored for subpartition +# +set session storage_engine= 'memory'; +create table t1 (f_int1 int(11) default null) engine = memory + partition by range (f_int1) subpartition by hash (f_int1) + (partition part1 values less than (1000) + (subpartition subpart11 engine = memory)); +drop table t1; +set session storage_engine='myisam'; + +# +# Bug #16782: Crash using REPLACE on table with primary key +# +create table t1 (f_int1 integer, f_int2 integer, primary key (f_int1)) + partition by hash(f_int1) partitions 2; +insert into t1 values (1,1),(2,2); +replace into t1 values (1,1),(2,2); +drop table t1; + +--echo End of 5.1 tests diff --git a/sql/event.cc b/sql/event.cc index 2dfede2cb63..d9e71a263b8 100644 --- a/sql/event.cc +++ b/sql/event.cc @@ -514,6 +514,28 @@ evex_open_event_table(THD *thd, enum thr_lock_type lock_type, TABLE **table) SYNOPSIS evex_db_find_event_aux() + thd Thread context + et evet_timed object containing dbname, name & definer + table TABLE object for open mysql.event table. + + RETURN VALUE + 0 - Routine found + EVEX_KEY_NOT_FOUND - No routine with given name +*/ + +inline int +evex_db_find_event_aux(THD *thd, event_timed *et, TABLE *table) +{ + return evex_db_find_event_by_name(thd, et->dbname, et->name, + et->definer, table); +} + + +/* + Find row in open mysql.event table representing event + + SYNOPSIS + evex_db_find_event_by_name() thd Thread context dbname Name of event's database rname Name of the event inside the db @@ -525,13 +547,13 @@ evex_open_event_table(THD *thd, enum thr_lock_type lock_type, TABLE **table) */ int -evex_db_find_event_aux(THD *thd, const LEX_STRING dbname, - const LEX_STRING ev_name, - const LEX_STRING user_name, - TABLE *table) +evex_db_find_event_by_name(THD *thd, const LEX_STRING dbname, + const LEX_STRING ev_name, + const LEX_STRING user_name, + TABLE *table) { byte key[MAX_KEY_LENGTH]; - DBUG_ENTER("evex_db_find_event_aux"); + DBUG_ENTER("evex_db_find_event_by_name"); DBUG_PRINT("enter", ("name: %.*s", ev_name.length, ev_name.str)); /* @@ -710,7 +732,7 @@ db_create_event(THD *thd, event_timed *et, my_bool create_if_not, } DBUG_PRINT("info", ("check existance of an event with the same name")); - if (!evex_db_find_event_aux(thd, et->dbname, et->name, et->definer, table)) + if (!evex_db_find_event_aux(thd, et, table)) { if (create_if_not) { @@ -848,7 +870,7 @@ db_update_event(THD *thd, event_timed *et, sp_name *new_name) goto err; } - if (!evex_db_find_event_aux(thd, new_name->m_db, new_name->m_name, + if (!evex_db_find_event_by_name(thd, new_name->m_db, new_name->m_name, et->definer, table)) { my_error(ER_EVENT_ALREADY_EXISTS, MYF(0), new_name->m_name.str); @@ -861,8 +883,7 @@ db_update_event(THD *thd, event_timed *et, sp_name *new_name) overwrite the key and SE will tell us that it cannot find the already found row (copied into record[1] later */ - if (EVEX_KEY_NOT_FOUND == evex_db_find_event_aux(thd, et->dbname, et->name, - et->definer, table)) + if (EVEX_KEY_NOT_FOUND == evex_db_find_event_aux(thd, et, table)) { my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), et->name.str); goto err; @@ -943,8 +964,8 @@ db_find_event(THD *thd, sp_name *name, LEX_STRING *definer, event_timed **ett, goto done; } - if ((ret= evex_db_find_event_aux(thd, name->m_db, name->m_name, *definer, - table))) + if ((ret= evex_db_find_event_by_name(thd, name->m_db, name->m_name, *definer, + table))) { my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), name->m_name.str); goto done; @@ -1089,7 +1110,7 @@ evex_remove_from_cache(LEX_STRING *db, LEX_STRING *name, bool use_lock, if (!sortcmp_lex_string(*name, et->name, system_charset_info) && !sortcmp_lex_string(*db, et->dbname, system_charset_info)) { - if (!et->is_running()) + if (et->can_spawn_now()) { DBUG_PRINT("evex_remove_from_cache", ("not running - free and delete")); et->free_sp(); @@ -1239,7 +1260,7 @@ int db_drop_event(THD *thd, event_timed *et, bool drop_if_exists, { TABLE *table; Open_tables_state backup; - uint ret; + int ret; DBUG_ENTER("db_drop_event"); ret= EVEX_OPEN_TABLE_FAILED; @@ -1251,7 +1272,7 @@ int db_drop_event(THD *thd, event_timed *et, bool drop_if_exists, goto done; } - if (!(ret= evex_db_find_event_aux(thd, et->dbname,et->name,et->definer,table))) + if (!(ret= evex_db_find_event_aux(thd, et, table))) { if ((ret= table->file->ha_delete_row(table->record[0]))) { @@ -1393,3 +1414,187 @@ evex_show_create_event(THD *thd, sp_name *spn, LEX_STRING definer) DBUG_RETURN(ret); } + + +/* + evex_drop_db_events - Drops all events in the selected database + + thd - Thread + db - ASCIIZ the name of the database + + Returns: + 0 - OK + 1 - Failed to delete a specific row + 2 - Got NULL while reading db name from a row + + Note: + The algo is the following + 1. Go through the in-memory cache, if the scheduler is working + and for every event whose dbname matches the database we drop + check whether is currently in execution: + - event_timed::can_spawn() returns true -> the event is not + being executed in a child thread. The reason not to use + event_timed::is_running() is that the latter shows only if + it is being executed, which is 99% of the time in the thread + but there are some initiliazations before and after the + anonymous SP is being called. So if we delete in this moment + -=> *boom*, so we have to check whether the thread has been + spawned and can_spawn() is the right method. + - event_timed::can_spawn() returns false -> being runned ATM + just set the flags so it should drop itself. + +*/ + +int +evex_drop_db_events(THD *thd, char *db) +{ + TABLE *table; + READ_RECORD read_record_info; + MYSQL_LOCK *lock; + int ret= 0; + uint i; + LEX_STRING db_lex= {db, strlen(db)}; + + DBUG_ENTER("evex_drop_db_events"); + DBUG_PRINT("info",("dropping events from %s", db)); + + + VOID(pthread_mutex_lock(&LOCK_event_arrays)); + + if ((ret= evex_open_event_table(thd, TL_WRITE, &table))) + { + sql_print_error("Table mysql.event is damaged."); + VOID(pthread_mutex_unlock(&LOCK_event_arrays)); + DBUG_RETURN(SP_OPEN_TABLE_FAILED); + } + + DBUG_PRINT("info",("%d elements in the queue", + evex_queue_num_elements(EVEX_EQ_NAME))); + VOID(pthread_mutex_lock(&LOCK_evex_running)); + if (!evex_is_running) + goto skip_memory; + + for (i= 0; i < evex_queue_num_elements(EVEX_EQ_NAME); ++i) + { + event_timed *et= evex_queue_element(&EVEX_EQ_NAME, i, event_timed*); + if (sortcmp_lex_string(et->dbname, db_lex, system_charset_info)) + continue; + + if (et->can_spawn_now_n_lock(thd)) + { + DBUG_PRINT("info",("event %s not running - direct delete", et->name.str)); + if (!(ret= evex_db_find_event_aux(thd, et, table))) + { + DBUG_PRINT("info",("event %s found on disk", et->name.str)); + if ((ret= table->file->ha_delete_row(table->record[0]))) + { + sql_print_error("Error while deleting a row - dropping " + "a database. Skipping the rest."); + my_error(ER_EVENT_DROP_FAILED, MYF(0), et->name.str); + goto end; + } + DBUG_PRINT("info",("deleted event [%s] num [%d]. Time to free mem", + et->name.str, i)); + } + else if (ret == EVEX_KEY_NOT_FOUND) + { + sql_print_error("Expected to find event %s.%s of %s on disk-not there.", + et->dbname.str, et->name.str, et->definer.str); + } + et->free_sp(); + delete et; + et= 0; + /* no need to call et->spawn_unlock because we already cleaned et */ + } + else + { + DBUG_PRINT("info",("event %s is running. setting exec_no_more and dropped", + et->name.str)); + et->flags|= EVENT_EXEC_NO_MORE; + et->dropped= TRUE; + } + DBUG_PRINT("info",("%d elements in the queue", + evex_queue_num_elements(EVEX_EQ_NAME))); + evex_queue_delete_element(&EVEX_EQ_NAME, i);// 1 is top + DBUG_PRINT("info",("%d elements in the queue", + evex_queue_num_elements(EVEX_EQ_NAME))); + /* + decrease so we start at the same position, there will be + less elements in the queue, it will still be ordered so on + next iteration it will be again i the current element or if + no more we finish. + */ + --i; + } + +skip_memory: + /* + The reasoning behind having two loops is the following: + If there was only one loop, the table-scan, then for every element which + matches, the queue in memory has to be searched to remove the element. + While if we go first over the queue and remove what's in there we have only + one pass over it and after finishing it, moving to table-scan for the disabled + events. This needs quite less time and means quite less locking on + LOCK_event_arrays. + */ + DBUG_PRINT("info",("Mem-cache checked, now going to db for disabled events")); + /* only enabled events are in memory, so we go now and delete the rest */ + init_read_record(&read_record_info, thd, table ,NULL,1,0); + while (!(read_record_info.read_record(&read_record_info)) && !ret) + { + char *et_db; + + if ((et_db= get_field(thd->mem_root, table->field[EVEX_FIELD_DB])) == NULL) + { + ret= 2; + break; + } + + LEX_STRING et_db_lex= {et_db, strlen(et_db)}; + if (!sortcmp_lex_string(et_db_lex, db_lex, system_charset_info)) + { + event_timed ett; + char *ptr; + + if ((ptr= get_field(thd->mem_root, table->field[EVEX_FIELD_STATUS])) + == NullS) + { + sql_print_error("Error while loading from mysql.event. " + "Table probably corrupted"); + goto end; + } + /* + When not running nothing is in memory so we have to clean + everything. + We don't delete EVENT_ENABLED events when the scheduler is running + because maybe this is an event which we asked to drop itself when + it is finished and it hasn't finished yet, so we don't touch it. + It will drop itself. The not running ENABLED events has been already + deleted from ha_delete_row() above in the loop over the QUEUE + (in case the executor is running). + 'D' stands for DISABLED, 'E' for ENABLED - it's an enum + */ + if ((evex_is_running && ptr[0] == 'D') || !evex_is_running) + { + DBUG_PRINT("info", ("Dropping %s.%s", et_db, ett.name.str)); + if ((ret= table->file->ha_delete_row(table->record[0]))) + { + my_error(ER_EVENT_DROP_FAILED, MYF(0), ett.name.str); + goto end; + } + } + } + } + DBUG_PRINT("info",("Disk checked for disabled events. Finishing.")); + +end: + VOID(pthread_mutex_unlock(&LOCK_evex_running)); + VOID(pthread_mutex_unlock(&LOCK_event_arrays)); + end_read_record(&read_record_info); + + thd->version--; // Force close to free memory + + close_thread_tables(thd); + + DBUG_RETURN(ret); +} diff --git a/sql/event.h b/sql/event.h index 4e56861afae..6df6267abc4 100644 --- a/sql/event.h +++ b/sql/event.h @@ -79,6 +79,8 @@ class event_timed { event_timed(const event_timed &); /* Prevent use of these */ void operator=(event_timed &); + my_bool in_spawned_thread; + ulong locked_by_thread_id; my_bool running; pthread_mutex_t LOCK_running; @@ -117,13 +119,14 @@ public: bool free_sphead_on_delete; uint flags;//all kind of purposes - event_timed():running(0), status_changed(false), last_executed_changed(false), - expression(0), created(0), modified(0), - on_completion(MYSQL_EVENT_ON_COMPLETION_DROP), - status(MYSQL_EVENT_ENABLED), sphead(0), sql_mode(0), - body_begin(0), dropped(false), free_sphead_on_delete(true), - flags(0) - + event_timed():in_spawned_thread(0),locked_by_thread_id(0), + running(0), status_changed(false), + last_executed_changed(false), expression(0), created(0), + modified(0), on_completion(MYSQL_EVENT_ON_COMPLETION_DROP), + status(MYSQL_EVENT_ENABLED), sphead(0), sql_mode(0), + body_begin(0), dropped(false), + free_sphead_on_delete(true), flags(0) + { pthread_mutex_init(&this->LOCK_running, MY_MUTEX_INIT_FAST); init(); @@ -200,11 +203,55 @@ public: return ret; } - void free_sp() + /* + Checks whether the object is being used in a spawned thread. + This method is for very basic checking. Use ::can_spawn_now_n_lock() + for most of the cases. + */ + + my_bool + can_spawn_now() + { + my_bool ret; + VOID(pthread_mutex_lock(&this->LOCK_running)); + ret= !in_spawned_thread; + VOID(pthread_mutex_unlock(&this->LOCK_running)); + return ret; + } + + /* + Checks whether this thread can lock the object for modification -> + preventing being spawned for execution, and locks if possible. + use ::can_spawn_now() only for basic checking because a race + condition may occur between the check and eventual modification (deletion) + of the object. + */ + + my_bool + can_spawn_now_n_lock(THD *thd); + + int + spawn_unlock(THD *thd); + + int + spawn_now(void * (*thread_func)(void*)); + + void + spawn_thread_finish(THD *thd); + + void + free_sp() { delete sphead; sphead= 0; } +protected: + bool + change_security_context(THD *thd, Security_context *s_ctx, + Security_context **backup); + + void + restore_security_context(THD *thd, Security_context *backup); }; @@ -233,6 +280,10 @@ event_reconstruct_interval_expression(String *buf, interval_type interval, longlong expression); +int +evex_drop_db_events(THD *thd, char *db); + + int init_events(); diff --git a/sql/event_executor.cc b/sql/event_executor.cc index 16d0482e26f..9483c2ab165 100644 --- a/sql/event_executor.cc +++ b/sql/event_executor.cc @@ -18,6 +18,11 @@ #include "event.h" #include "sp.h" +#define WAIT_STATUS_READY 0 +#define WAIT_STATUS_EMPTY_QUEUE 1 +#define WAIT_STATUS_NEW_TOP_EVENT 2 +#define WAIT_STATUS_STOP_EXECUTOR 3 + /* Make this define DBUG_FAULTY_THR to be able to put breakpoints inside @@ -49,7 +54,8 @@ static uint workers_count; static int evex_load_events_from_db(THD *thd); - +bool +evex_print_warnings(THD *thd, event_timed *et); /* TODO Andrey: Check for command line option whether to start @@ -260,7 +266,8 @@ init_event_thread(THD* thd) { DBUG_ENTER("init_event_thread"); thd->client_capabilities= 0; - thd->security_ctx->skip_grants(); + thd->security_ctx->master_access= 0; + thd->security_ctx->db_access= 0; thd->security_ctx->host= (char*)my_localhost; my_net_init(&thd->net, 0); thd->net.read_timeout = slave_net_timeout; @@ -292,6 +299,85 @@ init_event_thread(THD* thd) } +/* + This function waits till the time next event in the queue should be + executed. + + Returns + WAIT_STATUS_READY There is an event to be executed right now + WAIT_STATUS_EMPTY_QUEUE No events or the last event was dropped. + WAIT_STATUS_NEW_TOP_EVENT New event has entered the queue and scheduled + on top. Restart ticking. + WAIT_STATUS_STOP_EXECUTOR The thread was killed or SET global event_scheduler=0; +*/ + +static int +executor_wait_till_next_event_exec(THD *thd) +{ + event_timed *et; + TIME time_now; + int t2sleep; + + DBUG_ENTER("executor_wait_till_next_event_exec"); + /* + now let's see how much time to sleep, we know there is at least 1 + element in the queue. + */ + VOID(pthread_mutex_lock(&LOCK_event_arrays)); + if (!evex_queue_num_elements(EVEX_EQ_NAME)) + { + VOID(pthread_mutex_unlock(&LOCK_event_arrays)); + DBUG_RETURN(1); + } + et= evex_queue_first_element(&EVEX_EQ_NAME, event_timed*); + DBUG_ASSERT(et); + if (et->status == MYSQL_EVENT_DISABLED) + { + DBUG_PRINT("evex main thread",("Now it is disabled-exec no more")); + if (et->dropped) + et->drop(thd); + delete et; + evex_queue_delete_element(&EVEX_EQ_NAME, 1);// 1 is top + VOID(pthread_mutex_unlock(&LOCK_event_arrays)); + sql_print_information("Event found disabled, dropping."); + DBUG_RETURN(1); + } + + DBUG_PRINT("evex main thread",("computing time to sleep till next exec")); + // set the internal clock of thd + thd->end_time(); + my_tz_UTC->gmt_sec_to_TIME(&time_now, thd->query_start()); + t2sleep= evex_time_diff(&et->execute_at, &time_now); + VOID(pthread_mutex_unlock(&LOCK_event_arrays)); + + DBUG_PRINT("evex main thread",("unlocked LOCK_event_arrays")); + if (t2sleep > 0) + { + /* + We sleep t2sleep seconds but we check every second whether this thread + has been killed, or there is a new candidate + */ + while (t2sleep-- && !thd->killed && event_executor_running_global_var && + evex_queue_num_elements(EVEX_EQ_NAME) && + (evex_queue_first_element(&EVEX_EQ_NAME, event_timed*) == et)) + { + DBUG_PRINT("evex main thread",("will sleep a bit more")); + my_sleep(1000000); + } + } + + int ret= 0; + if (!evex_queue_num_elements(EVEX_EQ_NAME)) + ret= 1; + else if (evex_queue_first_element(&EVEX_EQ_NAME, event_timed*) != et) + ret= 2; + if (thd->killed && event_executor_running_global_var) + ret= 3; + + DBUG_RETURN(ret); +} + + /* The main scheduler thread. Inits the priority queue on start and destroys it on thread shutdown. Forks child threads for every event @@ -311,9 +397,9 @@ pthread_handler_t event_executor_main(void *arg) { THD *thd; /* needs to be first for thread_stack */ - ulonglong iter_num= 0; uint i=0, j=0; my_ulonglong cnt= 0; + TIME time_now; DBUG_ENTER("event_executor_main"); DBUG_PRINT("event_executor_main", ("EVEX thread started")); @@ -328,15 +414,16 @@ event_executor_main(void *arg) if (sizeof(my_time_t) != sizeof(time_t)) { - sql_print_error("sizeof(my_time_t) != sizeof(time_t) ." + sql_print_error("SCHEDULER: sizeof(my_time_t) != sizeof(time_t) ." "The scheduler will not work correctly. Stopping."); + DBUG_ASSERT(0); goto err_no_thd; } //TODO Andrey: Check for NULL if (!(thd = new THD)) // note that contructor of THD uses DBUG_ ! { - sql_print_error("Cannot create THD for event_executor_main"); + sql_print_error("SCHEDULER: Cannot create THD for the main thread."); goto err_no_thd; } thd->thread_stack = (char*)&thd; // remember where our stack is @@ -344,9 +431,12 @@ event_executor_main(void *arg) pthread_detach_this_thread(); if (init_event_thread(thd)) - goto err; - - // make this thread invisible it has no vio -> show processlist won't see + goto finish; + + /* + make this thread visible it has no vio -> show processlist won't see it + unless it's marked as system thread + */ thd->system_thread= 1; VOID(pthread_mutex_lock(&LOCK_thread_count)); @@ -355,7 +445,7 @@ event_executor_main(void *arg) thread_running++; VOID(pthread_mutex_unlock(&LOCK_thread_count)); - DBUG_PRINT("EVEX main thread", ("Initing events_queuey")); + DBUG_PRINT("EVEX main thread", ("Initing events_queue")); /* eventually manifest that we are running, not to crashe because of @@ -371,15 +461,14 @@ event_executor_main(void *arg) thd->security_ctx->user= my_strdup("event_scheduler", MYF(0)); if (evex_load_events_from_db(thd)) - goto err; + goto finish; evex_main_thread_id= thd->thread_id; - sql_print_information("Scheduler thread started"); + sql_print_information("SCHEDULER: Main thread started"); while (!thd->killed) { TIME time_now; - my_time_t now; event_timed *et; cnt++; @@ -388,7 +477,7 @@ event_executor_main(void *arg) thd->proc_info = "Sleeping"; if (!event_executor_running_global_var) { - sql_print_information("Scheduler asked to stop."); + sql_print_information("SCHEDULER: Asked to stop."); break; } @@ -397,62 +486,31 @@ event_executor_main(void *arg) my_sleep(1000000);// sleep 1s continue; } - - { - int t2sleep; - /* - now let's see how much time to sleep, we know there is at least 1 - element in the queue. - */ - VOID(pthread_mutex_lock(&LOCK_event_arrays)); - if (!evex_queue_num_elements(EVEX_EQ_NAME)) - { - VOID(pthread_mutex_unlock(&LOCK_event_arrays)); - continue; - } - et= evex_queue_first_element(&EVEX_EQ_NAME, event_timed*); - if (et->status == MYSQL_EVENT_DISABLED) - { - DBUG_PRINT("evex main thread",("Now it is disabled-exec no more")); - if (et->dropped) - et->drop(thd); - delete et; - evex_queue_delete_element(&EVEX_EQ_NAME, 1);// 1 is top - VOID(pthread_mutex_unlock(&LOCK_event_arrays)); - sql_print_information("Event found disabled, dropping."); - continue; - } - - DBUG_PRINT("evex main thread",("computing time to sleep till next exec")); - time((time_t *)&now); - my_tz_UTC->gmt_sec_to_TIME(&time_now, now); - t2sleep= evex_time_diff(&et->execute_at, &time_now); - VOID(pthread_mutex_unlock(&LOCK_event_arrays)); - - DBUG_PRINT("evex main thread",("unlocked LOCK_event_arrays")); - if (t2sleep > 0) - { - /* - We sleep t2sleep seconds but we check every second whether this thread - has been killed, or there is a new candidate - */ - while (t2sleep-- && !thd->killed && event_executor_running_global_var && - evex_queue_num_elements(EVEX_EQ_NAME) && - (evex_queue_first_element(&EVEX_EQ_NAME, event_timed*) == et)) - { - DBUG_PRINT("evex main thread",("will sleep a bit more")); - my_sleep(1000000); - } - } - if (!event_executor_running_global_var) - { - sql_print_information("Scheduler asked to stop."); - break; - } + +restart_ticking: + switch (executor_wait_till_next_event_exec(thd)) { + case WAIT_STATUS_READY: // time to execute the event on top + DBUG_PRINT("evex main thread",("time to execute an event")); + break; + case WAIT_STATUS_EMPTY_QUEUE: // no more events + DBUG_PRINT("evex main thread",("no more events")); + continue; + break; + case WAIT_STATUS_NEW_TOP_EVENT: // new event on top in the queue + DBUG_PRINT("evex main thread",("restart ticking")); + goto restart_ticking; + case WAIT_STATUS_STOP_EXECUTOR: + sql_print_information("SCHEDULER: Asked to stop."); + goto finish; + break; + default: + DBUG_ASSERT(0); } VOID(pthread_mutex_lock(&LOCK_event_arrays)); + thd->end_time(); + my_tz_UTC->gmt_sec_to_TIME(&time_now, thd->query_start()); if (!evex_queue_num_elements(EVEX_EQ_NAME)) { @@ -474,14 +532,14 @@ event_executor_main(void *arg) DBUG_PRINT("evex main thread",("it's right time")); if (et->status == MYSQL_EVENT_ENABLED) { - pthread_t th; + int fork_ret_code; DBUG_PRINT("evex main thread", ("[%10s] this exec at [%llu]", et->name.str, TIME_to_ulonglong_datetime(&et->execute_at))); et->mark_last_executed(thd); if (et->compute_next_execution_time()) { - sql_print_error("Error while computing time of %s.%s . " + sql_print_error("SCHEDULER: Error while computing time of %s.%s . " "Disabling after execution.", et->dbname.str, et->name.str); et->status= MYSQL_EVENT_DISABLED; @@ -490,13 +548,23 @@ event_executor_main(void *arg) TIME_to_ulonglong_datetime(&et->execute_at))); et->update_fields(thd); - ++iter_num; - DBUG_PRINT("info", (" Spawning a thread %d", iter_num)); #ifndef DBUG_FAULTY_THR - if (pthread_create(&th,&connection_attrib,event_executor_worker,(void*)et)) - { - sql_print_error("Problem while trying to create a thread"); - UNLOCK_MUTEX_AND_BAIL_OUT(LOCK_event_arrays, err); + thread_safe_increment(workers_count, &LOCK_workers_count); + switch ((fork_ret_code= et->spawn_now(event_executor_worker))) { + case EVENT_EXEC_CANT_FORK: + thread_safe_decrement(workers_count, &LOCK_workers_count); + sql_print_error("SCHEDULER: Problem while trying to create a thread"); + UNLOCK_MUTEX_AND_BAIL_OUT(LOCK_event_arrays, finish); + case EVENT_EXEC_ALREADY_EXEC: + thread_safe_decrement(workers_count, &LOCK_workers_count); + sql_print_information("SCHEDULER: %s.%s in execution. Skip this time.", + et->dbname.str, et->name.str); + break; + default: + DBUG_ASSERT(!fork_ret_code); + if (fork_ret_code) + thread_safe_decrement(workers_count, &LOCK_workers_count); + break; } #else event_executor_worker((void *) et); @@ -506,22 +574,21 @@ event_executor_main(void *arg) et->flags |= EVENT_EXEC_NO_MORE; if ((et->flags & EVENT_EXEC_NO_MORE) || et->status == MYSQL_EVENT_DISABLED) - evex_queue_delete_element(&EVEX_EQ_NAME, 1);// 1 is top + evex_queue_delete_element(&EVEX_EQ_NAME, 0);// 0 is top, internally 1 else evex_queue_first_updated(&EVEX_EQ_NAME); } DBUG_PRINT("evex main thread",("unlocking")); VOID(pthread_mutex_unlock(&LOCK_event_arrays)); }// while +finish: -err: // First manifest that this thread does not work and then destroy VOID(pthread_mutex_lock(&LOCK_evex_running)); evex_is_running= false; evex_main_thread_id= 0; VOID(pthread_mutex_unlock(&LOCK_evex_running)); - sql_print_information("Event scheduler stopping. Waiting for worker threads to finish."); /* TODO: A better will be with a conditional variable @@ -530,21 +597,33 @@ err: Read workers_count without lock, no need for locking. In the worst case we have to wait 1sec more. */ - while (workers_count) - my_sleep(1000000);// 1s + sql_print_information("SCHEDULER: Stopping. Waiting for worker threads to finish."); + while (1) + { + VOID(pthread_mutex_lock(&LOCK_workers_count)); + if (!workers_count) + { + VOID(pthread_mutex_unlock(&LOCK_workers_count)); + break; + } + VOID(pthread_mutex_unlock(&LOCK_workers_count)); + my_sleep(1000000);// 1s + } /* - LEX_STRINGs reside in the memory root and will be destroyed with it. - Hence no need of delete but only freeing of SP + First we free all objects ... + Lock because a DROP DATABASE could be running in parallel and it locks on these */ - // First we free all objects ... + sql_print_information("SCHEDULER: Emptying the queue."); + VOID(pthread_mutex_lock(&LOCK_event_arrays)); for (i= 0; i < evex_queue_num_elements(EVEX_EQ_NAME); ++i) { event_timed *et= evex_queue_element(&EVEX_EQ_NAME, i, event_timed*); et->free_sp(); delete et; } - // ... then we can thras the whole queue at once + VOID(pthread_mutex_unlock(&LOCK_event_arrays)); + // ... then we can thrash the whole queue at once evex_queue_destroy(&EVEX_EQ_NAME); thd->proc_info = "Clearing"; @@ -568,7 +647,7 @@ err_no_thd: VOID(pthread_mutex_unlock(&LOCK_evex_running)); free_root(&evex_mem_root, MYF(0)); - sql_print_information("Event scheduler stopped."); + sql_print_information("SCHEDULER: Stopped."); #ifndef DBUG_FAULTY_THR my_thread_end(); @@ -595,9 +674,6 @@ event_executor_worker(void *event_void) MEM_ROOT worker_mem_root; DBUG_ENTER("event_executor_worker"); - VOID(pthread_mutex_lock(&LOCK_workers_count)); - ++workers_count; - VOID(pthread_mutex_unlock(&LOCK_workers_count)); init_alloc_root(&worker_mem_root, MEM_ROOT_BLOCK_SIZE, MEM_ROOT_PREALLOC); @@ -606,7 +682,7 @@ event_executor_worker(void *event_void) if (!(thd = new THD)) // note that contructor of THD uses DBUG_ ! { - sql_print_error("Cannot create a THD structure in a scheduler worker thread"); + sql_print_error("SCHEDULER: Cannot create a THD structure in an worker."); goto err_no_thd; } thd->thread_stack = (char*)&thd; // remember where our stack is @@ -631,47 +707,25 @@ event_executor_worker(void *event_void) thd= current_thd; #endif - // thd->security_ctx->priv_host is char[MAX_HOSTNAME] - - strxnmov(thd->security_ctx->priv_host, sizeof(thd->security_ctx->priv_host), - event->definer_host.str, NullS); - - thd->security_ctx->user= thd->security_ctx->priv_user= - my_strdup(event->definer_user.str, MYF(0)); - - thd->db= event->dbname.str; - if (!check_access(thd, EVENT_ACL, event->dbname.str, 0, 0, 0, - is_schema_db(event->dbname.str))) { int ret; - DBUG_PRINT("info", (" EVEX EXECUTING event %s.%s [EXPR:%d]", - event->dbname.str, event->name.str,(int) event->expression)); - sql_print_information(" EVEX EXECUTING event %s.%s [EXPR:%d]", - event->dbname.str, event->name.str,(int) event->expression); + sql_print_information("SCHEDULER: Executing event %s.%s of %s [EXPR:%d]", + event->dbname.str, event->name.str, + event->definer.str, (int) event->expression); ret= event->execute(thd, &worker_mem_root); - sql_print_information(" EVEX EXECUTED event %s.%s [EXPR:%d]. RetCode=%d", - event->dbname.str, event->name.str, - (int) event->expression, ret); + evex_print_warnings(thd, event); + sql_print_information("SCHEDULER: Executed event %s.%s of %s [EXPR:%d]. " + "RetCode=%d", event->dbname.str, event->name.str, + event->definer.str, (int) event->expression, ret); if (ret == EVEX_COMPILE_ERROR) - sql_print_information(" EVEX COMPILE ERROR for event %s.%s", - event->dbname.str, event->name.str); - - DBUG_PRINT("info", (" EVEX EXECUTED event %s.%s [EXPR:%d]. RetCode=%d", - event->dbname.str, event->name.str, - (int) event->expression, ret)); - } - if ((event->flags & EVENT_EXEC_NO_MORE) || event->status==MYSQL_EVENT_DISABLED) - { - DBUG_PRINT("event_executor_worker", - ("%s exec no more. to drop=%d",event->name.str, event->dropped)); - if (event->dropped) - event->drop(thd); - delete event; + sql_print_information("SCHEDULER: COMPILE ERROR for event %s.%s of", + event->dbname.str, event->name.str, + event->definer.str); } + event->spawn_thread_finish(thd); - thd->db= 0; err: VOID(pthread_mutex_lock(&LOCK_thread_count)); @@ -699,10 +753,7 @@ err: err_no_thd: free_root(&worker_mem_root, MYF(0)); - - VOID(pthread_mutex_lock(&LOCK_workers_count)); - --workers_count; - VOID(pthread_mutex_unlock(&LOCK_workers_count)); + thread_safe_decrement(workers_count, &LOCK_workers_count); #ifndef DBUG_FAULTY_THR my_thread_end(); @@ -743,7 +794,7 @@ evex_load_events_from_db(THD *thd) if ((ret= evex_open_event_table(thd, TL_READ, &table))) { - sql_print_error("Table mysql.event is damaged."); + sql_print_error("SCHEDULER: Table mysql.event is damaged. Can not open."); DBUG_RETURN(SP_OPEN_TABLE_FAILED); } @@ -763,7 +814,7 @@ evex_load_events_from_db(THD *thd) if ((ret= et->load_from_row(&evex_mem_root, table))) { - sql_print_error("Error while loading from mysql.event. " + sql_print_error("SCHEDULER: Error while loading from mysql.event. " "Table probably corrupted"); goto end; } @@ -779,7 +830,7 @@ evex_load_events_from_db(THD *thd) if ((ret= et->compile(thd, &evex_mem_root))) { - sql_print_error("Error while compiling %s.%s. Aborting load.", + sql_print_error("SCHEDULER: Error while compiling %s.%s. Aborting load.", et->dbname.str, et->name.str); goto end; } @@ -787,8 +838,8 @@ evex_load_events_from_db(THD *thd) // let's find when to be executed if (et->compute_next_execution_time()) { - sql_print_error("Error while computing execution time of %s.%s. Skipping", - et->dbname.str, et->name.str); + sql_print_error("SCHEDULER: Error while computing execution time of %s.%s." + " Skipping", et->dbname.str, et->name.str); continue; } @@ -809,7 +860,7 @@ end: thd->version--; // Force close to free memory close_thread_tables(thd); - sql_print_information("Scheduler loaded %d event%s", count, (count == 1)?"":"s"); + sql_print_information("SCHEDULER: Loaded %d event%s", count, (count == 1)?"":"s"); DBUG_PRINT("info", ("Status code %d. Loaded %d event(s)", ret, count)); DBUG_RETURN(ret); @@ -849,3 +900,69 @@ sys_var_event_executor::update(THD *thd, set_var *var) DBUG_RETURN(0); } + +extern LEX_STRING warning_level_names[]; + +typedef void (*sql_print_xxx_func)(const char *format, ...); +static sql_print_xxx_func sql_print_xxx_handlers[3] = +{ + sql_print_information, + sql_print_warning, + sql_print_error +}; + + +/* + Prints the stack of infos, warnings, errors from thd to + the console so it can be fetched by the logs-into-tables and + checked later. + + Synopsis + evex_print_warnings + thd - thread used during the execution of the event + et - the event itself + + Returns + 0 - OK (always) + +*/ + +bool +evex_print_warnings(THD *thd, event_timed *et) +{ + MYSQL_ERROR *err; + DBUG_ENTER("evex_show_warnings"); + char msg_buf[1024]; + char prefix_buf[512]; + String prefix(prefix_buf, sizeof(prefix_buf), system_charset_info); + prefix.length(0); + + List_iterator_fast it(thd->warn_list); + while ((err= it++)) + { + String err_msg(msg_buf, sizeof(msg_buf), system_charset_info); + err_msg.length(0);// set it to 0 or we start adding at the end + if (!prefix.length()) + { + prefix.append("SCHEDULER: ["); + + append_identifier(thd,&prefix,et->definer_user.str,et->definer_user.length); + prefix.append('@'); + append_identifier(thd,&prefix,et->definer_host.str,et->definer_host.length); + prefix.append("][", 2); + append_identifier(thd,&prefix, et->dbname.str, et->dbname.length); + prefix.append('.'); + append_identifier(thd,&prefix, et->name.str, et->name.length); + prefix.append("] ", 2); + } + + err_msg.append(prefix); + err_msg.append(err->msg, strlen(err->msg), system_charset_info); + err_msg.append("]"); + DBUG_ASSERT(err->level < 3); + (sql_print_xxx_handlers[err->level])("%*s", err_msg.length(), err_msg.c_ptr()); + } + + + DBUG_RETURN(FALSE); +} diff --git a/sql/event_priv.h b/sql/event_priv.h index 0d4cf3a2993..4f575a96212 100644 --- a/sql/event_priv.h +++ b/sql/event_priv.h @@ -19,6 +19,10 @@ #include "mysql_priv.h" +#define EVENT_EXEC_STARTED 0 +#define EVENT_EXEC_ALREADY_EXEC 1 +#define EVENT_EXEC_CANT_FORK 2 + #define EVEX_USE_QUEUE #define UNLOCK_MUTEX_AND_BAIL_OUT(__mutex, __label) \ @@ -32,10 +36,10 @@ int my_time_compare(TIME *a, TIME *b); int -evex_db_find_event_aux(THD *thd, const LEX_STRING dbname, - const LEX_STRING rname, - const LEX_STRING definer, - TABLE *table); +evex_db_find_event_by_name(THD *thd, const LEX_STRING dbname, + const LEX_STRING ev_name, + const LEX_STRING user_name, + TABLE *table); int event_timed_compare_q(void *vptr, byte* a, byte *b); diff --git a/sql/event_timed.cc b/sql/event_timed.cc index a32090e9d2b..cb0c0167a54 100644 --- a/sql/event_timed.cc +++ b/sql/event_timed.cc @@ -412,21 +412,30 @@ event_timed::init_definer(THD *thd) { DBUG_ENTER("event_timed::init_definer"); + DBUG_PRINT("info",("init definer_user thd->mem_root=0x%lx " + "thd->sec_ctx->priv_user=0x%lx", thd->mem_root, + thd->security_ctx->priv_user)); definer_user.str= strdup_root(thd->mem_root, thd->security_ctx->priv_user); definer_user.length= strlen(thd->security_ctx->priv_user); + DBUG_PRINT("info",("init definer_host thd->s_c->priv_host=0x%lx", + thd->security_ctx->priv_host)); definer_host.str= strdup_root(thd->mem_root, thd->security_ctx->priv_host); definer_host.length= strlen(thd->security_ctx->priv_host); + DBUG_PRINT("info",("init definer as whole")); definer.length= definer_user.length + definer_host.length + 1; definer.str= alloc_root(thd->mem_root, definer.length + 1); + DBUG_PRINT("info",("copy the user")); memcpy(definer.str, definer_user.str, definer_user.length); definer.str[definer_user.length]= '@'; + DBUG_PRINT("info",("copy the host")); memcpy(definer.str + definer_user.length + 1, definer_host.str, definer_host.length); definer.str[definer.length]= '\0'; + DBUG_PRINT("info",("definer initted")); DBUG_RETURN(0); } @@ -908,7 +917,7 @@ event_timed::drop(THD *thd) Saves status and last_executed_at to the disk if changed. SYNOPSIS - event_timed::drop() + event_timed::update_fields() thd - thread context RETURN VALUE @@ -945,7 +954,7 @@ event_timed::update_fields(THD *thd) } - if ((ret= evex_db_find_event_aux(thd, dbname, name, definer, table))) + if ((ret= evex_db_find_event_by_name(thd, dbname, name, definer, table))) goto done; store_record(table,record[1]); @@ -1061,6 +1070,7 @@ event_timed::get_create_event(THD *thd, String *buf) RETURNS 0 success + -99 No rights on this.dbname.str -100 event in execution (parallel execution is impossible) others retcodes of sp_head::execute_procedure() */ @@ -1068,10 +1078,14 @@ event_timed::get_create_event(THD *thd, String *buf) int event_timed::execute(THD *thd, MEM_ROOT *mem_root) { - List empty_item_list; + Security_context *save_ctx; + /* this one is local and not needed after exec */ + Security_context security_ctx; int ret= 0; DBUG_ENTER("event_timed::execute"); + DBUG_PRINT("info", (" EVEX EXECUTING event %s.%s [EXPR:%d]", + dbname.str, name.str, (int) expression)); VOID(pthread_mutex_lock(&this->LOCK_running)); if (running) @@ -1082,30 +1096,110 @@ event_timed::execute(THD *thd, MEM_ROOT *mem_root) running= true; VOID(pthread_mutex_unlock(&this->LOCK_running)); - // TODO Andrey : make this as member variable and delete in destructor - empty_item_list.empty(); + DBUG_PRINT("info", ("master_access=%d db_access=%d", + thd->security_ctx->master_access, thd->security_ctx->db_access)); + change_security_context(thd, &security_ctx, &save_ctx); + DBUG_PRINT("info", ("master_access=%d db_access=%d", + thd->security_ctx->master_access, thd->security_ctx->db_access)); if (!sphead && (ret= compile(thd, mem_root))) goto done; - - ret= sphead->execute_procedure(thd, &empty_item_list); + /* Now we are sure we have valid this->sphead so we can copy the context */ + sphead->m_security_ctx= security_ctx; + thd->db= dbname.str; + thd->db_length= dbname.length; + if (!check_access(thd, EVENT_ACL,dbname.str, 0, 0, 0,is_schema_db(dbname.str))) + { + List empty_item_list; + empty_item_list.empty(); + ret= sphead->execute_procedure(thd, &empty_item_list); + } + else + { + DBUG_PRINT("error", ("%s@%s has no rights on %s", definer_user.str, + definer_host.str, dbname.str)); + ret= -99; + } + restore_security_context(thd, save_ctx); + DBUG_PRINT("info", ("master_access=%d db_access=%d", + thd->security_ctx->master_access, thd->security_ctx->db_access)); + thd->db= 0; VOID(pthread_mutex_lock(&this->LOCK_running)); running= false; VOID(pthread_mutex_unlock(&this->LOCK_running)); done: - // Don't cache sphead if allocated on another mem_root + /* + 1. Don't cache sphead if allocated on another mem_root + 2. Don't call security_ctx.destroy() because this will free our dbname.str + name.str and definer.str + */ if (mem_root && sphead) { delete sphead; sphead= 0; } + DBUG_PRINT("info", (" EVEX EXECUTED event %s.%s [EXPR:%d]. RetCode=%d", + dbname.str, name.str, (int) expression, ret)); DBUG_RETURN(ret); } +/* + Switches the security context + Synopsis + event_timed::change_security_context() + thd - thread + backup - where to store the old context + + RETURN + 0 - OK + 1 - Error (generates error too) +*/ +bool +event_timed::change_security_context(THD *thd, Security_context *s_ctx, + Security_context **backup) +{ + DBUG_ENTER("event_timed::change_security_context"); + DBUG_PRINT("info",("%s@%s@%s",definer_user.str,definer_host.str, dbname.str)); +#ifndef NO_EMBEDDED_ACCESS_CHECKS + s_ctx->init(); + *backup= 0; + if (acl_getroot_no_password(s_ctx, definer_user.str, definer_host.str, + definer_host.str, dbname.str)) + { + my_error(ER_NO_SUCH_USER, MYF(0), definer_user.str, definer_host.str); + DBUG_RETURN(TRUE); + } + *backup= thd->security_ctx; + thd->security_ctx= s_ctx; +#endif + DBUG_RETURN(FALSE); +} + + +/* + Restores the security context + Synopsis + event_timed::restore_security_context() + thd - thread + backup - switch to this context + */ + +void +event_timed::restore_security_context(THD *thd, Security_context *backup) +{ + DBUG_ENTER("event_timed::change_security_context"); +#ifndef NO_EMBEDDED_ACCESS_CHECKS + if (backup) + thd->security_ctx= backup; +#endif + DBUG_VOID_RETURN; +} + + /* Compiles an event before it's execution. Compiles the anonymous sp_head object held by the event @@ -1128,6 +1222,7 @@ event_timed::compile(THD *thd, MEM_ROOT *mem_root) MEM_ROOT *tmp_mem_root= 0; LEX *old_lex= thd->lex, lex; char *old_db; + int old_db_length; event_timed *ett; sp_name *spn; char *old_query; @@ -1161,7 +1256,9 @@ event_timed::compile(THD *thd, MEM_ROOT *mem_root) old_query_len= thd->query_length; old_query= thd->query; old_db= thd->db; + old_db_length= thd->db_length; thd->db= dbname.str; + thd->db_length= dbname.length; get_create_event(thd, &show_create); @@ -1227,3 +1324,135 @@ done: DBUG_RETURN(ret); } + +/* + Checks whether this thread can lock the object for modification -> + preventing being spawned for execution, and locks if possible. + use ::can_spawn_now() only for basic checking because a race + condition may occur between the check and eventual modification (deletion) + of the object. + + Returns + true - locked + false - cannot lock +*/ + +my_bool +event_timed::can_spawn_now_n_lock(THD *thd) +{ + my_bool ret= FALSE; + VOID(pthread_mutex_lock(&this->LOCK_running)); + if (!in_spawned_thread) + { + in_spawned_thread= TRUE; + ret= TRUE; + locked_by_thread_id= thd->thread_id; + } + VOID(pthread_mutex_unlock(&this->LOCK_running)); + return ret; +} + + +extern pthread_attr_t connection_attrib; + +/* + Checks whether is possible and forks a thread. Passes self as argument. + + Returns + EVENT_EXEC_STARTED - OK + EVENT_EXEC_ALREADY_EXEC - Thread not forked, already working + EVENT_EXEC_CANT_FORK - Unable to spawn thread (error) +*/ + +int +event_timed::spawn_now(void * (*thread_func)(void*)) +{ + int ret= EVENT_EXEC_STARTED; + static uint exec_num= 0; + DBUG_ENTER("event_timed::spawn_now"); + DBUG_PRINT("info", ("[%s.%s]", dbname.str, name.str)); + + VOID(pthread_mutex_lock(&this->LOCK_running)); + if (!in_spawned_thread) + { + pthread_t th; + in_spawned_thread= true; + if (pthread_create(&th, &connection_attrib, thread_func, (void*)this)) + { + DBUG_PRINT("info", ("problem while spawning thread")); + ret= EVENT_EXEC_CANT_FORK; + in_spawned_thread= false; + } +#ifndef DBUG_OFF + else + { + sql_print_information("SCHEDULER: Started thread %d", ++exec_num); + DBUG_PRINT("info", ("thread spawned")); + } +#endif + } + else + { + DBUG_PRINT("info", ("already in spawned thread. skipping")); + ret= EVENT_EXEC_ALREADY_EXEC; + } + VOID(pthread_mutex_unlock(&this->LOCK_running)); + + DBUG_RETURN(ret); +} + + +void +event_timed::spawn_thread_finish(THD *thd) +{ + DBUG_ENTER("event_timed::spawn_thread_finish"); + VOID(pthread_mutex_lock(&this->LOCK_running)); + in_spawned_thread= false; + if ((flags & EVENT_EXEC_NO_MORE) || status == MYSQL_EVENT_DISABLED) + { + DBUG_PRINT("info", ("%s exec no more. to drop=%d", name.str, dropped)); + if (dropped) + drop(thd); + VOID(pthread_mutex_unlock(&this->LOCK_running)); + delete this; + DBUG_VOID_RETURN; + } + VOID(pthread_mutex_unlock(&this->LOCK_running)); + DBUG_VOID_RETURN; +} + + +/* + Unlocks the object after it has been locked with ::can_spawn_now_n_lock() + + Returns + 0 - ok + 1 - not locked by this thread + +*/ + + +int +event_timed::spawn_unlock(THD *thd) +{ + int ret= 0; + VOID(pthread_mutex_lock(&this->LOCK_running)); + if (!in_spawned_thread) + { + if (locked_by_thread_id == thd->thread_id) + { + in_spawned_thread= FALSE; + locked_by_thread_id= 0; + } + else + { + sql_print_error("A thread tries to unlock when he hasn't locked. " + "thread_id=%ld locked by %ld", + thd->thread_id, locked_by_thread_id); + DBUG_ASSERT(0); + ret= 1; + } + } + VOID(pthread_mutex_unlock(&this->LOCK_running)); + return ret; +} diff --git a/sql/ha_ndbcluster.cc b/sql/ha_ndbcluster.cc index dadb9811e87..cd37e830952 100644 --- a/sql/ha_ndbcluster.cc +++ b/sql/ha_ndbcluster.cc @@ -34,6 +34,7 @@ #include #include "ha_ndbcluster_binlog.h" +#include "ha_ndbcluster_tables.h" #ifdef ndb_dynamite #undef assert @@ -476,8 +477,7 @@ ha_ndbcluster::invalidate_dictionary_cache(TABLE_SHARE *share, Ndb *ndb, #ifdef HAVE_NDB_BINLOG char key[FN_REFLEN]; - strxnmov(key, FN_LEN-1, mysql_data_home, "/", - dbname, "/", tabname, NullS); + build_table_filename(key, sizeof(key), dbname, tabname, ""); DBUG_PRINT("info", ("Getting ndbcluster mutex")); pthread_mutex_lock(&ndbcluster_mutex); NDB_SHARE *ndb_share= (NDB_SHARE*)hash_search(&ndbcluster_open_tables, @@ -4230,16 +4230,14 @@ int ha_ndbcluster::create(const char *name, NDBCOL col; uint pack_length, length, i, pk_length= 0; const void *data, *pack_data; - char name2[FN_HEADLEN]; bool create_from_engine= (info->table_options & HA_OPTION_CREATE_FROM_ENGINE); DBUG_ENTER("ha_ndbcluster::create"); DBUG_PRINT("enter", ("name: %s", name)); - strcpy(name2, name); - DBUG_ASSERT(*fn_rext((char*)name2) == 0); - set_dbname(name2); - set_tabname(name2); + DBUG_ASSERT(*fn_rext((char*)name) == 0); + set_dbname(name); + set_tabname(name); table= form; if (create_from_engine) @@ -4252,7 +4250,7 @@ int ha_ndbcluster::create(const char *name, if ((my_errno= write_ndb_file(name))) DBUG_RETURN(my_errno); #ifdef HAVE_NDB_BINLOG - ndbcluster_create_binlog_setup(get_ndb(), name2, strlen(name2), + ndbcluster_create_binlog_setup(get_ndb(), name, strlen(name), m_dbname, m_tabname, FALSE); #endif /* HAVE_NDB_BINLOG */ DBUG_RETURN(my_errno); @@ -4400,18 +4398,18 @@ int ha_ndbcluster::create(const char *name, First make sure we get a "fresh" share here, not an old trailing one... */ { - const char *key= name2; - uint length= (uint) strlen(key); + uint length= (uint) strlen(name); if ((share= (NDB_SHARE*) hash_search(&ndbcluster_open_tables, - (byte*) key, length))) + (byte*) name, length))) handle_trailing_share(share); } /* get a new share */ - if (!(share= get_share(name2, form, true, true))) + + if (!(share= get_share(name, form, true, true))) { - sql_print_error("NDB: allocating table share for %s failed", name2); + sql_print_error("NDB: allocating table share for %s failed", name); /* my_errno is set */ } pthread_mutex_unlock(&ndbcluster_mutex); @@ -4421,6 +4419,12 @@ int ha_ndbcluster::create(const char *name, const NDBTAB *t= dict->getTable(m_tabname); String event_name(INJECTOR_EVENT_LEN); ndb_rep_event_name(&event_name,m_dbname,m_tabname); + int do_event_op= ndb_binlog_running; + + if (!schema_share && + strcmp(share->db, NDB_REP_DB) == 0 && + strcmp(share->table_name, NDB_SCHEMA_TABLE) == 0) + do_event_op= 1; /* Always create an event for the table, as other mysql servers @@ -4429,7 +4433,7 @@ int ha_ndbcluster::create(const char *name, if (ndbcluster_create_event(ndb, t, event_name.c_ptr(), share) < 0) { /* this is only a serious error if the binlog is on */ - if (share && ndb_binlog_running) + if (share && do_event_op) { push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_ERROR, ER_GET_ERRMSG, ER(ER_GET_ERRMSG), @@ -4442,14 +4446,14 @@ int ha_ndbcluster::create(const char *name, sql_print_information("NDB Binlog: CREATE TABLE Event: %s", event_name.c_ptr()); - if (share && ndb_binlog_running && + if (share && do_event_op && ndbcluster_create_event_ops(share, t, event_name.c_ptr()) < 0) { sql_print_error("NDB Binlog: FAILED CREATE TABLE event operations." - " Event: %s", name2); + " Event: %s", name); /* a warning has been issued to the client */ } - if (share && !ndb_binlog_running) + if (share && !do_event_op) share->flags|= NSF_NO_BINLOG; ndbcluster_log_schema_op(current_thd, share, current_thd->query, current_thd->query_length, @@ -4732,9 +4736,8 @@ int ha_ndbcluster::rename_table(const char *from, const char *to) ERR_RETURN(dict->getNdbError()); } #ifdef HAVE_NDB_BINLOG - NDB_SHARE *share= 0; - if (ndb_binlog_running && - (share= get_share(from, 0, false))) + NDB_SHARE *share= get_share(from, 0, false); + if (share) { int r= rename_share(share, to); DBUG_ASSERT(r == 0); @@ -4795,7 +4798,7 @@ int ha_ndbcluster::rename_table(const char *from, const char *to) if (ndb_extra_logging) sql_print_information("NDB Binlog: RENAME Event: %s", event_name.c_ptr()); - if (share) + if (share && ndb_binlog_running) { if (ndbcluster_create_event_ops(share, ndbtab, event_name.c_ptr()) < 0) @@ -5319,7 +5322,7 @@ int ndbcluster_discover(THD* thd, const char *db, const char *name, NDBDICT* dict= ndb->getDictionary(); dict->set_local_table_data_size(sizeof(Ndb_local_table_statistics)); dict->invalidateTable(name); - strxnmov(key, FN_LEN-1, mysql_data_home, "/", db, "/", name, NullS); + build_table_filename(key, sizeof(key), db, name, ""); NDB_SHARE *share= get_share(key, 0, false); if (share && get_ndb_share_state(share) == NSS_ALTERED) { @@ -5453,13 +5456,14 @@ int ndbcluster_drop_database_impl(const char *path) } // Drop any tables belonging to database char full_path[FN_REFLEN]; - char *tmp= strxnmov(full_path, FN_REFLEN-1, share_prefix, dbname, "/", - NullS); + char *tmp= full_path + + build_table_filename(full_path, sizeof(full_path), dbname, "", ""); + ndb->setDatabaseName(dbname); List_iterator_fast it(drop_list); while ((tabname=it++)) { - strxnmov(tmp, FN_REFLEN - (tmp - full_path)-1, tabname, NullS); + tablename_to_filename(tabname, tmp, FN_REFLEN - (tmp - full_path)-1); if (ha_ndbcluster::delete_table(0, ndb, full_path, dbname, tabname)) { const NdbError err= dict->getNdbError(); @@ -5552,14 +5556,16 @@ int ndbcluster_find_all_files(THD *thd) continue; /* check if database exists */ - char *end= strxnmov(key, FN_LEN-1, mysql_data_home, "/", - elmt.database, NullS); + char *end= key + + build_table_filename(key, sizeof(key), elmt.database, "", ""); if (my_access(key, F_OK)) { /* no such database defined, skip table */ continue; } - end= strxnmov(end, FN_LEN-1-(end-key), "/", elmt.name, NullS); + /* finalize construction of path */ + end+= tablename_to_filename(elmt.name, end, + sizeof(key)-(end-key)); const void *data= 0, *pack_data= 0; uint length, pack_length; int discover= 0; @@ -5694,10 +5700,9 @@ int ndbcluster_find_files(THD *thd,const char *db,const char *path, } // File is not in NDB, check for .ndb file with this name - (void)strxnmov(name, FN_REFLEN-1, - mysql_data_home,"/",db,"/",file_name,ha_ndb_ext,NullS); + build_table_filename(name, sizeof(name), db, file_name, ha_ndb_ext); DBUG_PRINT("info", ("Check access for %s", name)); - if (access(name, F_OK)) + if (my_access(name, F_OK)) { DBUG_PRINT("info", ("%s did not exist on disk", name)); // .ndb file did not exist on disk, another table type @@ -5719,12 +5724,13 @@ int ndbcluster_find_files(THD *thd,const char *db,const char *path, #ifdef HAVE_NDB_BINLOG /* setup logging to binlog for all discovered tables */ { - char *end, *end1= - strxnmov(name, sizeof(name), mysql_data_home, "/", db, "/", NullS); + char *end, *end1= name + + build_table_filename(name, sizeof(name), db, "", ""); for (i= 0; i < ok_tables.records; i++) { file_name= (char*)hash_element(&ok_tables, i); - end= strxnmov(end1, sizeof(name) - (end1 - name), file_name, NullS); + end= end1 + + tablename_to_filename(file_name, end1, sizeof(name) - (end1 - name)); pthread_mutex_lock(&LOCK_open); ndbcluster_create_binlog_setup(ndb, name, end-name, db, file_name, TRUE); @@ -5741,9 +5747,8 @@ int ndbcluster_find_files(THD *thd,const char *db,const char *path, file_name= hash_element(&ndb_tables, i); if (!hash_search(&ok_tables, file_name, strlen(file_name))) { - strxnmov(name, sizeof(name)-1, - mysql_data_home, "/", db, "/", file_name, reg_ext, NullS); - if (access(name, F_OK)) + build_table_filename(name, sizeof(name), db, file_name, reg_ext); + if (my_access(name, F_OK)) { DBUG_PRINT("info", ("%s must be discovered", file_name)); // File is in list of ndb tables and not in ok_tables @@ -6277,7 +6282,7 @@ uint ndb_get_commitcount(THD *thd, char *dbname, char *tabname, NDB_SHARE *share; DBUG_ENTER("ndb_get_commitcount"); - (void)strxnmov(name, FN_REFLEN-1, share_prefix, dbname, "/", tabname, NullS); + build_table_filename(name, sizeof(name), dbname, tabname, ""); DBUG_PRINT("enter", ("name: %s", name)); pthread_mutex_lock(&ndbcluster_mutex); if (!(share=(NDB_SHARE*) hash_search(&ndbcluster_open_tables, @@ -6655,16 +6660,19 @@ static int rename_share(NDB_SHARE *share, const char *new_key) ("db.tablename: %s.%s use_count: %d commit_count: %d", share->db, share->table_name, share->use_count, share->commit_count)); - DBUG_PRINT("rename_share", - ("table->s->db.table_name: %s.%s", - share->table->s->db.str, share->table->s->table_name.str)); - - if (share->op == 0) + if (share->table) { - share->table->s->db.str= share->db; - share->table->s->db.length= strlen(share->db); - share->table->s->table_name.str= share->table_name; - share->table->s->table_name.length= strlen(share->table_name); + DBUG_PRINT("rename_share", + ("table->s->db.table_name: %s.%s", + share->table->s->db.str, share->table->s->table_name.str)); + + if (share->op == 0) + { + share->table->s->db.str= share->db; + share->table->s->db.length= strlen(share->db); + share->table->s->table_name.str= share->table_name; + share->table->s->table_name.length= strlen(share->table_name); + } } /* else rename will be handled when the ALTER event comes */ share->old_names= old_key; diff --git a/sql/ha_ndbcluster_binlog.cc b/sql/ha_ndbcluster_binlog.cc index fadffcbb1d3..06f946b013c 100644 --- a/sql/ha_ndbcluster_binlog.cc +++ b/sql/ha_ndbcluster_binlog.cc @@ -237,10 +237,33 @@ void ndbcluster_binlog_init_share(NDB_SHARE *share, TABLE *_table) { THD *thd= current_thd; MEM_ROOT *mem_root= &share->mem_root; + int do_event_op= ndb_binlog_running; share->op= 0; share->table= 0; - if (!ndb_binlog_running) + + if (!schema_share && + strcmp(share->db, NDB_REP_DB) == 0 && + strcmp(share->table_name, NDB_SCHEMA_TABLE) == 0) + do_event_op= 1; + + { + int i, no_nodes= g_ndb_cluster_connection->no_db_nodes(); + share->subscriber_bitmap= (MY_BITMAP*) + alloc_root(mem_root, no_nodes * sizeof(MY_BITMAP)); + for (i= 0; i < no_nodes; i++) + { + bitmap_init(&share->subscriber_bitmap[i], + (Uint32*)alloc_root(mem_root, max_ndb_nodes/8), + max_ndb_nodes, false); + bitmap_clear_all(&share->subscriber_bitmap[i]); + } + bitmap_init(&share->slock_bitmap, share->slock, + sizeof(share->slock)*8, false); + bitmap_clear_all(&share->slock_bitmap); + } + + if (!do_event_op) { if (_table) { @@ -315,21 +338,6 @@ void ndbcluster_binlog_init_share(NDB_SHARE *share, TABLE *_table) share->ndb_value[1]= (NdbValue*) alloc_root(mem_root, sizeof(NdbValue) * table->s->fields +1 /*extra for hidden key*/); - { - int i, no_nodes= g_ndb_cluster_connection->no_db_nodes(); - share->subscriber_bitmap= (MY_BITMAP*) - alloc_root(mem_root, no_nodes * sizeof(MY_BITMAP)); - for (i= 0; i < no_nodes; i++) - { - bitmap_init(&share->subscriber_bitmap[i], - (Uint32*)alloc_root(mem_root, max_ndb_nodes/8), - max_ndb_nodes, false); - bitmap_clear_all(&share->subscriber_bitmap[i]); - } - bitmap_init(&share->slock_bitmap, share->slock, - sizeof(share->slock)*8, false); - bitmap_clear_all(&share->slock_bitmap); - } if (table->s->primary_key == MAX_KEY) share->flags|= NSF_HIDDEN_PK; if (table->s->blob_fields != 0) @@ -648,11 +656,8 @@ static int ndbcluster_create_apply_status_table(THD *thd) if so, remove it since there is none in Ndb */ { - strxnmov(buf, sizeof(buf), - mysql_data_home, - "/" NDB_REP_DB "/" NDB_APPLY_TABLE, - reg_ext, NullS); - unpack_filename(buf,buf); + build_table_filename(buf, sizeof(buf), + NDB_REP_DB, NDB_APPLY_TABLE, reg_ext); my_delete(buf, MYF(0)); } @@ -700,11 +705,8 @@ static int ndbcluster_create_schema_table(THD *thd) if so, remove it since there is none in Ndb */ { - strxnmov(buf, sizeof(buf), - mysql_data_home, - "/" NDB_REP_DB "/" NDB_SCHEMA_TABLE, - reg_ext, NullS); - unpack_filename(buf,buf); + build_table_filename(buf, sizeof(buf), + NDB_REP_DB, NDB_SCHEMA_TABLE, reg_ext); my_delete(buf, MYF(0)); } @@ -929,8 +931,7 @@ int ndbcluster_log_schema_op(THD *thd, NDB_SHARE *share, if (get_a_share) { char key[FN_REFLEN]; - (void)strxnmov(key, FN_REFLEN, share_prefix, db, - "/", table_name, NullS); + build_table_filename(key, sizeof(key), db, table_name, ""); share= get_share(key, 0, false, false); } @@ -1358,6 +1359,7 @@ ndb_binlog_thread_handle_schema_event(THD *thd, Ndb *ndb, switch (ev_type) { case NDBEVENT::TE_UPDATE: + /* fall through */ case NDBEVENT::TE_INSERT: { Cluster_replication_schema *schema= (Cluster_replication_schema *) @@ -1375,21 +1377,20 @@ ndb_binlog_thread_handle_schema_event(THD *thd, Ndb *ndb, { case SOT_DROP_TABLE: /* binlog dropping table after any table operations */ - post_epoch_log_list->push_back(schema, mem_root); + if (ndb_binlog_running) + post_epoch_log_list->push_back(schema, mem_root); log_query= 0; break; case SOT_RENAME_TABLE: /* fall through */ case SOT_ALTER_TABLE: - /* fall through */ - if (!ndb_binlog_running) + if (ndb_binlog_running) { log_query= 1; break; /* discovery will be handled by binlog */ } /* fall through */ case SOT_CREATE_TABLE: - /* fall through */ pthread_mutex_lock(&LOCK_open); if (ndb_create_table_from_engine(thd, schema->db, schema->name)) { @@ -1407,7 +1408,8 @@ ndb_binlog_thread_handle_schema_event(THD *thd, Ndb *ndb, TRUE, /* print error */ TRUE); /* don't binlog the query */ /* binlog dropping database after any table operations */ - post_epoch_log_list->push_back(schema, mem_root); + if (ndb_binlog_running) + post_epoch_log_list->push_back(schema, mem_root); log_query= 0; break; case SOT_CREATE_DB: @@ -1422,8 +1424,8 @@ ndb_binlog_thread_handle_schema_event(THD *thd, Ndb *ndb, case SOT_CLEAR_SLOCK: { char key[FN_REFLEN]; - (void)strxnmov(key, FN_REFLEN, share_prefix, schema->db, - "/", schema->name, NullS); + build_table_filename(key, sizeof(key), + schema->db, schema->name, ""); NDB_SHARE *share= get_share(key, 0, false, false); if (share) { @@ -1463,7 +1465,7 @@ ndb_binlog_thread_handle_schema_event(THD *thd, Ndb *ndb, } } - if (log_query) + if (log_query && ndb_binlog_running) { char *thd_db_save= thd->db; thd->db= schema->db; @@ -1752,6 +1754,7 @@ int ndbcluster_create_binlog_setup(Ndb *ndb, const char *key, const char *table_name, my_bool share_may_exist) { + int do_event_op= ndb_binlog_running; DBUG_ENTER("ndbcluster_create_binlog_setup"); DBUG_PRINT("enter",("key: %s key_len: %d %s.%s share_may_exist: %d", key, key_len, db, table_name, share_may_exist)); @@ -1792,7 +1795,12 @@ int ndbcluster_create_binlog_setup(Ndb *ndb, const char *key, "allocating table share for %s failed", key); } - if (!ndb_binlog_running) + if (!schema_share && + strcmp(share->db, NDB_REP_DB) == 0 && + strcmp(share->table_name, NDB_SCHEMA_TABLE) == 0) + do_event_op= 1; + + if (!do_event_op) { share->flags|= NSF_NO_BINLOG; pthread_mutex_unlock(&ndbcluster_mutex); diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc index 0bd300df2e2..1897886ce45 100644 --- a/sql/ha_partition.cc +++ b/sql/ha_partition.cc @@ -360,7 +360,7 @@ int ha_partition::ha_initialise() other parameters are calculated on demand. HA_FILE_BASED is always set for partition handler since we use a special file for handling names of partitions, engine types. - HA_CAN_GEOMETRY, HA_CAN_FULLTEXT, HA_CAN_SQL_HANDLER, + HA_CAN_GEOMETRY, HA_CAN_FULLTEXT, HA_CAN_SQL_HANDLER, HA_DUPP_POS, HA_CAN_INSERT_DELAYED is disabled until further investigated. */ m_table_flags= m_file[0]->table_flags(); @@ -383,8 +383,8 @@ int ha_partition::ha_initialise() m_pkey_is_clustered= FALSE; m_table_flags&= file->table_flags(); } while (*(++file_array)); - m_table_flags&= ~(HA_CAN_GEOMETRY & HA_CAN_FULLTEXT & - HA_CAN_SQL_HANDLER & HA_CAN_INSERT_DELAYED); + m_table_flags&= ~(HA_CAN_GEOMETRY | HA_CAN_FULLTEXT | HA_DUPP_POS | + HA_CAN_SQL_HANDLER | HA_CAN_INSERT_DELAYED); m_table_flags|= HA_FILE_BASED | HA_REC_NOT_IN_SEQ; DBUG_RETURN(0); } @@ -1937,7 +1937,8 @@ bool ha_partition::create_handler_file(const char *name) name_buffer_ptr+= name_add(name_buffer_ptr, part_name, subpart_name); - *engine_array= (uchar) ha_legacy_type(part_elem->engine_type); + *engine_array= (uchar) ha_legacy_type(subpart_elem->engine_type); + DBUG_PRINT("info", ("engine: %u", *engine_array)); engine_array++; } } @@ -1954,7 +1955,7 @@ bool ha_partition::create_handler_file(const char *name) Create and write and close file to be used at open, delete_table and rename_table */ - fn_format(file_name, name, "", ".par", MY_APPEND_EXT); + fn_format(file_name, name, "", ha_par_ext, MY_APPEND_EXT); if ((file= my_create(file_name, CREATE_MODE, O_RDWR | O_TRUNC, MYF(MY_WME))) >= 0) { @@ -4683,6 +4684,7 @@ int ha_partition::extra(enum ha_extra_function operation) case HA_EXTRA_PREPARE_FOR_UPDATE: case HA_EXTRA_PREPARE_FOR_DELETE: case HA_EXTRA_FORCE_REOPEN: + case HA_EXTRA_FLUSH_CACHE: { if (m_myisam) DBUG_RETURN(loop_extra(operation)); diff --git a/sql/set_var.cc b/sql/set_var.cc index c9ba454f80c..c9feea49618 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -3488,7 +3488,7 @@ byte *sys_var_thd_dbug::value_ptr(THD *thd, enum_var_type type, LEX_STRING *b) DBUG_EXPLAIN_INITIAL(buf, sizeof(buf)); else DBUG_EXPLAIN(buf, sizeof(buf)); - (byte*) thd->strdup(buf); + return (byte*) thd->strdup(buf); } /**************************************************************************** diff --git a/sql/sql_db.cc b/sql/sql_db.cc index a7a7327bb87..885643cf899 100644 --- a/sql/sql_db.cc +++ b/sql/sql_db.cc @@ -20,6 +20,7 @@ #include "mysql_priv.h" #include #include "sp.h" +#include "event.h" #include #include #ifdef __WIN__ @@ -870,6 +871,7 @@ bool mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent) exit: (void)sp_drop_db_routines(thd, db); /* QQ Ignore errors for now */ + (void)evex_drop_db_events(thd, db); /* QQ Ignore errors for now */ start_waiting_global_read_lock(thd); /* If this database was the client's selected database, we silently change the diff --git a/sql/sql_error.cc b/sql/sql_error.cc index 191a6e0a1fd..74df3b68a0d 100644 --- a/sql/sql_error.cc +++ b/sql/sql_error.cc @@ -211,8 +211,13 @@ void push_warning_printf(THD *thd, MYSQL_ERROR::enum_warning_level level, TRUE Error sending data to client */ -static const char *warning_level_names[]= {"Note", "Warning", "Error", "?"}; -static int warning_level_length[]= { 4, 7, 5, 1 }; +LEX_STRING warning_level_names[]= +{ + {(char*) STRING_WITH_LEN("Note")}, + {(char*) STRING_WITH_LEN("Warning")}, + {(char*) STRING_WITH_LEN("Error")}, + {(char*) STRING_WITH_LEN("?")} +}; bool mysqld_show_warnings(THD *thd, ulong levels_to_show) { @@ -246,8 +251,8 @@ bool mysqld_show_warnings(THD *thd, ulong levels_to_show) if (idx > unit->select_limit_cnt) break; protocol->prepare_for_resend(); - protocol->store(warning_level_names[err->level], - warning_level_length[err->level], system_charset_info); + protocol->store(warning_level_names[err->level].str, + warning_level_names[err->level].length, system_charset_info); protocol->store((uint32) err->code); protocol->store(err->msg, strlen(err->msg), system_charset_info); if (protocol->write()) diff --git a/storage/ndb/include/util/File.hpp b/storage/ndb/include/util/File.hpp index 3ed0ad7a6f9..53ea88e0f52 100644 --- a/storage/ndb/include/util/File.hpp +++ b/storage/ndb/include/util/File.hpp @@ -191,10 +191,8 @@ public: int flush() const; private: - STATIC_CONST( MAX_FILE_NAME_SIZE = 128 ); - FILE* m_file; - char m_fileName[MAX_FILE_NAME_SIZE]; + char m_fileName[PATH_MAX]; const char* m_fileMode; /* Prohibit */ File_class (const File_class& aCopy); diff --git a/storage/ndb/src/common/portlib/NdbConfig.c b/storage/ndb/src/common/portlib/NdbConfig.c index b275143646f..c3f37727024 100644 --- a/storage/ndb/src/common/portlib/NdbConfig.c +++ b/storage/ndb/src/common/portlib/NdbConfig.c @@ -64,11 +64,11 @@ NdbConfig_NdbCfgName(int with_ndb_home){ int len= 0; if (with_ndb_home) { - buf= NdbConfig_AllocHomePath(128); + buf= NdbConfig_AllocHomePath(PATH_MAX); len= strlen(buf); } else - buf= NdbMem_Allocate(128); - basestring_snprintf(buf+len, 128, "Ndb.cfg"); + buf= NdbMem_Allocate(PATH_MAX); + basestring_snprintf(buf+len, PATH_MAX, "Ndb.cfg"); return buf; } @@ -90,56 +90,56 @@ char *get_prefix_buf(int len, int node_id) char* NdbConfig_ErrorFileName(int node_id){ - char *buf= get_prefix_buf(128, node_id); + char *buf= get_prefix_buf(PATH_MAX, node_id); int len= strlen(buf); - basestring_snprintf(buf+len, 128, "_error.log"); + basestring_snprintf(buf+len, PATH_MAX, "_error.log"); return buf; } char* NdbConfig_ClusterLogFileName(int node_id){ - char *buf= get_prefix_buf(128, node_id); + char *buf= get_prefix_buf(PATH_MAX, node_id); int len= strlen(buf); - basestring_snprintf(buf+len, 128, "_cluster.log"); + basestring_snprintf(buf+len, PATH_MAX, "_cluster.log"); return buf; } char* NdbConfig_SignalLogFileName(int node_id){ - char *buf= get_prefix_buf(128, node_id); + char *buf= get_prefix_buf(PATH_MAX, node_id); int len= strlen(buf); - basestring_snprintf(buf+len, 128, "_signal.log"); + basestring_snprintf(buf+len, PATH_MAX, "_signal.log"); return buf; } char* NdbConfig_TraceFileName(int node_id, int file_no){ - char *buf= get_prefix_buf(128, node_id); + char *buf= get_prefix_buf(PATH_MAX, node_id); int len= strlen(buf); - basestring_snprintf(buf+len, 128, "_trace.log.%u", file_no); + basestring_snprintf(buf+len, PATH_MAX, "_trace.log.%u", file_no); return buf; } char* NdbConfig_NextTraceFileName(int node_id){ - char *buf= get_prefix_buf(128, node_id); + char *buf= get_prefix_buf(PATH_MAX, node_id); int len= strlen(buf); - basestring_snprintf(buf+len, 128, "_trace.log.next"); + basestring_snprintf(buf+len, PATH_MAX, "_trace.log.next"); return buf; } char* NdbConfig_PidFileName(int node_id){ - char *buf= get_prefix_buf(128, node_id); + char *buf= get_prefix_buf(PATH_MAX, node_id); int len= strlen(buf); - basestring_snprintf(buf+len, 128, ".pid"); + basestring_snprintf(buf+len, PATH_MAX, ".pid"); return buf; } char* NdbConfig_StdoutFileName(int node_id){ - char *buf= get_prefix_buf(128, node_id); + char *buf= get_prefix_buf(PATH_MAX, node_id); int len= strlen(buf); - basestring_snprintf(buf+len, 128, "_out.log"); + basestring_snprintf(buf+len, PATH_MAX, "_out.log"); return buf; } diff --git a/storage/ndb/src/common/util/File.cpp b/storage/ndb/src/common/util/File.cpp index e514ad8e122..52ad3a4a51e 100644 --- a/storage/ndb/src/common/util/File.cpp +++ b/storage/ndb/src/common/util/File.cpp @@ -67,7 +67,7 @@ File_class::File_class(const char* aFileName, const char* mode) : m_file(NULL), m_fileMode(mode) { - BaseString::snprintf(m_fileName, MAX_FILE_NAME_SIZE, aFileName); + BaseString::snprintf(m_fileName, PATH_MAX, aFileName); } bool @@ -83,7 +83,7 @@ File_class::open(const char* aFileName, const char* mode) /** * Only copy if it's not the same string */ - BaseString::snprintf(m_fileName, MAX_FILE_NAME_SIZE, aFileName); + BaseString::snprintf(m_fileName, PATH_MAX, aFileName); } m_fileMode = mode; bool rc = true; diff --git a/tests/mysql_client_test.c b/tests/mysql_client_test.c index 14e6fd1f791..7db643574c5 100644 --- a/tests/mysql_client_test.c +++ b/tests/mysql_client_test.c @@ -1049,7 +1049,10 @@ void stmt_fetch_close(Stmt_fetch *fetch) reading from the rest. */ -my_bool fetch_n(const char **query_list, unsigned query_count) +enum fetch_type { USE_ROW_BY_ROW_FETCH= 0, USE_STORE_RESULT= 1 }; + +my_bool fetch_n(const char **query_list, unsigned query_count, + enum fetch_type fetch_type) { unsigned open_statements= query_count; int rc, error_count= 0; @@ -1065,6 +1068,15 @@ my_bool fetch_n(const char **query_list, unsigned query_count) query_list[fetch - fetch_array]); } + if (fetch_type == USE_STORE_RESULT) + { + for (fetch= fetch_array; fetch < fetch_array + query_count; ++fetch) + { + rc= mysql_stmt_store_result(fetch->handle); + check_execute(fetch->handle, rc); + } + } + while (open_statements) { for (fetch= fetch_array; fetch < fetch_array + query_count; ++fetch) @@ -11867,7 +11879,8 @@ static void test_basic_cursors() fill_tables(basic_tables, sizeof(basic_tables)/sizeof(*basic_tables)); - fetch_n(queries, sizeof(queries)/sizeof(*queries)); + fetch_n(queries, sizeof(queries)/sizeof(*queries), USE_ROW_BY_ROW_FETCH); + fetch_n(queries, sizeof(queries)/sizeof(*queries), USE_STORE_RESULT); DBUG_VOID_RETURN; } @@ -11880,7 +11893,8 @@ static void test_cursors_with_union() "SELECT t1.id FROM t1 WHERE t1.id < 5" }; myheader("test_cursors_with_union"); - fetch_n(queries, sizeof(queries)/sizeof(*queries)); + fetch_n(queries, sizeof(queries)/sizeof(*queries), USE_ROW_BY_ROW_FETCH); + fetch_n(queries, sizeof(queries)/sizeof(*queries), USE_STORE_RESULT); } /* @@ -14729,6 +14743,21 @@ static void test_bug12744() client_connect(0); } +/* Bug #16143: mysql_stmt_sqlstate returns an empty string instead of '00000' */ + +static void test_bug16143() +{ + MYSQL_STMT *stmt; + myheader("test_bug16143"); + + stmt= mysql_stmt_init(mysql); + /* Check mysql_stmt_sqlstate return "no error" */ + DIE_UNLESS(strcmp(mysql_stmt_sqlstate(stmt), "00000") == 0); + + mysql_stmt_close(stmt); +} + + /* Bug #16144: mysql_stmt_attr_get type error */ static void test_bug16144() @@ -15072,6 +15101,7 @@ static struct my_tests_st my_tests[]= { { "test_opt_reconnect", test_opt_reconnect }, { "test_bug15510", test_bug15510}, { "test_bug12744", test_bug12744 }, + { "test_bug16143", test_bug16143 }, { "test_bug16144", test_bug16144 }, { "test_bug15613", test_bug15613 }, { 0, 0 }