]> git.ipfire.org Git - thirdparty/postgresql.git/commitdiff
Move renametrig() from tablecmds.c to trigger.c --- if we're going to
authorTom Lane <tgl@sss.pgh.pa.us>
Fri, 26 Apr 2002 19:29:47 +0000 (19:29 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Fri, 26 Apr 2002 19:29:47 +0000 (19:29 +0000)
divide backend/commands by object type, let's try to pay at least
minimal attention to respecting that structure, eh?  Also reorder the
contents of tablecmds.c; it seems odd to me to put ALTER commands before
creation/deletion commands.

src/backend/commands/tablecmds.c
src/backend/commands/trigger.c
src/backend/commands/user.c
src/include/commands/tablecmds.h
src/include/commands/trigger.h

index e8b0d8bc013b3b527394e080a70069e220064d3c..7bdc238e77c635eed006053165daf3e166c9191f 100644 (file)
@@ -1,14 +1,14 @@
 /*-------------------------------------------------------------------------
  *
  * tablecmds.c
- *       Commands for altering table structures and settings
+ *       Commands for creating and altering table structures and settings
  *
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/commands/tablecmds.c,v 1.9 2002/04/24 02:48:54 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/commands/tablecmds.c,v 1.10 2002/04/26 19:29:47 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #include "utils/relcache.h"
 
 
-static void drop_default(Oid relid, int16 attnum);
-static bool needs_toast_table(Relation rel);
-static void CheckTupleType(Form_pg_class tuple_class);
-
+static List *MergeDomainAttributes(List *schema);
 static List *MergeAttributes(List *schema, List *supers, bool istemp,
                                List **supOids, List **supconstr, bool *supHasOids);
 static bool change_varattnos_of_a_node(Node *node, const AttrNumber *newattno);
 static void StoreCatalogInheritance(Oid relationId, List *supers);
 static int     findAttrByName(const char *attributeName, List *schema);
 static void setRelhassubclassInRelation(Oid relationId, bool relhassubclass);
-static List *MergeDomainAttributes(List *schema);
+static void drop_default(Oid relid, int16 attnum);
+static void CheckTupleType(Form_pg_class tuple_class);
+static bool needs_toast_table(Relation rel);
 
 /* Used by attribute and relation renaming routines: */
 
@@ -74,902 +73,955 @@ static void update_ri_trigger_args(Oid relid,
                                           bool update_relname);
 
 
-/* ----------------
- *             AlterTableAddColumn
- *             (formerly known as PerformAddAttribute)
- *
- *             adds an additional attribute to a relation
- *
- *             Adds attribute field(s) to a relation.  Each new attribute
- *             is given attnums in sequential order and is added to the
- *             ATTRIBUTE relation.  If the AMI fails, defunct tuples will
- *             remain in the ATTRIBUTE relation for later vacuuming.
- *             Later, there may be some reserved attribute names???
- *
- *             (If needed, can instead use elog to handle exceptions.)
- *
- *             Note:
- *                             Initial idea of ordering the tuple attributes so that all
- *             the variable length domains occured last was scratched.  Doing
- *             so would not speed access too much (in general) and would create
- *             many complications in formtuple, heap_getattr, and addattribute.
+/* ----------------------------------------------------------------
+ *             DefineRelation
+ *                             Creates a new relation.
  *
- *             scan attribute catalog for name conflict (within rel)
- *             scan type catalog for absence of data type (if not arg)
- *             create attnum magically???
- *             create attribute tuple
- *             insert attribute in attribute catalog
- *             modify reldesc
- *             create new relation tuple
- *             insert new relation in relation catalog
- *             delete original relation from relation catalog
- * ----------------
+ * If successful, returns the OID of the new relation.
+ * ----------------------------------------------------------------
  */
-void
-AlterTableAddColumn(Oid myrelid,
-                                       bool inherits,
-                                       ColumnDef *colDef)
+Oid
+DefineRelation(CreateStmt *stmt, char relkind)
 {
-       Relation        rel,
-                               pgclass,
-                               attrdesc;
-       HeapTuple       reltup;
-       HeapTuple       newreltup;
-       HeapTuple       attributeTuple;
-       Form_pg_attribute attribute;
-       FormData_pg_attribute attributeD;
+       char            relname[NAMEDATALEN];
+       Oid                     namespaceId;
+       List       *schema = stmt->tableElts;
+       int                     numberOfAttributes;
+       Oid                     relationId;
+       Relation        rel;
+       TupleDesc       descriptor;
+       List       *inheritOids;
+       List       *old_constraints;
+       bool            parentHasOids;
+       List       *rawDefaults;
+       List       *listptr;
        int                     i;
-       int                     minattnum,
-                               maxatts;
-       HeapTuple       typeTuple;
-       Form_pg_type tform;
-       int                     attndims;
+       AttrNumber      attnum;
 
        /*
-        * Grab an exclusive lock on the target table, which we will NOT
-        * release until end of transaction.
+        * Truncate relname to appropriate length (probably a waste of time,
+        * as parser should have done this already).
         */
-       rel = heap_open(myrelid, AccessExclusiveLock);
+       StrNCpy(relname, stmt->relation->relname, NAMEDATALEN);
 
-       if (rel->rd_rel->relkind != RELKIND_RELATION)
-               elog(ERROR, "ALTER TABLE: relation \"%s\" is not a table",
-                        RelationGetRelationName(rel));
+       /*
+        * Look up the namespace in which we are supposed to create the
+        * relation.
+        */
+       namespaceId = RangeVarGetCreationNamespace(stmt->relation);
 
        /*
-        * permissions checking.  this would normally be done in utility.c,
-        * but this particular routine is recursive.
-        *
-        * normally, only the owner of a class can change its schema.
+        * Merge domain attributes into the known columns before processing table
+        * inheritance.  Otherwise we risk adding double constraints to a
+        * domain-type column that's inherited.
         */
-       if (!allowSystemTableMods
-               && IsSystemRelation(rel))
-               elog(ERROR, "ALTER TABLE: relation \"%s\" is a system catalog",
-                        RelationGetRelationName(rel));
-       if (!pg_class_ownercheck(myrelid, GetUserId()))
-               elog(ERROR, "ALTER TABLE: \"%s\": permission denied",
-                        RelationGetRelationName(rel));
+       schema = MergeDomainAttributes(schema);
 
        /*
-        * 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.
+        * Look up inheritance ancestors and generate relation schema,
+        * including inherited attributes.
         */
-       if (inherits)
-       {
-               List       *child,
-                                  *children;
+       schema = MergeAttributes(schema, stmt->inhRelations,
+                                                        stmt->relation->istemp,
+                                                        &inheritOids, &old_constraints, &parentHasOids);
 
-               /* this routine is actually in the planner */
-               children = find_all_inheritors(myrelid);
+       numberOfAttributes = length(schema);
+       if (numberOfAttributes <= 0)
+               elog(ERROR, "DefineRelation: please inherit from a relation or define an attribute");
 
-               /*
-                * 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)
+       /*
+        * Create a relation descriptor from the relation schema and create
+        * the relation.  Note that in this stage only inherited (pre-cooked)
+        * defaults and constraints will be included into the new relation.
+        * (BuildDescForRelation takes care of the inherited defaults, but we
+        * have to copy inherited constraints here.)
+        */
+       descriptor = BuildDescForRelation(schema);
+
+       if (old_constraints != NIL)
+       {
+               ConstrCheck *check = (ConstrCheck *) palloc(length(old_constraints) *
+                                                                                                       sizeof(ConstrCheck));
+               int                     ncheck = 0;
+
+               foreach(listptr, old_constraints)
                {
-                       Oid                     childrelid = lfirsti(child);
+                       Constraint *cdef = (Constraint *) lfirst(listptr);
 
-                       if (childrelid == myrelid)
+                       if (cdef->contype != CONSTR_CHECK)
                                continue;
 
-                       AlterTableAddColumn(childrelid, false, colDef);
+                       if (cdef->name != NULL)
+                       {
+                               for (i = 0; i < ncheck; i++)
+                               {
+                                       if (strcmp(check[i].ccname, cdef->name) == 0)
+                                               elog(ERROR, "Duplicate CHECK constraint name: '%s'",
+                                                        cdef->name);
+                               }
+                               check[ncheck].ccname = cdef->name;
+                       }
+                       else
+                       {
+                               check[ncheck].ccname = (char *) palloc(NAMEDATALEN);
+                               snprintf(check[ncheck].ccname, NAMEDATALEN, "$%d", ncheck + 1);
+                       }
+                       Assert(cdef->raw_expr == NULL && cdef->cooked_expr != NULL);
+                       check[ncheck].ccbin = pstrdup(cdef->cooked_expr);
+                       ncheck++;
+               }
+               if (ncheck > 0)
+               {
+                       if (descriptor->constr == NULL)
+                       {
+                               descriptor->constr = (TupleConstr *) palloc(sizeof(TupleConstr));
+                               descriptor->constr->defval = NULL;
+                               descriptor->constr->num_defval = 0;
+                               descriptor->constr->has_not_null = false;
+                       }
+                       descriptor->constr->num_check = ncheck;
+                       descriptor->constr->check = check;
                }
        }
 
+       relationId = heap_create_with_catalog(relname,
+                                                                                 namespaceId,
+                                                                                 descriptor,
+                                                                                 relkind,
+                                                                                 stmt->hasoids || parentHasOids,
+                                                                                 allowSystemTableMods);
+
+       StoreCatalogInheritance(relationId, inheritOids);
+
        /*
-        * 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 must bump the command counter to make the newly-created relation
+        * tuple visible for opening.
+        */
+       CommandCounterIncrement();
+
+       /*
+        * Open the new relation and acquire exclusive lock on it.      This isn't
+        * really necessary for locking out other backends (since they can't
+        * see the new rel anyway until we commit), but it keeps the lock
+        * manager from complaining about deadlock risks.
+        */
+       rel = heap_open(relationId, AccessExclusiveLock);
+
+       /*
+        * Now add any newly specified column default values and CHECK
+        * constraints to the new relation.  These are passed to us in the
+        * form of raw parsetrees; we need to transform them to executable
+        * expression trees before they can be added. The most convenient way
+        * to do that is to apply the parser's transformExpr routine, but
+        * transformExpr doesn't work unless we have a pre-existing relation.
+        * So, the transformation has to be postponed to this final step of
+        * CREATE TABLE.
         *
-        * We do allow CHECK constraints, even though these theoretically could
-        * fail for NULL rows (eg, CHECK (newcol IS NOT NULL)).
+        * First, scan schema to find new column defaults.
         */
-       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.");
+       rawDefaults = NIL;
+       attnum = 0;
 
-       if (colDef->is_not_null)
-               elog(ERROR, "Adding NOT NULL columns is not implemented."
-                        "\n\tAdd the column, then use ALTER TABLE ... SET NOT NULL.");
+       foreach(listptr, schema)
+       {
+               ColumnDef  *colDef = lfirst(listptr);
+               RawColumnDefault *rawEnt;
 
-       pgclass = heap_openr(RelationRelationName, RowExclusiveLock);
+               attnum++;
 
-       reltup = SearchSysCache(RELOID,
-                                                       ObjectIdGetDatum(myrelid),
-                                                       0, 0, 0);
-       if (!HeapTupleIsValid(reltup))
-               elog(ERROR, "ALTER TABLE: relation \"%s\" not found",
-                        RelationGetRelationName(rel));
+               if (colDef->raw_default == NULL)
+                       continue;
+               Assert(colDef->cooked_default == NULL);
 
-       if (SearchSysCacheExists(ATTNAME,
-                                                        ObjectIdGetDatum(myrelid),
-                                                        PointerGetDatum(colDef->colname),
-                                                        0, 0))
-               elog(ERROR, "ALTER TABLE: column name \"%s\" already exists in table \"%s\"",
-                        colDef->colname, RelationGetRelationName(rel));
-
-       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);
-
-       if (colDef->typename->arrayBounds)
-               attndims = length(colDef->typename->arrayBounds);
-       else
-               attndims = 0;
-
-       typeTuple = typenameType(colDef->typename);
-       tform = (Form_pg_type) GETSTRUCT(typeTuple);
-
-       attributeTuple = heap_addheader(Natts_pg_attribute,
-                                                                       ATTRIBUTE_TUPLE_SIZE,
-                                                                       (void *) &attributeD);
-
-       attribute = (Form_pg_attribute) GETSTRUCT(attributeTuple);
-
-       attribute->attrelid = myrelid;
-       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)
-       {
-               Relation        idescs[Num_pg_attr_indices];
-
-               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, RowExclusiveLock);
+               rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
+               rawEnt->attnum = attnum;
+               rawEnt->raw_default = colDef->raw_default;
+               rawDefaults = lappend(rawDefaults, rawEnt);
+       }
 
        /*
-        * Update number of attributes in pg_class tuple
+        * Parse and add the defaults/constraints, if any.
         */
-       newreltup = heap_copytuple(reltup);
-
-       ((Form_pg_class) GETSTRUCT(newreltup))->relnatts = maxatts;
-       simple_heap_update(pgclass, &newreltup->t_self, newreltup);
-
-       /* keep catalog indices current */
-       if (RelationGetForm(pgclass)->relhasindex)
-       {
-               Relation        ridescs[Num_pg_class_indices];
-
-               CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, ridescs);
-               CatalogIndexInsert(ridescs, Num_pg_class_indices, pgclass, newreltup);
-               CatalogCloseIndices(Num_pg_class_indices, ridescs);
-       }
-
-       heap_freetuple(newreltup);
-       ReleaseSysCache(reltup);
-
-       heap_close(pgclass, NoLock);
-
-       heap_close(rel, NoLock);        /* close rel but keep lock! */
+       if (rawDefaults || stmt->constraints)
+               AddRelationRawConstraints(rel, rawDefaults, stmt->constraints);
 
        /*
-        * Make our catalog updates visible for subsequent steps.
+        * Clean up.  We keep lock on new relation (although it shouldn't be
+        * visible to anyone else anyway, until commit).
         */
-       CommandCounterIncrement();
+       heap_close(rel, NoLock);
 
-       /*
-        * 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_open(myrelid, AccessExclusiveLock);
-               AddRelationRawConstraints(rel, NIL, colDef->constraints);
-               heap_close(rel, NoLock);
-       }
+       return relationId;
+}
 
-       /*
-        * Automatically create the secondary relation for TOAST if it
-        * formerly had no such but now has toastable attributes.
-        */
-       AlterTableCreateToastTable(myrelid, true);
+/*
+ * RemoveRelation
+ *             Deletes a relation.
+ *
+ * Exceptions:
+ *             BadArg if name is invalid.
+ *
+ * Note:
+ *             If the relation has indices defined on it, then the index relations
+ * themselves will be destroyed, too.
+ */
+void
+RemoveRelation(const RangeVar *relation)
+{
+       Oid                     relOid;
+
+       relOid = RangeVarGetRelid(relation, false);
+       heap_drop_with_catalog(relOid, allowSystemTableMods);
 }
 
 /*
- * ALTER TABLE ALTER COLUMN DROP NOT NULL
+ * TruncateRelation
+ *                               Removes all the rows from a relation
+ *
+ * Exceptions:
+ *                               BadArg if name is invalid
+ *
+ * Note:
+ *                               Rows are removed, indices are truncated and reconstructed.
  */
 void
-AlterTableAlterColumnDropNotNull(Oid myrelid,
-                                                                bool inh, const char *colName)
+TruncateRelation(const RangeVar *relation)
 {
        Relation        rel;
-       HeapTuple       tuple;
-       AttrNumber      attnum;
-       Relation        attr_rel;
-       List            *indexoidlist;
-       List            *indexoidscan;
+       Oid                     relid;
 
-       rel = heap_open(myrelid, AccessExclusiveLock);
+       /* Grab exclusive lock in preparation for truncate */
+       rel = heap_openrv(relation, AccessExclusiveLock);
+       relid = RelationGetRelid(rel);
 
-       if (rel->rd_rel->relkind != RELKIND_RELATION)
-               elog(ERROR, "ALTER TABLE: relation \"%s\" is not a table",
+       if (rel->rd_rel->relkind == RELKIND_SEQUENCE)
+               elog(ERROR, "TRUNCATE cannot be used on sequences. '%s' is a sequence",
                         RelationGetRelationName(rel));
 
-       if (!allowSystemTableMods
-               && IsSystemRelation(rel))
-               elog(ERROR, "ALTER TABLE: relation \"%s\" is a system catalog",
+       if (rel->rd_rel->relkind == RELKIND_VIEW)
+               elog(ERROR, "TRUNCATE cannot be used on views. '%s' is a view",
                         RelationGetRelationName(rel));
 
-       if (!pg_class_ownercheck(myrelid, GetUserId()))
-               elog(ERROR, "ALTER TABLE: \"%s\": permission denied",
+       if (!allowSystemTableMods && IsSystemRelation(rel))
+               elog(ERROR, "TRUNCATE cannot be used on system tables. '%s' is a system table",
                         RelationGetRelationName(rel));
 
-       /*
-        * Propagate to children if desired
-        */
-       if (inh)
-       {
-               List       *child,
-                                  *children;
+       if (!pg_class_ownercheck(relid, GetUserId()))
+               elog(ERROR, "you do not own relation \"%s\"",
+                        RelationGetRelationName(rel));
 
-               /* this routine is actually in the planner */
-               children = find_all_inheritors(myrelid);
+       /* Keep the lock until transaction commit */
+       heap_close(rel, NoLock);
 
-               /*
-                * 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);
+       heap_truncate(relid);
+}
 
-                       if (childrelid == myrelid)
-                               continue;
-                       AlterTableAlterColumnDropNotNull(childrelid,
-                                                                                        false, colName);
-               }
-       }
 
-       /* -= now do the thing on this relation =- */
+/*
+ * MergeDomainAttributes
+ *      Returns a new table schema with the constraints, types, and other
+ *      attributes of domains resolved for fields using a domain as
+ *      their type.
+ */
+static List *
+MergeDomainAttributes(List *schema)
+{
+       List       *entry;
 
        /*
-        * get the number of the attribute
+        * Loop through the table elements supplied. These should
+        * never include inherited domains else they'll be
+        * double (or more) processed.
         */
-       tuple = SearchSysCache(ATTNAME,
-                                                  ObjectIdGetDatum(myrelid),
-                                                  PointerGetDatum(colName),
-                                                  0, 0);
-       if (!HeapTupleIsValid(tuple))
-               elog(ERROR, "ALTER TABLE: relation \"%s\" has no column \"%s\"",
-                        RelationGetRelationName(rel), colName);
-
-       attnum = ((Form_pg_attribute) GETSTRUCT(tuple))->attnum;
-       ReleaseSysCache(tuple);
+       foreach(entry, schema)
+       {
+               ColumnDef  *coldef = lfirst(entry);
+               HeapTuple  tuple;
+               Form_pg_type typeTup;
 
-       /* Prevent them from altering a system attribute */
-       if (attnum < 0)
-               elog(ERROR, "ALTER TABLE: Cannot alter system attribute \"%s\"",
-                        colName);
+               tuple = typenameType(coldef->typename);
+               typeTup = (Form_pg_type) GETSTRUCT(tuple);
 
-       /*
-        * Check that the attribute is not in a primary key
-        */
+               if (typeTup->typtype == 'd')
+               {
+                       /* Force the column to have the correct typmod. */
+                       coldef->typename->typmod = typeTup->typtypmod;
+                       /* XXX more to do here? */
+               }
 
-       /* Loop over all indices on the relation */
-       indexoidlist = RelationGetIndexList(rel);
+               /* Enforce type NOT NULL || column definition NOT NULL -> NOT NULL */
+               /* Currently only used for domains, but could be valid for all */
+               coldef->is_not_null |= typeTup->typnotnull;
 
-       foreach(indexoidscan, indexoidlist)
-       {
-               Oid             indexoid = lfirsti(indexoidscan);
-               HeapTuple       indexTuple;
-               Form_pg_index   indexStruct;
-               int             i;
+               ReleaseSysCache(tuple);
+       }
 
-               indexTuple = SearchSysCache(INDEXRELID,
-                                                                       ObjectIdGetDatum(indexoid),
-                                                                       0, 0, 0);
-               if (!HeapTupleIsValid(indexTuple))
-                       elog(ERROR, "ALTER TABLE: Index %u not found",
-                                indexoid);
-               indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
+       return schema;
+}
 
-               /* If the index is not a primary key, skip the check */
-               if (indexStruct->indisprimary)
-               {
-                       /*
-                        * Loop over each attribute in the primary key and
-                        * see if it matches the to-be-altered attribute
-                        */
-                       for (i = 0; i < INDEX_MAX_KEYS &&
-                                        indexStruct->indkey[i] != InvalidAttrNumber; i++)
-                       {
-                               if (indexStruct->indkey[i] == attnum)
-                                       elog(ERROR, "ALTER TABLE: Attribute \"%s\" is in a primary key", colName);
-                       }
-               }
-
-               ReleaseSysCache(indexTuple);
-       }
-
-       freeList(indexoidlist);
-
-       /*
-        * Okay, actually perform the catalog change
-        */
-       attr_rel = heap_openr(AttributeRelationName, RowExclusiveLock);
-
-       tuple = SearchSysCacheCopy(ATTNAME,
-                                                          ObjectIdGetDatum(myrelid),
-                                                          PointerGetDatum(colName),
-                                                          0, 0);
-       if (!HeapTupleIsValid(tuple)) /* shouldn't happen */
-               elog(ERROR, "ALTER TABLE: relation \"%s\" has no column \"%s\"",
-                        RelationGetRelationName(rel), colName);
-
-       ((Form_pg_attribute) GETSTRUCT(tuple))->attnotnull = FALSE;
-
-       simple_heap_update(attr_rel, &tuple->t_self, tuple);
-
-       /* keep the system catalog indices current */
-       if (RelationGetForm(attr_rel)->relhasindex)
-       {
-               Relation        idescs[Num_pg_attr_indices];
-
-               CatalogOpenIndices(Num_pg_attr_indices, Name_pg_attr_indices, idescs);
-               CatalogIndexInsert(idescs, Num_pg_attr_indices, attr_rel, tuple);
-               CatalogCloseIndices(Num_pg_attr_indices, idescs);
-       }
-
-       heap_close(attr_rel, RowExclusiveLock);
-
-       heap_close(rel, NoLock);
-}
-
-/*
- * ALTER TABLE ALTER COLUMN SET NOT NULL
+/*----------
+ * MergeAttributes
+ *             Returns new schema given initial schema and superclasses.
+ *
+ * Input arguments:
+ * 'schema' is the column/attribute definition for the table. (It's a list
+ *             of ColumnDef's.) It is destructively changed.
+ * 'supers' is a list of names (as RangeVar nodes) of parent relations.
+ * 'istemp' is TRUE if we are creating a temp relation.
+ *
+ * Output arguments:
+ * 'supOids' receives an integer list of the OIDs of the parent relations.
+ * 'supconstr' receives a list of constraints belonging to the parents,
+ *             updated as necessary to be valid for the child.
+ * 'supHasOids' is set TRUE if any parent has OIDs, else it is set FALSE.
+ *
+ * Return value:
+ * Completed schema list.
+ *
+ * Notes:
+ *       The order in which the attributes are inherited is very important.
+ *       Intuitively, the inherited attributes should come first. If a table
+ *       inherits from multiple parents, the order of those attributes are
+ *       according to the order of the parents specified in CREATE TABLE.
+ *
+ *       Here's an example:
+ *
+ *             create table person (name text, age int4, location point);
+ *             create table emp (salary int4, manager text) inherits(person);
+ *             create table student (gpa float8) inherits (person);
+ *             create table stud_emp (percent int4) inherits (emp, student);
+ *
+ *       The order of the attributes of stud_emp is:
+ *
+ *                                                     person {1:name, 2:age, 3:location}
+ *                                                     /        \
+ *                        {6:gpa}      student   emp {4:salary, 5:manager}
+ *                                                     \        /
+ *                                                stud_emp {7:percent}
+ *
+ *        If the same attribute name appears multiple times, then it appears
+ *        in the result table in the proper location for its first appearance.
+ *
+ *        Constraints (including NOT NULL constraints) for the child table
+ *        are the union of all relevant constraints, from both the child schema
+ *        and parent tables.
+ *
+ *        The default value for a child column is defined as:
+ *             (1) If the child schema specifies a default, that value is used.
+ *             (2) If neither the child nor any parent specifies a default, then
+ *                     the column will not have a default.
+ *             (3) If conflicting defaults are inherited from different parents
+ *                     (and not overridden by the child), an error is raised.
+ *             (4) Otherwise the inherited default is used.
+ *             Rule (3) is new in Postgres 7.1; in earlier releases you got a
+ *             rather arbitrary choice of which parent default to use.
+ *----------
  */
-void
-AlterTableAlterColumnSetNotNull(Oid myrelid,
-                                                               bool inh, const char *colName)
+static List *
+MergeAttributes(List *schema, List *supers, bool istemp,
+                               List **supOids, List **supconstr, bool *supHasOids)
 {
-       Relation        rel;
-       HeapTuple       tuple;
-       AttrNumber      attnum;
-       Relation        attr_rel;
-       HeapScanDesc    scan;
-       TupleDesc       tupdesc;
-
-       rel = heap_open(myrelid, AccessExclusiveLock);
-
-       if (rel->rd_rel->relkind != RELKIND_RELATION)
-               elog(ERROR, "ALTER TABLE: relation \"%s\" is not a table",
-                        RelationGetRelationName(rel));
-
-       if (!allowSystemTableMods
-               && IsSystemRelation(rel))
-               elog(ERROR, "ALTER TABLE: relation \"%s\" is a system catalog",
-                        RelationGetRelationName(rel));
-
-       if (!pg_class_ownercheck(myrelid, GetUserId()))
-               elog(ERROR, "ALTER TABLE: \"%s\": permission denied",
-                        RelationGetRelationName(rel));
+       List       *entry;
+       List       *inhSchema = NIL;
+       List       *parentOids = NIL;
+       List       *constraints = NIL;
+       bool            parentHasOids = false;
+       bool            have_bogus_defaults = false;
+       char       *bogus_marker = "Bogus!";            /* marks conflicting
+                                                                                                * defaults */
+       int                     child_attno;
 
        /*
-        * Propagate to children if desired
+        * Check for duplicate names in the explicit list of attributes.
+        *
+        * Although we might consider merging such entries in the same way that
+        * we handle name conflicts for inherited attributes, it seems to make
+        * more sense to assume such conflicts are errors.
         */
-       if (inh)
+       foreach(entry, schema)
        {
-               List       *child,
-                                  *children;
-
-               /* this routine is actually in the planner */
-               children = find_all_inheritors(myrelid);
+               ColumnDef  *coldef = lfirst(entry);
+               List       *rest;
 
-               /*
-                * 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)
+               foreach(rest, lnext(entry))
                {
-                       Oid                     childrelid = lfirsti(child);
+                       ColumnDef  *restdef = lfirst(rest);
 
-                       if (childrelid == myrelid)
-                               continue;
-                       AlterTableAlterColumnSetNotNull(childrelid,
-                                                                                       false, colName);
+                       if (strcmp(coldef->colname, restdef->colname) == 0)
+                               elog(ERROR, "CREATE TABLE: attribute \"%s\" duplicated",
+                                        coldef->colname);
                }
        }
 
-       /* -= now do the thing on this relation =- */
-
        /*
-        * get the number of the attribute
+        * Scan the parents left-to-right, and merge their attributes to form
+        * a list of inherited attributes (inhSchema).  Also check to see if
+        * we need to inherit an OID column.
         */
-       tuple = SearchSysCache(ATTNAME,
-                                                  ObjectIdGetDatum(myrelid),
-                                                  PointerGetDatum(colName),
-                                                  0, 0);
-       if (!HeapTupleIsValid(tuple))
-               elog(ERROR, "ALTER TABLE: relation \"%s\" has no column \"%s\"",
-                        RelationGetRelationName(rel), colName);
+       child_attno = 0;
+       foreach(entry, supers)
+       {
+               RangeVar   *parent = (RangeVar *) lfirst(entry);
+               Relation        relation;
+               TupleDesc       tupleDesc;
+               TupleConstr *constr;
+               AttrNumber *newattno;
+               AttrNumber      parent_attno;
 
-       attnum = ((Form_pg_attribute) GETSTRUCT(tuple))->attnum;
-       ReleaseSysCache(tuple);
+               relation = heap_openrv(parent, AccessShareLock);
 
-       /* Prevent them from altering a system attribute */
-       if (attnum < 0)
-               elog(ERROR, "ALTER TABLE: Cannot alter system attribute \"%s\"",
-                        colName);
+               if (relation->rd_rel->relkind != RELKIND_RELATION)
+                       elog(ERROR, "CREATE TABLE: inherited relation \"%s\" is not a table",
+                                parent->relname);
+               /* Permanent rels cannot inherit from temporary ones */
+               if (!istemp && isTempNamespace(RelationGetNamespace(relation)))
+                       elog(ERROR, "CREATE TABLE: cannot inherit from temp relation \"%s\"",
+                                parent->relname);
 
-       /*
-        * Perform a scan to ensure that there are no NULL
-        * values already in the relation
-        */
-       tupdesc = RelationGetDescr(rel);
+               /*
+                * We should have an UNDER permission flag for this, but for now,
+                * demand that creator of a child table own the parent.
+                */
+               if (!pg_class_ownercheck(RelationGetRelid(relation), GetUserId()))
+                       elog(ERROR, "you do not own table \"%s\"",
+                                parent->relname);
 
-       scan = heap_beginscan(rel, false, SnapshotNow, 0, NULL);
+               /*
+                * Reject duplications in the list of parents.
+                */
+               if (intMember(RelationGetRelid(relation), parentOids))
+                       elog(ERROR, "CREATE TABLE: inherited relation \"%s\" duplicated",
+                                parent->relname);
 
-       while (HeapTupleIsValid(tuple = heap_getnext(scan, 0)))
-       {
-               Datum           d;
-               bool            isnull;
+               parentOids = lappendi(parentOids, RelationGetRelid(relation));
+               setRelhassubclassInRelation(RelationGetRelid(relation), true);
 
-               d = heap_getattr(tuple, attnum, tupdesc, &isnull);
+               parentHasOids |= relation->rd_rel->relhasoids;
 
-               if (isnull)
-                       elog(ERROR, "ALTER TABLE: Attribute \"%s\" contains NULL values",
-                                colName);
-       }
+               tupleDesc = RelationGetDescr(relation);
+               constr = tupleDesc->constr;
 
-       heap_endscan(scan);
+               /*
+                * newattno[] will contain the child-table attribute numbers for
+                * the attributes of this parent table.  (They are not the same
+                * for parents after the first one.)
+                */
+               newattno = (AttrNumber *) palloc(tupleDesc->natts * sizeof(AttrNumber));
 
-       /*
-        * Okay, actually perform the catalog change
-        */
-       attr_rel = heap_openr(AttributeRelationName, RowExclusiveLock);
-
-       tuple = SearchSysCacheCopy(ATTNAME,
-                                                          ObjectIdGetDatum(myrelid),
-                                                          PointerGetDatum(colName),
-                                                          0, 0);
-       if (!HeapTupleIsValid(tuple)) /* shouldn't happen */
-               elog(ERROR, "ALTER TABLE: relation \"%s\" has no column \"%s\"",
-                        RelationGetRelationName(rel), colName);
+               for (parent_attno = 1; parent_attno <= tupleDesc->natts;
+                        parent_attno++)
+               {
+                       Form_pg_attribute attribute = tupleDesc->attrs[parent_attno - 1];
+                       char       *attributeName = NameStr(attribute->attname);
+                       int                     exist_attno;
+                       ColumnDef  *def;
+                       TypeName   *typename;
 
-       ((Form_pg_attribute) GETSTRUCT(tuple))->attnotnull = TRUE;
+                       /*
+                        * Does it conflict with some previously inherited column?
+                        */
+                       exist_attno = findAttrByName(attributeName, inhSchema);
+                       if (exist_attno > 0)
+                       {
+                               /*
+                                * Yes, try to merge the two column definitions. They must
+                                * have the same type and typmod.
+                                */
+                               elog(NOTICE, "CREATE TABLE: merging multiple inherited definitions of attribute \"%s\"",
+                                        attributeName);
+                               def = (ColumnDef *) nth(exist_attno - 1, inhSchema);
+                               if (typenameTypeId(def->typename) != attribute->atttypid ||
+                                       def->typename->typmod != attribute->atttypmod)
+                                       elog(ERROR, "CREATE TABLE: inherited attribute \"%s\" type conflict (%s and %s)",
+                                                attributeName,
+                                                TypeNameToString(def->typename),
+                                                typeidTypeName(attribute->atttypid));
+                               /* Merge of NOT NULL constraints = OR 'em together */
+                               def->is_not_null |= attribute->attnotnull;
+                               /* Default and other constraints are handled below */
+                               newattno[parent_attno - 1] = exist_attno;
+                       }
+                       else
+                       {
+                               /*
+                                * No, create a new inherited column
+                                */
+                               def = makeNode(ColumnDef);
+                               def->colname = pstrdup(attributeName);
+                               typename = makeNode(TypeName);
+                               typename->typeid = attribute->atttypid;
+                               typename->typmod = attribute->atttypmod;
+                               def->typename = typename;
+                               def->is_not_null = attribute->attnotnull;
+                               def->raw_default = NULL;
+                               def->cooked_default = NULL;
+                               def->constraints = NIL;
+                               inhSchema = lappend(inhSchema, def);
+                               newattno[parent_attno - 1] = ++child_attno;
+                       }
 
-       simple_heap_update(attr_rel, &tuple->t_self, tuple);
+                       /*
+                        * Copy default if any
+                        */
+                       if (attribute->atthasdef)
+                       {
+                               char       *this_default = NULL;
+                               AttrDefault *attrdef;
+                               int                     i;
 
-       /* keep the system catalog indices current */
-       if (RelationGetForm(attr_rel)->relhasindex)
-       {
-               Relation        idescs[Num_pg_attr_indices];
+                               /* Find default in constraint structure */
+                               Assert(constr != NULL);
+                               attrdef = constr->defval;
+                               for (i = 0; i < constr->num_defval; i++)
+                               {
+                                       if (attrdef[i].adnum == parent_attno)
+                                       {
+                                               this_default = attrdef[i].adbin;
+                                               break;
+                                       }
+                               }
+                               Assert(this_default != NULL);
 
-               CatalogOpenIndices(Num_pg_attr_indices, Name_pg_attr_indices, idescs);
-               CatalogIndexInsert(idescs, Num_pg_attr_indices, attr_rel, tuple);
-               CatalogCloseIndices(Num_pg_attr_indices, idescs);
-       }
+                               /*
+                                * If default expr could contain any vars, we'd need to
+                                * fix 'em, but it can't; so default is ready to apply to
+                                * child.
+                                *
+                                * If we already had a default from some prior parent, check
+                                * to see if they are the same.  If so, no problem; if
+                                * not, mark the column as having a bogus default. Below,
+                                * we will complain if the bogus default isn't overridden
+                                * by the child schema.
+                                */
+                               Assert(def->raw_default == NULL);
+                               if (def->cooked_default == NULL)
+                                       def->cooked_default = pstrdup(this_default);
+                               else if (strcmp(def->cooked_default, this_default) != 0)
+                               {
+                                       def->cooked_default = bogus_marker;
+                                       have_bogus_defaults = true;
+                               }
+                       }
+               }
 
-       heap_close(attr_rel, RowExclusiveLock);
+               /*
+                * Now copy the constraints of this parent, adjusting attnos using
+                * the completed newattno[] map
+                */
+               if (constr && constr->num_check > 0)
+               {
+                       ConstrCheck *check = constr->check;
+                       int                     i;
 
-       heap_close(rel, NoLock);
-}
+                       for (i = 0; i < constr->num_check; i++)
+                       {
+                               Constraint *cdef = makeNode(Constraint);
+                               Node       *expr;
 
+                               cdef->contype = CONSTR_CHECK;
+                               if (check[i].ccname[0] == '$')
+                                       cdef->name = NULL;
+                               else
+                                       cdef->name = pstrdup(check[i].ccname);
+                               cdef->raw_expr = NULL;
+                               /* adjust varattnos of ccbin here */
+                               expr = stringToNode(check[i].ccbin);
+                               change_varattnos_of_a_node(expr, newattno);
+                               cdef->cooked_expr = nodeToString(expr);
+                               constraints = lappend(constraints, cdef);
+                       }
+               }
 
-/*
- * ALTER TABLE ALTER COLUMN SET/DROP DEFAULT
- */
-void
-AlterTableAlterColumnDefault(Oid myrelid,
-                                                        bool inh, const char *colName,
-                                                        Node *newDefault)
-{
-       Relation        rel;
-       HeapTuple       tuple;
-       AttrNumber      attnum;
+               pfree(newattno);
 
-       rel = heap_open(myrelid, AccessExclusiveLock);
+               /*
+                * Close the parent rel, but keep our AccessShareLock on it until
+                * xact commit.  That will prevent someone else from deleting or
+                * ALTERing the parent before the child is committed.
+                */
+               heap_close(relation, NoLock);
+       }
 
        /*
-        * We allow defaults on views so that INSERT into a view can have
-        * default-ish behavior.  This works because the rewriter substitutes
-        * default values into INSERTs before it expands rules.
+        * If we had no inherited attributes, the result schema is just the
+        * explicitly declared columns.  Otherwise, we need to merge the
+        * declared columns into the inherited schema list.
         */
-       if (rel->rd_rel->relkind != RELKIND_RELATION &&
-               rel->rd_rel->relkind != RELKIND_VIEW)
-               elog(ERROR, "ALTER TABLE: relation \"%s\" is not a table or view",
-                        RelationGetRelationName(rel));
+       if (inhSchema != NIL)
+       {
+               foreach(entry, schema)
+               {
+                       ColumnDef  *newdef = lfirst(entry);
+                       char       *attributeName = newdef->colname;
+                       int                     exist_attno;
 
-       if (!allowSystemTableMods
-               && IsSystemRelation(rel))
-               elog(ERROR, "ALTER TABLE: relation \"%s\" is a system catalog",
-                        RelationGetRelationName(rel));
+                       /*
+                        * Does it conflict with some previously inherited column?
+                        */
+                       exist_attno = findAttrByName(attributeName, inhSchema);
+                       if (exist_attno > 0)
+                       {
+                               ColumnDef  *def;
 
-       if (!pg_class_ownercheck(myrelid, GetUserId()))
-               elog(ERROR, "ALTER TABLE: \"%s\": permission denied",
-                        RelationGetRelationName(rel));
+                               /*
+                                * Yes, try to merge the two column definitions. They must
+                                * have the same type and typmod.
+                                */
+                               elog(NOTICE, "CREATE TABLE: merging attribute \"%s\" with inherited definition",
+                                        attributeName);
+                               def = (ColumnDef *) nth(exist_attno - 1, inhSchema);
+                               if (typenameTypeId(def->typename) != typenameTypeId(newdef->typename) ||
+                                       def->typename->typmod != newdef->typename->typmod)
+                                       elog(ERROR, "CREATE TABLE: attribute \"%s\" type conflict (%s and %s)",
+                                                attributeName,
+                                                TypeNameToString(def->typename),
+                                                TypeNameToString(newdef->typename));
+                               /* Merge of NOT NULL constraints = OR 'em together */
+                               def->is_not_null |= newdef->is_not_null;
+                               /* If new def has a default, override previous default */
+                               if (newdef->raw_default != NULL)
+                               {
+                                       def->raw_default = newdef->raw_default;
+                                       def->cooked_default = newdef->cooked_default;
+                               }
+                       }
+                       else
+                       {
+                               /*
+                                * No, attach new column to result schema
+                                */
+                               inhSchema = lappend(inhSchema, newdef);
+                       }
+               }
+
+               schema = inhSchema;
+       }
 
        /*
-        * Propagate to children if desired
+        * If we found any conflicting parent default values, check to make
+        * sure they were overridden by the child.
         */
-       if (inh)
+       if (have_bogus_defaults)
        {
-               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)
+               foreach(entry, schema)
                {
-                       Oid                     childrelid = lfirsti(child);
+                       ColumnDef  *def = lfirst(entry);
 
-                       if (childrelid == myrelid)
-                               continue;
-                       AlterTableAlterColumnDefault(childrelid,
-                                                                                false, colName, newDefault);
+                       if (def->cooked_default == bogus_marker)
+                               elog(ERROR, "CREATE TABLE: attribute \"%s\" inherits conflicting default values"
+                                        "\n\tTo resolve the conflict, specify a default explicitly",
+                                        def->colname);
                }
        }
 
-       /* -= now do the thing on this relation =- */
+       *supOids = parentOids;
+       *supconstr = constraints;
+       *supHasOids = parentHasOids;
+       return schema;
+}
 
-       /*
-        * get the number of the attribute
-        */
-       tuple = SearchSysCache(ATTNAME,
-                                                  ObjectIdGetDatum(myrelid),
-                                                  PointerGetDatum(colName),
-                                                  0, 0);
-       if (!HeapTupleIsValid(tuple))
-               elog(ERROR, "ALTER TABLE: relation \"%s\" has no column \"%s\"",
-                        RelationGetRelationName(rel), colName);
-
-       attnum = ((Form_pg_attribute) GETSTRUCT(tuple))->attnum;
-       ReleaseSysCache(tuple);
-
-       if (newDefault)
-       {
-               /* SET DEFAULT */
-               RawColumnDefault *rawEnt;
-
-               /* Get rid of the old one first */
-               drop_default(myrelid, attnum);
-
-               rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
-               rawEnt->attnum = attnum;
-               rawEnt->raw_default = newDefault;
-
-               /*
-                * This function is intended for CREATE TABLE, so it processes a
-                * _list_ of defaults, but we just do one.
-                */
-               AddRelationRawConstraints(rel, makeList1(rawEnt), NIL);
-       }
-       else
+/*
+ * complementary static functions for MergeAttributes().
+ *
+ * Varattnos of pg_relcheck.rcbin must be rewritten when subclasses inherit
+ * constraints from parent classes, since the inherited attributes could
+ * be given different column numbers in multiple-inheritance cases.
+ *
+ * Note that the passed node tree is modified in place!
+ */
+static bool
+change_varattnos_walker(Node *node, const AttrNumber *newattno)
+{
+       if (node == NULL)
+               return false;
+       if (IsA(node, Var))
        {
-               /* DROP DEFAULT */
-               Relation        attr_rel;
-
-               /* Fix the pg_attribute row */
-               attr_rel = heap_openr(AttributeRelationName, RowExclusiveLock);
-
-               tuple = SearchSysCacheCopy(ATTNAME,
-                                                                  ObjectIdGetDatum(myrelid),
-                                                                  PointerGetDatum(colName),
-                                                                  0, 0);
-               if (!HeapTupleIsValid(tuple)) /* shouldn't happen */
-                       elog(ERROR, "ALTER TABLE: relation \"%s\" has no column \"%s\"",
-                                RelationGetRelationName(rel), colName);
-
-               ((Form_pg_attribute) GETSTRUCT(tuple))->atthasdef = FALSE;
-
-               simple_heap_update(attr_rel, &tuple->t_self, tuple);
+               Var                *var = (Var *) node;
 
-               /* keep the system catalog indices current */
-               if (RelationGetForm(attr_rel)->relhasindex)
+               if (var->varlevelsup == 0 && var->varno == 1 &&
+                       var->varattno > 0)
                {
-                       Relation        idescs[Num_pg_attr_indices];
-
-                       CatalogOpenIndices(Num_pg_attr_indices, Name_pg_attr_indices, idescs);
-                       CatalogIndexInsert(idescs, Num_pg_attr_indices, attr_rel, tuple);
-                       CatalogCloseIndices(Num_pg_attr_indices, idescs);
+                       /*
+                        * ??? the following may be a problem when the node is
+                        * multiply referenced though stringToNode() doesn't create
+                        * such a node currently.
+                        */
+                       Assert(newattno[var->varattno - 1] > 0);
+                       var->varattno = newattno[var->varattno - 1];
                }
-
-               heap_close(attr_rel, RowExclusiveLock);
-
-               /* get rid of actual default definition in pg_attrdef */
-               drop_default(myrelid, attnum);
+               return false;
        }
-
-       heap_close(rel, NoLock);
+       return expression_tree_walker(node, change_varattnos_walker,
+                                                                 (void *) newattno);
 }
 
-
-static void
-drop_default(Oid relid, int16 attnum)
+static bool
+change_varattnos_of_a_node(Node *node, const AttrNumber *newattno)
 {
-       ScanKeyData scankeys[2];
-       HeapScanDesc scan;
-       Relation        attrdef_rel;
-       HeapTuple       tuple;
-
-       attrdef_rel = heap_openr(AttrDefaultRelationName, RowExclusiveLock);
-       ScanKeyEntryInitialize(&scankeys[0], 0x0,
-                                                  Anum_pg_attrdef_adrelid, F_OIDEQ,
-                                                  ObjectIdGetDatum(relid));
-       ScanKeyEntryInitialize(&scankeys[1], 0x0,
-                                                  Anum_pg_attrdef_adnum, F_INT2EQ,
-                                                  Int16GetDatum(attnum));
-
-       scan = heap_beginscan(attrdef_rel, false, SnapshotNow, 2, scankeys);
-
-       if (HeapTupleIsValid(tuple = heap_getnext(scan, 0)))
-               simple_heap_delete(attrdef_rel, &tuple->t_self);
-
-       heap_endscan(scan);
-
-       heap_close(attrdef_rel, NoLock);
+       return change_varattnos_walker(node, newattno);
 }
 
-
 /*
- * ALTER TABLE ALTER COLUMN SET STATISTICS / STORAGE
+ * StoreCatalogInheritance
+ *             Updates the system catalogs with proper inheritance information.
+ *
+ * supers is an integer list of the OIDs of the new relation's direct
+ * ancestors.  NB: it is destructively changed to include indirect ancestors.
  */
-void
-AlterTableAlterColumnFlags(Oid myrelid,
-                                                  bool inh, const char *colName,
-                                                  Node *flagValue, const char *flagType)
+static void
+StoreCatalogInheritance(Oid relationId, List *supers)
 {
-       Relation        rel;
-       int                     newtarget = 1;
-       char        newstorage = 'p';
-       Relation        attrelation;
+       Relation        relation;
+       TupleDesc       desc;
+       int16           seqNumber;
+       List       *entry;
        HeapTuple       tuple;
-       Form_pg_attribute attrtuple;
-
-       rel = heap_open(myrelid, AccessExclusiveLock);
-
-       if (rel->rd_rel->relkind != RELKIND_RELATION)
-               elog(ERROR, "ALTER TABLE: relation \"%s\" is not a table",
-                        RelationGetRelationName(rel));
 
        /*
-        * we allow statistics case for system tables
+        * sanity checks
         */
-       if (*flagType != 'S' && !allowSystemTableMods && IsSystemRelation(rel))
-               elog(ERROR, "ALTER TABLE: relation \"%s\" is a system catalog",
-                        RelationGetRelationName(rel));
+       AssertArg(OidIsValid(relationId));
 
-       if (!pg_class_ownercheck(myrelid, GetUserId()))
-               elog(ERROR, "ALTER TABLE: \"%s\": permission denied",
-                        RelationGetRelationName(rel));
+       if (supers == NIL)
+               return;
 
        /*
-        * Check the supplied parameters before anything else
+        * Catalog INHERITS information using direct ancestors only.
         */
-       if (*flagType == 'S')
-       {
-               /* STATISTICS */
-               Assert(IsA(flagValue, Integer));
-               newtarget = intVal(flagValue);
+       relation = heap_openr(InheritsRelationName, RowExclusiveLock);
+       desc = RelationGetDescr(relation);
 
-               /*
-                * Limit target to sane range (should we raise an error instead?)
-                */
-               if (newtarget < 0)
-                       newtarget = 0;
-               else if (newtarget > 1000)
-                       newtarget = 1000;
-       }
-       else if (*flagType == 'M')
+       seqNumber = 1;
+       foreach(entry, supers)
        {
-               /* STORAGE */
-               char        *storagemode;
+               Oid                     entryOid = lfirsti(entry);
+               Datum           datum[Natts_pg_inherits];
+               char            nullarr[Natts_pg_inherits];
 
-               Assert(IsA(flagValue, String));
-               storagemode = strVal(flagValue);
+               datum[0] = ObjectIdGetDatum(relationId);                /* inhrel */
+               datum[1] = ObjectIdGetDatum(entryOid);  /* inhparent */
+               datum[2] = Int16GetDatum(seqNumber);    /* inhseqno */
 
-               if (strcasecmp(storagemode, "plain") == 0)
-                       newstorage = 'p';
-               else if (strcasecmp(storagemode, "external") == 0)
-                       newstorage = 'e';
-               else if (strcasecmp(storagemode, "extended") == 0)
-                       newstorage = 'x';
-               else if (strcasecmp(storagemode, "main") == 0)
-                       newstorage = 'm';
-               else
-                       elog(ERROR, "ALTER TABLE: \"%s\" storage not recognized",
-                                storagemode);
-       }
-       else
-       {
-               elog(ERROR, "ALTER TABLE: Invalid column flag: %c",
-                        (int) *flagType);
-       }
+               nullarr[0] = ' ';
+               nullarr[1] = ' ';
+               nullarr[2] = ' ';
 
-       /*
-        * Propagate to children if desired
-        */
-       if (inh)
-       {
-               List       *child,
-                                  *children;
+               tuple = heap_formtuple(desc, datum, nullarr);
 
-               /* this routine is actually in the planner */
-               children = find_all_inheritors(myrelid);
+               heap_insert(relation, tuple);
 
-               /*
-                * 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)
+               if (RelationGetForm(relation)->relhasindex)
                {
-                       Oid                     childrelid = lfirsti(child);
+                       Relation        idescs[Num_pg_inherits_indices];
 
-                       if (childrelid == myrelid)
-                               continue;
-                       AlterTableAlterColumnFlags(childrelid,
-                                                                          false, colName, flagValue, flagType);
+                       CatalogOpenIndices(Num_pg_inherits_indices, Name_pg_inherits_indices, idescs);
+                       CatalogIndexInsert(idescs, Num_pg_inherits_indices, relation, tuple);
+                       CatalogCloseIndices(Num_pg_inherits_indices, idescs);
                }
-       }
 
-       /* -= now do the thing on this relation =- */
+               heap_freetuple(tuple);
 
-       attrelation = heap_openr(AttributeRelationName, RowExclusiveLock);
+               seqNumber += 1;
+       }
 
-       tuple = SearchSysCacheCopy(ATTNAME,
-                                                          ObjectIdGetDatum(myrelid),
-                                                          PointerGetDatum(colName),
-                                                          0, 0);
-       if (!HeapTupleIsValid(tuple))
-               elog(ERROR, "ALTER TABLE: relation \"%s\" has no column \"%s\"",
-                        RelationGetRelationName(rel), colName);
-       attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
+       heap_close(relation, RowExclusiveLock);
 
-       if (attrtuple->attnum < 0)
-               elog(ERROR, "ALTER TABLE: cannot change system attribute \"%s\"",
-                        colName);
-       /*
-        * Now change the appropriate field
-        */
-       if (*flagType == 'S')
-               attrtuple->attstattarget = newtarget;
-       else if (*flagType == 'M')
+       /* ----------------
+        * Expand supers list to include indirect ancestors as well.
+        *
+        * Algorithm:
+        *      0. begin with list of direct superclasses.
+        *      1. append after each relationId, its superclasses, recursively.
+        *      2. remove all but last of duplicates.
+        * ----------------
+        */
+
+       /*
+        * 1. append after each relationId, its superclasses, recursively.
+        */
+       foreach(entry, supers)
        {
-               /*
-                * safety check: do not allow toasted storage modes unless column
-                * datatype is TOAST-aware.
-                */
-               if (newstorage == 'p' || TypeIsToastable(attrtuple->atttypid))
-                       attrtuple->attstorage = newstorage;
-               else
-                       elog(ERROR, "ALTER TABLE: Column datatype %s can only have storage \"plain\"",
-                                format_type_be(attrtuple->atttypid));
-       }
+               HeapTuple       tuple;
+               Oid                     id;
+               int16           number;
+               List       *next;
+               List       *current;
 
-       simple_heap_update(attrelation, &tuple->t_self, tuple);
+               id = (Oid) lfirsti(entry);
+               current = entry;
+               next = lnext(entry);
 
-       /* keep system catalog indices current */
-       {
-               Relation        irelations[Num_pg_attr_indices];
+               for (number = 1;; number += 1)
+               {
+                       tuple = SearchSysCache(INHRELID,
+                                                                  ObjectIdGetDatum(id),
+                                                                  Int16GetDatum(number),
+                                                                  0, 0);
+                       if (!HeapTupleIsValid(tuple))
+                               break;
 
-               CatalogOpenIndices(Num_pg_attr_indices, Name_pg_attr_indices, irelations);
-               CatalogIndexInsert(irelations, Num_pg_attr_indices, attrelation, tuple);
-               CatalogCloseIndices(Num_pg_attr_indices, irelations);
+                       lnext(current) = lconsi(((Form_pg_inherits)
+                                                                        GETSTRUCT(tuple))->inhparent,
+                                                                       NIL);
+
+                       ReleaseSysCache(tuple);
+
+                       current = lnext(current);
+               }
+               lnext(current) = next;
        }
 
-       heap_freetuple(tuple);
-       heap_close(attrelation, NoLock);
-       heap_close(rel, NoLock);        /* close rel, but keep lock! */
-}
+       /*
+        * 2. remove all but last of duplicates.
+        */
+       foreach(entry, supers)
+       {
+               Oid                     thisone;
+               bool            found;
+               List       *rest;
 
+again:
+               thisone = lfirsti(entry);
+               found = false;
+               foreach(rest, lnext(entry))
+               {
+                       if (thisone == lfirsti(rest))
+                       {
+                               found = true;
+                               break;
+                       }
+               }
+               if (found)
+               {
+                       /*
+                        * found a later duplicate, so remove this entry.
+                        */
+                       lfirsti(entry) = lfirsti(lnext(entry));
+                       lnext(entry) = lnext(lnext(entry));
 
+                       goto again;
+               }
+       }
+}
 
 /*
- * ALTER TABLE DROP COLUMN
+ * Look for an existing schema entry with the given name.
+ *
+ * Returns the index (starting with 1) if attribute already exists in schema,
+ * 0 if it doesn't.
  */
-void
-AlterTableDropColumn(Oid myrelid,
-                                        bool inh, const char *colName,
-                                        int behavior)
+static int
+findAttrByName(const char *attributeName, List *schema)
 {
-       elog(ERROR, "ALTER TABLE / DROP COLUMN is not implemented");
+       List       *s;
+       int                     i = 0;
+
+       foreach(s, schema)
+       {
+               ColumnDef  *def = lfirst(s);
+
+               ++i;
+               if (strcmp(attributeName, def->colname) == 0)
+                       return i;
+       }
+       return 0;
 }
 
+/*
+ * Update a relation's pg_class.relhassubclass entry to the given value
+ */
+static void
+setRelhassubclassInRelation(Oid relationId, bool relhassubclass)
+{
+       Relation        relationRelation;
+       HeapTuple       tuple;
+       Relation        idescs[Num_pg_class_indices];
+
+       /*
+        * Fetch a modifiable copy of the tuple, modify it, update pg_class.
+        */
+       relationRelation = heap_openr(RelationRelationName, RowExclusiveLock);
+       tuple = SearchSysCacheCopy(RELOID,
+                                                          ObjectIdGetDatum(relationId),
+                                                          0, 0, 0);
+       if (!HeapTupleIsValid(tuple))
+               elog(ERROR, "setRelhassubclassInRelation: cache lookup failed for relation %u", relationId);
+
+       ((Form_pg_class) GETSTRUCT(tuple))->relhassubclass = relhassubclass;
+       simple_heap_update(relationRelation, &tuple->t_self, tuple);
+
+       /* keep the catalog indices up to date */
+       CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, idescs);
+       CatalogIndexInsert(idescs, Num_pg_class_indices, relationRelation, tuple);
+       CatalogCloseIndices(Num_pg_class_indices, idescs);
+
+       heap_freetuple(tuple);
+       heap_close(relationRelation, RowExclusiveLock);
+}
 
 
 /*
- * ALTER TABLE ADD CONSTRAINT
+ *             renameatt               - changes the name of a attribute in a relation
+ *
+ *             Attname attribute is changed in attribute catalog.
+ *             No record of the previous attname is kept (correct?).
+ *
+ *             get proper relrelation from relation catalog (if not arg)
+ *             scan attribute catalog
+ *                             for name conflict (within rel)
+ *                             for original attribute (if not arg)
+ *             modify attname in attribute tuple
+ *             insert modified attribute in attribute catalog
+ *             delete original attribute from attribute catalog
  */
 void
-AlterTableAddConstraint(Oid myrelid,
-                                               bool inh, List *newConstraints)
+renameatt(Oid relid,
+                 const char *oldattname,
+                 const char *newattname,
+                 bool recurse)
 {
-       Relation        rel;
-       List       *listptr;
+       Relation        targetrelation;
+       Relation        attrelation;
+       HeapTuple       atttup;
+       List       *indexoidlist;
+       List       *indexoidscan;
 
        /*
         * Grab an exclusive lock on the target table, which we will NOT
         * release until end of transaction.
         */
-       rel = heap_open(myrelid, AccessExclusiveLock);
-
-       if (rel->rd_rel->relkind != RELKIND_RELATION)
-               elog(ERROR, "ALTER TABLE: relation \"%s\" is not a table",
-                        RelationGetRelationName(rel));
-
-       if (!allowSystemTableMods
-               && IsSystemRelation(rel))
-               elog(ERROR, "ALTER TABLE: relation \"%s\" is a system catalog",
-                        RelationGetRelationName(rel));
+       targetrelation = heap_open(relid, AccessExclusiveLock);
 
-       if (!pg_class_ownercheck(myrelid, GetUserId()))
-               elog(ERROR, "ALTER TABLE: \"%s\": permission denied",
-                        RelationGetRelationName(rel));
+       /*
+        * permissions checking.  this would normally be done in utility.c,
+        * but this particular routine is recursive.
+        *
+        * normally, only the owner of a class can change its schema.
+        */
+       if (!allowSystemTableMods 
+               && IsSystemRelation(targetrelation))
+               elog(ERROR, "renameatt: class \"%s\" is a system catalog",
+                        RelationGetRelationName(targetrelation));
+       if (!pg_class_ownercheck(relid, GetUserId()))
+               elog(ERROR, "renameatt: you do not own class \"%s\"",
+                        RelationGetRelationName(targetrelation));
 
-       if (inh)
+       /*
+        * if the 'recurse' flag is set then we are supposed to rename this
+        * attribute in all classes that inherit from 'relname' (as well as in
+        * 'relname').
+        *
+        * any permissions or problems with duplicate attributes will cause the
+        * whole transaction to abort, which is what we want -- all or
+        * nothing.
+        */
+       if (recurse)
        {
                List       *child,
                                   *children;
 
                /* this routine is actually in the planner */
-               children = find_all_inheritors(myrelid);
+               children = find_all_inheritors(relid);
 
                /*
                 * find_all_inheritors does the recursive search of the
@@ -980,1641 +1032,1354 @@ AlterTableAddConstraint(Oid myrelid,
                {
                        Oid                     childrelid = lfirsti(child);
 
-                       if (childrelid == myrelid)
+                       if (childrelid == relid)
                                continue;
-                       AlterTableAddConstraint(childrelid, false, newConstraints);
+                       /* note we need not recurse again! */
+                       renameatt(childrelid, oldattname, newattname, false);
                }
        }
 
-       foreach(listptr, newConstraints)
-       {
-               Node       *newConstraint = lfirst(listptr);
+       attrelation = heap_openr(AttributeRelationName, RowExclusiveLock);
 
-               switch (nodeTag(newConstraint))
-               {
-                       case T_Constraint:
-                               {
-                                       Constraint *constr = (Constraint *) newConstraint;
+       atttup = SearchSysCacheCopy(ATTNAME,
+                                                               ObjectIdGetDatum(relid),
+                                                               PointerGetDatum(oldattname),
+                                                               0, 0);
+       if (!HeapTupleIsValid(atttup))
+               elog(ERROR, "renameatt: attribute \"%s\" does not exist", oldattname);
 
-                                       /*
-                                        * Currently, we only expect to see CONSTR_CHECK nodes
-                                        * arriving here (see the preprocessing done in
-                                        * parser/analyze.c).  Use a switch anyway to make it
-                                        * easier to add more code later.
-                                        */
-                                       switch (constr->contype)
-                                       {
-                                               case CONSTR_CHECK:
-                                                       {
-                                                               ParseState *pstate;
-                                                               bool            successful = true;
-                                                               HeapScanDesc scan;
-                                                               ExprContext *econtext;
-                                                               TupleTableSlot *slot;
-                                                               HeapTuple       tuple;
-                                                               RangeTblEntry *rte;
-                                                               List       *qual;
-                                                               Node       *expr;
-                                                               char       *name;
+       if (((Form_pg_attribute) GETSTRUCT(atttup))->attnum < 0)
+               elog(ERROR, "renameatt: system attribute \"%s\" not renamed", oldattname);
 
-                                                               if (constr->name)
-                                                                       name = constr->name;
-                                                               else
-                                                                       name = "<unnamed>";
-
-                                                               /*
-                                                                * We need to make a parse state and range
-                                                                * table to allow us to transformExpr and
-                                                                * fix_opids to get a version of the
-                                                                * expression we can pass to ExecQual
-                                                                */
-                                                               pstate = make_parsestate(NULL);
-                                                               rte = addRangeTableEntryForRelation(pstate,
-                                                                                                                                       myrelid,
-                                                                                       makeAlias(RelationGetRelationName(rel), NIL),
-                                                                                                                                       false,
-                                                                                                                                       true);
-                                                               addRTEtoQuery(pstate, rte, true, true);
-
-                                                               /*
-                                                                * Convert the A_EXPR in raw_expr into an
-                                                                * EXPR
-                                                                */
-                                                               expr = transformExpr(pstate, constr->raw_expr);
-
-                                                               /*
-                                                                * Make sure it yields a boolean result.
-                                                                */
-                                                               if (exprType(expr) != BOOLOID)
-                                                                       elog(ERROR, "CHECK '%s' does not yield boolean result",
-                                                                                name);
-
-                                                               /*
-                                                                * Make sure no outside relations are
-                                                                * referred to.
-                                                                */
-                                                               if (length(pstate->p_rtable) != 1)
-                                                                       elog(ERROR, "Only relation '%s' can be referenced in CHECK",
-                                                                                RelationGetRelationName(rel));
-
-                                                               /*
-                                                                * Might as well try to reduce any
-                                                                * constant expressions.
-                                                                */
-                                                               expr = eval_const_expressions(expr);
-
-                                                               /* And fix the opids */
-                                                               fix_opids(expr);
-
-                                                               qual = makeList1(expr);
-
-                                                               /* Make tuple slot to hold tuples */
-                                                               slot = MakeTupleTableSlot();
-                                                               ExecSetSlotDescriptor(slot, RelationGetDescr(rel), false);
-                                                               /* Make an expression context for ExecQual */
-                                                               econtext = MakeExprContext(slot, CurrentMemoryContext);
-
-                                                               /*
-                                                                * 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);
-                                                                       if (!ExecQual(qual, econtext, true))
-                                                                       {
-                                                                               successful = false;
-                                                                               break;
-                                                                       }
-                                                                       ResetExprContext(econtext);
-                                                               }
-
-                                                               heap_endscan(scan);
-
-                                                               FreeExprContext(econtext);
-                                                               pfree(slot);
-
-                                                               if (!successful)
-                                                                       elog(ERROR, "AlterTableAddConstraint: rejected due to CHECK constraint %s", name);
-
-                                                               /*
-                                                                * Call AddRelationRawConstraints to do
-                                                                * the real adding -- It duplicates some
-                                                                * of the above, but does not check the
-                                                                * validity of the constraint against
-                                                                * tuples already in the table.
-                                                                */
-                                                               AddRelationRawConstraints(rel, NIL,
-                                                                                                         makeList1(constr));
-
-                                                               break;
-                                                       }
-                                               default:
-                                                       elog(ERROR, "ALTER TABLE / ADD CONSTRAINT is not implemented for that constraint type.");
-                                       }
-                                       break;
-                               }
-                       case T_FkConstraint:
-                               {
-                                       FkConstraint *fkconstraint = (FkConstraint *) newConstraint;
-                                       Relation        pkrel;
-                                       HeapScanDesc scan;
-                                       HeapTuple       tuple;
-                                       Trigger         trig;
-                                       List       *list;
-                                       int                     count;
-
-                                       /*
-                                        * Grab an exclusive lock on the pk table, so that
-                                        * someone doesn't delete rows out from under us.
-                                        *
-                                        * XXX wouldn't a lesser lock be sufficient?
-                                        */
-                                       pkrel = heap_openrv(fkconstraint->pktable,
-                                                                               AccessExclusiveLock);
-
-                                       /*
-                                        * Validity checks
-                                        */
-                                       if (pkrel->rd_rel->relkind != RELKIND_RELATION)
-                                               elog(ERROR, "referenced table \"%s\" not a relation",
-                                                        fkconstraint->pktable->relname);
-
-                                       if (isTempNamespace(RelationGetNamespace(pkrel)) &&
-                                               !isTempNamespace(RelationGetNamespace(rel)))
-                                               elog(ERROR, "ALTER TABLE / ADD CONSTRAINT: Unable to reference temporary table from permanent table constraint.");
+       /* should not already exist */
+       if (SearchSysCacheExists(ATTNAME,
+                                                        ObjectIdGetDatum(relid),
+                                                        PointerGetDatum(newattname),
+                                                        0, 0))
+               elog(ERROR, "renameatt: attribute \"%s\" exists", newattname);
 
-                                       /*
-                                        * First we check for limited correctness of the
-                                        * constraint.
-                                        *
-                                        * NOTE: we assume parser has already checked for
-                                        * existence of an appropriate unique index on the
-                                        * referenced relation, and that the column datatypes
-                                        * are comparable.
-                                        *
-                                        * Scan through each tuple, calling RI_FKey_check_ins
-                                        * (insert trigger) as if that tuple had just been
-                                        * inserted.  If any of those fail, it should
-                                        * elog(ERROR) and that's that.
-                                        */
-                                       MemSet(&trig, 0, sizeof(trig));
-                                       trig.tgoid = InvalidOid;
-                                       if (fkconstraint->constr_name)
-                                               trig.tgname = fkconstraint->constr_name;
-                                       else
-                                               trig.tgname = "<unknown>";
-                                       trig.tgenabled = TRUE;
-                                       trig.tgisconstraint = TRUE;
-                                       trig.tgconstrrelid = RelationGetRelid(pkrel);
-                                       trig.tgdeferrable = FALSE;
-                                       trig.tginitdeferred = FALSE;
+       namestrcpy(&(((Form_pg_attribute) GETSTRUCT(atttup))->attname),
+                          newattname);
 
-                                       trig.tgargs = (char **) palloc(
-                                        sizeof(char *) * (4 + length(fkconstraint->fk_attrs)
-                                                                          + length(fkconstraint->pk_attrs)));
+       simple_heap_update(attrelation, &atttup->t_self, atttup);
 
-                                       trig.tgargs[0] = trig.tgname;
-                                       trig.tgargs[1] = RelationGetRelationName(rel);
-                                       trig.tgargs[2] = RelationGetRelationName(pkrel);
-                                       trig.tgargs[3] = fkconstraint->match_type;
-                                       count = 4;
-                                       foreach(list, fkconstraint->fk_attrs)
-                                       {
-                                               Ident      *fk_at = lfirst(list);
+       /* keep system catalog indices current */
+       {
+               Relation        irelations[Num_pg_attr_indices];
 
-                                               trig.tgargs[count] = fk_at->name;
-                                               count += 2;
-                                       }
-                                       count = 5;
-                                       foreach(list, fkconstraint->pk_attrs)
-                                       {
-                                               Ident      *pk_at = lfirst(list);
+               CatalogOpenIndices(Num_pg_attr_indices, Name_pg_attr_indices, irelations);
+               CatalogIndexInsert(irelations, Num_pg_attr_indices, attrelation, atttup);
+               CatalogCloseIndices(Num_pg_attr_indices, irelations);
+       }
 
-                                               trig.tgargs[count] = pk_at->name;
-                                               count += 2;
-                                       }
-                                       trig.tgnargs = count - 1;
+       heap_freetuple(atttup);
 
-                                       scan = heap_beginscan(rel, false, SnapshotNow, 0, NULL);
+       /*
+        * Update column names of indexes that refer to the column being
+        * renamed.
+        */
+       indexoidlist = RelationGetIndexList(targetrelation);
 
-                                       while (HeapTupleIsValid(tuple = heap_getnext(scan, 0)))
-                                       {
-                                               /* Make a call to the check function */
+       foreach(indexoidscan, indexoidlist)
+       {
+               Oid                     indexoid = lfirsti(indexoidscan);
+               HeapTuple       indextup;
 
-                                               /*
-                                                * No parameters are passed, but we do set a
-                                                * context
-                                                */
-                                               FunctionCallInfoData fcinfo;
-                                               TriggerData trigdata;
+               /*
+                * First check to see if index is a functional index. If so, its
+                * column name is a function name and shouldn't be renamed here.
+                */
+               indextup = SearchSysCache(INDEXRELID,
+                                                                 ObjectIdGetDatum(indexoid),
+                                                                 0, 0, 0);
+               if (!HeapTupleIsValid(indextup))
+                       elog(ERROR, "renameatt: can't find index id %u", indexoid);
+               if (OidIsValid(((Form_pg_index) GETSTRUCT(indextup))->indproc))
+               {
+                       ReleaseSysCache(indextup);
+                       continue;
+               }
+               ReleaseSysCache(indextup);
 
-                                               MemSet(&fcinfo, 0, sizeof(fcinfo));
+               /*
+                * Okay, look to see if any column name of the index matches the
+                * old attribute name.
+                */
+               atttup = SearchSysCacheCopy(ATTNAME,
+                                                                       ObjectIdGetDatum(indexoid),
+                                                                       PointerGetDatum(oldattname),
+                                                                       0, 0);
+               if (!HeapTupleIsValid(atttup))
+                       continue;                       /* Nope, so ignore it */
 
-                                               /*
-                                                * We assume RI_FKey_check_ins won't look at
-                                                * flinfo...
-                                                */
+               /*
+                * Update the (copied) attribute tuple.
+                */
+               namestrcpy(&(((Form_pg_attribute) GETSTRUCT(atttup))->attname),
+                                  newattname);
 
-                                               trigdata.type = T_TriggerData;
-                                               trigdata.tg_event = TRIGGER_EVENT_INSERT | TRIGGER_EVENT_ROW;
-                                               trigdata.tg_relation = rel;
-                                               trigdata.tg_trigtuple = tuple;
-                                               trigdata.tg_newtuple = NULL;
-                                               trigdata.tg_trigger = &trig;
+               simple_heap_update(attrelation, &atttup->t_self, atttup);
 
-                                               fcinfo.context = (Node *) &trigdata;
+               /* keep system catalog indices current */
+               {
+                       Relation        irelations[Num_pg_attr_indices];
 
-                                               RI_FKey_check_ins(&fcinfo);
-                                       }
-                                       heap_endscan(scan);
+                       CatalogOpenIndices(Num_pg_attr_indices, Name_pg_attr_indices, irelations);
+                       CatalogIndexInsert(irelations, Num_pg_attr_indices, attrelation, atttup);
+                       CatalogCloseIndices(Num_pg_attr_indices, irelations);
+               }
+               heap_freetuple(atttup);
+       }
 
-                                       pfree(trig.tgargs);
+       freeList(indexoidlist);
 
-                                       heap_close(pkrel, NoLock);
+       heap_close(attrelation, RowExclusiveLock);
 
-                                       break;
-                               }
-                       default:
-                               elog(ERROR, "ALTER TABLE / ADD CONSTRAINT unable to determine type of constraint passed");
-               }
+       /*
+        * Update att name in any RI triggers associated with the relation.
+        */
+       if (targetrelation->rd_rel->reltriggers > 0)
+       {
+               /* update tgargs column reference where att is primary key */
+               update_ri_trigger_args(RelationGetRelid(targetrelation),
+                                                          oldattname, newattname,
+                                                          false, false);
+               /* update tgargs column reference where att is foreign key */
+               update_ri_trigger_args(RelationGetRelid(targetrelation),
+                                                          oldattname, newattname,
+                                                          true, false);
        }
 
-       /* Close rel, but keep lock till commit */
-       heap_close(rel, NoLock);
+       heap_close(targetrelation, NoLock); /* close rel but keep lock! */
 }
 
-
-
 /*
- * ALTER TABLE DROP CONSTRAINT
- * Note: It is legal to remove a constraint with name "" as it is possible
- * to add a constraint with name "".
- * Christopher Kings-Lynne
+ *             renamerel               - change the name of a relation
+ *
+ *             XXX - When renaming sequences, we don't bother to modify the
+ *                       sequence name that is stored within the sequence itself
+ *                       (this would cause problems with MVCC). In the future,
+ *                       the sequence name should probably be removed from the
+ *                       sequence, AFAIK there's no need for it to be there.
  */
 void
-AlterTableDropConstraint(Oid myrelid,
-                                                bool inh, const char *constrName,
-                                                int behavior)
+renamerel(Oid relid, const char *newrelname)
 {
-       Relation        rel;
-       int                     deleted;
+       Relation        targetrelation;
+       Relation        relrelation;    /* for RELATION relation */
+       HeapTuple       reltup;
+       Oid                     namespaceId;
+       char       *oldrelname;
+       char            relkind;
+       bool            relhastriggers;
+       Relation        irelations[Num_pg_class_indices];
 
        /*
-        * We don't support CASCADE yet  - in fact, RESTRICT doesn't work to
-        * the spec either!
+        * Grab an exclusive lock on the target table or index, which we will
+        * NOT release until end of transaction.
         */
-       if (behavior == CASCADE)
-               elog(ERROR, "ALTER TABLE / DROP CONSTRAINT does not support the CASCADE keyword");
+       targetrelation = relation_open(relid, AccessExclusiveLock);
+
+       oldrelname = pstrdup(RelationGetRelationName(targetrelation));
+       namespaceId = RelationGetNamespace(targetrelation);
+
+       /* Validity checks */
+       if (!allowSystemTableMods &&
+               IsSystemRelation(targetrelation))
+               elog(ERROR, "renamerel: system relation \"%s\" may not be renamed",
+                        oldrelname);
+
+       relkind = targetrelation->rd_rel->relkind;
+       relhastriggers = (targetrelation->rd_rel->reltriggers > 0);
 
        /*
-        * Acquire an exclusive lock on the target relation for the duration
-        * of the operation.
+        * Find relation's pg_class tuple, and make sure newrelname isn't in
+        * use.
         */
-       rel = heap_open(myrelid, AccessExclusiveLock);
-
-       /* Disallow DROP CONSTRAINT on views, indexes, sequences, etc */
-       if (rel->rd_rel->relkind != RELKIND_RELATION)
-               elog(ERROR, "ALTER TABLE: relation \"%s\" is not a table",
-                        RelationGetRelationName(rel));
+       relrelation = heap_openr(RelationRelationName, RowExclusiveLock);
 
-       if (!allowSystemTableMods
-               && IsSystemRelation(rel))
-               elog(ERROR, "ALTER TABLE: relation \"%s\" is a system catalog",
-                        RelationGetRelationName(rel));
+       reltup = SearchSysCacheCopy(RELOID,
+                                                               PointerGetDatum(relid),
+                                                               0, 0, 0);
+       if (!HeapTupleIsValid(reltup))
+               elog(ERROR, "renamerel: relation \"%s\" does not exist",
+                        oldrelname);
 
-       if (!pg_class_ownercheck(myrelid, GetUserId()))
-               elog(ERROR, "ALTER TABLE: \"%s\": permission denied",
-                        RelationGetRelationName(rel));
+       if (get_relname_relid(newrelname, namespaceId) != InvalidOid)
+               elog(ERROR, "renamerel: relation \"%s\" exists", newrelname);
 
        /*
-        * Since all we have is the name of the constraint, we have to look
-        * through all catalogs that could possibly contain a constraint for
-        * this relation. We also keep a count of the number of constraints
-        * removed.
+        * Update pg_class tuple with new relname.      (Scribbling on reltup is
+        * OK because it's a copy...)
         */
+       namestrcpy(&(((Form_pg_class) GETSTRUCT(reltup))->relname), newrelname);
 
-       deleted = 0;
+       simple_heap_update(relrelation, &reltup->t_self, reltup);
+
+       /* keep the system catalog indices current */
+       CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, irelations);
+       CatalogIndexInsert(irelations, Num_pg_class_indices, relrelation, reltup);
+       CatalogCloseIndices(Num_pg_class_indices, irelations);
+
+       heap_close(relrelation, NoLock);
+       heap_freetuple(reltup);
 
        /*
-        * First, we remove all CHECK constraints with the given name
+        * Also rename the associated type, if any.
         */
+       if (relkind != RELKIND_INDEX)
+               TypeRename(oldrelname, namespaceId, newrelname);
 
-       deleted += RemoveCheckConstraint(rel, constrName, inh);
+       /*
+        * Update rel name in any RI triggers associated with the relation.
+        */
+       if (relhastriggers)
+       {
+               /* update tgargs where relname is primary key */
+               update_ri_trigger_args(relid,
+                                                          oldrelname,
+                                                          newrelname,
+                                                          false, true);
+               /* update tgargs where relname is foreign key */
+               update_ri_trigger_args(relid,
+                                                          oldrelname,
+                                                          newrelname,
+                                                          true, true);
+       }
 
        /*
-        * Now we remove NULL, UNIQUE, PRIMARY KEY and FOREIGN KEY
-        * constraints.
-        *
-        * Unimplemented.
+        * Close rel, but keep exclusive lock!
         */
+       relation_close(targetrelation, NoLock);
+}
 
-       /* Close the target relation */
-       heap_close(rel, NoLock);
 
-       /* If zero constraints deleted, complain */
-       if (deleted == 0)
-               elog(ERROR, "ALTER TABLE / DROP CONSTRAINT: %s does not exist",
-                        constrName);
-       /* Otherwise if more than one constraint deleted, notify */
-       else if (deleted > 1)
-               elog(NOTICE, "Multiple constraints dropped");
+/*
+ * Given a trigger function OID, determine whether it is an RI trigger,
+ * and if so whether it is attached to PK or FK relation.
+ *
+ * XXX this probably doesn't belong here; should be exported by
+ * ri_triggers.c
+ */
+static int
+ri_trigger_type(Oid tgfoid)
+{
+       switch (tgfoid)
+       {
+               case F_RI_FKEY_CASCADE_DEL:
+               case F_RI_FKEY_CASCADE_UPD:
+               case F_RI_FKEY_RESTRICT_DEL:
+               case F_RI_FKEY_RESTRICT_UPD:
+               case F_RI_FKEY_SETNULL_DEL:
+               case F_RI_FKEY_SETNULL_UPD:
+               case F_RI_FKEY_SETDEFAULT_DEL:
+               case F_RI_FKEY_SETDEFAULT_UPD:
+               case F_RI_FKEY_NOACTION_DEL:
+               case F_RI_FKEY_NOACTION_UPD:
+                       return RI_TRIGGER_PK;
+
+               case F_RI_FKEY_CHECK_INS:
+               case F_RI_FKEY_CHECK_UPD:
+                       return RI_TRIGGER_FK;
+       }
+
+       return RI_TRIGGER_NONE;
 }
 
-/*
- * ALTER TABLE OWNER
- */
-void
-AlterTableOwner(Oid relationOid, int32 newOwnerSysId)
-{
-       Relation                target_rel;
-       Relation                class_rel;
-       HeapTuple               tuple;
-       Relation                idescs[Num_pg_class_indices];
-       Form_pg_class   tuple_class;
+/*
+ * Scan pg_trigger for RI triggers that are on the specified relation
+ * (if fk_scan is false) or have it as the tgconstrrel (if fk_scan
+ * is true).  Update RI trigger args fields matching oldname to contain
+ * newname instead.  If update_relname is true, examine the relname
+ * fields; otherwise examine the attname fields.
+ */
+static void
+update_ri_trigger_args(Oid relid,
+                                          const char *oldname,
+                                          const char *newname,
+                                          bool fk_scan,
+                                          bool update_relname)
+{
+       Relation        tgrel;
+       Relation        irel;
+       ScanKeyData skey[1];
+       IndexScanDesc idxtgscan;
+       RetrieveIndexResult idxres;
+       Datum           values[Natts_pg_trigger];
+       char            nulls[Natts_pg_trigger];
+       char            replaces[Natts_pg_trigger];
+
+       tgrel = heap_openr(TriggerRelationName, RowExclusiveLock);
+       if (fk_scan)
+               irel = index_openr(TriggerConstrRelidIndex);
+       else
+               irel = index_openr(TriggerRelidNameIndex);
+
+       ScanKeyEntryInitialize(&skey[0], 0x0,
+                                                  1,   /* column 1 of index in either case */
+                                                  F_OIDEQ,
+                                                  ObjectIdGetDatum(relid));
+       idxtgscan = index_beginscan(irel, false, 1, skey);
+
+       while ((idxres = index_getnext(idxtgscan, ForwardScanDirection)) != NULL)
+       {
+               HeapTupleData tupledata;
+               Buffer          buffer;
+               HeapTuple       tuple;
+               Form_pg_trigger pg_trigger;
+               bytea      *val;
+               bytea      *newtgargs;
+               bool            isnull;
+               int                     tg_type;
+               bool            examine_pk;
+               bool            changed;
+               int                     tgnargs;
+               int                     i;
+               int                     newlen;
+               const char *arga[RI_MAX_ARGUMENTS];
+               const char *argp;
+
+               tupledata.t_self = idxres->heap_iptr;
+               heap_fetch(tgrel, SnapshotNow, &tupledata, &buffer, idxtgscan);
+               pfree(idxres);
+               if (!tupledata.t_data)
+                       continue;
+               tuple = &tupledata;
+               pg_trigger = (Form_pg_trigger) GETSTRUCT(tuple);
+               tg_type = ri_trigger_type(pg_trigger->tgfoid);
+               if (tg_type == RI_TRIGGER_NONE)
+               {
+                       /* Not an RI trigger, forget it */
+                       ReleaseBuffer(buffer);
+                       continue;
+               }
+
+               /*
+                * It is an RI trigger, so parse the tgargs bytea.
+                *
+                * NB: we assume the field will never be compressed or moved out of
+                * line; so does trigger.c ...
+                */
+               tgnargs = pg_trigger->tgnargs;
+               val = (bytea *) fastgetattr(tuple,
+                                                                       Anum_pg_trigger_tgargs,
+                                                                       tgrel->rd_att, &isnull);
+               if (isnull || tgnargs < RI_FIRST_ATTNAME_ARGNO ||
+                       tgnargs > RI_MAX_ARGUMENTS)
+               {
+                       /* This probably shouldn't happen, but ignore busted triggers */
+                       ReleaseBuffer(buffer);
+                       continue;
+               }
+               argp = (const char *) VARDATA(val);
+               for (i = 0; i < tgnargs; i++)
+               {
+                       arga[i] = argp;
+                       argp += strlen(argp) + 1;
+               }
 
-       /* Get exclusive lock till end of transaction on the target table */
-       target_rel = heap_open(relationOid, AccessExclusiveLock);
+               /*
+                * Figure out which item(s) to look at.  If the trigger is
+                * primary-key type and attached to my rel, I should look at the
+                * PK fields; if it is foreign-key type and attached to my rel, I
+                * should look at the FK fields.  But the opposite rule holds when
+                * examining triggers found by tgconstrrel search.
+                */
+               examine_pk = (tg_type == RI_TRIGGER_PK) == (!fk_scan);
 
-       /* Get its pg_class tuple, too */
-       class_rel = heap_openr(RelationRelationName, RowExclusiveLock);
+               changed = false;
+               if (update_relname)
+               {
+                       /* Change the relname if needed */
+                       i = examine_pk ? RI_PK_RELNAME_ARGNO : RI_FK_RELNAME_ARGNO;
+                       if (strcmp(arga[i], oldname) == 0)
+                       {
+                               arga[i] = newname;
+                               changed = true;
+                       }
+               }
+               else
+               {
+                       /* Change attname(s) if needed */
+                       i = examine_pk ? RI_FIRST_ATTNAME_ARGNO + RI_KEYPAIR_PK_IDX :
+                               RI_FIRST_ATTNAME_ARGNO + RI_KEYPAIR_FK_IDX;
+                       for (; i < tgnargs; i += 2)
+                       {
+                               if (strcmp(arga[i], oldname) == 0)
+                               {
+                                       arga[i] = newname;
+                                       changed = true;
+                               }
+                       }
+               }
 
-       tuple = SearchSysCacheCopy(RELOID,
-                                                          ObjectIdGetDatum(relationOid),
-                                                          0, 0, 0);
-       if (!HeapTupleIsValid(tuple))
-               elog(ERROR, "ALTER TABLE: relation %u not found", relationOid);
-       tuple_class = (Form_pg_class) GETSTRUCT(tuple);
+               if (!changed)
+               {
+                       /* Don't need to update this tuple */
+                       ReleaseBuffer(buffer);
+                       continue;
+               }
 
-       /* Can we change the ownership of this tuple? */
-       CheckTupleType(tuple_class);
+               /*
+                * Construct modified tgargs bytea.
+                */
+               newlen = VARHDRSZ;
+               for (i = 0; i < tgnargs; i++)
+                       newlen += strlen(arga[i]) + 1;
+               newtgargs = (bytea *) palloc(newlen);
+               VARATT_SIZEP(newtgargs) = newlen;
+               newlen = VARHDRSZ;
+               for (i = 0; i < tgnargs; i++)
+               {
+                       strcpy(((char *) newtgargs) + newlen, arga[i]);
+                       newlen += strlen(arga[i]) + 1;
+               }
 
-       /*
-        * Okay, this is a valid tuple: change its ownership and
-        * write to the heap.
-        */
-       tuple_class->relowner = newOwnerSysId;
-       simple_heap_update(class_rel, &tuple->t_self, tuple);
+               /*
+                * Build modified tuple.
+                */
+               for (i = 0; i < Natts_pg_trigger; i++)
+               {
+                       values[i] = (Datum) 0;
+                       replaces[i] = ' ';
+                       nulls[i] = ' ';
+               }
+               values[Anum_pg_trigger_tgargs - 1] = PointerGetDatum(newtgargs);
+               replaces[Anum_pg_trigger_tgargs - 1] = 'r';
 
-       /* Keep the catalog indices up to date */
-       CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, idescs);
-       CatalogIndexInsert(idescs, Num_pg_class_indices, class_rel, tuple);
-       CatalogCloseIndices(Num_pg_class_indices, idescs);
+               tuple = heap_modifytuple(tuple, tgrel, values, nulls, replaces);
 
-       /*
-        * If we are operating on a table, also change the ownership of any
-        * indexes that belong to the table, as well as the table's toast
-        * table (if it has one)
-        */
-       if (tuple_class->relkind == RELKIND_RELATION ||
-               tuple_class->relkind == RELKIND_TOASTVALUE)
-       {
-               List *index_oid_list, *i;
+               /*
+                * Now we can release hold on original tuple.
+                */
+               ReleaseBuffer(buffer);
 
-               /* Find all the indexes belonging to this relation */
-               index_oid_list = RelationGetIndexList(target_rel);
+               /*
+                * Update pg_trigger and its indexes
+                */
+               simple_heap_update(tgrel, &tuple->t_self, tuple);
 
-               /* For each index, recursively change its ownership */
-               foreach(i, index_oid_list)
                {
-                       AlterTableOwner(lfirsti(i), newOwnerSysId);
+                       Relation        irelations[Num_pg_attr_indices];
+
+                       CatalogOpenIndices(Num_pg_trigger_indices, Name_pg_trigger_indices, irelations);
+                       CatalogIndexInsert(irelations, Num_pg_trigger_indices, tgrel, tuple);
+                       CatalogCloseIndices(Num_pg_trigger_indices, irelations);
                }
 
-               freeList(index_oid_list);
+               /* free up our scratch memory */
+               pfree(newtgargs);
+               heap_freetuple(tuple);
        }
 
-       if (tuple_class->relkind == RELKIND_RELATION)
-       {
-               /* If it has a toast table, recurse to change its ownership */
-               if (tuple_class->reltoastrelid != InvalidOid)
-               {
-                       AlterTableOwner(tuple_class->reltoastrelid, newOwnerSysId);
-               }
-       }
+       index_endscan(idxtgscan);
+       index_close(irel);
 
-       heap_freetuple(tuple);
-       heap_close(class_rel, RowExclusiveLock);
-       heap_close(target_rel, NoLock);
-}
+       heap_close(tgrel, RowExclusiveLock);
 
-static void
-CheckTupleType(Form_pg_class tuple_class)
-{
-       switch (tuple_class->relkind)
-       {
-               case RELKIND_RELATION:
-               case RELKIND_INDEX:
-               case RELKIND_VIEW:
-               case RELKIND_SEQUENCE:
-               case RELKIND_TOASTVALUE:
-                       /* ok to change owner */
-                       break;
-               default:
-                       elog(ERROR, "ALTER TABLE: relation \"%s\" is not a table, TOAST table, index, view, or sequence",
-                                NameStr(tuple_class->relname));
-       }
+       /*
+        * Increment cmd counter to make updates visible; this is needed in
+        * case the same tuple has to be updated again by next pass (can
+        * happen in case of a self-referential FK relationship).
+        */
+       CommandCounterIncrement();
 }
 
-/*
- * ALTER TABLE CREATE TOAST TABLE
+
+/* ----------------
+ *             AlterTableAddColumn
+ *             (formerly known as PerformAddAttribute)
+ *
+ *             adds an additional attribute to a relation
+ *
+ *             Adds attribute field(s) to a relation.  Each new attribute
+ *             is given attnums in sequential order and is added to the
+ *             ATTRIBUTE relation.  If the AMI fails, defunct tuples will
+ *             remain in the ATTRIBUTE relation for later vacuuming.
+ *             Later, there may be some reserved attribute names???
+ *
+ *             (If needed, can instead use elog to handle exceptions.)
+ *
+ *             Note:
+ *                             Initial idea of ordering the tuple attributes so that all
+ *             the variable length domains occured last was scratched.  Doing
+ *             so would not speed access too much (in general) and would create
+ *             many complications in formtuple, heap_getattr, and addattribute.
+ *
+ *             scan attribute catalog for name conflict (within rel)
+ *             scan type catalog for absence of data type (if not arg)
+ *             create attnum magically???
+ *             create attribute tuple
+ *             insert attribute in attribute catalog
+ *             modify reldesc
+ *             create new relation tuple
+ *             insert new relation in relation catalog
+ *             delete original relation from relation catalog
+ * ----------------
  */
 void
-AlterTableCreateToastTable(Oid relOid, bool silent)
+AlterTableAddColumn(Oid myrelid,
+                                       bool inherits,
+                                       ColumnDef *colDef)
 {
-       Relation        rel;
+       Relation        rel,
+                               pgclass,
+                               attrdesc;
        HeapTuple       reltup;
-       HeapTupleData classtuple;
-       TupleDesc       tupdesc;
-       Relation        class_rel;
-       Buffer          buffer;
-       Relation        ridescs[Num_pg_class_indices];
-       Oid                     toast_relid;
-       Oid                     toast_idxid;
-       char            toast_relname[NAMEDATALEN];
-       char            toast_idxname[NAMEDATALEN];
-       IndexInfo  *indexInfo;
-       Oid                     classObjectId[2];
+       HeapTuple       newreltup;
+       HeapTuple       attributeTuple;
+       Form_pg_attribute attribute;
+       FormData_pg_attribute attributeD;
+       int                     i;
+       int                     minattnum,
+                               maxatts;
+       HeapTuple       typeTuple;
+       Form_pg_type tform;
+       int                     attndims;
 
        /*
         * Grab an exclusive lock on the target table, which we will NOT
         * release until end of transaction.
         */
-       rel = heap_open(relOid, AccessExclusiveLock);
+       rel = heap_open(myrelid, AccessExclusiveLock);
 
        if (rel->rd_rel->relkind != RELKIND_RELATION)
                elog(ERROR, "ALTER TABLE: relation \"%s\" is not a table",
                         RelationGetRelationName(rel));
 
-       if (!pg_class_ownercheck(relOid, GetUserId()))
+       /*
+        * permissions checking.  this would normally be done in utility.c,
+        * but this particular routine is recursive.
+        *
+        * normally, only the owner of a class can change its schema.
+        */
+       if (!allowSystemTableMods
+               && IsSystemRelation(rel))
+               elog(ERROR, "ALTER TABLE: relation \"%s\" is a system catalog",
+                        RelationGetRelationName(rel));
+       if (!pg_class_ownercheck(myrelid, GetUserId()))
                elog(ERROR, "ALTER TABLE: \"%s\": permission denied",
                         RelationGetRelationName(rel));
 
        /*
-        * lock the pg_class tuple for update (is that really needed?)
+        * 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.
         */
-       class_rel = heap_openr(RelationRelationName, RowExclusiveLock);
+       if (inherits)
+       {
+               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);
+
+                       if (childrelid == myrelid)
+                               continue;
+
+                       AlterTableAddColumn(childrelid, false, colDef);
+               }
+       }
+
+       /*
+        * 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 ... SET NOT NULL.");
+
+       pgclass = heap_openr(RelationRelationName, RowExclusiveLock);
 
        reltup = SearchSysCache(RELOID,
-                                                       ObjectIdGetDatum(relOid),
+                                                       ObjectIdGetDatum(myrelid),
                                                        0, 0, 0);
        if (!HeapTupleIsValid(reltup))
                elog(ERROR, "ALTER TABLE: relation \"%s\" not found",
                         RelationGetRelationName(rel));
-       classtuple.t_self = reltup->t_self;
-       ReleaseSysCache(reltup);
 
-       switch (heap_mark4update(class_rel, &classtuple, &buffer))
-       {
-               case HeapTupleSelfUpdated:
-               case HeapTupleMayBeUpdated:
-                       break;
-               default:
-                       elog(ERROR, "couldn't lock pg_class tuple");
-       }
-       reltup = heap_copytuple(&classtuple);
-       ReleaseBuffer(buffer);
+       if (SearchSysCacheExists(ATTNAME,
+                                                        ObjectIdGetDatum(myrelid),
+                                                        PointerGetDatum(colDef->colname),
+                                                        0, 0))
+               elog(ERROR, "ALTER TABLE: column name \"%s\" already exists in table \"%s\"",
+                        colDef->colname, RelationGetRelationName(rel));
 
-       /*
-        * Is it already toasted?
-        */
-       if (((Form_pg_class) GETSTRUCT(reltup))->reltoastrelid != InvalidOid)
-       {
-               if (silent)
-               {
-                       heap_close(rel, NoLock);
-                       heap_close(class_rel, NoLock);
-                       heap_freetuple(reltup);
-                       return;
-               }
+       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;
 
-               elog(ERROR, "ALTER TABLE: relation \"%s\" already has a toast table",
-                        RelationGetRelationName(rel));
-       }
+       attrdesc = heap_openr(AttributeRelationName, RowExclusiveLock);
 
-       /*
-        * Check to see whether the table actually needs a TOAST table.
-        */
-       if (!needs_toast_table(rel))
-       {
-               if (silent)
-               {
-                       heap_close(rel, NoLock);
-                       heap_close(class_rel, NoLock);
-                       heap_freetuple(reltup);
-                       return;
-               }
+       if (colDef->typename->arrayBounds)
+               attndims = length(colDef->typename->arrayBounds);
+       else
+               attndims = 0;
 
-               elog(ERROR, "ALTER TABLE: relation \"%s\" does not need a toast table",
-                        RelationGetRelationName(rel));
-       }
+       typeTuple = typenameType(colDef->typename);
+       tform = (Form_pg_type) GETSTRUCT(typeTuple);
 
-       /*
-        * Create the toast table and its index
-        */
-       sprintf(toast_relname, "pg_toast_%u", relOid);
-       sprintf(toast_idxname, "pg_toast_%u_index", relOid);
+       attributeTuple = heap_addheader(Natts_pg_attribute,
+                                                                       ATTRIBUTE_TUPLE_SIZE,
+                                                                       (void *) &attributeD);
 
-       /* this is pretty painful...  need a tuple descriptor */
-       tupdesc = CreateTemplateTupleDesc(3);
-       TupleDescInitEntry(tupdesc, (AttrNumber) 1,
-                                          "chunk_id",
-                                          OIDOID,
-                                          -1, 0, false);
-       TupleDescInitEntry(tupdesc, (AttrNumber) 2,
-                                          "chunk_seq",
-                                          INT4OID,
-                                          -1, 0, false);
-       TupleDescInitEntry(tupdesc, (AttrNumber) 3,
-                                          "chunk_data",
-                                          BYTEAOID,
-                                          -1, 0, false);
+       attribute = (Form_pg_attribute) GETSTRUCT(attributeTuple);
 
-       /*
-        * Ensure that the toast table doesn't itself get toasted, or we'll be
-        * toast :-(.  This is essential for chunk_data because type bytea is
-        * toastable; hit the other two just to be sure.
-        */
-       tupdesc->attrs[0]->attstorage = 'p';
-       tupdesc->attrs[1]->attstorage = 'p';
-       tupdesc->attrs[2]->attstorage = 'p';
+       attribute->attrelid = myrelid;
+       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);
 
