On Tue, 2007-05-06 at 14:50 +0530, NikhilS wrote:
> PFA, a patch which provides the "CREATE .. INCLUDING INDEXES"
> functionality. This patch uses the same functionality introduced by
> the earlier patches in this thread albeit for the "INCLUDING INDEXES"
> case. 

Attached is a revised version of this patch. Sorry for taking so long to
make progress on this: my new job been keeping my busy, and I've
recently been ill.

This version updates the patch to CVS HEAD and has various fixes and
refactoring, including proper docs. I refactored get_opclass_name() into
lsyscache.c, but then realized that this means that lsyscache.c will
depend on commands/indexcmds.c (for GetDefaultOpClass()), which is
arguably improper, so I'm tempted to revert and just duplicate the
syscache lookups in both ruleutils.c and parse_utilcmd.c

Nikhil: why are both "options" and "inhreloptions" necessary in
IndexStmt? Won't at least one be NULL?

BTW, I notice that include/defrem.h contains declarations for several
different, marginally-related .c files (indexcmds.c, functioncmds.c,
operatorcmds.c, aggregatecmds.c, opclasscmds.c, define.c). I'm inclined
to separate these declarations into separate header files; any
objections to doing that?

-Neil

Index: doc/src/sgml/ref/create_table.sgml
===================================================================
RCS file: /home/neilc/postgres/cvs_root/pgsql/doc/src/sgml/ref/create_table.sgml,v
retrieving revision 1.108
diff -p -c -r1.108 create_table.sgml
*** doc/src/sgml/ref/create_table.sgml	3 Jun 2007 17:06:03 -0000	1.108
--- doc/src/sgml/ref/create_table.sgml	9 Jul 2007 04:34:40 -0000
*************** PostgreSQL documentation
*** 23,29 ****
  CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } ] TABLE <replaceable class="PARAMETER">table_name</replaceable> ( [
    { <replaceable class="PARAMETER">column_name</replaceable> <replaceable class="PARAMETER">data_type</replaceable> [ DEFAULT <replaceable>default_expr</> ] [ <replaceable class="PARAMETER">column_constraint</replaceable> [ ... ] ]
      | <replaceable>table_constraint</replaceable>
!     | LIKE <replaceable>parent_table</replaceable> [ { INCLUDING | EXCLUDING } { DEFAULTS | CONSTRAINTS } ] ... }
      [, ... ]
  ] )
  [ INHERITS ( <replaceable>parent_table</replaceable> [, ... ] ) ]
--- 23,29 ----
  CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } ] TABLE <replaceable class="PARAMETER">table_name</replaceable> ( [
    { <replaceable class="PARAMETER">column_name</replaceable> <replaceable class="PARAMETER">data_type</replaceable> [ DEFAULT <replaceable>default_expr</> ] [ <replaceable class="PARAMETER">column_constraint</replaceable> [ ... ] ]
      | <replaceable>table_constraint</replaceable>
!     | LIKE <replaceable>parent_table</replaceable> [ { INCLUDING | EXCLUDING } { DEFAULTS | CONSTRAINTS | INDEXES } ] ... }
      [, ... ]
  ] )
  [ INHERITS ( <replaceable>parent_table</replaceable> [, ... ] ) ]
*************** and <replaceable class="PARAMETER">table
*** 237,243 ****
     </varlistentry>
  
     <varlistentry>
!     <term><literal>LIKE <replaceable>parent_table</replaceable> [ { INCLUDING | EXCLUDING } { DEFAULTS | CONSTRAINTS } ]</literal></term>
      <listitem>
       <para>
        The <literal>LIKE</literal> clause specifies a table from which
--- 237,243 ----
     </varlistentry>
  
     <varlistentry>
!     <term><literal>LIKE <replaceable>parent_table</replaceable> [ { INCLUDING | EXCLUDING } { DEFAULTS | CONSTRAINTS | INDEXES } ]</literal></term>
      <listitem>
       <para>
        The <literal>LIKE</literal> clause specifies a table from which
*************** and <replaceable class="PARAMETER">table
*** 266,275 ****
        requested, all check constraints are copied.
       </para>
       <para>
        Note also that unlike <literal>INHERITS</literal>, copied columns and
        constraints are not merged with similarly named columns and constraints.
        If the same name is specified explicitly or in another
!       <literal>LIKE</literal> clause an error is signalled.
       </para>
      </listitem>
     </varlistentry>
