diff options
Diffstat (limited to 'src/backend')
-rw-r--r-- | src/backend/commands/tablecmds.c | 61 | ||||
-rw-r--r-- | src/backend/commands/trigger.c | 98 | ||||
-rw-r--r-- | src/backend/executor/execMain.c | 17 | ||||
-rw-r--r-- | src/backend/parser/gram.y | 3 | ||||
-rw-r--r-- | src/backend/utils/adt/ruleutils.c | 9 |
5 files changed, 173 insertions, 15 deletions
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 1754484eeeb..e97a4fc13aa 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.248 2008/03/27 03:57:33 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.249 2008/03/28 00:21:55 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -539,6 +539,9 @@ ExecuteTruncate(TruncateStmt *stmt) { List *rels = NIL; List *relids = NIL; + EState *estate; + ResultRelInfo *resultRelInfos; + ResultRelInfo *resultRelInfo; ListCell *cell; /* @@ -601,6 +604,45 @@ ExecuteTruncate(TruncateStmt *stmt) heap_truncate_check_FKs(rels, false); #endif + /* Prepare to catch AFTER triggers. */ + AfterTriggerBeginQuery(); + + /* + * To fire triggers, we'll need an EState as well as a ResultRelInfo + * for each relation. + */ + estate = CreateExecutorState(); + resultRelInfos = (ResultRelInfo *) + palloc(list_length(rels) * sizeof(ResultRelInfo)); + resultRelInfo = resultRelInfos; + foreach(cell, rels) + { + Relation rel = (Relation) lfirst(cell); + + InitResultRelInfo(resultRelInfo, + rel, + 0, /* dummy rangetable index */ + CMD_DELETE, /* don't need any index info */ + false); + resultRelInfo++; + } + estate->es_result_relations = resultRelInfos; + estate->es_num_result_relations = list_length(rels); + + /* + * Process all BEFORE STATEMENT TRUNCATE triggers before we begin + * truncating (this is because one of them might throw an error). + * Also, if we were to allow them to prevent statement execution, + * that would need to be handled here. + */ + resultRelInfo = resultRelInfos; + foreach(cell, rels) + { + estate->es_result_relation_info = resultRelInfo; + ExecBSTruncateTriggers(estate, resultRelInfo); + resultRelInfo++; + } + /* * OK, truncate each table. */ @@ -637,6 +679,23 @@ ExecuteTruncate(TruncateStmt *stmt) */ reindex_relation(heap_relid, true); } + + /* + * Process all AFTER STATEMENT TRUNCATE triggers. + */ + resultRelInfo = resultRelInfos; + foreach(cell, rels) + { + estate->es_result_relation_info = resultRelInfo; + ExecASTruncateTriggers(estate, resultRelInfo); + resultRelInfo++; + } + + /* Handle queued AFTER triggers */ + AfterTriggerEndQuery(estate); + + /* We can clean up the EState now */ + FreeExecutorState(estate); } /* diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c index 7dfe73aa068..9a7a0f81e73 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 - * $PostgreSQL: pgsql/src/backend/commands/trigger.c,v 1.230 2008/03/26 21:10:38 alvherre Exp $ + * $PostgreSQL: pgsql/src/backend/commands/trigger.c,v 1.231 2008/03/28 00:21:55 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -179,6 +179,18 @@ CreateTrigger(CreateTrigStmt *stmt, Oid constraintOid) errmsg("multiple UPDATE events specified"))); TRIGGER_SETT_UPDATE(tgtype); break; + case 't': + if (TRIGGER_FOR_TRUNCATE(tgtype)) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("multiple TRUNCATE events specified"))); + TRIGGER_SETT_TRUNCATE(tgtype); + /* Disallow ROW-level TRUNCATE triggers */ + if (stmt->row) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("TRUNCATE FOR EACH ROW triggers are not supported"))); + break; default: elog(ERROR, "unrecognized trigger event: %d", (int) stmt->actions[i]); @@ -1299,6 +1311,15 @@ InsertTrigger(TriggerDesc *trigdesc, Trigger *trigger, int indx) (*tp)[n[TRIGGER_EVENT_UPDATE]] = indx; (n[TRIGGER_EVENT_UPDATE])++; } + + if (TRIGGER_FOR_TRUNCATE(trigger->tgtype)) + { + tp = &(t[TRIGGER_EVENT_TRUNCATE]); + if (*tp == NULL) + *tp = (int *) palloc(trigdesc->numtriggers * sizeof(int)); + (*tp)[n[TRIGGER_EVENT_TRUNCATE]] = indx; + (n[TRIGGER_EVENT_TRUNCATE])++; + } } /* @@ -2030,6 +2051,75 @@ ExecARUpdateTriggers(EState *estate, ResultRelInfo *relinfo, } } +void +ExecBSTruncateTriggers(EState *estate, ResultRelInfo *relinfo) +{ + TriggerDesc *trigdesc; + int ntrigs; + int *tgindx; + int i; + TriggerData LocTriggerData; + + trigdesc = relinfo->ri_TrigDesc; + + if (trigdesc == NULL) + return; + + ntrigs = trigdesc->n_before_statement[TRIGGER_EVENT_TRUNCATE]; + tgindx = trigdesc->tg_before_statement[TRIGGER_EVENT_TRUNCATE]; + + if (ntrigs == 0) + return; + + LocTriggerData.type = T_TriggerData; + LocTriggerData.tg_event = TRIGGER_EVENT_TRUNCATE | + TRIGGER_EVENT_BEFORE; + LocTriggerData.tg_relation = relinfo->ri_RelationDesc; + LocTriggerData.tg_trigtuple = NULL; + LocTriggerData.tg_newtuple = NULL; + LocTriggerData.tg_trigtuplebuf = InvalidBuffer; + LocTriggerData.tg_newtuplebuf = InvalidBuffer; + for (i = 0; i < ntrigs; i++) + { + Trigger *trigger = &trigdesc->triggers[tgindx[i]]; + HeapTuple newtuple; + + if (SessionReplicationRole == SESSION_REPLICATION_ROLE_REPLICA) + { + if (trigger->tgenabled == TRIGGER_FIRES_ON_ORIGIN || + trigger->tgenabled == TRIGGER_DISABLED) + continue; + } + else /* ORIGIN or LOCAL role */ + { + if (trigger->tgenabled == TRIGGER_FIRES_ON_REPLICA || + trigger->tgenabled == TRIGGER_DISABLED) + continue; + } + LocTriggerData.tg_trigger = trigger; + newtuple = ExecCallTriggerFunc(&LocTriggerData, + tgindx[i], + relinfo->ri_TrigFunctions, + relinfo->ri_TrigInstrument, + GetPerTupleMemoryContext(estate)); + + if (newtuple) + ereport(ERROR, + (errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED), + errmsg("BEFORE STATEMENT trigger cannot return a value"))); + } +} + +void +ExecASTruncateTriggers(EState *estate, ResultRelInfo *relinfo) +{ + TriggerDesc *trigdesc = relinfo->ri_TrigDesc; + + if (trigdesc && trigdesc->n_after_statement[TRIGGER_EVENT_TRUNCATE] > 0) + AfterTriggerSaveEvent(relinfo, TRIGGER_EVENT_TRUNCATE, + false, NULL, NULL); +} + static HeapTuple GetTupleForTrigger(EState *estate, ResultRelInfo *relinfo, @@ -3572,6 +3662,12 @@ AfterTriggerSaveEvent(ResultRelInfo *relinfo, int event, bool row_trigger, elog(ERROR, "AfterTriggerSaveEvent() called outside of transaction"); /* + * event is used both as a bitmask and an array offset, + * so make sure we don't walk off the edge of our arrays + */ + Assert(event >= 0 && event < TRIGGER_NUM_EVENT_CLASSES); + + /* * Get the CTID's of OLD and NEW */ if (oldtup != NULL) diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index 4f06aa12410..db69ebb1403 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -26,7 +26,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.304 2008/03/26 21:10:38 alvherre Exp $ + * $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.305 2008/03/28 00:21:55 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -66,11 +66,6 @@ typedef struct evalPlanQual /* decls for local routines only used within this module */ static void InitPlan(QueryDesc *queryDesc, int eflags); -static void initResultRelInfo(ResultRelInfo *resultRelInfo, - Relation resultRelationDesc, - Index resultRelationIndex, - CmdType operation, - bool doInstrument); static void ExecEndPlan(PlanState *planstate, EState *estate); static TupleTableSlot *ExecutePlan(EState *estate, PlanState *planstate, CmdType operation, @@ -525,7 +520,7 @@ InitPlan(QueryDesc *queryDesc, int eflags) resultRelationOid = getrelid(resultRelationIndex, rangeTable); resultRelation = heap_open(resultRelationOid, RowExclusiveLock); - initResultRelInfo(resultRelInfo, + InitResultRelInfo(resultRelInfo, resultRelation, resultRelationIndex, operation, @@ -860,8 +855,8 @@ InitPlan(QueryDesc *queryDesc, int eflags) /* * Initialize ResultRelInfo data for one result relation */ -static void -initResultRelInfo(ResultRelInfo *resultRelInfo, +void +InitResultRelInfo(ResultRelInfo *resultRelInfo, Relation resultRelationDesc, Index resultRelationIndex, CmdType operation, @@ -997,11 +992,11 @@ ExecGetTriggerResultRel(EState *estate, Oid relid) /* * Make the new entry in the right context. Currently, we don't need any * index information in ResultRelInfos used only for triggers, so tell - * initResultRelInfo it's a DELETE. + * InitResultRelInfo it's a DELETE. */ oldcontext = MemoryContextSwitchTo(estate->es_query_cxt); rInfo = makeNode(ResultRelInfo); - initResultRelInfo(rInfo, + InitResultRelInfo(rInfo, rel, 0, /* dummy rangetable index */ CMD_DELETE, diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index ac14c2f2c74..21fff239c70 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -11,7 +11,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.610 2008/03/21 22:41:48 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.611 2008/03/28 00:21:55 tgl Exp $ * * HISTORY * AUTHOR DATE MAJOR EVENT @@ -2719,6 +2719,7 @@ TriggerOneEvent: INSERT { $$ = 'i'; } | DELETE_P { $$ = 'd'; } | UPDATE { $$ = 'u'; } + | TRUNCATE { $$ = 't'; } ; TriggerForSpec: diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index b1cff88b397..26bda928fa6 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -9,7 +9,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.271 2008/03/26 21:10:39 alvherre Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.272 2008/03/28 00:21:56 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -499,6 +499,13 @@ pg_get_triggerdef(PG_FUNCTION_ARGS) else appendStringInfo(&buf, " UPDATE"); } + if (TRIGGER_FOR_TRUNCATE(trigrec->tgtype)) + { + if (findx > 0) + appendStringInfo(&buf, " OR TRUNCATE"); + else + appendStringInfo(&buf, " TRUNCATE"); + } appendStringInfo(&buf, " ON %s ", generate_relation_name(trigrec->tgrelid)); |