CREATE LIKE INCLUDING COMMENTS and STORAGE, and INCLUDING ALL shortcut. Itagaki Takahiro.

This commit is contained in:
Andrew Dunstan 2009-10-12 19:49:24 +00:00
parent 0adaf4cb31
commit faa1afc6c1
15 changed files with 564 additions and 117 deletions

View File

@ -1,5 +1,5 @@
<!--
$PostgreSQL: pgsql/doc/src/sgml/ref/create_table.sgml,v 1.116 2009/09/18 05:00:41 petere Exp $
$PostgreSQL: pgsql/doc/src/sgml/ref/create_table.sgml,v 1.117 2009/10/12 19:49:24 adunstan Exp $
PostgreSQL documentation
-->
@ -24,7 +24,7 @@ PostgreSQL documentation
CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } ] TABLE <replaceable class="PARAMETER">table_name</replaceable> ( [
{ <replaceable class="PARAMETER">column_name</replaceable> <replaceable class="PARAMETER">data_type</replaceable> [ DEFAULT <replaceable>default_expr</replaceable> ] [ <replaceable class="PARAMETER">column_constraint</replaceable> [ ... ] ]
| <replaceable>table_constraint</replaceable>
| LIKE <replaceable>parent_table</replaceable> [ { INCLUDING | EXCLUDING } { DEFAULTS | CONSTRAINTS | INDEXES } ] ... }
| LIKE <replaceable>parent_table</replaceable> [ { INCLUDING | EXCLUDING } { DEFAULTS | CONSTRAINTS | INDEXES | STORAGE | COMMENTS | ALL } ] ... }
[, ... ]
] )
[ INHERITS ( <replaceable>parent_table</replaceable> [, ... ] ) ]
@ -230,6 +230,10 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } ] TABLE <replaceable class="PAR
will always be chosen for it.
</para>
<para>
Column storage parameters are also copied from parent tables.
</para>
<!--
<para>
<productname>PostgreSQL</> automatically allows the
@ -247,7 +251,7 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } ] TABLE <replaceable class="PAR
</varlistentry>
<varlistentry>
<term><literal>LIKE <replaceable>parent_table</replaceable> [ { INCLUDING | EXCLUDING } { DEFAULTS | CONSTRAINTS | INDEXES } ]</literal></term>
<term><literal>LIKE <replaceable>parent_table</replaceable> [ { INCLUDING | EXCLUDING } { DEFAULTS | CONSTRAINTS | INDEXES | STORAGE | COMMENTS | ALL } ]</literal></term>
<listitem>
<para>
The <literal>LIKE</literal> clause specifies a table from which
@ -280,6 +284,23 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } ] TABLE <replaceable class="PAR
table, unless the <literal>INCLUDING INDEXES</literal> clause is
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.

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/common/tupdesc.c,v 1.128 2009/08/02 22:14:51 tgl Exp $
* $PostgreSQL: pgsql/src/backend/access/common/tupdesc.c,v 1.129 2009/10/12 19:49:24 adunstan Exp $
*
* NOTES
* some of the executor utility code such as "ExecTypeFromTL" should be
@ -558,6 +558,8 @@ BuildDescForRelation(List *schema)
has_not_null |= entry->is_not_null;
desc->attrs[attnum - 1]->attislocal = entry->is_local;
desc->attrs[attnum - 1]->attinhcount = entry->inhcount;
if (entry->storage)
desc->attrs[attnum - 1]->attstorage = entry->storage;
}
if (has_not_null)

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/catalog/pg_constraint.c,v 1.47 2009/07/28 02:56:29 tgl Exp $
* $PostgreSQL: pgsql/src/backend/catalog/pg_constraint.c,v 1.48 2009/10/12 19:49:24 adunstan Exp $
*
*-------------------------------------------------------------------------
*/
@ -702,3 +702,65 @@ AlterConstraintNamespaces(Oid ownerId, Oid oldNspId,
heap_close(conRel, RowExclusiveLock);
}
/*
* GetConstraintByName
* Find a constraint with the specified name.
*/
Oid
GetConstraintByName(Oid relid, const char *conname)
{
Relation pg_constraint;
HeapTuple tuple;
SysScanDesc scan;
ScanKeyData skey[1];
Oid conOid = InvalidOid;
/*
* Fetch the constraint tuple from pg_constraint. There may be more than
* one match, because constraints are not required to have unique names;
* if so, error out.
*/
pg_constraint = heap_open(ConstraintRelationId, AccessShareLock);
ScanKeyInit(&skey[0],
Anum_pg_constraint_conrelid,
BTEqualStrategyNumber, F_OIDEQ, relid);
scan = systable_beginscan(pg_constraint, ConstraintRelidIndexId, true,
SnapshotNow, 1, skey);
while (HeapTupleIsValid(tuple = systable_getnext(scan)))
{
Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tuple);
if (strcmp(NameStr(con->conname), conname) == 0)
{
if (OidIsValid(conOid))
{
char *relname = get_rel_name(relid);
ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_OBJECT),
errmsg("table \"%s\" has multiple constraints named \"%s\"",
(relname ? relname : "(unknown)"), conname)));
}
conOid = HeapTupleGetOid(tuple);
}
}
systable_endscan(scan);
/* If no constraint exists for the relation specified, notify user */
if (!OidIsValid(conOid))
{
char *relname = get_rel_name(relid);
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("constraint \"%s\" for table \"%s\" does not exist",
conname, (relname ? relname : "(unknown)"))));
}
heap_close(pg_constraint, AccessShareLock);
return conOid;
}

