Index: doc/src/sgml/ref/create_table.sgml
===================================================================
RCS file: /repositories/postgreshome/cvs/pgsql/doc/src/sgml/ref/create_table.sgml,v
retrieving revision 1.107
diff -c -r1.107 create_table.sgml
*** doc/src/sgml/ref/create_table.sgml 1 Feb 2007 00:28:18 -0000 1.107
--- doc/src/sgml/ref/create_table.sgml 21 May 2007 06:50:34 -0000
***************
*** 259,269 ****
Not-null constraints are always copied to the new table.
! CHECK constraints will only be copied if
! INCLUDING CONSTRAINTS is specified; other types of
! constraints will never be copied. Also, no distinction is made between
! column constraints and table constraints — when constraints are
! requested, all check constraints are copied.
Note also that unlike INHERITS, copied columns and
--- 259,268 ----
Not-null constraints are always copied to the new table.
! CHECK, UNIQUE, and PRIMARY KEY constraints will only
! be copied if INCLUDING CONSTRAINTS is specified. Also,
! no distinction is made between column constraints and table constraints
! — when constraints are requested, all check constraints are copied.
Note also that unlike INHERITS, copied columns and
Index: src/backend/bootstrap/bootparse.y
===================================================================
RCS file: /repositories/postgreshome/cvs/pgsql/src/backend/bootstrap/bootparse.y,v
retrieving revision 1.88
diff -c -r1.88 bootparse.y
*** src/backend/bootstrap/bootparse.y 13 Mar 2007 00:33:39 -0000 1.88
--- src/backend/bootstrap/bootparse.y 21 May 2007 06:50:34 -0000
***************
*** 252,258 ****
LexIDStr($8),
NULL,
$10,
! NULL, NIL,
false, false, false,
false, false, true, false, false);
do_end();
--- 252,258 ----
LexIDStr($8),
NULL,
$10,
! NULL, NIL, NULL,
false, false, false,
false, false, true, false, false);
do_end();
***************
*** 270,276 ****
LexIDStr($9),
NULL,
$11,
! NULL, NIL,
true, false, false,
false, false, true, false, false);
do_end();
--- 270,276 ----
LexIDStr($9),
NULL,
$11,
! NULL, NIL, NULL,
true, false, false,
false, false, true, false, false);
do_end();
Index: src/backend/commands/indexcmds.c
===================================================================
RCS file: /repositories/postgreshome/cvs/pgsql/src/backend/commands/indexcmds.c,v
retrieving revision 1.158
diff -c -r1.158 indexcmds.c
*** src/backend/commands/indexcmds.c 2 May 2007 21:08:45 -0000 1.158
--- src/backend/commands/indexcmds.c 21 May 2007 06:50:34 -0000
***************
*** 100,105 ****
--- 100,106 ----
List *attributeList,
Expr *predicate,
List *options,
+ char *inhreloptions,
bool unique,
bool primary,
bool isconstraint,
***************
*** 393,400 ****
/*
* Parse AM-specific options, convert to text array form, validate.
*/
! reloptions = transformRelOptions((Datum) 0, options, false, false);
(void) index_reloptions(amoptions, reloptions, true);
--- 394,408 ----
/*
* Parse AM-specific options, convert to text array form, validate.
+ * The inh reloptions introduced due to using unique/primary indexes via
+ * the "CREATE LIKE INCLUDING CONSTRAINTS" statement also need to be merged here
*/
! if (inhreloptions)
! reloptions = deflatten_reloptions(inhreloptions);
! else
! reloptions = (Datum) 0;
!
! reloptions = transformRelOptions(reloptions, options, false, false);
(void) index_reloptions(amoptions, reloptions, true);
Index: src/backend/commands/tablecmds.c
===================================================================
RCS file: /repositories/postgreshome/cvs/pgsql/src/backend/commands/tablecmds.c,v
retrieving revision 1.225
diff -c -r1.225 tablecmds.c
*** src/backend/commands/tablecmds.c 18 May 2007 23:19:41 -0000 1.225
--- src/backend/commands/tablecmds.c 21 May 2007 06:50:34 -0000
***************
*** 3781,3786 ****
--- 3781,3787 ----
stmt->indexParams, /* parameters */
(Expr *) stmt->whereClause,
stmt->options,
+ stmt->inhreloptions,
stmt->unique,
stmt->primary,
stmt->isconstraint,
Index: src/backend/nodes/copyfuncs.c
===================================================================
RCS file: /repositories/postgreshome/cvs/pgsql/src/backend/nodes/copyfuncs.c,v
retrieving revision 1.375
diff -c -r1.375 copyfuncs.c
*** src/backend/nodes/copyfuncs.c 27 Apr 2007 22:05:47 -0000 1.375
--- src/backend/nodes/copyfuncs.c 21 May 2007 06:50:34 -0000
***************
*** 2161,2166 ****
--- 2161,2167 ----
COPY_STRING_FIELD(tableSpace);
COPY_NODE_FIELD(indexParams);
COPY_NODE_FIELD(options);
+ COPY_STRING_FIELD(inhreloptions);
COPY_NODE_FIELD(whereClause);
COPY_SCALAR_FIELD(unique);
COPY_SCALAR_FIELD(primary);
Index: src/backend/parser/analyze.c
===================================================================
RCS file: /repositories/postgreshome/cvs/pgsql/src/backend/parser/analyze.c,v
retrieving revision 1.363
diff -c -r1.363 analyze.c
*** src/backend/parser/analyze.c 27 Apr 2007 22:05:48 -0000 1.363
--- src/backend/parser/analyze.c 21 May 2007 06:50:34 -0000
***************
*** 27,39 ****
--- 27,42 ----
#include "postgres.h"
+ #include "access/genam.h"
#include "access/heapam.h"
#include "catalog/heap.h"
#include "catalog/index.h"
#include "catalog/namespace.h"
#include "catalog/pg_type.h"
+ #include "catalog/pg_opclass.h"
#include "commands/defrem.h"
#include "commands/prepare.h"
+ #include "commands/tablespace.h"
#include "commands/tablecmds.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
***************
*** 54,60 ****
--- 57,65 ----
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
+ #include "utils/relcache.h"
#include "utils/syscache.h"
+ #include "utils/fmgroids.h"
/* State shared by transformCreateSchemaStmt and its subroutines */
***************
*** 150,155 ****
--- 155,163 ----
static FromExpr *makeFromExpr(List *fromlist, Node *quals);
static bool check_parameter_resolution_walker(Node *node,
check_parameter_resolution_context *context);
+ static IndexStmt *generateConstraintIndexStmt(CreateStmtContext *cxt,
+ Relation parent_index, AttrNumber *attmap);
+ static void get_opclass(Oid opclass, Oid actual_datatype, List **oplist);
/*
***************
*** 1349,1378 ****
}
}
! /*
! * 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;
! for (ccnum = 0; ccnum < tupleDesc->constr->num_check; ccnum++)
{
! char *ccname = tupleDesc->constr->check[ccnum].ccname;
! char *ccbin = tupleDesc->constr->check[ccnum].ccbin;
! Node *ccbin_node = stringToNode(ccbin);
! Constraint *n = makeNode(Constraint);
! change_varattnos_of_a_node(ccbin_node, attmap);
! n->contype = CONSTR_CHECK;
! n->name = pstrdup(ccname);
! n->raw_expr = NULL;
! n->cooked_expr = nodeToString(ccbin_node);
! n->indexspace = NULL;
! cxt->ckconstraints = lappend(cxt->ckconstraints, (Node *) n);
}
}
--- 1357,1426 ----
}
}
! if (including_constraints)
{
! /*
! * Copy CHECK constraints, being careful to adjust attribute
! * numbers.
! */
! if (tupleDesc->constr)
! {
! AttrNumber *attmap;
! int ccnum;
! attmap = varattnos_map_schema(tupleDesc, cxt->columns);
! for (ccnum = 0; ccnum < tupleDesc->constr->num_check; ccnum++)
! {
! char *ccname = tupleDesc->constr->check[ccnum].ccname;
! char *ccbin = tupleDesc->constr->check[ccnum].ccbin;
! Node *ccbin_node = stringToNode(ccbin);
! Constraint *n = makeNode(Constraint);
!
! change_varattnos_of_a_node(ccbin_node, attmap);
!
! n->contype = CONSTR_CHECK;
! n->name = pstrdup(ccname);
! n->raw_expr = NULL;
! n->cooked_expr = nodeToString(ccbin_node);
! n->indexspace = NULL;
! cxt->ckconstraints = lappend(cxt->ckconstraints, (Node *) n);
! }
! }
!
! /* Copy constraint indexes, if any */
! if (relation->rd_rel->relhasindex)
{
! List *parent_index_list = RelationGetIndexList(relation);
! ListCell *parent_index_scan;
! IndexStmt *index;
! AttrNumber *attmap;
!
! foreach(parent_index_scan, parent_index_list)
! {
! Oid parent_index_oid = lfirst_oid(parent_index_scan);
! Relation parent_index;
! parent_index = index_open(parent_index_oid, AccessShareLock);
! /*
! * It would be best to generate IndexStmts here and append
! * them to the ixconstraints list. Constraint structure does
! * not contain all the index-relevant fields as well and hence
! * we use IndexStmts here.
! */
! attmap = varattnos_map_schema(tupleDesc, cxt->columns);
! index = generateConstraintIndexStmt(cxt, parent_index, attmap);
! if (index == NULL)
! {
! index_close(parent_index, AccessShareLock);
! continue;
! }
! /* Add the new indexstmt entry to the create context */
! cxt->ixconstraints = lappend(cxt->ixconstraints, index);
!
! /* Keep our lock on the index till xact commit */
! index_close(parent_index, NoLock);
! }
}
}
***************
*** 1396,1568 ****
* 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,
* create an index as for PRIMARY KEY, but do not insist on NOT NULL.
*/
foreach(listptr, cxt->ixconstraints)
{
! Constraint *constraint = lfirst(listptr);
ListCell *keys;
IndexElem *iparam;
! Assert(IsA(constraint, Constraint));
! Assert((constraint->contype == CONSTR_PRIMARY)
! || (constraint->contype == CONSTR_UNIQUE));
!
! index = makeNode(IndexStmt);
! index->unique = true;
! index->primary = (constraint->contype == CONSTR_PRIMARY);
! if (index->primary)
{
! if (cxt->pkey != NULL)
! ereport(ERROR,
! (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
! errmsg("multiple primary keys for table \"%s\" are not allowed",
! cxt->relation->relname)));
! cxt->pkey = index;
!
! /*
! * In ALTER TABLE case, a primary index might already exist, but
! * DefineIndex will check for it.
! */
}
- index->isconstraint = true;
-
- if (constraint->name != NULL)
- index->idxname = pstrdup(constraint->name);
else
! index->idxname = NULL; /* DefineIndex will choose name */
!
! index->relation = cxt->relation;
! index->accessMethod = DEFAULT_INDEX_TYPE;
! index->options = constraint->options;
! index->tableSpace = constraint->indexspace;
! index->indexParams = NIL;
! index->whereClause = NULL;
! index->concurrent = false;
!
! /*
! * Make sure referenced keys exist. If we are making a PRIMARY KEY
! * index, also make sure they are NOT NULL, if possible. (Although we
! * could leave it to DefineIndex to mark the columns NOT NULL, it's
! * more efficient to get it right the first time.)
! */
! foreach(keys, constraint->keys)
! {
! char *key = strVal(lfirst(keys));
! bool found = false;
! ColumnDef *column = NULL;
! ListCell *columns;
! foreach(columns, cxt->columns)
! {
! column = (ColumnDef *) lfirst(columns);
! Assert(IsA(column, ColumnDef));
! if (strcmp(column->colname, key) == 0)
! {
! found = true;
! break;
! }
! }
! if (found)
! {
! /* found column in the new table; force it to be NOT NULL */
! if (constraint->contype == CONSTR_PRIMARY)
! column->is_not_null = TRUE;
! }
! else if (SystemAttributeByName(key, cxt->hasoids) != NULL)
{
/*
! * column will be a system column in the new table, so accept
! * it. System columns can't ever be null, so no need to worry
! * about PRIMARY/NOT NULL constraint.
*/
- found = true;
}
! else if (cxt->inhRelations)
{
! /* try inherited tables */
! ListCell *inher;
! foreach(inher, cxt->inhRelations)
{
! RangeVar *inh = (RangeVar *) lfirst(inher);
! Relation rel;
! int count;
!
! Assert(IsA(inh, RangeVar));
! rel = heap_openrv(inh, AccessShareLock);
! if (rel->rd_rel->relkind != RELKIND_RELATION)
! ereport(ERROR,
! (errcode(ERRCODE_WRONG_OBJECT_TYPE),
! errmsg("inherited relation \"%s\" is not a table",
! inh->relname)));
! for (count = 0; count < rel->rd_att->natts; count++)
{
! Form_pg_attribute inhattr = rel->rd_att->attrs[count];
! char *inhname = NameStr(inhattr->attname);
! if (inhattr->attisdropped)
! continue;
! if (strcmp(key, inhname) == 0)
{
! found = true;
! /*
! * We currently have no easy way to force an
! * inherited column to be NOT NULL at creation, if
! * its parent wasn't so already. We leave it to
! * DefineIndex to fix things up in this case.
! */
! break;
}
}
- heap_close(rel, NoLock);
- if (found)
- break;
}
- }
! /*
! * In the ALTER TABLE case, don't complain about index keys not
! * created in the command; they may well exist already.
! * DefineIndex will complain about them if not, and will also take
! * care of marking them NOT NULL.
! */
! if (!found && !cxt->isalter)
! ereport(ERROR,
! (errcode(ERRCODE_UNDEFINED_COLUMN),
! errmsg("column \"%s\" named in key does not exist",
! key)));
! /* Check for PRIMARY KEY(foo, foo) */
! foreach(columns, index->indexParams)
! {
! iparam = (IndexElem *) lfirst(columns);
! if (iparam->name && strcmp(key, iparam->name) == 0)
{
! if (index->primary)
! ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_COLUMN),
errmsg("column \"%s\" appears twice in primary key constraint",
! key)));
! else
! ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_COLUMN),
errmsg("column \"%s\" appears twice in unique constraint",
! key)));
}
}
- /* OK, add it to the index definition */
- iparam = makeNode(IndexElem);
- iparam->name = pstrdup(key);
- iparam->expr = NULL;
- iparam->opclass = NIL;
- iparam->ordering = SORTBY_DEFAULT;
- iparam->nulls_ordering = SORTBY_NULLS_DEFAULT;
- index->indexParams = lappend(index->indexParams, iparam);
}
-
indexlist = lappend(indexlist, index);
}
--- 1444,1640 ----
* 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,
* create an index as for PRIMARY KEY, but do not insist on NOT NULL.
+ *
+ * If the table is being created using LIKE INCLUDING CONSTRAINTS, the
+ * ixconstraints list will contain a mix of Constraint and IndexStmt
+ * entries
*/
foreach(listptr, cxt->ixconstraints)
{
! Node *node = lfirst(listptr);
! Constraint *constraint;
ListCell *keys;
IndexElem *iparam;
! Assert(IsA(node, Constraint) || IsA(node, IndexStmt));
! if (IsA(node, IndexStmt))
{
! index = (IndexStmt *)node;
! if (index->primary)
! {
! if (cxt->pkey != NULL)
! ereport(ERROR,
! (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
! errmsg("multiple primary keys for table \"%s\" are not allowed",
! cxt->relation->relname)));
! cxt->pkey = index;
! }
}
else
! {
! Assert(IsA(node, Constraint));
! constraint = (Constraint *)node;
! Assert((constraint->contype == CONSTR_PRIMARY)
! || (constraint->contype == CONSTR_UNIQUE));
! index = makeNode(IndexStmt);
!
! index->unique = true;
! index->primary = (constraint->contype == CONSTR_PRIMARY);
! if (index->primary)
{
+ if (cxt->pkey != NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
+ errmsg("multiple primary keys for table \"%s\" are not allowed",
+ cxt->relation->relname)));
+ cxt->pkey = index;
+
/*
! * In ALTER TABLE case, a primary index might already exist, but
! * DefineIndex will check for it.
*/
}
! index->isconstraint = true;
!
! if (constraint->name != NULL)
! index->idxname = pstrdup(constraint->name);
! else
! index->idxname = NULL; /* DefineIndex will choose name */
!
! index->relation = cxt->relation;
! index->accessMethod = DEFAULT_INDEX_TYPE;
! index->options = constraint->options;
! index->tableSpace = constraint->indexspace;
! index->indexParams = NIL;
! index->whereClause = NULL;
! index->concurrent = false;
!
! /*
! * Make sure referenced keys exist. If we are making a PRIMARY KEY
! * index, also make sure they are NOT NULL, if possible. (Although we
! * could leave it to DefineIndex to mark the columns NOT NULL, it's
! * more efficient to get it right the first time.)
! */
! foreach(keys, constraint->keys)
{
! char *key = strVal(lfirst(keys));
! bool found = false;
! ColumnDef *column = NULL;
! ListCell *columns;
! foreach(columns, cxt->columns)
{
! column = (ColumnDef *) lfirst(columns);
! Assert(IsA(column, ColumnDef));
! if (strcmp(column->colname, key) == 0)
{
! found = true;
! break;
! }
! }
! if (found)
! {
! /* found column in the new table; force it to be NOT NULL */
! if (constraint->contype == CONSTR_PRIMARY)
! column->is_not_null = TRUE;
! }
! else if (SystemAttributeByName(key, cxt->hasoids) != NULL)
! {
! /*
! * column will be a system column in the new table, so accept
! * it. System columns can't ever be null, so no need to worry
! * about PRIMARY/NOT NULL constraint.
! */
! found = true;
! }
! else if (cxt->inhRelations)
! {
! /* try inherited tables */
! ListCell *inher;
! foreach(inher, cxt->inhRelations)
! {
! RangeVar *inh = (RangeVar *) lfirst(inher);
! Relation rel;
! int count;
!
! Assert(IsA(inh, RangeVar));
! rel = heap_openrv(inh, AccessShareLock);
! if (rel->rd_rel->relkind != RELKIND_RELATION)
! ereport(ERROR,
! (errcode(ERRCODE_WRONG_OBJECT_TYPE),
! errmsg("inherited relation \"%s\" is not a table",
! inh->relname)));
! for (count = 0; count < rel->rd_att->natts; count++)
{
! Form_pg_attribute inhattr = rel->rd_att->attrs[count];
! char *inhname = NameStr(inhattr->attname);
! if (inhattr->attisdropped)
! continue;
! if (strcmp(key, inhname) == 0)
! {
! found = true;
!
! /*
! * We currently have no easy way to force an
! * inherited column to be NOT NULL at creation, if
! * its parent wasn't so already. We leave it to
! * DefineIndex to fix things up in this case.
! */
! break;
! }
}
+ heap_close(rel, NoLock);
+ if (found)
+ break;
}
}
! /*
! * In the ALTER TABLE case, don't complain about index keys not
! * created in the command; they may well exist already.
! * DefineIndex will complain about them if not, and will also take
! * care of marking them NOT NULL.
! */
! if (!found && !cxt->isalter)
! ereport(ERROR,
! (errcode(ERRCODE_UNDEFINED_COLUMN),
! errmsg("column \"%s\" named in key does not exist",
! key)));
! /* Check for PRIMARY KEY(foo, foo) */
! foreach(columns, index->indexParams)
{
! iparam = (IndexElem *) lfirst(columns);
! if (iparam->name && strcmp(key, iparam->name) == 0)
! {
! if (index->primary)
! ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_COLUMN),
errmsg("column \"%s\" appears twice in primary key constraint",
! key)));
! else
! ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_COLUMN),
errmsg("column \"%s\" appears twice in unique constraint",
! key)));
! }
}
+
+ /* OK, add it to the index definition */
+ iparam = makeNode(IndexElem);
+ iparam->name = pstrdup(key);
+ iparam->expr = NULL;
+ iparam->opclass = NIL;
+ iparam->ordering = SORTBY_DEFAULT;
+ iparam->nulls_ordering = SORTBY_NULLS_DEFAULT;
+ index->indexParams = lappend(index->indexParams, iparam);
}
}
indexlist = lappend(indexlist, index);
}
***************
*** 3735,3737 ****
--- 3807,4055 ----
return expression_tree_walker(node, check_parameter_resolution_walker,
(void *) context);
}
+
+ /*
+ * generateConstraintIndexStmt:
+ *
+ * Generate an IndexStmt entry using information from an already existing
+ * index. Currently this function only generates entries for unique/primary
+ * indexes, but can be easily generalized to work for any index
+ * entry.
+ */
+ static IndexStmt
+ *generateConstraintIndexStmt(CreateStmtContext *cxt, Relation parent_index, 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;
+ ListCell *indexpr_item;
+ Oid indrelid, indexrelid;
+ int keyno;
+ Oid keycoltype;
+ Datum indclassDatum;
+ Datum indoptionDatum;
+ bool isnull;
+ oidvector *indclass;
+ int2vector *indoption;
+ IndexStmt *index = NULL;
+ Datum reloptions;
+
+ indexrelid = RelationGetRelid(parent_index);
+
+ /*
+ * Fetch the pg_index tuple by the Oid of the index
+ */
+ ht_idx = SearchSysCache(INDEXRELID,
+ ObjectIdGetDatum(indexrelid),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(ht_idx))
+ elog(ERROR, "cache lookup failed for index %u", indexrelid);
+ idxrec = (Form_pg_index) GETSTRUCT(ht_idx);
+
+ /*
+ * Go further only if this is a unique/primary index
+ */
+ if (!idxrec->indisprimary && !idxrec->indisunique)
+ {
+ ReleaseSysCache(ht_idx);
+ return NULL;
+ }
+
+ Assert(indexrelid == idxrec->indexrelid);
+ indrelid = idxrec->indrelid;
+
+ index = makeNode(IndexStmt);
+
+ index->unique = true;
+ index->concurrent = false;
+ index->primary = (idxrec->indisprimary);
+ index->relation = cxt->relation;
+ index->isconstraint = false;
+ index->idxname = NULL; /* DefineIndex will choose a suitable name */
+
+ /* 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 the pg_class tuple of the index relation
+ */
+ ht_idxrel = SearchSysCache(RELOID,
+ ObjectIdGetDatum(indexrelid),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(ht_idxrel))
+ elog(ERROR, "cache lookup failed for relation %u", indexrelid);
+
+ /*
+ * store the reloptions for later use by this new index
+ */
+ reloptions = SysCacheGetAttr(RELOID, ht_idxrel,
+ Anum_pg_class_reloptions, &isnull);
+ if (!isnull)
+ index->inhreloptions = flatten_reloptions(indexrelid);
+
+ idxrelrec = (Form_pg_class) GETSTRUCT(ht_idxrel);
+
+ /*
+ * Fetch the pg_am tuple of the index' 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 = 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);
+ }
+ else
+ indexprs = NIL;
+
+ 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 (attnum != 0)
+ {
+ /* Simple index column */
+ char *attname;
+
+ attname = get_relid_attribute_name(indrelid, attnum);
+ keycoltype = get_atttype(indrelid, attnum);
+
+ iparam->name = pstrdup(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 */
+ get_opclass(indclass->values[keyno], keycoltype, &(iparam->opclass));
+
+ iparam->ordering = SORTBY_DEFAULT;
+ iparam->nulls_ordering = SORTBY_NULLS_DEFAULT;
+
+ /* change options if relevant */
+ 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);
+ }
+
+ /* get the same tableSpace as the parent's */
+ index->tableSpace = get_tablespace_name(parent_index->rd_node.spcNode);
+
+ /*
+ * If it's a partial index, decompile and append the predicate
+ */
+ if (!heap_attisnull(ht_idx, Anum_pg_index_indpred))
+ {
+ Datum predDatum;
+ bool isnull;
+ char *predString;
+
+ /* Convert text string to node tree */
+ predDatum = SysCacheGetAttr(INDEXRELID, ht_idx,
+ Anum_pg_index_indpred, &isnull);
+ Assert(!isnull);
+ predString = DatumGetCString(DirectFunctionCall1(textout,
+ predDatum));
+ index->whereClause = (Node *) stringToNode(predString);
+ change_varattnos_of_a_node(index->whereClause, attmap);
+ pfree(predString);
+ }
+
+ /* Clean up */
+ ReleaseSysCache(ht_idx);
+ ReleaseSysCache(ht_idxrel);
+ ReleaseSysCache(ht_am);
+
+ return index;
+ }
+
+ /*
+ * get_opclass_name - fetch name of an index operator class
+ *
+ * If the opclass is the default for the given actual_datatype then opclass is
+ * NIL.
+ */
+ static void
+ get_opclass(Oid opclass, Oid actual_datatype,
+ List **oplist)
+ {
+ HeapTuple ht_opc;
+ Form_pg_opclass opcrec;
+
+ *oplist = NIL;
+ ht_opc = SearchSysCache(CLAOID,
+ ObjectIdGetDatum(opclass),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(ht_opc))
+ elog(ERROR, "cache lookup failed for opclass %u", opclass);
+ opcrec = (Form_pg_opclass) GETSTRUCT(ht_opc);
+
+ if (!OidIsValid(actual_datatype) ||
+ GetDefaultOpClass(actual_datatype, opcrec->opcmethod) != opclass)
+ {
+ /* Okay, we need the opclass name. Do we need to qualify it? */
+ if (OpclassIsVisible(opclass))
+ *oplist = list_make1(makeString(NameStr(opcrec->opcname)));
+ else
+ *oplist = list_make2(makeString(get_namespace_name(opcrec->opcnamespace)),
+ makeString(NameStr(opcrec->opcname)));
+
+ }
+ ReleaseSysCache(ht_opc);
+ }
Index: src/backend/tcop/utility.c
===================================================================
RCS file: /repositories/postgreshome/cvs/pgsql/src/backend/tcop/utility.c,v
retrieving revision 1.279
diff -c -r1.279 utility.c
*** src/backend/tcop/utility.c 27 Apr 2007 22:05:49 -0000 1.279
--- src/backend/tcop/utility.c 21 May 2007 06:50:34 -0000
***************
*** 823,828 ****
--- 823,829 ----
stmt->indexParams, /* parameters */
(Expr *) stmt->whereClause,
stmt->options,
+ stmt->inhreloptions,
stmt->unique,
stmt->primary,
stmt->isconstraint,
Index: src/backend/utils/adt/ruleutils.c
===================================================================
RCS file: /repositories/postgreshome/cvs/pgsql/src/backend/utils/adt/ruleutils.c,v
retrieving revision 1.257
diff -c -r1.257 ruleutils.c
*** src/backend/utils/adt/ruleutils.c 27 Mar 2007 23:21:10 -0000 1.257
--- src/backend/utils/adt/ruleutils.c 21 May 2007 06:50:34 -0000
***************
*** 193,199 ****
static char *generate_function_name(Oid funcid, int nargs, Oid *argtypes);
static char *generate_operator_name(Oid operid, Oid arg1, Oid arg2);
static text *string_to_text(char *str);
- static char *flatten_reloptions(Oid relid);
#define only_marker(rte) ((rte)->inh ? "" : "ONLY ")
--- 193,198 ----
***************
*** 5374,5380 ****
/*
* Generate a C string representing a relation's reloptions, or NULL if none.
*/
! static char *
flatten_reloptions(Oid relid)
{
char *result = NULL;
--- 5373,5379 ----
/*
* Generate a C string representing a relation's reloptions, or NULL if none.
*/
! char *
flatten_reloptions(Oid relid)
{
char *result = NULL;
***************
*** 5410,5412 ****
--- 5409,5439 ----
return result;
}
+
+ /*
+ * Generate an Array Datum representing a relation's reloptions using a char
+ * string
+ */
+ Datum
+ deflatten_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;
+ }
Index: src/include/commands/defrem.h
===================================================================
RCS file: /repositories/postgreshome/cvs/pgsql/src/include/commands/defrem.h,v
retrieving revision 1.81
diff -c -r1.81 defrem.h
*** src/include/commands/defrem.h 13 Mar 2007 00:33:43 -0000 1.81
--- src/include/commands/defrem.h 21 May 2007 06:50:34 -0000
***************
*** 26,31 ****
--- 26,32 ----
List *attributeList,
Expr *predicate,
List *options,
+ char *inhreloptions,
bool unique,
bool primary,
bool isconstraint,
Index: src/include/nodes/parsenodes.h
===================================================================
RCS file: /repositories/postgreshome/cvs/pgsql/src/include/nodes/parsenodes.h,v
retrieving revision 1.348
diff -c -r1.348 parsenodes.h
*** src/include/nodes/parsenodes.h 27 Apr 2007 22:05:49 -0000 1.348
--- src/include/nodes/parsenodes.h 21 May 2007 06:50:34 -0000
***************
*** 1499,1504 ****
--- 1499,1505 ----
char *tableSpace; /* tablespace, or NULL to use parent's */
List *indexParams; /* a list of IndexElem */
List *options; /* options from WITH clause */
+ char *inhreloptions; /* relopts inherited from parent index */
Node *whereClause; /* qualification (partial-index predicate) */
bool unique; /* is index unique? */
bool primary; /* is index on primary key? */
Index: src/include/utils/builtins.h
===================================================================
RCS file: /repositories/postgreshome/cvs/pgsql/src/include/utils/builtins.h,v
retrieving revision 1.293
diff -c -r1.293 builtins.h
*** src/include/utils/builtins.h 17 May 2007 23:31:49 -0000 1.293
--- src/include/utils/builtins.h 21 May 2007 06:50:34 -0000
***************
*** 569,574 ****
--- 569,576 ----
extern const char *quote_identifier(const char *ident);
extern char *quote_qualified_identifier(const char *namespace,
const char *ident);
+ extern char *flatten_reloptions(Oid relid);
+ extern Datum deflatten_reloptions(char *reloptstring);
/* tid.c */
extern Datum tidin(PG_FUNCTION_ARGS);
Index: src/test/regress/expected/inherit.out
===================================================================
RCS file: /repositories/postgreshome/cvs/pgsql/src/test/regress/expected/inherit.out,v
retrieving revision 1.20
diff -c -r1.20 inherit.out
*** src/test/regress/expected/inherit.out 27 Jun 2006 03:43:20 -0000 1.20
--- src/test/regress/expected/inherit.out 21 May 2007 06:50:34 -0000
***************
*** 622,638 ****
DROP TABLE inhg;
CREATE TABLE inhg (x text, LIKE inhx INCLUDING CONSTRAINTS, y text); /* Copies constraints */
INSERT INTO inhg VALUES ('x', 'text', 'y'); /* Succeeds */
! INSERT INTO inhg VALUES ('x', 'text', 'y'); /* Succeeds -- Unique constraints not copied */
INSERT INTO inhg VALUES ('x', 'foo', 'y'); /* fails due to constraint */
ERROR: new row for relation "inhg" violates check constraint "foo"
SELECT * FROM inhg; /* Two records with three columns in order x=x, xx=text, y=y */
x | xx | y
---+------+---
x | text | y
! x | text | y
! (2 rows)
DROP TABLE inhg;
-- Test changing the type of inherited columns
insert into d values('test','one','two','three');
alter table a alter column aa type integer using bit_length(aa);
--- 622,650 ----
DROP TABLE inhg;
CREATE TABLE inhg (x text, LIKE inhx INCLUDING CONSTRAINTS, y text); /* Copies constraints */
INSERT INTO inhg VALUES ('x', 'text', 'y'); /* Succeeds */
! INSERT INTO inhg VALUES ('x', 'text', 'y'); /* Fails -- Unique constraints copied */
! ERROR: duplicate key violates unique constraint "inhg_pkey"
INSERT INTO inhg VALUES ('x', 'foo', 'y'); /* fails due to constraint */
ERROR: new row for relation "inhg" violates check constraint "foo"
SELECT * FROM inhg; /* Two records with three columns in order x=x, xx=text, y=y */
x | xx | y
---+------+---
x | text | y
! (1 row)
DROP TABLE inhg;
+ /* Multiple primary keys creation should fail */
+ CREATE TABLE inhg (x text, LIKE inhx INCLUDING CONSTRAINTS, 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"
+ /* Ok to create multiple unique indexes */
+ CREATE TABLE inhg (x text UNIQUE, LIKE inhz INCLUDING CONSTRAINTS);
+ NOTICE: CREATE TABLE / UNIQUE will create implicit index "inhg_x_key" for table "inhg"
+ DROP TABLE inhg;
+ DROP TABLE inhz;
+ CREATE TABLE inhg (x text, LIKE inhx INCLUDING INDEXES, y text); /* Unimplemented */
+ ERROR: LIKE INCLUDING INDEXES is not implemented
-- Test changing the type of inherited columns
insert into d values('test','one','two','three');
alter table a alter column aa type integer using bit_length(aa);
Index: src/test/regress/sql/inherit.sql
===================================================================
RCS file: /repositories/postgreshome/cvs/pgsql/src/test/regress/sql/inherit.sql,v
retrieving revision 1.10
diff -c -r1.10 inherit.sql
*** src/test/regress/sql/inherit.sql 27 Jun 2006 03:43:20 -0000 1.10
--- src/test/regress/sql/inherit.sql 21 May 2007 06:50:34 -0000
***************
*** 151,161 ****
DROP TABLE inhg;
CREATE TABLE inhg (x text, LIKE inhx INCLUDING CONSTRAINTS, y text); /* Copies constraints */
INSERT INTO inhg VALUES ('x', 'text', 'y'); /* Succeeds */
! INSERT INTO inhg VALUES ('x', 'text', 'y'); /* Succeeds -- Unique constraints not copied */
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 */
DROP TABLE inhg;
-- Test changing the type of inherited columns
insert into d values('test','one','two','three');
--- 151,169 ----
DROP TABLE inhg;
CREATE TABLE inhg (x text, LIKE inhx INCLUDING CONSTRAINTS, y text); /* Copies constraints */
INSERT INTO inhg VALUES ('x', 'text', 'y'); /* Succeeds */
! INSERT INTO inhg VALUES ('x', 'text', 'y'); /* Fails -- Unique constraints copied */
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 */
DROP TABLE inhg;
+ /* Multiple primary keys creation should fail */
+ CREATE TABLE inhg (x text, LIKE inhx INCLUDING CONSTRAINTS, PRIMARY KEY(x)); /* fails */
+ CREATE TABLE inhz (xx text DEFAULT 'text', yy int UNIQUE);
+ /* Ok to create multiple unique indexes */
+ CREATE TABLE inhg (x text UNIQUE, LIKE inhz INCLUDING CONSTRAINTS);
+ DROP TABLE inhg;
+ DROP TABLE inhz;
+ CREATE TABLE inhg (x text, LIKE inhx INCLUDING INDEXES, y text); /* Unimplemented */
-- Test changing the type of inherited columns
insert into d values('test','one','two','three');