diff options
Diffstat (limited to 'src/backend/catalog')
| -rw-r--r-- | src/backend/catalog/heap.c | 147 | ||||
| -rw-r--r-- | src/backend/catalog/pg_constraint.c | 153 |
2 files changed, 149 insertions, 151 deletions
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c index 718c3b9091..a4cc33d4c9 100644 --- a/src/backend/catalog/heap.c +++ b/src/backend/catalog/heap.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/catalog/heap.c,v 1.269 2004/06/06 20:30:07 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/catalog/heap.c,v 1.270 2004/06/10 17:55:53 tgl Exp $ * * * INTERFACE ROUTINES @@ -1488,7 +1488,7 @@ AddRelationRawConstraints(Relation rel, ParseState *pstate; RangeTblEntry *rte; int numchecks; - int constr_name_ctr = 0; + List *checknames; ListCell *cell; Node *expr; CookedConstraint *cooked; @@ -1547,6 +1547,7 @@ AddRelationRawConstraints(Relation rel, * Process constraint expressions. */ numchecks = numoldchecks; + checknames = NIL; foreach(cell, rawConstraints) { Constraint *cdef = (Constraint *) lfirst(cell); @@ -1556,81 +1557,6 @@ AddRelationRawConstraints(Relation rel, continue; Assert(cdef->cooked_expr == NULL); - /* Check name uniqueness, or generate a new name */ - if (cdef->name != NULL) - { - ListCell *cell2; - - ccname = cdef->name; - /* Check against pre-existing constraints */ - if (ConstraintNameIsUsed(CONSTRAINT_RELATION, - RelationGetRelid(rel), - RelationGetNamespace(rel), - ccname)) - ereport(ERROR, - (errcode(ERRCODE_DUPLICATE_OBJECT), - errmsg("constraint \"%s\" for relation \"%s\" already exists", - ccname, RelationGetRelationName(rel)))); - /* Check against other new constraints */ - /* Needed because we don't do CommandCounterIncrement in loop */ - foreach(cell2, rawConstraints) - { - Constraint *cdef2 = (Constraint *) lfirst(cell2); - - if (cdef2 == cdef || - cdef2->contype != CONSTR_CHECK || - cdef2->raw_expr == NULL || - cdef2->name == NULL) - continue; - if (strcmp(cdef2->name, ccname) == 0) - ereport(ERROR, - (errcode(ERRCODE_DUPLICATE_OBJECT), - errmsg("check constraint \"%s\" already exists", - ccname))); - } - } - else - { - bool success; - - do - { - ListCell *cell2; - - /* - * Generate a name that does not conflict with - * pre-existing constraints, nor with any auto-generated - * names so far. - */ - ccname = GenerateConstraintName(CONSTRAINT_RELATION, - RelationGetRelid(rel), - RelationGetNamespace(rel), - &constr_name_ctr); - - /* - * Check against other new constraints, in case the user - * has specified a name that looks like an auto-generated - * name. - */ - success = true; - foreach(cell2, rawConstraints) - { - Constraint *cdef2 = (Constraint *) lfirst(cell2); - - if (cdef2 == cdef || - cdef2->contype != CONSTR_CHECK || - cdef2->raw_expr == NULL || - cdef2->name == NULL) - continue; - if (strcmp(cdef2->name, ccname) == 0) - { - success = false; - break; - } - } - } while (!success); - } - /* * Transform raw parsetree to executable expression. */ @@ -1663,6 +1589,73 @@ AddRelationRawConstraints(Relation rel, errmsg("cannot use aggregate function in check constraint"))); /* + * Check name uniqueness, or generate a name if none was given. + */ + if (cdef->name != NULL) + { + ListCell *cell2; + + ccname = cdef->name; + /* Check against pre-existing constraints */ + if (ConstraintNameIsUsed(CONSTRAINT_RELATION, + RelationGetRelid(rel), + RelationGetNamespace(rel), + ccname)) + ereport(ERROR, + (errcode(ERRCODE_DUPLICATE_OBJECT), + errmsg("constraint \"%s\" for relation \"%s\" already exists", + ccname, RelationGetRelationName(rel)))); + /* Check against other new constraints */ + /* Needed because we don't do CommandCounterIncrement in loop */ + foreach(cell2, checknames) + { + if (strcmp((char *) lfirst(cell2), ccname) == 0) + ereport(ERROR, + (errcode(ERRCODE_DUPLICATE_OBJECT), + errmsg("check constraint \"%s\" already exists", + ccname))); + } + } + else + { + /* + * When generating a name, we want to create "tab_col_check" + * for a column constraint and "tab_check" for a table + * constraint. We no longer have any info about the + * syntactic positioning of the constraint phrase, so we + * approximate this by seeing whether the expression references + * more than one column. (If the user played by the rules, + * the result is the same...) + * + * Note: pull_var_clause() doesn't descend into sublinks, + * but we eliminated those above; and anyway this only needs + * to be an approximate answer. + */ + List *vars; + char *colname; + + vars = pull_var_clause(expr, false); + + /* eliminate duplicates */ + vars = list_union(NIL, vars); + + if (list_length(vars) == 1) + colname = get_attname(RelationGetRelid(rel), + ((Var *) linitial(vars))->varattno); + else + colname = NULL; + + ccname = ChooseConstraintName(RelationGetRelationName(rel), + colname, + "check", + RelationGetNamespace(rel), + checknames); + } + + /* save name for future checks */ + checknames = lappend(checknames, ccname); + + /* * OK, store it. */ StoreRelCheck(rel, ccname, nodeToString(expr)); diff --git a/src/backend/catalog/pg_constraint.c b/src/backend/catalog/pg_constraint.c index 09f3acd5f3..4bd9409b8d 100644 --- a/src/backend/catalog/pg_constraint.c +++ b/src/backend/catalog/pg_constraint.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/catalog/pg_constraint.c,v 1.19 2003/11/29 19:51:46 pgsql Exp $ + * $PostgreSQL: pgsql/src/backend/catalog/pg_constraint.c,v 1.20 2004/06/10 17:55:53 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -22,6 +22,7 @@ #include "catalog/indexing.h" #include "catalog/pg_constraint.h" #include "catalog/pg_type.h" +#include "commands/defrem.h" #include "miscadmin.h" #include "utils/array.h" #include "utils/builtins.h" @@ -263,13 +264,19 @@ CreateConstraintEntry(const char *constraintName, /* * Test whether given name is currently used as a constraint name - * for the given relation. + * for the given object (relation or domain). * - * NB: Caller should hold exclusive lock on the given relation, else - * this test is not very meaningful. + * This is used to decide whether to accept a user-specified constraint name. + * It is deliberately not the same test as ChooseConstraintName uses to decide + * whether an auto-generated name is OK: here, we will allow it unless there + * is an identical constraint name in use *on the same object*. + * + * NB: Caller should hold exclusive lock on the given object, else + * this test can be fooled by concurrent additions. */ bool -ConstraintNameIsUsed(CONSTRAINTCATEGORY conCat, Oid objId, Oid objNamespace, const char *cname) +ConstraintNameIsUsed(ConstraintCategory conCat, Oid objId, + Oid objNamespace, const char *conname) { bool found; Relation conDesc; @@ -277,14 +284,14 @@ ConstraintNameIsUsed(CONSTRAINTCATEGORY conCat, Oid objId, Oid objNamespace, con ScanKeyData skey[2]; HeapTuple tup; - conDesc = heap_openr(ConstraintRelationName, RowExclusiveLock); + conDesc = heap_openr(ConstraintRelationName, AccessShareLock); found = false; ScanKeyInit(&skey[0], Anum_pg_constraint_conname, BTEqualStrategyNumber, F_NAMEEQ, - CStringGetDatum(cname)); + CStringGetDatum(conname)); ScanKeyInit(&skey[1], Anum_pg_constraint_connamespace, @@ -311,101 +318,99 @@ ConstraintNameIsUsed(CONSTRAINTCATEGORY conCat, Oid objId, Oid objNamespace, con } systable_endscan(conscan); - heap_close(conDesc, RowExclusiveLock); + heap_close(conDesc, AccessShareLock); return found; } /* - * Generate a currently-unused constraint name for the given relation. + * Select a nonconflicting name for a new constraint. + * + * The objective here is to choose a name that is unique within the + * specified namespace. Postgres does not require this, but the SQL + * spec does, and some apps depend on it. Therefore we avoid choosing + * default names that so conflict. + * + * name1, name2, and label are used the same way as for makeObjectName(), + * 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. * - * The passed counter should be initialized to 0 the first time through. - * If multiple constraint names are to be generated in a single command, - * pass the new counter value to each successive call, else the same - * name will be generated each time. + * 'others' can be a list of string names already chosen within the current + * command (but not yet reflected into the catalogs); we will not choose + * a duplicate of one of these either. * - * NB: Caller should hold exclusive lock on the given relation, else - * someone else might choose the same name concurrently! + * 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 + * the relation identified by name1. + * + * Returns a palloc'd string. */ char * -GenerateConstraintName(CONSTRAINTCATEGORY conCat, Oid objId, Oid objNamespace, int *counter) +ChooseConstraintName(const char *name1, const char *name2, + const char *label, Oid namespace, + List *others) { - bool found; + int pass = 0; + char *conname = NULL; + char modlabel[NAMEDATALEN]; Relation conDesc; - char *cname; + SysScanDesc conscan; + ScanKeyData skey[2]; + bool found; + ListCell *l; - cname = (char *) palloc(NAMEDATALEN * sizeof(char)); + conDesc = heap_openr(ConstraintRelationName, AccessShareLock); - conDesc = heap_openr(ConstraintRelationName, RowExclusiveLock); + /* try the unmodified label first */ + StrNCpy(modlabel, label, sizeof(modlabel)); - /* Loop until we find a non-conflicting constraint name */ - /* We assume there will be one eventually ... */ - do + for (;;) { - SysScanDesc conscan; - ScanKeyData skey[2]; - HeapTuple tup; - - ++(*counter); - snprintf(cname, NAMEDATALEN, "$%d", *counter); + conname = makeObjectName(name1, name2, modlabel); - /* - * This duplicates ConstraintNameIsUsed() so that we can avoid - * re-opening pg_constraint for each iteration. - */ found = false; - ScanKeyInit(&skey[0], - Anum_pg_constraint_conname, - BTEqualStrategyNumber, F_NAMEEQ, - CStringGetDatum(cname)); - - ScanKeyInit(&skey[1], - Anum_pg_constraint_connamespace, - BTEqualStrategyNumber, F_OIDEQ, - ObjectIdGetDatum(objNamespace)); - - conscan = systable_beginscan(conDesc, ConstraintNameNspIndex, true, - SnapshotNow, 2, skey); - - while (HeapTupleIsValid(tup = systable_getnext(conscan))) + foreach(l, others) { - Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tup); - - if (conCat == CONSTRAINT_RELATION && con->conrelid == objId) - { - found = true; - break; - } - else if (conCat == CONSTRAINT_DOMAIN && con->contypid == objId) + if (strcmp((char *) lfirst(l), conname) == 0) { found = true; break; } } - systable_endscan(conscan); - } while (found); + if (!found) + { + ScanKeyInit(&skey[0], + Anum_pg_constraint_conname, + BTEqualStrategyNumber, F_NAMEEQ, + CStringGetDatum(conname)); - heap_close(conDesc, RowExclusiveLock); + ScanKeyInit(&skey[1], + Anum_pg_constraint_connamespace, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(namespace)); - return cname; -} + conscan = systable_beginscan(conDesc, ConstraintNameNspIndex, true, + SnapshotNow, 2, skey); -/* - * Does the given name look like a generated constraint name? - * - * This is a test on the form of the name, *not* on whether it has - * actually been assigned. - */ -bool -ConstraintNameIsGenerated(const char *cname) -{ - if (cname[0] != '$') - return false; - if (strspn(cname + 1, "0123456789") != strlen(cname + 1)) - return false; - return true; + found = (HeapTupleIsValid(systable_getnext(conscan))); + + systable_endscan(conscan); + } + + if (!found) + break; + + /* found a conflict, so try a new name component */ + pfree(conname); + snprintf(modlabel, sizeof(modlabel), "%s%d", label, ++pass); + } + + heap_close(conDesc, AccessShareLock); + + return conname; } /* |
