diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index d263903622..1989cfec35 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -2109,7 +2109,8 @@ ChooseIndexName(const char *tabname, Oid namespaceId, * We know that less than NAMEDATALEN characters will actually be used, * so we can truncate the result once we've generated that many. * - * XXX See also ChooseExtendedStatisticNameAddition. + * XXX See also ChooseForeignKeyConstraintNameAddition and + * ChooseExtendedStatisticNameAddition. */ static char * ChooseIndexNameAddition(List *colnames) diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c index ad385785da..5ecbe4fd8a 100644 --- a/src/backend/commands/statscmds.c +++ b/src/backend/commands/statscmds.c @@ -496,7 +496,8 @@ ChooseExtendedStatisticName(const char *name1, const char *name2, * We know that less than NAMEDATALEN characters will actually be used, * so we can truncate the result once we've generated that many. * - * XXX see also ChooseIndexNameAddition. + * XXX see also ChooseForeignKeyConstraintNameAddition and + * ChooseIndexNameAddition. */ static char * ChooseExtendedStatisticNameAddition(List *exprs) diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index d2781cbf19..226b2843ed 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -401,6 +401,7 @@ static ObjectAddress ATExecAddConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel, Constraint *newConstraint, bool recurse, bool is_readd, LOCKMODE lockmode); +static char * ChooseForeignKeyConstraintNameAddition(List *colnames); static ObjectAddress ATExecAddIndexConstraint(AlteredTableInfo *tab, Relation rel, IndexStmt *stmt, LOCKMODE lockmode); static ObjectAddress ATAddCheckConstraint(List **wqueue, @@ -7063,7 +7064,7 @@ ATExecAddConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel, else newConstraint->conname = ChooseConstraintName(RelationGetRelationName(rel), - strVal(linitial(newConstraint->fk_attrs)), + ChooseForeignKeyConstraintNameAddition(newConstraint->fk_attrs), "fkey", RelationGetNamespace(rel), NIL); @@ -7082,6 +7083,43 @@ ATExecAddConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel, return address; } +/* + * Generate "name2" for a new foreign key given the list of column names that + * reference the referenced table. This will be passed to ChooseConstraintName + * along with the parent table name and a suitable label. + * + * We know that less than NAMEDATALEN characters will actually be used, + * so we can truncate the result once we've generated that many. + * + * XXX see also ChooseExtendedStatisticNameAddition and ChooseIndexNameAddition. + */ +static char * +ChooseForeignKeyConstraintNameAddition(List *colnames) +{ + char buf[NAMEDATALEN * 2]; + int buflen = 0; + ListCell *lc; + + buf[0] = '\0'; + foreach(lc, colnames) + { + const char *name = strVal(lfirst(lc)); + + if (buflen > 0) + buf[buflen++] = '_'; /* insert _ between names */ + + /* + * At this point we have buflen <= NAMEDATALEN. name should be less + * than NAMEDATALEN already, but use strlcpy for paranoia. + */ + strlcpy(buf + buflen, name, NAMEDATALEN); + buflen += strlen(buf + buflen); + if (buflen >= NAMEDATALEN) + break; + } + return pstrdup(buf); +} + /* * Add a check constraint to a single table and its children. Returns the * address of the constraint added to the parent relation, if one gets added, diff --git a/src/test/regress/expected/foreign_key.out b/src/test/regress/expected/foreign_key.out index 085c9aba11..d7c9056e8e 100644 --- a/src/test/regress/expected/foreign_key.out +++ b/src/test/regress/expected/foreign_key.out @@ -1394,6 +1394,19 @@ update pktable2 set d = 5; ERROR: update or delete on table "pktable2" violates foreign key constraint "fktable2_d_fkey" on table "fktable2" DETAIL: Key (d, e)=(4, 5) is still referenced from table "fktable2". drop table pktable2, fktable2; +-- Test default name of composite foreign keys +create table singular_pk_table (a int primary key); +create table composite_pk_table (b int, c int, primary key (b, c)); +create table composite_fk_table (a int, b int, c int, foreign key (a) references singular_pk_table, foreign key (b, c) references composite_pk_table); +alter table composite_fk_table add foreign key (a, c) references composite_pk_table; +select conname from pg_constraint where conrelid = 'composite_fk_table'::regclass order by conname; + conname +----------------------------- + composite_fk_table_a_c_fkey + composite_fk_table_a_fkey + composite_fk_table_b_c_fkey +(3 rows) + -- -- Test deferred FK check on a tuple deleted by a rolled-back subtransaction -- diff --git a/src/test/regress/sql/foreign_key.sql b/src/test/regress/sql/foreign_key.sql index 068ab2aab7..cf875582db 100644 --- a/src/test/regress/sql/foreign_key.sql +++ b/src/test/regress/sql/foreign_key.sql @@ -1030,6 +1030,13 @@ delete from pktable2; update pktable2 set d = 5; drop table pktable2, fktable2; +-- Test default name of composite foreign keys +create table singular_pk_table (a int primary key); +create table composite_pk_table (b int, c int, primary key (b, c)); +create table composite_fk_table (a int, b int, c int, foreign key (a) references singular_pk_table, foreign key (b, c) references composite_pk_table); +alter table composite_fk_table add foreign key (a, c) references composite_pk_table; +select conname from pg_constraint where conrelid = 'composite_fk_table'::regclass order by conname; + -- -- Test deferred FK check on a tuple deleted by a rolled-back subtransaction --