diff -cprN head/doc/src/sgml/ref/create_table.sgml work/doc/src/sgml/ref/create_table.sgml
*** head/doc/src/sgml/ref/create_table.sgml	2009-09-18 14:00:41.000000000 +0900
--- work/doc/src/sgml/ref/create_table.sgml	2009-09-28 11:16:34.150010328 +0900
*************** PostgreSQL documentation
*** 24,30 ****
  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> ] [ <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> [, ... ] ) ]
--- 24,30 ----
  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> ] [ <replaceable class="PARAMETER">column_constraint</replaceable> [ ... ] ]
      | <replaceable>table_constraint</replaceable>
!     | LIKE <replaceable>parent_table</replaceable> [ { INCLUDING | EXCLUDING } { DEFAULTS | CONSTRAINTS | INDEXES | STORAGE | COMMENTS | ALL } ] ... }
      [, ... ]
  ] )
  [ INHERITS ( <replaceable>parent_table</replaceable> [, ... ] ) ]
*************** CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY 
*** 230,235 ****
--- 230,239 ----
        will always be chosen for it.
       </para>
  
+      <para>
+       Column storage parameters are also copied from parent tables.
+      </para>
+ 
  <!--
       <para>
        <productname>PostgreSQL</> automatically allows the
*************** CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY 
*** 247,253 ****
     </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
--- 251,257 ----
     </varlistentry>
  
     <varlistentry>
!     <term><literal>LIKE <replaceable>parent_table</replaceable> [ { INCLUDING | EXCLUDING } { DEFAULTS | CONSTRAINTS | INDEXES | STORAGE | COMMENTS | ALL } ]</literal></term>
      <listitem>
       <para>
        The <literal>LIKE</literal> clause specifies a table from which
*************** CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY 
*** 281,286 ****
--- 285,307 ----
        specified.
       </para>
       <para>
+       Storage parameters for the copied column definitions will only be copied
+       if <literal>INCLUDING STORAGE</literal> is specified.  The default
+       behavior is to exclude storage parameters, resulting in the copied
+       columns in the new table having type-specific default parameters.  For
+       more on storage parameters, see <xref linkend="storage-toast">.
+      </para>
+      <para>
+       Comments for the copied column and constraint definitions will only be
+       copied if <literal>INCLUDING COMMENTS</literal> is specified.  The
+       default behavior is to exclude comments, resulting in the
+       copied columns and constraints in the new table having no comments.
+      </para>
+      <para>
+       <literal>INCLUDING ALL</literal> is an abbreviated form of
+       <literal>INCLUDING DEFAULTS INCLUDING CONSTRAINTS INCLUDING INDEXES INCLUDING STORAGES INCLUDING COMMENTS</literal>.
+      </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
diff -cprN head/src/backend/access/common/tupdesc.c work/src/backend/access/common/tupdesc.c
*** head/src/backend/access/common/tupdesc.c	2009-08-03 07:14:51.000000000 +0900
--- work/src/backend/access/common/tupdesc.c	2009-09-28 10:52:37.666113379 +0900
*************** BuildDescForRelation(List *schema)
*** 558,563 ****
--- 558,565 ----
  		has_not_null |= entry->is_not_null;
  		desc->attrs[attnum - 1]->attislocal = entry->is_local;
  		desc->attrs[attnum - 1]->attinhcount = entry->inhcount;
+ 		if (entry->storage)
+ 			desc->attrs[attnum - 1]->attstorage = entry->storage;
  	}
  
  	if (has_not_null)
diff -cprN head/src/backend/catalog/pg_constraint.c work/src/backend/catalog/pg_constraint.c
*** head/src/backend/catalog/pg_constraint.c	2009-07-28 11:56:29.000000000 +0900
--- work/src/backend/catalog/pg_constraint.c	2009-09-28 10:52:37.666113379 +0900
*************** AlterConstraintNamespaces(Oid ownerId, O
*** 702,704 ****
--- 702,766 ----
  
  	heap_close(conRel, RowExclusiveLock);
  }
