postgres.git / summary / log / commit / refs
commit 25b21c0bb712b615f22cf5fcc176764a416368bd
Author: Álvaro Herrera <alvherre@kurilemu.de>
Date: Tue Sep 08 12:06:06 2026 +0000
Reject REPACK (ANALYZE) in a transaction block
The current implementation doesn't support it correctly, leading to
mysterious warnings and some worse problems. Have the combination throw
an error.
This is intended as a temporary stopgap until we can implement it in a
future release, as it's a useful feature.
Author: Fujii Masao <masao.fujii@gmail.com>
Author: Antonin Houska <ah@cybertec.at>
Author: Osama Abdul Qader <osamaabdulqader.cs@gmail.com>
Reported-by: Nathan Bossart <nathandbossart@gmail.com>
Backpatch-through: 19
Discussion: https://postgr.es/m/apBCEl4hBHlq0axm@nathan
doc/src/sgml/ref/repack.sgml | 2 ++
src/backend/commands/repack.c | 13 ++++++++++++-
src/test/regress/expected/cluster.out | 17 ++++++++++++++++-
src/test/regress/sql/cluster.sql | 17 ++++++++++++++++-
4 files changed, 46 insertions(+), 3 deletions(-)
diff --git a/doc/src/sgml/ref/repack.sgml b/doc/src/sgml/ref/repack.sgml
index 0cb72b6b289..d9c7c9e6b95 100644
--- a/doc/src/sgml/ref/repack.sgml
+++ b/doc/src/sgml/ref/repack.sgml
@@ -329,6 +329,8 @@ REPACK [ ( <replaceable class="parameter">option</replaceable> [, ...] ) ] USING
<para>
Applies <xref linkend="sql-analyze"/> on the table after repacking. This is
currently only supported when a single (non-partitioned) table is specified.
+ This option cannot be used inside a transaction block, or from a function,
+ procedure, or <command>DO</command> block.
</para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 477c86b2ba6..89aa038d23d 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -303,7 +303,7 @@ ExecRepack(ParseState *pstate, RepackStmt *stmt, bool isTopLevel)
if ((params.options & CLUOPT_CONCURRENT) != 0)
{
/*
- * Make sure we're not in a transaction block.
+ * In concurrent mode, make sure we're not in a transaction block.
*
* The reason is that repack_setup_logical_decoding() could wait
* indefinitely for our XID to complete. (The deadlock detector would
@@ -314,6 +314,17 @@ ExecRepack(ParseState *pstate, RepackStmt *stmt, bool isTopLevel)
*/
PreventInTransactionBlock(isTopLevel, "REPACK (CONCURRENTLY)");
}
+ else if ((params.options & CLUOPT_ANALYZE) != 0)
+ {
+ /*
+ * With ANALYZE, process_single_relation() would commit the current
+ * transaction and start a new one, which would break our state if
+ * we're in a transaction block or PL-execution environment. Reject
+ * the option in that case. It may be possible to remove this
+ * restriction in the future.
+ */
+ PreventInTransactionBlock(isTopLevel, "REPACK (ANALYZE)");
+ }
/*
* If a single relation is specified, process it and we're done ... unless
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index d1bc8a13286..64a9c35fcd0 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -796,9 +796,24 @@ ORDER BY 1;
clstr_tst_pkey
(3 rows)
--- Verify partial analyze works
+-- Verify REPACK (ANALYZE) works, including partial analyze.
REPACK (ANALYZE) clstr_tst (a);
REPACK (ANALYZE) clstr_tst;
+-- Plain REPACK is allowed in a transaction block.
+BEGIN;
+REPACK clstr_tst;
+ROLLBACK;
+-- REPACK (ANALYZE) is not allowed in a transaction block.
+BEGIN;
+REPACK (ANALYZE) clstr_tst;
+ERROR: REPACK (ANALYZE) cannot run inside a transaction block
+ROLLBACK;
+-- REPACK is allowed within a PL, as long as there's no ANALYZE.
+DO $$ BEGIN EXECUTE 'REPACK clstr_tst'; END $$;
+DO $$ BEGIN EXECUTE 'REPACK (ANALYZE) clstr_tst'; END $$;
+ERROR: REPACK (ANALYZE) cannot be executed from a function or procedure
+CONTEXT: SQL statement "REPACK (ANALYZE) clstr_tst"
+PL/pgSQL function inline_code_block line 1 at EXECUTE
REPACK (VERBOSE) clstr_tst (a);
ERROR: ANALYZE option must be specified when a column list is provided
-- REPACK w/o argument performs no ordering, so we can only check which tables
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index e7a62367adf..8c8df698b31 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -380,9 +380,24 @@ INSERT INTO clstr_tst (b, c) VALUES (1111, 'this should fail');
SELECT conname FROM pg_constraint WHERE conrelid = 'clstr_tst'::regclass
ORDER BY 1;
--- Verify partial analyze works
+-- Verify REPACK (ANALYZE) works, including partial analyze.
REPACK (ANALYZE) clstr_tst (a);
REPACK (ANALYZE) clstr_tst;
+
+-- Plain REPACK is allowed in a transaction block.
+BEGIN;
+REPACK clstr_tst;
+ROLLBACK;
+
+-- REPACK (ANALYZE) is not allowed in a transaction block.
+BEGIN;
+REPACK (ANALYZE) clstr_tst;
+ROLLBACK;
+
+-- REPACK is allowed within a PL, as long as there's no ANALYZE.
+DO $$ BEGIN EXECUTE 'REPACK clstr_tst'; END $$;
+DO $$ BEGIN EXECUTE 'REPACK (ANALYZE) clstr_tst'; END $$;
+
REPACK (VERBOSE) clstr_tst (a);
-- REPACK w/o argument performs no ordering, so we can only check which tables