Hi everyone,

I believe I was replying to the wrong thread earlier.

The issue I was looking into is the crash caused by executing REPACK
(ANALYZE) inside a transaction block.

REPACK (ANALYZE) performs transaction management internally, including
committing and starting a new transaction while processing the relation. It
therefore cannot safely be executed from an existing transaction block.

I have prepared a patch that rejects REPACK (ANALYZE) with
PreventInTransactionBlock(), consistent with the existing restriction
for REPACK
(CONCURRENTLY). I also added a regression test covering execution inside a
transaction block.

The patch applies cleanly to the current tree and passes git diff --check.
Patch attached.

Regards,
Osama Abdul Qader

On Wed, Sep 2, 2026 at 2:17 PM Antonin Houska <[email protected]> wrote:

> Alvaro Herrera <[email protected]> wrote:
>
> > On 2026-Sep-02, Fujii Masao wrote:
> >
> > > + * that's just consistent with VACUUM (FULL, ANALYZE), which is a
> > > + * synonym for REPACK (ANALYZE).
> > >
> > > Is VACUUM (FULL, ANALYZE) really a synonym for REPACK (ANALYZE)?
> >
> > That's the intent, at least.  If there are things that work differently,
> > I would strive to change them so that they do work the same.  However,
> > some such changes might be too invasive for pg19, but I would still see
> > about changing those in pg20.
> >
> > Now, maybe there are things about VACUUM FULL ANALYZE that we don't like
> > (perhaps, for instance, they exist solely because of even older
> > backwards compatibility concerns) that we would prefer not to have in
> > REPACK.  I don't know if anything of that sort exists, but if so, I
> > would propose to seek decisions for each thing individually.
>
> Maybe the question was about the wording - "synonym" might indicate that
> both
> commands execute the same code. Perhaps the comment should rather say that
> REPACK (ANALYZE) is (intended to be) a replacement of VACUUM (FULL,
> ANALYZE).
>
> --
> Antonin Houska
> Web: https://www.cybertec-postgresql.com
>
>
>
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index edff54e734e..3a482a73ceb 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -314,6 +314,16 @@ ExecRepack(ParseState *pstate, RepackStmt *stmt, bool isTopLevel)
 		PreventInTransactionBlock(isTopLevel, "REPACK (CONCURRENTLY)");
 	}
 
+	else if ((params.options & CLUOPT_ANALYZE) != 0)
+	{
+		/*
+		 * REPACK (ANALYZE) performs transaction management internally.
+		 * It therefore cannot be executed from a transaction block or
+		 * from a function.
+		 */
+		PreventInTransactionBlock(isTopLevel, "REPACK (ANALYZE)");
+	}
+
 	/*
 	 * If a single relation is specified, process it and we're done ... unless
 	 * the relation is a partitioned table, in which case we fall through.
diff --git a/src/test/regress/sql/cluster.sql b/src/test/regress/sql/cluster.sql
index e7a62367adf..cb170731147 100644
--- a/src/test/regress/sql/cluster.sql
+++ b/src/test/regress/sql/cluster.sql
@@ -380,6 +380,10 @@ INSERT INTO clstr_tst (b, c) VALUES (1111, 'this should fail');
 SELECT conname FROM pg_constraint WHERE conrelid = 'clstr_tst'::regclass
 ORDER BY 1;
 
+-- REPACK (ANALYZE) must not be executed inside a transaction block
+BEGIN;
+REPACK (ANALYZE) clstr_tst;
+ROLLBACK;
 -- Verify partial analyze works
 REPACK (ANALYZE) clstr_tst (a);
 REPACK (ANALYZE) clstr_tst;

Reply via email to