Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 2f99123

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 213f0ff commit 2f99123

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
@@ -6739,6 +6739,26 @@ transformFkeyCheckAttrs(Relation pkrel,
67396739
bool found_deferrable = false;
67406740
List *indexoidlist;
67416741
ListCell *indexoidscan;
6742+
int i,
6743+
j;
6744+
6745+
/*
6746+
* Reject duplicate appearances of columns in the referenced-columns list.
6747+
* Such a case is forbidden by the SQL standard, and even if we thought it
6748+
* useful to allow it, there would be ambiguity about how to match the
6749+
* list to unique indexes (in particular, it'd be unclear which index
6750+
* opclass goes with which FK column).
6751+
*/
6752+
for (i = 0; i < numattrs; i++)
6753+
{
6754+
for (j = i + 1; j < numattrs; j++)
6755+
{
6756+
if (attnums[i] == attnums[j])
6757+
ereport(ERROR,
6758+
(errcode(ERRCODE_INVALID_FOREIGN_KEY),
6759+
errmsg("foreign key referenced-columns list must not contain duplicates")));
6760+
}
6761+
}
67426762

67436763
/*
67446764
* Get the list of index OIDs for the table from the relcache, and look up
@@ -6751,8 +6771,6 @@ transformFkeyCheckAttrs(Relation pkrel,
67516771
{
67526772
HeapTuple indexTuple;
67536773
Form_pg_index indexStruct;
6754-
int i,
6755-
j;
67566774

67576775
indexoid = lfirst_oid(indexoidscan);
67586776
indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid));
@@ -6771,19 +6789,25 @@ transformFkeyCheckAttrs(Relation pkrel,
67716789
heap_attisnull(indexTuple, Anum_pg_index_indpred) &&
67726790
heap_attisnull(indexTuple, Anum_pg_index_indexprs))
67736791
{
6774-
/* Must get indclass the hard way */
67756792
Datum indclassDatum;
67766793
bool isnull;
67776794
oidvector *indclass;
67786795

6796+
/* Must get indclass the hard way */
67796797
indclassDatum = SysCacheGetAttr(INDEXRELID, indexTuple,
67806798
Anum_pg_index_indclass, &isnull);
67816799
Assert(!isnull);
67826800
indclass = (oidvector *) DatumGetPointer(indclassDatum);
67836801

67846802
/*
67856803
* The given attnum list may match the index columns in any order.
6786-
* Check that each list is a subset of the other.
6804+
* Check for a match, and extract the appropriate opclasses while
6805+
* we're at it.
6806+
*
6807+
* We know that attnums[] is duplicate-free per the test at the
6808+
* start of this function, and we checked above that the number of
6809+
* index columns agrees, so if we find a match for each attnums[]
6810+
* entry then we must have a one-to-one match in some order.
67876811
*/
67886812
for (i = 0; i < numattrs; i++)
67896813
{
@@ -6792,31 +6816,14 @@ transformFkeyCheckAttrs(Relation pkrel,
67926816
{
67936817
if (attnums[i] == indexStruct->indkey.values[j])
67946818
{
6819+
opclasses[i] = indclass->values[j];
67956820
found = true;
67966821
break;
67976822
}
67986823
}
67996824
if (!found)
68006825
break;
68016826
}
6802-
if (found)
6803-
{
6804-
for (i = 0; i < numattrs; i++)
6805-
{
6806-
found = false;
6807-
for (j = 0; j < numattrs; j++)
6808-
{
6809-
if (attnums[j] == indexStruct->indkey.values[i])
6810-
{
6811-
opclasses[j] = indclass->values[i];
6812-
found = true;
6813-
break;
6814-
}
6815-
}
6816-
if (!found)
6817-
break;
6818-
}
6819-
}
68206827

68216828
/*
68226829
* Refuse to use a deferrable unique/primary key. This is per SQL

0 commit comments

Comments
 (0)