diff --git a/doc/src/sgml/ref/alter_table.sgml b/doc/src/sgml/ref/alter_table.sgml
index a86ffbba811156d27b698277aee98e193216baaa..2bfa5833134c8f8b4a67e8071e49fbcb7a220021 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.103 2009/02/09 20:57:59 alvherre Exp $
+$PostgreSQL: pgsql/doc/src/sgml/ref/alter_table.sgml,v 1.104 2009/02/11 21:11:15 tgl Exp $
 PostgreSQL documentation
 -->
 
@@ -52,6 +52,7 @@ where <replaceable class="PARAMETER">action</replaceable> is one of:
     ENABLE ALWAYS RULE <replaceable class="PARAMETER">rewrite_rule_name</replaceable>
     CLUSTER ON <replaceable class="PARAMETER">index_name</replaceable>
     SET WITHOUT CLUSTER
+    SET WITH OIDS
     SET WITHOUT OIDS
     SET ( <replaceable class="PARAMETER">storage_parameter</replaceable> = <replaceable class="PARAMETER">value</replaceable> [, ... ] )
     RESET ( <replaceable class="PARAMETER">storage_parameter</replaceable> [, ... ] )
@@ -185,7 +186,7 @@ where <replaceable class="PARAMETER">action</replaceable> is one of:
     <listitem>
      <para>
       This form adds a new constraint to a table using the same syntax as
-      <xref linkend="SQL-CREATETABLE" endterm="SQL-CREATETABLE-TITLE">. 
+      <xref linkend="SQL-CREATETABLE" endterm="SQL-CREATETABLE-TITLE">.
      </para>
     </listitem>
    </varlistentry>
@@ -217,10 +218,10 @@ where <replaceable class="PARAMETER">action</replaceable> is one of:
       The trigger firing mechanism is also affected by the configuration
       variable <xref linkend="guc-session-replication-role">. Simply enabled
       triggers will fire when the replication role is <quote>origin</>
-      (the default) or <quote>local</>. Triggers configured <literal>ENABLE REPLICA</literal>
-      will only fire if the session is in <quote>replica</> mode and triggers 
-      configured <literal>ENABLE ALWAYS</literal> will fire regardless of the current replication 
-      mode.
+      (the default) or <quote>local</>. Triggers configured as <literal>ENABLE
+      REPLICA</literal> will only fire if the session is in <quote>replica</>
+      mode, and triggers configured as <literal>ENABLE ALWAYS</literal> will
+      fire regardless of the current replication mode.
      </para>
     </listitem>
    </varlistentry>
@@ -243,7 +244,7 @@ where <replaceable class="PARAMETER">action</replaceable> is one of:
     <term><literal>CLUSTER</literal></term>
     <listitem>
      <para>
-      This form selects the default index for future 
+      This form selects the default index for future
       <xref linkend="SQL-CLUSTER" endterm="sql-cluster-title">
       operations.  It does not actually re-cluster the table.
      </para>
@@ -262,6 +263,23 @@ where <replaceable class="PARAMETER">action</replaceable> is one of:
     </listitem>
    </varlistentry>
 
+   <varlistentry>
+    <term><literal>SET WITH OIDS</literal></term>
+    <listitem>
+     <para>
+      This form adds an <literal>oid</literal> system column to the
+      table (see <xref linkend="ddl-system-columns">).
+      It does nothing if the table already has OIDs.
+     </para>
+
+     <para>
+      Note that this is not equivalent to <literal>ADD COLUMN oid oid</>;
+      that would add a normal column that happened to be named
+      <literal>oid</>, not a system column.
+     </para>
+    </listitem>
+   </varlistentry>
+
    <varlistentry>
     <term><literal>SET WITHOUT OIDS</literal></term>
     <listitem>
@@ -272,12 +290,6 @@ where <replaceable class="PARAMETER">action</replaceable> is one of:
       except that it will not complain if there is already no
       <literal>oid</literal> column.
      </para>
-
-     <para>
-      Note that there is no variant of <command>ALTER TABLE</command>
-      that allows OIDs to be restored to a table once they have been
-      removed.
-     </para>
     </listitem>
    </varlistentry>
 