+ 
+ /*
+  * GetConstraintByName
+  *		Find a constraint with the specified name.
+  */
+ Oid
+ GetConstraintByName(Oid relid, const char *conname)
+ {
+ 	Relation	pg_constraint;
+ 	HeapTuple	tuple;
+ 	SysScanDesc scan;
+ 	ScanKeyData skey[1];
+ 	Oid			conOid = InvalidOid;
+ 
+ 	/*
+ 	 * Fetch the constraint tuple from pg_constraint.  There may be more than
+ 	 * one match, because constraints are not required to have unique names;
+ 	 * if so, error out.
+ 	 */
+ 	pg_constraint = heap_open(ConstraintRelationId, AccessShareLock);
+ 
+ 	ScanKeyInit(&skey[0],
+ 				Anum_pg_constraint_conrelid,
+ 				BTEqualStrategyNumber, F_OIDEQ, relid);
+ 
+ 	scan = systable_beginscan(pg_constraint, ConstraintRelidIndexId, true,
+ 							  SnapshotNow, 1, skey);
+ 
+ 	while (HeapTupleIsValid(tuple = systable_getnext(scan)))
+ 	{
+ 		Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tuple);
+ 
+ 		if (strcmp(NameStr(con->conname), conname) == 0)
+ 		{
+ 			if (OidIsValid(conOid))
+ 			{
+ 				char *relname = get_rel_name(relid);
+ 				ereport(ERROR,
+ 						(errcode(ERRCODE_DUPLICATE_OBJECT),
+ 				 errmsg("table \"%s\" has multiple constraints named \"%s\"",
+ 					(relname ? relname : "(unknown)"), conname)));
+ 			}
+ 			conOid = HeapTupleGetOid(tuple);
+ 		}
+ 	}
+ 
+ 	systable_endscan(scan);
+ 
+ 	/* If no constraint exists for the relation specified, notify user */
+ 	if (!OidIsValid(conOid))
+ 	{
+ 		char *relname = get_rel_name(relid);
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_UNDEFINED_OBJECT),
+ 				 errmsg("constraint \"%s\" for table \"%s\" does not exist",
+ 						conname, (relname ? relname : "(unknown)"))));
+ 	}
+ 
+ 	heap_close(pg_constraint, AccessShareLock);
+ 
+ 	return conOid;
+ }
diff -cprN head/src/backend/commands/comment.c work/src/backend/commands/comment.c
*** head/src/backend/commands/comment.c	2009-06-11 23:48:55.000000000 +0900
--- work/src/backend/commands/comment.c	2009-09-28 10:52:55.412382289 +0900
*************** DeleteSharedComments(Oid oid, Oid classo
*** 463,468 ****
--- 463,523 ----
  }
  
  /*
+  * GetComment -- get the comment for an object, or null if not found.
+  */
+ char *
+ GetComment(Oid oid, Oid classoid, int32 subid)
+ {
+ 	Relation	description;
+ 	ScanKeyData skey[3];
+ 	SysScanDesc sd;
+ 	TupleDesc	tupdesc;
+ 	HeapTuple	tuple;
+ 	char	   *comment;
+ 
+ 	/* Use the index to search for a matching old tuple */
+ 
+ 	ScanKeyInit(&skey[0],
+ 				Anum_pg_description_objoid,
+ 				BTEqualStrategyNumber, F_OIDEQ,
+ 				ObjectIdGetDatum(oid));
+ 	ScanKeyInit(&skey[1],
+ 				Anum_pg_description_classoid,
+ 				BTEqualStrategyNumber, F_OIDEQ,
+ 				ObjectIdGetDatum(classoid));
+ 	ScanKeyInit(&skey[2],
+ 				Anum_pg_description_objsubid,
+ 				BTEqualStrategyNumber, F_INT4EQ,
+ 				Int32GetDatum(subid));
+ 
+ 	description = heap_open(DescriptionRelationId, AccessShareLock);
+ 	tupdesc = RelationGetDescr(description);
+ 
+ 	sd = systable_beginscan(description, DescriptionObjIndexId, true,
+ 							SnapshotNow, 3, skey);
+ 
+ 	comment  = NULL;
+ 	while ((tuple = systable_getnext(sd)) != NULL)
+ 	{
+ 		Datum	value;
+ 		bool	isnull;
+ 
+ 		/* Found the tuple, get description field */
+ 		value = heap_getattr(tuple, Anum_pg_description_description, tupdesc, &isnull);
+ 		if (!isnull)
+ 			comment = TextDatumGetCString(value);
+ 		break;					/* Assume there can be only one match */
+ 	}
+ 
+ 	systable_endscan(sd);
+ 
+ 	/* Done */
+ 	heap_close(description, AccessShareLock);
+ 
+ 	return comment;
+ }
+ 
+ /*
   * CommentRelation --
   *
   * This routine is used to add/drop a comment from a relation, where
*************** CommentConstraint(List *qualname, char *
*** 1064,1075 ****
  	List	   *relName;
  	char	   *conName;
  	RangeVar   *rel;
! 	Relation	pg_constraint,
! 				relation;
! 	HeapTuple	tuple;
! 	SysScanDesc scan;
! 	ScanKeyData skey[1];
! 	Oid			conOid = InvalidOid;
  
  	/* Separate relname and constraint name */
  	nnames = list_length(qualname);
--- 1119,1126 ----
  	List	   *relName;
  	char	   *conName;
  	RangeVar   *rel;
! 	Relation	relation;
! 	Oid			conOid;
  
  	/* Separate relname and constraint name */
  	nnames = list_length(qualname);
*************** CommentConstraint(List *qualname, char *
*** 1088,1137 ****
  		aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
  					   RelationGetRelationName(relation));
  
! 	/*
! 	 * Fetch the constraint tuple from pg_constraint.  There may be more than
! 	 * one match, because constraints are not required to have unique names;
! 	 * if so, error out.
! 	 */
! 	pg_constraint = heap_open(ConstraintRelationId, AccessShareLock);
! 
! 	ScanKeyInit(&skey[0],
! 				Anum_pg_constraint_conrelid,
! 				BTEqualStrategyNumber, F_OIDEQ,
! 				ObjectIdGetDatum(RelationGetRelid(relation)));
! 
! 	scan = systable_beginscan(pg_constraint, ConstraintRelidIndexId, true,
! 							  SnapshotNow, 1, skey);
! 
! 	while (HeapTupleIsValid(tuple = systable_getnext(scan)))
! 	{
! 		Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tuple);
! 
! 		if (strcmp(NameStr(con->conname), conName) == 0)
! 		{
! 			if (OidIsValid(conOid))
! 				ereport(ERROR,
! 						(errcode(ERRCODE_DUPLICATE_OBJECT),
! 				 errmsg("table \"%s\" has multiple constraints named \"%s\"",
! 						RelationGetRelationName(relation), conName)));
! 			conOid = HeapTupleGetOid(tuple);
! 		}
! 	}
! 
! 	systable_endscan(scan);
! 
! 	/* If no constraint exists for the relation specified, notify user */
! 	if (!OidIsValid(conOid))
! 		ereport(ERROR,
! 				(errcode(ERRCODE_UNDEFINED_OBJECT),
! 				 errmsg("constraint \"%s\" for table \"%s\" does not exist",
! 						conName, RelationGetRelationName(relation))));
  
  	/* Call CreateComments() to create/drop the comments */
  	CreateComments(conOid, ConstraintRelationId, 0, comment);
  
  	/* Done, but hold lock on relation */
- 	heap_close(pg_constraint, AccessShareLock);
  	heap_close(relation, NoLock);
  }
  
--- 1139,1150 ----
  		aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
  					   RelationGetRelationName(relation));
  
