mirror of
https://github.com/MariaDB/server.git
synced 2026-10-23 18:49:29 +03:00
MDEV-26123: Always mark spatial index reads as locking
A SELECT query could fail with ER_READ_ONLY_TRANSACTION when search on spatial index is done after search on non-spatial index. R-tree index search always require page locks. Previously, trx->will_lock was only set for spatial indexes when the transaction had not yet been started. If a transaction was already active due to a prior non-spatial index access, index_read() for spatial index returned HA_ERR_READ_ONLY_TRANSACTION instead of marking the transaction as locking. trx->will_lock must not be changed once a transaction has started, so index_read() cannot safely fix this up by itself. Fix: - ha_innobase::external_lock() : Set trx->will_lock before the transaction starts for any table with a spatial index (dict_table_t::has_spatial_index()), so this no longer depends on statement/index access order. - ha_innobase::index_read() : Drop the HA_ERR_READ_ONLY_TRANSACTION error; trx->will_lock is only set here as a pre-start fallback.
This commit is contained in:
@@ -286,3 +286,37 @@ connection default;
|
||||
disconnect con1;
|
||||
disconnect con2;
|
||||
DROP TABLE t1;
|
||||
#
|
||||
# MDEV 26123 : Always mark spatial index reads as locking
|
||||
#
|
||||
SET @old_autocommit = @@autocommit;
|
||||
CREATE TABLE t1 (
|
||||
c1 INT PRIMARY KEY,
|
||||
category INT NOT NULL,
|
||||
p POINT NOT NULL,
|
||||
KEY idx_category (category),
|
||||
SPATIAL INDEX sp_idx_p (p)
|
||||
) ENGINE=InnoDB;
|
||||
INSERT INTO t1 VALUES
|
||||
(1, 10, POINT(1,2));
|
||||
CREATE PROCEDURE populate_t1()
|
||||
BEGIN
|
||||
DECLARE i INT DEFAULT 2;
|
||||
WHILE i <= 5000 DO
|
||||
INSERT INTO t1 VALUES
|
||||
(i, 20, POINT(5,5));
|
||||
SET i = i + 1;
|
||||
END WHILE;
|
||||
END |
|
||||
CALL populate_t1();
|
||||
SELECT COUNT(*) FROM t1 WHERE ST_Contains(t1.p, (SELECT p FROM t1 WHERE category = 10));
|
||||
COUNT(*)
|
||||
1
|
||||
SET autocommit = OFF;
|
||||
SET STATEMENT transaction_read_only = ON FOR
|
||||
SELECT COUNT(*) FROM t1 WHERE category = (SELECT category FROM t1 WHERE ST_EQUALS(p, POINT(1,2)));
|
||||
COUNT(*)
|
||||
1
|
||||
SET autocommit = @old_autocommit;
|
||||
DROP PROCEDURE populate_t1;
|
||||
DROP TABLE t1;
|
||||
|
||||
@@ -188,3 +188,48 @@ COMMIT;
|
||||
--disconnect con1
|
||||
--disconnect con2
|
||||
DROP TABLE t1;
|
||||
|
||||
--echo #
|
||||
--echo # MDEV 26123 : Always mark spatial index reads as locking
|
||||
--echo #
|
||||
SET @old_autocommit = @@autocommit;
|
||||
|
||||
CREATE TABLE t1 (
|
||||
c1 INT PRIMARY KEY,
|
||||
category INT NOT NULL,
|
||||
p POINT NOT NULL,
|
||||
KEY idx_category (category),
|
||||
SPATIAL INDEX sp_idx_p (p)
|
||||
) ENGINE=InnoDB;
|
||||
|
||||
INSERT INTO t1 VALUES
|
||||
(1, 10, POINT(1,2));
|
||||
|
||||
# Populate enough rows that the R-tree grows past a single leaf page,
|
||||
# i.e. gains an internal (non-leaf) level. lock_place_prdt_page_lock()
|
||||
# is only called while a search descends through a non-leaf node, with
|
||||
# too few rows the whole index fits in one page.
|
||||
DELIMITER |;
|
||||
CREATE PROCEDURE populate_t1()
|
||||
BEGIN
|
||||
DECLARE i INT DEFAULT 2;
|
||||
|
||||
WHILE i <= 5000 DO
|
||||
INSERT INTO t1 VALUES
|
||||
(i, 20, POINT(5,5));
|
||||
SET i = i + 1;
|
||||
END WHILE;
|
||||
END |
|
||||
DELIMITER ;|
|
||||
CALL populate_t1();
|
||||
|
||||
SELECT COUNT(*) FROM t1 WHERE ST_Contains(t1.p, (SELECT p FROM t1 WHERE category = 10));
|
||||
|
||||
SET autocommit = OFF;
|
||||
SET STATEMENT transaction_read_only = ON FOR
|
||||
SELECT COUNT(*) FROM t1 WHERE category = (SELECT category FROM t1 WHERE ST_EQUALS(p, POINT(1,2)));
|
||||
|
||||
# Cleanup
|
||||
SET autocommit = @old_autocommit;
|
||||
DROP PROCEDURE populate_t1;
|
||||
DROP TABLE t1;
|
||||
|
||||
@@ -9188,14 +9188,11 @@ ha_innobase::index_read(
|
||||
DBUG_RETURN(HA_ERR_ROLLBACK);
|
||||
}
|
||||
|
||||
/* For R-Tree index, we will always place the page lock to
|
||||
pages being searched */
|
||||
if (index->is_spatial() && !m_prebuilt->trx->will_lock) {
|
||||
if (trx_state != TRX_STATE_NOT_STARTED) {
|
||||
DBUG_RETURN(HA_ERR_READ_ONLY_TRANSACTION);
|
||||
} else {
|
||||
m_prebuilt->trx->will_lock = true;
|
||||
}
|
||||
/* will_lock is normally already set by external_lock()'s
|
||||
has_spatial_index() check; this is just a pre-start fallback. */
|
||||
if (index->is_spatial() && !m_prebuilt->trx->will_lock
|
||||
&& trx_state == TRX_STATE_NOT_STARTED) {
|
||||
m_prebuilt->trx->will_lock = true;
|
||||
}
|
||||
|
||||
/* Note that if the index for which the search template is built is not
|
||||
@@ -16715,7 +16712,11 @@ set_lock:
|
||||
|
||||
if (not_started
|
||||
&& (m_prebuilt->select_lock_type != LOCK_NONE
|
||||
|| m_prebuilt->stored_select_lock_type != LOCK_NONE)) {
|
||||
|| m_prebuilt->stored_select_lock_type != LOCK_NONE
|
||||
/* R-Tree search needs a page lock, so a table
|
||||
with a spatial index must not start as a
|
||||
non-locking transaction. */
|
||||
|| m_prebuilt->table->has_spatial_index())) {
|
||||
|
||||
trx->will_lock = true;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user