* a quick copyObject() call before manipulating the query tree.
*
*
- * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
+ * 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.6 2007/11/15 21:14:37 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/parser/parse_utilcmd.c,v 2.20 2009/01/01 17:23:46 momjian Exp $
*
*-------------------------------------------------------------------------
*/
#include "access/genam.h"
#include "access/heapam.h"
+#include "access/reloptions.h"
+#include "catalog/dependency.h"
#include "catalog/heap.h"
#include "catalog/index.h"
#include "catalog/namespace.h"
#include "commands/tablespace.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
-#include "optimizer/clauses.h"
+#include "nodes/nodeFuncs.h"
#include "parser/analyze.h"
#include "parser/gramparse.h"
#include "parser/parse_clause.h"
/* Check for SERIAL pseudo-types */
is_serial = false;
- if (list_length(column->typename->names) == 1)
+ if (list_length(column->typename->names) == 1 &&
+ !column->typename->pct_type)
{
char *typname = strVal(linitial(column->typename->names));
column->typename->names = NIL;
column->typename->typeid = INT8OID;
}
+
+ /*
+ * We have to reject "serial[]" explicitly, because once we've
+ * set typeid, LookupTypeName won't notice arrayBounds. We don't
+ * need any special coding for serial(typmod) though.
+ */
+ if (is_serial && column->typename->arrayBounds != NIL)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("array of serial is not implemented")));
}
/* Do necessary work on the column type declaration */
char *sname;
char *qstring;
A_Const *snamenode;
+ TypeCast *castnode;
FuncCall *funccallnode;
CreateSeqStmt *seqstmt;
AlterSeqStmt *altseqstmt;
* TABLE.
*/
seqstmt = makeNode(CreateSeqStmt);
- seqstmt->sequence = makeRangeVar(snamespace, sname);
+ seqstmt->sequence = makeRangeVar(snamespace, sname, -1);
seqstmt->options = NIL;
cxt->blist = lappend(cxt->blist, seqstmt);
* done after this CREATE/ALTER TABLE.
*/
altseqstmt = makeNode(AlterSeqStmt);
- altseqstmt->sequence = makeRangeVar(snamespace, sname);
+ altseqstmt->sequence = makeRangeVar(snamespace, sname, -1);
attnamelist = list_make3(makeString(snamespace),
makeString(cxt->relation->relname),
makeString(column->colname));
snamenode = makeNode(A_Const);
snamenode->val.type = T_String;
snamenode->val.val.str = qstring;
- snamenode->typename = SystemTypeName("regclass");
+ snamenode->location = -1;
+ castnode = makeNode(TypeCast);
+ castnode->typename = SystemTypeName("regclass");
+ castnode->arg = (Node *) snamenode;
+ castnode->location = -1;
funccallnode = makeNode(FuncCall);
funccallnode->funcname = SystemFuncName("nextval");
- funccallnode->args = list_make1(snamenode);
+ funccallnode->args = list_make1(castnode);
funccallnode->agg_star = false;
funccallnode->agg_distinct = false;
+ funccallnode->func_variadic = false;
+ funccallnode->over = NULL;
funccallnode->location = -1;
constraint = makeNode(Constraint);
bool including_indexes = false;
ListCell *elem;
- relation = heap_openrv(inhRelation->relation, AccessShareLock);
+ relation = parserOpenTable(pstate, inhRelation->relation, AccessShareLock);
if (relation->rd_rel->relkind != RELKIND_RELATION)
ereport(ERROR,
}
}
+ /*
+ * Likewise, copy indexes if requested
+ */
if (including_indexes && relation->rd_rel->relhasindex)
{
- AttrNumber *attmap;
+ AttrNumber *attmap = varattnos_map_schema(tupleDesc, cxt->columns);
List *parent_indexes;
ListCell *l;
- attmap = varattnos_map_schema(tupleDesc, cxt->columns);
parent_indexes = RelationGetIndexList(relation);
foreach(l, parent_indexes)
parent_index = index_open(parent_index_oid, AccessShareLock);
/* Build CREATE INDEX statement to recreate the parent_index */
- index_stmt = generateClonedIndexStmt(cxt, parent_index,
- attmap);
+ index_stmt = generateClonedIndexStmt(cxt, parent_index, attmap);
- /* Add the new IndexStmt to the create context */
+ /* Save it in the inh_indexes list for the time being */
cxt->inh_indexes = lappend(cxt->inh_indexes, index_stmt);
- /* Keep our lock on the index till xact commit */
- index_close(parent_index, NoLock);
+ index_close(parent_index, AccessShareLock);
}
}
}
/*
- * Generate an IndexStmt entry using information from an already
- * existing index "source_idx".
- *
- * Note: Much of this functionality is cribbed from pg_get_indexdef.
+ * Generate an IndexStmt node using information from an already existing index
+ * "source_idx". Attribute numbers should be adjusted according to attmap.
*/
static IndexStmt *
generateClonedIndexStmt(CreateStmtContext *cxt, Relation source_idx,
AttrNumber *attmap)
{
- HeapTuple ht_idx;
+ Oid source_relid = RelationGetRelid(source_idx);
HeapTuple ht_idxrel;
- HeapTuple ht_am;
- Form_pg_index idxrec;
+ HeapTuple ht_idx;
Form_pg_class idxrelrec;
+ Form_pg_index idxrec;
Form_pg_am amrec;
- List *indexprs = NIL;
+ oidvector *indclass;
+ IndexStmt *index;
+ List *indexprs;
ListCell *indexpr_item;
Oid indrelid;
- Oid source_relid;
int keyno;
Oid keycoltype;
- Datum indclassDatum;
- Datum indoptionDatum;
+ Datum datum;
bool isnull;
- oidvector *indclass;
- int2vector *indoption;
- IndexStmt *index;
- Datum reloptions;
- source_relid = RelationGetRelid(source_idx);
+ /*
+ * Fetch pg_class tuple of source index. We can't use the copy in the
+ * relcache entry because it doesn't include optional fields.
+ */
+ ht_idxrel = SearchSysCache(RELOID,
+ ObjectIdGetDatum(source_relid),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(ht_idxrel))
+ elog(ERROR, "cache lookup failed for relation %u", source_relid);
+ idxrelrec = (Form_pg_class) GETSTRUCT(ht_idxrel);
- /* 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);
+ /* Fetch pg_index tuple for source index from relcache entry */
+ ht_idx = source_idx->rd_indextuple;
idxrec = (Form_pg_index) GETSTRUCT(ht_idx);
-
- Assert(source_relid == idxrec->indexrelid);
indrelid = idxrec->indrelid;
+ /* Fetch pg_am tuple for source index from relcache entry */
+ amrec = source_idx->rd_am;
+
+ /* Must get indclass the hard way, since it's not stored in relcache */
+ datum = SysCacheGetAttr(INDEXRELID, ht_idx,
+ Anum_pg_index_indclass, &isnull);
+ Assert(!isnull);
+ indclass = (oidvector *) DatumGetPointer(datum);
+
+ /* Begin building the IndexStmt */
index = makeNode(IndexStmt);
+ index->relation = cxt->relation;
+ index->accessMethod = pstrdup(NameStr(amrec->amname));
+ if (OidIsValid(idxrelrec->reltablespace))
+ index->tableSpace = get_tablespace_name(idxrelrec->reltablespace);
+ else
+ index->tableSpace = NULL;
index->unique = idxrec->indisunique;
- index->concurrent = false;
index->primary = idxrec->indisprimary;
- index->relation = cxt->relation;
- index->isconstraint = false;
+ index->concurrent = false;
/*
* We don't try to preserve the name of the source index; instead, just
*/
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
+ * If the index is marked PRIMARY, it's certainly from a constraint;
+ * else, if it's not marked UNIQUE, it certainly isn't; else, we have
+ * to search pg_depend to see if there's an associated unique constraint.
*/
- 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));
+ if (index->primary)
+ index->isconstraint = true;
+ else if (!index->unique)
+ index->isconstraint = false;
+ else
+ index->isconstraint = OidIsValid(get_index_constraint(source_relid));
/* Get the index expressions, if any */
- if (!heap_attisnull(ht_idx, Anum_pg_index_indexprs))
+ datum = SysCacheGetAttr(INDEXRELID, ht_idx,
+ Anum_pg_index_indexprs, &isnull);
+ if (!isnull)
{
- Datum exprsDatum;
- bool isnull;
char *exprsString;
- exprsDatum = SysCacheGetAttr(INDEXRELID, ht_idx,
- Anum_pg_index_indexprs, &isnull);
- exprsString = DatumGetCString(DirectFunctionCall1(textout,
- exprsDatum));
- Assert(!isnull);
+ exprsString = TextDatumGetCString(datum);
indexprs = (List *) stringToNode(exprsString);
}
+ else
+ indexprs = NIL;
- indexpr_item = list_head(indexprs);
+ /* Build the list of IndexElem */
+ index->indexParams = 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];
+ int16 opt = source_idx->rd_indoption[keyno];
iparam = makeNode(IndexElem);
if (indexpr_item == NULL)
elog(ERROR, "too few entries in indexprs list");
indexkey = (Node *) lfirst(indexpr_item);
+ indexpr_item = lnext(indexpr_item);
+
+ /* OK to modify indexkey since we are working on a private copy */
change_varattnos_of_a_node(indexkey, attmap);
+
iparam->name = NULL;
iparam->expr = indexkey;
- indexpr_item = lnext(indexpr_item);
keycoltype = exprType(indexkey);
}
/* Adjust options if necessary */
if (amrec->amcanorder)
{
- /* If it supports sort ordering, report DESC and NULLS opts */
+ /*
+ * If it supports sort ordering, copy DESC and NULLS opts.
+ * Don't set non-default settings unnecessarily, though,
+ * so as to improve the chance of recognizing equivalence
+ * to constraint indexes.
+ */
if (opt & INDOPTION_DESC)
+ {
iparam->ordering = SORTBY_DESC;
- if (opt & INDOPTION_NULLS_FIRST)
- iparam->nulls_ordering = SORTBY_NULLS_FIRST;
+ if ((opt & INDOPTION_NULLS_FIRST) == 0)
+ iparam->nulls_ordering = SORTBY_NULLS_LAST;
+ }
+ else
+ {
+ 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);
+ /* Copy reloptions if any */
+ datum = SysCacheGetAttr(RELOID, ht_idxrel,
+ Anum_pg_class_reloptions, &isnull);
+ if (!isnull)
+ index->options = untransformRelOptions(datum);
/* If it's a partial index, decompile and append the predicate */
- if (!heap_attisnull(ht_idx, Anum_pg_index_indpred))
+ datum = SysCacheGetAttr(INDEXRELID, ht_idx,
+ Anum_pg_index_indpred, &isnull);
+ if (!isnull)
{
- 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));
+ pred_str = TextDatumGetCString(datum);
index->whereClause = (Node *) stringToNode(pred_str);
+ /* Adjust attribute numbers */
change_varattnos_of_a_node(index->whereClause, attmap);
}
/* Clean up */
- ReleaseSysCache(ht_idx);
ReleaseSysCache(ht_idxrel);
- ReleaseSysCache(ht_am);
return index;
}
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)
+ if (GetDefaultOpClass(actual_datatype, opc_rec->opcmethod) != opclass)
{
+ /* For simplicity, we always schema-qualify the name */
char *nsp_name = get_namespace_name(opc_rec->opcnamespace);
- char *opc_name = NameStr(opc_rec->opcname);
+ char *opc_name = pstrdup(NameStr(opc_rec->opcname));
result = list_make2(makeString(nsp_name), makeString(opc_name));
}
/*
* transformIndexConstraints
- * Handle UNIQUE and PRIMARY KEY constraints, which create
- * indexes. We also merge index definitions arising from
+ * Handle UNIQUE and PRIMARY KEY constraints, which create indexes.
+ * We also merge in any index definitions arising from
* LIKE ... INCLUDING INDEXES.
*/
static void
{
Constraint *constraint = (Constraint *) lfirst(lc);
+ Assert(IsA(constraint, Constraint));
+ Assert(constraint->contype == CONSTR_PRIMARY ||
+ constraint->contype == CONSTR_UNIQUE);
+
index = transformIndexConstraint(constraint, cxt);
+
+ indexlist = lappend(indexlist, index);
+ }
+
+ /* Add in any indexes defined by LIKE ... INCLUDING INDEXES */
+ foreach(lc, cxt->inh_indexes)
+ {
+ index = (IndexStmt *) lfirst(lc);
+
+ 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;
+ }
+
indexlist = lappend(indexlist, index);
}
{
IndexStmt *priorindex = lfirst(k);
- if (equal(index->indexParams, priorindex->indexParams))
+ if (equal(index->indexParams, priorindex->indexParams) &&
+ equal(index->whereClause, priorindex->whereClause) &&
+ strcmp(index->accessMethod, priorindex->accessMethod) == 0)
{
+ priorindex->unique |= index->unique;
/*
* If the prior index is as yet unnamed, and this one is
* named, then transfer the name to the prior index. This
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)
- {
- 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);
- }
}
+/*
+ * transformIndexConstraint
+ * Transform one UNIQUE or PRIMARY KEY constraint for
+ * transformIndexConstraints.
+ */
static IndexStmt *
transformIndexConstraint(Constraint *constraint, CreateStmtContext *cxt)
{
ListCell *keys;
IndexElem *iparam;
- 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_GROUPING_ERROR),
errmsg("cannot use aggregate function in rule WHERE condition")));
+ if (pstate->p_hasWindowFuncs)
+ ereport(ERROR,
+ (errcode(ERRCODE_WINDOWING_ERROR),
+ errmsg("cannot use window function in rule WHERE condition")));
/*
* 'instead nothing' rules with a qualification need a query rangetable so
switch (cmd->subtype)
{
case AT_AddColumn:
+ case AT_AddColumnToView:
{
ColumnDef *def = (ColumnDef *) cmd->def;
- Assert(IsA(cmd->def, ColumnDef));
- transformColumnDefinition(pstate, &cxt,
- (ColumnDef *) cmd->def);
+ Assert(IsA(def, ColumnDef));
+ transformColumnDefinition(pstate, &cxt, def);
/*
* If the column has a non-null default, we can't skip
* validation of foreign keys.
*/
- if (((ColumnDef *) cmd->def)->raw_default != NULL)
+ if (def->raw_default != NULL)
skipValidation = false;
- newcmds = lappend(newcmds, cmd);
-
- /*
- * Convert an ADD COLUMN ... NOT NULL constraint to a
- * separate command
- */
- if (def->is_not_null)
- {
- /* Remove NOT NULL from AddColumn */
- def->is_not_null = false;
-
- /* Add as a separate AlterTableCmd */
- newcmd = makeNode(AlterTableCmd);
- newcmd->subtype = AT_SetNotNull;
- newcmd->name = pstrdup(def->colname);
- newcmds = lappend(newcmds, newcmd);
- }
-
/*
* All constraints are processed in other ways. Remove the
* original list
*/
def->constraints = NIL;
+ newcmds = lappend(newcmds, cmd);
break;
}
case AT_AddConstraint:
/*
* The original AddConstraint cmd node doesn't go to newcmds
*/
-
if (IsA(cmd->def, Constraint))
transformTableConstraint(pstate, &cxt,
(Constraint *) cmd->def);
/*
* transformIndexConstraints wants cxt.alist to contain only index
- * statements, so transfer anything we already have into save_alist.
+ * statements, so transfer anything we already have into save_alist
* immediately.
*/
save_alist = cxt.alist;