index-naming-1.patch

text/x-patch

Filename: index-naming-1.patch
Type: text/x-patch
Part: 0
Message: Possible patch for better index name choosing

Patch

Same data as JSON: GET /api/v1/attachments/:id/patch the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes. API reference →
Format: context
File+
src/backend/bootstrap/bootparse.y 2 1
src/backend/catalog/index.c 4 2
src/backend/commands/indexcmds.c 69 20
src/backend/nodes/copyfuncs.c 2 1
src/backend/nodes/equalfuncs.c 2 1
src/backend/nodes/outfuncs.c 2 1
src/backend/parser/gram.y 6 3
src/backend/parser/parse_target.c 27 0
src/backend/parser/parse_utilcmd.c 45 19
src/include/commands/defrem.h 1 0
src/include/nodes/parsenodes.h 9 3
src/include/parser/parse_target.h 1 0
Index: src/backend/bootstrap/bootparse.y
===================================================================
RCS file: /cvsroot/pgsql/src/backend/bootstrap/bootparse.y,v
retrieving revision 1.101
diff -c -r1.101 bootparse.y
*** src/backend/bootstrap/bootparse.y	7 Dec 2009 05:22:21 -0000	1.101
--- src/backend/bootstrap/bootparse.y	21 Dec 2009 02:47:04 -0000
***************
*** 322,328 ****
  				{
  					IndexElem *n = makeNode(IndexElem);
  					n->name = $1;
! 					n->expr = NULL;
  					n->opclass = list_make1(makeString($2));
  					n->ordering = SORTBY_DEFAULT;
  					n->nulls_ordering = SORTBY_NULLS_DEFAULT;
--- 322,329 ----
  				{
  					IndexElem *n = makeNode(IndexElem);
  					n->name = $1;
! 					n->iexpr = NULL;
! 					n->iexprname = NULL;
  					n->opclass = list_make1(makeString($2));
  					n->ordering = SORTBY_DEFAULT;
  					n->nulls_ordering = SORTBY_NULLS_DEFAULT;
Index: src/backend/catalog/index.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/catalog/index.c,v
retrieving revision 1.326
diff -c -r1.326 index.c
*** src/backend/catalog/index.c	9 Dec 2009 21:57:50 -0000	1.326
--- src/backend/catalog/index.c	21 Dec 2009 02:47:04 -0000
***************
*** 217,224 ****
  			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);
  
--- 217,226 ----
  			indexpr_item = lnext(indexpr_item);
  
  			/*
! 			 * Make the attribute's name "pg_expression_nnn" (maybe think of
! 			 * something better later?  we're kind of locked in now, though,
! 			 * because pg_dump exposes these names when dumping comments on
! 			 * index columns)
  			 */
  			sprintf(NameStr(to->attname), "pg_expression_%d", i + 1);
  
Index: src/backend/commands/indexcmds.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/commands/indexcmds.c,v
retrieving revision 1.188
diff -c -r1.188 indexcmds.c
*** src/backend/commands/indexcmds.c	7 Dec 2009 05:22:21 -0000	1.188
--- src/backend/commands/indexcmds.c	21 Dec 2009 02:47:05 -0000
***************
*** 252,282 ****
  	 */
  	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);
  		}
  	}
  
  	/*
--- 252,288 ----
  	 */
  	if (indexRelationName == NULL)
  	{
+ 		char   *tabname = RelationGetRelationName(rel);
+ 
  		if (primary)
  		{
! 			/* the pkey name does not depend on the specific column(s) */
! 			indexRelationName = ChooseRelationName(tabname,
  												   NULL,
  												   "pkey",
  												   namespaceId);
  		}
  		else if (exclusionOpNames != NIL)
  		{
! 			indexRelationName = ChooseRelationName(tabname,
! 												   ChooseIndexNameAddition(attributeList),
  												   "exclusion",
  												   namespaceId);
  		}
! 		else if (isconstraint)
  		{
! 			indexRelationName = ChooseRelationName(tabname,
! 												   ChooseIndexNameAddition(attributeList),
  												   "key",
  												   namespaceId);
  		}
+ 		else
+ 		{
+ 			indexRelationName = ChooseRelationName(tabname,
+ 												   ChooseIndexNameAddition(attributeList),
+ 												   "idx",
+ 												   namespaceId);
+ 		}
  	}
  
  	/*
***************
*** 884,890 ****
  			HeapTuple	atttuple;
  			Form_pg_attribute attform;
  
! 			Assert(attribute->expr == NULL);
  			atttuple = SearchSysCacheAttName(relId, attribute->name);
  			if (!HeapTupleIsValid(atttuple))
  			{
--- 890,896 ----
  			HeapTuple	atttuple;
  			Form_pg_attribute attform;
  
! 			Assert(attribute->iexpr == NULL);
  			atttuple = SearchSysCacheAttName(relId, attribute->name);
  			if (!HeapTupleIsValid(atttuple))
  			{
***************
*** 905,915 ****
  			atttype = attform->atttypid;
  			ReleaseSysCache(atttuple);
  		}
! 		else if (attribute->expr && IsA(attribute->expr, Var) &&
! 				 ((Var *) attribute->expr)->varattno != InvalidAttrNumber)
  		{
  			/* Tricky tricky, he wrote (column) ... treat as simple attr */
! 			Var		   *var = (Var *) attribute->expr;
  
  			indexInfo->ii_KeyAttrNumbers[attn] = var->varattno;
  			atttype = get_atttype(relId, var->varattno);
--- 911,921 ----
  			atttype = attform->atttypid;
  			ReleaseSysCache(atttuple);
  		}
! 		else if (attribute->iexpr && IsA(attribute->iexpr, Var) &&
! 				 ((Var *) attribute->iexpr)->varattno != InvalidAttrNumber)
  		{
  			/* Tricky tricky, he wrote (column) ... treat as simple attr */
! 			Var		   *var = (Var *) attribute->iexpr;
  
  			indexInfo->ii_KeyAttrNumbers[attn] = var->varattno;
  			atttype = get_atttype(relId, var->varattno);
***************
*** 917,938 ****
  		else
  		{
  			/* Index expression */
! 			Assert(attribute->expr != NULL);
  			indexInfo->ii_KeyAttrNumbers[attn] = 0;		/* marks expression */
  			indexInfo->ii_Expressions = lappend(indexInfo->ii_Expressions,
! 												attribute->expr);
! 			atttype = exprType(attribute->expr);
  
  			/*
  			 * We don't currently support generation of an actual query plan
  			 * for an index expression, only simple scalar expressions; hence
  			 * these restrictions.
  			 */
! 			if (contain_subplans(attribute->expr))
  				ereport(ERROR,
  						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
  						 errmsg("cannot use subquery in index expression")));
! 			if (contain_agg_clause(attribute->expr))
  				ereport(ERROR,
  						(errcode(ERRCODE_GROUPING_ERROR),
  				errmsg("cannot use aggregate function in index expression")));
--- 923,944 ----
  		else
  		{
  			/* Index expression */
! 			Assert(attribute->iexpr != NULL);
  			indexInfo->ii_KeyAttrNumbers[attn] = 0;		/* marks expression */
  			indexInfo->ii_Expressions = lappend(indexInfo->ii_Expressions,
! 												attribute->iexpr);
! 			atttype = exprType(attribute->iexpr);
  
  			/*
  			 * We don't currently support generation of an actual query plan
  			 * for an index expression, only simple scalar expressions; hence
  			 * these restrictions.
  			 */
! 			if (contain_subplans(attribute->iexpr))
  				ereport(ERROR,
  						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
  						 errmsg("cannot use subquery in index expression")));
! 			if (contain_agg_clause(attribute->iexpr))
  				ereport(ERROR,
  						(errcode(ERRCODE_GROUPING_ERROR),
  				errmsg("cannot use aggregate function in index expression")));
***************
*** 942,948 ****
  			 * if you aren't going to get the same result for the same data
  			 * every time, it's not clear what the index entries mean at all.
  			 */
! 			if (contain_mutable_functions(attribute->expr))
  				ereport(ERROR,
  						(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
  						 errmsg("functions in index expression must be marked IMMUTABLE")));
--- 948,954 ----
  			 * if you aren't going to get the same result for the same data
  			 * every time, it's not clear what the index entries mean at all.
  			 */
! 			if (contain_mutable_functions(attribute->iexpr))
  				ereport(ERROR,
  						(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
  						 errmsg("functions in index expression must be marked IMMUTABLE")));
***************
*** 1392,1397 ****
--- 1398,1446 ----
  }
  
  /*
+  * Generate "name2" for a new index given the list of IndexElems for it.
+  * 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.
+  */
+ char *
+ ChooseIndexNameAddition(List *indexElems)
+ {
+ 	char		buf[NAMEDATALEN * 2];
+ 	int			buflen = 0;
+ 	ListCell   *lc;
+ 
+ 	buf[0] = '\0';
+ 	foreach(lc, indexElems)
+ 	{
+ 		IndexElem  *ielem = (IndexElem *) lfirst(lc);
+ 		const char *name;
+ 
+ 		if (ielem->name)
+ 			name = ielem->name;				/* simple column reference */
+ 		else if (ielem->iexprname)
+ 			name = ielem->iexprname;		/* expression */
+ 		else
+ 			name = "expr";					/* default name for expression */
+ 
+ 		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);
+ }
+ 
+ /*
   * relationHasPrimaryKey -
   *
   *	See whether an existing relation has a primary key.
Index: src/backend/nodes/copyfuncs.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v
retrieving revision 1.454
diff -c -r1.454 copyfuncs.c
*** src/backend/nodes/copyfuncs.c	15 Dec 2009 17:57:46 -0000	1.454
--- src/backend/nodes/copyfuncs.c	21 Dec 2009 02:47:05 -0000
***************
*** 2120,2126 ****
  	IndexElem  *newnode = makeNode(IndexElem);
  
  	COPY_STRING_FIELD(name);
! 	COPY_NODE_FIELD(expr);
  	COPY_NODE_FIELD(opclass);
  	COPY_SCALAR_FIELD(ordering);
  	COPY_SCALAR_FIELD(nulls_ordering);
--- 2120,2127 ----
  	IndexElem  *newnode = makeNode(IndexElem);
  
  	COPY_STRING_FIELD(name);
! 	COPY_NODE_FIELD(iexpr);
! 	COPY_STRING_FIELD(iexprname);
  	COPY_NODE_FIELD(opclass);
  	COPY_SCALAR_FIELD(ordering);
  	COPY_SCALAR_FIELD(nulls_ordering);
Index: src/backend/nodes/equalfuncs.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v
retrieving revision 1.376
diff -c -r1.376 equalfuncs.c
*** src/backend/nodes/equalfuncs.c	15 Dec 2009 17:57:46 -0000	1.376
--- src/backend/nodes/equalfuncs.c	21 Dec 2009 02:47:05 -0000
***************
*** 2071,2077 ****
  _equalIndexElem(IndexElem *a, IndexElem *b)
  {
  	COMPARE_STRING_FIELD(name);
! 	COMPARE_NODE_FIELD(expr);
  	COMPARE_NODE_FIELD(opclass);
  	COMPARE_SCALAR_FIELD(ordering);
  	COMPARE_SCALAR_FIELD(nulls_ordering);
--- 2071,2078 ----
  _equalIndexElem(IndexElem *a, IndexElem *b)
  {
  	COMPARE_STRING_FIELD(name);
! 	COMPARE_NODE_FIELD(iexpr);
! 	COMPARE_STRING_FIELD(iexprname);
  	COMPARE_NODE_FIELD(opclass);
  	COMPARE_SCALAR_FIELD(ordering);
  	COMPARE_SCALAR_FIELD(nulls_ordering);
Index: src/backend/nodes/outfuncs.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v
retrieving revision 1.375
diff -c -r1.375 outfuncs.c
*** src/backend/nodes/outfuncs.c	15 Dec 2009 17:57:46 -0000	1.375
--- src/backend/nodes/outfuncs.c	21 Dec 2009 02:47:05 -0000
***************
*** 1945,1951 ****
  	WRITE_NODE_TYPE("INDEXELEM");
  
  	WRITE_STRING_FIELD(name);
! 	WRITE_NODE_FIELD(expr);
  	WRITE_NODE_FIELD(opclass);
  	WRITE_ENUM_FIELD(ordering, SortByDir);
  	WRITE_ENUM_FIELD(nulls_ordering, SortByNulls);
--- 1945,1952 ----
  	WRITE_NODE_TYPE("INDEXELEM");
  
  	WRITE_STRING_FIELD(name);
! 	WRITE_NODE_FIELD(iexpr);
! 	WRITE_STRING_FIELD(iexprname);
  	WRITE_NODE_FIELD(opclass);
  	WRITE_ENUM_FIELD(ordering, SortByDir);
  	WRITE_ENUM_FIELD(nulls_ordering, SortByNulls);
Index: src/backend/parser/gram.y
===================================================================
RCS file: /cvsroot/pgsql/src/backend/parser/gram.y,v
retrieving revision 2.697
diff -c -r2.697 gram.y
*** src/backend/parser/gram.y	15 Dec 2009 17:57:47 -0000	2.697
--- src/backend/parser/gram.y	21 Dec 2009 02:47:05 -0000
***************
*** 4886,4892 ****
  				{
  					$$ = makeNode(IndexElem);
  					$$->name = $1;
! 					$$->expr = NULL;
  					$$->opclass = $2;
  					$$->ordering = $3;
  					$$->nulls_ordering = $4;
--- 4886,4893 ----
  				{
  					$$ = makeNode(IndexElem);
  					$$->name = $1;
! 					$$->iexpr = NULL;
! 					$$->iexprname = NULL;
  					$$->opclass = $2;
  					$$->ordering = $3;
  					$$->nulls_ordering = $4;
***************
*** 4895,4901 ****
  				{
  					$$ = makeNode(IndexElem);
  					$$->name = NULL;
! 					$$->expr = $1;
  					$$->opclass = $2;
  					$$->ordering = $3;
  					$$->nulls_ordering = $4;
--- 4896,4903 ----
  				{
  					$$ = makeNode(IndexElem);
  					$$->name = NULL;
! 					$$->iexpr = $1;
! 					$$->iexprname = NULL;
  					$$->opclass = $2;
  					$$->ordering = $3;
  					$$->nulls_ordering = $4;
***************
*** 4904,4910 ****
  				{
  					$$ = makeNode(IndexElem);
  					$$->name = NULL;
! 					$$->expr = $2;
  					$$->opclass = $4;
  					$$->ordering = $5;
  					$$->nulls_ordering = $6;
--- 4906,4913 ----
  				{
  					$$ = makeNode(IndexElem);
  					$$->name = NULL;
! 					$$->iexpr = $2;
! 					$$->iexprname = NULL;
  					$$->opclass = $4;
  					$$->ordering = $5;
  					$$->nulls_ordering = $6;
Index: src/backend/parser/parse_target.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/parser/parse_target.c,v
retrieving revision 1.174
diff -c -r1.174 parse_target.c
*** src/backend/parser/parse_target.c	31 Oct 2009 01:41:31 -0000	1.174
--- src/backend/parser/parse_target.c	21 Dec 2009 02:47:05 -0000
***************
*** 1417,1422 ****
--- 1417,1449 ----
  	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;
+ 
+ 	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)
  {
Index: src/backend/parser/parse_utilcmd.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/parser/parse_utilcmd.c,v
retrieving revision 2.33
diff -c -r2.33 parse_utilcmd.c
*** src/backend/parser/parse_utilcmd.c	20 Dec 2009 18:28:14 -0000	2.33
--- src/backend/parser/parse_utilcmd.c	21 Dec 2009 02:47:05 -0000
***************
*** 48,53 ****
--- 48,54 ----
  #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"
***************
*** 750,756 ****
  				int				colno;
  
  				comment = GetComment(parent_index_oid, RelationRelationId, 0);
! 				
  				if (comment != NULL)
  				{
  					/*
--- 751,757 ----
  				int				colno;
  
  				comment = GetComment(parent_index_oid, RelationRelationId, 0);
! 
  				if (comment != NULL)
  				{
  					/*
***************
*** 827,858 ****
   * chooseIndexName
   *
   * Set name for unnamed index. See also the same logic in DefineIndex.
   */
  static char *
  chooseIndexName(const RangeVar *relation, IndexStmt *index_stmt)
  {
  	Oid	namespaceId;
! 	
  	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);
  	}
  }
  
