Skip to content

Commit b4dacab

Browse files
committed
Reject duplicate column names in foreign key referenced-columns lists.
Such cases are disallowed by the SQL spec, and even if we wanted to allow them, the semantics seem ambiguous: how should the FK columns be matched up with the columns of a unique index? (The matching could be significant in the presence of opclasses with different notions of equality, so this issue isn't just academic.) However, our code did not previously reject such cases, but instead would either fail to match to any unique index, or generate a bizarre opclass-lookup error because of sloppy thinking in the index-matching code. David Rowley
1 parent dc4871c commit b4dacab

File tree

1 file changed

+29
-22
lines changed

1 file changed

+29
-22
lines changed

src/backend/commands/tablecmds.c

Lines changed: 29 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -6419,6 +6419,26 @@ transformFkeyCheckAttrs(Relation pkrel,
64196419
bool found_deferrable = false;
64206420
List *indexoidlist;
64216421
ListCell *indexoidscan;
6422+
int i,
6423+
j;
6424+
6425+
/*
6426+
* Reject duplicate appearances of columns in the referenced-columns list.
6427+
* Such a case is forbidden by the SQL standard, and even if we thought it
6428+
* useful to allow it, there would be ambiguity about how to match the
6429+
* list to unique indexes (in particular, it'd be unclear which index
6430+
* opclass goes with which FK column).
6431+
*/
6432+
for (i = 0; i < numattrs; i++)
6433+
{
6434+
for (j = i + 1; j < numattrs; j++)
6435+
{
6436+
if (attnums[i] == attnums[j])
6437+
ereport(ERROR,
6438+
(errcode(ERRCODE_INVALID_FOREIGN_KEY),
6439+
errmsg("foreign key referenced-columns list must not contain duplicates")));
6440+
}
6441+
}
64226442

64236443
/*
64246444
* Get the list of index OIDs for the table from the relcache, and look up
@@ -6431,8 +6451,6 @@ transformFkeyCheckAttrs(Relation pkrel,
64316451
{
64326452
HeapTuple indexTuple;
64336453
Form_pg_index indexStruct;
6434-
int i,
6435-
j;
64366454

64376455
indexoid = lfirst_oid(indexoidscan);
64386456
indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid));
@@ -6451,19 +6469,25 @@ transformFkeyCheckAttrs(Relation pkrel,
64516469
heap_attisnull(indexTuple, Anum_pg_index_indpred) &&
64526470
heap_attisnull(indexTuple, Anum_pg_index_indexprs))
64536471
{
6454-
/* Must get indclass the hard way */
64556472
Datum indclassDatum;
64566473
bool isnull;
64576474
oidvector *indclass;
64586475

6476+
/* Must get indclass the hard way */
64596477
indclassDatum = SysCacheGetAttr(INDEXRELID, indexTuple,
64606478
Anum_pg_index_indclass, &isnull);
64616479
Assert(!isnull);
64626480
indclass = (oidvector *) DatumGetPointer(indclassDatum);
64636481

64646482
/*
64656483
* The given attnum list may match the index columns in any order.
6466-
* Check that each list is a subset of the other.
6484+
* Check for a match, and extract the appropriate opclasses while
6485+
* we're at it.
6486+
*
6487+
* We know that attnums[] is duplicate-free per the test at the
6488+
* start of this function, and we checked above that the number of
6489+
* index columns agrees, so if we find a match for each attnums[]
6490+
* entry then we must have a one-to-one match in some order.
64676491
*/
64686492
for (i = 0; i < numattrs; i++)
64696493
{
@@ -6472,31 +6496,14 @@ transformFkeyCheckAttrs(Relation pkrel,
64726496
{
64736497
if (attnums[i] == indexStruct->indkey.values[j])
64746498
{
6499+
opclasses[i] = indclass->values[j];
64756500
found = true;
64766501
break;
64776502
}
64786503
}
64796504
if (!found)
64806505
break;
64816506
}
6482-
if (found)
6483-
{
6484-
for (i = 0; i < numattrs; i++)
6485-
{
6486-
found = false;
6487-
for (j = 0; j < numattrs; j++)
6488-
{
6489-
if (attnums[j] == indexStruct->indkey.values[i])
6490-
{
6491-
opclasses[j] = indclass->values[i];
6492-
found = true;
6493-
break;
6494-
}
6495-
}
6496-
if (!found)
6497-
break;
6498-
}
6499-
}
65006507

65016508
/*
65026509
* Refuse to use a deferrable unique/primary key. This is per SQL

0 commit comments

Comments
 (0)