From: | Amit Langote <Langote_Amit_f8(at)lab(dot)ntt(dot)co(dot)jp> |
---|---|
To: | Tom Lane <tgl(at)sss(dot)pgh(dot)pa(dot)us>, pgsql-hackers(at)lists(dot)postgresql(dot)org |
Subject: | Re: Another way to fix inherited UPDATE/DELETE |
Date: | 2019-02-20 01:55:45 |
Message-ID: | 24c36077-a259-bf13-6631-8779954f8322@lab.ntt.co.jp |
Views: | Raw Message | Whole Thread | Download mbox | Resend email |
Thread: | |
Lists: | pgsql-hackers |
Hi,
On 2019/02/20 6:48, Tom Lane wrote:
> While contemplating the wreckage of
> https://commitfest.postgresql.org/22/1778/
> I had the beginnings of an idea of another way to fix that problem.
>
> The issue largely arises from the fact that for UPDATE, we expect
> the plan tree to emit a tuple that's ready to be stored back into
> the target rel ... well, almost, because it also has a CTID or some
> other row-identity column, so we have to do some work on it anyway.
> But the point is this means we potentially need a different
> targetlist for each child table in an inherited UPDATE.
>
> What if we dropped that idea, and instead defined the plan tree as
> returning only the columns that are updated by SET, plus the row
> identity? It would then be the ModifyTable node's job to fetch the
> original tuple using the row identity (which it must do anyway) and
> form the new tuple by combining the updated columns from the plan
> output with the non-updated columns from the original tuple.
>
> DELETE would be even simpler, since it only needs the row identity
> and nothing else.
I had bookmarked link to an archived email of yours from about 5 years
ago, in which you described a similar attack plan for UPDATE planning:
https://www.postgresql.org/message-id/1598.1399826841%40sss.pgh.pa.us
It's been kind of in the back of my mind for a while, even considered
implementing it based on your sketch back then, but didn't have solutions
for some issues surrounding optimization of updates of foreign partitions
(see below). Maybe I should've mentioned that on this thread at some point.
> Having done that, we could toss inheritance_planner into the oblivion
> it so richly deserves, and just treat all types of inheritance or
> partitioning queries as expand-at-the-bottom, as SELECT has always
> done it.
>
> Arguably, this would be more efficient even for non-inheritance join
> situations, as less data (typically) would need to propagate through the
> join tree. I'm not sure exactly how it'd shake out for trivial updates;
> we might be paying for two tuple deconstructions not one, though perhaps
> there's a way to finesse that. (One easy way would be to stick to the
> old approach when there is no inheritance going on.)
>
> In the case of a standard inheritance or partition tree, this seems to
> go through really easily, since all the children could share the same
> returned CTID column (I guess you'd also need a TABLEOID column so you
> could figure out which table to direct the update back into). It gets
> a bit harder if the tree contains some foreign tables, because they might
> have different concepts of row identity, but I'd think in most cases you
> could still combine those into a small number of output columns.
Regarding child target relations that are foreign tables, the
expand-target-inheritance-at-the-bottom approach perhaps leaves no way to
allow pushing the update (possibly with joins) to remote side?
-- no inheritance
explain (costs off, verbose) update ffoo f set a = f.a + 1 from fbar b
where f.a = b.a;
QUERY PLAN
──────────────────────────────────────────────────────────────────────────────────────────────────────
Update on public.ffoo f
-> Foreign Update
Remote SQL: UPDATE public.foo r1 SET a = (r1.a + 1) FROM
public.bar r2 WHERE ((r1.a = r2.a))
(3 rows)
-- inheritance
explain (costs off, verbose) update p set aa = aa + 1 from ffoo f where
p.aa = f.a;
QUERY PLAN
───────────────────────────────────────────────────────────────────────────────────────────────────────────
Update on public.p
Update on public.p1
Update on public.p2
Foreign Update on public.p3
-> Nested Loop
Output: (p1.aa + 1), p1.ctid, f.*
-> Seq Scan on public.p1
Output: p1.aa, p1.ctid
-> Foreign Scan on public.ffoo f
Output: f.*, f.a
Remote SQL: SELECT a FROM public.foo WHERE (($1::integer = a))
-> Nested Loop
Output: (p2.aa + 1), p2.ctid, f.*
-> Seq Scan on public.p2
Output: p2.aa, p2.ctid
-> Foreign Scan on public.ffoo f
Output: f.*, f.a
Remote SQL: SELECT a FROM public.foo WHERE (($1::integer = a))
-> Foreign Update
Remote SQL: UPDATE public.base3 r5 SET aa = (r5.aa + 1) FROM
public.foo r2 WHERE ((r5.aa = r2.a))
(20 rows)
Does that seem salvageable?
Thanks,
Amit
From | Date | Subject | |
---|---|---|---|
Next Message | Amit Langote | 2019-02-20 01:57:29 | Re: Another way to fix inherited UPDATE/DELETE |
Previous Message | Takahashi, Ryohei | 2019-02-20 01:53:22 | RE: SQL statement PREPARE does not work in ECPG |