--- 828,872 ----
   * chooseIndexName
   *
   * Set name for unnamed index. See also the same logic in DefineIndex.
+  *
+  * 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.
   */
  static char *
  chooseIndexName(const RangeVar *relation, IndexStmt *index_stmt)
  {
  	Oid	namespaceId;
! 
  	namespaceId = RangeVarGetCreationNamespace(relation);
  	if (index_stmt->primary)
  	{
! 		/* the pkey name does not depend on the specific column(s) */
! 		return ChooseRelationName(relation->relname,
! 								  NULL,
! 								  "pkey",
! 								  namespaceId);
  	}
  	else if (index_stmt->excludeOpNames != NIL)
  	{
! 		return ChooseRelationName(relation->relname,
! 								  ChooseIndexNameAddition(index_stmt->indexParams),
! 								  "exclusion",
! 								  namespaceId);
! 	}
! 	else if (index_stmt->isconstraint)
! 	{
! 		return ChooseRelationName(relation->relname,
! 								  ChooseIndexNameAddition(index_stmt->indexParams),
! 								  "key",
! 								  namespaceId);
  	}
  	else
  	{
! 		return ChooseRelationName(relation->relname,
! 								  ChooseIndexNameAddition(index_stmt->indexParams),
! 								  "idx",
! 								  namespaceId);
  	}
  }
  
