From 6115ab269fa7fb384f8af0b3aff8cffe3c8dff51 Mon Sep 17 00:00:00 2001 From: Alexandre Felipe Date: Wed, 16 Sep 2026 08:42:31 +0100 Subject: [PATCH-v3 1/4] logging and testcase This patch adds clearer logging and a (failing) test case on regress tablespace to be fixed by the next patch. To facilitate the review this patch keep DEBUG1 messages in the testcase To spot the bug keep an eie on repeated ctids, e.g. SELECT ctid, a FROM defer_t ORDER BY a; ctid | a -------+--- (0,1) | 1 (0,2) | 3 <- (0,2) | 3 <- (0,3) | 4 --- src/backend/catalog/storage.c | 13 + src/backend/commands/tablecmds.c | 8 + src/backend/commands/tablespace.c | 8 + src/backend/utils/cache/relcache.c | 5 + src/test/regress/expected/tablespace_xact.out | 1 + src/test/regress/parallel_schedule | 2 +- src/test/regress/sql/tablespace_xact.sql | 251 ++++++++++++++++++ 7 files changed, 287 insertions(+), 1 deletion(-) create mode 100644 src/test/regress/expected/tablespace_xact.out create mode 100644 src/test/regress/sql/tablespace_xact.sql diff --git a/src/backend/catalog/storage.c b/src/backend/catalog/storage.c index e443a4993c5..db4653b27e8 100644 --- a/src/backend/catalog/storage.c +++ b/src/backend/catalog/storage.c @@ -26,6 +26,7 @@ #include "access/xlogutils.h" #include "catalog/storage.h" #include "catalog/storage_xlog.h" +#include "common/relpath.h" #include "miscadmin.h" #include "pgstat.h" #include "storage/bulk_write.h" @@ -150,6 +151,9 @@ RelationCreateStorage(RelFileLocator rlocator, char relpersistence, srel = smgropen(rlocator, procNumber); smgrcreate(srel, MAIN_FORKNUM, false); + elog(DEBUG1, "RelationCreateStorage: %s", + relpathperm(rlocator, MAIN_FORKNUM).str); + if (needs_wal) log_smgrcreate(&srel->smgr_rlocator.locator, MAIN_FORKNUM); @@ -208,6 +212,10 @@ RelationDropStorage(Relation rel) { PendingRelDelete *pending; + elog(DEBUG1, "RelationDropStorage: oid %u %s", + RelationGetRelid(rel), + relpathperm(rel->rd_locator, MAIN_FORKNUM).str); + /* Add the relation to the list of stuff to delete at commit */ pending = (PendingRelDelete *) MemoryContextAlloc(TopMemoryContext, sizeof(PendingRelDelete)); @@ -701,6 +709,11 @@ smgrDoPendingDeletes(bool isCommit) { SMgrRelation srel; + elog(DEBUG1, + "smgrDoPendingDeletes: %s %s", + isCommit ? "commit" : "abort", + relpathperm(pending->rlocator, MAIN_FORKNUM).str); + srel = smgropen(pending->rlocator, pending->procNumber); /* allocate the initial array, or extend it, if needed */ diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 2f073ddb84a..1e6d0b1611f 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -69,6 +69,7 @@ #include "commands/user.h" #include "commands/vacuum.h" #include "common/int.h" +#include "common/relpath.h" #include "executor/executor.h" #include "foreign/fdwapi.h" #include "foreign/foreign.h" @@ -17560,6 +17561,13 @@ ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode) newrlocator.relNumber = newrelfilenumber; newrlocator.spcOid = newTableSpace; + elog(DEBUG1, + "SET TABLESPACE copy: rel \"%s\" oid %u, from %s to %s", + RelationGetRelationName(rel), + RelationGetRelid(rel), + relpathperm(rel->rd_locator, MAIN_FORKNUM).str, + relpathperm(newrlocator, MAIN_FORKNUM).str); + /* hand off to AM to actually create new rel storage and copy the data */ if (rel->rd_rel->relkind == RELKIND_INDEX) { diff --git a/src/backend/commands/tablespace.c b/src/backend/commands/tablespace.c index e01fb2db913..bba656e9b78 100644 --- a/src/backend/commands/tablespace.c +++ b/src/backend/commands/tablespace.c @@ -176,6 +176,8 @@ TablespaceCreateDbspace(Oid spcOid, Oid dbOid, bool isRedo) errmsg("could not create directory \"%s\": %m", dir))); } + + elog(DEBUG1, "TablespaceCreateDbspace: %s", dir); } LWLockRelease(TablespaceCreateLock); @@ -677,6 +679,12 @@ create_tablespace_directories(const char *location, const Oid tablespaceoid) errmsg("could not create symbolic link \"%s\": %m", linkloc))); + if (in_place) + elog(DEBUG1, "tablespace directories: %s", linkloc); + else + elog(DEBUG1, "tablespace directories: %s -> %s", + linkloc, location_with_version_dir); + pfree(linkloc); pfree(location_with_version_dir); } diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c index d8f04a05309..b73c42e7519 100644 --- a/src/backend/utils/cache/relcache.c +++ b/src/backend/utils/cache/relcache.c @@ -61,6 +61,7 @@ #include "catalog/pg_statistic_ext.h" #include "catalog/pg_subscription.h" #include "catalog/pg_tablespace.h" +#include "common/relpath.h" #include "catalog/pg_trigger.h" #include "catalog/pg_type.h" #include "catalog/schemapg.h" @@ -3975,6 +3976,10 @@ RelationAssumeNewRelfilelocator(Relation relation) if (relation->rd_firstRelfilelocatorSubid == InvalidSubTransactionId) relation->rd_firstRelfilelocatorSubid = relation->rd_newRelfilelocatorSubid; + elog(DEBUG1, "RelationAssumeNewRelfilelocator: oid %u %s", + RelationGetRelid(relation), + relpathperm(relation->rd_locator, MAIN_FORKNUM).str); + /* Flag relation as needing eoxact cleanup (to clear these fields) */ EOXactListAdd(relation); } diff --git a/src/test/regress/expected/tablespace_xact.out b/src/test/regress/expected/tablespace_xact.out new file mode 100644 index 00000000000..85d3d35f332 --- /dev/null +++ b/src/test/regress/expected/tablespace_xact.out @@ -0,0 +1 @@ +-- place holder -- \ No newline at end of file diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule index 75063f87a4a..865bde70d5c 100644 --- a/src/test/regress/parallel_schedule +++ b/src/test/regress/parallel_schedule @@ -145,4 +145,4 @@ test: fast_default # run tablespace test at the end because it drops the tablespace created during # setup that other tests may use. -test: tablespace +test: tablespace tablespace_xact diff --git a/src/test/regress/sql/tablespace_xact.sql b/src/test/regress/sql/tablespace_xact.sql new file mode 100644 index 00000000000..43fc20c7505 --- /dev/null +++ b/src/test/regress/sql/tablespace_xact.sql @@ -0,0 +1,251 @@ + +-- +-- Deferred heap copy for ALTER TABLE SET TABLESPACE on indexed tables +-- +SET allow_in_place_tablespaces = true; +SET client_min_messages = DEBUG1; +SET enable_seqscan = off; +SET enable_bitmapscan = off; + +CREATE SCHEMA tablespace_xact; +SET search_path TO tablespace_xact; + +CREATE TABLESPACE xact_tblspace LOCATION ''; +CREATE TABLESPACE xact_tblspace2 LOCATION ''; +PREPARE check_tablespace AS + SELECT c.relname, coalesce(s.spcname, '(default)') AS tablespace + FROM pg_class c + LEFT JOIN pg_tablespace s ON s.oid = c.reltablespace + LEFT JOIN pg_namespace n ON n.oid = c.relnamespace + WHERE n.nspname = 'tablespace_xact' + ORDER BY 1; + +-- COMMIT +CREATE TABLE defer_t (a int); +EXECUTE check_tablespace; +CREATE INDEX defer_t_idx ON defer_t (a); +INSERT INTO defer_t VALUES (1); +BEGIN; +INSERT INTO defer_t VALUES (2); +ALTER TABLE defer_t SET TABLESPACE xact_tblspace; +INSERT INTO defer_t VALUES (3); +COMMIT; +EXECUTE check_tablespace; +INSERT INTO defer_t VALUES (4), (5); +SELECT ctid, a FROM defer_t ORDER BY a; +DROP TABLE defer_t; + +-- ROLLBACK +CREATE TABLE defer_t (a int); +CREATE INDEX defer_t_idx ON defer_t (a); +INSERT INTO defer_t VALUES (1); +BEGIN; +INSERT INTO defer_t VALUES (2); +ALTER TABLE defer_t SET TABLESPACE xact_tblspace; +INSERT INTO defer_t VALUES (3); +ROLLBACK; +EXECUTE check_tablespace; +INSERT INTO defer_t VALUES (4), (5); +SELECT ctid, a FROM defer_t ORDER BY a; +DROP TABLE defer_t; + +-- COMMIT ENDING UP IN THE SAME SPACE + +CREATE TABLE defer_t (a int); +CREATE INDEX defer_t_idx ON defer_t (a); +INSERT INTO defer_t VALUES (1); +BEGIN; +INSERT INTO defer_t VALUES (2); +ALTER TABLE defer_t SET TABLESPACE xact_tblspace; +INSERT INTO defer_t VALUES (3); +ALTER TABLE defer_t SET TABLESPACE pg_default; +INSERT INTO defer_t VALUES (4); +COMMIT; +EXECUTE check_tablespace; +INSERT INTO defer_t VALUES (5); +SELECT ctid, a FROM defer_t ORDER BY a; +DROP TABLE defer_t; + +-- PREPARE TRANSACTION, COMMIT PREPARED +CREATE TABLE defer_t (a int); +CREATE INDEX defer_t_idx ON defer_t (a); +INSERT INTO defer_t VALUES (1); +BEGIN; +INSERT INTO defer_t VALUES (2); +ALTER TABLE defer_t SET TABLESPACE xact_tblspace; +INSERT INTO defer_t VALUES (3); +PREPARE TRANSACTION 'defer_tblsp_prep_commit'; +COMMIT PREPARED 'defer_tblsp_prep_commit'; +EXECUTE check_tablespace; +INSERT INTO defer_t VALUES (4), (5); +SELECT ctid, a FROM defer_t ORDER BY a; +DROP TABLE defer_t; + +-- PREPARE TRANSACTION, ROLLBACK PREPARED +CREATE TABLE defer_t (a int); +CREATE INDEX defer_t_idx ON defer_t (a); +INSERT INTO defer_t VALUES (1); +BEGIN; +INSERT INTO defer_t VALUES (2); +ALTER TABLE defer_t SET TABLESPACE xact_tblspace2; +INSERT INTO defer_t VALUES (3); +PREPARE TRANSACTION 'defer_tblsp_prep_rollback'; +ROLLBACK PREPARED 'defer_tblsp_prep_rollback'; +EXECUTE check_tablespace; +INSERT INTO defer_t VALUES (4), (5); +SELECT ctid, a FROM defer_t ORDER BY a; +DROP TABLE defer_t; + +-- Subtransaction: move in savepoint, subcommit, then top-level commit +CREATE TABLE defer_t (a int); +CREATE INDEX defer_t_idx ON defer_t (a); +INSERT INTO defer_t VALUES (1); +BEGIN; +INSERT INTO defer_t VALUES (2); +SAVEPOINT sp1; +INSERT INTO defer_t VALUES (3); +ALTER TABLE defer_t SET TABLESPACE xact_tblspace; +INSERT INTO defer_t VALUES (4); +RELEASE SAVEPOINT sp1; +INSERT INTO defer_t VALUES (5); +COMMIT; +EXECUTE check_tablespace; +INSERT INTO defer_t VALUES (6), (7); +SELECT ctid, a FROM defer_t ORDER BY a; +DROP TABLE defer_t; + +-- Subtransaction: move in savepoint, then rollback to savepoint +CREATE TABLE defer_t (a int); +CREATE INDEX defer_t_idx ON defer_t (a); +INSERT INTO defer_t VALUES (1); +BEGIN; +INSERT INTO defer_t VALUES (2); +SAVEPOINT sp1; +INSERT INTO defer_t VALUES (3); +ALTER TABLE defer_t SET TABLESPACE xact_tblspace; +INSERT INTO defer_t VALUES (4); +ROLLBACK TO SAVEPOINT sp1; +INSERT INTO defer_t VALUES (5); +COMMIT; +EXECUTE check_tablespace; +INSERT INTO defer_t VALUES (6), (7), (8); +SELECT ctid, a FROM defer_t ORDER BY a; +DROP TABLE defer_t; + +-- Subtransaction: subcommit move, then top-level rollback cancels move +CREATE TABLE defer_t (a int); +CREATE INDEX defer_t_idx ON defer_t (a); +INSERT INTO defer_t VALUES (1); +BEGIN; +INSERT INTO defer_t VALUES (2); +SAVEPOINT sp1; +INSERT INTO defer_t VALUES (3); +ALTER TABLE defer_t SET TABLESPACE xact_tblspace; +INSERT INTO defer_t VALUES (4); +RELEASE SAVEPOINT sp1; +INSERT INTO defer_t VALUES (5); +ROLLBACK; +EXECUTE check_tablespace; +INSERT INTO defer_t VALUES (6), (7), (8); +SELECT ctid, a FROM defer_t ORDER BY a; +DROP TABLE defer_t; + +-- Subtransaction: two deferred moves in one transaction +CREATE TABLE defer_t (a int); +CREATE INDEX defer_t_idx ON defer_t (a); +INSERT INTO defer_t VALUES (1); +BEGIN; +INSERT INTO defer_t VALUES (2); +SAVEPOINT sp1; +INSERT INTO defer_t VALUES (3); +ALTER TABLE defer_t SET TABLESPACE xact_tblspace; +INSERT INTO defer_t VALUES (4); +SAVEPOINT sp2; +INSERT INTO defer_t VALUES (5); +ALTER TABLE defer_t SET TABLESPACE xact_tblspace2; +INSERT INTO defer_t VALUES (6); +COMMIT; +EXECUTE check_tablespace; +INSERT INTO defer_t VALUES (7), (8), (9); +SELECT ctid, a FROM defer_t ORDER BY a; +DROP TABLE defer_t; + +CREATE TABLE defer_t (a int); +CREATE INDEX defer_t_idx ON defer_t (a); +INSERT INTO defer_t VALUES (1); +BEGIN; +INSERT INTO defer_t VALUES (2); +SAVEPOINT sp1; +INSERT INTO defer_t VALUES (3); +ALTER TABLE defer_t SET TABLESPACE xact_tblspace; +INSERT INTO defer_t VALUES (4); +SAVEPOINT sp2; +INSERT INTO defer_t VALUES (5); +ALTER TABLE defer_t SET TABLESPACE xact_tblspace2; +INSERT INTO defer_t VALUES (6); +ROLLBACK; +EXECUTE check_tablespace; +INSERT INTO defer_t VALUES (7), (8), (9); +SELECT ctid, a FROM defer_t ORDER BY a; +DROP TABLE defer_t; + +-- Partitioned table: SET TABLESPACE in a transaction; new partitions use it +CREATE TABLE defer_part (a int) PARTITION BY RANGE (a); +CREATE TABLE defer_part_p0 PARTITION OF defer_part FOR VALUES FROM (0) TO (10); +CREATE INDEX ON defer_part(a); + +INSERT INTO defer_part VALUES (1); +EXECUTE check_tablespace; +BEGIN; +ALTER TABLE defer_part SET TABLESPACE xact_tblspace; +-- defer_part tablespace is updated, but partitions are not moved +EXECUTE check_tablespace; +-- new partitions created in the new table space +CREATE TABLE defer_part_p1 PARTITION OF defer_part FOR VALUES FROM (10) TO (20); +INSERT INTO defer_part VALUES (15); +ALTER TABLE defer_part SET TABLESPACE xact_tblspace2; +CREATE TABLE defer_part_p2 PARTITION OF defer_part FOR VALUES FROM (20) TO (30); +ALTER TABLE defer_part SET TABLESPACE pg_default; +CREATE TABLE defer_part_p3 PARTITION OF defer_part FOR VALUES FROM (30) TO (40); +EXECUTE check_tablespace; +ROLLBACK; -- rollback delete partitions created in the transaction +EXECUTE check_tablespace; + +-- Partitioned table: SET TABLESPACE in a transaction; new partitions use it +CREATE TABLE defer_part (a int) PARTITION BY RANGE (a); +CREATE TABLE defer_part_p0 PARTITION OF defer_part FOR VALUES FROM (0) TO (10); +CREATE INDEX ON defer_part(a); +INSERT INTO defer_part VALUES (1); +EXECUTE check_tablespace; +BEGIN; +ALTER TABLE defer_part SET TABLESPACE xact_tblspace; +-- defer_part tablespace is updated, but partitions are not moved +EXECUTE check_tablespace; +-- new partitions created in the new table space +CREATE TABLE defer_part_p1 PARTITION OF defer_part FOR VALUES FROM (10) TO (20); +INSERT INTO defer_part VALUES (15); +ALTER TABLE defer_part SET TABLESPACE xact_tblspace2; +CREATE TABLE defer_part_p2 PARTITION OF defer_part FOR VALUES FROM (20) TO (30); +ALTER TABLE defer_part SET TABLESPACE pg_default; +CREATE TABLE defer_part_p3 PARTITION OF defer_part FOR VALUES FROM (30) TO (40); +EXECUTE check_tablespace; +ROLLBACK; -- rollback delete partitions created in the transaction +EXECUTE check_tablespace; + +BEGIN; +ALTER TABLE defer_part SET TABLESPACE xact_tblspace; +CREATE TABLE defer_part_p1 PARTITION OF defer_part FOR VALUES FROM (10) TO (20); +INSERT INTO defer_part VALUES (15); +ALTER TABLE defer_part SET TABLESPACE xact_tblspace2; +CREATE TABLE defer_part_p2 PARTITION OF defer_part FOR VALUES FROM (20) TO (30); +ALTER TABLE defer_part SET TABLESPACE pg_default; +CREATE TABLE defer_part_p3 PARTITION OF defer_part FOR VALUES FROM (30) TO (40); +COMMIT; -- commit persists partitions created in the transaction +EXECUTE check_tablespace; +DROP TABLE defer_part; +EXECUTE check_tablespace; + +DROP TABLESPACE xact_tblspace; +DROP TABLESPACE xact_tblspace2; + +DROP SCHEMA CASCADE tablespace_xact; \ No newline at end of file -- 2.53.0