| From: | Rafia Sabih <rafia(dot)pghackers(at)gmail(dot)com> |
|---|---|
| To: | Ayush Tiwari <ayushtiwari(dot)slg01(at)gmail(dot)com> |
| Cc: | exclusion(at)gmail(dot)com, pgsql-bugs(at)lists(dot)postgresql(dot)org, Álvaro Herrera <alvherre(at)kurilemu(dot)de> |
| Subject: | Re: BUG #19723: CREATE INDEX racing with ALTER INDEX ATTACH PARTITION triggers unexpected internal error |
| Date: | 2026-09-28 14:00:27 |
| Message-ID: | CA+FpmFf9Pf5gJofOiED3wU3006v_8XwM8WNs_f-Q3eQYKvZNNA@mail.gmail.com |
| Views: | Whole Thread | Raw Message | Download mbox | Resend email |
| Thread: | |
| Lists: | pgsql-bugs |
On Mon, 28 Sept 2026 at 06:44, Ayush Tiwari <ayushtiwari(dot)slg01(at)gmail(dot)com>
wrote:
> Hi,
>
> On Sat, 26 Sept 2026 at 19:55, Ayush Tiwari <ayushtiwari(dot)slg01(at)gmail(dot)com>
> wrote:
> > On Sat, 26 Sept 2026 at 18:51, PG Bug reporting form
> > <noreply(at)postgresql(dot)org> wrote:
> > >
> > > The following bug has been logged on the website:
> > >
> > > Bug reference: 19723
> > > Logged by: Alexander Lakhin
> > > Email address: exclusion(at)gmail(dot)com
> > > PostgreSQL version: 19beta4
> > > Operating system: Ubuntu 24.04
> > > Description:
> > >
> > > The following script:
> > > echo "
> > > CREATE TABLE t (a int, b int) PARTITION BY list (b);
> > > CREATE TABLE tp1 PARTITION OF t FOR VALUES IN (1);
> > > " | psql
> > >
> > > for ((i=1;i<=100;i++)); do
> > > echo "iteration $i"
> > > echo "
> > > DROP INDEX t_a_idx;
> > > DROP INDEX t_a_idx2;
> > > CREATE INDEX t_a_idx ON ONLY t (a);
> > > CREATE INDEX tp1_a_idx ON tp1 (a);
> > > " | psql
> > >
> > > echo "ALTER INDEX t_a_idx ATTACH PARTITION tp1_a_idx;" | psql &
> > > echo "CREATE INDEX t_a_idx2 ON t(a);" | psql
> > > wait
> > > grep 'ERROR: bogus pg_inherit row' server.log && break;
> > > done
> > >
> > > tirggers:
> > > iteration 3
> > > DROP INDEX
> > > DROP INDEX
> > > CREATE INDEX
> > > CREATE INDEX
> > > ERROR: bogus pg_inherit row: inhrelid 16400 inhparent 16399
> > > ALTER INDEX
> > > 2026-09-26 04:36:22.653 EDT|user|regression|6ab78406.1ddce8|XX000
> ERROR:
> > > bogus pg_inherit row: inhrelid 16400 inhparent 16399
> > >
> > > which is described as unexpected:
> > > /*
> > > * A pg_inherits row exists. If it's the same
> we
> > > want, then we're
> > > * good; if it differs, that amounts to a
> corrupt
> > > catalog and
> > > * should not happen.
> > > */
> > > if (inhForm->inhparent != parentOid)
> > > {
> > > /* unexpected: we should not get
> called in
> > > this case */
> > > elog(ERROR, "bogus pg_inherit row:
> inhrelid
> > > %u inhparent %u",
> > > inhForm->inhrelid,
> > > inhForm->inhparent);
> > > }
> > >
> > > Reproduced starting from 8b08f7d48.
> >
> > Thanks for the report with repro and bisect.
> >
> > I think I see how this happens. DefineIndex() calls has_superclass()
> > before locking the child index, although its comment says the caller
> > *must hold that lock*. If ALTER INDEX ... ATTACH hasn't committed yet,
> > CREATE INDEX sees the child as unattached, waits in index_open(), and
> > then tries to attach it to its new parent after ATTACH commits.
> >
> > Would it make sense to open the index before calling has_superclass()?
> > Then the check would see the attachment after the wait and skip that
> > index.
> >
> The below simple diff fixed the issue for me:
> >
> > ---
> > diff --git a/src/backend/commands/indexcmds.c
> b/src/backend/commands/indexcmds.c
> > index 5a0312fe772..561dd124e2c 100644
> > --- a/src/backend/commands/indexcmds.c
> > +++ b/src/backend/commands/indexcmds.c
> > @@ -1453,11 +1453,14 @@ DefineIndex(ParseState *pstate,
> > Relation cldidx;
> > IndexInfo *cldIdxInfo;
> >
> > + cldidx = index_open(cldidxid, lockmode);
> > /* this index is already partition of another one */
> > if (has_superclass(cldidxid))
> > + {
> > + index_close(cldidx, lockmode);
> > continue;
> > + }
> >
> > - cldidx = index_open(cldidxid, lockmode);
> > cldIdxInfo = BuildIndexInfo(cldidx);
>
Post some more testing, attaching patch file with above diff.
>
> I went through this patch and the fix looks good to me. However I see one
problem with this in the following scenario,
s1: BEGIN; ALTER INDEX c_a_idx RENAME TO c_renamed; -- c_a_idx attached
s2: CREATE INDEX pi_new ON p (a); -- now waits (used to complete)
s1: INSERT INTO c VALUES (1);
=> ERROR: deadlock detected
Unpatched, both sessions complete.
So basically it is occurring because of opening the index first including
ones that are
already attached. Before the patch, those were skipped without taking any
lock.
That lock conflicts with locks other sessions can hold on an attached
index without conflicting on the partition itself, so CREATE INDEX on
the parent can now block, or deadlock, where it didn't before. What I
suggest is
we could keep the unlocked check as a fast path and check again only after
taking the lock:
/* this index is already partition of another one */
if (has_superclass(cldidxid))
continue;
cldidx = index_open(cldidxid, lockmode);
/* recheck, in case it was attached while we waited for the lock */
if (has_superclass(cldidxid))
{
index_close(cldidx, lockmode);
continue;
}
Another remark is to add an isolation test for this.
I have attached a patch with these additions.
--
Regards,
Rafia Sabih
CYBERTEC PostgreSQL International GmbH
| Attachment | Content-Type | Size |
|---|---|---|
| 0001-Recheck-partition-index-attachment-after-locking-it.patch | application/octet-stream | 6.1 KB |
| From | Date | Subject | |
|---|---|---|---|
| Next Message | Trakshan Mishra | 2026-09-28 15:41:17 | Re: BUG #19684: Assertion in tuplesort_begin_heap() falsified by parallel plan with sort |
| Previous Message | Nitin Motiani | 2026-09-28 13:31:19 | Re: BUG #19724: ALTER TYPE ... ALTER ATTRIBUTE triggers internal error for base type of domain with check |