mirror of
https://github.com/MariaDB/server.git
synced 2025-07-30 16:24:05 +03:00
Fix Bug# 56228 - Dropping tables from within an active statement crashes server
InnoDB AUTOINC code expects the locks to be released in strict reverse order at the end of the statement. However, nested stored proedures and partition tables break this rule. We now allow the locks to be deleted from the trx->autoinc_locks vector in any order but optimise for the common (old) case. rb://441 Approved by Marko Makela
This commit is contained in:
30
mysql-test/suite/innodb_plugin/r/innodb-autoinc-56228.result
Normal file
30
mysql-test/suite/innodb_plugin/r/innodb-autoinc-56228.result
Normal file
@ -0,0 +1,30 @@
|
||||
DROP TABLE IF EXISTS t1_56228;
|
||||
Warnings:
|
||||
Note 1051 Unknown table 't1_56228'
|
||||
DROP TABLE IF EXISTS t2_56228;
|
||||
Warnings:
|
||||
Note 1051 Unknown table 't2_56228'
|
||||
DROP FUNCTION IF EXISTS bug56228;
|
||||
Warnings:
|
||||
Note 1305 FUNCTION bug56228 does not exist
|
||||
CREATE TEMPORARY TABLE t1_56228(
|
||||
c1 iNT AUTO_INCREMENT PRIMARY KEY) ENGINE=InnoDB;
|
||||
CREATE TEMPORARY TABLE t2_56228(
|
||||
c1 iNT AUTO_INCREMENT PRIMARY KEY) ENGINE=InnoDB;
|
||||
CREATE FUNCTION bug56228() RETURNS INT DETERMINISTIC
|
||||
BEGIN
|
||||
INSERT INTO t1_56228 VALUES(NULL);
|
||||
INSERT INTO t2_56228 VALUES(NULL);
|
||||
INSERT INTO t1_56228 VALUES(NULL);
|
||||
INSERT INTO t2_56228 VALUES(NULL);
|
||||
DROP TEMPORARY TABLE t1_56228;
|
||||
RETURN 42;
|
||||
END //
|
||||
SELECT bug56228();
|
||||
bug56228()
|
||||
42
|
||||
DROP FUNCTION bug56228;
|
||||
DROP TEMPORARY TABLE t2_56228;
|
||||
DROP TEMPORARY TABLE IF EXISTS t1_56228;
|
||||
Warnings:
|
||||
Note 1051 Unknown table 't1_56228'
|
@ -0,0 +1 @@
|
||||
--innodb_autoinc_lock_mode=0
|
42
mysql-test/suite/innodb_plugin/t/innodb-autoinc-56228.test
Normal file
42
mysql-test/suite/innodb_plugin/t/innodb-autoinc-56228.test
Normal file
@ -0,0 +1,42 @@
|
||||
-- source include/have_innodb_plugin.inc
|
||||
|
||||
let $innodb_file_format_check_orig=`select @@innodb_file_format_check`;
|
||||
|
||||
##
|
||||
# Bug #56228: dropping tables from within an active statement crashes server
|
||||
#
|
||||
DROP TABLE IF EXISTS t1_56228;
|
||||
DROP TABLE IF EXISTS t2_56228;
|
||||
DROP FUNCTION IF EXISTS bug56228;
|
||||
|
||||
CREATE TEMPORARY TABLE t1_56228(
|
||||
c1 iNT AUTO_INCREMENT PRIMARY KEY) ENGINE=InnoDB;
|
||||
CREATE TEMPORARY TABLE t2_56228(
|
||||
c1 iNT AUTO_INCREMENT PRIMARY KEY) ENGINE=InnoDB;
|
||||
|
||||
DELIMITER //;
|
||||
|
||||
CREATE FUNCTION bug56228() RETURNS INT DETERMINISTIC
|
||||
BEGIN
|
||||
INSERT INTO t1_56228 VALUES(NULL);
|
||||
INSERT INTO t2_56228 VALUES(NULL);
|
||||
INSERT INTO t1_56228 VALUES(NULL);
|
||||
INSERT INTO t2_56228 VALUES(NULL);
|
||||
DROP TEMPORARY TABLE t1_56228;
|
||||
RETURN 42;
|
||||
END //
|
||||
|
||||
DELIMITER ;//
|
||||
|
||||
SELECT bug56228();
|
||||
|
||||
DROP FUNCTION bug56228;
|
||||
DROP TEMPORARY TABLE t2_56228;
|
||||
DROP TEMPORARY TABLE IF EXISTS t1_56228;
|
||||
|
||||
#
|
||||
# restore environment to the state it was before this test execution
|
||||
#
|
||||
|
||||
-- disable_query_log
|
||||
eval set global innodb_file_format_check=$innodb_file_format_check_orig;
|
@ -93,6 +93,25 @@ ib_vector_get(
|
||||
ib_vector_t* vec, /*!< in: vector */
|
||||
ulint n); /*!< in: element index to get */
|
||||
|
||||
/****************************************************************//**
|
||||
Get last element. The vector must not be empty.
|
||||
@return last element */
|
||||
UNIV_INLINE
|
||||
void*
|
||||
ib_vector_get_last(
|
||||
/*===============*/
|
||||
ib_vector_t* vec); /*!< in: vector */
|
||||
|
||||
/****************************************************************//**
|
||||
Set the n'th element. */
|
||||
UNIV_INLINE
|
||||
void
|
||||
ib_vector_set(
|
||||
/*==========*/
|
||||
ib_vector_t* vec, /*!< in/out: vector */
|
||||
ulint n, /*!< in: element index to set */
|
||||
void* elem); /*!< in: data element */
|
||||
|
||||
/****************************************************************//**
|
||||
Remove the last element from the vector. */
|
||||
UNIV_INLINE
|
||||
|
@ -50,6 +50,35 @@ ib_vector_get(
|
||||
return(vec->data[n]);
|
||||
}
|
||||
|
||||
/****************************************************************//**
|
||||
Get last element. The vector must not be empty.
|
||||
@return last element */
|
||||
UNIV_INLINE
|
||||
void*
|
||||
ib_vector_get_last(
|
||||
/*===============*/
|
||||
ib_vector_t* vec) /*!< in: vector */
|
||||
{
|
||||
ut_a(vec->used > 0);
|
||||
|
||||
return(vec->data[vec->used - 1]);
|
||||
}
|
||||
|
||||
/****************************************************************//**
|
||||
Set the n'th element. */
|
||||
UNIV_INLINE
|
||||
void
|
||||
ib_vector_set(
|
||||
/*==========*/
|
||||
ib_vector_t* vec, /*!< in/out: vector */
|
||||
ulint n, /*!< in: element index to set */
|
||||
void* elem) /*!< in: data element */
|
||||
{
|
||||
ut_a(n < vec->used);
|
||||
|
||||
vec->data[n] = elem;
|
||||
}
|
||||
|
||||
/****************************************************************//**
|
||||
Remove the last element from the vector.
|
||||
@return last vector element */
|
||||
|
@ -3624,6 +3624,80 @@ lock_table_create(
|
||||
return(lock);
|
||||
}
|
||||
|
||||
/*************************************************************//**
|
||||
Pops autoinc lock requests from the transaction's autoinc_locks. We
|
||||
handle the case where there are gaps in the array and they need to
|
||||
be popped off the stack. */
|
||||
UNIV_INLINE
|
||||
void
|
||||
lock_table_pop_autoinc_locks(
|
||||
/*=========================*/
|
||||
trx_t* trx) /*!< in/out: transaction that owns the AUTOINC locks */
|
||||
{
|
||||
ut_ad(mutex_own(&kernel_mutex));
|
||||
ut_ad(!ib_vector_is_empty(trx->autoinc_locks));
|
||||
|
||||
/* Skip any gaps, gaps are NULL lock entries in the
|
||||
trx->autoinc_locks vector. */
|
||||
|
||||
do {
|
||||
ib_vector_pop(trx->autoinc_locks);
|
||||
|
||||
if (ib_vector_is_empty(trx->autoinc_locks)) {
|
||||
return;
|
||||
}
|
||||
|
||||
} while (ib_vector_get_last(trx->autoinc_locks) == NULL);
|
||||
}
|
||||
|
||||
/*************************************************************//**
|
||||
Removes an autoinc lock request from the transaction's autoinc_locks. */
|
||||
UNIV_INLINE
|
||||
void
|
||||
lock_table_remove_autoinc_lock(
|
||||
/*===========================*/
|
||||
lock_t* lock, /*!< in: table lock */
|
||||
trx_t* trx) /*!< in/out: transaction that owns the lock */
|
||||
{
|
||||
lock_t* autoinc_lock;
|
||||
lint i = ib_vector_size(trx->autoinc_locks) - 1;
|
||||
|
||||
ut_ad(mutex_own(&kernel_mutex));
|
||||
ut_ad(lock_get_mode(lock) == LOCK_AUTO_INC);
|
||||
ut_ad(lock_get_type_low(lock) & LOCK_TABLE);
|
||||
ut_ad(!ib_vector_is_empty(trx->autoinc_locks));
|
||||
|
||||
/* With stored functions and procedures the user may drop
|
||||
a table within the same "statement". This special case has
|
||||
to be handled by deleting only those AUTOINC locks that were
|
||||
held by the table being dropped. */
|
||||
|
||||
autoinc_lock = ib_vector_get(trx->autoinc_locks, i);
|
||||
|
||||
/* This is the default fast case. */
|
||||
|
||||
if (autoinc_lock == lock) {
|
||||
lock_table_pop_autoinc_locks(trx);
|
||||
} else {
|
||||
/* The last element should never be NULL */
|
||||
ut_a(autoinc_lock != NULL);
|
||||
|
||||
/* Handle freeing the locks from within the stack. */
|
||||
|
||||
while (--i >= 0) {
|
||||
autoinc_lock = ib_vector_get(trx->autoinc_locks, i);
|
||||
|
||||
if (UNIV_LIKELY(autoinc_lock == lock)) {
|
||||
ib_vector_set(trx->autoinc_locks, i, NULL);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Must find the autoinc lock. */
|
||||
ut_error;
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************//**
|
||||
Removes a table lock request from the queue and the trx list of locks;
|
||||
this is a low-level function which does NOT check if waiting requests
|
||||
@ -3663,10 +3737,8 @@ lock_table_remove_low(
|
||||
|
||||
if (!lock_get_wait(lock)
|
||||
&& !ib_vector_is_empty(trx->autoinc_locks)) {
|
||||
lock_t* autoinc_lock;
|
||||
|
||||
autoinc_lock = ib_vector_pop(trx->autoinc_locks);
|
||||
ut_a(autoinc_lock == lock);
|
||||
lock_table_remove_autoinc_lock(lock, trx);
|
||||
}
|
||||
|
||||
ut_a(table->n_waiting_or_granted_auto_inc_locks > 0);
|
||||
|
Reference in New Issue
Block a user