From d3bcc907cdbfdea63ee4a72d979674b445a83812 Mon Sep 17 00:00:00 2001
From: PegoraroF10 <marcos@f10.com.br>
Date: Thu, 30 Jul 2026 11:31:06 -0300
Subject: [PATCH v2] doc: Update REPACK-related table rewrite documentation

Adding REPACK introduced table-rewrite behavior that overlaps with
VACUUM FULL and CLUSTER, but some documentation still mentioned only one
of these commands or described the progress and locking behavior
imprecisely.

Clarify that table-rewriting operations, including REPACK, can change
CTIDs.  Also describe VACUUM FULL, CLUSTER, and REPACK consistently as
table-rewriting operations for disk-space recovery where appropriate,
document the pg_stat_progress_repack and compatibility
pg_stat_progress_cluster views consistently, and clarify the ACCESS
EXCLUSIVE locking behavior of REPACK, including CONCURRENTLY.

Backpatch to v19, where REPACK was introduced.
---
 doc/src/sgml/ddl.sgml         |  4 +++-
 doc/src/sgml/maintenance.sgml | 45 +++++++++++++++++++++--------------
 doc/src/sgml/monitoring.sgml  | 24 +++++++++++--------
 doc/src/sgml/mvcc.sgml        | 12 ++++++----
 4 files changed, 52 insertions(+), 33 deletions(-)

