Commit 47477491 authored by Neil Conway's avatar Neil Conway

Implement CREATE TABLE LIKE ... INCLUDING INDEXES. Patch from NikhilS,

based in part on an earlier patch from Trevor Hardcastle, and reviewed
by myself.
parent 77d27e43
<!-- <!--
$PostgreSQL: pgsql/doc/src/sgml/ref/create_table.sgml,v 1.108 2007/06/03 17:06:03 tgl Exp $ $PostgreSQL: pgsql/doc/src/sgml/ref/create_table.sgml,v 1.109 2007/07/17 05:02:00 neilc Exp $
PostgreSQL documentation PostgreSQL documentation
--> -->
...@@ -23,7 +23,7 @@ PostgreSQL documentation ...@@ -23,7 +23,7 @@ PostgreSQL documentation
CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } ] TABLE <replaceable class="PARAMETER">table_name</replaceable> ( [ 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 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> | <replaceable>table_constraint</replaceable>
| LIKE <replaceable>parent_table</replaceable> [ { INCLUDING | EXCLUDING } { DEFAULTS | CONSTRAINTS } ] ... } | LIKE <replaceable>parent_table</replaceable> [ { INCLUDING | EXCLUDING } { DEFAULTS | CONSTRAINTS | INDEXES } ] ... }
[, ... ] [, ... ]
] ) ] )
[ INHERITS ( <replaceable>parent_table</replaceable> [, ... ] ) ] [ INHERITS ( <replaceable>parent_table</replaceable> [, ... ] ) ]
...@@ -237,7 +237,7 @@ and <replaceable class="PARAMETER">table_constraint</replaceable> is: ...@@ -237,7 +237,7 @@ and <replaceable class="PARAMETER">table_constraint</replaceable> is:
</varlistentry> </varlistentry>
<varlistentry> <varlistentry>
<term><literal>LIKE <replaceable>parent_table</replaceable> [ { INCLUDING | EXCLUDING } { DEFAULTS | CONSTRAINTS } ]</literal></term> <term><literal>LIKE <replaceable>parent_table</replaceable> [ { INCLUDING | EXCLUDING } { DEFAULTS | CONSTRAINTS | INDEXES } ]</literal></term>
<listitem> <listitem>
<para> <para>
The <literal>LIKE</literal> clause specifies a table from which The <literal>LIKE</literal> clause specifies a table from which
...@@ -265,11 +265,16 @@ and <replaceable class="PARAMETER">table_constraint</replaceable> is: ...@@ -265,11 +265,16 @@ and <replaceable class="PARAMETER">table_constraint</replaceable> is:
column constraints and table constraints &mdash; when constraints are column constraints and table constraints &mdash; when constraints are
requested, all check constraints are copied. requested, all check constraints are copied.
</para> </para>
<para>
Any indexes on the original table will not be created on the new
table, unless the <literal>INCLUDING INDEXES</literal> clause is
specified.
</para>
<para> <para>
Note also that unlike <literal>INHERITS</literal>, copied columns and Note also that unlike <literal>INHERITS</literal>, copied columns and
constraints are not merged with similarly named columns and constraints. constraints are not merged with similarly named columns and constraints.
If the same name is specified explicitly or in another If the same name is specified explicitly or in another
<literal>LIKE</literal> clause an error is signalled. <literal>LIKE</literal> clause, an error is signalled.
</para> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/bootstrap/bootparse.y,v 1.88 2007/03/13 00:33:39 tgl Exp $ * $PostgreSQL: pgsql/src/backend/bootstrap/bootparse.y,v 1.89 2007/07/17 05:02:00 neilc Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -252,7 +252,7 @@ Boot_DeclareIndexStmt: ...@@ -252,7 +252,7 @@ Boot_DeclareIndexStmt:
LexIDStr($8), LexIDStr($8),
NULL, NULL,
$10, $10,
NULL, NIL, NULL, NIL, NULL,
false, false, false, false, false, false,
false, false, true, false, false); false, false, true, false, false);
do_end(); do_end();
...@@ -270,7 +270,7 @@ Boot_DeclareUniqueIndexStmt: ...@@ -270,7 +270,7 @@ Boot_DeclareUniqueIndexStmt:
LexIDStr($9), LexIDStr($9),
NULL, NULL,
$11, $11,
NULL, NIL, NULL, NIL, NULL,
true, false, false, true, false, false,
false, false, true, false, false); false, false, true, false, false);
do_end(); do_end();
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/indexcmds.c,v 1.160 2007/06/23 22:12:50 tgl Exp $ * $PostgreSQL: pgsql/src/backend/commands/indexcmds.c,v 1.161 2007/07/17 05:02:00 neilc Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -79,6 +79,8 @@ static bool relationHasPrimaryKey(Relation rel); ...@@ -79,6 +79,8 @@ static bool relationHasPrimaryKey(Relation rel);
* to index on. * to index on.
* 'predicate': the partial-index condition, or NULL if none. * 'predicate': the partial-index condition, or NULL if none.
* 'options': reloptions from WITH (in list-of-DefElem form). * 'options': reloptions from WITH (in list-of-DefElem form).
* 'src_options': reloptions from the source index, if this is a cloned
* index produced by CREATE TABLE LIKE ... INCLUDING INDEXES
* 'unique': make the index enforce uniqueness. * 'unique': make the index enforce uniqueness.
* 'primary': mark the index as a primary key in the catalogs. * 'primary': mark the index as a primary key in the catalogs.
* 'isconstraint': index is for a PRIMARY KEY or UNIQUE constraint, * 'isconstraint': index is for a PRIMARY KEY or UNIQUE constraint,
...@@ -100,6 +102,7 @@ DefineIndex(RangeVar *heapRelation, ...@@ -100,6 +102,7 @@ DefineIndex(RangeVar *heapRelation,
List *attributeList, List *attributeList,
Expr *predicate, Expr *predicate,
List *options, List *options,
char *src_options,
bool unique, bool unique,
bool primary, bool primary,
bool isconstraint, bool isconstraint,
...@@ -392,9 +395,17 @@ DefineIndex(RangeVar *heapRelation, ...@@ -392,9 +395,17 @@ DefineIndex(RangeVar *heapRelation,
} }
/* /*
* Parse AM-specific options, convert to text array form, validate. * Parse AM-specific options, convert to text array form,
* validate. The src_options introduced due to using indexes
* via the "CREATE LIKE INCLUDING INDEXES" statement also need to
* be merged here
*/ */
reloptions = transformRelOptions((Datum) 0, options, false, false); if (src_options)
reloptions = unflatten_reloptions(src_options);
else
reloptions = (Datum) 0;
reloptions = transformRelOptions(reloptions, options, false, false);
(void) index_reloptions(amoptions, reloptions, true); (void) index_reloptions(amoptions, reloptions, true);
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.229 2007/07/03 01:30:36 neilc Exp $ * $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.230 2007/07/17 05:02:00 neilc Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -3794,6 +3794,7 @@ ATExecAddIndex(AlteredTableInfo *tab, Relation rel, ...@@ -3794,6 +3794,7 @@ ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
stmt->indexParams, /* parameters */ stmt->indexParams, /* parameters */
(Expr *) stmt->whereClause, (Expr *) stmt->whereClause,
stmt->options, stmt->options,
stmt->src_options,
stmt->unique, stmt->unique,
stmt->primary, stmt->primary,
stmt->isconstraint, stmt->isconstraint,
......
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.379 2007/06/11 22:22:40 tgl Exp $ * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.380 2007/07/17 05:02:01 neilc Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -2192,6 +2192,7 @@ _copyIndexStmt(IndexStmt *from) ...@@ -2192,6 +2192,7 @@ _copyIndexStmt(IndexStmt *from)
COPY_STRING_FIELD(tableSpace); COPY_STRING_FIELD(tableSpace);
COPY_NODE_FIELD(indexParams); COPY_NODE_FIELD(indexParams);
COPY_NODE_FIELD(options); COPY_NODE_FIELD(options);
COPY_STRING_FIELD(src_options);
COPY_NODE_FIELD(whereClause); COPY_NODE_FIELD(whereClause);
COPY_SCALAR_FIELD(unique); COPY_SCALAR_FIELD(unique);
COPY_SCALAR_FIELD(primary); COPY_SCALAR_FIELD(primary);
......
...@@ -18,7 +18,7 @@ ...@@ -18,7 +18,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.310 2007/06/11 22:22:40 tgl Exp $ * $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.311 2007/07/17 05:02:01 neilc Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -1044,6 +1044,7 @@ _equalIndexStmt(IndexStmt *a, IndexStmt *b) ...@@ -1044,6 +1044,7 @@ _equalIndexStmt(IndexStmt *a, IndexStmt *b)
COMPARE_STRING_FIELD(tableSpace); COMPARE_STRING_FIELD(tableSpace);
COMPARE_NODE_FIELD(indexParams); COMPARE_NODE_FIELD(indexParams);
COMPARE_NODE_FIELD(options); COMPARE_NODE_FIELD(options);
COMPARE_STRING_FIELD(src_options);
COMPARE_NODE_FIELD(whereClause); COMPARE_NODE_FIELD(whereClause);
COMPARE_SCALAR_FIELD(unique); COMPARE_SCALAR_FIELD(unique);
COMPARE_SCALAR_FIELD(primary); COMPARE_SCALAR_FIELD(primary);
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.312 2007/07/17 01:21:43 tgl Exp $ * $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.313 2007/07/17 05:02:01 neilc Exp $
* *
* NOTES * NOTES
* Every node type that can appear in stored rules' parsetrees *must* * Every node type that can appear in stored rules' parsetrees *must*
...@@ -1541,6 +1541,7 @@ _outIndexStmt(StringInfo str, IndexStmt *node) ...@@ -1541,6 +1541,7 @@ _outIndexStmt(StringInfo str, IndexStmt *node)
WRITE_STRING_FIELD(tableSpace); WRITE_STRING_FIELD(tableSpace);
WRITE_NODE_FIELD(indexParams); WRITE_NODE_FIELD(indexParams);
WRITE_NODE_FIELD(options); WRITE_NODE_FIELD(options);
WRITE_STRING_FIELD(src_options);
WRITE_NODE_FIELD(whereClause); WRITE_NODE_FIELD(whereClause);
WRITE_BOOL_FIELD(unique); WRITE_BOOL_FIELD(unique);
WRITE_BOOL_FIELD(primary); WRITE_BOOL_FIELD(primary);
......
...@@ -19,20 +19,23 @@ ...@@ -19,20 +19,23 @@
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/backend/parser/parse_utilcmd.c,v 2.1 2007/06/23 22:12:51 tgl Exp $ * $PostgreSQL: pgsql/src/backend/parser/parse_utilcmd.c,v 2.2 2007/07/17 05:02:02 neilc Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
#include "postgres.h" #include "postgres.h"
#include "access/genam.h"
#include "access/heapam.h" #include "access/heapam.h"
#include "catalog/heap.h" #include "catalog/heap.h"
#include "catalog/index.h" #include "catalog/index.h"
#include "catalog/namespace.h" #include "catalog/namespace.h"
#include "catalog/pg_opclass.h"
#include "catalog/pg_type.h" #include "catalog/pg_type.h"
#include "commands/defrem.h" #include "commands/defrem.h"
#include "commands/tablecmds.h" #include "commands/tablecmds.h"
#include "commands/tablespace.h"
#include "miscadmin.h" #include "miscadmin.h"
#include "nodes/makefuncs.h" #include "nodes/makefuncs.h"
#include "optimizer/clauses.h" #include "optimizer/clauses.h"
...@@ -47,6 +50,7 @@ ...@@ -47,6 +50,7 @@
#include "utils/acl.h" #include "utils/acl.h"
#include "utils/builtins.h" #include "utils/builtins.h"
#include "utils/lsyscache.h" #include "utils/lsyscache.h"
#include "utils/relcache.h"
#include "utils/syscache.h" #include "utils/syscache.h"
...@@ -63,6 +67,7 @@ typedef struct ...@@ -63,6 +67,7 @@ typedef struct
List *ckconstraints; /* CHECK constraints */ List *ckconstraints; /* CHECK constraints */
List *fkconstraints; /* FOREIGN KEY constraints */ List *fkconstraints; /* FOREIGN KEY constraints */
List *ixconstraints; /* index-creating constraints */ List *ixconstraints; /* index-creating constraints */
List *inh_indexes; /* cloned indexes from INCLUDING INDEXES */
List *blist; /* "before list" of things to do before List *blist; /* "before list" of things to do before
* creating the table */ * creating the table */
List *alist; /* "after list" of things to do after creating List *alist; /* "after list" of things to do after creating
...@@ -93,8 +98,13 @@ static void transformTableConstraint(ParseState *pstate, ...@@ -93,8 +98,13 @@ static void transformTableConstraint(ParseState *pstate,
Constraint *constraint); Constraint *constraint);
static void transformInhRelation(ParseState *pstate, CreateStmtContext *cxt, static void transformInhRelation(ParseState *pstate, CreateStmtContext *cxt,
InhRelation *inhrelation); InhRelation *inhrelation);
static IndexStmt *generateClonedIndexStmt(CreateStmtContext *cxt,
Relation parent_index, AttrNumber *attmap);
static List *get_opclass(Oid opclass, Oid actual_datatype);
static void transformIndexConstraints(ParseState *pstate, static void transformIndexConstraints(ParseState *pstate,
CreateStmtContext *cxt); CreateStmtContext *cxt);
static IndexStmt *transformIndexConstraint(Constraint *constraint,
CreateStmtContext *cxt);
static void transformFKConstraints(ParseState *pstate, static void transformFKConstraints(ParseState *pstate,
CreateStmtContext *cxt, CreateStmtContext *cxt,
bool skipValidation, bool skipValidation,
...@@ -146,6 +156,7 @@ transformCreateStmt(CreateStmt *stmt, const char *queryString) ...@@ -146,6 +156,7 @@ transformCreateStmt(CreateStmt *stmt, const char *queryString)
cxt.ckconstraints = NIL; cxt.ckconstraints = NIL;
cxt.fkconstraints = NIL; cxt.fkconstraints = NIL;
cxt.ixconstraints = NIL; cxt.ixconstraints = NIL;
cxt.inh_indexes = NIL;
cxt.blist = NIL; cxt.blist = NIL;
cxt.alist = NIL; cxt.alist = NIL;
cxt.pkey = NULL; cxt.pkey = NULL;
...@@ -555,11 +566,6 @@ transformInhRelation(ParseState *pstate, CreateStmtContext *cxt, ...@@ -555,11 +566,6 @@ transformInhRelation(ParseState *pstate, CreateStmtContext *cxt,
} }
} }
if (including_indexes)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("LIKE INCLUDING INDEXES is not implemented")));
/* /*
* Insert the copied attributes into the cxt for the new table * Insert the copied attributes into the cxt for the new table
* definition. * definition.
...@@ -657,6 +663,35 @@ transformInhRelation(ParseState *pstate, CreateStmtContext *cxt, ...@@ -657,6 +663,35 @@ transformInhRelation(ParseState *pstate, CreateStmtContext *cxt,
} }
} }
if (including_indexes && relation->rd_rel->relhasindex)
{
AttrNumber *attmap;
List *parent_indexes;
ListCell *l;
attmap = varattnos_map_schema(tupleDesc, cxt->columns);
parent_indexes = RelationGetIndexList(relation);
foreach(l, parent_indexes)
{
Oid parent_index_oid = lfirst_oid(l);
Relation parent_index;
IndexStmt *index_stmt;
parent_index = index_open(parent_index_oid, AccessShareLock);
/* Build CREATE INDEX statement to recreate the parent_index */
index_stmt = generateClonedIndexStmt(cxt, parent_index,
attmap);
/* Add the new IndexStmt to the create context */
cxt->inh_indexes = lappend(cxt->inh_indexes, index_stmt);
/* Keep our lock on the index till xact commit */
index_close(parent_index, NoLock);
}
}
/* /*
* Close the parent rel, but keep our AccessShareLock on it until xact * Close the parent rel, but keep our AccessShareLock on it until xact
* commit. That will prevent someone else from deleting or ALTERing the * commit. That will prevent someone else from deleting or ALTERing the
...@@ -665,37 +700,342 @@ transformInhRelation(ParseState *pstate, CreateStmtContext *cxt, ...@@ -665,37 +700,342 @@ transformInhRelation(ParseState *pstate, CreateStmtContext *cxt,
heap_close(relation, NoLock); heap_close(relation, NoLock);
} }
/*
* Generate an IndexStmt entry using information from an already
* existing index "source_idx".
*
* Note: Much of this functionality is cribbed from pg_get_indexdef.
*/
static IndexStmt *
generateClonedIndexStmt(CreateStmtContext *cxt, Relation source_idx,
AttrNumber *attmap)
{
HeapTuple ht_idx;
HeapTuple ht_idxrel;
HeapTuple ht_am;
Form_pg_index idxrec;
Form_pg_class idxrelrec;
Form_pg_am amrec;
List *indexprs = NIL;
ListCell *indexpr_item;
Oid indrelid;
Oid source_relid;
int keyno;
Oid keycoltype;
Datum indclassDatum;
Datum indoptionDatum;
bool isnull;
oidvector *indclass;
int2vector *indoption;
IndexStmt *index;
Datum reloptions;
source_relid = RelationGetRelid(source_idx);
/* Fetch pg_index tuple for source index */
ht_idx = SearchSysCache(INDEXRELID,
ObjectIdGetDatum(source_relid),
0, 0, 0);
if (!HeapTupleIsValid(ht_idx))
elog(ERROR, "cache lookup failed for index %u", source_relid);
idxrec = (Form_pg_index) GETSTRUCT(ht_idx);
Assert(source_relid == idxrec->indexrelid);
indrelid = idxrec->indrelid;
index = makeNode(IndexStmt);
index->unique = idxrec->indisunique;
index->concurrent = false;
index->primary = idxrec->indisprimary;
index->relation = cxt->relation;
index->isconstraint = false;
/*
* We don't try to preserve the name of the source index; instead, just
* let DefineIndex() choose a reasonable name.
*/
index->idxname = NULL;
/* Must get indclass and indoption the hard way */
indclassDatum = SysCacheGetAttr(INDEXRELID, ht_idx,
Anum_pg_index_indclass, &isnull);
Assert(!isnull);
indclass = (oidvector *) DatumGetPointer(indclassDatum);
indoptionDatum = SysCacheGetAttr(INDEXRELID, ht_idx,
Anum_pg_index_indoption, &isnull);
Assert(!isnull);
indoption = (int2vector *) DatumGetPointer(indoptionDatum);
/* Fetch pg_class tuple of source index */
ht_idxrel = SearchSysCache(RELOID,
ObjectIdGetDatum(source_relid),
0, 0, 0);
if (!HeapTupleIsValid(ht_idxrel))
elog(ERROR, "cache lookup failed for relation %u", source_relid);
/*
* Store the reloptions for later use by this new index
*/
reloptions = SysCacheGetAttr(RELOID, ht_idxrel,
Anum_pg_class_reloptions, &isnull);
if (!isnull)
index->src_options = flatten_reloptions(source_relid);
idxrelrec = (Form_pg_class) GETSTRUCT(ht_idxrel);
/* Fetch pg_am tuple for the index's access method */
ht_am = SearchSysCache(AMOID,
ObjectIdGetDatum(idxrelrec->relam),
0, 0, 0);
if (!HeapTupleIsValid(ht_am))
elog(ERROR, "cache lookup failed for access method %u",
idxrelrec->relam);
amrec = (Form_pg_am) GETSTRUCT(ht_am);
index->accessMethod = pstrdup(NameStr(amrec->amname));
/* Get the index expressions, if any */
if (!heap_attisnull(ht_idx, Anum_pg_index_indexprs))
{
Datum exprsDatum;
bool isnull;
char *exprsString;
exprsDatum = SysCacheGetAttr(INDEXRELID, ht_idx,
Anum_pg_index_indexprs, &isnull);
exprsString = DatumGetCString(DirectFunctionCall1(textout,
exprsDatum));
Assert(!isnull);
indexprs = (List *) stringToNode(exprsString);
}
indexpr_item = list_head(indexprs);
for (keyno = 0; keyno < idxrec->indnatts; keyno++)
{
IndexElem *iparam;
AttrNumber attnum = idxrec->indkey.values[keyno];
int16 opt = indoption->values[keyno];
iparam = makeNode(IndexElem);
if (AttributeNumberIsValid(attnum))
{
/* Simple index column */
char *attname;
attname = get_relid_attribute_name(indrelid, attnum);
keycoltype = get_atttype(indrelid, attnum);
iparam->name = attname;
iparam->expr = NULL;
}
else
{
/* Expressional index */
Node *indexkey;
if (indexpr_item == NULL)
elog(ERROR, "too few entries in indexprs list");
indexkey = (Node *) lfirst(indexpr_item);
change_varattnos_of_a_node(indexkey, attmap);
iparam->name = NULL;
iparam->expr = indexkey;
indexpr_item = lnext(indexpr_item);
keycoltype = exprType(indexkey);
}
/* Add the operator class name, if non-default */
iparam->opclass = get_opclass(indclass->values[keyno], keycoltype);
iparam->ordering = SORTBY_DEFAULT;
iparam->nulls_ordering = SORTBY_NULLS_DEFAULT;
/* Adjust options if necessary */
if (amrec->amcanorder)
{
/* If it supports sort ordering, report DESC and NULLS opts */
if (opt & INDOPTION_DESC)
iparam->ordering = SORTBY_DESC;
if (opt & INDOPTION_NULLS_FIRST)
iparam->nulls_ordering = SORTBY_NULLS_FIRST;
}
index->indexParams = lappend(index->indexParams, iparam);
}
/* Use the same tablespace as the source index */
index->tableSpace = get_tablespace_name(source_idx->rd_node.spcNode);
/* If it's a partial index, decompile and append the predicate */
if (!heap_attisnull(ht_idx, Anum_pg_index_indpred))
{
Datum pred_datum;
bool isnull;
char *pred_str;
/* Convert text string to node tree */
pred_datum = SysCacheGetAttr(INDEXRELID, ht_idx,
Anum_pg_index_indpred, &isnull);
Assert(!isnull);
pred_str = DatumGetCString(DirectFunctionCall1(textout,
pred_datum));
index->whereClause = (Node *) stringToNode(pred_str);
change_varattnos_of_a_node(index->whereClause, attmap);
}
/* Clean up */
ReleaseSysCache(ht_idx);
ReleaseSysCache(ht_idxrel);
ReleaseSysCache(ht_am);
return index;
}
/*
* get_opclass - fetch name of an index operator class
*
* If the opclass is the default for the given actual_datatype, then
* the return value is NIL.
*/
static List *
get_opclass(Oid opclass, Oid actual_datatype)
{
HeapTuple ht_opc;
Form_pg_opclass opc_rec;
List *result = NIL;
ht_opc = SearchSysCache(CLAOID,
ObjectIdGetDatum(opclass),
0, 0, 0);
if (!HeapTupleIsValid(ht_opc))
elog(ERROR, "cache lookup failed for opclass %u", opclass);
opc_rec = (Form_pg_opclass) GETSTRUCT(ht_opc);
if (!OidIsValid(actual_datatype) ||
GetDefaultOpClass(actual_datatype, opc_rec->opcmethod) != opclass)
{
char *nsp_name = get_namespace_name(opc_rec->opcnamespace);
char *opc_name = NameStr(opc_rec->opcname);
result = list_make2(makeString(nsp_name), makeString(opc_name));
}
ReleaseSysCache(ht_opc);
return result;
}
/* /*
* transformIndexConstraints * transformIndexConstraints
* Handle UNIQUE and PRIMARY KEY constraints, which create indexes * Handle UNIQUE and PRIMARY KEY constraints, which create
* indexes. We also merge index definitions arising from
* LIKE ... INCLUDING INDEXES.
*/ */
static void static void
transformIndexConstraints(ParseState *pstate, CreateStmtContext *cxt) transformIndexConstraints(ParseState *pstate, CreateStmtContext *cxt)
{ {
IndexStmt *index; IndexStmt *index;
List *indexlist = NIL; List *indexlist = NIL;
ListCell *listptr; ListCell *lc;
ListCell *l;
/* /*
* Run through the constraints that need to generate an index. For PRIMARY * Run through the constraints that need to generate an index. For PRIMARY
* KEY, mark each column as NOT NULL and create an index. For UNIQUE, * KEY, mark each column as NOT NULL and create an index. For UNIQUE,
* create an index as for PRIMARY KEY, but do not insist on NOT NULL. * create an index as for PRIMARY KEY, but do not insist on NOT NULL.
*/ */
foreach(listptr, cxt->ixconstraints) foreach(lc, cxt->ixconstraints)
{
Constraint *constraint = (Constraint *) lfirst(lc);
index = transformIndexConstraint(constraint, cxt);
indexlist = lappend(indexlist, index);
}
/*
* Scan the index list and remove any redundant index specifications. This
* can happen if, for instance, the user writes UNIQUE PRIMARY KEY. A
* strict reading of SQL92 would suggest raising an error instead, but
* that strikes me as too anal-retentive. - tgl 2001-02-14
*
* XXX in ALTER TABLE case, it'd be nice to look for duplicate
* pre-existing indexes, too.
*/
Assert(cxt->alist == NIL);
if (cxt->pkey != NULL)
{
/* Make sure we keep the PKEY index in preference to others... */
cxt->alist = list_make1(cxt->pkey);
}
foreach(lc, indexlist)
{
bool keep = true;
ListCell *k;
index = lfirst(lc);
/* if it's pkey, it's already in cxt->alist */
if (index == cxt->pkey)
continue;
foreach(k, cxt->alist)
{
IndexStmt *priorindex = lfirst(k);
if (equal(index->indexParams, priorindex->indexParams))
{
/*
* If the prior index is as yet unnamed, and this one is
* named, then transfer the name to the prior index. This
* ensures that if we have named and unnamed constraints,
* we'll use (at least one of) the names for the index.
*/
if (priorindex->idxname == NULL)
priorindex->idxname = index->idxname;
keep = false;
break;
}
}
if (keep)
cxt->alist = lappend(cxt->alist, index);
}
/* Copy indexes defined by LIKE ... INCLUDING INDEXES */
foreach(lc, cxt->inh_indexes)
{
index = (IndexStmt *) lfirst(lc);
if (index->primary)
{ {
Constraint *constraint = lfirst(listptr); if (cxt->pkey)
ereport(ERROR,
(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
errmsg("multiple primary keys for table \"%s\" are not allowed",
cxt->relation->relname)));
cxt->pkey = index;
}
cxt->alist = lappend(cxt->alist, index);
}
}
static IndexStmt *
transformIndexConstraint(Constraint *constraint, CreateStmtContext *cxt)
{
IndexStmt *index;
ListCell *keys; ListCell *keys;
IndexElem *iparam; IndexElem *iparam;
Assert(IsA(constraint, Constraint)); Assert(constraint->contype == CONSTR_PRIMARY ||
Assert((constraint->contype == CONSTR_PRIMARY) constraint->contype == CONSTR_UNIQUE);
|| (constraint->contype == CONSTR_UNIQUE));
index = makeNode(IndexStmt); index = makeNode(IndexStmt);
index->unique = true; index->unique = true;
index->primary = (constraint->contype == CONSTR_PRIMARY); index->primary = (constraint->contype == CONSTR_PRIMARY);
if (index->primary) if (index->primary)
{ {
if (cxt->pkey != NULL) if (cxt->pkey != NULL)
...@@ -848,58 +1188,7 @@ transformIndexConstraints(ParseState *pstate, CreateStmtContext *cxt) ...@@ -848,58 +1188,7 @@ transformIndexConstraints(ParseState *pstate, CreateStmtContext *cxt)
index->indexParams = lappend(index->indexParams, iparam); index->indexParams = lappend(index->indexParams, iparam);
} }
indexlist = lappend(indexlist, index); return index;
}
/*
* Scan the index list and remove any redundant index specifications. This
* can happen if, for instance, the user writes UNIQUE PRIMARY KEY. A
* strict reading of SQL92 would suggest raising an error instead, but
* that strikes me as too anal-retentive. - tgl 2001-02-14
*
* XXX in ALTER TABLE case, it'd be nice to look for duplicate
* pre-existing indexes, too.
*/
Assert(cxt->alist == NIL);
if (cxt->pkey != NULL)
{
/* Make sure we keep the PKEY index in preference to others... */
cxt->alist = list_make1(cxt->pkey);
}
foreach(l, indexlist)
{
bool keep = true;
ListCell *k;
index = lfirst(l);
/* if it's pkey, it's already in cxt->alist */
if (index == cxt->pkey)
continue;
foreach(k, cxt->alist)
{
IndexStmt *priorindex = lfirst(k);
if (equal(index->indexParams, priorindex->indexParams))
{
/*
* If the prior index is as yet unnamed, and this one is
* named, then transfer the name to the prior index. This
* ensures that if we have named and unnamed constraints,
* we'll use (at least one of) the names for the index.
*/
if (priorindex->idxname == NULL)
priorindex->idxname = index->idxname;
keep = false;
break;
}
}
if (keep)
cxt->alist = lappend(cxt->alist, index);
}
} }
/* /*
...@@ -1376,6 +1665,7 @@ transformAlterTableStmt(AlterTableStmt *stmt, const char *queryString) ...@@ -1376,6 +1665,7 @@ transformAlterTableStmt(AlterTableStmt *stmt, const char *queryString)
cxt.ckconstraints = NIL; cxt.ckconstraints = NIL;
cxt.fkconstraints = NIL; cxt.fkconstraints = NIL;
cxt.ixconstraints = NIL; cxt.ixconstraints = NIL;
cxt.inh_indexes = NIL;
cxt.blist = NIL; cxt.blist = NIL;
cxt.alist = NIL; cxt.alist = NIL;
cxt.pkey = NULL; cxt.pkey = NULL;
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.283 2007/07/03 01:30:37 neilc Exp $ * $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.284 2007/07/17 05:02:02 neilc Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -886,6 +886,7 @@ ProcessUtility(Node *parsetree, ...@@ -886,6 +886,7 @@ ProcessUtility(Node *parsetree,
stmt->indexParams, /* parameters */ stmt->indexParams, /* parameters */
(Expr *) stmt->whereClause, (Expr *) stmt->whereClause,
stmt->options, stmt->options,
stmt->src_options,
stmt->unique, stmt->unique,
stmt->primary, stmt->primary,
stmt->isconstraint, stmt->isconstraint,
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.262 2007/06/18 21:40:58 tgl Exp $ * $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.263 2007/07/17 05:02:02 neilc Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -193,7 +193,6 @@ static char *generate_relation_name(Oid relid); ...@@ -193,7 +193,6 @@ static char *generate_relation_name(Oid relid);
static char *generate_function_name(Oid funcid, int nargs, Oid *argtypes); static char *generate_function_name(Oid funcid, int nargs, Oid *argtypes);
static char *generate_operator_name(Oid operid, Oid arg1, Oid arg2); static char *generate_operator_name(Oid operid, Oid arg1, Oid arg2);
static text *string_to_text(char *str); static text *string_to_text(char *str);
static char *flatten_reloptions(Oid relid);
#define only_marker(rte) ((rte)->inh ? "" : "ONLY ") #define only_marker(rte) ((rte)->inh ? "" : "ONLY ")
...@@ -763,8 +762,7 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, int prettyFlags) ...@@ -763,8 +762,7 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, int prettyFlags)
/* Add the operator class name */ /* Add the operator class name */
if (!colno) if (!colno)
get_opclass_name(indclass->values[keyno], keycoltype, get_opclass_name(indclass->values[keyno], keycoltype, &buf);
&buf);
/* Add options if relevant */ /* Add options if relevant */
if (amrec->amcanorder) if (amrec->amcanorder)
...@@ -5417,7 +5415,7 @@ string_to_text(char *str) ...@@ -5417,7 +5415,7 @@ string_to_text(char *str)
/* /*
* Generate a C string representing a relation's reloptions, or NULL if none. * Generate a C string representing a relation's reloptions, or NULL if none.
*/ */
static char * char *
flatten_reloptions(Oid relid) flatten_reloptions(Oid relid)
{ {
char *result = NULL; char *result = NULL;
...@@ -5453,3 +5451,31 @@ flatten_reloptions(Oid relid) ...@@ -5453,3 +5451,31 @@ flatten_reloptions(Oid relid)
return result; return result;
} }
/*
* Generate an Array Datum representing a relation's reloptions using
* a C string
*/
Datum
unflatten_reloptions(char *reloptstring)
{
Datum result = (Datum) 0;
if (reloptstring)
{
Datum sep, relopts;
/*
* We want to use text_to_array(reloptstring, ', ') --- but
* DirectFunctionCall2(text_to_array) does not work, because
* text_to_array() relies on fcinfo to be valid. So use
* OidFunctionCall2.
*/
sep = DirectFunctionCall1(textin, CStringGetDatum(", "));
relopts = DirectFunctionCall1(textin, CStringGetDatum(reloptstring));
result = OidFunctionCall2(F_TEXT_TO_ARRAY, relopts, sep);
}
return result;
}
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/commands/defrem.h,v 1.81 2007/03/13 00:33:43 tgl Exp $ * $PostgreSQL: pgsql/src/include/commands/defrem.h,v 1.82 2007/07/17 05:02:02 neilc Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -26,6 +26,7 @@ extern void DefineIndex(RangeVar *heapRelation, ...@@ -26,6 +26,7 @@ extern void DefineIndex(RangeVar *heapRelation,
List *attributeList, List *attributeList,
Expr *predicate, Expr *predicate,
List *options, List *options,
char *src_options,
bool unique, bool unique,
bool primary, bool primary,
bool isconstraint, bool isconstraint,
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.349 2007/06/23 22:12:52 tgl Exp $ * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.350 2007/07/17 05:02:02 neilc Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -1501,6 +1501,7 @@ typedef struct IndexStmt ...@@ -1501,6 +1501,7 @@ typedef struct IndexStmt
char *tableSpace; /* tablespace, or NULL to use parent's */ char *tableSpace; /* tablespace, or NULL to use parent's */
List *indexParams; /* a list of IndexElem */ List *indexParams; /* a list of IndexElem */
List *options; /* options from WITH clause */ List *options; /* options from WITH clause */
char *src_options; /* relopts inherited from source index */
Node *whereClause; /* qualification (partial-index predicate) */ Node *whereClause; /* qualification (partial-index predicate) */
bool unique; /* is index unique? */ bool unique; /* is index unique? */
bool primary; /* is index on primary key? */ bool primary; /* is index on primary key? */
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/utils/builtins.h,v 1.297 2007/06/26 16:48:09 alvherre Exp $ * $PostgreSQL: pgsql/src/include/utils/builtins.h,v 1.298 2007/07/17 05:02:02 neilc Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -560,6 +560,8 @@ extern List *deparse_context_for_plan(Node *outer_plan, Node *inner_plan, ...@@ -560,6 +560,8 @@ extern List *deparse_context_for_plan(Node *outer_plan, Node *inner_plan,
extern const char *quote_identifier(const char *ident); extern const char *quote_identifier(const char *ident);
extern char *quote_qualified_identifier(const char *namespace, extern char *quote_qualified_identifier(const char *namespace,
const char *ident); const char *ident);
extern char *flatten_reloptions(Oid relid);
extern Datum unflatten_reloptions(char *reloptstring);
/* tid.c */ /* tid.c */
extern Datum tidin(PG_FUNCTION_ARGS); extern Datum tidin(PG_FUNCTION_ARGS);
......
...@@ -633,6 +633,26 @@ SELECT * FROM inhg; /* Two records with three columns in order x=x, xx=text, y=y ...@@ -633,6 +633,26 @@ SELECT * FROM inhg; /* Two records with three columns in order x=x, xx=text, y=y
(2 rows) (2 rows)
DROP TABLE inhg; DROP TABLE inhg;
CREATE TABLE inhg (x text, LIKE inhx INCLUDING INDEXES, y text); /* copies indexes */
INSERT INTO inhg VALUES (5, 10);
INSERT INTO inhg VALUES (20, 10); -- should fail
ERROR: duplicate key value violates unique constraint "inhg_pkey"
DROP TABLE inhg;
/* Multiple primary keys creation should fail */
CREATE TABLE inhg (x text, LIKE inhx INCLUDING INDEXES, PRIMARY KEY(x)); /* fails */
ERROR: multiple primary keys for table "inhg" are not allowed
CREATE TABLE inhz (xx text DEFAULT 'text', yy int UNIQUE);
NOTICE: CREATE TABLE / UNIQUE will create implicit index "inhz_yy_key" for table "inhz"
CREATE UNIQUE INDEX inhz_xx_idx on inhz (xx) WHERE xx <> 'test';
/* Ok to create multiple unique indexes */
CREATE TABLE inhg (x text UNIQUE, LIKE inhz INCLUDING INDEXES);
NOTICE: CREATE TABLE / UNIQUE will create implicit index "inhg_x_key" for table "inhg"
INSERT INTO inhg (xx, yy, x) VALUES ('test', 5, 10);
INSERT INTO inhg (xx, yy, x) VALUES ('test', 10, 15);
INSERT INTO inhg (xx, yy, x) VALUES ('foo', 10, 15); -- should fail
ERROR: duplicate key value violates unique constraint "inhg_x_key"
DROP TABLE inhg;
DROP TABLE inhz;
-- Test changing the type of inherited columns -- Test changing the type of inherited columns
insert into d values('test','one','two','three'); insert into d values('test','one','two','three');
alter table a alter column aa type integer using bit_length(aa); alter table a alter column aa type integer using bit_length(aa);
......
...@@ -156,6 +156,21 @@ INSERT INTO inhg VALUES ('x', 'foo', 'y'); /* fails due to constraint */ ...@@ -156,6 +156,21 @@ INSERT INTO inhg VALUES ('x', 'foo', 'y'); /* fails due to constraint */
SELECT * FROM inhg; /* Two records with three columns in order x=x, xx=text, y=y */ SELECT * FROM inhg; /* Two records with three columns in order x=x, xx=text, y=y */
DROP TABLE inhg; DROP TABLE inhg;
CREATE TABLE inhg (x text, LIKE inhx INCLUDING INDEXES, y text); /* copies indexes */
INSERT INTO inhg VALUES (5, 10);
INSERT INTO inhg VALUES (20, 10); -- should fail
DROP TABLE inhg;
/* Multiple primary keys creation should fail */
CREATE TABLE inhg (x text, LIKE inhx INCLUDING INDEXES, PRIMARY KEY(x)); /* fails */
CREATE TABLE inhz (xx text DEFAULT 'text', yy int UNIQUE);
CREATE UNIQUE INDEX inhz_xx_idx on inhz (xx) WHERE xx <> 'test';
/* Ok to create multiple unique indexes */
CREATE TABLE inhg (x text UNIQUE, LIKE inhz INCLUDING INDEXES);
INSERT INTO inhg (xx, yy, x) VALUES ('test', 5, 10);
INSERT INTO inhg (xx, yy, x) VALUES ('test', 10, 15);
INSERT INTO inhg (xx, yy, x) VALUES ('foo', 10, 15); -- should fail
DROP TABLE inhg;
DROP TABLE inhz;
-- Test changing the type of inherited columns -- Test changing the type of inherited columns
insert into d values('test','one','two','three'); insert into d values('test','one','two','three');
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment