| From: | Tom Lane <tgl(at)sss(dot)pgh(dot)pa(dot)us> |
|---|---|
| To: | Dean Rasheed <dean(dot)a(dot)rasheed(at)gmail(dot)com> |
| Cc: | Ayush Tiwari <ayushtiwari(dot)slg01(at)gmail(dot)com>, assam258(at)gmail(dot)com, pgsql-hackers <pgsql-hackers(at)postgresql(dot)org> |
| Subject: | Re: [DEPARSE] EXPLAIN prints another ModifyTable's RETURNING aliases in a SubPlan |
| Date: | 2026-10-21 18:00:40 |
| Message-ID: | 2220492.1792605640@sss.pgh.pa.us |
| Views: | Whole Thread | Raw Message | Download mbox | Resend email |
| Thread: | |
| Lists: | pgsql-hackers |
I wrote:
> But wait a minute: there's still something pretty strange
> going on here. In the first place, Ayush's test case doesn't
> crash if the outer query doesn't try to use alias "new" for v:
Ah, I see the cause of that part. The parser's
transformReturningClause does this:
if (qry->returningNewAlias == NULL &&
refnameNamespaceItem(pstate, NULL, "new", -1, NULL) == NULL)
{
qry->returningNewAlias = "new";
addNSItemForReturning(pstate, "new", VAR_RETURNING_NEW);
}
(and likewise for returningOldAlias). So using "new" as an
alias in the outer query allows qry->returningNewAlias to be
left as NULL. I guess the assumption is that if you do that
then it's impossible to reference VAR_RETURNING_NEW explicitly?
But then when rewriteRuleAction unconditionally copies the null
value into the rule_action --- which does have VAR_RETURNING_NEW
Vars --- we've got trouble.
Contrarily, it's possible for the rule_action to have null
returningOldAlias/returningNewAlias through the same mechanism.
One regression test case that fails if I simply dike out
rewriteRuleAction's copying is
CREATE TEMP TABLE foo (f1 serial, f2 text, f3 int default 42);
ALTER TABLE foo ADD COLUMN f4 int8 DEFAULT 99;
CREATE RULE foo_del_rule AS ON DELETE TO foo DO INSTEAD
UPDATE foo SET f2 = f2||' (deleted)', f3 = -1, f4 = -1 WHERE f1 = OLD.f1
RETURNING *;
EXPLAIN (verbose, costs off)
DELETE FROM foo WHERE f1 = 4 RETURNING old.*,new.*, *;
Here, the rule_action's aliases are both NULL, which I take to be
because the rule mechanisms themselves set up dummy OLD and NEW RTEs.
So if we don't supply something during rewriting then those new
Asserts fire, as a consequence of having pushed VAR_RETURNING_OLD/NEW
Vars down into the rule action from the outer query.
This makes me think it is probably possible to construct scenarios
where a plan contains both VAR_RETURNING Vars that originated inside
a rule and some that originated from the calling query, in which
case using either level's aliases across-the-board would be wrong.
But we don't have a lot of choice, because that mixing happens in
the rewriter and its output format doesn't support recording multiple
sets of aliases (or does it? maybe the two sets of Vars will have
different varlevelsup?).
Anyway, for a back-patchable fix I favor the fragment I showed,
which keeps the lower-level aliases if any and only pushes down
the upper aliases if there are no aliases in the rule.
regards, tom lane
| From | Date | Subject | |
|---|---|---|---|
| Next Message | Bharath Rupireddy | 2026-10-21 18:15:00 | Re: Introduce XID age based replication slot invalidation |
| Previous Message | Bharath Rupireddy | 2026-10-21 17:37:52 | Re: Parallel vacuum: workers together with leader could overrun cost budget |