-       /*
-        * Note: the toast relation is placed in the regular pg_toast namespace
-        * even if its master relation is a temp table.  There cannot be any
-        * naming collision, and the toast rel will be destroyed when its master
-        * is, so there's no need to handle the toast rel as temp.
-        */
-       toast_relid = heap_create_with_catalog(toast_relname,
-                                                                                  PG_TOAST_NAMESPACE,
-                                                                                  tupdesc,
-                                                                                  RELKIND_TOASTVALUE,
-                                                                                  false,
-                                                                                  true);
+       ReleaseSysCache(typeTuple);
 
-       /* make the toast relation visible, else index creation will fail */
-       CommandCounterIncrement();
+       heap_insert(attrdesc, attributeTuple);
+
+       /* Update indexes on pg_attribute */
+       if (RelationGetForm(attrdesc)->relhasindex)
+       {
+               Relation        idescs[Num_pg_attr_indices];
+
+               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, RowExclusiveLock);
 
        /*
-        * Create unique index on chunk_id, chunk_seq.
-        *
-        * NOTE: the tuple toaster could actually function with a single-column
-        * index on chunk_id only.      However, it couldn't be unique then.  We
-        * want it to be unique as a check against the possibility of
-        * duplicate TOAST chunk OIDs.  Too, the index might be a little more
-        * efficient this way, since btree isn't all that happy with large
-        * numbers of equal keys.
+        * Update number of attributes in pg_class tuple
         */
