Use schema search path to find the first matching contraint name for SET
authorBruce Momjian <bruce@momjian.us>
Thu, 27 Apr 2006 00:33:46 +0000 (00:33 +0000)
committerBruce Momjian <bruce@momjian.us>
Thu, 27 Apr 2006 00:33:46 +0000 (00:33 +0000)
CONSTRAINT, rather than affecting all constraints in all schemas (which
is what we used to do).  Also allow schema specifications.

Kris Jurka

doc/src/sgml/ref/set_constraints.sgml
src/backend/commands/trigger.c
src/backend/parser/gram.y
src/include/nodes/parsenodes.h

index 3bcde91f386914e4013a2c42096599b06518f5d0..9af82008aa32b279adf9b08b0116fb50d3ca4cc3 100644 (file)
@@ -1,4 +1,4 @@
-<!-- $PostgreSQL: pgsql/doc/src/sgml/ref/set_constraints.sgml,v 1.12 2004/09/10 18:39:53 tgl Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/ref/set_constraints.sgml,v 1.13 2006/04/27 00:33:32 momjian Exp $ -->
 <refentry id="SQL-SET-CONSTRAINTS">
  <refmeta>
   <refentrytitle id="SQL-SET-CONSTRAINTS-title">SET CONSTRAINTS</refentrytitle>
