Commit db78f29b authored by Annamalai Gurusami's avatar Annamalai Gurusami

Bug #19306524 FAILING ASSERTION WITH TEMP TABLE FOR A PROCEDURE CALLED

FROM A FUNCTION

Scenario:

In a stored procedure, CREATE TABLE statement is not allowed.  But an
exception is provided for CREATE TEMPORARY TABLE.  We can create a temporary
table in a stored procedure.

Let there be two stored functions f1 and f2 and two stored procedures p1 and
p2.  Their properties are as follows:

. stored function f1() calls stored procedure p1().
. stored function f2() calls stored procedure p2().
. stored procedure p1() creates temporary table t1.
. stored procedure p2() does DML on t1.

Consider the following situation:

1.  Autocommit mode is on. 
2.  select f1()
3.  select f2()

Step 2:  In this step, t1 would be created via p1().  A table level transaction
lock would have been taken.  The ::external_lock() would not have been called
on this table.  At the end of step 2, because of autocommit mode on, this table
level lock will be released.

Step 3:  When we execute DML on table t1 via p2() we have two problems:

Problem 1:

The function ha_innobase::external_lock() would have been called but since
it is a select query no table level locks would have been taken.  Hence the
following assert will fail:

ut_ad(lock_table_has(thr_get_trx(thr), index->table, LOCK_IX));

Solution:

The solution would be to identify this situation and take a table level lock
and use the proper lock type prebuilt->select_lock_type = LOCK_X for DML
operations.

Problem 2:

Another problem is that in step 3, ha_innobase::open() is never called on
the table t1.  

Solution:

The solution would be to identify this situation and call re-init the handler
of table t1.

