diff -cpr HEAD/doc/src/sgml/ref/create_table.sgml inherits-parameters/doc/src/sgml/ref/create_table.sgml
*** HEAD/doc/src/sgml/ref/create_table.sgml	Tue Jul 29 14:19:30 2008
--- inherits-parameters/doc/src/sgml/ref/create_table.sgml	Tue Jul 29 14:20:01 2008
*************** and <replaceable class="PARAMETER">table
*** 229,234 ****
--- 229,237 ----
        will always be chosen for it.
       </para>
  
+      <para>
+       Storage parameters are also copied from parent tables.
+      </para>
  <!--
       <para>
        <productname>PostgreSQL</> automatically allows the
*************** and <replaceable class="PARAMETER">table
*** 251,257 ****
       <para>
        The <literal>LIKE</literal> clause specifies a table from which
        the new table automatically copies all column names, their data types,
!       and their not-null constraints.
       </para>
       <para>
        Unlike <literal>INHERITS</literal>, the new table and original table
--- 254,260 ----
       <para>
        The <literal>LIKE</literal> clause specifies a table from which
        the new table automatically copies all column names, their data types,
!       their not-null constraints, and their storage parameters.
       </para>
       <para>
        Unlike <literal>INHERITS</literal>, the new table and original table
diff -cpr HEAD/src/backend/access/common/reloptions.c inherits-parameters/src/backend/access/common/reloptions.c
*** HEAD/src/backend/access/common/reloptions.c	Tue Jul 29 14:19:30 2008
--- inherits-parameters/src/backend/access/common/reloptions.c	Tue Jul 29 14:20:01 2008
***************
*** 24,29 ****
--- 24,30 ----
  #include "utils/guc.h"
  #include "utils/rel.h"
  
