Improve error message for the case where a requested foreign key constraint
authorTom Lane
Wed, 12 Aug 2009 23:00:12 +0000 (23:00 +0000)
committerTom Lane
Wed, 12 Aug 2009 23:00:12 +0000 (23:00 +0000)
does match some unique index on the referenced table, but that index is
only deferrably unique.  We were doing this nicely for the
default-to-primary-key case, but were being lazy for the other case.

Dean Rasheed

src/backend/commands/tablecmds.c

index f51f1f8c487dd03cd00797c415b37e95346b7f41..07bc3932003b10e52135319c32f3d1700334433c 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.296 2009/08/07 15:27:56 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.297 2009/08/12 23:00:12 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -5117,6 +5117,7 @@ transformFkeyCheckAttrs(Relation pkrel,
 {
    Oid         indexoid = InvalidOid;
    bool        found = false;
+   bool        found_deferrable = false;
    List       *indexoidlist;
    ListCell   *indexoidscan;
 
@@ -5143,12 +5144,11 @@ transformFkeyCheckAttrs(Relation pkrel,
        indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
 
        /*
-        * Must have the right number of columns; must be unique (non
-        * deferrable) and not a partial index; forget it if there are any
-        * expressions, too
+        * Must have the right number of columns; must be unique and not a
+        * partial index; forget it if there are any expressions, too
         */
        if (indexStruct->indnatts == numattrs &&
-           indexStruct->indisunique && indexStruct->indimmediate &&
+           indexStruct->indisunique &&
            heap_attisnull(indexTuple, Anum_pg_index_indpred) &&
            heap_attisnull(indexTuple, Anum_pg_index_indexprs))
        {
@@ -5198,6 +5198,21 @@ transformFkeyCheckAttrs(Relation pkrel,
                        break;
                }
            }
+
+           /*
+            * Refuse to use a deferrable unique/primary key.  This is per
+            * SQL spec, and there would be a lot of interesting semantic
+            * problems if we tried to allow it.
+            */
+           if (found && !indexStruct->indimmediate)
+           {
+               /*
+                * Remember that we found an otherwise matching index, so
+                * that we can generate a more appropriate error message.
+                */
+               found_deferrable = true;
+               found = false;
+           }
        }
        ReleaseSysCache(indexTuple);
        if (found)
@@ -5205,10 +5220,18 @@ transformFkeyCheckAttrs(Relation pkrel,
    }
 
    if (!found)
-       ereport(ERROR,
-               (errcode(ERRCODE_INVALID_FOREIGN_KEY),
-                errmsg("there is no unique constraint matching given keys for referenced table \"%s\"",
-                       RelationGetRelationName(pkrel))));
+   {
+       if (found_deferrable)
+           ereport(ERROR,
+                   (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+                    errmsg("cannot use a deferrable unique constraint for referenced table \"%s\"",
+                           RelationGetRelationName(pkrel))));
+       else
+           ereport(ERROR,
+                   (errcode(ERRCODE_INVALID_FOREIGN_KEY),
+                    errmsg("there is no unique constraint matching given keys for referenced table \"%s\"",
+                           RelationGetRelationName(pkrel))));
+   }
 
    list_free(indexoidlist);