@@ -302,7 +314,8 @@ where <replaceable class="PARAMETER">action</replaceable> is one of:
        in the <literal>WITH (<replaceable
        class="PARAMETER">storage_parameter</>)</literal> syntax,
        <command>ALTER TABLE</> does not treat <literal>OIDS</> as a
-       storage parameter.
+       storage parameter.  Instead use the <literal>SET WITH OIDS</>
+       and <literal>SET WITHOUT OIDS</> forms to change OID status.
       </para>
      </note>
     </listitem>
@@ -373,7 +386,7 @@ where <replaceable class="PARAMETER">action</replaceable> is one of:
       moves the data file(s) associated with the table to the new tablespace.
       Indexes on the table, if any, are not moved; but they can be moved
       separately with additional <literal>SET TABLESPACE</literal> commands.
-      See also 
+      See also
       <xref linkend="SQL-CREATETABLESPACE" endterm="sql-createtablespace-title">.
      </para>
     </listitem>
@@ -637,7 +650,8 @@ where <replaceable class="PARAMETER">action</replaceable> is one of:
     Adding a column with a non-null default or changing the type of an
     existing column will require the entire table to be rewritten.  This
     might take a significant amount of time for a large table; and it will
-    temporarily require double the disk space.
+    temporarily require double the disk space.  Adding or removing a system
+    <literal>oid</> column likewise requires rewriting the entire table.
    </para>
 
    <para>
@@ -656,9 +670,11 @@ where <replaceable class="PARAMETER">action</replaceable> is one of:
     the column, but simply makes it invisible to SQL operations.  Subsequent
     insert and update operations in the table will store a null value for the
     column. Thus, dropping a column is quick but it will not immediately
-    reduce the on-disk size of your table, as the space occupied 
+    reduce the on-disk size of your table, as the space occupied
     by the dropped column is not reclaimed.  The space will be
-    reclaimed over time as existing rows are updated.
+    reclaimed over time as existing rows are updated.  (These statements do
+    not apply when dropping the system <literal>oid</> column; that is done
+    with an immediate rewrite.)
    </para>
 
    <para>
@@ -806,21 +822,21 @@ ALTER TABLE distributors ALTER COLUMN street DROP NOT NULL;
 </programlisting>
   </para>
 
-  <para> 
+  <para>
    To add a check constraint to a table and all its children:
 <programlisting>
 ALTER TABLE distributors ADD CONSTRAINT zipchk CHECK (char_length(zipcode) = 5);
 </programlisting>
   </para>
 
-  <para> 
+  <para>
    To remove a check constraint from a table and all its children:
 <programlisting>
 ALTER TABLE distributors DROP CONSTRAINT zipchk;
 </programlisting>
   </para>
 
-  <para> 
+  <para>
    To remove a check constraint from a table only:
 <programlisting>
 ALTER TABLE ONLY distributors DROP CONSTRAINT zipchk;
@@ -828,21 +844,21 @@ ALTER TABLE ONLY distributors DROP CONSTRAINT zipchk;
    (The check constraint remains in place for any child tables.)
   </para>
 
-  <para> 
+  <para>
    To add a foreign key constraint to a table:
 <programlisting>
 ALTER TABLE distributors ADD CONSTRAINT distfk FOREIGN KEY (address) REFERENCES addresses (address) MATCH FULL;
 </programlisting>
   </para>
 
-  <para> 
+  <para>
    To add a (multicolumn) unique constraint to a table:
 <programlisting>
 ALTER TABLE distributors ADD CONSTRAINT dist_id_zipcode_key UNIQUE (dist_id, zipcode);
 </programlisting>
   </para>
 
-  <para> 
+  <para>
    To add an automatically named primary key constraint to a table, noting
    that a table can only ever have one primary key:
 <programlisting>
@@ -850,14 +866,14 @@ ALTER TABLE distributors ADD PRIMARY KEY (dist_id);
 </programlisting>
   </para>
 
-  <para> 
+  <para>
    To move a table to a different tablespace:
 <programlisting>
 ALTER TABLE distributors SET TABLESPACE fasttablespace;
 </programlisting>
   </para>
 
