* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/backend/parser/parse_utilcmd.c,v 2.1 2007/06/23 22:12:51 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/parser/parse_utilcmd.c,v 2.2 2007/07/17 05:02:02 neilc Exp $
  *
  *-------------------------------------------------------------------------
  */
 
 #include "postgres.h"
 
+#include "access/genam.h"
 #include "access/heapam.h"
 #include "catalog/heap.h"
 #include "catalog/index.h"
 #include "catalog/namespace.h"
+#include "catalog/pg_opclass.h"
 #include "catalog/pg_type.h"
 #include "commands/defrem.h"
 #include "commands/tablecmds.h"
+#include "commands/tablespace.h"
 #include "miscadmin.h"
 #include "nodes/makefuncs.h"
 #include "optimizer/clauses.h"
 #include "utils/acl.h"
 #include "utils/builtins.h"
 #include "utils/lsyscache.h"
+#include "utils/relcache.h"
 #include "utils/syscache.h"
 
 
    List       *ckconstraints;  /* CHECK constraints */
    List       *fkconstraints;  /* FOREIGN KEY constraints */
    List       *ixconstraints;  /* index-creating constraints */
+   List       *inh_indexes;    /* cloned indexes from INCLUDING INDEXES */
    List       *blist;          /* "before list" of things to do before
                                 * creating the table */
    List       *alist;          /* "after list" of things to do after creating
                         Constraint *constraint);
 static void transformInhRelation(ParseState *pstate, CreateStmtContext *cxt,
                     InhRelation *inhrelation);
+static IndexStmt *generateClonedIndexStmt(CreateStmtContext *cxt, 
+                           Relation parent_index, AttrNumber *attmap);
+static List *get_opclass(Oid opclass, Oid actual_datatype);
 static void transformIndexConstraints(ParseState *pstate,
                          CreateStmtContext *cxt);
+static IndexStmt *transformIndexConstraint(Constraint *constraint,
+                                          CreateStmtContext *cxt);
 static void transformFKConstraints(ParseState *pstate,
                       CreateStmtContext *cxt,
                       bool skipValidation,
    cxt.ckconstraints = NIL;
    cxt.fkconstraints = NIL;
    cxt.ixconstraints = NIL;
+   cxt.inh_indexes = NIL;
    cxt.blist = NIL;
    cxt.alist = NIL;
    cxt.pkey = NULL;
        }
    }
 
-   if (including_indexes)
-       ereport(ERROR,
-               (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-                errmsg("LIKE INCLUDING INDEXES is not implemented")));
-
    /*
     * Insert the copied attributes into the cxt for the new table
     * definition.
        }
    }
 
+   if (including_indexes && relation->rd_rel->relhasindex)
+   {
+       AttrNumber *attmap;
+       List       *parent_indexes;
+       ListCell   *l;
+
+       attmap = varattnos_map_schema(tupleDesc, cxt->columns);
+       parent_indexes = RelationGetIndexList(relation);
+
+       foreach(l, parent_indexes)
+       {
+           Oid          parent_index_oid = lfirst_oid(l);
+           Relation     parent_index;
+           IndexStmt   *index_stmt;
+
+           parent_index = index_open(parent_index_oid, AccessShareLock);
+
+           /* Build CREATE INDEX statement to recreate the parent_index */
+           index_stmt = generateClonedIndexStmt(cxt, parent_index,
+                                                attmap);
+
+           /* Add the new IndexStmt to the create context */
+           cxt->inh_indexes = lappend(cxt->inh_indexes, index_stmt);
+
+           /* Keep our lock on the index till xact commit */
+           index_close(parent_index, NoLock);
+       }
+   }
+
    /*
     * Close the parent rel, but keep our AccessShareLock on it until xact
     * commit.  That will prevent someone else from deleting or ALTERing the
 }
 
 /*
- * transformIndexConstraints
- *     Handle UNIQUE and PRIMARY KEY constraints, which create indexes
+ * Generate an IndexStmt entry using information from an already
+ * existing index "source_idx".
+ *
+ * Note: Much of this functionality is cribbed from pg_get_indexdef.
  */