***************
*** 1039,1045 ****
  			keycoltype = get_atttype(indrelid, attnum);
  
  			iparam->name = attname;
! 			iparam->expr = NULL;
  		}
  		else
  		{
--- 1053,1060 ----
  			keycoltype = get_atttype(indrelid, attnum);
  
  			iparam->name = attname;
! 			iparam->iexpr = NULL;
! 			iparam->iexprname = NULL;
  		}
  		else
  		{
***************
*** 1055,1061 ****
  			change_varattnos_of_a_node(indexkey, attmap);
  
  			iparam->name = NULL;
! 			iparam->expr = indexkey;
  
  			keycoltype = exprType(indexkey);
  		}
--- 1070,1082 ----
  			change_varattnos_of_a_node(indexkey, attmap);
  
  			iparam->name = NULL;
! 			iparam->iexpr = indexkey;
! 
! 			/*
! 			 * Unfortunately, we have no good way to extract a name from
! 			 * the already-transformed index expression; so punt.
! 			 */
! 			iparam->iexprname = NULL;
  
  			keycoltype = exprType(indexkey);
  		}
***************
*** 1452,1458 ****
  		/* OK, add it to the index definition */
  		iparam = makeNode(IndexElem);
  		iparam->name = pstrdup(key);
! 		iparam->expr = NULL;
  		iparam->opclass = NIL;
  		iparam->ordering = SORTBY_DEFAULT;
  		iparam->nulls_ordering = SORTBY_NULLS_DEFAULT;
