From a041c4a94bb3549034660b2d02ed9ca2b51afd2a Mon Sep 17 00:00:00 2001 From: Fujii Masao Date: Thu, 27 Aug 2026 23:33:27 +0900 Subject: [PATCH v1] Prevent REPACK (ANALYZE) from functions REPACK (ANALYZE) adjusts the transaction command state and active snapshots between repacking the table and running ANALYZE. This is not safe when executed from a function-like context such as a DO block, and can leave SPI and snapshot state behind, eventually causing a segmentation fault. Fix this by rejecting REPACK (ANALYZE) when it is executed below the top level. Plain transaction blocks and subtransactions remain allowed, as they do not require the same restriction. --- doc/src/sgml/ref/repack.sgml | 2 ++ src/backend/commands/repack.c | 13 +++++++++++++ src/test/regress/expected/cluster.out | 15 ++++++++++++++- src/test/regress/sql/cluster.sql | 12 +++++++++++- 4 files changed, 40 insertions(+), 2 deletions(-) diff --git a/doc/src/sgml/ref/repack.sgml b/doc/src/sgml/ref/repack.sgml index 0cb72b6b289..e540917885d 100644 --- a/doc/src/sgml/ref/repack.sgml +++ b/doc/src/sgml/ref/repack.sgml @@ -329,6 +329,8 @@ REPACK [ ( option [, ...] ) ] USING Applies 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 function, procedure, or + DO block. diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c index 477c86b2ba6..5b0af42ba7d 100644 --- a/src/backend/commands/repack.c +++ b/src/backend/commands/repack.c @@ -315,6 +315,19 @@ ExecRepack(ParseState *pstate, RepackStmt *stmt, bool isTopLevel) PreventInTransactionBlock(isTopLevel, "REPACK (CONCURRENTLY)"); } + if ((params.options & CLUOPT_ANALYZE) != 0) + { + /* + * REPACK (ANALYZE) switches transaction command state and active + * snapshots between repacking the table and analyzing it, so it must + * not run inside a function. + */ + if (!isTopLevel) + ereport(ERROR, + (errcode(ERRCODE_ACTIVE_SQL_TRANSACTION), + errmsg("REPACK (ANALYZE) cannot be executed from a function or procedure"))); + } + /* * 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/expected/cluster.out b/src/test/regress/expected/cluster.out index d1bc8a13286..597011a4d19 100644 --- a/src/test/regress/expected/cluster.out +++ b/src/test/regress/expected/cluster.out @@ -796,9 +796,22 @@ 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 also allowed in a transaction block. +BEGIN; +REPACK (ANALYZE) clstr_tst; +ROLLBACK; +-- REPACK (ANALYZE) is not allowed from a function. +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..0bfe09460ec 100644 --- a/src/test/regress/sql/cluster.sql +++ b/src/test/regress/sql/cluster.sql @@ -380,9 +380,19 @@ 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 also allowed in a transaction block. +BEGIN; +REPACK (ANALYZE) clstr_tst; +ROLLBACK; +-- REPACK (ANALYZE) is not allowed from a function. +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 -- 2.55.0