diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/backend/commands/command.c | 95 | ||||
-rw-r--r-- | src/backend/parser/analyze.c | 151 | ||||
-rw-r--r-- | src/test/regress/expected/alter_table.out | 8 | ||||
-rw-r--r-- | src/test/regress/expected/foreign_key.out | 13 | ||||
-rw-r--r-- | src/test/regress/sql/alter_table.sql | 6 | ||||
-rw-r--r-- | src/test/regress/sql/foreign_key.sql | 6 |
6 files changed, 272 insertions, 7 deletions
diff --git a/src/backend/commands/command.c b/src/backend/commands/command.c index 60405ebd277..97b3563d9fc 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.96 2000/08/25 18:05:54 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/Attic/command.c,v 1.97 2000/08/29 04:20:43 momjian Exp $ * * NOTES * The PerformAddAttribute() code, like most of the relation @@ -45,9 +45,10 @@ #include "commands/view.h" #include "utils/temprel.h" #include "executor/spi_priv.h" +#include "catalog/pg_index.h" +#include "utils/relcache.h" #ifdef _DROP_COLUMN_HACK__ -#include "catalog/pg_index.h" #include "parser/parse.h" #endif /* _DROP_COLUMN_HACK__ */ #include "access/genam.h" @@ -1241,12 +1242,18 @@ AlterTableAddConstraint(char *relationName, case T_FkConstraint: { FkConstraint *fkconstraint = (FkConstraint *) newConstraint; - Relation rel; + Relation rel, pkrel; HeapScanDesc scan; HeapTuple tuple; Trigger trig; List *list; int count; + List *indexoidlist, + *indexoidscan; + Form_pg_index indexStruct = NULL; + Form_pg_attribute *rel_attrs = NULL; + int i; + int found=0; if (get_temp_rel_by_username(fkconstraint->pktable_name)!=NULL && get_temp_rel_by_username(relationName)==NULL) { @@ -1273,8 +1280,10 @@ AlterTableAddConstraint(char *relationName, * doesn't delete rows out from under us. */ - rel = heap_openr(fkconstraint->pktable_name, AccessExclusiveLock); - heap_close(rel, NoLock); + pkrel = heap_openr(fkconstraint->pktable_name, AccessExclusiveLock); + if (pkrel == NULL) + elog(ERROR, "referenced table \"%s\" not found", + fkconstraint->pktable_name); /* * Grab an exclusive lock on the fk table, and then scan @@ -1284,6 +1293,82 @@ AlterTableAddConstraint(char *relationName, * and that's that. */ rel = heap_openr(relationName, AccessExclusiveLock); + if (rel == NULL) + elog(ERROR, "table \"%s\" not found", + relationName); + + /* First we check for limited correctness of the constraint */ + + rel_attrs = pkrel->rd_att->attrs; + indexoidlist = RelationGetIndexList(pkrel); + + foreach(indexoidscan, indexoidlist) + { + Oid indexoid = lfirsti(indexoidscan); + HeapTuple indexTuple; + List *attrl; + indexTuple = SearchSysCacheTuple(INDEXRELID, + ObjectIdGetDatum(indexoid), + 0, 0, 0); + if (!HeapTupleIsValid(indexTuple)) + elog(ERROR, "transformFkeyGetPrimaryKey: index %u not found", + indexoid); + indexStruct = (Form_pg_index) GETSTRUCT(indexTuple); + + if (indexStruct->indisunique) { + /* go through the fkconstraint->pk_attrs list */ + foreach(attrl, fkconstraint->pk_attrs) { + Ident *attr=lfirst(attrl); + found=0; + for (i = 0; i < INDEX_MAX_KEYS && indexStruct->indkey[i] != 0; i++) + { + int pkattno = indexStruct->indkey[i]; + if (pkattno>0) { + char *name = NameStr(rel_attrs[pkattno-1]->attname); + if (strcmp(name, attr->name)==0) { + found=1; + break; + } + } + } + if (!found) + break; + } + } + if (found) + break; + indexStruct = NULL; + } + if (!found) + elog(ERROR, "UNIQUE constraint matching given keys for referenced table \"%s\" not found", + fkconstraint->pktable_name); + + freeList(indexoidlist); + heap_close(pkrel, NoLock); + + rel_attrs = rel->rd_att->attrs; + if (fkconstraint->fk_attrs!=NIL) { + int found=0; + List *fkattrs; + Ident *fkattr; + foreach(fkattrs, fkconstraint->fk_attrs) { + int count=0; + found=0; + fkattr=lfirst(fkattrs); + for (; count < rel->rd_att->natts; count++) { + char *name = NameStr(rel->rd_att->attrs[count]->attname); + if (strcmp(name, fkattr->name)==0) { + found=1; + break; + } + } + if (!found) + break; + } + if (!found) + elog(ERROR, "columns referenced in foreign key constraint not found."); + } + 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 f3cad18de86..fe21804a2c4 100644 --- a/src/backend/parser/analyze.c +++ b/src/backend/parser/analyze.c @@ -6,7 +6,7 @@ * Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: analyze.c,v 1.155 2000/08/22 12:59:04 ishii Exp $ + * $Id: analyze.c,v 1.156 2000/08/29 04:20:44 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -52,6 +52,7 @@ static void transformForUpdate(Query *qry, List *forUpdate); static void transformFkeyGetPrimaryKey(FkConstraint *fkconstraint); static void transformConstraintAttrs(List *constraintList); static void transformColumnType(ParseState *pstate, ColumnDef *column); +static void transformFkeyCheckAttrs(FkConstraint *fkconstraint); /* kluge to return extra info from transformCreateStmt() */ static List *extras_before; @@ -1062,6 +1063,33 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt) if (fkconstraint->constr_name == NULL) fkconstraint->constr_name = "<unnamed>"; + /* + * Check to see if the attributes mentioned by the constraint + * actually exist on this table. + */ + if (fkconstraint->fk_attrs!=NIL) { + int found=0; + List *cols; + List *fkattrs; + Ident *fkattr; + ColumnDef *col; + foreach(fkattrs, fkconstraint->fk_attrs) { + found=0; + fkattr=lfirst(fkattrs); + foreach(cols, columns) { + col=lfirst(cols); + if (strcmp(col->colname, fkattr->name)==0) { + found=1; + break; + } + } + if (!found) + break; + } + if (!found) + elog(ERROR, "columns referenced in foreign key constraint not found."); + } + /* * If the attribute list for the referenced table was omitted, * lookup for the definition of the primary key. If the @@ -1096,7 +1124,43 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt) fkconstraint->pktable_name); } } - + else { + if (strcmp(fkconstraint->pktable_name, stmt->relname)!=0) + transformFkeyCheckAttrs(fkconstraint); + else { + /* Get a unique/pk constraint from above */ + List *index; + int found=0; + foreach(index, ilist) + { + IndexStmt *ind=lfirst(index); + IndexElem *indparm; + List *indparms; + List *pkattrs; + Ident *pkattr; + if (ind->unique) { + foreach(pkattrs, fkconstraint->pk_attrs) { + found=0; + pkattr=lfirst(pkattrs); + foreach(indparms, ind->indexParams) { + indparm=lfirst(indparms); + if (strcmp(indparm->name, pkattr->name)==0) { + found=1; + break; + } + } + if (!found) + break; + } + } + if (found) + break; + } + if (!found) + elog(ERROR, "UNIQUE constraint matching given keys for referenced table \"%s\" not found", + fkconstraint->pktable_name); + } + } /* * Build a CREATE CONSTRAINT TRIGGER statement for the CHECK * action. @@ -2030,6 +2094,89 @@ transformForUpdate(Query *qry, List *forUpdate) /* + * transformFkeyCheckAttrs - + * + * Try to make sure that the attributes of a referenced table + * belong to a unique (or primary key) constraint. + * + */ +static void +transformFkeyCheckAttrs(FkConstraint *fkconstraint) +{ + Relation pkrel; + Form_pg_attribute *pkrel_attrs; + List *indexoidlist, + *indexoidscan; + Form_pg_index indexStruct = NULL; + int i; + int found=0; + + /* ---------- + * Open the referenced table and get the attributes list + * ---------- + */ + pkrel = heap_openr(fkconstraint->pktable_name, AccessShareLock); + if (pkrel == NULL) + elog(ERROR, "referenced table \"%s\" not found", + fkconstraint->pktable_name); + pkrel_attrs = pkrel->rd_att->attrs; + + /* ---------- + * Get the list of index OIDs for the table from the relcache, + * and look up each one in the pg_index syscache for each unique + * one, and then compare the attributes we were given to those + * defined. + * ---------- + */ + indexoidlist = RelationGetIndexList(pkrel); + + foreach(indexoidscan, indexoidlist) + { + Oid indexoid = lfirsti(indexoidscan); + HeapTuple indexTuple; + List *attrl; + indexTuple = SearchSysCacheTuple(INDEXRELID, + ObjectIdGetDatum(indexoid), + 0, 0, 0); + if (!HeapTupleIsValid(indexTuple)) + elog(ERROR, "transformFkeyGetPrimaryKey: index %u not found", + indexoid); + indexStruct = (Form_pg_index) GETSTRUCT(indexTuple); + + if (indexStruct->indisunique) { + /* go through the fkconstraint->pk_attrs list */ + foreach(attrl, fkconstraint->pk_attrs) { + Ident *attr=lfirst(attrl); + found=0; + for (i = 0; i < INDEX_MAX_KEYS && indexStruct->indkey[i] != 0; i++) + { + int pkattno = indexStruct->indkey[i]; + if (pkattno>0) { + char *name = NameStr(pkrel_attrs[pkattno - 1]->attname); + if (strcmp(name, attr->name)==0) { + found=1; + break; + } + } + } + if (!found) + break; + } + } + if (found) + break; + indexStruct = NULL; + } + if (!found) + elog(ERROR, "UNIQUE constraint matching given keys for referenced table \"%s\" not found", + fkconstraint->pktable_name); + + freeList(indexoidlist); + heap_close(pkrel, AccessShareLock); +} + + +/* * transformFkeyGetPrimaryKey - * * Try to find the primary key attributes of a referenced table if diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out index a0fef3b55ee..d79a65c983a 100644 --- a/src/test/regress/expected/alter_table.out +++ b/src/test/regress/expected/alter_table.out @@ -282,6 +282,14 @@ INSERT INTO tmp2 values (4); INSERT INTO tmp3 values (1,10); INSERT INTO tmp3 values (1,20); INSERT INTO tmp3 values (5,50); +-- Try (and fail) to add constraint due to invalid source columns +ALTER TABLE tmp3 add constraint tmpconstr foreign key(c) references tmp2 match full; +NOTICE: ALTER TABLE ... ADD CONSTRAINT will create implicit trigger(s) for FOREIGN KEY check(s) +ERROR: columns referenced in foreign key constraint not found. +-- Try (and fail) to add constraint due to invalide destination columns explicitly given +ALTER TABLE tmp3 add constraint tmpconstr foreign key(a) references tmp2(b) match full; +NOTICE: ALTER TABLE ... ADD CONSTRAINT will create implicit trigger(s) for FOREIGN KEY check(s) +ERROR: UNIQUE constraint matching given keys for referenced table "tmp2" not found -- Try (and fail) to add constraint due to invalid data ALTER TABLE tmp3 add constraint tmpconstr foreign key (a) references tmp2 match full; NOTICE: ALTER TABLE ... ADD CONSTRAINT will create implicit trigger(s) for FOREIGN KEY check(s) diff --git a/src/test/regress/expected/foreign_key.out b/src/test/regress/expected/foreign_key.out index 6a03b7259f9..105d07adfd8 100644 --- a/src/test/regress/expected/foreign_key.out +++ b/src/test/regress/expected/foreign_key.out @@ -690,3 +690,16 @@ 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; +CREATE TABLE PKTABLE (ptest1 int PRIMARY KEY); +NOTICE: CREATE TABLE/PRIMARY KEY will create implicit index 'pktable_pkey' for table 'pktable' +CREATE TABLE FKTABLE_FAIL1 ( ftest1 int, CONSTRAINT fkfail1 FOREIGN KEY (ftest2) REFERENCES PKTABLE); +NOTICE: CREATE TABLE will create implicit trigger(s) for FOREIGN KEY check(s) +ERROR: columns referenced in foreign key constraint not found. +CREATE TABLE FKTABLE_FAIL2 ( ftest1 int, CONSTRAINT fkfail1 FOREIGN KEY (ftest1) REFERENCES PKTABLE(ptest2)); +NOTICE: CREATE TABLE will create implicit trigger(s) for FOREIGN KEY check(s) +ERROR: UNIQUE constraint matching given keys for referenced table "pktable" not found +DROP TABLE FKTABLE_FAIL1; +ERROR: Relation 'fktable_fail1' does not exist +DROP TABLE FKTABLE_FAIL2; +ERROR: Relation 'fktable_fail2' does not exist +DROP TABLE PKTABLE; diff --git a/src/test/regress/sql/alter_table.sql b/src/test/regress/sql/alter_table.sql index 0642e96ba19..cb7b9a2a469 100644 --- a/src/test/regress/sql/alter_table.sql +++ b/src/test/regress/sql/alter_table.sql @@ -180,6 +180,12 @@ INSERT INTO tmp3 values (1,10); INSERT INTO tmp3 values (1,20); INSERT INTO tmp3 values (5,50); +-- Try (and fail) to add constraint due to invalid source columns +ALTER TABLE tmp3 add constraint tmpconstr foreign key(c) references tmp2 match full; + +-- Try (and fail) to add constraint due to invalide destination columns explicitly given +ALTER TABLE tmp3 add constraint tmpconstr foreign key(a) references tmp2(b) match full; + -- Try (and fail) to add constraint due to invalid data ALTER TABLE tmp3 add constraint tmpconstr foreign key (a) references tmp2 match full; diff --git a/src/test/regress/sql/foreign_key.sql b/src/test/regress/sql/foreign_key.sql index d08f2ce2d0f..ac29e50579c 100644 --- a/src/test/regress/sql/foreign_key.sql +++ b/src/test/regress/sql/foreign_key.sql @@ -411,4 +411,10 @@ SELECT * from FKTABLE; DROP TABLE FKTABLE; DROP TABLE PKTABLE; +CREATE TABLE PKTABLE (ptest1 int PRIMARY KEY); +CREATE TABLE FKTABLE_FAIL1 ( ftest1 int, CONSTRAINT fkfail1 FOREIGN KEY (ftest2) REFERENCES PKTABLE); +CREATE TABLE FKTABLE_FAIL2 ( ftest1 int, CONSTRAINT fkfail1 FOREIGN KEY (ftest1) REFERENCES PKTABLE(ptest2)); +DROP TABLE FKTABLE_FAIL1; +DROP TABLE FKTABLE_FAIL2; +DROP TABLE PKTABLE; |