aboutsummaryrefslogtreecommitdiff
path: root/src/backend/commands/command.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/commands/command.c')
-rw-r--r--src/backend/commands/command.c365
1 files changed, 178 insertions, 187 deletions
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);
}