1
0
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:
Mohammad Tafzeel Shams
2026-06-23 15:26:01 +05:30
parent 412d8fa525
commit 32e5c5726f
3 changed files with 89 additions and 9 deletions

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;
}