📄 trigger.c
字号:
get_rel_name(relid)); object.classId = RelationGetRelid(tgrel); object.objectId = HeapTupleGetOid(tup); object.objectSubId = 0; systable_endscan(tgscan); heap_close(tgrel, AccessShareLock); /* * Do the deletion */ performDeletion(&object, behavior);}/* * Guts of trigger deletion. */voidRemoveTriggerById(Oid trigOid){ Relation tgrel; SysScanDesc tgscan; ScanKeyData skey[1]; HeapTuple tup; Oid relid; Relation rel; Relation pgrel; HeapTuple tuple; Form_pg_class classForm; tgrel = heap_openr(TriggerRelationName, RowExclusiveLock); /* * Find the trigger to delete. */ ScanKeyEntryInitialize(&skey[0], 0x0, ObjectIdAttributeNumber, F_OIDEQ, ObjectIdGetDatum(trigOid)); tgscan = systable_beginscan(tgrel, TriggerOidIndex, true, SnapshotNow, 1, skey); tup = systable_getnext(tgscan); if (!HeapTupleIsValid(tup)) elog(ERROR, "could not find tuple for trigger %u", trigOid); /* * Open and exclusive-lock the relation the trigger belongs to. */ relid = ((Form_pg_trigger) GETSTRUCT(tup))->tgrelid; rel = heap_open(relid, AccessExclusiveLock); if (rel->rd_rel->relkind != RELKIND_RELATION) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("\"%s\" is not a table", RelationGetRelationName(rel)))); if (!allowSystemTableMods && IsSystemRelation(rel)) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("permission denied: \"%s\" is a system catalog", RelationGetRelationName(rel)))); /* * Delete the pg_trigger tuple. */ simple_heap_delete(tgrel, &tup->t_self); systable_endscan(tgscan); heap_close(tgrel, RowExclusiveLock); /* * Update relation's pg_class entry. Crucial side-effect: other * backends (and this one too!) are sent SI message to make them * rebuild relcache entries. * * Note this is OK only because we have AccessExclusiveLock on the rel, * so no one else is creating/deleting triggers on this rel at the * same time. */ pgrel = heap_openr(RelationRelationName, RowExclusiveLock); tuple = SearchSysCacheCopy(RELOID, ObjectIdGetDatum(relid), 0, 0, 0); if (!HeapTupleIsValid(tuple)) elog(ERROR, "cache lookup failed for relation %u", relid); classForm = (Form_pg_class) GETSTRUCT(tuple); if (classForm->reltriggers == 0) /* should not happen */ elog(ERROR, "relation \"%s\" has reltriggers = 0", RelationGetRelationName(rel)); classForm->reltriggers--; simple_heap_update(pgrel, &tuple->t_self, tuple); CatalogUpdateIndexes(pgrel, tuple); heap_freetuple(tuple); heap_close(pgrel, RowExclusiveLock); /* Keep lock on trigger's rel until end of xact */ heap_close(rel, NoLock);}/* * renametrig - changes the name of a trigger on a relation * * trigger name is changed in trigger catalog. * No record of the previous name is kept. * * get proper relrelation from relation catalog (if not arg) * scan trigger catalog * for name conflict (within rel) * for original trigger (if not arg) * modify tgname in trigger tuple * update row in catalog */voidrenametrig(Oid relid, const char *oldname, const char *newname){ Relation targetrel; Relation tgrel; HeapTuple tuple; SysScanDesc tgscan; ScanKeyData key[2]; /* * Grab an exclusive lock on the target table, which we will NOT * release until end of transaction. */ targetrel = heap_open(relid, AccessExclusiveLock); /* * Scan pg_trigger twice for existing triggers on relation. We do * this in order to ensure a trigger does not exist with newname (The * unique index on tgrelid/tgname would complain anyway) and to ensure * a trigger does exist with oldname. * * NOTE that this is cool only because we have AccessExclusiveLock on the * relation, so the trigger set won't be changing underneath us. */ tgrel = heap_openr(TriggerRelationName, RowExclusiveLock); /* * First pass -- look for name conflict */ ScanKeyEntryInitialize(&key[0], 0, Anum_pg_trigger_tgrelid, F_OIDEQ, ObjectIdGetDatum(relid)); ScanKeyEntryInitialize(&key[1], 0, Anum_pg_trigger_tgname, F_NAMEEQ, PointerGetDatum(newname)); tgscan = systable_beginscan(tgrel, TriggerRelidNameIndex, true, SnapshotNow, 2, key); if (HeapTupleIsValid(tuple = systable_getnext(tgscan))) ereport(ERROR, (errcode(ERRCODE_DUPLICATE_OBJECT), errmsg("trigger \"%s\" for relation \"%s\" already exists", newname, RelationGetRelationName(targetrel)))); systable_endscan(tgscan); /* * Second pass -- look for trigger existing with oldname and update */ ScanKeyEntryInitialize(&key[0], 0, Anum_pg_trigger_tgrelid, F_OIDEQ, ObjectIdGetDatum(relid)); ScanKeyEntryInitialize(&key[1], 0, Anum_pg_trigger_tgname, F_NAMEEQ, PointerGetDatum(oldname)); tgscan = systable_beginscan(tgrel, TriggerRelidNameIndex, true, SnapshotNow, 2, key); if (HeapTupleIsValid(tuple = systable_getnext(tgscan))) { /* * Update pg_trigger tuple with new tgname. */ tuple = heap_copytuple(tuple); /* need a modifiable copy */ namestrcpy(&((Form_pg_trigger) GETSTRUCT(tuple))->tgname, newname); simple_heap_update(tgrel, &tuple->t_self, tuple); /* keep system catalog indexes current */ CatalogUpdateIndexes(tgrel, tuple); /* * Invalidate relation's relcache entry so that other backends * (and this one too!) are sent SI message to make them rebuild * relcache entries. (Ideally this should happen * automatically...) */ CacheInvalidateRelcache(relid); } else { ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("trigger \"%s\" for table \"%s\" does not exist", oldname, RelationGetRelationName(targetrel)))); } systable_endscan(tgscan); heap_close(tgrel, RowExclusiveLock); /* * Close rel, but keep exclusive lock! */ heap_close(targetrel, NoLock);}/* * Build trigger data to attach to the given relcache entry. * * 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. */voidRelationBuildTriggers(Relation relation){ TriggerDesc *trigdesc; int ntrigs = relation->rd_rel->reltriggers; Trigger *triggers; int found = 0; Relation tgrel; ScanKeyData skey; SysScanDesc tgscan; HeapTuple htup; MemoryContext oldContext; Assert(ntrigs > 0); /* else I should not have been called */ triggers = (Trigger *) palloc(ntrigs * sizeof(Trigger)); /* * Note: since we scan the triggers using TriggerRelidNameIndex, we * will be reading the triggers in name order, except possibly during * emergency-recovery operations (ie, IsIgnoringSystemIndexes). This * in turn ensures that triggers will be fired in name order. */ ScanKeyEntryInitialize(&skey, (bits16) 0x0, (AttrNumber) Anum_pg_trigger_tgrelid, (RegProcedure) F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(relation))); tgrel = heap_openr(TriggerRelationName, AccessShareLock); tgscan = systable_beginscan(tgrel, TriggerRelidNameIndex, true, SnapshotNow, 1, &skey); while (HeapTupleIsValid(htup = systable_getnext(tgscan))) { Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(htup); Trigger *build; if (found >= ntrigs) elog(ERROR, "too many trigger records found for relation \"%s\"", RelationGetRelationName(relation)); build = &(triggers[found]); build->tgoid = HeapTupleGetOid(htup); build->tgname = DatumGetCString(DirectFunctionCall1(nameout, NameGetDatum(&pg_trigger->tgname))); build->tgfoid = pg_trigger->tgfoid; build->tgtype = pg_trigger->tgtype; build->tgenabled = pg_trigger->tgenabled; build->tgisconstraint = pg_trigger->tgisconstraint; build->tgconstrrelid = pg_trigger->tgconstrrelid; build->tgdeferrable = pg_trigger->tgdeferrable; build->tginitdeferred = pg_trigger->tginitdeferred; build->tgnargs = pg_trigger->tgnargs; memcpy(build->tgattr, &(pg_trigger->tgattr), FUNC_MAX_ARGS * sizeof(int16)); if (build->tgnargs > 0) { bytea *val; bool isnull; char *p; int i; val = (bytea *) fastgetattr(htup, Anum_pg_trigger_tgargs, tgrel->rd_att, &isnull); if (isnull) elog(ERROR, "tgargs is null in trigger for relation \"%s\"", RelationGetRelationName(relation)); p = (char *) VARDATA(val); build->tgargs = (char **) palloc(build->tgnargs * sizeof(char *)); for (i = 0; i < build->tgnargs; i++) { build->tgargs[i] = pstrdup(p); p += strlen(p) + 1; } } else build->tgargs = NULL; found++; } systable_endscan(tgscan); heap_close(tgrel, AccessShareLock); if (found != ntrigs) elog(ERROR, "%d trigger record(s) not found for relation \"%s\"", ntrigs - found, RelationGetRelationName(relation)); /* Build trigdesc */ trigdesc = (TriggerDesc *) palloc0(sizeof(TriggerDesc)); trigdesc->triggers = triggers; trigdesc->numtriggers = ntrigs; for (found = 0; found < ntrigs; found++) InsertTrigger(trigdesc, &(triggers[found]), found); /* 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 * * 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 voidInsertTrigger(TriggerDesc *trigdesc, Trigger *trigger, int indx){ uint16 *n; int **t, **tp; if (TRIGGER_FOR_ROW(trigger->tgtype)) { /* ROW trigger */ if (TRIGGER_FOR_BEFORE(trigger->tgtype)) { n = trigdesc->n_before_row; t = trigdesc->tg_before_row; } else { n = trigdesc->n_after_row; t = trigdesc->tg_after_row; } } else { /* STATEMENT trigger */ if (TRIGGER_FOR_BEFORE(trigger->tgtype)) { n = trigdesc->n_before_statement; t = trigdesc->tg_before_statement; } else { n = trigdesc->n_after_statement; t = trigdesc->tg_after_statement; } } if (TRIGGER_FOR_INSERT(trigger->tgtype)) { tp = &(t[TRIGGER_EVENT_INSERT]); if (*tp == NULL) *tp = (int *) palloc(trigdesc->numtriggers * sizeof(int)); (*tp)[n[TRIGGER_EVENT_INSERT]] = indx; (n[TRIGGER_EVENT_INSERT])++; } if (TRIGGER_FOR_DELETE(trigger->tgtype)) { tp = &(t[TRIGGER_EVENT_DELETE]); if (*tp == NULL) *tp = (int *) palloc(trigdesc->numtriggers * sizeof(int)); (*tp)[n[TRIGGER_EVENT_DELETE]] = indx; (n[TRIGGER_EVENT_DELETE])++; } if (TRIGGER_FOR_UPDATE(trigger->tgtype)) { tp = &(t[TRIGGER_EVENT_UPDATE]); if (*tp == NULL) *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;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -