On Mon, Mar 10, 2025 at 10:15:19AM -0500, Nathan Bossart wrote:
> On Sun, Mar 09, 2025 at 11:48:05AM -0400, Tom Lane wrote:
>> Nathan Bossart <nathandbossart@gmail.com> writes:
>>> On Sat, Mar 08, 2025 at 05:17:40PM -0500, Tom Lane wrote:
>>>> ReindexIndex() faces this same problem and solves it with some
>>>> very complex code that manages to get the table's lock first.
>>
>>> I noticed that amcheck's bt_index_check_internal() handles this problem,
>>> ...
>>> stats_lock_check_privileges() does something similar, but it's not as
>>> cautious about the "heapid != IndexGetRelation(indrelid, false)" race
>>> condition.
>>
>> Egad, we've already got three inconsistent implementations of this
>> functionality? I think the first step must be to unify them into
>> a common implementation, if at all possible.
>
> Agreed. I worry that trying to unify each bespoke implementation into a
> single function might result in an unwieldy mess, but I'll give it a
> shot...
I tried to unify these, but each one seems to be just different enough to
make it not worth the trouble. Instead, I took a look at each
implementation:
* amcheck's amcheck_lock_relation_and_check() seems correct to me.
* stats_lock_check_privileges() appears to be missing the second
IndexGetRelation() check after locking the table and index, so I added
that in 0001. Since this code is new to v18, I proposed to back-patch 0001
there.
* RangeVarCallbackForReindexIndex() was checking privileges on the table
before locking it, so I reversed it in 0002. Interestingly, this caused
test errors because LockRelationOid() checks for invalidation messages, so
the pg_class_aclcheck() call started failing with unhelpful errors due to
concurrently dropped relations. To deal with that, I switched to
pg_class_aclcheck_ext() so that we can handle missing relations.
Furthermore, I noticed that this callback seems to assume that as long as
the index does not change between calls, its table won't, either. That's
probably always true in practice, but even if it's completely true, I see
no reason to rely on it. So, I simplified the code to unconditionally
unlock any previously-locked table and to lock whatever IndexGetRelation()
returns. This could probably be back-patched, but in the absence of any
reports or any reproducible bugs, I don't think we should.
* 0003 fixes pg_prewarm's privilege checks by following a similar pattern.
This probably ought to get back-patched to all supported versions.
--
nathan