Re: BUG #18830: ExecInitMerge Segfault on MERGE

Tender Wang <tndrwang@gmail.com>

From: Tender Wang <tndrwang@gmail.com>
To: Amit Langote <amitlangote09@gmail.com>
Cc: tharakan@gmail.com, pgsql-bugs@lists.postgresql.org
Date: 2025-03-04T12:46:00Z
Lists: pgsql-bugs
Amit Langote <amitlangote09@gmail.com> 于2025年3月4日周二 20:30写道:

> On Tue, Mar 4, 2025 at 5:36 PM Tender Wang <tndrwang@gmail.com> wrote:
> > Amit Langote <amitlangote09@gmail.com> 于2025年3月4日周二 19:51写道:
> >> On Mon, Mar 3, 2025 at 9:16 PM Tender Wang <tndrwang@gmail.com> wrote:
> >> > 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(),
> >>
> >> This needs to be fixed saparately.
> >>
> >> > relationDesc = RelationGetDescr(resultRelInfo->ri_RelationDesc);
> >> >
> >> > crash happened.
> >>
> >> Do you have a case where this access to undefined
> >> ModifyTableState.resultRelInfo occurs? I would have thought that it
> >> should not happen.
> >
> >
> > "undefined" may not be accurate, "invalid" seems more correct.
> > I still use  the case:
> > 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);
> >
> > (gdb) p *resultRelInfo
> > $5 = {type = 2139062142, ri_RangeTableIndex = 2139062143,
> ri_RelationDesc = 0x180, ri_NumIndices = 195, ri_IndexRelationDescs =
> 0x500000182, ri_IndexRelationInfo = 0x7f8773b2aa38, ri_RowIdAttNo = 0,
> ri_extraUpdatedCols = 0x0, ri_projectNew = 0x0,
> >   ri_newTupleSlot = 0x0, ri_oldTupleSlot = 0x0, ri_projectNewInfoValid =
> false, ri_needLockTagTuple = false, ri_TrigDesc = 0x0, ri_TrigFunctions =
> 0x0, ri_TrigWhenExprs = 0x0, ri_TrigInstrument = 0x0, ri_ReturningSlot =
> 0x0, ri_TrigOldSlot = 0x0,
> >   ri_TrigNewSlot = 0x0, ri_AllNullSlot = 0x0, ri_FdwRoutine = 0x0,
> ri_FdwState = 0x0, ri_usesFdwDirectModify = false, ri_NumSlots = 0,
> ri_NumSlotsInitialized = 0, ri_BatchSize = 0, ri_Slots = 0x0, ri_PlanSlots
> = 0x0, ri_WithCheckOptions = 0x0,
> >   ri_WithCheckOptionExprs = 0x0, ri_ConstraintExprs = 0x0,
> ri_GeneratedExprsI = 0x0, ri_GeneratedExprsU = 0x0, ri_NumGeneratedNeededI
> = 0, ri_NumGeneratedNeededU = 0, ri_returningList = 0x0,
> ri_projectReturning = 0x0,
> >   ri_onConflictArbiterIndexes = 0x0, ri_onConflict = 0x0,
> ri_MergeActions = {0x0, 0x0, 0x0}, ri_MergeJoinCondition = 0x0,
> ri_PartitionCheckExpr = 0x0, ri_ChildToRootMap = 0x0,
> ri_ChildToRootMapValid = false, ri_RootToChildMap = 0x0,
> >   ri_RootToChildMapValid = false, ri_RootResultRelInfo = 0x0,
> ri_PartitionTupleSlot = 0x0, ri_CopyMultiInsertBuffer = 0x0,
> ri_ancestorResultRels = 0x0}
> >
> >  ri_RelationDesc = 0x180,  this address is not invalid.
>
> Does the query crash or are you just able to see invalid memory
> address using gdb?
>
>
git reset --hard  cbc127917e

The query will crash on  relationDesc =
RelationGetDescr(resultRelInfo->ri_RelationDesc);
#0  ExecInitMerge (mtstate=0x55b08fc19a00, estate=0x55b08fc18c20) at
nodeModifyTable.c:3663
3663                    relationDesc =
RelationGetDescr(resultRelInfo->ri_RelationDesc);
(gdb) bt
#0  ExecInitMerge (mtstate=0x55b08fc19a00, estate=0x55b08fc18c20) at
nodeModifyTable.c:3663
#1  0x000055b07f1046af in ExecInitModifyTable (node=0x55b08fc3d338,
estate=0x55b08fc18c20, eflags=0) at nodeModifyTable.c:4889
#2  0x000055b07f0bdd77 in ExecInitNode (node=0x55b08fc3d338,
estate=0x55b08fc18c20, eflags=0) at execProcnode.c:177
#3  0x000055b07f0b26a9 in InitPlan (queryDesc=0x55b08fbe4990, eflags=0) at
execMain.c:985
#4  0x000055b07f0b1435 in standard_ExecutorStart (queryDesc=0x55b08fbe4990,
eflags=0) at execMain.c:259
#5  0x000055b07f0b1143 in ExecutorStart (queryDesc=0x55b08fbe4990,
eflags=0) at execMain.c:135
#6  0x000055b07f395fab in ProcessQuery (plan=0x55b08fc3f0f8,
    sourceText=0x55b08fb19420 "merge into part_abc_view pt\nusing (select
stable_one() + 2 as pid) as q join part_abc_1 pt1 on (true)\non pt.a =
stable_one() +2\nwhen not matched then insert values(1, 'd', false);",
params=0x0, queryEnv=0x0,
    dest=0x55b08fc3f278, qc=0x7ffc72bad230) at pquery.c:155
#7  0x000055b07f397af1 in PortalRunMulti (portal=0x55b08fb99c40,
isTopLevel=true, setHoldSnapshot=false, dest=0x55b08fc3f278,
altdest=0x55b08fc3f278, qc=0x7ffc72bad230) at pquery.c:1271
#8  0x000055b07f396ffd in PortalRun (portal=0x55b08fb99c40,
count=9223372036854775807, isTopLevel=true, dest=0x55b08fc3f278,
altdest=0x55b08fc3f278, qc=0x7ffc72bad230) at pquery.c:787
#9  0x000055b07f38fafd in exec_simple_query (
    query_string=0x55b08fb19420 "merge into part_abc_view pt\nusing (select
stable_one() + 2 as pid) as q join part_abc_1 pt1 on (true)\non pt.a =
stable_one() +2\nwhen not matched then insert values(1, 'd', false);") at
postgres.c:1271
#10 0x000055b07f394e65 in PostgresMain (dbname=0x55b08fb533e8 "postgres",
username=0x55b08fb533c8 "ecs-user") at postgres.c:4691
#11 0x000055b07f38b71a in BackendMain (startup_data=0x7ffc72bad4b0 "",
startup_data_len=4) at backend_startup.c:107
#12 0x000055b07f298873 in postmaster_child_launch (child_type=B_BACKEND,
child_slot=1, startup_data=0x7ffc72bad4b0 "", startup_data_len=4,
client_sock=0x7ffc72bad510) at launch_backend.c:274
#13 0x000055b07f29f1ce in BackendStartup (client_sock=0x7ffc72bad510) at
postmaster.c:3519
#14 0x000055b07f29c7ec in ServerLoop () at postmaster.c:1688
#15 0x000055b07f29c0e4 in PostmasterMain (argc=3, argv=0x55b08fb13a30) at
postmaster.c:1386
#16 0x000055b07f13fbd1 in main (argc=3, argv=0x55b08fb13a30) at main.c:230

But after you commit the 75dfde1, crash happened on ExecMergeNotMatched(),
econtext->ecxt_scantuple = NULL;
because econtext is null.

After 75dfde1, mergeActionLists is  NIL,  it directly returns in if
(mergeActionLists == NIL).

I think the crash and the inconsistent result are all caused by cbc127917e.

-- 
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.