+       newreltup = heap_copytuple(reltup);
 
-       indexInfo = makeNode(IndexInfo);
-       indexInfo->ii_NumIndexAttrs = 2;
-       indexInfo->ii_NumKeyAttrs = 2;
-       indexInfo->ii_KeyAttrNumbers[0] = 1;
-       indexInfo->ii_KeyAttrNumbers[1] = 2;
-       indexInfo->ii_Predicate = NIL;
-       indexInfo->ii_FuncOid = InvalidOid;
-       indexInfo->ii_Unique = true;
+       ((Form_pg_class) GETSTRUCT(newreltup))->relnatts = maxatts;
+       simple_heap_update(pgclass, &newreltup->t_self, newreltup);
 
-       classObjectId[0] = OID_BTREE_OPS_OID;
-       classObjectId[1] = INT4_BTREE_OPS_OID;
+       /* keep catalog indices current */
+       if (RelationGetForm(pgclass)->relhasindex)
+       {
+               Relation        ridescs[Num_pg_class_indices];
 
-       toast_idxid = index_create(toast_relid, toast_idxname, indexInfo,
-                                                          BTREE_AM_OID, classObjectId,
-                                                          true, true);
+               CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, ridescs);
+               CatalogIndexInsert(ridescs, Num_pg_class_indices, pgclass, newreltup);
+               CatalogCloseIndices(Num_pg_class_indices, ridescs);
+       }
 
-       /*
-        * Update toast rel's pg_class entry to show that it has an index. The
-        * index OID is stored into the reltoastidxid field for easy access by
-        * the tuple toaster.
-        */
-       setRelhasindex(toast_relid, true, true, toast_idxid);
+       heap_freetuple(newreltup);
+       ReleaseSysCache(reltup);
 
-       /*
-        * Store the toast table's OID in the parent relation's tuple
-        */
-       ((Form_pg_class) GETSTRUCT(reltup))->reltoastrelid = toast_relid;
-       simple_heap_update(class_rel, &reltup->t_self, reltup);
+       heap_close(pgclass, NoLock);
+
+       heap_close(rel, NoLock);        /* close rel but keep lock! */
 
        /*
-        * Keep catalog indices current
+        * Make our catalog updates visible for subsequent steps.
         */
-       CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, ridescs);
-       CatalogIndexInsert(ridescs, Num_pg_class_indices, class_rel, reltup);
-       CatalogCloseIndices(Num_pg_class_indices, ridescs);
-
-       heap_freetuple(reltup);
+       CommandCounterIncrement();
 
        /*
-        * Close relations and make changes visible
+        * 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.
         */
-       heap_close(class_rel, NoLock);
-       heap_close(rel, NoLock);
+       if (colDef->constraints != NIL)
+       {
+               rel = heap_open(myrelid, AccessExclusiveLock);
+               AddRelationRawConstraints(rel, NIL, colDef->constraints);
+               heap_close(rel, NoLock);
+       }
 
