]> granicus.if.org Git - postgresql/commitdiff
Generate a WARNING when the column types in a foreign key constraint are
authorTom Lane <tgl@sss.pgh.pa.us>
Sat, 13 Mar 2004 22:09:14 +0000 (22:09 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Sat, 13 Mar 2004 22:09:14 +0000 (22:09 +0000)
incompatible enough to prevent indexscanning the referenced table.  Also,
improve the error message that pops out when we can't implement the FK at
all for lack of a usable equality operator.  Fabien Coelho, with some review
by Tom Lane.

src/backend/commands/tablecmds.c
src/test/regress/expected/alter_table.out
src/test/regress/expected/foreign_key.out
src/test/regress/sql/foreign_key.sql

index 3b87e6223b0b7c66796151880e6c1db8faf7494d..25794ce93fec499b4fe76ade132dec8c6f6d853f 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.99 2004/02/15 21:01:39 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.100 2004/03/13 22:09:13 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -89,10 +89,12 @@ static void AlterTableAddForeignKeyConstraint(Relation rel,
 static int transformColumnNameList(Oid relId, List *colList,
                                                int16 *attnums, Oid *atttypids);
 static int transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid,
-                                                  List **attnamelist,
-                                                  int16 *attnums, Oid *atttypids);
+                                                                         List **attnamelist,
+                                                                         int16 *attnums, Oid *atttypids,
+                                                                         Oid *opclasses);
 static Oid transformFkeyCheckAttrs(Relation pkrel,
-                                               int numattrs, int16 *attnums);
+                                                                  int numattrs, int16 *attnums,
+                                                                  Oid *opclasses);
 static void validateForeignKeyConstraint(FkConstraint *fkconstraint,
                                                         Relation rel, Relation pkrel);
 static void createForeignKeyTriggers(Relation rel, FkConstraint *fkconstraint,
@@ -3016,6 +3018,7 @@ AlterTableAddForeignKeyConstraint(Relation rel, FkConstraint *fkconstraint)
        int16           fkattnum[INDEX_MAX_KEYS];
        Oid                     pktypoid[INDEX_MAX_KEYS];
        Oid                     fktypoid[INDEX_MAX_KEYS];
+       Oid         opclasses[INDEX_MAX_KEYS];
        int                     i;
        int                     numfks,
                                numpks;
@@ -3088,11 +3091,11 @@ AlterTableAddForeignKeyConstraint(Relation rel, FkConstraint *fkconstraint)
         * Look up the referencing attributes to make sure they exist, and
         * record their attnums and type OIDs.
         */
-       for (i = 0; i < INDEX_MAX_KEYS; i++)
-       {
-               pkattnum[i] = fkattnum[i] = 0;
-               pktypoid[i] = fktypoid[i] = InvalidOid;
-       }
+       MemSet(pkattnum, 0, sizeof(pkattnum));
+       MemSet(fkattnum, 0, sizeof(fkattnum));
+       MemSet(pktypoid, 0, sizeof(pktypoid));
+       MemSet(fktypoid, 0, sizeof(fktypoid));
+       MemSet(opclasses, 0, sizeof(opclasses));
 
        numfks = transformColumnNameList(RelationGetRelid(rel),
                                                                         fkconstraint->fk_attrs,
@@ -3102,13 +3105,15 @@ AlterTableAddForeignKeyConstraint(Relation rel, FkConstraint *fkconstraint)
         * If the attribute list for the referenced table was omitted, lookup
         * the definition of the primary key and use it.  Otherwise, validate
         * the supplied attribute list.  In either case, discover the index
-        * OID and the attnums and type OIDs of the attributes.
+        * OID and index opclasses, and the attnums and type OIDs of the
+        * attributes.
         */
        if (fkconstraint->pk_attrs == NIL)
        {
                numpks = transformFkeyGetPrimaryKey(pkrel, &indexOid,
                                                                                        &fkconstraint->pk_attrs,
-                                                                                       pkattnum, pktypoid);
+                                                                                       pkattnum, pktypoid,
+                                                                                       opclasses);
        }
        else
        {
@@ -3116,7 +3121,8 @@ AlterTableAddForeignKeyConstraint(Relation rel, FkConstraint *fkconstraint)
                                                                                 fkconstraint->pk_attrs,
                                                                                 pkattnum, pktypoid);
                /* Look for an index matching the column list */
-               indexOid = transformFkeyCheckAttrs(pkrel, numpks, pkattnum);
+               indexOid = transformFkeyCheckAttrs(pkrel, numpks, pkattnum,
+                                                                                  opclasses);
        }
 
        /* Be sure referencing and referenced column types are comparable */
