From c9fe12831632efc437354cb0482fd6cb5f246a4a Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Mon, 22 Jan 2001 00:50:07 +0000 Subject: [PATCH] Clean up per-tuple memory leaks in trigger firing and plpgsql expression evaluation. --- src/backend/commands/copy.c | 9 ++-- src/backend/commands/trigger.c | 87 +++++++++++++++++++++++------------- src/backend/executor/execMain.c | 62 +++++++++++++------------ src/backend/executor/execUtils.c | 38 +++++++++------- src/backend/executor/nodeIndexscan.c | 11 +---- src/include/commands/trigger.h | 15 ++++--- src/include/executor/executor.h | 20 ++++++++- src/include/nodes/execnodes.h | 4 +- src/pl/plpgsql/src/pl_exec.c | 61 +++++-------------------- 9 files changed, 160 insertions(+), 147 deletions(-) diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c index e26dac47ce..82577b0426 100644 --- a/src/backend/commands/copy.c +++ b/src/backend/commands/copy.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.130 2001/01/19 06:54:57 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.131 2001/01/22 00:50:06 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -705,6 +705,9 @@ CopyFrom(Relation rel, bool binary, bool oids, FILE *fp, lineno++; + /* Reset the per-output-tuple exprcontext */ + ResetPerTupleExprContext(estate); + /* Initialize all values for row to NULL */ MemSet(values, 0, attr_count * sizeof(Datum)); MemSet(nulls, 'n', attr_count * sizeof(char)); @@ -861,7 +864,7 @@ CopyFrom(Relation rel, bool binary, bool oids, FILE *fp, { HeapTuple newtuple; - newtuple = ExecBRInsertTriggers(rel, tuple); + newtuple = ExecBRInsertTriggers(estate, rel, tuple); if (newtuple == NULL) /* "do nothing" */ skip_tuple = true; @@ -895,7 +898,7 @@ CopyFrom(Relation rel, bool binary, bool oids, FILE *fp, /* AFTER ROW INSERT Triggers */ if (rel->trigdesc && rel->trigdesc->n_after_row[TRIGGER_EVENT_INSERT] > 0) - ExecARInsertTriggers(rel, tuple); + ExecARInsertTriggers(estate, rel, tuple); } for (i = 0; i < attr_count; i++) diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c index d5946ebd3e..ccb2aa5fce 100644 --- a/src/backend/commands/trigger.c +++ b/src/backend/commands/trigger.c @@ -7,7 +7,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/trigger.c,v 1.82 2000/12/18 00:44:46 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/trigger.c,v 1.83 2001/01/22 00:50:07 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -36,7 +36,8 @@ static void DescribeTrigger(TriggerDesc *trigdesc, Trigger *trigger); static HeapTuple GetTupleForTrigger(EState *estate, ItemPointer tid, TupleTableSlot **newSlot); static HeapTuple ExecCallTriggerFunc(Trigger *trigger, - TriggerData *trigdata); + TriggerData *trigdata, + MemoryContext per_tuple_context); static void DeferredTriggerSaveEvent(Relation rel, int event, HeapTuple oldtup, HeapTuple newtup); @@ -831,10 +832,13 @@ equalTriggerDescs(TriggerDesc *trigdesc1, TriggerDesc *trigdesc2) } static HeapTuple -ExecCallTriggerFunc(Trigger *trigger, TriggerData *trigdata) +ExecCallTriggerFunc(Trigger *trigger, + TriggerData *trigdata, + MemoryContext per_tuple_context) { FunctionCallInfoData fcinfo; Datum result; + MemoryContext oldContext; /* * Fmgr lookup info is cached in the Trigger structure, @@ -844,6 +848,14 @@ ExecCallTriggerFunc(Trigger *trigger, TriggerData *trigdata) fmgr_info(trigger->tgfoid, &trigger->tgfunc); /* + * Do the function evaluation in the per-tuple memory context, + * so that leaked memory will be reclaimed once per tuple. + * Note in particular that any new tuple created by the trigger function + * will live till the end of the tuple cycle. + */ + oldContext = MemoryContextSwitchTo(per_tuple_context); + + /* * Call the function, passing no arguments but setting a context. */ MemSet(&fcinfo, 0, sizeof(fcinfo)); @@ -853,6 +865,8 @@ ExecCallTriggerFunc(Trigger *trigger, TriggerData *trigdata) result = FunctionCallInvoke(&fcinfo); + MemoryContextSwitchTo(oldContext); + /* * Trigger protocol allows function to return a null pointer, * but NOT to set the isnull result flag. @@ -865,7 +879,7 @@ ExecCallTriggerFunc(Trigger *trigger, TriggerData *trigdata) } HeapTuple -ExecBRInsertTriggers(Relation rel, HeapTuple trigtuple) +ExecBRInsertTriggers(EState *estate, Relation rel, HeapTuple trigtuple) { int ntrigs = rel->trigdesc->n_before_row[TRIGGER_EVENT_INSERT]; Trigger **trigger = rel->trigdesc->tg_before_row[TRIGGER_EVENT_INSERT]; @@ -884,20 +898,20 @@ ExecBRInsertTriggers(Relation rel, HeapTuple trigtuple) continue; LocTriggerData.tg_trigtuple = oldtuple = newtuple; LocTriggerData.tg_trigger = trigger[i]; - newtuple = ExecCallTriggerFunc(trigger[i], &LocTriggerData); + newtuple = ExecCallTriggerFunc(trigger[i], &LocTriggerData, + GetPerTupleMemoryContext(estate)); + if (oldtuple != newtuple && oldtuple != trigtuple) + heap_freetuple(oldtuple); if (newtuple == NULL) break; - else if (oldtuple != newtuple && oldtuple != trigtuple) - heap_freetuple(oldtuple); } return newtuple; } void -ExecARInsertTriggers(Relation rel, HeapTuple trigtuple) +ExecARInsertTriggers(EState *estate, Relation rel, HeapTuple trigtuple) { DeferredTriggerSaveEvent(rel, TRIGGER_EVENT_INSERT, NULL, trigtuple); - return; } bool @@ -926,7 +940,8 @@ ExecBRDeleteTriggers(EState *estate, ItemPointer tupleid) continue; LocTriggerData.tg_trigtuple = trigtuple; LocTriggerData.tg_trigger = trigger[i]; - newtuple = ExecCallTriggerFunc(trigger[i], &LocTriggerData); + newtuple = ExecCallTriggerFunc(trigger[i], &LocTriggerData, + GetPerTupleMemoryContext(estate)); if (newtuple == NULL) break; if (newtuple != trigtuple) @@ -944,7 +959,7 @@ ExecARDeleteTriggers(EState *estate, ItemPointer tupleid) HeapTuple trigtuple = GetTupleForTrigger(estate, tupleid, NULL); DeferredTriggerSaveEvent(rel, TRIGGER_EVENT_DELETE, trigtuple, NULL); - return; + heap_freetuple(trigtuple); } HeapTuple @@ -981,11 +996,12 @@ ExecBRUpdateTriggers(EState *estate, ItemPointer tupleid, HeapTuple newtuple) LocTriggerData.tg_trigtuple = trigtuple; LocTriggerData.tg_newtuple = oldtuple = newtuple; LocTriggerData.tg_trigger = trigger[i]; - newtuple = ExecCallTriggerFunc(trigger[i], &LocTriggerData); + newtuple = ExecCallTriggerFunc(trigger[i], &LocTriggerData, + GetPerTupleMemoryContext(estate)); + if (oldtuple != newtuple && oldtuple != intuple) + heap_freetuple(oldtuple); if (newtuple == NULL) break; - else if (oldtuple != newtuple && oldtuple != intuple) - heap_freetuple(oldtuple); } heap_freetuple(trigtuple); return newtuple; @@ -998,7 +1014,7 @@ ExecARUpdateTriggers(EState *estate, ItemPointer tupleid, HeapTuple newtuple) HeapTuple trigtuple = GetTupleForTrigger(estate, tupleid, NULL); DeferredTriggerSaveEvent(rel, TRIGGER_EVENT_UPDATE, trigtuple, newtuple); - return; + heap_freetuple(trigtuple); } @@ -1236,7 +1252,7 @@ deferredTriggerGetPreviousEvent(Oid relid, ItemPointer ctid) } elog(ERROR, - "deferredTriggerGetPreviousEvent(): event for tuple %s not found", + "deferredTriggerGetPreviousEvent: event for tuple %s not found", DatumGetCString(DirectFunctionCall1(tidout, PointerGetDatum(ctid)))); return NULL; } @@ -1250,7 +1266,8 @@ deferredTriggerGetPreviousEvent(Oid relid, ItemPointer ctid) * ---------- */ static void -deferredTriggerExecute(DeferredTriggerEvent event, int itemno) +deferredTriggerExecute(DeferredTriggerEvent event, int itemno, + MemoryContext per_tuple_context) { Relation rel; TriggerData LocTriggerData; @@ -1271,7 +1288,7 @@ deferredTriggerExecute(DeferredTriggerEvent event, int itemno) ItemPointerCopy(&(event->dte_oldctid), &(oldtuple.t_self)); heap_fetch(rel, SnapshotAny, &oldtuple, &oldbuffer); if (!oldtuple.t_data) - elog(ERROR, "deferredTriggerExecute(): failed to fetch old tuple"); + elog(ERROR, "deferredTriggerExecute: failed to fetch old tuple"); } if (ItemPointerIsValid(&(event->dte_newctid))) @@ -1279,7 +1296,7 @@ deferredTriggerExecute(DeferredTriggerEvent event, int itemno) ItemPointerCopy(&(event->dte_newctid), &(newtuple.t_self)); heap_fetch(rel, SnapshotAny, &newtuple, &newbuffer); if (!newtuple.t_data) - elog(ERROR, "deferredTriggerExecute(): failed to fetch new tuple"); + elog(ERROR, "deferredTriggerExecute: failed to fetch new tuple"); } /* ---------- @@ -1320,7 +1337,9 @@ deferredTriggerExecute(DeferredTriggerEvent event, int itemno) * updated tuple. * ---------- */ - rettuple = ExecCallTriggerFunc(LocTriggerData.tg_trigger, &LocTriggerData); + rettuple = ExecCallTriggerFunc(LocTriggerData.tg_trigger, + &LocTriggerData, + per_tuple_context); if (rettuple != NULL && rettuple != &oldtuple && rettuple != &newtuple) heap_freetuple(rettuple); @@ -1359,6 +1378,7 @@ deferredTriggerInvokeEvents(bool immediate_only) int still_deferred_ones; int eventno = -1; int i; + MemoryContext per_tuple_context; /* ---------- * For now we process all events - to speedup transaction blocks @@ -1369,10 +1389,21 @@ deferredTriggerInvokeEvents(bool immediate_only) * SET CONSTRAINTS ... command finishes and calls EndQuery. * ---------- */ + + /* Make a per-tuple memory context for trigger function calls */ + per_tuple_context = + AllocSetContextCreate(CurrentMemoryContext, + "DeferredTriggerTupleContext", + 0, + ALLOCSET_DEFAULT_INITSIZE, + ALLOCSET_DEFAULT_MAXSIZE); + foreach(el, deftrig_events) { eventno++; + MemoryContextReset(per_tuple_context); + /* ---------- * Get the event and check if it is completely done. * ---------- @@ -1409,7 +1440,7 @@ deferredTriggerInvokeEvents(bool immediate_only) * So let's fire it... * ---------- */ - deferredTriggerExecute(event, i); + deferredTriggerExecute(event, i, per_tuple_context); event->dte_item[i].dti_state |= TRIGGER_DEFERRED_DONE; } @@ -1421,6 +1452,8 @@ deferredTriggerInvokeEvents(bool immediate_only) if (!still_deferred_ones) event->dte_event |= TRIGGER_DEFERRED_DONE; } + + MemoryContextDelete(per_tuple_context); } @@ -1866,13 +1899,10 @@ DeferredTriggerSaveEvent(Relation rel, int event, * Check if we're interested in this row at all * ---------- */ - if (rel->trigdesc->n_after_row[TRIGGER_EVENT_INSERT] == 0 && - rel->trigdesc->n_after_row[TRIGGER_EVENT_UPDATE] == 0 && - rel->trigdesc->n_after_row[TRIGGER_EVENT_DELETE] == 0 && - rel->trigdesc->n_before_row[TRIGGER_EVENT_INSERT] == 0 && - rel->trigdesc->n_before_row[TRIGGER_EVENT_UPDATE] == 0 && - rel->trigdesc->n_before_row[TRIGGER_EVENT_DELETE] == 0) + ntriggers = rel->trigdesc->n_after_row[event]; + if (ntriggers <= 0) return; + triggers = rel->trigdesc->tg_after_row[event]; /* ---------- * Get the CTID's of OLD and NEW @@ -1893,9 +1923,6 @@ DeferredTriggerSaveEvent(Relation rel, int event, */ oldcxt = MemoryContextSwitchTo(deftrig_cxt); - ntriggers = rel->trigdesc->n_after_row[event]; - triggers = rel->trigdesc->tg_after_row[event]; - new_size = sizeof(DeferredTriggerEventData) + ntriggers * sizeof(DeferredTriggerEventItem); diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index 1c826292c5..a4dbaae302 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -27,7 +27,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.134 2001/01/01 21:22:54 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.135 2001/01/22 00:50:07 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -941,11 +941,13 @@ ExecutePlan(EState *estate, /* * Loop until we've processed the proper number of tuples from the - * plan.. + * plan. */ for (;;) { + /* Reset the per-output-tuple exprcontext */ + ResetPerTupleExprContext(estate); /* * Execute the plan and obtain a tuple @@ -1213,20 +1215,25 @@ ExecAppend(TupleTableSlot *slot, /* BEFORE ROW INSERT Triggers */ if (resultRelationDesc->trigdesc && - resultRelationDesc->trigdesc->n_before_row[TRIGGER_EVENT_INSERT] > 0) + resultRelationDesc->trigdesc->n_before_row[TRIGGER_EVENT_INSERT] > 0) { HeapTuple newtuple; - newtuple = ExecBRInsertTriggers(resultRelationDesc, tuple); + newtuple = ExecBRInsertTriggers(estate, resultRelationDesc, tuple); if (newtuple == NULL) /* "do nothing" */ return; if (newtuple != tuple) /* modified by Trigger(s) */ { - Assert(slot->ttc_shouldFree); - heap_freetuple(tuple); - slot->val = tuple = newtuple; + /* + * Insert modified tuple into tuple table slot, replacing the + * original. We assume that it was allocated in per-tuple + * memory context, and therefore will go away by itself. + * The tuple table slot should not try to clear it. + */ + ExecStoreTuple(newtuple, slot, InvalidBuffer, false); + tuple = newtuple; } } @@ -1257,8 +1264,9 @@ ExecAppend(TupleTableSlot *slot, ExecInsertIndexTuples(slot, &(tuple->t_self), estate, false); /* AFTER ROW INSERT Triggers */ - if (resultRelationDesc->trigdesc) - ExecARInsertTriggers(resultRelationDesc, tuple); + if (resultRelationDesc->trigdesc && + resultRelationDesc->trigdesc->n_after_row[TRIGGER_EVENT_INSERT] > 0) + ExecARInsertTriggers(estate, resultRelationDesc, tuple); } /* ---------------------------------------------------------------- @@ -1286,7 +1294,7 @@ ExecDelete(TupleTableSlot *slot, /* BEFORE ROW DELETE Triggers */ if (resultRelationDesc->trigdesc && - resultRelationDesc->trigdesc->n_before_row[TRIGGER_EVENT_DELETE] > 0) + resultRelationDesc->trigdesc->n_before_row[TRIGGER_EVENT_DELETE] > 0) { bool dodelete; @@ -1343,9 +1351,9 @@ ldelete:; */ /* AFTER ROW DELETE Triggers */ - if (resultRelationDesc->trigdesc) + if (resultRelationDesc->trigdesc && + resultRelationDesc->trigdesc->n_after_row[TRIGGER_EVENT_DELETE] > 0) ExecARDeleteTriggers(estate, tupleid); - } /* ---------------------------------------------------------------- @@ -1393,7 +1401,7 @@ ExecReplace(TupleTableSlot *slot, /* BEFORE ROW UPDATE Triggers */ if (resultRelationDesc->trigdesc && - resultRelationDesc->trigdesc->n_before_row[TRIGGER_EVENT_UPDATE] > 0) + resultRelationDesc->trigdesc->n_before_row[TRIGGER_EVENT_UPDATE] > 0) { HeapTuple newtuple; @@ -1404,9 +1412,14 @@ ExecReplace(TupleTableSlot *slot, if (newtuple != tuple) /* modified by Trigger(s) */ { - Assert(slot->ttc_shouldFree); - heap_freetuple(tuple); - slot->val = tuple = newtuple; + /* + * Insert modified tuple into tuple table slot, replacing the + * original. We assume that it was allocated in per-tuple + * memory context, and therefore will go away by itself. + * The tuple table slot should not try to clear it. + */ + ExecStoreTuple(newtuple, slot, InvalidBuffer, false); + tuple = newtuple; } } @@ -1478,7 +1491,8 @@ lreplace:; ExecInsertIndexTuples(slot, &(tuple->t_self), estate, true); /* AFTER ROW UPDATE Triggers */ - if (resultRelationDesc->trigdesc) + if (resultRelationDesc->trigdesc && + resultRelationDesc->trigdesc->n_after_row[TRIGGER_EVENT_UPDATE] > 0) ExecARUpdateTriggers(estate, tupleid, tuple); } @@ -1514,19 +1528,9 @@ ExecRelCheck(ResultRelInfo *resultRelInfo, /* * We will use the EState's per-tuple context for evaluating constraint - * expressions. Create it if it's not already there; if it is, reset it - * to free previously-used storage. + * expressions (creating it if it's not already there). */ - econtext = estate->es_per_tuple_exprcontext; - if (econtext == NULL) - { - oldContext = MemoryContextSwitchTo(estate->es_query_cxt); - estate->es_per_tuple_exprcontext = econtext = - MakeExprContext(NULL, estate->es_query_cxt); - MemoryContextSwitchTo(oldContext); - } - else - ResetExprContext(econtext); + econtext = GetPerTupleExprContext(estate); /* Arrange for econtext's scan tuple to be the tuple under test */ econtext->ecxt_scantuple = slot; diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c index 5d4d7f145b..a4c7143aa7 100644 --- a/src/backend/executor/execUtils.c +++ b/src/backend/executor/execUtils.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/execUtils.c,v 1.70 2000/12/27 23:59:11 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/execUtils.c,v 1.71 2001/01/22 00:50:07 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -230,6 +230,26 @@ FreeExprContext(ExprContext *econtext) pfree(econtext); } +/* + * Build a per-output-tuple ExprContext for an EState. + * + * This is normally invoked via GetPerTupleExprContext() macro. + */ +ExprContext * +MakePerTupleExprContext(EState *estate) +{ + if (estate->es_per_tuple_exprcontext == NULL) + { + MemoryContext oldContext; + + oldContext = MemoryContextSwitchTo(estate->es_query_cxt); + estate->es_per_tuple_exprcontext = + MakeExprContext(NULL, estate->es_query_cxt); + MemoryContextSwitchTo(oldContext); + } + return estate->es_per_tuple_exprcontext; +} + /* ---------------------------------------------------------------- * Result slot tuple type and ProjectionInfo support * ---------------------------------------------------------------- @@ -836,21 +856,9 @@ ExecInsertIndexTuples(TupleTableSlot *slot, /* * We will use the EState's per-tuple context for evaluating predicates - * and functional-index functions. Create it if it's not already there; - * if it is, reset it to free previously-used storage. + * and functional-index functions (creating it if it's not already there). */ - econtext = estate->es_per_tuple_exprcontext; - if (econtext == NULL) - { - MemoryContext oldContext; - - oldContext = MemoryContextSwitchTo(estate->es_query_cxt); - estate->es_per_tuple_exprcontext = econtext = - MakeExprContext(NULL, estate->es_query_cxt); - MemoryContextSwitchTo(oldContext); - } - else - ResetExprContext(econtext); + econtext = GetPerTupleExprContext(estate); /* Arrange for econtext's scan tuple to be the tuple under test */ econtext->ecxt_scantuple = slot; diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c index a8b29514b8..7c389975a7 100644 --- a/src/backend/executor/nodeIndexscan.c +++ b/src/backend/executor/nodeIndexscan.c @@ -8,14 +8,12 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/nodeIndexscan.c,v 1.54 2000/08/24 03:29:03 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/nodeIndexscan.c,v 1.55 2001/01/22 00:50:07 tgl Exp $ * *------------------------------------------------------------------------- */ /* * INTERFACE ROUTINES - * ExecInsertIndexTuples inserts tuples into indices on result relation - * * ExecIndexScan scans a relation using indices * ExecIndexNext using index to retrieve next tuple * ExecInitIndexScan creates and initializes state info. @@ -23,16 +21,9 @@ * ExecEndIndexScan releases all storage. * ExecIndexMarkPos marks scan position. * ExecIndexRestrPos restores scan position. - * - * NOTES - * the code supporting ExecInsertIndexTuples should be - * collected and merged with the genam stuff. - * */ #include "postgres.h" - - #include "access/genam.h" #include "access/heapam.h" #include "executor/execdebug.h" diff --git a/src/include/commands/trigger.h b/src/include/commands/trigger.h index c9fbcae54a..a9b689ba17 100644 --- a/src/include/commands/trigger.h +++ b/src/include/commands/trigger.h @@ -6,7 +6,7 @@ * Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: trigger.h,v 1.22 2000/12/18 00:44:48 tgl Exp $ + * $Id: trigger.h,v 1.23 2001/01/22 00:50:07 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -89,13 +89,16 @@ extern void FreeTriggerDesc(TriggerDesc *trigdesc); extern bool equalTriggerDescs(TriggerDesc *trigdesc1, TriggerDesc *trigdesc2); -extern HeapTuple ExecBRInsertTriggers(Relation rel, HeapTuple tuple); -extern void ExecARInsertTriggers(Relation rel, HeapTuple tuple); +extern HeapTuple ExecBRInsertTriggers(EState *estate, + Relation rel, HeapTuple tuple); +extern void ExecARInsertTriggers(EState *estate, + Relation rel, HeapTuple tuple); extern bool ExecBRDeleteTriggers(EState *estate, ItemPointer tupleid); extern void ExecARDeleteTriggers(EState *estate, ItemPointer tupleid); -extern HeapTuple ExecBRUpdateTriggers(EState *estate, ItemPointer tupleid, HeapTuple tuple); -extern void ExecARUpdateTriggers(EState *estate, ItemPointer tupleid, HeapTuple tuple); - +extern HeapTuple ExecBRUpdateTriggers(EState *estate, ItemPointer tupleid, + HeapTuple tuple); +extern void ExecARUpdateTriggers(EState *estate, ItemPointer tupleid, + HeapTuple tuple); /* ---------- diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h index 2197af567f..8e7f4a4863 100644 --- a/src/include/executor/executor.h +++ b/src/include/executor/executor.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: executor.h,v 1.53 2000/11/12 00:37:01 tgl Exp $ + * $Id: executor.h,v 1.54 2001/01/22 00:50:07 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -153,6 +153,24 @@ extern void FreeExprContext(ExprContext *econtext); #define ResetExprContext(econtext) \ MemoryContextReset((econtext)->ecxt_per_tuple_memory) +extern ExprContext *MakePerTupleExprContext(EState *estate); + +/* Get an EState's per-output-tuple exprcontext, making it if first use */ +#define GetPerTupleExprContext(estate) \ + ((estate)->es_per_tuple_exprcontext ? \ + (estate)->es_per_tuple_exprcontext : \ + MakePerTupleExprContext(estate)) + +#define GetPerTupleMemoryContext(estate) \ + (GetPerTupleExprContext(estate)->ecxt_per_tuple_memory) + +/* Reset an EState's per-output-tuple exprcontext, if one's been created */ +#define ResetPerTupleExprContext(estate) \ + do { \ + if ((estate)->es_per_tuple_exprcontext) \ + ResetExprContext((estate)->es_per_tuple_exprcontext); \ + } while (0) + extern void ExecOpenIndices(ResultRelInfo *resultRelInfo); extern void ExecCloseIndices(ResultRelInfo *resultRelInfo); extern void ExecInsertIndexTuples(TupleTableSlot *slot, ItemPointer tupleid, diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index 4ba3a70c1b..e4d29178c4 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: execnodes.h,v 1.53 2000/11/12 00:37:01 tgl Exp $ + * $Id: execnodes.h,v 1.54 2001/01/22 00:50:07 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -251,7 +251,7 @@ typedef struct EState MemoryContext es_query_cxt; /* per-query context in which EState lives */ /* * this ExprContext is for per-output-tuple operations, such as - * constraint checks and index-value computations. It can be reset + * constraint checks and index-value computations. It will be reset * for each output tuple. Note that it will be created only if needed. */ ExprContext *es_per_tuple_exprcontext; diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c index fde0ad5a4b..f71ef90ef3 100644 --- a/src/pl/plpgsql/src/pl_exec.c +++ b/src/pl/plpgsql/src/pl_exec.c @@ -3,7 +3,7 @@ * procedural language * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.35 2001/01/06 01:43:01 tgl Exp $ + * $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.36 2001/01/22 00:50:07 tgl Exp $ * * This software is copyrighted by Jan Wieck - Hamburg. * @@ -112,8 +112,6 @@ static void exec_prepare_plan(PLpgSQL_execstate * estate, PLpgSQL_expr * expr); static bool exec_simple_check_node(Node *node); static void exec_simple_check_plan(PLpgSQL_expr * expr); -static void exec_eval_clear_fcache(Node *node); -static bool exec_eval_clear_fcache_walker(Node *node, void *context); static Datum exec_eval_simple_expr(PLpgSQL_execstate * estate, PLpgSQL_expr * expr, bool *isNull, @@ -2530,10 +2528,17 @@ exec_eval_simple_expr(PLpgSQL_execstate * estate, ParamListInfo paramLI; /* ---------- - * Create a simple expression context to hold the arguments + * Create a simple expression context to hold the arguments. + * + * NOTE: we pass TopMemoryContext as the query-lifetime context for + * function cache nodes and suchlike allocations. This is necessary + * because that's where the expression tree itself is (it'll never be + * freed in this backend, and the function cache nodes must live as + * long as it does). The memory allocation for plpgsql's plan trees + * really needs to be redesigned... * ---------- */ - econtext = MakeExprContext(NULL, TransactionCommandContext); + econtext = MakeExprContext(NULL, TopMemoryContext); paramLI = (ParamListInfo) palloc((expr->nparams + 1) * sizeof(ParamListInfoData)); econtext->ecxt_param_list_info = paramLI; @@ -2602,12 +2607,6 @@ exec_eval_simple_expr(PLpgSQL_execstate * estate, *rettype = expr->plan_simple_type; /* ---------- - * Clear any function cache entries in the expression tree - * ---------- - */ - exec_eval_clear_fcache(expr->plan_simple_expr); - - /* ---------- * Now call the executor to evaluate the expression * ---------- */ @@ -2902,46 +2901,6 @@ exec_simple_check_plan(PLpgSQL_expr * expr) expr->plan_simple_type = exprType(tle->expr); } - -/* ---------- - * exec_eval_clear_fcache - The function cache is palloc()'d by - * the executor, and contains call specific - * data based on the arguments. This has - * to be recalculated. - * ---------- - */ -static void -exec_eval_clear_fcache(Node *node) -{ - /* This tree walk requires no special setup, so away we go... */ - exec_eval_clear_fcache_walker(node, NULL); -} - -static bool -exec_eval_clear_fcache_walker(Node *node, void *context) -{ - if (node == NULL) - return false; - if (IsA(node, Expr)) - { - Expr *expr = (Expr *) node; - - switch (expr->opType) - { - case OP_EXPR: - ((Oper *) (expr->oper))->op_fcache = NULL; - break; - case FUNC_EXPR: - ((Func *) (expr->oper))->func_fcache = NULL; - break; - default: - break; - } - } - return expression_tree_walker(node, exec_eval_clear_fcache_walker, - context); -} - /* ---------- * exec_set_found Set the global found variable * to true/false -- 2.11.0