-       CommandCounterIncrement();
+       /*
+        * Automatically create the secondary relation for TOAST if it
+        * formerly had no such but now has toastable attributes.
+        */
+       AlterTableCreateToastTable(myrelid, true);
 }
 
 /*
- * Check to see whether the table needs a TOAST table. It does only if
- * (1) there are any toastable attributes, and (2) the maximum length
- * of a tuple could exceed TOAST_TUPLE_THRESHOLD.  (We don't want to
- * create a toast table for something like "f1 varchar(20)".)
+ * ALTER TABLE ALTER COLUMN DROP NOT NULL
  */
-static bool
-needs_toast_table(Relation rel)
+void
+AlterTableAlterColumnDropNotNull(Oid myrelid,
+                                                                bool inh, const char *colName)
 {
-       int32           data_length = 0;
-       bool            maxlength_unknown = false;
-       bool            has_toastable_attrs = false;
-       TupleDesc       tupdesc;
-       Form_pg_attribute *att;
-       int32           tuple_length;
-       int                     i;
+       Relation        rel;
+       HeapTuple       tuple;
+       AttrNumber      attnum;
+       Relation        attr_rel;
+       List            *indexoidlist;
+       List            *indexoidscan;
 
-       tupdesc = rel->rd_att;
-       att = tupdesc->attrs;
+       rel = heap_open(myrelid, AccessExclusiveLock);
 
-       for (i = 0; i < tupdesc->natts; i++)
+       if (rel->rd_rel->relkind != RELKIND_RELATION)
+               elog(ERROR, "ALTER TABLE: relation \"%s\" is not a table",
+                        RelationGetRelationName(rel));
+
+       if (!allowSystemTableMods
+               && IsSystemRelation(rel))
+               elog(ERROR, "ALTER TABLE: relation \"%s\" is a system catalog",
+                        RelationGetRelationName(rel));
+
+       if (!pg_class_ownercheck(myrelid, GetUserId()))
+               elog(ERROR, "ALTER TABLE: \"%s\": permission denied",
+                        RelationGetRelationName(rel));
+
+       /*
+        * Propagate to children if desired
+        */
+       if (inh)
        {
-               data_length = att_align(data_length, att[i]->attlen, att[i]->attalign);
-               if (att[i]->attlen >= 0)
-               {
-                       /* Fixed-length types are never toastable */
-                       data_length += att[i]->attlen;
-               }
-               else
+               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)
                {
-                       int32           maxlen = type_maximum_size(att[i]->atttypid,
-                                                                                                  att[i]->atttypmod);
+                       Oid                     childrelid = lfirsti(child);
 
-                       if (maxlen < 0)
-                               maxlength_unknown = true;
-                       else
-                               data_length += maxlen;
-                       if (att[i]->attstorage != 'p')
-                               has_toastable_attrs = true;
+                       if (childrelid == myrelid)
+                               continue;
+                       AlterTableAlterColumnDropNotNull(childrelid,
+                                                                                        false, colName);
                }
        }
-       if (!has_toastable_attrs)
-               return false;                   /* nothing to toast? */
-       if (maxlength_unknown)
-               return true;                    /* any unlimited-length attrs? */
-       tuple_length = MAXALIGN(offsetof(HeapTupleHeaderData, t_bits) +
-                                                       BITMAPLEN(tupdesc->natts)) +
-               MAXALIGN(data_length);
-       return (tuple_length > TOAST_TUPLE_THRESHOLD);
-}
 
-
-/* ----------------------------------------------------------------
- *             DefineRelation
- *                             Creates a new relation.
- *
- * If successful, returns the OID of the new relation.
- * ----------------------------------------------------------------
- */
-Oid
-DefineRelation(CreateStmt *stmt, char relkind)
-{
-       char            relname[NAMEDATALEN];
-       Oid                     namespaceId;
-       List       *schema = stmt->tableElts;
-       int                     numberOfAttributes;
-       Oid                     relationId;
-       Relation        rel;
-       TupleDesc       descriptor;
-       List       *inheritOids;
-       List       *old_constraints;
-       bool            parentHasOids;
-       List       *rawDefaults;
-       List       *listptr;
-       int                     i;
-       AttrNumber      attnum;
+       /* -= now do the thing on this relation =- */
 
        /*
-        * Truncate relname to appropriate length (probably a waste of time,
-        * as parser should have done this already).
+        * get the number of the attribute
         */
-       StrNCpy(relname, stmt->relation->relname, NAMEDATALEN);
+       tuple = SearchSysCache(ATTNAME,
+                                                  ObjectIdGetDatum(myrelid),
+                                                  PointerGetDatum(colName),
+                                                  0, 0);
+       if (!HeapTupleIsValid(tuple))
+               elog(ERROR, "ALTER TABLE: relation \"%s\" has no column \"%s\"",
+                        RelationGetRelationName(rel), colName);
 
-       /*
-        * Look up the namespace in which we are supposed to create the
-        * relation.
-        */
-       namespaceId = RangeVarGetCreationNamespace(stmt->relation);
+       attnum = ((Form_pg_attribute) GETSTRUCT(tuple))->attnum;
+       ReleaseSysCache(tuple);
 
-       /*
-        * Merge domain attributes into the known columns before processing table
-        * inheritance.  Otherwise we risk adding double constraints to a
-        * domain-type column that's inherited.
-        */
-       schema = MergeDomainAttributes(schema);
+       /* Prevent them from altering a system attribute */
+       if (attnum < 0)
+               elog(ERROR, "ALTER TABLE: Cannot alter system attribute \"%s\"",
+                        colName);
 
        /*
-        * Look up inheritance ancestors and generate relation schema,
-        * including inherited attributes.
+        * Check that the attribute is not in a primary key
         */
-       schema = MergeAttributes(schema, stmt->inhRelations,
-                                                        stmt->relation->istemp,
-                                                        &inheritOids, &old_constraints, &parentHasOids);
-
-       numberOfAttributes = length(schema);
-       if (numberOfAttributes <= 0)
-               elog(ERROR, "DefineRelation: please inherit from a relation or define an attribute");
 
-       /*
-        * Create a relation descriptor from the relation schema and create
-        * the relation.  Note that in this stage only inherited (pre-cooked)
-        * defaults and constraints will be included into the new relation.
-        * (BuildDescForRelation takes care of the inherited defaults, but we
-        * have to copy inherited constraints here.)
-        */
-       descriptor = BuildDescForRelation(schema);
+       /* Loop over all indices on the relation */
+       indexoidlist = RelationGetIndexList(rel);
 
-       if (old_constraints != NIL)
+       foreach(indexoidscan, indexoidlist)
        {
-               ConstrCheck *check = (ConstrCheck *) palloc(length(old_constraints) *
-                                                                                                       sizeof(ConstrCheck));
-               int                     ncheck = 0;
-
-               foreach(listptr, old_constraints)
-               {
-                       Constraint *cdef = (Constraint *) lfirst(listptr);
+               Oid             indexoid = lfirsti(indexoidscan);
+               HeapTuple       indexTuple;
+               Form_pg_index   indexStruct;
+               int             i;
 
-                       if (cdef->contype != CONSTR_CHECK)
-                               continue;
+               indexTuple = SearchSysCache(INDEXRELID,
+                                                                       ObjectIdGetDatum(indexoid),
+                                                                       0, 0, 0);
+               if (!HeapTupleIsValid(indexTuple))
+                       elog(ERROR, "ALTER TABLE: Index %u not found",
+                                indexoid);
+               indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
 
-                       if (cdef->name != NULL)
-                       {
-                               for (i = 0; i < ncheck; i++)
-                               {
-                                       if (strcmp(check[i].ccname, cdef->name) == 0)
-                                               elog(ERROR, "Duplicate CHECK constraint name: '%s'",
-                                                        cdef->name);
-                               }
-                               check[ncheck].ccname = cdef->name;
-                       }
-                       else
-                       {
-                               check[ncheck].ccname = (char *) palloc(NAMEDATALEN);
-                               snprintf(check[ncheck].ccname, NAMEDATALEN, "$%d", ncheck + 1);
-                       }
-                       Assert(cdef->raw_expr == NULL && cdef->cooked_expr != NULL);
-                       check[ncheck].ccbin = pstrdup(cdef->cooked_expr);
-                       ncheck++;
-               }
-               if (ncheck > 0)
+               /* If the index is not a primary key, skip the check */
+               if (indexStruct->indisprimary)
                {
-                       if (descriptor->constr == NULL)
+                       /*
+                        * Loop over each attribute in the primary key and
+                        * see if it matches the to-be-altered attribute
+                        */
+                       for (i = 0; i < INDEX_MAX_KEYS &&
+                                        indexStruct->indkey[i] != InvalidAttrNumber; i++)
                        {
-                               descriptor->constr = (TupleConstr *) palloc(sizeof(TupleConstr));
-                               descriptor->constr->defval = NULL;
-                               descriptor->constr->num_defval = 0;
-                               descriptor->constr->has_not_null = false;
+                               if (indexStruct->indkey[i] == attnum)
+                                       elog(ERROR, "ALTER TABLE: Attribute \"%s\" is in a primary key", colName);
                        }
-                       descriptor->constr->num_check = ncheck;
-                       descriptor->constr->check = check;
                }
-       }
-
-       relationId = heap_create_with_catalog(relname,
-                                                                                 namespaceId,
-                                                                                 descriptor,
-                                                                                 relkind,
-                                                                                 stmt->hasoids || parentHasOids,
-                                                                                 allowSystemTableMods);
 
-       StoreCatalogInheritance(relationId, inheritOids);
+               ReleaseSysCache(indexTuple);
+       }
 
-       /*
-        * We must bump the command counter to make the newly-created relation
-        * tuple visible for opening.
-        */
-       CommandCounterIncrement();
+       freeList(indexoidlist);
 
        /*
-        * Open the new relation and acquire exclusive lock on it.      This isn't
-        * really necessary for locking out other backends (since they can't
-        * see the new rel anyway until we commit), but it keeps the lock
-        * manager from complaining about deadlock risks.
+        * Okay, actually perform the catalog change
         */
-       rel = heap_open(relationId, AccessExclusiveLock);
+       attr_rel = heap_openr(AttributeRelationName, RowExclusiveLock);
 
-       /*
-        * Now add any newly specified column default values and CHECK
-        * constraints to the new relation.  These are passed to us in the
-        * form of raw parsetrees; we need to transform them to executable
-        * expression trees before they can be added. The most convenient way
-        * to do that is to apply the parser's transformExpr routine, but
-        * transformExpr doesn't work unless we have a pre-existing relation.
-        * So, the transformation has to be postponed to this final step of
-        * CREATE TABLE.
-        *
-        * First, scan schema to find new column defaults.
-        */
-       rawDefaults = NIL;
-       attnum = 0;
+       tuple = SearchSysCacheCopy(ATTNAME,
+                                                          ObjectIdGetDatum(myrelid),
+                                                          PointerGetDatum(colName),
+                                                          0, 0);
+       if (!HeapTupleIsValid(tuple)) /* shouldn't happen */
+               elog(ERROR, "ALTER TABLE: relation \"%s\" has no column \"%s\"",
+                        RelationGetRelationName(rel), colName);
 
-       foreach(listptr, schema)
-       {
-               ColumnDef  *colDef = lfirst(listptr);
-               RawColumnDefault *rawEnt;
+       ((Form_pg_attribute) GETSTRUCT(tuple))->attnotnull = FALSE;
 
-               attnum++;
+       simple_heap_update(attr_rel, &tuple->t_self, tuple);
 
-               if (colDef->raw_default == NULL)
-                       continue;
-               Assert(colDef->cooked_default == NULL);
+       /* keep the system catalog indices current */
+       if (RelationGetForm(attr_rel)->relhasindex)
+       {
+               Relation        idescs[Num_pg_attr_indices];
 
-               rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
-               rawEnt->attnum = attnum;
-               rawEnt->raw_default = colDef->raw_default;
-               rawDefaults = lappend(rawDefaults, rawEnt);
+               CatalogOpenIndices(Num_pg_attr_indices, Name_pg_attr_indices, idescs);
+               CatalogIndexInsert(idescs, Num_pg_attr_indices, attr_rel, tuple);
+               CatalogCloseIndices(Num_pg_attr_indices, idescs);
        }
 
-       /*
-        * Parse and add the defaults/constraints, if any.
-        */
-       if (rawDefaults || stmt->constraints)
-               AddRelationRawConstraints(rel, rawDefaults, stmt->constraints);
+       heap_close(attr_rel, RowExclusiveLock);
 
-       /*
-        * Clean up.  We keep lock on new relation (although it shouldn't be
-        * visible to anyone else anyway, until commit).
-        */
        heap_close(rel, NoLock);
-
-       return relationId;
-}
-
-/*
- * RemoveRelation
- *             Deletes a relation.
- *
- * Exceptions:
- *             BadArg if name is invalid.
- *
- * Note:
- *             If the relation has indices defined on it, then the index relations
- * themselves will be destroyed, too.
- */
-void
-RemoveRelation(const RangeVar *relation)
-{
-       Oid                     relOid;
-
-       relOid = RangeVarGetRelid(relation, false);
-       heap_drop_with_catalog(relOid, allowSystemTableMods);
 }
 
 /*
- * TruncateRelation
- *                               Removes all the rows from a relation
- *
- * Exceptions:
- *                               BadArg if name is invalid
- *
- * Note:
- *                               Rows are removed, indices are truncated and reconstructed.
+ * ALTER TABLE ALTER COLUMN SET NOT NULL
  */
 void
-TruncateRelation(const RangeVar *relation)
+AlterTableAlterColumnSetNotNull(Oid myrelid,
+                                                               bool inh, const char *colName)
 {
        Relation        rel;
-       Oid                     relid;
-
-       /* Grab exclusive lock in preparation for truncate */
-       rel = heap_openrv(relation, AccessExclusiveLock);
-       relid = RelationGetRelid(rel);
+       HeapTuple       tuple;
+       AttrNumber      attnum;
+       Relation        attr_rel;
+       HeapScanDesc    scan;
+       TupleDesc       tupdesc;
 
-       if (rel->rd_rel->relkind == RELKIND_SEQUENCE)
-               elog(ERROR, "TRUNCATE cannot be used on sequences. '%s' is a sequence",
-                        RelationGetRelationName(rel));
+       rel = heap_open(myrelid, AccessExclusiveLock);
 
-       if (rel->rd_rel->relkind == RELKIND_VIEW)
-               elog(ERROR, "TRUNCATE cannot be used on views. '%s' is a view",
+       if (rel->rd_rel->relkind != RELKIND_RELATION)
+               elog(ERROR, "ALTER TABLE: relation \"%s\" is not a table",
                         RelationGetRelationName(rel));
 
-       if (!allowSystemTableMods && IsSystemRelation(rel))
-               elog(ERROR, "TRUNCATE cannot be used on system tables. '%s' is a system table",
+       if (!allowSystemTableMods
+               && IsSystemRelation(rel))
+               elog(ERROR, "ALTER TABLE: relation \"%s\" is a system catalog",
                         RelationGetRelationName(rel));
 
-       if (!pg_class_ownercheck(relid, GetUserId()))
-               elog(ERROR, "you do not own relation \"%s\"",
+       if (!pg_class_ownercheck(myrelid, GetUserId()))
+               elog(ERROR, "ALTER TABLE: \"%s\": permission denied",
                         RelationGetRelationName(rel));
 
-       /* Keep the lock until transaction commit */
-       heap_close(rel, NoLock);
-
-       heap_truncate(relid);
-}
-
-
-/*
- * MergeDomainAttributes
- *      Returns a new table schema with the constraints, types, and other
- *      attributes of domains resolved for fields using a domain as
- *      their type.
- */
-static List *
-MergeDomainAttributes(List *schema)
-{
-       List       *entry;
-
        /*
-        * Loop through the table elements supplied. These should
-        * never include inherited domains else they'll be
-        * double (or more) processed.
+        * Propagate to children if desired
         */
-       foreach(entry, schema)
+       if (inh)
        {
-               ColumnDef  *coldef = lfirst(entry);
-               HeapTuple  tuple;
-               Form_pg_type typeTup;
+               List       *child,
+                                  *children;
 
-               tuple = typenameType(coldef->typename);
-               typeTup = (Form_pg_type) GETSTRUCT(tuple);
+               /* this routine is actually in the planner */
+               children = find_all_inheritors(myrelid);
 
-               if (typeTup->typtype == 'd')
+               /*
+                * 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)
                {
-                       /* Force the column to have the correct typmod. */
-                       coldef->typename->typmod = typeTup->typtypmod;
-                       /* XXX more to do here? */
-               }
-
-               /* Enforce type NOT NULL || column definition NOT NULL -> NOT NULL */
-               /* Currently only used for domains, but could be valid for all */
-               coldef->is_not_null |= typeTup->typnotnull;
+                       Oid                     childrelid = lfirsti(child);
 
-               ReleaseSysCache(tuple);
+                       if (childrelid == myrelid)
+                               continue;
+                       AlterTableAlterColumnSetNotNull(childrelid,
+                                                                                       false, colName);
+               }
        }
 
-       return schema;
-}
-
-/*----------
- * MergeAttributes
- *             Returns new schema given initial schema and superclasses.
- *
- * Input arguments:
- * 'schema' is the column/attribute definition for the table. (It's a list
- *             of ColumnDef's.) It is destructively changed.
- * 'supers' is a list of names (as RangeVar nodes) of parent relations.
- * 'istemp' is TRUE if we are creating a temp relation.
- *
- * Output arguments:
- * 'supOids' receives an integer list of the OIDs of the parent relations.
- * 'supconstr' receives a list of constraints belonging to the parents,
- *             updated as necessary to be valid for the child.
- * 'supHasOids' is set TRUE if any parent has OIDs, else it is set FALSE.
- *
- * Return value:
- * Completed schema list.
- *
- * Notes:
- *       The order in which the attributes are inherited is very important.
- *       Intuitively, the inherited attributes should come first. If a table
- *       inherits from multiple parents, the order of those attributes are
- *       according to the order of the parents specified in CREATE TABLE.
- *
- *       Here's an example:
- *
- *             create table person (name text, age int4, location point);
- *             create table emp (salary int4, manager text) inherits(person);
- *             create table student (gpa float8) inherits (person);
- *             create table stud_emp (percent int4) inherits (emp, student);
- *
- *       The order of the attributes of stud_emp is:
- *
- *                                                     person {1:name, 2:age, 3:location}
- *                                                     /        \
- *                        {6:gpa}      student   emp {4:salary, 5:manager}
- *                                                     \        /
- *                                                stud_emp {7:percent}
- *
- *        If the same attribute name appears multiple times, then it appears
- *        in the result table in the proper location for its first appearance.
- *
- *        Constraints (including NOT NULL constraints) for the child table
- *        are the union of all relevant constraints, from both the child schema
- *        and parent tables.
- *
- *        The default value for a child column is defined as:
- *             (1) If the child schema specifies a default, that value is used.
- *             (2) If neither the child nor any parent specifies a default, then
- *                     the column will not have a default.
- *             (3) If conflicting defaults are inherited from different parents
- *                     (and not overridden by the child), an error is raised.
- *             (4) Otherwise the inherited default is used.
- *             Rule (3) is new in Postgres 7.1; in earlier releases you got a
- *             rather arbitrary choice of which parent default to use.
- *----------
- */
-static List *
-MergeAttributes(List *schema, List *supers, bool istemp,
-                               List **supOids, List **supconstr, bool *supHasOids)
-{
-       List       *entry;
-       List       *inhSchema = NIL;
-       List       *parentOids = NIL;
-       List       *constraints = NIL;
-       bool            parentHasOids = false;
-       bool            have_bogus_defaults = false;
-       char       *bogus_marker = "Bogus!";            /* marks conflicting
-                                                                                                * defaults */
-       int                     child_attno;
-
+       /* -= now do the thing on this relation =- */
+
        /*
-        * Check for duplicate names in the explicit list of attributes.
-        *
-        * Although we might consider merging such entries in the same way that
-        * we handle name conflicts for inherited attributes, it seems to make
-        * more sense to assume such conflicts are errors.
+        * get the number of the attribute
         */
-       foreach(entry, schema)
-       {
-               ColumnDef  *coldef = lfirst(entry);
-               List       *rest;
+       tuple = SearchSysCache(ATTNAME,
+                                                  ObjectIdGetDatum(myrelid),
+                                                  PointerGetDatum(colName),
+                                                  0, 0);
+       if (!HeapTupleIsValid(tuple))
+               elog(ERROR, "ALTER TABLE: relation \"%s\" has no column \"%s\"",
+                        RelationGetRelationName(rel), colName);
 
-               foreach(rest, lnext(entry))
-               {
-                       ColumnDef  *restdef = lfirst(rest);
+       attnum = ((Form_pg_attribute) GETSTRUCT(tuple))->attnum;
+       ReleaseSysCache(tuple);
 
-                       if (strcmp(coldef->colname, restdef->colname) == 0)
-                               elog(ERROR, "CREATE TABLE: attribute \"%s\" duplicated",
-                                        coldef->colname);
-               }
-       }
+       /* Prevent them from altering a system attribute */
+       if (attnum < 0)
+               elog(ERROR, "ALTER TABLE: Cannot alter system attribute \"%s\"",
+                        colName);
 
        /*
-        * Scan the parents left-to-right, and merge their attributes to form
-        * a list of inherited attributes (inhSchema).  Also check to see if
-        * we need to inherit an OID column.
+        * Perform a scan to ensure that there are no NULL
+        * values already in the relation
         */
-       child_attno = 0;
-       foreach(entry, supers)
-       {
-               RangeVar   *parent = (RangeVar *) lfirst(entry);
-               Relation        relation;
-               TupleDesc       tupleDesc;
-               TupleConstr *constr;
-               AttrNumber *newattno;
-               AttrNumber      parent_attno;
+       tupdesc = RelationGetDescr(rel);
 
-               relation = heap_openrv(parent, AccessShareLock);
+       scan = heap_beginscan(rel, false, SnapshotNow, 0, NULL);
 
-               if (relation->rd_rel->relkind != RELKIND_RELATION)
-                       elog(ERROR, "CREATE TABLE: inherited relation \"%s\" is not a table",
-                                parent->relname);
-               /* Permanent rels cannot inherit from temporary ones */
-               if (!istemp && isTempNamespace(RelationGetNamespace(relation)))
-                       elog(ERROR, "CREATE TABLE: cannot inherit from temp relation \"%s\"",
-                                parent->relname);
+       while (HeapTupleIsValid(tuple = heap_getnext(scan, 0)))
+       {
+               Datum           d;
+               bool            isnull;
 
-               /*
-                * We should have an UNDER permission flag for this, but for now,
-                * demand that creator of a child table own the parent.
-                */
-               if (!pg_class_ownercheck(RelationGetRelid(relation), GetUserId()))
-                       elog(ERROR, "you do not own table \"%s\"",
-                                parent->relname);
+               d = heap_getattr(tuple, attnum, tupdesc, &isnull);
 
-               /*
-                * Reject duplications in the list of parents.
-                */
-               if (intMember(RelationGetRelid(relation), parentOids))
-                       elog(ERROR, "CREATE TABLE: inherited relation \"%s\" duplicated",
-                                parent->relname);
+               if (isnull)
+                       elog(ERROR, "ALTER TABLE: Attribute \"%s\" contains NULL values",
+                                colName);
+       }
 
-               parentOids = lappendi(parentOids, RelationGetRelid(relation));
-               setRelhassubclassInRelation(RelationGetRelid(relation), true);
+       heap_endscan(scan);
 
-               parentHasOids |= relation->rd_rel->relhasoids;
+       /*
+        * Okay, actually perform the catalog change
+        */
+       attr_rel = heap_openr(AttributeRelationName, RowExclusiveLock);
 
-               tupleDesc = RelationGetDescr(relation);
-               constr = tupleDesc->constr;
+       tuple = SearchSysCacheCopy(ATTNAME,
+                                                          ObjectIdGetDatum(myrelid),
+                                                          PointerGetDatum(colName),
+                                                          0, 0);
+       if (!HeapTupleIsValid(tuple)) /* shouldn't happen */
+               elog(ERROR, "ALTER TABLE: relation \"%s\" has no column \"%s\"",
+                        RelationGetRelationName(rel), colName);
 
-               /*
-                * newattno[] will contain the child-table attribute numbers for
-                * the attributes of this parent table.  (They are not the same
-                * for parents after the first one.)
-                */
-               newattno = (AttrNumber *) palloc(tupleDesc->natts * sizeof(AttrNumber));
+       ((Form_pg_attribute) GETSTRUCT(tuple))->attnotnull = TRUE;
 
-               for (parent_attno = 1; parent_attno <= tupleDesc->natts;
-                        parent_attno++)
-               {
-                       Form_pg_attribute attribute = tupleDesc->attrs[parent_attno - 1];
-                       char       *attributeName = NameStr(attribute->attname);
-                       int                     exist_attno;
-                       ColumnDef  *def;
-                       TypeName   *typename;
+       simple_heap_update(attr_rel, &tuple->t_self, tuple);
 
-                       /*
-                        * Does it conflict with some previously inherited column?
-                        */
-                       exist_attno = findAttrByName(attributeName, inhSchema);
-                       if (exist_attno > 0)
-                       {
-                               /*
-                                * Yes, try to merge the two column definitions. They must
-                                * have the same type and typmod.
-                                */
-                               elog(NOTICE, "CREATE TABLE: merging multiple inherited definitions of attribute \"%s\"",
-                                        attributeName);
-                               def = (ColumnDef *) nth(exist_attno - 1, inhSchema);
-                               if (typenameTypeId(def->typename) != attribute->atttypid ||
-                                       def->typename->typmod != attribute->atttypmod)
-                                       elog(ERROR, "CREATE TABLE: inherited attribute \"%s\" type conflict (%s and %s)",
-                                                attributeName,
-                                                TypeNameToString(def->typename),
-                                                typeidTypeName(attribute->atttypid));
-                               /* Merge of NOT NULL constraints = OR 'em together */
-                               def->is_not_null |= attribute->attnotnull;
-                               /* Default and other constraints are handled below */
-                               newattno[parent_attno - 1] = exist_attno;
-                       }
-                       else
-                       {
-                               /*
-                                * No, create a new inherited column
-                                */
-                               def = makeNode(ColumnDef);
-                               def->colname = pstrdup(attributeName);
-                               typename = makeNode(TypeName);
-                               typename->typeid = attribute->atttypid;
-                               typename->typmod = attribute->atttypmod;
-                               def->typename = typename;
-                               def->is_not_null = attribute->attnotnull;
-                               def->raw_default = NULL;
-                               def->cooked_default = NULL;
-                               def->constraints = NIL;
-                               inhSchema = lappend(inhSchema, def);
-                               newattno[parent_attno - 1] = ++child_attno;
-                       }
+       /* keep the system catalog indices current */
+       if (RelationGetForm(attr_rel)->relhasindex)
+       {
+               Relation        idescs[Num_pg_attr_indices];
 
-                       /*
-                        * Copy default if any
-                        */
-                       if (attribute->atthasdef)
-                       {
-                               char       *this_default = NULL;
-                               AttrDefault *attrdef;
-                               int                     i;
+               CatalogOpenIndices(Num_pg_attr_indices, Name_pg_attr_indices, idescs);
+               CatalogIndexInsert(idescs, Num_pg_attr_indices, attr_rel, tuple);
+               CatalogCloseIndices(Num_pg_attr_indices, idescs);
+       }
 
-                               /* Find default in constraint structure */
-                               Assert(constr != NULL);
-                               attrdef = constr->defval;
-                               for (i = 0; i < constr->num_defval; i++)
-                               {
-                                       if (attrdef[i].adnum == parent_attno)
-                                       {
-                                               this_default = attrdef[i].adbin;
-                                               break;
-                                       }
-                               }
-                               Assert(this_default != NULL);
+       heap_close(attr_rel, RowExclusiveLock);
 
-                               /*
-                                * If default expr could contain any vars, we'd need to
-                                * fix 'em, but it can't; so default is ready to apply to
-                                * child.
-                                *
-                                * If we already had a default from some prior parent, check
-                                * to see if they are the same.  If so, no problem; if
-                                * not, mark the column as having a bogus default. Below,
-                                * we will complain if the bogus default isn't overridden
-                                * by the child schema.
-                                */
-                               Assert(def->raw_default == NULL);
-                               if (def->cooked_default == NULL)
-                                       def->cooked_default = pstrdup(this_default);
-                               else if (strcmp(def->cooked_default, this_default) != 0)
-                               {
-                                       def->cooked_default = bogus_marker;
-                                       have_bogus_defaults = true;
-                               }
-                       }
-               }
+       heap_close(rel, NoLock);
+}
 
-               /*
-                * Now copy the constraints of this parent, adjusting attnos using
-                * the completed newattno[] map
-                */
-               if (constr && constr->num_check > 0)
-               {
-                       ConstrCheck *check = constr->check;
-                       int                     i;
 
-                       for (i = 0; i < constr->num_check; i++)
-                       {
-                               Constraint *cdef = makeNode(Constraint);
-                               Node       *expr;
+/*
+ * ALTER TABLE ALTER COLUMN SET/DROP DEFAULT
+ */
+void
+AlterTableAlterColumnDefault(Oid myrelid,
+                                                        bool inh, const char *colName,
+                                                        Node *newDefault)
+{
+       Relation        rel;
+       HeapTuple       tuple;
+       AttrNumber      attnum;
 
-                               cdef->contype = CONSTR_CHECK;
-                               if (check[i].ccname[0] == '$')
-                                       cdef->name = NULL;
-                               else
-                                       cdef->name = pstrdup(check[i].ccname);
-                               cdef->raw_expr = NULL;
-                               /* adjust varattnos of ccbin here */
-                               expr = stringToNode(check[i].ccbin);
-                               change_varattnos_of_a_node(expr, newattno);
-                               cdef->cooked_expr = nodeToString(expr);
-                               constraints = lappend(constraints, cdef);
-                       }
-               }
+       rel = heap_open(myrelid, AccessExclusiveLock);
 
-               pfree(newattno);
+       /*
+        * We allow defaults on views so that INSERT into a view can have
+        * default-ish behavior.  This works because the rewriter substitutes
+        * default values into INSERTs before it expands rules.
+        */
+       if (rel->rd_rel->relkind != RELKIND_RELATION &&
+               rel->rd_rel->relkind != RELKIND_VIEW)
+               elog(ERROR, "ALTER TABLE: relation \"%s\" is not a table or view",
+                        RelationGetRelationName(rel));
+
+       if (!allowSystemTableMods
+               && IsSystemRelation(rel))
+               elog(ERROR, "ALTER TABLE: relation \"%s\" is a system catalog",
+                        RelationGetRelationName(rel));
 
-               /*
-                * Close the parent rel, but keep our AccessShareLock on it until
-                * xact commit.  That will prevent someone else from deleting or
-                * ALTERing the parent before the child is committed.
-                */
-               heap_close(relation, NoLock);
-       }
+       if (!pg_class_ownercheck(myrelid, GetUserId()))
+               elog(ERROR, "ALTER TABLE: \"%s\": permission denied",
+                        RelationGetRelationName(rel));
 
        /*
-        * If we had no inherited attributes, the result schema is just the
-        * explicitly declared columns.  Otherwise, we need to merge the
-        * declared columns into the inherited schema list.
+        * Propagate to children if desired
         */
-       if (inhSchema != NIL)
+       if (inh)
        {
-               foreach(entry, schema)
-               {
-                       ColumnDef  *newdef = lfirst(entry);
-                       char       *attributeName = newdef->colname;
-                       int                     exist_attno;
+               List       *child,
+                                  *children;
 
-                       /*
-                        * Does it conflict with some previously inherited column?
-                        */
-                       exist_attno = findAttrByName(attributeName, inhSchema);
-                       if (exist_attno > 0)
-                       {
-                               ColumnDef  *def;
+               /* this routine is actually in the planner */
+               children = find_all_inheritors(myrelid);
 
-                               /*
-                                * Yes, try to merge the two column definitions. They must
-                                * have the same type and typmod.
-                                */
-                               elog(NOTICE, "CREATE TABLE: merging attribute \"%s\" with inherited definition",
-                                        attributeName);
-                               def = (ColumnDef *) nth(exist_attno - 1, inhSchema);
-                               if (typenameTypeId(def->typename) != typenameTypeId(newdef->typename) ||
-                                       def->typename->typmod != newdef->typename->typmod)
-                                       elog(ERROR, "CREATE TABLE: attribute \"%s\" type conflict (%s and %s)",
-                                                attributeName,
-                                                TypeNameToString(def->typename),
-                                                TypeNameToString(newdef->typename));
-                               /* Merge of NOT NULL constraints = OR 'em together */
-                               def->is_not_null |= newdef->is_not_null;
-                               /* If new def has a default, override previous default */
-                               if (newdef->raw_default != NULL)
-                               {
-                                       def->raw_default = newdef->raw_default;
-                                       def->cooked_default = newdef->cooked_default;
-                               }
-                       }
-                       else
-                       {
-                               /*
-                                * No, attach new column to result schema
-                                */
-                               inhSchema = lappend(inhSchema, newdef);
-                       }
-               }
+               /*
+                * 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);
 
-               schema = inhSchema;
+                       if (childrelid == myrelid)
+                               continue;
+                       AlterTableAlterColumnDefault(childrelid,
+                                                                                false, colName, newDefault);
+               }
        }
 
+       /* -= now do the thing on this relation =- */
+
        /*
-        * If we found any conflicting parent default values, check to make
-        * sure they were overridden by the child.
+        * get the number of the attribute
         */
-       if (have_bogus_defaults)
+       tuple = SearchSysCache(ATTNAME,
+                                                  ObjectIdGetDatum(myrelid),
+                                                  PointerGetDatum(colName),
+                                                  0, 0);
+       if (!HeapTupleIsValid(tuple))
+               elog(ERROR, "ALTER TABLE: relation \"%s\" has no column \"%s\"",
+                        RelationGetRelationName(rel), colName);
+
+       attnum = ((Form_pg_attribute) GETSTRUCT(tuple))->attnum;
+       ReleaseSysCache(tuple);
+
+       if (newDefault)
        {
-               foreach(entry, schema)
-               {
-                       ColumnDef  *def = lfirst(entry);
+               /* SET DEFAULT */
+               RawColumnDefault *rawEnt;
 
-                       if (def->cooked_default == bogus_marker)
-                               elog(ERROR, "CREATE TABLE: attribute \"%s\" inherits conflicting default values"
-                                        "\n\tTo resolve the conflict, specify a default explicitly",
-                                        def->colname);
-               }
-       }
+               /* Get rid of the old one first */
+               drop_default(myrelid, attnum);
 
-       *supOids = parentOids;
-       *supconstr = constraints;
-       *supHasOids = parentHasOids;
-       return schema;
-}
+               rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
+               rawEnt->attnum = attnum;
+               rawEnt->raw_default = newDefault;
 
-/*
- * complementary static functions for MergeAttributes().
- *
- * Varattnos of pg_relcheck.rcbin must be rewritten when subclasses inherit
- * constraints from parent classes, since the inherited attributes could
- * be given different column numbers in multiple-inheritance cases.
- *
- * Note that the passed node tree is modified in place!
- */
-static bool
-change_varattnos_walker(Node *node, const AttrNumber *newattno)
-{
-       if (node == NULL)
-               return false;
-       if (IsA(node, Var))
+               /*
+                * This function is intended for CREATE TABLE, so it processes a
+                * _list_ of defaults, but we just do one.
+                */
+               AddRelationRawConstraints(rel, makeList1(rawEnt), NIL);
+       }
+       else
        {
-               Var                *var = (Var *) node;
+               /* DROP DEFAULT */
+               Relation        attr_rel;
 
-               if (var->varlevelsup == 0 && var->varno == 1 &&
-                       var->varattno > 0)
+               /* Fix the pg_attribute row */
+               attr_rel = heap_openr(AttributeRelationName, RowExclusiveLock);
+
+               tuple = SearchSysCacheCopy(ATTNAME,
+                                                                  ObjectIdGetDatum(myrelid),
+                                                                  PointerGetDatum(colName),
+                                                                  0, 0);
+               if (!HeapTupleIsValid(tuple)) /* shouldn't happen */
+                       elog(ERROR, "ALTER TABLE: relation \"%s\" has no column \"%s\"",
+                                RelationGetRelationName(rel), colName);
+
+               ((Form_pg_attribute) GETSTRUCT(tuple))->atthasdef = FALSE;
+
+               simple_heap_update(attr_rel, &tuple->t_self, tuple);
+
+               /* keep the system catalog indices current */
+               if (RelationGetForm(attr_rel)->relhasindex)
                {
-                       /*
-                        * ??? the following may be a problem when the node is
-                        * multiply referenced though stringToNode() doesn't create
-                        * such a node currently.
-                        */
-                       Assert(newattno[var->varattno - 1] > 0);
-                       var->varattno = newattno[var->varattno - 1];
+                       Relation        idescs[Num_pg_attr_indices];
+
+                       CatalogOpenIndices(Num_pg_attr_indices, Name_pg_attr_indices, idescs);
+                       CatalogIndexInsert(idescs, Num_pg_attr_indices, attr_rel, tuple);
+                       CatalogCloseIndices(Num_pg_attr_indices, idescs);
                }
-               return false;
+
+               heap_close(attr_rel, RowExclusiveLock);
+
+               /* get rid of actual default definition in pg_attrdef */
+               drop_default(myrelid, attnum);
        }
-       return expression_tree_walker(node, change_varattnos_walker,
-                                                                 (void *) newattno);
-}
 
-static bool
-change_varattnos_of_a_node(Node *node, const AttrNumber *newattno)
-{
-       return change_varattnos_walker(node, newattno);
+       heap_close(rel, NoLock);
 }
 
-/*
- * StoreCatalogInheritance
- *             Updates the system catalogs with proper inheritance information.
- *
- * supers is an integer list of the OIDs of the new relation's direct
- * ancestors.  NB: it is destructively changed to include indirect ancestors.
- */
+
 static void
-StoreCatalogInheritance(Oid relationId, List *supers)
+drop_default(Oid relid, int16 attnum)
 {
-       Relation        relation;
-       TupleDesc       desc;
-       int16           seqNumber;
-       List       *entry;
+       ScanKeyData scankeys[2];
+       HeapScanDesc scan;
+       Relation        attrdef_rel;
        HeapTuple       tuple;
 
-       /*
-        * sanity checks
-        */
-       AssertArg(OidIsValid(relationId));
+       attrdef_rel = heap_openr(AttrDefaultRelationName, RowExclusiveLock);
+       ScanKeyEntryInitialize(&scankeys[0], 0x0,
+                                                  Anum_pg_attrdef_adrelid, F_OIDEQ,
+                                                  ObjectIdGetDatum(relid));
+       ScanKeyEntryInitialize(&scankeys[1], 0x0,
+                                                  Anum_pg_attrdef_adnum, F_INT2EQ,
+                                                  Int16GetDatum(attnum));
 
-       if (supers == NIL)
-               return;
+       scan = heap_beginscan(attrdef_rel, false, SnapshotNow, 2, scankeys);
 
-       /*
-        * Catalog INHERITS information using direct ancestors only.
-        */
-       relation = heap_openr(InheritsRelationName, RowExclusiveLock);
-       desc = RelationGetDescr(relation);
+       if (HeapTupleIsValid(tuple = heap_getnext(scan, 0)))
+               simple_heap_delete(attrdef_rel, &tuple->t_self);
 
-       seqNumber = 1;
-       foreach(entry, supers)
-       {
-               Oid                     entryOid = lfirsti(entry);
-               Datum           datum[Natts_pg_inherits];
-               char            nullarr[Natts_pg_inherits];
+       heap_endscan(scan);
 
-               datum[0] = ObjectIdGetDatum(relationId);                /* inhrel */
-               datum[1] = ObjectIdGetDatum(entryOid);  /* inhparent */
-               datum[2] = Int16GetDatum(seqNumber);    /* inhseqno */
+       heap_close(attrdef_rel, NoLock);
+}
 
-               nullarr[0] = ' ';
-               nullarr[1] = ' ';
-               nullarr[2] = ' ';
 
-               tuple = heap_formtuple(desc, datum, nullarr);
+/*
+ * ALTER TABLE ALTER COLUMN SET STATISTICS / STORAGE
+ */
+void
+AlterTableAlterColumnFlags(Oid myrelid,
+                                                  bool inh, const char *colName,
+                                                  Node *flagValue, const char *flagType)
+{
+       Relation        rel;
+       int                     newtarget = 1;
+       char        newstorage = 'p';
+       Relation        attrelation;
+       HeapTuple       tuple;
+       Form_pg_attribute attrtuple;
+
+       rel = heap_open(myrelid, AccessExclusiveLock);
 
-               heap_insert(relation, tuple);
+       if (rel->rd_rel->relkind != RELKIND_RELATION)
+               elog(ERROR, "ALTER TABLE: relation \"%s\" is not a table",
+                        RelationGetRelationName(rel));
 
-               if (RelationGetForm(relation)->relhasindex)
-               {
-                       Relation        idescs[Num_pg_inherits_indices];
+       /*
+        * we allow statistics case for system tables
+        */
+       if (*flagType != 'S' && !allowSystemTableMods && IsSystemRelation(rel))
+               elog(ERROR, "ALTER TABLE: relation \"%s\" is a system catalog",
+                        RelationGetRelationName(rel));
 
-                       CatalogOpenIndices(Num_pg_inherits_indices, Name_pg_inherits_indices, idescs);
-                       CatalogIndexInsert(idescs, Num_pg_inherits_indices, relation, tuple);
-                       CatalogCloseIndices(Num_pg_inherits_indices, idescs);
-               }
+       if (!pg_class_ownercheck(myrelid, GetUserId()))
+               elog(ERROR, "ALTER TABLE: \"%s\": permission denied",
+                        RelationGetRelationName(rel));
 
-               heap_freetuple(tuple);
+       /*
+        * Check the supplied parameters before anything else
+        */
+       if (*flagType == 'S')
+       {
+               /* STATISTICS */
+               Assert(IsA(flagValue, Integer));
+               newtarget = intVal(flagValue);
 
-               seqNumber += 1;
+               /*
+                * Limit target to sane range (should we raise an error instead?)
+                */
+               if (newtarget < 0)
+                       newtarget = 0;
+               else if (newtarget > 1000)
+                       newtarget = 1000;
        }
+       else if (*flagType == 'M')
+       {
+               /* STORAGE */
+               char        *storagemode;
 
-       heap_close(relation, RowExclusiveLock);
+               Assert(IsA(flagValue, String));
+               storagemode = strVal(flagValue);
 
-       /* ----------------
-        * Expand supers list to include indirect ancestors as well.
-        *
-        * Algorithm:
-        *      0. begin with list of direct superclasses.
-        *      1. append after each relationId, its superclasses, recursively.
-        *      2. remove all but last of duplicates.
-        * ----------------
-        */
+               if (strcasecmp(storagemode, "plain") == 0)
+                       newstorage = 'p';
+               else if (strcasecmp(storagemode, "external") == 0)
+                       newstorage = 'e';
+               else if (strcasecmp(storagemode, "extended") == 0)
+                       newstorage = 'x';
+               else if (strcasecmp(storagemode, "main") == 0)
+                       newstorage = 'm';
+               else
+                       elog(ERROR, "ALTER TABLE: \"%s\" storage not recognized",
+                                storagemode);
+       }
+       else
+       {
+               elog(ERROR, "ALTER TABLE: Invalid column flag: %c",
+                        (int) *flagType);
+       }
 
        /*
-        * 1. append after each relationId, its superclasses, recursively.
+        * Propagate to children if desired
         */
-       foreach(entry, supers)
+       if (inh)
        {
-               HeapTuple       tuple;
-               Oid                     id;
-               int16           number;
-               List       *next;
-               List       *current;
+               List       *child,
+                                  *children;
 
-               id = (Oid) lfirsti(entry);
-               current = entry;
-               next = lnext(entry);
+               /* this routine is actually in the planner */
+               children = find_all_inheritors(myrelid);
 
-               for (number = 1;; number += 1)
+               /*
+                * 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)
                {
-                       tuple = SearchSysCache(INHRELID,
-                                                                  ObjectIdGetDatum(id),
-                                                                  Int16GetDatum(number),
-                                                                  0, 0);
-                       if (!HeapTupleIsValid(tuple))
-                               break;
-
-                       lnext(current) = lconsi(((Form_pg_inherits)
-                                                                        GETSTRUCT(tuple))->inhparent,
-                                                                       NIL);
-
-                       ReleaseSysCache(tuple);
+                       Oid                     childrelid = lfirsti(child);
 
-                       current = lnext(current);
+                       if (childrelid == myrelid)
+                               continue;
+                       AlterTableAlterColumnFlags(childrelid,
+                                                                          false, colName, flagValue, flagType);
                }
-               lnext(current) = next;
        }
 
+       /* -= now do the thing on this relation =- */
+
+       attrelation = heap_openr(AttributeRelationName, RowExclusiveLock);
+
+       tuple = SearchSysCacheCopy(ATTNAME,
+                                                          ObjectIdGetDatum(myrelid),
+                                                          PointerGetDatum(colName),
+                                                          0, 0);
+       if (!HeapTupleIsValid(tuple))
+               elog(ERROR, "ALTER TABLE: relation \"%s\" has no column \"%s\"",
+                        RelationGetRelationName(rel), colName);
+       attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
+
+       if (attrtuple->attnum < 0)
+               elog(ERROR, "ALTER TABLE: cannot change system attribute \"%s\"",
+                        colName);
        /*
-        * 2. remove all but last of duplicates.
+        * Now change the appropriate field
         */
-       foreach(entry, supers)
+       if (*flagType == 'S')
+               attrtuple->attstattarget = newtarget;
+       else if (*flagType == 'M')
        {
-               Oid                     thisone;
-               bool            found;
-               List       *rest;
-
-again:
-               thisone = lfirsti(entry);
-               found = false;
-               foreach(rest, lnext(entry))
-               {
-                       if (thisone == lfirsti(rest))
-                       {
-                               found = true;
-                               break;
-                       }
-               }
-               if (found)
-               {
-                       /*
-                        * found a later duplicate, so remove this entry.
-                        */
-                       lfirsti(entry) = lfirsti(lnext(entry));
-                       lnext(entry) = lnext(lnext(entry));
-
-                       goto again;
-               }
+               /*
+                * safety check: do not allow toasted storage modes unless column
+                * datatype is TOAST-aware.
+                */
+               if (newstorage == 'p' || TypeIsToastable(attrtuple->atttypid))
+                       attrtuple->attstorage = newstorage;
+               else
+                       elog(ERROR, "ALTER TABLE: Column datatype %s can only have storage \"plain\"",
+                                format_type_be(attrtuple->atttypid));
        }
-}
 
-/*
- * Look for an existing schema entry with the given name.
- *
- * Returns the index (starting with 1) if attribute already exists in schema,
- * 0 if it doesn't.
- */
-static int
-findAttrByName(const char *attributeName, List *schema)
-{
-       List       *s;
-       int                     i = 0;
+       simple_heap_update(attrelation, &tuple->t_self, tuple);
 
-       foreach(s, schema)
+       /* keep system catalog indices current */
        {
-               ColumnDef  *def = lfirst(s);
+               Relation        irelations[Num_pg_attr_indices];
 
-               ++i;
-               if (strcmp(attributeName, def->colname) == 0)
-                       return i;
+               CatalogOpenIndices(Num_pg_attr_indices, Name_pg_attr_indices, irelations);
+               CatalogIndexInsert(irelations, Num_pg_attr_indices, attrelation, tuple);
+               CatalogCloseIndices(Num_pg_attr_indices, irelations);
        }
-       return 0;
+
+       heap_freetuple(tuple);
+       heap_close(attrelation, NoLock);
+       heap_close(rel, NoLock);        /* close rel, but keep lock! */
 }
 
+
 /*
- * Update a relation's pg_class.relhassubclass entry to the given value
+ * ALTER TABLE DROP COLUMN
  */
-static void
-setRelhassubclassInRelation(Oid relationId, bool relhassubclass)
+void
+AlterTableDropColumn(Oid myrelid,
+                                        bool inh, const char *colName,
+                                        int behavior)
 {
-       Relation        relationRelation;
-       HeapTuple       tuple;
-       Relation        idescs[Num_pg_class_indices];
-
-       /*
-        * Fetch a modifiable copy of the tuple, modify it, update pg_class.
-        */
-       relationRelation = heap_openr(RelationRelationName, RowExclusiveLock);
-       tuple = SearchSysCacheCopy(RELOID,
-                                                          ObjectIdGetDatum(relationId),
-                                                          0, 0, 0);
-       if (!HeapTupleIsValid(tuple))
-               elog(ERROR, "setRelhassubclassInRelation: cache lookup failed for relation %u", relationId);
-
-       ((Form_pg_class) GETSTRUCT(tuple))->relhassubclass = relhassubclass;
-       simple_heap_update(relationRelation, &tuple->t_self, tuple);
-
-       /* keep the catalog indices up to date */
-       CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, idescs);
-       CatalogIndexInsert(idescs, Num_pg_class_indices, relationRelation, tuple);
-       CatalogCloseIndices(Num_pg_class_indices, idescs);
-
-       heap_freetuple(tuple);
-       heap_close(relationRelation, RowExclusiveLock);
+       elog(ERROR, "ALTER TABLE / DROP COLUMN is not implemented");
 }
 
 
 /*
- *             renameatt               - changes the name of a attribute in a relation
- *
- *             Attname attribute is changed in attribute catalog.
- *             No record of the previous attname is kept (correct?).
- *
- *             get proper relrelation from relation catalog (if not arg)
- *             scan attribute catalog
- *                             for name conflict (within rel)
- *                             for original attribute (if not arg)
- *             modify attname in attribute tuple
- *             insert modified attribute in attribute catalog
- *             delete original attribute from attribute catalog
+ * ALTER TABLE ADD CONSTRAINT
  */
 void
-renameatt(Oid relid,
-                 const char *oldattname,
-                 const char *newattname,
-                 bool recurse)
-{
-       Relation        targetrelation;
-       Relation        attrelation;
-       HeapTuple       atttup;
-       List       *indexoidlist;
-       List       *indexoidscan;
+AlterTableAddConstraint(Oid myrelid,
+                                               bool inh, List *newConstraints)
+{
+       Relation        rel;
+       List       *listptr;
 
        /*
         * Grab an exclusive lock on the target table, which we will NOT
         * release until end of transaction.
         */
-       targetrelation = heap_open(relid, AccessExclusiveLock);
+       rel = heap_open(myrelid, AccessExclusiveLock);
 
-       /*
-        * permissions checking.  this would normally be done in utility.c,
-        * but this particular routine is recursive.
-        *
-        * normally, only the owner of a class can change its schema.
-        */
-       if (!allowSystemTableMods 
-               && IsSystemRelation(targetrelation))
-               elog(ERROR, "renameatt: class \"%s\" is a system catalog",
-                        RelationGetRelationName(targetrelation));
-       if (!pg_class_ownercheck(relid, GetUserId()))
-               elog(ERROR, "renameatt: you do not own class \"%s\"",
-                        RelationGetRelationName(targetrelation));
+       if (rel->rd_rel->relkind != RELKIND_RELATION)
+               elog(ERROR, "ALTER TABLE: relation \"%s\" is not a table",
+                        RelationGetRelationName(rel));
 
-       /*
-        * if the 'recurse' flag is set then we are supposed to rename this
-        * attribute in all classes that inherit from 'relname' (as well as in
-        * 'relname').
-        *
-        * any permissions or problems with duplicate attributes will cause the
-        * whole transaction to abort, which is what we want -- all or
-        * nothing.
-        */
-       if (recurse)
+       if (!allowSystemTableMods
+               && IsSystemRelation(rel))
+               elog(ERROR, "ALTER TABLE: relation \"%s\" is a system catalog",
+                        RelationGetRelationName(rel));
+
+       if (!pg_class_ownercheck(myrelid, GetUserId()))
+               elog(ERROR, "ALTER TABLE: \"%s\": permission denied",
+                        RelationGetRelationName(rel));
+
+       if (inh)
        {
                List       *child,
                                   *children;
 
                /* this routine is actually in the planner */
-               children = find_all_inheritors(relid);
+               children = find_all_inheritors(myrelid);
 
                /*
                 * find_all_inheritors does the recursive search of the
@@ -2625,571 +2390,686 @@ renameatt(Oid relid,
                {
                        Oid                     childrelid = lfirsti(child);
 
-                       if (childrelid == relid)
+                       if (childrelid == myrelid)
                                continue;
-                       /* note we need not recurse again! */
-                       renameatt(childrelid, oldattname, newattname, false);
+                       AlterTableAddConstraint(childrelid, false, newConstraints);
                }
        }
 
-       attrelation = heap_openr(AttributeRelationName, RowExclusiveLock);
+       foreach(listptr, newConstraints)
+       {
+               Node       *newConstraint = lfirst(listptr);
 
-       atttup = SearchSysCacheCopy(ATTNAME,
-                                                               ObjectIdGetDatum(relid),
-                                                               PointerGetDatum(oldattname),
-                                                               0, 0);
-       if (!HeapTupleIsValid(atttup))
-               elog(ERROR, "renameatt: attribute \"%s\" does not exist", oldattname);
+               switch (nodeTag(newConstraint))
+               {
+                       case T_Constraint:
+                               {
+                                       Constraint *constr = (Constraint *) newConstraint;
 
-       if (((Form_pg_attribute) GETSTRUCT(atttup))->attnum < 0)
-               elog(ERROR, "renameatt: system attribute \"%s\" not renamed", oldattname);
+                                       /*
+                                        * Currently, we only expect to see CONSTR_CHECK nodes
+                                        * arriving here (see the preprocessing done in
+                                        * parser/analyze.c).  Use a switch anyway to make it
+                                        * easier to add more code later.
+                                        */
+                                       switch (constr->contype)
+                                       {
+                                               case CONSTR_CHECK:
+                                                       {
+                                                               ParseState *pstate;
+                                                               bool            successful = true;
+                                                               HeapScanDesc scan;
+                                                               ExprContext *econtext;
+                                                               TupleTableSlot *slot;
+                                                               HeapTuple       tuple;
+                                                               RangeTblEntry *rte;
+                                                               List       *qual;
+                                                               Node       *expr;
+                                                               char       *name;
 
-       /* should not already exist */
-       if (SearchSysCacheExists(ATTNAME,
-                                                        ObjectIdGetDatum(relid),
-                                                        PointerGetDatum(newattname),
-                                                        0, 0))
-               elog(ERROR, "renameatt: attribute \"%s\" exists", newattname);
+                                                               if (constr->name)
+                                                                       name = constr->name;
+                                                               else
+                                                                       name = "<unnamed>";
+
+                                                               /*
+                                                                * We need to make a parse state and range
+                                                                * table to allow us to transformExpr and
+                                                                * fix_opids to get a version of the
+                                                                * expression we can pass to ExecQual
+                                                                */
+                                                               pstate = make_parsestate(NULL);
+                                                               rte = addRangeTableEntryForRelation(pstate,
+                                                                                                                                       myrelid,
+                                                                                       makeAlias(RelationGetRelationName(rel), NIL),
+                                                                                                                                       false,
+                                                                                                                                       true);
+                                                               addRTEtoQuery(pstate, rte, true, true);
+
+                                                               /*
+                                                                * Convert the A_EXPR in raw_expr into an
+                                                                * EXPR
+                                                                */
+                                                               expr = transformExpr(pstate, constr->raw_expr);
+
+                                                               /*
+                                                                * Make sure it yields a boolean result.
+                                                                */
+                                                               if (exprType(expr) != BOOLOID)
+                                                                       elog(ERROR, "CHECK '%s' does not yield boolean result",
+                                                                                name);
+
+                                                               /*
+                                                                * Make sure no outside relations are
+                                                                * referred to.
+                                                                */
+                                                               if (length(pstate->p_rtable) != 1)
+                                                                       elog(ERROR, "Only relation '%s' can be referenced in CHECK",
+                                                                                RelationGetRelationName(rel));
+
+                                                               /*
+                                                                * Might as well try to reduce any
+                                                                * constant expressions.
+                                                                */
+                                                               expr = eval_const_expressions(expr);
+
+                                                               /* And fix the opids */
+                                                               fix_opids(expr);
+
+                                                               qual = makeList1(expr);
+
+                                                               /* Make tuple slot to hold tuples */
+                                                               slot = MakeTupleTableSlot();
+                                                               ExecSetSlotDescriptor(slot, RelationGetDescr(rel), false);
+                                                               /* Make an expression context for ExecQual */
+                                                               econtext = MakeExprContext(slot, CurrentMemoryContext);
+
+                                                               /*
+                                                                * 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);
+                                                                       if (!ExecQual(qual, econtext, true))
+                                                                       {
+                                                                               successful = false;
+                                                                               break;
+                                                                       }
+                                                                       ResetExprContext(econtext);
+                                                               }
+
+                                                               heap_endscan(scan);
+
+                                                               FreeExprContext(econtext);
+                                                               pfree(slot);
+
+                                                               if (!successful)
+                                                                       elog(ERROR, "AlterTableAddConstraint: rejected due to CHECK constraint %s", name);
+
+                                                               /*
+                                                                * Call AddRelationRawConstraints to do
+                                                                * the real adding -- It duplicates some
+                                                                * of the above, but does not check the
+                                                                * validity of the constraint against
+                                                                * tuples already in the table.
+                                                                */
+                                                               AddRelationRawConstraints(rel, NIL,
+                                                                                                         makeList1(constr));
+
+                                                               break;
+                                                       }
+                                               default:
+                                                       elog(ERROR, "ALTER TABLE / ADD CONSTRAINT is not implemented for that constraint type.");
+                                       }
+                                       break;
+                               }
+                       case T_FkConstraint:
+                               {
+                                       FkConstraint *fkconstraint = (FkConstraint *) newConstraint;
+                                       Relation        pkrel;
+                                       HeapScanDesc scan;
+                                       HeapTuple       tuple;
+                                       Trigger         trig;
+                                       List       *list;
+                                       int                     count;
+
+                                       /*
+                                        * Grab an exclusive lock on the pk table, so that
+                                        * someone doesn't delete rows out from under us.
+                                        *
+                                        * XXX wouldn't a lesser lock be sufficient?
+                                        */
+                                       pkrel = heap_openrv(fkconstraint->pktable,
+                                                                               AccessExclusiveLock);
+
+                                       /*
+                                        * Validity checks
+                                        */
+                                       if (pkrel->rd_rel->relkind != RELKIND_RELATION)
+                                               elog(ERROR, "referenced table \"%s\" not a relation",
+                                                        fkconstraint->pktable->relname);
+
+                                       if (isTempNamespace(RelationGetNamespace(pkrel)) &&
+                                               !isTempNamespace(RelationGetNamespace(rel)))
+                                               elog(ERROR, "ALTER TABLE / ADD CONSTRAINT: Unable to reference temporary table from permanent table constraint.");
+
+                                       /*
+                                        * First we check for limited correctness of the
+                                        * constraint.
+                                        *
+                                        * NOTE: we assume parser has already checked for
+                                        * existence of an appropriate unique index on the
+                                        * referenced relation, and that the column datatypes
+                                        * are comparable.
+                                        *
+                                        * Scan through each tuple, calling RI_FKey_check_ins
+                                        * (insert trigger) as if that tuple had just been
+                                        * inserted.  If any of those fail, it should
+                                        * elog(ERROR) and that's that.
+                                        */
+                                       MemSet(&trig, 0, sizeof(trig));
+                                       trig.tgoid = InvalidOid;
+                                       if (fkconstraint->constr_name)
+                                               trig.tgname = fkconstraint->constr_name;
+                                       else
+                                               trig.tgname = "<unknown>";
+                                       trig.tgenabled = TRUE;
+                                       trig.tgisconstraint = TRUE;
+                                       trig.tgconstrrelid = RelationGetRelid(pkrel);
+                                       trig.tgdeferrable = FALSE;
+                                       trig.tginitdeferred = FALSE;
+
+                                       trig.tgargs = (char **) palloc(
+                                        sizeof(char *) * (4 + length(fkconstraint->fk_attrs)
+                                                                          + length(fkconstraint->pk_attrs)));
+
+                                       trig.tgargs[0] = trig.tgname;
+                                       trig.tgargs[1] = RelationGetRelationName(rel);
+                                       trig.tgargs[2] = RelationGetRelationName(pkrel);
+                                       trig.tgargs[3] = fkconstraint->match_type;
+                                       count = 4;
+                                       foreach(list, fkconstraint->fk_attrs)
+                                       {
+                                               Ident      *fk_at = lfirst(list);
 
-       namestrcpy(&(((Form_pg_attribute) GETSTRUCT(atttup))->attname),
-                          newattname);
+                                               trig.tgargs[count] = fk_at->name;
+                                               count += 2;
+                                       }
+                                       count = 5;
+                                       foreach(list, fkconstraint->pk_attrs)
+                                       {
+                                               Ident      *pk_at = lfirst(list);
 
-       simple_heap_update(attrelation, &atttup->t_self, atttup);
+                                               trig.tgargs[count] = pk_at->name;
+                                               count += 2;
+                                       }
+                                       trig.tgnargs = count - 1;
 
-       /* keep system catalog indices current */
-       {
-               Relation        irelations[Num_pg_attr_indices];
+                                       scan = heap_beginscan(rel, false, SnapshotNow, 0, NULL);
 
-               CatalogOpenIndices(Num_pg_attr_indices, Name_pg_attr_indices, irelations);
-               CatalogIndexInsert(irelations, Num_pg_attr_indices, attrelation, atttup);
-               CatalogCloseIndices(Num_pg_attr_indices, irelations);
-       }
+                                       while (HeapTupleIsValid(tuple = heap_getnext(scan, 0)))
+                                       {
+                                               /* Make a call to the check function */
 
-       heap_freetuple(atttup);
+                                               /*
+                                                * No parameters are passed, but we do set a
+                                                * context
+                                                */
+                                               FunctionCallInfoData fcinfo;
+                                               TriggerData trigdata;
 
-       /*
-        * Update column names of indexes that refer to the column being
-        * renamed.
-        */
-       indexoidlist = RelationGetIndexList(targetrelation);
+                                               MemSet(&fcinfo, 0, sizeof(fcinfo));
 
-       foreach(indexoidscan, indexoidlist)
-       {
-               Oid                     indexoid = lfirsti(indexoidscan);
-               HeapTuple       indextup;
+                                               /*
+                                                * We assume RI_FKey_check_ins won't look at
+                                                * flinfo...
+                                                */
 
-               /*
-                * First check to see if index is a functional index. If so, its
-                * column name is a function name and shouldn't be renamed here.
-                */
-               indextup = SearchSysCache(INDEXRELID,
-                                                                 ObjectIdGetDatum(indexoid),
-                                                                 0, 0, 0);
-               if (!HeapTupleIsValid(indextup))
-                       elog(ERROR, "renameatt: can't find index id %u", indexoid);
-               if (OidIsValid(((Form_pg_index) GETSTRUCT(indextup))->indproc))
-               {
-                       ReleaseSysCache(indextup);
-                       continue;
-               }
-               ReleaseSysCache(indextup);
+                                               trigdata.type = T_TriggerData;
+                                               trigdata.tg_event = TRIGGER_EVENT_INSERT | TRIGGER_EVENT_ROW;
+                                               trigdata.tg_relation = rel;
+                                               trigdata.tg_trigtuple = tuple;
+                                               trigdata.tg_newtuple = NULL;
+                                               trigdata.tg_trigger = &trig;
 
-               /*
-                * Okay, look to see if any column name of the index matches the
-                * old attribute name.
-                */
-               atttup = SearchSysCacheCopy(ATTNAME,
-                                                                       ObjectIdGetDatum(indexoid),
-                                                                       PointerGetDatum(oldattname),
-                                                                       0, 0);
-               if (!HeapTupleIsValid(atttup))
-                       continue;                       /* Nope, so ignore it */
+                                               fcinfo.context = (Node *) &trigdata;
 
-               /*
-                * Update the (copied) attribute tuple.
-                */
-               namestrcpy(&(((Form_pg_attribute) GETSTRUCT(atttup))->attname),
-                                  newattname);
+                                               RI_FKey_check_ins(&fcinfo);
+                                       }
+                                       heap_endscan(scan);
 
-               simple_heap_update(attrelation, &atttup->t_self, atttup);
+                                       pfree(trig.tgargs);
 
-               /* keep system catalog indices current */
-               {
-                       Relation        irelations[Num_pg_attr_indices];
+                                       heap_close(pkrel, NoLock);
 
-                       CatalogOpenIndices(Num_pg_attr_indices, Name_pg_attr_indices, irelations);
-                       CatalogIndexInsert(irelations, Num_pg_attr_indices, attrelation, atttup);
-                       CatalogCloseIndices(Num_pg_attr_indices, irelations);
+                                       break;
+                               }
+                       default:
+                               elog(ERROR, "ALTER TABLE / ADD CONSTRAINT unable to determine type of constraint passed");
                }
-               heap_freetuple(atttup);
-       }
-
-       freeList(indexoidlist);
-
-       heap_close(attrelation, RowExclusiveLock);
-
-       /*
-        * Update att name in any RI triggers associated with the relation.
-        */
-       if (targetrelation->rd_rel->reltriggers > 0)
-       {
-               /* update tgargs column reference where att is primary key */
-               update_ri_trigger_args(RelationGetRelid(targetrelation),
-                                                          oldattname, newattname,
-                                                          false, false);
-               /* update tgargs column reference where att is foreign key */
-               update_ri_trigger_args(RelationGetRelid(targetrelation),
-                                                          oldattname, newattname,
-                                                          true, false);
        }
 
-       heap_close(targetrelation, NoLock); /* close rel but keep lock! */
+       /* Close rel, but keep lock till commit */
+       heap_close(rel, NoLock);
 }
 
+
 /*
- *             renamerel               - change the name of a relation
- *
- *             XXX - When renaming sequences, we don't bother to modify the
- *                       sequence name that is stored within the sequence itself
- *                       (this would cause problems with MVCC). In the future,
- *                       the sequence name should probably be removed from the
- *                       sequence, AFAIK there's no need for it to be there.
+ * ALTER TABLE DROP CONSTRAINT
+ * Note: It is legal to remove a constraint with name "" as it is possible
+ * to add a constraint with name "".
+ * Christopher Kings-Lynne
  */
 void
-renamerel(Oid relid, const char *newrelname)
+AlterTableDropConstraint(Oid myrelid,
+                                                bool inh, const char *constrName,
+                                                int behavior)
 {
-       Relation        targetrelation;
-       Relation        relrelation;    /* for RELATION relation */
-       HeapTuple       reltup;
-       Oid                     namespaceId;
-       char       *oldrelname;
-       char            relkind;
-       bool            relhastriggers;
-       Relation        irelations[Num_pg_class_indices];
+       Relation        rel;
+       int                     deleted;
 
        /*
-        * Grab an exclusive lock on the target table or index, which we will
-        * NOT release until end of transaction.
+        * We don't support CASCADE yet  - in fact, RESTRICT doesn't work to
+        * the spec either!
         */
-       targetrelation = relation_open(relid, AccessExclusiveLock);
-
-       oldrelname = pstrdup(RelationGetRelationName(targetrelation));
-       namespaceId = RelationGetNamespace(targetrelation);
-
-       /* Validity checks */
-       if (!allowSystemTableMods &&
-               IsSystemRelation(targetrelation))
-               elog(ERROR, "renamerel: system relation \"%s\" may not be renamed",
-                        oldrelname);
-
-       relkind = targetrelation->rd_rel->relkind;
-       relhastriggers = (targetrelation->rd_rel->reltriggers > 0);
+       if (behavior == CASCADE)
+               elog(ERROR, "ALTER TABLE / DROP CONSTRAINT does not support the CASCADE keyword");
 
        /*
-        * Find relation's pg_class tuple, and make sure newrelname isn't in
-        * use.
+        * Acquire an exclusive lock on the target relation for the duration
+        * of the operation.
         */
-       relrelation = heap_openr(RelationRelationName, RowExclusiveLock);
+       rel = heap_open(myrelid, AccessExclusiveLock);
 
-       reltup = SearchSysCacheCopy(RELOID,
-                                                               PointerGetDatum(relid),
-                                                               0, 0, 0);
-       if (!HeapTupleIsValid(reltup))
-               elog(ERROR, "renamerel: relation \"%s\" does not exist",
-                        oldrelname);
+       /* Disallow DROP CONSTRAINT on views, indexes, sequences, etc */
+       if (rel->rd_rel->relkind != RELKIND_RELATION)
+               elog(ERROR, "ALTER TABLE: relation \"%s\" is not a table",
+                        RelationGetRelationName(rel));
 
-       if (get_relname_relid(newrelname, namespaceId) != InvalidOid)
-               elog(ERROR, "renamerel: relation \"%s\" exists", newrelname);
+       if (!allowSystemTableMods
+               && IsSystemRelation(rel))
+               elog(ERROR, "ALTER TABLE: relation \"%s\" is a system catalog",
+                        RelationGetRelationName(rel));
+
+       if (!pg_class_ownercheck(myrelid, GetUserId()))
+               elog(ERROR, "ALTER TABLE: \"%s\": permission denied",
+                        RelationGetRelationName(rel));
 
        /*
-        * Update pg_class tuple with new relname.      (Scribbling on reltup is
-        * OK because it's a copy...)
+        * Since all we have is the name of the constraint, we have to look
+        * through all catalogs that could possibly contain a constraint for
+        * this relation. We also keep a count of the number of constraints
+        * removed.
         */
-       namestrcpy(&(((Form_pg_class) GETSTRUCT(reltup))->relname), newrelname);
-
-       simple_heap_update(relrelation, &reltup->t_self, reltup);
-
-       /* keep the system catalog indices current */
-       CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, irelations);
-       CatalogIndexInsert(irelations, Num_pg_class_indices, relrelation, reltup);
-       CatalogCloseIndices(Num_pg_class_indices, irelations);
 
-       heap_close(relrelation, NoLock);
-       heap_freetuple(reltup);
+       deleted = 0;
 
        /*
-        * Also rename the associated type, if any.
+        * First, we remove all CHECK constraints with the given name
         */
-       if (relkind != RELKIND_INDEX)
-               TypeRename(oldrelname, namespaceId, newrelname);
 
-       /*
-        * Update rel name in any RI triggers associated with the relation.
-        */
-       if (relhastriggers)
-       {
-               /* update tgargs where relname is primary key */
-               update_ri_trigger_args(relid,
-                                                          oldrelname,
-                                                          newrelname,
-                                                          false, true);
-               /* update tgargs where relname is foreign key */
-               update_ri_trigger_args(relid,
-                                                          oldrelname,
-                                                          newrelname,
-                                                          true, true);
-       }
+       deleted += RemoveCheckConstraint(rel, constrName, inh);
 
        /*
-        * Close rel, but keep exclusive lock!
+        * Now we remove NULL, UNIQUE, PRIMARY KEY and FOREIGN KEY
+        * constraints.
+        *
+        * Unimplemented.
         */
-       relation_close(targetrelation, NoLock);
-}
 
-/*
- *             renametrig              - changes the name of a trigger on a relation
- *
- *             trigger name is changed in trigger catalog.
- *             No record of the previous name is kept.
- *
- *             get proper relrelation from relation catalog (if not arg)
- *             scan trigger catalog
- *                             for name conflict (within rel)
- *                             for original trigger (if not arg)
- *             modify tgname in trigger tuple
- *             insert modified trigger in trigger catalog
- *             delete original trigger from trigger catalog
+       /* Close the target relation */
+       heap_close(rel, NoLock);
+
+       /* If zero constraints deleted, complain */
+       if (deleted == 0)
+               elog(ERROR, "ALTER TABLE / DROP CONSTRAINT: %s does not exist",
+                        constrName);
+       /* Otherwise if more than one constraint deleted, notify */
+       else if (deleted > 1)
+               elog(NOTICE, "Multiple constraints dropped");
+}
+
+/*
+ * ALTER TABLE OWNER
  */
-extern void renametrig(Oid relid,
-                 const char *oldname,
-                 const char *newname)
+void
+AlterTableOwner(Oid relationOid, int32 newOwnerSysId)
 {
-       Relation        targetrel;
-       Relation        tgrel;
-       HeapTuple       tuple;
-       SysScanDesc     tgscan;
-       ScanKeyData key;
-       bool            found = FALSE;
-       Relation        idescs[Num_pg_trigger_indices];
+       Relation                target_rel;
+       Relation                class_rel;
+       HeapTuple               tuple;
+       Relation                idescs[Num_pg_class_indices];
+       Form_pg_class   tuple_class;
 
-       /*
-        * Grab an exclusive lock on the target table, which we will NOT
-        * release until end of transaction.
-        */
-       targetrel = heap_open(relid, AccessExclusiveLock);
+       /* Get exclusive lock till end of transaction on the target table */
+       target_rel = heap_open(relationOid, AccessExclusiveLock);
 
-       /*
-        * Scan pg_trigger twice for existing triggers on relation.  We do this in
-        * order to ensure a trigger does not exist with newname (The unique index
-        * on tgrelid/tgname would complain anyway) and to ensure a trigger does
-        * exist with oldname.
-        *
-        * NOTE that this is cool only because we have AccessExclusiveLock on the
-        * relation, so the trigger set won't be changing underneath us.
-        */
-       tgrel = heap_openr(TriggerRelationName, RowExclusiveLock);
+       /* Get its pg_class tuple, too */
+       class_rel = heap_openr(RelationRelationName, RowExclusiveLock);
+
+       tuple = SearchSysCacheCopy(RELOID,
+                                                          ObjectIdGetDatum(relationOid),
+                                                          0, 0, 0);
+       if (!HeapTupleIsValid(tuple))
+               elog(ERROR, "ALTER TABLE: relation %u not found", relationOid);
+       tuple_class = (Form_pg_class) GETSTRUCT(tuple);
+
+       /* Can we change the ownership of this tuple? */
+       CheckTupleType(tuple_class);
 
        /*
-        * First pass -- look for name conflict
+        * Okay, this is a valid tuple: change its ownership and
+        * write to the heap.
         */
-       ScanKeyEntryInitialize(&key, 0,
-                                                  Anum_pg_trigger_tgrelid,
-                                                  F_OIDEQ,
-                                                  ObjectIdGetDatum(relid));
-       tgscan = systable_beginscan(tgrel, TriggerRelidNameIndex, true,
-                                                               SnapshotNow, 1, &key);
-       while (HeapTupleIsValid(tuple = systable_getnext(tgscan)))
-       {
-               Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(tuple);
+       tuple_class->relowner = newOwnerSysId;
+       simple_heap_update(class_rel, &tuple->t_self, tuple);
 
-               if (namestrcmp(&(pg_trigger->tgname), newname) == 0)
-                       elog(ERROR, "renametrig: trigger %s already defined on relation %s",
-                                newname, RelationGetRelationName(targetrel));
-       }
-       systable_endscan(tgscan);
+       /* Keep the catalog indices up to date */
+       CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, idescs);
+       CatalogIndexInsert(idescs, Num_pg_class_indices, class_rel, tuple);
+       CatalogCloseIndices(Num_pg_class_indices, idescs);
 
        /*
-        * Second pass -- look for trigger existing with oldname and update
+        * If we are operating on a table, also change the ownership of any
+        * indexes that belong to the table, as well as the table's toast
+        * table (if it has one)
         */
-       ScanKeyEntryInitialize(&key, 0,
-                                                  Anum_pg_trigger_tgrelid,
-                                                  F_OIDEQ,
-                                                  ObjectIdGetDatum(relid));
-       tgscan = systable_beginscan(tgrel, TriggerRelidNameIndex, true,
-                                                               SnapshotNow, 1, &key);
-       while (HeapTupleIsValid(tuple = systable_getnext(tgscan)))
+       if (tuple_class->relkind == RELKIND_RELATION ||
+               tuple_class->relkind == RELKIND_TOASTVALUE)
        {
-               Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(tuple);
+               List *index_oid_list, *i;
 
-               if (namestrcmp(&(pg_trigger->tgname), oldname) == 0)
-               {
-                       /*
-                        * Update pg_trigger tuple with new tgname.
-                        * (Scribbling on tuple is OK because it's a copy...)
-                        */
-                       namestrcpy(&(pg_trigger->tgname), newname);
-                       simple_heap_update(tgrel, &tuple->t_self, tuple);
+               /* Find all the indexes belonging to this relation */
+               index_oid_list = RelationGetIndexList(target_rel);
 
-                       /*
-                        * keep system catalog indices current
-                        */
-                       CatalogOpenIndices(Num_pg_trigger_indices, Name_pg_trigger_indices, idescs);
-                       CatalogIndexInsert(idescs, Num_pg_trigger_indices, tgrel, tuple);
-                       CatalogCloseIndices(Num_pg_trigger_indices, idescs);
+               /* For each index, recursively change its ownership */
+               foreach(i, index_oid_list)
+               {
+                       AlterTableOwner(lfirsti(i), newOwnerSysId);
+               }
 
-                       /*
-                        * Invalidate relation's relcache entry so that other
-                        * backends (and this one too!) are sent SI message to make them
-                        * rebuild relcache entries.
-                        */
-                       CacheInvalidateRelcache(relid);
+               freeList(index_oid_list);
+       }
 
-                       found = TRUE;
-                       break;
+       if (tuple_class->relkind == RELKIND_RELATION)
+       {
+               /* If it has a toast table, recurse to change its ownership */
+               if (tuple_class->reltoastrelid != InvalidOid)
+               {
+                       AlterTableOwner(tuple_class->reltoastrelid, newOwnerSysId);
                }
        }
-       systable_endscan(tgscan);
-
-       heap_close(tgrel, RowExclusiveLock);
-
-       if (!found)
-               elog(ERROR, "renametrig: trigger %s not defined on relation %s",
-                        oldname, RelationGetRelationName(targetrel));
 
-       /*
-        * Close rel, but keep exclusive lock!
-        */
-       heap_close(targetrel, NoLock);
+       heap_freetuple(tuple);
+       heap_close(class_rel, RowExclusiveLock);
+       heap_close(target_rel, NoLock);
 }
 
-
-/*
- * Given a trigger function OID, determine whether it is an RI trigger,
- * and if so whether it is attached to PK or FK relation.
- *
- * XXX this probably doesn't belong here; should be exported by
- * ri_triggers.c
- */
-static int
-ri_trigger_type(Oid tgfoid)
+static void
+CheckTupleType(Form_pg_class tuple_class)
 {
-       switch (tgfoid)
+       switch (tuple_class->relkind)
        {
-               case F_RI_FKEY_CASCADE_DEL:
-               case F_RI_FKEY_CASCADE_UPD:
-               case F_RI_FKEY_RESTRICT_DEL:
-               case F_RI_FKEY_RESTRICT_UPD:
-               case F_RI_FKEY_SETNULL_DEL:
-               case F_RI_FKEY_SETNULL_UPD:
-               case F_RI_FKEY_SETDEFAULT_DEL:
-               case F_RI_FKEY_SETDEFAULT_UPD:
-               case F_RI_FKEY_NOACTION_DEL:
-               case F_RI_FKEY_NOACTION_UPD:
-                       return RI_TRIGGER_PK;
-
-               case F_RI_FKEY_CHECK_INS:
-               case F_RI_FKEY_CHECK_UPD:
-                       return RI_TRIGGER_FK;
+               case RELKIND_RELATION:
+               case RELKIND_INDEX:
+               case RELKIND_VIEW:
+               case RELKIND_SEQUENCE:
+               case RELKIND_TOASTVALUE:
+                       /* ok to change owner */
+                       break;
+               default:
+                       elog(ERROR, "ALTER TABLE: relation \"%s\" is not a table, TOAST table, index, view, or sequence",
+                                NameStr(tuple_class->relname));
        }
-
-       return RI_TRIGGER_NONE;
 }
 
 /*
- * Scan pg_trigger for RI triggers that are on the specified relation
- * (if fk_scan is false) or have it as the tgconstrrel (if fk_scan
- * is true).  Update RI trigger args fields matching oldname to contain
- * newname instead.  If update_relname is true, examine the relname
- * fields; otherwise examine the attname fields.
+ * ALTER TABLE CREATE TOAST TABLE
  */
-static void
-update_ri_trigger_args(Oid relid,
-                                          const char *oldname,
-                                          const char *newname,
-                                          bool fk_scan,
-                                          bool update_relname)
+void
+AlterTableCreateToastTable(Oid relOid, bool silent)
 {
-       Relation        tgrel;
-       Relation        irel;
-       ScanKeyData skey[1];
-       IndexScanDesc idxtgscan;
-       RetrieveIndexResult idxres;
-       Datum           values[Natts_pg_trigger];
-       char            nulls[Natts_pg_trigger];
-       char            replaces[Natts_pg_trigger];
+       Relation        rel;
+       HeapTuple       reltup;
+       HeapTupleData classtuple;
+       TupleDesc       tupdesc;
+       Relation        class_rel;
+       Buffer          buffer;
+       Relation        ridescs[Num_pg_class_indices];
+       Oid                     toast_relid;
+       Oid                     toast_idxid;
+       char            toast_relname[NAMEDATALEN];
+       char            toast_idxname[NAMEDATALEN];
+       IndexInfo  *indexInfo;
+       Oid                     classObjectId[2];
 
-       tgrel = heap_openr(TriggerRelationName, RowExclusiveLock);
-       if (fk_scan)
-               irel = index_openr(TriggerConstrRelidIndex);
-       else
-               irel = index_openr(TriggerRelidNameIndex);
+       /*
+        * Grab an exclusive lock on the target table, which we will NOT
+        * release until end of transaction.
+        */
+       rel = heap_open(relOid, AccessExclusiveLock);
 
-       ScanKeyEntryInitialize(&skey[0], 0x0,
-                                                  1,   /* column 1 of index in either case */
-                                                  F_OIDEQ,
-                                                  ObjectIdGetDatum(relid));
-       idxtgscan = index_beginscan(irel, false, 1, skey);
+       if (rel->rd_rel->relkind != RELKIND_RELATION)
+               elog(ERROR, "ALTER TABLE: relation \"%s\" is not a table",
+                        RelationGetRelationName(rel));
 
-       while ((idxres = index_getnext(idxtgscan, ForwardScanDirection)) != NULL)
-       {
-               HeapTupleData tupledata;
-               Buffer          buffer;
-               HeapTuple       tuple;
-               Form_pg_trigger pg_trigger;
-               bytea      *val;
-               bytea      *newtgargs;
-               bool            isnull;
-               int                     tg_type;
-               bool            examine_pk;
-               bool            changed;
-               int                     tgnargs;
-               int                     i;
-               int                     newlen;
-               const char *arga[RI_MAX_ARGUMENTS];
-               const char *argp;
+       if (!pg_class_ownercheck(relOid, GetUserId()))
+               elog(ERROR, "ALTER TABLE: \"%s\": permission denied",
+                        RelationGetRelationName(rel));
 
-               tupledata.t_self = idxres->heap_iptr;
-               heap_fetch(tgrel, SnapshotNow, &tupledata, &buffer, idxtgscan);
-               pfree(idxres);
-               if (!tupledata.t_data)
-                       continue;
-               tuple = &tupledata;
-               pg_trigger = (Form_pg_trigger) GETSTRUCT(tuple);
-               tg_type = ri_trigger_type(pg_trigger->tgfoid);
-               if (tg_type == RI_TRIGGER_NONE)
-               {
-                       /* Not an RI trigger, forget it */
-                       ReleaseBuffer(buffer);
-                       continue;
-               }
+       /*
+        * lock the pg_class tuple for update (is that really needed?)
+        */
+       class_rel = heap_openr(RelationRelationName, RowExclusiveLock);
 
-               /*
-                * It is an RI trigger, so parse the tgargs bytea.
-                *
-                * NB: we assume the field will never be compressed or moved out of
-                * line; so does trigger.c ...
-                */
-               tgnargs = pg_trigger->tgnargs;
-               val = (bytea *) fastgetattr(tuple,
-                                                                       Anum_pg_trigger_tgargs,
-                                                                       tgrel->rd_att, &isnull);
-               if (isnull || tgnargs < RI_FIRST_ATTNAME_ARGNO ||
-                       tgnargs > RI_MAX_ARGUMENTS)
-               {
-                       /* This probably shouldn't happen, but ignore busted triggers */
-                       ReleaseBuffer(buffer);
-                       continue;
-               }
-               argp = (const char *) VARDATA(val);
-               for (i = 0; i < tgnargs; i++)
+       reltup = SearchSysCache(RELOID,
+                                                       ObjectIdGetDatum(relOid),
+                                                       0, 0, 0);
+       if (!HeapTupleIsValid(reltup))
+               elog(ERROR, "ALTER TABLE: relation \"%s\" not found",
+                        RelationGetRelationName(rel));
+       classtuple.t_self = reltup->t_self;
+       ReleaseSysCache(reltup);
+
+       switch (heap_mark4update(class_rel, &classtuple, &buffer))
+       {
+               case HeapTupleSelfUpdated:
+               case HeapTupleMayBeUpdated:
+                       break;
+               default:
+                       elog(ERROR, "couldn't lock pg_class tuple");
+       }
+       reltup = heap_copytuple(&classtuple);
+       ReleaseBuffer(buffer);
+
+       /*
+        * Is it already toasted?
+        */
+       if (((Form_pg_class) GETSTRUCT(reltup))->reltoastrelid != InvalidOid)
+       {
+               if (silent)
                {
-                       arga[i] = argp;
-                       argp += strlen(argp) + 1;
+                       heap_close(rel, NoLock);
+                       heap_close(class_rel, NoLock);
+                       heap_freetuple(reltup);
+                       return;
                }
 
-               /*
-                * Figure out which item(s) to look at.  If the trigger is
-                * primary-key type and attached to my rel, I should look at the
-                * PK fields; if it is foreign-key type and attached to my rel, I
-                * should look at the FK fields.  But the opposite rule holds when
-                * examining triggers found by tgconstrrel search.
-                */
-               examine_pk = (tg_type == RI_TRIGGER_PK) == (!fk_scan);
+               elog(ERROR, "ALTER TABLE: relation \"%s\" already has a toast table",
+                        RelationGetRelationName(rel));
+       }
 
-               changed = false;
-               if (update_relname)
-               {
-                       /* Change the relname if needed */
-                       i = examine_pk ? RI_PK_RELNAME_ARGNO : RI_FK_RELNAME_ARGNO;
-                       if (strcmp(arga[i], oldname) == 0)
-                       {
-                               arga[i] = newname;
-                               changed = true;
-                       }
-               }
-               else
+       /*
+        * Check to see whether the table actually needs a TOAST table.
+        */
+       if (!needs_toast_table(rel))
+       {
+               if (silent)
                {
-                       /* Change attname(s) if needed */
-                       i = examine_pk ? RI_FIRST_ATTNAME_ARGNO + RI_KEYPAIR_PK_IDX :
-                               RI_FIRST_ATTNAME_ARGNO + RI_KEYPAIR_FK_IDX;
-                       for (; i < tgnargs; i += 2)
-                       {
-                               if (strcmp(arga[i], oldname) == 0)
-                               {
-                                       arga[i] = newname;
-                                       changed = true;
-                               }
-                       }
+                       heap_close(rel, NoLock);
+                       heap_close(class_rel, NoLock);
+                       heap_freetuple(reltup);
+                       return;
                }
 
-               if (!changed)
-               {
-                       /* Don't need to update this tuple */
-                       ReleaseBuffer(buffer);
-                       continue;
-               }
+               elog(ERROR, "ALTER TABLE: relation \"%s\" does not need a toast table",
+                        RelationGetRelationName(rel));
+       }
 
-               /*
-                * Construct modified tgargs bytea.
-                */
-               newlen = VARHDRSZ;
-               for (i = 0; i < tgnargs; i++)
-                       newlen += strlen(arga[i]) + 1;
-               newtgargs = (bytea *) palloc(newlen);
-               VARATT_SIZEP(newtgargs) = newlen;
-               newlen = VARHDRSZ;
-               for (i = 0; i < tgnargs; i++)
-               {
-                       strcpy(((char *) newtgargs) + newlen, arga[i]);
-                       newlen += strlen(arga[i]) + 1;
-               }
+       /*
+        * Create the toast table and its index
+        */
+       sprintf(toast_relname, "pg_toast_%u", relOid);
+       sprintf(toast_idxname, "pg_toast_%u_index", relOid);
 
-               /*
-                * Build modified tuple.
-                */
-               for (i = 0; i < Natts_pg_trigger; i++)
-               {
-                       values[i] = (Datum) 0;
-                       replaces[i] = ' ';
-                       nulls[i] = ' ';
-               }
-               values[Anum_pg_trigger_tgargs - 1] = PointerGetDatum(newtgargs);
-               replaces[Anum_pg_trigger_tgargs - 1] = 'r';
+       /* this is pretty painful...  need a tuple descriptor */
+       tupdesc = CreateTemplateTupleDesc(3);
+       TupleDescInitEntry(tupdesc, (AttrNumber) 1,
+                                          "chunk_id",
+                                          OIDOID,
+                                          -1, 0, false);
+       TupleDescInitEntry(tupdesc, (AttrNumber) 2,
+                                          "chunk_seq",
+                                          INT4OID,
+                                          -1, 0, false);
+       TupleDescInitEntry(tupdesc, (AttrNumber) 3,
+                                          "chunk_data",
+                                          BYTEAOID,
+                                          -1, 0, false);
 
-               tuple = heap_modifytuple(tuple, tgrel, values, nulls, replaces);
+       /*
+        * Ensure that the toast table doesn't itself get toasted, or we'll be
+        * toast :-(.  This is essential for chunk_data because type bytea is
+        * toastable; hit the other two just to be sure.
+        */
+       tupdesc->attrs[0]->attstorage = 'p';
+       tupdesc->attrs[1]->attstorage = 'p';
+       tupdesc->attrs[2]->attstorage = 'p';
 
-               /*
-                * Now we can release hold on original tuple.
-                */
-               ReleaseBuffer(buffer);
+       /*
+        * Note: the toast relation is placed in the regular pg_toast namespace
+        * even if its master relation is a temp table.  There cannot be any
+        * naming collision, and the toast rel will be destroyed when its master
+        * is, so there's no need to handle the toast rel as temp.
+        */
+       toast_relid = heap_create_with_catalog(toast_relname,
+                                                                                  PG_TOAST_NAMESPACE,
+                                                                                  tupdesc,
+                                                                                  RELKIND_TOASTVALUE,
+                                                                                  false,
+                                                                                  true);
 
-               /*
-                * Update pg_trigger and its indexes
-                */
-               simple_heap_update(tgrel, &tuple->t_self, tuple);
+       /* make the toast relation visible, else index creation will fail */
+       CommandCounterIncrement();
 
-               {
-                       Relation        irelations[Num_pg_attr_indices];
+       /*
+        * Create unique index on chunk_id, chunk_seq.
+        *
+        * NOTE: the tuple toaster could actually function with a single-column
+        * index on chunk_id only.      However, it couldn't be unique then.  We
+        * want it to be unique as a check against the possibility of
+        * duplicate TOAST chunk OIDs.  Too, the index might be a little more
+        * efficient this way, since btree isn't all that happy with large
+        * numbers of equal keys.
+        */
 
-                       CatalogOpenIndices(Num_pg_trigger_indices, Name_pg_trigger_indices, irelations);
-                       CatalogIndexInsert(irelations, Num_pg_trigger_indices, tgrel, tuple);
-                       CatalogCloseIndices(Num_pg_trigger_indices, irelations);
-               }
+       indexInfo = makeNode(IndexInfo);
+       indexInfo->ii_NumIndexAttrs = 2;
+       indexInfo->ii_NumKeyAttrs = 2;
+       indexInfo->ii_KeyAttrNumbers[0] = 1;
+       indexInfo->ii_KeyAttrNumbers[1] = 2;
+       indexInfo->ii_Predicate = NIL;
+       indexInfo->ii_FuncOid = InvalidOid;
+       indexInfo->ii_Unique = true;
 
-               /* free up our scratch memory */
-               pfree(newtgargs);
-               heap_freetuple(tuple);
-       }
+       classObjectId[0] = OID_BTREE_OPS_OID;
+       classObjectId[1] = INT4_BTREE_OPS_OID;
 
-       index_endscan(idxtgscan);
-       index_close(irel);
+       toast_idxid = index_create(toast_relid, toast_idxname, indexInfo,
+                                                          BTREE_AM_OID, classObjectId,
+                                                          true, true);
 
-       heap_close(tgrel, RowExclusiveLock);
+       /*
+        * Update toast rel's pg_class entry to show that it has an index. The
+        * index OID is stored into the reltoastidxid field for easy access by
+        * the tuple toaster.
+        */
+       setRelhasindex(toast_relid, true, true, toast_idxid);
 
        /*
-        * Increment cmd counter to make updates visible; this is needed in
-        * case the same tuple has to be updated again by next pass (can
-        * happen in case of a self-referential FK relationship).
+        * Store the toast table's OID in the parent relation's tuple
+        */
+       ((Form_pg_class) GETSTRUCT(reltup))->reltoastrelid = toast_relid;
+       simple_heap_update(class_rel, &reltup->t_self, reltup);
+
+       /*
+        * Keep catalog indices current
+        */
+       CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, ridescs);
+       CatalogIndexInsert(ridescs, Num_pg_class_indices, class_rel, reltup);
+       CatalogCloseIndices(Num_pg_class_indices, ridescs);
+
+       heap_freetuple(reltup);
+
+       /*
+        * Close relations and make changes visible
         */
+       heap_close(class_rel, NoLock);
+       heap_close(rel, NoLock);
+
        CommandCounterIncrement();
 }
+
+/*
+ * Check to see whether the table needs a TOAST table. It does only if
+ * (1) there are any toastable attributes, and (2) the maximum length
+ * of a tuple could exceed TOAST_TUPLE_THRESHOLD.  (We don't want to
+ * create a toast table for something like "f1 varchar(20)".)
+ */
+static bool
+needs_toast_table(Relation rel)
+{
+       int32           data_length = 0;
+       bool            maxlength_unknown = false;
+       bool            has_toastable_attrs = false;
+       TupleDesc       tupdesc;
+       Form_pg_attribute *att;
+       int32           tuple_length;
+       int                     i;
+
+       tupdesc = rel->rd_att;
+       att = tupdesc->attrs;
+
+       for (i = 0; i < tupdesc->natts; i++)
+       {
+               data_length = att_align(data_length, att[i]->attlen, att[i]->attalign);
+               if (att[i]->attlen >= 0)
+               {
+                       /* Fixed-length types are never toastable */
+                       data_length += att[i]->attlen;
+               }
+               else
+               {
+                       int32           maxlen = type_maximum_size(att[i]->atttypid,
+                                                                                                  att[i]->atttypmod);
+
+                       if (maxlen < 0)
+                               maxlength_unknown = true;
+                       else
+                               data_length += maxlen;
+                       if (att[i]->attstorage != 'p')
+                               has_toastable_attrs = true;
+               }
+       }
+       if (!has_toastable_attrs)
+               return false;                   /* nothing to toast? */
+       if (maxlength_unknown)
+               return true;                    /* any unlimited-length attrs? */
+       tuple_length = MAXALIGN(offsetof(HeapTupleHeaderData, t_bits) +
+                                                       BITMAPLEN(tupdesc->natts)) +
+               MAXALIGN(data_length);
+       return (tuple_length > TOAST_TUPLE_THRESHOLD);
+}
index c00b4bebac8f9bdc5f1d6dbc5ead48107df4c307..b826297a7189aff893d02c2b15569e2c8ac3d9b1 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/commands/trigger.c,v 1.114 2002/04/19 16:36:08 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/commands/trigger.c,v 1.115 2002/04/26 19:29:47 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -503,6 +503,123 @@ RelationRemoveTriggers(Relation rel)
        heap_close(tgrel, RowExclusiveLock);
 }
 
