*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
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,
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;
* 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,
* 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
{
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 */
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);
}
* 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;
attnums[i] = pkattno;
atttypids[i] = attnumTypeId(pkrel, pkattno);
+ opclasses[i] = indexStruct->indclass[i];
*attnamelist = lappend(*attnamelist,
makeString(pstrdup(NameStr(*attnumAttName(pkrel, pkattno)))));
}
*
* 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;
{
if (attnums[j] == indexStruct->indkey[i])
{
+ opclasses[j] = indexStruct->indclass[i];
found = true;
break;
}
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
-- 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 );
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
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;
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.
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
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;
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.
-- 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
-- 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);