diff options
author | Tom Lane <tgl@sss.pgh.pa.us> | 2004-06-10 17:56:03 +0000 |
---|---|---|
committer | Tom Lane <tgl@sss.pgh.pa.us> | 2004-06-10 17:56:03 +0000 |
commit | 45616f5bbbb87745e0e82b00e77562d6502aa042 (patch) | |
tree | 18d24d180f5c0c954268e64f5b6fe62922fc106e /src/backend | |
parent | 75db5a665fac305ac0170f49f39c1b01d026e8d3 (diff) | |
download | postgresql-45616f5bbbb87745e0e82b00e77562d6502aa042.tar.gz postgresql-45616f5bbbb87745e0e82b00e77562d6502aa042.zip |
Clean up generation of default names for constraints, indexes, and serial
sequences, as per recent discussion. All these names are now of the
form table_column_type, with digits added if needed to make them unique.
Default constraint names are chosen to be unique across their whole schema,
not just within the parent object, so as to be more SQL-spec-compatible
and make the information schema views more useful.
Diffstat (limited to 'src/backend')
-rw-r--r-- | src/backend/catalog/heap.c | 147 | ||||
-rw-r--r-- | src/backend/catalog/pg_constraint.c | 153 | ||||
-rw-r--r-- | src/backend/commands/indexcmds.c | 148 | ||||
-rw-r--r-- | src/backend/commands/tablecmds.c | 74 | ||||
-rw-r--r-- | src/backend/commands/typecmds.c | 24 | ||||
-rw-r--r-- | src/backend/parser/analyze.c | 136 |
6 files changed, 334 insertions, 348 deletions
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c index 718c3b90910..a4cc33d4c9d 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 09f3acd5f39..4bd9409b8de 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; } /* diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 1fe8e58e585..d141c83c1bf 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/indexcmds.c,v 1.120 2004/05/26 04:41:11 neilc Exp $ + * $PostgreSQL: pgsql/src/backend/commands/indexcmds.c,v 1.121 2004/06/10 17:55:56 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -28,10 +28,10 @@ #include "commands/defrem.h" #include "commands/tablecmds.h" #include "executor/executor.h" +#include "mb/pg_wchar.h" #include "miscadmin.h" #include "optimizer/clauses.h" #include "optimizer/prep.h" -#include "parser/analyze.h" #include "parser/parsetree.h" #include "parser/parse_coerce.h" #include "parser/parse_expr.h" @@ -53,8 +53,6 @@ static void ComputeIndexAttrs(IndexInfo *indexInfo, Oid *classOidP, static Oid GetIndexOpClass(List *opclass, Oid attrType, char *accessMethodName, Oid accessMethodId); static Oid GetDefaultOpClass(Oid attrType, Oid accessMethodId); -static char *CreateIndexName(const char *table_name, const char *column_name, - const char *label, Oid inamespace); static bool relationHasPrimaryKey(Relation rel); @@ -159,18 +157,18 @@ DefineIndex(RangeVar *heapRelation, if (indexRelationName == NULL) { if (primary) - indexRelationName = CreateIndexName(RelationGetRelationName(rel), - NULL, - "pkey", - namespaceId); + indexRelationName = ChooseRelationName(RelationGetRelationName(rel), + NULL, + "pkey", + namespaceId); else { IndexElem *iparam = (IndexElem *) linitial(attributeList); - indexRelationName = CreateIndexName(RelationGetRelationName(rel), - iparam->name, - "key", - namespaceId); + indexRelationName = ChooseRelationName(RelationGetRelationName(rel), + iparam->name, + "key", + namespaceId); } } @@ -657,35 +655,131 @@ GetDefaultOpClass(Oid attrType, Oid accessMethodId) } /* - * Select a nonconflicting name for an index. + * makeObjectName() + * + * Create a name for an implicitly created index, sequence, constraint, etc. + * + * The parameters are typically: the original table name, the original field + * name, and a "type" string (such as "seq" or "pkey"). The field name + * and/or type can be NULL if not relevant. + * + * The result is a palloc'd string. + * + * The basic result we want is "name1_name2_label", omitting "_name2" or + * "_label" when those parameters are NULL. However, we must generate + * a name with less than NAMEDATALEN characters! So, we truncate one or + * both names if necessary to make a short-enough string. The label part + * is never truncated (so it had better be reasonably short). + * + * The caller is responsible for checking uniqueness of the generated + * name and retrying as needed; retrying will be done by altering the + * "label" string (which is why we never truncate that part). */ -static char * -CreateIndexName(const char *table_name, const char *column_name, - const char *label, Oid inamespace) +char * +makeObjectName(const char *name1, const char *name2, const char *label) { - int pass = 0; - char *iname = NULL; - char typename[NAMEDATALEN]; + char *name; + int overhead = 0; /* chars needed for label and underscores */ + int availchars; /* chars available for name(s) */ + int name1chars; /* chars allocated to name1 */ + int name2chars; /* chars allocated to name2 */ + int ndx; + + name1chars = strlen(name1); + if (name2) + { + name2chars = strlen(name2); + overhead++; /* allow for separating underscore */ + } + else + name2chars = 0; + if (label) + overhead += strlen(label) + 1; + + availchars = NAMEDATALEN - 1 - overhead; + Assert(availchars > 0); /* else caller chose a bad label */ /* - * The type name for makeObjectName is label, or labelN if that's - * necessary to prevent collision with existing indexes. + * If we must truncate, preferentially truncate the longer name. This + * logic could be expressed without a loop, but it's simple and + * obvious as a loop. */ - strncpy(typename, label, sizeof(typename)); + while (name1chars + name2chars > availchars) + { + if (name1chars > name2chars) + name1chars--; + else + name2chars--; + } + + if (name1) + name1chars = pg_mbcliplen(name1, name1chars, name1chars); + if (name2) + name2chars = pg_mbcliplen(name2, name2chars, name2chars); + + /* Now construct the string using the chosen lengths */ + name = palloc(name1chars + name2chars + overhead + 1); + memcpy(name, name1, name1chars); + ndx = name1chars; + if (name2) + { + name[ndx++] = '_'; + memcpy(name + ndx, name2, name2chars); + ndx += name2chars; + } + if (label) + { + name[ndx++] = '_'; + strcpy(name + ndx, label); + } + else + name[ndx] = '\0'; + + return name; +} + +/* + * Select a nonconflicting name for a new relation. This is ordinarily + * used to choose index names (which is why it's here) but it can also + * be used for sequences, or any autogenerated relation kind. + * + * 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. + * + * 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. However, if choosing multiple names + * within a single command, you'd better create the new object and do + * CommandCounterIncrement before choosing the next one! + * + * Returns a palloc'd string. + */ +char * +ChooseRelationName(const char *name1, const char *name2, + const char *label, Oid namespace) +{ + int pass = 0; + char *relname = NULL; + char modlabel[NAMEDATALEN]; + + /* try the unmodified label first */ + StrNCpy(modlabel, label, sizeof(modlabel)); for (;;) { - iname = makeObjectName(table_name, column_name, typename); + relname = makeObjectName(name1, name2, modlabel); - if (!OidIsValid(get_relname_relid(iname, inamespace))) + if (!OidIsValid(get_relname_relid(relname, namespace))) break; /* found a conflict, so try a new name component */ - pfree(iname); - snprintf(typename, sizeof(typename), "%s%d", label, ++pass); + pfree(relname); + snprintf(modlabel, sizeof(modlabel), "%s%d", label, ++pass); } - return iname; + return relname; } /* diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 3a95064442c..7b0b35840ee 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.112 2004/06/06 20:30:07 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.113 2004/06/10 17:55:56 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -124,8 +124,6 @@ typedef struct AlteredTableInfo List *changedConstraintDefs; /* string definitions of same */ List *changedIndexOids; /* OIDs of indexes to rebuild */ List *changedIndexDefs; /* string definitions of same */ - /* Workspace for ATExecAddConstraint */ - int constr_name_ctr; } AlteredTableInfo; /* Struct describing one new constraint to check in Phase 3 scan */ @@ -323,46 +321,45 @@ DefineRelation(CreateStmt *stmt, char relkind) if (old_constraints != NIL) { - ConstrCheck *check = (ConstrCheck *) palloc(list_length(old_constraints) * - sizeof(ConstrCheck)); + ConstrCheck *check = (ConstrCheck *) + palloc0(list_length(old_constraints) * sizeof(ConstrCheck)); int ncheck = 0; - int constr_name_ctr = 0; foreach(listptr, old_constraints) { Constraint *cdef = (Constraint *) lfirst(listptr); + bool dup = false; if (cdef->contype != CONSTR_CHECK) continue; - - if (cdef->name != NULL) + Assert(cdef->name != NULL); + Assert(cdef->raw_expr == NULL && cdef->cooked_expr != NULL); + /* + * In multiple-inheritance situations, it's possible to inherit + * the same grandparent constraint through multiple parents. + * Hence, discard inherited constraints that match as to both + * name and expression. Otherwise, gripe if the names conflict. + */ + for (i = 0; i < ncheck; i++) { - for (i = 0; i < ncheck; i++) + if (strcmp(check[i].ccname, cdef->name) != 0) + continue; + if (strcmp(check[i].ccbin, cdef->cooked_expr) == 0) { - if (strcmp(check[i].ccname, cdef->name) == 0) - ereport(ERROR, - (errcode(ERRCODE_DUPLICATE_OBJECT), + dup = true; + break; + } + ereport(ERROR, + (errcode(ERRCODE_DUPLICATE_OBJECT), errmsg("duplicate check constraint name \"%s\"", cdef->name))); - } - check[ncheck].ccname = cdef->name; } - else + if (!dup) { - /* - * Generate a constraint name. NB: this should match the - * form of names that GenerateConstraintName() may produce - * for names added later. We are assured that there is no - * name conflict, because MergeAttributes() did not pass - * back any names of this form. - */ - check[ncheck].ccname = (char *) palloc(NAMEDATALEN); - snprintf(check[ncheck].ccname, NAMEDATALEN, "$%d", - ++constr_name_ctr); + check[ncheck].ccname = cdef->name; + check[ncheck].ccbin = pstrdup(cdef->cooked_expr); + ncheck++; } - Assert(cdef->raw_expr == NULL && cdef->cooked_expr != NULL); - check[ncheck].ccbin = pstrdup(cdef->cooked_expr); - ncheck++; } if (ncheck > 0) { @@ -867,17 +864,7 @@ MergeAttributes(List *schema, List *supers, bool istemp, Node *expr; cdef->contype = CONSTR_CHECK; - - /* - * Do not inherit generated constraint names, since they - * might conflict across multiple inheritance parents. - * (But conflicts between user-assigned names will cause - * an error.) - */ - if (ConstraintNameIsGenerated(check[i].ccname)) - cdef->name = NULL; - else - cdef->name = pstrdup(check[i].ccname); + cdef->name = pstrdup(check[i].ccname); cdef->raw_expr = NULL; /* adjust varattnos of ccbin here */ expr = stringToNode(check[i].ccbin); @@ -3610,10 +3597,11 @@ ATExecAddConstraint(AlteredTableInfo *tab, Relation rel, Node *newConstraint) } else fkconstraint->constr_name = - GenerateConstraintName(CONSTRAINT_RELATION, - RelationGetRelid(rel), - RelationGetNamespace(rel), - &tab->constr_name_ctr); + ChooseConstraintName(RelationGetRelationName(rel), + strVal(linitial(fkconstraint->fk_attrs)), + "fkey", + RelationGetNamespace(rel), + NIL); ATAddForeignKeyConstraint(tab, rel, fkconstraint); diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c index 439ad91cc37..a64617d08d4 100644 --- a/src/backend/commands/typecmds.c +++ b/src/backend/commands/typecmds.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/typecmds.c,v 1.58 2004/06/04 20:35:21 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/commands/typecmds.c,v 1.59 2004/06/10 17:55:56 tgl Exp $ * * DESCRIPTION * The "DefineFoo" routines take the parse tree and pick out the @@ -83,7 +83,7 @@ static void domainOwnerCheck(HeapTuple tup, TypeName *typename); static char *domainAddConstraint(Oid domainOid, Oid domainNamespace, Oid baseTypeOid, int typMod, Constraint *constr, - int *counter, char *domainName); + char *domainName); /* @@ -509,7 +509,6 @@ DefineDomain(CreateDomainStmt *stmt) Oid basetypeoid; Oid domainoid; Form_pg_type baseType; - int counter = 0; /* Convert list of names to a name and namespace */ domainNamespace = QualifiedNameGetCreationNamespace(stmt->domainname, @@ -760,7 +759,7 @@ DefineDomain(CreateDomainStmt *stmt) case CONSTR_CHECK: domainAddConstraint(domainoid, domainNamespace, basetypeoid, stmt->typename->typmod, - constr, &counter, domainName); + constr, domainName); break; /* Other constraint types were fully processed above */ @@ -768,6 +767,9 @@ DefineDomain(CreateDomainStmt *stmt) default: break; } + + /* CCI so we can detect duplicate constraint names */ + CommandCounterIncrement(); } /* @@ -1463,7 +1465,6 @@ AlterDomainAddConstraint(List *names, Node *newConstraint) char *ccbin; Expr *expr; ExprState *exprstate; - int counter = 0; Constraint *constr; /* Make a TypeName so we can use standard type lookup machinery */ @@ -1547,7 +1548,7 @@ AlterDomainAddConstraint(List *names, Node *newConstraint) ccbin = domainAddConstraint(HeapTupleGetOid(tup), typTup->typnamespace, typTup->typbasetype, typTup->typtypmod, - constr, &counter, NameStr(typTup->typname)); + constr, NameStr(typTup->typname)); /* * Test all values stored in the attributes based on the domain the @@ -1788,7 +1789,7 @@ domainOwnerCheck(HeapTuple tup, TypeName *typename) static char * domainAddConstraint(Oid domainOid, Oid domainNamespace, Oid baseTypeOid, int typMod, Constraint *constr, - int *counter, char *domainName) + char *domainName) { Node *expr; char *ccsrc; @@ -1811,10 +1812,11 @@ domainAddConstraint(Oid domainOid, Oid domainNamespace, Oid baseTypeOid, constr->name, domainName))); } else - constr->name = GenerateConstraintName(CONSTRAINT_DOMAIN, - domainOid, - domainNamespace, - counter); + constr->name = ChooseConstraintName(domainName, + NULL, + "check", + domainNamespace, + NIL); /* * Convert the A_EXPR in raw_expr into an EXPR diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c index f6cdf96e296..e5537e26ae9 100644 --- a/src/backend/parser/analyze.c +++ b/src/backend/parser/analyze.c @@ -6,7 +6,7 @@ * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/backend/parser/analyze.c,v 1.304 2004/06/09 19:08:16 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/parser/analyze.c,v 1.305 2004/06/10 17:55:58 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -20,6 +20,7 @@ #include "catalog/namespace.h" #include "catalog/pg_index.h" #include "catalog/pg_type.h" +#include "commands/defrem.h" #include "commands/prepare.h" #include "miscadmin.h" #include "nodes/makefuncs.h" @@ -45,7 +46,6 @@ #include "utils/lsyscache.h" #include "utils/relcache.h" #include "utils/syscache.h" -#include "mb/pg_wchar.h" /* State shared by transformCreateSchemaStmt and its subroutines */ @@ -704,90 +704,6 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt, } /* - * makeObjectName() - * - * Create a name for an implicitly created index, sequence, constraint, etc. - * - * The parameters are: the original table name, the original field name, and - * a "type" string (such as "seq" or "pkey"). The field name and/or type - * can be NULL if not relevant. - * - * The result is a palloc'd string. - * - * The basic result we want is "name1_name2_type", omitting "_name2" or - * "_type" when those parameters are NULL. However, we must generate - * a name with less than NAMEDATALEN characters! So, we truncate one or - * both names if necessary to make a short-enough string. The type part - * is never truncated (so it had better be reasonably short). - * - * To reduce the probability of collisions, we might someday add more - * smarts to this routine, like including some "hash" characters computed - * from the truncated characters. Currently it seems best to keep it simple, - * so that the generated names are easily predictable by a person. - */ -char * -makeObjectName(const char *name1, const char *name2, const char *typename) -{ - char *name; - int overhead = 0; /* chars needed for type and underscores */ - int availchars; /* chars available for name(s) */ - int name1chars; /* chars allocated to name1 */ - int name2chars; /* chars allocated to name2 */ - int ndx; - - name1chars = strlen(name1); - if (name2) - { - name2chars = strlen(name2); - overhead++; /* allow for separating underscore */ - } - else - name2chars = 0; - if (typename) - overhead += strlen(typename) + 1; - - availchars = NAMEDATALEN - 1 - overhead; - - /* - * If we must truncate, preferentially truncate the longer name. This - * logic could be expressed without a loop, but it's simple and - * obvious as a loop. - */ - while (name1chars + name2chars > availchars) - { - if (name1chars > name2chars) - name1chars--; - else - name2chars--; - } - - if (name1) - name1chars = pg_mbcliplen(name1, name1chars, name1chars); - if (name2) - name2chars = pg_mbcliplen(name2, name2chars, name2chars); - - /* Now construct the string using the chosen lengths */ - name = palloc(name1chars + name2chars + overhead + 1); - strncpy(name, name1, name1chars); - ndx = name1chars; - if (name2) - { - name[ndx++] = '_'; - strncpy(name + ndx, name2, name2chars); - ndx += name2chars; - } - if (typename) - { - name[ndx++] = '_'; - strcpy(name + ndx, typename); - } - else - name[ndx] = '\0'; - - return name; -} - -/* * transformCreateStmt - * transforms the "create table" statement * SQL92 allows constraints to be scattered all over, so thumb through @@ -919,21 +835,34 @@ transformColumnDefinition(ParseState *pstate, CreateStmtContext *cxt, /* Special actions for SERIAL pseudo-types */ if (is_serial) { - char *sname; + Oid snamespaceid; char *snamespace; + char *sname; char *qstring; A_Const *snamenode; FuncCall *funccallnode; CreateSeqStmt *seqstmt; /* - * Determine name and namespace to use for the sequence. + * Determine namespace and name to use for the sequence. + * + * Although we use ChooseRelationName, it's not guaranteed that + * the selected sequence name won't conflict; given sufficiently + * long field names, two different serial columns in the same table + * could be assigned the same sequence name, and we'd not notice + * since we aren't creating the sequence quite yet. In practice + * this seems quite unlikely to be a problem, especially since + * few people would need two serial columns in one table. */ - sname = makeObjectName(cxt->relation->relname, column->colname, "seq"); - snamespace = get_namespace_name(RangeVarGetCreationNamespace(cxt->relation)); + snamespaceid = RangeVarGetCreationNamespace(cxt->relation); + snamespace = get_namespace_name(snamespaceid); + sname = ChooseRelationName(cxt->relation->relname, + column->colname, + "seq", + snamespaceid); ereport(NOTICE, - (errmsg("%s will create implicit sequence \"%s\" for \"serial\" column \"%s.%s\"", + (errmsg("%s will create implicit sequence \"%s\" for serial column \"%s.%s\"", cxt->stmtType, sname, cxt->relation->relname, column->colname))); @@ -975,7 +904,6 @@ transformColumnDefinition(ParseState *pstate, CreateStmtContext *cxt, constraint = makeNode(Constraint); constraint->contype = CONSTR_DEFAULT; - constraint->name = sname; constraint->raw_expr = (Node *) funccallnode; constraint->cooked_expr = NULL; constraint->keys = NIL; @@ -1044,30 +972,13 @@ transformColumnDefinition(ParseState *pstate, CreateStmtContext *cxt, break; case CONSTR_PRIMARY: - if (constraint->name == NULL) - constraint->name = makeObjectName(cxt->relation->relname, - NULL, - "pkey"); - if (constraint->keys == NIL) - constraint->keys = list_make1(makeString(column->colname)); - cxt->ixconstraints = lappend(cxt->ixconstraints, constraint); - break; - case CONSTR_UNIQUE: - if (constraint->name == NULL) - constraint->name = makeObjectName(cxt->relation->relname, - column->colname, - "key"); if (constraint->keys == NIL) constraint->keys = list_make1(makeString(column->colname)); cxt->ixconstraints = lappend(cxt->ixconstraints, constraint); break; case CONSTR_CHECK: - if (constraint->name == NULL) - constraint->name = makeObjectName(cxt->relation->relname, - column->colname, - NULL); cxt->ckconstraints = lappend(cxt->ckconstraints, constraint); break; @@ -1093,13 +1004,6 @@ transformTableConstraint(ParseState *pstate, CreateStmtContext *cxt, switch (constraint->contype) { case CONSTR_PRIMARY: - if (constraint->name == NULL) - constraint->name = makeObjectName(cxt->relation->relname, - NULL, - "pkey"); - cxt->ixconstraints = lappend(cxt->ixconstraints, constraint); - break; - case CONSTR_UNIQUE: cxt->ixconstraints = lappend(cxt->ixconstraints, constraint); break; |