From cfc5008a51f4f1e83e9209071882ace91abe1f3f Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Wed, 23 Dec 2009 02:35:25 +0000 Subject: [PATCH] Adjust naming of indexes and their columns per recent discussion. Index expression columns are now named after the FigureColname result for their expressions, rather than always being "pg_expression_N". Digits are appended to this name if needed to make the column name unique within the index. (That happens for regular columns too, thus fixing the old problem that CREATE INDEX fooi ON foo (f1, f1) fails. Before exclusion indexes there was no real reason to do such a thing, but now maybe there is.) Default names for indexes and associated constraints now include the column names of all their columns, not only the first one as in previous practice. (Of course, this will be truncated as needed to fit in NAMEDATALEN. Also, pkey indexes retain the historical behavior of not naming specific columns at all.) An example of the results: regression=# create table foo (f1 int, f2 text, regression(# exclude (f1 with =, lower(f2) with =)); NOTICE: CREATE TABLE / EXCLUDE will create implicit index "foo_f1_lower_exclusion" for table "foo" CREATE TABLE regression=# \d foo_f1_lower_exclusion Index "public.foo_f1_lower_exclusion" Column | Type | Definition --------+---------+------------ f1 | integer | f1 lower | text | lower(f2) btree, for table "public.foo" --- src/backend/bootstrap/bootparse.y | 3 +- src/backend/catalog/index.c | 22 ++- src/backend/catalog/toasting.c | 3 +- src/backend/commands/indexcmds.c | 220 ++++++++++++++++----- src/backend/nodes/copyfuncs.c | 3 +- src/backend/nodes/equalfuncs.c | 3 +- src/backend/nodes/outfuncs.c | 3 +- src/backend/parser/gram.y | 5 +- src/backend/parser/parse_target.c | 31 ++- src/backend/parser/parse_utilcmd.c | 47 ++--- src/include/catalog/index.h | 3 +- src/include/commands/defrem.h | 6 +- src/include/nodes/parsenodes.h | 3 +- src/include/parser/parse_target.h | 3 +- src/test/regress/expected/alter_table.out | 2 +- src/test/regress/expected/foreign_key.out | 6 +- src/test/regress/expected/inherit.out | 6 +- src/test/regress/output/constraints.source | 12 +- 18 files changed, 277 insertions(+), 104 deletions(-) diff --git a/src/backend/bootstrap/bootparse.y b/src/backend/bootstrap/bootparse.y index 18affeb098..5c85fcff5e 100644 --- a/src/backend/bootstrap/bootparse.y +++ b/src/backend/bootstrap/bootparse.y @@ -9,7 +9,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/bootstrap/bootparse.y,v 1.101 2009/12/07 05:22:21 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/bootstrap/bootparse.y,v 1.102 2009/12/23 02:35:18 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -323,6 +323,7 @@ boot_index_param: IndexElem *n = makeNode(IndexElem); n->name = $1; n->expr = NULL; + n->indexcolname = NULL; n->opclass = list_make1(makeString($2)); n->ordering = SORTBY_DEFAULT; n->nulls_ordering = SORTBY_NULLS_DEFAULT; diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 5394d2557e..9eb96b7718 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/catalog/index.c,v 1.326 2009/12/09 21:57:50 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/catalog/index.c,v 1.327 2009/12/23 02:35:18 tgl Exp $ * * * INTERFACE ROUTINES @@ -82,6 +82,7 @@ typedef struct /* non-export function prototypes */ static TupleDesc ConstructTupleDescriptor(Relation heapRelation, IndexInfo *indexInfo, + List *indexColNames, Oid accessMethodObjectId, Oid *classObjectId); static void InitializeAttributeOids(Relation indexRelation, @@ -117,10 +118,12 @@ static Oid IndexGetRelation(Oid indexId); static TupleDesc ConstructTupleDescriptor(Relation heapRelation, IndexInfo *indexInfo, + List *indexColNames, Oid accessMethodObjectId, Oid *classObjectId) { int numatts = indexInfo->ii_NumIndexAttrs; + ListCell *colnames_item = list_head(indexColNames); ListCell *indexpr_item = list_head(indexInfo->ii_Expressions); HeapTuple amtuple; Form_pg_am amform; @@ -216,12 +219,6 @@ ConstructTupleDescriptor(Relation heapRelation, indexkey = (Node *) lfirst(indexpr_item); indexpr_item = lnext(indexpr_item); - /* - * Make the attribute's name "pg_expresssion_nnn" (maybe think of - * something better later) - */ - sprintf(NameStr(to->attname), "pg_expression_%d", i + 1); - /* * Lookup the expression type in pg_type for the type length etc. */ @@ -268,6 +265,14 @@ ConstructTupleDescriptor(Relation heapRelation, */ to->attrelid = InvalidOid; + /* + * Set the attribute name as specified by caller. + */ + if (colnames_item == NULL) /* shouldn't happen */ + elog(ERROR, "too few entries in colnames list"); + namestrcpy(&to->attname, (const char *) lfirst(colnames_item)); + colnames_item = lnext(colnames_item); + /* * Check the opclass and index AM to see if either provides a keytype * (overriding the attribute type). Opclass takes precedence. @@ -494,6 +499,7 @@ UpdateIndexRelation(Oid indexoid, * generate an OID for the index. During bootstrap this may be * nonzero to specify a preselected OID. * indexInfo: same info executor uses to insert into the index + * indexColNames: column names to use for index (List of char *) * accessMethodObjectId: OID of index AM to use * tableSpaceId: OID of tablespace to use * classObjectId: array of index opclass OIDs, one per index column @@ -517,6 +523,7 @@ index_create(Oid heapRelationId, const char *indexRelationName, Oid indexRelationId, IndexInfo *indexInfo, + List *indexColNames, Oid accessMethodObjectId, Oid tableSpaceId, Oid *classObjectId, @@ -629,6 +636,7 @@ index_create(Oid heapRelationId, */ indexTupDesc = ConstructTupleDescriptor(heapRelation, indexInfo, + indexColNames, accessMethodObjectId, classObjectId); diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c index 05f3bb4969..a8c2da66dc 100644 --- a/src/backend/catalog/toasting.c +++ b/src/backend/catalog/toasting.c @@ -8,7 +8,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/catalog/toasting.c,v 1.21 2009/12/07 05:22:21 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/catalog/toasting.c,v 1.22 2009/12/23 02:35:18 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -255,6 +255,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, toast_idxid = index_create(toast_relid, toast_idxname, toastIndexOid, indexInfo, + list_make2("chunk_id", "chunk_seq"), BTREE_AM_OID, rel->rd_rel->reltablespace, classObjectId, coloptions, (Datum) 0, diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 00786442b3..833114abcf 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.188 2009/12/07 05:22:21 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/commands/indexcmds.c,v 1.189 2009/12/23 02:35:20 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -67,6 +67,7 @@ static void ComputeIndexAttrs(IndexInfo *indexInfo, bool isconstraint); static Oid GetIndexOpClass(List *opclass, Oid attrType, char *accessMethodName, Oid accessMethodId); +static char *ChooseIndexNameAddition(List *colnames); static bool relationHasPrimaryKey(Relation rel); @@ -128,6 +129,7 @@ DefineIndex(RangeVar *heapRelation, Oid relationId; Oid namespaceId; Oid tablespaceId; + List *indexColNames; Relation rel; Relation indexRelation; HeapTuple tuple; @@ -247,37 +249,21 @@ DefineIndex(RangeVar *heapRelation, if (rel->rd_rel->relisshared) tablespaceId = GLOBALTABLESPACE_OID; + /* + * Choose the index column names. + */ + indexColNames = ChooseIndexColumnNames(attributeList); + /* * Select name for index if caller didn't specify */ if (indexRelationName == NULL) - { - if (primary) - { - indexRelationName = ChooseRelationName(RelationGetRelationName(rel), - NULL, - "pkey", - namespaceId); - } - else if (exclusionOpNames != NIL) - { - IndexElem *iparam = (IndexElem *) linitial(attributeList); - - indexRelationName = ChooseRelationName(RelationGetRelationName(rel), - iparam->name, - "exclusion", - namespaceId); - } - else - { - IndexElem *iparam = (IndexElem *) linitial(attributeList); - - indexRelationName = ChooseRelationName(RelationGetRelationName(rel), - iparam->name, - "key", - namespaceId); - } - } + indexRelationName = ChooseIndexName(RelationGetRelationName(rel), + namespaceId, + indexColNames, + exclusionOpNames, + primary, + isconstraint); /* * look up the access method, verify it can handle the requested features @@ -488,35 +474,30 @@ DefineIndex(RangeVar *heapRelation, SET_LOCKTAG_RELATION(heaplocktag, heaprelid.dbId, heaprelid.relId); heap_close(rel, NoLock); - if (!concurrent) - { - indexRelationId = - index_create(relationId, indexRelationName, indexRelationId, - indexInfo, accessMethodId, tablespaceId, classObjectId, - coloptions, reloptions, primary, - isconstraint, deferrable, initdeferred, - allowSystemTableMods, skip_build, concurrent); - - return; /* We're done, in the standard case */ - } - /* - * For a concurrent build, we next insert the catalog entry and add - * constraints. We don't build the index just yet; we must first make the - * catalog entry so that the new index is visible to updating - * transactions. That will prevent them from making incompatible HOT - * updates. The new index will be marked not indisready and not - * indisvalid, so that no one else tries to either insert into it or use - * it for queries. We pass skip_build = true to prevent the build. + * Make the catalog entries for the index, including constraints. + * Then, if not skip_build || concurrent, actually build the index. */ indexRelationId = index_create(relationId, indexRelationName, indexRelationId, - indexInfo, accessMethodId, tablespaceId, classObjectId, + indexInfo, indexColNames, + accessMethodId, tablespaceId, classObjectId, coloptions, reloptions, primary, isconstraint, deferrable, initdeferred, - allowSystemTableMods, true, concurrent); + allowSystemTableMods, + skip_build || concurrent, + concurrent); + + if (!concurrent) + return; /* We're done, in the standard case */ /* + * For a concurrent build, it's important to make the catalog entries + * visible to other transactions before we start to build the index. + * That will prevent them from making incompatible HOT updates. The new + * index will be marked not indisready and not indisvalid, so that no one + * else tries to either insert into it or use it for queries. + * * We must commit our current transaction so that the index becomes * visible; then start another. Note that all the data structures we just * built are lost in the commit. The only data we keep past here are the @@ -1391,6 +1372,147 @@ ChooseRelationName(const char *name1, const char *name2, return relname; } +/* + * Select the name to be used for an index. + * + * The argument list is pretty ad-hoc :-( + */ +char * +ChooseIndexName(const char *tabname, Oid namespaceId, + List *colnames, List *exclusionOpNames, + bool primary, bool isconstraint) +{ + char *indexname; + + if (primary) + { + /* the primary key's name does not depend on the specific column(s) */ + indexname = ChooseRelationName(tabname, + NULL, + "pkey", + namespaceId); + } + else if (exclusionOpNames != NIL) + { + indexname = ChooseRelationName(tabname, + ChooseIndexNameAddition(colnames), + "exclusion", + namespaceId); + } + else if (isconstraint) + { + indexname = ChooseRelationName(tabname, + ChooseIndexNameAddition(colnames), + "key", + namespaceId); + } + else + { + indexname = ChooseRelationName(tabname, + ChooseIndexNameAddition(colnames), + "idx", + namespaceId); + } + + return indexname; +} + +/* + * Generate "name2" for a new index given the list of column names for it + * (as produced by ChooseIndexColumnNames). This will be passed to + * ChooseRelationName along with the parent table name and a suitable label. + * + * We know that less than NAMEDATALEN characters will actually be used, + * so we can truncate the result once we've generated that many. + */ +static char * +ChooseIndexNameAddition(List *colnames) +{ + char buf[NAMEDATALEN * 2]; + int buflen = 0; + ListCell *lc; + + buf[0] = '\0'; + foreach(lc, colnames) + { + const char *name = (const char *) lfirst(lc); + + if (buflen > 0) + buf[buflen++] = '_'; /* insert _ between names */ + + /* + * At this point we have buflen <= NAMEDATALEN. name should be less + * than NAMEDATALEN already, but use strlcpy for paranoia. + */ + strlcpy(buf + buflen, name, NAMEDATALEN); + buflen += strlen(buf + buflen); + if (buflen >= NAMEDATALEN) + break; + } + return pstrdup(buf); +} + +/* + * Select the actual names to be used for the columns of an index, given the + * list of IndexElems for the columns. This is mostly about ensuring the + * names are unique so we don't get a conflicting-attribute-names error. + * + * Returns a List of plain strings (char *, not String nodes). + */ +List * +ChooseIndexColumnNames(List *indexElems) +{ + List *result = NIL; + ListCell *lc; + + foreach(lc, indexElems) + { + IndexElem *ielem = (IndexElem *) lfirst(lc); + const char *origname; + const char *curname; + int i; + char buf[NAMEDATALEN]; + + /* Get the preliminary name from the IndexElem */ + if (ielem->indexcolname) + origname = ielem->indexcolname; /* caller-specified name */ + else if (ielem->name) + origname = ielem->name; /* simple column reference */ + else + origname = "expr"; /* default name for expression */ + + /* If it conflicts with any previous column, tweak it */ + curname = origname; + for (i = 1;; i++) + { + ListCell *lc2; + char nbuf[32]; + int nlen; + + foreach(lc2, result) + { + if (strcmp(curname, (char *) lfirst(lc2)) == 0) + break; + } + if (lc2 == NULL) + break; /* found nonconflicting name */ + + sprintf(nbuf, "%d", i); + + /* Ensure generated names are shorter than NAMEDATALEN */ + nlen = pg_mbcliplen(origname, strlen(origname), + NAMEDATALEN - 1 - strlen(nbuf)); + memcpy(buf, origname, nlen); + strcpy(buf + nlen, nbuf); + curname = buf; + } + + /* And attach to the result list */ + result = lappend(result, pstrdup(curname)); + } + return result; +} + /* * relationHasPrimaryKey - * diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index 018f36cef6..2722a93e07 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -15,7 +15,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.454 2009/12/15 17:57:46 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.455 2009/12/23 02:35:20 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -2121,6 +2121,7 @@ _copyIndexElem(IndexElem *from) COPY_STRING_FIELD(name); COPY_NODE_FIELD(expr); + COPY_STRING_FIELD(indexcolname); COPY_NODE_FIELD(opclass); COPY_SCALAR_FIELD(ordering); COPY_SCALAR_FIELD(nulls_ordering); diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index 2387aaba41..e53b4a89e6 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -22,7 +22,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.376 2009/12/15 17:57:46 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.377 2009/12/23 02:35:21 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -2072,6 +2072,7 @@ _equalIndexElem(IndexElem *a, IndexElem *b) { COMPARE_STRING_FIELD(name); COMPARE_NODE_FIELD(expr); + COMPARE_STRING_FIELD(indexcolname); COMPARE_NODE_FIELD(opclass); COMPARE_SCALAR_FIELD(ordering); COMPARE_SCALAR_FIELD(nulls_ordering); diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index cac346464b..419c005b50 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.375 2009/12/15 17:57:46 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.376 2009/12/23 02:35:21 tgl Exp $ * * NOTES * Every node type that can appear in stored rules' parsetrees *must* @@ -1946,6 +1946,7 @@ _outIndexElem(StringInfo str, IndexElem *node) WRITE_STRING_FIELD(name); WRITE_NODE_FIELD(expr); + WRITE_STRING_FIELD(indexcolname); WRITE_NODE_FIELD(opclass); WRITE_ENUM_FIELD(ordering, SortByDir); WRITE_ENUM_FIELD(nulls_ordering, SortByNulls); diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 22449579d0..7ff46e05cb 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -11,7 +11,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.697 2009/12/15 17:57:47 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.698 2009/12/23 02:35:22 tgl Exp $ * * HISTORY * AUTHOR DATE MAJOR EVENT @@ -4887,6 +4887,7 @@ index_elem: ColId opt_class opt_asc_desc opt_nulls_order $$ = makeNode(IndexElem); $$->name = $1; $$->expr = NULL; + $$->indexcolname = NULL; $$->opclass = $2; $$->ordering = $3; $$->nulls_ordering = $4; @@ -4896,6 +4897,7 @@ index_elem: ColId opt_class opt_asc_desc opt_nulls_order $$ = makeNode(IndexElem); $$->name = NULL; $$->expr = $1; + $$->indexcolname = NULL; $$->opclass = $2; $$->ordering = $3; $$->nulls_ordering = $4; @@ -4905,6 +4907,7 @@ index_elem: ColId opt_class opt_asc_desc opt_nulls_order $$ = makeNode(IndexElem); $$->name = NULL; $$->expr = $2; + $$->indexcolname = NULL; $$->opclass = $4; $$->ordering = $5; $$->nulls_ordering = $6; diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c index ce3f51ca6e..007a3cc693 100644 --- a/src/backend/parser/parse_target.c +++ b/src/backend/parser/parse_target.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/parse_target.c,v 1.174 2009/10/31 01:41:31 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/parser/parse_target.c,v 1.175 2009/12/23 02:35:22 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1410,13 +1410,40 @@ FigureColname(Node *node) { char *name = NULL; - FigureColnameInternal(node, &name); + (void) FigureColnameInternal(node, &name); if (name != NULL) return name; /* default result if we can't guess anything */ return "?column?"; } +/* + * FigureIndexColname - + * choose the name for an expression column in an index + * + * This is actually just like FigureColname, except we return NULL if + * we can't pick a good name. + */ +char * +FigureIndexColname(Node *node) +{ + char *name = NULL; + + (void) FigureColnameInternal(node, &name); + return name; +} + +/* + * FigureColnameInternal - + * internal workhorse for FigureColname + * + * Return value indicates strength of confidence in result: + * 0 - no information + * 1 - second-best name choice + * 2 - good name choice + * The return value is actually only used internally. + * If the result isn't zero, *name is set to the chosen name. + */ static int FigureColnameInternal(Node *node, char **name) { diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index 75c8d863dc..f09e78dd8e 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -19,7 +19,7 @@ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/backend/parser/parse_utilcmd.c,v 2.34 2009/12/22 23:54:17 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/parser/parse_utilcmd.c,v 2.35 2009/12/23 02:35:22 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -48,6 +48,7 @@ #include "parser/parse_clause.h" #include "parser/parse_expr.h" #include "parser/parse_relation.h" +#include "parser/parse_target.h" #include "parser/parse_type.h" #include "parser/parse_utilcmd.h" #include "parser/parser.h" @@ -789,34 +790,24 @@ transformInhRelation(ParseState *pstate, CreateStmtContext *cxt, /* * chooseIndexName * - * Set name for unnamed index. See also the same logic in DefineIndex. + * Compute name for an index. This must match code in indexcmds.c. + * + * XXX this is inherently broken because the indexes aren't created + * immediately, so we fail to resolve conflicts when the same name is + * derived for multiple indexes. However, that's a reasonably uncommon + * situation, so we'll live with it for now. */ static char * chooseIndexName(const RangeVar *relation, IndexStmt *index_stmt) { - Oid namespaceId; + Oid namespaceId; + List *colnames; namespaceId = RangeVarGetCreationNamespace(relation); - if (index_stmt->primary) - { - /* no need for column list with pkey */ - return ChooseRelationName(relation->relname, NULL, - "pkey", namespaceId); - } - else if (index_stmt->excludeOpNames != NIL) - { - IndexElem *iparam = (IndexElem *) linitial(index_stmt->indexParams); - - return ChooseRelationName(relation->relname, iparam->name, - "exclusion", namespaceId); - } - else - { - IndexElem *iparam = (IndexElem *) linitial(index_stmt->indexParams); - - return ChooseRelationName(relation->relname, iparam->name, - "key", namespaceId); - } + colnames = ChooseIndexColumnNames(index_stmt->indexParams); + return ChooseIndexName(relation->relname, namespaceId, + colnames, index_stmt->excludeOpNames, + index_stmt->primary, index_stmt->isconstraint); } /* @@ -828,6 +819,7 @@ generateClonedIndexStmt(CreateStmtContext *cxt, Relation source_idx, AttrNumber *attmap) { Oid source_relid = RelationGetRelid(source_idx); + Form_pg_attribute *attrs = RelationGetDescr(source_idx)->attrs; HeapTuple ht_idxrel; HeapTuple ht_idx; Form_pg_class idxrelrec; @@ -1023,6 +1015,9 @@ generateClonedIndexStmt(CreateStmtContext *cxt, Relation source_idx, keycoltype = exprType(indexkey); } + /* Copy the original index column name */ + iparam->indexcolname = pstrdup(NameStr(attrs[keyno]->attname)); + /* Add the operator class name, if non-default */ iparam->opclass = get_opclass(indclass->values[keyno], keycoltype); @@ -1416,6 +1411,7 @@ transformIndexConstraint(Constraint *constraint, CreateStmtContext *cxt) iparam = makeNode(IndexElem); iparam->name = pstrdup(key); iparam->expr = NULL; + iparam->indexcolname = NULL; iparam->opclass = NIL; iparam->ordering = SORTBY_DEFAULT; iparam->nulls_ordering = SORTBY_NULLS_DEFAULT; @@ -1544,6 +1540,11 @@ transformIndexStmt(IndexStmt *stmt, const char *queryString) if (ielem->expr) { + /* Extract preliminary index col name before transforming expr */ + if (ielem->indexcolname == NULL) + ielem->indexcolname = FigureIndexColname(ielem->expr); + + /* Now do parse transformation of the expression */ ielem->expr = transformExpr(pstate, ielem->expr); /* diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index a432260058..f2119a530f 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/catalog/index.h,v 1.78 2009/07/29 20:56:20 tgl Exp $ + * $PostgreSQL: pgsql/src/include/catalog/index.h,v 1.79 2009/12/23 02:35:22 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -32,6 +32,7 @@ extern Oid index_create(Oid heapRelationId, const char *indexRelationName, Oid indexRelationId, IndexInfo *indexInfo, + List *indexColNames, Oid accessMethodObjectId, Oid tableSpaceId, Oid *classObjectId, diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h index 1b665ff855..2ac7e160fe 100644 --- a/src/include/commands/defrem.h +++ b/src/include/commands/defrem.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/commands/defrem.h,v 1.98 2009/12/07 05:22:23 tgl Exp $ + * $PostgreSQL: pgsql/src/include/commands/defrem.h,v 1.99 2009/12/23 02:35:23 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -45,6 +45,10 @@ 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 char *ChooseIndexName(const char *tabname, Oid namespaceId, + List *colnames, List *exclusionOpNames, + bool primary, bool isconstraint); +extern List *ChooseIndexColumnNames(List *indexElems); extern Oid GetDefaultOpClass(Oid type_id, Oid am_id); /* commands/functioncmds.c */ diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 265c4e2a18..f93fff3892 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -13,7 +13,7 @@ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.419 2009/12/15 17:57:47 tgl Exp $ + * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.420 2009/12/23 02:35:23 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -501,6 +501,7 @@ typedef struct IndexElem NodeTag type; char *name; /* name of attribute to index, or NULL */ Node *expr; /* expression to index, or NULL */ + char *indexcolname; /* name for index column; NULL = default */ List *opclass; /* name of desired opclass; NIL = default */ SortByDir ordering; /* ASC/DESC/default */ SortByNulls nulls_ordering; /* FIRST/LAST/default */ diff --git a/src/include/parser/parse_target.h b/src/include/parser/parse_target.h index 88aeb31e2f..9b4202be00 100644 --- a/src/include/parser/parse_target.h +++ b/src/include/parser/parse_target.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/parser/parse_target.h,v 1.44 2009/01/01 17:24:00 momjian Exp $ + * $PostgreSQL: pgsql/src/include/parser/parse_target.h,v 1.45 2009/12/23 02:35:24 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -37,5 +37,6 @@ extern List *checkInsertTargets(ParseState *pstate, List *cols, extern TupleDesc expandRecordVariable(ParseState *pstate, Var *var, int levelsup); extern char *FigureColname(Node *node); +extern char *FigureIndexColname(Node *node); #endif /* PARSE_TARGET_H */ diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out index 20bf3de3ba..5aff44f23a 100644 --- a/src/test/regress/expected/alter_table.out +++ b/src/test/regress/expected/alter_table.out @@ -159,7 +159,7 @@ CREATE TABLE tmp2 (a int primary key); NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "tmp2_pkey" for table "tmp2" CREATE TABLE tmp3 (a int, b int); CREATE TABLE tmp4 (a int, b int, unique(a,b)); -NOTICE: CREATE TABLE / UNIQUE will create implicit index "tmp4_a_key" for table "tmp4" +NOTICE: CREATE TABLE / UNIQUE will create implicit index "tmp4_a_b_key" for table "tmp4" CREATE TABLE tmp5 (a int, b int); -- Insert rows into tmp2 (pktable) INSERT INTO tmp2 values (1); diff --git a/src/test/regress/expected/foreign_key.out b/src/test/regress/expected/foreign_key.out index 33e0edba03..0367f53233 100644 --- a/src/test/regress/expected/foreign_key.out +++ b/src/test/regress/expected/foreign_key.out @@ -736,7 +736,7 @@ ERROR: table "fktable_fail2" does not exist DROP TABLE PKTABLE; -- Test for referencing column number smaller than referenced constraint CREATE TABLE PKTABLE (ptest1 int, ptest2 int, UNIQUE(ptest1, ptest2)); -NOTICE: CREATE TABLE / UNIQUE will create implicit index "pktable_ptest1_key" for table "pktable" +NOTICE: CREATE TABLE / UNIQUE will create implicit index "pktable_ptest1_ptest2_key" for table "pktable" CREATE TABLE FKTABLE_FAIL1 (ftest1 int REFERENCES pktable(ptest1)); ERROR: there is no unique constraint matching given keys for referenced table "pktable" DROP TABLE FKTABLE_FAIL1; @@ -860,7 +860,7 @@ DETAIL: Key columns "ptest4" and "ptest1" are of incompatible types: inet and i create table pktable_base (base1 int not null); create table pktable (ptest1 int, primary key(base1), unique(base1, ptest1)) inherits (pktable_base); NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "pktable_pkey" for table "pktable" -NOTICE: CREATE TABLE / UNIQUE will create implicit index "pktable_base1_key" for table "pktable" +NOTICE: CREATE TABLE / UNIQUE will create implicit index "pktable_base1_ptest1_key" for table "pktable" create table fktable (ftest1 int references pktable(base1)); -- now some ins, upd, del insert into pktable(base1) values (1); @@ -1098,7 +1098,7 @@ CREATE TEMP TABLE pktable ( NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "pktable_pkey" for table "pktable" NOTICE: CREATE TABLE / UNIQUE will create implicit index "pktable_id2_key" for table "pktable" NOTICE: CREATE TABLE / UNIQUE will create implicit index "pktable_id3_key" for table "pktable" -NOTICE: CREATE TABLE / UNIQUE will create implicit index "pktable_id1_key" for table "pktable" +NOTICE: CREATE TABLE / UNIQUE will create implicit index "pktable_id1_id2_id3_key" for table "pktable" CREATE TEMP TABLE fktable ( x1 INT4 REFERENCES pktable(id1), x2 VARCHAR(4) REFERENCES pktable(id2), diff --git a/src/test/regress/expected/inherit.out b/src/test/regress/expected/inherit.out index aa1eb4a4dd..9c83a32f93 100644 --- a/src/test/regress/expected/inherit.out +++ b/src/test/regress/expected/inherit.out @@ -1031,8 +1031,8 @@ NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "t_all_pkey" for b | text | | extended | B Indexes: "t_all_pkey" PRIMARY KEY, btree (a) - "t_all_b_key" btree (b) - "t_all_key" btree ((a || b)) + "t_all_b_idx" btree (b) + "t_all_expr_idx" btree ((a || b)) Check constraints: "t1_a_check" CHECK (length(a) > 2) Has OIDs: no @@ -1040,7 +1040,7 @@ Has OIDs: no SELECT c.relname, objsubid, description FROM pg_description, pg_index i, pg_class c WHERE classoid = 'pg_class'::regclass AND objoid = i.indexrelid AND c.oid = i.indexrelid AND i.indrelid = 't_all'::regclass ORDER BY c.relname, objsubid; relname | objsubid | description -------------+----------+------------- - t_all_b_key | 0 | index b_key + t_all_b_idx | 0 | index b_key t_all_pkey | 0 | index pkey (2 rows) diff --git a/src/test/regress/output/constraints.source b/src/test/regress/output/constraints.source index 8928ca8beb..684394fd83 100644 --- a/src/test/regress/output/constraints.source +++ b/src/test/regress/output/constraints.source @@ -359,12 +359,12 @@ SELECT '' AS five, * FROM UNIQUE_TBL; DROP TABLE UNIQUE_TBL; CREATE TABLE UNIQUE_TBL (i int, t text, UNIQUE(i,t)); -NOTICE: CREATE TABLE / UNIQUE will create implicit index "unique_tbl_i_key" for table "unique_tbl" +NOTICE: CREATE TABLE / UNIQUE will create implicit index "unique_tbl_i_t_key" for table "unique_tbl" INSERT INTO UNIQUE_TBL VALUES (1, 'one'); INSERT INTO UNIQUE_TBL VALUES (2, 'two'); INSERT INTO UNIQUE_TBL VALUES (1, 'three'); INSERT INTO UNIQUE_TBL VALUES (1, 'one'); -ERROR: duplicate key value violates unique constraint "unique_tbl_i_key" +ERROR: duplicate key value violates unique constraint "unique_tbl_i_t_key" DETAIL: Key (i, t)=(1, one) already exists. INSERT INTO UNIQUE_TBL VALUES (5, 'one'); INSERT INTO UNIQUE_TBL (t) VALUES ('six'); @@ -523,7 +523,7 @@ CREATE TABLE circles ( (c1 WITH &&, (c2::circle) WITH ~=) WHERE (circle_center(c1) <> '(0,0)') ); -NOTICE: CREATE TABLE / EXCLUDE will create implicit index "circles_c1_exclusion" for table "circles" +NOTICE: CREATE TABLE / EXCLUDE will create implicit index "circles_c1_c2_exclusion" for table "circles" -- these should succeed because they don't match the index predicate INSERT INTO circles VALUES('<(0,0), 5>', '<(0,0), 5>'); INSERT INTO circles VALUES('<(0,0), 5>', '<(0,0), 5>'); @@ -531,7 +531,7 @@ INSERT INTO circles VALUES('<(0,0), 5>', '<(0,0), 5>'); INSERT INTO circles VALUES('<(10,10), 10>', '<(0,0), 5>'); -- fail, overlaps INSERT INTO circles VALUES('<(20,20), 10>', '<(0,0), 5>'); -ERROR: conflicting key value violates exclusion constraint "circles_c1_exclusion" +ERROR: conflicting key value violates exclusion constraint "circles_c1_c2_exclusion" DETAIL: Key (c1, (c2::circle))=(<(20,20),10>, <(0,0),5>) conflicts with existing key (c1, (c2::circle))=(<(10,10),10>, <(0,0),5>). -- succeed because c1 doesn't overlap INSERT INTO circles VALUES('<(20,20), 1>', '<(0,0), 5>'); @@ -540,8 +540,8 @@ INSERT INTO circles VALUES('<(20,20), 10>', '<(1,1), 5>'); -- should fail on existing data without the WHERE clause ALTER TABLE circles ADD EXCLUDE USING gist (c1 WITH &&, (c2::circle) WITH ~=); -NOTICE: ALTER TABLE / ADD EXCLUDE will create implicit index "circles_c1_exclusion1" for table "circles" -ERROR: could not create exclusion constraint "circles_c1_exclusion1" +NOTICE: ALTER TABLE / ADD EXCLUDE will create implicit index "circles_c1_c2_exclusion1" for table "circles" +ERROR: could not create exclusion constraint "circles_c1_c2_exclusion1" DETAIL: Key (c1, (c2::circle))=(<(0,0),5>, <(0,0),5>) conflicts with key (c1, (c2::circle))=(<(0,0),5>, <(0,0),5>). DROP TABLE circles; -- Check deferred exclusion constraint