diff options
| author | Tom Lane | 2010-10-10 17:43:33 +0000 |
|---|---|---|
| committer | Tom Lane | 2010-10-10 17:45:07 +0000 |
| commit | 2ec993a7cbdd8e251817ac6bbc9a704ce8346f73 (patch) | |
| tree | 1568fb4b00b6fa7997755113a3d0bbfead45c1fb /src/include | |
| parent | f7b15b5098ee89a2628129fbbef9901bded9d27b (diff) | |
Support triggers on views.
This patch adds the SQL-standard concept of an INSTEAD OF trigger, which
is fired instead of performing a physical insert/update/delete. The
trigger function is passed the entire old and/or new rows of the view,
and must figure out what to do to the underlying tables to implement
the update. So this feature can be used to implement updatable views
using trigger programming style rather than rule hacking.
In passing, this patch corrects the names of some columns in the
information_schema.triggers view. It seems the SQL committee renamed
them somewhere between SQL:99 and SQL:2003.
Dean Rasheed, reviewed by Bernd Helmle; some additional hacking by me.
Diffstat (limited to 'src/include')
| -rw-r--r-- | src/include/catalog/catversion.h | 2 | ||||
| -rw-r--r-- | src/include/catalog/pg_trigger.h | 31 | ||||
| -rw-r--r-- | src/include/commands/trigger.h | 58 | ||||
| -rw-r--r-- | src/include/nodes/parsenodes.h | 5 | ||||
| -rw-r--r-- | src/include/utils/rel.h | 41 |
5 files changed, 92 insertions, 45 deletions
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index c9212ab584b..910474cdcfc 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 201009281 +#define CATALOG_VERSION_NO 201010101 #endif diff --git a/src/include/catalog/pg_trigger.h b/src/include/catalog/pg_trigger.h index 46285ac3038..4eb72f20916 100644 --- a/src/include/catalog/pg_trigger.h +++ b/src/include/catalog/pg_trigger.h @@ -38,7 +38,7 @@ CATALOG(pg_trigger,2620) Oid tgrelid; /* relation trigger is attached to */ NameData tgname; /* trigger's name */ Oid tgfoid; /* OID of function to be called */ - int2 tgtype; /* BEFORE/AFTER UPDATE/DELETE/INSERT + int2 tgtype; /* BEFORE/AFTER/INSTEAD, UPDATE/DELETE/INSERT, * ROW/STATEMENT; see below */ char tgenabled; /* trigger's firing configuration WRT * session_replication_role */ @@ -91,22 +91,49 @@ typedef FormData_pg_trigger *Form_pg_trigger; #define TRIGGER_TYPE_DELETE (1 << 3) #define TRIGGER_TYPE_UPDATE (1 << 4) #define TRIGGER_TYPE_TRUNCATE (1 << 5) +#define TRIGGER_TYPE_INSTEAD (1 << 6) + +#define TRIGGER_TYPE_LEVEL_MASK (TRIGGER_TYPE_ROW) +#define TRIGGER_TYPE_STATEMENT 0 + +/* Note bits within TRIGGER_TYPE_TIMING_MASK aren't adjacent */ +#define TRIGGER_TYPE_TIMING_MASK \ + (TRIGGER_TYPE_BEFORE | TRIGGER_TYPE_INSTEAD) +#define TRIGGER_TYPE_AFTER 0 + +#define TRIGGER_TYPE_EVENT_MASK \ + (TRIGGER_TYPE_INSERT | TRIGGER_TYPE_DELETE | TRIGGER_TYPE_UPDATE | TRIGGER_TYPE_TRUNCATE) /* Macros for manipulating tgtype */ #define TRIGGER_CLEAR_TYPE(type) ((type) = 0) #define TRIGGER_SETT_ROW(type) ((type) |= TRIGGER_TYPE_ROW) +#define TRIGGER_SETT_STATEMENT(type) ((type) |= TRIGGER_TYPE_STATEMENT) #define TRIGGER_SETT_BEFORE(type) ((type) |= TRIGGER_TYPE_BEFORE) +#define TRIGGER_SETT_AFTER(type) ((type) |= TRIGGER_TYPE_AFTER) +#define TRIGGER_SETT_INSTEAD(type) ((type) |= TRIGGER_TYPE_INSTEAD) #define TRIGGER_SETT_INSERT(type) ((type) |= TRIGGER_TYPE_INSERT) #define TRIGGER_SETT_DELETE(type) ((type) |= TRIGGER_TYPE_DELETE) #define TRIGGER_SETT_UPDATE(type) ((type) |= TRIGGER_TYPE_UPDATE) #define TRIGGER_SETT_TRUNCATE(type) ((type) |= TRIGGER_TYPE_TRUNCATE) #define TRIGGER_FOR_ROW(type) ((type) & TRIGGER_TYPE_ROW) -#define TRIGGER_FOR_BEFORE(type) ((type) & TRIGGER_TYPE_BEFORE) +#define TRIGGER_FOR_BEFORE(type) (((type) & TRIGGER_TYPE_TIMING_MASK) == TRIGGER_TYPE_BEFORE) +#define TRIGGER_FOR_AFTER(type) (((type) & TRIGGER_TYPE_TIMING_MASK) == TRIGGER_TYPE_AFTER) +#define TRIGGER_FOR_INSTEAD(type) (((type) & TRIGGER_TYPE_TIMING_MASK) == TRIGGER_TYPE_INSTEAD) #define TRIGGER_FOR_INSERT(type) ((type) & TRIGGER_TYPE_INSERT) #define TRIGGER_FOR_DELETE(type) ((type) & TRIGGER_TYPE_DELETE) #define TRIGGER_FOR_UPDATE(type) ((type) & TRIGGER_TYPE_UPDATE) #define TRIGGER_FOR_TRUNCATE(type) ((type) & TRIGGER_TYPE_TRUNCATE) +/* + * Efficient macro for checking if tgtype matches a particular level + * (TRIGGER_TYPE_ROW or TRIGGER_TYPE_STATEMENT), timing (TRIGGER_TYPE_BEFORE, + * TRIGGER_TYPE_AFTER or TRIGGER_TYPE_INSTEAD), and event (TRIGGER_TYPE_INSERT, + * TRIGGER_TYPE_DELETE, TRIGGER_TYPE_UPDATE, or TRIGGER_TYPE_TRUNCATE). Note + * that a tgtype can match more than one event, but only one level or timing. + */ +#define TRIGGER_TYPE_MATCHES(type, level, timing, event) \ + (((type) & (TRIGGER_TYPE_LEVEL_MASK | TRIGGER_TYPE_TIMING_MASK | (event))) == ((level) | (timing) | (event))) + #endif /* PG_TRIGGER_H */ diff --git a/src/include/commands/trigger.h b/src/include/commands/trigger.h index 08bb22a3681..30dc314c00d 100644 --- a/src/include/commands/trigger.h +++ b/src/include/commands/trigger.h @@ -51,44 +51,48 @@ typedef struct TriggerData #define TRIGGER_EVENT_UPDATE 0x00000002 #define TRIGGER_EVENT_TRUNCATE 0x00000003 #define TRIGGER_EVENT_OPMASK 0x00000003 + #define TRIGGER_EVENT_ROW 0x00000004 + #define TRIGGER_EVENT_BEFORE 0x00000008 +#define TRIGGER_EVENT_AFTER 0x00000000 +#define TRIGGER_EVENT_INSTEAD 0x00000010 +#define TRIGGER_EVENT_TIMINGMASK 0x00000018 /* More TriggerEvent flags, used only within trigger.c */ -#define AFTER_TRIGGER_DEFERRABLE 0x00000010 -#define AFTER_TRIGGER_INITDEFERRED 0x00000020 +#define AFTER_TRIGGER_DEFERRABLE 0x00000020 +#define AFTER_TRIGGER_INITDEFERRED 0x00000040 -#define TRIGGER_FIRED_BY_INSERT(event) \ - (((TriggerEvent) (event) & TRIGGER_EVENT_OPMASK) == \ - TRIGGER_EVENT_INSERT) +#define TRIGGER_FIRED_BY_INSERT(event) \ + (((event) & TRIGGER_EVENT_OPMASK) == TRIGGER_EVENT_INSERT) -#define TRIGGER_FIRED_BY_DELETE(event) \ - (((TriggerEvent) (event) & TRIGGER_EVENT_OPMASK) == \ - TRIGGER_EVENT_DELETE) +#define TRIGGER_FIRED_BY_DELETE(event) \ + (((event) & TRIGGER_EVENT_OPMASK) == TRIGGER_EVENT_DELETE) -#define TRIGGER_FIRED_BY_UPDATE(event) \ - (((TriggerEvent) (event) & TRIGGER_EVENT_OPMASK) == \ - TRIGGER_EVENT_UPDATE) +#define TRIGGER_FIRED_BY_UPDATE(event) \ + (((event) & TRIGGER_EVENT_OPMASK) == TRIGGER_EVENT_UPDATE) #define TRIGGER_FIRED_BY_TRUNCATE(event) \ - (((TriggerEvent) (event) & TRIGGER_EVENT_OPMASK) == \ - TRIGGER_EVENT_TRUNCATE) + (((event) & TRIGGER_EVENT_OPMASK) == TRIGGER_EVENT_TRUNCATE) + +#define TRIGGER_FIRED_FOR_ROW(event) \ + ((event) & TRIGGER_EVENT_ROW) -#define TRIGGER_FIRED_FOR_ROW(event) \ - ((TriggerEvent) (event) & TRIGGER_EVENT_ROW) +#define TRIGGER_FIRED_FOR_STATEMENT(event) \ + (!TRIGGER_FIRED_FOR_ROW(event)) -#define TRIGGER_FIRED_FOR_STATEMENT(event) \ - (!TRIGGER_FIRED_FOR_ROW (event)) +#define TRIGGER_FIRED_BEFORE(event) \ + (((event) & TRIGGER_EVENT_TIMINGMASK) == TRIGGER_EVENT_BEFORE) -#define TRIGGER_FIRED_BEFORE(event) \ - ((TriggerEvent) (event) & TRIGGER_EVENT_BEFORE) +#define TRIGGER_FIRED_AFTER(event) \ + (((event) & TRIGGER_EVENT_TIMINGMASK) == TRIGGER_EVENT_AFTER) -#define TRIGGER_FIRED_AFTER(event) \ - (!TRIGGER_FIRED_BEFORE (event)) +#define TRIGGER_FIRED_INSTEAD(event) \ + (((event) & TRIGGER_EVENT_TIMINGMASK) == TRIGGER_EVENT_INSTEAD) /* - * Definitions for the replication role based firing. + * Definitions for replication role based firing. */ #define SESSION_REPLICATION_ROLE_ORIGIN 0 #define SESSION_REPLICATION_ROLE_REPLICA 1 @@ -135,6 +139,9 @@ extern void ExecARInsertTriggers(EState *estate, ResultRelInfo *relinfo, HeapTuple trigtuple, List *recheckIndexes); +extern HeapTuple ExecIRInsertTriggers(EState *estate, + ResultRelInfo *relinfo, + HeapTuple trigtuple); extern void ExecBSDeleteTriggers(EState *estate, ResultRelInfo *relinfo); extern void ExecASDeleteTriggers(EState *estate, @@ -146,6 +153,9 @@ extern bool ExecBRDeleteTriggers(EState *estate, extern void ExecARDeleteTriggers(EState *estate, ResultRelInfo *relinfo, ItemPointer tupleid); +extern bool ExecIRDeleteTriggers(EState *estate, + ResultRelInfo *relinfo, + HeapTuple trigtuple); extern void ExecBSUpdateTriggers(EState *estate, ResultRelInfo *relinfo); extern void ExecASUpdateTriggers(EState *estate, @@ -160,6 +170,10 @@ extern void ExecARUpdateTriggers(EState *estate, ItemPointer tupleid, HeapTuple newtuple, List *recheckIndexes); +extern HeapTuple ExecIRUpdateTriggers(EState *estate, + ResultRelInfo *relinfo, + HeapTuple oldtuple, + HeapTuple newtuple); extern void ExecBSTruncateTriggers(EState *estate, ResultRelInfo *relinfo); extern void ExecASTruncateTriggers(EState *estate, diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index b2f0fef5139..ca225d06ec1 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -1608,10 +1608,11 @@ typedef struct CreateTrigStmt RangeVar *relation; /* relation trigger is on */ List *funcname; /* qual. name of function to call */ List *args; /* list of (T_String) Values or NIL */ - bool before; /* BEFORE/AFTER */ bool row; /* ROW/STATEMENT */ + /* timing uses the TRIGGER_TYPE bits defined in catalog/pg_trigger.h */ + int16 timing; /* BEFORE, AFTER, or INSTEAD */ /* events uses the TRIGGER_TYPE bits defined in catalog/pg_trigger.h */ - int16 events; /* INSERT/UPDATE/DELETE/TRUNCATE */ + int16 events; /* "OR" of INSERT/UPDATE/DELETE/TRUNCATE */ List *columns; /* column names, or NIL for all columns */ Node *whenClause; /* qual expression, or NULL if none */ bool isconstraint; /* This is a constraint trigger */ diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h index 17ad88820d8..9ad92c299e8 100644 --- a/src/include/utils/rel.h +++ b/src/include/utils/rel.h @@ -71,26 +71,31 @@ typedef struct Trigger typedef struct TriggerDesc { + Trigger *triggers; /* array of Trigger structs */ + int numtriggers; /* number of array entries */ + /* - * 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. The class codes are defined - * by TRIGGER_EVENT_xxx macros in commands/trigger.h. + * These flags indicate whether the array contains at least one of each + * type of trigger. We use these to skip searching the array if not. */ -#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; + bool trig_insert_before_row; + bool trig_insert_after_row; + bool trig_insert_instead_row; + bool trig_insert_before_statement; + bool trig_insert_after_statement; + bool trig_update_before_row; + bool trig_update_after_row; + bool trig_update_instead_row; + bool trig_update_before_statement; + bool trig_update_after_statement; + bool trig_delete_before_row; + bool trig_delete_after_row; + bool trig_delete_instead_row; + bool trig_delete_before_statement; + bool trig_delete_after_statement; + /* there are no row-level truncate triggers */ + bool trig_truncate_before_statement; + bool trig_truncate_after_statement; } TriggerDesc; |