@@ -45,10 +45,10 @@ SET CONSTRAINTS { ALL | <replaceable class="parameter">name</replaceable> [, ...
 
   <para>
    <command>SET CONSTRAINTS</command> with a list of constraint names changes
-   the mode of just those constraints (which must all be deferrable).  If
-   there are multiple constraints matching any given name, all are affected.
-   <command>SET CONSTRAINTS ALL</command> changes the mode of all deferrable
-   constraints.
+   the mode of just those constraints (which must all be deferrable).  The
+   current schema search path is used to find the first matching name if
+   no schema name is specified.  <command>SET CONSTRAINTS ALL</command> 
+   changes the mode of all deferrable constraints.
   </para>
 
   <para>
@@ -93,13 +93,6 @@ SET CONSTRAINTS { ALL | <replaceable class="parameter">name</replaceable> [, ...
    foreign-key constraints.
   </para>
 
-  <para>
-   The SQL standard says that constraint names appearing in <command>SET
-   CONSTRAINTS</command> can be schema-qualified.  This is not yet
-   supported by <productname>PostgreSQL</productname>: the names must
-   be unqualified, and all constraints matching the command will be
-   affected no matter which schema they are in.
-  </para>
  </refsect1>
 </refentry>
 
index 336aa67fdd52898fcdc5cb8b4962c021fc89ecc5..256b2ca411d5cd0b441eca1872be5412b6c7e637 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.200 2006/03/05 15:58:25 momjian Exp $
+ *   $PostgreSQL: pgsql/src/backend/commands/trigger.c,v 1.201 2006/04/27 00:33:41 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -24,6 +24,7 @@
 #include "catalog/pg_proc.h"
 #include "catalog/pg_trigger.h"
 #include "catalog/pg_type.h"
+#include "commands/dbcommands.h"
 #include "commands/defrem.h"
 #include "commands/trigger.h"
 #include "executor/executor.h"
@@ -37,6 +38,7 @@
 #include "utils/inval.h"
 #include "utils/lsyscache.h"
 #include "utils/memutils.h"
+#include "utils/relcache.h"
 #include "utils/syscache.h"
 
 
@@ -2922,65 +2924,133 @@ AfterTriggerSetState(ConstraintsSetStmt *stmt)
 
        foreach(l, stmt->constraints)
        {
-           char       *cname = strVal(lfirst(l));
+           RangeVar   *constraint = lfirst(l);
            ScanKeyData skey;
            SysScanDesc tgscan;
            HeapTuple   htup;
            bool        found;
+           List       *namespaceSearchList;
+           ListCell   *namespaceSearchCell;
 
-           /*
-            * Check that only named constraints are set explicitly
-            */
-           if (strlen(cname) == 0)
-               ereport(ERROR,
-                       (errcode(ERRCODE_INVALID_NAME),
-                   errmsg("unnamed constraints cannot be set explicitly")));
+           if (constraint->catalogname)
+           {
+               if (strcmp(constraint->catalogname, get_database_name(MyDatabaseId)) != 0)
+                   ereport(ERROR,
+                           (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                            errmsg("cross-database references are not implemented: \"%s.%s.%s\"",
+                                   constraint->catalogname, constraint->schemaname,
+                                   constraint->relname)));
+           }
 
-           /*
-            * Setup to scan pg_trigger by tgconstrname ...
+           /* 
+            * If we're given the schema name with the constraint, look only
+            * in that schema.  If given a bare constraint name, use the
+            * search path to find the first matching constraint.
             */
-           ScanKeyInit(&skey,
-                       Anum_pg_trigger_tgconstrname,
-                       BTEqualStrategyNumber, F_NAMEEQ,
-                       PointerGetDatum(cname));
-
-           tgscan = systable_beginscan(tgrel, TriggerConstrNameIndexId, true,
-                                       SnapshotNow, 1, &skey);
+           if (constraint->schemaname) {
+               Oid namespaceId = LookupExplicitNamespace(constraint->schemaname);
+               namespaceSearchList = list_make1_oid(namespaceId);
+           } else {
+               namespaceSearchList = fetch_search_path(true);
+           }
 
-           /*
-            * ... and search for the constraint trigger row
-            */
            found = false;
-
-           while (HeapTupleIsValid(htup = systable_getnext(tgscan)))
+           foreach(namespaceSearchCell, namespaceSearchList)
            {
-               Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(htup);
+               Oid searchNamespaceId = lfirst_oid(namespaceSearchCell);
+
+               /*
+                * Setup to scan pg_trigger by tgconstrname ...
+                */
+               ScanKeyInit(&skey,
+                           Anum_pg_trigger_tgconstrname,
+                           BTEqualStrategyNumber, F_NAMEEQ,
+                           PointerGetDatum(constraint->relname));
+
+               tgscan = systable_beginscan(tgrel, TriggerConstrNameIndexId, true,
+                                           SnapshotNow, 1, &skey);
 
                /*
-                * If we found some, check that they fit the deferrability but
-                * skip referential action ones, since they are silently never
-                * deferrable.
+                * ... and search for the constraint trigger row
                 */
-               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)
+               while (HeapTupleIsValid(htup = systable_getnext(tgscan)))
                {
-                   if (stmt->deferred && !pg_trigger->tgdeferrable)
-                       ereport(ERROR,
-                               (errcode(ERRCODE_WRONG_OBJECT_TYPE),
-                                errmsg("constraint \"%s\" is not deferrable",
-                                       cname)));
-                   oidlist = lappend_oid(oidlist, HeapTupleGetOid(htup));
+                   Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(htup);
+                   Relation constraintRel;
+                   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)
+                   {
+                       constraintRel = RelationIdGetRelation(pg_trigger->tgconstrrelid);
+                   } else {
+                       constraintRel = RelationIdGetRelation(pg_trigger->tgrelid);
+                   }
+                   constraintNamespaceId = RelationGetNamespace(constraintRel);
+                   RelationClose(constraintRel);
+
+                   /*
+                    * 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));
+                   }
+                   found = true;
                }
-               found = true;
+
+               systable_endscan(tgscan);
+
+               /*
+                * Once we've found a matching constraint we do not search
+                * later parts of the search path.
+                */
+               if (found)
+                   break;
+
            }
 
-           systable_endscan(tgscan);
+           list_free(namespaceSearchList);
 
            /*
             * Not found ?
@@ -2989,7 +3059,7 @@ AfterTriggerSetState(ConstraintsSetStmt *stmt)
                ereport(ERROR,
                        (errcode(ERRCODE_UNDEFINED_OBJECT),
                         errmsg("constraint \"%s\" does not exist",
-                               cname)));
+                               constraint->relname)));
        }
        heap_close(tgrel, AccessShareLock);
 
index 46aff33bc2242a9ff7e27dd96ab05b0554c5baa0..8870261e44134192c24eee648c3ef067160b67b0 100644 (file)
@@ -11,7 +11,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.542 2006/04/25 14:11:55 momjian Exp $
+ *   $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.543 2006/04/27 00:33:45 momjian Exp $
  *
  * HISTORY
  *   AUTHOR            DATE            MAJOR EVENT
@@ -1283,7 +1283,7 @@ ConstraintsSetStmt:
 
 constraints_set_list:
            ALL                                     { $$ = NIL; }
-           | name_list                             { $$ = $1; }
+           | qualified_name_list                   { $$ = $1; }
        ;
 
 constraints_set_mode:
index e4eb385df87c983a7d94162dadc1e18c1b3e19e7..2da0f6605dd2d3e76ff7fa2a45d9c2b557853dd8 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.307 2006/04/15 17:45:41 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.308 2006/04/27 00:33:46 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1782,7 +1782,7 @@ typedef struct LockStmt
 typedef struct ConstraintsSetStmt
 {
    NodeTag     type;
-   List       *constraints;    /* List of names as Value strings */
+   List       *constraints;    /* List of names as RangeVars */
    bool        deferred;
 } ConstraintsSetStmt;