diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 158c594a848b088e6113b3e5483f6158c370a92c..c86b9990e3debfb357dfd3b9c5a7252afff115f2 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -6738,6 +6738,26 @@ transformFkeyCheckAttrs(Relation pkrel, bool found_deferrable = false; List *indexoidlist; ListCell *indexoidscan; + int i, + j; + + /* + * Reject duplicate appearances of columns in the referenced-columns list. + * Such a case is forbidden by the SQL standard, and even if we thought it + * useful to allow it, there would be ambiguity about how to match the + * list to unique indexes (in particular, it'd be unclear which index + * opclass goes with which FK column). + */ + for (i = 0; i < numattrs; i++) + { + for (j = i + 1; j < numattrs; j++) + { + if (attnums[i] == attnums[j]) + ereport(ERROR, + (errcode(ERRCODE_INVALID_FOREIGN_KEY), + errmsg("foreign key referenced-columns list must not contain duplicates"))); + } + } /* * Get the list of index OIDs for the table from the relcache, and look up @@ -6750,8 +6770,6 @@ transformFkeyCheckAttrs(Relation pkrel, { HeapTuple indexTuple; Form_pg_index indexStruct; - int i, - j; indexoid = lfirst_oid(indexoidscan); indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid)); @@ -6770,11 +6788,11 @@ transformFkeyCheckAttrs(Relation pkrel, heap_attisnull(indexTuple, Anum_pg_index_indpred) && heap_attisnull(indexTuple, Anum_pg_index_indexprs)) { - /* Must get indclass the hard way */ Datum indclassDatum; bool isnull; oidvector *indclass; + /* Must get indclass the hard way */ indclassDatum = SysCacheGetAttr(INDEXRELID, indexTuple, Anum_pg_index_indclass, &isnull); Assert(!isnull); @@ -6782,7 +6800,13 @@ transformFkeyCheckAttrs(Relation pkrel, /* * The given attnum list may match the index columns in any order. - * Check that each list is a subset of the other. + * Check for a match, and extract the appropriate opclasses while + * we're at it. + * + * We know that attnums[] is duplicate-free per the test at the + * start of this function, and we checked above that the number of + * index columns agrees, so if we find a match for each attnums[] + * entry then we must have a one-to-one match in some order. */ for (i = 0; i < numattrs; i++) { @@ -6791,6 +6815,7 @@ transformFkeyCheckAttrs(Relation pkrel, { if (attnums[i] == indexStruct->indkey.values[j]) { + opclasses[i] = indclass->values[j]; found = true; break; } @@ -6798,24 +6823,6 @@ transformFkeyCheckAttrs(Relation pkrel, if (!found) break; } - if (found) - { - for (i = 0; i < numattrs; i++) - { - found = false; - for (j = 0; j < numattrs; j++) - { - if (attnums[j] == indexStruct->indkey.values[i]) - { - opclasses[j] = indclass->values[i]; - found = true; - break; - } - } - if (!found) - break; - } - } /* * Refuse to use a deferrable unique/primary key. This is per SQL