--- 1473,1480 ----
  		/* OK, add it to the index definition */
  		iparam = makeNode(IndexElem);
  		iparam->name = pstrdup(key);
! 		iparam->iexpr = NULL;
! 		iparam->iexprname = NULL;
  		iparam->opclass = NIL;
  		iparam->ordering = SORTBY_DEFAULT;
  		iparam->nulls_ordering = SORTBY_NULLS_DEFAULT;
***************
*** 1579,1594 ****
  	{
  		IndexElem  *ielem = (IndexElem *) lfirst(l);
  
! 		if (ielem->expr)
  		{
! 			ielem->expr = transformExpr(pstate, ielem->expr);
  
  			/*
  			 * We check only that the result type is legitimate; this is for
  			 * consistency with what transformWhereClause() checks for the
  			 * predicate.  DefineIndex() will make more checks.
  			 */
! 			if (expression_returns_set(ielem->expr))
  				ereport(ERROR,
  						(errcode(ERRCODE_DATATYPE_MISMATCH),
  						 errmsg("index expression cannot return a set")));
--- 1601,1620 ----
  	{
  		IndexElem  *ielem = (IndexElem *) lfirst(l);
  
! 		if (ielem->iexpr)
  		{
! 			/* Extract index column name before transforming expr */
! 			ielem->iexprname = FigureIndexColname(ielem->iexpr);
! 
! 			/* Now do parse transformation of the expression */
! 			ielem->iexpr = transformExpr(pstate, ielem->iexpr);
  
  			/*
  			 * We check only that the result type is legitimate; this is for
  			 * consistency with what transformWhereClause() checks for the
  			 * predicate.  DefineIndex() will make more checks.
  			 */
! 			if (expression_returns_set(ielem->iexpr))
  				ereport(ERROR,
  						(errcode(ERRCODE_DATATYPE_MISMATCH),
  						 errmsg("index expression cannot return a set")));
Index: src/include/commands/defrem.h
===================================================================
RCS file: /cvsroot/pgsql/src/include/commands/defrem.h,v
retrieving revision 1.98
diff -c -r1.98 defrem.h
*** src/include/commands/defrem.h	7 Dec 2009 05:22:23 -0000	1.98
--- src/include/commands/defrem.h	21 Dec 2009 02:47:05 -0000
***************
*** 45,50 ****
--- 45,51 ----
  			   const char *label);
  extern char *ChooseRelationName(const char *name1, const char *name2,
  				   const char *label, Oid namespaceid);
+ extern char *ChooseIndexNameAddition(List *indexElems);
  extern Oid	GetDefaultOpClass(Oid type_id, Oid am_id);
  
  /* commands/functioncmds.c */
Index: src/include/nodes/parsenodes.h
===================================================================
RCS file: /cvsroot/pgsql/src/include/nodes/parsenodes.h,v
retrieving revision 1.419
diff -c -r1.419 parsenodes.h
*** src/include/nodes/parsenodes.h	15 Dec 2009 17:57:47 -0000	1.419
--- src/include/nodes/parsenodes.h	21 Dec 2009 02:47:06 -0000
***************
*** 493,506 ****
   * IndexElem - index parameters (used in CREATE INDEX)
   *
   * For a plain index attribute, 'name' is the name of the table column to
!  * index, and 'expr' is NULL.  For an index expression, 'name' is NULL and
!  * 'expr' is the expression tree.
   */
  typedef struct IndexElem
  {
  	NodeTag		type;
  	char	   *name;			/* name of attribute to index, or NULL */
! 	Node	   *expr;			/* expression to index, or NULL */
  	List	   *opclass;		/* name of desired opclass; NIL = default */
  	SortByDir	ordering;		/* ASC/DESC/default */
  	SortByNulls nulls_ordering; /* FIRST/LAST/default */
--- 493,512 ----
   * IndexElem - index parameters (used in CREATE INDEX)
   *
   * For a plain index attribute, 'name' is the name of the table column to
!  * index, and 'iexpr' is NULL.  For an index expression, 'name' is NULL and
!  * 'iexpr' is the expression tree.
!  *
!  * 'iexprname' is a name extracted from 'iexpr', or NULL for none/default.
!  * We need this field because the FigureColname machinery only works on
!  * not-yet-transformed expression trees, so we can't get it from iexpr
!  * after parse transformation.
   */
  typedef struct IndexElem
  {
  	NodeTag		type;
  	char	   *name;			/* name of attribute to index, or NULL */
! 	Node	   *iexpr;			/* expression to index, or NULL */
! 	char	   *iexprname;		/* name extracted from iexpr, or NULL */
  	List	   *opclass;		/* name of desired opclass; NIL = default */
  	SortByDir	ordering;		/* ASC/DESC/default */
  	SortByNulls nulls_ordering; /* FIRST/LAST/default */
Index: src/include/parser/parse_target.h
===================================================================
RCS file: /cvsroot/pgsql/src/include/parser/parse_target.h,v
retrieving revision 1.44
diff -c -r1.44 parse_target.h
*** src/include/parser/parse_target.h	1 Jan 2009 17:24:00 -0000	1.44
--- src/include/parser/parse_target.h	21 Dec 2009 02:47:06 -0000
***************
*** 37,41 ****
--- 37,42 ----
  extern TupleDesc expandRecordVariable(ParseState *pstate, Var *var,
  					 int levelsup);
  extern char *FigureColname(Node *node);
+ extern char *FigureIndexColname(Node *node);
  
  #endif   /* PARSE_TARGET_H */