+ static DefElem *searchDefList(List *defList, const char *name);
  
  /*
   * Transform a relation options list (list of DefElem) into the text array
*************** transformRelOptions(Datum oldOptions, Li
*** 73,93 ****
  
  		for (i = 0; i < noldoptions; i++)
  		{
! 			text	   *oldoption = DatumGetTextP(oldoptions[i]);
! 			char	   *text_str = VARDATA(oldoption);
! 			int			text_len = VARSIZE(oldoption) - VARHDRSZ;
! 
! 			/* Search for a match in defList */
! 			foreach(cell, defList)
! 			{
! 				DefElem    *def = lfirst(cell);
! 				int			kw_len = strlen(def->defname);
! 
! 				if (text_len > kw_len && text_str[kw_len] == '=' &&
! 					pg_strncasecmp(text_str, def->defname, kw_len) == 0)
! 					break;
! 			}
! 			if (!cell)
  			{
  				/* No match, so keep old option */
  				astate = accumArrayResult(astate, oldoptions[i],
--- 74,84 ----
  
  		for (i = 0; i < noldoptions; i++)
  		{
! 			char   *name = TextDatumGetCString(oldoptions[i]);
! 			char   *sep = strchr(name, '=');
! 			if (sep)
! 				*sep = '\0';
! 			if (searchDefList(defList, name) == NULL)
  			{
  				/* No match, so keep old option */
  				astate = accumArrayResult(astate, oldoptions[i],
*************** untransformRelOptions(Datum options)
*** 194,199 ****
--- 185,220 ----
  	return result;
  }
  
+ /*
+  * Merge two reloptions. Options in defList2 are merged into defList1.
+  * defList1 is modified destructively.
+  */
+ List *
+ mergeRelOptions(List *defList1, List *defList2, bool hasoids)
+ {
+ 	ListCell   *cell;
+ 	DefElem	   *def;
+ 
+ 	foreach(cell, defList2)
+ 	{
+ 		def = lfirst(cell);
+ 
+ 		if (pg_strcasecmp(def->defname, "oids") == 0)
+ 			continue;
+ 
+ 		/* Merge if defList1 does not have the option. */
+ 		if (searchDefList(defList1, def->defname) == NULL)
+ 			defList1 = lappend(defList1, def);
+ 	}
+ 
+ 	/* Overwrite oids option. */
+ 	if ((def = searchDefList(defList1, "oids")) == NULL)
+ 		defList1 = lappend(defList1, defWithOids(hasoids));
+ 	else if (defGetBoolean(def) != hasoids)
+ 		def->arg = (Node *) makeInteger(hasoids);
+ 
+ 	return defList1;
+ }
  
  /*
   * Interpret reloptions that are given in text-array format.
*************** index_reloptions(RegProcedure amoptions,
*** 378,381 ****
--- 399,418 ----
  		return NULL;
  
  	return DatumGetByteaP(result);
+ }
+ 
+ /* Search for a match in defList */
+ static DefElem *
+ searchDefList(List *defList, const char *name)
+ {
+ 	ListCell *cell;
+ 
+ 	foreach(cell, defList)
+ 	{
+ 		DefElem    *def = lfirst(cell);
+ 		if (pg_strcasecmp(name, def->defname) == 0)
+ 			return def;
+ 	}
+ 
+ 	return NULL;
  }
diff -cpr HEAD/src/backend/parser/parse_utilcmd.c inherits-parameters/src/backend/parser/parse_utilcmd.c
*** HEAD/src/backend/parser/parse_utilcmd.c	Tue Jul 29 14:19:33 2008
--- inherits-parameters/src/backend/parser/parse_utilcmd.c	Tue Jul 29 14:20:01 2008
*************** typedef struct
*** 65,70 ****
--- 65,71 ----
  	List	   *inhRelations;	/* relations to inherit from */
  	bool		isalter;		/* true if altering existing table */
  	bool		hasoids;		/* does relation have an OID column? */
+ 	List	   *options;		/* reloptions specified in WITH (...) */
  	List	   *columns;		/* ColumnDef items */
  	List	   *ckconstraints;	/* CHECK constraints */
  	List	   *fkconstraints;	/* FOREIGN KEY constraints */
*************** static void transformFKConstraints(Parse
*** 114,119 ****
--- 115,123 ----
  static void transformConstraintAttrs(List *constraintList);
  static void transformColumnType(ParseState *pstate, ColumnDef *column);
  static void setSchemaName(char *context_schema, char **stmt_schema_name);
+ static void mergeOptions(CreateStmtContext *cxt, Relation rel);
+ static AlterTableStmt *appendSetStorageCmd(AlterTableStmt *stmt,
+ 						RangeVar *rel, Form_pg_attribute attr);
  
  
  /*
*************** transformCreateStmt(CreateStmt *stmt, co
*** 138,143 ****
--- 142,148 ----
  	List	   *result;
  	List	   *save_alist;
  	ListCell   *elements;
+ 	AlterTableStmt *alterstmt = NULL;
  
  	/*
  	 * We must not scribble on the passed-in CreateStmt, so copy it.  (This is
*************** transformCreateStmt(CreateStmt *stmt, co
*** 177,182 ****
--- 182,188 ----
  	cxt.alist = NIL;
  	cxt.pkey = NULL;
  	cxt.hasoids = interpretOidsOption(stmt->options);
+ 	cxt.options = mergeRelOptions(stmt->options, NIL, cxt.hasoids);
  
  	/*
  	 * Run through each primary element in the table creation clause. Separate
*************** transformCreateStmt(CreateStmt *stmt, co
*** 215,220 ****
--- 221,257 ----
  		}
  	}
  
+ 	/* Copy reloptions and column storage parameters. */
+ 	foreach(elements, cxt.inhRelations)
+ 	{
+ 		RangeVar   *inh = lfirst(elements);
+ 		Relation	rel;
+ 		TupleDesc	tupleDesc;
+ 		int			i;
+ 
+ 		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)));
+ 
+ 		/* Merge reloptions in inherited tables. */
+ 		mergeOptions(&cxt, rel);
+ 
+ 		/* Copy column storage parameter. */
+ 		tupleDesc = RelationGetDescr(rel);
+ 		for (i = 0; i < tupleDesc->natts; i++)
+ 			alterstmt = appendSetStorageCmd(alterstmt, cxt.relation,
+ 											tupleDesc->attrs[i]);
+ 
+ 		heap_close(rel, NoLock);
+ 	}
+ 
+ 	if (alterstmt)
+ 		cxt.alist = lappend(cxt.alist, alterstmt);
+ 
  	/*
  	 * transformIndexConstraints wants cxt.alist to contain only index
  	 * statements, so transfer anything we already have into save_alist.
*************** transformCreateStmt(CreateStmt *stmt, co
*** 239,244 ****
--- 276,282 ----
  	 */
  	stmt->tableElts = cxt.columns;
  	stmt->constraints = cxt.ckconstraints;
+ 	stmt->options = cxt.options;
  
  	result = lappend(cxt.blist, stmt);
  	result = list_concat(result, cxt.alist);
*************** transformInhRelation(ParseState *pstate,
*** 545,550 ****
--- 583,589 ----
  	bool		including_constraints = false;
  	bool		including_indexes = false;
  	ListCell   *elem;
+ 	AlterTableStmt *alterstmt = NULL;
  
  	relation = heap_openrv(inhRelation->relation, AccessShareLock);
  
*************** transformInhRelation(ParseState *pstate,
*** 663,670 ****
--- 702,715 ----
  
  			def->cooked_default = pstrdup(this_default);
  		}
+ 
+ 		/* Copy column storage parameter. */
+ 		alterstmt = appendSetStorageCmd(alterstmt, cxt->relation, attribute);
  	}
  
+ 	if (alterstmt)
+ 		cxt->alist = lappend(cxt->alist, alterstmt);
+ 
  	/*
  	 * Copy CHECK constraints if requested, being careful to adjust attribute
  	 * numbers
*************** transformInhRelation(ParseState *pstate,
*** 721,726 ****
--- 766,774 ----
  		}
  	}
  
+ 	/* Merge reloptions and oids */
+ 	mergeOptions(cxt, relation);
+ 
  	/*
  	 * Close the parent rel, but keep our AccessShareLock on it until xact
  	 * commit.	That will prevent someone else from deleting or ALTERing the
*************** setSchemaName(char *context_schema, char
*** 2105,2108 ****
--- 2153,2242 ----
  				 errmsg("CREATE specifies a schema (%s) "
  						"different from the one being created (%s)",
  						*stmt_schema_name, context_schema)));
+ }
+ 
+ /*
+  * mergeOptions
+  *		Merge rel's reloptions and oids into cxt->options.
+  */
+ static void
+ mergeOptions(CreateStmtContext *cxt, Relation rel)
+ {
+ 	List	   *options = NIL;
+ 	Oid			relid = RelationGetRelid(rel);
+ 	HeapTuple	tuple;
+ 	Datum		datum;
+ 	bool		isnull;
+ 
+ 	/* The new relation should have oids if any of parents have oids. */
+ 	if (rel->rd_rel->relhasoids)
+ 		cxt->hasoids = true;
+ 
+ 	/* Copy reloptions if any */
+ 	tuple = SearchSysCache(RELOID, ObjectIdGetDatum(relid), 0, 0, 0);
+ 	if (!HeapTupleIsValid(tuple))
+ 		elog(ERROR, "cache lookup failed for relation %u", relid);
+ 	datum = SysCacheGetAttr(RELOID, tuple,
+ 							Anum_pg_class_reloptions, &isnull);
+ 	if (!isnull)
+ 		options = untransformRelOptions(datum);
+ 	ReleaseSysCache(tuple);
+ 
+ 	/* Do merge. */
+ 	cxt->options = mergeRelOptions(cxt->options, options, cxt->hasoids);
+ }
+ 
+ /*
+  * Append ALTER COLUMN SET STORAGE command to stmt if needed.
+  */
+ static AlterTableStmt *
+ appendSetStorageCmd(AlterTableStmt *stmt, RangeVar *rel, Form_pg_attribute attr)
+ {
+ 	char		   *storage;
+ 	AlterTableCmd  *altercmd;
+ 
+ 	/* Ignore dropped columns. */
+ 	if (attr->attisdropped)
+ 		return stmt;
+ 
+ 	/* No command needed if the default storage paramater is used. */
+ 	if (attr->attstorage == get_typstorage(attr->atttypid))
+ 		return stmt;
+ 
+ 	switch (attr->attstorage)
+ 	{
+ 		case 'p':
+ 			storage = "plain";
+ 			break;
+ 		case 'm':
+ 			storage = "main";
+ 			break;
+ 		case 'x':
+ 			storage = "extended";
+ 			break;
+ 		case 'e':
+ 			storage = "external";
+ 			break;
+ 		default:
+ 			elog(ERROR, "unexpected storage type: %c", attr->attstorage);
+ 			storage = NULL;
+ 			break;
+ 	}
+ 
+ 	altercmd = makeNode(AlterTableCmd);
+ 	altercmd->subtype = AT_SetStorage;
+ 	altercmd->name = pstrdup(NameStr(attr->attname));
+ 	altercmd->def = (Node *) makeString(storage);
+ 
+ 	/* SET STORAGE commands are merged into one AlterTableStmt. */
+ 	if (stmt == NULL)
+ 	{
+ 		stmt = makeNode(AlterTableStmt);
+ 		stmt->relation = rel;
+ 		stmt->cmds = NIL;
+ 		stmt->relkind = OBJECT_TABLE;
+ 	}
+ 	stmt->cmds = lappend(stmt->cmds, altercmd);
+ 
+ 	return stmt;
  }
diff -cpr HEAD/src/include/access/reloptions.h inherits-parameters/src/include/access/reloptions.h
*** HEAD/src/include/access/reloptions.h	Tue Jul 29 14:19:40 2008
--- inherits-parameters/src/include/access/reloptions.h	Tue Jul 29 14:20:01 2008
*************** extern Datum transformRelOptions(Datum o
*** 25,30 ****
--- 25,32 ----
  
  extern List *untransformRelOptions(Datum options);
  
+ extern List *mergeRelOptions(List *defList1, List *defList2, bool hasoids);
+ 
  extern void parseRelOptions(Datum options, int numkeywords,
  				const char *const * keywords,
  				char **values, bool validate);
diff -cpr HEAD/src/test/regress/expected/inherit.out inherits-parameters/src/test/regress/expected/inherit.out
*** HEAD/src/test/regress/expected/inherit.out	Tue Jul 29 14:19:45 2008
--- inherits-parameters/src/test/regress/expected/inherit.out	Tue Jul 29 14:20:01 2008
*************** drop table pp1 cascade;
*** 904,906 ****
--- 904,958 ----
  NOTICE:  drop cascades to 2 other objects
  DETAIL:  drop cascades to table cc1
  drop cascades to table cc2
+ -- Test inheritance with WITH-options
+ CREATE TABLE table_with_oids (a text) WITH (oids);
+ CREATE TABLE table_with_opts (b text) WITH (fillfactor=50);
+ ALTER TABLE table_with_opts ALTER COLUMN b SET STORAGE main;
+ CREATE TABLE table_inh () INHERITS (table_with_oids, table_with_opts);
+ SELECT relname, relhasoids, reloptions FROM pg_class WHERE oid = 'table_inh'::regclass;
+   relname  | relhasoids |   reloptions    
+ -----------+------------+-----------------
+  table_inh | t          | {fillfactor=50}
+ (1 row)
+ 
+ \d+ table_inh
+               Table "public.table_inh"
+  Column | Type | Modifiers | Storage  | Description 
+ --------+------+-----------+----------+-------------
+  a      | text |           | extended | 
+  b      | text |           | main     | 
+ Inherits: table_with_oids,
+           table_with_opts
+ Has OIDs: yes
+ 
+ CREATE TABLE table_like1 (LIKE table_with_oids, LIKE table_with_opts);
+ SELECT relname, relhasoids, reloptions FROM pg_class WHERE oid = 'table_like1'::regclass;
+    relname   | relhasoids |   reloptions    
+ -------------+------------+-----------------
+  table_like1 | t          | {fillfactor=50}
+ (1 row)
+ 
+ \d+ table_like1
+              Table "public.table_like1"
+  Column | Type | Modifiers | Storage  | Description 
+ --------+------+-----------+----------+-------------
+  a      | text |           | extended | 
+  b      | text |           | main     | 
+ Has OIDs: yes
+ 
+ CREATE TABLE table_like2 (LIKE table_with_opts, LIKE table_with_oids) WITH (fillfactor=75);
+ SELECT relname, relhasoids, reloptions FROM pg_class WHERE oid = 'table_like2'::regclass;
+    relname   | relhasoids |   reloptions    
+ -------------+------------+-----------------
+  table_like2 | t          | {fillfactor=75}
+ (1 row)
+ 
+ \d+ table_like2
+              Table "public.table_like2"
+  Column | Type | Modifiers | Storage  | Description 
+ --------+------+-----------+----------+-------------
+  b      | text |           | main     | 
+  a      | text |           | extended | 
+ Has OIDs: yes
+ 
+ DROP TABLE table_with_oids, table_with_opts, table_inh, table_like1, table_like2 CASCADE;
diff -cpr HEAD/src/test/regress/sql/inherit.sql inherits-parameters/src/test/regress/sql/inherit.sql
*** HEAD/src/test/regress/sql/inherit.sql	Tue Jul 29 14:19:45 2008
--- inherits-parameters/src/test/regress/sql/inherit.sql	Tue Jul 29 14:20:01 2008
*************** create table cc2(f4 float) inherits(pp1,
*** 276,278 ****
--- 276,293 ----
  alter table pp1 add column a2 int check (a2 > 0);
  \d cc2
  drop table pp1 cascade;
+ 
+ -- Test inheritance with WITH-options
+ CREATE TABLE table_with_oids (a text) WITH (oids);
+ CREATE TABLE table_with_opts (b text) WITH (fillfactor=50);
+ ALTER TABLE table_with_opts ALTER COLUMN b SET STORAGE main;
+ CREATE TABLE table_inh () INHERITS (table_with_oids, table_with_opts);
+ SELECT relname, relhasoids, reloptions FROM pg_class WHERE oid = 'table_inh'::regclass;
+ \d+ table_inh
+ CREATE TABLE table_like1 (LIKE table_with_oids, LIKE table_with_opts);
+ SELECT relname, relhasoids, reloptions FROM pg_class WHERE oid = 'table_like1'::regclass;
+ \d+ table_like1
+ CREATE TABLE table_like2 (LIKE table_with_opts, LIKE table_with_oids) WITH (fillfactor=75);
+ SELECT relname, relhasoids, reloptions FROM pg_class WHERE oid = 'table_like2'::regclass;
+ \d+ table_like2
+ DROP TABLE table_with_oids, table_with_opts, table_inh, table_like1, table_like2 CASCADE;