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