aboutsummaryrefslogtreecommitdiff
path: root/src/backend/commands/trigger.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/commands/trigger.c')
-rw-r--r--src/backend/commands/trigger.c205
1 files changed, 162 insertions, 43 deletions
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index 4af8a9f9cdc..b404bc3dc51 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.134 2002/10/03 21:06:23 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/commands/trigger.c,v 1.135 2002/10/14 16:51:29 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -49,7 +49,7 @@ static HeapTuple ExecCallTriggerFunc(TriggerData *trigdata,
static void DeferredTriggerSaveEvent(ResultRelInfo *relinfo, int event,
HeapTuple oldtup, HeapTuple newtup);
static void DeferredTriggerExecute(DeferredTriggerEvent event, int itemno,
- Relation rel, FmgrInfo *finfo,
+ Relation rel, TriggerDesc *trigdesc, FmgrInfo *finfo,
MemoryContext per_tuple_context);
@@ -680,9 +680,12 @@ renametrig(Oid relid,
/*
* Build trigger data to attach to the given relcache entry.
*
- * Note that trigger data must be allocated in CacheMemoryContext
- * to ensure it survives as long as the relcache entry. But we
- * are probably running in a less long-lived working context.
+ * Note that trigger data attached to a relcache entry must be stored in
+ * CacheMemoryContext to ensure it survives as long as the relcache entry.
+ * But we should be running in a less long-lived working context. To avoid
+ * leaking cache memory if this routine fails partway through, we build a
+ * temporary TriggerDesc in working memory and then copy the completed
+ * structure into cache memory.
*/
void
RelationBuildTriggers(Relation relation)
@@ -695,9 +698,11 @@ RelationBuildTriggers(Relation relation)
ScanKeyData skey;
SysScanDesc tgscan;
HeapTuple htup;
+ MemoryContext oldContext;
+
+ Assert(ntrigs > 0); /* else I should not have been called */
- triggers = (Trigger *) MemoryContextAlloc(CacheMemoryContext,
- ntrigs * sizeof(Trigger));
+ triggers = (Trigger *) palloc(ntrigs * sizeof(Trigger));
/*
* Note: since we scan the triggers using TriggerRelidNameIndex, we
@@ -726,9 +731,8 @@ RelationBuildTriggers(Relation relation)
build = &(triggers[found]);
build->tgoid = HeapTupleGetOid(htup);
- build->tgname = MemoryContextStrdup(CacheMemoryContext,
- DatumGetCString(DirectFunctionCall1(nameout,
- NameGetDatum(&pg_trigger->tgname))));
+ build->tgname = DatumGetCString(DirectFunctionCall1(nameout,
+ NameGetDatum(&pg_trigger->tgname)));
build->tgfoid = pg_trigger->tgfoid;
build->tgtype = pg_trigger->tgtype;
build->tgenabled = pg_trigger->tgenabled;
@@ -753,13 +757,10 @@ RelationBuildTriggers(Relation relation)
elog(ERROR, "RelationBuildTriggers: tgargs IS NULL for rel %s",
RelationGetRelationName(relation));
p = (char *) VARDATA(val);
- build->tgargs = (char **)
- MemoryContextAlloc(CacheMemoryContext,
- build->tgnargs * sizeof(char *));
+ build->tgargs = (char **) palloc(build->tgnargs * sizeof(char *));
for (i = 0; i < build->tgnargs; i++)
{
- build->tgargs[i] = MemoryContextStrdup(CacheMemoryContext,
- p);
+ build->tgargs[i] = pstrdup(p);
p += strlen(p) + 1;
}
}
@@ -778,18 +779,30 @@ RelationBuildTriggers(Relation relation)
RelationGetRelationName(relation));
/* Build trigdesc */
- trigdesc = (TriggerDesc *) MemoryContextAlloc(CacheMemoryContext,
- sizeof(TriggerDesc));
+ trigdesc = (TriggerDesc *) palloc(sizeof(TriggerDesc));
MemSet(trigdesc, 0, sizeof(TriggerDesc));
trigdesc->triggers = triggers;
trigdesc->numtriggers = ntrigs;
for (found = 0; found < ntrigs; found++)
InsertTrigger(trigdesc, &(triggers[found]), found);
- relation->trigdesc = trigdesc;
+ /* Copy completed trigdesc into cache storage */
+ oldContext = MemoryContextSwitchTo(CacheMemoryContext);
+ relation->trigdesc = CopyTriggerDesc(trigdesc);
+ MemoryContextSwitchTo(oldContext);
+
+ /* Release working memory */
+ FreeTriggerDesc(trigdesc);
}
-/* Insert the given trigger into the appropriate index list(s) for it */
+/*
+ * Insert the given trigger into the appropriate index list(s) for it
+ *
+ * To simplify storage management, we allocate each index list at the max
+ * possible size (trigdesc->numtriggers) if it's used at all. This does
+ * not waste space permanently since we're only building a temporary
+ * trigdesc at this point.
+ */
static void
InsertTrigger(TriggerDesc *trigdesc, Trigger *trigger, int indx)
{
@@ -830,11 +843,7 @@ InsertTrigger(TriggerDesc *trigdesc, Trigger *trigger, int indx)
{
tp = &(t[TRIGGER_EVENT_INSERT]);
if (*tp == NULL)
- *tp = (int *) MemoryContextAlloc(CacheMemoryContext,
- sizeof(int));
- else
- *tp = (int *) repalloc(*tp, (n[TRIGGER_EVENT_INSERT] + 1) *
- sizeof(int));
+ *tp = (int *) palloc(trigdesc->numtriggers * sizeof(int));
(*tp)[n[TRIGGER_EVENT_INSERT]] = indx;
(n[TRIGGER_EVENT_INSERT])++;
}
@@ -843,11 +852,7 @@ InsertTrigger(TriggerDesc *trigdesc, Trigger *trigger, int indx)
{
tp = &(t[TRIGGER_EVENT_DELETE]);
if (*tp == NULL)
- *tp = (int *) MemoryContextAlloc(CacheMemoryContext,
- sizeof(int));
- else
- *tp = (int *) repalloc(*tp, (n[TRIGGER_EVENT_DELETE] + 1) *
- sizeof(int));
+ *tp = (int *) palloc(trigdesc->numtriggers * sizeof(int));
(*tp)[n[TRIGGER_EVENT_DELETE]] = indx;
(n[TRIGGER_EVENT_DELETE])++;
}
@@ -856,16 +861,113 @@ InsertTrigger(TriggerDesc *trigdesc, Trigger *trigger, int indx)
{
tp = &(t[TRIGGER_EVENT_UPDATE]);
if (*tp == NULL)
- *tp = (int *) MemoryContextAlloc(CacheMemoryContext,
- sizeof(int));
- else
- *tp = (int *) repalloc(*tp, (n[TRIGGER_EVENT_UPDATE] + 1) *
- sizeof(int));
+ *tp = (int *) palloc(trigdesc->numtriggers * sizeof(int));
(*tp)[n[TRIGGER_EVENT_UPDATE]] = indx;
(n[TRIGGER_EVENT_UPDATE])++;
}
}
+/*
+ * Copy a TriggerDesc data structure.
+ *
+ * The copy is allocated in the current memory context.
+ */
+TriggerDesc *
+CopyTriggerDesc(TriggerDesc *trigdesc)
+{
+ TriggerDesc *newdesc;
+ uint16 *n;
+ int **t,
+ *tnew;
+ Trigger *trigger;
+ int i;
+
+ if (trigdesc == NULL || trigdesc->numtriggers <= 0)
+ return NULL;
+
+ newdesc = (TriggerDesc *) palloc(sizeof(TriggerDesc));
+ memcpy(newdesc, trigdesc, sizeof(TriggerDesc));
+
+ trigger = (Trigger *) palloc(trigdesc->numtriggers * sizeof(Trigger));
+ memcpy(trigger, trigdesc->triggers,
+ trigdesc->numtriggers * sizeof(Trigger));
+ newdesc->triggers = trigger;
+
+ for (i = 0; i < trigdesc->numtriggers; i++)
+ {
+ trigger->tgname = pstrdup(trigger->tgname);
+ if (trigger->tgnargs > 0)
+ {
+ char **newargs;
+ int16 j;
+
+ newargs = (char **) palloc(trigger->tgnargs * sizeof(char *));
+ for (j = 0; j < trigger->tgnargs; j++)
+ newargs[j] = pstrdup(trigger->tgargs[j]);
+ trigger->tgargs = newargs;
+ }
+ trigger++;
+ }
+
+ n = newdesc->n_before_statement;
+ t = newdesc->tg_before_statement;
+ for (i = 0; i < TRIGGER_NUM_EVENT_CLASSES; i++)
+ {
+ if (n[i] > 0)
+ {
+ tnew = (int *) palloc(n[i] * sizeof(int));
+ memcpy(tnew, t[i], n[i] * sizeof(int));
+ t[i] = tnew;
+ }
+ else
+ t[i] = NULL;
+ }
+ n = newdesc->n_before_row;
+ t = newdesc->tg_before_row;
+ for (i = 0; i < TRIGGER_NUM_EVENT_CLASSES; i++)
+ {
+ if (n[i] > 0)
+ {
+ tnew = (int *) palloc(n[i] * sizeof(int));
+ memcpy(tnew, t[i], n[i] * sizeof(int));
+ t[i] = tnew;
+ }
+ else
+ t[i] = NULL;
+ }
+ n = newdesc->n_after_row;
+ t = newdesc->tg_after_row;
+ for (i = 0; i < TRIGGER_NUM_EVENT_CLASSES; i++)
+ {
+ if (n[i] > 0)
+ {
+ tnew = (int *) palloc(n[i] * sizeof(int));
+ memcpy(tnew, t[i], n[i] * sizeof(int));
+ t[i] = tnew;
+ }
+ else
+ t[i] = NULL;
+ }
+ n = newdesc->n_after_statement;
+ t = newdesc->tg_after_statement;
+ for (i = 0; i < TRIGGER_NUM_EVENT_CLASSES; i++)
+ {
+ if (n[i] > 0)
+ {
+ tnew = (int *) palloc(n[i] * sizeof(int));
+ memcpy(tnew, t[i], n[i] * sizeof(int));
+ t[i] = tnew;
+ }
+ else
+ t[i] = NULL;
+ }
+
+ return newdesc;
+}
+
+/*
+ * Free a TriggerDesc data structure.
+ */
void
FreeTriggerDesc(TriggerDesc *trigdesc)
{
@@ -909,6 +1011,10 @@ FreeTriggerDesc(TriggerDesc *trigdesc)
pfree(trigdesc);
}
+/*
+ * Compare two TriggerDesc structures for logical equality.
+ */
+#ifdef NOT_USED
bool
equalTriggerDescs(TriggerDesc *trigdesc1, TriggerDesc *trigdesc2)
{
@@ -966,6 +1072,7 @@ equalTriggerDescs(TriggerDesc *trigdesc1, TriggerDesc *trigdesc2)
return false;
return true;
}
+#endif /* NOT_USED */
/*
* Call a trigger function.
@@ -1455,17 +1562,17 @@ deferredTriggerAddEvent(DeferredTriggerEvent event)
* event: event currently being fired.
* itemno: item within event currently being fired.
* rel: open relation for event.
- * finfo: array of fmgr lookup cache entries (one per trigger of relation).
+ * trigdesc: working copy of rel's trigger info.
+ * finfo: array of fmgr lookup cache entries (one per trigger in trigdesc).
* per_tuple_context: memory context to call trigger function in.
* ----------
*/
static void
DeferredTriggerExecute(DeferredTriggerEvent event, int itemno,
- Relation rel, FmgrInfo *finfo,
+ Relation rel, TriggerDesc *trigdesc, FmgrInfo *finfo,
MemoryContext per_tuple_context)
{
Oid tgoid = event->dte_item[itemno].dti_tgoid;
- TriggerDesc *trigdesc = rel->trigdesc;
TriggerData LocTriggerData;
HeapTupleData oldtuple;
HeapTupleData newtuple;
@@ -1530,7 +1637,7 @@ DeferredTriggerExecute(DeferredTriggerEvent event, int itemno,
}
/*
- * Call the trigger and throw away an eventually returned updated
+ * Call the trigger and throw away any eventually returned updated
* tuple.
*/
rettuple = ExecCallTriggerFunc(&LocTriggerData,
@@ -1569,6 +1676,7 @@ deferredTriggerInvokeEvents(bool immediate_only)
prev_event = NULL;
MemoryContext per_tuple_context;
Relation rel = NULL;
+ TriggerDesc *trigdesc = NULL;
FmgrInfo *finfo = NULL;
/*
@@ -1637,6 +1745,7 @@ deferredTriggerInvokeEvents(bool immediate_only)
{
if (rel)
heap_close(rel, NoLock);
+ FreeTriggerDesc(trigdesc);
if (finfo)
pfree(finfo);
@@ -1647,16 +1756,25 @@ deferredTriggerInvokeEvents(bool immediate_only)
rel = heap_open(event->dte_relid, NoLock);
/*
- * Allocate space to cache fmgr lookup info for
- * triggers of this relation.
+ * Copy relation's trigger info so that we have a stable
+ * copy no matter what the called triggers do.
+ */
+ trigdesc = CopyTriggerDesc(rel->trigdesc);
+
+ if (trigdesc == NULL)
+ elog(ERROR, "deferredTriggerInvokeEvents: relation %u has no triggers",
+ event->dte_relid);
+
+ /*
+ * Allocate space to cache fmgr lookup info for triggers.
*/
finfo = (FmgrInfo *)
- palloc(rel->trigdesc->numtriggers * sizeof(FmgrInfo));
+ palloc(trigdesc->numtriggers * sizeof(FmgrInfo));
MemSet(finfo, 0,
- rel->trigdesc->numtriggers * sizeof(FmgrInfo));
+ trigdesc->numtriggers * sizeof(FmgrInfo));
}
- DeferredTriggerExecute(event, i, rel, finfo,
+ DeferredTriggerExecute(event, i, rel, trigdesc, finfo,
per_tuple_context);
event->dte_item[i].dti_state |= TRIGGER_DEFERRED_DONE;
@@ -1708,6 +1826,7 @@ deferredTriggerInvokeEvents(bool immediate_only)
/* Release working resources */
if (rel)
heap_close(rel, NoLock);
+ FreeTriggerDesc(trigdesc);
if (finfo)
pfree(finfo);
MemoryContextDelete(per_tuple_context);