Re: Function scan FDW pushdown

From: Alexander Pyhalov <a(dot)pyhalov(at)postgrespro(dot)ru>
To: Alexander Korotkov <aekorotkov(at)gmail(dot)com>
Cc: solaimurugan vellaipandiyan <drsolaimurugan(dot)v(at)gmail(dot)com>, Álvaro Herrera <alvherre(at)kurilemu(dot)de>, g(dot)kashkin(at)postgrespro(dot)ru, Ashutosh Bapat <ashutosh(dot)bapat(dot)oss(at)gmail(dot)com>, PostgreSQL Hackers <pgsql-hackers(at)lists(dot)postgresql(dot)org>
Subject: Re: Function scan FDW pushdown
Date: 2026-05-20 10:17:04
Message-ID: 214e7cb76a9f142a08246660a649ca0b@postgrespro.ru
Views: Whole Thread | Raw Message | Download mbox | Resend email
Thread:
Lists: pgsql-hackers

Alexander Korotkov писал(а) 2026-05-19 21:21:
> Good evening!
>
> On Tue, May 19, 2026 at 6:25 PM Alexander Pyhalov
> <a(dot)pyhalov(at)postgrespro(dot)ru> wrote:
>>
>> Found one more issue in whole row var deparsing. It can appear on a
>> nullable outer side, and we should use the same logic as when
>> deparsing
>> table column reference. Otherwise we get records from nulls instead of
>> nulls (for example, "(NULL, NULL)" instead of NULL).
>
>
> Good catch, accepted.
>

Hi.

I've found another issue. The fact that in the new versions of the patch
RTE RelOptInfo misses fdw_private seems to be unfortunate. For example,
in the last version we haven't thought about classifying
baserestrictinfo. And if we do, we should pass fdw_private down to
foreign_expr_walker. Perhaps, we could attach it to RTE_FUNCTION rel
prior to calling classifyConditions(), but should we later set it back
to NULL? Another problem comes if we try to handle joins, which can
crearte subqueries (like INNER/OUTER UNIQUE). In this case we should
somehow cook fpinfo for get_relation_column_alias_ids(). Attaching patch
which tries to handle baserestrictinfos by passing fpinfo down to
foreign_expr_walker().

One more interesting example (included in the patch) is

EXPLAIN (VERBOSE, COSTS OFF)
WITH s AS MATERIALIZED (SELECT r1.* FROM remote_tbl r1
JOIN LATERAL
(SELECT r2.a FROM remote_tbl r2, f(r1.a) LIMIT 1) s
ON true)
SELECT * FROM s ORDER BY 1;

We get the following plan:

Sort
Output: s.a, s.b
Sort Key: s.a
CTE s
-> Nested Loop
Output: r1.a, r1.b
-> Foreign Scan on public.remote_tbl r1
Output: r1.a, r1.b
Remote SQL: SELECT a, b FROM public.base_tbl_fn
-> Foreign Scan
Output: NULL::integer
Relations: (public.remote_tbl r2) INNER JOIN (Function
f)
Remote SQL: SELECT NULL FROM (public.base_tbl_fn r1
INNER JOIN public.f($1::integer) f2(c1) ON (TRUE)) LIMIT 1::bigint
-> CTE Scan on s
Output: s.a, s.b

Here you can see that we use parameter in function argument. Don't know
if it's a real problem, but at least looks suspicious. In v3 patch used
contain_param_walker() in is_nonrel_relinfo_ok() (which mutated to
function_rte_pushdown_ok()) to avoid such plans.

One minor issue I've noticed is in function_rte_pushdown_ok():
+ if (rel->rtekind != RTE_FUNCTION)
+ return false;
+ rte = planner_rt_fetch(rel->relid, root);
+ if (rte->rtekind != RTE_FUNCTION)
+ return false;

Is the second rtekind check necessary?
--
Best regards,
Alexander Pyhalov,
Postgres Professional

Attachment Content-Type Size
v9-0001-postgres_fdw-push-down-FUNCTION-RTE-into-foreign-joi.patch text/x-diff 72.4 KB

In response to

Browse pgsql-hackers by date

  From Date Subject
Next Message Rafia Sabih 2026-05-20 10:40:11 Re: Bypassing cursors in postgres_fdw to enable parallel plans
Previous Message Japin Li 2026-05-20 10:00:56 Re: Add pg_get_publication_ddl function