-static void
-transformIndexConstraints(ParseState *pstate, CreateStmtContext *cxt)
+static IndexStmt *
+generateClonedIndexStmt(CreateStmtContext *cxt, Relation source_idx,
+                       AttrNumber *attmap)
 {
-   IndexStmt  *index;
-   List       *indexlist = NIL;
-   ListCell   *listptr;
-   ListCell   *l;
+   HeapTuple            ht_idx;
+   HeapTuple            ht_idxrel;
+   HeapTuple            ht_am;
+   Form_pg_index        idxrec;
+   Form_pg_class        idxrelrec;
+   Form_pg_am           amrec;
+   List                *indexprs = NIL;
+   ListCell            *indexpr_item;
+   Oid                  indrelid;
+   Oid                  source_relid;
+   int                  keyno;
+   Oid                  keycoltype;
+   Datum                indclassDatum;
+   Datum                indoptionDatum;
+   bool                 isnull;
+   oidvector           *indclass;
+   int2vector          *indoption;
+   IndexStmt           *index;
+   Datum                reloptions;
+
+   source_relid = RelationGetRelid(source_idx);
+
+   /* Fetch pg_index tuple for source index */
+   ht_idx = SearchSysCache(INDEXRELID,
+                           ObjectIdGetDatum(source_relid),
+                           0, 0, 0);
+   if (!HeapTupleIsValid(ht_idx))
+       elog(ERROR, "cache lookup failed for index %u", source_relid);
+   idxrec = (Form_pg_index) GETSTRUCT(ht_idx);
+
+   Assert(source_relid == idxrec->indexrelid);
+   indrelid = idxrec->indrelid;
+
+   index = makeNode(IndexStmt);
+   index->unique = idxrec->indisunique;
+   index->concurrent = false;
+   index->primary = idxrec->indisprimary;
+   index->relation = cxt->relation;
+   index->isconstraint = false;
 
    /*
-    * Run through the constraints that need to generate an index. For PRIMARY
-    * KEY, mark each column as NOT NULL and create an index. For UNIQUE,
-    * create an index as for PRIMARY KEY, but do not insist on NOT NULL.
+    * We don't try to preserve the name of the source index; instead, just
+    * let DefineIndex() choose a reasonable name.
+    */
+   index->idxname = NULL;
+
+   /* Must get indclass and indoption the hard way */
+   indclassDatum = SysCacheGetAttr(INDEXRELID, ht_idx,
+                                   Anum_pg_index_indclass, &isnull);
+   Assert(!isnull);
+   indclass = (oidvector *) DatumGetPointer(indclassDatum);
+   indoptionDatum = SysCacheGetAttr(INDEXRELID, ht_idx,
+                                    Anum_pg_index_indoption, &isnull);
+   Assert(!isnull);
+   indoption = (int2vector *) DatumGetPointer(indoptionDatum);
+
+   /* Fetch pg_class tuple of source index */
+   ht_idxrel = SearchSysCache(RELOID,
+                              ObjectIdGetDatum(source_relid),
+                              0, 0, 0);
+   if (!HeapTupleIsValid(ht_idxrel))
+       elog(ERROR, "cache lookup failed for relation %u", source_relid);
+
+   /*
+    * Store the reloptions for later use by this new index
     */
-   foreach(listptr, cxt->ixconstraints)
+   reloptions = SysCacheGetAttr(RELOID, ht_idxrel,
+                                Anum_pg_class_reloptions, &isnull);
+   if (!isnull)
+       index->src_options = flatten_reloptions(source_relid);
+
+   idxrelrec = (Form_pg_class) GETSTRUCT(ht_idxrel);
+
+   /* Fetch pg_am tuple for the index's access method */
+   ht_am = SearchSysCache(AMOID,
+                          ObjectIdGetDatum(idxrelrec->relam),
+                          0, 0, 0);
+   if (!HeapTupleIsValid(ht_am))
+       elog(ERROR, "cache lookup failed for access method %u",
+            idxrelrec->relam);
+   amrec = (Form_pg_am) GETSTRUCT(ht_am);
+   index->accessMethod = pstrdup(NameStr(amrec->amname));
+
+   /* Get the index expressions, if any */
+   if (!heap_attisnull(ht_idx, Anum_pg_index_indexprs))
    {
-       Constraint *constraint = lfirst(listptr);
-       ListCell   *keys;
-       IndexElem  *iparam;
+       Datum       exprsDatum;
+       bool        isnull;
+       char       *exprsString;
+
+       exprsDatum = SysCacheGetAttr(INDEXRELID, ht_idx,
+                                    Anum_pg_index_indexprs, &isnull);
+       exprsString = DatumGetCString(DirectFunctionCall1(textout,
+                                                         exprsDatum));
+       Assert(!isnull);
+       indexprs = (List *) stringToNode(exprsString);
+   }
 
-       Assert(IsA(constraint, Constraint));
-       Assert((constraint->contype == CONSTR_PRIMARY)
-              || (constraint->contype == CONSTR_UNIQUE));
+   indexpr_item = list_head(indexprs);
 
-       index = makeNode(IndexStmt);
+   for (keyno = 0; keyno < idxrec->indnatts; keyno++)
+   {
+       IndexElem   *iparam;
+       AttrNumber  attnum = idxrec->indkey.values[keyno];
+       int16       opt = indoption->values[keyno];
 
-       index->unique = true;
-       index->primary = (constraint->contype == CONSTR_PRIMARY);
-       if (index->primary)
+       iparam = makeNode(IndexElem);
+
+       if (AttributeNumberIsValid(attnum))
        {
-           if (cxt->pkey != NULL)
-               ereport(ERROR,
-                       (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
-                        errmsg("multiple primary keys for table \"%s\" are not allowed",
-                               cxt->relation->relname)));
-           cxt->pkey = index;
+           /* Simple index column */
+           char       *attname;
 
-           /*
-            * In ALTER TABLE case, a primary index might already exist, but
-            * DefineIndex will check for it.
-            */
-       }
-       index->isconstraint = true;
+           attname = get_relid_attribute_name(indrelid, attnum);
+           keycoltype = get_atttype(indrelid, attnum);
 
-       if (constraint->name != NULL)
-           index->idxname = pstrdup(constraint->name);
+           iparam->name = attname;
+           iparam->expr = NULL;
+       }
        else
-           index->idxname = NULL;      /* DefineIndex will choose name */
+       {
+           /* Expressional index */
+           Node       *indexkey;
+
+           if (indexpr_item == NULL)
+               elog(ERROR, "too few entries in indexprs list");
+           indexkey = (Node *) lfirst(indexpr_item);
+           change_varattnos_of_a_node(indexkey, attmap);
+           iparam->name = NULL;
+           iparam->expr = indexkey;
+
+           indexpr_item = lnext(indexpr_item);
+           keycoltype = exprType(indexkey);
+       }
 
-       index->relation = cxt->relation;
-       index->accessMethod = DEFAULT_INDEX_TYPE;
-       index->options = constraint->options;
-       index->tableSpace = constraint->indexspace;
-       index->indexParams = NIL;
-       index->whereClause = NULL;
-       index->concurrent = false;
+       /* Add the operator class name, if non-default */
+       iparam->opclass = get_opclass(indclass->values[keyno], keycoltype);
 
-       /*
-        * Make sure referenced keys exist.  If we are making a PRIMARY KEY
-        * index, also make sure they are NOT NULL, if possible. (Although we
-        * could leave it to DefineIndex to mark the columns NOT NULL, it's
-        * more efficient to get it right the first time.)
-        */
-       foreach(keys, constraint->keys)
+       iparam->ordering = SORTBY_DEFAULT;
+       iparam->nulls_ordering = SORTBY_NULLS_DEFAULT;
+
+       /* Adjust options if necessary */
+       if (amrec->amcanorder)
        {
-           char       *key = strVal(lfirst(keys));
-           bool        found = false;
-           ColumnDef  *column = NULL;
-           ListCell   *columns;
+           /* If it supports sort ordering, report DESC and NULLS opts */
+           if (opt & INDOPTION_DESC)
+               iparam->ordering = SORTBY_DESC;
+           if (opt & INDOPTION_NULLS_FIRST)
+               iparam->nulls_ordering = SORTBY_NULLS_FIRST;
+       }
 
-           foreach(columns, cxt->columns)
-           {
-               column = (ColumnDef *) lfirst(columns);
-               Assert(IsA(column, ColumnDef));
-               if (strcmp(column->colname, key) == 0)
-               {
-                   found = true;
-                   break;
-               }
-           }
-           if (found)
-           {
-               /* found column in the new table; force it to be NOT NULL */
-               if (constraint->contype == CONSTR_PRIMARY)
-                   column->is_not_null = TRUE;
-           }
-           else if (SystemAttributeByName(key, cxt->hasoids) != NULL)
-           {
-               /*
-                * column will be a system column in the new table, so accept
-                * it.  System columns can't ever be null, so no need to worry
-                * about PRIMARY/NOT NULL constraint.
-                */
-               found = true;
-           }
-           else if (cxt->inhRelations)
-           {
-               /* try inherited tables */
-               ListCell   *inher;
+       index->indexParams = lappend(index->indexParams, iparam);
+   }
 
-               foreach(inher, cxt->inhRelations)
-               {
-                   RangeVar   *inh = (RangeVar *) lfirst(inher);
-                   Relation    rel;
-                   int         count;
+   /* Use the same tablespace as the source index */
+   index->tableSpace = get_tablespace_name(source_idx->rd_node.spcNode);
 
-                   Assert(IsA(inh, RangeVar));
-                   rel = heap_openrv(inh, AccessShareLock);
-                   if (rel->rd_rel->relkind != RELKIND_RELATION)
-                       ereport(ERROR,
-                               (errcode(ERRCODE_WRONG_OBJECT_TYPE),
-                          errmsg("inherited relation \"%s\" is not a table",
-                                 inh->relname)));
-                   for (count = 0; count < rel->rd_att->natts; count++)
-                   {
-                       Form_pg_attribute inhattr = rel->rd_att->attrs[count];
-                       char       *inhname = NameStr(inhattr->attname);
-
-                       if (inhattr->attisdropped)
-                           continue;
-                       if (strcmp(key, inhname) == 0)
-                       {
-                           found = true;
-
-                           /*
-                            * We currently have no easy way to force an
-                            * inherited column to be NOT NULL at creation, if
-                            * its parent wasn't so already. We leave it to
-                            * DefineIndex to fix things up in this case.
-                            */
-                           break;
-                       }
-                   }
-                   heap_close(rel, NoLock);
-                   if (found)
-                       break;
-               }
-           }
+   /* If it's a partial index, decompile and append the predicate */
+   if (!heap_attisnull(ht_idx, Anum_pg_index_indpred))
+   {
+       Datum       pred_datum;
+       bool        isnull;
+       char       *pred_str;
+
+       /* Convert text string to node tree */
+       pred_datum = SysCacheGetAttr(INDEXRELID, ht_idx,
+                                    Anum_pg_index_indpred, &isnull);
+       Assert(!isnull);
+       pred_str = DatumGetCString(DirectFunctionCall1(textout,
+                                                      pred_datum));
+       index->whereClause = (Node *) stringToNode(pred_str);
+       change_varattnos_of_a_node(index->whereClause, attmap);
+   }
 
-           /*
-            * In the ALTER TABLE case, don't complain about index keys not
-            * created in the command; they may well exist already.
-            * DefineIndex will complain about them if not, and will also take
-            * care of marking them NOT NULL.
-            */
-           if (!found && !cxt->isalter)
-               ereport(ERROR,
-                       (errcode(ERRCODE_UNDEFINED_COLUMN),
-                        errmsg("column \"%s\" named in key does not exist",
-                               key)));
+   /* Clean up */
+   ReleaseSysCache(ht_idx);
+   ReleaseSysCache(ht_idxrel);
+   ReleaseSysCache(ht_am);
 
-           /* Check for PRIMARY KEY(foo, foo) */
-           foreach(columns, index->indexParams)
-           {
-               iparam = (IndexElem *) lfirst(columns);
-               if (iparam->name && strcmp(key, iparam->name) == 0)
-               {
-                   if (index->primary)
-                       ereport(ERROR,
-                               (errcode(ERRCODE_DUPLICATE_COLUMN),
-                                errmsg("column \"%s\" appears twice in primary key constraint",
-                                       key)));
-                   else
-                       ereport(ERROR,
-                               (errcode(ERRCODE_DUPLICATE_COLUMN),
-                                errmsg("column \"%s\" appears twice in unique constraint",
-                                       key)));
-               }
-           }
+   return index;
+}
 
