Re: BUG #18830: ExecInitMerge Segfault on MERGE

Tender Wang <tndrwang@gmail.com>

From: Tender Wang <tndrwang@gmail.com>
To: pgsql-bugs@lists.postgresql.org, Amit Langote <amitlangote09@gmail.com>
Cc: tharakan@gmail.com
Date: 2025-03-04T06:58:01Z
Lists: pgsql-bugs
Tender Wang <tndrwang@gmail.com> 于2025年3月3日周一 23:46写道:

>
> Thanks for reporting.
>  I can reproduce this crash.  On HEAD 3f1db99bfa, crash on a different
> place due to the commit 75dfde1.
> But cbc127917e is the root cause commit.
>
> MERGE INTO e  USING h  ON a = xmlserialize WHEN NOT MATCHED THEN INSERT
> VALUES (CAST(NULL AS text));
> The plan of the above query looks as below:
>                             QUERY PLAN
> ------------------------------------------------------------------
>  Merge on e  (cost=0.00..58.29 rows=0 width=0)
>    ->  Nested Loop Left Join  (cost=0.00..58.29 rows=12 width=10)
>          ->  Result  (cost=0.00..0.01 rows=1 width=0)
>          ->  Append  (cost=0.00..58.16 rows=12 width=10)
>                Subplans Removed: 2
>
> You can see that all partitions are pruned.  After cbc127917e, we only
> consider unpruned relations, then the list resultRelations is empty.
> the estate->es_unpruned_relids contains (1,2), the node->resultRelations
> contains (5,6).
>
> nrels = list_length(resultRelations);
> ...
> mtstate->resultRelInfo = (ResultRelInfo *)
>               palloc(nrels * sizeof(ResultRelInfo));
>
> The memory of mtstate->resultRelInfo point to is undefined. When we access
> its memory in ExecInitMerge(),
>
> relationDesc = RelationGetDescr(resultRelInfo->ri_RelationDesc);
>
> crash happened.
>
> After 75dfde1,  mergeActionLists list is empty, so in ExecInitMerge() we
> don't enter the foreach(lc, mergeActionLists),
> and the mtstate->ps.ps_ExprContext has no chance to initialize. So
> in ExecMergeNotMatched(),  econtext is NULL.
> econtext->ecxt_scantuple = NULL;
> The above statement can trigger a segment fault.
>
>  For Merge command NOT MATCH,  should we need the logic that only consider
> unpruned relations in ExecInitModifyTable()?
>
> Merge command seems more complex than update and delete.  Can we consider
> the unpruned relations in  ExecInitModifyTable()
> according to the command type. For merge, we do the logic before
> cbc127917e, for now?
>
>
 I found the partition_prune.sql does not cover merge into ... not match
case,  and I found an easy reproduce step,  seeing below:

postgres=#  merge into part_abc_view pt
using (select stable_one() + 2 as pid) as q join part_abc_1 pt1 on (true)
on pt.a = stable_one() +2
when not matched then insert values(1, 'd', false);
server closed the connection unexpectedly
        This probably means the server terminated abnormally
        before or while processing the request.
The connection to the server was lost. Attempting reset: Succeeded.


-- 
Thanks,
Tender Wang

Commits

Same data as JSON: GET /api/v1/messages/:b64id/commits the thread's linked commits as JSON, with link sources. API reference →
  1. Ensure first ModifyTable rel initialized if all are pruned

  2. Don't lock partitions pruned by initial pruning

  3. Fix an oversight in cbc127917 to handle MERGE correctly

  4. Remove some unnecessary fields from Plan trees.