]> granicus.if.org Git - postgresql/commitdiff
Improve the handling of SET CONSTRAINTS commands by having them search
authorTom Lane <tgl@sss.pgh.pa.us>
Sun, 17 Jan 2010 22:56:23 +0000 (22:56 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Sun, 17 Jan 2010 22:56:23 +0000 (22:56 +0000)
pg_constraint before searching pg_trigger.  This allows saner handling of
corner cases; in particular we now say "constraint is not deferrable"
rather than "constraint does not exist" when the command is applied to
a constraint that's inherently non-deferrable.  Per a gripe several months
ago from hubert depesz lubaczewski.

To make this work without breaking user-defined constraint triggers,
we have to add entries for them to pg_constraint.  However, in return
we can remove the pgconstrname column from pg_constraint, which represents
a fairly sizable space savings.  I also replaced the tgisconstraint column
with tgisinternal; the old meaning of tgisconstraint can now be had by
testing for nonzero tgconstraint, while there is no other way to get
the old meaning of nonzero tgconstraint, namely that the trigger was
internally generated rather than being user-created.

In passing, fix an old misstatement in the docs and comments, namely that
pg_trigger.tgdeferrable is exactly redundant with pg_constraint.condeferrable.
Actually, we mark RI action triggers as nondeferrable even when they belong to
a nominally deferrable FK constraint.  The SET CONSTRAINTS code now relies on
that instead of hard-coding a list of exception OIDs.

17 files changed:
doc/src/sgml/catalogs.sgml
doc/src/sgml/trigger.sgml
src/backend/catalog/index.c
src/backend/catalog/information_schema.sql
src/backend/commands/tablecmds.c
src/backend/commands/trigger.c
src/backend/tcop/utility.c
src/backend/utils/adt/ruleutils.c
src/bin/pg_dump/pg_dump.c
src/bin/psql/describe.c
src/include/catalog/catversion.h
src/include/catalog/indexing.h
src/include/catalog/pg_constraint.h
src/include/catalog/pg_trigger.h
src/include/commands/trigger.h
src/include/nodes/parsenodes.h
src/include/utils/rel.h

index ca90cd56cafc66b02ed04bcd6b83e99b725256d2..f917652a08536793c6a2790f0d4db67228763c5c 100644 (file)
@@ -1,4 +1,4 @@
-<!-- $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>
 
index 49571ca7c5cd0c9ac4b5e318157ef1c0e615f387..5418f314a31ca26e47d3854b5ac10af1df2f3884 100644 (file)
@@ -1,4 +1,4 @@
-<!-- $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>
@@ -506,7 +506,7 @@ typedef struct Trigger
     Oid         tgfoid;
     int16       tgtype;
     bool        tgenabled;
-    bool        tgisconstraint;
+    bool        tgisinternal;
     Oid         tgconstrrelid;
     Oid         tgconstrindid;
     Oid         tgconstraint;
index 7a6d914567aceedac9ac003d112a69a4269e5469..b84c586a99a0c24e0c172949f69eddfaea26e14d 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * 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
@@ -825,7 +825,8 @@ index_create(Oid heapRelationId,
                                                                           -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;
@@ -840,9 +841,7 @@ index_create(Oid heapRelationId,
                                trigger->constrrel = NULL;
 
                                (void) CreateTrigger(trigger, NULL, conOid, indexRelationId,
-                                                                        isprimary ? "PK_ConstraintTrigger" :
-                                                                        "Unique_ConstraintTrigger",
-                                                                        false);
+                                                                        true);
                        }
                }
                else
index 1b413626ddef71edde4741c0e93a6de722e3c2d2..64417b3ba8f01b3a6d85cfe71940df5e58e639ef 100644 (file)
@@ -4,7 +4,7 @@
  *
  * 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 $
  */
 
 /*
@@ -1666,7 +1666,7 @@ CREATE VIEW table_constraints AS
 
     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')
@@ -1868,7 +1868,7 @@ CREATE VIEW triggered_update_columns AS
           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
@@ -1953,7 +1953,7 @@ CREATE VIEW triggers AS
     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
index bf1234614c908762a3d524215a599583432950e8..43d8c0c56bf3cd6252f94f53cc0896c4078b6036 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * 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 $
  *
  *-------------------------------------------------------------------------
  */
