Skip to content

Commit 4ff4974

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 f63be26 commit 4ff4974

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
@@ -5165,6 +5165,26 @@ transformFkeyCheckAttrs(Relation pkrel,
51655165
bool found_deferrable = false;
51665166
List *indexoidlist;
51675167
ListCell *indexoidscan;
5168+
int i,
5169+
j;
5170+
5171+
/*
5172+
* Reject duplicate appearances of columns in the referenced-columns list.
5173+
* Such a case is forbidden by the SQL standard, and even if we thought it
5174+
* useful to allow it, there would be ambiguity about how to match the
5175+
* list to unique indexes (in particular, it'd be unclear which index
5176+
* opclass goes with which FK column).
5177+
*/
5178+
for (i = 0; i < numattrs; i++)
5179+
{
5180+
for (j = i + 1; j < numattrs; j++)
5181+
{
5182+
if (attnums[i] == attnums[j])
5183+
ereport(ERROR,
5184+
(errcode(ERRCODE_INVALID_FOREIGN_KEY),
5185+
errmsg("foreign key referenced-columns list must not contain duplicates")));
5186+
}
5187+
}
51685188

51695189
/*
51705190
* Get the list of index OIDs for the table from the relcache, and look up
@@ -5177,8 +5197,6 @@ transformFkeyCheckAttrs(Relation pkrel,
51775197
{
51785198
HeapTuple indexTuple;
51795199
Form_pg_index indexStruct;
5180-
int i,
5181-
j;
51825200

51835201
indexoid = lfirst_oid(indexoidscan);
51845202
indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid));
@@ -5197,19 +5215,25 @@ transformFkeyCheckAttrs(Relation pkrel,
51975215
heap_attisnull(indexTuple, Anum_pg_index_indpred) &&
51985216
heap_attisnull(indexTuple, Anum_pg_index_indexprs))
51995217
{
5200-
/* Must get indclass the hard way */
52015218
Datum indclassDatum;
52025219
bool isnull;
52035220
oidvector *indclass;
52045221

5222+
/* Must get indclass the hard way */
52055223
indclassDatum = SysCacheGetAttr(INDEXRELID, indexTuple,
52065224
Anum_pg_index_indclass, &isnull);
52075225
Assert(!isnull);
52085226
indclass = (oidvector *) DatumGetPointer(indclassDatum);
52095227

52105228
/*
52115229
* The given attnum list may match the index columns in any order.
5212-
* Check that each list is a subset of the other.
5230+
* Check for a match, and extract the appropriate opclasses while
5231+
* we're at it.
5232+
*
5233+
* We know that attnums[] is duplicate-free per the test at the
5234+
* start of this function, and we checked above that the number of
5235+
* index columns agrees, so if we find a match for each attnums[]
5236+
* entry then we must have a one-to-one match in some order.
52135237
*/
52145238
for (i = 0; i < numattrs; i++)
52155239
{
@@ -5218,31 +5242,14 @@ transformFkeyCheckAttrs(Relation pkrel,
52185242
{
52195243
if (attnums[i] == indexStruct->indkey.values[j])
52205244
{
5245+
opclasses[i] = indclass->values[j];
52215246
found = true;
52225247
break;
52235248
}
52245249
}
52255250
if (!found)
52265251
break;
52275252
}
5228-
if (found)
5229-
{
5230-
for (i = 0; i < numattrs; i++)
5231-
{
5232-
found = false;
5233-
for (j = 0; j < numattrs; j++)
5234-
{
5235-
if (attnums[j] == indexStruct->indkey.values[i])
5236-
{
5237-
opclasses[j] = indclass->values[i];
5238-
found = true;
5239-
break;
5240-
}
5241-
}
5242-
if (!found)
5243-
break;
5244-
}
5245-
}
52465253

52475254
/*
52485255
* Refuse to use a deferrable unique/primary key. This is per SQL

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy