Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Lane2025-04-23 16:03:02 +0000
committerTom Lane2025-04-23 16:03:02 +0000
commit3db61db48ef5b8898f7e85f98548fdec79d76524 (patch)
tree7765e7c6d128db5c08ad30ba8263b1f9e79854ed /src/backend
parent994a100b37ad8c2fb8282a9fce91a16b4c832277 (diff)
Change the names generated for child foreign key constraints.
When a foreign key constraint is placed on a partitioned table, we actually make two pg_constraint entries associated with that table. (I have my doubts about the wisdom of that, but it's been like that since v12 and post-feature-freeze is no time to be messing with such entrenched decisions.) The second "child" entry always had a name generated according to the default rule, "table_column(s)_fkey[nnn]", even if the primary entry had an unrelated user-specified name. The trouble with doing that is that the default name could collide with the user-specified name of some other constraint on the same table. While we were willing to adjust the generated name to avoid collisions, that only helps if it's made second; if it's made first then creation of the other constraint would fail, potentially causing dump/reload or pg_upgrade failures. The core of the problem here is that we're infringing on user namespace, so I doubt that there's any 100% solution other than to find a way to not need the "child" entry. In the meantime, it seems like it'd be an improvement to make the child's name be the name of the parent constraint with an underscore and digit(s) appended as necessary to make it unique. This rule can in theory fail in the same way, but it seems much less probable; for one thing, this rule is guaranteed not to match primary entries having auto-generated names. (While an auto-generated primary name isn't user-specified to begin with, it acts like that during dump/reload, so collisions against such names are definitely possible.) An additional bonus, visible in some of the regression test cases that change here, arises from the fact that some error messages cite the child constraint's name not the parent's. In the previous approach the two names could be completely unrelated, leading to user confusion --- the more so since psql's \d command hides child constraints. With this approach it's hopefully much clearer which constraint-the-user-knows-about is failing. However, that does mean that there's user-visible behavior change occurring here, making it seem like not something to back-patch. I feel it's not too late for v18, though. Reported-by: Kirill Reshke <reshkekirill@gmail.com> Author: Tom Lane <tgl@sss.pgh.pa.us> Reviewed-by: Alvaro Herrera <alvherre@kurilemu.de> Discussion: https://postgr.es/m/CALdSSPhGitjpTfzEMJN-Y2x+Q-5QChSxAsmSJ1-E8mQJLkHOqQ@mail.gmail.com
Diffstat (limited to 'src/backend')
-rw-r--r--src/backend/catalog/pg_constraint.c9
-rw-r--r--src/backend/commands/tablecmds.c10
2 files changed, 13 insertions, 6 deletions
diff --git a/src/backend/catalog/pg_constraint.c b/src/backend/catalog/pg_constraint.c
index 70528679e57..2d5ac1ea813 100644
--- a/src/backend/catalog/pg_constraint.c
+++ b/src/backend/catalog/pg_constraint.c
@@ -495,6 +495,8 @@ ConstraintNameExists(const char *conname, Oid namespaceid)
* name1, name2, and label are used the same way as for makeObjectName(),
* except that the label can't be NULL; digits will be appended to the label
* if needed to create a name that is unique within the specified namespace.
+ * If the given label is empty, we only consider names that include at least
+ * one added digit.
*
* 'others' can be a list of string names already chosen within the current
* command (but not yet reflected into the catalogs); we will not choose
@@ -523,8 +525,11 @@ ChooseConstraintName(const char *name1, const char *name2,
conDesc = table_open(ConstraintRelationId, AccessShareLock);
- /* try the unmodified label first */
- strlcpy(modlabel, label, sizeof(modlabel));
+ /* try the unmodified label first, unless it's empty */
+ if (label[0] != '\0')
+ strlcpy(modlabel, label, sizeof(modlabel));
+ else
+ snprintf(modlabel, sizeof(modlabel), "%s%d", label, ++pass);
for (;;)
{
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 265b1c397fb..2705cf11330 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -10712,14 +10712,16 @@ addFkConstraint(addFkConstraintSides fkside,
/*
* Caller supplies us with a constraint name; however, it may be used in
- * this partition, so come up with a different one in that case.
+ * this partition, so come up with a different one in that case. Unless
+ * truncation to NAMEDATALEN dictates otherwise, the new name will be the
+ * supplied name with an underscore and digit(s) appended.
*/
if (ConstraintNameIsUsed(CONSTRAINT_RELATION,
RelationGetRelid(rel),
constraintname))
- conname = ChooseConstraintName(RelationGetRelationName(rel),
- ChooseForeignKeyConstraintNameAddition(fkconstraint->fk_attrs),
- "fkey",
+ conname = ChooseConstraintName(constraintname,
+ NULL,
+ "",
RelationGetNamespace(rel), NIL);
else
conname = constraintname;