View File

@ -7,7 +7,7 @@
* Copyright (c) 1996-2009, PostgreSQL Global Development Group
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/comment.c,v 1.107 2009/06/11 14:48:55 momjian Exp $
* $PostgreSQL: pgsql/src/backend/commands/comment.c,v 1.108 2009/10/12 19:49:24 adunstan Exp $
*
*-------------------------------------------------------------------------
*/
@ -462,6 +462,61 @@ DeleteSharedComments(Oid oid, Oid classoid)
heap_close(shdescription, RowExclusiveLock);
}
/*
* GetComment -- get the comment for an object, or null if not found.
*/
char *
GetComment(Oid oid, Oid classoid, int32 subid)
{
Relation description;
ScanKeyData skey[3];
SysScanDesc sd;
TupleDesc tupdesc;
HeapTuple tuple;
char *comment;
/* Use the index to search for a matching old tuple */
ScanKeyInit(&skey[0],
Anum_pg_description_objoid,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(oid));
ScanKeyInit(&skey[1],
Anum_pg_description_classoid,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(classoid));
ScanKeyInit(&skey[2],
Anum_pg_description_objsubid,
BTEqualStrategyNumber, F_INT4EQ,
Int32GetDatum(subid));
description = heap_open(DescriptionRelationId, AccessShareLock);
tupdesc = RelationGetDescr(description);
sd = systable_beginscan(description, DescriptionObjIndexId, true,
SnapshotNow, 3, skey);
comment = NULL;
while ((tuple = systable_getnext(sd)) != NULL)
{
Datum value;
bool isnull;
/* Found the tuple, get description field */
value = heap_getattr(tuple, Anum_pg_description_description, tupdesc, &isnull);
if (!isnull)
comment = TextDatumGetCString(value);
break; /* Assume there can be only one match */
}
systable_endscan(sd);
/* Done */
heap_close(description, AccessShareLock);
return comment;
}
/*
* CommentRelation --
*
@ -1064,12 +1119,8 @@ CommentConstraint(List *qualname, char *comment)
List *relName;
char *conName;
RangeVar *rel;
Relation pg_constraint,
relation;
HeapTuple tuple;
SysScanDesc scan;
ScanKeyData skey[1];
Oid conOid = InvalidOid;
Relation relation;
Oid conOid;
/* Separate relname and constraint name */
nnames = list_length(qualname);
@ -1088,50 +1139,12 @@ CommentConstraint(List *qualname, char *comment)
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
RelationGetRelationName(relation));
/*
* Fetch the constraint tuple from pg_constraint. There may be more than
* one match, because constraints are not required to have unique names;
* if so, error out.
*/
pg_constraint = heap_open(ConstraintRelationId, AccessShareLock);
ScanKeyInit(&skey[0],
Anum_pg_constraint_conrelid,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(RelationGetRelid(relation)));
scan = systable_beginscan(pg_constraint, ConstraintRelidIndexId, true,
SnapshotNow, 1, skey);
while (HeapTupleIsValid(tuple = systable_getnext(scan)))
{
Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tuple);
if (strcmp(NameStr(con->conname), conName) == 0)
{
if (OidIsValid(conOid))
ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_OBJECT),
errmsg("table \"%s\" has multiple constraints named \"%s\"",
RelationGetRelationName(relation), conName)));
conOid = HeapTupleGetOid(tuple);
}
}
systable_endscan(scan);
/* If no constraint exists for the relation specified, notify user */
if (!OidIsValid(conOid))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("constraint \"%s\" for table \"%s\" does not exist",
conName, RelationGetRelationName(relation))));
conOid = GetConstraintByName(RelationGetRelid(relation), conName);
/* Call CreateComments() to create/drop the comments */
CreateComments(conOid, ConstraintRelationId, 0, comment);
/* Done, but hold lock on relation */
heap_close(pg_constraint, AccessShareLock);
heap_close(relation, NoLock);
}

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.301 2009/10/06 00:55:26 tgl Exp $
* $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.302 2009/10/12 19:49:24 adunstan Exp $
*
*-------------------------------------------------------------------------
*/
@ -39,6 +39,7 @@
#include "catalog/storage.h"
#include "catalog/toasting.h"
#include "commands/cluster.h"
#include "commands/comment.h"
#include "commands/defrem.h"
#include "commands/sequence.h"
#include "commands/tablecmds.h"
@ -332,6 +333,7 @@ static void ATExecAddInherit(Relation rel, RangeVar *parent);
static void ATExecDropInherit(Relation rel, RangeVar *parent);
static void copy_relation_data(SMgrRelation rel, SMgrRelation dst,
ForkNumber forkNum, bool istemp);
static const char * storage_name(char c);
/* ----------------------------------------------------------------
@ -1100,6 +1102,25 @@ truncate_check_rel(Relation rel)
CheckTableNotInUse(rel, "TRUNCATE");
}
/*----------------
* storage_name
* returns a name corresponding to a storage enum value
* For use in error messages
*/
static const char *
storage_name(char c)
{
switch (c)
{
case 'p': return "PLAIN";
case 'm': return "MAIN";
case 'x': return "EXTENDED";
case 'e': return "EXTERNAL";
default: return "???";
}
}
/*----------
* MergeAttributes
* Returns new schema given initial schema and superclasses.
@ -1168,6 +1189,7 @@ MergeAttributes(List *schema, List *supers, bool istemp,
List *constraints = NIL;
int parentsWithOids = 0;
bool have_bogus_defaults = false;
bool have_bogus_comments = false;
int child_attno;
static Node bogus_marker = { 0 }; /* marks conflicting defaults */
@ -1323,6 +1345,18 @@ MergeAttributes(List *schema, List *supers, bool istemp,
errdetail("%s versus %s",
TypeNameToString(def->typeName),
format_type_be(attribute->atttypid))));
/* Copy storage parameter */
if (def->storage == 0)
def->storage = attribute->attstorage;
else if (def->storage != attribute->attstorage)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("inherited column \"%s\" has a storage parameter conflict",
attributeName),
errdetail("%s versus %s", storage_name(def->storage),
storage_name(attribute->attstorage))));
def->inhcount++;
/* Merge of NOT NULL constraints = OR 'em together */
def->is_not_null |= attribute->attnotnull;
@ -1344,6 +1378,7 @@ MergeAttributes(List *schema, List *supers, bool istemp,
def->raw_default = NULL;
def->cooked_default = NULL;
def->constraints = NIL;
def->storage = attribute->attstorage;
inhSchema = lappend(inhSchema, def);
newattno[parent_attno - 1] = ++child_attno;
}
@ -1481,6 +1516,18 @@ MergeAttributes(List *schema, List *supers, bool istemp,
errdetail("%s versus %s",
TypeNameToString(def->typeName),
TypeNameToString(newdef->typeName))));
/* Copy storage parameter */
if (def->storage == 0)
def->storage = newdef->storage;
else if (newdef->storage != 0 && def->storage != newdef->storage)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("column \"%s\" has a storage parameter conflict",
attributeName),
errdetail("%s versus %s", storage_name(def->storage),
storage_name(newdef->storage))));
/* Mark the column as locally defined */
def->is_local = true;
/* Merge of NOT NULL constraints = OR 'em together */
@ -1533,6 +1580,20 @@ MergeAttributes(List *schema, List *supers, bool istemp,
}
}
/* Raise an error if we found conflicting comments. */
if (have_bogus_comments)
{
foreach(entry, schema)
{
ColumnDef *def = lfirst(entry);
if (def->cooked_default == &bogus_marker)
ereport(ERROR,
(errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
errmsg("column \"%s\" inherits conflicting comments", def->colname)));
}
}
*supOids = parentOids;
*supconstr = constraints;
*supOidCount = parentsWithOids;

View File

@ -15,7 +15,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.444 2009/10/12 18:10:45 tgl Exp $
* $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.445 2009/10/12 19:49:24 adunstan Exp $
*
*-------------------------------------------------------------------------
*/
@ -2489,7 +2489,7 @@ _copyInhRelation(InhRelation *from)
InhRelation *newnode = makeNode(InhRelation);
COPY_NODE_FIELD(relation);
COPY_NODE_FIELD(options);
COPY_SCALAR_FIELD(options);
return newnode;
}

View File

@ -22,7 +22,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.366 2009/10/12 18:10:45 tgl Exp $
* $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.367 2009/10/12 19:49:24 adunstan Exp $
*
*-------------------------------------------------------------------------
*/
@ -1107,7 +1107,7 @@ static bool
_equalInhRelation(InhRelation *a, InhRelation *b)
{
COMPARE_NODE_FIELD(relation);
COMPARE_NODE_FIELD(options);
COMPARE_SCALAR_FIELD(options);
return true;
}

View File

@ -11,7 +11,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.682 2009/10/08 02:39:22 tgl Exp $
* $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.683 2009/10/12 19:49:24 adunstan Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
@ -407,8 +407,7 @@ static TypeName *TableFuncTypeName(List *columns);
%type <keyword> col_name_keyword reserved_keyword
%type <node> TableConstraint TableLikeClause
%type <list> TableLikeOptionList
%type <ival> TableLikeOption
%type <ival> TableLikeOptionList TableLikeOption
%type <list> ColQualList
%type <node> ColConstraint ColConstraintElem ConstraintAttr
%type <ival> key_actions key_delete key_match key_update key_action
@ -466,7 +465,7 @@ static TypeName *TableFuncTypeName(List *columns);
CACHE CALLED CASCADE CASCADED CASE CAST CATALOG_P CHAIN CHAR_P
CHARACTER CHARACTERISTICS CHECK CHECKPOINT CLASS CLOSE
CLUSTER COALESCE COLLATE COLUMN COMMENT COMMIT
CLUSTER COALESCE COLLATE COLUMN COMMENT COMMENTS COMMIT
COMMITTED CONCURRENTLY CONFIGURATION CONNECTION CONSTRAINT CONSTRAINTS
CONTENT_P CONTINUE_P CONVERSION_P COPY COST CREATE CREATEDB
CREATEROLE CREATEUSER CROSS CSV CURRENT_P
@ -2417,17 +2416,18 @@ TableLikeClause:
;
TableLikeOptionList:
TableLikeOptionList TableLikeOption { $$ = lappend_int($1, $2); }
| /* EMPTY */ { $$ = NIL; }
TableLikeOptionList INCLUDING TableLikeOption { $$ = $1 | $3; }
| TableLikeOptionList EXCLUDING TableLikeOption { $$ = $1 & ~$3; }
| /* EMPTY */ { $$ = 0; }
;
TableLikeOption:
INCLUDING DEFAULTS { $$ = CREATE_TABLE_LIKE_INCLUDING_DEFAULTS; }
| EXCLUDING DEFAULTS { $$ = CREATE_TABLE_LIKE_EXCLUDING_DEFAULTS; }
| INCLUDING CONSTRAINTS { $$ = CREATE_TABLE_LIKE_INCLUDING_CONSTRAINTS; }
| EXCLUDING CONSTRAINTS { $$ = CREATE_TABLE_LIKE_EXCLUDING_CONSTRAINTS; }
| INCLUDING INDEXES { $$ = CREATE_TABLE_LIKE_INCLUDING_INDEXES; }
| EXCLUDING INDEXES { $$ = CREATE_TABLE_LIKE_EXCLUDING_INDEXES; }
DEFAULTS { $$ = CREATE_TABLE_LIKE_DEFAULTS; }
| CONSTRAINTS { $$ = CREATE_TABLE_LIKE_CONSTRAINTS; }
| INDEXES { $$ = CREATE_TABLE_LIKE_INDEXES; }
| STORAGE { $$ = CREATE_TABLE_LIKE_STORAGE; }
| COMMENTS { $$ = CREATE_TABLE_LIKE_COMMENTS; }
| ALL { $$ = CREATE_TABLE_LIKE_ALL; }
;
@ -10481,6 +10481,7 @@ unreserved_keyword:
| CLOSE
| CLUSTER
| COMMENT
| COMMENTS
| COMMIT
| COMMITTED
| CONCURRENTLY

View File

@ -19,7 +19,7 @@
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/backend/parser/parse_utilcmd.c,v 2.26 2009/10/06 00:55:26 tgl Exp $
* $PostgreSQL: pgsql/src/backend/parser/parse_utilcmd.c,v 2.27 2009/10/12 19:49:24 adunstan Exp $
*
*-------------------------------------------------------------------------
*/
@ -36,6 +36,7 @@
#include "catalog/pg_constraint.h"
#include "catalog/pg_opclass.h"
#include "catalog/pg_type.h"
#include "commands/comment.h"
#include "commands/defrem.h"
#include "commands/tablecmds.h"
#include "commands/tablespace.h"
@ -101,6 +102,7 @@ static void transformTableConstraint(ParseState *pstate,
Constraint *constraint);
static void transformInhRelation(ParseState *pstate, CreateStmtContext *cxt,
InhRelation *inhrelation);
static char *chooseIndexName(const RangeVar *relation, IndexStmt *index_stmt);
static IndexStmt *generateClonedIndexStmt(CreateStmtContext *cxt,
Relation parent_index, AttrNumber *attmap);
static List *get_opclass(Oid opclass, Oid actual_datatype);
@ -546,10 +548,7 @@ transformInhRelation(ParseState *pstate, CreateStmtContext *cxt,
TupleDesc tupleDesc;
TupleConstr *constr;
AclResult aclresult;
bool including_defaults = false;
bool including_constraints = false;
bool including_indexes = false;
ListCell *elem;
char *comment;
relation = parserOpenTable(pstate, inhRelation->relation, AccessShareLock);
@ -571,36 +570,6 @@ transformInhRelation(ParseState *pstate, CreateStmtContext *cxt,
tupleDesc = RelationGetDescr(relation);
constr = tupleDesc->constr;
foreach(elem, inhRelation->options)
{
int option = lfirst_int(elem);
switch (option)
{
case CREATE_TABLE_LIKE_INCLUDING_DEFAULTS:
including_defaults = true;
break;
case CREATE_TABLE_LIKE_EXCLUDING_DEFAULTS:
including_defaults = false;
break;
case CREATE_TABLE_LIKE_INCLUDING_CONSTRAINTS:
including_constraints = true;
break;
case CREATE_TABLE_LIKE_EXCLUDING_CONSTRAINTS:
including_constraints = false;
break;
case CREATE_TABLE_LIKE_INCLUDING_INDEXES:
including_indexes = true;
break;
case CREATE_TABLE_LIKE_EXCLUDING_INDEXES:
including_indexes = false;
break;
default:
elog(ERROR, "unrecognized CREATE TABLE LIKE option: %d",
option);
}
}
/*
* Insert the copied attributes into the cxt for the new table definition.
*/
@ -642,7 +611,8 @@ transformInhRelation(ParseState *pstate, CreateStmtContext *cxt,
/*
* Copy default, if present and the default has been requested
*/
if (attribute->atthasdef && including_defaults)
if (attribute->atthasdef &&
(inhRelation->options & CREATE_TABLE_LIKE_DEFAULTS))
{
Node *this_default = NULL;
AttrDefault *attrdef;
@ -668,13 +638,34 @@ transformInhRelation(ParseState *pstate, CreateStmtContext *cxt,
def->cooked_default = this_default;
}
/* Likewise, copy storage if requested */
if (inhRelation->options & CREATE_TABLE_LIKE_STORAGE)
def->storage = attribute->attstorage;
/* Likewise, copy comment if requested */
if ((inhRelation->options & CREATE_TABLE_LIKE_COMMENTS) &&
(comment = GetComment(attribute->attrelid, RelationRelationId,
attribute->attnum)) != NULL)
{
CommentStmt *stmt = makeNode(CommentStmt);
stmt->objtype = OBJECT_COLUMN;
stmt->objname = list_make3(makeString(cxt->relation->schemaname),
makeString(cxt->relation->relname),
makeString(def->colname));
stmt->objargs = NIL;
stmt->comment = comment;
cxt->alist = lappend(cxt->alist, stmt);
}
}
/*
* Copy CHECK constraints if requested, being careful to adjust attribute
* numbers
*/
if (including_constraints && tupleDesc->constr)
if ((inhRelation->options & CREATE_TABLE_LIKE_CONSTRAINTS) && tupleDesc->constr)
{
AttrNumber *attmap = varattnos_map_schema(tupleDesc, cxt->columns);
int ccnum;
@ -694,13 +685,31 @@ transformInhRelation(ParseState *pstate, CreateStmtContext *cxt,
n->raw_expr = NULL;
n->cooked_expr = nodeToString(ccbin_node);
cxt->ckconstraints = lappend(cxt->ckconstraints, n);
/* Copy comment on constraint */
if ((inhRelation->options & CREATE_TABLE_LIKE_COMMENTS) &&
(comment = GetComment(GetConstraintByName(RelationGetRelid(
relation), n->conname), ConstraintRelationId, 0)) != NULL)
{
CommentStmt *stmt = makeNode(CommentStmt);
stmt->objtype = OBJECT_CONSTRAINT;
stmt->objname = list_make3(makeString(cxt->relation->schemaname),
makeString(cxt->relation->relname),
makeString(n->conname));
stmt->objargs = NIL;
stmt->comment = comment;
cxt->alist = lappend(cxt->alist, stmt);
}
}
}
/*
* Likewise, copy indexes if requested
*/
if (including_indexes && relation->rd_rel->relhasindex)
if ((inhRelation->options & CREATE_TABLE_LIKE_INDEXES) &&
relation->rd_rel->relhasindex)
{
AttrNumber *attmap = varattnos_map_schema(tupleDesc, cxt->columns);
List *parent_indexes;
@ -719,6 +728,68 @@ transformInhRelation(ParseState *pstate, CreateStmtContext *cxt,
/* Build CREATE INDEX statement to recreate the parent_index */
index_stmt = generateClonedIndexStmt(cxt, parent_index, attmap);
/* Copy comment on index */
if (inhRelation->options & CREATE_TABLE_LIKE_COMMENTS)
{
CommentStmt *stmt;
ListCell *lc;
int i;
comment = GetComment(parent_index_oid, RelationRelationId, 0);
if (comment != NULL)
{
/* Assign name for index because CommentStmt requires name. */
if (index_stmt->idxname == NULL)
index_stmt->idxname = chooseIndexName(cxt->relation, index_stmt);
stmt = makeNode(CommentStmt);
stmt->objtype = OBJECT_INDEX;
stmt->objname = list_make2(makeString(cxt->relation->schemaname),
makeString(index_stmt->idxname));
stmt->objargs = NIL;
stmt->comment = comment;
cxt->alist = lappend(cxt->alist, stmt);
}
/* Copy comment on index's columns */
i = 0;
foreach(lc, index_stmt->indexParams)
{
char *attname;
i++;
comment = GetComment(parent_index_oid, RelationRelationId, i);
if (comment == NULL)
continue;
/* Assign name for index because CommentStmt requires name. */
if (index_stmt->idxname == NULL)
index_stmt->idxname = chooseIndexName(cxt->relation, index_stmt);
attname = ((IndexElem *) lfirst(lc))->name;
/* expression index has a dummy column name */
if (attname == NULL)
{
attname = palloc(NAMEDATALEN);
sprintf(attname, "pg_expression_%d", i);
}
stmt = makeNode(CommentStmt);
stmt->objtype = OBJECT_COLUMN;
stmt->objname = list_make3(
makeString(cxt->relation->schemaname),
makeString(index_stmt->idxname),
makeString(attname));
stmt->objargs = NIL;
stmt->comment = comment;
cxt->alist = lappend(cxt->alist, stmt);
}
}
/* Save it in the inh_indexes list for the time being */
cxt->inh_indexes = lappend(cxt->inh_indexes, index_stmt);
@ -734,6 +805,32 @@ transformInhRelation(ParseState *pstate, CreateStmtContext *cxt,
heap_close(relation, NoLock);
}
/*
* chooseIndexName
*
* Set name to unnamed index. See also the same logic in DefineIndex.
*/
static char *
chooseIndexName(const RangeVar *relation, IndexStmt *index_stmt)
{
Oid namespaceId;
namespaceId = RangeVarGetCreationNamespace(relation);
if (index_stmt->primary)
{
/* no need for column list with pkey */
return ChooseRelationName(relation->relname, NULL,
"pkey", namespaceId);
}
else
{
IndexElem *iparam = (IndexElem *) linitial(index_stmt->indexParams);
return ChooseRelationName(relation->relname, iparam->name,
"key", namespaceId);
}
}
/*
* Generate an IndexStmt node using information from an already existing index
* "source_idx". Attribute numbers should be adjusted according to attmap.

View File

@ -8,7 +8,7 @@
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/catalog/pg_constraint.h,v 1.32 2009/07/28 02:56:31 tgl Exp $
* $PostgreSQL: pgsql/src/include/catalog/pg_constraint.h,v 1.33 2009/10/12 19:49:24 adunstan Exp $
*
* NOTES
* the genbki.sh script reads this file and generates .bki
@ -226,5 +226,6 @@ extern char *ChooseConstraintName(const char *name1, const char *name2,
extern void AlterConstraintNamespaces(Oid ownerId, Oid oldNspId,
Oid newNspId, bool isType);
extern Oid GetConstraintByName(Oid relid, const char *conname);
#endif /* PG_CONSTRAINT_H */

View File

@ -1,5 +1,5 @@
/*
* $PostgreSQL: pgsql/src/include/commands/comment.h,v 1.24 2009/06/11 14:49:11 momjian Exp $
* $PostgreSQL: pgsql/src/include/commands/comment.h,v 1.25 2009/10/12 19:49:24 adunstan Exp $
*
*-------------------------------------------------------------------------
*
@ -39,4 +39,6 @@ extern void DeleteSharedComments(Oid oid, Oid classoid);
extern void CreateSharedComments(Oid oid, Oid classoid, char *comment);
extern char *GetComment(Oid oid, Oid classoid, int32 subid);
#endif /* COMMENT_H */

View File

@ -13,7 +13,7 @@
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.406 2009/10/12 18:10:51 tgl Exp $
* $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.407 2009/10/12 19:49:24 adunstan Exp $
*
*-------------------------------------------------------------------------
*/
@ -460,6 +460,7 @@ typedef struct ColumnDef
int inhcount; /* number of times column is inherited */
bool is_local; /* column has local (non-inherited) def'n */
bool is_not_null; /* NOT NULL constraint specified? */
char storage; /* storage parameter of column */
Node *raw_default; /* default value (untransformed parse tree) */
Node *cooked_default; /* default value (transformed expr tree) */
List *constraints; /* other constraints on column */
@ -472,17 +473,17 @@ typedef struct InhRelation
{
NodeTag type;
RangeVar *relation;
List *options; /* integer List of CreateStmtLikeOption */
bits32 options; /* bitmap of CreateStmtLikeOption */
} InhRelation;
typedef enum CreateStmtLikeOption
{
CREATE_TABLE_LIKE_INCLUDING_DEFAULTS,
CREATE_TABLE_LIKE_EXCLUDING_DEFAULTS,
CREATE_TABLE_LIKE_INCLUDING_CONSTRAINTS,
CREATE_TABLE_LIKE_EXCLUDING_CONSTRAINTS,
CREATE_TABLE_LIKE_INCLUDING_INDEXES,
CREATE_TABLE_LIKE_EXCLUDING_INDEXES
CREATE_TABLE_LIKE_DEFAULTS = 1 << 0,
CREATE_TABLE_LIKE_CONSTRAINTS = 1 << 1,
CREATE_TABLE_LIKE_INDEXES = 1 << 2,
CREATE_TABLE_LIKE_STORAGE = 1 << 3,
CREATE_TABLE_LIKE_COMMENTS = 1 << 4,
CREATE_TABLE_LIKE_ALL = 0xFFFFFFFF
} CreateStmtLikeOption;
/*

View File

@ -11,7 +11,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/include/parser/kwlist.h,v 1.3 2009/09/22 23:43:41 tgl Exp $
* $PostgreSQL: pgsql/src/include/parser/kwlist.h,v 1.4 2009/10/12 19:49:24 adunstan Exp $
*
*-------------------------------------------------------------------------
*/
@ -80,6 +80,7 @@ PG_KEYWORD("coalesce", COALESCE, COL_NAME_KEYWORD)
PG_KEYWORD("collate", COLLATE, RESERVED_KEYWORD)
PG_KEYWORD("column", COLUMN, RESERVED_KEYWORD)
PG_KEYWORD("comment", COMMENT, UNRESERVED_KEYWORD)
PG_KEYWORD("comments", COMMENTS, UNRESERVED_KEYWORD)
PG_KEYWORD("commit", COMMIT, UNRESERVED_KEYWORD)
PG_KEYWORD("committed", COMMITTED, UNRESERVED_KEYWORD)
PG_KEYWORD("concurrently", CONCURRENTLY, UNRESERVED_KEYWORD)

View File

@ -921,3 +921,139 @@ drop table pp1 cascade;
NOTICE: drop cascades to 2 other objects
DETAIL: drop cascades to table cc1
drop cascades to table cc2
-- including storage and comments
CREATE TABLE t1 (a text CHECK (length(a) > 2) PRIMARY KEY, b text);
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "t1_pkey" for table "t1"
CREATE INDEX t1_b_key ON t1 (b);
CREATE INDEX t1_fnidx ON t1 ((a || b));
COMMENT ON COLUMN t1.a IS 'A';
COMMENT ON COLUMN t1.b IS 'B';
COMMENT ON CONSTRAINT t1_a_check ON t1 IS 't1_a_check';
COMMENT ON INDEX t1_pkey IS 'index pkey';
COMMENT ON INDEX t1_b_key IS 'index b_key';
COMMENT ON COLUMN t1_pkey.a IS 'index column pkey.a';
COMMENT ON COLUMN t1_fnidx.pg_expression_1 IS 'index column fnidx';
ALTER TABLE t1 ALTER COLUMN a SET STORAGE MAIN;
CREATE TABLE t2 (c text);
ALTER TABLE t2 ALTER COLUMN c SET STORAGE EXTERNAL;
COMMENT ON COLUMN t2.c IS 'C';
CREATE TABLE t3 (a text CHECK (length(a) < 5), c text);
ALTER TABLE t3 ALTER COLUMN c SET STORAGE EXTERNAL;
ALTER TABLE t3 ALTER COLUMN a SET STORAGE MAIN;
COMMENT ON COLUMN t3.a IS 'A3';
COMMENT ON COLUMN t3.c IS 'C';
COMMENT ON CONSTRAINT t3_a_check ON t3 IS 't3_a_check';
CREATE TABLE t4 (a text, c text);
ALTER TABLE t4 ALTER COLUMN c SET STORAGE EXTERNAL;
CREATE TABLE t12_storage (LIKE t1 INCLUDING STORAGE, LIKE t2 INCLUDING STORAGE);
\d+ t12_storage
Table "public.t12_storage"
Column | Type | Modifiers | Storage | Description
--------+------+-----------+----------+-------------
a | text | not null | main |
b | text | | extended |
c | text | | external |
Has OIDs: no
CREATE TABLE t12_comments (LIKE t1 INCLUDING COMMENTS, LIKE t2 INCLUDING COMMENTS);
\d+ t12_comments
Table "public.t12_comments"
Column | Type | Modifiers | Storage | Description
--------+------+-----------+----------+-------------
a | text | not null | extended | A
b | text | | extended | B
c | text | | extended | C
Has OIDs: no
CREATE TABLE t1_inh (LIKE t1 INCLUDING CONSTRAINTS INCLUDING COMMENTS) INHERITS (t1);
NOTICE: merging column "a" with inherited definition
NOTICE: merging column "b" with inherited definition
NOTICE: merging constraint "t1_a_check" with inherited definition
\d+ t1_inh
Table "public.t1_inh"
Column | Type | Modifiers | Storage | Description
--------+------+-----------+----------+-------------
a | text | not null | main | A
b | text | | extended | B
Check constraints:
"t1_a_check" CHECK (length(a) > 2)
Inherits: t1
Has OIDs: no
SELECT description FROM pg_description, pg_constraint c WHERE classoid = 'pg_constraint'::regclass AND objoid = c.oid AND c.conrelid = 't1_inh'::regclass;
description
-------------
t1_a_check
(1 row)
CREATE TABLE t13_inh () INHERITS (t1, t3);
NOTICE: merging multiple inherited definitions of column "a"
\d+ t13_inh
Table "public.t13_inh"
Column | Type | Modifiers | Storage | Description
--------+------+-----------+----------+-------------
a | text | not null | main |
b | text | | extended |
c | text | | external |
Check constraints:
"t1_a_check" CHECK (length(a) > 2)
"t3_a_check" CHECK (length(a) < 5)
Inherits: t1,
t3
Has OIDs: no
CREATE TABLE t13_like (LIKE t3 INCLUDING CONSTRAINTS INCLUDING COMMENTS INCLUDING STORAGE) INHERITS (t1);
NOTICE: merging column "a" with inherited definition
\d+ t13_like
Table "public.t13_like"
Column | Type | Modifiers | Storage | Description
--------+------+-----------+----------+-------------
a | text | not null | main | A3
b | text | | extended |
c | text | | external | C
Check constraints:
"t1_a_check" CHECK (length(a) > 2)
"t3_a_check" CHECK (length(a) < 5)
Inherits: t1
Has OIDs: no
SELECT description FROM pg_description, pg_constraint c WHERE classoid = 'pg_constraint'::regclass AND objoid = c.oid AND c.conrelid = 't13_like'::regclass;
description
-------------
t3_a_check
(1 row)
CREATE TABLE t_all (LIKE t1 INCLUDING ALL);
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "t_all_pkey" for table "t_all"
\d+ t_all
Table "public.t_all"
Column | Type | Modifiers | Storage | Description
--------+------+-----------+----------+-------------
a | text | not null | main | A
b | text | | extended | B
Indexes:
"t_all_pkey" PRIMARY KEY, btree (a)
"t_all_b_key" btree (b)
"t_all_key" btree ((a || b))
Check constraints:
"t1_a_check" CHECK (length(a) > 2)
Has OIDs: no
SELECT c.relname, objsubid, description FROM pg_description, pg_index i, pg_class c WHERE classoid = 'pg_class'::regclass AND objoid = i.indexrelid AND c.oid = i.indexrelid AND i.indrelid = 't_all'::regclass ORDER BY c.relname, objsubid;
relname | objsubid | description
-------------+----------+---------------------
t_all_b_key | 0 | index b_key
t_all_key | 1 | index column fnidx
t_all_pkey | 0 | index pkey
t_all_pkey | 1 | index column pkey.a
(4 rows)
CREATE TABLE inh_error1 () INHERITS (t1, t4);
NOTICE: merging multiple inherited definitions of column "a"
ERROR: inherited column "a" has a storage parameter conflict
DETAIL: MAIN versus EXTENDED
CREATE TABLE inh_error2 (LIKE t4 INCLUDING STORAGE) INHERITS (t1);
NOTICE: merging column "a" with inherited definition
ERROR: column "a" has a storage parameter conflict
DETAIL: MAIN versus EXTENDED
DROP TABLE t1, t2, t3, t4, t12_storage, t12_comments, t1_inh, t13_inh, t13_like, t_all;

View File

@ -287,3 +287,52 @@ create table cc2(f4 float) inherits(pp1,cc1);
alter table pp1 add column a2 int check (a2 > 0);
\d cc2
drop table pp1 cascade;
-- including storage and comments
CREATE TABLE t1 (a text CHECK (length(a) > 2) PRIMARY KEY, b text);
CREATE INDEX t1_b_key ON t1 (b);
CREATE INDEX t1_fnidx ON t1 ((a || b));
COMMENT ON COLUMN t1.a IS 'A';
COMMENT ON COLUMN t1.b IS 'B';
COMMENT ON CONSTRAINT t1_a_check ON t1 IS 't1_a_check';
COMMENT ON INDEX t1_pkey IS 'index pkey';
COMMENT ON INDEX t1_b_key IS 'index b_key';
COMMENT ON COLUMN t1_pkey.a IS 'index column pkey.a';
COMMENT ON COLUMN t1_fnidx.pg_expression_1 IS 'index column fnidx';
ALTER TABLE t1 ALTER COLUMN a SET STORAGE MAIN;
CREATE TABLE t2 (c text);
ALTER TABLE t2 ALTER COLUMN c SET STORAGE EXTERNAL;
COMMENT ON COLUMN t2.c IS 'C';
CREATE TABLE t3 (a text CHECK (length(a) < 5), c text);
ALTER TABLE t3 ALTER COLUMN c SET STORAGE EXTERNAL;
ALTER TABLE t3 ALTER COLUMN a SET STORAGE MAIN;
COMMENT ON COLUMN t3.a IS 'A3';
COMMENT ON COLUMN t3.c IS 'C';
COMMENT ON CONSTRAINT t3_a_check ON t3 IS 't3_a_check';
CREATE TABLE t4 (a text, c text);
ALTER TABLE t4 ALTER COLUMN c SET STORAGE EXTERNAL;
CREATE TABLE t12_storage (LIKE t1 INCLUDING STORAGE, LIKE t2 INCLUDING STORAGE);
\d+ t12_storage
CREATE TABLE t12_comments (LIKE t1 INCLUDING COMMENTS, LIKE t2 INCLUDING COMMENTS);
\d+ t12_comments
CREATE TABLE t1_inh (LIKE t1 INCLUDING CONSTRAINTS INCLUDING COMMENTS) INHERITS (t1);
\d+ t1_inh
SELECT description FROM pg_description, pg_constraint c WHERE classoid = 'pg_constraint'::regclass AND objoid = c.oid AND c.conrelid = 't1_inh'::regclass;
CREATE TABLE t13_inh () INHERITS (t1, t3);
\d+ t13_inh
CREATE TABLE t13_like (LIKE t3 INCLUDING CONSTRAINTS INCLUDING COMMENTS INCLUDING STORAGE) INHERITS (t1);
\d+ t13_like
SELECT description FROM pg_description, pg_constraint c WHERE classoid = 'pg_constraint'::regclass AND objoid = c.oid AND c.conrelid = 't13_like'::regclass;
CREATE TABLE t_all (LIKE t1 INCLUDING ALL);
\d+ t_all
SELECT c.relname, objsubid, description FROM pg_description, pg_index i, pg_class c WHERE classoid = 'pg_class'::regclass AND objoid = i.indexrelid AND c.oid = i.indexrelid AND i.indrelid = 't_all'::regclass ORDER BY c.relname, objsubid;
CREATE TABLE inh_error1 () INHERITS (t1, t4);
CREATE TABLE inh_error2 (LIKE t4 INCLUDING STORAGE) INHERITS (t1);
DROP TABLE t1, t2, t3, t4, t12_storage, t12_comments, t1_inh, t13_inh, t13_like, t_all;