Avoid name collision with NOT NULL constraints

If a CREATE TABLE statement defined a constraint whose name is identical
to the name generated for a NOT NULL constraint, we'd throw an
(unnecessary) unique key violation error on
pg_constraint_conrelid_contypid_conname_index: this can easily be
avoided by choosing a different name for the NOT NULL constraint.

Fix by passing the constraint names already created by
AddRelationNewConstraints() to AddRelationNotNullConstraints(), so that
the latter can avoid name collisions with them.

Bug: #19393
Author: Laurenz Albe <laurenz.albe@cybertec.at>
Reported-by: Hüseyin Demir <huseyin.d3r@gmail.com>
Backpatch-through: 18
Discussion: https://postgr.es/m/19393-6a82427485a744cf@postgresql.org
pull/275/head
Álvaro Herrera 2 weeks ago
parent 36bbcd5be3
commit 0eeffd31bf
No known key found for this signature in database
GPG Key ID: 1C20ACB9D5C564AE
  1. 8
      src/backend/catalog/heap.c
  2. 18
      src/backend/commands/tablecmds.c
  3. 3
      src/include/catalog/heap.h
  4. 6
      src/test/regress/expected/create_table.out
  5. 7
      src/test/regress/sql/create_table.sql

@ -2886,14 +2886,16 @@ MergeWithExistingConstraint(Relation rel, const char *ccname, Node *expr,
* for each column, giving priority to user-specified ones, and setting
* inhcount according to how many parents cause each column to get a
* not-null constraint. If a user-specified name clashes with another
* user-specified name, an error is raised.
* user-specified name, an error is raised. 'existing_constraints'
* is a list of already defined constraint names, which should be avoided
* when generating further ones.
*
* Returns a list of AttrNumber for columns that need to have the attnotnull
* flag set.
*/
List *
AddRelationNotNullConstraints(Relation rel, List *constraints,
List *old_notnulls)
List *old_notnulls, List *existing_constraints)
{
List *givennames;
List *nnnames;
@ -2905,7 +2907,7 @@ AddRelationNotNullConstraints(Relation rel, List *constraints,
* because we must raise error for user-generated name conflicts, but for
* system-generated name conflicts we just generate another.
*/
nnnames = NIL;
nnnames = list_copy(existing_constraints); /* don't scribble on input */
givennames = NIL;
/*

@ -781,6 +781,7 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
List *rawDefaults;
List *cookedDefaults;
List *nncols;
List *connames = NIL;
Datum reloptions;
ListCell *listptr;
AttrNumber attnum;
@ -1335,11 +1336,20 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
/*
* Now add any newly specified CHECK constraints to the new relation. Same
* as for defaults above, but these need to come after partitioning is set
* up.
* up. We save the constraint names that were used, to avoid dupes below.
*/
if (stmt->constraints)
AddRelationNewConstraints(rel, NIL, stmt->constraints,
true, true, false, queryString);
{
List *conlist;
conlist = AddRelationNewConstraints(rel, NIL, stmt->constraints,
true, true, false, queryString);
foreach_ptr(CookedConstraint, cons, conlist)
{
if (cons->name != NULL)
connames = lappend(connames, cons->name);
}
}
/*
* Finally, merge the not-null constraints that are declared directly with
@ -1348,7 +1358,7 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
* columns that don't yet have it.
*/
nncols = AddRelationNotNullConstraints(rel, stmt->nnconstraints,
old_notnulls);
old_notnulls, connames);
foreach_int(attrnum, nncols)
set_attnotnull(NULL, rel, attrnum, true, false);

@ -117,7 +117,8 @@ extern List *AddRelationNewConstraints(Relation rel,
const char *queryString);
extern List *AddRelationNotNullConstraints(Relation rel,
List *constraints,
List *old_notnulls);
List *old_notnulls,
List *existing_constraints);
extern void RelationClearMissing(Relation rel);

@ -161,6 +161,12 @@ ALTER TABLE remember_node_subid ALTER c TYPE bigint;
SAVEPOINT q; DROP TABLE remember_node_subid; ROLLBACK TO q;
COMMIT;
DROP TABLE remember_node_subid;
-- generated NOT NULL constraint names must not collide with explicitly named constraints
CREATE TABLE two_not_null_constraints (
col integer NOT NULL,
CONSTRAINT two_not_null_constraints_col_not_null CHECK (col IS NOT NULL)
);
DROP TABLE two_not_null_constraints;
--
-- Partitioned tables
--

@ -105,6 +105,13 @@ SAVEPOINT q; DROP TABLE remember_node_subid; ROLLBACK TO q;
COMMIT;
DROP TABLE remember_node_subid;
-- generated NOT NULL constraint names must not collide with explicitly named constraints
CREATE TABLE two_not_null_constraints (
col integer NOT NULL,
CONSTRAINT two_not_null_constraints_col_not_null CHECK (col IS NOT NULL)
);
DROP TABLE two_not_null_constraints;
--
-- Partitioned tables
--

Loading…
Cancel
Save