--- 266,280 ----
        requested, all check constraints are copied.
       </para>
       <para>
+       Any indexes on the original table will not be created on the new
+       table, unless the <literal>INCLUDING INDEXES</literal> clause is
+       specified.
+      </para>
+      <para>
        Note also that unlike <literal>INHERITS</literal>, copied columns and
        constraints are not merged with similarly named columns and constraints.
        If the same name is specified explicitly or in another
!       <literal>LIKE</literal> clause, an error is signalled.
       </para>
      </listitem>
     </varlistentry>
Index: src/backend/bootstrap/bootparse.y
===================================================================
RCS file: /home/neilc/postgres/cvs_root/pgsql/src/backend/bootstrap/bootparse.y,v
retrieving revision 1.88
diff -p -c -r1.88 bootparse.y
*** src/backend/bootstrap/bootparse.y	13 Mar 2007 00:33:39 -0000	1.88
--- src/backend/bootstrap/bootparse.y	8 Jul 2007 00:49:29 -0000
*************** Boot_DeclareIndexStmt:
*** 252,258 ****
  								LexIDStr($8),
  								NULL,
  								$10,
! 								NULL, NIL,
  								false, false, false,
  								false, false, true, false, false);
  					do_end();
--- 252,258 ----
  								LexIDStr($8),
  								NULL,
  								$10,
! 								NULL, NIL, NULL,
  								false, false, false,
  								false, false, true, false, false);
  					do_end();
*************** Boot_DeclareUniqueIndexStmt:
*** 270,276 ****
  								LexIDStr($9),
  								NULL,
  								$11,
! 								NULL, NIL,
  								true, false, false,
  								false, false, true, false, false);
  					do_end();
--- 270,276 ----
  								LexIDStr($9),
  								NULL,
  								$11,
! 								NULL, NIL, NULL,
  								true, false, false,
  								false, false, true, false, false);
  					do_end();
