From e46dc6155a478f7bfb06729f77f02b1d145d930a Mon Sep 17 00:00:00 2001 From: Ayush Tiwari Date: Sun, 30 Aug 2026 07:42:21 +0000 Subject: [PATCH v1 1/2] Prevent orphaned tablespace dependencies DROP TABLESPACE checked pg_shdepend without first locking the tablespace object. A concurrent command could therefore add a shared dependency after that check, allowing both commands to commit and leaving an object that referenced a deleted tablespace. Take an exclusive lock on the tablespace before checking its shared dependencies. This conflicts with the lock taken when recording a shared dependency and makes either DROP or the dependency creator recheck after waiting. Add isolation coverage for both dependency-first and drop-first races. --- src/backend/commands/tablespace.c | 5 ++ .../tablespace-dependency-locking.out | 36 ++++++++++ src/test/isolation/isolation_schedule | 1 + .../specs/tablespace-dependency-locking.spec | 66 +++++++++++++++++++ 4 files changed, 108 insertions(+) create mode 100644 src/test/isolation/expected/tablespace-dependency-locking.out create mode 100644 src/test/isolation/specs/tablespace-dependency-locking.spec diff --git a/src/backend/commands/tablespace.c b/src/backend/commands/tablespace.c index e3c4a7fac87..99f6ecf2fd0 100644 --- a/src/backend/commands/tablespace.c +++ b/src/backend/commands/tablespace.c @@ -70,6 +70,7 @@ #include "miscadmin.h" #include "postmaster/bgwriter.h" #include "storage/fd.h" +#include "storage/lmgr.h" #include "storage/lwlock.h" #include "storage/procsignal.h" #include "storage/standby.h" @@ -457,6 +458,10 @@ DropTableSpace(DropTableSpaceStmt *stmt) aclcheck_error(ACLCHECK_NO_PRIV, OBJECT_TABLESPACE, tablespacename); + /* Prevent new shared dependencies while we drop the tablespace. */ + LockSharedObject(TableSpaceRelationId, tablespaceoid, 0, + AccessExclusiveLock); + /* Check for pg_shdepend entries depending on this tablespace */ if (checkSharedDependencies(TableSpaceRelationId, tablespaceoid, &detail, &detail_log)) diff --git a/src/test/isolation/expected/tablespace-dependency-locking.out b/src/test/isolation/expected/tablespace-dependency-locking.out new file mode 100644 index 00000000000..74d2b718ab2 --- /dev/null +++ b/src/test/isolation/expected/tablespace-dependency-locking.out @@ -0,0 +1,36 @@ +Parsed test spec with 3 sessions + +starting permutation: s1_begin s1_create_table_in_tablespace s2_drop_tablespace s1_commit s1_drop_table s1_drop_tablespace +step s1_begin: BEGIN; +step s1_create_table_in_tablespace: + CREATE TABLE tbl_tablespace (a int) PARTITION BY RANGE (a) + TABLESPACE regress_dependency_tablespace; + +step s2_drop_tablespace: DROP TABLESPACE regress_dependency_tablespace; +step s1_commit: COMMIT; +step s2_drop_tablespace: <... completed> +ERROR: tablespace "regress_dependency_tablespace" cannot be dropped because some objects depend on it +step s1_drop_table: DROP TABLE tbl_tablespace; +step s1_drop_tablespace: DROP TABLESPACE regress_dependency_tablespace; + +starting permutation: s1_begin s1_alter_tablespace s2_drop_tablespace s3_create_table_in_dropped_tablespace s1_rollback +step s1_begin: BEGIN; +step s1_alter_tablespace: + ALTER TABLESPACE regress_dependency_tablespace + SET (random_page_cost = 1.1); + +step s2_drop_tablespace: DROP TABLESPACE regress_dependency_tablespace; +step s3_create_table_in_dropped_tablespace: + DO $$ + BEGIN + EXECUTE 'CREATE TABLE tbl_tablespace (a int) PARTITION BY RANGE (a) + TABLESPACE regress_dependency_tablespace'; + EXCEPTION WHEN undefined_object THEN + RAISE NOTICE 'referenced tablespace was concurrently dropped'; + END + $$; + +step s1_rollback: ROLLBACK; +step s2_drop_tablespace: <... completed> +s3: NOTICE: referenced tablespace was concurrently dropped +step s3_create_table_in_dropped_tablespace: <... completed> diff --git a/src/test/isolation/isolation_schedule b/src/test/isolation/isolation_schedule index 1fcf4e63238..fc45d504d2b 100644 --- a/src/test/isolation/isolation_schedule +++ b/src/test/isolation/isolation_schedule @@ -128,5 +128,6 @@ test: matview-write-skew test: lock-nowait test: for-portion-of test: ddl-dependency-locking +test: tablespace-dependency-locking test: pub-concurrent-drop test: drop-owned-grant diff --git a/src/test/isolation/specs/tablespace-dependency-locking.spec b/src/test/isolation/specs/tablespace-dependency-locking.spec new file mode 100644 index 00000000000..fa0b00aeb99 --- /dev/null +++ b/src/test/isolation/specs/tablespace-dependency-locking.spec @@ -0,0 +1,66 @@ +# Test that concurrent DROP TABLESPACE and CREATE TABLE do not leave behind +# references to a non-existent tablespace. + +setup +{ + SET allow_in_place_tablespaces = true; +} + +setup +{ + CREATE TABLESPACE regress_dependency_tablespace LOCATION ''; +} + +teardown +{ + DROP TABLESPACE IF EXISTS regress_dependency_tablespace; +} + +session "s1" + +step "s1_begin" { BEGIN; } +step "s1_create_table_in_tablespace" +{ + CREATE TABLE tbl_tablespace (a int) PARTITION BY RANGE (a) + TABLESPACE regress_dependency_tablespace; +} +step "s1_alter_tablespace" +{ + ALTER TABLESPACE regress_dependency_tablespace + SET (random_page_cost = 1.1); +} +step "s1_commit" { COMMIT; } +step "s1_rollback" { ROLLBACK; } +step "s1_drop_table" { DROP TABLE tbl_tablespace; } +step "s1_drop_tablespace" { DROP TABLESPACE regress_dependency_tablespace; } + +teardown +{ + SET client_min_messages = warning; + DROP TABLE IF EXISTS tbl_tablespace; +} + +session "s2" + +step "s2_drop_tablespace" { DROP TABLESPACE regress_dependency_tablespace; } + +session "s3" + +step "s3_create_table_in_dropped_tablespace" +{ + DO $$ + BEGIN + EXECUTE 'CREATE TABLE tbl_tablespace (a int) PARTITION BY RANGE (a) + TABLESPACE regress_dependency_tablespace'; + EXCEPTION WHEN undefined_object THEN + RAISE NOTICE 'referenced tablespace was concurrently dropped'; + END + $$; +} + +# create table - drop tablespace +permutation "s1_begin" "s1_create_table_in_tablespace" "s2_drop_tablespace" "s1_commit" "s1_drop_table" "s1_drop_tablespace" + +# drop tablespace - create table; ALTER makes DROP wait while deleting the +# catalog tuple, after DROP has checked for dependencies +permutation "s1_begin" "s1_alter_tablespace" "s2_drop_tablespace" "s3_create_table_in_dropped_tablespace" "s1_rollback" -- 2.43.0