diff --git a/doc/src/sgml/trigger.sgml b/doc/src/sgml/trigger.sgml index 686c9580ed..bb88ccc4de 100644 --- a/doc/src/sgml/trigger.sgml +++ b/doc/src/sgml/trigger.sgml @@ -366,7 +366,6 @@ typedef struct Trigger Oid tgoid; char *tgname; Oid tgfoid; - FmgrInfo tgfunc; int16 tgtype; bool tgenabled; bool tgisconstraint; diff --git a/src/backend/access/common/scankey.c b/src/backend/access/common/scankey.c index dc2d36b52f..eb66d41bf2 100644 --- a/src/backend/access/common/scankey.c +++ b/src/backend/access/common/scankey.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/access/common/scankey.c,v 1.18 2001/01/24 19:42:47 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/access/common/scankey.c,v 1.19 2001/06/01 02:41:35 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -40,11 +40,13 @@ ScanKeyEntrySetIllegal(ScanKey entry) entry->sk_flags = 0; /* just in case... */ entry->sk_attno = InvalidAttrNumber; entry->sk_procedure = 0; /* should be InvalidRegProcedure */ + entry->sk_func.fn_oid = InvalidOid; + entry->sk_argument = (Datum) 0; } /* * ScanKeyEntryInitialize - * Initializes an scan key entry. + * Initializes a scan key entry. * * Note: * Assumes the scan key entry is valid. @@ -64,7 +66,6 @@ ScanKeyEntryInitialize(ScanKey entry, entry->sk_procedure = procedure; entry->sk_argument = argument; fmgr_info(procedure, &entry->sk_func); - entry->sk_nargs = entry->sk_func.fn_nargs; Assert(ScanKeyEntryIsLegal(entry)); } diff --git a/src/backend/access/index/indexam.c b/src/backend/access/index/indexam.c index 3cea6895f3..5b12930114 100644 --- a/src/backend/access/index/indexam.c +++ b/src/backend/access/index/indexam.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/access/index/indexam.c,v 1.49 2001/05/31 18:16:54 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/access/index/indexam.c,v 1.50 2001/06/01 02:41:35 tgl Exp $ * * INTERFACE ROUTINES * index_open - open an index relation by relationId @@ -232,7 +232,7 @@ index_beginscan(Relation relation, uint16 numberOfKeys, ScanKey key) { - IndexScanDesc scandesc; + IndexScanDesc scan; RegProcedure procedure; RELATION_CHECKS; @@ -249,14 +249,22 @@ index_beginscan(Relation relation, */ LockRelation(relation, AccessShareLock); - scandesc = (IndexScanDesc) + scan = (IndexScanDesc) DatumGetPointer(OidFunctionCall4(procedure, PointerGetDatum(relation), BoolGetDatum(scanFromEnd), UInt16GetDatum(numberOfKeys), PointerGetDatum(key))); - return scandesc; + /* + * We want to look up the amgettuple procedure just once per scan, + * not once per index_getnext call. So do it here and save + * the fmgr info result in the scan descriptor. + */ + GET_SCAN_PROCEDURE(beginscan, amgettuple); + fmgr_info(procedure, &scan->fn_getnext); + + return scan; } /* ---------------- @@ -345,19 +353,9 @@ index_getnext(IndexScanDesc scan, SCAN_CHECKS; - /* - * Look up the access procedure only once per scan. - */ - if (scan->fn_getnext.fn_oid == InvalidOid) - { - RegProcedure procedure; - - GET_SCAN_PROCEDURE(getnext, amgettuple); - fmgr_info(procedure, &scan->fn_getnext); - } - /* * have the am's gettuple proc do all the work. + * index_beginscan already set up fn_getnext. */ result = (RetrieveIndexResult) DatumGetPointer(FunctionCall2(&scan->fn_getnext, diff --git a/src/backend/access/index/istrat.c b/src/backend/access/index/istrat.c index 8cd1284972..188f69b571 100644 --- a/src/backend/access/index/istrat.c +++ b/src/backend/access/index/istrat.c @@ -9,7 +9,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/access/index/Attic/istrat.c,v 1.50 2001/05/30 19:53:40 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/access/index/Attic/istrat.c,v 1.51 2001/06/01 02:41:35 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -458,6 +458,8 @@ RelationInvokeStrategy(Relation relation, /* ---------------- * OperatorRelationFillScanKeyEntry + * + * Initialize a ScanKey entry given already-opened pg_operator relation. * ---------------- */ static void @@ -498,6 +500,7 @@ OperatorRelationFillScanKeyEntry(Relation operatorRelation, operatorObjectId); } + MemSet(entry, 0, sizeof(*entry)); entry->sk_flags = 0; entry->sk_procedure = ((Form_pg_operator) GETSTRUCT(tuple))->oprcode; @@ -511,14 +514,29 @@ OperatorRelationFillScanKeyEntry(Relation operatorRelation, "OperatorRelationFillScanKeyEntry: no procedure for operator %u", operatorObjectId); - fmgr_info(entry->sk_procedure, &entry->sk_func); - entry->sk_nargs = entry->sk_func.fn_nargs; + /* + * Formerly we initialized entry->sk_func here, but that's a waste of + * time because ScanKey entries in strategy maps are never actually + * used to invoke the operator. Furthermore, to do that we'd have to + * worry about setting the proper memory context (the map is probably + * not allocated in the current memory context!) + */ } /* * IndexSupportInitialize * Initializes an index strategy and associated support procedures. + * + * Data is returned into *indexStrategy, *indexSupport, and *isUnique, + * all of which are objects allocated by the caller. + * + * The primary input keys are indexObjectId and accessMethodObjectId. + * The caller also passes maxStrategyNumber, maxSupportNumber, and + * maxAttributeNumber, since these indicate the size of the indexStrategy + * and indexSupport arrays it has allocated --- but in practice these + * numbers must always match those obtainable from the system catalog + * entries for the index and access method. */ void IndexSupportInitialize(IndexStrategy indexStrategy, @@ -578,7 +596,7 @@ IndexSupportInitialize(IndexStrategy indexStrategy, if (!OidIsValid(iform->indkey[attIndex])) { if (attIndex == InvalidAttrNumber) - elog(ERROR, "IndexSupportInitialize: no pg_index tuple"); + elog(ERROR, "IndexSupportInitialize: bogus pg_index tuple"); break; } @@ -637,6 +655,7 @@ IndexSupportInitialize(IndexStrategy indexStrategy, heap_close(relation, AccessShareLock); } + /* Now load the strategy information for the index operators */ ScanKeyEntryInitialize(&entry[0], 0, Anum_pg_amop_amopid, F_OIDEQ, @@ -644,7 +663,8 @@ IndexSupportInitialize(IndexStrategy indexStrategy, ScanKeyEntryInitialize(&entry[1], 0, Anum_pg_amop_amopclaid, - F_OIDEQ, 0); + F_OIDEQ, + 0); /* will fill below */ relation = heap_openr(AccessMethodOperatorRelationName, AccessShareLock); operatorRelation = heap_openr(OperatorRelationName, AccessShareLock); @@ -670,9 +690,11 @@ IndexSupportInitialize(IndexStrategy indexStrategy, Form_pg_amop aform; aform = (Form_pg_amop) GETSTRUCT(tuple); + strategy = aform->amopstrategy; + Assert(strategy > 0 && strategy <= maxStrategyNumber); OperatorRelationFillScanKeyEntry(operatorRelation, aform->amopopr, - StrategyMapGetScanKeyEntry(map, aform->amopstrategy)); + StrategyMapGetScanKeyEntry(map, strategy)); } heap_endscan(scan); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index b6420a10e9..9b98bd75e9 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/catalog/index.c,v 1.152 2001/05/30 20:52:32 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/catalog/index.c,v 1.153 2001/06/01 02:41:35 tgl Exp $ * * * INTERFACE ROUTINES @@ -722,6 +722,9 @@ UpdateIndexPredicate(Oid indexoid, Node *oldPred, Node *predicate) /* ---------------------------------------------------------------- * InitIndexStrategy + * + * XXX this is essentially the same as relcache.c's + * IndexedAccessMethodInitialize(), and probably ought to be merged with it. * ---------------------------------------------------------------- */ void @@ -733,18 +736,16 @@ InitIndexStrategy(int numatts, RegProcedure *support; uint16 amstrategies; uint16 amsupport; - Oid attrelid; Size strsize; /* * get information from the index relation descriptor */ - attrelid = indexRelation->rd_att->attrs[0]->attrelid; amstrategies = indexRelation->rd_am->amstrategies; amsupport = indexRelation->rd_am->amsupport; /* - * get the size of the strategy + * compute the size of the strategy array */ strsize = AttributeNumberGetIndexStrategySize(numatts, amstrategies); @@ -779,7 +780,8 @@ InitIndexStrategy(int numatts, IndexSupportInitialize(strategy, support, &indexRelation->rd_uniqueindex, - attrelid, accessMethodObjectId, + RelationGetRelid(indexRelation), + accessMethodObjectId, amstrategies, amsupport, numatts); /* diff --git a/src/backend/catalog/pg_operator.c b/src/backend/catalog/pg_operator.c index db54ceede2..40ee84c018 100644 --- a/src/backend/catalog/pg_operator.c +++ b/src/backend/catalog/pg_operator.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/catalog/pg_operator.c,v 1.58 2001/05/20 20:28:17 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/catalog/pg_operator.c,v 1.59 2001/06/01 02:41:35 tgl Exp $ * * NOTES * these routines moved here from commands/define.c and somewhat cleaned up. @@ -71,12 +71,13 @@ static void OperatorUpd(Oid baseId, Oid commId, Oid negId); * * performs a scan on pg_operator for an operator tuple * with given name and left/right type oids. - * ---------------------------------------------------------------- + * * pg_operator_desc -- reldesc for pg_operator * operatorName -- name of operator to fetch * leftObjectId -- left data type oid of operator to fetch * rightObjectId -- right data type oid of operator to fetch * defined -- set TRUE if defined (not a shell) + * ---------------------------------------------------------------- */ static Oid OperatorGetWithOpenRelation(Relation pg_operator_desc, @@ -88,26 +89,23 @@ OperatorGetWithOpenRelation(Relation pg_operator_desc, HeapScanDesc pg_operator_scan; Oid operatorObjectId; HeapTuple tup; - - static ScanKeyData opKey[3] = { - {0, Anum_pg_operator_oprname, F_NAMEEQ}, - {0, Anum_pg_operator_oprleft, F_OIDEQ}, - {0, Anum_pg_operator_oprright, F_OIDEQ}, - }; - - fmgr_info(F_NAMEEQ, &opKey[0].sk_func); - fmgr_info(F_OIDEQ, &opKey[1].sk_func); - fmgr_info(F_OIDEQ, &opKey[2].sk_func); - opKey[0].sk_nargs = opKey[0].sk_func.fn_nargs; - opKey[1].sk_nargs = opKey[1].sk_func.fn_nargs; - opKey[2].sk_nargs = opKey[2].sk_func.fn_nargs; + ScanKeyData opKey[3]; /* * form scan key */ - opKey[0].sk_argument = PointerGetDatum(operatorName); - opKey[1].sk_argument = ObjectIdGetDatum(leftObjectId); - opKey[2].sk_argument = ObjectIdGetDatum(rightObjectId); + ScanKeyEntryInitialize(&opKey[0], 0x0, + Anum_pg_operator_oprname, + F_NAMEEQ, + PointerGetDatum(operatorName)); + ScanKeyEntryInitialize(&opKey[1], 0x0, + Anum_pg_operator_oprleft, + F_OIDEQ, + ObjectIdGetDatum(leftObjectId)); + ScanKeyEntryInitialize(&opKey[2], 0x0, + Anum_pg_operator_oprright, + F_OIDEQ, + ObjectIdGetDatum(rightObjectId)); /* * begin the scan @@ -451,7 +449,6 @@ OperatorDef(char *operatorName, int i, j; Relation pg_operator_desc; - HeapScanDesc pg_operator_scan; HeapTuple tup; char nulls[Natts_pg_operator]; @@ -471,19 +468,7 @@ OperatorDef(char *operatorName, int nargs; NameData oname; TupleDesc tupDesc; - - static ScanKeyData opKey[3] = { - {0, Anum_pg_operator_oprname, F_NAMEEQ}, - {0, Anum_pg_operator_oprleft, F_OIDEQ}, - {0, Anum_pg_operator_oprright, F_OIDEQ}, - }; - - fmgr_info(F_NAMEEQ, &opKey[0].sk_func); - fmgr_info(F_OIDEQ, &opKey[1].sk_func); - fmgr_info(F_OIDEQ, &opKey[2].sk_func); - opKey[0].sk_nargs = opKey[0].sk_func.fn_nargs; - opKey[1].sk_nargs = opKey[1].sk_func.fn_nargs; - opKey[2].sk_nargs = opKey[2].sk_func.fn_nargs; + ScanKeyData opKey[3]; operatorObjectId = OperatorGet(operatorName, leftTypeName, @@ -753,13 +738,22 @@ OperatorDef(char *operatorName, */ if (operatorObjectId) { - opKey[0].sk_argument = PointerGetDatum(operatorName); - opKey[1].sk_argument = ObjectIdGetDatum(leftTypeId); - opKey[2].sk_argument = ObjectIdGetDatum(rightTypeId); - /* Make sure we can see the shell even if it is new in current cmd */ CommandCounterIncrement(); + ScanKeyEntryInitialize(&opKey[0], 0x0, + Anum_pg_operator_oprname, + F_NAMEEQ, + PointerGetDatum(operatorName)); + ScanKeyEntryInitialize(&opKey[1], 0x0, + Anum_pg_operator_oprleft, + F_OIDEQ, + ObjectIdGetDatum(leftTypeId)); + ScanKeyEntryInitialize(&opKey[2], 0x0, + Anum_pg_operator_oprright, + F_OIDEQ, + ObjectIdGetDatum(rightTypeId)); + pg_operator_scan = heap_beginscan(pg_operator_desc, 0, SnapshotSelf, /* no cache? */ @@ -789,7 +783,6 @@ OperatorDef(char *operatorName, heap_insert(pg_operator_desc, tup); operatorObjectId = tup->t_data->t_oid; - } if (RelationGetForm(pg_operator_desc)->relhasindex) @@ -841,17 +834,11 @@ OperatorUpd(Oid baseId, Oid commId, Oid negId) char nulls[Natts_pg_operator]; char replaces[Natts_pg_operator]; Datum values[Natts_pg_operator]; - - static ScanKeyData opKey[1] = { - {0, ObjectIdAttributeNumber, F_OIDEQ}, - }; - - fmgr_info(F_OIDEQ, &opKey[0].sk_func); - opKey[0].sk_nargs = opKey[0].sk_func.fn_nargs; + ScanKeyData opKey[1]; for (i = 0; i < Natts_pg_operator; ++i) { - values[i] = (Datum) NULL; + values[i] = (Datum) 0; replaces[i] = ' '; nulls[i] = ' '; } @@ -865,7 +852,10 @@ OperatorUpd(Oid baseId, Oid commId, Oid negId) */ CommandCounterIncrement(); - opKey[0].sk_argument = ObjectIdGetDatum(commId); + ScanKeyEntryInitialize(&opKey[0], 0x0, + ObjectIdAttributeNumber, + F_OIDEQ, + ObjectIdGetDatum(commId)); pg_operator_scan = heap_beginscan(pg_operator_desc, 0, @@ -993,7 +983,6 @@ OperatorUpd(Oid baseId, Oid commId, Oid negId) heap_endscan(pg_operator_scan); - heap_close(pg_operator_desc, RowExclusiveLock); } diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c index fbbade1033..a19f1a303b 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.137 2001/05/27 09:59:29 petere Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.138 2001/06/01 02:41:35 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -636,6 +636,7 @@ CopyFrom(Relation rel, bool binary, bool oids, FILE *fp, resultRelInfo = makeNode(ResultRelInfo); resultRelInfo->ri_RangeTableIndex = 1; /* dummy */ resultRelInfo->ri_RelationDesc = rel; + resultRelInfo->ri_TrigDesc = rel->trigdesc; ExecOpenIndices(resultRelInfo); @@ -868,12 +869,12 @@ CopyFrom(Relation rel, bool binary, bool oids, FILE *fp, skip_tuple = false; /* BEFORE ROW INSERT Triggers */ - if (rel->trigdesc && - rel->trigdesc->n_before_row[TRIGGER_EVENT_INSERT] > 0) + if (resultRelInfo->ri_TrigDesc && + resultRelInfo->ri_TrigDesc->n_before_row[TRIGGER_EVENT_INSERT] > 0) { HeapTuple newtuple; - newtuple = ExecBRInsertTriggers(estate, rel, tuple); + newtuple = ExecBRInsertTriggers(estate, resultRelInfo, tuple); if (newtuple == NULL) /* "do nothing" */ skip_tuple = true; @@ -903,8 +904,8 @@ CopyFrom(Relation rel, bool binary, bool oids, FILE *fp, ExecInsertIndexTuples(slot, &(tuple->t_self), estate, false); /* AFTER ROW INSERT Triggers */ - if (rel->trigdesc) - ExecARInsertTriggers(estate, rel, tuple); + if (resultRelInfo->ri_TrigDesc) + ExecARInsertTriggers(estate, resultRelInfo, tuple); } for (i = 0; i < attr_count; i++) diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c index 70c146530f..013221860a 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.91 2001/05/27 09:59:29 petere Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/trigger.c,v 1.92 2001/06/01 02:41:35 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -32,14 +32,19 @@ #include "utils/syscache.h" -static void DescribeTrigger(TriggerDesc *trigdesc, Trigger *trigger); -static HeapTuple GetTupleForTrigger(EState *estate, ItemPointer tid, - TupleTableSlot **newSlot); -static HeapTuple ExecCallTriggerFunc(Trigger *trigger, - TriggerData *trigdata, - MemoryContext per_tuple_context); -static void DeferredTriggerSaveEvent(Relation rel, int event, - HeapTuple oldtup, HeapTuple newtup); +static void InsertTrigger(TriggerDesc *trigdesc, Trigger *trigger, int indx); +static HeapTuple GetTupleForTrigger(EState *estate, + ResultRelInfo *relinfo, + ItemPointer tid, + TupleTableSlot **newSlot); +static HeapTuple ExecCallTriggerFunc(TriggerData *trigdata, + FmgrInfo *finfo, + MemoryContext per_tuple_context); +static void DeferredTriggerSaveEvent(ResultRelInfo *relinfo, int event, + HeapTuple oldtup, HeapTuple newtup); +static void DeferredTriggerExecute(DeferredTriggerEvent event, int itemno, + Relation rel, FmgrInfo *finfo, + MemoryContext per_tuple_context); void @@ -577,8 +582,6 @@ RelationBuildTriggers(Relation relation) DatumGetCString(DirectFunctionCall1(nameout, NameGetDatum(&pg_trigger->tgname)))); build->tgfoid = pg_trigger->tgfoid; - build->tgfunc.fn_oid = InvalidOid; /* mark FmgrInfo as - * uninitialized */ build->tgtype = pg_trigger->tgtype; build->tgenabled = pg_trigger->tgenabled; build->tgisconstraint = pg_trigger->tgisconstraint; @@ -641,21 +644,22 @@ RelationBuildTriggers(Relation relation) trigdesc->triggers = triggers; trigdesc->numtriggers = ntrigs; for (found = 0; found < ntrigs; found++) - DescribeTrigger(trigdesc, &(triggers[found])); + InsertTrigger(trigdesc, &(triggers[found]), found); relation->trigdesc = trigdesc; } +/* Insert the given trigger into the appropriate index list(s) for it */ static void -DescribeTrigger(TriggerDesc *trigdesc, Trigger *trigger) +InsertTrigger(TriggerDesc *trigdesc, Trigger *trigger, int indx) { uint16 *n; - Trigger ***t, - ***tp; + int **t, + **tp; - if (TRIGGER_FOR_ROW(trigger->tgtype)) /* Is ROW/STATEMENT - * trigger */ + if (TRIGGER_FOR_ROW(trigger->tgtype)) { + /* ROW trigger */ if (TRIGGER_FOR_BEFORE(trigger->tgtype)) { n = trigdesc->n_before_row; @@ -668,8 +672,8 @@ DescribeTrigger(TriggerDesc *trigdesc, Trigger *trigger) } } else -/* STATEMENT (NI) */ { + /* STATEMENT trigger */ if (TRIGGER_FOR_BEFORE(trigger->tgtype)) { n = trigdesc->n_before_statement; @@ -686,12 +690,12 @@ DescribeTrigger(TriggerDesc *trigdesc, Trigger *trigger) { tp = &(t[TRIGGER_EVENT_INSERT]); if (*tp == NULL) - *tp = (Trigger **) MemoryContextAlloc(CacheMemoryContext, - sizeof(Trigger *)); + *tp = (int *) MemoryContextAlloc(CacheMemoryContext, + sizeof(int)); else - *tp = (Trigger **) repalloc(*tp, (n[TRIGGER_EVENT_INSERT] + 1) * - sizeof(Trigger *)); - (*tp)[n[TRIGGER_EVENT_INSERT]] = trigger; + *tp = (int *) repalloc(*tp, (n[TRIGGER_EVENT_INSERT] + 1) * + sizeof(int)); + (*tp)[n[TRIGGER_EVENT_INSERT]] = indx; (n[TRIGGER_EVENT_INSERT])++; } @@ -699,12 +703,12 @@ DescribeTrigger(TriggerDesc *trigdesc, Trigger *trigger) { tp = &(t[TRIGGER_EVENT_DELETE]); if (*tp == NULL) - *tp = (Trigger **) MemoryContextAlloc(CacheMemoryContext, - sizeof(Trigger *)); + *tp = (int *) MemoryContextAlloc(CacheMemoryContext, + sizeof(int)); else - *tp = (Trigger **) repalloc(*tp, (n[TRIGGER_EVENT_DELETE] + 1) * - sizeof(Trigger *)); - (*tp)[n[TRIGGER_EVENT_DELETE]] = trigger; + *tp = (int *) repalloc(*tp, (n[TRIGGER_EVENT_DELETE] + 1) * + sizeof(int)); + (*tp)[n[TRIGGER_EVENT_DELETE]] = indx; (n[TRIGGER_EVENT_DELETE])++; } @@ -712,21 +716,20 @@ DescribeTrigger(TriggerDesc *trigdesc, Trigger *trigger) { tp = &(t[TRIGGER_EVENT_UPDATE]); if (*tp == NULL) - *tp = (Trigger **) MemoryContextAlloc(CacheMemoryContext, - sizeof(Trigger *)); + *tp = (int *) MemoryContextAlloc(CacheMemoryContext, + sizeof(int)); else - *tp = (Trigger **) repalloc(*tp, (n[TRIGGER_EVENT_UPDATE] + 1) * - sizeof(Trigger *)); - (*tp)[n[TRIGGER_EVENT_UPDATE]] = trigger; + *tp = (int *) repalloc(*tp, (n[TRIGGER_EVENT_UPDATE] + 1) * + sizeof(int)); + (*tp)[n[TRIGGER_EVENT_UPDATE]] = indx; (n[TRIGGER_EVENT_UPDATE])++; } - } void FreeTriggerDesc(TriggerDesc *trigdesc) { - Trigger ***t; + int **t; Trigger *trigger; int i; @@ -734,19 +737,19 @@ FreeTriggerDesc(TriggerDesc *trigdesc) return; t = trigdesc->tg_before_statement; - for (i = 0; i < 4; i++) + for (i = 0; i < TRIGGER_NUM_EVENT_CLASSES; i++) if (t[i] != NULL) pfree(t[i]); t = trigdesc->tg_before_row; - for (i = 0; i < 4; i++) + for (i = 0; i < TRIGGER_NUM_EVENT_CLASSES; i++) if (t[i] != NULL) pfree(t[i]); t = trigdesc->tg_after_row; - for (i = 0; i < 4; i++) + for (i = 0; i < TRIGGER_NUM_EVENT_CLASSES; i++) if (t[i] != NULL) pfree(t[i]); t = trigdesc->tg_after_statement; - for (i = 0; i < 4; i++) + for (i = 0; i < TRIGGER_NUM_EVENT_CLASSES; i++) if (t[i] != NULL) pfree(t[i]); @@ -806,7 +809,6 @@ equalTriggerDescs(TriggerDesc *trigdesc1, TriggerDesc *trigdesc2) return false; if (trig1->tgfoid != trig2->tgfoid) return false; - /* need not examine tgfunc, if tgfoid matches */ if (trig1->tgtype != trig2->tgtype) return false; if (trig1->tgenabled != trig2->tgenabled) @@ -832,9 +834,18 @@ equalTriggerDescs(TriggerDesc *trigdesc1, TriggerDesc *trigdesc2) return true; } +/* + * Call a trigger function. + * + * trigdata: trigger descriptor. + * finfo: possibly-cached call info for the function. + * per_tuple_context: memory context to execute the function in. + * + * Returns the tuple (or NULL) as returned by the function. + */ static HeapTuple -ExecCallTriggerFunc(Trigger *trigger, - TriggerData *trigdata, +ExecCallTriggerFunc(TriggerData *trigdata, + FmgrInfo *finfo, MemoryContext per_tuple_context) { FunctionCallInfoData fcinfo; @@ -842,11 +853,13 @@ ExecCallTriggerFunc(Trigger *trigger, MemoryContext oldContext; /* - * Fmgr lookup info is cached in the Trigger structure, so that we - * need not repeat the lookup on every call. + * We cache fmgr lookup info, to avoid making the lookup + * again on each call. */ - if (trigger->tgfunc.fn_oid == InvalidOid) - fmgr_info(trigger->tgfoid, &trigger->tgfunc); + if (finfo->fn_oid == InvalidOid) + fmgr_info(trigdata->tg_trigger->tgfoid, finfo); + + Assert(finfo->fn_oid == trigdata->tg_trigger->tgfoid); /* * Do the function evaluation in the per-tuple memory context, so that @@ -861,7 +874,7 @@ ExecCallTriggerFunc(Trigger *trigger, */ MemSet(&fcinfo, 0, sizeof(fcinfo)); - fcinfo.flinfo = &trigger->tgfunc; + fcinfo.flinfo = finfo; fcinfo.context = (Node *) trigdata; result = FunctionCallInvoke(&fcinfo); @@ -880,26 +893,40 @@ ExecCallTriggerFunc(Trigger *trigger, } HeapTuple -ExecBRInsertTriggers(EState *estate, Relation rel, HeapTuple trigtuple) +ExecBRInsertTriggers(EState *estate, ResultRelInfo *relinfo, + HeapTuple trigtuple) { - int ntrigs = rel->trigdesc->n_before_row[TRIGGER_EVENT_INSERT]; - Trigger **trigger = rel->trigdesc->tg_before_row[TRIGGER_EVENT_INSERT]; + TriggerDesc *trigdesc = relinfo->ri_TrigDesc; + int ntrigs = trigdesc->n_before_row[TRIGGER_EVENT_INSERT]; + int *tgindx = trigdesc->tg_before_row[TRIGGER_EVENT_INSERT]; HeapTuple newtuple = trigtuple; HeapTuple oldtuple; TriggerData LocTriggerData; int i; + /* Allocate cache space for fmgr lookup info, if not done yet */ + if (relinfo->ri_TrigFunctions == NULL) + { + relinfo->ri_TrigFunctions = (FmgrInfo *) + palloc(trigdesc->numtriggers * sizeof(FmgrInfo)); + MemSet(relinfo->ri_TrigFunctions, 0, + trigdesc->numtriggers * sizeof(FmgrInfo)); + } + LocTriggerData.type = T_TriggerData; LocTriggerData.tg_event = TRIGGER_EVENT_INSERT | TRIGGER_EVENT_ROW | TRIGGER_EVENT_BEFORE; - LocTriggerData.tg_relation = rel; + LocTriggerData.tg_relation = relinfo->ri_RelationDesc; LocTriggerData.tg_newtuple = NULL; for (i = 0; i < ntrigs; i++) { - if (!trigger[i]->tgenabled) + Trigger *trigger = &trigdesc->triggers[tgindx[i]]; + + if (!trigger->tgenabled) continue; LocTriggerData.tg_trigtuple = oldtuple = newtuple; - LocTriggerData.tg_trigger = trigger[i]; - newtuple = ExecCallTriggerFunc(trigger[i], &LocTriggerData, + LocTriggerData.tg_trigger = trigger; + newtuple = ExecCallTriggerFunc(&LocTriggerData, + relinfo->ri_TrigFunctions + tgindx[i], GetPerTupleMemoryContext(estate)); if (oldtuple != newtuple && oldtuple != trigtuple) heap_freetuple(oldtuple); @@ -910,42 +937,59 @@ ExecBRInsertTriggers(EState *estate, Relation rel, HeapTuple trigtuple) } void -ExecARInsertTriggers(EState *estate, Relation rel, HeapTuple trigtuple) +ExecARInsertTriggers(EState *estate, ResultRelInfo *relinfo, + HeapTuple trigtuple) { + TriggerDesc *trigdesc = relinfo->ri_TrigDesc; + /* Must save info if there are any deferred triggers on this rel */ - 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) - DeferredTriggerSaveEvent(rel, TRIGGER_EVENT_INSERT, NULL, trigtuple); + if (trigdesc->n_after_row[TRIGGER_EVENT_INSERT] > 0 || + trigdesc->n_after_row[TRIGGER_EVENT_UPDATE] > 0 || + trigdesc->n_after_row[TRIGGER_EVENT_DELETE] > 0) + DeferredTriggerSaveEvent(relinfo, TRIGGER_EVENT_INSERT, + NULL, trigtuple); } bool -ExecBRDeleteTriggers(EState *estate, ItemPointer tupleid) +ExecBRDeleteTriggers(EState *estate, ResultRelInfo *relinfo, + ItemPointer tupleid) { - Relation rel = estate->es_result_relation_info->ri_RelationDesc; - int ntrigs = rel->trigdesc->n_before_row[TRIGGER_EVENT_DELETE]; - Trigger **trigger = rel->trigdesc->tg_before_row[TRIGGER_EVENT_DELETE]; + TriggerDesc *trigdesc = relinfo->ri_TrigDesc; + int ntrigs = trigdesc->n_before_row[TRIGGER_EVENT_DELETE]; + int *tgindx = trigdesc->tg_before_row[TRIGGER_EVENT_DELETE]; TriggerData LocTriggerData; HeapTuple trigtuple; HeapTuple newtuple = NULL; TupleTableSlot *newSlot; int i; - trigtuple = GetTupleForTrigger(estate, tupleid, &newSlot); + trigtuple = GetTupleForTrigger(estate, relinfo, tupleid, &newSlot); if (trigtuple == NULL) return false; + /* Allocate cache space for fmgr lookup info, if not done yet */ + if (relinfo->ri_TrigFunctions == NULL) + { + relinfo->ri_TrigFunctions = (FmgrInfo *) + palloc(trigdesc->numtriggers * sizeof(FmgrInfo)); + MemSet(relinfo->ri_TrigFunctions, 0, + trigdesc->numtriggers * sizeof(FmgrInfo)); + } + LocTriggerData.type = T_TriggerData; LocTriggerData.tg_event = TRIGGER_EVENT_DELETE | TRIGGER_EVENT_ROW | TRIGGER_EVENT_BEFORE; - LocTriggerData.tg_relation = rel; + LocTriggerData.tg_relation = relinfo->ri_RelationDesc; LocTriggerData.tg_newtuple = NULL; for (i = 0; i < ntrigs; i++) { - if (!trigger[i]->tgenabled) + Trigger *trigger = &trigdesc->triggers[tgindx[i]]; + + if (!trigger->tgenabled) continue; LocTriggerData.tg_trigtuple = trigtuple; - LocTriggerData.tg_trigger = trigger[i]; - newtuple = ExecCallTriggerFunc(trigger[i], &LocTriggerData, + LocTriggerData.tg_trigger = trigger; + newtuple = ExecCallTriggerFunc(&LocTriggerData, + relinfo->ri_TrigFunctions + tgindx[i], GetPerTupleMemoryContext(estate)); if (newtuple == NULL) break; @@ -958,27 +1002,31 @@ ExecBRDeleteTriggers(EState *estate, ItemPointer tupleid) } void -ExecARDeleteTriggers(EState *estate, ItemPointer tupleid) +ExecARDeleteTriggers(EState *estate, ResultRelInfo *relinfo, + ItemPointer tupleid) { - Relation rel = estate->es_result_relation_info->ri_RelationDesc; + TriggerDesc *trigdesc = relinfo->ri_TrigDesc; /* Must save info if there are upd/del deferred triggers on this rel */ - if (rel->trigdesc->n_after_row[TRIGGER_EVENT_UPDATE] > 0 || - rel->trigdesc->n_after_row[TRIGGER_EVENT_DELETE] > 0) + if (trigdesc->n_after_row[TRIGGER_EVENT_UPDATE] > 0 || + trigdesc->n_after_row[TRIGGER_EVENT_DELETE] > 0) { - HeapTuple trigtuple = GetTupleForTrigger(estate, tupleid, NULL); + HeapTuple trigtuple = GetTupleForTrigger(estate, relinfo, + tupleid, NULL); - DeferredTriggerSaveEvent(rel, TRIGGER_EVENT_DELETE, trigtuple, NULL); + DeferredTriggerSaveEvent(relinfo, TRIGGER_EVENT_DELETE, + trigtuple, NULL); heap_freetuple(trigtuple); } } HeapTuple -ExecBRUpdateTriggers(EState *estate, ItemPointer tupleid, HeapTuple newtuple) +ExecBRUpdateTriggers(EState *estate, ResultRelInfo *relinfo, + ItemPointer tupleid, HeapTuple newtuple) { - Relation rel = estate->es_result_relation_info->ri_RelationDesc; - int ntrigs = rel->trigdesc->n_before_row[TRIGGER_EVENT_UPDATE]; - Trigger **trigger = rel->trigdesc->tg_before_row[TRIGGER_EVENT_UPDATE]; + TriggerDesc *trigdesc = relinfo->ri_TrigDesc; + int ntrigs = trigdesc->n_before_row[TRIGGER_EVENT_UPDATE]; + int *tgindx = trigdesc->tg_before_row[TRIGGER_EVENT_UPDATE]; TriggerData LocTriggerData; HeapTuple trigtuple; HeapTuple oldtuple; @@ -986,7 +1034,7 @@ ExecBRUpdateTriggers(EState *estate, ItemPointer tupleid, HeapTuple newtuple) TupleTableSlot *newSlot; int i; - trigtuple = GetTupleForTrigger(estate, tupleid, &newSlot); + trigtuple = GetTupleForTrigger(estate, relinfo, tupleid, &newSlot); if (trigtuple == NULL) return NULL; @@ -997,17 +1045,29 @@ ExecBRUpdateTriggers(EState *estate, ItemPointer tupleid, HeapTuple newtuple) if (newSlot != NULL) intuple = newtuple = ExecRemoveJunk(estate->es_junkFilter, newSlot); + /* Allocate cache space for fmgr lookup info, if not done yet */ + if (relinfo->ri_TrigFunctions == NULL) + { + relinfo->ri_TrigFunctions = (FmgrInfo *) + palloc(trigdesc->numtriggers * sizeof(FmgrInfo)); + MemSet(relinfo->ri_TrigFunctions, 0, + trigdesc->numtriggers * sizeof(FmgrInfo)); + } + LocTriggerData.type = T_TriggerData; LocTriggerData.tg_event = TRIGGER_EVENT_UPDATE | TRIGGER_EVENT_ROW | TRIGGER_EVENT_BEFORE; - LocTriggerData.tg_relation = rel; + LocTriggerData.tg_relation = relinfo->ri_RelationDesc; for (i = 0; i < ntrigs; i++) { - if (!trigger[i]->tgenabled) + Trigger *trigger = &trigdesc->triggers[tgindx[i]]; + + if (!trigger->tgenabled) continue; LocTriggerData.tg_trigtuple = trigtuple; LocTriggerData.tg_newtuple = oldtuple = newtuple; - LocTriggerData.tg_trigger = trigger[i]; - newtuple = ExecCallTriggerFunc(trigger[i], &LocTriggerData, + LocTriggerData.tg_trigger = trigger; + newtuple = ExecCallTriggerFunc(&LocTriggerData, + relinfo->ri_TrigFunctions + tgindx[i], GetPerTupleMemoryContext(estate)); if (oldtuple != newtuple && oldtuple != intuple) heap_freetuple(oldtuple); @@ -1019,26 +1079,30 @@ ExecBRUpdateTriggers(EState *estate, ItemPointer tupleid, HeapTuple newtuple) } void -ExecARUpdateTriggers(EState *estate, ItemPointer tupleid, HeapTuple newtuple) +ExecARUpdateTriggers(EState *estate, ResultRelInfo *relinfo, + ItemPointer tupleid, HeapTuple newtuple) { - Relation rel = estate->es_result_relation_info->ri_RelationDesc; + TriggerDesc *trigdesc = relinfo->ri_TrigDesc; /* Must save info if there are upd/del deferred triggers on this rel */ - if (rel->trigdesc->n_after_row[TRIGGER_EVENT_UPDATE] > 0 || - rel->trigdesc->n_after_row[TRIGGER_EVENT_DELETE] > 0) + if (trigdesc->n_after_row[TRIGGER_EVENT_UPDATE] > 0 || + trigdesc->n_after_row[TRIGGER_EVENT_DELETE] > 0) { - HeapTuple trigtuple = GetTupleForTrigger(estate, tupleid, NULL); + HeapTuple trigtuple = GetTupleForTrigger(estate, relinfo, + tupleid, NULL); - DeferredTriggerSaveEvent(rel, TRIGGER_EVENT_UPDATE, trigtuple, newtuple); + DeferredTriggerSaveEvent(relinfo, TRIGGER_EVENT_UPDATE, + trigtuple, newtuple); heap_freetuple(trigtuple); } } static HeapTuple -GetTupleForTrigger(EState *estate, ItemPointer tid, TupleTableSlot **newSlot) +GetTupleForTrigger(EState *estate, ResultRelInfo *relinfo, + ItemPointer tid, TupleTableSlot **newSlot) { - Relation relation = estate->es_result_relation_info->ri_RelationDesc; + Relation relation = relinfo->ri_RelationDesc; HeapTupleData tuple; HeapTuple result; Buffer buffer; @@ -1070,8 +1134,8 @@ ltrmark:; else if (!(ItemPointerEquals(&(tuple.t_self), tid))) { TupleTableSlot *epqslot = EvalPlanQual(estate, - estate->es_result_relation_info->ri_RangeTableIndex, - &(tuple.t_self)); + relinfo->ri_RangeTableIndex, + &(tuple.t_self)); if (!(TupIsNull(epqslot))) { @@ -1293,35 +1357,46 @@ deferredTriggerGetPreviousEvent(Oid relid, ItemPointer ctid) /* ---------- - * deferredTriggerExecute() + * DeferredTriggerExecute() * * Fetch the required tuples back from the heap and fire one * single trigger function. + * + * Frequently, this will be fired many times in a row for triggers of + * a single relation. Therefore, we cache the open relation and provide + * fmgr lookup cache space at the caller level. + * + * 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). + * per_tuple_context: memory context to call trigger function in. * ---------- */ static void -deferredTriggerExecute(DeferredTriggerEvent event, int itemno, +DeferredTriggerExecute(DeferredTriggerEvent event, int itemno, + Relation rel, FmgrInfo *finfo, MemoryContext per_tuple_context) { - Relation rel; + Oid tgoid = event->dte_item[itemno].dti_tgoid; + TriggerDesc *trigdesc = rel->trigdesc; TriggerData LocTriggerData; HeapTupleData oldtuple; HeapTupleData newtuple; HeapTuple rettuple; Buffer oldbuffer; Buffer newbuffer; + int tgindx; /* - * Open the heap and fetch the required OLD and NEW tuples. + * Fetch the required OLD and NEW tuples. */ - rel = heap_open(event->dte_relid, NoLock); - if (ItemPointerIsValid(&(event->dte_oldctid))) { 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))) @@ -1329,7 +1404,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"); } /* @@ -1340,27 +1415,33 @@ deferredTriggerExecute(DeferredTriggerEvent event, int itemno, TRIGGER_EVENT_ROW; LocTriggerData.tg_relation = rel; + LocTriggerData.tg_trigger = NULL; + for (tgindx = 0; tgindx < trigdesc->numtriggers; tgindx++) + { + if (trigdesc->triggers[tgindx].tgoid == tgoid) + { + LocTriggerData.tg_trigger = &(trigdesc->triggers[tgindx]); + break; + } + } + if (LocTriggerData.tg_trigger == NULL) + elog(ERROR, "DeferredTriggerExecute: can't find trigger %u", tgoid); + switch (event->dte_event & TRIGGER_EVENT_OPMASK) { case TRIGGER_EVENT_INSERT: LocTriggerData.tg_trigtuple = &newtuple; LocTriggerData.tg_newtuple = NULL; - LocTriggerData.tg_trigger = - rel->trigdesc->tg_after_row[TRIGGER_EVENT_INSERT][itemno]; break; case TRIGGER_EVENT_UPDATE: LocTriggerData.tg_trigtuple = &oldtuple; LocTriggerData.tg_newtuple = &newtuple; - LocTriggerData.tg_trigger = - rel->trigdesc->tg_after_row[TRIGGER_EVENT_UPDATE][itemno]; break; case TRIGGER_EVENT_DELETE: LocTriggerData.tg_trigtuple = &oldtuple; LocTriggerData.tg_newtuple = NULL; - LocTriggerData.tg_trigger = - rel->trigdesc->tg_after_row[TRIGGER_EVENT_DELETE][itemno]; break; } @@ -1368,8 +1449,8 @@ deferredTriggerExecute(DeferredTriggerEvent event, int itemno, * Call the trigger and throw away an eventually returned updated * tuple. */ - rettuple = ExecCallTriggerFunc(LocTriggerData.tg_trigger, - &LocTriggerData, + rettuple = ExecCallTriggerFunc(&LocTriggerData, + finfo + tgindx, per_tuple_context); if (rettuple != NULL && rettuple != &oldtuple && rettuple != &newtuple) heap_freetuple(rettuple); @@ -1381,14 +1462,12 @@ deferredTriggerExecute(DeferredTriggerEvent event, int itemno, ReferentialIntegritySnapshotOverride = false; /* - * Release buffers and close the relation + * Release buffers */ if (ItemPointerIsValid(&(event->dte_oldctid))) ReleaseBuffer(oldbuffer); if (ItemPointerIsValid(&(event->dte_newctid))) ReleaseBuffer(newbuffer); - - heap_close(rel, NoLock); } @@ -1403,9 +1482,9 @@ static void deferredTriggerInvokeEvents(bool immediate_only) { DeferredTriggerEvent event; - int still_deferred_ones; - int i; MemoryContext per_tuple_context; + Relation rel = NULL; + FmgrInfo *finfo = NULL; /* * For now we process all events - to speedup transaction blocks we @@ -1426,6 +1505,8 @@ deferredTriggerInvokeEvents(bool immediate_only) for (event = deftrig_events; event != NULL; event = event->dte_next) { + bool still_deferred_ones; + int i; /* * Check if event is completely done. @@ -1458,9 +1539,32 @@ deferredTriggerInvokeEvents(bool immediate_only) } /* - * So let's fire it... + * So let's fire it... but first, open the correct relation + * if this is not the same relation as before. */ - deferredTriggerExecute(event, i, per_tuple_context); + if (rel == NULL || rel->rd_id != event->dte_relid) + { + if (rel) + heap_close(rel, NoLock); + if (finfo) + pfree(finfo); + /* + * We assume that an appropriate lock is still held by the + * executor, so grab no new lock here. + */ + rel = heap_open(event->dte_relid, NoLock); + /* + * Allocate space to cache fmgr lookup info for triggers + * of this relation. + */ + finfo = (FmgrInfo *) + palloc(rel->trigdesc->numtriggers * sizeof(FmgrInfo)); + MemSet(finfo, 0, + rel->trigdesc->numtriggers * sizeof(FmgrInfo)); + } + + DeferredTriggerExecute(event, i, rel, finfo, per_tuple_context); + event->dte_item[i].dti_state |= TRIGGER_DEFERRED_DONE; } @@ -1471,6 +1575,10 @@ deferredTriggerInvokeEvents(bool immediate_only) event->dte_event |= TRIGGER_DEFERRED_DONE; } + if (rel) + heap_close(rel, NoLock); + if (finfo) + pfree(finfo); MemoryContextDelete(per_tuple_context); } @@ -1892,16 +2000,18 @@ DeferredTriggerSetState(ConstraintsSetStmt *stmt) * ---------- */ static void -DeferredTriggerSaveEvent(Relation rel, int event, +DeferredTriggerSaveEvent(ResultRelInfo *relinfo, int event, HeapTuple oldtup, HeapTuple newtup) { + Relation rel = relinfo->ri_RelationDesc; + TriggerDesc *trigdesc = relinfo->ri_TrigDesc; MemoryContext oldcxt; DeferredTriggerEvent new_event; DeferredTriggerEvent prev_event; int new_size; int i; int ntriggers; - Trigger **triggers; + int *tgindx; ItemPointerData oldctid; ItemPointerData newctid; TriggerData LocTriggerData; @@ -1927,8 +2037,8 @@ DeferredTriggerSaveEvent(Relation rel, int event, */ oldcxt = MemoryContextSwitchTo(deftrig_cxt); - ntriggers = rel->trigdesc->n_after_row[event]; - triggers = rel->trigdesc->tg_after_row[event]; + ntriggers = trigdesc->n_after_row[event]; + tgindx = trigdesc->tg_after_row[event]; new_size = offsetof(DeferredTriggerEventData, dte_item[0]) + ntriggers * sizeof(DeferredTriggerEventItem); @@ -1941,13 +2051,15 @@ DeferredTriggerSaveEvent(Relation rel, int event, new_event->dte_n_items = ntriggers; for (i = 0; i < ntriggers; i++) { - new_event->dte_item[i].dti_tgoid = triggers[i]->tgoid; + Trigger *trigger = &trigdesc->triggers[tgindx[i]]; + + new_event->dte_item[i].dti_tgoid = trigger->tgoid; new_event->dte_item[i].dti_state = - ((triggers[i]->tgdeferrable) ? + ((trigger->tgdeferrable) ? TRIGGER_DEFERRED_DEFERRABLE : 0) | - ((triggers[i]->tginitdeferred) ? + ((trigger->tginitdeferred) ? TRIGGER_DEFERRED_INITDEFERRED : 0) | - ((rel->trigdesc->n_before_row[event] > 0) ? + ((trigdesc->n_before_row[event] > 0) ? TRIGGER_DEFERRED_HAS_BEFORE : 0); } @@ -1978,13 +2090,14 @@ DeferredTriggerSaveEvent(Relation rel, int event, */ for (i = 0; i < ntriggers; i++) { + Trigger *trigger = &trigdesc->triggers[tgindx[i]]; bool is_ri_trigger; bool key_unchanged; /* * We are interested in RI_FKEY triggers only. */ - switch (triggers[i]->tgfoid) + switch (trigger->tgfoid) { case F_RI_FKEY_NOACTION_UPD: case F_RI_FKEY_CASCADE_UPD: @@ -2006,7 +2119,7 @@ DeferredTriggerSaveEvent(Relation rel, int event, LocTriggerData.tg_relation = rel; LocTriggerData.tg_trigtuple = oldtup; LocTriggerData.tg_newtuple = newtup; - LocTriggerData.tg_trigger = triggers[i]; + LocTriggerData.tg_trigger = trigger; key_unchanged = RI_FKey_keyequal_upd(&LocTriggerData); diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index 3ba9b9136f..38c34f03be 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.142 2001/05/27 20:48:51 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.143 2001/06/01 02:41:35 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -830,6 +830,8 @@ initResultRelInfo(ResultRelInfo *resultRelInfo, resultRelInfo->ri_NumIndices = 0; resultRelInfo->ri_IndexRelationDescs = NULL; resultRelInfo->ri_IndexRelationInfo = NULL; + resultRelInfo->ri_TrigDesc = resultRelationDesc->trigdesc; + resultRelInfo->ri_TrigFunctions = NULL; resultRelInfo->ri_ConstraintExprs = NULL; resultRelInfo->ri_junkFilter = NULL; @@ -1232,12 +1234,12 @@ ExecAppend(TupleTableSlot *slot, resultRelationDesc = resultRelInfo->ri_RelationDesc; /* BEFORE ROW INSERT Triggers */ - if (resultRelationDesc->trigdesc && - resultRelationDesc->trigdesc->n_before_row[TRIGGER_EVENT_INSERT] > 0) + if (resultRelInfo->ri_TrigDesc && + resultRelInfo->ri_TrigDesc->n_before_row[TRIGGER_EVENT_INSERT] > 0) { HeapTuple newtuple; - newtuple = ExecBRInsertTriggers(estate, resultRelationDesc, tuple); + newtuple = ExecBRInsertTriggers(estate, resultRelInfo, tuple); if (newtuple == NULL) /* "do nothing" */ return; @@ -1283,8 +1285,8 @@ ExecAppend(TupleTableSlot *slot, ExecInsertIndexTuples(slot, &(tuple->t_self), estate, false); /* AFTER ROW INSERT Triggers */ - if (resultRelationDesc->trigdesc) - ExecARInsertTriggers(estate, resultRelationDesc, tuple); + if (resultRelInfo->ri_TrigDesc) + ExecARInsertTriggers(estate, resultRelInfo, tuple); } /* ---------------------------------------------------------------- @@ -1311,12 +1313,12 @@ ExecDelete(TupleTableSlot *slot, resultRelationDesc = resultRelInfo->ri_RelationDesc; /* BEFORE ROW DELETE Triggers */ - if (resultRelationDesc->trigdesc && - resultRelationDesc->trigdesc->n_before_row[TRIGGER_EVENT_DELETE] > 0) + if (resultRelInfo->ri_TrigDesc && + resultRelInfo->ri_TrigDesc->n_before_row[TRIGGER_EVENT_DELETE] > 0) { bool dodelete; - dodelete = ExecBRDeleteTriggers(estate, tupleid); + dodelete = ExecBRDeleteTriggers(estate, resultRelInfo, tupleid); if (!dodelete) /* "do nothing" */ return; @@ -1370,8 +1372,8 @@ ldelete:; */ /* AFTER ROW DELETE Triggers */ - if (resultRelationDesc->trigdesc) - ExecARDeleteTriggers(estate, tupleid); + if (resultRelInfo->ri_TrigDesc) + ExecARDeleteTriggers(estate, resultRelInfo, tupleid); } /* ---------------------------------------------------------------- @@ -1418,12 +1420,13 @@ ExecReplace(TupleTableSlot *slot, resultRelationDesc = resultRelInfo->ri_RelationDesc; /* BEFORE ROW UPDATE Triggers */ - if (resultRelationDesc->trigdesc && - resultRelationDesc->trigdesc->n_before_row[TRIGGER_EVENT_UPDATE] > 0) + if (resultRelInfo->ri_TrigDesc && + resultRelInfo->ri_TrigDesc->n_before_row[TRIGGER_EVENT_UPDATE] > 0) { HeapTuple newtuple; - newtuple = ExecBRUpdateTriggers(estate, tupleid, tuple); + newtuple = ExecBRUpdateTriggers(estate, resultRelInfo, + tupleid, tuple); if (newtuple == NULL) /* "do nothing" */ return; @@ -1519,8 +1522,8 @@ lreplace:; ExecInsertIndexTuples(slot, &(tuple->t_self), estate, true); /* AFTER ROW UPDATE Triggers */ - if (resultRelationDesc->trigdesc) - ExecARUpdateTriggers(estate, tupleid, tuple); + if (resultRelInfo->ri_TrigDesc) + ExecARUpdateTriggers(estate, resultRelInfo, tupleid, tuple); } static char * diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c index f843f2bb16..cd54ff7665 100644 --- a/src/backend/utils/cache/catcache.c +++ b/src/backend/utils/cache/catcache.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/cache/catcache.c,v 1.77 2001/03/22 03:59:55 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/cache/catcache.c,v 1.78 2001/06/01 02:41:36 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -205,7 +205,6 @@ CatalogCacheInitializeCache(CatCache *cache) /* * switch to the cache context so our allocations do not vanish at the * end of a transaction - * */ if (!CacheMemoryContext) CreateCacheMemoryContext(); @@ -214,13 +213,11 @@ CatalogCacheInitializeCache(CatCache *cache) /* * copy the relcache's tuple descriptor to permanent cache storage - * */ tupdesc = CreateTupleDescCopyConstr(RelationGetDescr(relation)); /* * return to the caller's memory context and close the rel - * */ MemoryContextSwitchTo(oldcxt); @@ -231,7 +228,6 @@ CatalogCacheInitializeCache(CatCache *cache) /* * initialize cache's key information - * */ for (i = 0; i < cache->cc_nkeys; ++i) { @@ -255,9 +251,23 @@ CatalogCacheInitializeCache(CatCache *cache) */ cache->cc_skey[i].sk_procedure = EQPROC(keytype); + /* + * Note: to avoid any possible leakage of scan temporary data into + * the cache context, we do not switch into CacheMemoryContext while + * calling fmgr_info here. Instead set fn_mcxt on return. This + * would fail to work correctly if fmgr_info allocated any subsidiary + * data structures to attach to the FmgrInfo record; but it doesn't + * do so for built-in functions, and all the comparator functions + * for system caches should most assuredly be built-in functions. + * Currently there's no real need to fix fn_mcxt either, but let's do + * that anyway just to make sure it's not pointing to a dead context + * later on. + */ + fmgr_info(cache->cc_skey[i].sk_procedure, &cache->cc_skey[i].sk_func); - cache->cc_skey[i].sk_nargs = cache->cc_skey[i].sk_func.fn_nargs; + + cache->cc_skey[i].sk_func.fn_mcxt = CacheMemoryContext; /* Initialize sk_attno suitably for HeapKeyTest() and heap scans */ cache->cc_skey[i].sk_attno = cache->cc_key[i]; @@ -270,7 +280,6 @@ CatalogCacheInitializeCache(CatCache *cache) /* * mark this cache fully initialized - * */ cache->cc_tupdesc = tupdesc; } @@ -705,7 +714,6 @@ InitCatCache(int id, * certain system indexes that support critical syscaches. * We can't use an indexscan to fetch these, else we'll get into * infinite recursion. A plain heap scan will work, however. - * */ static bool IndexScanOK(CatCache *cache, ScanKey cur_skey) diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c index d04e0cd559..6431bd9382 100644 --- a/src/backend/utils/cache/relcache.c +++ b/src/backend/utils/cache/relcache.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/cache/relcache.c,v 1.135 2001/05/30 14:15:26 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/cache/relcache.c,v 1.136 2001/06/01 02:41:36 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1161,19 +1161,20 @@ IndexedAccessMethodInitialize(Relation relation) int natts; Size stratSize; Size supportSize; - uint16 relamstrategies; - uint16 relamsupport; + uint16 amstrategies; + uint16 amsupport; natts = relation->rd_rel->relnatts; - relamstrategies = relation->rd_am->amstrategies; - stratSize = AttributeNumberGetIndexStrategySize(natts, relamstrategies); + amstrategies = relation->rd_am->amstrategies; + amsupport = relation->rd_am->amsupport; + + stratSize = AttributeNumberGetIndexStrategySize(natts, amstrategies); strategy = (IndexStrategy) MemoryContextAlloc(CacheMemoryContext, stratSize); - relamsupport = relation->rd_am->amsupport; - if (relamsupport > 0) + if (amsupport > 0) { - supportSize = natts * (relamsupport * sizeof(RegProcedure)); + supportSize = natts * (amsupport * sizeof(RegProcedure)); support = (RegProcedure *) MemoryContextAlloc(CacheMemoryContext, supportSize); } @@ -1182,9 +1183,9 @@ IndexedAccessMethodInitialize(Relation relation) IndexSupportInitialize(strategy, support, &relation->rd_uniqueindex, - relation->rd_att->attrs[0]->attrelid, + RelationGetRelid(relation), relation->rd_rel->relam, - relamstrategies, relamsupport, natts); + amstrategies, amsupport, natts); RelationSetIndexSupport(relation, strategy, support); } @@ -1212,26 +1213,22 @@ formrdesc(char *relationName, /* * allocate new relation desc - * */ relation = (Relation) palloc(sizeof(RelationData)); MemSet((char *) relation, 0, sizeof(RelationData)); /* * don't open the unix file yet.. - * */ relation->rd_fd = -1; /* * initialize reference count - * */ RelationSetReferenceCount(relation, 1); /* * all entries built with this routine are nailed-in-cache - * */ relation->rd_isnailed = true; @@ -1241,7 +1238,6 @@ formrdesc(char *relationName, * The data we insert here is pretty incomplete/bogus, but it'll serve to * get us launched. RelationCacheInitializePhase2() will read the * real data from pg_class and replace what we've done here. - * */ relation->rd_rel = (Form_pg_class) palloc(CLASS_TUPLE_SIZE); MemSet(relation->rd_rel, 0, CLASS_TUPLE_SIZE); @@ -1266,13 +1262,11 @@ formrdesc(char *relationName, /* * initialize attribute tuple form - * */ relation->rd_att = CreateTemplateTupleDesc(natts); /* * initialize tuple desc info - * */ for (i = 0; i < natts; i++) { @@ -1283,14 +1277,12 @@ formrdesc(char *relationName, } /* - * initialize relation id - * + * initialize relation id from info in att array (my, this is ugly) */ RelationGetRelid(relation) = relation->rd_att->attrs[0]->attrelid; /* * initialize the relation's lock manager and RelFileNode information - * */ RelationInitLockInfo(relation); /* see lmgr.c */ @@ -1303,7 +1295,6 @@ formrdesc(char *relationName, /* * initialize the rel-has-index flag, using hardwired knowledge - * */ relation->rd_rel->relhasindex = false; @@ -1322,7 +1313,6 @@ formrdesc(char *relationName, /* * add new reldesc to relcache - * */ RelationCacheInsert(relation); } @@ -2755,10 +2745,8 @@ init_irels(void) { fmgr_info(SMD(i).sk_procedure, &(SMD(i).sk_func)); - SMD(i).sk_nargs = SMD(i).sk_func.fn_nargs; } - /* * use a real field called rd_istrat instead of the bogosity of * hanging invisible fields off the end of a structure - jolly diff --git a/src/backend/utils/fmgr/fmgr.c b/src/backend/utils/fmgr/fmgr.c index 090807129f..544dc840c1 100644 --- a/src/backend/utils/fmgr/fmgr.c +++ b/src/backend/utils/fmgr/fmgr.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/fmgr/fmgr.c,v 1.52 2001/05/19 09:28:08 petere Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/fmgr/fmgr.c,v 1.53 2001/06/01 02:41:36 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -114,6 +114,15 @@ fmgr_lookupByName(const char *name) /* * This routine fills a FmgrInfo struct, given the OID * of the function to be called. + * + * The caller's CurrentMemoryContext is used as the fn_mcxt of the info + * struct; this means that any subsidiary data attached to the info struct + * (either by fmgr_info itself, or later on by a function call handler) + * will be allocated in that context. The caller must ensure that this + * context is at least as long-lived as the info struct itself. This is + * not a problem in typical cases where the info struct is on the stack or + * in freshly-palloc'd space, but one must take extra care when the info + * struct is in a long-lived table. */ void fmgr_info(Oid functionId, FmgrInfo *finfo) @@ -124,8 +133,9 @@ fmgr_info(Oid functionId, FmgrInfo *finfo) char *prosrc; /* - * fn_oid *must* be filled in last. Code may assume that is fn_oid is valid, - * the whole struct is valid. Some FmgrInfo struct's do survive elogs. + * fn_oid *must* be filled in last. Some code assumes that if fn_oid is + * valid, the whole struct is valid. Some FmgrInfo struct's do survive + * elogs. */ finfo->fn_oid = InvalidOid; finfo->fn_extra = NULL; @@ -133,10 +143,8 @@ fmgr_info(Oid functionId, FmgrInfo *finfo) if ((fbp = fmgr_isbuiltin(functionId)) != NULL) { - /* - * Fast path for builtin functions: don't bother consulting - * pg_proc + * Fast path for builtin functions: don't bother consulting pg_proc */ finfo->fn_nargs = fbp->nargs; finfo->fn_strict = fbp->strict; @@ -171,7 +179,6 @@ fmgr_info(Oid functionId, FmgrInfo *finfo) switch (procedureStruct->prolang) { case INTERNALlanguageId: - /* * For an ordinary builtin function, we should never get here * because the isbuiltin() search above will have succeeded. diff --git a/src/include/access/skey.h b/src/include/access/skey.h index 0cb9dc5d68..2a00e26744 100644 --- a/src/include/access/skey.h +++ b/src/include/access/skey.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: skey.h,v 1.14 2001/01/24 19:43:19 momjian Exp $ + * $Id: skey.h,v 1.15 2001/06/01 02:41:36 tgl Exp $ * * * Note: @@ -25,18 +25,18 @@ typedef struct ScanKeyData bits16 sk_flags; /* flags */ AttrNumber sk_attno; /* domain number */ RegProcedure sk_procedure; /* procedure OID */ - FmgrInfo sk_func; - int32 sk_nargs; + FmgrInfo sk_func; /* fmgr call info for procedure */ Datum sk_argument; /* data to compare */ } ScanKeyData; typedef ScanKeyData *ScanKey; +/* ScanKeyData flags */ +#define SK_ISNULL 0x1 /* sk_argument is NULL */ +#define SK_UNARY 0x2 /* unary function (currently unsupported) */ +#define SK_NEGATE 0x4 /* negate function result */ +#define SK_COMMUTE 0x8 /* commute function (not fully supported) */ -#define SK_ISNULL 0x1 -#define SK_UNARY 0x2 -#define SK_NEGATE 0x4 -#define SK_COMMUTE 0x8 #define ScanUnmarked 0x01 #define ScanUncheckedPrevious 0x02 diff --git a/src/include/commands/trigger.h b/src/include/commands/trigger.h index 46c4a49764..ddb9a327db 100644 --- a/src/include/commands/trigger.h +++ b/src/include/commands/trigger.h @@ -6,7 +6,7 @@ * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: trigger.h,v 1.26 2001/03/22 04:00:43 momjian Exp $ + * $Id: trigger.h,v 1.27 2001/06/01 02:41:36 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -90,15 +90,25 @@ extern void FreeTriggerDesc(TriggerDesc *trigdesc); extern bool equalTriggerDescs(TriggerDesc *trigdesc1, TriggerDesc *trigdesc2); extern HeapTuple ExecBRInsertTriggers(EState *estate, - Relation rel, HeapTuple tuple); + ResultRelInfo *relinfo, + HeapTuple trigtuple); 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); + ResultRelInfo *relinfo, + HeapTuple trigtuple); +extern bool ExecBRDeleteTriggers(EState *estate, + ResultRelInfo *relinfo, + ItemPointer tupleid); +extern void ExecARDeleteTriggers(EState *estate, + ResultRelInfo *relinfo, + ItemPointer tupleid); +extern HeapTuple ExecBRUpdateTriggers(EState *estate, + ResultRelInfo *relinfo, + ItemPointer tupleid, + HeapTuple newtuple); +extern void ExecARUpdateTriggers(EState *estate, + ResultRelInfo *relinfo, + ItemPointer tupleid, + HeapTuple newtuple); /* ---------- diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index 18903369c1..1dc68c192f 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: execnodes.h,v 1.60 2001/05/27 20:48:51 tgl Exp $ + * $Id: execnodes.h,v 1.61 2001/06/01 02:41:36 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -198,16 +198,18 @@ typedef struct JunkFilter /* ---------------- * ResultRelInfo information * - * whenever we update an existing relation, we have to - * update indices on the relation. The ResultRelInfo class - * is used to hold all the information on result relations, - * including indices.. -cim 10/15/89 + * Whenever we update an existing relation, we have to + * update indices on the relation, and perhaps also fire triggers. + * The ResultRelInfo class is used to hold all the information needed + * about a result relation, including indices.. -cim 10/15/89 * * RangeTableIndex result relation's range table index * RelationDesc relation descriptor for result relation * NumIndices # of indices existing on result relation * IndexRelationDescs array of relation descriptors for indices * IndexRelationInfo array of key/attr info for indices + * TrigDesc triggers to be fired, if any + * TrigFunctions cached lookup info for trigger functions * ConstraintExprs array of constraint-checking expressions * junkFilter for removing junk attributes from tuples * ---------------- @@ -220,6 +222,8 @@ typedef struct ResultRelInfo int ri_NumIndices; RelationPtr ri_IndexRelationDescs; IndexInfo **ri_IndexRelationInfo; + TriggerDesc *ri_TrigDesc; + FmgrInfo *ri_TrigFunctions; List **ri_ConstraintExprs; JunkFilter *ri_junkFilter; } ResultRelInfo; diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h index 5853be716d..f1574b3ffd 100644 --- a/src/include/utils/rel.h +++ b/src/include/utils/rel.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: rel.h,v 1.45 2001/03/22 04:01:14 momjian Exp $ + * $Id: rel.h,v 1.46 2001/06/01 02:41:36 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -47,13 +47,11 @@ typedef LockInfoData *LockInfo; * Likewise, this struct really belongs to trigger.h, but for convenience * we put it here. */ - typedef struct Trigger { Oid tgoid; char *tgname; Oid tgfoid; - FmgrInfo tgfunc; int16 tgtype; bool tgenabled; bool tgisconstraint; @@ -66,16 +64,23 @@ typedef struct Trigger typedef struct TriggerDesc { - /* index data to identify which triggers are which */ - uint16 n_before_statement[4]; - uint16 n_before_row[4]; - uint16 n_after_row[4]; - uint16 n_after_statement[4]; - Trigger **tg_before_statement[4]; - Trigger **tg_before_row[4]; - Trigger **tg_after_row[4]; - Trigger **tg_after_statement[4]; - /* the actual array of triggers is here */ + /* + * Index data to identify which triggers are which. Since each trigger + * can appear in more than one class, for each class we provide a list + * of integer indexes into the triggers array. + */ +#define TRIGGER_NUM_EVENT_CLASSES 4 + + uint16 n_before_statement[TRIGGER_NUM_EVENT_CLASSES]; + uint16 n_before_row[TRIGGER_NUM_EVENT_CLASSES]; + uint16 n_after_row[TRIGGER_NUM_EVENT_CLASSES]; + uint16 n_after_statement[TRIGGER_NUM_EVENT_CLASSES]; + int *tg_before_statement[TRIGGER_NUM_EVENT_CLASSES]; + int *tg_before_row[TRIGGER_NUM_EVENT_CLASSES]; + int *tg_after_row[TRIGGER_NUM_EVENT_CLASSES]; + int *tg_after_statement[TRIGGER_NUM_EVENT_CLASSES]; + + /* The actual array of triggers is here */ Trigger *triggers; int numtriggers; } TriggerDesc;