@@ -3128,14 +3134,48 @@ AlterTableAddForeignKeyConstraint(Relation rel, FkConstraint *fkconstraint)
        for (i = 0; i < numpks; i++)
        {
                /*
-                * fktypoid[i] is the foreign key table's i'th element's type
-                * pktypoid[i] is the primary key table's i'th element's type
+                * pktypoid[i] is the primary key table's i'th key's type
+                * fktypoid[i] is the foreign key table's i'th key's type
                 *
-                * We let oper() do our work for us, including ereport(ERROR) if the
-                * types don't compare with =
+                * Note that we look for an operator with the PK type on the left;
+                * when the types are different this is critical because the PK index
+                * will need operators with the indexkey on the left.  (Ordinarily
+                * both commutator operators will exist if either does, but we won't
+                * get the right answer from the test below on opclass membership
+                * unless we select the proper operator.)
                 */
                Operator        o = oper(makeList1(makeString("=")),
-                                                        fktypoid[i], pktypoid[i], false);
+                                                        pktypoid[i], fktypoid[i], true);
+
+               if (o == NULL)
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_UNDEFINED_FUNCTION),
+                                        errmsg("foreign key constraint \"%s\" "
+                                                       "cannot be implemented",
+                                                       fkconstraint->constr_name),
+                                        errdetail("Key columns \"%s\" and \"%s\" "
+                                                          "are of incompatible types: %s and %s.",
+                                                          strVal(nth(i, fkconstraint->fk_attrs)),
+                                                          strVal(nth(i, fkconstraint->pk_attrs)),
+                                                          format_type_be(fktypoid[i]),
+                                                          format_type_be(pktypoid[i]))));
+
+               /*
+                * Check that the found operator is compatible with the PK index,
+                * and generate a warning if not, since otherwise costly seqscans
+                * will be incurred to check FK validity.
+                */
+               if (!op_in_opclass(oprid(o), opclasses[i]))
+                       ereport(WARNING, 
+                                       (errmsg("foreign key constraint \"%s\" "
+                                                       "will require costly sequential scans",
+                                                       fkconstraint->constr_name),
+                                        errdetail("Key columns \"%s\" and \"%s\" "
+                                                          "are of different types: %s and %s.",
+                                                          strVal(nth(i, fkconstraint->fk_attrs)),
+                                                          strVal(nth(i, fkconstraint->pk_attrs)),
+                                                          format_type_be(fktypoid[i]),
+                                                          format_type_be(pktypoid[i]))));
 
                ReleaseSysCache(o);
        }
@@ -3225,13 +3265,19 @@ transformColumnNameList(Oid relId, List *colList,
  * transformFkeyGetPrimaryKey -
  *
  *     Look up the names, attnums, and types of the primary key attributes
- *     for the pkrel.  Used when the column list in the REFERENCES specification
- *     is omitted.
+ *     for the pkrel.  Also return the index OID and index opclasses of the
+ *     index supporting the primary key.
+ *
+ *     All parameters except pkrel are output parameters.  Also, the function
+ *     return value is the number of attributes in the primary key.
+ *
+ *     Used when the column list in the REFERENCES specification is omitted.
  */
 static int
 transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid,
                                                   List **attnamelist,
-                                                  int16 *attnums, Oid *atttypids)
+                                                  int16 *attnums, Oid *atttypids,
+                                                  Oid *opclasses)
 {
        List       *indexoidlist,
                           *indexoidscan;
@@ -3287,6 +3333,7 @@ transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid,
 
                attnums[i] = pkattno;
                atttypids[i] = attnumTypeId(pkrel, pkattno);
+               opclasses[i] = indexStruct->indclass[i];
                *attnamelist = lappend(*attnamelist,
                   makeString(pstrdup(NameStr(*attnumAttName(pkrel, pkattno)))));
        }
@@ -3301,11 +3348,13 @@ transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid,
  *
  *     Make sure that the attributes of a referenced table belong to a unique
  *     (or primary key) constraint.  Return the OID of the index supporting
