From 7a3bddc92493e8d32a94d8b079e2ed44d047413f Mon Sep 17 00:00:00 2001 From: Heikki Linnakangas Date: Mon, 5 Feb 2024 11:01:30 +0200 Subject: [PATCH 1/2] Fix assertion if index is dropped during REFRESH CONCURRENTLY When assertions are disabled, the built SQL statement is invalid and you get a "syntax error". So this isn't a serious problem, but let's avoid the assertion failure. Backpatch to all supported versions. Reviewed-by: Noah Misch --- src/backend/commands/matview.c | 7 +++++-- src/test/regress/expected/matview.out | 16 ++++++++++++++++ src/test/regress/sql/matview.sql | 17 +++++++++++++++++ 3 files changed, 38 insertions(+), 2 deletions(-) diff --git a/src/backend/commands/matview.c b/src/backend/commands/matview.c index dc8efd4d892..ad42c9e3c83 100644 --- a/src/backend/commands/matview.c +++ b/src/backend/commands/matview.c @@ -1352,9 +1352,12 @@ refresh_by_match_merge(Oid matviewOid, Oid tempOid, Oid relowner, * * ExecRefreshMatView() checks that after taking the exclusive lock on the * matview. So at least one unique index is guaranteed to exist here - * because the lock is still being held; so an Assert seems sufficient. + * because the lock is still being held. (One known exception is if a + * function called as part of refreshing the matview drops the index. + * That's a pretty silly thing to do.) */ - Assert(foundUniqueIndex); + if (!foundUniqueIndex) + elog(ERROR, "could not find suitable unique index on materialized view"); diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out index 1f33fdbc1f7..db5c4ea6ac8 100644 --- a/src/test/regress/expected/matview.out +++ b/src/test/regress/expected/matview.out @@ -619,6 +619,22 @@ REFRESH MATERIALIZED VIEW mvtest_mv_foo; REFRESH MATERIALIZED VIEW CONCURRENTLY mvtest_mv_foo; DROP OWNED BY regress_user_mvtest CASCADE; DROP ROLE regress_user_mvtest; +-- Concurrent refresh requires a unique index on the materialized +-- view. Test what happens if it's dropped during the refresh. +CREATE OR REPLACE FUNCTION mvtest_drop_the_index() + RETURNS bool AS $$ +BEGIN + EXECUTE 'DROP INDEX IF EXISTS mvtest_drop_idx'; + RETURN true; +END; +$$ LANGUAGE plpgsql; +CREATE MATERIALIZED VIEW drop_idx_matview AS + SELECT 1 as i WHERE mvtest_drop_the_index(); +NOTICE: index "mvtest_drop_idx" does not exist, skipping +CREATE UNIQUE INDEX mvtest_drop_idx ON drop_idx_matview (i); +REFRESH MATERIALIZED VIEW CONCURRENTLY drop_idx_matview; +ERROR: could not find suitable unique index on materialized view +DROP MATERIALIZED VIEW drop_idx_matview; -- clean up -- make sure that create WITH NO DATA works via SPI BEGIN; CREATE FUNCTION mvtest_func() diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql index 9b4e0d7d0e9..1b26b26ab38 100644 --- a/src/test/regress/sql/matview.sql +++ b/src/test/regress/sql/matview.sql @@ -236,6 +236,23 @@ REFRESH MATERIALIZED VIEW CONCURRENTLY mvtest_mv_foo; DROP OWNED BY regress_user_mvtest CASCADE; DROP ROLE regress_user_mvtest; +-- Concurrent refresh requires a unique index on the materialized +-- view. Test what happens if it's dropped during the refresh. +CREATE OR REPLACE FUNCTION mvtest_drop_the_index() + RETURNS bool AS $$ +BEGIN + EXECUTE 'DROP INDEX IF EXISTS mvtest_drop_idx'; + RETURN true; +END; +$$ LANGUAGE plpgsql; + +CREATE MATERIALIZED VIEW drop_idx_matview AS + SELECT 1 as i WHERE mvtest_drop_the_index(); + +CREATE UNIQUE INDEX mvtest_drop_idx ON drop_idx_matview (i); +REFRESH MATERIALIZED VIEW CONCURRENTLY drop_idx_matview; +DROP MATERIALIZED VIEW drop_idx_matview; -- clean up + -- make sure that create WITH NO DATA works via SPI BEGIN; CREATE FUNCTION mvtest_func() From eea4f516fb9dc0d5ae34a1ecf6b5a70d0d4ca646 Mon Sep 17 00:00:00 2001 From: reshke Date: Mon, 23 Mar 2026 09:32:54 +0000 Subject: [PATCH 2/2] Add allow_segment_DML to utility test function --- src/test/regress/expected/matview.out | 2 ++ src/test/regress/sql/matview.sql | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out index db5c4ea6ac8..17a7173415b 100644 --- a/src/test/regress/expected/matview.out +++ b/src/test/regress/expected/matview.out @@ -621,6 +621,7 @@ DROP OWNED BY regress_user_mvtest CASCADE; DROP ROLE regress_user_mvtest; -- Concurrent refresh requires a unique index on the materialized -- view. Test what happens if it's dropped during the refresh. +SET allow_segment_DML = ON; CREATE OR REPLACE FUNCTION mvtest_drop_the_index() RETURNS bool AS $$ BEGIN @@ -635,6 +636,7 @@ CREATE UNIQUE INDEX mvtest_drop_idx ON drop_idx_matview (i); REFRESH MATERIALIZED VIEW CONCURRENTLY drop_idx_matview; ERROR: could not find suitable unique index on materialized view DROP MATERIALIZED VIEW drop_idx_matview; -- clean up +RESET allow_segment_DML; -- make sure that create WITH NO DATA works via SPI BEGIN; CREATE FUNCTION mvtest_func() diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql index 1b26b26ab38..ec117f79af2 100644 --- a/src/test/regress/sql/matview.sql +++ b/src/test/regress/sql/matview.sql @@ -238,6 +238,7 @@ DROP ROLE regress_user_mvtest; -- Concurrent refresh requires a unique index on the materialized -- view. Test what happens if it's dropped during the refresh. +SET allow_segment_DML = ON; CREATE OR REPLACE FUNCTION mvtest_drop_the_index() RETURNS bool AS $$ BEGIN @@ -252,6 +253,7 @@ CREATE MATERIALIZED VIEW drop_idx_matview AS CREATE UNIQUE INDEX mvtest_drop_idx ON drop_idx_matview (i); REFRESH MATERIALIZED VIEW CONCURRENTLY drop_idx_matview; DROP MATERIALIZED VIEW drop_idx_matview; -- clean up +RESET allow_segment_DML; -- make sure that create WITH NO DATA works via SPI BEGIN;