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(
thr_lock_type lock_type)
{
trx_t* trx;
DBUG_ENTER("ha_innobase::start_stmt");
update_thd(thd);
......@@ -9195,6 +9196,28 @@ ha_innobase::start_stmt(
prebuilt->hint_need_to_fetch_extra_cols = 0;
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) {
/* This handle is for a temporary table created inside
this same LOCK TABLES; since MySQL does NOT call external_lock
......@@ -9227,7 +9250,7 @@ ha_innobase::start_stmt(
innobase_register_trx(ht, thd, trx);
return(0);
DBUG_RETURN(0);
}
/******************************************************************//**
......
......@@ -1367,6 +1367,14 @@ dict_table_init_referenced_rbt(
/*===========================*/
dict_table_t* table); /*!< in: the table object whose
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
#include "dict0dict.ic"
......
......@@ -1004,3 +1004,15 @@ dict_table_init_referenced_rbt(
ut_a(table->referenced_rbt != NULL);
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
the terms of the GNU General Public License as published by the Free Software
......@@ -3089,6 +3089,9 @@ row_drop_table_for_mysql(
ulint namelen;
ibool locked_dictionary = FALSE;
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);
......@@ -3099,7 +3102,7 @@ row_drop_table_for_mysql(
"InnoDB: Shut down mysqld and edit my.cnf so that newraw"
" is replaced with raw.\n", stderr);
return(DB_ERROR);
DBUG_RETURN(DB_ERROR);
}
trx->op_info = "dropping table";
......@@ -3504,7 +3507,7 @@ funct_exit:
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