Commit aa1de737 authored by Nisha Gopalakrishnan's avatar Nisha Gopalakrishnan

Bug#20094067: BACKPORT BUG#19683834 TO 5.5 AND 5.6

Backporting the patch and the test case fixed as part
of BUG#16041903 and BUG#19683834 respectively.
parent 7a408dbd
...@@ -7809,3 +7809,111 @@ Warnings: ...@@ -7809,3 +7809,111 @@ Warnings:
Error 1424 Recursive stored functions and triggers are not allowed. Error 1424 Recursive stored functions and triggers are not allowed.
Error 1305 FUNCTION test.f1 does not exist Error 1305 FUNCTION test.f1 does not exist
DROP FUNCTION f1; DROP FUNCTION f1;
#
# BUG 16041903: CONTINUE HANDLER NOT INVOKED
# IN A STORED FUNCTION AFTER A LOCK WAIT TIMEOUT
#
# Save and set lock wait timeout
SET @lock_wait_timeout_saved= @@lock_wait_timeout;
SET @innodb_lock_wait_timeout_saved= @@innodb_lock_wait_timeout;
SET @@lock_wait_timeout= 1;
SET @@innodb_lock_wait_timeout= 1;
# Create a function with exit handler:
CREATE FUNCTION f1() RETURNS VARCHAR(20)
BEGIN
DECLARE EXIT HANDLER FOR SQLSTATE '42S02' RETURN 'No such table';
INSERT INTO no_such_table VALUES (1);
END//
# Create a function calling f1():
CREATE FUNCTION f2() RETURNS VARCHAR(20)
BEGIN
RETURN f1();
END//
# Create a function provoking deadlock:
CREATE FUNCTION f3() RETURNS VARCHAR(20)
BEGIN
UPDATE t1 SET i= 1 WHERE i= 1;
RETURN 'Will never get here';
END//
# Create a function calling f3, to create
# a deadlock indirectly:
CREATE FUNCTION f4() RETURNS VARCHAR(20)
BEGIN
RETURN f3();
END//
# Open another connection, create and initialize a table
# to be used for provoking deadlock, put a lock on the table:
CREATE TABLE t1 (i INT) ENGINE=InnoDB;
INSERT INTO t1 VALUES (1);
SET AUTOCOMMIT= 0;
UPDATE t1 SET i=1 WHERE i=1;
# On the default connection, do an update to provoke a
# deadlock, then call the function with handler. This case
# fails without the patch (with error ER_NO_SUCH_TABLE):
SET AUTOCOMMIT= 0;
UPDATE t1 SET i=1 WHERE i=1;
ERROR HY000: Lock wait timeout exceeded; try restarting transaction
SELECT f1() AS 'f1():';
f1():
No such table
Warnings:
Error 1146 Table 'test.no_such_table' doesn't exist
# Provoke another deadlock, then call the function with
# handler indirectly. This case fails without the patch
# (with error ER_NO_SUCH_TABLE):
UPDATE t1 SET i= 1 WHERE i= 1;
ERROR HY000: Lock wait timeout exceeded; try restarting transaction
SELECT f2() AS 'f2():';
f2():
No such table
Warnings:
Error 1146 Table 'test.no_such_table' doesn't exist
# Provoke yet another deadlock, but now from within a function,
# then call the function with handler. This succeeds even
# without the patch because is_fatal_sub_stmt_error is reset
# in restore_sub_stmt after the failing function has been
# executed. The test case is included anyway for better coverage:
SELECT f3() AS 'f3():';
ERROR HY000: Lock wait timeout exceeded; try restarting transaction
SELECT f1() AS 'f1():';
f1():
No such table
Warnings:
Error 1146 Table 'test.no_such_table' doesn't exist
# Provoke yet another deadlock, but now from within a function,
# calling another function, then call the function with handler.
# This succeeds even without the patch because
# is_fatal_sub_stmt_error is reset in restore_sub_stmt after
# the failing function has been executed. The test case is
# included anyway for better coverage:
SELECT f4() AS 'f4():';
ERROR HY000: Lock wait timeout exceeded; try restarting transaction
SELECT f1() AS 'f1():';
f1():
No such table
Warnings:
Error 1146 Table 'test.no_such_table' doesn't exist
# Disconnect, drop functions and table:
DROP FUNCTION f4;
DROP FUNCTION f3;
DROP FUNCTION f2;
DROP FUNCTION f1;
DROP TABLE t1;
# Reset lock wait timeouts
SET @@lock_wait_timeout= @lock_wait_timeout_saved;
SET @@innodb_lock_wait_timeout= @innodb_lock_wait_timeout_saved;
#
# BUG 16041903: End of test case
#
...@@ -195,3 +195,28 @@ b val ...@@ -195,3 +195,28 @@ b val
14 g 14 g
drop trigger t1_after_insert; drop trigger t1_after_insert;
drop table t1,t2; drop table t1,t2;
#
#Bug#19683834 SOME INNODB ERRORS CAUSES STORED FUNCTION
# AND TRIGGER HANDLERS TO BE IGNORED
#Code fixed in Bug#16041903
CREATE TABLE t1 (id int unsigned PRIMARY KEY, val int DEFAULT 0)
ENGINE=InnoDB;
INSERT INTO t1 (id) VALUES (1), (2);
CREATE TABLE t2 (id int PRIMARY KEY);
CREATE TABLE t3 LIKE t2;
CREATE TRIGGER bef_insert BEFORE INSERT ON t2 FOR EACH ROW
BEGIN
DECLARE CONTINUE HANDLER FOR 1062 BEGIN END;
INSERT INTO t3 (id) VALUES (NEW.id);
INSERT INTO t3 (id) VALUES (NEW.id);
END//
START TRANSACTION;
UPDATE t1 SET val = val + 1;
connect con2,localhost,root,,test,,;
SET SESSION innodb_lock_wait_timeout = 2;
UPDATE t1 SET val = val + 1;
ERROR HY000: Lock wait timeout exceeded; try restarting transaction
INSERT INTO t2 (id) VALUES (1);
disconnect con2;
connection default;
DROP TABLE t3, t2, t1;
...@@ -15,6 +15,9 @@ ...@@ -15,6 +15,9 @@
# Tests that require multibyte character sets, which are not always available, # Tests that require multibyte character sets, which are not always available,
# go into separate files (e.g. sp-ucs2.test) # go into separate files (e.g. sp-ucs2.test)
# Save the initial number of concurrent sessions
--source include/count_sessions.inc
use test; use test;
# Test tables # Test tables
...@@ -9093,3 +9096,118 @@ delimiter ;$ ...@@ -9093,3 +9096,118 @@ delimiter ;$
SELECT f1(); SELECT f1();
DROP FUNCTION f1; DROP FUNCTION f1;
--echo
--echo #
--echo # BUG 16041903: CONTINUE HANDLER NOT INVOKED
--echo # IN A STORED FUNCTION AFTER A LOCK WAIT TIMEOUT
--echo #
--echo
--echo # Save and set lock wait timeout
SET @lock_wait_timeout_saved= @@lock_wait_timeout;
SET @innodb_lock_wait_timeout_saved= @@innodb_lock_wait_timeout;
SET @@lock_wait_timeout= 1;
SET @@innodb_lock_wait_timeout= 1;
--echo
--echo # Create a function with exit handler:
DELIMITER //;
CREATE FUNCTION f1() RETURNS VARCHAR(20)
BEGIN
DECLARE EXIT HANDLER FOR SQLSTATE '42S02' RETURN 'No such table';
INSERT INTO no_such_table VALUES (1);
END//
--echo
--echo # Create a function calling f1():
CREATE FUNCTION f2() RETURNS VARCHAR(20)
BEGIN
RETURN f1();
END//
--echo
--echo # Create a function provoking deadlock:
CREATE FUNCTION f3() RETURNS VARCHAR(20)
BEGIN
UPDATE t1 SET i= 1 WHERE i= 1;
RETURN 'Will never get here';
END//
--echo
--echo # Create a function calling f3, to create
--echo # a deadlock indirectly:
CREATE FUNCTION f4() RETURNS VARCHAR(20)
BEGIN
RETURN f3();
END//
DELIMITER ;//
--echo
--echo # Open another connection, create and initialize a table
--echo # to be used for provoking deadlock, put a lock on the table:
connect (con1,localhost,root,,);
CREATE TABLE t1 (i INT) ENGINE=InnoDB;
INSERT INTO t1 VALUES (1);
SET AUTOCOMMIT= 0;
UPDATE t1 SET i=1 WHERE i=1;
--echo
--echo # On the default connection, do an update to provoke a
--echo # deadlock, then call the function with handler. This case
--echo # fails without the patch (with error ER_NO_SUCH_TABLE):
--connection default
SET AUTOCOMMIT= 0;
--error ER_LOCK_WAIT_TIMEOUT
UPDATE t1 SET i=1 WHERE i=1;
SELECT f1() AS 'f1():';
--echo
--echo # Provoke another deadlock, then call the function with
--echo # handler indirectly. This case fails without the patch
--echo # (with error ER_NO_SUCH_TABLE):
--error ER_LOCK_WAIT_TIMEOUT
UPDATE t1 SET i= 1 WHERE i= 1;
SELECT f2() AS 'f2():';
--echo
--echo # Provoke yet another deadlock, but now from within a function,
--echo # then call the function with handler. This succeeds even
--echo # without the patch because is_fatal_sub_stmt_error is reset
--echo # in restore_sub_stmt after the failing function has been
--echo # executed. The test case is included anyway for better coverage:
--error ER_LOCK_WAIT_TIMEOUT
SELECT f3() AS 'f3():';
SELECT f1() AS 'f1():';
--echo # Provoke yet another deadlock, but now from within a function,
--echo # calling another function, then call the function with handler.
--echo # This succeeds even without the patch because
--echo # is_fatal_sub_stmt_error is reset in restore_sub_stmt after
--echo # the failing function has been executed. The test case is
--echo # included anyway for better coverage:
--error ER_LOCK_WAIT_TIMEOUT
SELECT f4() AS 'f4():';
SELECT f1() AS 'f1():';
--echo
--echo # Disconnect, drop functions and table:
--disconnect con1
DROP FUNCTION f4;
DROP FUNCTION f3;
DROP FUNCTION f2;
DROP FUNCTION f1;
DROP TABLE t1;
--echo
--echo # Reset lock wait timeouts
SET @@lock_wait_timeout= @lock_wait_timeout_saved;
SET @@innodb_lock_wait_timeout= @innodb_lock_wait_timeout_saved;
--echo #
--echo # BUG 16041903: End of test case
--echo #
# Wait till we reached the initial number of concurrent sessions
--source include/wait_until_count_sessions.inc
...@@ -2,6 +2,9 @@ ...@@ -2,6 +2,9 @@
# (or just InnoDB storage engine) # (or just InnoDB storage engine)
--source include/have_innodb.inc --source include/have_innodb.inc
# Save the initial number of concurrent sessions
--source include/count_sessions.inc
--disable_warnings --disable_warnings
drop table if exists t1; drop table if exists t1;
--enable_warnings --enable_warnings
...@@ -182,3 +185,54 @@ insert into t1 values ( 654, 'a'), ( 654, 'b'), ( 654, 'c'), ...@@ -182,3 +185,54 @@ insert into t1 values ( 654, 'a'), ( 654, 'b'), ( 654, 'c'),
select * from t2 order by b; select * from t2 order by b;
drop trigger t1_after_insert; drop trigger t1_after_insert;
drop table t1,t2; drop table t1,t2;
--echo #
--echo #Bug#19683834 SOME INNODB ERRORS CAUSES STORED FUNCTION
--echo # AND TRIGGER HANDLERS TO BE IGNORED
--echo #Code fixed in Bug#16041903
--enable_connect_log
CREATE TABLE t1 (id int unsigned PRIMARY KEY, val int DEFAULT 0)
ENGINE=InnoDB;
INSERT INTO t1 (id) VALUES (1), (2);
CREATE TABLE t2 (id int PRIMARY KEY);
CREATE TABLE t3 LIKE t2;
# Trigger with continue handler for ER_DUP_ENTRY(1062)
DELIMITER //;
CREATE TRIGGER bef_insert BEFORE INSERT ON t2 FOR EACH ROW
BEGIN
DECLARE CONTINUE HANDLER FOR 1062 BEGIN END;
INSERT INTO t3 (id) VALUES (NEW.id);
INSERT INTO t3 (id) VALUES (NEW.id);
END//
DELIMITER ;//
# Transaction 1: Grab locks on t1
START TRANSACTION;
UPDATE t1 SET val = val + 1;
# Transaction 2:
--connect (con2,localhost,root,,test,,)
SET SESSION innodb_lock_wait_timeout = 2;
# Trigger lock timeout (1205)
--error ER_LOCK_WAIT_TIMEOUT
UPDATE t1 SET val = val + 1;
# This insert should go through, as the continue handler should
# handle ER_DUP_ENTRY, even after ER_LOCK_WAIT_TIMEOUT (Bug#16041903)
INSERT INTO t2 (id) VALUES (1);
# Cleanup
disconnect con2;
--source include/wait_until_disconnected.inc
connection default;
DROP TABLE t3, t2, t1;
--disable_connect_log
# Wait till we reached the initial number of concurrent sessions
--source include/wait_until_count_sessions.inc
/* Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. /* Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved.
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
...@@ -4019,7 +4019,7 @@ request_backoff_action(enum_open_table_action action_arg, ...@@ -4019,7 +4019,7 @@ request_backoff_action(enum_open_table_action action_arg,
if (action_arg != OT_REOPEN_TABLES && m_has_locks) if (action_arg != OT_REOPEN_TABLES && m_has_locks)
{ {
my_error(ER_LOCK_DEADLOCK, MYF(0)); my_error(ER_LOCK_DEADLOCK, MYF(0));
mark_transaction_to_rollback(m_thd, true); m_thd->mark_transaction_to_rollback(true);
return TRUE; return TRUE;
} }
/* /*
......
/* /*
Copyright (c) 2000, 2014, Oracle and/or its affiliates. All rights reserved. Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved.
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
...@@ -815,7 +815,7 @@ THD::THD() ...@@ -815,7 +815,7 @@ THD::THD()
stmt_da(&main_da), stmt_da(&main_da),
is_fatal_error(0), is_fatal_error(0),
transaction_rollback_request(0), transaction_rollback_request(0),
is_fatal_sub_stmt_error(0), is_fatal_sub_stmt_error(false),
rand_used(0), rand_used(0),
time_zone_used(0), time_zone_used(0),
in_lock_tables(0), in_lock_tables(0),
...@@ -3679,7 +3679,8 @@ extern "C" int thd_binlog_format(const MYSQL_THD thd) ...@@ -3679,7 +3679,8 @@ extern "C" int thd_binlog_format(const MYSQL_THD thd)
extern "C" void thd_mark_transaction_to_rollback(MYSQL_THD thd, bool all) extern "C" void thd_mark_transaction_to_rollback(MYSQL_THD thd, bool all)
{ {
mark_transaction_to_rollback(thd, all); DBUG_ASSERT(thd);
thd->mark_transaction_to_rollback(all);
} }
extern "C" bool thd_binlog_filter_ok(const MYSQL_THD thd) extern "C" bool thd_binlog_filter_ok(const MYSQL_THD thd)
...@@ -3873,9 +3874,12 @@ void THD::restore_sub_statement_state(Sub_statement_state *backup) ...@@ -3873,9 +3874,12 @@ void THD::restore_sub_statement_state(Sub_statement_state *backup)
If we've left sub-statement mode, reset the fatal error flag. If we've left sub-statement mode, reset the fatal error flag.
Otherwise keep the current value, to propagate it up the sub-statement Otherwise keep the current value, to propagate it up the sub-statement
stack. stack.
NOTE: is_fatal_sub_stmt_error can be set only if we've been in the
sub-statement mode.
*/ */
if (!in_sub_stmt) if (!in_sub_stmt)
is_fatal_sub_stmt_error= FALSE; is_fatal_sub_stmt_error= false;
if ((variables.option_bits & OPTION_BIN_LOG) && is_update_query(lex->sql_command) && if ((variables.option_bits & OPTION_BIN_LOG) && is_update_query(lex->sql_command) &&
!is_current_stmt_binlog_format_row()) !is_current_stmt_binlog_format_row())
...@@ -3988,27 +3992,28 @@ void THD::get_definer(LEX_USER *definer) ...@@ -3988,27 +3992,28 @@ void THD::get_definer(LEX_USER *definer)
/** /**
Mark transaction to rollback and mark error as fatal to a sub-statement. Mark transaction to rollback and mark error as fatal to a sub-statement.
@param thd Thread handle
@param all TRUE <=> rollback main transaction. @param all TRUE <=> rollback main transaction.
*/ */
void mark_transaction_to_rollback(THD *thd, bool all) void THD::mark_transaction_to_rollback(bool all)
{ {
if (thd) /*
{ There is no point in setting is_fatal_sub_stmt_error unless
thd->is_fatal_sub_stmt_error= TRUE; we are actually in_sub_stmt.
thd->transaction_rollback_request= all; */
/* if (in_sub_stmt)
Aborted transactions can not be IGNOREd. is_fatal_sub_stmt_error= true;
Switch off the IGNORE flag for the current transaction_rollback_request= all;
SELECT_LEX. This should allow my_error() /*
to report the error and abort the execution Aborted transactions can not be IGNOREd.
flow, even in presence Switch off the IGNORE flag for the current
of IGNORE clause. SELECT_LEX. This should allow my_error()
*/ to report the error and abort the execution
if (thd->lex->current_select) flow, even in presence
thd->lex->current_select->no_error= FALSE; of IGNORE clause.
} */
if (lex->current_select)
lex->current_select->no_error= false;
} }
/*************************************************************************** /***************************************************************************
Handling of XA id cacheing Handling of XA id cacheing
......
/* Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. /* Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved.
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
...@@ -595,8 +595,6 @@ typedef struct system_status_var ...@@ -595,8 +595,6 @@ typedef struct system_status_var
#define last_system_status_var questions #define last_system_status_var questions
void mark_transaction_to_rollback(THD *thd, bool all);
#ifdef MYSQL_SERVER #ifdef MYSQL_SERVER
void free_tmp_table(THD *thd, TABLE *entry); void free_tmp_table(THD *thd, TABLE *entry);
...@@ -2864,6 +2862,7 @@ public: ...@@ -2864,6 +2862,7 @@ public:
LEX_STRING get_invoker_user() { return invoker_user; } LEX_STRING get_invoker_user() { return invoker_user; }
LEX_STRING get_invoker_host() { return invoker_host; } LEX_STRING get_invoker_host() { return invoker_host; }
bool has_invoker() { return invoker_user.length > 0; } bool has_invoker() { return invoker_user.length > 0; }
void mark_transaction_to_rollback(bool all);
private: private:
/** The current internal error handler for this thread, or NULL. */ /** The current internal error handler for this thread, or NULL. */
...@@ -3685,7 +3684,6 @@ void add_to_status(STATUS_VAR *to_var, STATUS_VAR *from_var); ...@@ -3685,7 +3684,6 @@ void add_to_status(STATUS_VAR *to_var, STATUS_VAR *from_var);
void add_diff_to_status(STATUS_VAR *to_var, STATUS_VAR *from_var, void add_diff_to_status(STATUS_VAR *to_var, STATUS_VAR *from_var,
STATUS_VAR *dec_var); STATUS_VAR *dec_var);
void mark_transaction_to_rollback(THD *thd, bool all);
/* Inline functions */ /* Inline functions */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment