Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commita210be7

Browse files
committed
Fix dangling-pointer problem in before-row update trigger processing.
ExecUpdate checked for whether ExecBRUpdateTriggers had returned a newtuple value by seeing if the returned tuple was pointer-equal to the oldone. But the "old one" was in estate->es_junkFilter's result slot, whichwould be scribbled on if we had done an EvalPlanQual update in response toa concurrent update of the target tuple; therefore we were comparing adangling pointer to a live one. Given the right set of circumstances wecould get a false match, resulting in not forcing the tuple to be stored inthe slot we thought it was stored in. In the case reported by Maxim Bogukin bug #5798, this led to "cannot extract system attribute from virtualtuple" failures when trying to do "RETURNING ctid". I believe there is avery-low-probability chance of more serious errors, such as generatingincorrect index entries based on the original rather than thetrigger-modified version of the row.In HEAD, change all of ExecBRInsertTriggers, ExecIRInsertTriggers,ExecBRUpdateTriggers, and ExecIRUpdateTriggers so that they continue tohave similar APIs. In the back branches I just changedExecBRUpdateTriggers, since there is no bug in the ExecBRInsertTriggerscase.
1 parentfee7802 commita210be7

File tree

4 files changed

+161
-144
lines changed

4 files changed

+161
-144
lines changed

‎src/backend/commands/copy.c

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1836,7 +1836,7 @@ CopyFrom(CopyState cstate)
18361836
ResultRelInfo*resultRelInfo;
18371837
EState*estate=CreateExecutorState();/* for ExecConstraints() */
18381838
ExprContext*econtext;
1839-
TupleTableSlot*slot;
1839+
TupleTableSlot*myslot;
18401840
MemoryContextoldcontext=CurrentMemoryContext;
18411841
ErrorContextCallbackerrcontext;
18421842
CommandIdmycid=GetCurrentCommandId(true);
@@ -1932,8 +1932,10 @@ CopyFrom(CopyState cstate)
19321932
estate->es_result_relation_info=resultRelInfo;
19331933

19341934
/* Set up a tuple slot too */
1935-
slot=ExecInitExtraTupleSlot(estate);
1936-
ExecSetSlotDescriptor(slot,tupDesc);
1935+
myslot=ExecInitExtraTupleSlot(estate);
1936+
ExecSetSlotDescriptor(myslot,tupDesc);
1937+
/* Triggers might need a slot as well */
1938+
estate->es_trig_tuple_slot=ExecInitExtraTupleSlot(estate);
19371939

19381940
/* Prepare to catch AFTER triggers. */
19391941
AfterTriggerBeginQuery();
@@ -1960,6 +1962,7 @@ CopyFrom(CopyState cstate)
19601962

19611963
for (;;)
19621964
{
1965+
TupleTableSlot*slot;
19631966
boolskip_tuple;
19641967
Oidloaded_oid=InvalidOid;
19651968

@@ -1983,32 +1986,28 @@ CopyFrom(CopyState cstate)
19831986
/* Triggers and stuff need to be invoked in query context. */
19841987
MemoryContextSwitchTo(oldcontext);
19851988

1989+
/* Place tuple in tuple slot --- but slot shouldn't free it */
1990+
slot=myslot;
1991+
ExecStoreTuple(tuple,slot,InvalidBuffer, false);
1992+
19861993
skip_tuple= false;
19871994

19881995
/* BEFORE ROW INSERT Triggers */
19891996
if (resultRelInfo->ri_TrigDesc&&
19901997
resultRelInfo->ri_TrigDesc->trig_insert_before_row)
19911998
{
1992-
HeapTuplenewtuple;
1993-
1994-
newtuple=ExecBRInsertTriggers(estate,resultRelInfo,tuple);
1999+
slot=ExecBRInsertTriggers(estate,resultRelInfo,slot);
19952000

1996-
if (newtuple==NULL)/* "do nothing" */
2001+
if (slot==NULL)/* "do nothing" */
19972002
skip_tuple= true;
1998-
elseif (newtuple!=tuple)/* modified by Trigger(s) */
1999-
{
2000-
heap_freetuple(tuple);
2001-
tuple=newtuple;
2002-
}
2003+
else/* trigger might have changed tuple */
2004+
tuple=ExecMaterializeSlot(slot);
20032005
}
20042006

20052007
if (!skip_tuple)
20062008
{
20072009
List*recheckIndexes=NIL;
20082010

2009-
/* Place tuple in tuple slot */
2010-
ExecStoreTuple(tuple,slot,InvalidBuffer, false);
2011-
20122011
/* Check the constraints of the tuple */
20132012
if (cstate->rel->rd_att->constr)
20142013
ExecConstraints(resultRelInfo,slot,estate);

‎src/backend/commands/trigger.c

Lines changed: 120 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1909,12 +1909,13 @@ ExecASInsertTriggers(EState *estate, ResultRelInfo *relinfo)
19091909
false,NULL,NULL,NIL,NULL);
19101910
}
19111911

1912-
HeapTuple
1912+
TupleTableSlot*
19131913
ExecBRInsertTriggers(EState*estate,ResultRelInfo*relinfo,
1914-
HeapTupletrigtuple)
1914+
TupleTableSlot*slot)
19151915
{
19161916
TriggerDesc*trigdesc=relinfo->ri_TrigDesc;
1917-
HeapTuplenewtuple=trigtuple;
1917+
HeapTupleslottuple=ExecMaterializeSlot(slot);
1918+
HeapTuplenewtuple=slottuple;
19181919
HeapTupleoldtuple;
19191920
TriggerDataLocTriggerData;
19201921
inti;
@@ -1947,12 +1948,29 @@ ExecBRInsertTriggers(EState *estate, ResultRelInfo *relinfo,
19471948
relinfo->ri_TrigFunctions,
19481949
relinfo->ri_TrigInstrument,
19491950
GetPerTupleMemoryContext(estate));
1950-
if (oldtuple!=newtuple&&oldtuple!=trigtuple)
1951+
if (oldtuple!=newtuple&&oldtuple!=slottuple)
19511952
heap_freetuple(oldtuple);
19521953
if (newtuple==NULL)
1953-
break;
1954+
returnNULL;/* "do nothing" */
1955+
}
1956+
1957+
if (newtuple!=slottuple)
1958+
{
1959+
/*
1960+
* Return the modified tuple using the es_trig_tuple_slot. We assume
1961+
* the tuple was allocated in per-tuple memory context, and therefore
1962+
* will go away by itself. The tuple table slot should not try to
1963+
* clear it.
1964+
*/
1965+
TupleTableSlot*newslot=estate->es_trig_tuple_slot;
1966+
TupleDesctupdesc=RelationGetDescr(relinfo->ri_RelationDesc);
1967+
1968+
if (newslot->tts_tupleDescriptor!=tupdesc)
1969+
ExecSetSlotDescriptor(newslot,tupdesc);
1970+
ExecStoreTuple(newtuple,newslot,InvalidBuffer, false);
1971+
slot=newslot;
19541972
}
1955-
returnnewtuple;
1973+
returnslot;
19561974
}
19571975

19581976
void
@@ -1966,12 +1984,13 @@ ExecARInsertTriggers(EState *estate, ResultRelInfo *relinfo,
19661984
true,NULL,trigtuple,recheckIndexes,NULL);
19671985
}
19681986

1969-
HeapTuple
1987+
TupleTableSlot*
19701988
ExecIRInsertTriggers(EState*estate,ResultRelInfo*relinfo,
1971-
HeapTupletrigtuple)
1989+
TupleTableSlot*slot)
19721990
{
19731991
TriggerDesc*trigdesc=relinfo->ri_TrigDesc;
1974-
HeapTuplenewtuple=trigtuple;
1992+
HeapTupleslottuple=ExecMaterializeSlot(slot);
1993+
HeapTuplenewtuple=slottuple;
19751994
HeapTupleoldtuple;
19761995
TriggerDataLocTriggerData;
19771996
inti;
@@ -2004,12 +2023,29 @@ ExecIRInsertTriggers(EState *estate, ResultRelInfo *relinfo,
20042023
relinfo->ri_TrigFunctions,
20052024
relinfo->ri_TrigInstrument,
20062025
GetPerTupleMemoryContext(estate));
2007-
if (oldtuple!=newtuple&&oldtuple!=trigtuple)
2026+
if (oldtuple!=newtuple&&oldtuple!=slottuple)
20082027
heap_freetuple(oldtuple);
20092028
if (newtuple==NULL)
2010-
break;
2029+
returnNULL;/* "do nothing" */
2030+
}
2031+
2032+
if (newtuple!=slottuple)
2033+
{
2034+
/*
2035+
* Return the modified tuple using the es_trig_tuple_slot. We assume
2036+
* the tuple was allocated in per-tuple memory context, and therefore
2037+
* will go away by itself. The tuple table slot should not try to
2038+
* clear it.
2039+
*/
2040+
TupleTableSlot*newslot=estate->es_trig_tuple_slot;
2041+
TupleDesctupdesc=RelationGetDescr(relinfo->ri_RelationDesc);
2042+
2043+
if (newslot->tts_tupleDescriptor!=tupdesc)
2044+
ExecSetSlotDescriptor(newslot,tupdesc);
2045+
ExecStoreTuple(newtuple,newslot,InvalidBuffer, false);
2046+
slot=newslot;
20112047
}
2012-
returnnewtuple;
2048+
returnslot;
20132049
}
20142050

20152051
void
@@ -2257,32 +2293,44 @@ ExecASUpdateTriggers(EState *estate, ResultRelInfo *relinfo)
22572293
GetModifiedColumns(relinfo,estate));
22582294
}
22592295

2260-
HeapTuple
2296+
TupleTableSlot*
22612297
ExecBRUpdateTriggers(EState*estate,EPQState*epqstate,
22622298
ResultRelInfo*relinfo,
2263-
ItemPointertupleid,HeapTuplenewtuple)
2299+
ItemPointertupleid,TupleTableSlot*slot)
22642300
{
22652301
TriggerDesc*trigdesc=relinfo->ri_TrigDesc;
2302+
HeapTupleslottuple=ExecMaterializeSlot(slot);
2303+
HeapTuplenewtuple=slottuple;
22662304
TriggerDataLocTriggerData;
22672305
HeapTupletrigtuple;
22682306
HeapTupleoldtuple;
2269-
HeapTupleintuple=newtuple;
22702307
TupleTableSlot*newSlot;
22712308
inti;
22722309
Bitmapset*modifiedCols;
22732310

2311+
/* get a copy of the on-disk tuple we are planning to update */
22742312
trigtuple=GetTupleForTrigger(estate,epqstate,relinfo,tupleid,
22752313
&newSlot);
22762314
if (trigtuple==NULL)
2277-
returnNULL;
2315+
returnNULL;/* cancel the update action */
22782316

22792317
/*
2280-
* In READ COMMITTED isolation level it's possible thatnewtuple was
2318+
* In READ COMMITTED isolation level it's possible thattarget tuple was
22812319
* changed due to concurrent update. In that case we have a raw subplan
2282-
* output tuple and need to run it through the junk filter.
2320+
* output tuple in newSlot, and need to run it through the junk filter to
2321+
* produce an insertable tuple.
2322+
*
2323+
* Caution: more than likely, the passed-in slot is the same as the
2324+
* junkfilter's output slot, so we are clobbering the original value of
2325+
* slottuple by doing the filtering. This is OK since neither we nor our
2326+
* caller have any more interest in the prior contents of that slot.
22832327
*/
22842328
if (newSlot!=NULL)
2285-
intuple=newtuple=ExecRemoveJunk(relinfo->ri_junkFilter,newSlot);
2329+
{
2330+
slot=ExecFilterJunk(relinfo->ri_junkFilter,newSlot);
2331+
slottuple=ExecMaterializeSlot(slot);
2332+
newtuple=slottuple;
2333+
}
22862334

22872335
modifiedCols=GetModifiedColumns(relinfo,estate);
22882336

@@ -2314,13 +2362,33 @@ ExecBRUpdateTriggers(EState *estate, EPQState *epqstate,
23142362
relinfo->ri_TrigFunctions,
23152363
relinfo->ri_TrigInstrument,
23162364
GetPerTupleMemoryContext(estate));
2317-
if (oldtuple!=newtuple&&oldtuple!=intuple)
2365+
if (oldtuple!=newtuple&&oldtuple!=slottuple)
23182366
heap_freetuple(oldtuple);
23192367
if (newtuple==NULL)
2320-
break;
2368+
{
2369+
heap_freetuple(trigtuple);
2370+
returnNULL;/* "do nothing" */
2371+
}
23212372
}
23222373
heap_freetuple(trigtuple);
2323-
returnnewtuple;
2374+
2375+
if (newtuple!=slottuple)
2376+
{
2377+
/*
2378+
* Return the modified tuple using the es_trig_tuple_slot. We assume
2379+
* the tuple was allocated in per-tuple memory context, and therefore
2380+
* will go away by itself. The tuple table slot should not try to
2381+
* clear it.
2382+
*/
2383+
TupleTableSlot*newslot=estate->es_trig_tuple_slot;
2384+
TupleDesctupdesc=RelationGetDescr(relinfo->ri_RelationDesc);
2385+
2386+
if (newslot->tts_tupleDescriptor!=tupdesc)
2387+
ExecSetSlotDescriptor(newslot,tupdesc);
2388+
ExecStoreTuple(newtuple,newslot,InvalidBuffer, false);
2389+
slot=newslot;
2390+
}
2391+
returnslot;
23242392
}
23252393