-           /* 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;
-           index->indexParams = lappend(index->indexParams, iparam);
-       }
+/*
+ * get_opclass         - fetch name of an index operator class
+ *
+ * If the opclass is the default for the given actual_datatype, then
+ * the return value is NIL.
+ */
+static List *
+get_opclass(Oid opclass, Oid actual_datatype)
+{
+   HeapTuple            ht_opc;
+   Form_pg_opclass      opc_rec;
+   List                *result = NIL;
+
+   ht_opc = SearchSysCache(CLAOID,
+                           ObjectIdGetDatum(opclass),
+                           0, 0, 0);
+   if (!HeapTupleIsValid(ht_opc))
+       elog(ERROR, "cache lookup failed for opclass %u", opclass);
+   opc_rec = (Form_pg_opclass) GETSTRUCT(ht_opc);
+
+   if (!OidIsValid(actual_datatype) ||
+       GetDefaultOpClass(actual_datatype, opc_rec->opcmethod) != opclass)
+   {
+       char *nsp_name = get_namespace_name(opc_rec->opcnamespace);
+       char *opc_name = NameStr(opc_rec->opcname);
+
+       result = list_make2(makeString(nsp_name), makeString(opc_name));
+   }
 
+   ReleaseSysCache(ht_opc);
+   return result;
+}
+
+
+/*
+ * transformIndexConstraints
+ *     Handle UNIQUE and PRIMARY KEY constraints, which create
+ *     indexes. We also merge index definitions arising from
+ *     LIKE ... INCLUDING INDEXES.
+ */
+static void
+transformIndexConstraints(ParseState *pstate, CreateStmtContext *cxt)
+{
+   IndexStmt  *index;
+   List       *indexlist = NIL;
+   ListCell   *lc;
+
+   /*
+    * Run through the constraints that need to generate an index. For PRIMARY
+    * KEY, mark each column as NOT NULL and create an index. For UNIQUE,
+    * create an index as for PRIMARY KEY, but do not insist on NOT NULL.
+    */
+   foreach(lc, cxt->ixconstraints)
+   {
+       Constraint *constraint = (Constraint *) lfirst(lc);
+
+       index = transformIndexConstraint(constraint, cxt);
        indexlist = lappend(indexlist, index);
    }
 
        cxt->alist = list_make1(cxt->pkey);
    }
 