! 	conOid = GetConstraintByName(RelationGetRelid(relation), conName);
  
  	/* Call CreateComments() to create/drop the comments */
  	CreateComments(conOid, ConstraintRelationId, 0, comment);
  
  	/* Done, but hold lock on relation */
  	heap_close(relation, NoLock);
  }
  
diff -cprN head/src/backend/commands/tablecmds.c work/src/backend/commands/tablecmds.c
*** head/src/backend/commands/tablecmds.c	2009-09-27 07:42:01.000000000 +0900
--- work/src/backend/commands/tablecmds.c	2009-09-28 10:52:55.414056751 +0900
***************
*** 39,44 ****
--- 39,45 ----
  #include "catalog/storage.h"
  #include "catalog/toasting.h"
  #include "commands/cluster.h"
+ #include "commands/comment.h"
  #include "commands/defrem.h"
  #include "commands/sequence.h"
  #include "commands/tablecmds.h"
*************** truncate_check_rel(Relation rel)
*** 1099,1104 ****
--- 1100,1118 ----
  	CheckTableNotInUse(rel, "TRUNCATE");
  }
  
+ static const char *
+ storage_name(char c)
+ {
+ 	switch (c)
+ 	{
+ 		case 'p': return "PLAIN";
+ 		case 'm': return "MAIN";
+ 		case 'x': return "EXTENDED";
+ 		case 'e': return "EXTERNAL";
+ 		default: return "???";
+ 	}
+ }
+ 
  /*----------
   * MergeAttributes
   *		Returns new schema given initial schema and superclasses.
*************** MergeAttributes(List *schema, List *supe
*** 1167,1172 ****
--- 1181,1187 ----
  	List	   *constraints = NIL;
  	int			parentsWithOids = 0;
  	bool		have_bogus_defaults = false;
+ 	bool		have_bogus_comments = false;
  	char	   *bogus_marker = "Bogus!";		/* marks conflicting defaults */
  	int			child_attno;
  
*************** MergeAttributes(List *schema, List *supe
*** 1322,1327 ****
--- 1337,1354 ----
  							 errdetail("%s versus %s",
  									   TypeNameToString(def->typeName),
  									   format_type_be(attribute->atttypid))));
+ 
+ 				/* Copy storage parameter */
+ 				if (def->storage == 0)
+ 					def->storage = attribute->attstorage;
+ 				else if (def->storage != attribute->attstorage)
+ 					ereport(ERROR,
+ 							(errcode(ERRCODE_DATATYPE_MISMATCH),
+ 						errmsg("inherited column \"%s\" has a storage parameter conflict",
+ 							   attributeName),
+ 							   errdetail("%s versus %s", storage_name(def->storage),
+ 										 storage_name(attribute->attstorage))));
+ 
  				def->inhcount++;
  				/* Merge of NOT NULL constraints = OR 'em together */
  				def->is_not_null |= attribute->attnotnull;
*************** MergeAttributes(List *schema, List *supe
*** 1343,1348 ****
--- 1370,1376 ----
  				def->raw_default = NULL;
  				def->cooked_default = NULL;
  				def->constraints = NIL;
+ 				def->storage = attribute->attstorage;
  				inhSchema = lappend(inhSchema, def);
  				newattno[parent_attno - 1] = ++child_attno;
  			}
*************** MergeAttributes(List *schema, List *supe
*** 1480,1485 ****
--- 1508,1525 ----
  							 errdetail("%s versus %s",
  									   TypeNameToString(def->typeName),
  									   TypeNameToString(newdef->typeName))));
+ 
+ 				/* Copy storage parameter */
+ 				if (def->storage == 0)
+ 					def->storage = newdef->storage;
+ 				else if (newdef->storage != 0 && def->storage != newdef->storage)
+ 					ereport(ERROR,
+ 							(errcode(ERRCODE_DATATYPE_MISMATCH),
+ 						errmsg("column \"%s\" has a storage parameter conflict",
+ 							   attributeName),
+ 							   errdetail("%s versus %s", storage_name(def->storage),
+ 										 storage_name(newdef->storage))));
+ 
  				/* Mark the column as locally defined */
  				def->is_local = true;
  				/* Merge of NOT NULL constraints = OR 'em together */
*************** MergeAttributes(List *schema, List *supe
*** 1532,1537 ****
--- 1572,1591 ----
  		}
  	}
  
+ 	/* Raise an error if we found conflicting comments. */
+ 	if (have_bogus_comments)
+ 	{
+ 		foreach(entry, schema)
+ 		{
+ 			ColumnDef  *def = lfirst(entry);
+ 
+ 			if (def->cooked_default == bogus_marker)
+ 				ereport(ERROR,
+ 						(errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
+ 				  errmsg("column \"%s\" inherits conflicting comments", def->colname)));
+ 		}
+ 	}
+ 
  	*supOids = parentOids;
  	*supconstr = constraints;
  	*supOidCount = parentsWithOids;
diff -cprN head/src/backend/nodes/copyfuncs.c work/src/backend/nodes/copyfuncs.c
*** head/src/backend/nodes/copyfuncs.c	2009-09-23 08:43:37.000000000 +0900
--- work/src/backend/nodes/copyfuncs.c	2009-09-28 11:11:53.099995625 +0900
*************** _copyInhRelation(InhRelation *from)
*** 2416,2422 ****
  	InhRelation *newnode = makeNode(InhRelation);
  
  	COPY_NODE_FIELD(relation);
! 	COPY_NODE_FIELD(options);
  
  	return newnode;
  }
--- 2416,2422 ----
  	InhRelation *newnode = makeNode(InhRelation);
  
  	COPY_NODE_FIELD(relation);
! 	COPY_SCALAR_FIELD(options);
  
  	return newnode;
  }
