aboutsummaryrefslogtreecommitdiff
path: root/src/test
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2022-03-21 12:22:13 -0400
committerTom Lane <tgl@sss.pgh.pa.us>2022-03-21 12:22:13 -0400
commit7b6ec86532c2ca585d671239bba867fe380448ed (patch)
treeb8ff9888fea2cf8433cbe917bd3256ac45f348d1 /src/test
parent1f8bc448680bf93a974cb5f52d496514ff67720c (diff)
downloadpostgresql-7b6ec86532c2ca585d671239bba867fe380448ed.tar.gz
postgresql-7b6ec86532c2ca585d671239bba867fe380448ed.zip
Fix risk of deadlock failure while dropping a partitioned index.
DROP INDEX needs to lock the index's table before the index itself, else it will deadlock against ordinary queries that acquire the relation locks in that order. This is correctly mechanized for plain indexes by RangeVarCallbackForDropRelation; but in the case of a partitioned index, we neglected to lock the child tables in advance of locking the child indexes. We can fix that by traversing the inheritance tree and acquiring the needed locks in RemoveRelations, after we have acquired our locks on the parent partitioned table and index. While at it, do some refactoring to eliminate confusion between the actual and expected relkind in RangeVarCallbackForDropRelation. We can save a couple of syscache lookups too, by having that function pass back info that RemoveRelations will need. Back-patch to v11 where partitioned indexes were added. Jimmy Yih, Gaurab Dey, Tom Lane Discussion: https://postgr.es/m/BYAPR05MB645402330042E17D91A70C12BD5F9@BYAPR05MB6454.namprd05.prod.outlook.com
Diffstat (limited to 'src/test')
-rw-r--r--src/test/isolation/expected/partition-drop-index-locking.out100
-rw-r--r--src/test/isolation/isolation_schedule1
-rw-r--r--src/test/isolation/specs/partition-drop-index-locking.spec47
3 files changed, 148 insertions, 0 deletions
diff --git a/src/test/isolation/expected/partition-drop-index-locking.out b/src/test/isolation/expected/partition-drop-index-locking.out
new file mode 100644
index 00000000000..9acd51dfdef
--- /dev/null
+++ b/src/test/isolation/expected/partition-drop-index-locking.out
@@ -0,0 +1,100 @@
+Parsed test spec with 3 sessions
+
+starting permutation: s1begin s1lock s2begin s2drop s1select s3getlocks s1commit s3getlocks s2commit
+step s1begin: BEGIN;
+step s1lock: LOCK TABLE part_drop_index_locking_subpart_child IN ACCESS SHARE MODE;
+step s2begin: BEGIN;
+step s2drop: DROP INDEX part_drop_index_locking_idx; <waiting ...>
+step s1select: SELECT * FROM part_drop_index_locking_subpart_child;
+id
+--
+(0 rows)
+
+step s3getlocks:
+ SELECT s.query, c.relname, l.mode, l.granted
+ FROM pg_locks l
+ JOIN pg_class c ON l.relation = c.oid
+ JOIN pg_stat_activity s ON l.pid = s.pid
+ WHERE c.relname LIKE 'part_drop_index_locking%'
+ ORDER BY s.query, c.relname, l.mode, l.granted;
+
+query |relname |mode |granted
+----------------------------------------------------+---------------------------------------------+-------------------+-------
+DROP INDEX part_drop_index_locking_idx; |part_drop_index_locking |AccessExclusiveLock|t
+DROP INDEX part_drop_index_locking_idx; |part_drop_index_locking_idx |AccessExclusiveLock|t
+DROP INDEX part_drop_index_locking_idx; |part_drop_index_locking_subpart |AccessExclusiveLock|t
+DROP INDEX part_drop_index_locking_idx; |part_drop_index_locking_subpart_child |AccessExclusiveLock|f
+SELECT * FROM part_drop_index_locking_subpart_child;|part_drop_index_locking_subpart_child |AccessShareLock |t
+SELECT * FROM part_drop_index_locking_subpart_child;|part_drop_index_locking_subpart_child_id_idx |AccessShareLock |t
+SELECT * FROM part_drop_index_locking_subpart_child;|part_drop_index_locking_subpart_child_id_idx1|AccessShareLock |t
+(7 rows)
+
+step s1commit: COMMIT;
+step s2drop: <... completed>
+step s3getlocks:
+ SELECT s.query, c.relname, l.mode, l.granted
+ FROM pg_locks l
+ JOIN pg_class c ON l.relation = c.oid
+ JOIN pg_stat_activity s ON l.pid = s.pid
+ WHERE c.relname LIKE 'part_drop_index_locking%'
+ ORDER BY s.query, c.relname, l.mode, l.granted;
+
+query |relname |mode |granted
+---------------------------------------+--------------------------------------------+-------------------+-------
+DROP INDEX part_drop_index_locking_idx;|part_drop_index_locking |AccessExclusiveLock|t
+DROP INDEX part_drop_index_locking_idx;|part_drop_index_locking_idx |AccessExclusiveLock|t
+DROP INDEX part_drop_index_locking_idx;|part_drop_index_locking_subpart |AccessExclusiveLock|t
+DROP INDEX part_drop_index_locking_idx;|part_drop_index_locking_subpart_child |AccessExclusiveLock|t
+DROP INDEX part_drop_index_locking_idx;|part_drop_index_locking_subpart_child_id_idx|AccessExclusiveLock|t
+DROP INDEX part_drop_index_locking_idx;|part_drop_index_locking_subpart_id_idx |AccessExclusiveLock|t
+(6 rows)
+
+step s2commit: COMMIT;
+
+starting permutation: s1begin s1lock s2begin s2dropsub s1select s3getlocks s1commit s3getlocks s2commit
+step s1begin: BEGIN;
+step s1lock: LOCK TABLE part_drop_index_locking_subpart_child IN ACCESS SHARE MODE;
+step s2begin: BEGIN;
+step s2dropsub: DROP INDEX part_drop_index_locking_subpart_idx; <waiting ...>
+step s1select: SELECT * FROM part_drop_index_locking_subpart_child;
+id
+--
+(0 rows)
+
+step s3getlocks:
+ SELECT s.query, c.relname, l.mode, l.granted
+ FROM pg_locks l
+ JOIN pg_class c ON l.relation = c.oid
+ JOIN pg_stat_activity s ON l.pid = s.pid
+ WHERE c.relname LIKE 'part_drop_index_locking%'
+ ORDER BY s.query, c.relname, l.mode, l.granted;
+
+query |relname |mode |granted
+----------------------------------------------------+---------------------------------------------+-------------------+-------
+DROP INDEX part_drop_index_locking_subpart_idx; |part_drop_index_locking_subpart |AccessExclusiveLock|t
+DROP INDEX part_drop_index_locking_subpart_idx; |part_drop_index_locking_subpart_child |AccessExclusiveLock|f
+DROP INDEX part_drop_index_locking_subpart_idx; |part_drop_index_locking_subpart_idx |AccessExclusiveLock|t
+SELECT * FROM part_drop_index_locking_subpart_child;|part_drop_index_locking_subpart_child |AccessShareLock |t
+SELECT * FROM part_drop_index_locking_subpart_child;|part_drop_index_locking_subpart_child_id_idx |AccessShareLock |t
+SELECT * FROM part_drop_index_locking_subpart_child;|part_drop_index_locking_subpart_child_id_idx1|AccessShareLock |t
+(6 rows)
+
+step s1commit: COMMIT;
+step s2dropsub: <... completed>
+step s3getlocks:
+ SELECT s.query, c.relname, l.mode, l.granted
+ FROM pg_locks l
+ JOIN pg_class c ON l.relation = c.oid
+ JOIN pg_stat_activity s ON l.pid = s.pid
+ WHERE c.relname LIKE 'part_drop_index_locking%'
+ ORDER BY s.query, c.relname, l.mode, l.granted;
+
+query |relname |mode |granted
+-----------------------------------------------+---------------------------------------------+-------------------+-------
+DROP INDEX part_drop_index_locking_subpart_idx;|part_drop_index_locking_subpart |AccessExclusiveLock|t
+DROP INDEX part_drop_index_locking_subpart_idx;|part_drop_index_locking_subpart_child |AccessExclusiveLock|t
+DROP INDEX part_drop_index_locking_subpart_idx;|part_drop_index_locking_subpart_child_id_idx1|AccessExclusiveLock|t
+DROP INDEX part_drop_index_locking_subpart_idx;|part_drop_index_locking_subpart_idx |AccessExclusiveLock|t
+(4 rows)
+
+step s2commit: COMMIT;
diff --git a/src/test/isolation/isolation_schedule b/src/test/isolation/isolation_schedule
index 0dae483e827..8e870981501 100644
--- a/src/test/isolation/isolation_schedule
+++ b/src/test/isolation/isolation_schedule
@@ -90,6 +90,7 @@ test: predicate-hash
test: predicate-gist
test: predicate-gin
test: partition-concurrent-attach
+test: partition-drop-index-locking
test: partition-key-update-1
test: partition-key-update-2
test: partition-key-update-3
diff --git a/src/test/isolation/specs/partition-drop-index-locking.spec b/src/test/isolation/specs/partition-drop-index-locking.spec
new file mode 100644
index 00000000000..34e8b528b8d
--- /dev/null
+++ b/src/test/isolation/specs/partition-drop-index-locking.spec
@@ -0,0 +1,47 @@
+# Verify that DROP INDEX properly locks all downward sub-partitions
+# and partitions before locking the indexes.
+
+setup
+{
+ CREATE TABLE part_drop_index_locking (id int) PARTITION BY RANGE(id);
+ CREATE TABLE part_drop_index_locking_subpart PARTITION OF part_drop_index_locking FOR VALUES FROM (1) TO (100) PARTITION BY RANGE(id);
+ CREATE TABLE part_drop_index_locking_subpart_child PARTITION OF part_drop_index_locking_subpart FOR VALUES FROM (1) TO (100);
+ CREATE INDEX part_drop_index_locking_idx ON part_drop_index_locking(id);
+ CREATE INDEX part_drop_index_locking_subpart_idx ON part_drop_index_locking_subpart(id);
+}
+
+teardown
+{
+ DROP TABLE part_drop_index_locking;
+}
+
+# SELECT will take AccessShare lock first on the table and then on its index.
+# We can simulate the case where DROP INDEX starts between those steps
+# by manually taking the table lock beforehand.
+session s1
+step s1begin { BEGIN; }
+step s1lock { LOCK TABLE part_drop_index_locking_subpart_child IN ACCESS SHARE MODE; }
+step s1select { SELECT * FROM part_drop_index_locking_subpart_child; }
+step s1commit { COMMIT; }
+
+session s2
+step s2begin { BEGIN; }
+step s2drop { DROP INDEX part_drop_index_locking_idx; }
+step s2dropsub { DROP INDEX part_drop_index_locking_subpart_idx; }
+step s2commit { COMMIT; }
+
+session s3
+step s3getlocks {
+ SELECT s.query, c.relname, l.mode, l.granted
+ FROM pg_locks l
+ JOIN pg_class c ON l.relation = c.oid
+ JOIN pg_stat_activity s ON l.pid = s.pid
+ WHERE c.relname LIKE 'part_drop_index_locking%'
+ ORDER BY s.query, c.relname, l.mode, l.granted;
+}
+
+# Run DROP INDEX on top partitioned table
+permutation s1begin s1lock s2begin s2drop(s1commit) s1select s3getlocks s1commit s3getlocks s2commit
+
+# Run DROP INDEX on top sub-partition table
+permutation s1begin s1lock s2begin s2dropsub(s1commit) s1select s3getlocks s1commit s3getlocks s2commit