- *     the constraint.
+ *     the constraint, as well as the opclasses associated with the index
+ *     columns.
  */
 static Oid
 transformFkeyCheckAttrs(Relation pkrel,
-                                               int numattrs, int16 *attnums)
+                                               int numattrs, int16 *attnums,
+                                               Oid *opclasses)                 /* output parameter */
 {
        Oid                     indexoid = InvalidOid;
        bool            found = false;
@@ -3370,6 +3419,7 @@ transformFkeyCheckAttrs(Relation pkrel,
                                        {
                                                if (attnums[j] == indexStruct->indkey[i])
                                                {
+                                                       opclasses[j] = indexStruct->indclass[i];
                                                        found = true;
                                                        break;
                                                }
index c9350de7df027a703b98b47f032d9a9af18c6dab..0396351f1ff413bd0df2f7dfb3a32148a2c27d26 100644 (file)
@@ -194,20 +194,24 @@ NOTICE:  CREATE TABLE / PRIMARY KEY will create implicit index "pktable_pkey" fo
 CREATE TEMP TABLE FKTABLE (ftest1 inet);
 -- This next should fail, because inet=int does not exist
 ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1) references pktable;
-ERROR:  operator does not exist: inet = integer
-HINT:  No operator matches the given name and argument type(s). You may need to add explicit type casts.
+ERROR:  foreign key constraint "$1" cannot be implemented
+DETAIL:  Key columns "ftest1" and "ptest1" are of incompatible types: inet and integer.
 -- This should also fail for the same reason, but here we
 -- give the column name
 ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1) references pktable(ptest1);
-ERROR:  operator does not exist: inet = integer
-HINT:  No operator matches the given name and argument type(s). You may need to add explicit type casts.
+ERROR:  foreign key constraint "$1" cannot be implemented
+DETAIL:  Key columns "ftest1" and "ptest1" are of incompatible types: inet and integer.
 -- This should succeed, even though they are different types
 -- because varchar=int does exist
 DROP TABLE FKTABLE;
 CREATE TEMP TABLE FKTABLE (ftest1 varchar);
 ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1) references pktable;
+WARNING:  foreign key constraint "$1" will require costly sequential scans
+DETAIL:  Key columns "ftest1" and "ptest1" are of different types: character varying and integer.
 -- As should this
 ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1) references pktable(ptest1);
+WARNING:  foreign key constraint "$2" will require costly sequential scans
+DETAIL:  Key columns "ftest1" and "ptest1" are of different types: character varying and integer.
 DROP TABLE pktable cascade;
 NOTICE:  drop cascades to constraint $2 on table fktable
 NOTICE:  drop cascades to constraint $1 on table fktable
@@ -218,27 +222,27 @@ NOTICE:  CREATE TABLE / PRIMARY KEY will create implicit index "pktable_pkey" fo
 -- This should fail, because we just chose really odd types
 CREATE TEMP TABLE FKTABLE (ftest1 cidr, ftest2 timestamp);
 ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1, ftest2) references pktable;
-ERROR:  operator does not exist: cidr = integer
-HINT:  No operator matches the given name and argument type(s). You may need to add explicit type casts.
+ERROR:  foreign key constraint "$1" cannot be implemented
+DETAIL:  Key columns "ftest1" and "ptest1" are of incompatible types: cidr and integer.
 DROP TABLE FKTABLE;
 -- Again, so should this...
 CREATE TEMP TABLE FKTABLE (ftest1 cidr, ftest2 timestamp);
 ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1, ftest2)
      references pktable(ptest1, ptest2);
-ERROR:  operator does not exist: cidr = integer
-HINT:  No operator matches the given name and argument type(s). You may need to add explicit type casts.
+ERROR:  foreign key constraint "$1" cannot be implemented
+DETAIL:  Key columns "ftest1" and "ptest1" are of incompatible types: cidr and integer.
 DROP TABLE FKTABLE;
 -- This fails because we mixed up the column ordering
 CREATE TEMP TABLE FKTABLE (ftest1 int, ftest2 inet);
 ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1, ftest2)
      references pktable(ptest2, ptest1);
-ERROR:  operator does not exist: integer = inet
-HINT:  No operator matches the given name and argument type(s). You may need to add explicit type casts.
+ERROR:  foreign key constraint "$1" cannot be implemented
+DETAIL:  Key columns "ftest1" and "ptest2" are of incompatible types: integer and inet.
 -- As does this...
 ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest2, ftest1)
      references pktable(ptest1, ptest2);
