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
* 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;
* 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;
/*
List *rawDefaults;
List *cookedDefaults;
List *nncols;
+ List *connames = NIL;
Datum reloptions;
ListCell *listptr;
AttrNumber attnum;
/*
* 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
* 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);
const char *queryString);
extern List *AddRelationNotNullConstraints(Relation rel,
List *constraints,
- List *old_notnulls);
+ List *old_notnulls,
+ List *existing_constraints);
extern void RelationClearMissing(Relation rel);
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
--
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
--