From 43172981a95f4541ca97e7b369fe72720bbbf4bc Mon Sep 17 00:00:00 2001
From: Bharath Rupireddy <bharath.rupireddyforpostgres@gmail.com>
Date: Mon, 31 Aug 2026 21:13:46 +0000
Subject: [PATCH v2] Fix error message for concurrent repack on materialized
 views.

Previously, running REPACK (CONCURRENTLY) on a materialized view
failed with a confusing "has no identity index" error.

REPACK (CONCURRENTLY) replays changes decoded from WAL, but a
materialized view produces none, since it cannot be modified by
DML and REFRESH rewrites it through a transient heap that is not
logically decoded.

Fix this by reporting a clear error for materialized views up
front, alongside the existing checks for system catalogs and
TOAST tables, and document the limitation.

Author: Bharath Rupireddy <bharath.rupireddyforpostgres@gmail.com>
Reviewed-by: Kiran Kaki <itskkpg@gmail.com>
Discussion: https://postgr.es/m/CALj2ACXPvy4_LkQ0nHKgbZjAGK9fkDqRYUvXA3hgwxPm_X7Eng@mail.gmail.com
Backpatch-through: 19
---
 contrib/test_decoding/expected/repack.out | 7 +++++++
 contrib/test_decoding/sql/repack.sql      | 6 ++++++
 doc/src/sgml/ref/repack.sgml              | 6 ++++++
 src/backend/commands/repack.c             | 8 ++++++++
 4 files changed, 27 insertions(+)

diff --git a/contrib/test_decoding/expected/repack.out b/contrib/test_decoding/expected/repack.out
index 5ddc63238c5..5ed65399758 100644
--- a/contrib/test_decoding/expected/repack.out
+++ b/contrib/test_decoding/expected/repack.out
@@ -81,6 +81,13 @@ REPACK (CONCURRENTLY) repack_conc_unlogged;
 ERROR:  cannot execute REPACK (CONCURRENTLY) on relation "repack_conc_unlogged"
 HINT:  REPACK (CONCURRENTLY) is only allowed for permanent relations.
 DROP TABLE repack_conc_unlogged;
+-- Doesn't support materialized views
+CREATE MATERIALIZED VIEW repack_conc_matview AS SELECT 1 AS i;
+CREATE UNIQUE INDEX ON repack_conc_matview (i);
+REPACK (CONCURRENTLY) repack_conc_matview;
+ERROR:  cannot execute REPACK (CONCURRENTLY) on relation "repack_conc_matview"
+DETAIL:  This operation is not supported for materialized views.
+DROP MATERIALIZED VIEW repack_conc_matview;
 -- Doesn't support tables with REPLICA IDENTITY NOTHING, even if they have a primary key
 CREATE TABLE repack_conc_replident (i int PRIMARY KEY);
 ALTER TABLE repack_conc_replident REPLICA IDENTITY NOTHING;
diff --git a/contrib/test_decoding/sql/repack.sql b/contrib/test_decoding/sql/repack.sql
index f461f5479f4..08e08fee686 100644
--- a/contrib/test_decoding/sql/repack.sql
+++ b/contrib/test_decoding/sql/repack.sql
@@ -59,6 +59,12 @@ CREATE UNLOGGED TABLE repack_conc_unlogged (i int PRIMARY KEY);
 REPACK (CONCURRENTLY) repack_conc_unlogged;
 DROP TABLE repack_conc_unlogged;
 
+-- Doesn't support materialized views
+CREATE MATERIALIZED VIEW repack_conc_matview AS SELECT 1 AS i;
+CREATE UNIQUE INDEX ON repack_conc_matview (i);
+REPACK (CONCURRENTLY) repack_conc_matview;
+DROP MATERIALIZED VIEW repack_conc_matview;
+
 -- Doesn't support tables with REPLICA IDENTITY NOTHING, even if they have a primary key
 CREATE TABLE repack_conc_replident (i int PRIMARY KEY);
 ALTER TABLE repack_conc_replident REPLICA IDENTITY NOTHING;
diff --git a/doc/src/sgml/ref/repack.sgml b/doc/src/sgml/ref/repack.sgml
index 0cb72b6b289..777681252ef 100644
--- a/doc/src/sgml/ref/repack.sgml
+++ b/doc/src/sgml/ref/repack.sgml
@@ -279,6 +279,12 @@ REPACK [ ( <replaceable class="parameter">option</replaceable> [, ...] ) ] USING
         </para>
        </listitem>
 
+       <listitem>
+        <para>
+          The relation is a materialized view.
+        </para>
+       </listitem>
+
        <listitem>
         <para>
           The table is a system catalog or a <acronym>TOAST</acronym> table.
diff --git a/src/backend/commands/repack.c b/src/backend/commands/repack.c
index 477c86b2ba6..b85d3efb3e6 100644
--- a/src/backend/commands/repack.c
+++ b/src/backend/commands/repack.c
@@ -907,6 +907,14 @@ check_concurrent_repack_requirements(Relation rel, Oid *ident_idx_p)
 				errhint("%s is only allowed for permanent relations.",
 						"REPACK (CONCURRENTLY)"));
 
+	/* A materialized view produces no logically decoded changes. */
+	if (rel->rd_rel->relkind == RELKIND_MATVIEW)
+		ereport(ERROR,
+				errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				errmsg("cannot execute %s on relation \"%s\"",
+					   "REPACK (CONCURRENTLY)", RelationGetRelationName(rel)),
+				errdetail_relkind_not_supported(rel->rd_rel->relkind));
+
 	/*
 	 * With NOTHING, WAL does not contain the old tuple; FULL is not yet
 	 * supported.
-- 
2.47.3

