Commit f177660a authored by Peter Eisentraut's avatar Peter Eisentraut

Include all columns in default names for foreign key constraints

When creating a name for a foreign key constraint when none is
specified, use all column names instead of only the first one, similar
to how it is already done for index names.

Author: Paul Martinez <hellopfm@gmail.com>
Reviewed-by: default avatarPeter Eisentraut <peter.eisentraut@2ndquadrant.com>
Discussion: https://www.postgresql.org/message-id/flat/CAF+2_SFjky6XRfLNRXpkG97W6PRbOO_mjAxqXzAAimU=c7w7_A@mail.gmail.com
parent bbb96c37
......@@ -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)
......
......@@ -498,7 +498,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)
......
......@@ -402,6 +402,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,
......@@ -7191,7 +7192,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);
......@@ -7210,6 +7211,45 @@ ATExecAddConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
return address;
}
/*
* Generate the column-name portion of the constraint name 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 the "fkey" suffix.
*
* 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,
......
......@@ -655,26 +655,26 @@ CREATE TEMP TABLE PKTABLE (ptest1 int, ptest2 inet,
-- This should fail, because we just chose really odd types
CREATE TEMP TABLE FKTABLE (ftest1 cidr, ftest2 timestamp);
ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1, ftest2) references pktable;
ERROR: foreign key constraint "fktable_ftest1_fkey" cannot be implemented
ERROR: foreign key constraint "fktable_ftest1_ftest2_fkey" cannot be implemented
DETAIL: Key columns "ftest1" and "ptest1" are of incompatible types: cidr and integer.
DROP TABLE FKTABLE;
-- Again, so should this...
CREATE TEMP TABLE FKTABLE (ftest1 cidr, ftest2 timestamp);
ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1, ftest2)
references pktable(ptest1, ptest2);
ERROR: foreign key constraint "fktable_ftest1_fkey" cannot be implemented
ERROR: foreign key constraint "fktable_ftest1_ftest2_fkey" cannot be implemented
DETAIL: Key columns "ftest1" and "ptest1" are of incompatible types: cidr and integer.
DROP TABLE FKTABLE;
-- This fails because we mixed up the column ordering
CREATE TEMP TABLE FKTABLE (ftest1 int, ftest2 inet);
ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1, ftest2)
references pktable(ptest2, ptest1);
ERROR: foreign key constraint "fktable_ftest1_fkey" cannot be implemented
ERROR: foreign key constraint "fktable_ftest1_ftest2_fkey" cannot be implemented
DETAIL: Key columns "ftest1" and "ptest2" are of incompatible types: integer and inet.
-- As does this...
ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest2, ftest1)
references pktable(ptest1, ptest2);
ERROR: foreign key constraint "fktable_ftest2_fkey" cannot be implemented
ERROR: foreign key constraint "fktable_ftest2_ftest1_fkey" cannot be implemented
DETAIL: Key columns "ftest2" and "ptest1" are of incompatible types: inet and integer.
DROP TABLE FKTABLE;
DROP TABLE PKTABLE;
......
This diff is collapsed.
......@@ -2510,21 +2510,21 @@ insert into rule_and_refint_t3 values (1, 11, 12, 'row2');
insert into rule_and_refint_t3 values (1, 12, 11, 'row3');
insert into rule_and_refint_t3 values (1, 12, 12, 'row4');
insert into rule_and_refint_t3 values (1, 11, 13, 'row5');
ERROR: insert or update on table "rule_and_refint_t3" violates foreign key constraint "rule_and_refint_t3_id3a_fkey1"
ERROR: insert or update on table "rule_and_refint_t3" violates foreign key constraint "rule_and_refint_t3_id3a_id3c_fkey"
DETAIL: Key (id3a, id3c)=(1, 13) is not present in table "rule_and_refint_t2".
insert into rule_and_refint_t3 values (1, 13, 11, 'row6');
ERROR: insert or update on table "rule_and_refint_t3" violates foreign key constraint "rule_and_refint_t3_id3a_fkey"
ERROR: insert or update on table "rule_and_refint_t3" violates foreign key constraint "rule_and_refint_t3_id3a_id3b_fkey"
DETAIL: Key (id3a, id3b)=(1, 13) is not present in table "rule_and_refint_t1".
-- Ordinary table
insert into rule_and_refint_t3 values (1, 13, 11, 'row6')
on conflict do nothing;
ERROR: insert or update on table "rule_and_refint_t3" violates foreign key constraint "rule_and_refint_t3_id3a_fkey"
ERROR: insert or update on table "rule_and_refint_t3" violates foreign key constraint "rule_and_refint_t3_id3a_id3b_fkey"
DETAIL: Key (id3a, id3b)=(1, 13) is not present in table "rule_and_refint_t1".
-- rule not fired, so fk violation
insert into rule_and_refint_t3 values (1, 13, 11, 'row6')
on conflict (id3a, id3b, id3c) do update
set id3b = excluded.id3b;
ERROR: insert or update on table "rule_and_refint_t3" violates foreign key constraint "rule_and_refint_t3_id3a_fkey"
ERROR: insert or update on table "rule_and_refint_t3" violates foreign key constraint "rule_and_refint_t3_id3a_id3b_fkey"
DETAIL: Key (id3a, id3b)=(1, 13) is not present in table "rule_and_refint_t1".
-- rule fired, so unsupported
insert into shoelace values ('sl9', 0, 'pink', 35.0, 'inch', 0.0)
......@@ -2541,10 +2541,10 @@ create rule rule_and_refint_t3_ins as on insert to rule_and_refint_t3
and (rule_and_refint_t3.id3b = new.id3b))
and (rule_and_refint_t3.id3c = new.id3c));
insert into rule_and_refint_t3 values (1, 11, 13, 'row7');
ERROR: insert or update on table "rule_and_refint_t3" violates foreign key constraint "rule_and_refint_t3_id3a_fkey1"
ERROR: insert or update on table "rule_and_refint_t3" violates foreign key constraint "rule_and_refint_t3_id3a_id3c_fkey"
DETAIL: Key (id3a, id3c)=(1, 13) is not present in table "rule_and_refint_t2".
insert into rule_and_refint_t3 values (1, 13, 11, 'row8');
ERROR: insert or update on table "rule_and_refint_t3" violates foreign key constraint "rule_and_refint_t3_id3a_fkey"
ERROR: insert or update on table "rule_and_refint_t3" violates foreign key constraint "rule_and_refint_t3_id3a_id3b_fkey"
DETAIL: Key (id3a, id3b)=(1, 13) is not present in table "rule_and_refint_t1".
--
-- disallow dropping a view's rule (bug #5072)
......
......@@ -1070,6 +1070,20 @@ delete from pktable2;
update pktable2 set d = 5;
drop table pktable2, fktable2;
-- Test truncation of long foreign key names
create table pktable1 (a int primary key);
create table pktable2 (a int, b int, primary key (a, b));
create table fktable2 (
a int,
b int,
very_very_long_column_name_to_exceed_63_characters int,
foreign key (very_very_long_column_name_to_exceed_63_characters) references pktable1,
foreign key (a, very_very_long_column_name_to_exceed_63_characters) references pktable2,
foreign key (a, very_very_long_column_name_to_exceed_63_characters) references pktable2
);
select conname from pg_constraint where conrelid = 'fktable2'::regclass order by conname;
drop table pktable1, pktable2, fktable2;
--
-- Test deferred FK check on a tuple deleted by a rolled-back subtransaction
--
......@@ -1184,7 +1198,7 @@ UPDATE fk_partitioned_fk SET a = a + 1 WHERE a = 2501;
UPDATE fk_notpartitioned_pk SET b = 502 WHERE a = 500;
UPDATE fk_notpartitioned_pk SET b = 1502 WHERE a = 1500;
UPDATE fk_notpartitioned_pk SET b = 2504 WHERE a = 2500;
ALTER TABLE fk_partitioned_fk DROP CONSTRAINT fk_partitioned_fk_a_fkey;
ALTER TABLE fk_partitioned_fk DROP CONSTRAINT fk_partitioned_fk_a_b_fkey;
-- done.
DROP TABLE fk_notpartitioned_pk, fk_partitioned_fk;
......@@ -1246,7 +1260,7 @@ DELETE FROM fk_notpartitioned_pk;
SELECT count(*) FROM fk_partitioned_fk WHERE a IS NULL;
-- ON UPDATE/DELETE SET DEFAULT
ALTER TABLE fk_partitioned_fk DROP CONSTRAINT fk_partitioned_fk_a_fkey;
ALTER TABLE fk_partitioned_fk DROP CONSTRAINT fk_partitioned_fk_a_b_fkey;
ALTER TABLE fk_partitioned_fk ADD FOREIGN KEY (a, b)
REFERENCES fk_notpartitioned_pk
ON DELETE SET DEFAULT ON UPDATE SET DEFAULT;
......@@ -1261,7 +1275,7 @@ UPDATE fk_notpartitioned_pk SET a = 1500 WHERE a = 2502;
SELECT * FROM fk_partitioned_fk WHERE b = 142857;
-- ON UPDATE/DELETE CASCADE
ALTER TABLE fk_partitioned_fk DROP CONSTRAINT fk_partitioned_fk_a_fkey;
ALTER TABLE fk_partitioned_fk DROP CONSTRAINT fk_partitioned_fk_a_b_fkey;
ALTER TABLE fk_partitioned_fk ADD FOREIGN KEY (a, b)
REFERENCES fk_notpartitioned_pk
ON DELETE CASCADE ON UPDATE CASCADE;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment