Odd code around ginScanToDelete

From: Andres Freund <andres(at)anarazel(dot)de>
To: pgsql-hackers(at)postgresql(dot)org, Alexander Korotkov <aekorotkov(at)gmail(dot)com>
Subject: Odd code around ginScanToDelete
Date: 2026-02-03 16:26:56
Message-ID: utrlxij43fbguzw4kldte2spc4btoldizutcqyrfakqnbrp3ir@ph3sphpj4asz
Views: Whole Thread | Raw Message | Download mbox | Resend email
Thread:
Lists: pgsql-hackers

Hi,

While looking at converting more places to UnlockReleaseBuffer(), in the
course of making UnlockReleaseBuffer() faster than the two separate
operations, I found this code:

static bool
ginScanToDelete(GinVacuumState *gvs, BlockNumber blkno, bool isRoot,
DataPageDeleteStack *parent, OffsetNumber myoff)
...

if (!meDelete)
{
if (BufferIsValid(me->leftBuffer))
UnlockReleaseBuffer(me->leftBuffer);
me->leftBuffer = buffer;
}
else
{
if (!isRoot)
LockBuffer(buffer, GIN_UNLOCK);

ReleaseBuffer(buffer);
}

if (isRoot)
ReleaseBuffer(buffer);

Which sure looks like it'd release buffer twice if isRoot is set? I guess
that's not reachable, because presumably the root page will always go down the
!meDelete path. But it sure made me wonder if there's a hard to reach bug.

This code was introduced in
commit e14641197a5
Author: Alexander Korotkov <akorotkov(at)postgresql(dot)org>
Date: 2019-11-19 23:07:36 +0300

Fix deadlock between ginDeletePage() and ginStepRight()

I didn't trace it further to see if it existed before that in some fashion.

There's another oddity here: ginScanToDelete() requires that the root page has
been locked by the caller already, but will afaict re-read the root page? But
then have code to avoid locking it again, because that would not have worked?
Seems odd.

Greetings,

Andres Freund

Responses

Browse pgsql-hackers by date

  From Date Subject
Next Message Christoph Berg 2026-02-03 16:27:24 Re: Re[2]: [PATCH] Add last_executed timestamp to pg_stat_statements
Previous Message Mircea Cadariu 2026-02-03 16:05:40 Re: Propagate XLogFindNextRecord error to callers