23262394
void
@@ -2342,14 +2410,15 @@ ExecARUpdateTriggers(EState *estate, ResultRelInfo *relinfo,
23422410
}
23432411
}
23442412

2345-
HeapTuple
2413+
TupleTableSlot*
23462414
ExecIRUpdateTriggers(EState*estate,ResultRelInfo*relinfo,
2347-
HeapTupleoldtuple,HeapTuplenewtuple)
2415+
HeapTupletrigtuple,TupleTableSlot*slot)
23482416
{
23492417
TriggerDesc*trigdesc=relinfo->ri_TrigDesc;
2418+
HeapTupleslottuple=ExecMaterializeSlot(slot);
2419+
HeapTuplenewtuple=slottuple;
23502420
TriggerDataLocTriggerData;
2351-
HeapTupleintuple=newtuple;
2352-
HeapTuplerettuple;
2421+
HeapTupleoldtuple;
23532422
inti;
23542423

23552424
LocTriggerData.type=T_TriggerData;
@@ -2367,26 +2436,42 @@ ExecIRUpdateTriggers(EState *estate, ResultRelInfo *relinfo,
23672436
TRIGGER_TYPE_UPDATE))
23682437
continue;
23692438
if (!TriggerEnabled(estate,relinfo,trigger,LocTriggerData.tg_event,
2370-
NULL,oldtuple,newtuple))
2439+
NULL,trigtuple,newtuple))
23712440
continue;
23722441

2373-
LocTriggerData.tg_trigtuple=oldtuple;
2374-
LocTriggerData.tg_newtuple=newtuple;
2442+
LocTriggerData.tg_trigtuple=trigtuple;
2443+
LocTriggerData.tg_newtuple=oldtuple=newtuple;
23752444
LocTriggerData.tg_trigtuplebuf=InvalidBuffer;
23762445
LocTriggerData.tg_newtuplebuf=InvalidBuffer;
23772446
LocTriggerData.tg_trigger=trigger;
2378-
rettuple=ExecCallTriggerFunc(&LocTriggerData,
2447+
newtuple=ExecCallTriggerFunc(&LocTriggerData,
23792448
i,
23802449
relinfo->ri_TrigFunctions,
23812450
relinfo->ri_TrigInstrument,
23822451
GetPerTupleMemoryContext(estate));
2383-
if (newtuple!=rettuple&&newtuple!=intuple)
2384-
heap_freetuple(newtuple);
2385-
newtuple=rettuple;
2452+
if (oldtuple!=newtuple&&oldtuple!=slottuple)
2453+
heap_freetuple(oldtuple);
23862454
if (newtuple==NULL)
2387-
break;
2455+
returnNULL;/* "do nothing" */
2456+
}
2457+
2458+
if (newtuple!=slottuple)
2459+
{
2460+
/*
2461+
* Return the modified tuple using the es_trig_tuple_slot. We assume
2462+
* the tuple was allocated in per-tuple memory context, and therefore
2463+
* will go away by itself. The tuple table slot should not try to
2464+
* clear it.
2465+
*/
2466+
TupleTableSlot*newslot=estate->es_trig_tuple_slot;
2467+
TupleDesctupdesc=RelationGetDescr(relinfo->ri_RelationDesc);
2468+
2469+
if (newslot->tts_tupleDescriptor!=tupdesc)
2470+
ExecSetSlotDescriptor(newslot,tupdesc);
2471+
ExecStoreTuple(newtuple,newslot,InvalidBuffer, false);
2472+
slot=newslot;
23882473
}
2389-
returnnewtuple;
2474+
returnslot;
23902475
}
23912476

23922477
void

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp