aboutsummaryrefslogtreecommitdiff
path: root/src/backend
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend')
-rw-r--r--src/backend/commands/tablecmds.c61
-rw-r--r--src/backend/commands/trigger.c98
-rw-r--r--src/backend/executor/execMain.c17
-rw-r--r--src/backend/parser/gram.y3
-rw-r--r--src/backend/utils/adt/ruleutils.c9
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));