Index: src/backend/commands/indexcmds.c
===================================================================
RCS file: /home/neilc/postgres/cvs_root/pgsql/src/backend/commands/indexcmds.c,v
retrieving revision 1.160
diff -p -c -r1.160 indexcmds.c
*** src/backend/commands/indexcmds.c	23 Jun 2007 22:12:50 -0000	1.160
--- src/backend/commands/indexcmds.c	10 Jul 2007 05:46:18 -0000
*************** static bool relationHasPrimaryKey(Relati
*** 79,84 ****
--- 79,85 ----
   *		to index on.
   * 'predicate': the partial-index condition, or NULL if none.
   * 'options': reloptions from WITH (in list-of-DefElem form).
+  * 'inhreloptions':
   * 'unique': make the index enforce uniqueness.
   * 'primary': mark the index as a primary key in the catalogs.
   * 'isconstraint': index is for a PRIMARY KEY or UNIQUE constraint,
*************** DefineIndex(RangeVar *heapRelation,
*** 100,105 ****
--- 101,107 ----
  			List *attributeList,
  			Expr *predicate,
  			List *options,
+ 			char *inhreloptions,
  			bool unique,
  			bool primary,
  			bool isconstraint,
*************** DefineIndex(RangeVar *heapRelation,
*** 392,400 ****
  	}
  
  	/*
! 	 * Parse AM-specific options, convert to text array form, validate.
  	 */
! 	reloptions = transformRelOptions((Datum) 0, options, false, false);
  
  	(void) index_reloptions(amoptions, reloptions, true);
  
--- 394,410 ----
  	}
  
  	/*
! 	 * Parse AM-specific options, convert to text array form,
! 	 * validate.  The inh reloptions introduced due to using indexes
! 	 * via the "CREATE LIKE INCLUDING INDEXES" statement also need to
! 	 * be merged here
  	 */
! 	if (inhreloptions)
! 		reloptions = unflatten_reloptions(inhreloptions);
! 	else
! 		reloptions = (Datum) 0;
! 
! 	reloptions = transformRelOptions(reloptions, options, false, false);
  
  	(void) index_reloptions(amoptions, reloptions, true);
  
Index: src/backend/commands/tablecmds.c
===================================================================
RCS file: /home/neilc/postgres/cvs_root/pgsql/src/backend/commands/tablecmds.c,v
retrieving revision 1.229
diff -p -c -r1.229 tablecmds.c
*** src/backend/commands/tablecmds.c	3 Jul 2007 01:30:36 -0000	1.229
--- src/backend/commands/tablecmds.c	8 Jul 2007 00:49:29 -0000
*************** ATExecAddIndex(AlteredTableInfo *tab, Re
*** 3794,3799 ****
--- 3794,3800 ----
  				stmt->indexParams,		/* parameters */
  				(Expr *) stmt->whereClause,
  				stmt->options,
+ 				stmt->inhreloptions,
  				stmt->unique,
  				stmt->primary,
  				stmt->isconstraint,
Index: src/backend/nodes/copyfuncs.c
===================================================================
RCS file: /home/neilc/postgres/cvs_root/pgsql/src/backend/nodes/copyfuncs.c,v
retrieving revision 1.379
diff -p -c -r1.379 copyfuncs.c
*** src/backend/nodes/copyfuncs.c	11 Jun 2007 22:22:40 -0000	1.379
--- src/backend/nodes/copyfuncs.c	8 Jul 2007 00:49:29 -0000
*************** _copyIndexStmt(IndexStmt *from)
*** 2192,2197 ****
--- 2192,2198 ----
  	COPY_STRING_FIELD(tableSpace);
  	COPY_NODE_FIELD(indexParams);
  	COPY_NODE_FIELD(options);
+ 	COPY_STRING_FIELD(inhreloptions);
  	COPY_NODE_FIELD(whereClause);
  	COPY_SCALAR_FIELD(unique);
  	COPY_SCALAR_FIELD(primary);
Index: src/backend/nodes/equalfuncs.c
===================================================================
RCS file: /home/neilc/postgres/cvs_root/pgsql/src/backend/nodes/equalfuncs.c,v
retrieving revision 1.310
diff -p -c -r1.310 equalfuncs.c
*** src/backend/nodes/equalfuncs.c	11 Jun 2007 22:22:40 -0000	1.310
--- src/backend/nodes/equalfuncs.c	8 Jul 2007 01:33:06 -0000
*************** _equalIndexStmt(IndexStmt *a, IndexStmt 
*** 1044,1049 ****
--- 1044,1050 ----
  	COMPARE_STRING_FIELD(tableSpace);
  	COMPARE_NODE_FIELD(indexParams);
  	COMPARE_NODE_FIELD(options);
+ 	COMPARE_STRING_FIELD(inhreloptions);
  	COMPARE_NODE_FIELD(whereClause);
  	COMPARE_SCALAR_FIELD(unique);
  	COMPARE_SCALAR_FIELD(primary);
Index: src/backend/nodes/outfuncs.c
===================================================================
RCS file: /home/neilc/postgres/cvs_root/pgsql/src/backend/nodes/outfuncs.c,v
retrieving revision 1.311
diff -p -c -r1.311 outfuncs.c
*** src/backend/nodes/outfuncs.c	11 Jun 2007 22:22:40 -0000	1.311
--- src/backend/nodes/outfuncs.c	8 Jul 2007 01:34:11 -0000
*************** _outIndexStmt(StringInfo str, IndexStmt 
*** 1541,1546 ****
--- 1541,1547 ----
  	WRITE_STRING_FIELD(tableSpace);
  	WRITE_NODE_FIELD(indexParams);
  	WRITE_NODE_FIELD(options);
+ 	WRITE_STRING_FIELD(inhreloptions);
  	WRITE_NODE_FIELD(whereClause);
  	WRITE_BOOL_FIELD(unique);
  	WRITE_BOOL_FIELD(primary);
Index: src/backend/parser/parse_utilcmd.c
===================================================================
RCS file: /home/neilc/postgres/cvs_root/pgsql/src/backend/parser/parse_utilcmd.c,v
retrieving revision 2.1
diff -p -c -r2.1 parse_utilcmd.c
*** src/backend/parser/parse_utilcmd.c	23 Jun 2007 22:12:51 -0000	2.1
--- src/backend/parser/parse_utilcmd.c	10 Jul 2007 05:18:16 -0000
***************
*** 26,38 ****
--- 26,41 ----
  
  #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"
***************
*** 47,52 ****
--- 50,56 ----
  #include "utils/acl.h"
  #include "utils/builtins.h"
  #include "utils/lsyscache.h"
+ #include "utils/relcache.h"
  #include "utils/syscache.h"
  
  
*************** static void transformTableConstraint(Par
*** 93,100 ****
--- 97,108 ----
  						 Constraint *constraint);
  static void transformInhRelation(ParseState *pstate, CreateStmtContext *cxt,
  					 InhRelation *inhrelation);
+ static IndexStmt *generateClonedIndexStmt(CreateStmtContext *cxt, 
+ 							Relation parent_index, AttrNumber *attmap);
  static void transformIndexConstraints(ParseState *pstate,
  						  CreateStmtContext *cxt);
+ static IndexStmt *transformIndexConstraint(Constraint *constraint,
+ 										   CreateStmtContext *cxt);
  static void transformFKConstraints(ParseState *pstate,
  					   CreateStmtContext *cxt,
  					   bool skipValidation,
*************** transformInhRelation(ParseState *pstate,
*** 555,565 ****
  		}
  	}
  
- 	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.
--- 563,568 ----
*************** transformInhRelation(ParseState *pstate,
*** 657,662 ****
--- 660,696 ----
  		}
  	}
  
+ 	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 a CREATE INDEX statement to recreate the parent_index.
+ 			 */
+ 			index_stmt = generateClonedIndexStmt(cxt, parent_index,
+ 												 attmap);
+ 
+ 			/* Add the new IndexStmt to the create context */
+ 			cxt->ixconstraints = lappend(cxt->ixconstraints, 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
*************** transformInhRelation(ParseState *pstate,
*** 666,851 ****
  }
  
  /*
!  * transformIndexConstraints
!  *		Handle UNIQUE and PRIMARY KEY constraints, which create indexes
   */
! static void
! transformIndexConstraints(ParseState *pstate, CreateStmtContext *cxt)
  {
! 	IndexStmt  *index;
! 	List	   *indexlist = NIL;
! 	ListCell   *listptr;
! 	ListCell   *l;
  
! 	/*
! 	 * 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(listptr, cxt->ixconstraints)
! 	{
! 		Constraint *constraint = lfirst(listptr);
! 		ListCell   *keys;
! 		IndexElem  *iparam;
  
! 		Assert(IsA(constraint, Constraint));
! 		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);
  		}
  
  		indexlist = lappend(indexlist, index);
--- 700,945 ----
  }
  
  /*
!  * 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 IndexStmt *
! generateClonedIndexStmt(CreateStmtContext *cxt, Relation source_idx,
! 							AttrNumber *attmap)
  {
! 	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;
! 
! 	/*
! 	 * 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
! 	 */
! 	reloptions = SysCacheGetAttr(RELOID, ht_idxrel,
! 								 Anum_pg_class_reloptions, &isnull);
! 	if (!isnull)
! 		index->inhreloptions = 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))
! 	{
! 		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);
! 	}
! 
! 	indexpr_item = list_head(indexprs);
! 
! 	for (keyno = 0; keyno < idxrec->indnatts; keyno++)
! 	{
! 		IndexElem	*iparam;
! 		AttrNumber	attnum = idxrec->indkey.values[keyno];
! 		int16		opt = indoption->values[keyno];
  
! 		iparam = makeNode(IndexElem);
  
! 		if (attnum != 0)
! 		{
! 			/* Simple index column */
! 			char	   *attname;
  
! 			attname = get_relid_attribute_name(indrelid, attnum);
! 			keycoltype = get_atttype(indrelid, attnum);
  
! 			iparam->name = attname;
! 			iparam->expr = NULL;
! 		}
! 		else
  		{
! 			/* 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);
  		}
  
! 		/* Add the operator class name, if non-default */
! 		iparam->opclass = get_opclass_name(indclass->values[keyno],
! 										   keycoltype);
  
! 		iparam->ordering = SORTBY_DEFAULT;
! 		iparam->nulls_ordering = SORTBY_NULLS_DEFAULT;
  
! 		/* Adjust options if necessary */
! 		if (amrec->amcanorder)
  		{
! 			/* 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;
! 		}
  
! 		index->indexParams = lappend(index->indexParams, iparam);
! 	}
  
! 	/* Use the same tablespace as the source index */
! 	index->tableSpace = get_tablespace_name(source_idx->rd_node.spcNode);
  
! 	/* 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);
! 	}
  
! 	/* Clean up */
! 	ReleaseSysCache(ht_idx);
! 	ReleaseSysCache(ht_idxrel);
! 	ReleaseSysCache(ht_am);
! 
! 	return index;
! }
! 
! /*
!  * transformIndexConstraints
!  *		Handle UNIQUE and PRIMARY KEY constraints, which create 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.
! 	 *
! 	 * If the table is being created using LIKE INCLUDING INDEXES, the
! 	 * ixconstraints list will contain a mix of Constraint and IndexStmt
! 	 * entries
! 	 */
! 	foreach(lc, cxt->ixconstraints)
! 	{
! 		Node	   *node = lfirst(lc);
! 
! 		switch (nodeTag(node))
! 		{
! 			case T_Constraint:
! 				index = transformIndexConstraint((Constraint *) node, cxt);
! 				break;
! 
! 			case T_IndexStmt:
! 				index = (IndexStmt *) node;	
! 				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;
  				}
! 				break;
  
! 			default:
! 				elog(ERROR, "unrecognized node type: %d", nodeTag(node));
! 				index = NULL;	/* keep the compiler quiet */
  		}
  
  		indexlist = lappend(indexlist, index);
*************** transformIndexConstraints(ParseState *ps
*** 867,878 ****
  		cxt->alist = list_make1(cxt->pkey);
  	}
  
! 	foreach(l, indexlist)
  	{
  		bool		keep = true;
  		ListCell   *k;
  
! 		index = lfirst(l);
  
  		/* if it's pkey, it's already in cxt->alist */
  		if (index == cxt->pkey)
--- 961,972 ----
  		cxt->alist = list_make1(cxt->pkey);
  	}
  
! 	foreach(lc, indexlist)
  	{
  		bool		keep = true;
  		ListCell   *k;
  
! 		index = lfirst(lc);
  
  		/* if it's pkey, it's already in cxt->alist */
  		if (index == cxt->pkey)
*************** transformIndexConstraints(ParseState *ps
*** 902,907 ****
--- 996,1170 ----
  	}
  }
  
+ 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;
+ }
+ 
  /*
   * transformFKConstraints
   *		handle FOREIGN KEY constraints
Index: src/backend/tcop/utility.c
===================================================================
RCS file: /home/neilc/postgres/cvs_root/pgsql/src/backend/tcop/utility.c,v
retrieving revision 1.283
diff -p -c -r1.283 utility.c
*** src/backend/tcop/utility.c	3 Jul 2007 01:30:37 -0000	1.283
--- src/backend/tcop/utility.c	8 Jul 2007 00:49:30 -0000
*************** ProcessUtility(Node *parsetree,
*** 886,891 ****
--- 886,892 ----
  							stmt->indexParams,	/* parameters */
  							(Expr *) stmt->whereClause,
  							stmt->options,
+ 							stmt->inhreloptions,
  							stmt->unique,
  							stmt->primary,
  							stmt->isconstraint,
Index: src/backend/utils/adt/ruleutils.c
===================================================================
RCS file: /home/neilc/postgres/cvs_root/pgsql/src/backend/utils/adt/ruleutils.c,v
retrieving revision 1.262
diff -p -c -r1.262 ruleutils.c
*** src/backend/utils/adt/ruleutils.c	18 Jun 2007 21:40:58 -0000	1.262
--- src/backend/utils/adt/ruleutils.c	10 Jul 2007 05:50:13 -0000
*************** static void get_from_clause_alias(Alias 
*** 184,191 ****
  					  deparse_context *context);
  static void get_from_clause_coldeflist(List *names, List *types, List *typmods,
  						   deparse_context *context);
! static void get_opclass_name(Oid opclass, Oid actual_datatype,
! 				 StringInfo buf);
  static Node *processIndirection(Node *node, deparse_context *context,
  				   bool printit);
  static void printSubscripts(ArrayRef *aref, deparse_context *context);
--- 184,190 ----
  					  deparse_context *context);
  static void get_from_clause_coldeflist(List *names, List *types, List *typmods,
  						   deparse_context *context);
! static void get_opclass(Oid opclass, Oid actual_datatype, StringInfo buf);
  static Node *processIndirection(Node *node, deparse_context *context,
  				   bool printit);
  static void printSubscripts(ArrayRef *aref, deparse_context *context);
*************** static char *generate_relation_name(Oid 
*** 193,199 ****
  static char *generate_function_name(Oid funcid, int nargs, Oid *argtypes);
  static char *generate_operator_name(Oid operid, Oid arg1, Oid arg2);
  static text *string_to_text(char *str);
- static char *flatten_reloptions(Oid relid);
  
  #define only_marker(rte)  ((rte)->inh ? "" : "ONLY ")
  
--- 192,197 ----
*************** pg_get_indexdef_worker(Oid indexrelid, i
*** 763,770 ****
  
  		/* Add the operator class name */
  		if (!colno)
! 			get_opclass_name(indclass->values[keyno], keycoltype,
! 							 &buf);
  
  		/* Add options if relevant */
  		if (amrec->amcanorder)
--- 761,767 ----
  
  		/* Add the operator class name */
  		if (!colno)
! 			get_opclass(indclass->values[keyno], keycoltype, &buf);
  
  		/* Add options if relevant */
  		if (amrec->amcanorder)
*************** get_from_clause_coldeflist(List *names, 
*** 4995,5001 ****
  }
  
  /*
!  * get_opclass_name			- fetch name of an index operator class
   *
   * The opclass name is appended (after a space) to buf.
   *
--- 4992,4998 ----
  }
  
  /*
!  * get_opclass			- fetch name of an index operator class
   *
   * The opclass name is appended (after a space) to buf.
   *
*************** get_from_clause_coldeflist(List *names, 
*** 5004,5040 ****
   * InvalidOid for actual_datatype.)
   */
  static void
! get_opclass_name(Oid opclass, Oid actual_datatype,
! 				 StringInfo buf)
  {
! 	HeapTuple	ht_opc;
! 	Form_pg_opclass opcrec;
! 	char	   *opcname;
! 	char	   *nspname;
! 
! 	ht_opc = SearchSysCache(CLAOID,
! 							ObjectIdGetDatum(opclass),
! 							0, 0, 0);
! 	if (!HeapTupleIsValid(ht_opc))
! 		elog(ERROR, "cache lookup failed for opclass %u", opclass);
! 	opcrec = (Form_pg_opclass) GETSTRUCT(ht_opc);
  
! 	if (!OidIsValid(actual_datatype) ||
! 		GetDefaultOpClass(actual_datatype, opcrec->opcmethod) != opclass)
  	{
  		/* Okay, we need the opclass name.	Do we need to qualify it? */
! 		opcname = NameStr(opcrec->opcname);
  		if (OpclassIsVisible(opclass))
  			appendStringInfo(buf, " %s", quote_identifier(opcname));
  		else
  		{
! 			nspname = get_namespace_name(opcrec->opcnamespace);
  			appendStringInfo(buf, " %s.%s",
  							 quote_identifier(nspname),
  							 quote_identifier(opcname));
  		}
  	}
- 	ReleaseSysCache(ht_opc);
  }
  
  /*
--- 5001,5024 ----
   * InvalidOid for actual_datatype.)
   */
  static void
! get_opclass(Oid opclass, Oid actual_datatype, StringInfo buf)
  {
! 	List *name_list = get_opclass_name(opclass, actual_datatype);
  
! 	if (name_list)
  	{
  		/* Okay, we need the opclass name.	Do we need to qualify it? */
! 		char *opcname = strVal(linitial(name_list));
  		if (OpclassIsVisible(opclass))
  			appendStringInfo(buf, " %s", quote_identifier(opcname));
  		else
  		{
! 			char *nspname = strVal(lsecond(name_list));
  			appendStringInfo(buf, " %s.%s",
  							 quote_identifier(nspname),
  							 quote_identifier(opcname));
  		}
  	}
  }
  
  /*
*************** string_to_text(char *str)
*** 5417,5423 ****
  /*
   * Generate a C string representing a relation's reloptions, or NULL if none.
   */
! static char *
  flatten_reloptions(Oid relid)
  {
  	char	   *result = NULL;
--- 5401,5407 ----
  /*
   * Generate a C string representing a relation's reloptions, or NULL if none.
   */
! char *
  flatten_reloptions(Oid relid)
  {
  	char	   *result = NULL;
*************** flatten_reloptions(Oid relid)
*** 5453,5455 ****
--- 5437,5467 ----
  
  	return result;
  }
+ 
+ /*
+  * Generate an Array Datum representing a relation's reloptions using
+  * a C string
+  */
+ Datum
+ unflatten_reloptions(char *reloptstring)
+ {
+ 	Datum		result = (Datum) 0;
+ 
+ 	if (reloptstring)
+ 	{
+ 		Datum		sep, relopts;
+ 
+ 		/*
+ 		 * We want to use text_to_array(reloptstring, ', ') --- but
+ 		 * DirectFunctionCall2(text_to_array) does not work, because
+ 		 * text_to_array() relies on fcinfo to be valid.  So use
+ 		 * OidFunctionCall2.
+ 		 */
+ 		sep = DirectFunctionCall1(textin, CStringGetDatum(", "));
+ 		relopts = DirectFunctionCall1(textin, CStringGetDatum(reloptstring));
+ 
+ 		result = OidFunctionCall2(F_TEXT_TO_ARRAY, relopts, sep);
+ 	}
+ 
+ 	return result;
+ }
Index: src/backend/utils/cache/lsyscache.c
===================================================================
RCS file: /home/neilc/postgres/cvs_root/pgsql/src/backend/utils/cache/lsyscache.c,v
retrieving revision 1.152
diff -p -c -r1.152 lsyscache.c
*** src/backend/utils/cache/lsyscache.c	11 May 2007 17:57:12 -0000	1.152
--- src/backend/utils/cache/lsyscache.c	10 Jul 2007 05:35:21 -0000
***************
*** 27,32 ****
--- 27,33 ----
  #include "catalog/pg_proc.h"
  #include "catalog/pg_statistic.h"
  #include "catalog/pg_type.h"
+ #include "commands/defrem.h"
  #include "miscadmin.h"
  #include "nodes/makefuncs.h"
  #include "utils/array.h"
*************** get_opclass_input_type(Oid opclass)
*** 980,985 ****
--- 981,1024 ----
  	return result;
  }
  
+ /*
+  * get_opclass
+  *
+  *		Returns the qualified name of the specified index opclass, as
+  *		a pair of string Values.
+  *
+  * If the opclass is the default for the given actual_datatype, then
+  * this returns NIL. (If you don't want this behavior, just pass
+  * InvalidOid for actual_datatype.)
+  */
+ List *
+ get_opclass_name(Oid opclass, Oid actual_datatype)
+ {
+ 	HeapTuple			 ht_opc;
+ 	Form_pg_opclass 	 opc_rec;
+ 	List 				*name_list = 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);
+ 
+ 		name_list = list_make2(makeString(nsp_name), makeString(opc_name));
+ 	}
+ 
+ 	ReleaseSysCache(ht_opc);
+ 	return name_list;
+ }
+ 
  /*				---------- OPERATOR CACHE ----------					 */
  
  /*
Index: src/include/commands/defrem.h
===================================================================
RCS file: /home/neilc/postgres/cvs_root/pgsql/src/include/commands/defrem.h,v
retrieving revision 1.81
diff -p -c -r1.81 defrem.h
*** src/include/commands/defrem.h	13 Mar 2007 00:33:43 -0000	1.81
--- src/include/commands/defrem.h	8 Jul 2007 00:49:30 -0000
*************** extern void DefineIndex(RangeVar *heapRe
*** 26,31 ****
--- 26,32 ----
  			List *attributeList,
  			Expr *predicate,
  			List *options,
+ 			char *inhreloptions,
  			bool unique,
  			bool primary,
  			bool isconstraint,
Index: src/include/nodes/parsenodes.h
===================================================================
RCS file: /home/neilc/postgres/cvs_root/pgsql/src/include/nodes/parsenodes.h,v
retrieving revision 1.349
diff -p -c -r1.349 parsenodes.h
*** src/include/nodes/parsenodes.h	23 Jun 2007 22:12:52 -0000	1.349
--- src/include/nodes/parsenodes.h	8 Jul 2007 00:49:30 -0000
*************** typedef struct IndexStmt
*** 1501,1506 ****
--- 1501,1507 ----
  	char	   *tableSpace;		/* tablespace, or NULL to use parent's */
  	List	   *indexParams;	/* a list of IndexElem */
  	List	   *options;		/* options from WITH clause */
+ 	char	   *inhreloptions;	/* relopts inherited from parent index */
  	Node	   *whereClause;	/* qualification (partial-index predicate) */
  	bool		unique;			/* is index unique? */
  	bool		primary;		/* is index on primary key? */
Index: src/include/utils/builtins.h
===================================================================
RCS file: /home/neilc/postgres/cvs_root/pgsql/src/include/utils/builtins.h,v
retrieving revision 1.297
diff -p -c -r1.297 builtins.h
*** src/include/utils/builtins.h	26 Jun 2007 16:48:09 -0000	1.297
--- src/include/utils/builtins.h	10 Jul 2007 05:46:07 -0000
*************** extern List *deparse_context_for_plan(No
*** 560,565 ****
--- 560,567 ----
  extern const char *quote_identifier(const char *ident);
  extern char *quote_qualified_identifier(const char *namespace,
  						   const char *ident);
+ extern char *flatten_reloptions(Oid relid);
+ extern Datum unflatten_reloptions(char *reloptstring);
  
  /* tid.c */
  extern Datum tidin(PG_FUNCTION_ARGS);
Index: src/include/utils/lsyscache.h
===================================================================
RCS file: /home/neilc/postgres/cvs_root/pgsql/src/include/utils/lsyscache.h,v
retrieving revision 1.119
diff -p -c -r1.119 lsyscache.h
*** src/include/utils/lsyscache.h	6 Jun 2007 23:00:47 -0000	1.119
--- src/include/utils/lsyscache.h	10 Jul 2007 05:14:32 -0000
*************** extern void get_atttypetypmod(Oid relid,
*** 61,66 ****
--- 61,67 ----
  extern char *get_constraint_name(Oid conoid);
  extern Oid	get_opclass_family(Oid opclass);
  extern Oid	get_opclass_input_type(Oid opclass);
+ extern List *get_opclass_name(Oid opclass, Oid actual_datatype);
  extern RegProcedure get_opcode(Oid opno);
  extern char *get_opname(Oid opno);
  extern void op_input_types(Oid opno, Oid *lefttype, Oid *righttype);
Index: src/test/regress/expected/inherit.out
===================================================================
RCS file: /home/neilc/postgres/cvs_root/pgsql/src/test/regress/expected/inherit.out,v
retrieving revision 1.21
diff -p -c -r1.21 inherit.out
*** src/test/regress/expected/inherit.out	3 Jun 2007 22:16:03 -0000	1.21
--- src/test/regress/expected/inherit.out	8 Jul 2007 00:49:30 -0000
*************** SELECT * FROM inhg; /* Two records with 
*** 633,638 ****
--- 633,650 ----
  (2 rows)
  
  DROP TABLE inhg;
+ CREATE TABLE inhg (x text, LIKE inhx INCLUDING INDEXES, y text); /* copies indexes */
+ DROP TABLE inhg;
+ /* Multiple primary keys creation should fail */
+ CREATE TABLE inhg (x text, LIKE inhx INCLUDING INDEXES, PRIMARY KEY(x)); /* fails */
+ ERROR:  multiple primary keys for table "inhg" are not allowed
+ CREATE TABLE inhz (xx text DEFAULT 'text', yy int UNIQUE);
+ NOTICE:  CREATE TABLE / UNIQUE will create implicit index "inhz_yy_key" for table "inhz"
+ /* Ok to create multiple unique indexes */
+ CREATE TABLE inhg (x text UNIQUE, LIKE inhz INCLUDING INDEXES);
+ NOTICE:  CREATE TABLE / UNIQUE will create implicit index "inhg_x_key" for table "inhg"
+ DROP TABLE inhg;
+ DROP TABLE inhz;
  -- Test changing the type of inherited columns
  insert into d values('test','one','two','three');
  alter table a alter column aa type integer using bit_length(aa);
Index: src/test/regress/sql/inherit.sql
===================================================================
RCS file: /home/neilc/postgres/cvs_root/pgsql/src/test/regress/sql/inherit.sql,v
retrieving revision 1.10
diff -p -c -r1.10 inherit.sql
*** src/test/regress/sql/inherit.sql	27 Jun 2006 03:43:20 -0000	1.10
--- src/test/regress/sql/inherit.sql	8 Jul 2007 00:49:30 -0000
*************** INSERT INTO inhg VALUES ('x', 'foo',  'y
*** 156,161 ****
--- 156,170 ----
  SELECT * FROM inhg; /* Two records with three columns in order x=x, xx=text, y=y */
  DROP TABLE inhg;
  
+ CREATE TABLE inhg (x text, LIKE inhx INCLUDING INDEXES, y text); /* copies indexes */
+ DROP TABLE inhg;
+ /* Multiple primary keys creation should fail */
+ CREATE TABLE inhg (x text, LIKE inhx INCLUDING INDEXES, PRIMARY KEY(x)); /* fails */
+ CREATE TABLE inhz (xx text DEFAULT 'text', yy int UNIQUE);
+ /* Ok to create multiple unique indexes */
+ CREATE TABLE inhg (x text UNIQUE, LIKE inhz INCLUDING INDEXES);
+ DROP TABLE inhg;
+ DROP TABLE inhz;
  
  -- Test changing the type of inherited columns
  insert into d values('test','one','two','three');
---------------------------(end of broadcast)---------------------------
TIP 3: Have you checked our extensive FAQ?

               http://www.postgresql.org/docs/faq

Reply via email to