-  <para> 
+  <para>
    To move a table to a different schema:
 <programlisting>
 ALTER TABLE myschema.distributors SET SCHEMA yourschema;
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index cda87e28b307b9f4610790f85c287a88a34503e7..14c9f6b8bb0fc302c206d7616e75b50476b73fab 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.279 2009/02/02 19:31:38 alvherre Exp $
+ *	  $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.280 2009/02/11 21:11:16 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -138,6 +138,7 @@ typedef struct AlteredTableInfo
 	List	   *constraints;	/* List of NewConstraint */
 	List	   *newvals;		/* List of NewColumnValue */
 	bool		new_notnull;	/* T if we added new NOT NULL constraints */
+	bool		new_changeoids;	/* T if we added/dropped the OID column */
 	Oid			newTableSpace;	/* new tablespace; 0 means no change */
 	/* Objects to rebuild after completing ALTER TYPE operations */
 	List	   *changedConstraintOids;	/* OIDs of constraints to rebuild */
@@ -269,8 +270,10 @@ static void ATOneLevelRecursion(List **wqueue, Relation rel,
 static void ATPrepAddColumn(List **wqueue, Relation rel, bool recurse,
 				AlterTableCmd *cmd);
 static void ATExecAddColumn(AlteredTableInfo *tab, Relation rel,
-				ColumnDef *colDef);
+				ColumnDef *colDef, bool isOid);
 static void add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid);
+static void ATPrepAddOids(List **wqueue, Relation rel, bool recurse,
+				AlterTableCmd *cmd);
 static void ATExecDropNotNull(Relation rel, const char *colName);
 static void ATExecSetNotNull(AlteredTableInfo *tab, Relation rel,
 				 const char *colName);
@@ -282,7 +285,7 @@ static void ATExecSetStatistics(Relation rel, const char *colName,
 					Node *newValue);
 static void ATExecSetStorage(Relation rel, const char *colName,
 				 Node *newValue);
-static void ATExecDropColumn(Relation rel, const char *colName,
+static void ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
 				 DropBehavior behavior,
 				 bool recurse, bool recursing);
 static void ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
@@ -2452,6 +2455,13 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
 			/* No command-specific prep needed */
 			pass = AT_PASS_MISC;
 			break;
+		case AT_AddOids:		/* SET WITH OIDS */
+			ATSimplePermissions(rel, false);
+			/* Performs own recursion */
+			if (!rel->rd_rel->relhasoids || recursing)
+				ATPrepAddOids(wqueue, rel, recurse, cmd);
+			pass = AT_PASS_ADD_COL;
+			break;
 		case AT_DropOids:		/* SET WITHOUT OIDS */
 			ATSimplePermissions(rel, false);
 			/* Performs own recursion */
@@ -2589,7 +2599,7 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
 	{
 		case AT_AddColumn:		/* ADD COLUMN */
 		case AT_AddColumnToView: /* add column via CREATE OR REPLACE VIEW */
-			ATExecAddColumn(tab, rel, (ColumnDef *) cmd->def);
+			ATExecAddColumn(tab, rel, (ColumnDef *) cmd->def, false);
 			break;
 		case AT_ColumnDefault:	/* ALTER COLUMN DEFAULT */
 			ATExecColumnDefault(rel, cmd->name, cmd->def);
@@ -2607,10 +2617,12 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
 			ATExecSetStorage(rel, cmd->name, cmd->def);
 			break;
 		case AT_DropColumn:		/* DROP COLUMN */
-			ATExecDropColumn(rel, cmd->name, cmd->behavior, false, false);
+			ATExecDropColumn(wqueue, rel, cmd->name,
+							 cmd->behavior, false, false);
 			break;
 		case AT_DropColumnRecurse:		/* DROP COLUMN with recursion */
-			ATExecDropColumn(rel, cmd->name, cmd->behavior, true, false);
+			ATExecDropColumn(wqueue, rel, cmd->name,
+							 cmd->behavior, true, false);
 			break;
 		case AT_AddIndex:		/* ADD INDEX */
 			ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, false);
@@ -2644,6 +2656,11 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
 		case AT_DropCluster:	/* SET WITHOUT CLUSTER */
 			ATExecDropCluster(rel);
 			break;
+		case AT_AddOids:		/* SET WITH OIDS */
+			/* Use the ADD COLUMN code, unless prep decided to do nothing */
+			if (cmd->def != NULL)
+				ATExecAddColumn(tab, rel, (ColumnDef *) cmd->def, true);
+			break;
 		case AT_DropOids:		/* SET WITHOUT OIDS */
 
 			/*
@@ -2748,9 +2765,9 @@ ATRewriteTables(List **wqueue)
 
 		/*
 		 * We only need to rewrite the table if at least one column needs to
-		 * be recomputed.
+		 * be recomputed, or we are adding/removing the OID column.
 		 */
-		if (tab->newvals != NIL)
+		if (tab->newvals != NIL || tab->new_changeoids)
 		{
 			/* Build a temporary relation and copy data */
 			Oid			OIDNewHeap;
@@ -2976,8 +2993,6 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap)
 	{
 		NewColumnValue *ex = lfirst(l);
 
-		needscan = true;
-
 		ex->exprstate = ExecPrepareExpr((Expr *) ex->expr, estate);
 	}
 
@@ -3000,7 +3015,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap)
 			needscan = true;
 	}
 
-	if (needscan)
+	if (newrel || needscan)
 	{
 		ExprContext *econtext;
 		Datum	   *values;
@@ -3479,7 +3494,7 @@ ATPrepAddColumn(List **wqueue, Relation rel, bool recurse,
 
 static void
 ATExecAddColumn(AlteredTableInfo *tab, Relation rel,
-				ColumnDef *colDef)
+				ColumnDef *colDef, bool isOid)
 {
 	Oid			myrelid = RelationGetRelid(rel);
 	Relation	pgclass,
@@ -3512,7 +3527,7 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel,
 			Oid			ctypeId;
 			int32		ctypmod;
 
-			/* Okay if child matches by type */
+			/* Child column must match by type */
 			ctypeId = typenameTypeId(NULL, colDef->typename, &ctypmod);
 			if (ctypeId != childatt->atttypid ||
 				ctypmod != childatt->atttypmod)
@@ -3521,6 +3536,13 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel,
 						 errmsg("child table \"%s\" has different type for column \"%s\"",
 							RelationGetRelationName(rel), colDef->colname)));
 
+			/* If it's OID, child column must actually be OID */
+			if (isOid && childatt->attnum != ObjectIdAttributeNumber)
+				ereport(ERROR,
+						(errcode(ERRCODE_DATATYPE_MISMATCH),
+						 errmsg("child table \"%s\" has a conflicting \"%s\" column",
+							RelationGetRelationName(rel), colDef->colname)));
+
 			/* Bump the existing child att's inhcount */
 			childatt->attinhcount++;
 			simple_heap_update(attrdesc, &tuple->t_self, tuple);
@@ -3560,12 +3582,18 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel,
 				 errmsg("column \"%s\" of relation \"%s\" already exists",
 						colDef->colname, RelationGetRelationName(rel))));
 
-	newattnum = ((Form_pg_class) GETSTRUCT(reltup))->relnatts + 1;
-	if (newattnum > MaxHeapAttributeNumber)
-		ereport(ERROR,
-				(errcode(ERRCODE_TOO_MANY_COLUMNS),
-				 errmsg("tables can have at most %d columns",
-						MaxHeapAttributeNumber)));
+	/* Determine the new attribute's number */
+	if (isOid)
+		newattnum = ObjectIdAttributeNumber;
+	else
+	{
+		newattnum = ((Form_pg_class) GETSTRUCT(reltup))->relnatts + 1;
+		if (newattnum > MaxHeapAttributeNumber)
+			ereport(ERROR,
+					(errcode(ERRCODE_TOO_MANY_COLUMNS),
+					 errmsg("tables can have at most %d columns",
+							MaxHeapAttributeNumber)));
+	}
 
 	typeTuple = typenameType(NULL, colDef->typename, &typmod);
 	tform = (Form_pg_type) GETSTRUCT(typeTuple);
@@ -3578,7 +3606,7 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel,
 	attribute.attrelid = myrelid;
 	namestrcpy(&(attribute.attname), colDef->colname);
 	attribute.atttypid = typeOid;
-	attribute.attstattarget = -1;
+	attribute.attstattarget = (newattnum > 0) ? -1 : 0;
 	attribute.attlen = tform->typlen;
 	attribute.attcacheoff = -1;
 	attribute.atttypmod = typmod;
@@ -3601,9 +3629,12 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel,
 	heap_close(attrdesc, RowExclusiveLock);
 
 	/*
-	 * Update number of attributes in pg_class tuple
+	 * Update pg_class tuple as appropriate
 	 */
-	((Form_pg_class) GETSTRUCT(reltup))->relnatts = newattnum;
+	if (isOid)
+		((Form_pg_class) GETSTRUCT(reltup))->relhasoids = true;
+	else
+		((Form_pg_class) GETSTRUCT(reltup))->relnatts = newattnum;
 
 	simple_heap_update(pgclass, &reltup->t_self, reltup);
 
@@ -3665,7 +3696,7 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel,
 	 * defaults, not even for domain-typed columns.  And in any case we mustn't
 	 * invoke Phase 3 on a view, since it has no storage.
 	 */
-	if (relkind != RELKIND_VIEW)
+	if (relkind != RELKIND_VIEW && attribute.attnum > 0)
 	{
 		defval = (Expr *) build_column_default(rel, attribute.attnum);
 
@@ -3702,10 +3733,20 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel,
 
 		/*
 		 * If the new column is NOT NULL, tell Phase 3 it needs to test that.
+		 * (Note we don't do this for an OID column.  OID will be marked not
+		 * null, but since it's filled specially, there's no need to test
+		 * anything.)
 		 */
 		tab->new_notnull |= colDef->is_not_null;
 	}
 
+	/*
+	 * If we are adding an OID column, we have to tell Phase 3 to rewrite
+	 * the table to fix that.
+	 */
+	if (isOid)
+		tab->new_changeoids = true;
+
 	/*
 	 * Add needed dependency entries for the new column.
 	 */
@@ -3730,6 +3771,30 @@ add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid)
 	recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
 }
 
+/*
+ * ALTER TABLE SET WITH OIDS
+ *
+ * Basically this is an ADD COLUMN for the special OID column.  We have
+ * to cons up a ColumnDef node because the ADD COLUMN code needs one.
+ */
+static void
+ATPrepAddOids(List **wqueue, Relation rel, bool recurse, AlterTableCmd *cmd)
+{
+	/* If we're recursing to a child table, the ColumnDef is already set up */
+	if (cmd->def == NULL)
+	{
+		ColumnDef  *cdef = makeNode(ColumnDef);
+
+		cdef->colname = pstrdup("oid");
+		cdef->typename = makeTypeNameFromOid(OIDOID, -1);
+		cdef->inhcount = 0;
+		cdef->is_local = true;
+		cdef->is_not_null = true;
+		cmd->def = (Node *) cdef;
+	}
+	ATPrepAddColumn(wqueue, rel, recurse, cmd);
+}
+
 /*
  * ALTER TABLE ALTER COLUMN DROP NOT NULL
  */
@@ -4088,12 +4153,10 @@ ATExecSetStorage(Relation rel, const char *colName, Node *newValue)
  * because we have to decide at runtime whether to recurse or not depending
  * on whether attinhcount goes to zero or not.	(We can't check this in a
  * static pre-pass because it won't handle multiple inheritance situations
- * correctly.)	Since DROP COLUMN doesn't need to create any work queue
- * entries for Phase 3, it's okay to recurse internally in this routine
- * without considering the work queue.
+ * correctly.)
  */
 static void
