diff --git a/doc/src/sgml/ref/create_table.sgml b/doc/src/sgml/ref/create_table.sgml
index dc9b6855d91..a63333cb6f9 100644
--- a/doc/src/sgml/ref/create_table.sgml
+++ b/doc/src/sgml/ref/create_table.sgml
@@ -1,5 +1,5 @@
@@ -24,7 +24,7 @@ PostgreSQL documentation
CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } ] TABLE table_name ( [
{ column_name data_type [ DEFAULT default_expr ] [ column_constraint [ ... ] ]
| table_constraint
- | LIKE parent_table [ { INCLUDING | EXCLUDING } { DEFAULTS | CONSTRAINTS | INDEXES } ] ... }
+ | LIKE parent_table [ { INCLUDING | EXCLUDING } { DEFAULTS | CONSTRAINTS | INDEXES | STORAGE | COMMENTS | ALL } ] ... }
[, ... ]
] )
[ INHERITS ( parent_table [, ... ] ) ]
@@ -230,6 +230,10 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } ] TABLE
- LIKE parent_table [ { INCLUDING | EXCLUDING } { DEFAULTS | CONSTRAINTS | INDEXES } ]
+ LIKE parent_table [ { INCLUDING | EXCLUDING } { DEFAULTS | CONSTRAINTS | INDEXES | STORAGE | COMMENTS | ALL } ]
The LIKE clause specifies a table from which
@@ -280,6 +284,23 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } ] TABLE .
+
+
+ Comments for the copied column, constraint, index and columns of index
+ definitions will only be copied if INCLUDING COMMENTS
+ is specified. The default behavior is to exclude comments, resulting in
+ the copied columns and constraints in the new table having no comments.
+
+
+ INCLUDING ALL is an abbreviated form of
+ INCLUDING DEFAULTS INCLUDING CONSTRAINTS INCLUDING INDEXES INCLUDING STORAGE INCLUDING COMMENTS.
+
Note also that unlike INHERITS, copied columns and
constraints are not merged with similarly named columns and constraints.
diff --git a/src/backend/access/common/tupdesc.c b/src/backend/access/common/tupdesc.c
index 9c561beed19..f19fc9bc95a 100644
--- a/src/backend/access/common/tupdesc.c
+++ b/src/backend/access/common/tupdesc.c
@@ -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)
diff --git a/src/backend/catalog/pg_constraint.c b/src/backend/catalog/pg_constraint.c
index 81c35100c7b..9aa13517ea4 100644
--- a/src/backend/catalog/pg_constraint.c
+++ b/src/backend/catalog/pg_constraint.c
@@ -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;
+}
diff --git a/src/backend/commands/comment.c b/src/backend/commands/comment.c
index b6a90a248bf..610816db6d0 100644
--- a/src/backend/commands/comment.c
+++ b/src/backend/commands/comment.c
@@ -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 $
*
*-------------------------------------------------------------------------
*/
@@ -462,6 +462,61 @@ DeleteSharedComments(Oid oid, Oid classoid)
heap_close(shdescription, RowExclusiveLock);
}
+/*
+ * 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 --
*
@@ -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);
}
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 2d728991b67..a0d3f41886e 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -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;
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index eef333b7187..8365586a00b 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -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;
}
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index ef8abc4773f..2c916af1b78 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -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;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index b86df8a870f..50ce33070fd 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -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 col_name_keyword reserved_keyword
%type TableConstraint TableLikeClause
-%type TableLikeOptionList
-%type TableLikeOption
+%type TableLikeOptionList TableLikeOption
%type ColQualList
%type ColConstraint ColConstraintElem ConstraintAttr
%type 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
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index f7a122c0d64..7dfdd28601f 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -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);
@@ -734,6 +805,32 @@ transformInhRelation(ParseState *pstate, CreateStmtContext *cxt,
heap_close(relation, NoLock);
}
+/*
+ * 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.
diff --git a/src/include/catalog/pg_constraint.h b/src/include/catalog/pg_constraint.h
index 08305e68b65..9822fc8c082 100644
--- a/src/include/catalog/pg_constraint.h
+++ b/src/include/catalog/pg_constraint.h
@@ -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 */
diff --git a/src/include/commands/comment.h b/src/include/commands/comment.h
index f302bd67559..fe36ae9c24b 100644
--- a/src/include/commands/comment.h
+++ b/src/include/commands/comment.h
@@ -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 */
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 757aebef1a4..cf755724ccc 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -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;
/*
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
index d1fd91795b6..4b1284f3382 100644
--- a/src/include/parser/kwlist.h
+++ b/src/include/parser/kwlist.h
@@ -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)
diff --git a/src/test/regress/expected/inherit.out b/src/test/regress/expected/inherit.out
index 69078ae50ab..aebadd2c9c8 100644
--- a/src/test/regress/expected/inherit.out
+++ b/src/test/regress/expected/inherit.out
@@ -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;
diff --git a/src/test/regress/sql/inherit.sql b/src/test/regress/sql/inherit.sql
index e68c658ec77..6f1a492676a 100644
--- a/src/test/regress/sql/inherit.sql
+++ b/src/test/regress/sql/inherit.sql
@@ -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;