Re: Two constraints with the same name not always allowed - Mailing list pgsql-bugs
From | Tom Lane |
---|---|
Subject | Re: Two constraints with the same name not always allowed |
Date | |
Msg-id | 2152.1535927464@sss.pgh.pa.us Whole thread Raw |
In response to | Re: Two constraints with the same name not always allowed (Tom Lane <tgl@sss.pgh.pa.us>) |
List | pgsql-bugs |
I wrote: > I think the code supposes that checking for duplicate relation name > is sufficient; but of course it is not if we want a table's constraints > to have distinct names, since they may not all correspond to indexes. > I do not think we can back-patch a change here --- it might break > databases that are working satisfactorily today. But it seems like > we could tighten this up in HEAD and maybe v11. Attached is a draft patchset for this. 0001 replaces the existing index with a unique one and makes necessary backend code adjustments. Said adjustments could have been as simple as s/ConstraintRelidIndexId/ConstraintRelidTypidNameIndexId/g -- I tried that, and it passed regression tests -- but I couldn't resist the temptation to fix a few places that could make better use of the redesigned index. 0002 adds user-friendliness by installing a nicer error message for the complained-of case and by improving ChooseIndexName to avoid autogenerating index names that will conflict with existing constraints. I didn't look for possible documentation changes yet, but I think the code changes are OK. regards, tom lane diff --git a/src/backend/catalog/pg_constraint.c b/src/backend/catalog/pg_constraint.c index 7a6d158..3d65464 100644 *** a/src/backend/catalog/pg_constraint.c --- b/src/backend/catalog/pg_constraint.c *************** CloneForeignKeyConstraints(Oid parentId, *** 443,449 **** ScanKeyInit(&key, Anum_pg_constraint_conrelid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(parentId)); ! scan = systable_beginscan(pg_constraint, ConstraintRelidIndexId, true, NULL, 1, &key); while ((tuple = systable_getnext(scan)) != NULL) --- 443,449 ---- ScanKeyInit(&key, Anum_pg_constraint_conrelid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(parentId)); ! scan = systable_beginscan(pg_constraint, ConstraintRelidTypidNameIndexId, true, NULL, 1, &key); while ((tuple = systable_getnext(scan)) != NULL) *************** AlterConstraintNamespaces(Oid ownerId, O *** 944,975 **** Oid newNspId, bool isType, ObjectAddresses *objsMoved) { Relation conRel; ! ScanKeyData key[1]; SysScanDesc scan; HeapTuple tup; conRel = heap_open(ConstraintRelationId, RowExclusiveLock); ! if (isType) ! { ! ScanKeyInit(&key[0], ! Anum_pg_constraint_contypid, ! BTEqualStrategyNumber, F_OIDEQ, ! ObjectIdGetDatum(ownerId)); ! ! scan = systable_beginscan(conRel, ConstraintTypidIndexId, true, ! NULL, 1, key); ! } ! else ! { ! ScanKeyInit(&key[0], ! Anum_pg_constraint_conrelid, ! BTEqualStrategyNumber, F_OIDEQ, ! ObjectIdGetDatum(ownerId)); ! scan = systable_beginscan(conRel, ConstraintRelidIndexId, true, ! NULL, 1, key); ! } while (HeapTupleIsValid((tup = systable_getnext(scan)))) { --- 944,966 ---- Oid newNspId, bool isType, ObjectAddresses *objsMoved) { Relation conRel; ! ScanKeyData key[2]; SysScanDesc scan; HeapTuple tup; conRel = heap_open(ConstraintRelationId, RowExclusiveLock); ! ScanKeyInit(&key[0], ! Anum_pg_constraint_conrelid, ! BTEqualStrategyNumber, F_OIDEQ, ! ObjectIdGetDatum(isType ? InvalidOid : ownerId)); ! ScanKeyInit(&key[1], ! Anum_pg_constraint_contypid, ! BTEqualStrategyNumber, F_OIDEQ, ! ObjectIdGetDatum(isType ? ownerId : InvalidOid)); ! scan = systable_beginscan(conRel, ConstraintRelidTypidNameIndexId, true, ! NULL, 2, key); while (HeapTupleIsValid((tup = systable_getnext(scan)))) { *************** get_relation_constraint_oid(Oid relid, c *** 1059,1095 **** Relation pg_constraint; HeapTuple tuple; SysScanDesc scan; ! ScanKeyData skey[1]; Oid conOid = InvalidOid; - /* - * Fetch the constraint tuple from pg_constraint. There may be more than - * one match, because constraints are not required to have unique names; - * if so, error out. - */ pg_constraint = heap_open(ConstraintRelationId, AccessShareLock); ScanKeyInit(&skey[0], Anum_pg_constraint_conrelid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(relid)); ! scan = systable_beginscan(pg_constraint, ConstraintRelidIndexId, true, ! NULL, 1, skey); ! while (HeapTupleIsValid(tuple = systable_getnext(scan))) { ! Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tuple); ! ! if (strcmp(NameStr(con->conname), conname) == 0) ! { ! if (OidIsValid(conOid)) ! ereport(ERROR, ! (errcode(ERRCODE_DUPLICATE_OBJECT), ! errmsg("table \"%s\" has multiple constraints named \"%s\"", ! get_rel_name(relid), conname))); ! conOid = HeapTupleGetOid(tuple); ! } } systable_endscan(scan); --- 1050,1080 ---- Relation pg_constraint; HeapTuple tuple; SysScanDesc scan; ! ScanKeyData skey[3]; Oid conOid = InvalidOid; pg_constraint = heap_open(ConstraintRelationId, AccessShareLock); ScanKeyInit(&skey[0], Anum_pg_constraint_conrelid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(relid)); + ScanKeyInit(&skey[1], + Anum_pg_constraint_contypid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(InvalidOid)); + ScanKeyInit(&skey[2], + Anum_pg_constraint_conname, + BTEqualStrategyNumber, F_NAMEEQ, + CStringGetDatum(conname)); ! scan = systable_beginscan(pg_constraint, ConstraintRelidTypidNameIndexId, true, ! NULL, 3, skey); ! /* There can be at most one matching row */ ! if (HeapTupleIsValid(tuple = systable_getnext(scan))) { ! conOid = HeapTupleGetOid(tuple); } systable_endscan(scan); *************** get_relation_constraint_attnos(Oid relid *** 1126,1192 **** Relation pg_constraint; HeapTuple tuple; SysScanDesc scan; ! ScanKeyData skey[1]; /* Set *constraintOid, to avoid complaints about uninitialized vars */ *constraintOid = InvalidOid; - /* - * Fetch the constraint tuple from pg_constraint. There may be more than - * one match, because constraints are not required to have unique names; - * if so, error out. - */ pg_constraint = heap_open(ConstraintRelationId, AccessShareLock); ScanKeyInit(&skey[0], Anum_pg_constraint_conrelid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(relid)); ! scan = systable_beginscan(pg_constraint, ConstraintRelidIndexId, true, ! NULL, 1, skey); ! while (HeapTupleIsValid(tuple = systable_getnext(scan))) { - Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tuple); Datum adatum; bool isNull; - ArrayType *arr; - int16 *attnums; - int numcols; - int i; - - /* Check the constraint name */ - if (strcmp(NameStr(con->conname), conname) != 0) - continue; - if (OidIsValid(*constraintOid)) - ereport(ERROR, - (errcode(ERRCODE_DUPLICATE_OBJECT), - errmsg("table \"%s\" has multiple constraints named \"%s\"", - get_rel_name(relid), conname))); *constraintOid = HeapTupleGetOid(tuple); /* Extract the conkey array, ie, attnums of constrained columns */ adatum = heap_getattr(tuple, Anum_pg_constraint_conkey, RelationGetDescr(pg_constraint), &isNull); ! if (isNull) ! continue; /* no constrained columns */ ! arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */ ! numcols = ARR_DIMS(arr)[0]; ! if (ARR_NDIM(arr) != 1 || ! numcols < 0 || ! ARR_HASNULL(arr) || ! ARR_ELEMTYPE(arr) != INT2OID) ! elog(ERROR, "conkey is not a 1-D smallint array"); ! attnums = (int16 *) ARR_DATA_PTR(arr); ! /* Construct the result value */ ! for (i = 0; i < numcols; i++) ! { ! conattnos = bms_add_member(conattnos, ! attnums[i] - FirstLowInvalidHeapAttributeNumber); } } --- 1111,1172 ---- Relation pg_constraint; HeapTuple tuple; SysScanDesc scan; ! ScanKeyData skey[3]; /* Set *constraintOid, to avoid complaints about uninitialized vars */ *constraintOid = InvalidOid; pg_constraint = heap_open(ConstraintRelationId, AccessShareLock); ScanKeyInit(&skey[0], Anum_pg_constraint_conrelid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(relid)); + ScanKeyInit(&skey[1], + Anum_pg_constraint_contypid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(InvalidOid)); + ScanKeyInit(&skey[2], + Anum_pg_constraint_conname, + BTEqualStrategyNumber, F_NAMEEQ, + CStringGetDatum(conname)); ! scan = systable_beginscan(pg_constraint, ConstraintRelidTypidNameIndexId, true, ! NULL, 3, skey); ! /* There can be at most one matching row */ ! if (HeapTupleIsValid(tuple = systable_getnext(scan))) { Datum adatum; bool isNull; *constraintOid = HeapTupleGetOid(tuple); /* Extract the conkey array, ie, attnums of constrained columns */ adatum = heap_getattr(tuple, Anum_pg_constraint_conkey, RelationGetDescr(pg_constraint), &isNull); ! if (!isNull) ! { ! ArrayType *arr; ! int numcols; ! int16 *attnums; ! int i; ! arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */ ! numcols = ARR_DIMS(arr)[0]; ! if (ARR_NDIM(arr) != 1 || ! numcols < 0 || ! ARR_HASNULL(arr) || ! ARR_ELEMTYPE(arr) != INT2OID) ! elog(ERROR, "conkey is not a 1-D smallint array"); ! attnums = (int16 *) ARR_DATA_PTR(arr); ! /* Construct the result value */ ! for (i = 0; i < numcols; i++) ! { ! conattnos = bms_add_member(conattnos, ! attnums[i] - FirstLowInvalidHeapAttributeNumber); ! } } } *************** get_relation_idx_constraint_oid(Oid rela *** 1224,1230 **** BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(relationId)); ! scan = systable_beginscan(pg_constraint, ConstraintRelidIndexId, true, NULL, 1, &key); while ((tuple = systable_getnext(scan)) != NULL) { --- 1204,1210 ---- BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(relationId)); ! scan = systable_beginscan(pg_constraint, ConstraintRelidTypidNameIndexId, true, NULL, 1, &key); while ((tuple = systable_getnext(scan)) != NULL) { *************** get_domain_constraint_oid(Oid typid, con *** 1254,1291 **** Relation pg_constraint; HeapTuple tuple; SysScanDesc scan; ! ScanKeyData skey[1]; Oid conOid = InvalidOid; - /* - * Fetch the constraint tuple from pg_constraint. There may be more than - * one match, because constraints are not required to have unique names; - * if so, error out. - */ pg_constraint = heap_open(ConstraintRelationId, AccessShareLock); ScanKeyInit(&skey[0], Anum_pg_constraint_contypid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(typid)); ! scan = systable_beginscan(pg_constraint, ConstraintTypidIndexId, true, ! NULL, 1, skey); ! ! while (HeapTupleIsValid(tuple = systable_getnext(scan))) ! { ! Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tuple); ! if (strcmp(NameStr(con->conname), conname) == 0) ! { ! if (OidIsValid(conOid)) ! ereport(ERROR, ! (errcode(ERRCODE_DUPLICATE_OBJECT), ! errmsg("domain %s has multiple constraints named \"%s\"", ! format_type_be(typid), conname))); ! conOid = HeapTupleGetOid(tuple); ! } ! } systable_endscan(scan); --- 1234,1263 ---- Relation pg_constraint; HeapTuple tuple; SysScanDesc scan; ! ScanKeyData skey[3]; Oid conOid = InvalidOid; pg_constraint = heap_open(ConstraintRelationId, AccessShareLock); ScanKeyInit(&skey[0], + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(InvalidOid)); + ScanKeyInit(&skey[1], Anum_pg_constraint_contypid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(typid)); + ScanKeyInit(&skey[2], + Anum_pg_constraint_conname, + BTEqualStrategyNumber, F_NAMEEQ, + CStringGetDatum(conname)); ! scan = systable_beginscan(pg_constraint, ConstraintRelidTypidNameIndexId, true, ! NULL, 3, skey); ! /* There can be at most one matching row */ ! if (HeapTupleIsValid(tuple = systable_getnext(scan))) ! conOid = HeapTupleGetOid(tuple); systable_endscan(scan); *************** get_primary_key_attnos(Oid relid, bool d *** 1335,1341 **** BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(relid)); ! scan = systable_beginscan(pg_constraint, ConstraintRelidIndexId, true, NULL, 1, skey); while (HeapTupleIsValid(tuple = systable_getnext(scan))) --- 1307,1313 ---- BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(relid)); ! scan = systable_beginscan(pg_constraint, ConstraintRelidTypidNameIndexId, true, NULL, 1, skey); while (HeapTupleIsValid(tuple = systable_getnext(scan))) diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index f46af41..a3edef5 100644 *** a/src/backend/commands/tablecmds.c --- b/src/backend/commands/tablecmds.c *************** ATExecAlterConstraint(Relation rel, Alte *** 7801,7810 **** Constraint *cmdcon; Relation conrel; SysScanDesc scan; ! ScanKeyData key; HeapTuple contuple; ! Form_pg_constraint currcon = NULL; ! bool found = false; ObjectAddress address; cmdcon = castNode(Constraint, cmd->def); --- 7801,7809 ---- Constraint *cmdcon; Relation conrel; SysScanDesc scan; ! ScanKeyData skey[3]; HeapTuple contuple; ! Form_pg_constraint currcon; ObjectAddress address; cmdcon = castNode(Constraint, cmd->def); *************** ATExecAlterConstraint(Relation rel, Alte *** 7814,7842 **** /* * Find and check the target constraint */ ! ScanKeyInit(&key, Anum_pg_constraint_conrelid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(rel))); ! scan = systable_beginscan(conrel, ConstraintRelidIndexId, ! true, NULL, 1, &key); ! ! while (HeapTupleIsValid(contuple = systable_getnext(scan))) ! { ! currcon = (Form_pg_constraint) GETSTRUCT(contuple); ! if (strcmp(NameStr(currcon->conname), cmdcon->conname) == 0) ! { ! found = true; ! break; ! } ! } ! if (!found) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("constraint \"%s\" of relation \"%s\" does not exist", cmdcon->conname, RelationGetRelationName(rel)))); if (currcon->contype != CONSTRAINT_FOREIGN) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), --- 7813,7841 ---- /* * Find and check the target constraint */ ! ScanKeyInit(&skey[0], Anum_pg_constraint_conrelid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(rel))); ! ScanKeyInit(&skey[1], ! Anum_pg_constraint_contypid, ! BTEqualStrategyNumber, F_OIDEQ, ! ObjectIdGetDatum(InvalidOid)); ! ScanKeyInit(&skey[2], ! Anum_pg_constraint_conname, ! BTEqualStrategyNumber, F_NAMEEQ, ! CStringGetDatum(cmdcon->conname)); ! scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId, ! true, NULL, 3, skey); ! /* There can be at most one matching row */ ! if (!HeapTupleIsValid(contuple = systable_getnext(scan))) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("constraint \"%s\" of relation \"%s\" does not exist", cmdcon->conname, RelationGetRelationName(rel)))); + currcon = (Form_pg_constraint) GETSTRUCT(contuple); if (currcon->contype != CONSTRAINT_FOREIGN) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), *************** ATExecValidateConstraint(Relation rel, c *** 7969,7978 **** { Relation conrel; SysScanDesc scan; ! ScanKeyData key; HeapTuple tuple; ! Form_pg_constraint con = NULL; ! bool found = false; ObjectAddress address; conrel = heap_open(ConstraintRelationId, RowExclusiveLock); --- 7968,7976 ---- { Relation conrel; SysScanDesc scan; ! ScanKeyData skey[3]; HeapTuple tuple; ! Form_pg_constraint con; ObjectAddress address; conrel = heap_open(ConstraintRelationId, RowExclusiveLock); *************** ATExecValidateConstraint(Relation rel, c *** 7980,8008 **** /* * Find and check the target constraint */ ! ScanKeyInit(&key, Anum_pg_constraint_conrelid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(rel))); ! scan = systable_beginscan(conrel, ConstraintRelidIndexId, ! true, NULL, 1, &key); ! ! while (HeapTupleIsValid(tuple = systable_getnext(scan))) ! { ! con = (Form_pg_constraint) GETSTRUCT(tuple); ! if (strcmp(NameStr(con->conname), constrName) == 0) ! { ! found = true; ! break; ! } ! } ! if (!found) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("constraint \"%s\" of relation \"%s\" does not exist", constrName, RelationGetRelationName(rel)))); if (con->contype != CONSTRAINT_FOREIGN && con->contype != CONSTRAINT_CHECK) ereport(ERROR, --- 7978,8006 ---- /* * Find and check the target constraint */ ! ScanKeyInit(&skey[0], Anum_pg_constraint_conrelid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(rel))); ! ScanKeyInit(&skey[1], ! Anum_pg_constraint_contypid, ! BTEqualStrategyNumber, F_OIDEQ, ! ObjectIdGetDatum(InvalidOid)); ! ScanKeyInit(&skey[2], ! Anum_pg_constraint_conname, ! BTEqualStrategyNumber, F_NAMEEQ, ! CStringGetDatum(constrName)); ! scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId, ! true, NULL, 3, skey); ! /* There can be at most one matching row */ ! if (!HeapTupleIsValid(tuple = systable_getnext(scan))) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("constraint \"%s\" of relation \"%s\" does not exist", constrName, RelationGetRelationName(rel)))); + con = (Form_pg_constraint) GETSTRUCT(tuple); if (con->contype != CONSTRAINT_FOREIGN && con->contype != CONSTRAINT_CHECK) ereport(ERROR, *************** ATExecDropConstraint(Relation rel, const *** 8865,8871 **** Relation conrel; Form_pg_constraint con; SysScanDesc scan; ! ScanKeyData key; HeapTuple tuple; bool found = false; bool is_no_inherit_constraint = false; --- 8863,8869 ---- Relation conrel; Form_pg_constraint con; SysScanDesc scan; ! ScanKeyData skey[3]; HeapTuple tuple; bool found = false; bool is_no_inherit_constraint = false; *************** ATExecDropConstraint(Relation rel, const *** 8879,8900 **** /* * Find and drop the target constraint */ ! ScanKeyInit(&key, Anum_pg_constraint_conrelid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(rel))); ! scan = systable_beginscan(conrel, ConstraintRelidIndexId, ! true, NULL, 1, &key); ! while (HeapTupleIsValid(tuple = systable_getnext(scan))) { ObjectAddress conobj; con = (Form_pg_constraint) GETSTRUCT(tuple); - if (strcmp(NameStr(con->conname), constrName) != 0) - continue; - /* Don't drop inherited constraints */ if (con->coninhcount > 0 && !recursing) ereport(ERROR, --- 8877,8904 ---- /* * Find and drop the target constraint */ ! ScanKeyInit(&skey[0], Anum_pg_constraint_conrelid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(rel))); ! ScanKeyInit(&skey[1], ! Anum_pg_constraint_contypid, ! BTEqualStrategyNumber, F_OIDEQ, ! ObjectIdGetDatum(InvalidOid)); ! ScanKeyInit(&skey[2], ! Anum_pg_constraint_conname, ! BTEqualStrategyNumber, F_NAMEEQ, ! CStringGetDatum(constrName)); ! scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId, ! true, NULL, 3, skey); ! /* There can be at most one matching row */ ! if (HeapTupleIsValid(tuple = systable_getnext(scan))) { ObjectAddress conobj; con = (Form_pg_constraint) GETSTRUCT(tuple); /* Don't drop inherited constraints */ if (con->coninhcount > 0 && !recursing) ereport(ERROR, *************** ATExecDropConstraint(Relation rel, const *** 8932,8940 **** performDeletion(&conobj, behavior, 0); found = true; - - /* constraint found and dropped -- no need to keep looping */ - break; } systable_endscan(scan); --- 8936,8941 ---- *************** ATExecDropConstraint(Relation rel, const *** 8990,9016 **** childrel = heap_open(childrelid, NoLock); CheckTableNotInUse(childrel, "ALTER TABLE"); ! ScanKeyInit(&key, Anum_pg_constraint_conrelid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(childrelid)); ! scan = systable_beginscan(conrel, ConstraintRelidIndexId, ! true, NULL, 1, &key); ! ! /* scan for matching tuple - there should only be one */ ! while (HeapTupleIsValid(tuple = systable_getnext(scan))) ! { ! con = (Form_pg_constraint) GETSTRUCT(tuple); ! ! /* Right now only CHECK constraints can be inherited */ ! if (con->contype != CONSTRAINT_CHECK) ! continue; ! ! if (strcmp(NameStr(con->conname), constrName) == 0) ! break; ! } ! if (!HeapTupleIsValid(tuple)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("constraint \"%s\" of relation \"%s\" does not exist", --- 8991,9013 ---- childrel = heap_open(childrelid, NoLock); CheckTableNotInUse(childrel, "ALTER TABLE"); ! ScanKeyInit(&skey[0], Anum_pg_constraint_conrelid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(childrelid)); ! ScanKeyInit(&skey[1], ! Anum_pg_constraint_contypid, ! BTEqualStrategyNumber, F_OIDEQ, ! ObjectIdGetDatum(InvalidOid)); ! ScanKeyInit(&skey[2], ! Anum_pg_constraint_conname, ! BTEqualStrategyNumber, F_NAMEEQ, ! CStringGetDatum(constrName)); ! scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId, ! true, NULL, 3, skey); ! /* There can be at most one matching row */ ! if (!HeapTupleIsValid(tuple = systable_getnext(scan))) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("constraint \"%s\" of relation \"%s\" does not exist", *************** ATExecDropConstraint(Relation rel, const *** 9023,9028 **** --- 9020,9029 ---- con = (Form_pg_constraint) GETSTRUCT(copy_tuple); + /* Right now only CHECK constraints can be inherited */ + if (con->contype != CONSTRAINT_CHECK) + elog(ERROR, "inherited constraint is not a CHECK constraint"); + if (con->coninhcount <= 0) /* shouldn't happen */ elog(ERROR, "relation %u has non-inherited constraint \"%s\"", childrelid, constrName); *************** MergeConstraintsIntoExisting(Relation ch *** 11824,11830 **** Anum_pg_constraint_conrelid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(parent_rel))); ! parent_scan = systable_beginscan(catalog_relation, ConstraintRelidIndexId, true, NULL, 1, &parent_key); while (HeapTupleIsValid(parent_tuple = systable_getnext(parent_scan))) --- 11825,11831 ---- Anum_pg_constraint_conrelid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(parent_rel))); ! parent_scan = systable_beginscan(catalog_relation, ConstraintRelidTypidNameIndexId, true, NULL, 1, &parent_key); while (HeapTupleIsValid(parent_tuple = systable_getnext(parent_scan))) *************** MergeConstraintsIntoExisting(Relation ch *** 11847,11853 **** Anum_pg_constraint_conrelid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(child_rel))); ! child_scan = systable_beginscan(catalog_relation, ConstraintRelidIndexId, true, NULL, 1, &child_key); while (HeapTupleIsValid(child_tuple = systable_getnext(child_scan))) --- 11848,11854 ---- Anum_pg_constraint_conrelid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(child_rel))); ! child_scan = systable_beginscan(catalog_relation, ConstraintRelidTypidNameIndexId, true, NULL, 1, &child_key); while (HeapTupleIsValid(child_tuple = systable_getnext(child_scan))) *************** RemoveInheritance(Relation child_rel, Re *** 12068,12074 **** Anum_pg_constraint_conrelid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(parent_rel))); ! scan = systable_beginscan(catalogRelation, ConstraintRelidIndexId, true, NULL, 1, key); connames = NIL; --- 12069,12075 ---- Anum_pg_constraint_conrelid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(parent_rel))); ! scan = systable_beginscan(catalogRelation, ConstraintRelidTypidNameIndexId, true, NULL, 1, key); connames = NIL; *************** RemoveInheritance(Relation child_rel, Re *** 12088,12094 **** Anum_pg_constraint_conrelid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(child_rel))); ! scan = systable_beginscan(catalogRelation, ConstraintRelidIndexId, true, NULL, 1, key); while (HeapTupleIsValid(constraintTuple = systable_getnext(scan))) --- 12089,12095 ---- Anum_pg_constraint_conrelid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(child_rel))); ! scan = systable_beginscan(catalogRelation, ConstraintRelidTypidNameIndexId, true, NULL, 1, key); while (HeapTupleIsValid(constraintTuple = systable_getnext(scan))) *************** ATPrepChangePersistence(Relation rel, bo *** 12829,12835 **** BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(rel))); scan = systable_beginscan(pg_constraint, ! toLogged ? ConstraintRelidIndexId : InvalidOid, true, NULL, 1, skey); while (HeapTupleIsValid(tuple = systable_getnext(scan))) --- 12830,12836 ---- BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(rel))); scan = systable_beginscan(pg_constraint, ! toLogged ? ConstraintRelidTypidNameIndexId : InvalidOid, true, NULL, 1, skey); while (HeapTupleIsValid(tuple = systable_getnext(scan))) diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c index 175ecc8..a67f9ce 100644 *** a/src/backend/commands/typecmds.c --- b/src/backend/commands/typecmds.c *************** AlterDomainNotNull(List *names, bool not *** 2444,2449 **** --- 2444,2451 ---- * AlterDomainDropConstraint * * Implements the ALTER DOMAIN DROP CONSTRAINT statement + * + * Returns ObjectAddress of the modified domain. */ ObjectAddress AlterDomainDropConstraint(List *names, const char *constrName, *************** AlterDomainDropConstraint(List *names, c *** 2455,2464 **** Relation rel; Relation conrel; SysScanDesc conscan; ! ScanKeyData key[1]; HeapTuple contup; bool found = false; ! ObjectAddress address = InvalidObjectAddress; /* Make a TypeName so we can use standard type lookup machinery */ typename = makeTypeNameFromNameList(names); --- 2457,2466 ---- Relation rel; Relation conrel; SysScanDesc conscan; ! ScanKeyData skey[3]; HeapTuple contup; bool found = false; ! ObjectAddress address; /* Make a TypeName so we can use standard type lookup machinery */ typename = makeTypeNameFromNameList(names); *************** AlterDomainDropConstraint(List *names, c *** 2477,2513 **** /* Grab an appropriate lock on the pg_constraint relation */ conrel = heap_open(ConstraintRelationId, RowExclusiveLock); ! /* Use the index to scan only constraints of the target relation */ ! ScanKeyInit(&key[0], Anum_pg_constraint_contypid, BTEqualStrategyNumber, F_OIDEQ, ! ObjectIdGetDatum(HeapTupleGetOid(tup))); ! conscan = systable_beginscan(conrel, ConstraintTypidIndexId, true, ! NULL, 1, key); ! /* ! * Scan over the result set, removing any matching entries. ! */ ! while ((contup = systable_getnext(conscan)) != NULL) { ! Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(contup); ! ! if (strcmp(NameStr(con->conname), constrName) == 0) ! { ! ObjectAddress conobj; ! conobj.classId = ConstraintRelationId; ! conobj.objectId = HeapTupleGetOid(contup); ! conobj.objectSubId = 0; ! performDeletion(&conobj, behavior, 0); ! found = true; ! } } - ObjectAddressSet(address, TypeRelationId, domainoid); - /* Clean up after the scan */ systable_endscan(conscan); heap_close(conrel, RowExclusiveLock); --- 2479,2514 ---- /* Grab an appropriate lock on the pg_constraint relation */ conrel = heap_open(ConstraintRelationId, RowExclusiveLock); ! /* Find and remove the target constraint */ ! ScanKeyInit(&skey[0], ! Anum_pg_constraint_conrelid, ! BTEqualStrategyNumber, F_OIDEQ, ! ObjectIdGetDatum(InvalidOid)); ! ScanKeyInit(&skey[1], Anum_pg_constraint_contypid, BTEqualStrategyNumber, F_OIDEQ, ! ObjectIdGetDatum(domainoid)); ! ScanKeyInit(&skey[2], ! Anum_pg_constraint_conname, ! BTEqualStrategyNumber, F_NAMEEQ, ! CStringGetDatum(constrName)); ! conscan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId, true, ! NULL, 3, skey); ! /* There can be at most one matching row */ ! if ((contup = systable_getnext(conscan)) != NULL) { ! ObjectAddress conobj; ! conobj.classId = ConstraintRelationId; ! conobj.objectId = HeapTupleGetOid(contup); ! conobj.objectSubId = 0; ! performDeletion(&conobj, behavior, 0); ! found = true; } /* Clean up after the scan */ systable_endscan(conscan); heap_close(conrel, RowExclusiveLock); *************** AlterDomainDropConstraint(List *names, c *** 2527,2532 **** --- 2528,2535 ---- constrName, TypeNameToString(typename)))); } + ObjectAddressSet(address, TypeRelationId, domainoid); + return address; } *************** AlterDomainValidateConstraint(List *name *** 2652,2667 **** Relation typrel; Relation conrel; HeapTuple tup; ! Form_pg_constraint con = NULL; Form_pg_constraint copy_con; char *conbin; SysScanDesc scan; Datum val; - bool found = false; bool isnull; HeapTuple tuple; HeapTuple copyTuple; ! ScanKeyData key; ObjectAddress address; /* Make a TypeName so we can use standard type lookup machinery */ --- 2655,2669 ---- Relation typrel; Relation conrel; HeapTuple tup; ! Form_pg_constraint con; Form_pg_constraint copy_con; char *conbin; SysScanDesc scan; Datum val; bool isnull; HeapTuple tuple; HeapTuple copyTuple; ! ScanKeyData skey[3]; ObjectAddress address; /* Make a TypeName so we can use standard type lookup machinery */ *************** AlterDomainValidateConstraint(List *name *** 2682,2710 **** * Find and check the target constraint */ conrel = heap_open(ConstraintRelationId, RowExclusiveLock); ! ScanKeyInit(&key, Anum_pg_constraint_contypid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(domainoid)); ! scan = systable_beginscan(conrel, ConstraintTypidIndexId, ! true, NULL, 1, &key); ! while (HeapTupleIsValid(tuple = systable_getnext(scan))) ! { ! con = (Form_pg_constraint) GETSTRUCT(tuple); ! if (strcmp(NameStr(con->conname), constrName) == 0) ! { ! found = true; ! break; ! } ! } ! if (!found) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("constraint \"%s\" of domain \"%s\" does not exist", constrName, TypeNameToString(typename)))); if (con->contype != CONSTRAINT_CHECK) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), --- 2684,2714 ---- * Find and check the target constraint */ conrel = heap_open(ConstraintRelationId, RowExclusiveLock); ! ! ScanKeyInit(&skey[0], ! Anum_pg_constraint_conrelid, ! BTEqualStrategyNumber, F_OIDEQ, ! ObjectIdGetDatum(InvalidOid)); ! ScanKeyInit(&skey[1], Anum_pg_constraint_contypid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(domainoid)); ! ScanKeyInit(&skey[2], ! Anum_pg_constraint_conname, ! BTEqualStrategyNumber, F_NAMEEQ, ! CStringGetDatum(constrName)); ! scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId, true, ! NULL, 3, skey); ! /* There can be at most one matching row */ ! if (!HeapTupleIsValid(tuple = systable_getnext(scan))) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("constraint \"%s\" of domain \"%s\" does not exist", constrName, TypeNameToString(typename)))); + con = (Form_pg_constraint) GETSTRUCT(tuple); if (con->contype != CONSTRAINT_CHECK) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c index 6125421..a4fc001 100644 *** a/src/backend/utils/cache/relcache.c --- b/src/backend/utils/cache/relcache.c *************** CheckConstraintFetch(Relation relation) *** 4016,4022 **** ObjectIdGetDatum(RelationGetRelid(relation))); conrel = heap_open(ConstraintRelationId, AccessShareLock); ! conscan = systable_beginscan(conrel, ConstraintRelidIndexId, true, NULL, 1, skey); while (HeapTupleIsValid(htup = systable_getnext(conscan))) --- 4016,4022 ---- ObjectIdGetDatum(RelationGetRelid(relation))); conrel = heap_open(ConstraintRelationId, AccessShareLock); ! conscan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId, true, NULL, 1, skey); while (HeapTupleIsValid(htup = systable_getnext(conscan))) *************** RelationGetFKeyList(Relation relation) *** 4127,4133 **** ObjectIdGetDatum(RelationGetRelid(relation))); conrel = heap_open(ConstraintRelationId, AccessShareLock); ! conscan = systable_beginscan(conrel, ConstraintRelidIndexId, true, NULL, 1, &skey); while (HeapTupleIsValid(htup = systable_getnext(conscan))) --- 4127,4133 ---- ObjectIdGetDatum(RelationGetRelid(relation))); conrel = heap_open(ConstraintRelationId, AccessShareLock); ! conscan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId, true, NULL, 1, &skey); while (HeapTupleIsValid(htup = systable_getnext(conscan))) *************** RelationGetExclusionInfo(Relation indexR *** 5105,5110 **** --- 5105,5114 ---- * Search pg_constraint for the constraint associated with the index. To * make this not too painfully slow, we use the index on conrelid; that * will hold the parent relation's OID not the index's own OID. + * + * Note: if we wanted to rely on the constraint name matching the index's + * name, we could just do a direct lookup using pg_constraint's unique + * index. For the moment it doesn't seem worth requiring that. */ ScanKeyInit(&skey[0], Anum_pg_constraint_conrelid, *************** RelationGetExclusionInfo(Relation indexR *** 5112,5118 **** ObjectIdGetDatum(indexRelation->rd_index->indrelid)); conrel = heap_open(ConstraintRelationId, AccessShareLock); ! conscan = systable_beginscan(conrel, ConstraintRelidIndexId, true, NULL, 1, skey); found = false; --- 5116,5122 ---- ObjectIdGetDatum(indexRelation->rd_index->indrelid)); conrel = heap_open(ConstraintRelationId, AccessShareLock); ! conscan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId, true, NULL, 1, skey); found = false; diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h index 2491582..254fbef 100644 *** a/src/include/catalog/indexing.h --- b/src/include/catalog/indexing.h *************** DECLARE_UNIQUE_INDEX(pg_collation_oid_in *** 121,128 **** DECLARE_INDEX(pg_constraint_conname_nsp_index, 2664, on pg_constraint using btree(conname name_ops, connamespace oid_ops)); #define ConstraintNameNspIndexId 2664 ! DECLARE_INDEX(pg_constraint_conrelid_index, 2665, on pg_constraint using btree(conrelid oid_ops)); ! #define ConstraintRelidIndexId 2665 DECLARE_INDEX(pg_constraint_contypid_index, 2666, on pg_constraint using btree(contypid oid_ops)); #define ConstraintTypidIndexId 2666 DECLARE_UNIQUE_INDEX(pg_constraint_oid_index, 2667, on pg_constraint using btree(oid oid_ops)); --- 121,128 ---- DECLARE_INDEX(pg_constraint_conname_nsp_index, 2664, on pg_constraint using btree(conname name_ops, connamespace oid_ops)); #define ConstraintNameNspIndexId 2664 ! DECLARE_UNIQUE_INDEX(pg_constraint_conrelid_contypid_conname_index, 2665, on pg_constraint using btree(conrelid oid_ops,contypid oid_ops, conname name_ops)); ! #define ConstraintRelidTypidNameIndexId 2665 DECLARE_INDEX(pg_constraint_contypid_index, 2666, on pg_constraint using btree(contypid oid_ops)); #define ConstraintTypidIndexId 2666 DECLARE_UNIQUE_INDEX(pg_constraint_oid_index, 2667, on pg_constraint using btree(oid oid_ops)); diff --git a/src/include/catalog/pg_constraint.h b/src/include/catalog/pg_constraint.h index 7c1c0e1..4435310 100644 *** a/src/include/catalog/pg_constraint.h --- b/src/include/catalog/pg_constraint.h *************** CATALOG(pg_constraint,2606,ConstraintRel *** 39,44 **** --- 39,48 ---- * global lock to generate a globally unique name for a nameless * constraint. We associate a namespace with constraint names only for * SQL-spec compatibility. + * + * However, we do require conname to be unique among the constraints of a + * single relation or domain. This is enforced by a unique index on + * conrelid + contypid + conname. */ NameData conname; /* name of this constraint */ Oid connamespace; /* OID of namespace containing constraint */ diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index b256054..70a1767 100644 *** a/src/backend/catalog/index.c --- b/src/backend/catalog/index.c *************** index_create(Relation heapRelation, *** 842,847 **** --- 842,853 ---- if (shared_relation && tableSpaceId != GLOBALTABLESPACE_OID) elog(ERROR, "shared relations must be placed in pg_global tablespace"); + /* + * Check for duplicate name (both as to the index, and as to the + * associated constraint if any). Such cases would fail on the relevant + * catalogs' unique indexes anyway, but we prefer to give a friendlier + * error message. + */ if (get_relname_relid(indexRelationName, namespaceId)) { if ((flags & INDEX_CREATE_IF_NOT_EXISTS) != 0) *************** index_create(Relation heapRelation, *** 860,865 **** --- 866,885 ---- indexRelationName))); } + if ((flags & INDEX_CREATE_ADD_CONSTRAINT) != 0 && + ConstraintNameIsUsed(CONSTRAINT_RELATION, heapRelationId, + namespaceId, indexRelationName)) + { + /* + * INDEX_CREATE_IF_NOT_EXISTS does not apply here, since the + * conflicting constraint is not an index. + */ + ereport(ERROR, + (errcode(ERRCODE_DUPLICATE_OBJECT), + errmsg("constraint \"%s\" for relation \"%s\" already exists", + indexRelationName, RelationGetRelationName(heapRelation)))); + } + /* * construct tuple descriptor for index tuples */ diff --git a/src/backend/catalog/pg_constraint.c b/src/backend/catalog/pg_constraint.c index 3d65464..51f16ce 100644 *** a/src/backend/catalog/pg_constraint.c --- b/src/backend/catalog/pg_constraint.c *************** ConstraintNameIsUsed(ConstraintCategory *** 701,706 **** --- 701,744 ---- } /* + * Does any constraint of the given name exist in the given namespace? + * + * This is used for code that wants to match ChooseConstraintName's rule + * that we should avoid autogenerating duplicate constraint names within a + * namespace. + */ + bool + ConstraintNameExists(const char *conname, Oid namespaceid) + { + bool found; + Relation conDesc; + SysScanDesc conscan; + ScanKeyData skey[2]; + + conDesc = heap_open(ConstraintRelationId, AccessShareLock); + + ScanKeyInit(&skey[0], + Anum_pg_constraint_conname, + BTEqualStrategyNumber, F_NAMEEQ, + CStringGetDatum(conname)); + + ScanKeyInit(&skey[1], + Anum_pg_constraint_connamespace, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(namespaceid)); + + conscan = systable_beginscan(conDesc, ConstraintNameNspIndexId, true, + NULL, 2, skey); + + found = (HeapTupleIsValid(systable_getnext(conscan))); + + systable_endscan(conscan); + heap_close(conDesc, AccessShareLock); + + return found; + } + + /* * Select a nonconflicting name for a new constraint. * * The objective here is to choose a name that is unique within the *************** RenameConstraintById(Oid conId, const ch *** 899,906 **** con = (Form_pg_constraint) GETSTRUCT(tuple); /* ! * We need to check whether the name is already in use --- note that there ! * currently is not a unique index that would catch this. */ if (OidIsValid(con->conrelid) && ConstraintNameIsUsed(CONSTRAINT_RELATION, --- 937,943 ---- con = (Form_pg_constraint) GETSTRUCT(tuple); /* ! * For user-friendliness, check whether the name is already in use. */ if (OidIsValid(con->conrelid) && ConstraintNameIsUsed(CONSTRAINT_RELATION, diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index d54c78c..ab3d9a0 100644 *** a/src/backend/commands/indexcmds.c --- b/src/backend/commands/indexcmds.c *************** makeObjectName(const char *name1, const *** 1995,2000 **** --- 1995,2006 ---- * 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 isconstraint is true, we also avoid choosing a name matching any + * existing constraint in the same namespace. (This is stricter than what + * Postgres itself requires, but the SQL standard says that constraint names + * should be unique within schemas, so we follow that for autogenerated + * constraint names.) + * * Note: it is theoretically possible to get a collision anyway, if someone * else chooses the same name concurrently. This is fairly unlikely to be * a problem in practice, especially if one is holding an exclusive lock on *************** makeObjectName(const char *name1, const *** 2006,2012 **** */ char * ChooseRelationName(const char *name1, const char *name2, ! const char *label, Oid namespaceid) { int pass = 0; char *relname = NULL; --- 2012,2019 ---- */ char * ChooseRelationName(const char *name1, const char *name2, ! const char *label, Oid namespaceid, ! bool isconstraint) { int pass = 0; char *relname = NULL; *************** ChooseRelationName(const char *name1, co *** 2020,2026 **** relname = makeObjectName(name1, name2, modlabel); if (!OidIsValid(get_relname_relid(relname, namespaceid))) ! break; /* found a conflict, so try a new name component */ pfree(relname); --- 2027,2037 ---- relname = makeObjectName(name1, name2, modlabel); if (!OidIsValid(get_relname_relid(relname, namespaceid))) ! { ! if (!isconstraint || ! !ConstraintNameExists(relname, namespaceid)) ! break; ! } /* found a conflict, so try a new name component */ pfree(relname); *************** ChooseIndexName(const char *tabname, Oid *** 2048,2075 **** indexname = ChooseRelationName(tabname, NULL, "pkey", ! namespaceId); } else if (exclusionOpNames != NIL) { indexname = ChooseRelationName(tabname, ChooseIndexNameAddition(colnames), "excl", ! namespaceId); } else if (isconstraint) { indexname = ChooseRelationName(tabname, ChooseIndexNameAddition(colnames), "key", ! namespaceId); } else { indexname = ChooseRelationName(tabname, ChooseIndexNameAddition(colnames), "idx", ! namespaceId); } return indexname; --- 2059,2090 ---- indexname = ChooseRelationName(tabname, NULL, "pkey", ! namespaceId, ! true); } else if (exclusionOpNames != NIL) { indexname = ChooseRelationName(tabname, ChooseIndexNameAddition(colnames), "excl", ! namespaceId, ! true); } else if (isconstraint) { indexname = ChooseRelationName(tabname, ChooseIndexNameAddition(colnames), "key", ! namespaceId, ! true); } else { indexname = ChooseRelationName(tabname, ChooseIndexNameAddition(colnames), "idx", ! namespaceId, ! false); } return indexname; diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index 656b1b5..f5c1e2a 100644 *** a/src/backend/parser/parse_utilcmd.c --- b/src/backend/parser/parse_utilcmd.c *************** generateSerialExtraStmts(CreateStmtConte *** 440,446 **** sname = ChooseRelationName(cxt->relation->relname, column->colname, "seq", ! snamespaceid); } ereport(DEBUG1, --- 440,447 ---- sname = ChooseRelationName(cxt->relation->relname, column->colname, "seq", ! snamespaceid, ! false); } ereport(DEBUG1, diff --git a/src/include/catalog/pg_constraint.h b/src/include/catalog/pg_constraint.h index 4435310..49df169 100644 *** a/src/include/catalog/pg_constraint.h --- b/src/include/catalog/pg_constraint.h *************** extern void RenameConstraintById(Oid con *** 244,249 **** --- 244,250 ---- extern bool ConstraintNameIsUsed(ConstraintCategory conCat, Oid objId, Oid objNamespace, const char *conname); + extern bool ConstraintNameExists(const char *conname, Oid namespaceid); extern char *ChooseConstraintName(const char *name1, const char *name2, const char *label, Oid namespaceid, List *others); diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h index 6b83723..1d05a4b 100644 *** a/src/include/commands/defrem.h --- b/src/include/commands/defrem.h *************** extern void ReindexMultipleTables(const *** 41,47 **** extern char *makeObjectName(const char *name1, const char *name2, const char *label); extern char *ChooseRelationName(const char *name1, const char *name2, ! const char *label, Oid namespaceid); extern bool CheckIndexCompatible(Oid oldId, const char *accessMethodName, List *attributeList, --- 41,48 ---- extern char *makeObjectName(const char *name1, const char *name2, const char *label); extern char *ChooseRelationName(const char *name1, const char *name2, ! const char *label, Oid namespaceid, ! bool isconstraint); extern bool CheckIndexCompatible(Oid oldId, const char *accessMethodName, List *attributeList, diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out index 0218c2c..dccc9b2 100644 *** a/src/test/regress/expected/alter_table.out --- b/src/test/regress/expected/alter_table.out *************** Check constraints: *** 2994,2999 **** --- 2994,3034 ---- DROP TABLE alter2.tt8; DROP SCHEMA alter2; + -- + -- Check conflicts between index and CHECK constraint names + -- + CREATE TABLE tt9(c integer); + ALTER TABLE tt9 ADD CHECK(c > 1); + ALTER TABLE tt9 ADD CHECK(c > 2); -- picks nonconflicting name + ALTER TABLE tt9 ADD CONSTRAINT foo CHECK(c > 3); + ALTER TABLE tt9 ADD CONSTRAINT foo CHECK(c > 4); -- fail, dup name + ERROR: constraint "foo" for relation "tt9" already exists + ALTER TABLE tt9 ADD UNIQUE(c); + ALTER TABLE tt9 ADD UNIQUE(c); -- picks nonconflicting name + ALTER TABLE tt9 ADD CONSTRAINT tt9_c_key UNIQUE(c); -- fail, dup name + ERROR: relation "tt9_c_key" already exists + ALTER TABLE tt9 ADD CONSTRAINT foo UNIQUE(c); -- fail, dup name + ERROR: constraint "foo" for relation "tt9" already exists + ALTER TABLE tt9 ADD CONSTRAINT tt9_c_key CHECK(c > 5); -- fail, dup name + ERROR: constraint "tt9_c_key" for relation "tt9" already exists + ALTER TABLE tt9 ADD CONSTRAINT tt9_c_key2 CHECK(c > 6); + ALTER TABLE tt9 ADD UNIQUE(c); -- picks nonconflicting name + \d tt9 + Table "public.tt9" + Column | Type | Collation | Nullable | Default + --------+---------+-----------+----------+--------- + c | integer | | | + Indexes: + "tt9_c_key" UNIQUE CONSTRAINT, btree (c) + "tt9_c_key1" UNIQUE CONSTRAINT, btree (c) + "tt9_c_key3" UNIQUE CONSTRAINT, btree (c) + Check constraints: + "foo" CHECK (c > 3) + "tt9_c_check" CHECK (c > 1) + "tt9_c_check1" CHECK (c > 2) + "tt9_c_key2" CHECK (c > 6) + + DROP TABLE tt9; -- Check that comments on constraints and indexes are not lost at ALTER TABLE. CREATE TABLE comment_test ( id int, diff --git a/src/test/regress/sql/alter_table.sql b/src/test/regress/sql/alter_table.sql index 22cf4ef..b904978 100644 *** a/src/test/regress/sql/alter_table.sql --- b/src/test/regress/sql/alter_table.sql *************** ALTER TABLE IF EXISTS tt8 SET SCHEMA alt *** 1865,1870 **** --- 1865,1888 ---- DROP TABLE alter2.tt8; DROP SCHEMA alter2; + -- + -- Check conflicts between index and CHECK constraint names + -- + CREATE TABLE tt9(c integer); + ALTER TABLE tt9 ADD CHECK(c > 1); + ALTER TABLE tt9 ADD CHECK(c > 2); -- picks nonconflicting name + ALTER TABLE tt9 ADD CONSTRAINT foo CHECK(c > 3); + ALTER TABLE tt9 ADD CONSTRAINT foo CHECK(c > 4); -- fail, dup name + ALTER TABLE tt9 ADD UNIQUE(c); + ALTER TABLE tt9 ADD UNIQUE(c); -- picks nonconflicting name + ALTER TABLE tt9 ADD CONSTRAINT tt9_c_key UNIQUE(c); -- fail, dup name + ALTER TABLE tt9 ADD CONSTRAINT foo UNIQUE(c); -- fail, dup name + ALTER TABLE tt9 ADD CONSTRAINT tt9_c_key CHECK(c > 5); -- fail, dup name + ALTER TABLE tt9 ADD CONSTRAINT tt9_c_key2 CHECK(c > 6); + ALTER TABLE tt9 ADD UNIQUE(c); -- picks nonconflicting name + \d tt9 + DROP TABLE tt9; + -- Check that comments on constraints and indexes are not lost at ALTER TABLE. CREATE TABLE comment_test (
pgsql-bugs by date: