diff --git a/doc/src/sgml/ref/alter_table.sgml b/doc/src/sgml/ref/alter_table.sgml
index 3411c1f24a71451d844bf63ce1f44ecd6a2fd8e6..0c536c6d3b83cca23cf1765cc3be775c0aaac743 100644
--- a/doc/src/sgml/ref/alter_table.sgml
+++ b/doc/src/sgml/ref/alter_table.sgml
@@ -1,5 +1,5 @@
 <!--
-$PostgreSQL: pgsql/doc/src/sgml/ref/alter_table.sgml,v 1.66 2004/03/09 16:57:47 neilc Exp $
+$PostgreSQL: pgsql/doc/src/sgml/ref/alter_table.sgml,v 1.67 2004/03/23 19:35:15 tgl Exp $
 PostgreSQL documentation
 -->
 
@@ -150,12 +150,11 @@ ALTER TABLE <replaceable class="PARAMETER">name</replaceable>
     <term><literal>SET WITHOUT OIDS</literal></term>
     <listitem>
      <para>
-      This form removes the <literal>oid</literal> column from the
-      table.  Removing OIDs from a table does not occur immediately.
-      The space that the OID uses will be reclaimed when the row is
-      updated.  Without updating the row, both the space and the value
-      of the OID are kept indefinitely.  This is semantically similar
-      to the <literal>DROP COLUMN</literal> process.
+      This form removes the <literal>oid</literal> system column from the
+      table.  This is exactly equivalent to
+      <literal>DROP COLUMN oid RESTRICT</literal>,
+      except that it will not complain if there is already no
+      <literal>oid</literal> column.
      </para>
 
      <para>
diff --git a/src/backend/bootstrap/bootparse.y b/src/backend/bootstrap/bootparse.y
index e768fb24b4f478086202a252df97a1535e5d1d82..e6bde26d33726422705d52fd448a9deb82f21b62 100644
--- a/src/backend/bootstrap/bootparse.y
+++ b/src/backend/bootstrap/bootparse.y
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/bootstrap/bootparse.y,v 1.64 2004/01/07 18:56:25 neilc Exp $
+ *	  $PostgreSQL: pgsql/src/backend/bootstrap/bootparse.y,v 1.65 2004/03/23 19:35:16 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -197,6 +197,8 @@ Boot_CreateStmt:
 													  tupdesc,
 													  RELKIND_RELATION,
 													  $3,
+													  true,
+													  0,
 													  ONCOMMIT_NOOP,
 													  true);
 						elog(DEBUG4, "relation created with oid %u", id);
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 1fcc8e071da36ce7d612b60d617073f4cdc10341..ed68f9124d595cffe352f7f0cf7f9ff5cd7f76d8 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/catalog/heap.c,v 1.260 2004/02/15 21:01:39 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/catalog/heap.c,v 1.261 2004/03/23 19:35:16 tgl Exp $
  *
  *
  * INTERFACE ROUTINES
@@ -458,7 +458,9 @@ CheckAttributeType(const char *attname, Oid atttypid)
 static void
 AddNewAttributeTuples(Oid new_rel_oid,
 					  TupleDesc tupdesc,
-					  char relkind)
+					  char relkind,
+					  bool oidislocal,
+					  int oidinhcount)
 {
 	Form_pg_attribute *dpp;
 	int			i;
@@ -531,11 +533,18 @@ AddNewAttributeTuples(Oid new_rel_oid,
 									 false,
 									 ATTRIBUTE_TUPLE_SIZE,
 									 (void *) *dpp);
+				attStruct = (Form_pg_attribute) GETSTRUCT(tup);
 
 				/* Fill in the correct relation OID in the copied tuple */
-				attStruct = (Form_pg_attribute) GETSTRUCT(tup);
 				attStruct->attrelid = new_rel_oid;
 
+				/* Fill in correct inheritance info for the OID column */
+				if (attStruct->attnum == ObjectIdAttributeNumber)
+				{
+					attStruct->attislocal = oidislocal;
+					attStruct->attinhcount = oidinhcount;
+				}
+
 				/*
 				 * Unneeded since they should be OK in the constant data
 				 * anyway
@@ -713,6 +722,8 @@ heap_create_with_catalog(const char *relname,
 						 TupleDesc tupdesc,
 						 char relkind,
 						 bool shared_relation,
+						 bool oidislocal,
+						 int oidinhcount,
 						 OnCommitAction oncommit,
 						 bool allow_system_table_mods)
 {
@@ -786,7 +797,8 @@ heap_create_with_catalog(const char *relname,
 	 * now add tuples to pg_attribute for the attributes in our new
 	 * relation.
 	 */
-	AddNewAttributeTuples(new_rel_oid, new_rel_desc->rd_att, relkind);
+	AddNewAttributeTuples(new_rel_oid, new_rel_desc->rd_att, relkind,
+						  oidislocal, oidinhcount);
 
 	/*
 	 * make a dependency link to force the relation to be deleted if its
@@ -973,35 +985,46 @@ RemoveAttributeById(Oid relid, AttrNumber attnum)
 			 attnum, relid);
 	attStruct = (Form_pg_attribute) GETSTRUCT(tuple);
 
-	/* Mark the attribute as dropped */
-	attStruct->attisdropped = true;
+	if (attnum < 0)
+	{
+		/* System attribute (probably OID) ... just delete the row */
 
-	/*
-	 * Set the type OID to invalid.  A dropped attribute's type link
-	 * cannot be relied on (once the attribute is dropped, the type might
-	 * be too). Fortunately we do not need the type row --- the only
-	 * really essential information is the type's typlen and typalign,
-	 * which are preserved in the attribute's attlen and attalign.  We set
-	 * atttypid to zero here as a means of catching code that incorrectly
-	 * expects it to be valid.
-	 */
-	attStruct->atttypid = InvalidOid;
+		simple_heap_delete(attr_rel, &tuple->t_self);
+	}
+	else
+	{
+		/* Dropping user attributes is lots harder */
 
-	/* Remove any NOT NULL constraint the column may have */
-	attStruct->attnotnull = false;
+		/* Mark the attribute as dropped */
+		attStruct->attisdropped = true;
 
-	/* We don't want to keep stats for it anymore */
-	attStruct->attstattarget = 0;
+		/*
+		 * Set the type OID to invalid.  A dropped attribute's type link
+		 * cannot be relied on (once the attribute is dropped, the type might
+		 * be too). Fortunately we do not need the type row --- the only
+		 * really essential information is the type's typlen and typalign,
+		 * which are preserved in the attribute's attlen and attalign.  We set
+		 * atttypid to zero here as a means of catching code that incorrectly
+		 * expects it to be valid.
+		 */
+		attStruct->atttypid = InvalidOid;
 
-	/* Change the column name to something that isn't likely to conflict */
-	snprintf(newattname, sizeof(newattname),
-			 "........pg.dropped.%d........", attnum);
-	namestrcpy(&(attStruct->attname), newattname);
+		/* Remove any NOT NULL constraint the column may have */
+		attStruct->attnotnull = false;
 
-	simple_heap_update(attr_rel, &tuple->t_self, tuple);
+		/* We don't want to keep stats for it anymore */
+		attStruct->attstattarget = 0;
 
-	/* keep the system catalog indexes current */
-	CatalogUpdateIndexes(attr_rel, tuple);
+		/* Change the column name to something that isn't likely to conflict */
+		snprintf(newattname, sizeof(newattname),
+				 "........pg.dropped.%d........", attnum);
+		namestrcpy(&(attStruct->attname), newattname);
+
+		simple_heap_update(attr_rel, &tuple->t_self, tuple);
+
+		/* keep the system catalog indexes current */
+		CatalogUpdateIndexes(attr_rel, tuple);
+	}
 
 	/*
 	 * Because updating the pg_attribute row will trigger a relcache flush
@@ -1011,7 +1034,8 @@ RemoveAttributeById(Oid relid, AttrNumber attnum)
 
 	heap_close(attr_rel, RowExclusiveLock);
 
-	RemoveStatistics(rel, attnum);
+	if (attnum > 0)
+		RemoveStatistics(rel, attnum);
 
 	relation_close(rel, NoLock);
 }
diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c
index e39320d9edd9d50fb755a053f0975e6fc702288c..a10bbbd746cb258c27be34fd7a5c4d62a15bef18 100644
--- a/src/backend/commands/cluster.c
+++ b/src/backend/commands/cluster.c
@@ -11,7 +11,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/commands/cluster.c,v 1.119 2003/11/29 19:51:47 pgsql Exp $
+ *	  $PostgreSQL: pgsql/src/backend/commands/cluster.c,v 1.120 2004/03/23 19:35:16 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -501,6 +501,8 @@ make_new_heap(Oid OIDOldHeap, const char *NewName)
 										  tupdesc,
 										  OldHeap->rd_rel->relkind,
 										  OldHeap->rd_rel->relisshared,
+										  true,
+										  0,
 										  ONCOMMIT_NOOP,
 										  allowSystemTableMods);
 
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 25794ce93fec499b4fe76ade132dec8c6f6d853f..1a6bd2fdfc0d004dc14a565b5cdbaa2f2ffec0b7 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.100 2004/03/13 22:09:13 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.101 2004/03/23 19:35:16 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -77,7 +77,7 @@ static List *on_commits = NIL;
 
 
 static List *MergeAttributes(List *schema, List *supers, bool istemp,
-				List **supOids, List **supconstr, bool *supHasOids);
+				List **supOids, List **supconstr, int *supOidCount);
 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);
@@ -133,7 +133,8 @@ DefineRelation(CreateStmt *stmt, char relkind)
 	TupleDesc	descriptor;
 	List	   *inheritOids;
 	List	   *old_constraints;
-	bool		parentHasOids;
+	bool		localHasOids;
+	int			parentOidCount;
 	List	   *rawDefaults;
 	List	   *listptr;
 	int			i;
@@ -177,7 +178,7 @@ DefineRelation(CreateStmt *stmt, char relkind)
 	 */
 	schema = MergeAttributes(schema, stmt->inhRelations,
 							 stmt->relation->istemp,
-						 &inheritOids, &old_constraints, &parentHasOids);
+						 &inheritOids, &old_constraints, &parentOidCount);
 
 	/*
 	 * Create a relation descriptor from the relation schema and create
@@ -188,10 +189,8 @@ DefineRelation(CreateStmt *stmt, char relkind)
 	 */
 	descriptor = BuildDescForRelation(schema);
 
-	if (parentHasOids)
-		descriptor->tdhasoid = true;
-	else
-		descriptor->tdhasoid = interpretOidsOption(stmt->hasoids);
+	localHasOids = interpretOidsOption(stmt->hasoids);
+	descriptor->tdhasoid = (localHasOids || parentOidCount > 0);
 
 	if (old_constraints != NIL)
 	{
@@ -255,6 +254,8 @@ DefineRelation(CreateStmt *stmt, char relkind)
 										  descriptor,
 										  relkind,
 										  false,
+										  localHasOids,
+										  parentOidCount,
 										  stmt->oncommit,
 										  allowSystemTableMods);
 
@@ -436,7 +437,7 @@ TruncateRelation(const RangeVar *relation)
  * 'supOids' receives a 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.
+ * 'supOidCount' is set to the number of parents that have OID columns.
  *
  * Return value:
  * Completed schema list.
@@ -482,13 +483,13 @@ TruncateRelation(const RangeVar *relation)
  */
 static List *
 MergeAttributes(List *schema, List *supers, bool istemp,
-				List **supOids, List **supconstr, bool *supHasOids)
+				List **supOids, List **supconstr, int *supOidCount)
 {
 	List	   *entry;
 	List	   *inhSchema = NIL;
 	List	   *parentOids = NIL;
 	List	   *constraints = NIL;
-	bool		parentHasOids = false;
+	int			parentsWithOids = 0;
 	bool		have_bogus_defaults = false;
 	char	   *bogus_marker = "Bogus!";		/* marks conflicting
 												 * defaults */
@@ -566,7 +567,8 @@ MergeAttributes(List *schema, List *supers, bool istemp,
 
 		parentOids = lappendo(parentOids, RelationGetRelid(relation));
 
-		parentHasOids |= relation->rd_rel->relhasoids;
+		if (relation->rd_rel->relhasoids)
+			parentsWithOids++;
 
 		tupleDesc = RelationGetDescr(relation);
 		constr = tupleDesc->constr;
@@ -825,7 +827,7 @@ MergeAttributes(List *schema, List *supers, bool istemp,
 
 	*supOids = parentOids;
 	*supconstr = constraints;
-	*supHasOids = parentHasOids;
+	*supOidCount = parentsWithOids;
 	return schema;
 }
 
@@ -2422,92 +2424,30 @@ AlterTableAlterColumnFlags(Oid myrelid, bool recurse,
 }
 
 /*
- * ALTER TABLE SET {WITHOUT} OIDS
+ * ALTER TABLE SET WITH/WITHOUT OIDS
  */
 void
-AlterTableAlterOids(Oid myrelid, bool recurse, bool setOid)
+AlterTableAlterOids(Oid myrelid, bool setOid, bool recurse,
+					DropBehavior behavior)
 {
 	Relation	rel;
-	Relation	class_rel;
-	HeapTuple	tuple;
-	Form_pg_class tuple_class;
 
 	rel = heap_open(myrelid, AccessExclusiveLock);
 
-	if (rel->rd_rel->relkind != RELKIND_RELATION)
-		ereport(ERROR,
-				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
-				 errmsg("\"%s\" is not a table",
-						RelationGetRelationName(rel))));
-
-	/* Permissions checks */
-	if (!pg_class_ownercheck(myrelid, GetUserId()))
-		aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
-					   RelationGetRelationName(rel));
-
-	if (!allowSystemTableMods && IsSystemRelation(rel))
-		ereport(ERROR,
-				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-				 errmsg("permission denied: \"%s\" is a system catalog",
-						RelationGetRelationName(rel))));
-
-	/*
-	 * Propagate to children if desired
-	 */
-	if (recurse)
-	{
-		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;
-
-			AlterTableAlterOids(childrelid, false, setOid);
-		}
-	}
-
-	/* Do the thing on this relation */
-	class_rel = heap_openr(RelationRelationName, RowExclusiveLock);
-
-	tuple = SearchSysCacheCopy(RELOID,
-							   ObjectIdGetDatum(myrelid),
-							   0, 0, 0);
-	if (!HeapTupleIsValid(tuple))
-		elog(ERROR, "cache lookup failed for relation %u", myrelid);
-	tuple_class = (Form_pg_class) GETSTRUCT(tuple);
-
 	/*
 	 * check to see if we actually need to change anything
 	 */
-	if (tuple_class->relhasoids == setOid)
+	if (rel->rd_rel->relhasoids == setOid)
 	{
-		heap_close(class_rel, RowExclusiveLock);
 		heap_close(rel, NoLock);	/* close rel, but keep lock! */
 		return;
 	}
 
-	tuple_class->relhasoids = setOid;
-	simple_heap_update(class_rel, &tuple->t_self, tuple);
-
-	/* Keep the catalog indexes up to date */
-	CatalogUpdateIndexes(class_rel, tuple);
-
 	if (setOid)
 	{
 		/*
-		 * TODO: Generate the now required OID pg_attribute entry
+		 * TODO: Generate the now required OID pg_attribute entry, and
+		 * modify physical rows to have OIDs.
 		 */
 		ereport(ERROR,
 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
@@ -2515,34 +2455,10 @@ AlterTableAlterOids(Oid myrelid, bool recurse, bool setOid)
 	}
 	else
 	{
-		HeapTuple	atttup;
-		Relation	attrel;
-
-		/* Add / Remove the oid record from pg_attribute */
-		attrel = heap_open(RelOid_pg_attribute, RowExclusiveLock);
-
-		/*
-		 * Oids are being removed from the relation, so we need to remove
-		 * the oid pg_attribute record relating.
-		 */
-		atttup = SearchSysCache(ATTNUM,
-								ObjectIdGetDatum(myrelid),
-								Int16GetDatum(ObjectIdAttributeNumber),
-								0, 0);
-		if (!HeapTupleIsValid(atttup))
-			elog(ERROR, "cache lookup failed for attribute %d of relation %u",
-				 ObjectIdAttributeNumber, myrelid);
-
-		simple_heap_delete(attrel, &atttup->t_self);
-
-		ReleaseSysCache(atttup);
+		heap_close(rel, NoLock);	/* close rel, but keep lock! */
 
-		heap_close(attrel, RowExclusiveLock);
+		AlterTableDropColumn(myrelid, recurse, false, "oid", behavior);
 	}
-
-	heap_close(class_rel, RowExclusiveLock);
-
-	heap_close(rel, NoLock);	/* close rel, but keep lock! */
 }
 
 /*
@@ -2555,7 +2471,8 @@ AlterTableDropColumn(Oid myrelid, bool recurse, bool recursing,
 {
 	Relation	rel;
 	AttrNumber	attnum;
-	TupleDesc	tupleDesc;
+	HeapTuple	tuple;
+	Form_pg_attribute targetatt;
 	ObjectAddress object;
 
 	rel = heap_open(myrelid, AccessExclusiveLock);
@@ -2580,29 +2497,32 @@ AlterTableDropColumn(Oid myrelid, bool recurse, bool recursing,
 	/*
 	 * get the number of the attribute
 	 */
-	attnum = get_attnum(myrelid, colName);
-	if (attnum == InvalidAttrNumber)
+	tuple = SearchSysCacheAttName(myrelid, colName);
+	if (!HeapTupleIsValid(tuple))
 		ereport(ERROR,
 				(errcode(ERRCODE_UNDEFINED_COLUMN),
-			 errmsg("column \"%s\" of relation \"%s\" does not exist",
-					colName, RelationGetRelationName(rel))));
+				 errmsg("column \"%s\" of relation \"%s\" does not exist",
+						colName, RelationGetRelationName(rel))));
+	targetatt = (Form_pg_attribute) GETSTRUCT(tuple);
 
-	/* Can't drop a system attribute */
-	/* XXX perhaps someday allow dropping OID? */
-	if (attnum < 0)
+	attnum = targetatt->attnum;
+
+	/* Can't drop a system attribute, except OID */
+	if (attnum <= 0 && attnum != ObjectIdAttributeNumber)
 		ereport(ERROR,
 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 				 errmsg("cannot drop system column \"%s\"",
 						colName)));
 
 	/* Don't drop inherited columns */
-	tupleDesc = RelationGetDescr(rel);
-	if (tupleDesc->attrs[attnum - 1]->attinhcount > 0 && !recursing)
+	if (targetatt->attinhcount > 0 && !recursing)
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
 				 errmsg("cannot drop inherited column \"%s\"",
 						colName)));
 
+	ReleaseSysCache(tuple);
+
 	/*
 	 * If we are asked to drop ONLY in this table (no recursion), we need
 	 * to mark the inheritors' attribute as locally defined rather than
@@ -2622,7 +2542,6 @@ AlterTableDropColumn(Oid myrelid, bool recurse, bool recursing,
 		{
 			Oid			childrelid = lfirsto(child);
 			Relation	childrel;
-			HeapTuple	tuple;
 			Form_pg_attribute childatt;
 
 			childrel = heap_open(childrelid, AccessExclusiveLock);
@@ -2670,7 +2589,6 @@ AlterTableDropColumn(Oid myrelid, bool recurse, bool recursing,
 		{
 			Oid			childrelid = lfirsto(child);
 			Relation	childrel;
-			HeapTuple	tuple;
 			Form_pg_attribute childatt;
 
 			if (childrelid == myrelid)
@@ -2712,7 +2630,7 @@ AlterTableDropColumn(Oid myrelid, bool recurse, bool recursing,
 	}
 
 	/*
-	 * Perform the actual deletion
+	 * Perform the actual column deletion
 	 */
 	object.classId = RelOid_pg_class;
 	object.objectId = myrelid;
@@ -2720,6 +2638,32 @@ AlterTableDropColumn(Oid myrelid, bool recurse, bool recursing,
 
 	performDeletion(&object, behavior);
 
+	/*
+	 * If we dropped the OID column, must adjust pg_class.relhasoids
+	 */
+	if (attnum == ObjectIdAttributeNumber)
+	{
+		Relation	class_rel;
+		Form_pg_class tuple_class;
+
+		class_rel = heap_openr(RelationRelationName, RowExclusiveLock);
+
+		tuple = SearchSysCacheCopy(RELOID,
+								   ObjectIdGetDatum(myrelid),
+								   0, 0, 0);
+		if (!HeapTupleIsValid(tuple))
+			elog(ERROR, "cache lookup failed for relation %u", myrelid);
+		tuple_class = (Form_pg_class) GETSTRUCT(tuple);
+
+		tuple_class->relhasoids = false;
+		simple_heap_update(class_rel, &tuple->t_self, tuple);
+
+		/* Keep the catalog indexes up to date */
+		CatalogUpdateIndexes(class_rel, tuple);
+
+		heap_close(class_rel, RowExclusiveLock);
+	}
+
 	heap_close(rel, NoLock);	/* close rel, but keep lock! */
 }
 
@@ -4171,6 +4115,8 @@ AlterTableCreateToastTable(Oid relOid, bool silent)
 										   tupdesc,
 										   RELKIND_TOASTVALUE,
 										   shared_relation,
+										   true,
+										   0,
 										   ONCOMMIT_NOOP,
 										   true);
 
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index caae6e880e5ed3edb88072336cedd1260bbd7176..13b38d3752994d3dd794625571774b36b0371f12 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -26,7 +26,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.229 2004/03/02 18:56:15 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.230 2004/03/23 19:35:16 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -797,6 +797,8 @@ InitPlan(QueryDesc *queryDesc, bool explainOnly)
 												  tupdesc,
 												  RELKIND_RELATION,
 												  false,
+												  true,
+												  0,
 												  ONCOMMIT_NOOP,
 												  allowSystemTableMods);
 
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 8a23fcc70ef66908f409d8bc2ee0a232d0ad115e..11b3ff168ac65f6226786bd9a78b1fb31e03ca2e 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -10,7 +10,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.210 2004/02/10 01:55:26 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.211 2004/03/23 19:35:17 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -608,10 +608,11 @@ ProcessUtility(Node *parsetree,
 					case 'L':	/* CLUSTER ON */
 						AlterTableClusterOn(relid, stmt->name);
 						break;
-					case 'o':	/* ADD OIDS */
+					case 'o':	/* SET WITHOUT OIDS */
 						AlterTableAlterOids(relid,
+											false,
 							  interpretInhOption(stmt->relation->inhOpt),
-											false);
+											DROP_RESTRICT);
 						break;
 					default:	/* oops */
 						elog(ERROR, "unrecognized alter table type: %d",
diff --git a/src/include/catalog/heap.h b/src/include/catalog/heap.h
index 741b0a129e654040c954ee8eb5ab9723d1afb44f..f635ef1bd09d345e6cd33bd69be5fa3c3188f6ba 100644
--- a/src/include/catalog/heap.h
+++ b/src/include/catalog/heap.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/heap.h,v 1.64 2004/02/15 21:01:39 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/heap.h,v 1.65 2004/03/23 19:35:17 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -41,6 +41,8 @@ extern Oid heap_create_with_catalog(const char *relname,
 						 TupleDesc tupdesc,
 						 char relkind,
 						 bool shared_relation,
+						 bool oidislocal,
+						 int oidinhcount,
 						 OnCommitAction oncommit,
 						 bool allow_system_table_mods);
 
diff --git a/src/include/commands/tablecmds.h b/src/include/commands/tablecmds.h
index 1f71eebe8ec37082ac38857c95638e9af1a18643..889b4dfa626bd5bf9ce709a83f750c2144bd0c85 100644
--- a/src/include/commands/tablecmds.h
+++ b/src/include/commands/tablecmds.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/commands/tablecmds.h,v 1.14 2003/11/29 22:40:59 pgsql Exp $
+ * $PostgreSQL: pgsql/src/include/commands/tablecmds.h,v 1.15 2004/03/23 19:35:17 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -49,7 +49,8 @@ extern void AlterTableCreateToastTable(Oid relOid, bool silent);
 
 extern void AlterTableOwner(Oid relationOid, int32 newOwnerSysId);
 
-extern void AlterTableAlterOids(Oid myrelid, bool recurse, bool setOid);
+extern void AlterTableAlterOids(Oid myrelid, bool setOid, bool recurse,
+								DropBehavior behavior);
 
 extern Oid	DefineRelation(CreateStmt *stmt, char relkind);
 
diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out
index 0396351f1ff413bd0df2f7dfb3a32148a2c27d26..089651c7278b0c35f75884e617ef6e5aabe67551 100644
--- a/src/test/regress/expected/alter_table.out
+++ b/src/test/regress/expected/alter_table.out
@@ -688,9 +688,11 @@ delete from atacc1;
 -- try dropping a non-existent column, should fail
 alter table atacc1 drop bar;
 ERROR:  column "bar" of relation "atacc1" does not exist
--- try dropping the oid column, should fail
+-- try dropping the oid column, should succeed
 alter table atacc1 drop oid;
-ERROR:  cannot drop system column "oid"
+-- try dropping the xmin column, should fail
+alter table atacc1 drop xmin;
+ERROR:  cannot drop system column "xmin"
 -- try creating a view and altering that, should fail
 create view myview as select * from atacc1;
 select * from myview;
diff --git a/src/test/regress/sql/alter_table.sql b/src/test/regress/sql/alter_table.sql
index 749af267107b1b2d23569552ca7efe23669bb78e..dcd968a0b6560eb719aa7d8f9e5c1b5501eeb536 100644
--- a/src/test/regress/sql/alter_table.sql
+++ b/src/test/regress/sql/alter_table.sql
@@ -632,9 +632,12 @@ delete from atacc1;
 -- try dropping a non-existent column, should fail
 alter table atacc1 drop bar;
 
--- try dropping the oid column, should fail
+-- try dropping the oid column, should succeed
 alter table atacc1 drop oid;
 
+-- try dropping the xmin column, should fail
+alter table atacc1 drop xmin;
+
 -- try creating a view and altering that, should fail
 create view myview as select * from atacc1;
 select * from myview;