-ATExecDropColumn(Relation rel, const char *colName,
+ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
 				 DropBehavior behavior,
 				 bool recurse, bool recursing)
 {
@@ -4178,7 +4241,8 @@ ATExecDropColumn(Relation rel, const char *colName,
 				if (childatt->attinhcount == 1 && !childatt->attislocal)
 				{
 					/* Time to delete this child column, too */
-					ATExecDropColumn(childrel, colName, behavior, true, true);
+					ATExecDropColumn(wqueue, childrel, colName,
+									 behavior, true, true);
 				}
 				else
 				{
@@ -4230,12 +4294,14 @@ ATExecDropColumn(Relation rel, const char *colName,
 	performDeletion(&object, behavior);
 
 	/*
-	 * If we dropped the OID column, must adjust pg_class.relhasoids
+	 * If we dropped the OID column, must adjust pg_class.relhasoids and
+	 * tell Phase 3 to physically get rid of the column.
 	 */
 	if (attnum == ObjectIdAttributeNumber)
 	{
 		Relation	class_rel;
 		Form_pg_class tuple_class;
+		AlteredTableInfo *tab;
 
 		class_rel = heap_open(RelationRelationId, RowExclusiveLock);
 
@@ -4254,6 +4320,12 @@ ATExecDropColumn(Relation rel, const char *colName,
 		CatalogUpdateIndexes(class_rel, tuple);
 
 		heap_close(class_rel, RowExclusiveLock);
+
+		/* Find or create work queue entry for this table */
+		tab = ATGetQueueEntry(wqueue, rel);
+
+		/* Tell Phase 3 to physically remove the OID column */
+		tab->new_changeoids = true;
 	}
 }
 
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 1bf99765f8c44d7b5364d95c021593b6a2e7f353..6688324e05ab8f2ba43ed2421f6570ca5bd23b19 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -11,7 +11,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.657 2009/02/02 19:31:39 alvherre Exp $
+ *	  $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.658 2009/02/11 21:11:16 tgl Exp $
  *
  * HISTORY
  *	  AUTHOR			DATE			MAJOR EVENT
@@ -1636,6 +1636,13 @@ alter_table_cmd:
 					n->behavior = $4;
 					$$ = (Node *)n;
 				}
+			/* ALTER TABLE <name> SET WITH OIDS  */
+			| SET WITH OIDS
+				{
+					AlterTableCmd *n = makeNode(AlterTableCmd);
+					n->subtype = AT_AddOids;
+					$$ = (Node *)n;
+				}
 			/* ALTER TABLE <name> SET WITHOUT OIDS  */
 			| SET WITHOUT OIDS
 				{
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 7523982662dacffaec41cdb772f7b5e2803491ad..23ffaabb4e11aa664115e9bab707238d3845f2ad 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -13,7 +13,7 @@
  * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.390 2009/02/02 19:31:40 alvherre Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.391 2009/02/11 21:11:16 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1127,6 +1127,7 @@ typedef enum AlterTableType
 	AT_ChangeOwner,				/* change owner */
 	AT_ClusterOn,				/* CLUSTER ON */
 	AT_DropCluster,				/* SET WITHOUT CLUSTER */
+	AT_AddOids,					/* SET WITH OIDS */
 	AT_DropOids,				/* SET WITHOUT OIDS */
 	AT_SetTableSpace,			/* SET TABLESPACE */
 	AT_SetRelOptions,			/* SET (...) -- AM specific parameters */
diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out
index 4768c8b186c216c76fc43bbb977fc5b8431e43a7..b14d61fd34fb6d25cf13064d1722a4c4e22778da 100644
--- a/src/test/regress/expected/alter_table.out
+++ b/src/test/regress/expected/alter_table.out
@@ -1171,7 +1171,7 @@ NOTICE:  drop cascades to 2 other objects
 DETAIL:  drop cascades to table c1
 drop cascades to table gc1
 --
--- Test the ALTER TABLE WITHOUT OIDS command
+-- Test the ALTER TABLE SET WITH/WITHOUT OIDS command
 --
 create table altstartwith (col integer) with oids;
 insert into altstartwith values (1);
@@ -1192,9 +1192,17 @@ select * from altstartwith;
    1
 (1 row)
 
--- Run inheritance tests
+alter table altstartwith set with oids;
+select oid > 0, * from altstartwith;
+ ?column? | col 
+----------+-----
+ t        |   1
+(1 row)
+
+drop table altstartwith;
+-- Check inheritance cases
 create table altwithoid (col integer) with oids;
--- Inherits parents oid column
+-- Inherits parents oid column anyway
 create table altinhoid () inherits (altwithoid) without oids;
 insert into altinhoid values (1);
 select oid > 0, * from altwithoid;
@@ -1210,7 +1218,6 @@ select oid > 0, * from altinhoid;
 (1 row)
 
 alter table altwithoid set without oids;
-alter table altinhoid set without oids;
 select oid > 0, * from altwithoid; -- fails
 ERROR:  column "oid" does not exist
 LINE 1: select oid > 0, * from altwithoid;
@@ -1231,6 +1238,63 @@ select * from altinhoid;
    1
 (1 row)
 
+alter table altwithoid set with oids;
+select oid > 0, * from altwithoid;
+ ?column? | col 
+----------+-----
+ t        |   1
+(1 row)
+
+select oid > 0, * from altinhoid;
+ ?column? | col 
+----------+-----
+ t        |   1
+(1 row)
+
+drop table altwithoid cascade;
+NOTICE:  drop cascades to table altinhoid
+create table altwithoid (col integer) without oids;
+-- child can have local oid column
+create table altinhoid () inherits (altwithoid) with oids;
+insert into altinhoid values (1);
+select oid > 0, * from altwithoid; -- fails
+ERROR:  column "oid" does not exist
+LINE 1: select oid > 0, * from altwithoid;
+               ^
+select oid > 0, * from altinhoid;
+ ?column? | col 
+----------+-----
+ t        |   1
+(1 row)
+
+alter table altwithoid set with oids;
+NOTICE:  merging definition of column "oid" for child "altinhoid"
+select oid > 0, * from altwithoid;
+ ?column? | col 
+----------+-----
+ t        |   1
+(1 row)
+
+select oid > 0, * from altinhoid;
+ ?column? | col 
+----------+-----
+ t        |   1
+(1 row)
+
+-- the child's local definition should remain
+alter table altwithoid set without oids;
+select oid > 0, * from altwithoid; -- fails
+ERROR:  column "oid" does not exist
+LINE 1: select oid > 0, * from altwithoid;
+               ^
+select oid > 0, * from altinhoid;
+ ?column? | col 
+----------+-----
+ t        |   1
+(1 row)
+
+drop table altwithoid cascade;
+NOTICE:  drop cascades to table altinhoid
 -- test renumbering of child-table columns in inherited operations
 create table p1 (f1 int);
 create table c1 (f2 text, f3 int) inherits (p1);
diff --git a/src/test/regress/sql/alter_table.sql b/src/test/regress/sql/alter_table.sql
index 20d1c2ba83e418abc127fcfaac418b68fcd90ed9..e041cec8439d539d0eb1adfe1fa9b4113b707c3b 100644
--- a/src/test/regress/sql/alter_table.sql
+++ b/src/test/regress/sql/alter_table.sql
@@ -921,7 +921,7 @@ order by relname, attnum;
 drop table p1, p2 cascade;
 
 --
--- Test the ALTER TABLE WITHOUT OIDS command
+-- Test the ALTER TABLE SET WITH/WITHOUT OIDS command
 --
 create table altstartwith (col integer) with oids;
 
@@ -934,10 +934,16 @@ alter table altstartwith set without oids;
 select oid > 0, * from altstartwith; -- fails
 select * from altstartwith;
 
--- Run inheritance tests
+alter table altstartwith set with oids;
+
+select oid > 0, * from altstartwith;
+
+drop table altstartwith;
+
+-- Check inheritance cases
 create table altwithoid (col integer) with oids;
 
--- Inherits parents oid column
+-- Inherits parents oid column anyway
 create table altinhoid () inherits (altwithoid) without oids;
 
 insert into altinhoid values (1);
@@ -946,13 +952,42 @@ select oid > 0, * from altwithoid;
 select oid > 0, * from altinhoid;
 
 alter table altwithoid set without oids;
-alter table altinhoid set without oids;
 
 select oid > 0, * from altwithoid; -- fails
 select oid > 0, * from altinhoid; -- fails
 select * from altwithoid;
 select * from altinhoid;
 
+alter table altwithoid set with oids;
+
+select oid > 0, * from altwithoid;
+select oid > 0, * from altinhoid;
+
+drop table altwithoid cascade;
+
+create table altwithoid (col integer) without oids;
+
+-- child can have local oid column
+create table altinhoid () inherits (altwithoid) with oids;
+
+insert into altinhoid values (1);
+
+select oid > 0, * from altwithoid; -- fails
+select oid > 0, * from altinhoid;
+
+alter table altwithoid set with oids;
+
+select oid > 0, * from altwithoid;
+select oid > 0, * from altinhoid;
+
+-- the child's local definition should remain
+alter table altwithoid set without oids;
+
+select oid > 0, * from altwithoid; -- fails
+select oid > 0, * from altinhoid;
+
+drop table altwithoid cascade;
+
 -- test renumbering of child-table columns in inherited operations
 
 create table p1 (f1 int);