diff -cprN head/src/backend/nodes/equalfuncs.c work/src/backend/nodes/equalfuncs.c
*** head/src/backend/nodes/equalfuncs.c	2009-09-23 08:43:38.000000000 +0900
--- work/src/backend/nodes/equalfuncs.c	2009-09-28 11:12:33.998113738 +0900
*************** static bool
*** 1087,1093 ****
  _equalInhRelation(InhRelation *a, InhRelation *b)
  {
  	COMPARE_NODE_FIELD(relation);
! 	COMPARE_NODE_FIELD(options);
  
  	return true;
  }
--- 1087,1093 ----
  _equalInhRelation(InhRelation *a, InhRelation *b)
  {
  	COMPARE_NODE_FIELD(relation);
! 	COMPARE_SCALAR_FIELD(options);
  
  	return true;
  }
diff -cprN head/src/backend/parser/gram.y work/src/backend/parser/gram.y
*** head/src/backend/parser/gram.y	2009-09-23 08:43:38.000000000 +0900
--- work/src/backend/parser/gram.y	2009-09-28 11:17:17.711010198 +0900
*************** static TypeName *TableFuncTypeName(List 
*** 399,406 ****
  %type <keyword> col_name_keyword reserved_keyword
  
  %type <node>	TableConstraint TableLikeClause
! %type <list>	TableLikeOptionList
! %type <ival>	TableLikeOption
  %type <list>	ColQualList
  %type <node>	ColConstraint ColConstraintElem ConstraintAttr
  %type <ival>	key_actions key_delete key_match key_update key_action
--- 399,405 ----
  %type <keyword> col_name_keyword reserved_keyword
  
  %type <node>	TableConstraint TableLikeClause
! %type <ival>	TableLikeOptionList TableLikeOption
  %type <list>	ColQualList
  %type <node>	ColConstraint ColConstraintElem ConstraintAttr
  %type <ival>	key_actions key_delete key_match key_update key_action
*************** static TypeName *TableFuncTypeName(List 
*** 458,464 ****
  
  	CACHE CALLED CASCADE CASCADED CASE CAST CATALOG_P CHAIN CHAR_P
  	CHARACTER CHARACTERISTICS CHECK CHECKPOINT CLASS CLOSE
! 	CLUSTER COALESCE COLLATE COLUMN COMMENT COMMIT
  	COMMITTED CONCURRENTLY CONFIGURATION CONNECTION CONSTRAINT CONSTRAINTS
  	CONTENT_P CONTINUE_P CONVERSION_P COPY COST CREATE CREATEDB
  	CREATEROLE CREATEUSER CROSS CSV CURRENT_P
--- 457,463 ----
  
  	CACHE CALLED CASCADE CASCADED CASE CAST CATALOG_P CHAIN CHAR_P
  	CHARACTER CHARACTERISTICS CHECK CHECKPOINT CLASS CLOSE
! 	CLUSTER COALESCE COLLATE COLUMN COMMENT COMMENTS COMMIT
  	COMMITTED CONCURRENTLY CONFIGURATION CONNECTION CONSTRAINT CONSTRAINTS
  	CONTENT_P CONTINUE_P CONVERSION_P COPY COST CREATE CREATEDB
  	CREATEROLE CREATEUSER CROSS CSV CURRENT_P
*************** TableLikeClause:
*** 2392,2408 ****
  		;
  
  TableLikeOptionList:
! 				TableLikeOptionList TableLikeOption	{ $$ = lappend_int($1, $2); }
! 				| /* EMPTY */						{ $$ = NIL; }
  		;
  
  TableLikeOption:
! 				INCLUDING DEFAULTS					{ $$ = 	CREATE_TABLE_LIKE_INCLUDING_DEFAULTS; }
! 				| EXCLUDING DEFAULTS				{ $$ = 	CREATE_TABLE_LIKE_EXCLUDING_DEFAULTS; }
! 				| INCLUDING CONSTRAINTS				{ $$ = 	CREATE_TABLE_LIKE_INCLUDING_CONSTRAINTS; }
! 				| EXCLUDING CONSTRAINTS				{ $$ = 	CREATE_TABLE_LIKE_EXCLUDING_CONSTRAINTS; }
! 				| INCLUDING INDEXES					{ $$ = 	CREATE_TABLE_LIKE_INCLUDING_INDEXES; }
! 				| EXCLUDING INDEXES					{ $$ = 	CREATE_TABLE_LIKE_EXCLUDING_INDEXES; }
  		;
  
  
--- 2391,2408 ----
  		;
  
  TableLikeOptionList:
! 				TableLikeOptionList INCLUDING TableLikeOption	{ $$ = $1 | $3; }
! 				| TableLikeOptionList EXCLUDING TableLikeOption	{ $$ = $1 & ~$3; }
! 				| /* EMPTY */						{ $$ = 0; }
  		;
  
  TableLikeOption:
! 				DEFAULTS			{ $$ = CREATE_TABLE_LIKE_DEFAULTS; }
! 				| CONSTRAINTS		{ $$ = CREATE_TABLE_LIKE_CONSTRAINTS; }
! 				| INDEXES			{ $$ = CREATE_TABLE_LIKE_INDEXES; }
! 				| STORAGE			{ $$ = CREATE_TABLE_LIKE_STORAGE; }
! 				| COMMENTS			{ $$ = CREATE_TABLE_LIKE_COMMENTS; }
! 				| ALL				{ $$ = CREATE_TABLE_LIKE_ALL; }
  		;
  
  
*************** unreserved_keyword:
*** 10325,10330 ****
--- 10325,10331 ----
  			| CLOSE
  			| CLUSTER
  			| COMMENT
+ 			| COMMENTS
  			| COMMIT
  			| COMMITTED
  			| CONCURRENTLY
diff -cprN head/src/backend/parser/parse_utilcmd.c work/src/backend/parser/parse_utilcmd.c
*** head/src/backend/parser/parse_utilcmd.c	2009-07-30 11:45:37.000000000 +0900
--- work/src/backend/parser/parse_utilcmd.c	2009-09-28 11:04:46.729994997 +0900
***************
*** 36,41 ****
--- 36,42 ----
  #include "catalog/pg_constraint.h"
  #include "catalog/pg_opclass.h"
  #include "catalog/pg_type.h"
+ #include "commands/comment.h"
  #include "commands/defrem.h"
  #include "commands/tablecmds.h"
  #include "commands/tablespace.h"
*************** transformInhRelation(ParseState *pstate,
*** 546,555 ****
  	TupleDesc	tupleDesc;
  	TupleConstr *constr;
  	AclResult	aclresult;
! 	bool		including_defaults = false;
! 	bool		including_constraints = false;
! 	bool		including_indexes = false;
! 	ListCell   *elem;
  
  	relation = parserOpenTable(pstate, inhRelation->relation, AccessShareLock);
  
--- 547,553 ----
  	TupleDesc	tupleDesc;
  	TupleConstr *constr;
  	AclResult	aclresult;
! 	char	   *comment;
  
  	relation = parserOpenTable(pstate, inhRelation->relation, AccessShareLock);
  
*************** transformInhRelation(ParseState *pstate,
*** 571,606 ****
  	tupleDesc = RelationGetDescr(relation);
  	constr = tupleDesc->constr;
  
- 	foreach(elem, inhRelation->options)
- 	{
- 		int			option = lfirst_int(elem);
- 
- 		switch (option)
- 		{
- 			case CREATE_TABLE_LIKE_INCLUDING_DEFAULTS:
- 				including_defaults = true;
- 				break;
- 			case CREATE_TABLE_LIKE_EXCLUDING_DEFAULTS:
- 				including_defaults = false;
- 				break;
- 			case CREATE_TABLE_LIKE_INCLUDING_CONSTRAINTS:
- 				including_constraints = true;
- 				break;
- 			case CREATE_TABLE_LIKE_EXCLUDING_CONSTRAINTS:
- 				including_constraints = false;
- 				break;
- 			case CREATE_TABLE_LIKE_INCLUDING_INDEXES:
- 				including_indexes = true;
- 				break;
- 			case CREATE_TABLE_LIKE_EXCLUDING_INDEXES:
- 				including_indexes = false;
- 				break;
- 			default:
- 				elog(ERROR, "unrecognized CREATE TABLE LIKE option: %d",
- 					 option);
- 		}
- 	}
- 
  	/*
  	 * Insert the copied attributes into the cxt for the new table definition.
  	 */
--- 569,574 ----
*************** transformInhRelation(ParseState *pstate,
*** 642,648 ****
  		/*
  		 * Copy default, if present and the default has been requested
  		 */
! 		if (attribute->atthasdef && including_defaults)
  		{
  			char	   *this_default = NULL;
  			AttrDefault *attrdef;
--- 610,617 ----
  		/*
  		 * Copy default, if present and the default has been requested
  		 */
! 		if (attribute->atthasdef &&
! 			(inhRelation->options & CREATE_TABLE_LIKE_DEFAULTS))
  		{
  			char	   *this_default = NULL;
  			AttrDefault *attrdef;
*************** transformInhRelation(ParseState *pstate,
*** 668,680 ****
  
  			def->cooked_default = pstrdup(this_default);
  		}
  	}
  
  	/*
  	 * Copy CHECK constraints if requested, being careful to adjust attribute
  	 * numbers
  	 */
! 	if (including_constraints && tupleDesc->constr)
  	{
  		AttrNumber *attmap = varattnos_map_schema(tupleDesc, cxt->columns);
  		int			ccnum;
--- 637,670 ----
  
  			def->cooked_default = pstrdup(this_default);
  		}
+ 
+ 		/* Likewise, copy storage if requested */
+ 		if (inhRelation->options & CREATE_TABLE_LIKE_STORAGE)
+ 			def->storage = attribute->attstorage;
+ 
+ 		/* Likewise, copy comment if requested */
+ 		if ((inhRelation->options & CREATE_TABLE_LIKE_COMMENTS) &&
+ 			(comment = GetComment(attribute->attrelid, RelationRelationId,
+ 			attribute->attnum)) != NULL)
+ 		{
+ 			CommentStmt *stmt = makeNode(CommentStmt);
+ 
+ 			stmt->objtype = OBJECT_COLUMN;
+ 			stmt->objname = list_make3(makeString(cxt->relation->schemaname),
+ 									   makeString(cxt->relation->relname),
+ 									   makeString(def->colname));
+ 			stmt->objargs = NIL;
+ 			stmt->comment = comment;
+ 
+ 			cxt->alist = lappend(cxt->alist, stmt);
+ 		}
  	}
  
  	/*
  	 * Copy CHECK constraints if requested, being careful to adjust attribute
  	 * numbers
  	 */
! 	if ((inhRelation->options & CREATE_TABLE_LIKE_CONSTRAINTS) && tupleDesc->constr)
  	{
  		AttrNumber *attmap = varattnos_map_schema(tupleDesc, cxt->columns);
  		int			ccnum;
*************** transformInhRelation(ParseState *pstate,
*** 694,706 ****
  			n->raw_expr = NULL;
  			n->cooked_expr = nodeToString(ccbin_node);
  			cxt->ckconstraints = lappend(cxt->ckconstraints, n);
  		}
  	}
  
  	/*
  	 * Likewise, copy indexes if requested
  	 */
! 	if (including_indexes && relation->rd_rel->relhasindex)
  	{
  		AttrNumber *attmap = varattnos_map_schema(tupleDesc, cxt->columns);
  		List	   *parent_indexes;
--- 684,714 ----
  			n->raw_expr = NULL;
  			n->cooked_expr = nodeToString(ccbin_node);
  			cxt->ckconstraints = lappend(cxt->ckconstraints, n);
+ 
+ 			/* Copy comment on constraint */
+ 			if ((inhRelation->options & CREATE_TABLE_LIKE_COMMENTS) &&
+ 				(comment = GetComment(GetConstraintByName(RelationGetRelid(
+ 				relation), n->conname), ConstraintRelationId, 0)) != NULL)
+ 			{
+ 				CommentStmt *stmt = makeNode(CommentStmt);
+ 
+ 				stmt->objtype = OBJECT_CONSTRAINT;
+ 				stmt->objname = list_make3(makeString(cxt->relation->schemaname),
+ 										   makeString(cxt->relation->relname),
+ 										   makeString(n->conname));
+ 				stmt->objargs = NIL;
+ 				stmt->comment = comment;
+ 
+ 				cxt->alist = lappend(cxt->alist, stmt);
+ 			}
  		}
  	}
  
  	/*
  	 * Likewise, copy indexes if requested
  	 */
! 	if ((inhRelation->options & CREATE_TABLE_LIKE_INDEXES) &&
! 		relation->rd_rel->relhasindex)
  	{
  		AttrNumber *attmap = varattnos_map_schema(tupleDesc, cxt->columns);
  		List	   *parent_indexes;
*************** transformInhRelation(ParseState *pstate,
*** 719,724 ****
--- 727,734 ----
  			/* Build CREATE INDEX statement to recreate the parent_index */
  			index_stmt = generateClonedIndexStmt(cxt, parent_index, attmap);
  
+ 			/* XXX: should copy comment on index? */
+ 
  			/* Save it in the inh_indexes list for the time being */
  			cxt->inh_indexes = lappend(cxt->inh_indexes, index_stmt);
  
diff -cprN head/src/bin/psql/sql_help.c work/src/bin/psql/sql_help.c
*** head/src/bin/psql/sql_help.c	2009-09-27 17:07:40.000000000 +0900
--- work/src/bin/psql/sql_help.c	2009-09-28 11:13:06.231320101 +0900
***************
*** 3,9 ****
   * *** generated from the DocBook documentation.
   *
   * generated by
!  *     /usr/local/bin/suidperl create_help.pl ../../../doc/src/sgml/ref sql_help
   *
   */
  
--- 3,9 ----
   * *** generated from the DocBook documentation.
   *
   * generated by
!  *     /usr/bin/perl create_help.pl ../../../doc/src/sgml/ref sql_help
   *
   */
  
*************** sql_help_CREATE_TABLE(PQExpBuffer buf)
*** 1279,1285 ****
  					  "CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } ] TABLE %s ( [\n"
  					  "  { %s %s [ DEFAULT %s ] [ %s [ ... ] ]\n"
  					  "    | %s\n"
! 					  "    | LIKE %s [ { INCLUDING | EXCLUDING } { DEFAULTS | CONSTRAINTS | INDEXES } ] ... }\n"
  					  "    [, ... ]\n"
  					  "] )\n"
  					  "[ INHERITS ( %s [, ... ] ) ]\n"
--- 1279,1285 ----
  					  "CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } ] TABLE %s ( [\n"
  					  "  { %s %s [ DEFAULT %s ] [ %s [ ... ] ]\n"
  					  "    | %s\n"
! 					  "    | LIKE %s [ { INCLUDING | EXCLUDING } { DEFAULTS | CONSTRAINTS | INDEXES | STORAGE | COMMENTS } ] ... }\n"
  					  "    [, ... ]\n"
  					  "] )\n"
  					  "[ INHERITS ( %s [, ... ] ) ]\n"
diff -cprN head/src/include/catalog/pg_constraint.h work/src/include/catalog/pg_constraint.h
*** head/src/include/catalog/pg_constraint.h	2009-07-28 11:56:31.000000000 +0900
--- work/src/include/catalog/pg_constraint.h	2009-09-28 10:52:37.673113381 +0900
*************** extern char *ChooseConstraintName(const 
*** 226,230 ****
--- 226,231 ----
  
  extern void AlterConstraintNamespaces(Oid ownerId, Oid oldNspId,
  						  Oid newNspId, bool isType);
+ extern Oid GetConstraintByName(Oid relid, const char *conname);
  
  #endif   /* PG_CONSTRAINT_H */
diff -cprN head/src/include/commands/comment.h work/src/include/commands/comment.h
*** head/src/include/commands/comment.h	2009-06-11 23:49:11.000000000 +0900
--- work/src/include/commands/comment.h	2009-09-28 10:52:37.673113381 +0900
*************** extern void DeleteSharedComments(Oid oid
*** 39,42 ****
--- 39,44 ----
  
  extern void CreateSharedComments(Oid oid, Oid classoid, char *comment);
  
+ extern char *GetComment(Oid oid, Oid classoid, int32 subid);
+ 
  #endif   /* COMMENT_H */
diff -cprN head/src/include/nodes/parsenodes.h work/src/include/nodes/parsenodes.h
*** head/src/include/nodes/parsenodes.h	2009-09-23 08:43:41.000000000 +0900
--- work/src/include/nodes/parsenodes.h	2009-09-28 10:58:36.806014857 +0900
*************** typedef struct ColumnDef
*** 461,466 ****
--- 461,467 ----
  	int			inhcount;		/* number of times column is inherited */
  	bool		is_local;		/* column has local (non-inherited) def'n */
  	bool		is_not_null;	/* NOT NULL constraint specified? */
+ 	char		storage;		/* storage parameter of column */
  	Node	   *raw_default;	/* default value (untransformed parse tree) */
  	char	   *cooked_default; /* nodeToString representation */
  	List	   *constraints;	/* other constraints on column */
*************** typedef struct InhRelation
*** 473,489 ****
  {
  	NodeTag		type;
  	RangeVar   *relation;
! 	List	   *options;		/* integer List of CreateStmtLikeOption */
  } InhRelation;
  
  typedef enum CreateStmtLikeOption
  {
! 	CREATE_TABLE_LIKE_INCLUDING_DEFAULTS,
! 	CREATE_TABLE_LIKE_EXCLUDING_DEFAULTS,
! 	CREATE_TABLE_LIKE_INCLUDING_CONSTRAINTS,
! 	CREATE_TABLE_LIKE_EXCLUDING_CONSTRAINTS,
! 	CREATE_TABLE_LIKE_INCLUDING_INDEXES,
! 	CREATE_TABLE_LIKE_EXCLUDING_INDEXES
  } CreateStmtLikeOption;
  
  /*
--- 474,490 ----
  {
  	NodeTag		type;
  	RangeVar   *relation;
! 	bits32		options;		/* bitmap of CreateStmtLikeOption */
  } InhRelation;
  
  typedef enum CreateStmtLikeOption
  {
! 	CREATE_TABLE_LIKE_DEFAULTS		= 1 << 0,
! 	CREATE_TABLE_LIKE_CONSTRAINTS	= 1 << 1,
! 	CREATE_TABLE_LIKE_INDEXES		= 1 << 2,
! 	CREATE_TABLE_LIKE_STORAGE		= 1 << 3,
! 	CREATE_TABLE_LIKE_COMMENTS		= 1 << 4,
! 	CREATE_TABLE_LIKE_ALL			= 0xFFFFFFFF
  } CreateStmtLikeOption;
  
  /*
diff -cprN head/src/include/parser/kwlist.h work/src/include/parser/kwlist.h
*** head/src/include/parser/kwlist.h	2009-09-23 08:43:41.000000000 +0900
--- work/src/include/parser/kwlist.h	2009-09-28 10:52:37.674113571 +0900
*************** PG_KEYWORD("coalesce", COALESCE, COL_NAM
*** 80,85 ****
--- 80,86 ----
  PG_KEYWORD("collate", COLLATE, RESERVED_KEYWORD)
  PG_KEYWORD("column", COLUMN, RESERVED_KEYWORD)
  PG_KEYWORD("comment", COMMENT, UNRESERVED_KEYWORD)
+ PG_KEYWORD("comments", COMMENTS, UNRESERVED_KEYWORD)
  PG_KEYWORD("commit", COMMIT, UNRESERVED_KEYWORD)
  PG_KEYWORD("committed", COMMITTED, UNRESERVED_KEYWORD)
  PG_KEYWORD("concurrently", CONCURRENTLY, UNRESERVED_KEYWORD)
diff -cprN head/src/test/regress/expected/inherit.out work/src/test/regress/expected/inherit.out
*** head/src/test/regress/expected/inherit.out	2009-08-02 04:59:41.000000000 +0900
--- work/src/test/regress/expected/inherit.out	2009-09-28 10:52:37.675113379 +0900
*************** drop table pp1 cascade;
*** 906,908 ****
--- 906,1012 ----
  NOTICE:  drop cascades to 2 other objects
  DETAIL:  drop cascades to table cc1
  drop cascades to table cc2
+ -- including storage and comments
+ CREATE TABLE t1 (a text CHECK (length(a) > 2), b text);
+ COMMENT ON COLUMN t1.a IS 'A';
+ COMMENT ON COLUMN t1.b IS 'B';
+ COMMENT ON CONSTRAINT t1_a_check ON t1 IS 't1_a_check';
+ ALTER TABLE t1 ALTER COLUMN a SET STORAGE MAIN;
+ CREATE TABLE t2 (c text);
+ ALTER TABLE t2 ALTER COLUMN c SET STORAGE EXTERNAL;
+ COMMENT ON COLUMN t2.c IS 'C';
+ CREATE TABLE t3 (a text CHECK (length(a) < 5), c text);
+ ALTER TABLE t3 ALTER COLUMN c SET STORAGE EXTERNAL;
+ ALTER TABLE t3 ALTER COLUMN a SET STORAGE MAIN;
+ COMMENT ON COLUMN t3.a IS 'A3';
+ COMMENT ON COLUMN t3.c IS 'C';
+ COMMENT ON CONSTRAINT t3_a_check ON t3 IS 't3_a_check';
+ CREATE TABLE t4 (a text, c text);
+ ALTER TABLE t4 ALTER COLUMN c SET STORAGE EXTERNAL;
+ CREATE TABLE t12_storage (LIKE t1 INCLUDING STORAGE, LIKE t2 INCLUDING STORAGE);
+ \d+ t12_storage
+              Table "public.t12_storage"
+  Column | Type | Modifiers | Storage  | Description 
+ --------+------+-----------+----------+-------------
+  a      | text |           | main     | 
+  b      | text |           | extended | 
+  c      | text |           | external | 
+ Has OIDs: no
+ 
+ CREATE TABLE t12_comments (LIKE t1 INCLUDING COMMENTS, LIKE t2 INCLUDING COMMENTS);
+ \d+ t12_comments
+             Table "public.t12_comments"
+  Column | Type | Modifiers | Storage  | Description 
+ --------+------+-----------+----------+-------------
+  a      | text |           | extended | A
+  b      | text |           | extended | B
+  c      | text |           | extended | C
+ Has OIDs: no
+ 
+ CREATE TABLE t1_inh (LIKE t1 INCLUDING CONSTRAINTS INCLUDING COMMENTS) INHERITS (t1);
+ NOTICE:  merging column "a" with inherited definition
+ NOTICE:  merging column "b" with inherited definition
+ NOTICE:  merging constraint "t1_a_check" with inherited definition
+ \d+ t1_inh
+                Table "public.t1_inh"
+  Column | Type | Modifiers | Storage  | Description 
+ --------+------+-----------+----------+-------------
+  a      | text |           | main     | A
+  b      | text |           | extended | B
+ Check constraints:
+     "t1_a_check" CHECK (length(a) > 2)
+ Inherits: t1
+ Has OIDs: no
+ 
+ SELECT description FROM pg_description, pg_constraint c WHERE classoid = 'pg_constraint'::regclass AND objoid = c.oid AND c.conrelid = 't1_inh'::regclass;
+  description 
+ -------------
+  t1_a_check
+ (1 row)
+ 
+ CREATE TABLE t13_inh () INHERITS (t1, t3);
+ NOTICE:  merging multiple inherited definitions of column "a"
+ \d+ t13_inh
+                Table "public.t13_inh"
+  Column | Type | Modifiers | Storage  | Description 
+ --------+------+-----------+----------+-------------
+  a      | text |           | main     | 
+  b      | text |           | extended | 
+  c      | text |           | external | 
+ Check constraints:
+     "t1_a_check" CHECK (length(a) > 2)
+     "t3_a_check" CHECK (length(a) < 5)
+ Inherits: t1,
+           t3
+ Has OIDs: no
+ 
+ CREATE TABLE t13_like (LIKE t3 INCLUDING CONSTRAINTS INCLUDING COMMENTS INCLUDING STORAGE) INHERITS (t1);
+ NOTICE:  merging column "a" with inherited definition
+ \d+ t13_like
+               Table "public.t13_like"
+  Column | Type | Modifiers | Storage  | Description 
+ --------+------+-----------+----------+-------------
+  a      | text |           | main     | A3
+  b      | text |           | extended | 
+  c      | text |           | external | C
+ Check constraints:
+     "t1_a_check" CHECK (length(a) > 2)
+     "t3_a_check" CHECK (length(a) < 5)
+ Inherits: t1
+ Has OIDs: no
+ 
+ SELECT description FROM pg_description, pg_constraint c WHERE classoid = 'pg_constraint'::regclass AND objoid = c.oid AND c.conrelid = 't13_like'::regclass;
+  description 
+ -------------
+  t3_a_check
+ (1 row)
+ 
+ CREATE TABLE inh_error1 () INHERITS (t1, t4);
+ NOTICE:  merging multiple inherited definitions of column "a"
+ ERROR:  inherited column "a" has a storage parameter conflict
+ DETAIL:  MAIN versus EXTENDED
+ CREATE TABLE inh_error2 (LIKE t4 INCLUDING STORAGE) INHERITS (t1);
+ NOTICE:  merging column "a" with inherited definition
+ ERROR:  column "a" has a storage parameter conflict
+ DETAIL:  MAIN versus EXTENDED
+ DROP TABLE t1, t2, t3, t4, t12_storage, t12_comments, t1_inh, t13_inh, t13_like;
diff -cprN head/src/test/regress/sql/inherit.sql work/src/test/regress/sql/inherit.sql
*** head/src/test/regress/sql/inherit.sql	2008-05-10 08:32:05.000000000 +0900
--- work/src/test/regress/sql/inherit.sql	2009-09-28 10:52:37.675113379 +0900
*************** create table cc2(f4 float) inherits(pp1,
*** 276,278 ****
--- 276,317 ----
  alter table pp1 add column a2 int check (a2 > 0);
  \d cc2
  drop table pp1 cascade;
+ 
+ -- including storage and comments
+ CREATE TABLE t1 (a text CHECK (length(a) > 2), b text);
+ COMMENT ON COLUMN t1.a IS 'A';
+ COMMENT ON COLUMN t1.b IS 'B';
+ COMMENT ON CONSTRAINT t1_a_check ON t1 IS 't1_a_check';
+ ALTER TABLE t1 ALTER COLUMN a SET STORAGE MAIN;
+ 
+ CREATE TABLE t2 (c text);
+ ALTER TABLE t2 ALTER COLUMN c SET STORAGE EXTERNAL;
+ COMMENT ON COLUMN t2.c IS 'C';
+ 
+ CREATE TABLE t3 (a text CHECK (length(a) < 5), c text);
+ ALTER TABLE t3 ALTER COLUMN c SET STORAGE EXTERNAL;
+ ALTER TABLE t3 ALTER COLUMN a SET STORAGE MAIN;
+ COMMENT ON COLUMN t3.a IS 'A3';
+ COMMENT ON COLUMN t3.c IS 'C';
+ COMMENT ON CONSTRAINT t3_a_check ON t3 IS 't3_a_check';
+ 
+ CREATE TABLE t4 (a text, c text);
+ ALTER TABLE t4 ALTER COLUMN c SET STORAGE EXTERNAL;
+ 
+ CREATE TABLE t12_storage (LIKE t1 INCLUDING STORAGE, LIKE t2 INCLUDING STORAGE);
+ \d+ t12_storage
+ CREATE TABLE t12_comments (LIKE t1 INCLUDING COMMENTS, LIKE t2 INCLUDING COMMENTS);
+ \d+ t12_comments
+ CREATE TABLE t1_inh (LIKE t1 INCLUDING CONSTRAINTS INCLUDING COMMENTS) INHERITS (t1);
+ \d+ t1_inh
+ SELECT description FROM pg_description, pg_constraint c WHERE classoid = 'pg_constraint'::regclass AND objoid = c.oid AND c.conrelid = 't1_inh'::regclass;
+ CREATE TABLE t13_inh () INHERITS (t1, t3);
+ \d+ t13_inh
+ CREATE TABLE t13_like (LIKE t3 INCLUDING CONSTRAINTS INCLUDING COMMENTS INCLUDING STORAGE) INHERITS (t1);
+ \d+ t13_like
+ SELECT description FROM pg_description, pg_constraint c WHERE classoid = 'pg_constraint'::regclass AND objoid = c.oid AND c.conrelid = 't13_like'::regclass;
+ 
+ CREATE TABLE inh_error1 () INHERITS (t1, t4);
+ CREATE TABLE inh_error2 (LIKE t4 INCLUDING STORAGE) INHERITS (t1);
+ 
+ DROP TABLE t1, t2, t3, t4, t12_storage, t12_comments, t1_inh, t13_inh, t13_like;