-ERROR:  operator does not exist: inet = integer
-HINT:  No operator matches the given name and argument type(s). You may need to add explicit type casts.
+ERROR:  foreign key constraint "$1" cannot be implemented
+DETAIL:  Key columns "ftest2" and "ptest1" are of incompatible types: inet and integer.
 -- temp tables should go away by themselves, need not drop them.
 -- test check constraint adding
 create table atacc1 ( test int );
index 57a4ae16471adae7f5071106efd6cf77563db599..bd4bccfc9d460c22d35fea5bfde828d1d75353fa 100644 (file)
@@ -728,19 +728,23 @@ CREATE TABLE PKTABLE (ptest1 int PRIMARY KEY);
 NOTICE:  CREATE TABLE / PRIMARY KEY will create implicit index "pktable_pkey" for table "pktable"
 -- This next should fail, because inet=int does not exist
 CREATE TABLE FKTABLE (ftest1 inet REFERENCES pktable);
-ERROR:  operator does not exist: inet = integer
-HINT:  No operator matches the given name and argument type(s). You may need to add explicit type casts.
+ERROR:  foreign key constraint "$1" cannot be implemented
+DETAIL:  Key columns "ftest1" and "ptest1" are of incompatible types: inet and integer.
 -- This should also fail for the same reason, but here we
 -- give the column name
 CREATE TABLE FKTABLE (ftest1 inet REFERENCES pktable(ptest1));
-ERROR:  operator does not exist: inet = integer
-HINT:  No operator matches the given name and argument type(s). You may need to add explicit type casts.
--- This should succeed, even though they are different types
--- because varchar=int does exist
+ERROR:  foreign key constraint "$1" cannot be implemented
+DETAIL:  Key columns "ftest1" and "ptest1" are of incompatible types: inet and integer.
+-- This should succeed (with a warning), even though they are different types
+-- because int=varchar does exist
 CREATE TABLE FKTABLE (ftest1 varchar REFERENCES pktable);
+WARNING:  foreign key constraint "$1" will require costly sequential scans
+DETAIL:  Key columns "ftest1" and "ptest1" are of different types: character varying and integer.
 DROP TABLE FKTABLE;
 -- As should this
 CREATE TABLE FKTABLE (ftest1 varchar REFERENCES pktable(ptest1));
+WARNING:  foreign key constraint "$1" will require costly sequential scans
+DETAIL:  Key columns "ftest1" and "ptest1" are of different types: character varying and integer.
 DROP TABLE FKTABLE;
 DROP TABLE PKTABLE;
 -- Two columns, two tables
@@ -748,24 +752,24 @@ CREATE TABLE PKTABLE (ptest1 int, ptest2 inet, PRIMARY KEY(ptest1, ptest2));
 NOTICE:  CREATE TABLE / PRIMARY KEY will create implicit index "pktable_pkey" for table "pktable"
 -- This should fail, because we just chose really odd types
 CREATE TABLE FKTABLE (ftest1 cidr, ftest2 timestamp, FOREIGN KEY(ftest1, ftest2) REFERENCES pktable);
-ERROR:  operator does not exist: cidr = integer
-HINT:  No operator matches the given name and argument type(s). You may need to add explicit type casts.
+ERROR:  foreign key constraint "$1" cannot be implemented
+DETAIL:  Key columns "ftest1" and "ptest1" are of incompatible types: cidr and integer.
 -- Again, so should this...
 CREATE TABLE FKTABLE (ftest1 cidr, ftest2 timestamp, FOREIGN KEY(ftest1, ftest2) REFERENCES pktable(ptest1, ptest2));
-ERROR:  operator does not exist: cidr = integer
-HINT:  No operator matches the given name and argument type(s). You may need to add explicit type casts.
+ERROR:  foreign key constraint "$1" cannot be implemented
+DETAIL:  Key columns "ftest1" and "ptest1" are of incompatible types: cidr and integer.
 -- This fails because we mixed up the column ordering
 CREATE TABLE FKTABLE (ftest1 int, ftest2 inet, FOREIGN KEY(ftest2, ftest1) REFERENCES pktable);
-ERROR:  operator does not exist: inet = integer
-HINT:  No operator matches the given name and argument type(s). You may need to add explicit type casts.
+ERROR:  foreign key constraint "$1" cannot be implemented
+DETAIL:  Key columns "ftest2" and "ptest1" are of incompatible types: inet and integer.
 -- As does this...
 CREATE TABLE FKTABLE (ftest1 int, ftest2 inet, FOREIGN KEY(ftest2, ftest1) REFERENCES pktable(ptest1, ptest2));
