aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/backend/catalog/pg_type.c13
-rw-r--r--src/backend/commands/command.c365
-rw-r--r--src/backend/parser/analyze.c1844
-rw-r--r--src/backend/tcop/utility.c4
-rw-r--r--src/include/commands/command.h4
-rw-r--r--src/include/nodes/parsenodes.h11
-rw-r--r--src/test/regress/expected/alter_table.out28
-rw-r--r--src/test/regress/expected/foreign_key.out2
8 files changed, 1112 insertions, 1159 deletions
diff --git a/src/backend/catalog/pg_type.c b/src/backend/catalog/pg_type.c
index b31892860a2..b79bd7068fc 100644
--- a/src/backend/catalog/pg_type.c
+++ b/src/backend/catalog/pg_type.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/catalog/pg_type.c,v 1.63 2001/09/06 02:07:42 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/catalog/pg_type.c,v 1.64 2001/10/12 00:07:14 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -555,9 +555,8 @@ TypeRename(const char *oldTypeName, const char *newTypeName)
* makeArrayTypeName(typeName);
* - given a base type name, make an array of type name out of it
*
- * the CALLER is responsible for pfreeing the
+ * the caller is responsible for pfreeing the result
*/
-
char *
makeArrayTypeName(char *typeName)
{
@@ -565,10 +564,8 @@ makeArrayTypeName(char *typeName)
if (!typeName)
return NULL;
- arr = palloc(strlen(typeName) + 2);
- arr[0] = '_';
- strcpy(arr + 1, typeName);
-
+ arr = palloc(NAMEDATALEN);
+ snprintf(arr, NAMEDATALEN,
+ "_%.*s", NAMEDATALEN - 2, typeName);
return arr;
-
}
diff --git a/src/backend/commands/command.c b/src/backend/commands/command.c
index 2b3cc08e2d4..a81d097d8b0 100644
--- a/src/backend/commands/command.c
+++ b/src/backend/commands/command.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/commands/Attic/command.c,v 1.143 2001/10/05 17:28:11 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/commands/Attic/command.c,v 1.144 2001/10/12 00:07:14 tgl Exp $
*
* NOTES
* The PerformAddAttribute() code, like most of the relation
@@ -309,9 +309,10 @@ AlterTableAddColumn(const char *relationName,
int i;
int minattnum,
maxatts;
- Relation idescs[Num_pg_attr_indices];
- Relation ridescs[Num_pg_class_indices];
- bool hasindex;
+ HeapTuple typeTuple;
+ Form_pg_type tform;
+ char *typename;
+ int attndims;
/*
* permissions checking. this would normally be done in utility.c,
@@ -339,57 +340,64 @@ AlterTableAddColumn(const char *relationName,
heap_close(rel, NoLock); /* close rel but keep lock! */
/*
- * we can't add a not null attribute
- */
- if (colDef->is_not_null)
- elog(ERROR, "Can't add a NOT NULL attribute to an existing relation");
-
- if (colDef->raw_default || colDef->cooked_default)
- elog(ERROR, "Adding columns with defaults is not implemented.");
-
-
- /*
- * if the first element in the 'schema' list is a "*" then we are
- * supposed to add this attribute to all classes that inherit from
- * 'relationName' (as well as to 'relationName').
+ * Recurse to add the column to child classes, if requested.
*
* any permissions or problems with duplicate attributes will cause the
* whole transaction to abort, which is what we want -- all or
* nothing.
*/
- if (colDef != NULL)
+ if (inherits)
{
- if (inherits)
- {
- List *child,
- *children;
+ List *child,
+ *children;
- /* this routine is actually in the planner */
- children = find_all_inheritors(myrelid);
+ /* this routine is actually in the planner */
+ children = find_all_inheritors(myrelid);
- /*
- * find_all_inheritors does the recursive search of the
- * inheritance hierarchy, so all we have to do is process all
- * of the relids in the list that it returns.
- */
- foreach(child, children)
- {
- Oid childrelid = lfirsti(child);
- char *childrelname;
+ /*
+ * find_all_inheritors does the recursive search of the
+ * inheritance hierarchy, so all we have to do is process all
+ * of the relids in the list that it returns.
+ */
+ foreach(child, children)
+ {
+ Oid childrelid = lfirsti(child);
+ char *childrelname;
- if (childrelid == myrelid)
- continue;
- rel = heap_open(childrelid, AccessExclusiveLock);
- childrelname = pstrdup(RelationGetRelationName(rel));
- heap_close(rel, AccessExclusiveLock);
+ if (childrelid == myrelid)
+ continue;
+ rel = heap_open(childrelid, AccessExclusiveLock);
+ childrelname = pstrdup(RelationGetRelationName(rel));
+ heap_close(rel, AccessExclusiveLock);
- AlterTableAddColumn(childrelname, false, colDef);
+ AlterTableAddColumn(childrelname, false, colDef);
- pfree(childrelname);
- }
+ pfree(childrelname);
}
}
+ /*
+ * OK, get on with it...
+ *
+ * Implementation restrictions: because we don't touch the table rows,
+ * the new column values will initially appear to be NULLs. (This
+ * happens because the heap tuple access routines always check for
+ * attnum > # of attributes in tuple, and return NULL if so.) Therefore
+ * we can't support a DEFAULT value in SQL92-compliant fashion, and
+ * we also can't allow a NOT NULL constraint.
+ *
+ * We do allow CHECK constraints, even though these theoretically
+ * could fail for NULL rows (eg, CHECK (newcol IS NOT NULL)).
+ */
+ if (colDef->raw_default || colDef->cooked_default)
+ elog(ERROR, "Adding columns with defaults is not implemented."
+ "\n\tAdd the column, then use ALTER TABLE SET DEFAULT.");
+
+ if (colDef->is_not_null)
+ elog(ERROR, "Adding NOT NULL columns is not implemented."
+ "\n\tAdd the column, then use ALTER TABLE ADD CONSTRAINT.");
+
+
rel = heap_openr(RelationRelationName, RowExclusiveLock);
reltup = SearchSysCache(RELNAME,
@@ -400,22 +408,39 @@ AlterTableAddColumn(const char *relationName,
elog(ERROR, "ALTER TABLE: relation \"%s\" not found",
relationName);
+ if (SearchSysCacheExists(ATTNAME,
+ ObjectIdGetDatum(reltup->t_data->t_oid),
+ PointerGetDatum(colDef->colname),
+ 0, 0))
+ elog(ERROR, "ALTER TABLE: column name \"%s\" already exists in table \"%s\"",
+ colDef->colname, relationName);
+
minattnum = ((Form_pg_class) GETSTRUCT(reltup))->relnatts;
maxatts = minattnum + 1;
if (maxatts > MaxHeapAttributeNumber)
elog(ERROR, "ALTER TABLE: relations limited to %d columns",
MaxHeapAttributeNumber);
+ i = minattnum + 1;
attrdesc = heap_openr(AttributeRelationName, RowExclusiveLock);
- /*
- * Open all (if any) pg_attribute indices
- */
- hasindex = RelationGetForm(attrdesc)->relhasindex;
- if (hasindex)
- CatalogOpenIndices(Num_pg_attr_indices, Name_pg_attr_indices, idescs);
+ if (colDef->typename->arrayBounds)
+ {
+ attndims = length(colDef->typename->arrayBounds);
+ typename = makeArrayTypeName(colDef->typename->name);
+ }
+ else
+ {
+ attndims = 0;
+ typename = colDef->typename->name;
+ }
- attributeD.attrelid = reltup->t_data->t_oid;
+ typeTuple = SearchSysCache(TYPENAME,
+ PointerGetDatum(typename),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(typeTuple))
+ elog(ERROR, "ALTER TABLE: type \"%s\" does not exist", typename);
+ tform = (Form_pg_type) GETSTRUCT(typeTuple);
attributeTuple = heap_addheader(Natts_pg_attribute,
ATTRIBUTE_TUPLE_SIZE,
@@ -423,70 +448,36 @@ AlterTableAddColumn(const char *relationName,
attribute = (Form_pg_attribute) GETSTRUCT(attributeTuple);
- i = 1 + minattnum;
-
+ attribute->attrelid = reltup->t_data->t_oid;
+ namestrcpy(&(attribute->attname), colDef->colname);
+ attribute->atttypid = typeTuple->t_data->t_oid;
+ attribute->attstattarget = DEFAULT_ATTSTATTARGET;
+ attribute->attlen = tform->typlen;
+ attribute->attcacheoff = -1;
+ attribute->atttypmod = colDef->typename->typmod;
+ attribute->attnum = i;
+ attribute->attbyval = tform->typbyval;
+ attribute->attndims = attndims;
+ attribute->attisset = (bool) (tform->typtype == 'c');
+ attribute->attstorage = tform->typstorage;
+ attribute->attalign = tform->typalign;
+ attribute->attnotnull = colDef->is_not_null;
+ attribute->atthasdef = (colDef->raw_default != NULL ||
+ colDef->cooked_default != NULL);
+
+ ReleaseSysCache(typeTuple);
+
+ heap_insert(attrdesc, attributeTuple);
+
+ /* Update indexes on pg_attribute */
+ if (RelationGetForm(attrdesc)->relhasindex)
{
- HeapTuple typeTuple;
- Form_pg_type tform;
- char *typename;
- int attndims;
-
- if (SearchSysCacheExists(ATTNAME,
- ObjectIdGetDatum(reltup->t_data->t_oid),
- PointerGetDatum(colDef->colname),
- 0, 0))
- elog(ERROR, "ALTER TABLE: column name \"%s\" already exists in table \"%s\"",
- colDef->colname, relationName);
+ Relation idescs[Num_pg_attr_indices];
- /*
- * check to see if it is an array attribute.
- */
-
- typename = colDef->typename->name;
-
- if (colDef->typename->arrayBounds)
- {
- attndims = length(colDef->typename->arrayBounds);
- typename = makeArrayTypeName(colDef->typename->name);
- }
- else
- attndims = 0;
-
- typeTuple = SearchSysCache(TYPENAME,
- PointerGetDatum(typename),
- 0, 0, 0);
- if (!HeapTupleIsValid(typeTuple))
- elog(ERROR, "ALTER TABLE: type \"%s\" does not exist", typename);
- tform = (Form_pg_type) GETSTRUCT(typeTuple);
-
- namestrcpy(&(attribute->attname), colDef->colname);
- attribute->atttypid = typeTuple->t_data->t_oid;
- attribute->attlen = tform->typlen;
- attribute->attstattarget = DEFAULT_ATTSTATTARGET;
- attribute->attcacheoff = -1;
- attribute->atttypmod = colDef->typename->typmod;
- attribute->attnum = i;
- attribute->attbyval = tform->typbyval;
- attribute->attndims = attndims;
- attribute->attisset = (bool) (tform->typtype == 'c');
- attribute->attstorage = tform->typstorage;
- attribute->attalign = tform->typalign;
- attribute->attnotnull = false;
- attribute->atthasdef = (colDef->raw_default != NULL ||
- colDef->cooked_default != NULL);
-
- ReleaseSysCache(typeTuple);
-
- heap_insert(attrdesc, attributeTuple);
- if (hasindex)
- CatalogIndexInsert(idescs,
- Num_pg_attr_indices,
- attrdesc,
- attributeTuple);
- }
-
- if (hasindex)
+ CatalogOpenIndices(Num_pg_attr_indices, Name_pg_attr_indices, idescs);
+ CatalogIndexInsert(idescs, Num_pg_attr_indices, attrdesc, attributeTuple);
CatalogCloseIndices(Num_pg_attr_indices, idescs);
+ }
heap_close(attrdesc, NoLock);
@@ -499,9 +490,14 @@ AlterTableAddColumn(const char *relationName,
simple_heap_update(rel, &newreltup->t_self, newreltup);
/* keep catalog indices current */
- CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, ridescs);
- CatalogIndexInsert(ridescs, Num_pg_class_indices, rel, newreltup);
- CatalogCloseIndices(Num_pg_class_indices, ridescs);
+ if (RelationGetForm(rel)->relhasindex)
+ {
+ Relation ridescs[Num_pg_class_indices];
+
+ CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, ridescs);
+ CatalogIndexInsert(ridescs, Num_pg_class_indices, rel, newreltup);
+ CatalogCloseIndices(Num_pg_class_indices, ridescs);
+ }
heap_freetuple(newreltup);
ReleaseSysCache(reltup);
@@ -509,10 +505,27 @@ AlterTableAddColumn(const char *relationName,
heap_close(rel, NoLock);
/*
+ * Make our catalog updates visible for subsequent steps.
+ */
+ CommandCounterIncrement();
+
+ /*
+ * Add any CHECK constraints attached to the new column.
+ *
+ * To do this we must re-open the rel so that its new attr list
+ * gets loaded into the relcache.
+ */
+ if (colDef->constraints != NIL)
+ {
+ rel = heap_openr(relationName, AccessExclusiveLock);
+ AddRelationRawConstraints(rel, NIL, colDef->constraints);
+ heap_close(rel, NoLock);
+ }
+
+ /*
* Automatically create the secondary relation for TOAST if it
* formerly had no such but now has toastable attributes.
*/
- CommandCounterIncrement();
AlterTableCreateToastTable(relationName, true);
}
@@ -596,7 +609,6 @@ AlterTableAlterColumnDefault(const char *relationName,
if (newDefault)
{
/* SET DEFAULT */
- List *rawDefaults = NIL;
RawColumnDefault *rawEnt;
/* Get rid of the old one first */
@@ -605,13 +617,12 @@ AlterTableAlterColumnDefault(const char *relationName,
rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
rawEnt->attnum = attnum;
rawEnt->raw_default = newDefault;
- rawDefaults = lappend(rawDefaults, rawEnt);
/*
* This function is intended for CREATE TABLE, so it processes a
* _list_ of defaults, but we just do one.
*/
- AddRelationRawConstraints(rel, rawDefaults, NIL);
+ AddRelationRawConstraints(rel, makeList1(rawEnt), NIL);
}
else
{
@@ -1169,10 +1180,11 @@ AlterTableDropColumn(const char *relationName,
*/
void
AlterTableAddConstraint(char *relationName,
- bool inh, Node *newConstraint)
+ bool inh, List *newConstraints)
{
- if (newConstraint == NULL)
- elog(ERROR, "ALTER TABLE / ADD CONSTRAINT passed invalid constraint.");
+ Relation rel;
+ Oid myrelid;
+ List *listptr;
#ifndef NO_SECURITY
if (!pg_ownercheck(GetUserId(), relationName, RELNAME))
@@ -1184,6 +1196,41 @@ AlterTableAddConstraint(char *relationName,
elog(ERROR, "ALTER TABLE ADD CONSTRAINT: %s is not a table",
relationName);
+ rel = heap_openr(relationName, AccessExclusiveLock);
+ myrelid = RelationGetRelid(rel);
+
+ if (inh) {
+ List *child,
+ *children;
+
+ /* this routine is actually in the planner */
+ children = find_all_inheritors(myrelid);
+
+ /*
+ * find_all_inheritors does the recursive search of the
+ * inheritance hierarchy, so all we have to do is process all
+ * of the relids in the list that it returns.
+ */
+ foreach(child, children)
+ {
+ Oid childrelid = lfirsti(child);
+ char *childrelname;
+ Relation childrel;
+
+ if (childrelid == myrelid)
+ continue;
+ childrel = heap_open(childrelid, AccessExclusiveLock);
+ childrelname = pstrdup(RelationGetRelationName(childrel));
+ heap_close(childrel, AccessExclusiveLock);
+ AlterTableAddConstraint(childrelname, false, newConstraints);
+ pfree(childrelname);
+ }
+ }
+
+ foreach(listptr, newConstraints)
+ {
+ Node *newConstraint = lfirst(listptr);
+
switch (nodeTag(newConstraint))
{
case T_Constraint:
@@ -1202,33 +1249,14 @@ AlterTableAddConstraint(char *relationName,
HeapTuple tuple;
RangeTblEntry *rte;
List *qual;
- List *constlist;
- Relation rel;
Node *expr;
char *name;
- Oid myrelid;
if (constr->name)
name = constr->name;
else
name = "<unnamed>";
- constlist = makeList1(constr);
-
- rel = heap_openr(relationName, AccessExclusiveLock);
- myrelid = RelationGetRelid(rel);
-
- /* make sure it is not a view */
- if (rel->rd_rel->relkind == RELKIND_VIEW)
- elog(ERROR, "ALTER TABLE: cannot add constraint to a view");
-
- /*
- * Scan all of the rows, looking for a false
- * match
- */
- scan = heap_beginscan(rel, false, SnapshotNow, 0, NULL);
- AssertState(scan != NULL);
-
/*
* We need to make a parse state and range
* table to allow us to transformExpr and
@@ -1280,6 +1308,8 @@ AlterTableAddConstraint(char *relationName,
* Scan through the rows now, checking the
* expression at each row.
*/
+ scan = heap_beginscan(rel, false, SnapshotNow, 0, NULL);
+
while (HeapTupleIsValid(tuple = heap_getnext(scan, 0)))
{
ExecStoreTuple(tuple, slot, InvalidBuffer, false);
@@ -1291,11 +1321,11 @@ AlterTableAddConstraint(char *relationName,
ResetExprContext(econtext);
}
+ heap_endscan(scan);
+
FreeExprContext(econtext);
pfree(slot);
- heap_endscan(scan);
-
if (!successful)
elog(ERROR, "AlterTableAddConstraint: rejected due to CHECK constraint %s", name);
@@ -1306,38 +1336,8 @@ AlterTableAddConstraint(char *relationName,
* the constraint against tuples already in
* the table.
*/
- AddRelationRawConstraints(rel, NIL, constlist);
- heap_close(rel, NoLock);
-
- if (inh) {
- List *child,
- *children;
-
- /* this routine is actually in the planner */
- children = find_all_inheritors(myrelid);
-
- /*
- * find_all_inheritors does the recursive search of the
- * inheritance hierarchy, so all we have to do is process all
- * of the relids in the list that it returns.
- */
- foreach(child, children)
- {
- Oid childrelid = lfirsti(child);
- char *childrelname;
-
- if (childrelid == myrelid)
- continue;
- rel = heap_open(childrelid, AccessExclusiveLock);
- childrelname = pstrdup(RelationGetRelationName(rel));
- heap_close(rel, AccessExclusiveLock);
-
- AlterTableAddConstraint(childrelname, false, newConstraint);
-
- pfree(childrelname);
- }
- }
- pfree(constlist);
+ AddRelationRawConstraints(rel, NIL,
+ makeList1(constr));
break;
}
@@ -1345,7 +1345,6 @@ AlterTableAddConstraint(char *relationName,
{
char *iname = constr->name;
bool istemp = is_temp_rel_name(relationName);
- Relation rel;
List *indexoidlist;
List *indexoidscan;
Form_pg_attribute *rel_attrs;
@@ -1389,7 +1388,6 @@ AlterTableAddConstraint(char *relationName,
}
/* Need to check for unique key already on field(s) */
- rel = heap_openr(relationName, AccessExclusiveLock);
/*
* First we check for limited correctness of the
@@ -1490,9 +1488,6 @@ AlterTableAddConstraint(char *relationName,
elog(NOTICE, "Unique constraint supercedes existing index on relation \"%s\". Drop the existing index to remove redundancy.", relationName);
pfree(iname);
- /* Finally, close relation */
- heap_close(rel, NoLock);
-
break;
}
default:
@@ -1503,8 +1498,7 @@ AlterTableAddConstraint(char *relationName,
case T_FkConstraint:
{
FkConstraint *fkconstraint = (FkConstraint *) newConstraint;
- Relation rel,
- pkrel;
+ Relation pkrel;
HeapScanDesc scan;
HeapTuple tuple;
Trigger trig;
@@ -1538,16 +1532,11 @@ AlterTableAddConstraint(char *relationName,
fkconstraint->pktable_name);
/*
- * Grab an exclusive lock on the fk table, and then scan
- * through each tuple, calling the RI_FKey_Match_Ins
+ * Scan through each tuple, calling the RI_FKey_Match_Ins
* (insert trigger) as if that tuple had just been
* inserted. If any of those fail, it should elog(ERROR)
* and that's that.
*/
- rel = heap_openr(relationName, AccessExclusiveLock);
- if (rel->rd_rel->relkind != RELKIND_RELATION)
- elog(ERROR, "referencing table \"%s\" not a relation",
- relationName);
/*
* First we check for limited correctness of the
@@ -1741,8 +1730,6 @@ AlterTableAddConstraint(char *relationName,
RI_FKey_check_ins(&fcinfo);
}
heap_endscan(scan);
- heap_close(rel, NoLock); /* close rel but keep
- * lock! */
pfree(trig.tgargs);
break;
@@ -1750,6 +1737,10 @@ AlterTableAddConstraint(char *relationName,
default:
elog(ERROR, "ALTER TABLE / ADD CONSTRAINT unable to determine type of constraint passed");
}
+ }
+
+ /* Close rel, but keep lock till commit */
+ heap_close(rel, NoLock);
}
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index d4af6f68619..94e5e1602eb 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.200 2001/10/03 20:54:20 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.201 2001/10/12 00:07:14 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -36,11 +36,33 @@
#include "utils/numeric.h"
#include "utils/relcache.h"
#include "utils/syscache.h"
+#include "utils/temprel.h"
#ifdef MULTIBYTE
#include "mb/pg_wchar.h"
#endif
+
+/* State shared by transformCreateStmt and its subroutines */
+typedef struct
+{
+ const char *stmtType; /* "CREATE TABLE" or "ALTER TABLE" */
+ char *relname; /* name of relation */
+ List *inhRelnames; /* names of relations to inherit from */
+ bool istemp; /* is it to be a temp relation? */
+ Oid relOid; /* OID of table, if ALTER TABLE case */
+ List *columns; /* ColumnDef items */
+ List *ckconstraints; /* CHECK constraints */
+ List *fkconstraints; /* FOREIGN KEY constraints */
+ List *ixconstraints; /* index-creating constraints */
+ List *blist; /* "before list" of things to do before
+ * creating the table */
+ List *alist; /* "after list" of things to do after
+ * creating the table */
+ IndexStmt *pkey; /* PRIMARY KEY index, if any */
+} CreateStmtContext;
+
+
static Query *transformStmt(ParseState *pstate, Node *stmt);
static Query *transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt);
static Query *transformInsertStmt(ParseState *pstate, InsertStmt *stmt);
@@ -52,17 +74,28 @@ static Node *transformSetOperationTree(ParseState *pstate, SelectStmt *stmt);
static Query *transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt);
static Query *transformCreateStmt(ParseState *pstate, CreateStmt *stmt);
static Query *transformAlterTableStmt(ParseState *pstate, AlterTableStmt *stmt);
+static void transformColumnDefinition(ParseState *pstate,
+ CreateStmtContext *cxt,
+ ColumnDef *column);
+static void transformTableConstraint(ParseState *pstate,
+ CreateStmtContext *cxt,
+ Constraint *constraint);
+static void transformIndexConstraints(ParseState *pstate,
+ CreateStmtContext *cxt);
+static void transformFKConstraints(ParseState *pstate,
+ CreateStmtContext *cxt);
static Node *transformTypeRefs(ParseState *pstate, Node *stmt);
static void transformTypeRefsList(ParseState *pstate, List *l);
static void transformTypeRef(ParseState *pstate, TypeName *tn);
static List *getSetColTypes(ParseState *pstate, Node *node);
static void transformForUpdate(Query *qry, List *forUpdate);
-static void transformFkeyGetPrimaryKey(FkConstraint *fkconstraint, Oid *pktypoid);
static void transformConstraintAttrs(List *constraintList);
static void transformColumnType(ParseState *pstate, ColumnDef *column);
static void transformFkeyCheckAttrs(FkConstraint *fkconstraint, Oid *pktypoid);
-
+static void transformFkeyGetPrimaryKey(FkConstraint *fkconstraint, Oid *pktypoid);
+static bool relationHasPrimaryKey(char *relname);
+static Oid transformFkeyGetColType(CreateStmtContext *cxt, char *colname);
static void release_pstate_resources(ParseState *pstate);
static FromExpr *makeFromExpr(List *fromlist, Node *quals);
@@ -679,296 +712,341 @@ CreateIndexName(char *table_name, char *column_name,
static Query *
transformCreateStmt(ParseState *pstate, CreateStmt *stmt)
{
+ CreateStmtContext cxt;
Query *q;
List *elements;
- Node *element;
- List *columns;
- List *dlist;
- ColumnDef *column;
- List *constraints,
- *clist;
- Constraint *constraint;
- List *fkconstraints, /* List of FOREIGN KEY constraints to */
- *fkclist; /* add finally */
- FkConstraint *fkconstraint;
- List *keys;
- Ident *key;
- List *blist = NIL; /* "before list" of things to do before
- * creating the table */
- List *ilist = NIL; /* "index list" of things to do after
- * creating the table */
- IndexStmt *index,
- *pkey = NULL;
- IndexElem *iparam;
- bool saw_nullable;
- bool is_serial;
- q = makeNode(Query);
- q->commandType = CMD_UTILITY;
-
- fkconstraints = NIL;
- constraints = stmt->constraints;
- columns = NIL;
- dlist = NIL;
+ cxt.stmtType = "CREATE TABLE";
+ cxt.relname = stmt->relname;
+ cxt.inhRelnames = stmt->inhRelnames;
+ cxt.istemp = stmt->istemp;
+ cxt.relOid = InvalidOid;
+ cxt.columns = NIL;
+ cxt.ckconstraints = NIL;
+ cxt.fkconstraints = NIL;
+ cxt.ixconstraints = NIL;
+ cxt.blist = NIL;
+ cxt.alist = NIL;
+ cxt.pkey = NULL;
/*
- * Run through each primary element in the table creation clause
+ * Run through each primary element in the table creation clause.
+ * Separate column defs from constraints, and do preliminary analysis.
*/
foreach(elements, stmt->tableElts)
{
- element = lfirst(elements);
+ Node *element = lfirst(elements);
+
switch (nodeTag(element))
{
case T_ColumnDef:
- column = (ColumnDef *) element;
- columns = lappend(columns, column);
+ transformColumnDefinition(pstate, &cxt,
+ (ColumnDef *) element);
+ break;
- /* Check for SERIAL pseudo-types */
- is_serial = false;
- if (strcmp(column->typename->name, "serial") == 0 ||
- strcmp(column->typename->name, "serial4") == 0)
- {
- is_serial = true;
- column->typename->name = pstrdup("int4");
- }
- else if (strcmp(column->typename->name, "bigserial") == 0 ||
- strcmp(column->typename->name, "serial8") == 0)
- {
- is_serial = true;
- column->typename->name = pstrdup("int8");
- }
+ case T_Constraint:
+ transformTableConstraint(pstate, &cxt,
+ (Constraint *) element);
+ break;
- /* Do necessary work on the column type declaration */
- transformColumnType(pstate, column);
+ case T_FkConstraint:
+ /* No pre-transformation needed */
+ cxt.fkconstraints = lappend(cxt.fkconstraints, element);
+ break;
- /* Special actions for SERIAL pseudo-types */
- if (is_serial)
- {
- char *sname;
- char *qstring;
- A_Const *snamenode;
- FuncCall *funccallnode;
- CreateSeqStmt *sequence;
+ default:
+ elog(ERROR, "parser: unrecognized node (internal error)");
+ }
+ }
- /*
- * Create appropriate constraints for SERIAL. We do
- * this in full, rather than shortcutting, so that we
- * will detect any conflicting constraints the user
- * wrote (like a different DEFAULT).
- */
- sname = makeObjectName(stmt->relname, column->colname,
- "seq");
+ Assert(stmt->constraints == NIL);
- /*
- * Create an expression tree representing the function
- * call nextval('"sequencename"')
- */
- qstring = palloc(strlen(sname) + 2 + 1);
- sprintf(qstring, "\"%s\"", sname);
- snamenode = makeNode(A_Const);
- snamenode->val.type = T_String;
- snamenode->val.val.str = qstring;
- funccallnode = makeNode(FuncCall);
- funccallnode->funcname = "nextval";
- funccallnode->args = makeList1(snamenode);
- funccallnode->agg_star = false;
- funccallnode->agg_distinct = false;
-
- constraint = makeNode(Constraint);
- constraint->contype = CONSTR_DEFAULT;
- constraint->name = sname;
- constraint->raw_expr = (Node *) funccallnode;
- constraint->cooked_expr = NULL;
- constraint->keys = NIL;
- column->constraints = lappend(column->constraints,
- constraint);
-
- constraint = makeNode(Constraint);
- constraint->contype = CONSTR_UNIQUE;
- constraint->name = NULL; /* assign later */
- column->constraints = lappend(column->constraints,
- constraint);
-
- constraint = makeNode(Constraint);
- constraint->contype = CONSTR_NOTNULL;
- column->constraints = lappend(column->constraints,
- constraint);
+ /*
+ * Postprocess constraints that give rise to index definitions.
+ */
+ transformIndexConstraints(pstate, &cxt);
- /*
- * Build a CREATE SEQUENCE command to create the
- * sequence object, and add it to the list of things
- * to be done before this CREATE TABLE.
- */
- sequence = makeNode(CreateSeqStmt);
- sequence->seqname = pstrdup(sname);
- sequence->istemp = stmt->istemp;
- sequence->options = NIL;
+ /*
+ * Postprocess foreign-key constraints.
+ */
+ transformFKConstraints(pstate, &cxt);
+
+ /*
+ * Output results.
+ */
+ q = makeNode(Query);
+ q->commandType = CMD_UTILITY;
+ q->utilityStmt = (Node *) stmt;
+ stmt->tableElts = cxt.columns;
+ stmt->constraints = cxt.ckconstraints;
+ extras_before = cxt.blist;
+ extras_after = cxt.alist;
- elog(NOTICE, "CREATE TABLE will create implicit sequence '%s' for SERIAL column '%s.%s'",
- sequence->seqname, stmt->relname, column->colname);
+ return q;
+}
- blist = lappend(blist, sequence);
- }
+static void
+transformColumnDefinition(ParseState *pstate, CreateStmtContext *cxt,
+ ColumnDef *column)
+{
+ bool is_serial;
+ bool saw_nullable;
+ Constraint *constraint;
+ List *clist;
+ Ident *key;
- /* Process column constraints, if any... */
- transformConstraintAttrs(column->constraints);
+ cxt->columns = lappend(cxt->columns, column);
- saw_nullable = false;
+ /* Check for SERIAL pseudo-types */
+ is_serial = false;
+ if (strcmp(column->typename->name, "serial") == 0 ||
+ strcmp(column->typename->name, "serial4") == 0)
+ {
+ is_serial = true;
+ column->typename->name = pstrdup("int4");
+ }
+ else if (strcmp(column->typename->name, "bigserial") == 0 ||
+ strcmp(column->typename->name, "serial8") == 0)
+ {
+ is_serial = true;
+ column->typename->name = pstrdup("int8");
+ }
- foreach(clist, column->constraints)
- {
- constraint = lfirst(clist);
+ /* Do necessary work on the column type declaration */
+ transformColumnType(pstate, column);
- /*
- * If this column constraint is a FOREIGN KEY
- * constraint, then we fill in the current attributes
- * name and throw it into the list of FK constraints
- * to be processed later.
- */
- if (IsA(constraint, FkConstraint))
- {
- Ident *id = makeNode(Ident);
+ /* Special actions for SERIAL pseudo-types */
+ if (is_serial)
+ {
+ char *sname;
+ char *qstring;
+ A_Const *snamenode;
+ FuncCall *funccallnode;
+ CreateSeqStmt *sequence;
- id->name = column->colname;
- id->indirection = NIL;
- id->isRel = false;
+ /*
+ * Create appropriate constraints for SERIAL. We do
+ * this in full, rather than shortcutting, so that we
+ * will detect any conflicting constraints the user
+ * wrote (like a different DEFAULT).
+ */
+ sname = makeObjectName(cxt->relname, column->colname, "seq");
- fkconstraint = (FkConstraint *) constraint;
- fkconstraint->fk_attrs = makeList1(id);
+ /*
+ * Create an expression tree representing the function
+ * call nextval('"sequencename"')
+ */
+ qstring = palloc(strlen(sname) + 2 + 1);
+ sprintf(qstring, "\"%s\"", sname);
+ snamenode = makeNode(A_Const);
+ snamenode->val.type = T_String;
+ snamenode->val.val.str = qstring;
+ funccallnode = makeNode(FuncCall);
+ funccallnode->funcname = "nextval";
+ funccallnode->args = makeList1(snamenode);
+ funccallnode->agg_star = false;
+ funccallnode->agg_distinct = false;
+
+ constraint = makeNode(Constraint);
+ constraint->contype = CONSTR_DEFAULT;
+ constraint->name = sname;
+ constraint->raw_expr = (Node *) funccallnode;
+ constraint->cooked_expr = NULL;
+ constraint->keys = NIL;
+ column->constraints = lappend(column->constraints, constraint);
+
+ constraint = makeNode(Constraint);
+ constraint->contype = CONSTR_UNIQUE;
+ constraint->name = NULL; /* assign later */
+ column->constraints = lappend(column->constraints, constraint);
+
+ constraint = makeNode(Constraint);
+ constraint->contype = CONSTR_NOTNULL;
+ column->constraints = lappend(column->constraints, constraint);
- fkconstraints = lappend(fkconstraints, constraint);
- continue;
- }
+ /*
+ * Build a CREATE SEQUENCE command to create the
+ * sequence object, and add it to the list of things
+ * to be done before this CREATE/ALTER TABLE.
+ */
+ sequence = makeNode(CreateSeqStmt);
+ sequence->seqname = pstrdup(sname);
+ sequence->istemp = cxt->istemp;
+ sequence->options = NIL;
- switch (constraint->contype)
- {
- case CONSTR_NULL:
- if (saw_nullable && column->is_not_null)
- elog(ERROR, "CREATE TABLE/(NOT) NULL conflicting declaration"
- " for '%s.%s'", stmt->relname, column->colname);
- column->is_not_null = FALSE;
- saw_nullable = true;
- break;
+ elog(NOTICE, "%s will create implicit sequence '%s' for SERIAL column '%s.%s'",
+ cxt->stmtType, sequence->seqname, cxt->relname, column->colname);
- case CONSTR_NOTNULL:
- if (saw_nullable && !column->is_not_null)
- elog(ERROR, "CREATE TABLE/(NOT) NULL conflicting declaration"
- " for '%s.%s'", stmt->relname, column->colname);
- column->is_not_null = TRUE;
- saw_nullable = true;
- break;
+ cxt->blist = lappend(cxt->blist, sequence);
+ }
- case CONSTR_DEFAULT:
- if (column->raw_default != NULL)
- elog(ERROR, "CREATE TABLE/DEFAULT multiple values specified"
- " for '%s.%s'", stmt->relname, column->colname);
- column->raw_default = constraint->raw_expr;
- Assert(constraint->cooked_expr == NULL);
- break;
+ /* Process column constraints, if any... */
+ transformConstraintAttrs(column->constraints);
- case CONSTR_PRIMARY:
- if (constraint->name == NULL)
- constraint->name = makeObjectName(stmt->relname, NULL, "pkey");
- if (constraint->keys == NIL)
- {
- key = makeNode(Ident);
- key->name = pstrdup(column->colname);
- constraint->keys = makeList1(key);
- }
- dlist = lappend(dlist, constraint);
- break;
+ saw_nullable = false;
- case CONSTR_UNIQUE:
- if (constraint->name == NULL)
- constraint->name = makeObjectName(stmt->relname, column->colname, "key");
- if (constraint->keys == NIL)
- {
- key = makeNode(Ident);
- key->name = pstrdup(column->colname);
- constraint->keys = makeList1(key);
- }
- dlist = lappend(dlist, constraint);
- break;
+ foreach(clist, column->constraints)
+ {
+ constraint = lfirst(clist);
- case CONSTR_CHECK:
- if (constraint->name == NULL)
- constraint->name = makeObjectName(stmt->relname, column->colname, NULL);
- constraints = lappend(constraints, constraint);
- break;
+ /*
+ * If this column constraint is a FOREIGN KEY
+ * constraint, then we fill in the current attributes
+ * name and throw it into the list of FK constraints
+ * to be processed later.
+ */
+ if (IsA(constraint, FkConstraint))
+ {
+ FkConstraint *fkconstraint = (FkConstraint *) constraint;
+ Ident *id = makeNode(Ident);
- case CONSTR_ATTR_DEFERRABLE:
- case CONSTR_ATTR_NOT_DEFERRABLE:
- case CONSTR_ATTR_DEFERRED:
- case CONSTR_ATTR_IMMEDIATE:
- /* transformConstraintAttrs took care of these */
- break;
+ id->name = column->colname;
+ id->indirection = NIL;
+ id->isRel = false;
- default:
- elog(ERROR, "parser: unrecognized constraint (internal error)");
- break;
- }
- }
- break;
+ fkconstraint->fk_attrs = makeList1(id);
- case T_Constraint:
- constraint = (Constraint *) element;
- switch (constraint->contype)
- {
- case CONSTR_PRIMARY:
- if (constraint->name == NULL)
- constraint->name = makeObjectName(stmt->relname, NULL, "pkey");
- dlist = lappend(dlist, constraint);
- break;
+ cxt->fkconstraints = lappend(cxt->fkconstraints, fkconstraint);
+ continue;
+ }
- case CONSTR_UNIQUE:
- dlist = lappend(dlist, constraint);
- break;
+ Assert(IsA(constraint, Constraint));
- case CONSTR_CHECK:
- constraints = lappend(constraints, constraint);
- break;
+ switch (constraint->contype)
+ {
+ case CONSTR_NULL:
+ if (saw_nullable && column->is_not_null)
+ elog(ERROR, "%s/(NOT) NULL conflicting declaration for '%s.%s'",
+ cxt->stmtType, cxt->relname, column->colname);
+ column->is_not_null = FALSE;
+ saw_nullable = true;
+ break;
- case CONSTR_NULL:
- case CONSTR_NOTNULL:
- case CONSTR_DEFAULT:
- case CONSTR_ATTR_DEFERRABLE:
- case CONSTR_ATTR_NOT_DEFERRABLE:
- case CONSTR_ATTR_DEFERRED:
- case CONSTR_ATTR_IMMEDIATE:
- elog(ERROR, "parser: illegal context for constraint (internal error)");
- break;
+ case CONSTR_NOTNULL:
+ if (saw_nullable && !column->is_not_null)
+ elog(ERROR, "%s/(NOT) NULL conflicting declaration for '%s.%s'",
+ cxt->stmtType, cxt->relname, column->colname);
+ column->is_not_null = TRUE;
+ saw_nullable = true;
+ break;
- default:
- elog(ERROR, "parser: unrecognized constraint (internal error)");
- break;
+ case CONSTR_DEFAULT:
+ if (column->raw_default != NULL)
+ elog(ERROR, "%s/DEFAULT multiple values specified for '%s.%s'",
+ cxt->stmtType, cxt->relname, column->colname);
+ column->raw_default = constraint->raw_expr;
+ Assert(constraint->cooked_expr == NULL);
+ break;
+
+ case CONSTR_PRIMARY:
+ if (constraint->name == NULL)
+ constraint->name = makeObjectName(cxt->relname,
+ NULL,
+ "pkey");
+ if (constraint->keys == NIL)
+ {
+ key = makeNode(Ident);
+ key->name = pstrdup(column->colname);
+ constraint->keys = makeList1(key);
}
+ cxt->ixconstraints = lappend(cxt->ixconstraints, constraint);
break;
- case T_FkConstraint:
+ case CONSTR_UNIQUE:
+ if (constraint->name == NULL)
+ constraint->name = makeObjectName(cxt->relname,
+ column->colname,
+ "key");
+ if (constraint->keys == NIL)
+ {
+ key = makeNode(Ident);
+ key->name = pstrdup(column->colname);
+ constraint->keys = makeList1(key);
+ }
+ cxt->ixconstraints = lappend(cxt->ixconstraints, constraint);
+ break;
- /*
- * Table level FOREIGN KEY constraints are already
- * complete. Just remember for later.
- */
- fkconstraints = lappend(fkconstraints, element);
+ case CONSTR_CHECK:
+ if (constraint->name == NULL)
+ constraint->name = makeObjectName(cxt->relname,
+ column->colname,
+ NULL);
+ cxt->ckconstraints = lappend(cxt->ckconstraints, constraint);
+ break;
+
+ case CONSTR_ATTR_DEFERRABLE:
+ case CONSTR_ATTR_NOT_DEFERRABLE:
+ case CONSTR_ATTR_DEFERRED:
+ case CONSTR_ATTR_IMMEDIATE:
+ /* transformConstraintAttrs took care of these */
break;
default:
- elog(ERROR, "parser: unrecognized node (internal error)");
+ elog(ERROR, "parser: unrecognized constraint (internal error)");
+ break;
}
}
+}
- stmt->tableElts = columns;
- stmt->constraints = constraints;
+static void
+transformTableConstraint(ParseState *pstate, CreateStmtContext *cxt,
+ Constraint *constraint)
+{
+ switch (constraint->contype)
+ {
+ case CONSTR_PRIMARY:
+ if (constraint->name == NULL)
+ constraint->name = makeObjectName(cxt->relname,
+ NULL,
+ "pkey");
+ cxt->ixconstraints = lappend(cxt->ixconstraints, constraint);
+ break;
-/* Now run through the "deferred list" to complete the query transformation.
- * 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.
- */
- while (dlist != NIL)
+ case CONSTR_UNIQUE:
+ cxt->ixconstraints = lappend(cxt->ixconstraints, constraint);
+ break;
+
+ case CONSTR_CHECK:
+ cxt->ckconstraints = lappend(cxt->ckconstraints, constraint);
+ break;
+
+ case CONSTR_NULL:
+ case CONSTR_NOTNULL:
+ case CONSTR_DEFAULT:
+ case CONSTR_ATTR_DEFERRABLE:
+ case CONSTR_ATTR_NOT_DEFERRABLE:
+ case CONSTR_ATTR_DEFERRED:
+ case CONSTR_ATTR_IMMEDIATE:
+ elog(ERROR, "parser: illegal context for constraint (internal error)");
+ break;
+
+ default:
+ elog(ERROR, "parser: unrecognized constraint (internal error)");
+ break;
+ }
+}
+
+static void
+transformIndexConstraints(ParseState *pstate, CreateStmtContext *cxt)
+{
+ List *listptr;
+ List *keys;
+ IndexStmt *index;
+ IndexElem *iparam;
+ ColumnDef *column;
+ List *columns;
+ List *indexlist = NIL;
+
+ /*
+ * 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 = lfirst(dlist);
+ Constraint *constraint = lfirst(listptr);
+
Assert(IsA(constraint, Constraint));
Assert((constraint->contype == CONSTR_PRIMARY)
|| (constraint->contype == CONSTR_UNIQUE));
@@ -979,32 +1057,40 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt)
index->primary = (constraint->contype == CONSTR_PRIMARY);
if (index->primary)
{
- if (pkey != NULL)
- elog(ERROR, "CREATE TABLE/PRIMARY KEY multiple primary keys"
- " for table '%s' are not allowed", stmt->relname);
- pkey = index;
+ /* In ALTER TABLE case, a primary index might already exist */
+ if (cxt->pkey != NULL ||
+ (OidIsValid(cxt->relOid) &&
+ relationHasPrimaryKey(cxt->relname)))
+ elog(ERROR, "%s/PRIMARY KEY multiple primary keys"
+ " for table '%s' are not allowed",
+ cxt->stmtType, cxt->relname);
+ cxt->pkey = index;
}
if (constraint->name != NULL)
index->idxname = pstrdup(constraint->name);
else if (constraint->contype == CONSTR_PRIMARY)
- index->idxname = makeObjectName(stmt->relname, NULL, "pkey");
+ index->idxname = makeObjectName(cxt->relname, NULL, "pkey");
else
index->idxname = NULL; /* will set it later */
- index->relname = stmt->relname;
+ index->relname = cxt->relname;
index->accessMethod = "btree";
index->indexParams = NIL;
index->whereClause = NULL;
+ /*
+ * Make sure referenced keys exist. If we are making a
+ * PRIMARY KEY index, also make sure they are NOT NULL.
+ */
foreach(keys, constraint->keys)
{
+ Ident *key = (Ident *) lfirst(keys);
bool found = false;
- key = (Ident *) lfirst(keys);
Assert(IsA(key, Ident));
column = NULL;
- foreach(columns, stmt->tableElts)
+ foreach(columns, cxt->columns)
{
column = lfirst(columns);
Assert(IsA(column, ColumnDef));
@@ -1020,13 +1106,12 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt)
if (constraint->contype == CONSTR_PRIMARY)
column->is_not_null = TRUE;
}
- else
+ else if (cxt->inhRelnames)
{
/* try inherited tables */
- List *inhRelnames = stmt->inhRelnames;
List *inher;
- foreach(inher, inhRelnames)
+ foreach(inher, cxt->inhRelnames)
{
Value *inh = lfirst(inher);
Relation rel;
@@ -1072,18 +1157,41 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt)
break;
}
}
+ else if (OidIsValid(cxt->relOid))
+ {
+ /* ALTER TABLE case: does column already exist? */
+ HeapTuple atttuple;
+
+ atttuple = SearchSysCache(ATTNAME,
+ ObjectIdGetDatum(cxt->relOid),
+ PointerGetDatum(key->name),
+ 0, 0);
+ if (HeapTupleIsValid(atttuple))
+ {
+ found = true;
+ /*
+ * We require pre-existing column to be already marked
+ * NOT NULL.
+ */
+ if (constraint->contype == CONSTR_PRIMARY &&
+ !((Form_pg_attribute) GETSTRUCT(atttuple))->attnotnull)
+ elog(ERROR, "Existing attribute \"%s\" cannot be a PRIMARY KEY because it is not marked NOT NULL",
+ key->name);
+ ReleaseSysCache(atttuple);
+ }
+ }
if (!found)
- elog(ERROR, "CREATE TABLE: column \"%s\" named in key does not exist",
- key->name);
+ elog(ERROR, "%s: column \"%s\" named in key does not exist",
+ cxt->stmtType, key->name);
/* Check for PRIMARY KEY(foo, foo) */
foreach(columns, index->indexParams)
{
iparam = (IndexElem *) lfirst(columns);
if (strcmp(key->name, iparam->name) == 0)
- elog(ERROR, "CREATE TABLE: column \"%s\" appears twice in %s constraint",
- key->name,
+ elog(ERROR, "%s: column \"%s\" appears twice in %s constraint",
+ cxt->stmtType, key->name,
index->primary ? "PRIMARY KEY" : "UNIQUE");
}
@@ -1095,8 +1203,7 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt)
index->indexParams = lappend(index->indexParams, iparam);
}
- ilist = lappend(ilist, index);
- dlist = lnext(dlist);
+ indexlist = lappend(indexlist, index);
}
/*
@@ -1105,31 +1212,32 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt)
* KEY or SERIAL UNIQUE. 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.
*/
- dlist = ilist;
- ilist = NIL;
- if (pkey != NULL)
+ cxt->alist = NIL;
+ if (cxt->pkey != NULL)
{
/* Make sure we keep the PKEY index in preference to others... */
- ilist = makeList1(pkey);
+ cxt->alist = makeList1(cxt->pkey);
}
- while (dlist != NIL)
+ while (indexlist != NIL)
{
- index = lfirst(dlist);
+ index = lfirst(indexlist);
- /* if it's pkey, it's already in ilist */
- if (index != pkey)
+ /* if it's pkey, it's already in cxt->alist */
+ if (index != cxt->pkey)
{
bool keep = true;
List *priorlist;
- foreach(priorlist, ilist)
+ foreach(priorlist, cxt->alist)
{
IndexStmt *priorindex = lfirst(priorlist);
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
@@ -1145,502 +1253,411 @@ transformCreateStmt(ParseState *pstate, CreateStmt *stmt)
}
if (keep)
- ilist = lappend(ilist, index);
+ cxt->alist = lappend(cxt->alist, index);
}
- dlist = lnext(dlist);
+ indexlist = lnext(indexlist);
}
/*
* Finally, select unique names for all not-previously-named indices,
* and display notice messages.
+ *
+ * XXX in ALTER TABLE case, we fail to consider name collisions
+ * against pre-existing indexes.
*/
- dlist = ilist;
- while (dlist != NIL)
+ foreach(indexlist, cxt->alist)
{
- index = lfirst(dlist);
+ index = lfirst(indexlist);
if (index->idxname == NULL && index->indexParams != NIL)
{
iparam = lfirst(index->indexParams);
- index->idxname = CreateIndexName(stmt->relname, iparam->name,
- "key", ilist);
+ index->idxname = CreateIndexName(cxt->relname, iparam->name,
+ "key", cxt->alist);
}
if (index->idxname == NULL) /* should not happen */
- elog(ERROR, "CREATE TABLE: failed to make implicit index name");
+ elog(ERROR, "%s: failed to make implicit index name",
+ cxt->stmtType);
- elog(NOTICE, "CREATE TABLE/%s will create implicit index '%s' for table '%s'",
- (index->primary ? "PRIMARY KEY" : "UNIQUE"),
- index->idxname, stmt->relname);
-
- dlist = lnext(dlist);
+ elog(NOTICE, "%s/%s will create implicit index '%s' for table '%s'",
+ cxt->stmtType, (index->primary ? "PRIMARY KEY" : "UNIQUE"),
+ index->idxname, cxt->relname);
}
+}
- q->utilityStmt = (Node *) stmt;
- extras_before = blist;
- extras_after = ilist;
+static void
+transformFKConstraints(ParseState *pstate, CreateStmtContext *cxt)
+{
+ CreateTrigStmt *fk_trigger;
+ List *fkactions = NIL;
+ List *fkclist;
+ List *fk_attr;
+ List *pk_attr;
+ Ident *id;
+ Oid pktypoid[INDEX_MAX_KEYS];
+ Oid fktypoid[INDEX_MAX_KEYS];
+ int i;
+
+ if (cxt->fkconstraints == NIL)
+ return;
- /*
- * Now process the FOREIGN KEY constraints and add appropriate queries
- * to the extras_after statements list.
- */
- if (fkconstraints != NIL)
+ elog(NOTICE, "%s will create implicit trigger(s) for FOREIGN KEY check(s)",
+ cxt->stmtType);
+
+ foreach(fkclist, cxt->fkconstraints)
{
- CreateTrigStmt *fk_trigger;
- List *fk_attr;
- List *pk_attr;
- Ident *id;
- Oid pktypoid[INDEX_MAX_KEYS];
- Oid fktypoid[INDEX_MAX_KEYS];
- int i;
+ FkConstraint *fkconstraint = (FkConstraint *) lfirst(fkclist);
+ int attnum;
+ List *fkattrs;
- for (i=0; i<INDEX_MAX_KEYS; i++) {
- pktypoid[i]=fktypoid[i]=0;
- }
- elog(NOTICE, "CREATE TABLE will create implicit trigger(s) for FOREIGN KEY check(s)");
+ /*
+ * If the constraint has no name, set it to <unnamed>
+ */
+ if (fkconstraint->constr_name == NULL)
+ fkconstraint->constr_name = "<unnamed>";
+
+ for (attnum=0; attnum<INDEX_MAX_KEYS; attnum++)
+ pktypoid[attnum] = fktypoid[attnum] = InvalidOid;
- foreach(fkclist, fkconstraints)
+ /*
+ * Look up the referencing attributes to make sure they exist
+ * (or will exist) in this table, and remember their type OIDs.
+ */
+ attnum = 0;
+ foreach(fkattrs, fkconstraint->fk_attrs)
{
- fkconstraint = (FkConstraint *) lfirst(fkclist);
+ Ident *fkattr = lfirst(fkattrs);
- /*
- * If the constraint has no name, set it to <unnamed>
- *
- */
- if (fkconstraint->constr_name == NULL)
- fkconstraint->constr_name = "<unnamed>";
+ if (attnum >= INDEX_MAX_KEYS)
+ elog(ERROR, "Can only have %d keys in a foreign key",
+ INDEX_MAX_KEYS);
+ fktypoid[attnum++] = transformFkeyGetColType(cxt,
+ fkattr->name);
+ }
- /*
- * Check to see if the attributes mentioned by the constraint
- * actually exist on this table.
- */
- if (fkconstraint->fk_attrs != NIL)
+ /*
+ * If the attribute list for the referenced table was omitted,
+ * lookup the definition of the primary key.
+ */
+ if (fkconstraint->pk_attrs == NIL)
+ {
+ if (strcmp(fkconstraint->pktable_name, cxt->relname) != 0)
+ transformFkeyGetPrimaryKey(fkconstraint, pktypoid);
+ else if (cxt->pkey != NULL)
{
- int found = 0;
- int attnum=0;
- List *cols;
- List *fkattrs;
- Ident *fkattr = NULL;
- ColumnDef *col;
-
- foreach(fkattrs, fkconstraint->fk_attrs)
- {
- found = 0;
- fkattr = lfirst(fkattrs);
- foreach(cols, stmt->tableElts)
- {
- col = lfirst(cols);
- if (strcmp(col->colname, fkattr->name) == 0)
- {
- char *buff=TypeNameToInternalName(col->typename);
- Oid type=typenameTypeId(buff);
- if (!OidIsValid(type)) {
- elog(ERROR, "Unable to lookup type %s", col->typename->name);
- }
- fktypoid[attnum++]=type;
- found = 1;
- break;
- }
- }
- if (!found) {
- List *inher;
- List *inhRelnames = stmt->inhRelnames;
- Relation rel;
+ /* Use the to-be-created primary key */
+ List *attr;
- foreach(inher, inhRelnames)
- {
- Value *inh = lfirst(inher);
- int count;
-
- Assert(IsA(inh, String));
- rel = heap_openr(strVal(inh), AccessShareLock);
- if (rel->rd_rel->relkind != RELKIND_RELATION)
- elog(ERROR, "inherited table \"%s\" is not a relation",
- strVal(inh));
- for (count = 0; count < rel->rd_att->natts; count++)
- {
- char *name = NameStr(rel->rd_att->attrs[count]->attname);
-
- if (strcmp(fkattr->name, name) == 0)
- {
- fktypoid[attnum++]=rel->rd_att->attrs[count]->atttypid;
- found = 1;
- break;
- }
- }
- heap_close(rel, NoLock);
- if (found)
- break;
- }
- }
- if (!found)
- break;
+ attnum=0;
+ foreach(attr, cxt->pkey->indexParams)
+ {
+ IndexElem *ielem = lfirst(attr);
+ Ident *pkattr = (Ident *) makeNode(Ident);
+
+ pkattr->name = pstrdup(ielem->name);
+ pkattr->indirection = NIL;
+ pkattr->isRel = false;
+ fkconstraint->pk_attrs = lappend(fkconstraint->pk_attrs,
+ pkattr);
+ if (attnum >= INDEX_MAX_KEYS)
+ elog(ERROR, "Can only have %d keys in a foreign key",
+ INDEX_MAX_KEYS);
+ pktypoid[attnum++] = transformFkeyGetColType(cxt,
+ ielem->name);
}
- if (!found)
- elog(ERROR, "columns referenced in foreign key constraint not found.");
}
-
- /*
- * If the attribute list for the referenced table was omitted,
- * lookup for the definition of the primary key. If the
- * referenced table is this table, use the definition we found
- * above, rather than looking to the system tables.
- *
- */
- if (fkconstraint->fk_attrs != NIL && fkconstraint->pk_attrs == NIL)
+ else
{
- if (strcmp(fkconstraint->pktable_name, stmt->relname) != 0)
+ /* In ALTER TABLE case, primary key may already exist */
+ if (OidIsValid(cxt->relOid))
transformFkeyGetPrimaryKey(fkconstraint, pktypoid);
- else if (pkey != NULL)
- {
- List *pkey_attr = pkey->indexParams;
- List *attr;
- List *findattr;
- IndexElem *ielem;
- Ident *pkattr;
- int attnum=0;
- ColumnDef *col;
-
- foreach(attr, pkey_attr)
- {
- ielem = lfirst(attr);
- pkattr = (Ident *) makeNode(Ident);
- pkattr->name = pstrdup(ielem->name);
- pkattr->indirection = NIL;
- pkattr->isRel = false;
- fkconstraint->pk_attrs = lappend(fkconstraint->pk_attrs, pkattr);
- foreach (findattr, stmt->tableElts) {
- col=lfirst(findattr);
- if (strcmp(col->colname, ielem->name)==0) {
- char *buff=TypeNameToInternalName(col->typename);
- Oid type=typenameTypeId(buff);
- if (!OidIsValid(type)) {
- elog(ERROR, "Unable to lookup type %s", col->typename->name);
- }
- pktypoid[attnum++]=type; /* need to convert typename */
- break;
- }
- }
- }
- }
else
- {
elog(ERROR, "PRIMARY KEY for referenced table \"%s\" not found",
fkconstraint->pktable_name);
- }
}
+ }
+ else
+ {
+ /* Validate the specified referenced key list */
+ if (strcmp(fkconstraint->pktable_name, cxt->relname) != 0)
+ transformFkeyCheckAttrs(fkconstraint, pktypoid);
else
{
- if (strcmp(fkconstraint->pktable_name, stmt->relname) != 0)
- transformFkeyCheckAttrs(fkconstraint, pktypoid);
- else
+ /* Look for a matching new unique/primary constraint */
+ List *index;
+ bool found = false;
+
+ foreach(index, cxt->alist)
{
- /* Get a unique/pk constraint from above */
- List *index;
- int found = 0;
+ IndexStmt *ind = lfirst(index);
+ List *pkattrs;
- foreach(index, ilist)
+ if (!ind->unique)
+ continue;
+ if (length(ind->indexParams) !=
+ length(fkconstraint->pk_attrs))
+ continue;
+ attnum=0;
+ foreach(pkattrs, fkconstraint->pk_attrs)
{
- IndexStmt *ind = lfirst(index);
- IndexElem *indparm;
+ Ident *pkattr = lfirst(pkattrs);
List *indparms;
- List *pkattrs;
- List *findattr;
- ColumnDef *col;
- Ident *pkattr;
- if (ind->unique)
+ found = false;
+ foreach(indparms, ind->indexParams)
{
- int count = 0;
- int attnum=0;
-
- foreach(indparms, ind->indexParams)
- count++;
- if (count != length(fkconstraint->pk_attrs))
- found = 0;
- else
+ IndexElem *indparm = lfirst(indparms);
+
+ if (strcmp(indparm->name, pkattr->name) == 0)
{
- foreach(pkattrs, fkconstraint->pk_attrs)
- {
- found = 0;
- pkattr = lfirst(pkattrs);
- foreach(indparms, ind->indexParams)
- {
- indparm = lfirst(indparms);
- if (strcmp(indparm->name, pkattr->name) == 0)
- {
- foreach (findattr, stmt->tableElts) {
- col=lfirst(findattr);
- if (strcmp(col->colname, indparm->name)==0) {
- char *buff=TypeNameToInternalName(col->typename);
- Oid type=typenameTypeId(buff);
- if (!OidIsValid(type)) {
- elog(ERROR, "Unable to lookup type %s", col->typename->name);
- }
- pktypoid[attnum++]=type;
- found=1;
- break;
- }
- }
- if (!found) {
- List *inher;
- List *inhRelnames=stmt->inhRelnames;
- Relation rel;
- foreach (inher, inhRelnames) {
- Value *inh=lfirst(inher);
- int count;
- Assert(IsA(inh, String));
- rel=heap_openr(strVal(inh), AccessShareLock);
- if (rel->rd_rel->relkind!=RELKIND_RELATION)
- elog(ERROR, "inherited table \"%s\" is not a relation", strVal(inh));
- for (count=0; count<rel->rd_att->natts; count++) {
- char *name=NameStr(rel->rd_att->attrs[count]->attname);
- if (strcmp(pkattr->name, name)==0) {
- pktypoid[attnum++]=rel->rd_att->attrs[count]->atttypid;
- found=1;
- break;
- }
- }
- heap_close(rel, NoLock);
- if (found)
- break;
- }
- }
- break;
- }
- }
- if (!found)
- break;
- }
+ found = true;
+ break;
}
}
- if (found)
+ if (!found)
break;
+ if (attnum >= INDEX_MAX_KEYS)
+ elog(ERROR, "Can only have %d keys in a foreign key",
+ INDEX_MAX_KEYS);
+ pktypoid[attnum++] = transformFkeyGetColType(cxt,
+ pkattr->name);
}
- if (!found)
+ if (found)
+ break;
+ }
+ if (!found)
+ {
+ /* In ALTER TABLE case, such an index may already exist */
+ if (OidIsValid(cxt->relOid))
+ transformFkeyCheckAttrs(fkconstraint, pktypoid);
+ else
elog(ERROR, "UNIQUE constraint matching given keys for referenced table \"%s\" not found",
fkconstraint->pktable_name);
}
}
+ }
- for (i = 0; i < INDEX_MAX_KEYS && fktypoid[i] != 0; i++) {
- /*
- * fktypoid[i] is the foreign key table's i'th element's type oid
- * pktypoid[i] is the primary key table's i'th element's type oid
- * We let oper() do our work for us, including elog(ERROR) if the
- * types don't compare with =
- */
- Operator o=oper("=", fktypoid[i], pktypoid[i], false);
- ReleaseSysCache(o);
- }
+ /* Be sure referencing and referenced column types are comparable */
+ for (i = 0; i < INDEX_MAX_KEYS && fktypoid[i] != 0; i++)
+ {
/*
- * Build a CREATE CONSTRAINT TRIGGER statement for the CHECK
- * action.
- *
+ * fktypoid[i] is the foreign key table's i'th element's type oid
+ * pktypoid[i] is the primary key table's i'th element's type oid
+ * We let oper() do our work for us, including elog(ERROR) if the
+ * types don't compare with =
*/
- fk_trigger = (CreateTrigStmt *) makeNode(CreateTrigStmt);
- fk_trigger->trigname = fkconstraint->constr_name;
- fk_trigger->relname = stmt->relname;
- fk_trigger->funcname = "RI_FKey_check_ins";
- fk_trigger->before = false;
- fk_trigger->row = true;
- fk_trigger->actions[0] = 'i';
- fk_trigger->actions[1] = 'u';
- fk_trigger->actions[2] = '\0';
- fk_trigger->lang = NULL;
- fk_trigger->text = NULL;
-
- fk_trigger->attr = NIL;
- fk_trigger->when = NULL;
- fk_trigger->isconstraint = true;
- fk_trigger->deferrable = fkconstraint->deferrable;
- fk_trigger->initdeferred = fkconstraint->initdeferred;
- fk_trigger->constrrelname = fkconstraint->pktable_name;
-
- fk_trigger->args = NIL;
- fk_trigger->args = lappend(fk_trigger->args,
- makeString(fkconstraint->constr_name));
- fk_trigger->args = lappend(fk_trigger->args,
- makeString(stmt->relname));
- fk_trigger->args = lappend(fk_trigger->args,
- makeString(fkconstraint->pktable_name));
- fk_trigger->args = lappend(fk_trigger->args,
+ Operator o=oper("=", fktypoid[i], pktypoid[i], false);
+ ReleaseSysCache(o);
+ }
+
+ /*
+ * Build a CREATE CONSTRAINT TRIGGER statement for the CHECK
+ * action.
+ */
+ fk_trigger = (CreateTrigStmt *) makeNode(CreateTrigStmt);
+ fk_trigger->trigname = fkconstraint->constr_name;
+ fk_trigger->relname = cxt->relname;
+ fk_trigger->funcname = "RI_FKey_check_ins";
+ fk_trigger->before = false;
+ fk_trigger->row = true;
+ fk_trigger->actions[0] = 'i';
+ fk_trigger->actions[1] = 'u';
+ fk_trigger->actions[2] = '\0';
+ fk_trigger->lang = NULL;
+ fk_trigger->text = NULL;
+
+ fk_trigger->attr = NIL;
+ fk_trigger->when = NULL;
+ fk_trigger->isconstraint = true;
+ fk_trigger->deferrable = fkconstraint->deferrable;
+ fk_trigger->initdeferred = fkconstraint->initdeferred;
+ fk_trigger->constrrelname = fkconstraint->pktable_name;
+
+ fk_trigger->args = NIL;
+ fk_trigger->args = lappend(fk_trigger->args,
+ makeString(fkconstraint->constr_name));
+ fk_trigger->args = lappend(fk_trigger->args,
+ makeString(cxt->relname));
+ fk_trigger->args = lappend(fk_trigger->args,
+ makeString(fkconstraint->pktable_name));
+ fk_trigger->args = lappend(fk_trigger->args,
makeString(fkconstraint->match_type));
- fk_attr = fkconstraint->fk_attrs;
- pk_attr = fkconstraint->pk_attrs;
- if (length(fk_attr) != length(pk_attr))
- {
- elog(NOTICE, "Illegal FOREIGN KEY definition REFERENCES \"%s\"",
- fkconstraint->pktable_name);
- elog(ERROR, "number of key attributes in referenced table must be equal to foreign key");
- }
- while (fk_attr != NIL)
- {
- id = (Ident *) lfirst(fk_attr);
- fk_trigger->args = lappend(fk_trigger->args,
- makeString(id->name));
+ fk_attr = fkconstraint->fk_attrs;
+ pk_attr = fkconstraint->pk_attrs;
+ if (length(fk_attr) != length(pk_attr))
+ elog(ERROR, "number of key attributes in referenced table must be equal to foreign key"
+ "\n\tIllegal FOREIGN KEY definition references \"%s\"",
+ fkconstraint->pktable_name);
+
+ while (fk_attr != NIL)
+ {
+ id = (Ident *) lfirst(fk_attr);
+ fk_trigger->args = lappend(fk_trigger->args,
+ makeString(id->name));
- id = (Ident *) lfirst(pk_attr);
- fk_trigger->args = lappend(fk_trigger->args,
- makeString(id->name));
+ id = (Ident *) lfirst(pk_attr);
+ fk_trigger->args = lappend(fk_trigger->args,
+ makeString(id->name));
- fk_attr = lnext(fk_attr);
- pk_attr = lnext(pk_attr);
- }
+ fk_attr = lnext(fk_attr);
+ pk_attr = lnext(pk_attr);
+ }
- extras_after = lappend(extras_after, (Node *) fk_trigger);
+ fkactions = lappend(fkactions, (Node *) fk_trigger);
- /*
- * Build a CREATE CONSTRAINT TRIGGER statement for the ON
- * DELETE action fired on the PK table !!!
- *
- */
- fk_trigger = (CreateTrigStmt *) makeNode(CreateTrigStmt);
- fk_trigger->trigname = fkconstraint->constr_name;
- fk_trigger->relname = fkconstraint->pktable_name;
- fk_trigger->before = false;
- fk_trigger->row = true;
- fk_trigger->actions[0] = 'd';
- fk_trigger->actions[1] = '\0';
- fk_trigger->lang = NULL;
- fk_trigger->text = NULL;
-
- fk_trigger->attr = NIL;
- fk_trigger->when = NULL;
- fk_trigger->isconstraint = true;
- fk_trigger->deferrable = fkconstraint->deferrable;
- fk_trigger->initdeferred = fkconstraint->initdeferred;
- fk_trigger->constrrelname = stmt->relname;
- switch ((fkconstraint->actions & FKCONSTR_ON_DELETE_MASK)
- >> FKCONSTR_ON_DELETE_SHIFT)
- {
- case FKCONSTR_ON_KEY_NOACTION:
- fk_trigger->funcname = "RI_FKey_noaction_del";
- break;
- case FKCONSTR_ON_KEY_RESTRICT:
- fk_trigger->deferrable = false;
- fk_trigger->initdeferred = false;
- fk_trigger->funcname = "RI_FKey_restrict_del";
- break;
- case FKCONSTR_ON_KEY_CASCADE:
- fk_trigger->funcname = "RI_FKey_cascade_del";
- break;
- case FKCONSTR_ON_KEY_SETNULL:
- fk_trigger->funcname = "RI_FKey_setnull_del";
- break;
- case FKCONSTR_ON_KEY_SETDEFAULT:
- fk_trigger->funcname = "RI_FKey_setdefault_del";
- break;
- default:
- elog(ERROR, "Only one ON DELETE action can be specified for FOREIGN KEY constraint");
- break;
- }
+ /*
+ * Build a CREATE CONSTRAINT TRIGGER statement for the ON
+ * DELETE action fired on the PK table !!!
+ */
+ fk_trigger = (CreateTrigStmt *) makeNode(CreateTrigStmt);
+ fk_trigger->trigname = fkconstraint->constr_name;
+ fk_trigger->relname = fkconstraint->pktable_name;
+ fk_trigger->before = false;
+ fk_trigger->row = true;
+ fk_trigger->actions[0] = 'd';
+ fk_trigger->actions[1] = '\0';
+ fk_trigger->lang = NULL;
+ fk_trigger->text = NULL;
+
+ fk_trigger->attr = NIL;
+ fk_trigger->when = NULL;
+ fk_trigger->isconstraint = true;
+ fk_trigger->deferrable = fkconstraint->deferrable;
+ fk_trigger->initdeferred = fkconstraint->initdeferred;
+ fk_trigger->constrrelname = cxt->relname;
+ switch ((fkconstraint->actions & FKCONSTR_ON_DELETE_MASK)
+ >> FKCONSTR_ON_DELETE_SHIFT)
+ {
+ case FKCONSTR_ON_KEY_NOACTION:
+ fk_trigger->funcname = "RI_FKey_noaction_del";
+ break;
+ case FKCONSTR_ON_KEY_RESTRICT:
+ fk_trigger->deferrable = false;
+ fk_trigger->initdeferred = false;
+ fk_trigger->funcname = "RI_FKey_restrict_del";
+ break;
+ case FKCONSTR_ON_KEY_CASCADE:
+ fk_trigger->funcname = "RI_FKey_cascade_del";
+ break;
+ case FKCONSTR_ON_KEY_SETNULL:
+ fk_trigger->funcname = "RI_FKey_setnull_del";
+ break;
+ case FKCONSTR_ON_KEY_SETDEFAULT:
+ fk_trigger->funcname = "RI_FKey_setdefault_del";
+ break;
+ default:
+ elog(ERROR, "Only one ON DELETE action can be specified for FOREIGN KEY constraint");
+ break;
+ }
- fk_trigger->args = NIL;
- fk_trigger->args = lappend(fk_trigger->args,
- makeString(fkconstraint->constr_name));
- fk_trigger->args = lappend(fk_trigger->args,
- makeString(stmt->relname));
- fk_trigger->args = lappend(fk_trigger->args,
- makeString(fkconstraint->pktable_name));
- fk_trigger->args = lappend(fk_trigger->args,
+ fk_trigger->args = NIL;
+ fk_trigger->args = lappend(fk_trigger->args,
+ makeString(fkconstraint->constr_name));
+ fk_trigger->args = lappend(fk_trigger->args,
+ makeString(cxt->relname));
+ fk_trigger->args = lappend(fk_trigger->args,
+ makeString(fkconstraint->pktable_name));
+ fk_trigger->args = lappend(fk_trigger->args,
makeString(fkconstraint->match_type));
- fk_attr = fkconstraint->fk_attrs;
- pk_attr = fkconstraint->pk_attrs;
- while (fk_attr != NIL)
- {
- id = (Ident *) lfirst(fk_attr);
- fk_trigger->args = lappend(fk_trigger->args,
- makeString(id->name));
+ fk_attr = fkconstraint->fk_attrs;
+ pk_attr = fkconstraint->pk_attrs;
+ while (fk_attr != NIL)
+ {
+ id = (Ident *) lfirst(fk_attr);
+ fk_trigger->args = lappend(fk_trigger->args,
+ makeString(id->name));
- id = (Ident *) lfirst(pk_attr);
- fk_trigger->args = lappend(fk_trigger->args,
- makeString(id->name));
+ id = (Ident *) lfirst(pk_attr);
+ fk_trigger->args = lappend(fk_trigger->args,
+ makeString(id->name));
- fk_attr = lnext(fk_attr);
- pk_attr = lnext(pk_attr);
- }
+ fk_attr = lnext(fk_attr);
+ pk_attr = lnext(pk_attr);
+ }
- extras_after = lappend(extras_after, (Node *) fk_trigger);
+ fkactions = lappend(fkactions, (Node *) fk_trigger);
- /*
- * Build a CREATE CONSTRAINT TRIGGER statement for the ON
- * UPDATE action fired on the PK table !!!
- *
- */
- fk_trigger = (CreateTrigStmt *) makeNode(CreateTrigStmt);
- fk_trigger->trigname = fkconstraint->constr_name;
- fk_trigger->relname = fkconstraint->pktable_name;
- fk_trigger->before = false;
- fk_trigger->row = true;
- fk_trigger->actions[0] = 'u';
- fk_trigger->actions[1] = '\0';
- fk_trigger->lang = NULL;
- fk_trigger->text = NULL;
-
- fk_trigger->attr = NIL;
- fk_trigger->when = NULL;
- fk_trigger->isconstraint = true;
- fk_trigger->deferrable = fkconstraint->deferrable;
- fk_trigger->initdeferred = fkconstraint->initdeferred;
- fk_trigger->constrrelname = stmt->relname;
- switch ((fkconstraint->actions & FKCONSTR_ON_UPDATE_MASK)
- >> FKCONSTR_ON_UPDATE_SHIFT)
- {
- case FKCONSTR_ON_KEY_NOACTION:
- fk_trigger->funcname = "RI_FKey_noaction_upd";
- break;
- case FKCONSTR_ON_KEY_RESTRICT:
- fk_trigger->deferrable = false;
- fk_trigger->initdeferred = false;
- fk_trigger->funcname = "RI_FKey_restrict_upd";
- break;
- case FKCONSTR_ON_KEY_CASCADE:
- fk_trigger->funcname = "RI_FKey_cascade_upd";
- break;
- case FKCONSTR_ON_KEY_SETNULL:
- fk_trigger->funcname = "RI_FKey_setnull_upd";
- break;
- case FKCONSTR_ON_KEY_SETDEFAULT:
- fk_trigger->funcname = "RI_FKey_setdefault_upd";
- break;
- default:
- elog(ERROR, "Only one ON UPDATE action can be specified for FOREIGN KEY constraint");
- break;
- }
+ /*
+ * Build a CREATE CONSTRAINT TRIGGER statement for the ON
+ * UPDATE action fired on the PK table !!!
+ */
+ fk_trigger = (CreateTrigStmt *) makeNode(CreateTrigStmt);
+ fk_trigger->trigname = fkconstraint->constr_name;
+ fk_trigger->relname = fkconstraint->pktable_name;
+ fk_trigger->before = false;
+ fk_trigger->row = true;
+ fk_trigger->actions[0] = 'u';
+ fk_trigger->actions[1] = '\0';
+ fk_trigger->lang = NULL;
+ fk_trigger->text = NULL;
+
+ fk_trigger->attr = NIL;
+ fk_trigger->when = NULL;
+ fk_trigger->isconstraint = true;
+ fk_trigger->deferrable = fkconstraint->deferrable;
+ fk_trigger->initdeferred = fkconstraint->initdeferred;
+ fk_trigger->constrrelname = cxt->relname;
+ switch ((fkconstraint->actions & FKCONSTR_ON_UPDATE_MASK)
+ >> FKCONSTR_ON_UPDATE_SHIFT)
+ {
+ case FKCONSTR_ON_KEY_NOACTION:
+ fk_trigger->funcname = "RI_FKey_noaction_upd";
+ break;
+ case FKCONSTR_ON_KEY_RESTRICT:
+ fk_trigger->deferrable = false;
+ fk_trigger->initdeferred = false;
+ fk_trigger->funcname = "RI_FKey_restrict_upd";
+ break;
+ case FKCONSTR_ON_KEY_CASCADE:
+ fk_trigger->funcname = "RI_FKey_cascade_upd";
+ break;
+ case FKCONSTR_ON_KEY_SETNULL:
+ fk_trigger->funcname = "RI_FKey_setnull_upd";
+ break;
+ case FKCONSTR_ON_KEY_SETDEFAULT:
+ fk_trigger->funcname = "RI_FKey_setdefault_upd";
+ break;
+ default:
+ elog(ERROR, "Only one ON UPDATE action can be specified for FOREIGN KEY constraint");
+ break;
+ }
- fk_trigger->args = NIL;
- fk_trigger->args = lappend(fk_trigger->args,
- makeString(fkconstraint->constr_name));
- fk_trigger->args = lappend(fk_trigger->args,
- makeString(stmt->relname));
- fk_trigger->args = lappend(fk_trigger->args,
- makeString(fkconstraint->pktable_name));
- fk_trigger->args = lappend(fk_trigger->args,
+ fk_trigger->args = NIL;
+ fk_trigger->args = lappend(fk_trigger->args,
+ makeString(fkconstraint->constr_name));
+ fk_trigger->args = lappend(fk_trigger->args,
+ makeString(cxt->relname));
+ fk_trigger->args = lappend(fk_trigger->args,
+ makeString(fkconstraint->pktable_name));
+ fk_trigger->args = lappend(fk_trigger->args,
makeString(fkconstraint->match_type));
- fk_attr = fkconstraint->fk_attrs;
- pk_attr = fkconstraint->pk_attrs;
- while (fk_attr != NIL)
- {
- id = (Ident *) lfirst(fk_attr);
- fk_trigger->args = lappend(fk_trigger->args,
- makeString(id->name));
-
- id = (Ident *) lfirst(pk_attr);
- fk_trigger->args = lappend(fk_trigger->args,
- makeString(id->name));
+ fk_attr = fkconstraint->fk_attrs;
+ pk_attr = fkconstraint->pk_attrs;
+ while (fk_attr != NIL)
+ {
+ id = (Ident *) lfirst(fk_attr);
+ fk_trigger->args = lappend(fk_trigger->args,
+ makeString(id->name));
- fk_attr = lnext(fk_attr);
- pk_attr = lnext(pk_attr);
- }
+ id = (Ident *) lfirst(pk_attr);
+ fk_trigger->args = lappend(fk_trigger->args,
+ makeString(id->name));
- extras_after = lappend(extras_after, (Node *) fk_trigger);
+ fk_attr = lnext(fk_attr);
+ pk_attr = lnext(pk_attr);
}
- }
- return q;
-} /* transformCreateStmt() */
+ fkactions = lappend(fkactions, (Node *) fk_trigger);
+ }
+ /*
+ * Attach completed list of extra actions to cxt->alist. We cannot
+ * do this earlier, because we assume above that cxt->alist still
+ * holds only IndexStmts.
+ */
+ cxt->alist = nconc(cxt->alist, fkactions);
+}
/*
* transformIndexStmt -
@@ -2481,262 +2498,89 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt)
/*
* tranformAlterTableStmt -
* transform an Alter Table Statement
- *
*/
static Query *
transformAlterTableStmt(ParseState *pstate, AlterTableStmt *stmt)
{
+ CreateStmtContext cxt;
Query *qry;
- qry = makeNode(Query);
- qry->commandType = CMD_UTILITY;
-
/*
- * The only subtypes that currently have special handling are 'A'dd
- * column and Add 'C'onstraint. In addition, right now only Foreign
- * Key 'C'onstraints have a special transformation.
- *
+ * The only subtypes that currently require parse transformation
+ * handling are 'A'dd column and Add 'C'onstraint. These largely
+ * re-use code from CREATE TABLE.
*/
switch (stmt->subtype)
{
case 'A':
- transformColumnType(pstate, (ColumnDef *) stmt->def);
+ cxt.stmtType = "ALTER TABLE";
+ cxt.relname = stmt->relname;
+ cxt.inhRelnames = NIL;
+ cxt.istemp = is_temp_rel_name(stmt->relname);
+ cxt.relOid = GetSysCacheOid(RELNAME,
+ PointerGetDatum(stmt->relname),
+ 0, 0, 0);
+ cxt.columns = NIL;
+ cxt.ckconstraints = NIL;
+ cxt.fkconstraints = NIL;
+ cxt.ixconstraints = NIL;
+ cxt.blist = NIL;
+ cxt.alist = NIL;
+ cxt.pkey = NULL;
+
+ Assert(IsA(stmt->def, ColumnDef));
+ transformColumnDefinition(pstate, &cxt,
+ (ColumnDef *) stmt->def);
+
+ transformIndexConstraints(pstate, &cxt);
+ transformFKConstraints(pstate, &cxt);
+
+ ((ColumnDef *) stmt->def)->constraints = cxt.ckconstraints;
+ extras_before = cxt.blist;
+ extras_after = cxt.alist;
break;
- case 'C':
- if (stmt->def && IsA(stmt->def, FkConstraint))
- {
- CreateTrigStmt *fk_trigger;
- List *fk_attr;
- List *pk_attr;
- Ident *id;
- FkConstraint *fkconstraint;
-
- extras_after = NIL;
- elog(NOTICE, "ALTER TABLE ... ADD CONSTRAINT will create implicit trigger(s) for FOREIGN KEY check(s)");
-
- fkconstraint = (FkConstraint *) stmt->def;
-
- /*
- * If the constraint has no name, set it to <unnamed>
- *
- */
- if (fkconstraint->constr_name == NULL)
- fkconstraint->constr_name = "<unnamed>";
-
- /*
- * If the attribute list for the referenced table was
- * omitted, lookup for the definition of the primary key
- *
- */
- if (fkconstraint->fk_attrs != NIL && fkconstraint->pk_attrs == NIL) {
- Oid pktypoid[INDEX_MAX_KEYS];
- transformFkeyGetPrimaryKey(fkconstraint, pktypoid);
- }
-
- /*
- * Build a CREATE CONSTRAINT TRIGGER statement for the
- * CHECK action.
- *
- */
- fk_trigger = (CreateTrigStmt *) makeNode(CreateTrigStmt);
- fk_trigger->trigname = fkconstraint->constr_name;
- fk_trigger->relname = stmt->relname;
- fk_trigger->funcname = "RI_FKey_check_ins";
- fk_trigger->before = false;
- fk_trigger->row = true;
- fk_trigger->actions[0] = 'i';
- fk_trigger->actions[1] = 'u';
- fk_trigger->actions[2] = '\0';
- fk_trigger->lang = NULL;
- fk_trigger->text = NULL;
-
- fk_trigger->attr = NIL;
- fk_trigger->when = NULL;
- fk_trigger->isconstraint = true;
- fk_trigger->deferrable = fkconstraint->deferrable;
- fk_trigger->initdeferred = fkconstraint->initdeferred;
- fk_trigger->constrrelname = fkconstraint->pktable_name;
-
- fk_trigger->args = NIL;
- fk_trigger->args = lappend(fk_trigger->args,
- makeString(fkconstraint->constr_name));
- fk_trigger->args = lappend(fk_trigger->args,
- makeString(stmt->relname));
- fk_trigger->args = lappend(fk_trigger->args,
- makeString(fkconstraint->pktable_name));
- fk_trigger->args = lappend(fk_trigger->args,
- makeString(fkconstraint->match_type));
- fk_attr = fkconstraint->fk_attrs;
- pk_attr = fkconstraint->pk_attrs;
- if (length(fk_attr) != length(pk_attr))
- {
- elog(NOTICE, "Illegal FOREIGN KEY definition REFERENCES \"%s\"",
- fkconstraint->pktable_name);
- elog(ERROR, "number of key attributes in referenced table must be equal to foreign key");
- }
- while (fk_attr != NIL)
- {
- id = (Ident *) lfirst(fk_attr);
- fk_trigger->args = lappend(fk_trigger->args,
- makeString(id->name));
-
- id = (Ident *) lfirst(pk_attr);
- fk_trigger->args = lappend(fk_trigger->args,
- makeString(id->name));
-
- fk_attr = lnext(fk_attr);
- pk_attr = lnext(pk_attr);
- }
-
- extras_after = lappend(extras_after, (Node *) fk_trigger);
-
- /*
- * Build a CREATE CONSTRAINT TRIGGER statement for the ON
- * DELETE action fired on the PK table !!!
- *
- */
- fk_trigger = (CreateTrigStmt *) makeNode(CreateTrigStmt);
- fk_trigger->trigname = fkconstraint->constr_name;
- fk_trigger->relname = fkconstraint->pktable_name;
- switch ((fkconstraint->actions & FKCONSTR_ON_DELETE_MASK)
- >> FKCONSTR_ON_DELETE_SHIFT)
- {
- case FKCONSTR_ON_KEY_NOACTION:
- fk_trigger->funcname = "RI_FKey_noaction_del";
- break;
- case FKCONSTR_ON_KEY_RESTRICT:
- fk_trigger->funcname = "RI_FKey_restrict_del";
- break;
- case FKCONSTR_ON_KEY_CASCADE:
- fk_trigger->funcname = "RI_FKey_cascade_del";
- break;
- case FKCONSTR_ON_KEY_SETNULL:
- fk_trigger->funcname = "RI_FKey_setnull_del";
- break;
- case FKCONSTR_ON_KEY_SETDEFAULT:
- fk_trigger->funcname = "RI_FKey_setdefault_del";
- break;
- default:
- elog(ERROR, "Only one ON DELETE action can be specified for FOREIGN KEY constraint");
- break;
- }
- fk_trigger->before = false;
- fk_trigger->row = true;
- fk_trigger->actions[0] = 'd';
- fk_trigger->actions[1] = '\0';
- fk_trigger->lang = NULL;
- fk_trigger->text = NULL;
-
- fk_trigger->attr = NIL;
- fk_trigger->when = NULL;
- fk_trigger->isconstraint = true;
- fk_trigger->deferrable = fkconstraint->deferrable;
- fk_trigger->initdeferred = fkconstraint->initdeferred;
- fk_trigger->constrrelname = stmt->relname;
-
- fk_trigger->args = NIL;
- fk_trigger->args = lappend(fk_trigger->args,
- makeString(fkconstraint->constr_name));
- fk_trigger->args = lappend(fk_trigger->args,
- makeString(stmt->relname));
- fk_trigger->args = lappend(fk_trigger->args,
- makeString(fkconstraint->pktable_name));
- fk_trigger->args = lappend(fk_trigger->args,
- makeString(fkconstraint->match_type));
- fk_attr = fkconstraint->fk_attrs;
- pk_attr = fkconstraint->pk_attrs;
- while (fk_attr != NIL)
- {
- id = (Ident *) lfirst(fk_attr);
- fk_trigger->args = lappend(fk_trigger->args,
- makeString(id->name));
-
- id = (Ident *) lfirst(pk_attr);
- fk_trigger->args = lappend(fk_trigger->args,
- makeString(id->name));
-
- fk_attr = lnext(fk_attr);
- pk_attr = lnext(pk_attr);
- }
- extras_after = lappend(extras_after, (Node *) fk_trigger);
-
- /*
- * Build a CREATE CONSTRAINT TRIGGER statement for the ON
- * UPDATE action fired on the PK table !!!
- *
- */
- fk_trigger = (CreateTrigStmt *) makeNode(CreateTrigStmt);
- fk_trigger->trigname = fkconstraint->constr_name;
- fk_trigger->relname = fkconstraint->pktable_name;
- switch ((fkconstraint->actions & FKCONSTR_ON_UPDATE_MASK)
- >> FKCONSTR_ON_UPDATE_SHIFT)
- {
- case FKCONSTR_ON_KEY_NOACTION:
- fk_trigger->funcname = "RI_FKey_noaction_upd";
- break;
- case FKCONSTR_ON_KEY_RESTRICT:
- fk_trigger->funcname = "RI_FKey_restrict_upd";
- break;
- case FKCONSTR_ON_KEY_CASCADE:
- fk_trigger->funcname = "RI_FKey_cascade_upd";
- break;
- case FKCONSTR_ON_KEY_SETNULL:
- fk_trigger->funcname = "RI_FKey_setnull_upd";
- break;
- case FKCONSTR_ON_KEY_SETDEFAULT:
- fk_trigger->funcname = "RI_FKey_setdefault_upd";
- break;
- default:
- elog(ERROR, "Only one ON UPDATE action can be specified for FOREIGN KEY constraint");
- break;
- }
- fk_trigger->before = false;
- fk_trigger->row = true;
- fk_trigger->actions[0] = 'u';
- fk_trigger->actions[1] = '\0';
- fk_trigger->lang = NULL;
- fk_trigger->text = NULL;
-
- fk_trigger->attr = NIL;
- fk_trigger->when = NULL;
- fk_trigger->isconstraint = true;
- fk_trigger->deferrable = fkconstraint->deferrable;
- fk_trigger->initdeferred = fkconstraint->initdeferred;
- fk_trigger->constrrelname = stmt->relname;
-
- fk_trigger->args = NIL;
- fk_trigger->args = lappend(fk_trigger->args,
- makeString(fkconstraint->constr_name));
- fk_trigger->args = lappend(fk_trigger->args,
- makeString(stmt->relname));
- fk_trigger->args = lappend(fk_trigger->args,
- makeString(fkconstraint->pktable_name));
- fk_trigger->args = lappend(fk_trigger->args,
- makeString(fkconstraint->match_type));
- fk_attr = fkconstraint->fk_attrs;
- pk_attr = fkconstraint->pk_attrs;
- while (fk_attr != NIL)
- {
- id = (Ident *) lfirst(fk_attr);
- fk_trigger->args = lappend(fk_trigger->args,
- makeString(id->name));
-
- id = (Ident *) lfirst(pk_attr);
- fk_trigger->args = lappend(fk_trigger->args,
- makeString(id->name));
+ case 'C':
+ cxt.stmtType = "ALTER TABLE";
+ cxt.relname = stmt->relname;
+ cxt.inhRelnames = NIL;
+ cxt.istemp = is_temp_rel_name(stmt->relname);
+ cxt.relOid = GetSysCacheOid(RELNAME,
+ PointerGetDatum(stmt->relname),
+ 0, 0, 0);
+ cxt.columns = NIL;
+ cxt.ckconstraints = NIL;
+ cxt.fkconstraints = NIL;
+ cxt.ixconstraints = NIL;
+ cxt.blist = NIL;
+ cxt.alist = NIL;
+ cxt.pkey = NULL;
+
+ if (IsA(stmt->def, Constraint))
+ transformTableConstraint(pstate, &cxt,
+ (Constraint *) stmt->def);
+ else if (IsA(stmt->def, FkConstraint))
+ cxt.fkconstraints = lappend(cxt.fkconstraints, stmt->def);
+ else
+ elog(ERROR, "Unexpected node type in ALTER TABLE ADD CONSTRAINT");
- fk_attr = lnext(fk_attr);
- pk_attr = lnext(pk_attr);
- }
+ transformIndexConstraints(pstate, &cxt);
+ transformFKConstraints(pstate, &cxt);
- extras_after = lappend(extras_after, (Node *) fk_trigger);
- }
+ Assert(cxt.columns == NIL);
+ stmt->def = (Node *) nconc(cxt.ckconstraints, cxt.fkconstraints);
+ extras_before = cxt.blist;
+ extras_after = cxt.alist;
break;
+
default:
break;
}
+
+ qry = makeNode(Query);
+ qry->commandType = CMD_UTILITY;
qry->utilityStmt = (Node *) stmt;
+
return qry;
}
@@ -2931,9 +2775,8 @@ transformForUpdate(Query *qry, List *forUpdate)
/*
* transformFkeyCheckAttrs -
*
- * Try to make sure that the attributes of a referenced table
+ * Make sure that the attributes of a referenced table
* belong to a unique (or primary key) constraint.
- *
*/
static void
transformFkeyCheckAttrs(FkConstraint *fkconstraint, Oid *pktypoid)
@@ -2949,9 +2792,6 @@ transformFkeyCheckAttrs(FkConstraint *fkconstraint, Oid *pktypoid)
* Open the referenced table and get the attributes list
*/
pkrel = heap_openr(fkconstraint->pktable_name, AccessShareLock);
- if (pkrel == NULL)
- elog(ERROR, "referenced table \"%s\" not found",
- fkconstraint->pktable_name);
pkrel_attrs = pkrel->rd_att->attrs;
/*
@@ -2971,7 +2811,7 @@ transformFkeyCheckAttrs(FkConstraint *fkconstraint, Oid *pktypoid)
ObjectIdGetDatum(indexoid),
0, 0, 0);
if (!HeapTupleIsValid(indexTuple))
- elog(ERROR, "transformFkeyGetPrimaryKey: index %u not found",
+ elog(ERROR, "transformFkeyCheckAttrs: index %u not found",
indexoid);
indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
@@ -2980,7 +2820,8 @@ transformFkeyCheckAttrs(FkConstraint *fkconstraint, Oid *pktypoid)
List *attrl;
int attnum=0;
- for (i = 0; i < INDEX_MAX_KEYS && indexStruct->indkey[i] != 0; i++);
+ for (i = 0; i < INDEX_MAX_KEYS && indexStruct->indkey[i] != 0; i++)
+ ;
if (i != length(fkconstraint->pk_attrs))
found = false;
else
@@ -3030,7 +2871,6 @@ transformFkeyCheckAttrs(FkConstraint *fkconstraint, Oid *pktypoid)
*
* Try to find the primary key attributes of a referenced table if
* the column list in the REFERENCES specification was omitted.
- *
*/
static void
transformFkeyGetPrimaryKey(FkConstraint *fkconstraint, Oid *pktypoid)
@@ -3048,9 +2888,6 @@ transformFkeyGetPrimaryKey(FkConstraint *fkconstraint, Oid *pktypoid)
* Open the referenced table and get the attributes list
*/
pkrel = heap_openr(fkconstraint->pktable_name, AccessShareLock);
- if (pkrel == NULL)
- elog(ERROR, "referenced table \"%s\" not found",
- fkconstraint->pktable_name);
pkrel_attrs = pkrel->rd_att->attrs;
/*
@@ -3095,11 +2932,10 @@ transformFkeyGetPrimaryKey(FkConstraint *fkconstraint, Oid *pktypoid)
int pkattno = indexStruct->indkey[i];
Ident *pkattr = makeNode(Ident);
- pkattr->name = DatumGetCString(DirectFunctionCall1(nameout,
- NameGetDatum(&(pkrel_attrs[pkattno - 1]->attname))));
+ pkattr->name = pstrdup(NameStr(pkrel_attrs[pkattno-1]->attname));
pkattr->indirection = NIL;
pkattr->isRel = false;
- pktypoid[attnum++]=pkrel_attrs[pkattno-1]->atttypid;
+ pktypoid[attnum++] = pkrel_attrs[pkattno-1]->atttypid;
fkconstraint->pk_attrs = lappend(fkconstraint->pk_attrs, pkattr);
}
@@ -3110,6 +2946,130 @@ transformFkeyGetPrimaryKey(FkConstraint *fkconstraint, Oid *pktypoid)
}
/*
+ * relationHasPrimaryKey -
+ *
+ * See whether an existing relation has a primary key.
+ */
+static bool
+relationHasPrimaryKey(char *relname)
+{
+ bool result = false;
+ Relation rel;
+ List *indexoidlist,
+ *indexoidscan;
+
+ rel = heap_openr(relname, AccessShareLock);
+
+ /*
+ * Get the list of index OIDs for the table from the relcache, and
+ * look up each one in the pg_index syscache until we find one marked
+ * primary key (hopefully there isn't more than one such).
+ */
+ indexoidlist = RelationGetIndexList(rel);
+
+ foreach(indexoidscan, indexoidlist)
+ {
+ Oid indexoid = lfirsti(indexoidscan);
+ HeapTuple indexTuple;
+
+ indexTuple = SearchSysCache(INDEXRELID,
+ ObjectIdGetDatum(indexoid),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(indexTuple))
+ elog(ERROR, "relationHasPrimaryKey: index %u not found",
+ indexoid);
+ result = ((Form_pg_index) GETSTRUCT(indexTuple))->indisprimary;
+ ReleaseSysCache(indexTuple);
+ if (result)
+ break;
+ }
+
+ freeList(indexoidlist);
+
+ heap_close(rel, AccessShareLock);
+
+ return result;
+}
+
+/*
+ * transformFkeyGetColType -
+ *
+ * Find a referencing column by name, and return its type OID.
+ * Error if it can't be found.
+ */
+static Oid
+transformFkeyGetColType(CreateStmtContext *cxt, char *colname)
+{
+ List *cols;
+ List *inher;
+ Oid result;
+
+ /* First look for column among the newly-created columns */
+ foreach(cols, cxt->columns)
+ {
+ ColumnDef *col = lfirst(cols);
+
+ if (strcmp(col->colname, colname) == 0)
+ {
+ char *buff = TypeNameToInternalName(col->typename);
+
+ result = typenameTypeId(buff);
+ if (!OidIsValid(result))
+ elog(ERROR, "Unable to lookup type %s",
+ col->typename->name);
+ return result;
+ }
+ }
+ /* Look for column among inherited columns (if CREATE TABLE case) */
+ foreach(inher, cxt->inhRelnames)
+ {
+ Value *inh = lfirst(inher);
+ Relation rel;
+ int count;
+
+ Assert(IsA(inh, String));
+ rel = heap_openr(strVal(inh), AccessShareLock);
+ if (rel->rd_rel->relkind != RELKIND_RELATION)
+ elog(ERROR, "inherited table \"%s\" is not a relation",
+ strVal(inh));
+ for (count = 0; count < rel->rd_att->natts; count++)
+ {
+ char *name = NameStr(rel->rd_att->attrs[count]->attname);
+
+ if (strcmp(name, colname) == 0)
+ {
+ result = rel->rd_att->attrs[count]->atttypid;
+
+ heap_close(rel, NoLock);
+ return result;
+ }
+ }
+ heap_close(rel, NoLock);
+ }
+ /* Look for column among existing columns (if ALTER TABLE case) */
+ if (OidIsValid(cxt->relOid))
+ {
+ HeapTuple atttuple;
+
+ atttuple = SearchSysCache(ATTNAME,
+ ObjectIdGetDatum(cxt->relOid),
+ PointerGetDatum(colname),
+ 0, 0);
+ if (HeapTupleIsValid(atttuple))
+ {
+ result = ((Form_pg_attribute) GETSTRUCT(atttuple))->atttypid;
+
+ ReleaseSysCache(atttuple);
+ return result;
+ }
+ }
+
+ elog(ERROR, "%s: column \"%s\" referenced in foreign key constraint does not exist",
+ cxt->stmtType, colname);
+ return InvalidOid; /* keep compiler quiet */
+}
+
+/*
* Preprocess a list of column constraint clauses
* to attach constraint attributes to their primary constraint nodes
* and detect inconsistent/misplaced constraint attributes.
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 84a12a33e04..6822fe803bd 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -10,7 +10,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/tcop/utility.c,v 1.119 2001/10/09 14:00:22 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/tcop/utility.c,v 1.120 2001/10/12 00:07:14 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -450,7 +450,7 @@ ProcessUtility(Node *parsetree,
case 'C': /* ADD CONSTRAINT */
AlterTableAddConstraint(stmt->relname,
interpretInhOption(stmt->inhOpt),
- stmt->def);
+ (List *) stmt->def);
break;
case 'X': /* DROP CONSTRAINT */
AlterTableDropConstraint(stmt->relname,
diff --git a/src/include/commands/command.h b/src/include/commands/command.h
index 7eb1a4fab84..78aa06a2dfb 100644
--- a/src/include/commands/command.h
+++ b/src/include/commands/command.h
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: command.h,v 1.27 2001/05/07 00:43:25 tgl Exp $
+ * $Id: command.h,v 1.28 2001/10/12 00:07:15 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -56,7 +56,7 @@ extern void AlterTableDropColumn(const char *relationName,
int behavior);
extern void AlterTableAddConstraint(char *relationName,
- bool inh, Node *newConstraint);
+ bool inh, List *newConstraints);
extern void AlterTableDropConstraint(const char *relationName,
bool inh, const char *constrName,
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index e190fb20bbb..44961bcca94 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: parsenodes.h,v 1.145 2001/10/02 21:39:36 tgl Exp $
+ * $Id: parsenodes.h,v 1.146 2001/10/12 00:07:15 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -187,6 +187,12 @@ typedef struct CopyStmt
/* ----------------------
* Create Table Statement
+ *
+ * NOTE: in the raw gram.y output, ColumnDef, Constraint, and FkConstraint
+ * nodes are intermixed in tableElts, and constraints is NIL. After parse
+ * analysis, tableElts contains just ColumnDefs, and constraints contains
+ * just Constraint nodes (in fact, only CONSTR_CHECK nodes, in the present
+ * implementation).
* ----------------------
*/
typedef struct CreateStmt
@@ -196,8 +202,7 @@ typedef struct CreateStmt
List *tableElts; /* column definitions (list of ColumnDef) */
List *inhRelnames; /* relations to inherit from (list of
* T_String Values) */
- List *constraints; /* constraints (list of Constraint and
- * FkConstraint nodes) */
+ List *constraints; /* constraints (list of Constraint nodes) */
bool istemp; /* is this a temp table? */
bool hasoids; /* should it have OIDs? */
} CreateStmt;
diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out
index de702ff54c5..eb9d17ae0df 100644
--- a/src/test/regress/expected/alter_table.out
+++ b/src/test/regress/expected/alter_table.out
@@ -287,25 +287,25 @@ INSERT INTO tmp3 values (1,20);
INSERT INTO tmp3 values (5,50);
-- Try (and fail) to add constraint due to invalid source columns
ALTER TABLE tmp3 add constraint tmpconstr foreign key(c) references tmp2 match full;
-NOTICE: ALTER TABLE ... ADD CONSTRAINT will create implicit trigger(s) for FOREIGN KEY check(s)
-ERROR: columns referenced in foreign key constraint not found.
+NOTICE: ALTER TABLE will create implicit trigger(s) for FOREIGN KEY check(s)
+ERROR: ALTER TABLE: column "c" referenced in foreign key constraint does not exist
-- Try (and fail) to add constraint due to invalide destination columns explicitly given
ALTER TABLE tmp3 add constraint tmpconstr foreign key(a) references tmp2(b) match full;
-NOTICE: ALTER TABLE ... ADD CONSTRAINT will create implicit trigger(s) for FOREIGN KEY check(s)
+NOTICE: ALTER TABLE will create implicit trigger(s) for FOREIGN KEY check(s)
ERROR: UNIQUE constraint matching given keys for referenced table "tmp2" not found
-- Try (and fail) to add constraint due to invalid data
ALTER TABLE tmp3 add constraint tmpconstr foreign key (a) references tmp2 match full;
-NOTICE: ALTER TABLE ... ADD CONSTRAINT will create implicit trigger(s) for FOREIGN KEY check(s)
+NOTICE: ALTER TABLE will create implicit trigger(s) for FOREIGN KEY check(s)
ERROR: tmpconstr referential integrity violation - key referenced from tmp3 not found in tmp2
-- Delete failing row
DELETE FROM tmp3 where a=5;
-- Try (and succeed)
ALTER TABLE tmp3 add constraint tmpconstr foreign key (a) references tmp2 match full;
-NOTICE: ALTER TABLE ... ADD CONSTRAINT will create implicit trigger(s) for FOREIGN KEY check(s)
+NOTICE: ALTER TABLE will create implicit trigger(s) for FOREIGN KEY check(s)
-- Try (and fail) to create constraint from tmp5(a) to tmp4(a) - unique constraint on
-- tmp4 is a,b
ALTER TABLE tmp5 add constraint tmpconstr foreign key(a) references tmp4(a) match full;
-NOTICE: ALTER TABLE ... ADD CONSTRAINT will create implicit trigger(s) for FOREIGN KEY check(s)
+NOTICE: ALTER TABLE will create implicit trigger(s) for FOREIGN KEY check(s)
ERROR: UNIQUE constraint matching given keys for referenced table "tmp4" not found
DROP TABLE tmp5;
DROP TABLE tmp4;
@@ -321,13 +321,13 @@ NOTICE: CREATE TABLE/PRIMARY KEY will create implicit index 'pktable_pkey' for
CREATE TEMP TABLE FKTABLE (ftest1 text);
-- This next should fail, because text=int does not exist
ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1) references pktable;
-NOTICE: ALTER TABLE ... ADD CONSTRAINT will create implicit trigger(s) for FOREIGN KEY check(s)
+NOTICE: ALTER TABLE will create implicit trigger(s) for FOREIGN KEY check(s)
ERROR: Unable to identify an operator '=' for types 'text' and 'integer'
You will have to retype this query using an explicit cast
-- This should also fail for the same reason, but here we
-- give the column name
ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1) references pktable(ptest1);
-NOTICE: ALTER TABLE ... ADD CONSTRAINT will create implicit trigger(s) for FOREIGN KEY check(s)
+NOTICE: ALTER TABLE will create implicit trigger(s) for FOREIGN KEY check(s)
ERROR: Unable to identify an operator '=' for types 'text' and 'integer'
You will have to retype this query using an explicit cast
-- This should succeed, even though they are different types
@@ -335,10 +335,10 @@ ERROR: Unable to identify an operator '=' for types 'text' and 'integer'
DROP TABLE FKTABLE;
CREATE TEMP TABLE FKTABLE (ftest1 varchar);
ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1) references pktable;
-NOTICE: ALTER TABLE ... ADD CONSTRAINT will create implicit trigger(s) for FOREIGN KEY check(s)
+NOTICE: ALTER TABLE will create implicit trigger(s) for FOREIGN KEY check(s)
-- As should this
ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1) references pktable(ptest1);
-NOTICE: ALTER TABLE ... ADD CONSTRAINT will create implicit trigger(s) for FOREIGN KEY check(s)
+NOTICE: ALTER TABLE will create implicit trigger(s) for FOREIGN KEY check(s)
DROP TABLE pktable;
NOTICE: DROP TABLE implicitly drops referential integrity trigger from table "fktable"
NOTICE: DROP TABLE implicitly drops referential integrity trigger from table "fktable"
@@ -349,7 +349,7 @@ NOTICE: CREATE TABLE/PRIMARY KEY will create implicit index 'pktable_pkey' for
-- This should fail, because we just chose really odd types
CREATE TEMP TABLE FKTABLE (ftest1 cidr, ftest2 datetime);
ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1, ftest2) references pktable;
-NOTICE: ALTER TABLE ... ADD CONSTRAINT will create implicit trigger(s) for FOREIGN KEY check(s)
+NOTICE: ALTER TABLE will create implicit trigger(s) for FOREIGN KEY check(s)
ERROR: Unable to identify an operator '=' for types 'cidr' and 'integer'
You will have to retype this query using an explicit cast
-- Again, so should this...
@@ -357,7 +357,7 @@ DROP TABLE FKTABLE;
CREATE TEMP TABLE FKTABLE (ftest1 cidr, ftest2 datetime);
ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1, ftest2)
references pktable(ptest1, ptest2);
-NOTICE: ALTER TABLE ... ADD CONSTRAINT will create implicit trigger(s) for FOREIGN KEY check(s)
+NOTICE: ALTER TABLE will create implicit trigger(s) for FOREIGN KEY check(s)
ERROR: Unable to identify an operator '=' for types 'cidr' and 'integer'
You will have to retype this query using an explicit cast
-- This fails because we mixed up the column ordering
@@ -365,13 +365,13 @@ DROP TABLE FKTABLE;
CREATE TEMP TABLE FKTABLE (ftest1 int, ftest2 text);
ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest1, ftest2)
references pktable(ptest2, ptest1);
-NOTICE: ALTER TABLE ... ADD CONSTRAINT will create implicit trigger(s) for FOREIGN KEY check(s)
+NOTICE: ALTER TABLE will create implicit trigger(s) for FOREIGN KEY check(s)
ERROR: Unable to identify an operator '=' for types 'integer' and 'text'
You will have to retype this query using an explicit cast
-- As does this...
ALTER TABLE FKTABLE ADD FOREIGN KEY(ftest2, ftest1)
references pktable(ptest1, ptest2);
-NOTICE: ALTER TABLE ... ADD CONSTRAINT will create implicit trigger(s) for FOREIGN KEY check(s)
+NOTICE: ALTER TABLE will create implicit trigger(s) for FOREIGN KEY check(s)
ERROR: Unable to identify an operator '=' for types 'text' and 'integer'
You will have to retype this query using an explicit cast
-- temp tables should go away by themselves, need not drop them.
diff --git a/src/test/regress/expected/foreign_key.out b/src/test/regress/expected/foreign_key.out
index 523b5b96f7a..95e66841567 100644
--- a/src/test/regress/expected/foreign_key.out
+++ b/src/test/regress/expected/foreign_key.out
@@ -694,7 +694,7 @@ CREATE TABLE PKTABLE (ptest1 int PRIMARY KEY);
NOTICE: CREATE TABLE/PRIMARY KEY will create implicit index 'pktable_pkey' for table 'pktable'
CREATE TABLE FKTABLE_FAIL1 ( ftest1 int, CONSTRAINT fkfail1 FOREIGN KEY (ftest2) REFERENCES PKTABLE);
NOTICE: CREATE TABLE will create implicit trigger(s) for FOREIGN KEY check(s)
-ERROR: columns referenced in foreign key constraint not found.
+ERROR: CREATE TABLE: column "ftest2" referenced in foreign key constraint does not exist
CREATE TABLE FKTABLE_FAIL2 ( ftest1 int, CONSTRAINT fkfail1 FOREIGN KEY (ftest1) REFERENCES PKTABLE(ptest2));
NOTICE: CREATE TABLE will create implicit trigger(s) for FOREIGN KEY check(s)
ERROR: UNIQUE constraint matching given keys for referenced table "pktable" not found