OSDN Git Service

CREATE LIKE INCLUDING COMMENTS and STORAGE, and INCLUDING ALL shortcut. Itagaki Takahiro.
authorAndrew Dunstan <andrew@dunslane.net>
Mon, 12 Oct 2009 19:49:24 +0000 (19:49 +0000)
committerAndrew Dunstan <andrew@dunslane.net>
Mon, 12 Oct 2009 19:49:24 +0000 (19:49 +0000)
15 files changed:
doc/src/sgml/ref/create_table.sgml
src/backend/access/common/tupdesc.c
src/backend/catalog/pg_constraint.c
src/backend/commands/comment.c
src/backend/commands/tablecmds.c
src/backend/nodes/copyfuncs.c
src/backend/nodes/equalfuncs.c
src/backend/parser/gram.y
src/backend/parser/parse_utilcmd.c
src/include/catalog/pg_constraint.h
src/include/commands/comment.h
src/include/nodes/parsenodes.h
src/include/parser/kwlist.h
src/test/regress/expected/inherit.out
src/test/regress/sql/inherit.sql

index dc9b685..a63333c 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-$PostgreSQL: pgsql/doc/src/sgml/ref/create_table.sgml,v 1.116 2009/09/18 05:00:41 petere Exp $
+$PostgreSQL: pgsql/doc/src/sgml/ref/create_table.sgml,v 1.117 2009/10/12 19:49:24 adunstan Exp $
 PostgreSQL documentation
 -->
 
@@ -24,7 +24,7 @@ PostgreSQL documentation
 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 } ] ... }
+    | LIKE <replaceable>parent_table</replaceable> [ { INCLUDING | EXCLUDING } { DEFAULTS | CONSTRAINTS | INDEXES | STORAGE | COMMENTS | ALL } ] ... }
     [, ... ]
 ] )
 [ INHERITS ( <replaceable>parent_table</replaceable> [, ... ] ) ]
@@ -230,6 +230,10 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } ] TABLE <replaceable class="PAR
       will always be chosen for it.
      </para>
 
+     <para>
+      Column storage parameters are also copied from parent tables.
+     </para>
+
 <!--
      <para>
       <productname>PostgreSQL</> automatically allows the
@@ -247,7 +251,7 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } ] TABLE <replaceable class="PAR
    </varlistentry>
 
    <varlistentry>
-    <term><literal>LIKE <replaceable>parent_table</replaceable> [ { INCLUDING | EXCLUDING } { DEFAULTS | CONSTRAINTS | INDEXES } ]</literal></term>
+    <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
@@ -281,6 +285,23 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } ] TABLE <replaceable class="PAR
       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, constraint, index and columns of index
+         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 STORAGE 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
index 9c561be..f19fc9b 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/access/common/tupdesc.c,v 1.128 2009/08/02 22:14:51 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/access/common/tupdesc.c,v 1.129 2009/10/12 19:49:24 adunstan Exp $
  *
  * NOTES
  *       some of the executor utility code such as "ExecTypeFromTL" should be
@@ -558,6 +558,8 @@ BuildDescForRelation(List *schema)
                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)
index 81c3510..9aa1351 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/catalog/pg_constraint.c,v 1.47 2009/07/28 02:56:29 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/catalog/pg_constraint.c,v 1.48 2009/10/12 19:49:24 adunstan Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -702,3 +702,65 @@ AlterConstraintNamespaces(Oid ownerId, Oid oldNspId,
 
        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;
+}
index b6a90a2..610816d 100644 (file)
@@ -7,7 +7,7 @@
  * Copyright (c) 1996-2009, PostgreSQL Global Development Group
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/commands/comment.c,v 1.107 2009/06/11 14:48:55 momjian Exp $
+ *       $PostgreSQL: pgsql/src/backend/commands/comment.c,v 1.108 2009/10/12 19:49:24 adunstan Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -463,6 +463,61 @@ DeleteSharedComments(Oid oid, Oid classoid)
 }
 
 /*
+ * 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
@@ -1064,12 +1119,8 @@ CommentConstraint(List *qualname, char *comment)
        List       *relName;
        char       *conName;
        RangeVar   *rel;
-       Relation        pg_constraint,
-                               relation;
-       HeapTuple       tuple;
-       SysScanDesc scan;
-       ScanKeyData skey[1];
-       Oid                     conOid = InvalidOid;
+       Relation        relation;
+       Oid                     conOid;
 
        /* Separate relname and constraint name */
        nnames = list_length(qualname);
@@ -1088,50 +1139,12 @@ CommentConstraint(List *qualname, char *comment)
                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))));
+       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(pg_constraint, AccessShareLock);
        heap_close(relation, NoLock);
 }
 
index 2d72899..a0d3f41 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.301 2009/10/06 00:55:26 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.302 2009/10/12 19:49:24 adunstan Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -39,6 +39,7 @@
 #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"
@@ -332,6 +333,7 @@ static void ATExecAddInherit(Relation rel, RangeVar *parent);
 static void ATExecDropInherit(Relation rel, RangeVar *parent);
 static void copy_relation_data(SMgrRelation rel, SMgrRelation dst,
                                   ForkNumber forkNum, bool istemp);
+static const char * storage_name(char c);
 
 
 /* ----------------------------------------------------------------
@@ -1100,6 +1102,25 @@ truncate_check_rel(Relation rel)
        CheckTableNotInUse(rel, "TRUNCATE");
 }
 
+
+/*----------------
+ * storage_name
+ *    returns a name corresponding to a storage enum value
+ * For use in error messages
+ */
+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.
@@ -1168,6 +1189,7 @@ MergeAttributes(List *schema, List *supers, bool istemp,
        List       *constraints = NIL;
        int                     parentsWithOids = 0;
        bool            have_bogus_defaults = false;
+       bool            have_bogus_comments = false;
        int                     child_attno;
        static Node     bogus_marker = { 0 };           /* marks conflicting defaults */
 
@@ -1323,6 +1345,18 @@ MergeAttributes(List *schema, List *supers, bool istemp,
                                                         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;
@@ -1344,6 +1378,7 @@ MergeAttributes(List *schema, List *supers, bool istemp,
                                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;
                        }
@@ -1481,6 +1516,18 @@ MergeAttributes(List *schema, List *supers, bool istemp,
                                                         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 */
@@ -1533,6 +1580,20 @@ MergeAttributes(List *schema, List *supers, bool istemp,
                }
        }
 
+       /* 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;
index eef333b..8365586 100644 (file)
@@ -15,7 +15,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.444 2009/10/12 18:10:45 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.445 2009/10/12 19:49:24 adunstan Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -2489,7 +2489,7 @@ _copyInhRelation(InhRelation *from)
        InhRelation *newnode = makeNode(InhRelation);
 
        COPY_NODE_FIELD(relation);
-       COPY_NODE_FIELD(options);
+       COPY_SCALAR_FIELD(options);
 
        return newnode;
 }
index ef8abc4..2c916af 100644 (file)
@@ -22,7 +22,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.366 2009/10/12 18:10:45 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.367 2009/10/12 19:49:24 adunstan Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1107,7 +1107,7 @@ static bool
 _equalInhRelation(InhRelation *a, InhRelation *b)
 {
        COMPARE_NODE_FIELD(relation);
-       COMPARE_NODE_FIELD(options);
+       COMPARE_SCALAR_FIELD(options);
 
        return true;
 }
index b86df8a..50ce330 100644 (file)
@@ -11,7 +11,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.682 2009/10/08 02:39:22 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.683 2009/10/12 19:49:24 adunstan Exp $
  *
  * HISTORY
  *       AUTHOR                        DATE                    MAJOR EVENT
@@ -407,8 +407,7 @@ static TypeName *TableFuncTypeName(List *columns);
 %type <keyword> col_name_keyword reserved_keyword
 
 %type <node>   TableConstraint TableLikeClause
-%type <list>   TableLikeOptionList
-%type <ival>   TableLikeOption
+%type <ival>   TableLikeOptionList TableLikeOption
 %type <list>   ColQualList
 %type <node>   ColConstraint ColConstraintElem ConstraintAttr
 %type <ival>   key_actions key_delete key_match key_update key_action
@@ -466,7 +465,7 @@ static TypeName *TableFuncTypeName(List *columns);
 
        CACHE CALLED CASCADE CASCADED CASE CAST CATALOG_P CHAIN CHAR_P
        CHARACTER CHARACTERISTICS CHECK CHECKPOINT CLASS CLOSE
-       CLUSTER COALESCE COLLATE COLUMN COMMENT COMMIT
+       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
@@ -2417,17 +2416,18 @@ TableLikeClause:
                ;
 
 TableLikeOptionList:
-                               TableLikeOptionList TableLikeOption     { $$ = lappend_int($1, $2); }
-                               | /* EMPTY */                                           { $$ = NIL; }
+                               TableLikeOptionList INCLUDING TableLikeOption   { $$ = $1 | $3; }
+                               | TableLikeOptionList EXCLUDING TableLikeOption { $$ = $1 & ~$3; }
+                               | /* EMPTY */                                           { $$ = 0; }
                ;
 
 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; }
+                               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; }
                ;
 
 
@@ -10481,6 +10481,7 @@ unreserved_keyword:
                        | CLOSE
                        | CLUSTER
                        | COMMENT
+                       | COMMENTS
                        | COMMIT
                        | COMMITTED
                        | CONCURRENTLY
index f7a122c..7dfdd28 100644 (file)
@@ -19,7 +19,7 @@
  * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- *     $PostgreSQL: pgsql/src/backend/parser/parse_utilcmd.c,v 2.26 2009/10/06 00:55:26 tgl Exp $
+ *     $PostgreSQL: pgsql/src/backend/parser/parse_utilcmd.c,v 2.27 2009/10/12 19:49:24 adunstan Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -36,6 +36,7 @@
 #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"
@@ -101,6 +102,7 @@ static void transformTableConstraint(ParseState *pstate,
                                                 Constraint *constraint);
 static void transformInhRelation(ParseState *pstate, CreateStmtContext *cxt,
                                         InhRelation *inhrelation);
+static char *chooseIndexName(const RangeVar *relation, IndexStmt *index_stmt);
 static IndexStmt *generateClonedIndexStmt(CreateStmtContext *cxt,
                                                Relation parent_index, AttrNumber *attmap);
 static List *get_opclass(Oid opclass, Oid actual_datatype);
@@ -546,10 +548,7 @@ transformInhRelation(ParseState *pstate, CreateStmtContext *cxt,
        TupleDesc       tupleDesc;
        TupleConstr *constr;
        AclResult       aclresult;
-       bool            including_defaults = false;
-       bool            including_constraints = false;
-       bool            including_indexes = false;
-       ListCell   *elem;
+       char       *comment;
 
        relation = parserOpenTable(pstate, inhRelation->relation, AccessShareLock);
 
@@ -571,36 +570,6 @@ transformInhRelation(ParseState *pstate, CreateStmtContext *cxt,
        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.
         */
@@ -642,7 +611,8 @@ transformInhRelation(ParseState *pstate, CreateStmtContext *cxt,
                /*
                 * Copy default, if present and the default has been requested
                 */
-               if (attribute->atthasdef && including_defaults)
+               if (attribute->atthasdef &&
+                       (inhRelation->options & CREATE_TABLE_LIKE_DEFAULTS))
                {
                        Node       *this_default = NULL;
                        AttrDefault *attrdef;
@@ -668,13 +638,34 @@ transformInhRelation(ParseState *pstate, CreateStmtContext *cxt,
 
                        def->cooked_default = 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 (including_constraints && tupleDesc->constr)
+       if ((inhRelation->options & CREATE_TABLE_LIKE_CONSTRAINTS) && tupleDesc->constr)
        {
                AttrNumber *attmap = varattnos_map_schema(tupleDesc, cxt->columns);
                int                     ccnum;
@@ -694,13 +685,31 @@ transformInhRelation(ParseState *pstate, CreateStmtContext *cxt,
                        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 (including_indexes && relation->rd_rel->relhasindex)
+       if ((inhRelation->options & CREATE_TABLE_LIKE_INDEXES) &&
+               relation->rd_rel->relhasindex)
        {
                AttrNumber *attmap = varattnos_map_schema(tupleDesc, cxt->columns);
                List       *parent_indexes;
@@ -719,6 +728,68 @@ transformInhRelation(ParseState *pstate, CreateStmtContext *cxt,
                        /* Build CREATE INDEX statement to recreate the parent_index */
                        index_stmt = generateClonedIndexStmt(cxt, parent_index, attmap);
 
+                       /* Copy comment on index */
+                       if (inhRelation->options & CREATE_TABLE_LIKE_COMMENTS)
+                       {
+                               CommentStmt        *stmt;
+                               ListCell           *lc;
+                               int                             i;
+
+                               comment = GetComment(parent_index_oid, RelationRelationId, 0);
+                               
+                               if (comment != NULL)
+                               {
+                                       /* Assign name for index because CommentStmt requires name. */
+                                       if (index_stmt->idxname == NULL)
+                                               index_stmt->idxname = chooseIndexName(cxt->relation, index_stmt);
+
+                                       stmt = makeNode(CommentStmt);
+                                       stmt->objtype = OBJECT_INDEX;
+                                       stmt->objname = list_make2(makeString(cxt->relation->schemaname),
+                                                                                          makeString(index_stmt->idxname));
+                                       stmt->objargs = NIL;
+                                       stmt->comment = comment;
+
+                                       cxt->alist = lappend(cxt->alist, stmt);
+                               }
+
+                               /* Copy comment on index's columns */
+                               i = 0;
+                               foreach(lc, index_stmt->indexParams)
+                               {
+                                       char       *attname;
+
+                                       i++;
+                                       comment = GetComment(parent_index_oid, RelationRelationId, i);
+                                       if (comment == NULL)
+                                               continue;
+
+                                       /* Assign name for index because CommentStmt requires name. */
+                                       if (index_stmt->idxname == NULL)
+                                               index_stmt->idxname = chooseIndexName(cxt->relation, index_stmt);
+
+                                       attname = ((IndexElem *) lfirst(lc))->name;
+
+                                       /* expression index has a dummy column name */
+                                       if (attname == NULL)
+                                       {
+                                               attname = palloc(NAMEDATALEN);
+                                               sprintf(attname, "pg_expression_%d", i);
+                                       }
+
+                                       stmt = makeNode(CommentStmt);
+                                       stmt->objtype = OBJECT_COLUMN;
+                                       stmt->objname = list_make3(
+                                                                               makeString(cxt->relation->schemaname),
+                                                                               makeString(index_stmt->idxname),
+                                                                               makeString(attname));
+                                       stmt->objargs = NIL;
+                                       stmt->comment = comment;
+
+                                       cxt->alist = lappend(cxt->alist, stmt);
+                               }
+                       }
+
                        /* Save it in the inh_indexes list for the time being */
                        cxt->inh_indexes = lappend(cxt->inh_indexes, index_stmt);
 
@@ -735,6 +806,32 @@ transformInhRelation(ParseState *pstate, CreateStmtContext *cxt,
 }
 
 /*
+ * chooseIndexName
+ *
+ * Set name to unnamed index. See also the same logic in DefineIndex.
+ */
+static char *
+chooseIndexName(const RangeVar *relation, IndexStmt *index_stmt)
+{
+       Oid     namespaceId;
+       
+       namespaceId = RangeVarGetCreationNamespace(relation);
+       if (index_stmt->primary)
+       {
+               /* no need for column list with pkey */
+               return ChooseRelationName(relation->relname, NULL, 
+                                                                 "pkey", namespaceId);
+       }
+       else
+       {
+               IndexElem  *iparam = (IndexElem *) linitial(index_stmt->indexParams);
+
+               return ChooseRelationName(relation->relname, iparam->name,
+                                                                 "key", namespaceId);
+       }
+}
+
+/*
  * Generate an IndexStmt node using information from an already existing index
  * "source_idx".  Attribute numbers should be adjusted according to attmap.
  */
index 08305e6..9822fc8 100644 (file)
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/pg_constraint.h,v 1.32 2009/07/28 02:56:31 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/pg_constraint.h,v 1.33 2009/10/12 19:49:24 adunstan Exp $
  *
  * NOTES
  *       the genbki.sh script reads this file and generates .bki
@@ -226,5 +226,6 @@ extern char *ChooseConstraintName(const char *name1, const char *name2,
 
 extern void AlterConstraintNamespaces(Oid ownerId, Oid oldNspId,
                                                  Oid newNspId, bool isType);
+extern Oid GetConstraintByName(Oid relid, const char *conname);
 
 #endif   /* PG_CONSTRAINT_H */
index f302bd6..fe36ae9 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * $PostgreSQL: pgsql/src/include/commands/comment.h,v 1.24 2009/06/11 14:49:11 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/commands/comment.h,v 1.25 2009/10/12 19:49:24 adunstan Exp $
  *
  *-------------------------------------------------------------------------
  *
@@ -39,4 +39,6 @@ extern void DeleteSharedComments(Oid oid, Oid classoid);
 
 extern void CreateSharedComments(Oid oid, Oid classoid, char *comment);
 
+extern char *GetComment(Oid oid, Oid classoid, int32 subid);
+
 #endif   /* COMMENT_H */
index 757aebe..cf75572 100644 (file)
@@ -13,7 +13,7 @@
  * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.406 2009/10/12 18:10:51 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.407 2009/10/12 19:49:24 adunstan Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -460,6 +460,7 @@ typedef struct ColumnDef
        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) */
        Node       *cooked_default; /* default value (transformed expr tree) */
        List       *constraints;        /* other constraints on column */
@@ -472,17 +473,17 @@ typedef struct InhRelation
 {
        NodeTag         type;
        RangeVar   *relation;
-       List       *options;            /* integer List of CreateStmtLikeOption */
+       bits32          options;                /* bitmap 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
+       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;
 
 /*
index d1fd917..4b1284f 100644 (file)
@@ -11,7 +11,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/include/parser/kwlist.h,v 1.3 2009/09/22 23:43:41 tgl Exp $
+ *       $PostgreSQL: pgsql/src/include/parser/kwlist.h,v 1.4 2009/10/12 19:49:24 adunstan Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -80,6 +80,7 @@ PG_KEYWORD("coalesce", COALESCE, COL_NAME_KEYWORD)
 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)
index 69078ae..aebadd2 100644 (file)
@@ -921,3 +921,139 @@ drop table pp1 cascade;
 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) PRIMARY KEY, b text);
+NOTICE:  CREATE TABLE / PRIMARY KEY will create implicit index "t1_pkey" for table "t1"
+CREATE INDEX t1_b_key ON t1 (b);
+CREATE INDEX t1_fnidx ON t1 ((a || b));
+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';
+COMMENT ON INDEX t1_pkey IS 'index pkey';
+COMMENT ON INDEX t1_b_key IS 'index b_key';
+COMMENT ON COLUMN t1_pkey.a IS 'index column pkey.a';
+COMMENT ON COLUMN t1_fnidx.pg_expression_1 IS 'index column fnidx';
+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 | not null  | 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 | not null  | 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 | not null  | 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 | not null  | 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 | not null  | 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 t_all (LIKE t1 INCLUDING ALL);
+NOTICE:  CREATE TABLE / PRIMARY KEY will create implicit index "t_all_pkey" for table "t_all"
+\d+ t_all
+                Table "public.t_all"
+ Column | Type | Modifiers | Storage  | Description 
+--------+------+-----------+----------+-------------
+ a      | text | not null  | main     | A
+ b      | text |           | extended | B
+Indexes:
+    "t_all_pkey" PRIMARY KEY, btree (a)
+    "t_all_b_key" btree (b)
+    "t_all_key" btree ((a || b))
+Check constraints:
+    "t1_a_check" CHECK (length(a) > 2)
+Has OIDs: no
+
+SELECT c.relname, objsubid, description FROM pg_description, pg_index i, pg_class c WHERE classoid = 'pg_class'::regclass AND objoid = i.indexrelid AND c.oid = i.indexrelid AND i.indrelid = 't_all'::regclass ORDER BY c.relname, objsubid;
+   relname   | objsubid |     description     
+-------------+----------+---------------------
+ t_all_b_key |        0 | index b_key
+ t_all_key   |        1 | index column fnidx
+ t_all_pkey  |        0 | index pkey
+ t_all_pkey  |        1 | index column pkey.a
+(4 rows)
+
+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, t_all;
index e68c658..6f1a492 100644 (file)
@@ -287,3 +287,52 @@ create table cc2(f4 float) inherits(pp1,cc1);
 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) PRIMARY KEY, b text);
+CREATE INDEX t1_b_key ON t1 (b);
+CREATE INDEX t1_fnidx ON t1 ((a || b));
+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';
+COMMENT ON INDEX t1_pkey IS 'index pkey';
+COMMENT ON INDEX t1_b_key IS 'index b_key';
+COMMENT ON COLUMN t1_pkey.a IS 'index column pkey.a';
+COMMENT ON COLUMN t1_fnidx.pg_expression_1 IS 'index column fnidx';
+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 t_all (LIKE t1 INCLUDING ALL);
+\d+ t_all
+SELECT c.relname, objsubid, description FROM pg_description, pg_index i, pg_class c WHERE classoid = 'pg_class'::regclass AND objoid = i.indexrelid AND c.oid = i.indexrelid AND i.indrelid = 't_all'::regclass ORDER BY c.relname, objsubid;
+
+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, t_all;