-ERROR:  operator does not exist: inet = integer
-HINT:  No operator matches the given name and argument type(s). You may need to add explicit type casts.
+ERROR:  foreign key constraint "$1" cannot be implemented
+DETAIL:  Key columns "ftest2" and "ptest1" are of incompatible types: inet and integer.
 -- And again..
 CREATE TABLE FKTABLE (ftest1 int, ftest2 inet, FOREIGN KEY(ftest1, ftest2) REFERENCES pktable(ptest2, ptest1));
-ERROR:  operator does not exist: integer = inet
-HINT:  No operator matches the given name and argument type(s). You may need to add explicit type casts.
+ERROR:  foreign key constraint "$1" cannot be implemented
+DETAIL:  Key columns "ftest1" and "ptest2" are of incompatible types: integer and inet.
 -- This works...
 CREATE TABLE FKTABLE (ftest1 int, ftest2 inet, FOREIGN KEY(ftest2, ftest1) REFERENCES pktable(ptest2, ptest1));
 DROP TABLE FKTABLE;
@@ -788,20 +792,20 @@ DROP TABLE PKTABLE;
 CREATE TABLE PKTABLE (ptest1 int, ptest2 inet, ptest3 int, ptest4 inet, PRIMARY KEY(ptest1, ptest2), FOREIGN KEY(ptest3,
 ptest4) REFERENCES pktable(ptest2, ptest1));
 NOTICE:  CREATE TABLE / PRIMARY KEY will create implicit index "pktable_pkey" for table "pktable"
-ERROR:  operator does not exist: integer = inet
-HINT:  No operator matches the given name and argument type(s). You may need to add explicit type casts.
+ERROR:  foreign key constraint "$1" cannot be implemented
+DETAIL:  Key columns "ptest3" and "ptest2" are of incompatible types: integer and inet.
 -- Nor should this... (same reason, we have 4,3 referencing 1,2 which mismatches types
 CREATE TABLE PKTABLE (ptest1 int, ptest2 inet, ptest3 int, ptest4 inet, PRIMARY KEY(ptest1, ptest2), FOREIGN KEY(ptest4,
 ptest3) REFERENCES pktable(ptest1, ptest2));
 NOTICE:  CREATE TABLE / PRIMARY KEY will create implicit index "pktable_pkey" for table "pktable"
-ERROR:  operator does not exist: inet = integer
-HINT:  No operator matches the given name and argument type(s). You may need to add explicit type casts.
+ERROR:  foreign key constraint "$1" cannot be implemented
+DETAIL:  Key columns "ptest4" and "ptest1" are of incompatible types: inet and integer.
 -- Not this one either... Same as the last one except we didn't defined the columns being referenced.
 CREATE TABLE PKTABLE (ptest1 int, ptest2 inet, ptest3 int, ptest4 inet, PRIMARY KEY(ptest1, ptest2), FOREIGN KEY(ptest4,
 ptest3) REFERENCES pktable);
 NOTICE:  CREATE TABLE / PRIMARY KEY will create implicit index "pktable_pkey" for table "pktable"
-ERROR:  operator does not exist: inet = integer
-HINT:  No operator matches the given name and argument type(s). You may need to add explicit type casts.
+ERROR:  foreign key constraint "$1" cannot be implemented
+DETAIL:  Key columns "ptest4" and "ptest1" are of incompatible types: inet and integer.
 --
 -- Now some cases with inheritance
 -- Basic 2 table case: 1 column of matching types.
@@ -897,21 +901,21 @@ create table pktable(ptest1 inet, primary key(base1, ptest1)) inherits (pktable_
 NOTICE:  CREATE TABLE / PRIMARY KEY will create implicit index "pktable_pkey" for table "pktable"
 -- just generally bad types (with and without column references on the referenced table)
 create table fktable(ftest1 cidr, ftest2 int[], foreign key (ftest1, ftest2) references pktable);
-ERROR:  operator does not exist: cidr = integer
-HINT:  No operator matches the given name and argument type(s). You may need to add explicit type casts.
+ERROR:  foreign key constraint "$1" cannot be implemented
+DETAIL:  Key columns "ftest1" and "base1" are of incompatible types: cidr and integer.
 create table fktable(ftest1 cidr, ftest2 int[], foreign key (ftest1, ftest2) references pktable(base1, ptest1));
-ERROR:  operator does not exist: cidr = integer
-HINT:  No operator matches the given name and argument type(s). You may need to add explicit type casts.
+ERROR:  foreign key constraint "$1" cannot be implemented
+DETAIL:  Key columns "ftest1" and "base1" are of incompatible types: cidr and integer.
 -- let's mix up which columns reference which
 create table fktable(ftest1 int, ftest2 inet, foreign key(ftest2, ftest1) references pktable);
-ERROR:  operator does not exist: inet = integer
-HINT:  No operator matches the given name and argument type(s). You may need to add explicit type casts.
+ERROR:  foreign key constraint "$1" cannot be implemented
+DETAIL:  Key columns "ftest2" and "base1" are of incompatible types: inet and integer.
 create table fktable(ftest1 int, ftest2 inet, foreign key(ftest2, ftest1) references pktable(base1, ptest1));
-ERROR:  operator does not exist: inet = integer
-HINT:  No operator matches the given name and argument type(s). You may need to add explicit type casts.
+ERROR:  foreign key constraint "$1" cannot be implemented
+DETAIL:  Key columns "ftest2" and "base1" are of incompatible types: inet and integer.
 create table fktable(ftest1 int, ftest2 inet, foreign key(ftest1, ftest2) references pktable(ptest1, base1));
-ERROR:  operator does not exist: integer = inet
-HINT:  No operator matches the given name and argument type(s). You may need to add explicit type casts.
+ERROR:  foreign key constraint "$1" cannot be implemented
+DETAIL:  Key columns "ftest1" and "ptest1" are of incompatible types: integer and inet.
 drop table pktable;
 drop table pktable_base;
 -- 2 columns (1 table), mismatched types
@@ -919,23 +923,23 @@ create table pktable_base(base1 int not null, base2 int);
 create table pktable(ptest1 inet, ptest2 inet[], primary key(base1, ptest1), foreign key(base2, ptest2) references
                                              pktable(base1, ptest1)) inherits (pktable_base);
 NOTICE:  CREATE TABLE / PRIMARY KEY will create implicit index "pktable_pkey" for table "pktable"
-ERROR:  operator does not exist: inet[] = inet
-HINT:  No operator matches the given name and argument type(s). You may need to add explicit type casts.
+ERROR:  foreign key constraint "$1" cannot be implemented
+DETAIL:  Key columns "ptest2" and "ptest1" are of incompatible types: inet[] and inet.
 create table pktable(ptest1 inet, ptest2 inet, primary key(base1, ptest1), foreign key(base2, ptest2) references
                                              pktable(ptest1, base1)) inherits (pktable_base);
 NOTICE:  CREATE TABLE / PRIMARY KEY will create implicit index "pktable_pkey" for table "pktable"
-ERROR:  operator does not exist: integer = inet
-HINT:  No operator matches the given name and argument type(s). You may need to add explicit type casts.
+ERROR:  foreign key constraint "$1" cannot be implemented
+DETAIL:  Key columns "base2" and "ptest1" are of incompatible types: integer and inet.
 create table pktable(ptest1 inet, ptest2 inet, primary key(base1, ptest1), foreign key(ptest2, base2) references
                                              pktable(base1, ptest1)) inherits (pktable_base);
 NOTICE:  CREATE TABLE / PRIMARY KEY will create implicit index "pktable_pkey" for table "pktable"
-ERROR:  operator does not exist: inet = integer
-HINT:  No operator matches the given name and argument type(s). You may need to add explicit type casts.
+ERROR:  foreign key constraint "$1" cannot be implemented
+DETAIL:  Key columns "ptest2" and "base1" are of incompatible types: inet and integer.
 create table pktable(ptest1 inet, ptest2 inet, primary key(base1, ptest1), foreign key(ptest2, base2) references
                                              pktable(base1, ptest1)) inherits (pktable_base);
 NOTICE:  CREATE TABLE / PRIMARY KEY will create implicit index "pktable_pkey" for table "pktable"
-ERROR:  operator does not exist: inet = integer
-HINT:  No operator matches the given name and argument type(s). You may need to add explicit type casts.
+ERROR:  foreign key constraint "$1" cannot be implemented
+DETAIL:  Key columns "ptest2" and "base1" are of incompatible types: inet and integer.
 drop table pktable;
 ERROR:  table "pktable" does not exist
 drop table pktable_base;
@@ -1034,3 +1038,70 @@ INSERT INTO fktable VALUES (100, 200);
 COMMIT;
 ERROR:  insert or update on table "fktable" violates foreign key constraint "$1"
 DETAIL:  Key (fk)=(200) is not present in table "pktable".
+-- test notice about expensive referential integrity checks,
+-- where the index cannot be used because of type incompatibilities.
+CREATE TEMP TABLE pktable (
+        id1     INT4 PRIMARY KEY,
+        id2     VARCHAR(4) UNIQUE,
+        id3     REAL UNIQUE,
+        UNIQUE(id1, id2, id3)
+);
+NOTICE:  CREATE TABLE / PRIMARY KEY will create implicit index "pktable_pkey" for table "pktable"
+NOTICE:  CREATE TABLE / UNIQUE will create implicit index "pktable_id2_key" for table "pktable"
+NOTICE:  CREATE TABLE / UNIQUE will create implicit index "pktable_id3_key" for table "pktable"
+NOTICE:  CREATE TABLE / UNIQUE will create implicit index "pktable_id1_key" for table "pktable"
+CREATE TEMP TABLE fktable (
+        x1      INT4 REFERENCES pktable(id1),
+        x2      VARCHAR(4) REFERENCES pktable(id2),
+        x3      REAL REFERENCES pktable(id3),
+        x4      TEXT,
+        x5      INT2
+);
+-- check individual constraints with alter table.
+-- should generate warnings
+ALTER TABLE fktable ADD CONSTRAINT fk_2_3
+FOREIGN KEY (x2) REFERENCES pktable(id3);
+WARNING:  foreign key constraint "fk_2_3" will require costly sequential scans
+DETAIL:  Key columns "x2" and "id3" are of different types: character varying and real.
+ALTER TABLE fktable ADD CONSTRAINT fk_2_1
+FOREIGN KEY (x2) REFERENCES pktable(id1);
+WARNING:  foreign key constraint "fk_2_1" will require costly sequential scans
+DETAIL:  Key columns "x2" and "id1" are of different types: character varying and integer.
+ALTER TABLE fktable ADD CONSTRAINT fk_3_1
+FOREIGN KEY (x3) REFERENCES pktable(id1);
+WARNING:  foreign key constraint "fk_3_1" will require costly sequential scans
+DETAIL:  Key columns "x3" and "id1" are of different types: real and integer.
+-- should NOT generate warnings
+-- int4 promotes to text, so this is ok
+ALTER TABLE fktable ADD CONSTRAINT fk_1_2
+FOREIGN KEY (x1) REFERENCES pktable(id2);
+-- int4 promotes to real
+ALTER TABLE fktable ADD CONSTRAINT fk_1_3
+FOREIGN KEY (x1) REFERENCES pktable(id3);
+-- text is compatible with varchar
+ALTER TABLE fktable ADD CONSTRAINT fk_4_2
+FOREIGN KEY (x4) REFERENCES pktable(id2);
+-- int2 is part of int4 opclass as of 7.5
+ALTER TABLE fktable ADD CONSTRAINT fk_5_1
+FOREIGN KEY (x5) REFERENCES pktable(id1);
+-- check multikey cases, especially out-of-order column lists
+-- no warnings here
+ALTER TABLE fktable ADD CONSTRAINT fk_123_123
+FOREIGN KEY (x1,x2,x3) REFERENCES pktable(id1,id2,id3);
+ALTER TABLE fktable ADD CONSTRAINT fk_213_213
+FOREIGN KEY (x2,x1,x3) REFERENCES pktable(id2,id1,id3);
+ALTER TABLE fktable ADD CONSTRAINT fk_253_213
+FOREIGN KEY (x2,x5,x3) REFERENCES pktable(id2,id1,id3);
+-- warnings here
+ALTER TABLE fktable ADD CONSTRAINT fk_123_231
+FOREIGN KEY (x1,x2,x3) REFERENCES pktable(id2,id3,id1);
+WARNING:  foreign key constraint "fk_123_231" will require costly sequential scans
+DETAIL:  Key columns "x2" and "id3" are of different types: character varying and real.
+WARNING:  foreign key constraint "fk_123_231" will require costly sequential scans
+DETAIL:  Key columns "x3" and "id1" are of different types: real and integer.
+ALTER TABLE fktable ADD CONSTRAINT fk_241_132
+FOREIGN KEY (x2,x4,x1) REFERENCES pktable(id1,id3,id2);
+WARNING:  foreign key constraint "fk_241_132" will require costly sequential scans
+DETAIL:  Key columns "x2" and "id1" are of different types: character varying and integer.
+WARNING:  foreign key constraint "fk_241_132" will require costly sequential scans
+DETAIL:  Key columns "x4" and "id3" are of different types: text and real.
index 10e4da0baf08f4033a2e7c24ddf8f3be3f370698..34fb787680d8ead35ffa30a1f3b056b7358dd560 100644 (file)
@@ -443,8 +443,8 @@ CREATE TABLE FKTABLE (ftest1 inet REFERENCES pktable);
 -- This should also fail for the same reason, but here we
 -- give the column name
 CREATE TABLE FKTABLE (ftest1 inet REFERENCES pktable(ptest1));
--- This should succeed, even though they are different types
--- because varchar=int does exist
+-- This should succeed (with a warning), even though they are different types
+-- because int=varchar does exist
 CREATE TABLE FKTABLE (ftest1 varchar REFERENCES pktable);
 DROP TABLE FKTABLE;
 -- As should this
@@ -698,3 +698,73 @@ INSERT INTO fktable VALUES (100, 200);
 
 -- error here on commit
 COMMIT;
+
+-- test notice about expensive referential integrity checks,
+-- where the index cannot be used because of type incompatibilities.
+
+CREATE TEMP TABLE pktable (
+        id1     INT4 PRIMARY KEY,
+        id2     VARCHAR(4) UNIQUE,
+        id3     REAL UNIQUE,
+        UNIQUE(id1, id2, id3)
+);
+
+CREATE TEMP TABLE fktable (
+        x1      INT4 REFERENCES pktable(id1),
+        x2      VARCHAR(4) REFERENCES pktable(id2),
+        x3      REAL REFERENCES pktable(id3),
+        x4      TEXT,
+        x5      INT2
+);
+
+-- check individual constraints with alter table.
+
+-- should generate warnings
+
+ALTER TABLE fktable ADD CONSTRAINT fk_2_3
+FOREIGN KEY (x2) REFERENCES pktable(id3);
+
+ALTER TABLE fktable ADD CONSTRAINT fk_2_1
+FOREIGN KEY (x2) REFERENCES pktable(id1);
+
+ALTER TABLE fktable ADD CONSTRAINT fk_3_1
+FOREIGN KEY (x3) REFERENCES pktable(id1);
+
+-- should NOT generate warnings
+
+-- int4 promotes to text, so this is ok
+ALTER TABLE fktable ADD CONSTRAINT fk_1_2
+FOREIGN KEY (x1) REFERENCES pktable(id2);
+
+-- int4 promotes to real
+ALTER TABLE fktable ADD CONSTRAINT fk_1_3
+FOREIGN KEY (x1) REFERENCES pktable(id3);
+
+-- text is compatible with varchar
+ALTER TABLE fktable ADD CONSTRAINT fk_4_2
+FOREIGN KEY (x4) REFERENCES pktable(id2);
+
+-- int2 is part of int4 opclass as of 7.5
+ALTER TABLE fktable ADD CONSTRAINT fk_5_1
+FOREIGN KEY (x5) REFERENCES pktable(id1);
+
+-- check multikey cases, especially out-of-order column lists
+
+-- no warnings here
+
+ALTER TABLE fktable ADD CONSTRAINT fk_123_123
+FOREIGN KEY (x1,x2,x3) REFERENCES pktable(id1,id2,id3);
+
+ALTER TABLE fktable ADD CONSTRAINT fk_213_213
+FOREIGN KEY (x2,x1,x3) REFERENCES pktable(id2,id1,id3);
+
+ALTER TABLE fktable ADD CONSTRAINT fk_253_213
+FOREIGN KEY (x2,x5,x3) REFERENCES pktable(id2,id1,id3);
+
+-- warnings here
+
+ALTER TABLE fktable ADD CONSTRAINT fk_123_231
+FOREIGN KEY (x1,x2,x3) REFERENCES pktable(id2,id3,id1);
+
+ALTER TABLE fktable ADD CONSTRAINT fk_241_132
+FOREIGN KEY (x2,x4,x1) REFERENCES pktable(id1,id3,id2);