Nathan Bossart <[email protected]> wrote:

> On Fri, Aug 28, 2026 at 01:09:59AM +0900, Fujii Masao wrote:
> > On Thu, Aug 27, 2026 at 10:56 PM Nathan Bossart
> > <[email protected]> wrote:
> >> Presumably we need to handle transaction blocks a bit like how vacuum()
> >> does.  Or maybe even prevent REPACK (ANALYZE) within a transaction block.
> > 
> > I looked into this a bit. I think the problem is not ordinary
> > transaction blocks themselves, but non-top-level execution, such as the
> > DO block in the reproducer.
> > 
> > So the attached patch rejects only non-top-level REPACK (ANALYZE)
> > commands.
> 
> Hm.  Couldn't we do something like the in_outer_xact/use_own_xacts stuff in
> vacuum() to get it working instead?

I think there are just two different concepts (for historical reasons?):
vacuum_rel() expects no active transaction on entry, while cluster_rel()
handles transaction boundaries on its own.

Since REPACK (ANALYZE) is effectively VACUUM (FULL, ANALYZE), I'd prefer the
same behavior, i.e. prohibiting execution both in a transaction block and in a
function:

postgres=# BEGIN; VACUUM (FULL, ANALYZE) t; END;
BEGIN
ERROR:  VACUUM cannot run inside a transaction block
ROLLBACK

postgres=# DO $$ BEGIN EXECUTE 'VACUUM (FULL, ANALYZE) t'; END $$;
ERROR:  VACUUM cannot be executed from a function or procedure
CONTEXT:  SQL statement "VACUUM (FULL, ANALYZE) t"
PL/pgSQL function inline_code_block line 1 at EXECUTE

postgres=# BEGIN; REPACK (ANALYZE) t; END;
BEGIN
ERROR:  REPACK (ANALYZE) cannot run inside a transaction block
ROLLBACK

postgres=# DO $$ BEGIN EXECUTE 'REPACK (ANALYZE) t'; END $$;
ERROR:  REPACK (ANALYZE) cannot be executed from a function or procedure
CONTEXT:  SQL statement "REPACK (ANALYZE) t"
PL/pgSQL function inline_code_block line 1 at EXECUTE

The attached patch does that.

-- 
Antonin Houska
Web: https://www.cybertec-postgresql.com

>From b577bc4b61474b65a80b5ab8b5333c6ccd751e81 Mon Sep 17 00:00:00 2001
From: Antonin Houska <[email protected]>
Date: Fri, 28 Aug 2026 19:53:26 +0200
Subject: [PATCH] Do not allow REPACK (ANALYZE) in function and in transaction
 block.

If REPACK (ANALYZE) is called from a pl/pgsql function, cluster_rel() might
start a new transaction while SPI session is in progress. Use
PreventInTransactionBlock() to avoid that.

That function also raises error if REPACK (ANALYZE) is called in a transaction
block, but that's fine: VACUUM (FULL, ANALYZE) - a synonym of REPACK (ANALYZE)
- also raises ERROR in that case.
---
 src/backend/commands/repack.c | 14 ++++++++++++++
 1 file changed, 14 insertions(+)

diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 477c86b2ba6..81877029199 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -314,6 +314,20 @@ ExecRepack(ParseState *pstate, RepackStmt *stmt, bool isTopLevel)
 		 */
 		PreventInTransactionBlock(isTopLevel, "REPACK (CONCURRENTLY)");
 	}
+	else if ((params.options & CLUOPT_ANALYZE) != 0)
+	{
+		/*
+		 * Technically, transaction block is not a problem for REPACK
+		 * (ANALYZE), but if it's called from a pl/pgsql function,
+		 * cluster_rel() might start a new transaction while SPI session is in
+		 * progress. Make sure ERROR is raised instead.
+		 *
+		 * This way we also prohibit execution in a transaction block, but
+		 * that's just consistent with VACUUM (FULL, ANALYZE), which is a
+		 * synonym for REPACK (ANALYZE).
+		 */
+		PreventInTransactionBlock(isTopLevel, "REPACK (ANALYZE)");
+	}
 
 	/*
 	 * If a single relation is specified, process it and we're done ... unless
-- 
2.52.0

Reply via email to