1
0
mirror of https://github.com/MariaDB/server.git synced 2025-11-19 19:03:26 +03:00

Merge mysql.com:/home/mydev/mysql-5.1

into  mysql.com:/home/mydev/mysql-5.1-bug8841
This commit is contained in:
ingo@mysql.com
2006-02-16 07:01:15 +01:00
30 changed files with 1287 additions and 351 deletions

View File

@@ -1443,38 +1443,40 @@ int do_system(struct st_query *q)
/* /*
Print the content between echo and <delimiter> to result file. Print the content between echo and <delimiter> 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 SYNOPSIS
do_echo() do_echo()
q called command q called command
DESCRIPTION DESCRIPTION
Usage 1:
echo text echo text
Print the text after echo until end of command to result file Print the text after echo until end of command to result file
Usage 2:
echo $<var_name> echo $<var_name>
Print the content of the variable <var_name> to result file Print the content of the variable <var_name> to result file
echo Some text $<var_name>
Print "Some text" plus the content of the variable <var_name> to
result file
echo Some text \$<var_name>
Print "Some text" plus $<var_name> 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, ds_echo;
DYNAMIC_STRING *ds;
VAR v;
var_init(&v,0,0,0,0);
ds= &ds_res; ds= &ds_res;
eval_expr(&v, p, 0); /* NULL terminated */ init_dynamic_string(&ds_echo, "", 256, 256);
if (v.str_val_len) do_eval(&ds_echo, command->first_argument);
dynstr_append_mem(ds, v.str_val, v.str_val_len); dynstr_append_mem(ds, ds_echo.str, ds_echo.length);
dynstr_append_mem(ds, "\n", 1); dynstr_append_mem(ds, "\n", 1);
var_free(&v); dynstr_free(&ds_echo);
q->last_argument= q->end; command->last_argument= command->end;
return 0; 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) if (mysql_stmt_fetch(stmt) != MYSQL_NO_DATA)
die("fetch didn't end with MYSQL_NO_DATA from statement: %d %s", 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(); free_replace_column();
@@ -4215,18 +4217,16 @@ static void run_query_stmt(MYSQL *mysql, struct st_query *command,
parameter markers. parameter markers.
*/ */
#ifdef BUG14013_FIXED if (cursor_protocol_enabled)
{
/* /*
Use cursor when retrieving result Use cursor when retrieving result
*/ */
if (cursor_protocol_enabled)
{
ulong type= CURSOR_TYPE_READ_ONLY; ulong type= CURSOR_TYPE_READ_ONLY;
if (mysql_stmt_attr_set(stmt, STMT_ATTR_CURSOR_TYPE, (void*) &type)) if (mysql_stmt_attr_set(stmt, STMT_ATTR_CURSOR_TYPE, (void*) &type))
die("mysql_stmt_attr_set(STMT_ATTR_CURSOR_TYPE) failed': %d %s", 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 Execute the query
@@ -4238,6 +4238,13 @@ static void run_query_stmt(MYSQL *mysql, struct st_query *command,
goto end; 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 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 mysql_stmt_store_result(), this is our only way to know how much
@@ -4303,6 +4310,7 @@ static void run_query_stmt(MYSQL *mysql, struct st_query *command,
/* Append warnings to ds - if there are any */ /* Append warnings to ds - if there are any */
if (append_warnings(&ds_execute_warnings, mysql) || if (append_warnings(&ds_execute_warnings, mysql) ||
ds_execute_warnings.length ||
ds_prepare_warnings.length || ds_prepare_warnings.length ||
ds_warnings->length) ds_warnings->length)
{ {

View File

@@ -2002,6 +2002,7 @@ mysql_stmt_init(MYSQL *mysql)
stmt->mysql= mysql; stmt->mysql= mysql;
stmt->read_row_func= stmt_read_row_no_result_set; stmt->read_row_func= stmt_read_row_no_result_set;
stmt->prefetch_rows= DEFAULT_PREFETCH_ROWS; stmt->prefetch_rows= DEFAULT_PREFETCH_ROWS;
strmov(stmt->sqlstate, not_error_sqlstate);
/* The rest of statement members was bzeroed inside malloc */ /* The rest of statement members was bzeroed inside malloc */
DBUG_RETURN(stmt); DBUG_RETURN(stmt);
@@ -4751,12 +4752,39 @@ int STDCALL mysql_stmt_store_result(MYSQL_STMT *stmt)
if (!stmt->field_count) if (!stmt->field_count)
DBUG_RETURN(0); 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); set_stmt_error(stmt, CR_COMMANDS_OUT_OF_SYNC, unknown_sqlstate);
DBUG_RETURN(1); 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) if (result->data)
{ {
free_root(&result->alloc, MYF(MY_KEEP_PREALLOC)); free_root(&result->alloc, MYF(MY_KEEP_PREALLOC));
@@ -4797,6 +4825,10 @@ int STDCALL mysql_stmt_store_result(MYSQL_STMT *stmt)
DBUG_RETURN(1); 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) if (stmt->update_max_length)
{ {
MYSQL_ROWS *cur= result->data; MYSQL_ROWS *cur= result->data;

View File

@@ -1,5 +1,29 @@
create database if not exists events_test; create database if not exists events_test;
use 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; drop event if exists event1;
Warnings: Warnings:
Note 1305 Event event1 does not exist 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 db name body status interval_field interval_value
events_test e_43 set @a = 4 ENABLED SECOND 1 events_test e_43 set @a = 4 ENABLED SECOND 1
drop event e_43; 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; set global event_scheduler = 0;
create table t_event3 (a int, b float); create table t_event3 (a int, b float);
drop event if exists event3; drop event if exists event3;
@@ -331,8 +363,8 @@ create event закачка on schedule every 10 hour do select get_lock("test_l
show processlist; show processlist;
Id User Host db Command Time State Info Id User Host db Command Time State Info
# root localhost events_test Query # NULL show processlist # root localhost events_test Query # NULL show processlist
# event_scheduler NULL Connect # Sleeping NULL # event_scheduler connecting host NULL Connect # Sleeping NULL
# root events_test Connect # User lock select get_lock("test_lock2", 20) # root localhost events_test Connect # User lock select get_lock("test_lock2", 20)
"Release the mutex, the event worker should finish." "Release the mutex, the event worker should finish."
select release_lock("test_lock2"); select release_lock("test_lock2");
release_lock("test_lock2") release_lock("test_lock2")
@@ -350,8 +382,7 @@ set global event_scheduler=0;
show processlist; show processlist;
Id User Host db Command Time State Info Id User Host db Command Time State Info
# root localhost events_test Query # NULL show processlist # root localhost events_test Query # NULL show processlist
# event_scheduler NULL Connect # Sleeping NULL # event_scheduler connecting host NULL Connect # Sleeping NULL
# root events_test Connect # User lock select get_lock("test_lock2_1", 20)
"Release the lock so the child process should finish. Hence the scheduler also" "Release the lock so the child process should finish. Hence the scheduler also"
select release_lock("test_lock2_1"); select release_lock("test_lock2_1");
release_lock("test_lock2_1") release_lock("test_lock2_1")

View File

@@ -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;

View File

@@ -201,8 +201,14 @@ source database
- world''s most - world''s most
-- popular open -- popular open
# source database # source database
'$message' '# MySQL: The
"$message" - world''s most
-- popular open
# source database'
"# MySQL: The
- world''s most
-- popular open
# source database"
hej hej
hej 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 variable name in let mysqltest: At line 1: Missing variable name in let
mysqltest: At line 1: Missing assignment operator in let mysqltest: At line 1: Missing assignment operator in let
# Execute: --echo # <whatever> success: $success
# <whatever> success: 1
# Execute: echo # <whatever> success: $success ;
# <whatever> 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: Missing file name in source
mysqltest: At line 1: Could not open file ./non_existingFile 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 mysqltest: In included file "MYSQLTEST_VARDIR/tmp/recursive.sql": At line 1: Source directives are nesting too deep

View File

@@ -8,8 +8,6 @@ a b c
2 two two 2 two two
alter table t1 drop index c; alter table t1 drop index c;
select * from t1 where c = 'two'; select * from t1 where c = 'two';
ERROR HY000: Table definition has changed, please retry transaction
select * from t1 where c = 'two';
a b c a b c
2 two two 2 two two
drop table t1; drop table t1;

View File

@@ -30,14 +30,6 @@ drop table t1;
create table t1 (a int) engine=ndbcluster; create table t1 (a int) engine=ndbcluster;
insert into t1 value (2); insert into t1 value (2);
select * from t1; 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 a
2 2
flush status; flush status;
@@ -58,15 +50,9 @@ a
select * from t3; select * from t3;
a b c last_col a b c last_col
1 Hi! 89 Longtext column 1 Hi! 89 Longtext column
show status like 'handler_discover%';
Variable_name Value
Handler_discover 1
show tables like 't4'; show tables like 't4';
Tables_in_test (t4) Tables_in_test (t4)
t4 t4
show status like 'handler_discover%';
Variable_name Value
Handler_discover 2
show tables; show tables;
Tables_in_test Tables_in_test
t1 t1
@@ -74,4 +60,3 @@ t2
t3 t3
t4 t4
drop table t1, t2, t3, t4; drop table t1, t2, t3, t4;
drop table t1, t3, t4;

View File

@@ -300,3 +300,16 @@ select * from t1 where f1 = 10;
f1 f2 f1 f2
10 1 10 1
drop table t1; 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

View File

@@ -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_basic : Results are not deterministic, Tomas will fix
ndb_binlog_ddl_multi : Bug#17038 [PATCH PENDING] ndb_binlog_ddl_multi : Bug#17038 [PATCH PENDING]
ndb_gis : garbled msgs from corrupt THD* ndb_gis : garbled msgs from corrupt THD*
ndb_load : Bug#17233
partition_03ndb : Bug#16385 partition_03ndb : Bug#16385
ps_7ndb : dbug assert in RBR mode when executing test suite ps_7ndb : dbug assert in RBR mode when executing test suite
rpl_bit_npk : Bug#13418 rpl_bit_npk : Bug#13418

View File

@@ -1,5 +1,38 @@
create database if not exists events_test; create database if not exists events_test;
use 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; 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; 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; 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; select db, name, body, status, interval_field, interval_value from mysql.event;
drop event e_43; drop event e_43;
--sleep 1 --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; set global event_scheduler = 0;
create table t_event3 (a int, b float); 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" --echo "Sleep a bit so the server closes the second connection"
--sleep 2 --sleep 2
create event e_26 on schedule at '2017-01-01 00:00:00' disable do set @a = 5; 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; select db, name, body, definer, convert_tz(execute_at, 'UTC', 'SYSTEM'), on_completion from mysql.event;
drop event e_26; drop event e_26;

View File

@@ -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;

View File

@@ -538,6 +538,19 @@ echo $novar1;
--error 1 --error 1
--exec echo "let hi;" | $MYSQL_TEST 2>&1 --exec echo "let hi;" | $MYSQL_TEST 2>&1
# More advanced test for bug#17280
let $success= 1;
--echo # Execute: --echo # <whatever> success: \$success
--echo # <whatever> success: $success
--echo # Execute: echo # <whatever> success: \$success ;
echo # <whatever> 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 # Test to assign let from query
# let $<var_name>=`<query>`; # let $<var_name>=`<query>`;

View File

@@ -17,8 +17,6 @@ select * from t1 where c = 'two';
connection server1; connection server1;
alter table t1 drop index c; alter table t1 drop index c;
connection server2; connection server2;
-- error 1412
select * from t1 where c = 'two';
select * from t1 where c = 'two'; select * from t1 where c = 'two';
connection server1; connection server1;
drop table t1; drop table t1;

View File

@@ -41,11 +41,12 @@ drop table t1;
create table t1 (a int) engine=ndbcluster; create table t1 (a int) engine=ndbcluster;
insert into t1 value (2); insert into t1 value (2);
connection server1; connection server1;
# Currently a retry is required remotely ## Currently a retry is required remotely
--error 1412 #--error 1412
select * from t1; #select * from t1;
show warnings; #show warnings;
flush table t1; #flush table t1;
# Table definition change should be propagated automatically
select * from t1; select * from t1;
# Connect to server2 and use the tables from there # 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; connection server1;
select * from t1; select * from t1;
select * from t3; select * from t3;
show status like 'handler_discover%';
show tables like 't4'; show tables like 't4';
show status like 'handler_discover%';
show tables; show tables;
drop table t1, t2, t3, t4; drop table t1, t2, t3, t4;
connection server2;
drop table t1, t3, t4;
# End of 4.1 tests # End of 4.1 tests

View File

@@ -380,3 +380,25 @@ partition partf values less than (10000));
insert into t1 values(10,1); insert into t1 values(10,1);
select * from t1 where f1 = 10; select * from t1 where f1 = 10;
drop table t1; 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

View File

@@ -514,6 +514,28 @@ evex_open_event_table(THD *thd, enum thr_lock_type lock_type, TABLE **table)
SYNOPSIS SYNOPSIS
evex_db_find_event_aux() 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 thd Thread context
dbname Name of event's database dbname Name of event's database
rname Name of the event inside the db 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 int
evex_db_find_event_aux(THD *thd, const LEX_STRING dbname, evex_db_find_event_by_name(THD *thd, const LEX_STRING dbname,
const LEX_STRING ev_name, const LEX_STRING ev_name,
const LEX_STRING user_name, const LEX_STRING user_name,
TABLE *table) TABLE *table)
{ {
byte key[MAX_KEY_LENGTH]; 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)); 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")); 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) if (create_if_not)
{ {
@@ -848,7 +870,7 @@ db_update_event(THD *thd, event_timed *et, sp_name *new_name)
goto err; 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)) et->definer, table))
{ {
my_error(ER_EVENT_ALREADY_EXISTS, MYF(0), new_name->m_name.str); 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 overwrite the key and SE will tell us that it cannot find the already found
row (copied into record[1] later row (copied into record[1] later
*/ */
if (EVEX_KEY_NOT_FOUND == evex_db_find_event_aux(thd, et->dbname, et->name, if (EVEX_KEY_NOT_FOUND == evex_db_find_event_aux(thd, et, table))
et->definer, table))
{ {
my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), et->name.str); my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), et->name.str);
goto err; goto err;
@@ -943,7 +964,7 @@ db_find_event(THD *thd, sp_name *name, LEX_STRING *definer, event_timed **ett,
goto done; goto done;
} }
if ((ret= evex_db_find_event_aux(thd, name->m_db, name->m_name, *definer, if ((ret= evex_db_find_event_by_name(thd, name->m_db, name->m_name, *definer,
table))) table)))
{ {
my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), name->m_name.str); my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), name->m_name.str);
@@ -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) && if (!sortcmp_lex_string(*name, et->name, system_charset_info) &&
!sortcmp_lex_string(*db, et->dbname, 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")); DBUG_PRINT("evex_remove_from_cache", ("not running - free and delete"));
et->free_sp(); et->free_sp();
@@ -1239,7 +1260,7 @@ int db_drop_event(THD *thd, event_timed *et, bool drop_if_exists,
{ {
TABLE *table; TABLE *table;
Open_tables_state backup; Open_tables_state backup;
uint ret; int ret;
DBUG_ENTER("db_drop_event"); DBUG_ENTER("db_drop_event");
ret= EVEX_OPEN_TABLE_FAILED; ret= EVEX_OPEN_TABLE_FAILED;
@@ -1251,7 +1272,7 @@ int db_drop_event(THD *thd, event_timed *et, bool drop_if_exists,
goto done; 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]))) 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); 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);
}

View File

@@ -79,6 +79,8 @@ class event_timed
{ {
event_timed(const event_timed &); /* Prevent use of these */ event_timed(const event_timed &); /* Prevent use of these */
void operator=(event_timed &); void operator=(event_timed &);
my_bool in_spawned_thread;
ulong locked_by_thread_id;
my_bool running; my_bool running;
pthread_mutex_t LOCK_running; pthread_mutex_t LOCK_running;
@@ -117,12 +119,13 @@ public:
bool free_sphead_on_delete; bool free_sphead_on_delete;
uint flags;//all kind of purposes uint flags;//all kind of purposes
event_timed():running(0), status_changed(false), last_executed_changed(false), event_timed():in_spawned_thread(0),locked_by_thread_id(0),
expression(0), created(0), modified(0), running(0), status_changed(false),
on_completion(MYSQL_EVENT_ON_COMPLETION_DROP), 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), status(MYSQL_EVENT_ENABLED), sphead(0), sql_mode(0),
body_begin(0), dropped(false), free_sphead_on_delete(true), body_begin(0), dropped(false),
flags(0) free_sphead_on_delete(true), flags(0)
{ {
pthread_mutex_init(&this->LOCK_running, MY_MUTEX_INIT_FAST); pthread_mutex_init(&this->LOCK_running, MY_MUTEX_INIT_FAST);
@@ -200,11 +203,55 @@ public:
return ret; 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; delete sphead;
sphead= 0; 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, interval_type interval,
longlong expression); longlong expression);
int
evex_drop_db_events(THD *thd, char *db);
int int
init_events(); init_events();

View File

@@ -18,6 +18,11 @@
#include "event.h" #include "event.h"
#include "sp.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 Make this define DBUG_FAULTY_THR to be able to put breakpoints inside
@@ -49,7 +54,8 @@ static uint workers_count;
static int static int
evex_load_events_from_db(THD *thd); 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 TODO Andrey: Check for command line option whether to start
@@ -260,7 +266,8 @@ init_event_thread(THD* thd)
{ {
DBUG_ENTER("init_event_thread"); DBUG_ENTER("init_event_thread");
thd->client_capabilities= 0; 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; thd->security_ctx->host= (char*)my_localhost;
my_net_init(&thd->net, 0); my_net_init(&thd->net, 0);
thd->net.read_timeout = slave_net_timeout; thd->net.read_timeout = slave_net_timeout;
@@ -293,113 +300,25 @@ init_event_thread(THD* thd)
/* /*
The main scheduler thread. Inits the priority queue on start and This function waits till the time next event in the queue should be
destroys it on thread shutdown. Forks child threads for every event executed.
execution. Sleeps between thread forking and does not do a busy wait.
SYNOPSIS
event_executor_main()
arg - unused
NOTES
1. The host of the thead is my_localhost
2. thd->net is initted with NULL - no communication.
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;
*/ */
pthread_handler_t static int
event_executor_main(void *arg) executor_wait_till_next_event_exec(THD *thd)
{ {
THD *thd; /* needs to be first for thread_stack */
ulonglong iter_num= 0;
uint i=0, j=0;
my_ulonglong cnt= 0;
DBUG_ENTER("event_executor_main");
DBUG_PRINT("event_executor_main", ("EVEX thread started"));
// init memory root
init_alloc_root(&evex_mem_root, MEM_ROOT_BLOCK_SIZE, MEM_ROOT_PREALLOC);
// needs to call my_thread_init(), otherwise we get a coredump in DBUG_ stuff
my_thread_init();
if (sizeof(my_time_t) != sizeof(time_t))
{
sql_print_error("sizeof(my_time_t) != sizeof(time_t) ."
"The scheduler will not work correctly. Stopping.");
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");
goto err_no_thd;
}
thd->thread_stack = (char*)&thd; // remember where our stack is
pthread_detach_this_thread();
if (init_event_thread(thd))
goto err;
// make this thread invisible it has no vio -> show processlist won't see
thd->system_thread= 1;
VOID(pthread_mutex_lock(&LOCK_thread_count));
threads.append(thd);
thread_count++;
thread_running++;
VOID(pthread_mutex_unlock(&LOCK_thread_count));
DBUG_PRINT("EVEX main thread", ("Initing events_queuey"));
/*
eventually manifest that we are running, not to crashe because of
usage of non-initialized memory structures.
*/
VOID(pthread_mutex_lock(&LOCK_evex_running));
VOID(pthread_mutex_lock(&LOCK_event_arrays));
evex_queue_init(&EVEX_EQ_NAME);
VOID(pthread_mutex_unlock(&LOCK_event_arrays));
evex_is_running= true;
VOID(pthread_mutex_unlock(&LOCK_evex_running));
thd->security_ctx->user= my_strdup("event_scheduler", MYF(0));
if (evex_load_events_from_db(thd))
goto err;
evex_main_thread_id= thd->thread_id;
sql_print_information("Scheduler thread started");
while (!thd->killed)
{
TIME time_now;
my_time_t now;
event_timed *et; event_timed *et;
TIME time_now;
cnt++;
DBUG_PRINT("info", ("EVEX External Loop %d thd->k", cnt));
thd->proc_info = "Sleeping";
if (!event_executor_running_global_var)
{
sql_print_information("Scheduler asked to stop.");
break;
}
if (!evex_queue_num_elements(EVEX_EQ_NAME))
{
my_sleep(1000000);// sleep 1s
continue;
}
{
int t2sleep; 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 now let's see how much time to sleep, we know there is at least 1
element in the queue. element in the queue.
@@ -408,9 +327,10 @@ event_executor_main(void *arg)
if (!evex_queue_num_elements(EVEX_EQ_NAME)) if (!evex_queue_num_elements(EVEX_EQ_NAME))
{ {
VOID(pthread_mutex_unlock(&LOCK_event_arrays)); VOID(pthread_mutex_unlock(&LOCK_event_arrays));
continue; DBUG_RETURN(1);
} }
et= evex_queue_first_element(&EVEX_EQ_NAME, event_timed*); et= evex_queue_first_element(&EVEX_EQ_NAME, event_timed*);
DBUG_ASSERT(et);
if (et->status == MYSQL_EVENT_DISABLED) if (et->status == MYSQL_EVENT_DISABLED)
{ {
DBUG_PRINT("evex main thread",("Now it is disabled-exec no more")); DBUG_PRINT("evex main thread",("Now it is disabled-exec no more"));
@@ -420,12 +340,13 @@ event_executor_main(void *arg)
evex_queue_delete_element(&EVEX_EQ_NAME, 1);// 1 is top evex_queue_delete_element(&EVEX_EQ_NAME, 1);// 1 is top
VOID(pthread_mutex_unlock(&LOCK_event_arrays)); VOID(pthread_mutex_unlock(&LOCK_event_arrays));
sql_print_information("Event found disabled, dropping."); sql_print_information("Event found disabled, dropping.");
continue; DBUG_RETURN(1);
} }
DBUG_PRINT("evex main thread",("computing time to sleep till next exec")); DBUG_PRINT("evex main thread",("computing time to sleep till next exec"));
time((time_t *)&now); // set the internal clock of thd
my_tz_UTC->gmt_sec_to_TIME(&time_now, now); thd->end_time();
my_tz_UTC->gmt_sec_to_TIME(&time_now, thd->query_start());
t2sleep= evex_time_diff(&et->execute_at, &time_now); t2sleep= evex_time_diff(&et->execute_at, &time_now);
VOID(pthread_mutex_unlock(&LOCK_event_arrays)); VOID(pthread_mutex_unlock(&LOCK_event_arrays));
@@ -444,15 +365,152 @@ event_executor_main(void *arg)
my_sleep(1000000); 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
execution. Sleeps between thread forking and does not do a busy wait.
SYNOPSIS
event_executor_main()
arg - unused
NOTES
1. The host of the thead is my_localhost
2. thd->net is initted with NULL - no communication.
*/
pthread_handler_t
event_executor_main(void *arg)
{
THD *thd; /* needs to be first for thread_stack */
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"));
// init memory root
init_alloc_root(&evex_mem_root, MEM_ROOT_BLOCK_SIZE, MEM_ROOT_PREALLOC);
// needs to call my_thread_init(), otherwise we get a coredump in DBUG_ stuff
my_thread_init();
if (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("SCHEDULER: Cannot create THD for the main thread.");
goto err_no_thd;
}
thd->thread_stack = (char*)&thd; // remember where our stack is
pthread_detach_this_thread();
if (init_event_thread(thd))
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));
threads.append(thd);
thread_count++;
thread_running++;
VOID(pthread_mutex_unlock(&LOCK_thread_count));
DBUG_PRINT("EVEX main thread", ("Initing events_queue"));
/*
eventually manifest that we are running, not to crashe because of
usage of non-initialized memory structures.
*/
VOID(pthread_mutex_lock(&LOCK_evex_running));
VOID(pthread_mutex_lock(&LOCK_event_arrays));
evex_queue_init(&EVEX_EQ_NAME);
VOID(pthread_mutex_unlock(&LOCK_event_arrays));
evex_is_running= true;
VOID(pthread_mutex_unlock(&LOCK_evex_running));
thd->security_ctx->user= my_strdup("event_scheduler", MYF(0));
if (evex_load_events_from_db(thd))
goto finish;
evex_main_thread_id= thd->thread_id;
sql_print_information("SCHEDULER: Main thread started");
while (!thd->killed)
{
TIME time_now;
event_timed *et;
cnt++;
DBUG_PRINT("info", ("EVEX External Loop %d thd->k", cnt));
thd->proc_info = "Sleeping";
if (!event_executor_running_global_var) if (!event_executor_running_global_var)
{ {
sql_print_information("Scheduler asked to stop."); sql_print_information("SCHEDULER: Asked to stop.");
break; break;
} }
if (!evex_queue_num_elements(EVEX_EQ_NAME))
{
my_sleep(1000000);// sleep 1s
continue;
}
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)); 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)) 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")); DBUG_PRINT("evex main thread",("it's right time"));
if (et->status == MYSQL_EVENT_ENABLED) 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, DBUG_PRINT("evex main thread", ("[%10s] this exec at [%llu]", et->name.str,
TIME_to_ulonglong_datetime(&et->execute_at))); TIME_to_ulonglong_datetime(&et->execute_at)));
et->mark_last_executed(thd); et->mark_last_executed(thd);
if (et->compute_next_execution_time()) 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.", "Disabling after execution.",
et->dbname.str, et->name.str); et->dbname.str, et->name.str);
et->status= MYSQL_EVENT_DISABLED; et->status= MYSQL_EVENT_DISABLED;
@@ -490,13 +548,23 @@ event_executor_main(void *arg)
TIME_to_ulonglong_datetime(&et->execute_at))); TIME_to_ulonglong_datetime(&et->execute_at)));
et->update_fields(thd); et->update_fields(thd);
++iter_num;
DBUG_PRINT("info", (" Spawning a thread %d", iter_num));
#ifndef DBUG_FAULTY_THR #ifndef DBUG_FAULTY_THR
if (pthread_create(&th,&connection_attrib,event_executor_worker,(void*)et)) thread_safe_increment(workers_count, &LOCK_workers_count);
{ switch ((fork_ret_code= et->spawn_now(event_executor_worker))) {
sql_print_error("Problem while trying to create a thread"); case EVENT_EXEC_CANT_FORK:
UNLOCK_MUTEX_AND_BAIL_OUT(LOCK_event_arrays, err); 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 #else
event_executor_worker((void *) et); event_executor_worker((void *) et);
@@ -506,22 +574,21 @@ event_executor_main(void *arg)
et->flags |= EVENT_EXEC_NO_MORE; et->flags |= EVENT_EXEC_NO_MORE;
if ((et->flags & EVENT_EXEC_NO_MORE) || et->status == MYSQL_EVENT_DISABLED) 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 else
evex_queue_first_updated(&EVEX_EQ_NAME); evex_queue_first_updated(&EVEX_EQ_NAME);
} }
DBUG_PRINT("evex main thread",("unlocking")); DBUG_PRINT("evex main thread",("unlocking"));
VOID(pthread_mutex_unlock(&LOCK_event_arrays)); VOID(pthread_mutex_unlock(&LOCK_event_arrays));
}// while }// while
finish:
err:
// First manifest that this thread does not work and then destroy // First manifest that this thread does not work and then destroy
VOID(pthread_mutex_lock(&LOCK_evex_running)); VOID(pthread_mutex_lock(&LOCK_evex_running));
evex_is_running= false; evex_is_running= false;
evex_main_thread_id= 0; evex_main_thread_id= 0;
VOID(pthread_mutex_unlock(&LOCK_evex_running)); 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 TODO: A better will be with a conditional variable
@@ -530,21 +597,33 @@ err:
Read workers_count without lock, no need for locking. Read workers_count without lock, no need for locking.
In the worst case we have to wait 1sec more. In the worst case we have to wait 1sec more.
*/ */
while (workers_count) 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 my_sleep(1000000);// 1s
}
/* /*
LEX_STRINGs reside in the memory root and will be destroyed with it. First we free all objects ...
Hence no need of delete but only freeing of SP 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) for (i= 0; i < evex_queue_num_elements(EVEX_EQ_NAME); ++i)
{ {
event_timed *et= evex_queue_element(&EVEX_EQ_NAME, i, event_timed*); event_timed *et= evex_queue_element(&EVEX_EQ_NAME, i, event_timed*);
et->free_sp(); et->free_sp();
delete et; 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); evex_queue_destroy(&EVEX_EQ_NAME);
thd->proc_info = "Clearing"; thd->proc_info = "Clearing";
@@ -568,7 +647,7 @@ err_no_thd:
VOID(pthread_mutex_unlock(&LOCK_evex_running)); VOID(pthread_mutex_unlock(&LOCK_evex_running));
free_root(&evex_mem_root, MYF(0)); free_root(&evex_mem_root, MYF(0));
sql_print_information("Event scheduler stopped."); sql_print_information("SCHEDULER: Stopped.");
#ifndef DBUG_FAULTY_THR #ifndef DBUG_FAULTY_THR
my_thread_end(); my_thread_end();
@@ -595,9 +674,6 @@ event_executor_worker(void *event_void)
MEM_ROOT worker_mem_root; MEM_ROOT worker_mem_root;
DBUG_ENTER("event_executor_worker"); 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); 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_ ! 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; goto err_no_thd;
} }
thd->thread_stack = (char*)&thd; // remember where our stack is thd->thread_stack = (char*)&thd; // remember where our stack is
@@ -631,47 +707,25 @@ event_executor_worker(void *event_void)
thd= current_thd; thd= current_thd;
#endif #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; int ret;
DBUG_PRINT("info", (" EVEX EXECUTING event %s.%s [EXPR:%d]", sql_print_information("SCHEDULER: Executing event %s.%s of %s [EXPR:%d]",
event->dbname.str, event->name.str,(int) event->expression)); event->dbname.str, event->name.str,
sql_print_information(" EVEX EXECUTING event %s.%s [EXPR:%d]", event->definer.str, (int) event->expression);
event->dbname.str, event->name.str,(int) event->expression);
ret= event->execute(thd, &worker_mem_root); ret= event->execute(thd, &worker_mem_root);
sql_print_information(" EVEX EXECUTED event %s.%s [EXPR:%d]. RetCode=%d", evex_print_warnings(thd, event);
event->dbname.str, event->name.str, sql_print_information("SCHEDULER: Executed event %s.%s of %s [EXPR:%d]. "
(int) event->expression, ret); "RetCode=%d", event->dbname.str, event->name.str,
event->definer.str, (int) event->expression, ret);
if (ret == EVEX_COMPILE_ERROR) if (ret == EVEX_COMPILE_ERROR)
sql_print_information(" EVEX COMPILE ERROR for event %s.%s", sql_print_information("SCHEDULER: COMPILE ERROR for event %s.%s of",
event->dbname.str, event->name.str);
DBUG_PRINT("info", (" EVEX EXECUTED event %s.%s [EXPR:%d]. RetCode=%d",
event->dbname.str, event->name.str, event->dbname.str, event->name.str,
(int) event->expression, ret)); event->definer.str);
}
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;
} }
event->spawn_thread_finish(thd);
thd->db= 0;
err: err:
VOID(pthread_mutex_lock(&LOCK_thread_count)); VOID(pthread_mutex_lock(&LOCK_thread_count));
@@ -699,10 +753,7 @@ err:
err_no_thd: err_no_thd:
free_root(&worker_mem_root, MYF(0)); free_root(&worker_mem_root, MYF(0));
thread_safe_decrement(workers_count, &LOCK_workers_count);
VOID(pthread_mutex_lock(&LOCK_workers_count));
--workers_count;
VOID(pthread_mutex_unlock(&LOCK_workers_count));
#ifndef DBUG_FAULTY_THR #ifndef DBUG_FAULTY_THR
my_thread_end(); my_thread_end();
@@ -743,7 +794,7 @@ evex_load_events_from_db(THD *thd)
if ((ret= evex_open_event_table(thd, TL_READ, &table))) 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); 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))) 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"); "Table probably corrupted");
goto end; goto end;
} }
@@ -779,7 +830,7 @@ evex_load_events_from_db(THD *thd)
if ((ret= et->compile(thd, &evex_mem_root))) 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); et->dbname.str, et->name.str);
goto end; goto end;
} }
@@ -787,8 +838,8 @@ evex_load_events_from_db(THD *thd)
// let's find when to be executed // let's find when to be executed
if (et->compute_next_execution_time()) if (et->compute_next_execution_time())
{ {
sql_print_error("Error while computing execution time of %s.%s. Skipping", sql_print_error("SCHEDULER: Error while computing execution time of %s.%s."
et->dbname.str, et->name.str); " Skipping", et->dbname.str, et->name.str);
continue; continue;
} }
@@ -809,7 +860,7 @@ end:
thd->version--; // Force close to free memory thd->version--; // Force close to free memory
close_thread_tables(thd); 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_PRINT("info", ("Status code %d. Loaded %d event(s)", ret, count));
DBUG_RETURN(ret); DBUG_RETURN(ret);
@@ -849,3 +900,69 @@ sys_var_event_executor::update(THD *thd, set_var *var)
DBUG_RETURN(0); 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<MYSQL_ERROR> 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);
}

View File

@@ -19,6 +19,10 @@
#include "mysql_priv.h" #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 EVEX_USE_QUEUE
#define UNLOCK_MUTEX_AND_BAIL_OUT(__mutex, __label) \ #define UNLOCK_MUTEX_AND_BAIL_OUT(__mutex, __label) \
@@ -32,9 +36,9 @@ int
my_time_compare(TIME *a, TIME *b); my_time_compare(TIME *a, TIME *b);
int int
evex_db_find_event_aux(THD *thd, const LEX_STRING dbname, evex_db_find_event_by_name(THD *thd, const LEX_STRING dbname,
const LEX_STRING rname, const LEX_STRING ev_name,
const LEX_STRING definer, const LEX_STRING user_name,
TABLE *table); TABLE *table);
int int

View File

@@ -412,21 +412,30 @@ event_timed::init_definer(THD *thd)
{ {
DBUG_ENTER("event_timed::init_definer"); 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.str= strdup_root(thd->mem_root, thd->security_ctx->priv_user);
definer_user.length= strlen(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.str= strdup_root(thd->mem_root, thd->security_ctx->priv_host);
definer_host.length= strlen(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.length= definer_user.length + definer_host.length + 1;
definer.str= alloc_root(thd->mem_root, definer.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); memcpy(definer.str, definer_user.str, definer_user.length);
definer.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, memcpy(definer.str + definer_user.length + 1, definer_host.str,
definer_host.length); definer_host.length);
definer.str[definer.length]= '\0'; definer.str[definer.length]= '\0';
DBUG_PRINT("info",("definer initted"));
DBUG_RETURN(0); DBUG_RETURN(0);
} }
@@ -908,7 +917,7 @@ event_timed::drop(THD *thd)
Saves status and last_executed_at to the disk if changed. Saves status and last_executed_at to the disk if changed.
SYNOPSIS SYNOPSIS
event_timed::drop() event_timed::update_fields()
thd - thread context thd - thread context
RETURN VALUE 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; goto done;
store_record(table,record[1]); store_record(table,record[1]);
@@ -1061,6 +1070,7 @@ event_timed::get_create_event(THD *thd, String *buf)
RETURNS RETURNS
0 success 0 success
-99 No rights on this.dbname.str
-100 event in execution (parallel execution is impossible) -100 event in execution (parallel execution is impossible)
others retcodes of sp_head::execute_procedure() others retcodes of sp_head::execute_procedure()
*/ */
@@ -1068,10 +1078,14 @@ event_timed::get_create_event(THD *thd, String *buf)
int int
event_timed::execute(THD *thd, MEM_ROOT *mem_root) event_timed::execute(THD *thd, MEM_ROOT *mem_root)
{ {
List<Item> empty_item_list; Security_context *save_ctx;
/* this one is local and not needed after exec */
Security_context security_ctx;
int ret= 0; int ret= 0;
DBUG_ENTER("event_timed::execute"); 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)); VOID(pthread_mutex_lock(&this->LOCK_running));
if (running) if (running)
@@ -1082,30 +1096,110 @@ event_timed::execute(THD *thd, MEM_ROOT *mem_root)
running= true; running= true;
VOID(pthread_mutex_unlock(&this->LOCK_running)); VOID(pthread_mutex_unlock(&this->LOCK_running));
// TODO Andrey : make this as member variable and delete in destructor DBUG_PRINT("info", ("master_access=%d db_access=%d",
empty_item_list.empty(); 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))) if (!sphead && (ret= compile(thd, mem_root)))
goto done; goto done;
/* 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<Item> empty_item_list;
empty_item_list.empty();
ret= sphead->execute_procedure(thd, &empty_item_list); 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)); VOID(pthread_mutex_lock(&this->LOCK_running));
running= false; running= false;
VOID(pthread_mutex_unlock(&this->LOCK_running)); VOID(pthread_mutex_unlock(&this->LOCK_running));
done: 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) if (mem_root && sphead)
{ {
delete sphead; delete sphead;
sphead= 0; sphead= 0;
} }
DBUG_PRINT("info", (" EVEX EXECUTED event %s.%s [EXPR:%d]. RetCode=%d",
dbname.str, name.str, (int) expression, ret));
DBUG_RETURN(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 Compiles an event before it's execution. Compiles the anonymous
sp_head object held by the event 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; MEM_ROOT *tmp_mem_root= 0;
LEX *old_lex= thd->lex, lex; LEX *old_lex= thd->lex, lex;
char *old_db; char *old_db;
int old_db_length;
event_timed *ett; event_timed *ett;
sp_name *spn; sp_name *spn;
char *old_query; char *old_query;
@@ -1161,7 +1256,9 @@ event_timed::compile(THD *thd, MEM_ROOT *mem_root)
old_query_len= thd->query_length; old_query_len= thd->query_length;
old_query= thd->query; old_query= thd->query;
old_db= thd->db; old_db= thd->db;
old_db_length= thd->db_length;
thd->db= dbname.str; thd->db= dbname.str;
thd->db_length= dbname.length;
get_create_event(thd, &show_create); get_create_event(thd, &show_create);
@@ -1227,3 +1324,135 @@ done:
DBUG_RETURN(ret); 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;
}

View File

@@ -34,6 +34,7 @@
#include <ndbapi/NdbIndexStat.hpp> #include <ndbapi/NdbIndexStat.hpp>
#include "ha_ndbcluster_binlog.h" #include "ha_ndbcluster_binlog.h"
#include "ha_ndbcluster_tables.h"
#ifdef ndb_dynamite #ifdef ndb_dynamite
#undef assert #undef assert
@@ -476,8 +477,7 @@ ha_ndbcluster::invalidate_dictionary_cache(TABLE_SHARE *share, Ndb *ndb,
#ifdef HAVE_NDB_BINLOG #ifdef HAVE_NDB_BINLOG
char key[FN_REFLEN]; char key[FN_REFLEN];
strxnmov(key, FN_LEN-1, mysql_data_home, "/", build_table_filename(key, sizeof(key), dbname, tabname, "");
dbname, "/", tabname, NullS);
DBUG_PRINT("info", ("Getting ndbcluster mutex")); DBUG_PRINT("info", ("Getting ndbcluster mutex"));
pthread_mutex_lock(&ndbcluster_mutex); pthread_mutex_lock(&ndbcluster_mutex);
NDB_SHARE *ndb_share= (NDB_SHARE*)hash_search(&ndbcluster_open_tables, NDB_SHARE *ndb_share= (NDB_SHARE*)hash_search(&ndbcluster_open_tables,
@@ -4230,16 +4230,14 @@ int ha_ndbcluster::create(const char *name,
NDBCOL col; NDBCOL col;
uint pack_length, length, i, pk_length= 0; uint pack_length, length, i, pk_length= 0;
const void *data, *pack_data; const void *data, *pack_data;
char name2[FN_HEADLEN];
bool create_from_engine= (info->table_options & HA_OPTION_CREATE_FROM_ENGINE); bool create_from_engine= (info->table_options & HA_OPTION_CREATE_FROM_ENGINE);
DBUG_ENTER("ha_ndbcluster::create"); DBUG_ENTER("ha_ndbcluster::create");
DBUG_PRINT("enter", ("name: %s", name)); DBUG_PRINT("enter", ("name: %s", name));
strcpy(name2, name); DBUG_ASSERT(*fn_rext((char*)name) == 0);
DBUG_ASSERT(*fn_rext((char*)name2) == 0); set_dbname(name);
set_dbname(name2); set_tabname(name);
set_tabname(name2);
table= form; table= form;
if (create_from_engine) if (create_from_engine)
@@ -4252,7 +4250,7 @@ int ha_ndbcluster::create(const char *name,
if ((my_errno= write_ndb_file(name))) if ((my_errno= write_ndb_file(name)))
DBUG_RETURN(my_errno); DBUG_RETURN(my_errno);
#ifdef HAVE_NDB_BINLOG #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); m_dbname, m_tabname, FALSE);
#endif /* HAVE_NDB_BINLOG */ #endif /* HAVE_NDB_BINLOG */
DBUG_RETURN(my_errno); 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... First make sure we get a "fresh" share here, not an old trailing one...
*/ */
{ {
const char *key= name2; uint length= (uint) strlen(name);
uint length= (uint) strlen(key);
if ((share= (NDB_SHARE*) hash_search(&ndbcluster_open_tables, if ((share= (NDB_SHARE*) hash_search(&ndbcluster_open_tables,
(byte*) key, length))) (byte*) name, length)))
handle_trailing_share(share); handle_trailing_share(share);
} }
/* /*
get a new 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 */ /* my_errno is set */
} }
pthread_mutex_unlock(&ndbcluster_mutex); pthread_mutex_unlock(&ndbcluster_mutex);
@@ -4421,6 +4419,12 @@ int ha_ndbcluster::create(const char *name,
const NDBTAB *t= dict->getTable(m_tabname); const NDBTAB *t= dict->getTable(m_tabname);
String event_name(INJECTOR_EVENT_LEN); String event_name(INJECTOR_EVENT_LEN);
ndb_rep_event_name(&event_name,m_dbname,m_tabname); 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 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) if (ndbcluster_create_event(ndb, t, event_name.c_ptr(), share) < 0)
{ {
/* this is only a serious error if the binlog is on */ /* 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, push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_ERROR,
ER_GET_ERRMSG, ER(ER_GET_ERRMSG), 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", sql_print_information("NDB Binlog: CREATE TABLE Event: %s",
event_name.c_ptr()); 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) ndbcluster_create_event_ops(share, t, event_name.c_ptr()) < 0)
{ {
sql_print_error("NDB Binlog: FAILED CREATE TABLE event operations." sql_print_error("NDB Binlog: FAILED CREATE TABLE event operations."
" Event: %s", name2); " Event: %s", name);
/* a warning has been issued to the client */ /* a warning has been issued to the client */
} }
if (share && !ndb_binlog_running) if (share && !do_event_op)
share->flags|= NSF_NO_BINLOG; share->flags|= NSF_NO_BINLOG;
ndbcluster_log_schema_op(current_thd, share, ndbcluster_log_schema_op(current_thd, share,
current_thd->query, current_thd->query_length, 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()); ERR_RETURN(dict->getNdbError());
} }
#ifdef HAVE_NDB_BINLOG #ifdef HAVE_NDB_BINLOG
NDB_SHARE *share= 0; NDB_SHARE *share= get_share(from, 0, false);
if (ndb_binlog_running && if (share)
(share= get_share(from, 0, false)))
{ {
int r= rename_share(share, to); int r= rename_share(share, to);
DBUG_ASSERT(r == 0); DBUG_ASSERT(r == 0);
@@ -4795,7 +4798,7 @@ int ha_ndbcluster::rename_table(const char *from, const char *to)
if (ndb_extra_logging) if (ndb_extra_logging)
sql_print_information("NDB Binlog: RENAME Event: %s", sql_print_information("NDB Binlog: RENAME Event: %s",
event_name.c_ptr()); event_name.c_ptr());
if (share) if (share && ndb_binlog_running)
{ {
if (ndbcluster_create_event_ops(share, ndbtab, if (ndbcluster_create_event_ops(share, ndbtab,
event_name.c_ptr()) < 0) event_name.c_ptr()) < 0)
@@ -5319,7 +5322,7 @@ int ndbcluster_discover(THD* thd, const char *db, const char *name,
NDBDICT* dict= ndb->getDictionary(); NDBDICT* dict= ndb->getDictionary();
dict->set_local_table_data_size(sizeof(Ndb_local_table_statistics)); dict->set_local_table_data_size(sizeof(Ndb_local_table_statistics));
dict->invalidateTable(name); 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); NDB_SHARE *share= get_share(key, 0, false);
if (share && get_ndb_share_state(share) == NSS_ALTERED) 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 // Drop any tables belonging to database
char full_path[FN_REFLEN]; char full_path[FN_REFLEN];
char *tmp= strxnmov(full_path, FN_REFLEN-1, share_prefix, dbname, "/", char *tmp= full_path +
NullS); build_table_filename(full_path, sizeof(full_path), dbname, "", "");
ndb->setDatabaseName(dbname); ndb->setDatabaseName(dbname);
List_iterator_fast<char> it(drop_list); List_iterator_fast<char> it(drop_list);
while ((tabname=it++)) 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)) if (ha_ndbcluster::delete_table(0, ndb, full_path, dbname, tabname))
{ {
const NdbError err= dict->getNdbError(); const NdbError err= dict->getNdbError();
@@ -5552,14 +5556,16 @@ int ndbcluster_find_all_files(THD *thd)
continue; continue;
/* check if database exists */ /* check if database exists */
char *end= strxnmov(key, FN_LEN-1, mysql_data_home, "/", char *end= key +
elmt.database, NullS); build_table_filename(key, sizeof(key), elmt.database, "", "");
if (my_access(key, F_OK)) if (my_access(key, F_OK))
{ {
/* no such database defined, skip table */ /* no such database defined, skip table */
continue; 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; const void *data= 0, *pack_data= 0;
uint length, pack_length; uint length, pack_length;
int discover= 0; 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 // File is not in NDB, check for .ndb file with this name
(void)strxnmov(name, FN_REFLEN-1, build_table_filename(name, sizeof(name), db, file_name, ha_ndb_ext);
mysql_data_home,"/",db,"/",file_name,ha_ndb_ext,NullS);
DBUG_PRINT("info", ("Check access for %s", name)); 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)); DBUG_PRINT("info", ("%s did not exist on disk", name));
// .ndb file did not exist on disk, another table type // .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 #ifdef HAVE_NDB_BINLOG
/* setup logging to binlog for all discovered tables */ /* setup logging to binlog for all discovered tables */
{ {
char *end, *end1= char *end, *end1= name +
strxnmov(name, sizeof(name), mysql_data_home, "/", db, "/", NullS); build_table_filename(name, sizeof(name), db, "", "");
for (i= 0; i < ok_tables.records; i++) for (i= 0; i < ok_tables.records; i++)
{ {
file_name= (char*)hash_element(&ok_tables, 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); pthread_mutex_lock(&LOCK_open);
ndbcluster_create_binlog_setup(ndb, name, end-name, ndbcluster_create_binlog_setup(ndb, name, end-name,
db, file_name, TRUE); 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); file_name= hash_element(&ndb_tables, i);
if (!hash_search(&ok_tables, file_name, strlen(file_name))) if (!hash_search(&ok_tables, file_name, strlen(file_name)))
{ {
strxnmov(name, sizeof(name)-1, build_table_filename(name, sizeof(name), db, file_name, reg_ext);
mysql_data_home, "/", db, "/", file_name, reg_ext, NullS); if (my_access(name, F_OK))
if (access(name, F_OK))
{ {
DBUG_PRINT("info", ("%s must be discovered", file_name)); DBUG_PRINT("info", ("%s must be discovered", file_name));
// File is in list of ndb tables and not in ok_tables // 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; NDB_SHARE *share;
DBUG_ENTER("ndb_get_commitcount"); 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)); DBUG_PRINT("enter", ("name: %s", name));
pthread_mutex_lock(&ndbcluster_mutex); pthread_mutex_lock(&ndbcluster_mutex);
if (!(share=(NDB_SHARE*) hash_search(&ndbcluster_open_tables, if (!(share=(NDB_SHARE*) hash_search(&ndbcluster_open_tables,
@@ -6655,6 +6660,8 @@ static int rename_share(NDB_SHARE *share, const char *new_key)
("db.tablename: %s.%s use_count: %d commit_count: %d", ("db.tablename: %s.%s use_count: %d commit_count: %d",
share->db, share->table_name, share->db, share->table_name,
share->use_count, share->commit_count)); share->use_count, share->commit_count));
if (share->table)
{
DBUG_PRINT("rename_share", DBUG_PRINT("rename_share",
("table->s->db.table_name: %s.%s", ("table->s->db.table_name: %s.%s",
share->table->s->db.str, share->table->s->table_name.str)); share->table->s->db.str, share->table->s->table_name.str));
@@ -6666,6 +6673,7 @@ static int rename_share(NDB_SHARE *share, const char *new_key)
share->table->s->table_name.str= share->table_name; share->table->s->table_name.str= share->table_name;
share->table->s->table_name.length= strlen(share->table_name); share->table->s->table_name.length= strlen(share->table_name);
} }
}
/* else rename will be handled when the ALTER event comes */ /* else rename will be handled when the ALTER event comes */
share->old_names= old_key; share->old_names= old_key;
// ToDo free old_names after ALTER EVENT // ToDo free old_names after ALTER EVENT

View File

@@ -237,10 +237,33 @@ void ndbcluster_binlog_init_share(NDB_SHARE *share, TABLE *_table)
{ {
THD *thd= current_thd; THD *thd= current_thd;
MEM_ROOT *mem_root= &share->mem_root; MEM_ROOT *mem_root= &share->mem_root;
int do_event_op= ndb_binlog_running;
share->op= 0; share->op= 0;
share->table= 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) if (_table)
{ {
@@ -315,21 +338,6 @@ void ndbcluster_binlog_init_share(NDB_SHARE *share, TABLE *_table)
share->ndb_value[1]= (NdbValue*) share->ndb_value[1]= (NdbValue*)
alloc_root(mem_root, sizeof(NdbValue) * table->s->fields alloc_root(mem_root, sizeof(NdbValue) * table->s->fields
+1 /*extra for hidden key*/); +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) if (table->s->primary_key == MAX_KEY)
share->flags|= NSF_HIDDEN_PK; share->flags|= NSF_HIDDEN_PK;
if (table->s->blob_fields != 0) 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 if so, remove it since there is none in Ndb
*/ */
{ {
strxnmov(buf, sizeof(buf), build_table_filename(buf, sizeof(buf),
mysql_data_home, NDB_REP_DB, NDB_APPLY_TABLE, reg_ext);
"/" NDB_REP_DB "/" NDB_APPLY_TABLE,
reg_ext, NullS);
unpack_filename(buf,buf);
my_delete(buf, MYF(0)); 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 if so, remove it since there is none in Ndb
*/ */
{ {
strxnmov(buf, sizeof(buf), build_table_filename(buf, sizeof(buf),
mysql_data_home, NDB_REP_DB, NDB_SCHEMA_TABLE, reg_ext);
"/" NDB_REP_DB "/" NDB_SCHEMA_TABLE,
reg_ext, NullS);
unpack_filename(buf,buf);
my_delete(buf, MYF(0)); my_delete(buf, MYF(0));
} }
@@ -929,8 +931,7 @@ int ndbcluster_log_schema_op(THD *thd, NDB_SHARE *share,
if (get_a_share) if (get_a_share)
{ {
char key[FN_REFLEN]; char key[FN_REFLEN];
(void)strxnmov(key, FN_REFLEN, share_prefix, db, build_table_filename(key, sizeof(key), db, table_name, "");
"/", table_name, NullS);
share= get_share(key, 0, false, false); share= get_share(key, 0, false, false);
} }
@@ -1358,6 +1359,7 @@ ndb_binlog_thread_handle_schema_event(THD *thd, Ndb *ndb,
switch (ev_type) switch (ev_type)
{ {
case NDBEVENT::TE_UPDATE: case NDBEVENT::TE_UPDATE:
/* fall through */
case NDBEVENT::TE_INSERT: case NDBEVENT::TE_INSERT:
{ {
Cluster_replication_schema *schema= (Cluster_replication_schema *) 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: case SOT_DROP_TABLE:
/* binlog dropping table after any table operations */ /* binlog dropping table after any table operations */
if (ndb_binlog_running)
post_epoch_log_list->push_back(schema, mem_root); post_epoch_log_list->push_back(schema, mem_root);
log_query= 0; log_query= 0;
break; break;
case SOT_RENAME_TABLE: case SOT_RENAME_TABLE:
/* fall through */ /* fall through */
case SOT_ALTER_TABLE: case SOT_ALTER_TABLE:
/* fall through */ if (ndb_binlog_running)
if (!ndb_binlog_running)
{ {
log_query= 1; log_query= 1;
break; /* discovery will be handled by binlog */ break; /* discovery will be handled by binlog */
} }
/* fall through */ /* fall through */
case SOT_CREATE_TABLE: case SOT_CREATE_TABLE:
/* fall through */
pthread_mutex_lock(&LOCK_open); pthread_mutex_lock(&LOCK_open);
if (ndb_create_table_from_engine(thd, schema->db, schema->name)) if (ndb_create_table_from_engine(thd, schema->db, schema->name))
{ {
@@ -1407,6 +1408,7 @@ ndb_binlog_thread_handle_schema_event(THD *thd, Ndb *ndb,
TRUE, /* print error */ TRUE, /* print error */
TRUE); /* don't binlog the query */ TRUE); /* don't binlog the query */
/* binlog dropping database after any table operations */ /* binlog dropping database after any table operations */
if (ndb_binlog_running)
post_epoch_log_list->push_back(schema, mem_root); post_epoch_log_list->push_back(schema, mem_root);
log_query= 0; log_query= 0;
break; break;
@@ -1422,8 +1424,8 @@ ndb_binlog_thread_handle_schema_event(THD *thd, Ndb *ndb,
case SOT_CLEAR_SLOCK: case SOT_CLEAR_SLOCK:
{ {
char key[FN_REFLEN]; char key[FN_REFLEN];
(void)strxnmov(key, FN_REFLEN, share_prefix, schema->db, build_table_filename(key, sizeof(key),
"/", schema->name, NullS); schema->db, schema->name, "");
NDB_SHARE *share= get_share(key, 0, false, false); NDB_SHARE *share= get_share(key, 0, false, false);
if (share) 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; char *thd_db_save= thd->db;
thd->db= schema->db; thd->db= schema->db;
@@ -1752,6 +1754,7 @@ int ndbcluster_create_binlog_setup(Ndb *ndb, const char *key,
const char *table_name, const char *table_name,
my_bool share_may_exist) my_bool share_may_exist)
{ {
int do_event_op= ndb_binlog_running;
DBUG_ENTER("ndbcluster_create_binlog_setup"); DBUG_ENTER("ndbcluster_create_binlog_setup");
DBUG_PRINT("enter",("key: %s key_len: %d %s.%s share_may_exist: %d", DBUG_PRINT("enter",("key: %s key_len: %d %s.%s share_may_exist: %d",
key, key_len, db, table_name, share_may_exist)); 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); "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; share->flags|= NSF_NO_BINLOG;
pthread_mutex_unlock(&ndbcluster_mutex); pthread_mutex_unlock(&ndbcluster_mutex);

View File

@@ -360,7 +360,7 @@ int ha_partition::ha_initialise()
other parameters are calculated on demand. other parameters are calculated on demand.
HA_FILE_BASED is always set for partition handler since we use a HA_FILE_BASED is always set for partition handler since we use a
special file for handling names of partitions, engine types. 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. HA_CAN_INSERT_DELAYED is disabled until further investigated.
*/ */
m_table_flags= m_file[0]->table_flags(); m_table_flags= m_file[0]->table_flags();
@@ -383,8 +383,8 @@ int ha_partition::ha_initialise()
m_pkey_is_clustered= FALSE; m_pkey_is_clustered= FALSE;
m_table_flags&= file->table_flags(); m_table_flags&= file->table_flags();
} while (*(++file_array)); } while (*(++file_array));
m_table_flags&= ~(HA_CAN_GEOMETRY & HA_CAN_FULLTEXT & m_table_flags&= ~(HA_CAN_GEOMETRY | HA_CAN_FULLTEXT | HA_DUPP_POS |
HA_CAN_SQL_HANDLER & HA_CAN_INSERT_DELAYED); HA_CAN_SQL_HANDLER | HA_CAN_INSERT_DELAYED);
m_table_flags|= HA_FILE_BASED | HA_REC_NOT_IN_SEQ; m_table_flags|= HA_FILE_BASED | HA_REC_NOT_IN_SEQ;
DBUG_RETURN(0); DBUG_RETURN(0);
} }
@@ -1937,7 +1937,8 @@ bool ha_partition::create_handler_file(const char *name)
name_buffer_ptr+= name_add(name_buffer_ptr, name_buffer_ptr+= name_add(name_buffer_ptr,
part_name, part_name,
subpart_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++; engine_array++;
} }
} }
@@ -1954,7 +1955,7 @@ bool ha_partition::create_handler_file(const char *name)
Create and write and close file Create and write and close file
to be used at open, delete_table and rename_table 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, if ((file= my_create(file_name, CREATE_MODE, O_RDWR | O_TRUNC,
MYF(MY_WME))) >= 0) 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_UPDATE:
case HA_EXTRA_PREPARE_FOR_DELETE: case HA_EXTRA_PREPARE_FOR_DELETE:
case HA_EXTRA_FORCE_REOPEN: case HA_EXTRA_FORCE_REOPEN:
case HA_EXTRA_FLUSH_CACHE:
{ {
if (m_myisam) if (m_myisam)
DBUG_RETURN(loop_extra(operation)); DBUG_RETURN(loop_extra(operation));

View File

@@ -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)); DBUG_EXPLAIN_INITIAL(buf, sizeof(buf));
else else
DBUG_EXPLAIN(buf, sizeof(buf)); DBUG_EXPLAIN(buf, sizeof(buf));
(byte*) thd->strdup(buf); return (byte*) thd->strdup(buf);
} }
/**************************************************************************** /****************************************************************************

View File

@@ -20,6 +20,7 @@
#include "mysql_priv.h" #include "mysql_priv.h"
#include <mysys_err.h> #include <mysys_err.h>
#include "sp.h" #include "sp.h"
#include "event.h"
#include <my_dir.h> #include <my_dir.h>
#include <m_ctype.h> #include <m_ctype.h>
#ifdef __WIN__ #ifdef __WIN__
@@ -870,6 +871,7 @@ bool mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent)
exit: exit:
(void)sp_drop_db_routines(thd, db); /* QQ Ignore errors for now */ (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); start_waiting_global_read_lock(thd);
/* /*
If this database was the client's selected database, we silently change the If this database was the client's selected database, we silently change the

View File

@@ -211,8 +211,13 @@ void push_warning_printf(THD *thd, MYSQL_ERROR::enum_warning_level level,
TRUE Error sending data to client TRUE Error sending data to client
*/ */
static const char *warning_level_names[]= {"Note", "Warning", "Error", "?"}; LEX_STRING warning_level_names[]=
static int warning_level_length[]= { 4, 7, 5, 1 }; {
{(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) 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) if (idx > unit->select_limit_cnt)
break; break;
protocol->prepare_for_resend(); protocol->prepare_for_resend();
protocol->store(warning_level_names[err->level], protocol->store(warning_level_names[err->level].str,
warning_level_length[err->level], system_charset_info); warning_level_names[err->level].length, system_charset_info);
protocol->store((uint32) err->code); protocol->store((uint32) err->code);
protocol->store(err->msg, strlen(err->msg), system_charset_info); protocol->store(err->msg, strlen(err->msg), system_charset_info);
if (protocol->write()) if (protocol->write())

View File

@@ -191,10 +191,8 @@ public:
int flush() const; int flush() const;
private: private:
STATIC_CONST( MAX_FILE_NAME_SIZE = 128 );
FILE* m_file; FILE* m_file;
char m_fileName[MAX_FILE_NAME_SIZE]; char m_fileName[PATH_MAX];
const char* m_fileMode; const char* m_fileMode;
/* Prohibit */ /* Prohibit */
File_class (const File_class& aCopy); File_class (const File_class& aCopy);

View File

@@ -64,11 +64,11 @@ NdbConfig_NdbCfgName(int with_ndb_home){
int len= 0; int len= 0;
if (with_ndb_home) { if (with_ndb_home) {
buf= NdbConfig_AllocHomePath(128); buf= NdbConfig_AllocHomePath(PATH_MAX);
len= strlen(buf); len= strlen(buf);
} else } else
buf= NdbMem_Allocate(128); buf= NdbMem_Allocate(PATH_MAX);
basestring_snprintf(buf+len, 128, "Ndb.cfg"); basestring_snprintf(buf+len, PATH_MAX, "Ndb.cfg");
return buf; return buf;
} }
@@ -90,56 +90,56 @@ char *get_prefix_buf(int len, int node_id)
char* char*
NdbConfig_ErrorFileName(int node_id){ 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); int len= strlen(buf);
basestring_snprintf(buf+len, 128, "_error.log"); basestring_snprintf(buf+len, PATH_MAX, "_error.log");
return buf; return buf;
} }
char* char*
NdbConfig_ClusterLogFileName(int node_id){ 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); int len= strlen(buf);
basestring_snprintf(buf+len, 128, "_cluster.log"); basestring_snprintf(buf+len, PATH_MAX, "_cluster.log");
return buf; return buf;
} }
char* char*
NdbConfig_SignalLogFileName(int node_id){ 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); int len= strlen(buf);
basestring_snprintf(buf+len, 128, "_signal.log"); basestring_snprintf(buf+len, PATH_MAX, "_signal.log");
return buf; return buf;
} }
char* char*
NdbConfig_TraceFileName(int node_id, int file_no){ 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); 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; return buf;
} }
char* char*
NdbConfig_NextTraceFileName(int node_id){ 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); int len= strlen(buf);
basestring_snprintf(buf+len, 128, "_trace.log.next"); basestring_snprintf(buf+len, PATH_MAX, "_trace.log.next");
return buf; return buf;
} }
char* char*
NdbConfig_PidFileName(int node_id){ 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); int len= strlen(buf);
basestring_snprintf(buf+len, 128, ".pid"); basestring_snprintf(buf+len, PATH_MAX, ".pid");
return buf; return buf;
} }
char* char*
NdbConfig_StdoutFileName(int node_id){ 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); int len= strlen(buf);
basestring_snprintf(buf+len, 128, "_out.log"); basestring_snprintf(buf+len, PATH_MAX, "_out.log");
return buf; return buf;
} }

View File

@@ -67,7 +67,7 @@ File_class::File_class(const char* aFileName, const char* mode) :
m_file(NULL), m_file(NULL),
m_fileMode(mode) m_fileMode(mode)
{ {
BaseString::snprintf(m_fileName, MAX_FILE_NAME_SIZE, aFileName); BaseString::snprintf(m_fileName, PATH_MAX, aFileName);
} }
bool bool
@@ -83,7 +83,7 @@ File_class::open(const char* aFileName, const char* mode)
/** /**
* Only copy if it's not the same string * 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; m_fileMode = mode;
bool rc = true; bool rc = true;

View File

@@ -1049,7 +1049,10 @@ void stmt_fetch_close(Stmt_fetch *fetch)
reading from the rest. 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; unsigned open_statements= query_count;
int rc, error_count= 0; 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]); 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) while (open_statements)
{ {
for (fetch= fetch_array; fetch < fetch_array + query_count; ++fetch) 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)); 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; DBUG_VOID_RETURN;
} }
@@ -11880,7 +11893,8 @@ static void test_cursors_with_union()
"SELECT t1.id FROM t1 WHERE t1.id < 5" "SELECT t1.id FROM t1 WHERE t1.id < 5"
}; };
myheader("test_cursors_with_union"); 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); 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 */ /* Bug #16144: mysql_stmt_attr_get type error */
static void test_bug16144() static void test_bug16144()
@@ -15072,6 +15101,7 @@ static struct my_tests_st my_tests[]= {
{ "test_opt_reconnect", test_opt_reconnect }, { "test_opt_reconnect", test_opt_reconnect },
{ "test_bug15510", test_bug15510}, { "test_bug15510", test_bug15510},
{ "test_bug12744", test_bug12744 }, { "test_bug12744", test_bug12744 },
{ "test_bug16143", test_bug16143 },
{ "test_bug16144", test_bug16144 }, { "test_bug16144", test_bug16144 },
{ "test_bug15613", test_bug15613 }, { "test_bug15613", test_bug15613 },
{ 0, 0 } { 0, 0 }