diff --git a/doc/src/sgml/ddl.sgml b/doc/src/sgml/ddl.sgml
index 9006b947e74..4ae6ff2d614 100644
--- a/doc/src/sgml/ddl.sgml
+++ b/doc/src/sgml/ddl.sgml
@@ -1568,7 +1568,9 @@ CREATE TABLE circles (
       although the <structfield>ctid</structfield> can be used to
       locate the row version very quickly, a row's
       <structfield>ctid</structfield> will change if it is
-      updated or moved by <command>VACUUM FULL</command>.  Therefore
+      updated, or moved by a table-rewriting operation such as
+      <command>VACUUM FULL</command>, <command>CLUSTER</command>, or
+      <command>REPACK</command>.  Therefore
       <structfield>ctid</structfield> should not be used as a row
       identifier.  A primary key should be used to identify logical rows.
      </para>
diff --git a/doc/src/sgml/maintenance.sgml b/doc/src/sgml/maintenance.sgml
index 33ab4edf87c..c0972b04212 100644
--- a/doc/src/sgml/maintenance.sgml
+++ b/doc/src/sgml/maintenance.sgml
@@ -171,11 +171,13 @@
     future reuse.  However, it will not return the space to the operating
     system, except in the special case where one or more pages at the
     end of a table become entirely free and an exclusive table lock can be
-    easily obtained.  In contrast, <command>VACUUM FULL</command> actively compacts
-    tables by writing a complete new version of the table file with no dead
-    space.  This minimizes the size of the table, but can take a long time.
-    It also requires extra disk space for the new copy of the table, until
-    the operation completes.
+    easily obtained.  In contrast, table-rewriting commands such as
+    <command>VACUUM FULL</command>, <command>CLUSTER</command>, and
+    <command>REPACK</command> actively compact tables by writing a complete
+    new version of the table file without the dead space left by old row
+    versions.  This minimizes the size of the table, but can take a long time.
+    It also requires extra disk space for the new copy of the table, until the
+    operation completes.
    </para>
 
    <para>
@@ -186,12 +188,14 @@
     is not to keep tables at their minimum size, but to maintain steady-state
     usage of disk space: each table occupies space equivalent to its
     minimum size plus however much space gets used up between vacuum runs.
-    Although <command>VACUUM FULL</command> can be used to shrink a table back
-    to its minimum size and return the disk space to the operating system,
-    there is not much point in this if the table will just grow again in the
-    future.  Thus, moderately-frequent standard <command>VACUUM</command> runs are a
-    better approach than infrequent <command>VACUUM FULL</command> runs for
-    maintaining heavily-updated tables.
+    Although table-rewriting compaction operations such as
+    <command>VACUUM FULL</command>, <command>CLUSTER</command>, and
+    <command>REPACK</command> can be used to shrink a table back to its
+    minimum size and return the disk space to the operating system, there is
+    not much point in this if the table will just grow again in the future.
+    Thus, moderately-frequent standard <command>VACUUM</command> runs are a
+    better approach than infrequent use of such operations for maintaining
+    heavily-updated tables.
    </para>
 
    <para>
@@ -199,7 +203,8 @@
     doing all the work at night when load is low.
     The difficulty with doing vacuuming according to a fixed schedule
     is that if a table has an unexpected spike in update activity, it may
-    get bloated to the point that <command>VACUUM FULL</command> is really necessary
+    get bloated to the point that a table-rewriting compaction operation such
+    as <command>VACUUM FULL</command> or <command>REPACK</command> may be needed
     to reclaim space.  Using the autovacuum daemon alleviates this problem,
     since the daemon schedules vacuuming dynamically in response to update
     activity.  It is unwise to disable the daemon completely unless you
@@ -227,16 +232,20 @@
     a table contains large numbers of dead row versions as a result of
     massive update or delete activity.  If you have such a table and
     you need to reclaim the excess disk space it occupies, you will need
-    to use <command>VACUUM FULL</command>, or alternatively
-    <link linkend="sql-cluster"><command>CLUSTER</command></link>
+    to use <command>VACUUM FULL</command>,
+    <link linkend="sql-cluster"><command>CLUSTER</command></link>,
+    <link linkend="sql-repack"><command>REPACK</command></link>,
     or one of the table-rewriting variants of
     <link linkend="sql-altertable"><command>ALTER TABLE</command></link>.
     These commands rewrite an entire new copy of the table and build
     new indexes for it.  All these options require an
-    <literal>ACCESS EXCLUSIVE</literal> lock.  Note that
-    they also temporarily use extra disk space approximately equal to the size
-    of the table, since the old copies of the table and indexes can't be
-    released until the new ones are complete.
+    <literal>ACCESS EXCLUSIVE</literal> lock, except that
+    <command>REPACK</command> can be run with <literal>CONCURRENTLY</literal>,
+    in which case the <literal>ACCESS EXCLUSIVE</literal> lock is held only
+    while swapping the table and index files.  Note that they also
+    temporarily use extra disk space approximately equal to the size of the
+    table, since the old copies of the table and indexes can't be released
+    until the new ones are complete.
    </para>
    </tip>
 
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 32cb6fdbd76..53d81ac63d1 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -417,7 +417,7 @@ postgres   27093  0.0  0.0  30096  2752 ?        Ss   11:34   0:00 postgres: ser
       <entry><structname>pg_stat_progress_repack</structname><indexterm><primary>pg_stat_progress_repack</primary></indexterm></entry>
       <entry>One row for each backend running <command>REPACK</command>,
        <command>CLUSTER</command> or <command>VACUUM FULL</command>, showing current progress.
-       <xref linkend="repack-progress-reporting"/>.
+       See <xref linkend="repack-progress-reporting"/>.
       </entry>
      </row>
 
@@ -6726,7 +6726,8 @@ FROM pg_stat_get_backend_idset() AS backendid;
    <command>VACUUM FULL</command> is running,
    the backwards-compatibility <structname>pg_stat_progress_cluster</structname>
    view will
-   contain a row for each backend that is currently running either command.
+   contain a row for each backend that is currently running one of these
+   commands.
    The tables below describe the information that will be reported and
    provide information about how to interpret it.
   </para>
@@ -7400,10 +7401,11 @@ FROM pg_stat_get_backend_idset() AS backendid;
   </indexterm>
 
   <para>
-   Whenever <command>REPACK</command> is running,
+   Whenever <command>REPACK</command>, <command>CLUSTER</command>, or
+   <command>VACUUM FULL</command> is running,
    the <structname>pg_stat_progress_repack</structname> view will contain a
-   row for each backend that is currently running the command.  The tables
-   below describe the information that will be reported and provide
+   row for each backend that is currently running one of these commands.
+   The tables below describe the information that will be reported and provide
    information about how to interpret it.
   </para>
 
@@ -7454,7 +7456,7 @@ FROM pg_stat_get_backend_idset() AS backendid;
        <structfield>relid</structfield> <type>oid</type>
       </para>
       <para>
-       OID of the table being repacked.
+       OID of the table being processed.
       </para></entry>
      </row>
 
@@ -7463,8 +7465,8 @@ FROM pg_stat_get_backend_idset() AS backendid;
        <structfield>command</structfield> <type>text</type>
       </para>
       <para>
-       The command that is running. Either <command>REPACK</command> or
-       <command>VACUUM FULL</command>, or <command>CLUSTER</command>.
+       The command that is running. One of <command>CLUSTER</command>,
+       <command>REPACK</command>, or <command>VACUUM FULL</command>.
       </para></entry>
      </row>
 
@@ -7657,10 +7659,12 @@ FROM pg_stat_get_backend_idset() AS backendid;
    currently vacuuming.  The tables below describe the information
    that will be reported and provide information about how to interpret it.
    Progress for <command>VACUUM FULL</command> commands is reported via
-   <structname>pg_stat_progress_cluster</structname>
+   <structname>pg_stat_progress_repack</structname>, and is also visible via
+   the backwards-compatibility <structname>pg_stat_progress_cluster</structname>
    because both <command>VACUUM FULL</command> and <command>CLUSTER</command>
    rewrite the table, while regular <command>VACUUM</command> only modifies it
-   in place. See <xref linkend="cluster-progress-reporting"/>.
+   in place. See <xref linkend="repack-progress-reporting"/> and
+   <xref linkend="cluster-progress-reporting"/>.
   </para>
 
   <table id="pg-stat-progress-vacuum-view" xreflabel="pg_stat_progress_vacuum">
diff --git a/doc/src/sgml/mvcc.sgml b/doc/src/sgml/mvcc.sgml
index 9cb52302f23..ff01e97ea3d 100644
--- a/doc/src/sgml/mvcc.sgml
+++ b/doc/src/sgml/mvcc.sgml
@@ -1095,10 +1095,14 @@ ERROR:  could not serialize access due to read/write dependencies among transact
         <para>
          Acquired by the <command>DROP TABLE</command>,
          <command>TRUNCATE</command>, <command>REINDEX</command>,
-         <command>CLUSTER</command>, <command>VACUUM FULL</command>,
-         and <command>REFRESH MATERIALIZED VIEW</command> (without
-         <option>CONCURRENTLY</option>)
-         commands. Many forms of <command>ALTER INDEX</command> and <command>ALTER TABLE</command> also acquire
+         <command>CLUSTER</command>, <command>VACUUM FULL</command>, and
+         <command>REFRESH MATERIALIZED VIEW</command> (without
+         <option>CONCURRENTLY</option>) commands.
+         <command>REPACK</command> also acquires this lock mode.  When run
+         with <option>CONCURRENTLY</option>, <command>REPACK</command> acquires
+         it only while swapping the table and index files.
+         Many forms of <command>ALTER INDEX</command> and
+         <command>ALTER TABLE</command> also acquire
          a lock at this level. This is also the default lock mode for
          <command>LOCK TABLE</command> statements that do not specify
          a mode explicitly.
-- 
2.55.0

