On Mon, Jun 29, 2026 at 2:21 PM shveta malik <[email protected]> wrote:
>
> IMO, we should silently skip CLT by expanding the check in
> AlterTableMoveAll() similar to system-tables.
>

Agreed, otherwise, it will break the entire bulk operation. I have
tried to fix this in the attached and modified the required tests. I
think we should merge 0001 and 0002, and move these tablespace related
tests to 0001.

-- 
With Regards,
Amit Kapila.
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 973d3607838..a3cda01d80f 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -6924,7 +6924,11 @@ ATSimplePermissions(AlterTableType cmdtype, Relation 
rel, int allowed_targets)
        /*
         * Conflict log tables are used internally for logical replication 
conflict
         * logging and should not be altered directly, as it could disrupt 
conflict
-        * logging.
+        * logging. Direct ALTER commands are already rejected during relation
+        * lookup in RangeVarCallbackForAlterRelation(), and AlterTableMoveAll()
+        * skips these tables, so a conflict log table does not normally reach 
here;
+        * this check guards any internal caller that arrives via
+        * AlterTableInternal().
         */
        if (IsConflictLogTableClass(rel->rd_rel))
                ereport(ERROR,
@@ -17592,11 +17596,16 @@ AlterTableMoveAll(AlterTableMoveAllStmt *stmt)
                 *
                 * Also, explicitly avoid any shared tables, temp tables, or 
TOAST
                 * (TOAST will be moved with the main table).
+                *
+                * Conflict log tables are system-managed for logical 
replication and
+                * cannot be altered directly, so skip them as well; otherwise 
a single
+                * such table in the source tablespace would abort the whole 
bulk move.
                 */
                if (IsCatalogNamespace(relForm->relnamespace) ||
                        relForm->relisshared ||
                        isAnyTempNamespace(relForm->relnamespace) ||
-                       IsToastNamespace(relForm->relnamespace))
+                       IsToastNamespace(relForm->relnamespace) ||
+                       IsConflictLogTableNamespace(relForm->relnamespace))
                        continue;
 
                /* Only move the object type requested */
diff --git a/src/test/subscription/t/035_conflicts.pl 
b/src/test/subscription/t/035_conflicts.pl
index 86d3f8903c5..4260c4a5803 100644
--- a/src/test/subscription/t/035_conflicts.pl
+++ b/src/test/subscription/t/035_conflicts.pl
@@ -716,12 +716,23 @@ ok( $node_A->poll_query_until(
        "the slot 'pg_conflict_detection' has been dropped on Node A");
 
 ###############################################################################
-# Verify that ATSimplePermissions blocks moving a CLT via
-# ALTER TABLE ALL IN TABLESPACE.  We use an isolated database containing only
-# the CLT so no other tables are moved before the error fires.
+# A conflict log table is system-managed and cannot be altered directly, so
+# moving it to another tablespace must be rejected.
+###############################################################################
+
+(undef, undef, $stderr) = $node_subscriber->psql('postgres',
+       "ALTER TABLE $clt SET TABLESPACE pg_default");
+like(
+       $stderr,
+       qr/permission denied: "pg_conflict_log_\d+" is a conflict log table/,
+       "moving a conflict log table with ALTER TABLE SET TABLESPACE is 
rejected");
+
+###############################################################################
+# ALTER TABLE ALL IN TABLESPACE must skip conflict log tables, the same way it
+# skips catalog and TOAST tables, instead of failing.  Use an isolated database
+# so the bulk move only touches the objects created here.
 ###############################################################################
 
-# Create an isolated database with a single CLT and no user tables.
 $node_subscriber->safe_psql('postgres', "CREATE DATABASE clt_ts_test");
 $node_subscriber->safe_psql('clt_ts_test',
        "CREATE SUBSCRIPTION sub_ts_test
@@ -729,6 +740,9 @@ $node_subscriber->safe_psql('clt_ts_test',
             PUBLICATION pub
             WITH (connect=false, conflict_log_destination='table')");
 
+# A plain user table that should be moved, alongside the CLT that must not be.
+$node_subscriber->safe_psql('clt_ts_test', "CREATE TABLE user_tbl (i int)");
+
 # Create a tablespace backed by a directory inside the data dir.
 my $ts_dir = $node_subscriber->data_dir . '/backup_space';
 mkdir($ts_dir)
@@ -736,23 +750,31 @@ mkdir($ts_dir)
 $node_subscriber->safe_psql('postgres',
        "CREATE TABLESPACE backup_space LOCATION '$ts_dir'");
 
-# The CLT is the only non-system table in pg_default of clt_ts_test, so
-# ALTER TABLE ALL IN TABLESPACE hits it immediately and ATSimplePermissions
-# raises the expected error.
-(undef, undef, $stderr) = $node_subscriber->psql('clt_ts_test',
+# The bulk move succeeds: the user table is relocated while the CLT is skipped.
+$node_subscriber->safe_psql('clt_ts_test',
        "ALTER TABLE ALL IN TABLESPACE pg_default SET TABLESPACE backup_space");
-like(
-       $stderr,
-       qr/permission denied: "pg_conflict_log_\d+" is a conflict log table/,
-       "ATSimplePermissions blocks moving CLT via ALTER TABLE ALL IN 
TABLESPACE");
 
-# Cleanup
+is( $node_subscriber->safe_psql(
+               'clt_ts_test',
+               "SELECT reltablespace <> 0 FROM pg_class WHERE relname = 
'user_tbl'"),
+       't',
+       "ALTER TABLE ALL IN TABLESPACE moves an ordinary user table");
+
+is( $node_subscriber->safe_psql(
+               'clt_ts_test',
+               "SELECT count(*) FROM pg_class c JOIN pg_subscription s
+                  ON c.relname = 'pg_conflict_log_' || s.oid
+                WHERE s.subname = 'sub_ts_test' AND c.reltablespace <> 0"),
+       '0',
+       "ALTER TABLE ALL IN TABLESPACE skips the conflict log table");
+
+# Cleanup.  The subscription has no real publisher connection, so detach its
+# slot before dropping it.
 $node_subscriber->safe_psql('clt_ts_test',
        "ALTER SUBSCRIPTION sub_ts_test DISABLE");
 $node_subscriber->safe_psql('clt_ts_test',
        "ALTER SUBSCRIPTION sub_ts_test SET (slot_name = NONE)");
-$node_subscriber->safe_psql('clt_ts_test',
-       "DROP SUBSCRIPTION sub_ts_test");
+$node_subscriber->safe_psql('clt_ts_test', "DROP SUBSCRIPTION sub_ts_test");
 $node_subscriber->safe_psql('postgres', "DROP DATABASE clt_ts_test");
 $node_subscriber->safe_psql('postgres', "DROP TABLESPACE backup_space");
 

Reply via email to