summaryrefslogtreecommitdiff
path: root/src/backend/commands
diff options
context:
space:
mode:
authorTom Lane2025-04-05 00:11:48 +0000
committerTom Lane2025-04-05 00:11:48 +0000
commit43b8e6c4abfb632bbff5b3a26dc39bf3f188afff (patch)
treea3c419a4a0e7e35a34475900ca19678f7e369097 /src/backend/commands
parent0f43083d16f4be7c01efa80d05d0eef5e5ff69d3 (diff)
Repair misbehavior with duplicate entries in FK SET column lists.
Since v15 we've had an option to apply a foreign key constraint's ON DELETE SET DEFAULT or SET NULL action to just some of the referencing columns. There was not a check for duplicate entries in the list of columns-to-set, though. That caused a potential memory stomp in CreateConstraintEntry(), which incautiously assumed that the list of columns-to-set couldn't be longer than the number of key columns. Even after fixing that, the case doesn't work because you get an error like "multiple assignments to same column" from the SQL command that is generated to do the update. We could either raise an error for duplicate columns or silently suppress the dups, and after a bit of thought I chose to do the latter. This is motivated by the fact that duplicates in the FK column list are legal, so it's not real clear why duplicates in the columns-to-set list shouldn't be. Of course there's no need to actually set the column more than once. I left in the fix in CreateConstraintEntry() too, just because it didn't seem like such low-level code ought to be making assumptions about what it's handed. Bug: #18879 Reported-by: Yu Liang <[email protected]> Author: Tom Lane <[email protected]> Discussion: https://postgr.es/m/[email protected] Backpatch-through: 15
Diffstat (limited to 'src/backend/commands')
-rw-r--r--src/backend/commands/tablecmds.c35
1 files changed, 28 insertions, 7 deletions
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 11fcb51a165..4397123398e 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -557,8 +557,8 @@ static ObjectAddress ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *
Relation rel, Constraint *fkconstraint,
bool recurse, bool recursing,
LOCKMODE lockmode);
-static void validateFkOnDeleteSetColumns(int numfks, const int16 *fkattnums,
- int numfksetcols, const int16 *fksetcolsattnums,
+static int validateFkOnDeleteSetColumns(int numfks, const int16 *fkattnums,
+ int numfksetcols, int16 *fksetcolsattnums,
List *fksetcols);
static ObjectAddress addFkConstraint(addFkConstraintSides fkside,
char *constraintname,
@@ -10037,9 +10037,10 @@ ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
numfkdelsetcols = transformColumnNameList(RelationGetRelid(rel),
fkconstraint->fk_del_set_cols,
fkdelsetcols, NULL, NULL);
- validateFkOnDeleteSetColumns(numfks, fkattnum,
- numfkdelsetcols, fkdelsetcols,
- fkconstraint->fk_del_set_cols);
+ numfkdelsetcols = validateFkOnDeleteSetColumns(numfks, fkattnum,
+ numfkdelsetcols,
+ fkdelsetcols,
+ fkconstraint->fk_del_set_cols);
/*
* If the attribute list for the referenced table was omitted, lookup the
@@ -10499,17 +10500,23 @@ ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
* validateFkOnDeleteSetColumns
* Verifies that columns used in ON DELETE SET NULL/DEFAULT (...)
* column lists are valid.
+ *
+ * If there are duplicates in the fksetcolsattnums[] array, this silently
+ * removes the dups. The new count of numfksetcols is returned.
*/
-void
+static int
validateFkOnDeleteSetColumns(int numfks, const int16 *fkattnums,
- int numfksetcols, const int16 *fksetcolsattnums,
+ int numfksetcols, int16 *fksetcolsattnums,
List *fksetcols)
{
+ int numcolsout = 0;
+
for (int i = 0; i < numfksetcols; i++)
{
int16 setcol_attnum = fksetcolsattnums[i];
bool seen = false;
+ /* Make sure it's in fkattnums[] */
for (int j = 0; j < numfks; j++)
{
if (fkattnums[j] == setcol_attnum)
@@ -10527,7 +10534,21 @@ validateFkOnDeleteSetColumns(int numfks, const int16 *fkattnums,
(errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
errmsg("column \"%s\" referenced in ON DELETE SET action must be part of foreign key", col)));
}
+
+ /* Now check for dups */
+ seen = false;
+ for (int j = 0; j < numcolsout; j++)
+ {
+ if (fksetcolsattnums[j] == setcol_attnum)
+ {
+ seen = true;
+ break;
+ }
+ }
+ if (!seen)
+ fksetcolsattnums[numcolsout++] = setcol_attnum;
}
+ return numcolsout;
}
/*