* 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.2 2007/07/17 05:02:02 neilc 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"
Constraint *constraint);
static void transformInhRelation(ParseState *pstate, CreateStmtContext *cxt,
InhRelation *inhrelation);
-static IndexStmt *generateClonedIndexStmt(CreateStmtContext *cxt,
- Relation parent_index, AttrNumber *attmap);
+static IndexStmt *generateClonedIndexStmt(CreateStmtContext *cxt,
+ Relation parent_index, AttrNumber *attmap);
static List *get_opclass(Oid opclass, Oid actual_datatype);
static void transformIndexConstraints(ParseState *pstate,
CreateStmtContext *cxt);
static IndexStmt *transformIndexConstraint(Constraint *constraint,
- CreateStmtContext *cxt);
+ CreateStmtContext *cxt);
static void transformFKConstraints(ParseState *pstate,
CreateStmtContext *cxt,
bool skipValidation,
ListCell *elements;
/*
- * We must not scribble on the passed-in CreateStmt, so copy it. (This
- * is overkill, but easy.)
+ * We must not scribble on the passed-in CreateStmt, so copy it. (This is
+ * overkill, but easy.)
*/
stmt = (CreateStmt *) copyObject(stmt);
+ /*
+ * If the target relation name isn't schema-qualified, make it so. This
+ * prevents some corner cases in which added-on rewritten commands might
+ * think they should apply to other relations that have the same name and
+ * are earlier in the search path. "istemp" is equivalent to a
+ * specification of pg_temp, so no need for anything extra in that case.
+ */
+ if (stmt->relation->schemaname == NULL && !stmt->relation->istemp)
+ {
+ Oid namespaceid = RangeVarGetCreationNamespace(stmt->relation);
+
+ stmt->relation->schemaname = get_namespace_name(namespaceid);
+ }
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
/* 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);
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("multiple default values specified for column \"%s\" of table \"%s\"",
column->colname, cxt->relation->relname)));
- /* Note: DEFAULT NULL maps to constraint->raw_expr == NULL */
column->raw_default = constraint->raw_expr;
Assert(constraint->cooked_expr == NULL);
saw_default = true;
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,
}
/*
- * Insert the copied attributes into the cxt for the new table
- * definition.
+ * Insert the copied attributes into the cxt for the new table definition.
*/
for (parent_attno = 1; parent_attno <= tupleDesc->natts;
parent_attno++)
}
/*
- * Copy CHECK constraints if requested, being careful to adjust
- * attribute numbers
+ * Copy CHECK constraints if requested, being careful to adjust attribute
+ * numbers
*/
if (including_constraints && tupleDesc->constr)
{
}
}
+ /*
+ * 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)
{
- Oid parent_index_oid = lfirst_oid(l);
- Relation parent_index;
- IndexStmt *index_stmt;
+ 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);
+ 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;
- 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);
+ Oid source_relid = RelationGetRelid(source_idx);
+ HeapTuple ht_idxrel;
+ HeapTuple ht_idx;
+ Form_pg_class idxrelrec;
+ Form_pg_index idxrec;
+ Form_pg_am amrec;
+ oidvector *indclass;
+ IndexStmt *index;
+ List *indexprs;
+ ListCell *indexpr_item;
+ Oid indrelid;
+ int keyno;
+ Oid keycoltype;
+ Datum datum;
+ bool isnull;
+
+ /*
+ * 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);
- Assert(source_relid == idxrec->indexrelid);
+ /* Fetch pg_index tuple for source index from relcache entry */
+ ht_idx = source_idx->rd_indextuple;
+ idxrec = (Form_pg_index) GETSTRUCT(ht_idx);
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;
+ 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;
}
static List *
get_opclass(Oid opclass, Oid actual_datatype)
{
- HeapTuple ht_opc;
- Form_pg_opclass opc_rec;
- List *result = NIL;
+ HeapTuple ht_opc;
+ Form_pg_opclass opc_rec;
+ List *result = NIL;
ht_opc = SearchSysCache(CLAOID,
ObjectIdGetDatum(opclass),
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)
{
- char *nsp_name = get_namespace_name(opc_rec->opcnamespace);
- char *opc_name = NameStr(opc_rec->opcname);
+ /* For simplicity, we always schema-qualify the name */
+ char *nsp_name = get_namespace_name(opc_rec->opcnamespace);
+ 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)
{
- IndexStmt *index;
- ListCell *keys;
- IndexElem *iparam;
-
- Assert(constraint->contype == CONSTR_PRIMARY ||
- constraint->contype == CONSTR_UNIQUE);
+ IndexStmt *index;
+ ListCell *keys;
+ IndexElem *iparam;
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)));
+ errmsg("multiple primary keys for table \"%s\" are not allowed",
+ cxt->relation->relname)));
cxt->pkey = index;
/*
if (constraint->name != NULL)
index->idxname = pstrdup(constraint->name);
else
- index->idxname = NULL; /* DefineIndex will choose name */
+ index->idxname = NULL; /* DefineIndex will choose name */
index->relation = cxt->relation;
index->accessMethod = DEFAULT_INDEX_TYPE;
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.)
+ * 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)
{
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.
+ * 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;
}
if (rel->rd_rel->relkind != RELKIND_RELATION)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
- errmsg("inherited relation \"%s\" is not a table",
- inh->relname)));
+ 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];
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.
+ * 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;
}
/*
* 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.
+ * 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,
else
ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_COLUMN),
- errmsg("column \"%s\" appears twice in unique constraint",
- key)));
+ errmsg("column \"%s\" appears twice in unique constraint",
+ key)));
}
}
* transformIndexStmt - parse analysis for CREATE INDEX
*
* Note: this is a no-op for an index not using either index expressions or
- * a predicate expression. There are several code paths that create indexes
+ * a predicate expression. There are several code paths that create indexes
* without bothering to call this, because they know they don't have any
* such expressions to deal with.
*/
ListCell *l;
/*
- * We must not scribble on the passed-in IndexStmt, so copy it. (This
- * is overkill, but easy.)
+ * We must not scribble on the passed-in IndexStmt, so copy it. (This is
+ * overkill, but easy.)
*/
stmt = (IndexStmt *) copyObject(stmt);
/*
- * Open the parent table with appropriate locking. We must do this
+ * Open the parent table with appropriate locking. We must do this
* because addRangeTableEntry() would acquire only AccessShareLock,
- * leaving DefineIndex() needing to do a lock upgrade with consequent
- * risk of deadlock. Make sure this stays in sync with the type of
- * lock DefineIndex() wants.
+ * leaving DefineIndex() needing to do a lock upgrade with consequent risk
+ * of deadlock. Make sure this stays in sync with the type of lock
+ * DefineIndex() wants.
*/
rel = heap_openrv(stmt->relation,
- (stmt->concurrent ? ShareUpdateExclusiveLock : ShareLock));
+ (stmt->concurrent ? ShareUpdateExclusiveLock : ShareLock));
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
/*
- * Put the parent table into the rtable so that the expressions can
- * refer to its fields without qualification.
+ * Put the parent table into the rtable so that the expressions can refer
+ * to its fields without qualification.
*/
rte = addRangeTableEntry(pstate, stmt->relation, NULL, false, true);
/* take care of the where clause */
*whereClause = transformWhereClause(pstate,
- (Node *) copyObject(stmt->whereClause),
+ (Node *) copyObject(stmt->whereClause),
"WHERE");
if (list_length(pstate->p_rtable) != 2) /* naughty, naughty... */
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
nothing_qry->commandType = CMD_NOTHING;
nothing_qry->rtable = pstate->p_rtable;
- nothing_qry->jointree = makeFromExpr(NIL, NULL); /* no join wanted */
+ nothing_qry->jointree = makeFromExpr(NIL, NULL); /* no join wanted */
*actions = list_make1(nothing_qry);
}
has_new;
/*
- * Since outer ParseState isn't parent of inner, have to pass
- * down the query text by hand.
+ * Since outer ParseState isn't parent of inner, have to pass down
+ * the query text by hand.
*/
sub_pstate->p_sourcetext = queryString;
AlterTableCmd *newcmd;
/*
- * We must not scribble on the passed-in AlterTableStmt, so copy it.
- * (This is overkill, but easy.)
+ * We must not scribble on the passed-in AlterTableStmt, so copy it. (This
+ * is overkill, but easy.)
*/
stmt = (AlterTableStmt *) copyObject(stmt);
/*
- * Acquire exclusive lock on the target relation, which will be held
- * until end of transaction. This ensures any decisions we make here
- * based on the state of the relation will still be good at execution.
- * We must get exclusive lock now because execution will; taking a lower
- * grade lock now and trying to upgrade later risks deadlock.
+ * Acquire exclusive lock on the target relation, which will be held until
+ * end of transaction. This ensures any decisions we make here based on
+ * the state of the relation will still be good at execution. We must get
+ * exclusive lock now because execution will; taking a lower grade lock
+ * now and trying to upgrade later risks deadlock.
*/
rel = relation_openrv(stmt->relation, AccessExclusiveLock);
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;
/*
* All we really need to do here is verify that the type is valid.
*/
- Type ctype = typenameType(pstate, column->typename);
+ Type ctype = typenameType(pstate, column->typename, NULL);
ReleaseSysCache(ctype);
}