From 89c0924995b1b34993cec1e392e23299f2a71ffb Mon Sep 17 00:00:00 2001
From: Andrew Dunstan <andrew@dunslane.net>
Date: Sun, 25 Oct 2026 11:58:07 -0400
Subject: [PATCH v14 1/3] Use AccessExclusiveLock for unrecognized options in
 ALTER ... SET/RESET

AlterTableGetRelOptionsLockLevel() runs before the relation is opened,
so an extension AM's options might not yet be registered in the
current backend.  It skipped names it didn't find, and the command
then took ShareUpdateExclusiveLock even for options declared to need
AccessExclusiveLock.  For example, ALTER INDEX ... SET (length = 80)
on a bloom index took a weaker lock in a new session than after
LOAD 'bloom'.

Fix by returning AccessExclusiveLock if any option isn't registered.
Add a bloom regression test.

Author: Rui Zhao <zhaorui126@gmail.com>
Reviewed-by: Andrew Dunstan <andrew@dunslane.net>
Discussion: https://www.postgresql.org/message-id/CAHWVJhFfwJxkWrSJ=aruXt0kRP_+Pz3HcTZTssjOnTF1UensjQ@mail.gmail.com
---
 contrib/bloom/expected/bloom.out       | 28 ++++++++++++++++++++++++++
 contrib/bloom/sql/bloom.sql            | 19 +++++++++++++++++
 src/backend/access/common/reloptions.c | 10 +++++++++
 3 files changed, 57 insertions(+)

diff --git a/contrib/bloom/expected/bloom.out b/contrib/bloom/expected/bloom.out
index edc855121e1..2393f8932c4 100644
--- a/contrib/bloom/expected/bloom.out
+++ b/contrib/bloom/expected/bloom.out
@@ -228,3 +228,31 @@ CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (length=0);
 ERROR:  value 0 out of bounds for option "length"
 CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (col1=0);
 ERROR:  value 0 out of bounds for option "col1"
+\set VERBOSITY default
+-- In a new backend, bloom's options are not yet registered when ALTER INDEX
+-- chooses its lock level; the lock taken must still be the one bloom
+-- declares.
+\c
+BEGIN;
+ALTER INDEX bloomidx SET (length = 80);
+SELECT mode FROM pg_locks
+  WHERE pid = pg_backend_pid() AND relation = 'bloomidx'::regclass
+  ORDER BY mode;
+        mode         
+---------------------
+ AccessExclusiveLock
+(1 row)
+
+ROLLBACK;
+\c
+BEGIN;
+ALTER INDEX bloomidx RESET (col1);
+SELECT mode FROM pg_locks
+  WHERE pid = pg_backend_pid() AND relation = 'bloomidx'::regclass
+  ORDER BY mode;
+        mode         
+---------------------
+ AccessExclusiveLock
+(1 row)
+
+ROLLBACK;
diff --git a/contrib/bloom/sql/bloom.sql b/contrib/bloom/sql/bloom.sql
index fa63b301c6e..03f7b8a7348 100644
--- a/contrib/bloom/sql/bloom.sql
+++ b/contrib/bloom/sql/bloom.sql
@@ -93,3 +93,22 @@ SELECT reloptions FROM pg_class WHERE oid = 'bloomidx'::regclass;
 \set VERBOSITY terse
 CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (length=0);
 CREATE INDEX bloomidx2 ON tst USING bloom (i, t) WITH (col1=0);
+\set VERBOSITY default
+
+-- In a new backend, bloom's options are not yet registered when ALTER INDEX
+-- chooses its lock level; the lock taken must still be the one bloom
+-- declares.
+\c
+BEGIN;
+ALTER INDEX bloomidx SET (length = 80);
+SELECT mode FROM pg_locks
+  WHERE pid = pg_backend_pid() AND relation = 'bloomidx'::regclass
+  ORDER BY mode;
+ROLLBACK;
+\c
+BEGIN;
+ALTER INDEX bloomidx RESET (col1);
+SELECT mode FROM pg_locks
+  WHERE pid = pg_backend_pid() AND relation = 'bloomidx'::regclass
+  ORDER BY mode;
+ROLLBACK;
diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c
index ea9a0417909..c5bd5ecd0c8 100644
--- a/src/backend/access/common/reloptions.c
+++ b/src/backend/access/common/reloptions.c
@@ -2467,6 +2467,7 @@ AlterTableGetRelOptionsLockLevel(List *defList)
 	foreach(cell, defList)
 	{
 		DefElem    *def = (DefElem *) lfirst(cell);
+		bool		found = false;
 		int			i;
 
 		for (i = 0; relOpts[i]; i++)
@@ -2475,10 +2476,19 @@ AlterTableGetRelOptionsLockLevel(List *defList)
 						def->defname,
 						relOpts[i]->namelen + 1) == 0)
 			{
+				found = true;
 				if (lockmode < relOpts[i]->lockmode)
 					lockmode = relOpts[i]->lockmode;
 			}
 		}
+
+		/*
+		 * An unrecognized option might belong to an access method whose
+		 * library has not yet been loaded in this backend, so we can't tell
+		 * what lock it needs.  Use the strongest one.
+		 */
+		if (!found)
+			return AccessExclusiveLock;
 	}
 
 	return lockmode;
-- 
2.43.0

