*** pgsql.old/src/backend/parser/analyze.c Sat Jul 1 21:04:09 2000 --- pgsql/src/backend/parser/analyze.c Sat Jul 29 19:25:41 2000 *************** *** 48,53 **** --- 48,54 ---- 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; *************** *** 1020,1025 **** --- 1021,1053 ---- if (fkconstraint->constr_name == NULL) fkconstraint->constr_name = ""; + /* + * 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 *************** *** 1054,1060 **** fkconstraint->pktable_name); } } ! /* * Build a CREATE CONSTRAINT TRIGGER statement for the CHECK * action. --- 1082,1124 ---- 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. *************** *** 1972,1977 **** --- 2036,2122 ---- } qry->rowMark = rowMark; + } + + + /* + * 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]; + char *name = nameout(&(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); } *** pgsql.old/src/backend/commands/command.c Sun Jul 30 15:32:56 2000 --- pgsql/src/backend/commands/command.c Sat Jul 29 19:32:33 2000 *************** *** 44,52 **** #include "commands/view.h" #include "utils/temprel.h" #include "executor/spi_priv.h" #ifdef _DROP_COLUMN_HACK__ - #include "catalog/pg_index.h" #include "parser/parse.h" #endif /* _DROP_COLUMN_HACK__ */ #include "access/genam.h" --- 44,53 ---- #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 "parser/parse.h" #endif /* _DROP_COLUMN_HACK__ */ #include "access/genam.h" *************** *** 1234,1245 **** case T_FkConstraint: { FkConstraint *fkconstraint = (FkConstraint *) newConstraint; ! Relation rel; HeapScanDesc scan; HeapTuple tuple; Trigger trig; List *list; int count; if (get_temp_rel_by_username(fkconstraint->pktable_name)!=NULL && get_temp_rel_by_username(relationName)==NULL) { --- 1235,1252 ---- case T_FkConstraint: { FkConstraint *fkconstraint = (FkConstraint *) newConstraint; ! 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) { *************** *** 1266,1273 **** * doesn't delete rows out from under us. */ ! rel = heap_openr(fkconstraint->pktable_name, AccessExclusiveLock); ! heap_close(rel, NoLock); /* * Grab an exclusive lock on the fk table, and then scan --- 1273,1282 ---- * doesn't delete rows out from under us. */ ! 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 *************** *** 1277,1282 **** --- 1286,1365 ---- * 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]; + char *name = nameout(&(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 < pkrel->rd_att->natts; count++) { + char *name = nameout(&(rel_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;