Re: Function scan FDW pushdown

Alexander Pyhalov <a.pyhalov@postgrespro.ru>

From: Alexander Pyhalov <a.pyhalov@postgrespro.ru>
To: Alexander Korotkov <aekorotkov@gmail.com>
Cc: solaimurugan vellaipandiyan <drsolaimurugan.v@gmail.com>, Álvaro Herrera <alvherre@kurilemu.de>, g.kashkin@postgrespro.ru, Ashutosh Bapat <ashutosh.bapat.oss@gmail.com>, PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>
Date: 2026-05-20T10:17:04Z
Lists: pgsql-hackers

Attachments

Alexander Korotkov писал(а) 2026-05-19 21:21:
> Good evening!
> 
> On Tue, May 19, 2026 at 6:25 PM Alexander Pyhalov
> <a.pyhalov@postgrespro.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