-<!-- $PostgreSQL: pgsql/doc/src/sgml/catalogs.sgml,v 2.217 2010/01/10 01:23:08 rhaas Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/catalogs.sgml,v 2.218 2010/01/17 22:56:21 tgl Exp $ -->
<!--
Documentation of the system catalogs, directed toward PostgreSQL developers
-->
catalog, not here.
</para>
+ <para>
+ User-defined constraint triggers (created with <command>CREATE CONSTRAINT
+ TRIGGER</>) also give rise to an entry in this table.
+ </para>
+
<para>
Check constraints on domains are stored here, too.
</para>
<literal>f</> = foreign key constraint,
<literal>p</> = primary key constraint,
<literal>u</> = unique constraint,
+ <literal>t</> = constraint trigger,
<literal>x</> = exclusion constraint
</entry>
</row>
<entry><structfield>conkey</structfield></entry>
<entry><type>int2[]</type></entry>
<entry><literal><link linkend="catalog-pg-attribute"><structname>pg_attribute</structname></link>.attnum</></entry>
- <entry>If a table constraint (including a foreign key), list of the constrained columns</entry>
+ <entry>If a table constraint (including foreign keys, but not constraint
+ triggers), list of the constrained columns</entry>
</row>
<row>
</row>
<row>
- <entry><structfield>tgisconstraint</structfield></entry>
+ <entry><structfield>tgisinternal</structfield></entry>
<entry><type>bool</type></entry>
<entry></entry>
- <entry>True if trigger is a <quote>constraint trigger</></entry>
- </row>
-
- <row>
- <entry><structfield>tgconstrname</structfield></entry>
- <entry><type>name</type></entry>
- <entry></entry>
- <entry>Constraint name, if a constraint trigger</entry>
+ <entry>True if trigger is internally generated (usually, to enforce
+ the constraint identified by <structfield>tgconstraint</>)</entry>
</row>
<row>
<entry><structfield>tgconstraint</structfield></entry>
<entry><type>oid</type></entry>
<entry><literal><link linkend="catalog-pg-constraint"><structname>pg_constraint</structname></link>.oid</literal></entry>
- <entry>The <structname>pg_constraint</> entry owning the trigger, if any</entry>
+ <entry>The <structname>pg_constraint</> entry associated with the trigger, if any</entry>
</row>
<row>
<note>
<para>
When <structfield>tgconstraint</> is nonzero,
- <structfield>tgisconstraint</> must be true, and
- <structfield>tgconstrname</>, <structfield>tgconstrrelid</>,
- <structfield>tgconstrindid</>,
- <structfield>tgdeferrable</>, <structfield>tginitdeferred</> are redundant
- with the referenced <structname>pg_constraint</> entry. The reason we
- keep these fields is that we support <quote>stand-alone</> constraint
- triggers with no corresponding <structname>pg_constraint</> entry.
+ <structfield>tgconstrrelid</>, <structfield>tgconstrindid</>,
+ <structfield>tgdeferrable</>, and <structfield>tginitdeferred</> are
+ largely redundant with the referenced <structname>pg_constraint</> entry.
+ However, it is possible for a non-deferrable trigger to be associated
+ with a deferrable constraint: foreign key constraints can have some
+ deferrable and some non-deferrable triggers.
</para>
</note>
-<!-- $PostgreSQL: pgsql/doc/src/sgml/trigger.sgml,v 1.61 2009/11/23 21:41:20 petere Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/trigger.sgml,v 1.62 2010/01/17 22:56:21 tgl Exp $ -->
<chapter id="triggers">
<title>Triggers</title>
Oid tgfoid;
int16 tgtype;
bool tgenabled;
- bool tgisconstraint;
+ bool tgisinternal;
Oid tgconstrrelid;
Oid tgconstrindid;
Oid tgconstraint;
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/catalog/index.c,v 1.329 2010/01/06 03:03:58 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/catalog/index.c,v 1.330 2010/01/17 22:56:21 tgl Exp $
*
*
* INTERFACE ROUTINES
-1);
trigger = makeNode(CreateTrigStmt);
- trigger->trigname = pstrdup(indexRelationName);
+ trigger->trigname = (isprimary ? "PK_ConstraintTrigger" :
+ "Unique_ConstraintTrigger");
trigger->relation = heapRel;
trigger->funcname = SystemFuncName("unique_key_recheck");
trigger->args = NIL;
trigger->constrrel = NULL;
(void) CreateTrigger(trigger, NULL, conOid, indexRelationId,
- isprimary ? "PK_ConstraintTrigger" :
- "Unique_ConstraintTrigger",
- false);
+ true);
}
}
else
*
* Copyright (c) 2003-2010, PostgreSQL Global Development Group
*
- * $PostgreSQL: pgsql/src/backend/catalog/information_schema.sql,v 1.63 2010/01/02 16:57:36 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/catalog/information_schema.sql,v 1.64 2010/01/17 22:56:21 tgl Exp $
*/
/*
WHERE nc.oid = c.connamespace AND nr.oid = r.relnamespace
AND c.conrelid = r.oid
- AND c.contype <> 'x' -- ignore nonstandard exclusion constraints
+ AND c.contype NOT IN ('t', 'x') -- ignore nonstandard constraints
AND r.relkind = 'r'
AND (NOT pg_is_other_temp_schema(nr.oid))
AND (pg_has_role(r.relowner, 'USAGE')
AND c.oid = t.tgrelid
AND t.oid = ta.tgoid
AND (a.attrelid, a.attnum) = (t.tgrelid, ta.tgattnum)
- AND NOT t.tgisconstraint
+ AND NOT t.tgisinternal
AND (NOT pg_is_other_temp_schema(n.oid))
AND (pg_has_role(c.relowner, 'USAGE')
-- SELECT privilege omitted, per SQL standard
WHERE n.oid = c.relnamespace
AND c.oid = t.tgrelid
AND t.tgtype & em.num <> 0
- AND NOT t.tgisconstraint
+ AND NOT t.tgisinternal
AND (NOT pg_is_other_temp_schema(n.oid))
AND (pg_has_role(c.relowner, 'USAGE')
-- SELECT privilege omitted, per SQL standard
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.315 2010/01/15 09:19:01 heikki Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.316 2010/01/17 22:56:21 tgl Exp $
*
*-------------------------------------------------------------------------
*/
trig.tgoid = InvalidOid;
trig.tgname = fkconstraint->conname;
trig.tgenabled = TRIGGER_FIRES_ON_ORIGIN;
- trig.tgisconstraint = TRUE;
+ trig.tgisinternal = TRUE;
trig.tgconstrrelid = RelationGetRelid(pkrel);
trig.tgconstrindid = pkindOid;
trig.tgconstraint = constraintOid;
CreateTrigStmt *fk_trigger;
fk_trigger = makeNode(CreateTrigStmt);
- fk_trigger->trigname = fkconstraint->conname;
+ fk_trigger->trigname = "RI_ConstraintTrigger";
fk_trigger->relation = myRel;
fk_trigger->before = false;
fk_trigger->row = true;
fk_trigger->constrrel = fkconstraint->pktable;
fk_trigger->args = NIL;
- (void) CreateTrigger(fk_trigger, NULL, constraintOid, indexOid,
- "RI_ConstraintTrigger", false);
+ (void) CreateTrigger(fk_trigger, NULL, constraintOid, indexOid, true);
/* Make changes-so-far visible */
CommandCounterIncrement();
* DELETE action on the referenced table.
*/
fk_trigger = makeNode(CreateTrigStmt);
- fk_trigger->trigname = fkconstraint->conname;
+ fk_trigger->trigname = "RI_ConstraintTrigger";
fk_trigger->relation = fkconstraint->pktable;
fk_trigger->before = false;
fk_trigger->row = true;
}
fk_trigger->args = NIL;
- (void) CreateTrigger(fk_trigger, NULL, constraintOid, indexOid,
- "RI_ConstraintTrigger", false);
+ (void) CreateTrigger(fk_trigger, NULL, constraintOid, indexOid, true);
/* Make changes-so-far visible */
CommandCounterIncrement();
* UPDATE action on the referenced table.
*/
fk_trigger = makeNode(CreateTrigStmt);
- fk_trigger->trigname = fkconstraint->conname;
+ fk_trigger->trigname = "RI_ConstraintTrigger";
fk_trigger->relation = fkconstraint->pktable;
fk_trigger->before = false;
fk_trigger->row = true;
}
fk_trigger->args = NIL;
- (void) CreateTrigger(fk_trigger, NULL, constraintOid, indexOid,
- "RI_ConstraintTrigger", false);
+ (void) CreateTrigger(fk_trigger, NULL, constraintOid, indexOid, true);
}
/*
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/commands/trigger.c,v 1.258 2010/01/02 16:57:38 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/trigger.c,v 1.259 2010/01/17 22:56:21 tgl Exp $
*
*-------------------------------------------------------------------------
*/
* constraintOid, if nonzero, says that this trigger is being created
* internally to implement that constraint. A suitable pg_depend entry will
* be made to link the trigger to that constraint. constraintOid is zero when
- * executing a user-entered CREATE TRIGGER command.
+ * executing a user-entered CREATE TRIGGER command. (For CREATE CONSTRAINT
+ * TRIGGER, we build a pg_constraint entry internally.)
*
* indexOid, if nonzero, is the OID of an index associated with the constraint.
* We do nothing with this except store it into pg_trigger.tgconstrindid.
*
- * prefix is NULL for user-created triggers. For internally generated
- * constraint triggers, it is a prefix string to use in building the
- * trigger name. (stmt->trigname is the constraint name in such cases.)
+ * If isInternal is true then this is an internally-generated trigger.
+ * This argument sets the tgisinternal field of the pg_trigger entry, and
+ * if TRUE causes us to modify the given trigger name to ensure uniqueness.
*
- * If checkPermissions is true we require ACL_TRIGGER permissions on the
- * relation. If not, the caller already checked permissions. (This is
- * currently redundant with constraintOid being zero, but it's clearer to
- * have a separate argument.)
+ * When isInternal is not true we require ACL_TRIGGER permissions on the
+ * relation. For internal triggers the caller must apply any required
+ * permission checks.
*
* Note: can return InvalidOid if we decided to not create a trigger at all,
* but a foreign-key constraint. This is a kluge for backwards compatibility.
*/
Oid
CreateTrigger(CreateTrigStmt *stmt, const char *queryString,
- Oid constraintOid, Oid indexOid, const char *prefix,
- bool checkPermissions)
+ Oid constraintOid, Oid indexOid,
+ bool isInternal)
{
int16 tgtype;
int ncolumns;
Oid funcoid;
Oid funcrettype;
Oid trigoid;
- char constrtrigname[NAMEDATALEN];
+ char internaltrigname[NAMEDATALEN];
char *trigname;
- char *constrname;
Oid constrrelid = InvalidOid;
ObjectAddress myself,
referenced;
constrrelid = RangeVarGetRelid(stmt->constrrel, false);
/* permission checks */
- if (checkPermissions)
+ if (!isInternal)
{
aclresult = pg_class_aclcheck(RelationGetRelid(rel), GetUserId(),
ACL_TRIGGER);
* convert this legacy representation into a regular foreign key
* constraint. Ugly, but necessary for loading old dump files.
*/
- if (stmt->isconstraint && !OidIsValid(constraintOid) &&
+ if (stmt->isconstraint && !isInternal &&
list_length(stmt->args) >= 6 &&
(list_length(stmt->args) % 2) == 0 &&
RI_FKey_trigger_type(funcoid) != RI_TRIGGER_NONE)
return InvalidOid;
}
+ /*
+ * If it's a user-entered CREATE CONSTRAINT TRIGGER command, make a
+ * corresponding pg_constraint entry.
+ */
+ if (stmt->isconstraint && !OidIsValid(constraintOid))
+ {
+ /* Internal callers should have made their own constraints */
+ Assert(!isInternal);
+ constraintOid = CreateConstraintEntry(stmt->trigname,
+ RelationGetNamespace(rel),
+ CONSTRAINT_TRIGGER,
+ stmt->deferrable,
+ stmt->initdeferred,
+ RelationGetRelid(rel),
+ NULL, /* no conkey */
+ 0,
+ InvalidOid, /* no domain */
+ InvalidOid, /* no index */
+ InvalidOid, /* no foreign key */
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ 0,
+ ' ',
+ ' ',
+ ' ',
+ NULL, /* no exclusion */
+ NULL, /* no check constraint */
+ NULL,
+ NULL,
+ true, /* islocal */
+ 0); /* inhcount */
+ }
+
/*
* Generate the trigger's OID now, so that we can use it in the name if
* needed.
trigoid = GetNewOid(tgrel);
/*
- * If trigger is for a constraint, stmt->trigname is the constraint
- * name; save that and build a unique trigger name based on the supplied
- * prefix, to avoid collisions with user-selected trigger names.
+ * If trigger is internally generated, modify the provided trigger name
+ * to ensure uniqueness by appending the trigger OID. (Callers will
+ * usually supply a simple constant trigger name in these cases.)
*/
- if (prefix != NULL)
+ if (isInternal)
{
- snprintf(constrtrigname, sizeof(constrtrigname),
- "%s_%u", prefix, trigoid);
- trigname = constrtrigname;
- constrname = stmt->trigname;
- }
- else if (stmt->isconstraint)
- {
- /* user constraint trigger: trigger name is also constraint name */
- trigname = stmt->trigname;
- constrname = stmt->trigname;
+ snprintf(internaltrigname, sizeof(internaltrigname),
+ "%s_%u", stmt->trigname, trigoid);
+ trigname = internaltrigname;
}
else
{
- /* regular trigger: use empty constraint name */
+ /* user-defined trigger; use the specified trigger name as-is */
trigname = stmt->trigname;
- constrname = "";
}
/*
* Scan pg_trigger for existing triggers on relation. We do this only to
* give a nice error message if there's already a trigger of the same
* name. (The unique index on tgrelid/tgname would complain anyway.)
+ * We can skip this for internally generated triggers, since the name
+ * modification above should be sufficient.
*
* NOTE that this is cool only because we have AccessExclusiveLock on the
* relation, so the trigger set won't be changing underneath us.
*/
- ScanKeyInit(&key,
- Anum_pg_trigger_tgrelid,
- BTEqualStrategyNumber, F_OIDEQ,
- ObjectIdGetDatum(RelationGetRelid(rel)));
- tgscan = systable_beginscan(tgrel, TriggerRelidNameIndexId, true,
- SnapshotNow, 1, &key);
- while (HeapTupleIsValid(tuple = systable_getnext(tgscan)))
+ if (!isInternal)
{
- Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(tuple);
+ ScanKeyInit(&key,
+ Anum_pg_trigger_tgrelid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(RelationGetRelid(rel)));
+ tgscan = systable_beginscan(tgrel, TriggerRelidNameIndexId, true,
+ SnapshotNow, 1, &key);
+ while (HeapTupleIsValid(tuple = systable_getnext(tgscan)))
+ {
+ Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(tuple);
- if (namestrcmp(&(pg_trigger->tgname), trigname) == 0)
- ereport(ERROR,
- (errcode(ERRCODE_DUPLICATE_OBJECT),
- errmsg("trigger \"%s\" for relation \"%s\" already exists",
- trigname, stmt->relation->relname)));
+ if (namestrcmp(&(pg_trigger->tgname), trigname) == 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_OBJECT),
+ errmsg("trigger \"%s\" for relation \"%s\" already exists",
+ trigname, stmt->relation->relname)));
+ }
+ systable_endscan(tgscan);
}
- systable_endscan(tgscan);
/*
* Build the new pg_trigger tuple.
values[Anum_pg_trigger_tgfoid - 1] = ObjectIdGetDatum(funcoid);
values[Anum_pg_trigger_tgtype - 1] = Int16GetDatum(tgtype);
values[Anum_pg_trigger_tgenabled - 1] = CharGetDatum(TRIGGER_FIRES_ON_ORIGIN);
- values[Anum_pg_trigger_tgisconstraint - 1] = BoolGetDatum(stmt->isconstraint);
- values[Anum_pg_trigger_tgconstrname - 1] = DirectFunctionCall1(namein,
- CStringGetDatum(constrname));
+ values[Anum_pg_trigger_tgisinternal - 1] = BoolGetDatum(isInternal);
values[Anum_pg_trigger_tgconstrrelid - 1] = ObjectIdGetDatum(constrrelid);
values[Anum_pg_trigger_tgconstrindid - 1] = ObjectIdGetDatum(indexOid);
values[Anum_pg_trigger_tgconstraint - 1] = ObjectIdGetDatum(constraintOid);
referenced.objectSubId = 0;
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
- if (OidIsValid(constraintOid))
+ if (isInternal && OidIsValid(constraintOid))
{
/*
- * It's for a constraint, so make it an internal dependency of the
- * constraint. We can skip depending on the relations, as there'll be
- * an indirect dependency via the constraint.
+ * Internally-generated trigger for a constraint, so make it an
+ * internal dependency of the constraint. We can skip depending on
+ * the relation(s), as there'll be an indirect dependency via the
+ * constraint.
*/
referenced.classId = ConstraintRelationId;
referenced.objectId = constraintOid;
else
{
/*
- * Regular CREATE TRIGGER, so place dependencies. We make trigger be
+ * User CREATE TRIGGER, so place dependencies. We make trigger be
* auto-dropped if its relation is dropped or if the FK relation is
* dropped. (Auto drop is compatible with our pre-7.3 behavior.)
*/
}
/* Not possible to have an index dependency in this case */
Assert(!OidIsValid(indexOid));
+ /*
+ * If it's a user-specified constraint trigger, make the constraint
+ * internally dependent on the trigger instead of vice versa.
+ */
+ if (OidIsValid(constraintOid))
+ {
+ referenced.classId = ConstraintRelationId;
+ referenced.objectId = constraintOid;
+ referenced.objectSubId = 0;
+ recordDependencyOn(&referenced, &myself, DEPENDENCY_INTERNAL);
+ }
}
/* If column-specific trigger, add normal dependencies on columns */
{
Form_pg_trigger oldtrig = (Form_pg_trigger) GETSTRUCT(tuple);
- if (OidIsValid(oldtrig->tgconstraint))
+ if (oldtrig->tgisinternal)
{
/* system trigger ... ok to process? */
if (skip_system)
build->tgfoid = pg_trigger->tgfoid;
build->tgtype = pg_trigger->tgtype;
build->tgenabled = pg_trigger->tgenabled;
- build->tgisconstraint = pg_trigger->tgisconstraint;
+ build->tgisinternal = pg_trigger->tgisinternal;
build->tgconstrrelid = pg_trigger->tgconstrrelid;
build->tgconstrindid = pg_trigger->tgconstrindid;
build->tgconstraint = pg_trigger->tgconstraint;
return false;
if (trig1->tgenabled != trig2->tgenabled)
return false;
- if (trig1->tgisconstraint != trig2->tgisconstraint)
+ if (trig1->tgisinternal != trig2->tgisinternal)
return false;
if (trig1->tgconstrrelid != trig2->tgconstrrelid)
return false;
}
else
{
+ Relation conrel;
Relation tgrel;
- ListCell *l;
- List *oidlist = NIL;
+ List *conoidlist = NIL;
+ List *tgoidlist = NIL;
+ ListCell *lc;
- /* ----------
+ /*
* Handle SET CONSTRAINTS constraint-name [, ...]
- * First lookup all trigger Oid's for the constraint names.
- * ----------
+ *
+ * First, identify all the named constraints and make a list of their
+ * OIDs. Since, unlike the SQL spec, we allow multiple constraints
+ * of the same name within a schema, the specifications are not
+ * necessarily unique. Our strategy is to target all matching
+ * constraints within the first search-path schema that has any
+ * matches, but disregard matches in schemas beyond the first match.
+ * (This is a bit odd but it's the historical behavior.)
*/
- tgrel = heap_open(TriggerRelationId, AccessShareLock);
+ conrel = heap_open(ConstraintRelationId, AccessShareLock);
- foreach(l, stmt->constraints)
+ foreach(lc, stmt->constraints)
{
- RangeVar *constraint = lfirst(l);
- ScanKeyData skey;
- SysScanDesc tgscan;
- HeapTuple htup;
+ RangeVar *constraint = lfirst(lc);
bool found;
- List *namespaceSearchList;
- ListCell *namespaceSearchCell;
+ List *namespacelist;
+ ListCell *nslc;
if (constraint->catalogname)
{
{
Oid namespaceId = LookupExplicitNamespace(constraint->schemaname);
- namespaceSearchList = list_make1_oid(namespaceId);
+ namespacelist = list_make1_oid(namespaceId);
}
else
{
- namespaceSearchList = fetch_search_path(true);
+ namespacelist = fetch_search_path(true);
}
found = false;
- foreach(namespaceSearchCell, namespaceSearchList)
+ foreach(nslc, namespacelist)
{
- Oid searchNamespaceId = lfirst_oid(namespaceSearchCell);
+ Oid namespaceId = lfirst_oid(nslc);
+ SysScanDesc conscan;
+ ScanKeyData skey[2];
+ HeapTuple tup;
- /*
- * Setup to scan pg_trigger by tgconstrname ...
- */
- ScanKeyInit(&skey,
- Anum_pg_trigger_tgconstrname,
+ ScanKeyInit(&skey[0],
+ Anum_pg_constraint_conname,
BTEqualStrategyNumber, F_NAMEEQ,
- PointerGetDatum(constraint->relname));
+ CStringGetDatum(constraint->relname));
+ ScanKeyInit(&skey[1],
+ Anum_pg_constraint_connamespace,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(namespaceId));
- tgscan = systable_beginscan(tgrel, TriggerConstrNameIndexId, true,
- SnapshotNow, 1, &skey);
+ conscan = systable_beginscan(conrel, ConstraintNameNspIndexId,
+ true, SnapshotNow, 2, skey);
- /*
- * ... and search for the constraint trigger row
- */
- while (HeapTupleIsValid(htup = systable_getnext(tgscan)))
+ while (HeapTupleIsValid(tup = systable_getnext(conscan)))
{
- Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(htup);
- Oid constraintNamespaceId;
-
- /*
- * Foreign key constraints have triggers on both the
- * parent and child tables. Since these tables may be in
- * different schemas we must pick the child table because
- * that table "owns" the constraint.
- *
- * Referential triggers on the parent table other than
- * NOACTION_DEL and NOACTION_UPD are ignored below, so it
- * is possible to not check them here, but it seems safer
- * to always check.
- */
- if (pg_trigger->tgfoid == F_RI_FKEY_NOACTION_DEL ||
- pg_trigger->tgfoid == F_RI_FKEY_NOACTION_UPD ||
- pg_trigger->tgfoid == F_RI_FKEY_RESTRICT_UPD ||
- pg_trigger->tgfoid == F_RI_FKEY_RESTRICT_DEL ||
- pg_trigger->tgfoid == F_RI_FKEY_CASCADE_UPD ||
- pg_trigger->tgfoid == F_RI_FKEY_CASCADE_DEL ||
- pg_trigger->tgfoid == F_RI_FKEY_SETNULL_UPD ||
- pg_trigger->tgfoid == F_RI_FKEY_SETNULL_DEL ||
- pg_trigger->tgfoid == F_RI_FKEY_SETDEFAULT_UPD ||
- pg_trigger->tgfoid == F_RI_FKEY_SETDEFAULT_DEL)
- constraintNamespaceId = get_rel_namespace(pg_trigger->tgconstrrelid);
- else
- constraintNamespaceId = get_rel_namespace(pg_trigger->tgrelid);
+ Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tup);
- /*
- * If this constraint is not in the schema we're currently
- * searching for, keep looking.
- */
- if (constraintNamespaceId != searchNamespaceId)
- continue;
-
- /*
- * If we found some, check that they fit the deferrability
- * but skip referential action ones, since they are
- * silently never deferrable.
- */
- if (pg_trigger->tgfoid != F_RI_FKEY_RESTRICT_UPD &&
- pg_trigger->tgfoid != F_RI_FKEY_RESTRICT_DEL &&
- pg_trigger->tgfoid != F_RI_FKEY_CASCADE_UPD &&
- pg_trigger->tgfoid != F_RI_FKEY_CASCADE_DEL &&
- pg_trigger->tgfoid != F_RI_FKEY_SETNULL_UPD &&
- pg_trigger->tgfoid != F_RI_FKEY_SETNULL_DEL &&
- pg_trigger->tgfoid != F_RI_FKEY_SETDEFAULT_UPD &&
- pg_trigger->tgfoid != F_RI_FKEY_SETDEFAULT_DEL)
- {
- if (stmt->deferred && !pg_trigger->tgdeferrable)
- ereport(ERROR,
- (errcode(ERRCODE_WRONG_OBJECT_TYPE),
- errmsg("constraint \"%s\" is not deferrable",
- constraint->relname)));
- oidlist = lappend_oid(oidlist, HeapTupleGetOid(htup));
- }
+ if (con->condeferrable)
+ conoidlist = lappend_oid(conoidlist,
+ HeapTupleGetOid(tup));
+ else if (stmt->deferred)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("constraint \"%s\" is not deferrable",
+ constraint->relname)));
found = true;
}
- systable_endscan(tgscan);
+ systable_endscan(conscan);
/*
* Once we've found a matching constraint we do not search
*/
if (found)
break;
-
}
- list_free(namespaceSearchList);
+ list_free(namespacelist);
/*
* Not found ?
errmsg("constraint \"%s\" does not exist",
constraint->relname)));
}
+
+ heap_close(conrel, AccessShareLock);
+
+ /*
+ * Now, locate the trigger(s) implementing each of these constraints,
+ * and make a list of their OIDs.
+ */
+ tgrel = heap_open(TriggerRelationId, AccessShareLock);
+
+ foreach(lc, conoidlist)
+ {
+ Oid conoid = lfirst_oid(lc);
+ bool found;
+ ScanKeyData skey;
+ SysScanDesc tgscan;
+ HeapTuple htup;
+
+ found = false;
+
+ ScanKeyInit(&skey,
+ Anum_pg_trigger_tgconstraint,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(conoid));
+
+ tgscan = systable_beginscan(tgrel, TriggerConstraintIndexId, true,
+ SnapshotNow, 1, &skey);
+
+ while (HeapTupleIsValid(htup = systable_getnext(tgscan)))
+ {
+ Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(htup);
+
+ /*
+ * Silently skip triggers that are marked as non-deferrable
+ * in pg_trigger. This is not an error condition, since
+ * a deferrable RI constraint may have some non-deferrable
+ * actions.
+ */
+ if (pg_trigger->tgdeferrable)
+ tgoidlist = lappend_oid(tgoidlist,
+ HeapTupleGetOid(htup));
+
+ found = true;
+ }
+
+ systable_endscan(tgscan);
+
+ /* Safety check: a deferrable constraint should have triggers */
+ if (!found)
+ elog(ERROR, "no triggers found for constraint with OID %u",
+ conoid);
+ }
+
heap_close(tgrel, AccessShareLock);
/*
- * Set the trigger states of individual triggers for this xact.
+ * Now we can set the trigger states of individual triggers for this
+ * xact.
*/
- foreach(l, oidlist)
+ foreach(lc, tgoidlist)
{
- Oid tgoid = lfirst_oid(l);
+ Oid tgoid = lfirst_oid(lc);
SetConstraintState state = afterTriggers->state;
bool found = false;
int i;
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.329 2010/01/15 22:36:34 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.330 2010/01/17 22:56:22 tgl Exp $
*
*-------------------------------------------------------------------------
*/
break;
case T_CreateTrigStmt:
- CreateTrigger((CreateTrigStmt *) parsetree, queryString,
- InvalidOid, InvalidOid, NULL, true);
+ (void) CreateTrigger((CreateTrigStmt *) parsetree, queryString,
+ InvalidOid, InvalidOid, false);
break;
case T_DropPropertyStmt:
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.318 2010/01/02 16:57:55 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.319 2010/01/17 22:56:22 tgl Exp $
*
*-------------------------------------------------------------------------
*/
tgname = NameStr(trigrec->tgname);
appendStringInfo(&buf, "CREATE %sTRIGGER %s",
- trigrec->tgisconstraint ? "CONSTRAINT " : "",
+ OidIsValid(trigrec->tgconstraint) ? "CONSTRAINT " : "",
quote_identifier(tgname));
appendStringInfoString(&buf, pretty ? "\n " : " ");
generate_relation_name(trigrec->tgrelid, NIL));
appendStringInfoString(&buf, pretty ? "\n " : " ");
- if (trigrec->tgisconstraint)
+ if (OidIsValid(trigrec->tgconstraint))
{
if (OidIsValid(trigrec->tgconstrrelid))
{
break;
}
+ case CONSTRAINT_TRIGGER:
+ /*
+ * There isn't an ALTER TABLE syntax for creating a user-defined
+ * constraint trigger, but it seems better to print something
+ * than throw an error; if we throw error then this function
+ * couldn't safely be applied to all rows of pg_constraint.
+ */
+ appendStringInfo(&buf, "TRIGGER");
+ break;
case CONSTRAINT_EXCLUSION:
{
Oid indexOid = conForm->conindid;
* by PostgreSQL
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.c,v 1.566 2010/01/06 05:18:18 momjian Exp $
+ * $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.c,v 1.567 2010/01/17 22:56:22 tgl Exp $
*
*-------------------------------------------------------------------------
*/
"tgenabled, tableoid, oid "
"FROM pg_catalog.pg_trigger t "
"WHERE tgrelid = '%u'::pg_catalog.oid "
- "AND tgconstraint = 0",
+ "AND NOT tgisinternal",
tbinfo->dobj.catId.oid);
}
else if (g_fout->remoteVersion >= 80300)
*
* Copyright (c) 2000-2010, PostgreSQL Global Development Group
*
- * $PostgreSQL: pgsql/src/bin/psql/describe.c,v 1.233 2010/01/02 16:57:59 momjian Exp $
+ * $PostgreSQL: pgsql/src/bin/psql/describe.c,v 1.234 2010/01/17 22:56:23 tgl Exp $
*/
#include "postgres_fe.h"
PQclear(result);
}
- /* print triggers (but ignore RI and unique constraint triggers) */
+ /* print triggers (but only user-defined triggers) */
if (tableinfo.hastriggers)
{
printfPQExpBuffer(&buf,
"FROM pg_catalog.pg_trigger t\n"
"WHERE t.tgrelid = '%s' AND ",
oid);
- if (pset.sversion >= 80300)
+ if (pset.sversion >= 80500)
+ appendPQExpBuffer(&buf, "NOT t.tgisinternal");
+ else if (pset.sversion >= 80300)
appendPQExpBuffer(&buf, "t.tgconstraint = 0");
else
appendPQExpBuffer(&buf,
* Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.573 2010/01/15 09:19:07 heikki Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.574 2010/01/17 22:56:23 tgl Exp $
*
*-------------------------------------------------------------------------
*/
*/
/* yyyymmddN */
-#define CATALOG_VERSION_NO 201001151
+#define CATALOG_VERSION_NO 201001171
#endif
* Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/catalog/indexing.h,v 1.115 2010/01/05 01:06:56 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/indexing.h,v 1.116 2010/01/17 22:56:23 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#define TablespaceNameIndexId 2698
/* This following index is not used for a cache and is not unique */
-DECLARE_INDEX(pg_trigger_tgconstrname_index, 2699, on pg_trigger using btree(tgconstrname name_ops));
-#define TriggerConstrNameIndexId 2699
+DECLARE_INDEX(pg_trigger_tgconstraint_index, 2699, on pg_trigger using btree(tgconstraint oid_ops));
+#define TriggerConstraintIndexId 2699
DECLARE_UNIQUE_INDEX(pg_trigger_tgrelid_tgname_index, 2701, on pg_trigger using btree(tgrelid oid_ops, tgname name_ops));
#define TriggerRelidNameIndexId 2701
DECLARE_UNIQUE_INDEX(pg_trigger_oid_index, 2702, on pg_trigger using btree(oid oid_ops));
* Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/catalog/pg_constraint.h,v 1.36 2010/01/05 01:06:56 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/pg_constraint.h,v 1.37 2010/01/17 22:56:23 tgl Exp $
*
* NOTES
* the genbki.pl script reads this file and generates .bki
* Postgres practice, and partly because we don't want to have to obtain a
* global lock to generate a globally unique name for a nameless
* constraint. We associate a namespace with constraint names only for
- * SQL92 compatibility.
+ * SQL-spec compatibility.
*/
NameData conname; /* name of this constraint */
Oid connamespace; /* OID of namespace containing constraint */
*/
/*
- * Columns of conrelid that the constraint applies to
+ * Columns of conrelid that the constraint applies to, if known
+ * (this is NULL for trigger constraints)
*/
int2 conkey[1];
#define CONSTRAINT_FOREIGN 'f'
#define CONSTRAINT_PRIMARY 'p'
#define CONSTRAINT_UNIQUE 'u'
+#define CONSTRAINT_TRIGGER 't'
#define CONSTRAINT_EXCLUSION 'x'
/*
* Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/catalog/pg_trigger.h,v 1.38 2010/01/05 01:06:57 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/pg_trigger.h,v 1.39 2010/01/17 22:56:23 tgl Exp $
*
* NOTES
* the genbki.pl script reads this file and generates .bki
* pg_trigger definition. cpp turns this into
* typedef struct FormData_pg_trigger
*
- * Note: when tgconstraint is nonzero, tgisconstraint must be true, and
- * tgconstrname, tgconstrrelid, tgconstrindid, tgdeferrable, tginitdeferred
- * are redundant with the referenced pg_constraint entry. The reason we keep
- * these fields is that we support "stand-alone" constraint triggers with no
- * corresponding pg_constraint entry.
+ * Note: when tgconstraint is nonzero, tgconstrrelid, tgconstrindid,
+ * tgdeferrable, and tginitdeferred are largely redundant with the referenced
+ * pg_constraint entry. However, it is possible for a non-deferrable trigger
+ * to be associated with a deferrable constraint.
* ----------------
*/
#define TriggerRelationId 2620
* ROW/STATEMENT; see below */
char tgenabled; /* trigger's firing configuration WRT
* session_replication_role */
- bool tgisconstraint; /* trigger is a constraint trigger */
- NameData tgconstrname; /* constraint name */
+ bool tgisinternal; /* trigger is system-generated */
Oid tgconstrrelid; /* constraint's FROM table, if any */
Oid tgconstrindid; /* constraint's supporting index, if any */
- Oid tgconstraint; /* owning pg_constraint entry, if any */
+ Oid tgconstraint; /* associated pg_constraint entry, if any */
bool tgdeferrable; /* constraint trigger is deferrable */
bool tginitdeferred; /* constraint trigger is deferred initially */
int2 tgnargs; /* # of extra arguments in tgargs */
* compiler constants for pg_trigger
* ----------------
*/
-#define Natts_pg_trigger 16
+#define Natts_pg_trigger 15
#define Anum_pg_trigger_tgrelid 1
#define Anum_pg_trigger_tgname 2
#define Anum_pg_trigger_tgfoid 3
#define Anum_pg_trigger_tgtype 4
#define Anum_pg_trigger_tgenabled 5
-#define Anum_pg_trigger_tgisconstraint 6
-#define Anum_pg_trigger_tgconstrname 7
-#define Anum_pg_trigger_tgconstrrelid 8
-#define Anum_pg_trigger_tgconstrindid 9
-#define Anum_pg_trigger_tgconstraint 10
-#define Anum_pg_trigger_tgdeferrable 11
-#define Anum_pg_trigger_tginitdeferred 12
-#define Anum_pg_trigger_tgnargs 13
-#define Anum_pg_trigger_tgattr 14
-#define Anum_pg_trigger_tgargs 15
-#define Anum_pg_trigger_tgqual 16
+#define Anum_pg_trigger_tgisinternal 6
+#define Anum_pg_trigger_tgconstrrelid 7
+#define Anum_pg_trigger_tgconstrindid 8
+#define Anum_pg_trigger_tgconstraint 9
+#define Anum_pg_trigger_tgdeferrable 10
+#define Anum_pg_trigger_tginitdeferred 11
+#define Anum_pg_trigger_tgnargs 12
+#define Anum_pg_trigger_tgattr 13
+#define Anum_pg_trigger_tgargs 14
+#define Anum_pg_trigger_tgqual 15
/* Bits within tgtype */
#define TRIGGER_TYPE_ROW (1 << 0)
* Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/commands/trigger.h,v 1.79 2010/01/02 16:58:03 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/commands/trigger.h,v 1.80 2010/01/17 22:56:23 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#define TRIGGER_DISABLED 'D'
extern Oid CreateTrigger(CreateTrigStmt *stmt, const char *queryString,
- Oid constraintOid, Oid indexOid, const char *prefix,
- bool checkPermissions);
+ Oid constraintOid, Oid indexOid,
+ bool isInternal);
extern void DropTrigger(Oid relid, const char *trigname,
DropBehavior behavior, bool missing_ok);
* Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.424 2010/01/15 22:36:35 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.425 2010/01/17 22:56:23 tgl Exp $
*
*-------------------------------------------------------------------------
*/
int16 events; /* INSERT/UPDATE/DELETE/TRUNCATE */
List *columns; /* column names, or NIL for all columns */
Node *whenClause; /* qual expression, or NULL if none */
-
- /* The following are used for constraint triggers (RI and unique checks) */
bool isconstraint; /* This is a constraint trigger */
+ /* The remaining fields are only used for constraint triggers */
bool deferrable; /* [NOT] DEFERRABLE */
bool initdeferred; /* INITIALLY {DEFERRED|IMMEDIATE} */
RangeVar *constrrel; /* opposite relation, if RI trigger */
* Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/utils/rel.h,v 1.119 2010/01/10 22:19:17 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/utils/rel.h,v 1.120 2010/01/17 22:56:23 tgl Exp $
*
*-------------------------------------------------------------------------
*/
Oid tgfoid;
int16 tgtype;
char tgenabled;
- bool tgisconstraint;
+ bool tgisinternal;
Oid tgconstrrelid;
Oid tgconstrindid;
Oid tgconstraint;