+/*
+ *             renametrig              - changes the name of a trigger on a relation
+ *
+ *             trigger name is changed in trigger catalog.
+ *             No record of the previous name is kept.
+ *
+ *             get proper relrelation from relation catalog (if not arg)
+ *             scan trigger catalog
+ *                             for name conflict (within rel)
+ *                             for original trigger (if not arg)
+ *             modify tgname in trigger tuple
+ *             insert modified trigger in trigger catalog
+ *             delete original trigger from trigger catalog
+ */
+void
+renametrig(Oid relid,
+                  const char *oldname,
+                  const char *newname)
+{
+       Relation        targetrel;
+       Relation        tgrel;
+       HeapTuple       tuple;
+       SysScanDesc     tgscan;
+       ScanKeyData key;
+       bool            found = FALSE;
+       Relation        idescs[Num_pg_trigger_indices];
+
+       /*
+        * Grab an exclusive lock on the target table, which we will NOT
+        * release until end of transaction.
+        */
+       targetrel = heap_open(relid, AccessExclusiveLock);
+
+       /*
+        * Scan pg_trigger twice for existing triggers on relation.  We do this in
+        * order to ensure a trigger does not exist with newname (The unique index
+        * on tgrelid/tgname would complain anyway) and to ensure a trigger does
+        * exist with oldname.
+        *
+        * NOTE that this is cool only because we have AccessExclusiveLock on the
+        * relation, so the trigger set won't be changing underneath us.
+        */
+       tgrel = heap_openr(TriggerRelationName, RowExclusiveLock);
+
+       /*
+        * First pass -- look for name conflict
+        */
+       ScanKeyEntryInitialize(&key, 0,
+                                                  Anum_pg_trigger_tgrelid,
+                                                  F_OIDEQ,
+                                                  ObjectIdGetDatum(relid));
+       tgscan = systable_beginscan(tgrel, TriggerRelidNameIndex, true,
+                                                               SnapshotNow, 1, &key);
+       while (HeapTupleIsValid(tuple = systable_getnext(tgscan)))
+       {
+               Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(tuple);
+
+               if (namestrcmp(&(pg_trigger->tgname), newname) == 0)
+                       elog(ERROR, "renametrig: trigger %s already defined on relation %s",
+                                newname, RelationGetRelationName(targetrel));
+       }
+       systable_endscan(tgscan);
+
+       /*
+        * Second pass -- look for trigger existing with oldname and update
+        */
+       ScanKeyEntryInitialize(&key, 0,
+                                                  Anum_pg_trigger_tgrelid,
+                                                  F_OIDEQ,
+                                                  ObjectIdGetDatum(relid));
+       tgscan = systable_beginscan(tgrel, TriggerRelidNameIndex, true,
+                                                               SnapshotNow, 1, &key);
+       while (HeapTupleIsValid(tuple = systable_getnext(tgscan)))
+       {
+               Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(tuple);
+
+               if (namestrcmp(&(pg_trigger->tgname), oldname) == 0)
+               {
+                       /*
+                        * Update pg_trigger tuple with new tgname.
+                        * (Scribbling on tuple is OK because it's a copy...)
+                        */
+                       namestrcpy(&(pg_trigger->tgname), newname);
+                       simple_heap_update(tgrel, &tuple->t_self, tuple);
+
+                       /*
+                        * keep system catalog indices current
+                        */
+                       CatalogOpenIndices(Num_pg_trigger_indices, Name_pg_trigger_indices, idescs);
+                       CatalogIndexInsert(idescs, Num_pg_trigger_indices, tgrel, tuple);
+                       CatalogCloseIndices(Num_pg_trigger_indices, idescs);
+
+                       /*
+                        * Invalidate relation's relcache entry so that other
+                        * backends (and this one too!) are sent SI message to make them
+                        * rebuild relcache entries.
+                        */
+                       CacheInvalidateRelcache(relid);
+
+                       found = TRUE;
+                       break;
+               }
+       }
+       systable_endscan(tgscan);
+
+       heap_close(tgrel, RowExclusiveLock);
+
+       if (!found)
+               elog(ERROR, "renametrig: trigger %s not defined on relation %s",
+                        oldname, RelationGetRelationName(targetrel));
+
+       /*
+        * Close rel, but keep exclusive lock!
+        */
+       heap_close(targetrel, NoLock);
+}
+
 /*
  * Build trigger data to attach to the given relcache entry.
  *
index eb80acbd008396e13b71ef3d97d2112a04505e47..d9d219edd0087f612f170d0cbef25f0af27a5d18 100644 (file)
@@ -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/commands/user.c,v 1.96 2002/04/18 21:16:16 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/commands/user.c,v 1.97 2002/04/26 19:29:47 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -646,7 +646,7 @@ CreateUser(CreateUserStmt *stmt)
 /*
  * ALTER USER
  */
-extern void
+void
 AlterUser(AlterUserStmt *stmt)
 {
        Datum           new_record[Natts_pg_shadow];
index f3dfcd6b9b1af0c6c5afbb14725e36b6ceb618e1..1ce35eb164806fb9f17481129440510c97a68bbd 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: tablecmds.h,v 1.2 2002/04/24 02:48:55 momjian Exp $
+ * $Id: tablecmds.h,v 1.3 2002/04/26 19:29:47 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -61,8 +61,4 @@ extern void renameatt(Oid relid,
 extern void renamerel(Oid relid,
                  const char *newrelname);
 
-extern void renametrig(Oid relid,
-                 const char *oldname,
-                 const char *newname);
-
 #endif   /* TABLECMDS_H */
index 4166b47bb74dfd649fc3f760d1d94bbf71e8ec4d..4719bf0c914c5006c34aaad1fb167913e8e9e09a 100644 (file)
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: trigger.h,v 1.34 2002/04/01 22:36:13 tgl Exp $
+ * $Id: trigger.h,v 1.35 2002/04/26 19:29:47 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -105,6 +105,8 @@ extern void CreateTrigger(CreateTrigStmt *stmt);
 extern void DropTrigger(Oid relid, const char *trigname);
 extern void RelationRemoveTriggers(Relation rel);
 
+extern void renametrig(Oid relid, const char *oldname, const char *newname);
+
 extern void RelationBuildTriggers(Relation relation);
 
 extern void FreeTriggerDesc(TriggerDesc *trigdesc);