rb#6429 approved by Krunal.
parent 44fd241a
#
# Bug #19306524 FAILING ASSERTION WITH TEMP TABLE FOR A PROCEDURE
# CALLED FROM A FUNCTION
#
call mtr.add_suppression("MySQL is trying to drop table");
CREATE PROCEDURE cachedata(
IN obj_id BIGINT UNSIGNED,
IN start DATETIME,
IN end DATETIME
)
cachedata:BEGIN
DECLARE cache_count BIGINT;
SET @timestamp := NOW();
CREATE TEMPORARY TABLE IF NOT EXISTS cachedata (
timestamp DATETIME,
object_id BIGINT UNSIGNED NOT NULL,
start DATETIME,
end DATETIME,
seqno BIGINT AUTO_INCREMENT,
value FLOAT,
PRIMARY KEY (seqno),
INDEX (timestamp),
INDEX (object_id, start, end)
) ENGINE=INNODB;
DELETE FROM cachedata WHERE
timestamp < DATE_SUB(@timestamp, INTERVAL 15 SECOND);
SELECT count(*) INTO cache_count FROM cachedata WHERE
object_id = obj_id
AND start = start
AND end = end;
IF cache_count > 0 THEN LEAVE cachedata;
END IF;
INSERT INTO cachedata (timestamp, object_id, start, end, value) VALUES
(@timestamp, obj_id, start, end, 1234),
(@timestamp, obj_id, start, end, 4567),
(@timestamp, obj_id, start, end, 8901),
(@timestamp, obj_id, start, end, 1234),
(@timestamp, obj_id, start, end, 4567),
(@timestamp, obj_id, start, end, 8901),
(@timestamp, obj_id, start, end, 1234),
(@timestamp, obj_id, start, end, 4567),
(@timestamp, obj_id, start, end, 8901),
(@timestamp, obj_id, start, end, 1234),
(@timestamp, obj_id, start, end, 4567),
(@timestamp, obj_id, start, end, 8901),
(@timestamp, obj_id, start, end, 2345),
(@timestamp, obj_id, start, end, 1234),
(@timestamp, obj_id, start, end, 4567),
(@timestamp, obj_id, start, end, 8901),
(@timestamp, obj_id, start, end, 2345),
(@timestamp, obj_id, start, end, 1234),
(@timestamp, obj_id, start, end, 4567),
(@timestamp, obj_id, start, end, 8901),
(@timestamp, obj_id, start, end, 2345);
END$$
CREATE FUNCTION get_cache(
obj_id BIGINT UNSIGNED,
start DATETIME,
end DATETIME
)
RETURNS FLOAT
READS SQL DATA
BEGIN
DECLARE result FLOAT;
CALL cachedata(obj_id, start, end);
SELECT SUM(value) INTO result FROM cachedata WHERE
object_id = obj_id
AND start = start
AND end = end;
RETURN result;
END$$
SELECT get_cache(1, '2014-01-01', '2014-02-01');
get_cache(1, '2014-01-01', '2014-02-01')
95247
select sleep(1);
sleep(1)
0
SELECT get_cache(1, '2014-01-01', '2014-02-01');
get_cache(1, '2014-01-01', '2014-02-01')
95247
select sleep(1);
sleep(1)
0
SELECT get_cache(1, '2014-01-01', '2014-02-01');
get_cache(1, '2014-01-01', '2014-02-01')
95247
select sleep(1);
sleep(1)
0
SELECT get_cache(1, '2014-01-01', '2014-02-01');
get_cache(1, '2014-01-01', '2014-02-01')
95247
select sleep(1);
sleep(1)
0
SELECT get_cache(1, '2014-01-01', '2014-02-01');
get_cache(1, '2014-01-01', '2014-02-01')
95247
select sleep(1);
sleep(1)
0
SELECT get_cache(1, '2014-01-01', '2014-02-01');
get_cache(1, '2014-01-01', '2014-02-01')
95247
select sleep(1);
sleep(1)
0
SELECT get_cache(1, '2014-01-01', '2014-02-01');
get_cache(1, '2014-01-01', '2014-02-01')
95247
select sleep(1);
sleep(1)
0
SELECT get_cache(1, '2014-01-01', '2014-02-01');
get_cache(1, '2014-01-01', '2014-02-01')
95247
select sleep(1);
sleep(1)
0
SELECT get_cache(1, '2014-01-01', '2014-02-01');
get_cache(1, '2014-01-01', '2014-02-01')
95247
select sleep(1);
sleep(1)
0
SELECT get_cache(1, '2014-01-01', '2014-02-01');
get_cache(1, '2014-01-01', '2014-02-01')
95247
select sleep(1);
sleep(1)
0
SELECT get_cache(1, '2014-01-01', '2014-02-01');
get_cache(1, '2014-01-01', '2014-02-01')
95247
select sleep(1);
sleep(1)
0
SELECT get_cache(1, '2014-01-01', '2014-02-01');
get_cache(1, '2014-01-01', '2014-02-01')
95247
select sleep(1);
sleep(1)
0
SELECT get_cache(1, '2014-01-01', '2014-02-01');
get_cache(1, '2014-01-01', '2014-02-01')
95247
select sleep(1);
sleep(1)
0
SELECT get_cache(1, '2014-01-01', '2014-02-01');
get_cache(1, '2014-01-01', '2014-02-01')
95247
select sleep(1);
sleep(1)
0
SELECT get_cache(1, '2014-01-01', '2014-02-01');
get_cache(1, '2014-01-01', '2014-02-01')
95247
select sleep(1);
sleep(1)
0
SELECT get_cache(1, '2014-01-01', '2014-02-01');
get_cache(1, '2014-01-01', '2014-02-01')
95247
select sleep(1);
sleep(1)
0
SELECT get_cache(1, '2014-01-01', '2014-02-01');
get_cache(1, '2014-01-01', '2014-02-01')
95247
select sleep(1);
sleep(1)
0
SELECT get_cache(1, '2014-01-01', '2014-02-01');
get_cache(1, '2014-01-01', '2014-02-01')
95247
select sleep(1);
sleep(1)
0
SELECT get_cache(1, '2014-01-01', '2014-02-01');
get_cache(1, '2014-01-01', '2014-02-01')
95247
select sleep(1);
sleep(1)
0
SELECT get_cache(1, '2014-01-01', '2014-02-01');
get_cache(1, '2014-01-01', '2014-02-01')
95247
select sleep(1);
sleep(1)
0
SELECT get_cache(1, '2014-01-01', '2014-02-01');
get_cache(1, '2014-01-01', '2014-02-01')
95247
select sleep(1);
sleep(1)
0
SELECT get_cache(1, '2014-01-01', '2014-02-01');
get_cache(1, '2014-01-01', '2014-02-01')
95247
select sleep(1);
sleep(1)
0
SELECT get_cache(1, '2014-01-01', '2014-02-01');
get_cache(1, '2014-01-01', '2014-02-01')
95247
select sleep(1);
sleep(1)
0
SELECT get_cache(1, '2014-01-01', '2014-02-01');
get_cache(1, '2014-01-01', '2014-02-01')
95247
select sleep(1);
sleep(1)
0
SELECT get_cache(1, '2014-01-01', '2014-02-01');
get_cache(1, '2014-01-01', '2014-02-01')
95247
select sleep(1);
sleep(1)
0
SELECT get_cache(1, '2014-01-01', '2014-02-01');
get_cache(1, '2014-01-01', '2014-02-01')
95247
select sleep(1);
sleep(1)
0
SELECT get_cache(1, '2014-01-01', '2014-02-01');
get_cache(1, '2014-01-01', '2014-02-01')
95247
select sleep(1);
sleep(1)
0
SELECT get_cache(1, '2014-01-01', '2014-02-01');
get_cache(1, '2014-01-01', '2014-02-01')
95247
select sleep(1);
sleep(1)
0
SELECT get_cache(1, '2014-01-01', '2014-02-01');
get_cache(1, '2014-01-01', '2014-02-01')
95247
select sleep(1);
sleep(1)
0
SELECT get_cache(1, '2014-01-01', '2014-02-01');
get_cache(1, '2014-01-01', '2014-02-01')
95247
select sleep(1);
sleep(1)
0
DROP FUNCTION get_cache;
DROP PROCEDURE cachedata;
--source include/have_innodb.inc
--source include/big_test.inc
--echo #
--echo # Bug #19306524 FAILING ASSERTION WITH TEMP TABLE FOR A PROCEDURE
--echo # CALLED FROM A FUNCTION
--echo #
call mtr.add_suppression("MySQL is trying to drop table");
DELIMITER $$;
CREATE PROCEDURE cachedata(
IN obj_id BIGINT UNSIGNED,
IN start DATETIME,
IN end DATETIME
)
cachedata:BEGIN
DECLARE cache_count BIGINT;
SET @timestamp := NOW();
CREATE TEMPORARY TABLE IF NOT EXISTS cachedata (
timestamp DATETIME,
object_id BIGINT UNSIGNED NOT NULL,
start DATETIME,
end DATETIME,
seqno BIGINT AUTO_INCREMENT,
value FLOAT,
PRIMARY KEY (seqno),
INDEX (timestamp),
INDEX (object_id, start, end)
) ENGINE=INNODB;
DELETE FROM cachedata WHERE
timestamp < DATE_SUB(@timestamp, INTERVAL 15 SECOND);
SELECT count(*) INTO cache_count FROM cachedata WHERE
object_id = obj_id
AND start = start
AND end = end;
IF cache_count > 0 THEN LEAVE cachedata;
END IF;
INSERT INTO cachedata (timestamp, object_id, start, end, value) VALUES
(@timestamp, obj_id, start, end, 1234),
(@timestamp, obj_id, start, end, 4567),
(@timestamp, obj_id, start, end, 8901),
(@timestamp, obj_id, start, end, 1234),
(@timestamp, obj_id, start, end, 4567),
(@timestamp, obj_id, start, end, 8901),
(@timestamp, obj_id, start, end, 1234),
(@timestamp, obj_id, start, end, 4567),
(@timestamp, obj_id, start, end, 8901),
(@timestamp, obj_id, start, end, 1234),
(@timestamp, obj_id, start, end, 4567),
(@timestamp, obj_id, start, end, 8901),
(@timestamp, obj_id, start, end, 2345),
(@timestamp, obj_id, start, end, 1234),
(@timestamp, obj_id, start, end, 4567),
(@timestamp, obj_id, start, end, 8901),
(@timestamp, obj_id, start, end, 2345),
(@timestamp, obj_id, start, end, 1234),
(@timestamp, obj_id, start, end, 4567),
(@timestamp, obj_id, start, end, 8901),
(@timestamp, obj_id, start, end, 2345);
END$$
CREATE FUNCTION get_cache(
obj_id BIGINT UNSIGNED,
start DATETIME,
end DATETIME
)
RETURNS FLOAT
READS SQL DATA
BEGIN
DECLARE result FLOAT;
CALL cachedata(obj_id, start, end);
SELECT SUM(value) INTO result FROM cachedata WHERE
object_id = obj_id
AND start = start
AND end = end;
RETURN result;
END$$
DELIMITER ;$$
let $i = 30;
while ($i)
{
SELECT get_cache(1, '2014-01-01', '2014-02-01');
select sleep(1);
dec $i;
}
DROP FUNCTION get_cache;
DROP PROCEDURE cachedata;
...@@ -9173,6 +9173,7 @@ ha_innobase::start_stmt( ...@@ -9173,6 +9173,7 @@ ha_innobase::start_stmt(
thr_lock_type lock_type) thr_lock_type lock_type)
{ {
trx_t* trx; trx_t* trx;
DBUG_ENTER("ha_innobase::start_stmt");
update_thd(thd); update_thd(thd);
...@@ -9195,6 +9196,28 @@ ha_innobase::start_stmt( ...@@ -9195,6 +9196,28 @@ ha_innobase::start_stmt(
prebuilt->hint_need_to_fetch_extra_cols = 0; prebuilt->hint_need_to_fetch_extra_cols = 0;
reset_template(prebuilt); reset_template(prebuilt);
if (dict_table_is_temporary(prebuilt->table)
&& prebuilt->mysql_has_locked
&& prebuilt->select_lock_type == LOCK_NONE) {
ulint error;
switch (thd_sql_command(thd)) {
case SQLCOM_INSERT:
case SQLCOM_UPDATE:
case SQLCOM_DELETE:
init_table_handle_for_HANDLER();
prebuilt->select_lock_type = LOCK_X;
error = row_lock_table_for_mysql(prebuilt, NULL, 1);
if (error != DB_SUCCESS) {
error = convert_error_code_to_mysql(
(int) error, 0, thd);
DBUG_RETURN((int) error);
}
break;
}
}
if (!prebuilt->mysql_has_locked) { if (!prebuilt->mysql_has_locked) {
/* This handle is for a temporary table created inside /* This handle is for a temporary table created inside
this same LOCK TABLES; since MySQL does NOT call external_lock this same LOCK TABLES; since MySQL does NOT call external_lock
...@@ -9227,7 +9250,7 @@ ha_innobase::start_stmt( ...@@ -9227,7 +9250,7 @@ ha_innobase::start_stmt(
innobase_register_trx(ht, thd, trx); innobase_register_trx(ht, thd, trx);
return(0); DBUG_RETURN(0);
} }
/******************************************************************//** /******************************************************************//**
......
...@@ -1367,6 +1367,14 @@ dict_table_init_referenced_rbt( ...@@ -1367,6 +1367,14 @@ dict_table_init_referenced_rbt(
/*===========================*/ /*===========================*/
dict_table_t* table); /*!< in: the table object whose dict_table_t* table); /*!< in: the table object whose
table->referenced_rbt will be initialized */ table->referenced_rbt will be initialized */
/********************************************************************//**
Check if it is a temporary table.
@return true if temporary table flag is set. */
UNIV_INLINE
ibool
dict_table_is_temporary(
/*====================*/
const dict_table_t* table); /*!< in: table to check */
#ifndef UNIV_NONINL #ifndef UNIV_NONINL
#include "dict0dict.ic" #include "dict0dict.ic"
......
...@@ -1004,3 +1004,15 @@ dict_table_init_referenced_rbt( ...@@ -1004,3 +1004,15 @@ dict_table_init_referenced_rbt(
ut_a(table->referenced_rbt != NULL); ut_a(table->referenced_rbt != NULL);
return(table->referenced_rbt); return(table->referenced_rbt);
} }
/********************************************************************//**
Check if it is a temporary table.
@return true if temporary table flag is set. */
UNIV_INLINE
ibool
dict_table_is_temporary(
/*====================*/
const dict_table_t* table) /*!< in: table to check */
{
return(table->flags & (DICT_TF2_TEMPORARY << DICT_TF2_SHIFT));
}
/***************************************************************************** /*****************************************************************************
Copyright (c) 2000, 2013, Oracle and/or its affiliates. All Rights Reserved. Copyright (c) 2000, 2014, Oracle and/or its affiliates. All Rights Reserved.
This program is free software; you can redistribute it and/or modify it under 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 the Free Software the terms of the GNU General Public License as published by the Free Software
...@@ -3089,6 +3089,9 @@ row_drop_table_for_mysql( ...@@ -3089,6 +3089,9 @@ row_drop_table_for_mysql(
ulint namelen; ulint namelen;
ibool locked_dictionary = FALSE; ibool locked_dictionary = FALSE;
pars_info_t* info = NULL; pars_info_t* info = NULL;
DBUG_ENTER("row_drop_table_for_mysql");
DBUG_PRINT("row_drop_table_for_mysql", ("table: %s", name));
ut_a(name != NULL); ut_a(name != NULL);
...@@ -3099,7 +3102,7 @@ row_drop_table_for_mysql( ...@@ -3099,7 +3102,7 @@ row_drop_table_for_mysql(
"InnoDB: Shut down mysqld and edit my.cnf so that newraw" "InnoDB: Shut down mysqld and edit my.cnf so that newraw"
" is replaced with raw.\n", stderr); " is replaced with raw.\n", stderr);
return(DB_ERROR); DBUG_RETURN(DB_ERROR);
} }
trx->op_info = "dropping table"; trx->op_info = "dropping table";
...@@ -3504,7 +3507,7 @@ funct_exit: ...@@ -3504,7 +3507,7 @@ funct_exit:
srv_wake_master_thread(); srv_wake_master_thread();
return((int) err); DBUG_RETURN((int) err);
} }
/*********************************************************************//** /*********************************************************************//**
......
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