From b577bc4b61474b65a80b5ab8b5333c6ccd751e81 Mon Sep 17 00:00:00 2001
From: Antonin Houska <ah@cybertec.at>
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

