]> granicus.if.org Git - postgresql/commitdiff
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;