📄 trigger.c
字号:
CStringGetDatum(trigname)); tgscan = systable_beginscan(tgrel, TriggerRelidNameIndexId, true, SnapshotNow, 2, skey); tup = systable_getnext(tgscan); if (!HeapTupleIsValid(tup)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("trigger \"%s\" for table \"%s\" does not exist", trigname, get_rel_name(relid)))); if (!pg_class_ownercheck(relid, GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, get_rel_name(relid)); object.classId = TriggerRelationId; 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_open(TriggerRelationId, RowExclusiveLock); /* * Find the trigger to delete. */ ScanKeyInit(&skey[0], ObjectIdAttributeNumber, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(trigOid)); tgscan = systable_beginscan(tgrel, TriggerOidIndexId, 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_open(RelationRelationId, 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_open(TriggerRelationId, RowExclusiveLock); /* * First pass -- look for name conflict */ ScanKeyInit(&key[0], Anum_pg_trigger_tgrelid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(relid)); ScanKeyInit(&key[1], Anum_pg_trigger_tgname, BTEqualStrategyNumber, F_NAMEEQ, PointerGetDatum(newname)); tgscan = systable_beginscan(tgrel, TriggerRelidNameIndexId, 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 */ ScanKeyInit(&key[0], Anum_pg_trigger_tgrelid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(relid)); ScanKeyInit(&key[1], Anum_pg_trigger_tgname, BTEqualStrategyNumber, F_NAMEEQ, PointerGetDatum(oldname)); tgscan = systable_beginscan(tgrel, TriggerRelidNameIndexId, 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(targetrel); } 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);}/* * EnableDisableTrigger() * * Called by ALTER TABLE ENABLE/DISABLE TRIGGER * to change 'tgenabled' flag for the specified trigger(s) * * rel: relation to process (caller must hold suitable lock on it) * tgname: trigger to process, or NULL to scan all triggers * enable: new value for tgenabled flag * skip_system: if true, skip "system" triggers (constraint triggers) * * Caller should have checked permissions for the table; here we also * enforce that superuser privilege is required to alter the state of * system triggers */voidEnableDisableTrigger(Relation rel, const char *tgname, bool enable, bool skip_system){ Relation tgrel; int nkeys; ScanKeyData keys[2]; SysScanDesc tgscan; HeapTuple tuple; bool found; bool changed; /* Scan the relevant entries in pg_triggers */ tgrel = heap_open(TriggerRelationId, RowExclusiveLock); ScanKeyInit(&keys[0], Anum_pg_trigger_tgrelid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(rel))); if (tgname) { ScanKeyInit(&keys[1], Anum_pg_trigger_tgname, BTEqualStrategyNumber, F_NAMEEQ, CStringGetDatum(tgname)); nkeys = 2; } else nkeys = 1; tgscan = systable_beginscan(tgrel, TriggerRelidNameIndexId, true, SnapshotNow, nkeys, keys); found = changed = false; while (HeapTupleIsValid(tuple = systable_getnext(tgscan))) { Form_pg_trigger oldtrig = (Form_pg_trigger) GETSTRUCT(tuple); if (oldtrig->tgisconstraint) { /* system trigger ... ok to process? */ if (skip_system) continue; if (!superuser()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("permission denied: \"%s\" is a system trigger", NameStr(oldtrig->tgname)))); } found = true; if (oldtrig->tgenabled != enable) { /* need to change this one ... make a copy to scribble on */ HeapTuple newtup = heap_copytuple(tuple); Form_pg_trigger newtrig = (Form_pg_trigger) GETSTRUCT(newtup); newtrig->tgenabled = enable; simple_heap_update(tgrel, &newtup->t_self, newtup); /* Keep catalog indexes current */ CatalogUpdateIndexes(tgrel, newtup); heap_freetuple(newtup); changed = true; } } systable_endscan(tgscan); heap_close(tgrel, RowExclusiveLock); if (tgname && !found) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("trigger \"%s\" for table \"%s\" does not exist", tgname, RelationGetRelationName(rel)))); /* * If we changed anything, broadcast a SI inval message to force each * backend (including our own!) to rebuild relation's relcache entry. * Otherwise they will fail to apply the change promptly. */ if (changed) CacheInvalidateRelcache(rel);}/* * 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 TriggerRelidNameIndexId, 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. */ ScanKeyInit(&skey, Anum_pg_trigger_tgrelid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(relation))); tgrel = heap_open(TriggerRelationId, AccessShareLock); tgscan = systable_beginscan(tgrel, TriggerRelidNameIndexId, 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; /* tgattr is first var-width field, so OK to access directly */ build->tgnattr = pg_trigger->tgattr.dim1; if (build->tgnattr > 0) { build->tgattr = (int2 *) palloc(build->tgnattr * sizeof(int2)); memcpy(build->tgattr, &(pg_trigger->tgattr.values), build->tgnattr * sizeof(int2)); } else build->tgattr = NULL; if (build->tgnargs > 0) { bytea *val; bool isnull; char *p; int i; val = (bytea *) DatumGetPointer(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
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -