Re: Allow table AMs to define their own reloptions

From: Rui Zhao <zhaorui126(at)gmail(dot)com>
To: Andrew Dunstan <andrew(at)dunslane(dot)net>
Cc: Zsolt Parragi <zsolt(dot)parragi(at)percona(dot)com>, pgsql-hackers(at)lists(dot)postgresql(dot)org, Álvaro Herrera <alvherre(at)kurilemu(dot)de>, Ajit Awekar <ajitpostgres(at)gmail(dot)com>
Subject: Re: Allow table AMs to define their own reloptions
Date: 2026-10-21 14:56:06
Message-ID: CAHWVJhFfwJxkWrSJ=aruXt0kRP_+Pz3HcTZTssjOnTF1UensjQ@mail.gmail.com
Views: Whole Thread | Raw Message | Download mbox | Resend email
Thread:
Lists: pgsql-hackers

Hi Andrew,

Thanks for adding the helper for standard options, which is very helpful
for AM extensions.

I found an issue:
In a fresh backend, ALTER TABLE can change an AM-specific option without
taking the lock requested by the AM. dummy_table_am registers option_int
with AccessExclusiveLock, but I got ShareUpdateExclusiveLock on v13.

Setup in one connection:

CREATE EXTENSION dummy_table_am;
CREATE TABLE t_std (a int) USING dummy_table_am WITH (option_int = 7);

Then, in a new connection:

BEGIN;
ALTER TABLE t_std SET (option_int = 9);
SELECT mode FROM pg_locks
WHERE pid = pg_backend_pid() AND relation = 't_std'::regclass;
-- ShareUpdateExclusiveLock
ROLLBACK;

With LOAD 'dummy_table_am' before BEGIN in another new connection, the
same commands take AccessExclusiveLock. RESET has the same problem.

The reason is that ProcessUtilitySlow() calls AlterTableGetLockLevel()
before opening the relation and loading its AM. option_int is not
registered at that point.
AlterTableGetRelOptionsLockLevel() ignores the unknown name and returns
NoLock, leaving AlterTableGetLockLevel()'s ShareUpdateExclusiveLock
minimum unchanged. Loading the AM later does not upgrade that lock.

This is an existing helper issue, not a v13 regression. I reproduced it
with bloom on unpatched master at e75fcc9867f. Setup in one connection:

CREATE EXTENSION bloom;
CREATE TABLE bloom_probe (a int);
CREATE INDEX bloom_probe_idx ON bloom_probe USING bloom (a);

Then, in a new connection:

BEGIN;
ALTER INDEX bloom_probe_idx SET (length = 100);
SELECT mode FROM pg_locks
WHERE pid = pg_backend_pid() AND relation = 'bloom_probe_idx'::regclass
ORDER BY mode;
-- ShareUpdateExclusiveLock
ROLLBACK;

With LOAD 'bloom' before BEGIN in another new connection, the same
commands take AccessExclusiveLock.

With 0001 on top of v13, an option not yet registered requires
AccessExclusiveLock. Registered options keep their declared lock levels.
Until the AM is loaded, we cannot tell which lock the option requires.
This ensures the lock is strong enough. I think this is a harmless and
safe fix with minimal changes.

The TAP test checks SET and RESET for both test AMs, with and without
loading the library first, and checks that fillfactor still uses the
weaker lock.

Core regression, isolation and dummy_table_am tests passed with the
patch. Without the fix, the four cases without LOAD fail.

Regards,
Rui

Attachment Content-Type Size
nocfbot-0001-Use-a-strong-lock-for-unregistered-relation-options.patch application/octet-stream 4.0 KB

In response to

Browse pgsql-hackers by date

  From Date Subject
Next Message shihao zhong 2026-10-21 15:17:44 Re: [PG19] psql backslash commands fail with translated messages and a non UTF-8 client encoding
Previous Message Tom Lane 2026-10-21 14:46:56 Re: [DEPARSE] EXPLAIN prints another ModifyTable's RETURNING aliases in a SubPlan