diff --git a/src/backend/commands/command.c b/src/backend/commands/command.c index 13a78f1177..cd7e1d2952 100644 --- a/src/backend/commands/command.c +++ b/src/backend/commands/command.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/Attic/command.c,v 1.126 2001/05/07 00:43:17 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/Attic/command.c,v 1.127 2001/05/09 21:10:38 momjian Exp $ * * NOTES * The PerformAddAttribute() code, like most of the relation @@ -39,6 +39,7 @@ #include "parser/parse_expr.h" #include "parser/parse_clause.h" #include "parser/parse_relation.h" +#include "parser/parse_oper.h" #include "nodes/makefuncs.h" #include "optimizer/planmain.h" #include "optimizer/clauses.h" @@ -1342,6 +1343,13 @@ AlterTableAddConstraint(char *relationName, int i; bool found = false; + Oid fktypoid[INDEX_MAX_KEYS]; + Oid pktypoid[INDEX_MAX_KEYS]; + int attloc; + + for (i=0; ipktable_name) && !is_temp_rel_name(relationName)) elog(ERROR, "ALTER TABLE / ADD CONSTRAINT: Unable to reference temporary table from permanent table constraint."); @@ -1403,6 +1411,7 @@ AlterTableAddConstraint(char *relationName, found = false; else { + attloc=0; /* go through the fkconstraint->pk_attrs list */ foreach(attrl, fkconstraint->pk_attrs) { @@ -1419,6 +1428,11 @@ AlterTableAddConstraint(char *relationName, if (strcmp(name, attr->name) == 0) { + /* We get the type of this attribute here and + * store it so we can use it later for making + * sure the types are comparable. + */ + pktypoid[attloc++]=rel_attrs[pkattno-1]->atttypid; found = true; break; } @@ -1448,6 +1462,7 @@ AlterTableAddConstraint(char *relationName, Ident *fkattr; found = false; + attloc = 0; foreach(fkattrs, fkconstraint->fk_attrs) { int count; @@ -1460,6 +1475,11 @@ AlterTableAddConstraint(char *relationName, if (strcmp(name, fkattr->name) == 0) { + /* + * Here once again we get the types, this + * time for the fk table's attributes + */ + fktypoid[attloc++]=rel->rd_att->attrs[count]->atttypid; found = true; break; } @@ -1471,6 +1491,17 @@ AlterTableAddConstraint(char *relationName, elog(ERROR, "columns referenced in foreign key constraint not found."); } + for (i=0; i < INDEX_MAX_KEYS && fktypoid[i] !=0; i++) { + /* + * fktypoid[i] is the foreign key table's i'th element's type oid + * pktypoid[i] is the primary key table's i'th element's type oid + * We let oper() do our work for us, including elog(ERROR) if the + * types can't compare with = + */ + Operator o=oper("=", fktypoid[i], pktypoid[i], false); + ReleaseSysCache(o); + } + trig.tgoid = 0; if (fkconstraint->constr_name) trig.tgname = fkconstraint->constr_name; diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c index 76cc095bc4..c5d9b484c6 100644 --- a/src/backend/parser/analyze.c +++ b/src/backend/parser/analyze.c @@ -6,7 +6,7 @@ * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.184 2001/05/07 00:43:22 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.185 2001/05/09 21:10:39 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -24,6 +24,8 @@ #include "parser/parse_agg.h" #include "parser/parse_clause.h" #include "parser/parse_coerce.h" +#include "parser/parse_expr.h" +#include "parser/parse_oper.h" #include "parser/parse_relation.h" #include "parser/parse_target.h" #include "parser/parse_type.h" @@ -52,10 +54,10 @@ static Query *transformAlterTableStmt(ParseState *pstate, AlterTableStmt *stmt); static List *getSetColTypes(ParseState *pstate, Node *node); static void transformForUpdate(Query *qry, List *forUpdate); -static void transformFkeyGetPrimaryKey(FkConstraint *fkconstraint); +static void transformFkeyGetPrimaryKey(FkConstraint *fkconstraint, Oid *pktypoid); static void transformConstraintAttrs(List *constraintList); static void transformColumnType(ParseState *pstate, ColumnDef *column); -static void transformFkeyCheckAttrs(FkConstraint *fkconstraint); +static void transformFkeyCheckAttrs(FkConstraint *fkconstraint, Oid *pktypoid); static void release_pstate_resources(ParseState *pstate); static FromExpr *makeFromExpr(List *fromlist, Node *quals); @@ -1139,7 +1141,13 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt) List *fk_attr; List *pk_attr; Ident *id; + Oid pktypoid[INDEX_MAX_KEYS]; + Oid fktypoid[INDEX_MAX_KEYS]; + int i; + for (i=0; ifk_attrs != NIL) { int found = 0; + int attnum=0; List *cols; List *fkattrs; Ident *fkattr = NULL; @@ -1174,46 +1183,50 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt) col = lfirst(cols); if (strcmp(col->colname, fkattr->name) == 0) { + char *buff=TypeNameToInternalName(col->typename); + Oid type=typenameTypeId(buff); + if (!OidIsValid(type)) { + elog(ERROR, "Unable to lookup type %s", col->typename->name); + } + fktypoid[attnum++]=type; found = 1; break; } } - if (!found) + if (!found) { + List *inher; + List *inhRelnames = stmt->inhRelnames; + Relation rel; + + foreach(inher, inhRelnames) + { + Value *inh = lfirst(inher); + int count; + + Assert(IsA(inh, String)); + rel = heap_openr(strVal(inh), AccessShareLock); + if (rel->rd_rel->relkind != RELKIND_RELATION) + elog(ERROR, "inherited table \"%s\" is not a relation", + strVal(inh)); + for (count = 0; count < rel->rd_att->natts; count++) + { + char *name = NameStr(rel->rd_att->attrs[count]->attname); + + if (strcmp(fkattr->name, name) == 0) + { + fktypoid[attnum++]=rel->rd_att->attrs[count]->atttypid; + found = 1; + break; + } + } + heap_close(rel, NoLock); + if (found) + break; + } + } + if (!found) break; } - if (!found) - { /* try inherited tables */ - List *inher; - List *inhRelnames = stmt->inhRelnames; - Relation rel; - - foreach(inher, inhRelnames) - { - Value *inh = lfirst(inher); - int count; - - Assert(IsA(inh, String)); - rel = heap_openr(strVal(inh), AccessShareLock); - if (rel->rd_rel->relkind != RELKIND_RELATION) - elog(ERROR, "inherited table \"%s\" is not a relation", - strVal(inh)); - for (count = 0; count < rel->rd_att->natts; count++) - { - char *name = NameStr(rel->rd_att->attrs[count]->attname); - - if (strcmp(fkattr->name, name) == 0) - { - found = 1; - break; - } - } - heap_close(rel, NoLock); - if (found) - break; - } - } - else - found = 1; if (!found) elog(ERROR, "columns referenced in foreign key constraint not found."); } @@ -1228,13 +1241,16 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt) if (fkconstraint->fk_attrs != NIL && fkconstraint->pk_attrs == NIL) { if (strcmp(fkconstraint->pktable_name, stmt->relname) != 0) - transformFkeyGetPrimaryKey(fkconstraint); + transformFkeyGetPrimaryKey(fkconstraint, pktypoid); else if (pkey != NULL) { List *pkey_attr = pkey->indexParams; List *attr; + List *findattr; IndexElem *ielem; Ident *pkattr; + int attnum=0; + ColumnDef *col; foreach(attr, pkey_attr) { @@ -1244,6 +1260,18 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt) pkattr->indirection = NIL; pkattr->isRel = false; fkconstraint->pk_attrs = lappend(fkconstraint->pk_attrs, pkattr); + foreach (findattr, stmt->tableElts) { + col=lfirst(findattr); + if (strcmp(col->colname, ielem->name)==0) { + char *buff=TypeNameToInternalName(col->typename); + Oid type=typenameTypeId(buff); + if (!OidIsValid(type)) { + elog(ERROR, "Unable to lookup type %s", col->typename->name); + } + pktypoid[attnum++]=type; /* need to convert typename */ + break; + } + } } } else @@ -1255,7 +1283,7 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt) else { if (strcmp(fkconstraint->pktable_name, stmt->relname) != 0) - transformFkeyCheckAttrs(fkconstraint); + transformFkeyCheckAttrs(fkconstraint, pktypoid); else { /* Get a unique/pk constraint from above */ @@ -1268,11 +1296,14 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt) IndexElem *indparm; List *indparms; List *pkattrs; + List *findattr; + ColumnDef *col; Ident *pkattr; if (ind->unique) { int count = 0; + int attnum=0; foreach(indparms, ind->indexParams) count++; @@ -1289,7 +1320,43 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt) indparm = lfirst(indparms); if (strcmp(indparm->name, pkattr->name) == 0) { - found = 1; + foreach (findattr, stmt->tableElts) { + col=lfirst(findattr); + if (strcmp(col->colname, indparm->name)==0) { + char *buff=TypeNameToInternalName(col->typename); + Oid type=typenameTypeId(buff); + if (!OidIsValid(type)) { + elog(ERROR, "Unable to lookup type %s", col->typename->name); + } + pktypoid[attnum++]=type; + found=1; + break; + } + } + if (!found) { + List *inher; + List *inhRelnames=stmt->inhRelnames; + Relation rel; + foreach (inher, inhRelnames) { + Value *inh=lfirst(inher); + int count; + Assert(IsA(inh, String)); + rel=heap_openr(strVal(inh), AccessShareLock); + if (rel->rd_rel->relkind!=RELKIND_RELATION) + elog(ERROR, "inherited table \"%s\" is not a relation", strVal(inh)); + for (count=0; countrd_att->natts; count++) { + char *name=NameStr(rel->rd_att->attrs[count]->attname); + if (strcmp(pkattr->name, name)==0) { + pktypoid[attnum++]=rel->rd_att->attrs[count]->atttypid; + found=1; + break; + } + } + heap_close(rel, NoLock); + if (found) + break; + } + } break; } } @@ -1307,6 +1374,16 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt) } } + for (i = 0; i < INDEX_MAX_KEYS && fktypoid[i] != 0; i++) { + /* + * fktypoid[i] is the foreign key table's i'th element's type oid + * pktypoid[i] is the primary key table's i'th element's type oid + * We let oper() do our work for us, including elog(ERROR) if the + * types don't compare with = + */ + Operator o=oper("=", fktypoid[i], pktypoid[i], false); + ReleaseSysCache(o); + } /* * Build a CREATE CONSTRAINT TRIGGER statement for the CHECK * action. @@ -2399,8 +2476,10 @@ transformAlterTableStmt(ParseState *pstate, AlterTableStmt *stmt) * omitted, lookup for the definition of the primary key * */ - if (fkconstraint->fk_attrs != NIL && fkconstraint->pk_attrs == NIL) - transformFkeyGetPrimaryKey(fkconstraint); + if (fkconstraint->fk_attrs != NIL && fkconstraint->pk_attrs == NIL) { + Oid pktypoid[INDEX_MAX_KEYS]; + transformFkeyGetPrimaryKey(fkconstraint, pktypoid); + } /* * Build a CREATE CONSTRAINT TRIGGER statement for the @@ -2702,7 +2781,7 @@ transformForUpdate(Query *qry, List *forUpdate) * */ static void -transformFkeyCheckAttrs(FkConstraint *fkconstraint) +transformFkeyCheckAttrs(FkConstraint *fkconstraint, Oid *pktypoid) { Relation pkrel; Form_pg_attribute *pkrel_attrs; @@ -2744,6 +2823,7 @@ transformFkeyCheckAttrs(FkConstraint *fkconstraint) if (indexStruct->indisunique) { List *attrl; + int attnum=0; for (i = 0; i < INDEX_MAX_KEYS && indexStruct->indkey[i] != 0; i++); if (i != length(fkconstraint->pk_attrs)) @@ -2766,6 +2846,7 @@ transformFkeyCheckAttrs(FkConstraint *fkconstraint) if (strcmp(name, attr->name) == 0) { + pktypoid[attnum++]=pkrel_attrs[pkattno-1]->atttypid; found = true; break; } @@ -2797,7 +2878,7 @@ transformFkeyCheckAttrs(FkConstraint *fkconstraint) * */ static void -transformFkeyGetPrimaryKey(FkConstraint *fkconstraint) +transformFkeyGetPrimaryKey(FkConstraint *fkconstraint, Oid *pktypoid) { Relation pkrel; Form_pg_attribute *pkrel_attrs; @@ -2806,6 +2887,7 @@ transformFkeyGetPrimaryKey(FkConstraint *fkconstraint) HeapTuple indexTuple = NULL; Form_pg_index indexStruct = NULL; int i; + int attnum=0; /* * Open the referenced table and get the attributes list @@ -2862,6 +2944,7 @@ transformFkeyGetPrimaryKey(FkConstraint *fkconstraint) NameGetDatum(&(pkrel_attrs[pkattno - 1]->attname)))); pkattr->indirection = NIL; pkattr->isRel = false; + pktypoid[attnum++]=pkrel_attrs[pkattno-1]->atttypid; fkconstraint->pk_attrs = lappend(fkconstraint->pk_attrs, pkattr); } diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out index 082a0be478..dab0c7f9c5 100644 --- a/src/test/regress/expected/alter_table.out +++ b/src/test/regress/expected/alter_table.out @@ -313,3 +313,60 @@ DROP TABLE tmp3; NOTICE: DROP TABLE implicitly drops referential integrity trigger from table "tmp2" NOTICE: DROP TABLE implicitly drops referential integrity trigger from table "tmp2" DROP TABLE tmp2; +-- Foreign key adding test with mixed types +CREATE TABLE PKTABLE (ptest1 int PRIMARY KEY); +NOTICE: CREATE TABLE/PRIMARY KEY will create implicit index 'pktable_pkey' for table 'pktable' +CREATE TABLE FKTABLE (ftest1 text); +-- This next should fail, because text=int does not exist +ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1) references pktable; +NOTICE: ALTER TABLE ... ADD CONSTRAINT will create implicit trigger(s) for FOREIGN KEY check(s) +ERROR: Unable to identify an operator '=' for types 'text' and 'int4' + You will have to retype this query using an explicit cast +-- 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); +NOTICE: ALTER TABLE ... ADD CONSTRAINT will create implicit trigger(s) for FOREIGN KEY check(s) +ERROR: Unable to identify an operator '=' for types 'text' and 'int4' + You will have to retype this query using an explicit cast +-- This should succeed, even though they are different types +-- because varchar=int does exist +DROP TABLE FKTABLE; +CREATE TABLE FKTABLE (ftest1 varchar); +ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1) references pktable; +NOTICE: ALTER TABLE ... ADD CONSTRAINT will create implicit trigger(s) for FOREIGN KEY check(s) +-- As should this +ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1) references pktable(ptest1); +NOTICE: ALTER TABLE ... ADD CONSTRAINT will create implicit trigger(s) for FOREIGN KEY check(s) +DROP TABLE pktable; +NOTICE: DROP TABLE implicitly drops referential integrity trigger from table "fktable" +NOTICE: DROP TABLE implicitly drops referential integrity trigger from table "fktable" +DROP TABLE fktable; +CREATE TABLE PKTABLE (ptest1 int, ptest2 text, 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 datetime); +ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1, ftest2) references pktable; +NOTICE: ALTER TABLE ... ADD CONSTRAINT will create implicit trigger(s) for FOREIGN KEY check(s) +ERROR: Unable to identify an operator '=' for types 'cidr' and 'int4' + You will have to retype this query using an explicit cast +-- Again, so should this... +DROP TABLE FKTABLE; +CREATE TABLE FKTABLE (ftest1 cidr, ftest2 datetime); +ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1, ftest2) references pktable(ptest1, ptest2); +NOTICE: ALTER TABLE ... ADD CONSTRAINT will create implicit trigger(s) for FOREIGN KEY check(s) +ERROR: Unable to identify an operator '=' for types 'cidr' and 'int4' + You will have to retype this query using an explicit cast +-- This fails because we mixed up the column ordering +DROP TABLE FKTABLE; +CREATE TABLE FKTABLE (ftest1 int, ftest2 text); +ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1, ftest2) references pktable(ptest2, ptest1); +NOTICE: ALTER TABLE ... ADD CONSTRAINT will create implicit trigger(s) for FOREIGN KEY check(s) +ERROR: Unable to identify an operator '=' for types 'int4' and 'text' + You will have to retype this query using an explicit cast +-- As does this... +ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest2, ftest1) references pktable(ptest1, ptest2); +NOTICE: ALTER TABLE ... ADD CONSTRAINT will create implicit trigger(s) for FOREIGN KEY check(s) +ERROR: Unable to identify an operator '=' for types 'text' and 'int4' + You will have to retype this query using an explicit cast +DROP TABLE FKTABLE; +DROP TABLE PKTABLE; diff --git a/src/test/regress/expected/foreign_key.out b/src/test/regress/expected/foreign_key.out index 075b6aa2f8..990c59c90c 100644 --- a/src/test/regress/expected/foreign_key.out +++ b/src/test/regress/expected/foreign_key.out @@ -712,3 +712,253 @@ ERROR: UNIQUE constraint matching given keys for referenced table "pktable" not DROP TABLE FKTABLE_FAIL1; ERROR: table "fktable_fail1" does not exist DROP TABLE PKTABLE; +-- +-- Tests for mismatched types +-- +-- Basic one column, two table setup +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 text=int does not exist +CREATE TABLE FKTABLE (ftest1 text REFERENCES pktable); +NOTICE: CREATE TABLE will create implicit trigger(s) for FOREIGN KEY check(s) +ERROR: Unable to identify an operator '=' for types 'text' and 'int4' + You will have to retype this query using an explicit cast +-- This should also fail for the same reason, but here we +-- give the column name +CREATE TABLE FKTABLE (ftest1 text REFERENCES pktable(ptest1)); +NOTICE: CREATE TABLE will create implicit trigger(s) for FOREIGN KEY check(s) +ERROR: Unable to identify an operator '=' for types 'text' and 'int4' + You will have to retype this query using an explicit cast +-- This should succeed, even though they are different types +-- because varchar=int does exist +CREATE TABLE FKTABLE (ftest1 varchar REFERENCES pktable); +NOTICE: CREATE TABLE will create implicit trigger(s) for FOREIGN KEY check(s) +DROP TABLE FKTABLE; +NOTICE: DROP TABLE implicitly drops referential integrity trigger from table "pktable" +NOTICE: DROP TABLE implicitly drops referential integrity trigger from table "pktable" +-- As should this +CREATE TABLE FKTABLE (ftest1 varchar REFERENCES pktable(ptest1)); +NOTICE: CREATE TABLE will create implicit trigger(s) for FOREIGN KEY check(s) +DROP TABLE FKTABLE; +NOTICE: DROP TABLE implicitly drops referential integrity trigger from table "pktable" +NOTICE: DROP TABLE implicitly drops referential integrity trigger from table "pktable" +DROP TABLE PKTABLE; +-- Two columns, two tables +CREATE TABLE PKTABLE (ptest1 int, ptest2 text, 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 datetime, FOREIGN KEY(ftest1, ftest2) REFERENCES pktable); +NOTICE: CREATE TABLE will create implicit trigger(s) for FOREIGN KEY check(s) +ERROR: Unable to identify an operator '=' for types 'cidr' and 'int4' + You will have to retype this query using an explicit cast +-- Again, so should this... +CREATE TABLE FKTABLE (ftest1 cidr, ftest2 datetime, FOREIGN KEY(ftest1, ftest2) REFERENCES pktable(ptest1, ptest2)); +NOTICE: CREATE TABLE will create implicit trigger(s) for FOREIGN KEY check(s) +ERROR: Unable to identify an operator '=' for types 'cidr' and 'int4' + You will have to retype this query using an explicit cast +-- This fails because we mixed up the column ordering +CREATE TABLE FKTABLE (ftest1 int, ftest2 text, FOREIGN KEY(ftest2, ftest1) REFERENCES pktable); +NOTICE: CREATE TABLE will create implicit trigger(s) for FOREIGN KEY check(s) +ERROR: Unable to identify an operator '=' for types 'text' and 'int4' + You will have to retype this query using an explicit cast +-- As does this... +CREATE TABLE FKTABLE (ftest1 int, ftest2 text, FOREIGN KEY(ftest2, ftest1) REFERENCES pktable(ptest1, ptest2)); +NOTICE: CREATE TABLE will create implicit trigger(s) for FOREIGN KEY check(s) +ERROR: Unable to identify an operator '=' for types 'text' and 'int4' + You will have to retype this query using an explicit cast +-- And again.. +CREATE TABLE FKTABLE (ftest1 int, ftest2 text, FOREIGN KEY(ftest1, ftest2) REFERENCES pktable(ptest2, ptest1)); +NOTICE: CREATE TABLE will create implicit trigger(s) for FOREIGN KEY check(s) +ERROR: Unable to identify an operator '=' for types 'int4' and 'text' + You will have to retype this query using an explicit cast +-- This works... +CREATE TABLE FKTABLE (ftest1 int, ftest2 text, FOREIGN KEY(ftest2, ftest1) REFERENCES pktable(ptest2, ptest1)); +NOTICE: CREATE TABLE will create implicit trigger(s) for FOREIGN KEY check(s) +DROP TABLE FKTABLE; +NOTICE: DROP TABLE implicitly drops referential integrity trigger from table "pktable" +NOTICE: DROP TABLE implicitly drops referential integrity trigger from table "pktable" +-- As does this +CREATE TABLE FKTABLE (ftest1 int, ftest2 text, FOREIGN KEY(ftest1, ftest2) REFERENCES pktable(ptest1, ptest2)); +NOTICE: CREATE TABLE will create implicit trigger(s) for FOREIGN KEY check(s) +DROP TABLE FKTABLE; +NOTICE: DROP TABLE implicitly drops referential integrity trigger from table "pktable" +NOTICE: DROP TABLE implicitly drops referential integrity trigger from table "pktable" +DROP TABLE PKTABLE; +-- Two columns, same table +-- Make sure this still works... +CREATE TABLE PKTABLE (ptest1 int, ptest2 text, ptest3 int, ptest4 text, PRIMARY KEY(ptest1, ptest2), FOREIGN KEY(ptest3, +ptest4) REFERENCES pktable(ptest1, ptest2)); +NOTICE: CREATE TABLE/PRIMARY KEY will create implicit index 'pktable_pkey' for table 'pktable' +NOTICE: CREATE TABLE will create implicit trigger(s) for FOREIGN KEY check(s) +DROP TABLE PKTABLE; +-- And this, +CREATE TABLE PKTABLE (ptest1 int, ptest2 text, ptest3 int, ptest4 text, PRIMARY KEY(ptest1, ptest2), FOREIGN KEY(ptest3, +ptest4) REFERENCES pktable); +NOTICE: CREATE TABLE/PRIMARY KEY will create implicit index 'pktable_pkey' for table 'pktable' +NOTICE: CREATE TABLE will create implicit trigger(s) for FOREIGN KEY check(s) +DROP TABLE PKTABLE; +-- This shouldn't (mixed up columns) +CREATE TABLE PKTABLE (ptest1 int, ptest2 text, ptest3 int, ptest4 text, 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' +NOTICE: CREATE TABLE will create implicit trigger(s) for FOREIGN KEY check(s) +ERROR: Unable to identify an operator '=' for types 'int4' and 'text' + You will have to retype this query using an explicit cast +-- Nor should this... (same reason, we have 4,3 referencing 1,2 which mismatches types +CREATE TABLE PKTABLE (ptest1 int, ptest2 text, ptest3 int, ptest4 text, 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' +NOTICE: CREATE TABLE will create implicit trigger(s) for FOREIGN KEY check(s) +ERROR: Unable to identify an operator '=' for types 'text' and 'int4' + You will have to retype this query using an explicit cast +-- Not this one either... Same as the last one except we didn't defined the columns being referenced. +CREATE TABLE PKTABLE (ptest1 int, ptest2 text, ptest3 int, ptest4 text, PRIMARY KEY(ptest1, ptest2), FOREIGN KEY(ptest4, +ptest3) REFERENCES pktable); +NOTICE: CREATE TABLE/PRIMARY KEY will create implicit index 'pktable_pkey' for table 'pktable' +NOTICE: CREATE TABLE will create implicit trigger(s) for FOREIGN KEY check(s) +ERROR: Unable to identify an operator '=' for types 'text' and 'int4' + You will have to retype this query using an explicit cast +-- +-- Now some cases with inheritance +-- Basic 2 table case: 1 column of matching types. +create table pktable_base (base1 int not null); +create table pktable (ptest1 int, primary key(base1), unique(base1, ptest1)) inherits (pktable_base); +NOTICE: CREATE TABLE/PRIMARY KEY will create implicit index 'pktable_pkey' for table 'pktable' +NOTICE: CREATE TABLE/UNIQUE will create implicit index 'pktable_base1_key' for table 'pktable' +create table fktable (ftest1 int references pktable(base1)); +NOTICE: CREATE TABLE will create implicit trigger(s) for FOREIGN KEY check(s) +-- now some ins, upd, del +insert into pktable(base1) values (1); +insert into pktable(base1) values (2); +-- let's insert a non-existant fktable value +insert into fktable(ftest1) values (3); +ERROR: referential integrity violation - key referenced from fktable not found in pktable +-- let's make a valid row for that +insert into pktable(base1) values (3); +insert into fktable(ftest1) values (3); +-- let's try removing a row that should fail from pktable +delete from pktable where base1>2; +ERROR: referential integrity violation - key in pktable still referenced from fktable +-- okay, let's try updating all of the base1 values to *4 +-- which should fail. +update pktable set base1=base1*4; +ERROR: referential integrity violation - key in pktable still referenced from fktable +-- okay, let's try an update that should work. +update pktable set base1=base1*4 where base1<3; +-- and a delete that should work +delete from pktable where base1>3; +-- cleanup +drop table fktable; +NOTICE: DROP TABLE implicitly drops referential integrity trigger from table "pktable" +NOTICE: DROP TABLE implicitly drops referential integrity trigger from table "pktable" +delete from pktable; +-- Now 2 columns 2 tables, matching types +create table fktable (ftest1 int, ftest2 int, foreign key(ftest1, ftest2) references pktable(base1, ptest1)); +NOTICE: CREATE TABLE will create implicit trigger(s) for FOREIGN KEY check(s) +-- now some ins, upd, del +insert into pktable(base1, ptest1) values (1, 1); +insert into pktable(base1, ptest1) values (2, 2); +-- let's insert a non-existant fktable value +insert into fktable(ftest1, ftest2) values (3, 1); +ERROR: referential integrity violation - key referenced from fktable not found in pktable +-- let's make a valid row for that +insert into pktable(base1,ptest1) values (3, 1); +insert into fktable(ftest1, ftest2) values (3, 1); +-- let's try removing a row that should fail from pktable +delete from pktable where base1>2; +ERROR: referential integrity violation - key in pktable still referenced from fktable +-- okay, let's try updating all of the base1 values to *4 +-- which should fail. +update pktable set base1=base1*4; +ERROR: referential integrity violation - key in pktable still referenced from fktable +-- okay, let's try an update that should work. +update pktable set base1=base1*4 where base1<3; +-- and a delete that should work +delete from pktable where base1>3; +-- cleanup +drop table fktable; +NOTICE: DROP TABLE implicitly drops referential integrity trigger from table "pktable" +NOTICE: DROP TABLE implicitly drops referential integrity trigger from table "pktable" +drop table pktable; +drop table pktable_base; +-- Now we'll do one all in 1 table with 2 columns of matching types +create table pktable_base(base1 int not null, base2 int); +create table pktable(ptest1 int, ptest2 int, 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' +NOTICE: CREATE TABLE will create implicit trigger(s) for FOREIGN KEY check(s) +insert into pktable (base1, ptest1, base2, ptest2) values (1, 1, 1, 1); +insert into pktable (base1, ptest1, base2, ptest2) values (2, 1, 1, 1); +insert into pktable (base1, ptest1, base2, ptest2) values (2, 2, 2, 1); +insert into pktable (base1, ptest1, base2, ptest2) values (1, 3, 2, 2); +-- fails (3,2) isn't in base1, ptest1 +insert into pktable (base1, ptest1, base2, ptest2) values (2, 3, 3, 2); +ERROR: referential integrity violation - key referenced from pktable not found in pktable +-- fails (2,2) is being referenced +delete from pktable where base1=2; +ERROR: referential integrity violation - key in pktable still referenced from pktable +-- fails (1,1) is being referenced (twice) +update pktable set base1=3 where base1=1; +ERROR: referential integrity violation - key in pktable still referenced from pktable +-- this sequence of two deletes will work, since after the first there will be no (2,*) references +delete from pktable where base2=2; +delete from pktable where base1=2; +drop table pktable; +drop table pktable_base; +-- 2 columns (2 tables), mismatched types +create table pktable_base(base1 int not null); +create table pktable(ptest1 text, primary key(base1, ptest1)) inherits (pktable_base); +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); +NOTICE: CREATE TABLE will create implicit trigger(s) for FOREIGN KEY check(s) +ERROR: Unable to identify an operator '=' for types 'cidr' and 'int4' + You will have to retype this query using an explicit cast +create table fktable(ftest1 cidr, ftest2 int[], foreign key (ftest1, ftest2) references pktable(base1, ptest1)); +NOTICE: CREATE TABLE will create implicit trigger(s) for FOREIGN KEY check(s) +ERROR: Unable to identify an operator '=' for types 'cidr' and 'int4' + You will have to retype this query using an explicit cast +-- let's mix up which columns reference which +create table fktable(ftest1 int, ftest2 text, foreign key(ftest2, ftest1) references pktable); +NOTICE: CREATE TABLE will create implicit trigger(s) for FOREIGN KEY check(s) +ERROR: Unable to identify an operator '=' for types 'text' and 'int4' + You will have to retype this query using an explicit cast +create table fktable(ftest1 int, ftest2 text, foreign key(ftest2, ftest1) references pktable(base1, ptest1)); +NOTICE: CREATE TABLE will create implicit trigger(s) for FOREIGN KEY check(s) +ERROR: Unable to identify an operator '=' for types 'text' and 'int4' + You will have to retype this query using an explicit cast +create table fktable(ftest1 int, ftest2 text, foreign key(ftest1, ftest2) references pktable(ptest1, base1)); +NOTICE: CREATE TABLE will create implicit trigger(s) for FOREIGN KEY check(s) +ERROR: Unable to identify an operator '=' for types 'int4' and 'text' + You will have to retype this query using an explicit cast +drop table pktable; +drop table pktable_base; +-- 2 columns (1 table), mismatched types +create table pktable_base(base1 int not null, base2 int); +create table pktable(ptest1 text, ptest2 text[], 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' +NOTICE: CREATE TABLE will create implicit trigger(s) for FOREIGN KEY check(s) +ERROR: Unable to identify an operator '=' for types '_text' and 'text' + You will have to retype this query using an explicit cast +create table pktable(ptest1 text, ptest2 text, 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' +NOTICE: CREATE TABLE will create implicit trigger(s) for FOREIGN KEY check(s) +ERROR: Unable to identify an operator '=' for types 'int4' and 'text' + You will have to retype this query using an explicit cast +create table pktable(ptest1 text, ptest2 text, 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' +NOTICE: CREATE TABLE will create implicit trigger(s) for FOREIGN KEY check(s) +ERROR: Unable to identify an operator '=' for types 'text' and 'int4' + You will have to retype this query using an explicit cast +create table pktable(ptest1 text, ptest2 text, 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' +NOTICE: CREATE TABLE will create implicit trigger(s) for FOREIGN KEY check(s) +ERROR: Unable to identify an operator '=' for types 'text' and 'int4' + You will have to retype this query using an explicit cast +drop table pktable; +ERROR: table "pktable" does not exist +drop table pktable_base; diff --git a/src/test/regress/sql/alter_table.sql b/src/test/regress/sql/alter_table.sql index f90710f8d9..f0ac095c53 100644 --- a/src/test/regress/sql/alter_table.sql +++ b/src/test/regress/sql/alter_table.sql @@ -212,3 +212,39 @@ DROP TABLE tmp3; DROP TABLE tmp2; +-- Foreign key adding test with mixed types + +CREATE TABLE PKTABLE (ptest1 int PRIMARY KEY); +CREATE TABLE FKTABLE (ftest1 text); +-- This next should fail, because text=int does not exist +ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1) references pktable; +-- 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); +-- This should succeed, even though they are different types +-- because varchar=int does exist +DROP TABLE FKTABLE; +CREATE TABLE FKTABLE (ftest1 varchar); +ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1) references pktable; +-- As should this +ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1) references pktable(ptest1); +DROP TABLE pktable; +DROP TABLE fktable; + +CREATE TABLE PKTABLE (ptest1 int, ptest2 text, PRIMARY KEY(ptest1, ptest2)); +-- This should fail, because we just chose really odd types +CREATE TABLE FKTABLE (ftest1 cidr, ftest2 datetime); +ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1, ftest2) references pktable; +-- Again, so should this... +DROP TABLE FKTABLE; +CREATE TABLE FKTABLE (ftest1 cidr, ftest2 datetime); +ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1, ftest2) references pktable(ptest1, ptest2); +-- This fails because we mixed up the column ordering +DROP TABLE FKTABLE; +CREATE TABLE FKTABLE (ftest1 int, ftest2 text); +ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1, ftest2) references pktable(ptest2, ptest1); +-- As does this... +ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest2, ftest1) references pktable(ptest1, ptest2); + +DROP TABLE FKTABLE; +DROP TABLE PKTABLE; diff --git a/src/test/regress/sql/foreign_key.sql b/src/test/regress/sql/foreign_key.sql index 91ff0aafe5..587fd850f7 100644 --- a/src/test/regress/sql/foreign_key.sql +++ b/src/test/regress/sql/foreign_key.sql @@ -425,3 +425,159 @@ CREATE TABLE FKTABLE_FAIL1 (ftest1 int REFERENCES pktable(ptest1)); DROP TABLE FKTABLE_FAIL1; DROP TABLE PKTABLE; + +-- +-- Tests for mismatched types +-- +-- Basic one column, two table setup +CREATE TABLE PKTABLE (ptest1 int PRIMARY KEY); +-- This next should fail, because text=int does not exist +CREATE TABLE FKTABLE (ftest1 text REFERENCES pktable); +-- This should also fail for the same reason, but here we +-- give the column name +CREATE TABLE FKTABLE (ftest1 text REFERENCES pktable(ptest1)); +-- This should succeed, even though they are different types +-- because varchar=int does exist +CREATE TABLE FKTABLE (ftest1 varchar REFERENCES pktable); +DROP TABLE FKTABLE; +-- As should this +CREATE TABLE FKTABLE (ftest1 varchar REFERENCES pktable(ptest1)); +DROP TABLE FKTABLE; +DROP TABLE PKTABLE; + +-- Two columns, two tables +CREATE TABLE PKTABLE (ptest1 int, ptest2 text, PRIMARY KEY(ptest1, ptest2)); +-- This should fail, because we just chose really odd types +CREATE TABLE FKTABLE (ftest1 cidr, ftest2 datetime, FOREIGN KEY(ftest1, ftest2) REFERENCES pktable); +-- Again, so should this... +CREATE TABLE FKTABLE (ftest1 cidr, ftest2 datetime, FOREIGN KEY(ftest1, ftest2) REFERENCES pktable(ptest1, ptest2)); +-- This fails because we mixed up the column ordering +CREATE TABLE FKTABLE (ftest1 int, ftest2 text, FOREIGN KEY(ftest2, ftest1) REFERENCES pktable); +-- As does this... +CREATE TABLE FKTABLE (ftest1 int, ftest2 text, FOREIGN KEY(ftest2, ftest1) REFERENCES pktable(ptest1, ptest2)); +-- And again.. +CREATE TABLE FKTABLE (ftest1 int, ftest2 text, FOREIGN KEY(ftest1, ftest2) REFERENCES pktable(ptest2, ptest1)); +-- This works... +CREATE TABLE FKTABLE (ftest1 int, ftest2 text, FOREIGN KEY(ftest2, ftest1) REFERENCES pktable(ptest2, ptest1)); +DROP TABLE FKTABLE; +-- As does this +CREATE TABLE FKTABLE (ftest1 int, ftest2 text, FOREIGN KEY(ftest1, ftest2) REFERENCES pktable(ptest1, ptest2)); +DROP TABLE FKTABLE; +DROP TABLE PKTABLE; + +-- Two columns, same table +-- Make sure this still works... +CREATE TABLE PKTABLE (ptest1 int, ptest2 text, ptest3 int, ptest4 text, PRIMARY KEY(ptest1, ptest2), FOREIGN KEY(ptest3, +ptest4) REFERENCES pktable(ptest1, ptest2)); +DROP TABLE PKTABLE; +-- And this, +CREATE TABLE PKTABLE (ptest1 int, ptest2 text, ptest3 int, ptest4 text, PRIMARY KEY(ptest1, ptest2), FOREIGN KEY(ptest3, +ptest4) REFERENCES pktable); +DROP TABLE PKTABLE; +-- This shouldn't (mixed up columns) +CREATE TABLE PKTABLE (ptest1 int, ptest2 text, ptest3 int, ptest4 text, PRIMARY KEY(ptest1, ptest2), FOREIGN KEY(ptest3, +ptest4) REFERENCES pktable(ptest2, ptest1)); +-- Nor should this... (same reason, we have 4,3 referencing 1,2 which mismatches types +CREATE TABLE PKTABLE (ptest1 int, ptest2 text, ptest3 int, ptest4 text, PRIMARY KEY(ptest1, ptest2), FOREIGN KEY(ptest4, +ptest3) REFERENCES pktable(ptest1, ptest2)); +-- Not this one either... Same as the last one except we didn't defined the columns being referenced. +CREATE TABLE PKTABLE (ptest1 int, ptest2 text, ptest3 int, ptest4 text, PRIMARY KEY(ptest1, ptest2), FOREIGN KEY(ptest4, +ptest3) REFERENCES pktable); + +-- +-- Now some cases with inheritance +-- Basic 2 table case: 1 column of matching types. +create table pktable_base (base1 int not null); +create table pktable (ptest1 int, primary key(base1), unique(base1, ptest1)) inherits (pktable_base); +create table fktable (ftest1 int references pktable(base1)); +-- now some ins, upd, del +insert into pktable(base1) values (1); +insert into pktable(base1) values (2); +-- let's insert a non-existant fktable value +insert into fktable(ftest1) values (3); +-- let's make a valid row for that +insert into pktable(base1) values (3); +insert into fktable(ftest1) values (3); +-- let's try removing a row that should fail from pktable +delete from pktable where base1>2; +-- okay, let's try updating all of the base1 values to *4 +-- which should fail. +update pktable set base1=base1*4; +-- okay, let's try an update that should work. +update pktable set base1=base1*4 where base1<3; +-- and a delete that should work +delete from pktable where base1>3; +-- cleanup +drop table fktable; +delete from pktable; + +-- Now 2 columns 2 tables, matching types +create table fktable (ftest1 int, ftest2 int, foreign key(ftest1, ftest2) references pktable(base1, ptest1)); +-- now some ins, upd, del +insert into pktable(base1, ptest1) values (1, 1); +insert into pktable(base1, ptest1) values (2, 2); +-- let's insert a non-existant fktable value +insert into fktable(ftest1, ftest2) values (3, 1); +-- let's make a valid row for that +insert into pktable(base1,ptest1) values (3, 1); +insert into fktable(ftest1, ftest2) values (3, 1); +-- let's try removing a row that should fail from pktable +delete from pktable where base1>2; +-- okay, let's try updating all of the base1 values to *4 +-- which should fail. +update pktable set base1=base1*4; +-- okay, let's try an update that should work. +update pktable set base1=base1*4 where base1<3; +-- and a delete that should work +delete from pktable where base1>3; +-- cleanup +drop table fktable; +drop table pktable; +drop table pktable_base; + +-- Now we'll do one all in 1 table with 2 columns of matching types +create table pktable_base(base1 int not null, base2 int); +create table pktable(ptest1 int, ptest2 int, primary key(base1, ptest1), foreign key(base2, ptest2) references + pktable(base1, ptest1)) inherits (pktable_base); +insert into pktable (base1, ptest1, base2, ptest2) values (1, 1, 1, 1); +insert into pktable (base1, ptest1, base2, ptest2) values (2, 1, 1, 1); +insert into pktable (base1, ptest1, base2, ptest2) values (2, 2, 2, 1); +insert into pktable (base1, ptest1, base2, ptest2) values (1, 3, 2, 2); +-- fails (3,2) isn't in base1, ptest1 +insert into pktable (base1, ptest1, base2, ptest2) values (2, 3, 3, 2); +-- fails (2,2) is being referenced +delete from pktable where base1=2; +-- fails (1,1) is being referenced (twice) +update pktable set base1=3 where base1=1; +-- this sequence of two deletes will work, since after the first there will be no (2,*) references +delete from pktable where base2=2; +delete from pktable where base1=2; +drop table pktable; +drop table pktable_base; + +-- 2 columns (2 tables), mismatched types +create table pktable_base(base1 int not null); +create table pktable(ptest1 text, primary key(base1, ptest1)) inherits (pktable_base); +-- 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); +create table fktable(ftest1 cidr, ftest2 int[], foreign key (ftest1, ftest2) references pktable(base1, ptest1)); +-- let's mix up which columns reference which +create table fktable(ftest1 int, ftest2 text, foreign key(ftest2, ftest1) references pktable); +create table fktable(ftest1 int, ftest2 text, foreign key(ftest2, ftest1) references pktable(base1, ptest1)); +create table fktable(ftest1 int, ftest2 text, foreign key(ftest1, ftest2) references pktable(ptest1, base1)); +drop table pktable; +drop table pktable_base; + +-- 2 columns (1 table), mismatched types +create table pktable_base(base1 int not null, base2 int); +create table pktable(ptest1 text, ptest2 text[], primary key(base1, ptest1), foreign key(base2, ptest2) references + pktable(base1, ptest1)) inherits (pktable_base); +create table pktable(ptest1 text, ptest2 text, primary key(base1, ptest1), foreign key(base2, ptest2) references + pktable(ptest1, base1)) inherits (pktable_base); +create table pktable(ptest1 text, ptest2 text, primary key(base1, ptest1), foreign key(ptest2, base2) references + pktable(base1, ptest1)) inherits (pktable_base); +create table pktable(ptest1 text, ptest2 text, primary key(base1, ptest1), foreign key(ptest2, base2) references + pktable(base1, ptest1)) inherits (pktable_base); +drop table pktable; +drop table pktable_base; +