-   foreach(l, indexlist)
+   foreach(lc, indexlist)
    {
        bool        keep = true;
        ListCell   *k;
 
-       index = lfirst(l);
+       index = lfirst(lc);
 
        /* if it's pkey, it's already in cxt->alist */
        if (index == cxt->pkey)
        if (keep)
            cxt->alist = lappend(cxt->alist, index);
    }
+
+   /* Copy indexes defined by LIKE ... INCLUDING INDEXES */
+   foreach(lc, cxt->inh_indexes)
+   {
+       index = (IndexStmt *) lfirst(lc);
+
+       if (index->primary)
+       {
+           if (cxt->pkey)
+               ereport(ERROR,
+                       (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
+                        errmsg("multiple primary keys for table \"%s\" are not allowed",
+                               cxt->relation->relname)));
+
+           cxt->pkey = index;
+       }
+
+       cxt->alist = lappend(cxt->alist, index);
+   }
+}
+
+static IndexStmt *
+transformIndexConstraint(Constraint *constraint, CreateStmtContext *cxt)
+{
+   IndexStmt   *index;
+   ListCell    *keys;
+   IndexElem   *iparam;
+
+   Assert(constraint->contype == CONSTR_PRIMARY ||
+          constraint->contype == CONSTR_UNIQUE);
+
+   index = makeNode(IndexStmt);
+   index->unique = true;
+   index->primary = (constraint->contype == CONSTR_PRIMARY);
+
+   if (index->primary)
+   {
+       if (cxt->pkey != NULL)
+           ereport(ERROR,
+                   (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
+                    errmsg("multiple primary keys for table \"%s\" are not allowed",
+                           cxt->relation->relname)));
+       cxt->pkey = index;
+
+       /*
+        * In ALTER TABLE case, a primary index might already exist, but
+        * DefineIndex will check for it.
+        */
+   }
+   index->isconstraint = true;
+
+   if (constraint->name != NULL)
+       index->idxname = pstrdup(constraint->name);
+   else
+       index->idxname = NULL;      /* DefineIndex will choose name */
+
+   index->relation = cxt->relation;
+   index->accessMethod = DEFAULT_INDEX_TYPE;
+   index->options = constraint->options;
+   index->tableSpace = constraint->indexspace;
+   index->indexParams = NIL;
+   index->whereClause = NULL;
+   index->concurrent = false;
+
+   /*
+    * Make sure referenced keys exist.  If we are making a PRIMARY KEY
+    * index, also make sure they are NOT NULL, if possible. (Although we
+    * could leave it to DefineIndex to mark the columns NOT NULL, it's
+    * more efficient to get it right the first time.)
+    */
+   foreach(keys, constraint->keys)
+   {
+       char       *key = strVal(lfirst(keys));
+       bool        found = false;
+       ColumnDef  *column = NULL;
+       ListCell   *columns;
+
+       foreach(columns, cxt->columns)
+       {
+           column = (ColumnDef *) lfirst(columns);
+           Assert(IsA(column, ColumnDef));
+           if (strcmp(column->colname, key) == 0)
+           {
+               found = true;
+               break;
+           }
+       }
+       if (found)
+       {
+           /* found column in the new table; force it to be NOT NULL */
+           if (constraint->contype == CONSTR_PRIMARY)
+               column->is_not_null = TRUE;
+       }
+       else if (SystemAttributeByName(key, cxt->hasoids) != NULL)
+       {
+           /*
+            * column will be a system column in the new table, so accept
+            * it.  System columns can't ever be null, so no need to worry
+            * about PRIMARY/NOT NULL constraint.
+            */
+           found = true;
+       }
+       else if (cxt->inhRelations)
+       {
+           /* try inherited tables */
+           ListCell   *inher;
+
+           foreach(inher, cxt->inhRelations)
+           {
+               RangeVar   *inh = (RangeVar *) lfirst(inher);
+               Relation    rel;
+               int         count;
+
+               Assert(IsA(inh, RangeVar));
+               rel = heap_openrv(inh, AccessShareLock);
+               if (rel->rd_rel->relkind != RELKIND_RELATION)
+                   ereport(ERROR,
+                           (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+                            errmsg("inherited relation \"%s\" is not a table",
+                                   inh->relname)));
+               for (count = 0; count < rel->rd_att->natts; count++)
+               {
+                   Form_pg_attribute inhattr = rel->rd_att->attrs[count];
+                   char       *inhname = NameStr(inhattr->attname);
+
+                   if (inhattr->attisdropped)
+                       continue;
+                   if (strcmp(key, inhname) == 0)
+                   {
+                       found = true;
+
+                       /*
+                        * We currently have no easy way to force an
+                        * inherited column to be NOT NULL at creation, if
+                        * its parent wasn't so already. We leave it to
+                        * DefineIndex to fix things up in this case.
+                        */
+                       break;
+                   }
+               }
+               heap_close(rel, NoLock);
+               if (found)
+                   break;
+           }
+       }
+
+       /*
+        * In the ALTER TABLE case, don't complain about index keys not
+        * created in the command; they may well exist already.
+        * DefineIndex will complain about them if not, and will also take
+        * care of marking them NOT NULL.
+        */
+       if (!found && !cxt->isalter)
+           ereport(ERROR,
+                   (errcode(ERRCODE_UNDEFINED_COLUMN),
+                    errmsg("column \"%s\" named in key does not exist",
+                           key)));
+
+       /* Check for PRIMARY KEY(foo, foo) */
+       foreach(columns, index->indexParams)
+       {
+           iparam = (IndexElem *) lfirst(columns);
+           if (iparam->name && strcmp(key, iparam->name) == 0)
+           {
+               if (index->primary)
+                   ereport(ERROR,
+                           (errcode(ERRCODE_DUPLICATE_COLUMN),
+                            errmsg("column \"%s\" appears twice in primary key constraint",
+                                   key)));
+               else
+                   ereport(ERROR,
+                           (errcode(ERRCODE_DUPLICATE_COLUMN),
+                            errmsg("column \"%s\" appears twice in unique constraint",
+                                   key)));
+           }
+       }
+
+       /* 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;
+       index->indexParams = lappend(index->indexParams, iparam);
+   }
+
+   return index;
 }
 
 /*
    cxt.ckconstraints = NIL;
    cxt.fkconstraints = NIL;
    cxt.ixconstraints = NIL;
+   cxt.inh_indexes = NIL;
    cxt.blist = NIL;
    cxt.alist = NIL;
    cxt.pkey = NULL;