diff --git a/src/backend/bootstrap/bootparse.y b/src/backend/bootstrap/bootparse.y
index 18affeb098858f141f39a2cc15e9fbac2f54b61c..5c85fcff5ef4a24fa29c0cce765193aca36460d5 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 5394d2557eb03e8518a19f8ca0539f289e7032a0..9eb96b7718d189aae1f6576dbb0e10bee1208af0 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 05f3bb49691f83a4733bf359d5bae43a3dcf886c..a8c2da66dc3ca764086f71d3f895f76069be0a9d 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 00786442b3f35472332505634c27979d62cf3b4a..833114abcf0b08a111beffd0f6ff3e8f12a5bc5b 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 018f36cef60e9bcdedfa3603e651f756b38a5357..2722a93e074efb3e7b0c29c06b373df8a6159e8a 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 2387aaba410b582b34e0062f48c026cdcd40b4eb..e53b4a89e6e4eae5eef94fcff7a7261f55f3d5dc 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 cac346464bd15b998e4b56e6fb34a7271a521d07..419c005b5087c64446a89a82a541ef84e6caddda 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 22449579d0f7372bc2fa8fdf5ddee02e14a4ccb6..7ff46e05cb1c654bf39a2d8fc27dc789f3cebf12 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 ce3f51ca6e5c0009ca054b6c4432506236f062ac..007a3cc6936dba53b029ab211cd47b6b53411717 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 75c8d863dcd8dd6c73983ee88b33018dc84b64ac..f09e78dd8e3ddbaf8d2025ccb52c6159231050ec 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 a432260058f1308ca11fb878ecc11be9b0f6f223..f2119a530f9568a0615667a14ad05111e7c43750 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 1b665ff8550ba257cc4a78077d945f7b9cbb4652..2ac7e160fe837fac1b53dbbbf31738257f29e871 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 265c4e2a18acc9296b0b75c04748f91cf2e5ce6c..f93fff3892b729ae3566a58408983f66772e71cb 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 88aeb31e2f928050bbc7075349f5831ad0fe74d0..9b4202be003985504d201bfec03af5b9590f4e07 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 20bf3de3bad2580433990051b2f894f31b3207e9..5aff44f23aa8539f868324b052be3fe3344f5d7f 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 33e0edba03a351ea5baffca3f982a9425db11efc..0367f532337366df79701d1b963c32b43d9e014d 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 aa1eb4a4ddbfa3f8fbae038b70d39327576caeab..9c83a32f9344f5eec3f91ca07f9737f2165d7f29 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 8928ca8beba7b0683bdf53830e12b819729ce1df..684394fd83d2a624e1c5ca5d4c454f05f9c8c251 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