OSDN Git Service

This is mostly the same as an earlier patch I
authorBruce Momjian <bruce@momjian.us>
Tue, 29 Aug 2000 04:20:47 +0000 (04:20 +0000)
committerBruce Momjian <bruce@momjian.us>
Tue, 29 Aug 2000 04:20:47 +0000 (04:20 +0000)
didn't hear anything about, but which would
have broken with the function manager changes
anyway.

Well, this patch checks that a unique constraint
of some form (unique or pk) is on the referenced
columns of an FK constraint and that the columns
in the referencing table exist at creation time.
The former is to move closer to SQL compatibility
and the latter is in answer to a bug report.
I also added a basic check of this functionality
to the alter table and foreign key regression
tests.

Stephan Szabo
sszabo@bigpanda.com

src/backend/commands/command.c
src/backend/parser/analyze.c
src/test/regress/expected/alter_table.out
src/test/regress/expected/foreign_key.out
src/test/regress/sql/alter_table.sql
src/test/regress/sql/foreign_key.sql

index 60405eb..97b3563 100644 (file)
@@ -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
 #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;
index f3cad18..fe21804 100644 (file)
@@ -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
index a0fef3b..d79a65c 100644 (file)
@@ -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)
index 6a03b72..105d07a 100644 (file)
@@ -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;
index 0642e96..cb7b9a2 100644 (file)
@@ -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;
 
index d08f2ce..ac29e50 100644 (file)
@@ -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;