| From: | Kirill Reshke <reshkekirill(at)gmail(dot)com> |
|---|---|
| To: | Tender Wang <tndrwang(at)gmail(dot)com> |
| Cc: | idrissaajalloh(at)gmail(dot)com, pgsql-bugs(at)lists(dot)postgresql(dot)org |
| Subject: | Re: BUG #19861: Self-join elimination drops a VOLATILE predicate and changes the query result |
| Date: | 2026-11-11 11:03:10 |
| Message-ID: | CALdSSPgFzGF2oxJF5OxjoaKe82JqA7T1w9Sb8A_9UrqEzjErCQ@mail.gmail.com |
| Views: | Whole Thread | Raw Message | Download mbox | Resend email |
| Thread: | |
| Lists: | pgsql-bugs |
On Wed, 11 Nov 2026 at 10:17, Tender Wang <tndrwang(at)gmail(dot)com> wrote:
>
> Tender Wang <tndrwang(at)gmail(dot)com> 于2026年11月11日周三 10:23写道:
> >
> > Hi,
> >
> > PG Bug reporting form <noreply(at)postgresql(dot)org> 于2026年11月11日周三 09:54写道:
> > >
> > > This occurs when self-join elimination is enabled for a primary-key
> > > self-join with alias-specific calls to the same VOLATILE predicate.
> > >
> >
> > After one relation is removed, SJE rewrites its Vars to reference the
> > remaining relation.
> > In this case, that turns
> >
> > is_odd_call(a.id)
> > is_odd_call(b.id)
> >
> > into two structurally identical expressions referencing the same relation.
> > fixup_selfjoin_quals() then treats the second expression as a
> > duplicate and removes it.
> > That is not valid for volatile expressions, since the two occurrences
> > represent separately observable evaluations.
> >
> > The attached patch conservatively disables SJE when the query jointree
> > contains volatile functions.
> >
>
> Checking in remove_useless_self_joins() seems too strict; it can skip
> some self joins that have no volatile functions in quals.
> I moved the check into remove_self_joins_one_group() as attached v2.
>
>
> --
> Thanks,
> Tender Wang
I tested v2 on master, It fixes the report, however, there are
remaining target-list cases where SJE changes the result.
CREATE TABLE sje_target (id int PRIMARY KEY);
CREATE SEQUENCE sje_seq;
INSERT INTO sje_target VALUES (0);
SELECT nextval('sje_seq') + a.id AS x,
nextval('sje_seq') + b.id AS y
FROM sje_target a JOIN sje_target b ON a.id = b.id
ORDER BY x;
With SJE off this returns (1,2), with last_value=2. With v2 and SJE on
it returns (1,1), with last_value=1.
The second case changes the row count:
SELECT generate_series(1, (nextval('sje_seq') + a.id)::int) + 0 AS x,
generate_series(1, (nextval('sje_seq') + b.id)::int) + 0 AS y
FROM sje_target a JOIN sje_target b ON a.id = b.id;
SJE off returns (1,1) and (NULL,2). v2 SJE fix /master on returns just (1,1).
So, ChangeVarNodes rewrites their correlated references before their
targets are planned, checking only root->processed_tlist would miss
the this case?
--
Best regards,
Kirill Reshke
| From | Date | Subject | |
|---|---|---|---|
| Next Message | PG Bug reporting form | 2026-11-11 11:57:42 | BUG #19875: Foreign-key self-update rejects an accepted implicit I/O cast |
| Previous Message | Daniel Gustafsson | 2026-11-11 11:02:07 | Re: BUG #19867: pg_verifybackup accepts out-of-range Timeline in backup manifest |