diff -cprN head/doc/src/sgml/func.sgml standard-column-trigger/doc/src/sgml/func.sgml
*** head/doc/src/sgml/func.sgml 2009-08-17 04:55:21.000000000 +0900
--- standard-column-trigger/doc/src/sgml/func.sgml 2009-09-14 17:44:53.781713886 +0900
*************** SELECT pg_type_is_visible('myschema.widg
*** 12370,12375 ****
--- 12370,12380 ----
get CREATE [ CONSTRAINT ] TRIGGER> command for trigger
+ pg_get_triggerdef(trigger_oid, pretty_bool>)
+ text
+ get CREATE [ CONSTRAINT ] TRIGGER> command for trigger
+
+
pg_get_userbyid(role_oid)
name
get role name with given OID
diff -cprN head/doc/src/sgml/ref/create_trigger.sgml standard-column-trigger/doc/src/sgml/ref/create_trigger.sgml
*** head/doc/src/sgml/ref/create_trigger.sgml 2008-11-14 19:22:46.000000000 +0900
--- standard-column-trigger/doc/src/sgml/ref/create_trigger.sgml 2009-09-14 17:23:24.167839706 +0900
*************** CREATE TRIGGER rd_att, values, nulls);
--- 342,380 ----
CStringGetDatum(""));
}
! /* build column references for UPDATE OF */
! ncolumns = list_length(stmt->columns);
! if (ncolumns == 0)
! columns = NULL;
! else
! {
! ListCell *cell;
! int x = 0;
!
! columns = (int2 *) palloc(ncolumns * sizeof(int2));
!
! foreach (cell, stmt->columns)
! {
! char *name = strVal(lfirst(cell));
! int attnum;
! int y;
!
! /* Lookup column name. System columns are not allowed. */
! attnum = attnameAttNum(rel, name, false);
!
! /* Check for duplicates */
! for (y = x - 1; y >= 0; y--)
! {
! if (columns[y] == attnum)
! ereport(ERROR,
! (errcode(ERRCODE_DUPLICATE_COLUMN),
! errmsg("column \"%s\" specified more than once", name)));
! }
!
! columns[x++] = attnum;
! }
! }
! tgattr = buildint2vector(columns, ncolumns);
values[Anum_pg_trigger_tgattr - 1] = PointerGetDatum(tgattr);
tuple = heap_form_tuple(tgrel->rd_att, values, nulls);
*************** CreateTrigger(CreateTrigStmt *stmt,
*** 433,438 ****
--- 470,492 ----
Assert(!OidIsValid(indexOid));
}
+ /* Add dependency on columns */
+ if (columns != NULL)
+ {
+ int i;
+ Form_pg_attribute *attrs;
+
+ attrs = RelationGetDescr(rel)->attrs;
+
+ referenced.classId = RelationRelationId;
+ referenced.objectId = RelationGetRelid(rel);
+ for (i = 0; i < ncolumns; i++)
+ {
+ referenced.objectSubId = attrs[columns[i] - 1]->attnum;
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+ }
+ }
+
/* Keep lock on target rel until end of xact */
heap_close(rel, NoLock);
*************** ExecBSInsertTriggers(EState *estate, Res
*** 1625,1642 ****
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],
--- 1679,1687 ----
Trigger *trigger = &trigdesc->triggers[tgindx[i]];
HeapTuple newtuple;
! if (!TriggerEnabled(trigger, NULL))
! continue;
!
LocTriggerData.tg_trigger = trigger;
newtuple = ExecCallTriggerFunc(&LocTriggerData,
tgindx[i],
*************** ExecASInsertTriggers(EState *estate, Res
*** 1658,1664 ****
if (trigdesc && trigdesc->n_after_statement[TRIGGER_EVENT_INSERT] > 0)
AfterTriggerSaveEvent(relinfo, TRIGGER_EVENT_INSERT,
! false, NULL, NULL, NIL);
}
HeapTuple
--- 1703,1709 ----
if (trigdesc && trigdesc->n_after_statement[TRIGGER_EVENT_INSERT] > 0)
AfterTriggerSaveEvent(relinfo, TRIGGER_EVENT_INSERT,
! false, NULL, NULL, NIL, NULL);
}
HeapTuple
*************** ExecBRInsertTriggers(EState *estate, Res
*** 1684,1701 ****
{
Trigger *trigger = &trigdesc->triggers[tgindx[i]];
! 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_trigtuple = oldtuple = newtuple;
LocTriggerData.tg_trigtuplebuf = InvalidBuffer;
LocTriggerData.tg_trigger = trigger;
--- 1729,1737 ----
{
Trigger *trigger = &trigdesc->triggers[tgindx[i]];
! if (!TriggerEnabled(trigger, NULL))
! continue;
!
LocTriggerData.tg_trigtuple = oldtuple = newtuple;
LocTriggerData.tg_trigtuplebuf = InvalidBuffer;
LocTriggerData.tg_trigger = trigger;
*************** ExecARInsertTriggers(EState *estate, Res
*** 1720,1726 ****
if (trigdesc && trigdesc->n_after_row[TRIGGER_EVENT_INSERT] > 0)
AfterTriggerSaveEvent(relinfo, TRIGGER_EVENT_INSERT,
! true, NULL, trigtuple, recheckIndexes);
}
void
--- 1756,1762 ----
if (trigdesc && trigdesc->n_after_row[TRIGGER_EVENT_INSERT] > 0)
AfterTriggerSaveEvent(relinfo, TRIGGER_EVENT_INSERT,
! true, NULL, trigtuple, recheckIndexes, NULL);
}
void
*************** ExecBSDeleteTriggers(EState *estate, Res
*** 1756,1773 ****
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],
--- 1792,1800 ----
Trigger *trigger = &trigdesc->triggers[tgindx[i]];
HeapTuple newtuple;
! if (!TriggerEnabled(trigger, NULL))
! continue;
!
LocTriggerData.tg_trigger = trigger;
newtuple = ExecCallTriggerFunc(&LocTriggerData,
tgindx[i],
*************** ExecASDeleteTriggers(EState *estate, Res
*** 1789,1795 ****
if (trigdesc && trigdesc->n_after_statement[TRIGGER_EVENT_DELETE] > 0)
AfterTriggerSaveEvent(relinfo, TRIGGER_EVENT_DELETE,
! false, NULL, NULL, NIL);
}
bool
--- 1816,1822 ----
if (trigdesc && trigdesc->n_after_statement[TRIGGER_EVENT_DELETE] > 0)
AfterTriggerSaveEvent(relinfo, TRIGGER_EVENT_DELETE,
! false, NULL, NULL, NIL, NULL);
}
bool
*************** ExecBRDeleteTriggers(EState *estate, Res
*** 1821,1838 ****
{
Trigger *trigger = &trigdesc->triggers[tgindx[i]];
! 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_trigtuple = trigtuple;
LocTriggerData.tg_trigtuplebuf = InvalidBuffer;
LocTriggerData.tg_trigger = trigger;
--- 1848,1856 ----
{
Trigger *trigger = &trigdesc->triggers[tgindx[i]];
! if (!TriggerEnabled(trigger, NULL))
! continue;
!
LocTriggerData.tg_trigtuple = trigtuple;
LocTriggerData.tg_trigtuplebuf = InvalidBuffer;
LocTriggerData.tg_trigger = trigger;
*************** ExecARDeleteTriggers(EState *estate, Res
*** 1866,1872 ****
tupleid, NULL);
AfterTriggerSaveEvent(relinfo, TRIGGER_EVENT_DELETE,
! true, trigtuple, NULL, NIL);
heap_freetuple(trigtuple);
}
}
--- 1884,1890 ----
tupleid, NULL);
AfterTriggerSaveEvent(relinfo, TRIGGER_EVENT_DELETE,
! true, trigtuple, NULL, NIL, NULL);
heap_freetuple(trigtuple);
}
}
*************** ExecBSUpdateTriggers(EState *estate, Res
*** 1904,1921 ****
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],
--- 1922,1930 ----
Trigger *trigger = &trigdesc->triggers[tgindx[i]];
HeapTuple newtuple;
! if (!TriggerEnabled(trigger, NULL))
! continue;
!
LocTriggerData.tg_trigger = trigger;
newtuple = ExecCallTriggerFunc(&LocTriggerData,
tgindx[i],
*************** ExecASUpdateTriggers(EState *estate, Res
*** 1937,1943 ****
if (trigdesc && trigdesc->n_after_statement[TRIGGER_EVENT_UPDATE] > 0)
AfterTriggerSaveEvent(relinfo, TRIGGER_EVENT_UPDATE,
! false, NULL, NULL, NIL);
}
HeapTuple
--- 1946,1952 ----
if (trigdesc && trigdesc->n_after_statement[TRIGGER_EVENT_UPDATE] > 0)
AfterTriggerSaveEvent(relinfo, TRIGGER_EVENT_UPDATE,
! false, NULL, NULL, NIL, NULL);
}
HeapTuple
*************** ExecBRUpdateTriggers(EState *estate, Res
*** 1953,1963 ****
--- 1962,1975 ----
HeapTuple intuple = newtuple;
TupleTableSlot *newSlot;
int i;
+ Bitmapset *modifiedCols;
trigtuple = GetTupleForTrigger(estate, relinfo, tupleid, &newSlot);
if (trigtuple == NULL)
return NULL;
+ modifiedCols = rt_fetch(relinfo->ri_RangeTableIndex, estate->es_range_table)->modifiedCols;
+
/*
* In READ COMMITTED isolation level it's possible that newtuple was
* changed due to concurrent update.
*************** ExecBRUpdateTriggers(EState *estate, Res
*** 1974,1991 ****
{
Trigger *trigger = &trigdesc->triggers[tgindx[i]];
! 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_trigtuple = trigtuple;
LocTriggerData.tg_newtuple = oldtuple = newtuple;
LocTriggerData.tg_trigtuplebuf = InvalidBuffer;
--- 1986,1994 ----
{
Trigger *trigger = &trigdesc->triggers[tgindx[i]];
! if (!TriggerEnabled(trigger, modifiedCols))
! continue;
!
LocTriggerData.tg_trigtuple = trigtuple;
LocTriggerData.tg_newtuple = oldtuple = newtuple;
LocTriggerData.tg_trigtuplebuf = InvalidBuffer;
*************** ExecARUpdateTriggers(EState *estate, Res
*** 2018,2024 ****
tupleid, NULL);
AfterTriggerSaveEvent(relinfo, TRIGGER_EVENT_UPDATE,
! true, trigtuple, newtuple, recheckIndexes);
heap_freetuple(trigtuple);
}
}
--- 2021,2028 ----
tupleid, NULL);
AfterTriggerSaveEvent(relinfo, TRIGGER_EVENT_UPDATE,
! true, trigtuple, newtuple, recheckIndexes,
! rt_fetch(relinfo->ri_RangeTableIndex, estate->es_range_table)->modifiedCols);
heap_freetuple(trigtuple);
}
}
*************** ExecBSTruncateTriggers(EState *estate, R
*** 2056,2073 ****
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],
--- 2060,2068 ----
Trigger *trigger = &trigdesc->triggers[tgindx[i]];
HeapTuple newtuple;
! if (!TriggerEnabled(trigger, NULL))
! continue;
!
LocTriggerData.tg_trigger = trigger;
newtuple = ExecCallTriggerFunc(&LocTriggerData,
tgindx[i],
*************** ExecASTruncateTriggers(EState *estate, R
*** 2089,2095 ****
if (trigdesc && trigdesc->n_after_statement[TRIGGER_EVENT_TRUNCATE] > 0)
AfterTriggerSaveEvent(relinfo, TRIGGER_EVENT_TRUNCATE,
! false, NULL, NULL, NIL);
}
--- 2084,2090 ----
if (trigdesc && trigdesc->n_after_statement[TRIGGER_EVENT_TRUNCATE] > 0)
AfterTriggerSaveEvent(relinfo, TRIGGER_EVENT_TRUNCATE,
! false, NULL, NULL, NIL, NULL);
}
*************** AfterTriggerPendingOnRel(Oid relid)
*** 3813,3819 ****
static void
AfterTriggerSaveEvent(ResultRelInfo *relinfo, int event, bool row_trigger,
HeapTuple oldtup, HeapTuple newtup,
! List *recheckIndexes)
{
Relation rel = relinfo->ri_RelationDesc;
TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
--- 3808,3814 ----
static void
AfterTriggerSaveEvent(ResultRelInfo *relinfo, int event, bool row_trigger,
HeapTuple oldtup, HeapTuple newtup,
! List *recheckIndexes, Bitmapset *modifiedCols)
{
Relation rel = relinfo->ri_RelationDesc;
TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
*************** AfterTriggerSaveEvent(ResultRelInfo *rel
*** 3915,3933 ****
{
Trigger *trigger = &trigdesc->triggers[tgindx[i]];
! /* Ignore disabled triggers */
! 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;
! }
/*
* If this is an UPDATE of a PK table or FK table that does not change
--- 3910,3917 ----
{
Trigger *trigger = &trigdesc->triggers[tgindx[i]];
! if (!TriggerEnabled(trigger, modifiedCols))
! continue;
/*
* If this is an UPDATE of a PK table or FK table that does not change
*************** AfterTriggerSaveEvent(ResultRelInfo *rel
*** 4000,4002 ****
--- 3984,4026 ----
&new_event, &new_shared);
}
}
+
+ static bool
+ TriggerEnabled(Trigger *trigger, Bitmapset *modifiedCols)
+ {
+ if (SessionReplicationRole == SESSION_REPLICATION_ROLE_REPLICA)
+ {
+ if (trigger->tgenabled == TRIGGER_FIRES_ON_ORIGIN ||
+ trigger->tgenabled == TRIGGER_DISABLED)
+ return false;
+ }
+ else /* ORIGIN or LOCAL role */
+ {
+ if (trigger->tgenabled == TRIGGER_FIRES_ON_REPLICA ||
+ trigger->tgenabled == TRIGGER_DISABLED)
+ return false;
+ }
+
+ /* Check for column-level triggers */
+ if (trigger->tgnattr > 0 && modifiedCols != NULL &&
+ (trigger->tgtype & (TRIGGER_TYPE_UPDATE | TRIGGER_TYPE_ROW)) ==
+ (TRIGGER_TYPE_UPDATE | TRIGGER_TYPE_ROW))
+ {
+ int i;
+ bool modified;
+
+ modified = false;
+ for (i = 0; i < trigger->tgnattr; i++)
+ {
+ if (bms_is_member(trigger->tgattr[i] - FirstLowInvalidHeapAttributeNumber, modifiedCols))
+ {
+ modified = true;
+ break;
+ }
+ }
+ if (!modified)
+ return false;
+ }
+
+ return true;
+ }
diff -cprN head/src/backend/parser/gram.y standard-column-trigger/src/backend/parser/gram.y
*** head/src/backend/parser/gram.y 2009-08-19 08:40:20.000000000 +0900
--- standard-column-trigger/src/backend/parser/gram.y 2009-09-14 17:23:24.271734848 +0900
*************** static TypeName *TableFuncTypeName(List
*** 248,254 ****
%type TriggerActionTime TriggerForSpec opt_trusted opt_restart_seqs
%type opt_lancompiler
! %type TriggerEvents TriggerOneEvent
%type TriggerFuncArg
%type relation_name copy_file_name
--- 248,254 ----
%type TriggerActionTime TriggerForSpec opt_trusted opt_restart_seqs
%type opt_lancompiler
! %type TriggerEvents TriggerOneEvent
%type TriggerFuncArg
%type relation_name copy_file_name
*************** CreateTrigStmt:
*** 3160,3166 ****
n->args = $13;
n->before = $4;
n->row = $8;
! n->events = $5;
n->isconstraint = FALSE;
n->deferrable = FALSE;
n->initdeferred = FALSE;
--- 3160,3167 ----
n->args = $13;
n->before = $4;
n->row = $8;
! n->events = intVal(linitial($5));
! n->columns = llast($5);
n->isconstraint = FALSE;
n->deferrable = FALSE;
n->initdeferred = FALSE;
*************** CreateTrigStmt:
*** 3180,3186 ****
n->args = $18;
n->before = FALSE;
n->row = TRUE;
! n->events = $6;
n->isconstraint = TRUE;
n->deferrable = ($10 & 1) != 0;
n->initdeferred = ($10 & 2) != 0;
--- 3181,3188 ----
n->args = $18;
n->before = FALSE;
n->row = TRUE;
! n->events = intVal(linitial($6));
! n->columns = llast($6);
n->isconstraint = TRUE;
n->deferrable = ($10 & 1) != 0;
n->initdeferred = ($10 & 2) != 0;
*************** TriggerEvents:
*** 3199,3215 ****
{ $$ = $1; }
| TriggerEvents OR TriggerOneEvent
{
! if ($1 & $3)
parser_yyerror("duplicate trigger events specified");
! $$ = $1 | $3;
}
;
TriggerOneEvent:
! INSERT { $$ = TRIGGER_TYPE_INSERT; }
! | DELETE_P { $$ = TRIGGER_TYPE_DELETE; }
! | UPDATE { $$ = TRIGGER_TYPE_UPDATE; }
! | TRUNCATE { $$ = TRIGGER_TYPE_TRUNCATE; }
;
TriggerForSpec:
--- 3201,3222 ----
{ $$ = $1; }
| TriggerEvents OR TriggerOneEvent
{
! int events1 = intVal(linitial($1));
! int events2 = intVal(linitial($3));
!
! if (events1 & events2)
parser_yyerror("duplicate trigger events specified");
! $$ = list_make2(makeInteger(events1 | events2),
! list_concat(llast($1), llast($3)));
}
;
TriggerOneEvent:
! INSERT { $$ = list_make2(makeInteger(TRIGGER_TYPE_INSERT), NIL); }
! | DELETE_P { $$ = list_make2(makeInteger(TRIGGER_TYPE_DELETE), NIL); }
! | UPDATE { $$ = list_make2(makeInteger(TRIGGER_TYPE_UPDATE), NIL); }
! | UPDATE OF columnList { $$ = list_make2(makeInteger(TRIGGER_TYPE_UPDATE), $3); }
! | TRUNCATE { $$ = list_make2(makeInteger(TRIGGER_TYPE_TRUNCATE), NIL); }
;
TriggerForSpec:
diff -cprN head/src/backend/utils/adt/ruleutils.c standard-column-trigger/src/backend/utils/adt/ruleutils.c
*** head/src/backend/utils/adt/ruleutils.c 2009-08-02 04:59:41.000000000 +0900
--- standard-column-trigger/src/backend/utils/adt/ruleutils.c 2009-09-14 17:59:36.415778604 +0900
*************** static char *deparse_expression_pretty(N
*** 139,144 ****
--- 139,145 ----
bool forceprefix, bool showimplicit,
int prettyFlags, int startIndent);
static char *pg_get_viewdef_worker(Oid viewoid, int prettyFlags);
+ static char *pg_get_triggerdef_worker(Oid trigid, bool pretty);
static void decompile_column_index_array(Datum column_index_array, Oid relId,
StringInfo buf);
static char *pg_get_ruledef_worker(Oid ruleoid, int prettyFlags);
*************** Datum
*** 462,467 ****
--- 463,484 ----
pg_get_triggerdef(PG_FUNCTION_ARGS)
{
Oid trigid = PG_GETARG_OID(0);
+
+ PG_RETURN_TEXT_P(string_to_text(pg_get_triggerdef_worker(trigid, 0)));
+ }
+
+ Datum
+ pg_get_triggerdef_ext(PG_FUNCTION_ARGS)
+ {
+ Oid trigid = PG_GETARG_OID(0);
+ bool pretty = PG_GETARG_BOOL(1);
+
+ PG_RETURN_TEXT_P(string_to_text(pg_get_triggerdef_worker(trigid, pretty)));
+ }
+
+ static char *
+ pg_get_triggerdef_worker(Oid trigid, bool pretty)
+ {
HeapTuple ht_trig;
Form_pg_trigger trigrec;
StringInfoData buf;
*************** pg_get_triggerdef(PG_FUNCTION_ARGS)
*** 498,506 ****
initStringInfo(&buf);
tgname = NameStr(trigrec->tgname);
! appendStringInfo(&buf, "CREATE %sTRIGGER %s ",
trigrec->tgisconstraint ? "CONSTRAINT " : "",
! quote_identifier(tgname));
if (TRIGGER_FOR_BEFORE(trigrec->tgtype))
appendStringInfo(&buf, "BEFORE");
--- 515,524 ----
initStringInfo(&buf);
tgname = NameStr(trigrec->tgname);
! appendStringInfo(&buf, "CREATE %sTRIGGER %s%s",
trigrec->tgisconstraint ? "CONSTRAINT " : "",
! quote_identifier(tgname),
! (pretty ? "\n " : " "));
if (TRIGGER_FOR_BEFORE(trigrec->tgtype))
appendStringInfo(&buf, "BEFORE");
*************** pg_get_triggerdef(PG_FUNCTION_ARGS)
*** 525,530 ****
--- 543,562 ----
appendStringInfo(&buf, " OR UPDATE");
else
appendStringInfo(&buf, " UPDATE");
+ if (trigrec->tgattr.dim1 > 0)
+ {
+ int i;
+
+ appendStringInfoString(&buf, " OF ");
+ for (i = 0; i < trigrec->tgattr.dim1; i++)
+ {
+ if (i > 0)
+ appendStringInfoString(&buf, ", ");
+ appendStringInfoString(&buf,
+ quote_identifier(get_relid_attribute_name(
+ trigrec->tgrelid, trigrec->tgattr.values[i])));
+ }
+ }
}
if (TRIGGER_FOR_TRUNCATE(trigrec->tgtype))
{
*************** pg_get_triggerdef(PG_FUNCTION_ARGS)
*** 533,561 ****
else
appendStringInfo(&buf, " TRUNCATE");
}
! appendStringInfo(&buf, " ON %s ",
! generate_relation_name(trigrec->tgrelid, NIL));
if (trigrec->tgisconstraint)
{
if (trigrec->tgconstrrelid != InvalidOid)
! appendStringInfo(&buf, "FROM %s ",
! generate_relation_name(trigrec->tgconstrrelid,
! NIL));
if (!trigrec->tgdeferrable)
appendStringInfo(&buf, "NOT ");
appendStringInfo(&buf, "DEFERRABLE INITIALLY ");
if (trigrec->tginitdeferred)
! appendStringInfo(&buf, "DEFERRED ");
else
! appendStringInfo(&buf, "IMMEDIATE ");
!
}
if (TRIGGER_FOR_ROW(trigrec->tgtype))
! appendStringInfo(&buf, "FOR EACH ROW ");
else
! appendStringInfo(&buf, "FOR EACH STATEMENT ");
appendStringInfo(&buf, "EXECUTE PROCEDURE %s(",
generate_function_name(trigrec->tgfoid, 0, NULL, NULL));
--- 565,595 ----
else
appendStringInfo(&buf, " TRUNCATE");
}
! appendStringInfo(&buf, " ON %s%s",
! generate_relation_name(trigrec->tgrelid, NIL),
! (pretty ? "\n " : " "));
if (trigrec->tgisconstraint)
{
if (trigrec->tgconstrrelid != InvalidOid)
! appendStringInfo(&buf, "FROM %s%s",
! generate_relation_name(trigrec->tgconstrrelid, NIL),
! (pretty ? "\n " : " "));
if (!trigrec->tgdeferrable)
appendStringInfo(&buf, "NOT ");
appendStringInfo(&buf, "DEFERRABLE INITIALLY ");
if (trigrec->tginitdeferred)
! appendStringInfo(&buf, "DEFERRED");
else
! appendStringInfo(&buf, "IMMEDIATE");
! appendStringInfoString(&buf, pretty ? "\n " : " ");
}
if (TRIGGER_FOR_ROW(trigrec->tgtype))
! appendStringInfo(&buf, "FOR EACH ROW");
else
! appendStringInfo(&buf, "FOR EACH STATEMENT");
! appendStringInfoString(&buf, pretty ? "\n " : " ");
appendStringInfo(&buf, "EXECUTE PROCEDURE %s(",
generate_function_name(trigrec->tgfoid, 0, NULL, NULL));
*************** pg_get_triggerdef(PG_FUNCTION_ARGS)
*** 593,599 ****
heap_close(tgrel, AccessShareLock);
! PG_RETURN_TEXT_P(string_to_text(buf.data));
}
/* ----------
--- 627,633 ----
heap_close(tgrel, AccessShareLock);
! return buf.data;
}
/* ----------
diff -cprN head/src/bin/pg_dump/pg_dump.c standard-column-trigger/src/bin/pg_dump/pg_dump.c
*** head/src/bin/pg_dump/pg_dump.c 2009-09-12 04:17:04.000000000 +0900
--- standard-column-trigger/src/bin/pg_dump/pg_dump.c 2009-09-14 18:48:39.680720773 +0900
*************** getTriggers(TableInfo tblinfo[], int num
*** 4263,4269 ****
i_tgconstrrelname,
i_tgenabled,
i_tgdeferrable,
! i_tginitdeferred;
int ntups;
for (i = 0; i < numTables; i++)
--- 4263,4270 ----
i_tgconstrrelname,
i_tgenabled,
i_tgdeferrable,
! i_tginitdeferred,
! i_tgdef;
int ntups;
for (i = 0; i < numTables; i++)
*************** getTriggers(TableInfo tblinfo[], int num
*** 4283,4289 ****
selectSourceSchema(tbinfo->dobj.namespace->dobj.name);
resetPQExpBuffer(query);
! if (g_fout->remoteVersion >= 80300)
{
/*
* We ignore triggers that are tied to a foreign-key constraint
--- 4284,4302 ----
selectSourceSchema(tbinfo->dobj.namespace->dobj.name);
resetPQExpBuffer(query);
! if (g_fout->remoteVersion >= 80500)
! {
! appendPQExpBuffer(query,
! "SELECT tgname, "
! "tgfoid::pg_catalog.regproc AS tgfname, "
! "pg_catalog.pg_get_triggerdef(oid, true) AS tgdef, "
! "tgenabled, tableoid, oid "
! "FROM pg_catalog.pg_trigger t "
! "WHERE tgrelid = '%u'::pg_catalog.oid "
! "AND tgconstraint = 0",
! tbinfo->dobj.catId.oid);
! }
! else if (g_fout->remoteVersion >= 80300)
{
/*
* We ignore triggers that are tied to a foreign-key constraint
*************** getTriggers(TableInfo tblinfo[], int num
*** 4370,4375 ****
--- 4383,4389 ----
i_tgenabled = PQfnumber(res, "tgenabled");
i_tgdeferrable = PQfnumber(res, "tgdeferrable");
i_tginitdeferred = PQfnumber(res, "tginitdeferred");
+ i_tgdef = PQfnumber(res, "tgdef");
tginfo = (TriggerInfo *) malloc(ntups * sizeof(TriggerInfo));
*************** getTriggers(TableInfo tblinfo[], int num
*** 4382,4419 ****
tginfo[j].dobj.name = strdup(PQgetvalue(res, j, i_tgname));
tginfo[j].dobj.namespace = tbinfo->dobj.namespace;
tginfo[j].tgtable = tbinfo;
- tginfo[j].tgfname = strdup(PQgetvalue(res, j, i_tgfname));
- tginfo[j].tgtype = atoi(PQgetvalue(res, j, i_tgtype));
- tginfo[j].tgnargs = atoi(PQgetvalue(res, j, i_tgnargs));
- tginfo[j].tgargs = strdup(PQgetvalue(res, j, i_tgargs));
- tginfo[j].tgisconstraint = *(PQgetvalue(res, j, i_tgisconstraint)) == 't';
tginfo[j].tgenabled = *(PQgetvalue(res, j, i_tgenabled));
! tginfo[j].tgdeferrable = *(PQgetvalue(res, j, i_tgdeferrable)) == 't';
! tginfo[j].tginitdeferred = *(PQgetvalue(res, j, i_tginitdeferred)) == 't';
! if (tginfo[j].tgisconstraint)
! {
! tginfo[j].tgconstrname = strdup(PQgetvalue(res, j, i_tgconstrname));
! tginfo[j].tgconstrrelid = atooid(PQgetvalue(res, j, i_tgconstrrelid));
! if (OidIsValid(tginfo[j].tgconstrrelid))
{
! if (PQgetisnull(res, j, i_tgconstrrelname))
{
! write_msg(NULL, "query produced null referenced table name for foreign key trigger \"%s\" on table \"%s\" (OID of table: %u)\n",
! tginfo[j].dobj.name, tbinfo->dobj.name,
! tginfo[j].tgconstrrelid);
! exit_nicely();
}
! tginfo[j].tgconstrrelname = strdup(PQgetvalue(res, j, i_tgconstrrelname));
}
else
tginfo[j].tgconstrrelname = NULL;
! }
! else
! {
! tginfo[j].tgconstrname = NULL;
! tginfo[j].tgconstrrelid = InvalidOid;
! tginfo[j].tgconstrrelname = NULL;
}
}
--- 4396,4442 ----
tginfo[j].dobj.name = strdup(PQgetvalue(res, j, i_tgname));
tginfo[j].dobj.namespace = tbinfo->dobj.namespace;
tginfo[j].tgtable = tbinfo;
tginfo[j].tgenabled = *(PQgetvalue(res, j, i_tgenabled));
! if (i_tgdef >= 0)
! {
! tginfo[j].tgdef = strdup(PQgetvalue(res, j, i_tgdef));
! }
! else
! {
! tginfo[j].tgdef = NULL;
!
! tginfo[j].tgfname = strdup(PQgetvalue(res, j, i_tgfname));
! tginfo[j].tgtype = atoi(PQgetvalue(res, j, i_tgtype));
! tginfo[j].tgnargs = atoi(PQgetvalue(res, j, i_tgnargs));
! tginfo[j].tgargs = strdup(PQgetvalue(res, j, i_tgargs));
! tginfo[j].tgisconstraint = *(PQgetvalue(res, j, i_tgisconstraint)) == 't';
! tginfo[j].tgdeferrable = *(PQgetvalue(res, j, i_tgdeferrable)) == 't';
! tginfo[j].tginitdeferred = *(PQgetvalue(res, j, i_tginitdeferred)) == 't';
! if (tginfo[j].tgisconstraint)
{
! tginfo[j].tgconstrname = strdup(PQgetvalue(res, j, i_tgconstrname));
! tginfo[j].tgconstrrelid = atooid(PQgetvalue(res, j, i_tgconstrrelid));
! if (OidIsValid(tginfo[j].tgconstrrelid))
{
! if (PQgetisnull(res, j, i_tgconstrrelname))
! {
! write_msg(NULL, "query produced null referenced table name for foreign key trigger \"%s\" on table \"%s\" (OID of table: %u)\n",
! tginfo[j].dobj.name, tbinfo->dobj.name,
! tginfo[j].tgconstrrelid);
! exit_nicely();
! }
! tginfo[j].tgconstrrelname = strdup(PQgetvalue(res, j, i_tgconstrrelname));
}
! else
! tginfo[j].tgconstrrelname = NULL;
}
else
+ {
+ tginfo[j].tgconstrname = NULL;
+ tginfo[j].tgconstrrelid = InvalidOid;
tginfo[j].tgconstrrelname = NULL;
! }
}
}
*************** dumpTrigger(Archive *fout, TriggerInfo *
*** 11031,11143 ****
appendPQExpBuffer(delqry, "%s;\n",
fmtId(tbinfo->dobj.name));
! if (tginfo->tgisconstraint)
{
! appendPQExpBuffer(query, "CREATE CONSTRAINT TRIGGER ");
! appendPQExpBufferStr(query, fmtId(tginfo->tgconstrname));
}
else
{
! appendPQExpBuffer(query, "CREATE TRIGGER ");
! appendPQExpBufferStr(query, fmtId(tginfo->dobj.name));
! }
! appendPQExpBuffer(query, "\n ");
!
! /* Trigger type */
! findx = 0;
! if (TRIGGER_FOR_BEFORE(tginfo->tgtype))
! appendPQExpBuffer(query, "BEFORE");
! else
! appendPQExpBuffer(query, "AFTER");
! if (TRIGGER_FOR_INSERT(tginfo->tgtype))
! {
! appendPQExpBuffer(query, " INSERT");
! findx++;
! }
! if (TRIGGER_FOR_DELETE(tginfo->tgtype))
! {
! if (findx > 0)
! appendPQExpBuffer(query, " OR DELETE");
! else
! appendPQExpBuffer(query, " DELETE");
! findx++;
! }
! if (TRIGGER_FOR_UPDATE(tginfo->tgtype))
! {
! if (findx > 0)
! appendPQExpBuffer(query, " OR UPDATE");
else
! appendPQExpBuffer(query, " UPDATE");
! }
! if (TRIGGER_FOR_TRUNCATE(tginfo->tgtype))
! {
! if (findx > 0)
! appendPQExpBuffer(query, " OR TRUNCATE");
else
! appendPQExpBuffer(query, " TRUNCATE");
! }
! appendPQExpBuffer(query, " ON %s\n",
! fmtId(tbinfo->dobj.name));
! if (tginfo->tgisconstraint)
! {
! if (OidIsValid(tginfo->tgconstrrelid))
{
! /* If we are using regclass, name is already quoted */
! if (g_fout->remoteVersion >= 70300)
! appendPQExpBuffer(query, " FROM %s\n ",
! tginfo->tgconstrrelname);
else
! appendPQExpBuffer(query, " FROM %s\n ",
! fmtId(tginfo->tgconstrrelname));
}
! if (!tginfo->tgdeferrable)
! appendPQExpBuffer(query, "NOT ");
! appendPQExpBuffer(query, "DEFERRABLE INITIALLY ");
! if (tginfo->tginitdeferred)
! appendPQExpBuffer(query, "DEFERRED\n");
else
! appendPQExpBuffer(query, "IMMEDIATE\n");
! }
! if (TRIGGER_FOR_ROW(tginfo->tgtype))
! appendPQExpBuffer(query, " FOR EACH ROW\n ");
! else
! appendPQExpBuffer(query, " FOR EACH STATEMENT\n ");
! /* In 7.3, result of regproc is already quoted */
! if (g_fout->remoteVersion >= 70300)
! appendPQExpBuffer(query, "EXECUTE PROCEDURE %s(",
! tginfo->tgfname);
! else
! appendPQExpBuffer(query, "EXECUTE PROCEDURE %s(",
! fmtId(tginfo->tgfname));
! tgargs = (char *) PQunescapeBytea((unsigned char *) tginfo->tgargs,
! &lentgargs);
! p = tgargs;
! for (findx = 0; findx < tginfo->tgnargs; findx++)
! {
! /* find the embedded null that terminates this trigger argument */
! size_t tlen = strlen(p);
!
! if (p + tlen >= tgargs + lentgargs)
! {
! /* hm, not found before end of bytea value... */
! write_msg(NULL, "invalid argument string (%s) for trigger \"%s\" on table \"%s\"\n",
! tginfo->tgargs,
! tginfo->dobj.name,
! tbinfo->dobj.name);
! exit_nicely();
! }
! if (findx > 0)
! appendPQExpBuffer(query, ", ");
! appendStringLiteralAH(query, p, fout);
! p += tlen + 1;
}
- free(tgargs);
- appendPQExpBuffer(query, ");\n");
if (tginfo->tgenabled != 't' && tginfo->tgenabled != 'O')
{
--- 11054,11173 ----
appendPQExpBuffer(delqry, "%s;\n",
fmtId(tbinfo->dobj.name));
! if (tginfo->tgdef)
{
! appendPQExpBuffer(query, "%s;\n", tginfo->tgdef);
}
else
{
! if (tginfo->tgisconstraint)
! {
! appendPQExpBuffer(query, "CREATE CONSTRAINT TRIGGER ");
! appendPQExpBufferStr(query, fmtId(tginfo->tgconstrname));
! }
else
! {
! appendPQExpBuffer(query, "CREATE TRIGGER ");
! appendPQExpBufferStr(query, fmtId(tginfo->dobj.name));
! }
! appendPQExpBuffer(query, "\n ");
!
! /* Trigger type */
! findx = 0;
! if (TRIGGER_FOR_BEFORE(tginfo->tgtype))
! appendPQExpBuffer(query, "BEFORE");
else
! appendPQExpBuffer(query, "AFTER");
! if (TRIGGER_FOR_INSERT(tginfo->tgtype))
! {
! appendPQExpBuffer(query, " INSERT");
! findx++;
! }
! if (TRIGGER_FOR_DELETE(tginfo->tgtype))
! {
! if (findx > 0)
! appendPQExpBuffer(query, " OR DELETE");
! else
! appendPQExpBuffer(query, " DELETE");
! findx++;
! }
! if (TRIGGER_FOR_UPDATE(tginfo->tgtype))
! {
! if (findx > 0)
! appendPQExpBuffer(query, " OR UPDATE");
! else
! appendPQExpBuffer(query, " UPDATE");
! }
! if (TRIGGER_FOR_TRUNCATE(tginfo->tgtype))
! {
! if (findx > 0)
! appendPQExpBuffer(query, " OR TRUNCATE");
! else
! appendPQExpBuffer(query, " TRUNCATE");
! }
! appendPQExpBuffer(query, " ON %s\n",
! fmtId(tbinfo->dobj.name));
! if (tginfo->tgisconstraint)
{
! if (OidIsValid(tginfo->tgconstrrelid))
! {
! /* If we are using regclass, name is already quoted */
! if (g_fout->remoteVersion >= 70300)
! appendPQExpBuffer(query, " FROM %s\n ",
! tginfo->tgconstrrelname);
! else
! appendPQExpBuffer(query, " FROM %s\n ",
! fmtId(tginfo->tgconstrrelname));
! }
! if (!tginfo->tgdeferrable)
! appendPQExpBuffer(query, "NOT ");
! appendPQExpBuffer(query, "DEFERRABLE INITIALLY ");
! if (tginfo->tginitdeferred)
! appendPQExpBuffer(query, "DEFERRED\n");
else
! appendPQExpBuffer(query, "IMMEDIATE\n");
}
!
! if (TRIGGER_FOR_ROW(tginfo->tgtype))
! appendPQExpBuffer(query, " FOR EACH ROW\n ");
else
! appendPQExpBuffer(query, " FOR EACH STATEMENT\n ");
! /* In 7.3, result of regproc is already quoted */
! if (g_fout->remoteVersion >= 70300)
! appendPQExpBuffer(query, "EXECUTE PROCEDURE %s(",
! tginfo->tgfname);
! else
! appendPQExpBuffer(query, "EXECUTE PROCEDURE %s(",
! fmtId(tginfo->tgfname));
! tgargs = (char *) PQunescapeBytea((unsigned char *) tginfo->tgargs,
! &lentgargs);
! p = tgargs;
! for (findx = 0; findx < tginfo->tgnargs; findx++)
! {
! /* find the embedded null that terminates this trigger argument */
! size_t tlen = strlen(p);
! if (p + tlen >= tgargs + lentgargs)
! {
! /* hm, not found before end of bytea value... */
! write_msg(NULL, "invalid argument string (%s) for trigger \"%s\" on table \"%s\"\n",
! tginfo->tgargs,
! tginfo->dobj.name,
! tbinfo->dobj.name);
! exit_nicely();
! }
! if (findx > 0)
! appendPQExpBuffer(query, ", ");
! appendStringLiteralAH(query, p, fout);
! p += tlen + 1;
! }
! free(tgargs);
! appendPQExpBuffer(query, ");\n");
}
if (tginfo->tgenabled != 't' && tginfo->tgenabled != 'O')
{
diff -cprN head/src/bin/pg_dump/pg_dump.h standard-column-trigger/src/bin/pg_dump/pg_dump.h
*** head/src/bin/pg_dump/pg_dump.h 2009-08-03 07:14:52.000000000 +0900
--- standard-column-trigger/src/bin/pg_dump/pg_dump.h 2009-09-14 18:38:58.859946528 +0900
*************** typedef struct _triggerInfo
*** 327,332 ****
--- 327,333 ----
char tgenabled;
bool tgdeferrable;
bool tginitdeferred;
+ char *tgdef;
} TriggerInfo;
/*
diff -cprN head/src/include/catalog/pg_proc.h standard-column-trigger/src/include/catalog/pg_proc.h
*** head/src/include/catalog/pg_proc.h 2009-09-01 11:54:52.000000000 +0900
--- standard-column-trigger/src/include/catalog/pg_proc.h 2009-09-14 17:27:45.399743291 +0900
*************** DATA(insert OID = 2599 ( pg_timezone_ab
*** 4082,4087 ****
--- 4082,4089 ----
DESCR("get the available time zone abbreviations");
DATA(insert OID = 2856 ( pg_timezone_names PGNSP PGUID 12 1 1000 0 f f f t t s 0 0 2249 "" "{25,25,1186,16}" "{o,o,o,o}" "{name,abbrev,utc_offset,is_dst}" _null_ pg_timezone_names _null_ _null_ _null_ ));
DESCR("get the available time zone names");
+ DATA(insert OID = 2730 ( pg_get_triggerdef PGNSP PGUID 12 1 0 0 f f f t f s 2 0 25 "26 16" _null_ _null_ _null_ _null_ pg_get_triggerdef_ext _null_ _null_ _null_ ));
+ DESCR("trigger description with pretty-print option");
/* non-persistent series generator */
DATA(insert OID = 1066 ( generate_series PGNSP PGUID 12 1 1000 0 f f f t t i 3 0 23 "23 23 23" _null_ _null_ _null_ _null_ generate_series_step_int4 _null_ _null_ _null_ ));
diff -cprN head/src/include/catalog/pg_trigger.h standard-column-trigger/src/include/catalog/pg_trigger.h
*** head/src/include/catalog/pg_trigger.h 2009-07-28 11:56:31.000000000 +0900
--- standard-column-trigger/src/include/catalog/pg_trigger.h 2009-09-14 17:23:24.277745679 +0900
*************** CATALOG(pg_trigger,2620)
*** 53,59 ****
int2 tgnargs; /* # of extra arguments in tgargs */
/* VARIABLE LENGTH FIELDS: */
! int2vector tgattr; /* reserved for column-specific triggers */
bytea tgargs; /* first\000second\000tgnargs\000 */
} FormData_pg_trigger;
--- 53,59 ----
int2 tgnargs; /* # of extra arguments in tgargs */
/* VARIABLE LENGTH FIELDS: */
! int2vector tgattr; /* column-specific triggers */
bytea tgargs; /* first\000second\000tgnargs\000 */
} FormData_pg_trigger;
diff -cprN head/src/include/nodes/parsenodes.h standard-column-trigger/src/include/nodes/parsenodes.h
*** head/src/include/nodes/parsenodes.h 2009-08-03 07:14:53.000000000 +0900
--- standard-column-trigger/src/include/nodes/parsenodes.h 2009-09-14 17:23:24.277745679 +0900
*************** typedef struct CreateTrigStmt
*** 1553,1558 ****
--- 1553,1559 ----
bool row; /* ROW/STATEMENT */
/* events uses the TRIGGER_TYPE bits defined in catalog/pg_trigger.h */
int16 events; /* INSERT/UPDATE/DELETE/TRUNCATE */
+ List *columns; /* column names, or NIL for all columns */
/* The following are used for constraint triggers (RI and unique checks) */
bool isconstraint; /* This is a constraint trigger */
diff -cprN head/src/include/utils/builtins.h standard-column-trigger/src/include/utils/builtins.h
*** head/src/include/utils/builtins.h 2009-09-10 04:00:09.000000000 +0900
--- standard-column-trigger/src/include/utils/builtins.h 2009-09-14 17:24:52.149745975 +0900
*************** extern Datum pg_get_indexdef_ext(PG_FUNC
*** 590,595 ****
--- 590,596 ----
extern char *pg_get_indexdef_string(Oid indexrelid);
extern char *pg_get_indexdef_columns(Oid indexrelid, bool pretty);
extern Datum pg_get_triggerdef(PG_FUNCTION_ARGS);
+ extern Datum pg_get_triggerdef_ext(PG_FUNCTION_ARGS);
extern Datum pg_get_constraintdef(PG_FUNCTION_ARGS);
extern Datum pg_get_constraintdef_ext(PG_FUNCTION_ARGS);
extern char *pg_get_constraintdef_string(Oid constraintId);
diff -cprN head/src/test/regress/expected/triggers.out standard-column-trigger/src/test/regress/expected/triggers.out
*** head/src/test/regress/expected/triggers.out 2008-11-06 03:49:28.000000000 +0900
--- standard-column-trigger/src/test/regress/expected/triggers.out 2009-09-14 18:00:12.683748455 +0900
*************** CREATE TABLE main_table (a int, b int);
*** 278,314 ****
COPY main_table (a,b) FROM stdin;
CREATE FUNCTION trigger_func() RETURNS trigger LANGUAGE plpgsql AS '
BEGIN
! RAISE NOTICE ''trigger_func() called: action = %, when = %, level = %'', TG_OP, TG_WHEN, TG_LEVEL;
RETURN NULL;
END;';
CREATE TRIGGER before_ins_stmt_trig BEFORE INSERT ON main_table
! FOR EACH STATEMENT EXECUTE PROCEDURE trigger_func();
CREATE TRIGGER after_ins_stmt_trig AFTER INSERT ON main_table
! FOR EACH STATEMENT EXECUTE PROCEDURE trigger_func();
--
-- if neither 'FOR EACH ROW' nor 'FOR EACH STATEMENT' was specified,
-- CREATE TRIGGER should default to 'FOR EACH STATEMENT'
--
! CREATE TRIGGER before_upd_stmt_trig AFTER UPDATE ON main_table
! EXECUTE PROCEDURE trigger_func();
! CREATE TRIGGER before_upd_row_trig AFTER UPDATE ON main_table
! FOR EACH ROW EXECUTE PROCEDURE trigger_func();
INSERT INTO main_table DEFAULT VALUES;
! NOTICE: trigger_func() called: action = INSERT, when = BEFORE, level = STATEMENT
! NOTICE: trigger_func() called: action = INSERT, when = AFTER, level = STATEMENT
UPDATE main_table SET a = a + 1 WHERE b < 30;
! NOTICE: trigger_func() called: action = UPDATE, when = AFTER, level = ROW
! NOTICE: trigger_func() called: action = UPDATE, when = AFTER, level = ROW
! NOTICE: trigger_func() called: action = UPDATE, when = AFTER, level = ROW
! NOTICE: trigger_func() called: action = UPDATE, when = AFTER, level = ROW
! NOTICE: trigger_func() called: action = UPDATE, when = AFTER, level = STATEMENT
-- UPDATE that effects zero rows should still call per-statement trigger
UPDATE main_table SET a = a + 2 WHERE b > 100;
! NOTICE: trigger_func() called: action = UPDATE, when = AFTER, level = STATEMENT
-- COPY should fire per-row and per-statement INSERT triggers
COPY main_table (a, b) FROM stdin;
! NOTICE: trigger_func() called: action = INSERT, when = BEFORE, level = STATEMENT
! NOTICE: trigger_func() called: action = INSERT, when = AFTER, level = STATEMENT
SELECT * FROM main_table ORDER BY a, b;
a | b
----+----
--- 278,314 ----
COPY main_table (a,b) FROM stdin;
CREATE FUNCTION trigger_func() RETURNS trigger LANGUAGE plpgsql AS '
BEGIN
! RAISE NOTICE ''trigger_func(%) called: action = %, when = %, level = %'', TG_ARGV[0], TG_OP, TG_WHEN, TG_LEVEL;
RETURN NULL;
END;';
CREATE TRIGGER before_ins_stmt_trig BEFORE INSERT ON main_table
! FOR EACH STATEMENT EXECUTE PROCEDURE trigger_func('before_ins_stmt');
CREATE TRIGGER after_ins_stmt_trig AFTER INSERT ON main_table
! FOR EACH STATEMENT EXECUTE PROCEDURE trigger_func('after_ins_stmt');
--
-- if neither 'FOR EACH ROW' nor 'FOR EACH STATEMENT' was specified,
-- CREATE TRIGGER should default to 'FOR EACH STATEMENT'
--
! CREATE TRIGGER after_upd_stmt_trig AFTER UPDATE ON main_table
! EXECUTE PROCEDURE trigger_func('after_upd_stmt');
! CREATE TRIGGER after_upd_row_trig AFTER UPDATE ON main_table
! FOR EACH ROW EXECUTE PROCEDURE trigger_func('after_upd_row');
INSERT INTO main_table DEFAULT VALUES;
! NOTICE: trigger_func(before_ins_stmt) called: action = INSERT, when = BEFORE, level = STATEMENT
! NOTICE: trigger_func(after_ins_stmt) called: action = INSERT, when = AFTER, level = STATEMENT
UPDATE main_table SET a = a + 1 WHERE b < 30;
! NOTICE: trigger_func(after_upd_row) called: action = UPDATE, when = AFTER, level = ROW
! NOTICE: trigger_func(after_upd_row) called: action = UPDATE, when = AFTER, level = ROW
! NOTICE: trigger_func(after_upd_row) called: action = UPDATE, when = AFTER, level = ROW
! NOTICE: trigger_func(after_upd_row) called: action = UPDATE, when = AFTER, level = ROW
! NOTICE: trigger_func(after_upd_stmt) called: action = UPDATE, when = AFTER, level = STATEMENT
-- UPDATE that effects zero rows should still call per-statement trigger
UPDATE main_table SET a = a + 2 WHERE b > 100;
! NOTICE: trigger_func(after_upd_stmt) called: action = UPDATE, when = AFTER, level = STATEMENT
-- COPY should fire per-row and per-statement INSERT triggers
COPY main_table (a, b) FROM stdin;
! NOTICE: trigger_func(before_ins_stmt) called: action = INSERT, when = BEFORE, level = STATEMENT
! NOTICE: trigger_func(after_ins_stmt) called: action = INSERT, when = AFTER, level = STATEMENT
SELECT * FROM main_table ORDER BY a, b;
a | b
----+----
*************** SELECT * FROM main_table ORDER BY a, b;
*** 322,327 ****
--- 322,394 ----
|
(8 rows)
+ -- Column-level triggers should only fire on after row-level updates
+ DROP TRIGGER after_upd_row_trig ON main_table;
+ CREATE TRIGGER before_upd_a_row_trig BEFORE UPDATE OF a ON main_table
+ FOR EACH ROW EXECUTE PROCEDURE trigger_func('before_upd_a_row');
+ CREATE TRIGGER after_upd_b_row_trig AFTER UPDATE OF b ON main_table
+ FOR EACH ROW EXECUTE PROCEDURE trigger_func('after_upd_b_row');
+ CREATE TRIGGER after_upd_a_b_row_trig AFTER UPDATE OF a, b ON main_table
+ FOR EACH ROW EXECUTE PROCEDURE trigger_func('after_upd_a_b_row');
+ SELECT pg_get_triggerdef(oid) FROM pg_trigger WHERE tgrelid = 'main_table'::regclass AND tgname = 'after_upd_a_b_row_trig';
+ pg_get_triggerdef
+ -------------------------------------------------------------------------------------------------------------------------------------------
+ CREATE TRIGGER after_upd_a_b_row_trig AFTER UPDATE OF a, b ON main_table FOR EACH ROW EXECUTE PROCEDURE trigger_func('after_upd_a_b_row')
+ (1 row)
+
+ SELECT pg_get_triggerdef(oid, true) FROM pg_trigger WHERE tgrelid = 'main_table'::regclass AND tgname = 'after_upd_a_b_row_trig';
+ pg_get_triggerdef
+ ---------------------------------------------------------
+ CREATE TRIGGER after_upd_a_b_row_trig
+ AFTER UPDATE OF a, b ON main_table
+ FOR EACH ROW
+ EXECUTE PROCEDURE trigger_func('after_upd_a_b_row')
+ (1 row)
+
+ UPDATE main_table SET a = 50;
+ NOTICE: trigger_func(before_upd_a_row) called: action = UPDATE, when = BEFORE, level = ROW
+ NOTICE: trigger_func(before_upd_a_row) called: action = UPDATE, when = BEFORE, level = ROW
+ NOTICE: trigger_func(before_upd_a_row) called: action = UPDATE, when = BEFORE, level = ROW
+ NOTICE: trigger_func(before_upd_a_row) called: action = UPDATE, when = BEFORE, level = ROW
+ NOTICE: trigger_func(before_upd_a_row) called: action = UPDATE, when = BEFORE, level = ROW
+ NOTICE: trigger_func(before_upd_a_row) called: action = UPDATE, when = BEFORE, level = ROW
+ NOTICE: trigger_func(before_upd_a_row) called: action = UPDATE, when = BEFORE, level = ROW
+ NOTICE: trigger_func(before_upd_a_row) called: action = UPDATE, when = BEFORE, level = ROW
+ NOTICE: trigger_func(after_upd_stmt) called: action = UPDATE, when = AFTER, level = STATEMENT
+ UPDATE main_table SET b = 10;
+ NOTICE: trigger_func(after_upd_a_b_row) called: action = UPDATE, when = AFTER, level = ROW
+ NOTICE: trigger_func(after_upd_b_row) called: action = UPDATE, when = AFTER, level = ROW
+ NOTICE: trigger_func(after_upd_a_b_row) called: action = UPDATE, when = AFTER, level = ROW
+ NOTICE: trigger_func(after_upd_b_row) called: action = UPDATE, when = AFTER, level = ROW
+ NOTICE: trigger_func(after_upd_a_b_row) called: action = UPDATE, when = AFTER, level = ROW
+ NOTICE: trigger_func(after_upd_b_row) called: action = UPDATE, when = AFTER, level = ROW
+ NOTICE: trigger_func(after_upd_a_b_row) called: action = UPDATE, when = AFTER, level = ROW
+ NOTICE: trigger_func(after_upd_b_row) called: action = UPDATE, when = AFTER, level = ROW
+ NOTICE: trigger_func(after_upd_a_b_row) called: action = UPDATE, when = AFTER, level = ROW
+ NOTICE: trigger_func(after_upd_b_row) called: action = UPDATE, when = AFTER, level = ROW
+ NOTICE: trigger_func(after_upd_a_b_row) called: action = UPDATE, when = AFTER, level = ROW
+ NOTICE: trigger_func(after_upd_b_row) called: action = UPDATE, when = AFTER, level = ROW
+ NOTICE: trigger_func(after_upd_a_b_row) called: action = UPDATE, when = AFTER, level = ROW
+ NOTICE: trigger_func(after_upd_b_row) called: action = UPDATE, when = AFTER, level = ROW
+ NOTICE: trigger_func(after_upd_a_b_row) called: action = UPDATE, when = AFTER, level = ROW
+ NOTICE: trigger_func(after_upd_b_row) called: action = UPDATE, when = AFTER, level = ROW
+ NOTICE: trigger_func(after_upd_stmt) called: action = UPDATE, when = AFTER, level = STATEMENT
+ CREATE TRIGGER error_upd_and_col BEFORE UPDATE OR UPDATE OF a ON main_table
+ FOR EACH ROW EXECUTE PROCEDURE trigger_func('error_upd_and_col');
+ ERROR: duplicate trigger events specified at or near "ON"
+ LINE 1: ...ER error_upd_and_col BEFORE UPDATE OR UPDATE OF a ON main_ta...
+ ^
+ CREATE TRIGGER error_upd_a_a BEFORE UPDATE OF a, a ON main_table
+ FOR EACH ROW EXECUTE PROCEDURE trigger_func('error_upd_a_a');
+ ERROR: column "a" specified more than once
+ ALTER TABLE main_table DROP COLUMN b;
+ ERROR: cannot drop table main_table column b because other objects depend on it
+ DETAIL: trigger after_upd_b_row_trig on table main_table depends on table main_table column b
+ trigger after_upd_a_b_row_trig on table main_table depends on table main_table column b
+ HINT: Use DROP ... CASCADE to drop the dependent objects too.
+ DROP TRIGGER after_upd_a_b_row_trig ON main_table;
+ DROP TRIGGER after_upd_b_row_trig ON main_table;
+ ALTER TABLE main_table DROP COLUMN b;
-- Test enable/disable triggers
create table trigtest (i serial primary key);
NOTICE: CREATE TABLE will create implicit sequence "trigtest_i_seq" for serial column "trigtest.i"
diff -cprN head/src/test/regress/sql/triggers.sql standard-column-trigger/src/test/regress/sql/triggers.sql
*** head/src/test/regress/sql/triggers.sql 2008-11-06 03:49:28.000000000 +0900
--- standard-column-trigger/src/test/regress/sql/triggers.sql 2009-09-14 17:41:40.577757046 +0900
*************** COPY main_table (a,b) FROM stdin;
*** 220,244 ****
CREATE FUNCTION trigger_func() RETURNS trigger LANGUAGE plpgsql AS '
BEGIN
! RAISE NOTICE ''trigger_func() called: action = %, when = %, level = %'', TG_OP, TG_WHEN, TG_LEVEL;
RETURN NULL;
END;';
CREATE TRIGGER before_ins_stmt_trig BEFORE INSERT ON main_table
! FOR EACH STATEMENT EXECUTE PROCEDURE trigger_func();
CREATE TRIGGER after_ins_stmt_trig AFTER INSERT ON main_table
! FOR EACH STATEMENT EXECUTE PROCEDURE trigger_func();
--
-- if neither 'FOR EACH ROW' nor 'FOR EACH STATEMENT' was specified,
-- CREATE TRIGGER should default to 'FOR EACH STATEMENT'
--
! CREATE TRIGGER before_upd_stmt_trig AFTER UPDATE ON main_table
! EXECUTE PROCEDURE trigger_func();
! CREATE TRIGGER before_upd_row_trig AFTER UPDATE ON main_table
! FOR EACH ROW EXECUTE PROCEDURE trigger_func();
INSERT INTO main_table DEFAULT VALUES;
--- 220,244 ----
CREATE FUNCTION trigger_func() RETURNS trigger LANGUAGE plpgsql AS '
BEGIN
! RAISE NOTICE ''trigger_func(%) called: action = %, when = %, level = %'', TG_ARGV[0], TG_OP, TG_WHEN, TG_LEVEL;
RETURN NULL;
END;';
CREATE TRIGGER before_ins_stmt_trig BEFORE INSERT ON main_table
! FOR EACH STATEMENT EXECUTE PROCEDURE trigger_func('before_ins_stmt');
CREATE TRIGGER after_ins_stmt_trig AFTER INSERT ON main_table
! FOR EACH STATEMENT EXECUTE PROCEDURE trigger_func('after_ins_stmt');
--
-- if neither 'FOR EACH ROW' nor 'FOR EACH STATEMENT' was specified,
-- CREATE TRIGGER should default to 'FOR EACH STATEMENT'
--
! CREATE TRIGGER after_upd_stmt_trig AFTER UPDATE ON main_table
! EXECUTE PROCEDURE trigger_func('after_upd_stmt');
! CREATE TRIGGER after_upd_row_trig AFTER UPDATE ON main_table
! FOR EACH ROW EXECUTE PROCEDURE trigger_func('after_upd_row');
INSERT INTO main_table DEFAULT VALUES;
*************** COPY main_table (a, b) FROM stdin;
*** 254,259 ****
--- 254,285 ----
SELECT * FROM main_table ORDER BY a, b;
+ -- Column-level triggers should only fire on after row-level updates
+ DROP TRIGGER after_upd_row_trig ON main_table;
+
+ CREATE TRIGGER before_upd_a_row_trig BEFORE UPDATE OF a ON main_table
+ FOR EACH ROW EXECUTE PROCEDURE trigger_func('before_upd_a_row');
+ CREATE TRIGGER after_upd_b_row_trig AFTER UPDATE OF b ON main_table
+ FOR EACH ROW EXECUTE PROCEDURE trigger_func('after_upd_b_row');
+ CREATE TRIGGER after_upd_a_b_row_trig AFTER UPDATE OF a, b ON main_table
+ FOR EACH ROW EXECUTE PROCEDURE trigger_func('after_upd_a_b_row');
+
+ SELECT pg_get_triggerdef(oid) FROM pg_trigger WHERE tgrelid = 'main_table'::regclass AND tgname = 'after_upd_a_b_row_trig';
+ SELECT pg_get_triggerdef(oid, true) FROM pg_trigger WHERE tgrelid = 'main_table'::regclass AND tgname = 'after_upd_a_b_row_trig';
+
+ UPDATE main_table SET a = 50;
+ UPDATE main_table SET b = 10;
+
+ CREATE TRIGGER error_upd_and_col BEFORE UPDATE OR UPDATE OF a ON main_table
+ FOR EACH ROW EXECUTE PROCEDURE trigger_func('error_upd_and_col');
+ CREATE TRIGGER error_upd_a_a BEFORE UPDATE OF a, a ON main_table
+ FOR EACH ROW EXECUTE PROCEDURE trigger_func('error_upd_a_a');
+
+ ALTER TABLE main_table DROP COLUMN b;
+ DROP TRIGGER after_upd_a_b_row_trig ON main_table;
+ DROP TRIGGER after_upd_b_row_trig ON main_table;
+ ALTER TABLE main_table DROP COLUMN b;
+
-- Test enable/disable triggers
create table trigtest (i serial primary key);