@@ -5338,7 +5338,7 @@ validateForeignKeyConstraint(Constraint *fkconstraint,
        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;
@@ -5399,7 +5399,7 @@ CreateFKCheckTrigger(RangeVar *myRel, Constraint *fkconstraint,
        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;
@@ -5424,8 +5424,7 @@ CreateFKCheckTrigger(RangeVar *myRel, Constraint *fkconstraint,
        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();
@@ -5463,7 +5462,7 @@ createForeignKeyTriggers(Relation rel, Constraint *fkconstraint,
         * 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;
@@ -5506,8 +5505,7 @@ createForeignKeyTriggers(Relation rel, Constraint *fkconstraint,
        }
        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();
@@ -5517,7 +5515,7 @@ createForeignKeyTriggers(Relation rel, Constraint *fkconstraint,
         * 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;
@@ -5560,8 +5558,7 @@ createForeignKeyTriggers(Relation rel, Constraint *fkconstraint,
        }
        fk_trigger->args = NIL;
 
-       (void) CreateTrigger(fk_trigger, NULL, constraintOid, indexOid,
-                                                "RI_ConstraintTrigger", false);
+       (void) CreateTrigger(fk_trigger, NULL, constraintOid, indexOid, true);
 }
 
 /*
index ead586a2f21ff26123551461a93d31c7ad569536..e585f1517ad320308825200bbf5caa0e483dcae5 100644 (file)
@@ -7,7 +7,7 @@
  * 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 $
  *
  *-------------------------------------------------------------------------
  */
@@ -93,27 +93,27 @@ static void AfterTriggerSaveEvent(EState *estate, ResultRelInfo *relinfo,
  * 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;
@@ -135,9 +135,8 @@ CreateTrigger(CreateTrigStmt *stmt, const char *queryString,
        Oid                     funcoid;
        Oid                     funcrettype;
        Oid                     trigoid;
-       char            constrtrigname[NAMEDATALEN];
+       char            internaltrigname[NAMEDATALEN];
        char       *trigname;
-       char       *constrname;
        Oid                     constrrelid = InvalidOid;
        ObjectAddress myself,
                                referenced;
@@ -160,7 +159,7 @@ CreateTrigger(CreateTrigStmt *stmt, const char *queryString,
                constrrelid = RangeVarGetRelid(stmt->constrrel, false);
 
        /* permission checks */
-       if (checkPermissions)
+       if (!isInternal)
        {
                aclresult = pg_class_aclcheck(RelationGetRelid(rel), GetUserId(),
                                                                          ACL_TRIGGER);
@@ -338,7 +337,7 @@ CreateTrigger(CreateTrigStmt *stmt, const char *queryString,
         * 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)
@@ -351,6 +350,41 @@ CreateTrigger(CreateTrigStmt *stmt, const char *queryString,
                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.
@@ -360,55 +394,52 @@ CreateTrigger(CreateTrigStmt *stmt, const char *queryString,
        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.
@@ -421,9 +452,7 @@ CreateTrigger(CreateTrigStmt *stmt, const char *queryString,
        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);
@@ -580,12 +609,13 @@ CreateTrigger(CreateTrigStmt *stmt, const char *queryString,
        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;
@@ -595,7 +625,7 @@ CreateTrigger(CreateTrigStmt *stmt, const char *queryString,
        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.)
                 */
@@ -612,6 +642,17 @@ CreateTrigger(CreateTrigStmt *stmt, const char *queryString,
                }
                /* 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 */
@@ -1221,7 +1262,7 @@ EnableDisableTrigger(Relation rel, const char *tgname,
        {
                Form_pg_trigger oldtrig = (Form_pg_trigger) GETSTRUCT(tuple);
 
-               if (OidIsValid(oldtrig->tgconstraint))
+               if (oldtrig->tgisinternal)
                {
                        /* system trigger ... ok to process? */
                        if (skip_system)
@@ -1341,7 +1382,7 @@ RelationBuildTriggers(Relation relation)
                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;
@@ -1699,7 +1740,7 @@ equalTriggerDescs(TriggerDesc *trigdesc1, TriggerDesc *trigdesc2)
                                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;
@@ -3838,26 +3879,31 @@ AfterTriggerSetState(ConstraintsSetStmt *stmt)
        }
        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)
                        {
@@ -3878,94 +3924,49 @@ AfterTriggerSetState(ConstraintsSetStmt *stmt)
                        {
                                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
@@ -3973,10 +3974,9 @@ AfterTriggerSetState(ConstraintsSetStmt *stmt)
                                 */
                                if (found)
                                        break;
-
                        }
 
-                       list_free(namespaceSearchList);
+                       list_free(namespacelist);
 
                        /*
                         * Not found ?
@@ -3987,14 +3987,67 @@ AfterTriggerSetState(ConstraintsSetStmt *stmt)
                                                 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(loidlist)
+               foreach(lc, tgoidlist)
                {
-                       Oid                     tgoid = lfirst_oid(l);
+                       Oid                     tgoid = lfirst_oid(lc);
                        SetConstraintState state = afterTriggers->state;
                        bool            found = false;
                        int                     i;
index 96b0aa735f40f2e8673195b1728f359704b7b657..8daddbefd0b27de5d7837850e510a50946adec67 100644 (file)
@@ -10,7 +10,7 @@
  *
  *
  * 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 $
  *
  *-------------------------------------------------------------------------
  */
@@ -1001,8 +1001,8 @@ standard_ProcessUtility(Node *parsetree,
                        break;
 
                case T_CreateTrigStmt:
-                       CreateTrigger((CreateTrigStmt *) parsetree, queryString,
-                                                 InvalidOid, InvalidOid, NULL, true);
+                       (void) CreateTrigger((CreateTrigStmt *) parsetree, queryString,
+                                                                InvalidOid, InvalidOid, false);
                        break;
 
                case T_DropPropertyStmt:
index d9cc1258f0a325559c466047924d759e82cb9f2e..2eeb0bc5152392cff473adca12bd57c7dd82cb09 100644 (file)
@@ -9,7 +9,7 @@
  *
  *
  * 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 $
  *
  *-------------------------------------------------------------------------
  */
@@ -519,7 +519,7 @@ pg_get_triggerdef_worker(Oid trigid, bool pretty)
 
        tgname = NameStr(trigrec->tgname);
        appendStringInfo(&buf, "CREATE %sTRIGGER %s",
-                                        trigrec->tgisconstraint ? "CONSTRAINT " : "",
+                                        OidIsValid(trigrec->tgconstraint) ? "CONSTRAINT " : "",
                                         quote_identifier(tgname));
        appendStringInfoString(&buf, pretty ? "\n    " : " ");
 
@@ -577,7 +577,7 @@ pg_get_triggerdef_worker(Oid trigid, bool pretty)
                                         generate_relation_name(trigrec->tgrelid, NIL));
        appendStringInfoString(&buf, pretty ? "\n    " : " ");
 
-       if (trigrec->tgisconstraint)
+       if (OidIsValid(trigrec->tgconstraint))
        {
                if (OidIsValid(trigrec->tgconstrrelid))
                {
@@ -1276,6 +1276,15 @@ pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
 
                                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;
index 6c9cb886b99b39d3b1e8dfa7984d12502f4560b9..05fc7ad5a44cecbab9392e59f1156a50b0493b2a 100644 (file)
@@ -12,7 +12,7 @@
  *     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 $
  *
  *-------------------------------------------------------------------------
  */
@@ -4584,7 +4584,7 @@ getTriggers(TableInfo tblinfo[], int numTables)
                                                          "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)
index 4fdf8641744465dd0faaba89df972002aac05020..dca1f577c03fd6a2ff2052b4d644203a27a8a351 100644 (file)
@@ -8,7 +8,7 @@
  *
  * 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"
 
@@ -1849,7 +1849,7 @@ describeOneTableDetails(const char *schemaname,
                        PQclear(result);
                }
 
-               /* print triggers (but ignore RI and unique constraint triggers) */
+               /* print triggers (but only user-defined triggers) */
                if (tableinfo.hastriggers)
                {
                        printfPQExpBuffer(&buf,
@@ -1859,7 +1859,9 @@ describeOneTableDetails(const char *schemaname,
                                                          "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,
index b2c92860f07eb85f7de492b6ef4f35294393b859..1740b2b38c459f22cda7c1bea0ce50884d48aedb 100644 (file)
@@ -37,7 +37,7 @@
  * 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 $
  *
  *-------------------------------------------------------------------------
  */
@@ -53,6 +53,6 @@
  */
 
 /*                                                     yyyymmddN */
-#define CATALOG_VERSION_NO     201001151
+#define CATALOG_VERSION_NO     201001171
 
 #endif
index 6bbb6f71926ada2a19e3469f9f23664db577eb63..0d29875e52e8fd397c4a12b72fd126e28281cb56 100644 (file)
@@ -8,7 +8,7 @@
  * 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 $
  *
  *-------------------------------------------------------------------------
  */
@@ -220,8 +220,8 @@ DECLARE_UNIQUE_INDEX(pg_tablespace_spcname_index, 2698, on pg_tablespace using b
 #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));
index 4242b24c0e1ced200fe4135965f4c3344fdfe3cf..1aada159dbb2691a8d9ae30614dc9a0192403d1f 100644 (file)
@@ -8,7 +8,7 @@
  * 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
@@ -38,7 +38,7 @@ CATALOG(pg_constraint,2606)
         * 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 */
@@ -92,7 +92,8 @@ CATALOG(pg_constraint,2606)
         */
 
        /*
-        * 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];
 
@@ -177,6 +178,7 @@ typedef FormData_pg_constraint *Form_pg_constraint;
 #define CONSTRAINT_FOREIGN                     'f'
 #define CONSTRAINT_PRIMARY                     'p'
 #define CONSTRAINT_UNIQUE                      'u'
+#define CONSTRAINT_TRIGGER                     't'
 #define CONSTRAINT_EXCLUSION           'x'
 
 /*
index 8e1a4bf12b770f0058b31347e5147620918542d1..7c9547504333291aaf572522b8f6e125a3639526 100644 (file)
@@ -8,7 +8,7 @@
  * 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
@@ -43,11 +42,10 @@ CATALOG(pg_trigger,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 */
@@ -69,23 +67,22 @@ typedef FormData_pg_trigger *Form_pg_trigger;
  *             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)
index b5f44bb33e1350c392157f9458f26f168a48899c..db79eb4be732e188ea6cafced0aafd6f7a7a2cc1 100644 (file)
@@ -6,7 +6,7 @@
  * 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 $
  *
  *-------------------------------------------------------------------------
  */
@@ -105,8 +105,8 @@ extern PGDLLIMPORT int SessionReplicationRole;
 #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);
index a03597c9f375fa4c093d4e0c188ffabb364ecf19..c752d071d52ddcecc27db3b6490219887b61db82 100644 (file)
@@ -13,7 +13,7 @@
  * 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 $
  *
  *-------------------------------------------------------------------------
  */
@@ -1593,9 +1593,8 @@ typedef struct CreateTrigStmt
        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 */
index 250a127ed3171304b07773362c604ca54834fdac..344f37a68729f0277e2b8209e52d93fee52641e4 100644 (file)
@@ -7,7 +7,7 @@
  * 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 $
  *
  *-------------------------------------------------------------------------
  */
@@ -56,7 +56,7 @@ typedef struct Trigger
        Oid                     tgfoid;
        int16           tgtype;
        char            tgenabled;
-       bool            tgisconstraint;
+       bool            tgisinternal;
        Oid                     tgconstrrelid;
        Oid                     tgconstrindid;
        Oid                     tgconstraint;