CREATE LIKE INCLUDING COMMENTS and STORAGES
Here is a patch to implement the following items in our ToDo list:
* Add CREATE TABLE LIKE ... INCLUDING COMMENTS
* Have CREATE TABLE LIKE copy column storage parameters
The syntax is:
CREATE TABLE clone_table (LIKE template_table INCLUDING COMMENTS)
-- also copy comments on columns.
CREATE TABLE clone_table (LIKE template_table INCLUDING STORAGES)
-- also copy storage parameters on columns.
Also, storage parameters of inherited columns are inherited automatically.
There might be room for improvement:
* Should INCLUDING COMMENTS also copy comments on indexes?
It copies only comments on columns for now.
* Should we have additonal syntax to define storage parameters inline
of CREATE TABLE? For example,
CREATE TABLE tbl (col text STORAGE MAIN);
CREATE TABLE fails if there is a conflicted storage parameter for now.
ERROR: column "col" has a storage parameter conflict
DETAIL: MAIN versus EXTENDED
but there is no way to resolve the confliction unless we modify the
definitions of original tables. Meantime, we can overwrite DEFAULTs
to resolve conflictions by INCLUDING DEFAULTS.
* Should we have "INCLUDING ALL" as an abbreviated form?
Many INCLUDING options in CREATE LIKE seems to be messy:
CREATE TABLE clone_table (LIKE template_table
INCLUDING DEFAULTS
INCLUDING CONSTRAINTS
INCLUDING INDEXES
INCLUDING STORAGES
INCLUDING COMMENTS);
Comments welcome.
Regards,
---
ITAGAKI Takahiro
NTT Open Source Software Center
Attachments:
create-including-20090907.patchapplication/octet-stream; name=create-including-20090907.patchDownload
diff -cpr --exclude=CVS 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-07-30 05:56:17.000000000 +0900
--- work/doc/src/sgml/ref/create_table.sgml 2009-09-06 17:17:39.768335000 +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 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 class="PARAMETER">column_constraint</replaceable> [ ... ] ]
| <replaceable>table_constraint</replaceable>
! | LIKE <replaceable>parent_table</replaceable> [ { INCLUDING | EXCLUDING } { DEFAULTS | CONSTRAINTS | INDEXES | STORAGES | COMMENTS } ] ... }
[, ... ]
] )
[ INHERITS ( <replaceable>parent_table</replaceable> [, ... ] ) ]
*************** and <replaceable class="PARAMETER">table
*** 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
*************** and <replaceable class="PARAMETER">table
*** 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 | STORAGES | COMMENTS } ]</literal></term>
<listitem>
<para>
The <literal>LIKE</literal> clause specifies a table from which
*************** and <replaceable class="PARAMETER">table
*** 281,286 ****
--- 285,302 ----
specified.
</para>
<para>
+ Storage parameters for the copied column definitions will only be
+ copied if <literal>INCLUDING STORAGES</literal> is specified. The
+ default behavior is to exclude storage paramters, resulting in the
+ copied columns in the new table having type-specific default parameters.
+ </para>
+ <para>
+ Comments for the copied column definitions will only be
+ copied if <literal>INCLUDING COMMENTS</literal> is specified. The
+ default behavior is to exclude comments, resulting in the
+ copied columns in the new table having no comments.
+ </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 -cpr --exclude=CVS 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-06 14:47:00.698569582 +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 -cpr --exclude=CVS 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-06 13:17:56.294992259 +0900
*************** DeleteSharedComments(Oid oid, Oid classo
*** 463,468 ****
--- 463,523 ----
}
/*
+ * GetComment -- get the comment for a 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
diff -cpr --exclude=CVS head/src/backend/commands/tablecmds.c work/src/backend/commands/tablecmds.c
*** head/src/backend/commands/tablecmds.c 2009-08-24 04:23:41.000000000 +0900
--- work/src/backend/commands/tablecmds.c 2009-09-06 16:25:45.324561796 +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)
*** 1098,1103 ****
--- 1099,1117 ----
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
*** 1166,1171 ****
--- 1180,1186 ----
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
*** 1321,1326 ****
--- 1336,1353 ----
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
*** 1342,1347 ****
--- 1369,1375 ----
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
*** 1479,1484 ****
--- 1507,1524 ----
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
*** 1531,1536 ****
--- 1571,1590 ----
}
}
+ /* error is 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 -cpr --exclude=CVS head/src/backend/parser/gram.y work/src/backend/parser/gram.y
*** head/src/backend/parser/gram.y 2009-08-19 08:40:20.000000000 +0900
--- work/src/backend/parser/gram.y 2009-09-06 15:56:07.469540714 +0900
*************** static TypeName *TableFuncTypeName(List
*** 455,461 ****
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
--- 455,461 ----
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
*************** static TypeName *TableFuncTypeName(List
*** 511,517 ****
SAVEPOINT SCHEMA SCROLL SEARCH SECOND_P SECURITY SELECT SEQUENCE
SERIALIZABLE SERVER SESSION SESSION_USER SET SETOF SHARE
SHOW SIMILAR SIMPLE SMALLINT SOME STABLE STANDALONE_P START STATEMENT
! STATISTICS STDIN STDOUT STORAGE STRICT_P STRIP_P SUBSTRING SUPERUSER_P
SYMMETRIC SYSID SYSTEM_P
TABLE TABLESPACE TEMP TEMPLATE TEMPORARY TEXT_P THEN TIME TIMESTAMP
--- 511,517 ----
SAVEPOINT SCHEMA SCROLL SEARCH SECOND_P SECURITY SELECT SEQUENCE
SERIALIZABLE SERVER SESSION SESSION_USER SET SETOF SHARE
SHOW SIMILAR SIMPLE SMALLINT SOME STABLE STANDALONE_P START STATEMENT
! STATISTICS STDIN STDOUT STORAGE STORAGES STRICT_P STRIP_P SUBSTRING SUPERUSER_P
SYMMETRIC SYSID SYSTEM_P
TABLE TABLESPACE TEMP TEMPLATE TEMPORARY TEXT_P THEN TIME TIMESTAMP
*************** TableLikeOption:
*** 2350,2355 ****
--- 2350,2359 ----
| EXCLUDING CONSTRAINTS { $$ = CREATE_TABLE_LIKE_EXCLUDING_CONSTRAINTS; }
| INCLUDING INDEXES { $$ = CREATE_TABLE_LIKE_INCLUDING_INDEXES; }
| EXCLUDING INDEXES { $$ = CREATE_TABLE_LIKE_EXCLUDING_INDEXES; }
+ | INCLUDING STORAGES { $$ = CREATE_TABLE_LIKE_INCLUDING_STORAGES; }
+ | EXCLUDING STORAGES { $$ = CREATE_TABLE_LIKE_EXCLUDING_STORAGES; }
+ | INCLUDING COMMENTS { $$ = CREATE_TABLE_LIKE_INCLUDING_COMMENTS; }
+ | EXCLUDING COMMENTS { $$ = CREATE_TABLE_LIKE_EXCLUDING_COMMENTS; }
;
*************** unreserved_keyword:
*** 10239,10244 ****
--- 10243,10249 ----
| CLOSE
| CLUSTER
| COMMENT
+ | COMMENTS
| COMMIT
| COMMITTED
| CONCURRENTLY
*************** unreserved_keyword:
*** 10416,10421 ****
--- 10421,10427 ----
| STDIN
| STDOUT
| STORAGE
+ | STORAGES
| STRICT_P
| STRIP_P
| SUPERUSER_P
diff -cpr --exclude=CVS 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-06 16:58:33.718541820 +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,
*** 549,554 ****
--- 550,557 ----
bool including_defaults = false;
bool including_constraints = false;
bool including_indexes = false;
+ bool including_storages = false;
+ bool including_comments = false;
ListCell *elem;
relation = parserOpenTable(pstate, inhRelation->relation, AccessShareLock);
*************** transformInhRelation(ParseState *pstate,
*** 595,600 ****
--- 598,615 ----
case CREATE_TABLE_LIKE_EXCLUDING_INDEXES:
including_indexes = false;
break;
+ case CREATE_TABLE_LIKE_INCLUDING_STORAGES:
+ including_storages = true;
+ break;
+ case CREATE_TABLE_LIKE_EXCLUDING_STORAGES:
+ including_storages = false;
+ break;
+ case CREATE_TABLE_LIKE_INCLUDING_COMMENTS:
+ including_comments = true;
+ break;
+ case CREATE_TABLE_LIKE_EXCLUDING_COMMENTS:
+ including_comments = false;
+ break;
default:
elog(ERROR, "unrecognized CREATE TABLE LIKE option: %d",
option);
*************** transformInhRelation(ParseState *pstate,
*** 668,673 ****
--- 683,712 ----
def->cooked_default = pstrdup(this_default);
}
+
+ /* Likewise, copy storage if requested */
+ if (including_storages)
+ def->storage = attribute->attstorage;
+
+ /* Likewise, copy comment if requested */
+ if (including_comments)
+ {
+ char *comment;
+
+ if ((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);
+ }
+ }
}
/*
diff -cpr --exclude=CVS 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-06 13:15:24.482240402 +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 -cpr --exclude=CVS head/src/include/nodes/parsenodes.h work/src/include/nodes/parsenodes.h
*** head/src/include/nodes/parsenodes.h 2009-08-03 07:14:53.000000000 +0900
--- work/src/include/nodes/parsenodes.h 2009-09-06 16:40:59.379534872 +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 enum CreateStmtLikeOption
*** 483,489 ****
CREATE_TABLE_LIKE_INCLUDING_CONSTRAINTS,
CREATE_TABLE_LIKE_EXCLUDING_CONSTRAINTS,
CREATE_TABLE_LIKE_INCLUDING_INDEXES,
! CREATE_TABLE_LIKE_EXCLUDING_INDEXES
} CreateStmtLikeOption;
/*
--- 484,494 ----
CREATE_TABLE_LIKE_INCLUDING_CONSTRAINTS,
CREATE_TABLE_LIKE_EXCLUDING_CONSTRAINTS,
CREATE_TABLE_LIKE_INCLUDING_INDEXES,
! CREATE_TABLE_LIKE_EXCLUDING_INDEXES,
! CREATE_TABLE_LIKE_INCLUDING_STORAGES,
! CREATE_TABLE_LIKE_EXCLUDING_STORAGES,
! CREATE_TABLE_LIKE_INCLUDING_COMMENTS,
! CREATE_TABLE_LIKE_EXCLUDING_COMMENTS
} CreateStmtLikeOption;
/*
diff -cpr --exclude=CVS head/src/include/parser/kwlist.h work/src/include/parser/kwlist.h
*** head/src/include/parser/kwlist.h 2009-04-06 17:42:53.000000000 +0900
--- work/src/include/parser/kwlist.h 2009-09-06 15:13:57.966535522 +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)
*************** PG_KEYWORD("statistics", STATISTICS, UNR
*** 348,353 ****
--- 349,355 ----
PG_KEYWORD("stdin", STDIN, UNRESERVED_KEYWORD)
PG_KEYWORD("stdout", STDOUT, UNRESERVED_KEYWORD)
PG_KEYWORD("storage", STORAGE, UNRESERVED_KEYWORD)
+ PG_KEYWORD("storages", STORAGES, UNRESERVED_KEYWORD)
PG_KEYWORD("strict", STRICT_P, UNRESERVED_KEYWORD)
PG_KEYWORD("strip", STRIP_P, UNRESERVED_KEYWORD)
PG_KEYWORD("substring", SUBSTRING, COL_NAME_KEYWORD)
diff -cpr --exclude=CVS 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-06 16:29:56.346467000 +0900
*************** drop table pp1 cascade;
*** 906,908 ****
--- 906,989 ----
NOTICE: drop cascades to 2 other objects
DETAIL: drop cascades to table cc1
drop cascades to table cc2
+ -- including storages and comments
+ CREATE TABLE t1 (a text, b text);
+ COMMENT ON COLUMN t1.a IS 'A';
+ COMMENT ON COLUMN t1.b IS 'B';
+ 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, 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';
+ CREATE TABLE t4 (a text, c text);
+ ALTER TABLE t4 ALTER COLUMN c SET STORAGE EXTERNAL;
+ CREATE TABLE t12_storages (LIKE t1 INCLUDING STORAGES, LIKE t2 INCLUDING STORAGES);
+ \d+ t12_storages
+ Table "public.t12_storages"
+ 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 COMMENTS) INHERITS (t1);
+ NOTICE: merging column "a" with inherited definition
+ NOTICE: merging column "b" with inherited definition
+ \d+ t1_inh
+ Table "public.t1_inh"
+ Column | Type | Modifiers | Storage | Description
+ --------+------+-----------+----------+-------------
+ a | text | | main | A
+ b | text | | extended | B
+ Inherits: t1
+ Has OIDs: no
+
+ 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 |
+ Inherits: t1,
+ t3
+ Has OIDs: no
+
+ CREATE TABLE t13_like (LIKE t3 INCLUDING COMMENTS INCLUDING STORAGES) 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
+ Inherits: t1
+ Has OIDs: no
+
+ 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 STORAGES) 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_storages, t12_comments, t1_inh, t13_inh, t13_like;
diff -cpr --exclude=CVS 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-06 16:29:22.903535459 +0900
*************** create table cc2(f4 float) inherits(pp1,
*** 276,278 ****
--- 276,312 ----
alter table pp1 add column a2 int check (a2 > 0);
\d cc2
drop table pp1 cascade;
+
+ -- including storages and comments
+ CREATE TABLE t1 (a text, b text);
+ COMMENT ON COLUMN t1.a IS 'A';
+ COMMENT ON COLUMN t1.b IS 'B';
+ 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, 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';
+
+ CREATE TABLE t4 (a text, c text);
+ ALTER TABLE t4 ALTER COLUMN c SET STORAGE EXTERNAL;
+
+ CREATE TABLE t12_storages (LIKE t1 INCLUDING STORAGES, LIKE t2 INCLUDING STORAGES);
+ \d+ t12_storages
+ CREATE TABLE t12_comments (LIKE t1 INCLUDING COMMENTS, LIKE t2 INCLUDING COMMENTS);
+ \d+ t12_comments
+ CREATE TABLE t1_inh (LIKE t1 INCLUDING COMMENTS) INHERITS (t1);
+ \d+ t1_inh
+ CREATE TABLE t13_inh () INHERITS (t1, t3);
+ \d+ t13_inh
+ CREATE TABLE t13_like (LIKE t3 INCLUDING COMMENTS INCLUDING STORAGES) INHERITS (t1);
+ \d+ t13_like
+ CREATE TABLE inh_error1 () INHERITS (t1, t4);
+ CREATE TABLE inh_error2 (LIKE t4 INCLUDING STORAGES) INHERITS (t1);
+
+ DROP TABLE t1, t2, t3, t4, t12_storages, t12_comments, t1_inh, t13_inh, t13_like;
On Mon, Sep 07, 2009 at 12:15:21PM +0900, Itagaki Takahiro wrote:
Here is a patch to implement the following items in our ToDo list:
* Add CREATE TABLE LIKE ... INCLUDING COMMENTS
* Have CREATE TABLE LIKE copy column storage parametersThe syntax is:
CREATE TABLE clone_table (LIKE template_table INCLUDING COMMENTS)
-- also copy comments on columns.
CREATE TABLE clone_table (LIKE template_table INCLUDING STORAGES)
This should probably read INCLUDING STORAGE (singular) instead of
STORAGES.
Cheers,
David.
--
David Fetter <david@fetter.org> http://fetter.org/
Phone: +1 415 235 3778 AIM: dfetter666 Yahoo!: dfetter
Skype: davidfetter XMPP: david.fetter@gmail.com
Remember to vote!
Consider donating to Postgres: http://www.postgresql.org/about/donate
David Fetter <david@fetter.org> wrote:
On Mon, Sep 07, 2009 at 12:15:21PM +0900, Itagaki Takahiro wrote:
Here is a patch to implement the following items in our ToDo list:
* Add CREATE TABLE LIKE ... INCLUDING COMMENTS
* Have CREATE TABLE LIKE copy column storage parametersThe syntax is:
CREATE TABLE clone_table (LIKE template_table INCLUDING STORAGES)This should probably read INCLUDING STORAGE (singular) instead of
STORAGES.
Thanks. I fixed it to INCLUDING STORAGE.
In addition, I modified INCLUDING COMMENTS to copy comments not only
on columns but also on constraints. However, comments on indexes are
not copied because copied indexes are named in DefineIndex();
We don't know names of new indexes when we build a command list.
Regards,
---
ITAGAKI Takahiro
NTT Open Source Software Center
Attachments:
create-including-20090908.patchapplication/octet-stream; name=create-including-20090908.patchDownload
diff -cprN head/doc/src/sgml/ref/create_table.sgml create-including/doc/src/sgml/ref/create_table.sgml
*** head/doc/src/sgml/ref/create_table.sgml 2009-07-30 05:56:17.000000000 +0900
--- create-including/doc/src/sgml/ref/create_table.sgml 2009-09-08 14:47:21.235472191 +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 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 class="PARAMETER">column_constraint</replaceable> [ ... ] ]
| <replaceable>table_constraint</replaceable>
! | LIKE <replaceable>parent_table</replaceable> [ { INCLUDING | EXCLUDING } { DEFAULTS | CONSTRAINTS | INDEXES | STORAGE | COMMENTS } ] ... }
[, ... ]
] )
[ INHERITS ( <replaceable>parent_table</replaceable> [, ... ] ) ]
*************** and <replaceable class="PARAMETER">table
*** 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
*************** and <replaceable class="PARAMETER">table
*** 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 } ]</literal></term>
<listitem>
<para>
The <literal>LIKE</literal> clause specifies a table from which
*************** and <replaceable class="PARAMETER">table
*** 281,286 ****
--- 285,302 ----
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 paramters, resulting in the
+ copied columns in the new table having type-specific default parameters.
+ </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>
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 create-including/src/backend/access/common/tupdesc.c
*** head/src/backend/access/common/tupdesc.c 2009-08-03 07:14:51.000000000 +0900
--- create-including/src/backend/access/common/tupdesc.c 2009-09-08 11:04:08.769493826 +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 create-including/src/backend/catalog/pg_constraint.c
*** head/src/backend/catalog/pg_constraint.c 2009-07-28 11:56:29.000000000 +0900
--- create-including/src/backend/catalog/pg_constraint.c 2009-09-08 14:42:34.603475070 +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 create-including/src/backend/commands/comment.c
*** head/src/backend/commands/comment.c 2009-06-11 23:48:55.000000000 +0900
--- create-including/src/backend/commands/comment.c 2009-09-08 14:41:57.916520185 +0900
*************** DeleteSharedComments(Oid oid, Oid classo
*** 463,468 ****
--- 463,523 ----
}
/*
+ * GetComment -- get the comment for a 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 create-including/src/backend/commands/tablecmds.c
*** head/src/backend/commands/tablecmds.c 2009-08-24 04:23:41.000000000 +0900
--- create-including/src/backend/commands/tablecmds.c 2009-09-08 11:04:08.772592787 +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)
*** 1098,1103 ****
--- 1099,1117 ----
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
*** 1166,1171 ****
--- 1180,1186 ----
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
*** 1321,1326 ****
--- 1336,1353 ----
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
*** 1342,1347 ****
--- 1369,1375 ----
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
*** 1479,1484 ****
--- 1507,1524 ----
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
*** 1531,1536 ****
--- 1571,1590 ----
}
}
+ /* error is 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/parser/gram.y create-including/src/backend/parser/gram.y
*** head/src/backend/parser/gram.y 2009-08-19 08:40:20.000000000 +0900
--- create-including/src/backend/parser/gram.y 2009-09-08 11:15:23.048574084 +0900
*************** static TypeName *TableFuncTypeName(List
*** 455,461 ****
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
--- 455,461 ----
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
*************** TableLikeOption:
*** 2350,2355 ****
--- 2350,2359 ----
| EXCLUDING CONSTRAINTS { $$ = CREATE_TABLE_LIKE_EXCLUDING_CONSTRAINTS; }
| INCLUDING INDEXES { $$ = CREATE_TABLE_LIKE_INCLUDING_INDEXES; }
| EXCLUDING INDEXES { $$ = CREATE_TABLE_LIKE_EXCLUDING_INDEXES; }
+ | INCLUDING STORAGE { $$ = CREATE_TABLE_LIKE_INCLUDING_STORAGE; }
+ | EXCLUDING STORAGE { $$ = CREATE_TABLE_LIKE_EXCLUDING_STORAGE; }
+ | INCLUDING COMMENTS { $$ = CREATE_TABLE_LIKE_INCLUDING_COMMENTS; }
+ | EXCLUDING COMMENTS { $$ = CREATE_TABLE_LIKE_EXCLUDING_COMMENTS; }
;
*************** unreserved_keyword:
*** 10239,10244 ****
--- 10243,10249 ----
| CLOSE
| CLUSTER
| COMMENT
+ | COMMENTS
| COMMIT
| COMMITTED
| CONCURRENTLY
diff -cprN head/src/backend/parser/parse_utilcmd.c create-including/src/backend/parser/parse_utilcmd.c
*** head/src/backend/parser/parse_utilcmd.c 2009-07-30 11:45:37.000000000 +0900
--- create-including/src/backend/parser/parse_utilcmd.c 2009-09-08 14:41:57.856469694 +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,
*** 549,555 ****
--- 550,559 ----
bool including_defaults = false;
bool including_constraints = false;
bool including_indexes = false;
+ bool including_storage = false;
+ bool including_comments = false;
ListCell *elem;
+ char *comment;
relation = parserOpenTable(pstate, inhRelation->relation, AccessShareLock);
*************** transformInhRelation(ParseState *pstate,
*** 595,600 ****
--- 599,616 ----
case CREATE_TABLE_LIKE_EXCLUDING_INDEXES:
including_indexes = false;
break;
+ case CREATE_TABLE_LIKE_INCLUDING_STORAGE:
+ including_storage = true;
+ break;
+ case CREATE_TABLE_LIKE_EXCLUDING_STORAGE:
+ including_storage = false;
+ break;
+ case CREATE_TABLE_LIKE_INCLUDING_COMMENTS:
+ including_comments = true;
+ break;
+ case CREATE_TABLE_LIKE_EXCLUDING_COMMENTS:
+ including_comments = false;
+ break;
default:
elog(ERROR, "unrecognized CREATE TABLE LIKE option: %d",
option);
*************** transformInhRelation(ParseState *pstate,
*** 668,673 ****
--- 684,709 ----
def->cooked_default = pstrdup(this_default);
}
+
+ /* Likewise, copy storage if requested */
+ if (including_storage)
+ def->storage = attribute->attstorage;
+
+ /* Likewise, copy comment if requested */
+ if (including_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);
+ }
}
/*
*************** transformInhRelation(ParseState *pstate,
*** 694,699 ****
--- 730,752 ----
n->raw_expr = NULL;
n->cooked_expr = nodeToString(ccbin_node);
cxt->ckconstraints = lappend(cxt->ckconstraints, n);
+
+ /* Copy comment on constraint */
+ if (including_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);
+ }
}
}
*************** transformInhRelation(ParseState *pstate,
*** 719,724 ****
--- 772,779 ----
/* 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/include/catalog/pg_constraint.h create-including/src/include/catalog/pg_constraint.h
*** head/src/include/catalog/pg_constraint.h 2009-07-28 11:56:31.000000000 +0900
--- create-including/src/include/catalog/pg_constraint.h 2009-09-08 14:41:58.042494586 +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 create-including/src/include/commands/comment.h
*** head/src/include/commands/comment.h 2009-06-11 23:49:11.000000000 +0900
--- create-including/src/include/commands/comment.h 2009-09-08 11:04:08.777490543 +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 create-including/src/include/nodes/parsenodes.h
*** head/src/include/nodes/parsenodes.h 2009-08-03 07:14:53.000000000 +0900
--- create-including/src/include/nodes/parsenodes.h 2009-09-08 11:15:10.997473634 +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 enum CreateStmtLikeOption
*** 483,489 ****
CREATE_TABLE_LIKE_INCLUDING_CONSTRAINTS,
CREATE_TABLE_LIKE_EXCLUDING_CONSTRAINTS,
CREATE_TABLE_LIKE_INCLUDING_INDEXES,
! CREATE_TABLE_LIKE_EXCLUDING_INDEXES
} CreateStmtLikeOption;
/*
--- 484,494 ----
CREATE_TABLE_LIKE_INCLUDING_CONSTRAINTS,
CREATE_TABLE_LIKE_EXCLUDING_CONSTRAINTS,
CREATE_TABLE_LIKE_INCLUDING_INDEXES,
! CREATE_TABLE_LIKE_EXCLUDING_INDEXES,
! CREATE_TABLE_LIKE_INCLUDING_STORAGE,
! CREATE_TABLE_LIKE_EXCLUDING_STORAGE,
! CREATE_TABLE_LIKE_INCLUDING_COMMENTS,
! CREATE_TABLE_LIKE_EXCLUDING_COMMENTS
} CreateStmtLikeOption;
/*
diff -cprN head/src/include/parser/kwlist.h create-including/src/include/parser/kwlist.h
*** head/src/include/parser/kwlist.h 2009-04-06 17:42:53.000000000 +0900
--- create-including/src/include/parser/kwlist.h 2009-09-08 11:14:31.709820694 +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 create-including/src/test/regress/expected/inherit.out
*** head/src/test/regress/expected/inherit.out 2009-08-02 04:59:41.000000000 +0900
--- create-including/src/test/regress/expected/inherit.out 2009-09-08 14:43:16.176523000 +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 create-including/src/test/regress/sql/inherit.sql
*** head/src/test/regress/sql/inherit.sql 2008-05-10 08:32:05.000000000 +0900
--- create-including/src/test/regress/sql/inherit.sql 2009-09-08 14:35:11.808502204 +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;
Itagaki Takahiro wrote:
David Fetter <david@fetter.org> wrote:
On Mon, Sep 07, 2009 at 12:15:21PM +0900, Itagaki Takahiro wrote:
Here is a patch to implement the following items in our ToDo list:
* Add CREATE TABLE LIKE ... INCLUDING COMMENTS
* Have CREATE TABLE LIKE copy column storage parametersThe syntax is:
CREATE TABLE clone_table (LIKE template_table INCLUDING STORAGES)This should probably read INCLUDING STORAGE (singular) instead of
STORAGES.Thanks. I fixed it to INCLUDING STORAGE.
This INCLUDING STORAGE is supposed to copy reloptions? In that case I
think this is still a misnomer; to me it sounds like it's copying the
underlying storage i.e. data, which would be very surprising. What
about "INCLUDING STORAGE OPTIONS"?
--
Alvaro Herrera http://www.CommandPrompt.com/
PostgreSQL Replication, Consulting, Custom Development, 24x7 support
Alvaro Herrera <alvherre@commandprompt.com> wrote:
This INCLUDING STORAGE is supposed to copy reloptions?
No. It copies only storage parameters of columns to control TOAST policy.
It might be good to have some features to copy reloptions with convenient
way, but it will be done in another patch.
to me it sounds like it's copying the
underlying storage i.e. data, which would be very surprising. What
about "INCLUDING STORAGE OPTIONS"?
Hmm, but we have the following syntax already:
ALTER TABLE table ALTER COLUMN column SET STORAGE ...
Do you also think it should be "SET STORAGE OPTION ..." ?
Regards,
---
ITAGAKI Takahiro
NTT Open Source Software Center
2009/9/9 Itagaki Takahiro <itagaki.takahiro@oss.ntt.co.jp>:
Alvaro Herrera <alvherre@commandprompt.com> wrote:
This INCLUDING STORAGE is supposed to copy reloptions?
No. It copies only storage parameters of columns to control TOAST policy.
It might be good to have some features to copy reloptions with convenient
way, but it will be done in another patch.to me it sounds like it's copying the
underlying storage i.e. data, which would be very surprising. What
about "INCLUDING STORAGE OPTIONS"?
It *would* be very surprising. An option to include data would
probably be called "INCLUDING DATA" =)
Hmm, but we have the following syntax already:
ALTER TABLE table ALTER COLUMN column SET STORAGE ...
Do you also think it should be "SET STORAGE OPTION ..." ?
Personally, I think INCLUDING STORAGE makes as much sense as you can
expect using just one word, and as Itagaki-san points out it
correlates well with the syntax for ALTER COLUMN.
Cheers,
BJ
2009/9/7 Itagaki Takahiro <itagaki.takahiro@oss.ntt.co.jp>:
Here is a patch to implement the following items in our ToDo list:
* Add CREATE TABLE LIKE ... INCLUDING COMMENTS
* Have CREATE TABLE LIKE copy column storage parameters
Hello Itagaki-san,
I am doing an initial review of your patch. I applied the version
labelled 20090908 (applied with minor fuzz to HEAD). It compiled
cleanly and the feature appears to work as advertised.
I did a little bit of copy-editing on my way through (changes
attached) but the patch seems to be in very good shape. The
documentation is clearly worded, although I did add a cross-reference
in the bit about STORAGE. The regression tests seem to give a pretty
good coverage of both the success and failure modes.
In response to the questions you raised in your post:
* Should INCLUDING COMMENTS also copy comments on indexes?
It copies only comments on columns for now.
It probably should, but if this is difficult to work in, I don't see
anything wrong with leaving it out of this patch and making it a TODO.
* Should we have additonal syntax to define storage parameters inline
of CREATE TABLE? For example,
CREATE TABLE tbl (col text STORAGE MAIN);
CREATE TABLE fails if there is a conflicted storage parameter for now.
ERROR: column "col" has a storage parameter conflict
DETAIL: MAIN versus EXTENDED
but there is no way to resolve the confliction unless we modify the
definitions of original tables. Meantime, we can overwrite DEFAULTs
to resolve conflictions by INCLUDING DEFAULTS.
I think I'm failing to understand why this would be an issue. Why
would the user be specifying columns in the CREATE TABLE statement
that already exist in the table they are cloning?
* Should we have "INCLUDING ALL" as an abbreviated form?
Many INCLUDING options in CREATE LIKE seems to be messy:
CREATE TABLE clone_table (LIKE template_table
INCLUDING DEFAULTS
INCLUDING CONSTRAINTS
INCLUDING INDEXES
INCLUDING STORAGES
INCLUDING COMMENTS);
+1 for adding INCLUDING ALL. The grammar should also support
EXCLUDING ALL for symmetry, even though EXCLUDING ALL is the default
behaviour.
However I do think that this should be a separate patch ... add to TODO?
Cheers,
BJ
Attachments:
create-including-20090908-changes.diffapplication/octet-stream; name=create-including-20090908-changes.diffDownload
diff --git a/doc/src/sgml/ref/create_table.sgml b/doc/src/sgml/ref/create_table.sgml
index e33d73d..636b3e5 100644
--- a/doc/src/sgml/ref/create_table.sgml
+++ b/doc/src/sgml/ref/create_table.sgml
@@ -285,10 +285,11 @@ 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 paramters, resulting in the
- copied columns in the new table having type-specific default parameters.
+ 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
diff --git a/src/backend/commands/comment.c b/src/backend/commands/comment.c
index 335d226..ce243ea 100644
--- a/src/backend/commands/comment.c
+++ b/src/backend/commands/comment.c
@@ -463,7 +463,7 @@ DeleteSharedComments(Oid oid, Oid classoid)
}
/*
- * GetComment -- get the comment for a object, or null if not found.
+ * GetComment -- get the comment for an object, or null if not found.
*/
char *
GetComment(Oid oid, Oid classoid, int32 subid)
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index dded36b..cf77cce 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -1571,7 +1571,7 @@ MergeAttributes(List *schema, List *supers, bool istemp,
}
}
- /* error is conflicting comments */
+ /* Raise an error if we found conflicting comments. */
if (have_bogus_comments)
{
foreach(entry, schema)
Brendan Jurd <direvus@gmail.com> wrote:
I am doing an initial review of your patch.
Thank you for reviewing.
I merged your fix and add INCLUDING ALL option to the new patch.
I changed InhRelation.options to be a bitmap of CreateStmtLikeOption.
INCLUDING just adds bits, and EXCLUDING drops bits.
Now this patch adds:
* CREATE TABLE LIKE ... INCLUDING COMMENTS (for columns and constraints)
* CREATE TABLE LIKE ... INCLUDING STORAGE
* CREATE TABLE LIKE ... INCLUDING ALL
I think I'm failing to understand why this would be an issue. Why
would the user be specifying columns in the CREATE TABLE statement
that already exist in the table they are cloning?
Without inline-STORAGE syntax, we cannot resolve conflictions of
storage parameters unless we can define tables without STORAGE
and then re-add options with ALTER TABLE.
There might be ToDo items:
* Make INCLUDING COMMENTS also copy comments on indexes.
* Add syntax to define storage options inline like
CREATE TABLE tbl (col text STORAGE MAIN).
Regards,
---
ITAGAKI Takahiro
NTT Open Source Software Center
Attachments:
create-including_20090928.patchapplication/octet-stream; name=create-including_20090928.patchDownload
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;
2009/9/28 Itagaki Takahiro <itagaki.takahiro@oss.ntt.co.jp>:
Thank you for reviewing.
I merged your fix and add INCLUDING ALL option to the new patch.
I changed InhRelation.options to be a bitmap of CreateStmtLikeOption.
INCLUDING just adds bits, and EXCLUDING drops bits.
I had two hunks fail trying to apply your new patch to the latest (git) HEAD:
patching file doc/src/sgml/ref/create_table.sgml
patching file src/backend/access/common/tupdesc.c
patching file src/backend/catalog/pg_constraint.c
patching file src/backend/commands/comment.c
patching file src/backend/commands/tablecmds.c
patching file src/backend/nodes/copyfuncs.c
patching file src/backend/nodes/equalfuncs.c
patching file src/backend/parser/gram.y
patching file src/backend/parser/parse_utilcmd.c
patching file src/bin/psql/sql_help.c
Hunk #1 FAILED at 3.
Hunk #2 FAILED at 1279.
2 out of 2 hunks FAILED -- saving rejects to file src/bin/psql/sql_help.c.rej
patching file src/include/catalog/pg_constraint.h
patching file src/include/commands/comment.h
patching file src/include/nodes/parsenodes.h
patching file src/include/parser/kwlist.h
patching file src/test/regress/expected/inherit.out
patching file src/test/regress/sql/inherit.sql
I have attached the rejects file.
Cheers,
BJ
Attachments:
sql_help.c.rejapplication/octet-stream; name=sql_help.c.rejDownload
***************
*** 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
*
*/
***************
*** 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"
Brendan Jurd <direvus@gmail.com> wrote:
patching file src/bin/psql/sql_help.c
Hunk #1 FAILED at 3.
Hunk #2 FAILED at 1279.
2 out of 2 hunks FAILED -- saving rejects to file src/bin/psql/sql_help.c.rejI have attached the rejects file.
Oops, sql_help.c is an automatic generated file. Please ignore the part.
Regards,
---
ITAGAKI Takahiro
NTT Open Source Software Center
2009/9/28 Itagaki Takahiro <itagaki.takahiro@oss.ntt.co.jp>:
Brendan Jurd <direvus@gmail.com> wrote:
patching file src/bin/psql/sql_help.c
Hunk #1 FAILED at 3.
Hunk #2 FAILED at 1279.
2 out of 2 hunks FAILED -- saving rejects to file src/bin/psql/sql_help.c.rejOops, sql_help.c is an automatic generated file. Please ignore the part.
With the sql_help.c changes removed, the patch applied fine and
testing went well.
I noticed only the following in the new documentation in CREATE TABLE:
diff --git a/doc/src/sgml/ref/create_table.sgml
b/doc/src/sgml/ref/create_table.sgml
index 6417007..9ea8a49 100644
--- a/doc/src/sgml/ref/create_table.sgml
+++ b/doc/src/sgml/ref/create_table.sgml
@@ -299,7 +299,7 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } ]
TABLE <replaceable class="PAR
</para>
<para>
<literal>INCLUDING ALL</literal> is an abbreviated form of
- <literal>INCLUDING DEFAULTS INCLUDING CONSTRAINTS INCLUDING
INDEXES INCLUDING STORAGES INCLUDING COMMENTS</literal>.
+ <literal>INCLUDING DEFAULTS INCLUDING CONSTRAINTS INCLUDING
INDEXES INCLUDING STORAGE INCLUDING COMMENTS</literal>.
</para>
<para>
Note also that unlike <literal>INHERITS</literal>, copied columns and
Aside from the bogus hunks in the patch, and this one typo, the patch
looks to be in excellent shape.
Cheers,
BJ
I removed hunks by sql_help.c and fix a typo in documentation.
An updated patch attached.
Brendan Jurd <direvus@gmail.com> wrote:
With the sql_help.c changes removed, the patch applied fine and
testing went well.I noticed only the following in the new documentation in CREATE TABLE: - <literal>INCLUDING DEFAULTS INCLUDING CONSTRAINTS INCLUDING INDEXES INCLUDING STORAGES INCLUDING COMMENTS</literal>. + <literal>INCLUDING DEFAULTS INCLUDING CONSTRAINTS INCLUDING INDEXES INCLUDING STORAGE INCLUDING COMMENTS</literal>.Aside from the bogus hunks in the patch, and this one typo, the patch
looks to be in excellent shape.
Regards,
---
ITAGAKI Takahiro
NTT Open Source Software Center
Attachments:
create-including_20090928a.patchapplication/octet-stream; name=create-including_20090928a.patchDownload
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 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
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/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;
Itagaki Takahiro escribi�:
I removed hunks by sql_help.c and fix a typo in documentation.
An updated patch attached.
Hmm, so it works to specify LIKE t1 INCLUDING COMMENTS EXCLUDING COMMENTS?
--
Alvaro Herrera http://www.CommandPrompt.com/
PostgreSQL Replication, Consulting, Custom Development, 24x7 support
Alvaro Herrera <alvherre@commandprompt.com> wrote:
Hmm, so it works to specify LIKE t1 INCLUDING COMMENTS EXCLUDING COMMENTS?
Only last specifer is applied, which is the same behavior as of now.
EXCLUDING is typically useless because all of the default values are
EXCLUDING, but "INCLUDING ALL EXCLUDING xxx" are meaningful; it copies
all components except xxx.
Regards,
---
ITAGAKI Takahiro
NTT Open Source Software Center
Itagaki Takahiro wrote:
I removed hunks by sql_help.c and fix a typo in documentation.
An updated patch attached.Brendan Jurd <direvus@gmail.com> wrote:
With the sql_help.c changes removed, the patch applied fine and
testing went well.I noticed only the following in the new documentation in CREATE TABLE: - <literal>INCLUDING DEFAULTS INCLUDING CONSTRAINTS INCLUDING INDEXES INCLUDING STORAGES INCLUDING COMMENTS</literal>. + <literal>INCLUDING DEFAULTS INCLUDING CONSTRAINTS INCLUDING INDEXES INCLUDING STORAGE INCLUDING COMMENTS</literal>.Aside from the bogus hunks in the patch, and this one typo, the patch
looks to be in excellent shape.
I'm reviewing this patch with a view to committing it, since the other
patch I was going to look at still seemed to be subject to some
discussion. In general it looks OK, but I'm wondering why we are not
copying comments on cloned indexes. I realize that might involve a bit
more code, but I think I'd feel happier if we cloned all the comments we
reasonably could from the outset. Is it really that hard to do?
cheers
andrew
Recently, I encountered a situation where the docs on (or impl?)
INCLUDING INDEXES and INCLUDING CONSTRAINTS are not clearly defined
for primary keys. Should it be noted in the docs that in this case, we
are referring to the technical implementation of a primary key, i.e. a
unique index and a not null constraint, thus both conditions are
required?
testdb=> CREATE TABLE foo (id int primary key);
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index
"foo_pkey" for table "foo"
CREATE TABLE
testdb=> \d foo;
Table "public.foo"
Column | Type | Modifiers
--------+---------+-----------
id | integer | not null
Indexes:
"foo_pkey" PRIMARY KEY, btree (id)
testdb=> CREATE TABLE foo2 (LIKE FOO INCLUDING CONSTRAINTS EXCLUDING INDEXES);
CREATE TABLE
testdb=> \d foo2
Table "public.foo2"
Column | Type | Modifiers
--------+---------+-----------
id | integer | not null
testdb=> CREATE TABLE foo3 (LIKE FOO EXCLUDING CONSTRAINTS INCLUDING INDEXES);
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index
"foo3_pkey" for table "foo3"
CREATE TABLE
testdb=> \d foo3;
Table "public.foo3"
Column | Type | Modifiers
--------+---------+-----------
id | integer | not null
Indexes:
"foo3_pkey" PRIMARY KEY, btree (id)
testdb=>
Regards,
Khee Chin.
Khee Chin <kheechin@gmail.com> wrote:
Recently, I encountered a situation where the docs on (or impl?)
INCLUDING INDEXES and INCLUDING CONSTRAINTS are not clearly defined
for primary keys. Should it be noted in the docs that in this case, we
are referring to the technical implementation of a primary key, i.e. a
unique index and a not null constraint, thus both conditions are
required?
It might be a confusable feature, but it should be discussed separated
from this patch. IMO, almost all user will use "INCLUDING ALL"
if the syntax is added by the patch.
Regards,
---
ITAGAKI Takahiro
NTT Open Source Software Center
Andrew Dunstan <andrew@dunslane.net> wrote:
I'm wondering why we are not
copying comments on cloned indexes. I realize that might involve a bit
more code, but I think I'd feel happier if we cloned all the comments we
reasonably could from the outset. Is it really that hard to do?
I found it is not so difficult as I expected; patch attached. Now it copies
comments on indexes and columns of the indexes on INCLUDING COMMENTS.
Regression test and documentation are also adjusted. Please review around
chooseIndexName() and uses of it.
The codes becomes a bit complex and might be ugly because we will have some
duplicated codes; "pg_expression_%d" hacks and uses of ChooseRelationName()
are spread into index.c, indexcmds.c and parse_utilcmd.c.
Regards,
---
ITAGAKI Takahiro
NTT Open Source Software Center
Attachments:
create-including_20091005.patchapplication/octet-stream; name=create-including_20091005.patchDownload
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-10-05 10:00:12.756304704 +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, 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
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-10-05 09:30:43.971316532 +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-10-05 09:30:43.971316532 +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-10-05 09:30:43.972761818 +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-10-05 09:30:43.975503803 +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-10-05 09:30:43.976605826 +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-10-05 09:30:43.977605766 +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-10-05 09:30:43.980344371 +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-10-05 11:27:13.744300386 +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"
*************** static void transformTableConstraint(Par
*** 101,106 ****
--- 102,108 ----
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);
*************** 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);
--- 548,554 ----
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.
*/
--- 570,575 ----
*************** 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;
--- 611,618 ----
/*
* 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;
--- 638,671 ----
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;
--- 685,715 ----
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 ****
--- 728,795 ----
/* 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);
*************** transformInhRelation(ParseState *pstate,
*** 735,740 ****
--- 806,833 ----
}
/*
+ * 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)
+ 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 -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-10-05 09:30:43.981380980 +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-10-05 09:30:43.982605560 +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-10-05 09:30:43.983605501 +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-10-05 09:30:43.983605501 +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-10-05 11:27:36.664486000 +0900
*************** drop table pp1 cascade;
*** 906,908 ****
--- 906,1044 ----
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 -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-10-05 11:23:56.616669016 +0900
*************** create table cc2(f4 float) inherits(pp1,
*** 276,278 ****
--- 276,327 ----
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;
Itagaki Takahiro wrote:
Andrew Dunstan <andrew@dunslane.net> wrote:
I'm wondering why we are not
copying comments on cloned indexes. I realize that might involve a bit
more code, but I think I'd feel happier if we cloned all the comments we
reasonably could from the outset. Is it really that hard to do?I found it is not so difficult as I expected; patch attached. Now it copies
comments on indexes and columns of the indexes on INCLUDING COMMENTS.
Regression test and documentation are also adjusted. Please review around
chooseIndexName() and uses of it.The codes becomes a bit complex and might be ugly because we will have some
duplicated codes; "pg_expression_%d" hacks and uses of ChooseRelationName()
are spread into index.c, indexcmds.c and parse_utilcmd.c.
I don't think that's a terrible tragedy - you haven't copied huge swags
of code. Committed with slight adjustments for bitrot etc.
cheers
andrew