Thread

  1. Re: Function scan FDW pushdown

    Alexander Pyhalov <a.pyhalov@postgrespro.ru> — 2026-05-19T15:25:54Z

    Alexander Korotkov писал(а) 2026-05-19 16:00:
    > Hi, Alexander.
    > 
    > On Mon, May 18, 2026 at 11:06 PM Alexander Pyhalov
    > <a.pyhalov@postgrespro.ru> wrote:
    >> Hi. I am a bit confused about this comment (and code):
    >> 
    >>                         /*
    >>                          * DirectModify on a foreign join: pass NIL/0 
    >> for
    >> the function
    >>                          * metadata.  We don't currently push function
    >> RTEs through the
    >>                          * direct-modify path, so there are no 
    >> whole-row
    >> Vars pointing at
    >>                          * function-RTE tuples to reconstruct.
    >>                          */
    >>                         tupdesc = 
    >> get_tupdesc_for_join_scan_tuples(node,
    >> NIL, 0);
    >> 
    >> We evidently go through this code path when executing example
    >> 
    >> UPDATE remote_tbl r SET b=5 FROM UNNEST(array[box '((2,3),(-2,-3))']) 
    >> AS
    >> t (bx) WHERE r.a = area(t.bx)
    >>   RETURNING a,b;
    >> 
    >> But don't need whole row var in returning list.... However, we still 
    >> can
    >> step on this issue.
    > 
    > Yes, we go through this code path, and it works as long as whole-row
    > var is not needed.
    > 
    >> UPDATE remote_tbl r SET b=5 FROM UNNEST(array[box '((2,3),(-2,-3))'],
    >> array[int '1']) AS t (bx,  i) WHERE r.a = area(t.bx)
    >> RETURNING a,b,t;
    >> 
    >> ERROR:  input of anonymous composite types is not implemented
    >> CONTEXT:  whole-row reference to foreign table "t"
    > 
    > But if whole row var is actually used, then the assumption is broken.
    > So, we need to build a whole-row var anyway.  I've fixed this in the
    > attached patch, and added your sample query as a regression test case.
    > 
    
    Good evening.
    
    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).
    
    Also I wonder if it is possible for get_tupdesc_for_join_scan_tuples() 
    to get NULL rtfuncdata when it looks at RTE_FUNCTION RTE here:
    
    1759                 else if (rte->rtekind == RTE_FUNCTION && rtfuncdata 
    != NIL)
    1760                 {
    1761                         /*
    1762                          * A whole-row Var points at a FUNCTION RTE 
    absorbed into the
    1763                          * foreign join.  Synthesize an anonymous 
    composite TupleDesc from
    1764                          * the per-function return-type metadata we 
    saved at plan time;
    1765                          * the deparser emits these as 
    ROW(f<rti>.c1, f<rti>.c2, ...).
    1766                          */
    1767                         List       *funcdata;
    1768                         TupleDesc       rte_tupdesc;
    1769                         int                     num_funcs;
    1770                         int                     attnum;
    1771                         ListCell   *lc1,
    
    ?
    
    -- 
    Best regards,
    Alexander Pyhalov,
    Postgres Professional