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/commands/indexcmds.c | |
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/commands/indexcmds.c')
-rw-r--r-- | src/backend/commands/indexcmds.c | 148 |
1 files changed, 121 insertions, 27 deletions
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; } /* |