diff --git a/contrib/fulltextindex/fti.c b/contrib/fulltextindex/fti.c
index 0c4cceb3f59cc4bc97113c2270d8fafbf764fac7..6c3e2fb888eecfcfd359aec2f0ea9045e272b1f9 100644
--- a/contrib/fulltextindex/fti.c
+++ b/contrib/fulltextindex/fti.c
@@ -190,7 +190,7 @@ fti(PG_FUNCTION_ARGS)
 	tupdesc = rel->rd_att;		/* what the tuple looks like (?) */
 
 	/* get oid of current tuple, needed by all, so place here */
-	oid = rel->rd_rel->relhasoids ? HeapTupleGetOid(rettuple) : InvalidOid;
+	oid = HeapTupleGetOid(rettuple);
 	if (!OidIsValid(oid))
 		elog(ERROR, "Full Text Indexing: Oid of current tuple is invalid");
 
diff --git a/contrib/rserv/rserv.c b/contrib/rserv/rserv.c
index 8672eb79cb6a2602151b8d8b891928270d9ad5f4..dc5f1268c9792cf4ba7c487e940ea5f75f098e91 100644
--- a/contrib/rserv/rserv.c
+++ b/contrib/rserv/rserv.c
@@ -102,10 +102,7 @@ _rserv_log_()
 
 	if (keynum == ObjectIdAttributeNumber)
 	{
-		snprintf(oidbuf, "%u", 64,
-				 rel->rd_rel->relhasoids
-		         ? HeapTupleGetOid(tuple)
-		         : InvalidOid);
+		snprintf(oidbuf, "%u", sizeof(oidbuf), HeapTupleGetOid(tuple));
 		key = oidbuf;
 	}
 	else
diff --git a/contrib/tablefunc/tablefunc.c b/contrib/tablefunc/tablefunc.c
index 37a6e723a65862228717d1b62e3acfde5664d665..e372c4f3d677ad8defbbbb48598d85d10038634e 100644
--- a/contrib/tablefunc/tablefunc.c
+++ b/contrib/tablefunc/tablefunc.c
@@ -614,7 +614,7 @@ make_crosstab_tupledesc(TupleDesc spi_tupdesc, int num_catagories)
 	 * spi result input column.
 	 */
 	natts = num_catagories + 1;
-	tupdesc = CreateTemplateTupleDesc(natts, WITHOUTOID);
+	tupdesc = CreateTemplateTupleDesc(natts, false);
 
 	/* first the rowname column */
 	attnum = 1;
diff --git a/doc/src/sgml/page.sgml b/doc/src/sgml/page.sgml
index 7551085dc94461e530d04aea88b11dbf4bfa8d7b..d7096a4bbe13c2b14582325c36de2efaddf49e5e 100644
--- a/doc/src/sgml/page.sgml
+++ b/doc/src/sgml/page.sgml
@@ -4,13 +4,17 @@
 
 <abstract>
 <para>
-A description of the database file default page format.
+A description of the database file page format.
 </para>
 </abstract>
 
 <para>
-This section provides an overview of the page format used by <productname>PostgreSQL</productname>
-tables.  User-defined access methods need not use this page format.
+This section provides an overview of the page format used by
+<productname>PostgreSQL</productname> tables and indexes.  (Index
+access methods need not use this page format.  At present, all index
+methods do use this basic format, but the data kept on index metapages
+usually doesn't follow the item layout rules exactly.)  TOAST tables
+and sequences are formatted just like a regular table.
 </para>
 
 <para>
@@ -18,15 +22,13 @@ In the following explanation, a
 <firstterm>byte</firstterm>
 is assumed to contain 8 bits.  In addition, the term
 <firstterm>item</firstterm>
-refers to data that is stored in <productname>PostgreSQL</productname> tables.
+refers to an individual data value that is stored on a page.  In a table,
+an item is a tuple (row); in an index, an item is an index entry.
 </para>
 
 <para>
 
-<xref linkend="page-table"> shows how pages in both normal
- <productname>PostgreSQL</productname> tables and
- <productname>PostgreSQL</productname> indexes (e.g., a B-tree index)
-are structured. This structure is also used for toast tables and sequences.
+<xref linkend="page-table"> shows the basic layout of a page.
 There are five parts to each page.
 
 </para>
@@ -48,12 +50,13 @@ Item
 
 <row>
  <entry>PageHeaderData</entry>
- <entry>20 bytes long. Contains general information about the page to allow to access it.</entry>
+ <entry>20 bytes long. Contains general information about the page, including
+free space pointers.</entry>
 </row>
 
 <row>
-<entry>itemPointerData</entry>
-<entry>List of (offset,length) pairs pointing to the actual item.</entry>
+<entry>ItemPointerData</entry>
+<entry>Array of (offset,length) pairs pointing to the actual items.</entry>
 </row>
 
 <row>
@@ -62,13 +65,14 @@ Item
 </row>
 
 <row>
-<entry>items</entry>
-<entry>The actual items themselves. Different access method have different data here.</entry>
+<entry>Items</entry>
+<entry>The actual items themselves.</entry>
 </row>
 
 <row>
 <entry>Special Space</entry>
-<entry>Access method specific data. Different method store different data. Unused by normal tables.</entry>
+<entry>Index access method specific data. Different methods store different
+data. Empty in ordinary tables.</entry>
 </row>
 
 </tbody>
@@ -78,11 +82,12 @@ Item
  <para>
 
   The first 20 bytes of each page consists of a page header
-  (PageHeaderData). It's format is detailed in <xref
+  (PageHeaderData). Its format is detailed in <xref
   linkend="pageheaderdata-table">. The first two fields deal with WAL
   related stuff. This is followed by three 2-byte integer fields
-  (<firstterm>lower</firstterm>, <firstterm>upper</firstterm>, and
-  <firstterm>special</firstterm>). These represent byte offsets to the start
+  (<structfield>pd_lower</structfield>, <structfield>pd_upper</structfield>,
+  and <structfield>pd_special</structfield>). These represent byte offsets to
+  the start
   of unallocated space, to the end of unallocated space, and to the start of
   the special space. 
   
@@ -104,7 +109,7 @@ Item
   <row>
    <entry>pd_lsn</entry>
    <entry>XLogRecPtr</entry>
-   <entry>6 bytes</entry>
+   <entry>8 bytes</entry>
    <entry>LSN: next byte after last byte of xlog</entry>
   </row>
   <row>
@@ -132,38 +137,51 @@ Item
    <entry>Offset to start of special space.</entry>
   </row>
   <row>
-   <entry>pd_opaque</entry>
-   <entry>OpaqueData</entry>
+   <entry>pd_pagesize_version</entry>
+   <entry>uint16</entry>
    <entry>2 bytes</entry>
-   <entry>AM-generic information. Currently just stores the page size.</entry>
+   <entry>Page size and layout version number information.</entry>
   </row>
  </tbody>
  </tgroup>
  </table>
 
+ <para>
+  All the details may be found in src/include/storage/bufpage.h.
+ </para>
+
  <para>  
   Special space is a region at the end of the page that is allocated at page
   initialization time and contains information specific to an access method. 
-  The last 2 bytes of the page header, <firstterm>opaque</firstterm>,
-  currently only stores the page size.  Page size is stored in each page
-  because frames in the buffer pool may be subdivided into equal sized pages
-  on a frame by frame basis within a table (is this true? - mvo).
-
+  The last 2 bytes of the page header,
+  <structfield>pd_pagesize_version</structfield>, store both the page size
+  and a version indicator.  Beginning with
+  <productname>PostgreSQL</productname> 7.3 the version number is 1; prior
+  releases used version number 0.  (The basic page layout and header format
+  has not changed, but the layout of heap tuple headers has.)  The page size
+  is basically only present as a cross-check; there is no support for having
+  more than one page size in an installation.
  </para>
 
  <para>
 
   Following the page header are item identifiers
-  (<firstterm>ItemIdData</firstterm>).  New item identifiers are allocated
-  from the first four bytes of unallocated space.  Because an item
-  identifier is never moved until it is freed, its index may be used to
-  indicate the location of an item on a page.  In fact, every pointer to an
-  item (<firstterm>ItemPointer</firstterm>, also know as
-  <firstterm>CTID</firstterm>) created by
-  <productname>PostgreSQL</productname> consists of a frame number and an
-  index of an item identifier.  An item identifier contains a byte-offset to
+  (<type>ItemIdData</type>), each requiring four bytes.
+  An item identifier contains a byte-offset to
   the start of an item, its length in bytes, and a set of attribute bits
   which affect its interpretation.
+  New item identifiers are allocated
+  as needed from the beginning of the unallocated space.
+  The number of item identifiers present can be determined by looking at
+  <structfield>pd_lower</>, which is increased to allocate a new identifier.
+  Because an item
+  identifier is never moved until it is freed, its index may be used on a
+  long-term basis to reference an item, even when the item itself is moved
+  around on the page to compact free space.  In fact, every pointer to an
+  item (<type>ItemPointer</type>, also known as
+  <type>CTID</type>) created by
+  <productname>PostgreSQL</productname> consists of a page number and the
+  index of an item identifier.
 
  </para>
 
@@ -171,8 +189,8 @@ Item
  
   The items themselves are stored in space allocated backwards from the end
   of unallocated space.  The exact structure varies depending on what the
-  table is to contain. Sequences and tables both use a structure named
-  <firstterm>HeapTupleHeaderData</firstterm>, describe below.
+  table is to contain. Tables and sequences both use a structure named
+  <type>HeapTupleHeaderData</type>, described below.
 
  </para>
  
@@ -180,20 +198,33 @@ Item
  
   The final section is the "special section" which may contain anything the
   access method wishes to store. Ordinary tables do not use this at all
-  (indicated by setting the offset to the pagesize).
+  (indicated by setting <structfield>pd_special</> to equal the pagesize).
   
  </para>
  
  <para>
 
-  All tuples are structured the same way. A header of around 31 bytes
-  followed by an optional null bitmask and the data. The header is detailed
-  below in <xref linkend="heaptupleheaderdata-table">.  The null bitmask is
-  only present if the <firstterm>HEAP_HASNULL</firstterm> bit is set in the
-  <firstterm>t_infomask</firstterm>. If it is present it takes up the space
-  between the end of the header and the beginning of the data, as indicated
-  by the <firstterm>t_hoff</firstterm> field. In this list of bits, a 1 bit
-  indicates not-null, a 0 bit is a null.
+  All table tuples are structured the same way. There is a fixed-size
+  header (occupying 23 bytes on most machines), followed by an optional null
+  bitmap, an optional object ID field, and the user data. The header is
+  detailed
+  in <xref linkend="heaptupleheaderdata-table">.  The actual user data
+  (fields of the tuple) begins at the offset indicated by
+  <structfield>t_hoff</>, which must always be a multiple of the MAXALIGN
+  distance for the platform.
+  The null bitmap is
+  only present if the <firstterm>HEAP_HASNULL</firstterm> bit is set in
+  <structfield>t_infomask</structfield>. If it is present it begins just after
+  the fixed header and occupies enough bytes to have one bit per data column
+  (that is, <structfield>t_natts</> bits altogether). In this list of bits, a
+  1 bit indicates not-null, a 0 bit is a null.  When the bitmap is not
+  present, all columns are assumed not-null.
+  The object ID is only present if the <firstterm>HEAP_HASOID</firstterm> bit
+  is set in <structfield>t_infomask</structfield>.  If present, it appears just
+  before the <structfield>t_hoff</> boundary.  Any padding needed to make
+  <structfield>t_hoff</> a MAXALIGN multiple will appear between the null
+  bitmap and the object ID.  (This in turn ensures that the object ID is
+  suitably aligned.)
   
  </para>
  
@@ -211,34 +242,34 @@ Item
  </thead>
  <tbody>
   <row>
-   <entry>t_oid</entry>
-   <entry>Oid</entry>
+   <entry>t_xmin</entry>
+   <entry>TransactionId</entry>
    <entry>4 bytes</entry>
-   <entry>OID of this tuple</entry>
+   <entry>insert XID stamp</entry>
   </row>
   <row>
    <entry>t_cmin</entry>
    <entry>CommandId</entry>
    <entry>4 bytes</entry>
-   <entry>insert CID stamp</entry>
+   <entry>insert CID stamp (overlays with t_xmax)</entry>
   </row>
   <row>
-   <entry>t_cmax</entry>
-   <entry>CommandId</entry>
+   <entry>t_xmax</entry>
+   <entry>TransactionId</entry>
    <entry>4 bytes</entry>
-   <entry>delete CID stamp</entry>
+   <entry>delete XID stamp</entry>
   </row>
   <row>
-   <entry>t_xmin</entry>
-   <entry>TransactionId</entry>
+   <entry>t_cmax</entry>
+   <entry>CommandId</entry>
    <entry>4 bytes</entry>
-   <entry>insert XID stamp</entry>
+   <entry>delete CID stamp (overlays with t_xvac)</entry>
   </row>
   <row>
-   <entry>t_xmax</entry>
+   <entry>t_xvac</entry>
    <entry>TransactionId</entry>
    <entry>4 bytes</entry>
-   <entry>delete XID stamp</entry>
+   <entry>XID for VACUUM operation moving tuple</entry>
   </row>
   <row>
    <entry>t_ctid</entry>
@@ -256,30 +287,28 @@ Item
    <entry>t_infomask</entry>
    <entry>uint16</entry>
    <entry>2 bytes</entry>
-   <entry>Various flags</entry>
+   <entry>various flags</entry>
   </row>
   <row>
    <entry>t_hoff</entry>
    <entry>uint8</entry>
    <entry>1 byte</entry>
-   <entry>length of tuple header. Also offset of data.</entry>
+   <entry>offset to user data</entry>
   </row>
  </tbody>
  </tgroup>
  </table>
 
  <para>
- 
-  All the details may be found in src/include/storage/bufpage.h.
-  
+   All the details may be found in src/include/access/htup.h.
  </para>
 
  <para>
  
   Interpreting the actual data can only be done with information obtained
   from other tables, mostly <firstterm>pg_attribute</firstterm>. The
-  particular fields are <firstterm>attlen</firstterm> and
-  <firstterm>attalign</firstterm>. There is no way to directly get a
+  particular fields are <structfield>attlen</structfield> and
+  <structfield>attalign</structfield>. There is no way to directly get a
   particular attribute, except when there are only fixed width fields and no
   NULLs. All this trickery is wrapped up in the functions
   <firstterm>heap_getattr</firstterm>, <firstterm>fastgetattr</firstterm>
@@ -293,7 +322,7 @@ Item
   the next. Then make sure you have the right alignment.  If the field is a
   fixed width field, then all the bytes are simply placed. If it's a
   variable length field (attlen == -1) then it's a bit more complicated,
-  using the variable length structure <firstterm>varattrib</firstterm>.
+  using the variable length structure <type>varattrib</type>.
   Depending on the flags, the data may be either inline, compressed or in
   another table (TOAST).
   
diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c
index 5735da26d46da011210108b48003f882d7547cf7..884db0ad21f685677b6a991038b86cf6df9b06dc 100644
--- a/src/backend/access/common/heaptuple.c
+++ b/src/backend/access/common/heaptuple.c
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/access/common/heaptuple.c,v 1.80 2002/08/25 17:20:00 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/access/common/heaptuple.c,v 1.81 2002/09/02 01:05:03 tgl Exp $
  *
  * NOTES
  *	  The old interface functions have been converted to macros
@@ -80,7 +80,7 @@ DataFill(char *data,
 		bitmask = CSIGNBIT;
 	}
 
-	*infomask &= HEAP_XACT_MASK;
+	*infomask &= ~(HEAP_HASNULL | HEAP_HASVARWIDTH | HEAP_HASEXTENDED);
 
 	for (i = 0; i < numberOfAttributes; i++)
 	{
@@ -584,8 +584,6 @@ heap_formtuple(TupleDesc tupleDescriptor,
 		elog(ERROR, "heap_formtuple: numberOfAttributes %d exceeds limit %d",
 			 numberOfAttributes, MaxTupleAttributeNumber);
 
-	AssertTupleDescHasOidIsValid(tupleDescriptor);
-
 	for (i = 0; i < numberOfAttributes; i++)
 	{
 		if (nulls[i] != ' ')
@@ -600,7 +598,7 @@ heap_formtuple(TupleDesc tupleDescriptor,
 	if (hasnull)
 		len += BITMAPLEN(numberOfAttributes);
 
-	if (tupleDescriptor->tdhasoid == WITHOID)
+	if (tupleDescriptor->tdhasoid)
 		len += sizeof(Oid);
 
 	hoff = len = MAXALIGN(len); /* align user data safely */
@@ -626,6 +624,9 @@ heap_formtuple(TupleDesc tupleDescriptor,
 			 &td->t_infomask,
 			 (hasnull ? td->t_bits : NULL));
 
+	if (tupleDescriptor->tdhasoid)
+		td->t_infomask |= HEAP_HASOID;
+
 	td->t_infomask |= HEAP_XMAX_INVALID;
 
 	return tuple;
@@ -651,7 +652,6 @@ heap_modifytuple(HeapTuple tuple,
 	char	   *nulls;
 	bool		isNull;
 	HeapTuple	newTuple;
-	uint8		infomask;
 
 	/*
 	 * sanity checks
@@ -702,18 +702,10 @@ heap_modifytuple(HeapTuple tuple,
 							  nulls);
 
 	/*
-	 * copy the header except for t_len, t_natts, t_hoff, t_bits,
-	 * t_infomask
+	 * copy the identification info of the old tuple: t_ctid, t_self,
+	 * and OID (if any)
 	 */
-	infomask = newTuple->t_data->t_infomask;
-	/*
-	 * copy t_xmin, t_cid, t_xmax, t_ctid, t_natts, t_infomask
-	 */
-	memmove((char *) newTuple->t_data,	/* XXX */
-			(char *) tuple->t_data,
-			offsetof(HeapTupleHeaderData, t_hoff));	/* XXX */
-	newTuple->t_data->t_infomask = infomask;
-	newTuple->t_data->t_natts = numberOfAttributes;
+	newTuple->t_data->t_ctid = tuple->t_data->t_ctid;
 	newTuple->t_self = tuple->t_self;
 	newTuple->t_tableOid = tuple->t_tableOid;
 	if (relation->rd_rel->relhasoids)
@@ -776,11 +768,11 @@ heap_addheader(int natts,		/* max domain index */
 	tuple->t_datamcxt = CurrentMemoryContext;
 	tuple->t_data = td = (HeapTupleHeader) ((char *) tuple + HEAPTUPLESIZE);
 
-	MemSet((char *) td, 0, len);
+	MemSet((char *) td, 0, hoff);
 
 	td->t_hoff = hoff;
 	td->t_natts = natts;
-	td->t_infomask = HEAP_XMAX_INVALID; /* XXX sufficient? */
+	td->t_infomask = withoid ? (HEAP_XMAX_INVALID | HEAP_HASOID) : HEAP_XMAX_INVALID;
 
 	memcpy((char *) td + hoff, structure, structlen);
 
diff --git a/src/backend/access/common/tupdesc.c b/src/backend/access/common/tupdesc.c
index f90717aa12a39f627c9836e711fc5c58a7792afe..6d6cdf38ce838a32107a49495bd5b5e5edcc0037 100644
--- a/src/backend/access/common/tupdesc.c
+++ b/src/backend/access/common/tupdesc.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/access/common/tupdesc.c,v 1.87 2002/08/30 19:23:18 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/access/common/tupdesc.c,v 1.88 2002/09/02 01:05:03 tgl Exp $
  *
  * NOTES
  *	  some of the executor utility code such as "ExecTypeFromTL" should be
@@ -37,7 +37,7 @@
  * ----------------------------------------------------------------
  */
 TupleDesc
-CreateTemplateTupleDesc(int natts, hasoid_t withoid)
+CreateTemplateTupleDesc(int natts, bool hasoid)
 {
 	uint32		size;
 	TupleDesc	desc;
@@ -59,7 +59,7 @@ CreateTemplateTupleDesc(int natts, hasoid_t withoid)
 	MemSet(desc->attrs, 0, size);
 
 	desc->natts = natts;
-	desc->tdhasoid = withoid;
+	desc->tdhasoid = hasoid;
 
 	return desc;
 }
@@ -67,11 +67,12 @@ CreateTemplateTupleDesc(int natts, hasoid_t withoid)
 /* ----------------------------------------------------------------
  *		CreateTupleDesc
  *
- *		This function allocates a new TupleDesc from Form_pg_attribute array
+ *		This function allocates a new TupleDesc pointing to a given
+ *		Form_pg_attribute array
  * ----------------------------------------------------------------
  */
 TupleDesc
-CreateTupleDesc(int natts, Form_pg_attribute *attrs)
+CreateTupleDesc(int natts, bool hasoid, Form_pg_attribute *attrs)
 {
 	TupleDesc	desc;
 
@@ -84,7 +85,7 @@ CreateTupleDesc(int natts, Form_pg_attribute *attrs)
 	desc->attrs = attrs;
 	desc->natts = natts;
 	desc->constr = NULL;
-	desc->tdhasoid = UNDEFOID;
+	desc->tdhasoid = hasoid;
 
 	return desc;
 }
@@ -129,7 +130,6 @@ CreateTupleDescCopy(TupleDesc tupdesc)
  *
  *		This function creates a new TupleDesc by copying from an existing
  *		TupleDesc (with Constraints)
- *
  * ----------------------------------------------------------------
  */
 TupleDesc
@@ -477,6 +477,9 @@ TupleDescInitEntry(TupleDesc desc,
  * BuildDescForRelation
  *
  * Given a relation schema (list of ColumnDef nodes), build a TupleDesc.
+ *
+ * Note: the default assumption is no OIDs; caller may modify the returned
+ * TupleDesc if it wants OIDs.
  */
 TupleDesc
 BuildDescForRelation(List *schema)
@@ -497,7 +500,7 @@ BuildDescForRelation(List *schema)
 	 * allocate a new tuple descriptor
 	 */
 	natts = length(schema);
-	desc = CreateTemplateTupleDesc(natts, UNDEFOID);
+	desc = CreateTemplateTupleDesc(natts, false);
 	constr->has_not_null = false;
 
 	attnum = 0;
@@ -667,7 +670,7 @@ TypeGetTupleDesc(Oid typeoid, List *colaliases)
 		/* OK, get the column alias */
 		attname = strVal(lfirst(colaliases));
 
-		tupdesc = CreateTemplateTupleDesc(1, WITHOUTOID);
+		tupdesc = CreateTemplateTupleDesc(1, false);
 		TupleDescInitEntry(tupdesc,
 						   (AttrNumber) 1,
 						   attname,
diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index 89e0fec0ed5eed215eda1f1d00efe2674b8a5bc7..9acd6b33858c468f5f9870fb39a167983af312f3 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/access/heap/heapam.c,v 1.146 2002/08/29 00:17:02 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/access/heap/heapam.c,v 1.147 2002/09/02 01:05:03 tgl Exp $
  *
  *
  * INTERFACE ROUTINES
@@ -1116,6 +1116,10 @@ heap_insert(Relation relation, HeapTuple tup, CommandId cid)
 
 	if (relation->rd_rel->relhasoids)
 	{
+#ifdef NOT_USED
+		/* this is redundant with an Assert in HeapTupleSetOid */
+		Assert(tup->t_data->t_infomask & HEAP_HASOID);
+#endif
 		/*
 		 * If the object id of this tuple has already been assigned, trust
 		 * the caller.	There are a couple of ways this can happen.  At
@@ -1125,22 +1129,21 @@ heap_insert(Relation relation, HeapTuple tup, CommandId cid)
 		 * to support a persistent object store (objects need to contain
 		 * pointers to one another).
 		 */
-		AssertTupleDescHasOid(relation->rd_att);
 		if (!OidIsValid(HeapTupleGetOid(tup)))
 			HeapTupleSetOid(tup, newoid());
 		else
 			CheckMaxObjectId(HeapTupleGetOid(tup));
 	}
+	else
+	{
+		/* check there is not space for an OID */
+		Assert(!(tup->t_data->t_infomask & HEAP_HASOID));
+	}
 
 	tup->t_data->t_infomask &= ~(HEAP_XACT_MASK);
+	tup->t_data->t_infomask |= HEAP_XMAX_INVALID;
 	HeapTupleHeaderSetXmin(tup->t_data, GetCurrentTransactionId());
 	HeapTupleHeaderSetCmin(tup->t_data, cid);
-	HeapTupleHeaderSetXmaxInvalid(tup->t_data);
-	/*
-	 * Do *not* set Cmax!  This would overwrite Cmin.
-	 */
-	/* HeapTupleHeaderSetCmax(tup->t_data, FirstCommandId); */
-	tup->t_data->t_infomask |= HEAP_XMAX_INVALID;
 	tup->t_tableOid = relation->rd_id;
 
 #ifdef TUPLE_TOASTER_ACTIVE
@@ -1181,13 +1184,7 @@ heap_insert(Relation relation, HeapTuple tup, CommandId cid)
 		rdata[0].len = SizeOfHeapInsert;
 		rdata[0].next = &(rdata[1]);
 
-		if (relation->rd_rel->relhasoids)
-		{
-			AssertTupleDescHasOid(relation->rd_att);
-			xlhdr.t_oid = HeapTupleGetOid(tup);
-		}
-		else
-			xlhdr.t_oid = InvalidOid;
+		xlhdr.t_oid = HeapTupleGetOid(tup);
 		xlhdr.t_natts = tup->t_data->t_natts;
 		xlhdr.t_hoff = tup->t_data->t_hoff;
 		xlhdr.mask = tup->t_data->t_infomask;
@@ -1234,10 +1231,6 @@ heap_insert(Relation relation, HeapTuple tup, CommandId cid)
 	 */
 	CacheInvalidateHeapTuple(relation, tup);
 
-	if (!relation->rd_rel->relhasoids)
-		return InvalidOid;
-		
-	AssertTupleDescHasOid(relation->rd_att);
 	return HeapTupleGetOid(tup);
 }
 
@@ -1343,7 +1336,9 @@ l1:
 
 	/* store transaction information of xact deleting the tuple */
 	tp.t_data->t_infomask &= ~(HEAP_XMAX_COMMITTED |
-							 HEAP_XMAX_INVALID | HEAP_MARKED_FOR_UPDATE);
+							   HEAP_XMAX_INVALID |
+							   HEAP_MARKED_FOR_UPDATE |
+							   HEAP_MOVED);
 	HeapTupleHeaderSetXmax(tp.t_data, GetCurrentTransactionId());
 	HeapTupleHeaderSetCmax(tp.t_data, cid);
 	/* Make sure there is no forward chain link in t_ctid */
@@ -1543,14 +1538,22 @@ l2:
 	/* Fill in OID and transaction status data for newtup */
 	if (relation->rd_rel->relhasoids)
 	{
-		AssertTupleDescHasOid(relation->rd_att);
+#ifdef NOT_USED
+		/* this is redundant with an Assert in HeapTupleSetOid */
+		Assert(newtup->t_data->t_infomask & HEAP_HASOID);
+#endif
 		HeapTupleSetOid(newtup, HeapTupleGetOid(&oldtup));
 	}
+	else
+	{
+		/* check there is not space for an OID */
+		Assert(!(newtup->t_data->t_infomask & HEAP_HASOID));
+	}
+
 	newtup->t_data->t_infomask &= ~(HEAP_XACT_MASK);
 	newtup->t_data->t_infomask |= (HEAP_XMAX_INVALID | HEAP_UPDATED);
 	HeapTupleHeaderSetXmin(newtup->t_data, GetCurrentTransactionId());
 	HeapTupleHeaderSetCmin(newtup->t_data, cid);
-	HeapTupleHeaderSetXmaxInvalid(newtup->t_data);
 
 	/*
 	 * If the toaster needs to be activated, OR if the new tuple will not
@@ -1586,7 +1589,8 @@ l2:
 
 		oldtup.t_data->t_infomask &= ~(HEAP_XMAX_COMMITTED |
 									   HEAP_XMAX_INVALID |
-									   HEAP_MARKED_FOR_UPDATE);
+									   HEAP_MARKED_FOR_UPDATE |
+									   HEAP_MOVED);
 		oldtup.t_data->t_infomask |= HEAP_XMAX_UNLOGGED;
 		HeapTupleHeaderSetXmax(oldtup.t_data, GetCurrentTransactionId());
 		HeapTupleHeaderSetCmax(oldtup.t_data, cid);
@@ -1677,7 +1681,8 @@ l2:
 	{
 		oldtup.t_data->t_infomask &= ~(HEAP_XMAX_COMMITTED |
 									   HEAP_XMAX_INVALID |
-									   HEAP_MARKED_FOR_UPDATE);
+									   HEAP_MARKED_FOR_UPDATE |
+									   HEAP_MOVED);
 		HeapTupleHeaderSetXmax(oldtup.t_data, GetCurrentTransactionId());
 		HeapTupleHeaderSetCmax(oldtup.t_data, cid);
 	}
@@ -1852,7 +1857,9 @@ l3:
 	((PageHeader) BufferGetPage(*buffer))->pd_sui = ThisStartUpID;
 
 	/* store transaction information of xact marking the tuple */
-	tuple->t_data->t_infomask &= ~(HEAP_XMAX_COMMITTED | HEAP_XMAX_INVALID);
+	tuple->t_data->t_infomask &= ~(HEAP_XMAX_COMMITTED |
+								   HEAP_XMAX_INVALID |
+								   HEAP_MOVED);
 	tuple->t_data->t_infomask |= HEAP_MARKED_FOR_UPDATE;
 	HeapTupleHeaderSetXmax(tuple->t_data, GetCurrentTransactionId());
 	HeapTupleHeaderSetCmax(tuple->t_data, cid);
@@ -2032,13 +2039,7 @@ log_heap_update(Relation reln, Buffer oldbuf, ItemPointerData from,
 	rdata[1].len = 0;
 	rdata[1].next = &(rdata[2]);
 
-	if (reln->rd_rel->relhasoids)
-	{
-		AssertTupleDescHasOid(reln->rd_att);
-		xlhdr.hdr.t_oid = HeapTupleGetOid(newtup);
-	}
-	else
-		xlhdr.hdr.t_oid = InvalidOid;
+	xlhdr.hdr.t_oid = HeapTupleGetOid(newtup);
 	xlhdr.hdr.t_natts = newtup->t_data->t_natts;
 	xlhdr.hdr.t_hoff = newtup->t_data->t_hoff;
 	xlhdr.hdr.mask = newtup->t_data->t_infomask;
@@ -2197,12 +2198,10 @@ heap_xlog_delete(bool redo, XLogRecPtr lsn, XLogRecord *record)
 
 	if (redo)
 	{
-		/*
-		 * On redo from WAL we cannot rely on a tqual-routine
-		 * to have reset HEAP_MOVED.
-		 */
-		htup->t_infomask &= ~(HEAP_MOVED | HEAP_XMAX_COMMITTED |
-							  HEAP_XMAX_INVALID | HEAP_MARKED_FOR_UPDATE);
+		htup->t_infomask &= ~(HEAP_XMAX_COMMITTED |
+							  HEAP_XMAX_INVALID |
+							  HEAP_MARKED_FOR_UPDATE |
+							  HEAP_MOVED);
 		HeapTupleHeaderSetXmax(htup, record->xl_xid);
 		HeapTupleHeaderSetCmax(htup, FirstCommandId);
 		/* Make sure there is no forward chain link in t_ctid */
@@ -2284,14 +2283,9 @@ heap_xlog_insert(bool redo, XLogRecPtr lsn, XLogRecord *record)
 		htup->t_infomask = HEAP_XMAX_INVALID | xlhdr.mask;
 		HeapTupleHeaderSetXmin(htup, record->xl_xid);
 		HeapTupleHeaderSetCmin(htup, FirstCommandId);
-		HeapTupleHeaderSetXmaxInvalid(htup);
-		HeapTupleHeaderSetCmax(htup, FirstCommandId);
 		htup->t_ctid = xlrec->target.tid;
 		if (reln->rd_rel->relhasoids)
-		{
-			AssertTupleDescHasOid(reln->rd_att);
 			HeapTupleHeaderSetOid(htup, xlhdr.t_oid);
-		}
 
 		offnum = PageAddItem(page, (Item) htup, newlen, offnum,
 							 LP_USED | OverwritePageMode);
@@ -2372,8 +2366,9 @@ heap_xlog_update(bool redo, XLogRecPtr lsn, XLogRecord *record, bool move)
 	{
 		if (move)
 		{
-			htup->t_infomask &=
-				~(HEAP_XMIN_COMMITTED | HEAP_XMIN_INVALID | HEAP_MOVED_IN);
+			htup->t_infomask &= ~(HEAP_XMIN_COMMITTED |
+								  HEAP_XMIN_INVALID |
+								  HEAP_MOVED_IN);
 			htup->t_infomask |= HEAP_MOVED_OFF;
 			HeapTupleHeaderSetXvac(htup, record->xl_xid);
 			/* Make sure there is no forward chain link in t_ctid */
@@ -2381,12 +2376,10 @@ heap_xlog_update(bool redo, XLogRecPtr lsn, XLogRecord *record, bool move)
 		}
 		else
 		{
-			/*
-			 * On redo from WAL we cannot rely on a tqual-routine
-			 * to have reset HEAP_MOVED.
-			 */
-			htup->t_infomask &= ~(HEAP_MOVED | HEAP_XMAX_COMMITTED |
-							 HEAP_XMAX_INVALID | HEAP_MARKED_FOR_UPDATE);
+			htup->t_infomask &= ~(HEAP_XMAX_COMMITTED |
+								  HEAP_XMAX_INVALID |
+								  HEAP_MARKED_FOR_UPDATE |
+								  HEAP_MOVED);
 			HeapTupleHeaderSetXmax(htup, record->xl_xid);
 			HeapTupleHeaderSetCmax(htup, FirstCommandId);
 			/* Set forward chain link in t_ctid */
@@ -2466,10 +2459,8 @@ newsame:;
 		htup->t_natts = xlhdr.t_natts;
 		htup->t_hoff = xlhdr.t_hoff;
 		if (reln->rd_rel->relhasoids)
-		{
-			AssertTupleDescHasOid(reln->rd_att);
 			HeapTupleHeaderSetOid(htup, xlhdr.t_oid);
-		}
+
 		if (move)
 		{
 			TransactionId xid[2];   /* xmax, xmin */
@@ -2479,7 +2470,8 @@ newsame:;
 			       (char *) xlrec + hsize, 2 * sizeof(TransactionId));
 			htup->t_infomask = xlhdr.mask;
 			htup->t_infomask &= ~(HEAP_XMIN_COMMITTED |
-								  HEAP_XMIN_INVALID | HEAP_MOVED_OFF);
+								  HEAP_XMIN_INVALID |
+								  HEAP_MOVED_OFF);
 			htup->t_infomask |= HEAP_MOVED_IN;
 			HeapTupleHeaderSetXmin(htup, xid[1]);
 			HeapTupleHeaderSetXmax(htup, xid[0]);
@@ -2490,8 +2482,6 @@ newsame:;
 			htup->t_infomask = HEAP_XMAX_INVALID | xlhdr.mask;
 			HeapTupleHeaderSetXmin(htup, record->xl_xid);
 			HeapTupleHeaderSetCmin(htup, FirstCommandId);
-			HeapTupleHeaderSetXmaxInvalid(htup);
-			HeapTupleHeaderSetCmax(htup, FirstCommandId);
 		}
 		/* Make sure there is no forward chain link in t_ctid */
 		htup->t_ctid = xlrec->newtid;
diff --git a/src/backend/access/heap/tuptoaster.c b/src/backend/access/heap/tuptoaster.c
index 1c09af2b30840eed8df8c1dea10ed862b1cb859d..ab6eff82629bcb5fda485af32b336c54ff68527d 100644
--- a/src/backend/access/heap/tuptoaster.c
+++ b/src/backend/access/heap/tuptoaster.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/access/heap/tuptoaster.c,v 1.34 2002/08/06 02:36:33 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/access/heap/tuptoaster.c,v 1.35 2002/09/02 01:05:03 tgl Exp $
  *
  *
  * INTERFACE ROUTINES
@@ -726,7 +726,7 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup)
 		new_len = offsetof(HeapTupleHeaderData, t_bits);
 		if (has_nulls)
 			new_len += BITMAPLEN(numAttrs);
-		if (rel->rd_rel->relhasoids)
+		if (olddata->t_infomask & HEAP_HASOID)
 			new_len += sizeof(Oid);
 		new_len = MAXALIGN(new_len);
 		Assert(new_len == olddata->t_hoff);
diff --git a/src/backend/bootstrap/bootparse.y b/src/backend/bootstrap/bootparse.y
index a50e9ac88d25e5ff3ad866f078d531c082777c59..113f11de712f719cbdfad6febfcb0b8fa804ecc2 100644
--- a/src/backend/bootstrap/bootparse.y
+++ b/src/backend/bootstrap/bootparse.y
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/bootstrap/bootparse.y,v 1.51 2002/08/30 22:18:05 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/bootstrap/bootparse.y,v 1.52 2002/09/02 01:05:03 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -166,20 +166,20 @@ Boot_CreateStmt:
 				}
 		  RPAREN
 				{
+					TupleDesc tupdesc;
+
 					do_start();
 
+					tupdesc = CreateTupleDesc(numattr, !($4), attrtypes);
+
 					if ($2)
 					{
-						TupleDesc tupdesc;
-
 						if (boot_reldesc)
 						{
 							elog(DEBUG3, "create bootstrap: warning, open relation exists, closing first");
 							closerel(NULL);
 						}
 
-						tupdesc = CreateTupleDesc(numattr, attrtypes);
-						tupdesc->tdhasoid = BoolToHasOid(! ($4));
 						boot_reldesc = heap_create(LexIDStr($5),
 												   PG_CATALOG_NAMESPACE,
 												   tupdesc,
@@ -191,15 +191,12 @@ Boot_CreateStmt:
 					else
 					{
 						Oid id;
-						TupleDesc tupdesc;
 
-						tupdesc = CreateTupleDesc(numattr,attrtypes);
 						id = heap_create_with_catalog(LexIDStr($5),
 													  PG_CATALOG_NAMESPACE,
 													  tupdesc,
 													  RELKIND_RELATION,
 													  $3,
-													  ! ($4),
 													  true);
 						elog(DEBUG3, "relation created with oid %u", id);
 					}
diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c
index ef9ee498dbce4e60135aa2ae8006c5c7041a5de3..28faa61b2943ad61d545a3523e3bc9d148773afe 100644
--- a/src/backend/bootstrap/bootstrap.c
+++ b/src/backend/bootstrap/bootstrap.c
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/bootstrap/bootstrap.c,v 1.139 2002/08/30 22:18:05 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/bootstrap/bootstrap.c,v 1.140 2002/09/02 01:05:03 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -493,7 +493,6 @@ boot_openrel(char *relname)
 		app = Typ;
 		while ((tup = heap_getnext(scan, ForwardScanDirection)) != NULL)
 		{
-			AssertTupleDescHasOid(rel->rd_att);
 			(*app)->am_oid = HeapTupleGetOid(tup);
 			memcpy((char *) &(*app)->am_typ,
 				   (char *) GETSTRUCT(tup),
@@ -677,16 +676,14 @@ InsertOneTuple(Oid objectid)
 
 	elog(DEBUG3, "inserting row oid %u, %d columns", objectid, numattr);
 
-	tupDesc = CreateTupleDesc(numattr, attrtypes);
-	tupDesc->tdhasoid = BoolToHasOid(RelationGetForm(boot_reldesc)->relhasoids);
+	tupDesc = CreateTupleDesc(numattr,
+							  RelationGetForm(boot_reldesc)->relhasoids,
+							  attrtypes);
 	tuple = heap_formtuple(tupDesc, values, Blanks);
-
 	if (objectid != (Oid) 0)
-	{
-		AssertTupleDescHasOid(tupDesc);
 		HeapTupleSetOid(tuple, objectid);
-	}
 	pfree(tupDesc);				/* just free's tupDesc, not the attrtypes */
+
 	simple_heap_insert(boot_reldesc, tuple);
 	heap_freetuple(tuple);
 	elog(DEBUG3, "row inserted");
@@ -878,7 +875,6 @@ gettype(char *type)
 		app = Typ;
 		while ((tup = heap_getnext(scan, ForwardScanDirection)) != NULL)
 		{
-			AssertTupleDescHasOid(rel->rd_att);
 			(*app)->am_oid = HeapTupleGetOid(tup);
 			memmove((char *) &(*app++)->am_typ,
 					(char *) GETSTRUCT(tup),
diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c
index f7dc8e5d014123baf5cfcdce98c97266c9cdef83..ea3131b3fc7645acc683e2649b8dc51d21a9f688 100644
--- a/src/backend/catalog/aclchk.c
+++ b/src/backend/catalog/aclchk.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/catalog/aclchk.c,v 1.75 2002/08/22 00:01:41 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/catalog/aclchk.c,v 1.76 2002/09/02 01:05:03 tgl Exp $
  *
  * NOTES
  *	  See acl.h.
@@ -565,7 +565,6 @@ ExecuteGrantStmt_Namespace(GrantStmt *stmt)
 			elog(ERROR, "namespace \"%s\" not found", nspname);
 		pg_namespace_tuple = (Form_pg_namespace) GETSTRUCT(tuple);
 
-		AssertTupleDescHasOid(relation->rd_att);
 		if (!pg_namespace_ownercheck(HeapTupleGetOid(tuple), GetUserId()))
 			aclcheck_error(ACLCHECK_NOT_OWNER, nspname);
 
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index faea56df47c51d3e0ad44ac2d6d42d6a652f6ecb..09afaf2df5c93e1f6cec88fea16ce1495712368e 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/catalog/heap.c,v 1.223 2002/08/29 04:38:04 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/catalog/heap.c,v 1.224 2002/09/02 01:05:03 tgl Exp $
  *
  *
  * INTERFACE ROUTINES
@@ -349,7 +349,7 @@ heap_storage_create(Relation rel)
  * --------------------------------
  */
 static void
-CheckAttributeNames(TupleDesc tupdesc, bool relhasoids, char relkind)
+CheckAttributeNames(TupleDesc tupdesc, char relkind)
 {
 	int			i;
 	int			j;
@@ -366,7 +366,7 @@ CheckAttributeNames(TupleDesc tupdesc, bool relhasoids, char relkind)
 		for (i = 0; i < natts; i++)
 		{
 			if (SystemAttributeByName(NameStr(tupdesc->attrs[i]->attname),
-									  relhasoids) != NULL)
+									  tupdesc->tdhasoid) != NULL)
 				elog(ERROR, "name of column \"%s\" conflicts with an existing system column",
 					 NameStr(tupdesc->attrs[i]->attname));
 		}
@@ -419,7 +419,6 @@ CheckAttributeNames(TupleDesc tupdesc, bool relhasoids, char relkind)
 static void
 AddNewAttributeTuples(Oid new_rel_oid,
 					  TupleDesc tupdesc,
-					  bool relhasoids,
 					  char relkind)
 {
 	Form_pg_attribute *dpp;
@@ -483,7 +482,8 @@ AddNewAttributeTuples(Oid new_rel_oid,
 		dpp = SysAtt;
 		for (i = 0; i < -1 - FirstLowInvalidHeapAttributeNumber; i++)
 		{
-			if (relhasoids || (*dpp)->attnum != ObjectIdAttributeNumber)
+			if (tupdesc->tdhasoid ||
+				(*dpp)->attnum != ObjectIdAttributeNumber)
 			{
 				Form_pg_attribute attStruct;
 
@@ -595,7 +595,6 @@ AddNewRelationTuple(Relation pg_class_desc,
 						 (void *) new_rel_reltup);
 
 	/* force tuple to have the desired OID */
-	AssertTupleDescHasOid(pg_class_desc->rd_att);
 	HeapTupleSetOid(tup, new_rel_oid);
 
 	/*
@@ -671,7 +670,6 @@ heap_create_with_catalog(const char *relname,
 						 TupleDesc tupdesc,
 						 char relkind,
 						 bool shared_relation,
-						 bool relhasoids,
 						 bool allow_system_table_mods)
 {
 	Relation	pg_class_desc;
@@ -687,12 +685,10 @@ heap_create_with_catalog(const char *relname,
 		elog(ERROR, "Number of columns is out of range (1 to %d)",
 			 MaxHeapAttributeNumber);
 
-	CheckAttributeNames(tupdesc, relhasoids, relkind);
+	CheckAttributeNames(tupdesc, relkind);
 
 	if (get_relname_relid(relname, relnamespace))
 		elog(ERROR, "Relation '%s' already exists", relname);
-
-	tupdesc->tdhasoid = BoolToHasOid(relhasoids);
 	
 	/*
 	 * Create the relcache entry (mostly dummy at this point) and the
@@ -747,8 +743,7 @@ 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,
-						  relhasoids, relkind);
+	AddNewAttributeTuples(new_rel_oid, new_rel_desc->rd_att, relkind);
 
 	/*
 	 * make a dependency link to force the relation to be deleted if
@@ -987,7 +982,6 @@ RemoveAttrDefault(Oid relid, AttrNumber attnum,
 	scan = systable_beginscan(attrdef_rel, AttrDefaultIndex, true,
 							  SnapshotNow, 2, scankeys);
 
-	AssertTupleDescHasOid(attrdef_rel->rd_att);
 	/* There should be at most one matching tuple, but we loop anyway */
 	while (HeapTupleIsValid(tuple = systable_getnext(scan)))
 	{
@@ -1774,7 +1768,6 @@ RemoveRelConstraints(Relation rel, const char *constrName,
 	conscan = systable_beginscan(conrel, ConstraintRelidIndex, true,
 								 SnapshotNow, 1, key);
 
-	AssertTupleDescHasOid(conrel->rd_att);
 	/*
 	 * Scan over the result set, removing any matching entries.
 	 */
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index bf6608eb4030aaa223759d7caa4531fa996e3ddd..93939a978e5e67fe4c68057d61c8011ccaa9703a 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/catalog/index.c,v 1.192 2002/08/30 19:23:18 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/catalog/index.c,v 1.193 2002/09/02 01:05:04 tgl Exp $
  *
  *
  * INTERFACE ROUTINES
@@ -111,7 +111,7 @@ BuildFuncTupleDesc(Oid funcOid,
 	/*
 	 * Allocate and zero a tuple descriptor for a one-column tuple.
 	 */
-	funcTupDesc = CreateTemplateTupleDesc(1, UNDEFOID);
+	funcTupDesc = CreateTemplateTupleDesc(1, false);
 	funcTupDesc->attrs[0] = (Form_pg_attribute) palloc(ATTRIBUTE_TUPLE_SIZE);
 	MemSet(funcTupDesc->attrs[0], 0, ATTRIBUTE_TUPLE_SIZE);
 
@@ -199,7 +199,7 @@ ConstructTupleDescriptor(Relation heapRelation,
 	 * allocate the new tuple descriptor
 	 */
 
-	indexTupDesc = CreateTemplateTupleDesc(numatts, WITHOUTOID);
+	indexTupDesc = CreateTemplateTupleDesc(numatts, false);
 
 	/* ----------------
 	 *	  for each attribute we are indexing, obtain its attribute
@@ -328,7 +328,6 @@ UpdateRelationRelation(Relation indexRelation)
 	 * the new tuple must have the oid already chosen for the index.
 	 * sure would be embarrassing to do this sort of thing in polite company.
 	 */
-	AssertTupleDescHasOid(pg_class->rd_att);
 	HeapTupleSetOid(tuple, RelationGetRelid(indexRelation));
 	simple_heap_insert(pg_class, tuple);
 
@@ -577,7 +576,7 @@ index_create(Oid heapRelationId,
 											indexInfo->ii_KeyAttrNumbers,
 												classObjectId);
 
-	indexTupDesc->tdhasoid = WITHOUTOID;
+	indexTupDesc->tdhasoid = false;
 	/*
 	 * create the index relation's relcache entry and physical disk file.
 	 * (If we fail further down, it's the smgr's responsibility to remove
@@ -609,7 +608,7 @@ index_create(Oid heapRelationId,
 	indexRelation->rd_rel->relowner = GetUserId();
 	indexRelation->rd_rel->relam = accessMethodObjectId;
 	indexRelation->rd_rel->relkind = RELKIND_INDEX;
-	indexRelation->rd_rel->relhasoids = false;  /* WITHOUTOID! */
+	indexRelation->rd_rel->relhasoids = false;
 
 	/*
 	 * store index's pg_class entry
diff --git a/src/backend/catalog/namespace.c b/src/backend/catalog/namespace.c
index f8a271f908201087bbb88c789857502b3481510b..1b4f2aef8e647f450568e5a4366452752b8d6482 100644
--- a/src/backend/catalog/namespace.c
+++ b/src/backend/catalog/namespace.c
@@ -13,7 +13,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/catalog/namespace.c,v 1.33 2002/08/30 22:18:05 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/catalog/namespace.c,v 1.34 2002/09/02 01:05:04 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1585,7 +1585,6 @@ RemoveTempRelations(Oid tempNamespaceId)
 			case RELKIND_RELATION:
 			case RELKIND_SEQUENCE:
 			case RELKIND_VIEW:
-				AssertTupleDescHasOid(pgclass->rd_att);
 				object.classId = RelOid_pg_class;
 				object.objectId = HeapTupleGetOid(tuple);
 				object.objectSubId = 0;
diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c
index e8608925bf51844b3c4bafcb34d61ec071bd83b7..805ce61930b185ce3b3b0e317495b88ea74607a7 100644
--- a/src/backend/catalog/pg_proc.c
+++ b/src/backend/catalog/pg_proc.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/catalog/pg_proc.c,v 1.91 2002/08/29 00:17:03 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/catalog/pg_proc.c,v 1.92 2002/09/02 01:05:04 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -230,7 +230,6 @@ ProcedureCreate(const char *procedureName,
 		/* start out with empty permissions */
 		nulls[Anum_pg_proc_proacl-1] = 'n';
 
-		AssertTupleDescHasOid(tupDesc);
 		tup = heap_formtuple(tupDesc, values, nulls);
 		simple_heap_insert(rel, tup);
 		is_update = false;
@@ -239,7 +238,6 @@ ProcedureCreate(const char *procedureName,
 	/* Need to update indexes for either the insert or update case */
 	CatalogUpdateIndexes(rel, tup);
 
-	AssertTupleDescHasOid(tupDesc);
 	retval = HeapTupleGetOid(tup);
 
 	/*
diff --git a/src/backend/catalog/pg_type.c b/src/backend/catalog/pg_type.c
index ec0704b78e60e89c1d4920419800a0e2a5805657..e3eb6abe32f00e5d4f6a5ef3caa89d434a358dd8 100644
--- a/src/backend/catalog/pg_type.c
+++ b/src/backend/catalog/pg_type.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/catalog/pg_type.c,v 1.80 2002/08/29 00:17:03 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/catalog/pg_type.c,v 1.81 2002/09/02 01:05:04 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -267,7 +267,6 @@ TypeCreate(const char *typeName,
 
 		simple_heap_update(pg_type_desc, &tup->t_self, tup);
 
-		AssertTupleDescHasOid(pg_type_desc->rd_att);
 		typeObjectId = HeapTupleGetOid(tup);
 	}
 	else
@@ -279,7 +278,6 @@ TypeCreate(const char *typeName,
 							 nulls);
 
 		/* preassign tuple Oid, if one was given */
-		AssertTupleDescHasOid(tupDesc);
 		HeapTupleSetOid(tup, assignedTypeOid);
 
 		typeObjectId = simple_heap_insert(pg_type_desc, tup);
diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c
index e8d2aa7e7e3ca3e8876eb10cc53bcb103e037cdf..ab0110a3f4fc098e7e1ea98dc2ddae840e269bb9 100644
--- a/src/backend/commands/cluster.c
+++ b/src/backend/commands/cluster.c
@@ -11,7 +11,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/commands/cluster.c,v 1.87 2002/08/27 03:38:27 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/commands/cluster.c,v 1.88 2002/09/02 01:05:04 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -199,7 +199,6 @@ make_new_heap(Oid OIDOldHeap, const char *NewName)
 										  tupdesc,
 										  OldHeap->rd_rel->relkind,
 										  OldHeap->rd_rel->relisshared,
-										  OldHeap->rd_rel->relhasoids,
 										  allowSystemTableMods);
 
 	/*
diff --git a/src/backend/commands/comment.c b/src/backend/commands/comment.c
index ea04fd687dc08f6a22e828ceae2842d0a8b6d182..a6925314858b212ed3f159b6d24cab818b70ca2e 100644
--- a/src/backend/commands/comment.c
+++ b/src/backend/commands/comment.c
@@ -7,7 +7,7 @@
  * Copyright (c) 1996-2001, PostgreSQL Global Development Group
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/commands/comment.c,v 1.58 2002/08/29 00:17:03 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/commands/comment.c,v 1.59 2002/09/02 01:05:04 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -516,7 +516,6 @@ CommentRule(List *qualname, char *comment)
 		if (HeapTupleIsValid(tuple))
 		{
 			reloid = ((Form_pg_rewrite) GETSTRUCT(tuple))->ev_class;
-			AssertTupleDescHasOid(RewriteRelation->rd_att);
 			ruleoid = HeapTupleGetOid(tuple);
 		}
 		else
@@ -557,7 +556,6 @@ CommentRule(List *qualname, char *comment)
 		if (!HeapTupleIsValid(tuple))
 			elog(ERROR, "rule \"%s\" does not exist", rulename);
 		Assert(reloid == ((Form_pg_rewrite) GETSTRUCT(tuple))->ev_class);
-		AssertTupleDescHasOid(relation->rd_att);
 		ruleoid = HeapTupleGetOid(tuple);
 		ReleaseSysCache(tuple);
 	}
@@ -769,7 +767,6 @@ CommentTrigger(List *qualname, char *comment)
 		elog(ERROR, "trigger \"%s\" for relation \"%s\" does not exist",
 			 trigname, RelationGetRelationName(relation));
 
-	AssertTupleDescHasOid(pg_trigger->rd_att);
 	oid = HeapTupleGetOid(triggertuple);
 
 	systable_endscan(scan);
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index 56d40c5bf0f2db91dbfc2aa36afd45297e63346a..c8463ac8f86181a0d25c60154337ee2a7f4bf966 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.169 2002/08/29 07:22:21 ishii Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.170 2002/09/02 01:05:04 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -597,10 +597,8 @@ CopyTo(Relation rel, List *attnumlist, bool binary, bool oids,
 			/* Send OID if wanted --- note fld_count doesn't include it */
 			if (oids)
 			{
-				Oid oid;
-				
-				AssertTupleDescHasOid(tupDesc);
-				oid = HeapTupleGetOid(tuple);
+				Oid oid = HeapTupleGetOid(tuple);
+
 				fld_size = sizeof(Oid);
 				CopySendData(&fld_size, sizeof(int16), fp);
 				CopySendData(&oid, sizeof(Oid), fp);
@@ -611,7 +609,6 @@ CopyTo(Relation rel, List *attnumlist, bool binary, bool oids,
 			/* Text format has no per-tuple header, but send OID if wanted */
 			if (oids)
 			{
-				AssertTupleDescHasOid(tupDesc);
 				string = DatumGetCString(DirectFunctionCall1(oidout,
 								ObjectIdGetDatum(HeapTupleGetOid(tuple))));
 				CopySendString(string, fp);
diff --git a/src/backend/commands/dbcommands.c b/src/backend/commands/dbcommands.c
index c511199a8b4d470dd4498da765b16ebd26361e90..ba94c54fcd99b2d42a927a0743640f9f96354db5 100644
--- a/src/backend/commands/dbcommands.c
+++ b/src/backend/commands/dbcommands.c
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/commands/dbcommands.c,v 1.100 2002/08/29 07:22:21 ishii Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/commands/dbcommands.c,v 1.101 2002/09/02 01:05:04 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -335,7 +335,6 @@ createdb(const CreatedbStmt *stmt)
 
 	tuple = heap_formtuple(pg_database_dsc, new_record, new_record_nulls);
 
-	AssertTupleDescHasOid(pg_database_dsc);
 	HeapTupleSetOid(tuple, dboid);		/* override heap_insert's OID
 										 * selection */
 
@@ -589,10 +588,7 @@ get_db_info(const char *name, Oid *dbIdP, int4 *ownerIdP,
 
 		/* oid of the database */
 		if (dbIdP)
-		{
-			AssertTupleDescHasOid(relation->rd_att);
 			*dbIdP = HeapTupleGetOid(tuple);
-		}
 		/* sysid of the owner */
 		if (ownerIdP)
 			*ownerIdP = dbform->datdba;
diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
index efae790dd8ce257141a77a2bfb978146d9d91f7d..bf8b6fb27baf0eb9f2beea4955216e877859df0f 100644
--- a/src/backend/commands/explain.c
+++ b/src/backend/commands/explain.c
@@ -5,7 +5,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994-5, Regents of the University of California
  *
- * $Header: /cvsroot/pgsql/src/backend/commands/explain.c,v 1.85 2002/08/29 00:17:03 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/commands/explain.c,v 1.86 2002/09/02 01:05:04 tgl Exp $
  *
  */
 
@@ -69,7 +69,7 @@ ExplainQuery(ExplainStmt *stmt, CommandDest dest)
 	List	   *l;
 
 	/* need a tuple descriptor representing a single TEXT column */
-	tupdesc = CreateTemplateTupleDesc(1, WITHOUTOID);
+	tupdesc = CreateTemplateTupleDesc(1, false);
 	TupleDescInitEntry(tupdesc, (AttrNumber) 1, "QUERY PLAN",
 					   TEXTOID, -1, 0, false);
 
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 8529c15fdb5f3b5e8ca6a3636d73101d186a2ed0..8709111a860db42c7c7190d12df08defba474132 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/commands/tablecmds.c,v 1.37 2002/08/30 19:23:19 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/commands/tablecmds.c,v 1.38 2002/09/02 01:05:04 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -150,7 +150,8 @@ DefineRelation(CreateStmt *stmt, char relkind)
 	 * have to copy inherited constraints here.)
 	 */
 	descriptor = BuildDescForRelation(schema);
-	descriptor->tdhasoid = BoolToHasOid(stmt->hasoids || parentHasOids);
+
+	descriptor->tdhasoid = (stmt->hasoids || parentHasOids);
 
 	if (old_constraints != NIL)
 	{
@@ -212,7 +213,6 @@ DefineRelation(CreateStmt *stmt, char relkind)
 										  descriptor,
 										  relkind,
 										  false,
-										  stmt->hasoids || parentHasOids,
 										  allowSystemTableMods);
 
 	StoreCatalogInheritance(relationId, inheritOids);
@@ -1733,7 +1733,6 @@ AlterTableAddColumn(Oid myrelid,
 
 	ReleaseSysCache(typeTuple);
 
-	AssertTupleDescHasNoOid(attrdesc->rd_att);
 	simple_heap_insert(attrdesc, attributeTuple);
 
 	/* Update indexes on pg_attribute */
@@ -1747,7 +1746,7 @@ AlterTableAddColumn(Oid myrelid,
 	newreltup = heap_copytuple(reltup);
 
 	((Form_pg_class) GETSTRUCT(newreltup))->relnatts = maxatts;
-	AssertTupleDescHasOid(pgclass->rd_att);
+
 	simple_heap_update(pgclass, &newreltup->t_self, newreltup);
 
 	/* keep catalog indexes current */
@@ -3430,7 +3429,7 @@ AlterTableCreateToastTable(Oid relOid, bool silent)
 	snprintf(toast_idxname, NAMEDATALEN, "pg_toast_%u_index", relOid);
 
 	/* this is pretty painful...  need a tuple descriptor */
-	tupdesc = CreateTemplateTupleDesc(3, WITHOUTOID);
+	tupdesc = CreateTemplateTupleDesc(3, false);
 	TupleDescInitEntry(tupdesc, (AttrNumber) 1,
 					   "chunk_id",
 					   OIDOID,
@@ -3464,7 +3463,6 @@ AlterTableCreateToastTable(Oid relOid, bool silent)
 										   tupdesc,
 										   RELKIND_TOASTVALUE,
 										   shared_relation,
-										   false,
 										   true);
 
 	/* make the toast relation visible, else index creation will fail */
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index 16da206adec2bd50c1d9eac971a4b5ae64e52998..4b765105148a3f8b47e2feaf519a26f5dc843296 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -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.129 2002/08/25 17:20:00 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/commands/trigger.c,v 1.130 2002/09/02 01:05:04 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -301,7 +301,6 @@ CreateTrigger(CreateTrigStmt *stmt, bool forConstraint)
 	tuple = heap_formtuple(tgrel->rd_att, values, nulls);
 
 	/* force tuple to have the desired OID */
-	AssertTupleDescHasOid(tgrel->rd_att);
 	HeapTupleSetOid(tuple, trigoid);
 
 	/*
@@ -421,7 +420,6 @@ DropTrigger(Oid relid, const char *trigname, DropBehavior behavior)
 	if (!pg_class_ownercheck(relid, GetUserId()))
 		aclcheck_error(ACLCHECK_NOT_OWNER, get_rel_name(relid));
 
-	AssertTupleDescHasOid(tgrel->rd_att);
 	object.classId = RelationGetRelid(tgrel);
 	object.objectId = HeapTupleGetOid(tup);
 	object.objectSubId = 0;
@@ -683,7 +681,6 @@ RelationBuildTriggers(Relation relation)
 				 RelationGetRelationName(relation));
 		build = &(triggers[found]);
 
-		AssertTupleDescHasOid(tgrel->rd_att);
 		build->tgoid = HeapTupleGetOid(htup);
 		build->tgname = MemoryContextStrdup(CacheMemoryContext,
 							 DatumGetCString(DirectFunctionCall1(nameout,
@@ -1923,7 +1920,6 @@ DeferredTriggerSetState(ConstraintsSetStmt *stmt)
 					elog(ERROR, "Constraint '%s' is not deferrable",
 						 cname);
 
-				AssertTupleDescHasOid(tgrel->rd_att);
 				constr_oid = HeapTupleGetOid(htup);
 				loid = lappendi(loid, constr_oid);
 				found = true;
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index cda893fab75e007fdf010b8fe60e14fa49b217e2..3e8cc79596f4a730b399cc8b505b2d583a56db05 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -13,7 +13,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/commands/vacuum.c,v 1.235 2002/08/30 22:18:05 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/commands/vacuum.c,v 1.236 2002/09/02 01:05:04 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -396,7 +396,6 @@ getrels(const RangeVar *vacrel, const char *stmttype)
 		{
 			/* Make a relation list entry for this guy */
 			oldcontext = MemoryContextSwitchTo(vac_context);
-			AssertTupleDescHasOid(pgclass->rd_att);
 			vrl = lappendi(vrl, HeapTupleGetOid(tuple));
 			MemoryContextSwitchTo(oldcontext);
 		}
@@ -1861,8 +1860,9 @@ repair_frag(VRelStats *vacrelstats, Relation onerel,
 					/* NO ELOG(ERROR) TILL CHANGES ARE LOGGED */
 					START_CRIT_SECTION();
 
-					tuple.t_data->t_infomask &=
-						~(HEAP_XMIN_COMMITTED | HEAP_XMIN_INVALID | HEAP_MOVED_IN);
+					tuple.t_data->t_infomask &= ~(HEAP_XMIN_COMMITTED |
+												  HEAP_XMIN_INVALID |
+												  HEAP_MOVED_IN);
 					tuple.t_data->t_infomask |= HEAP_MOVED_OFF;
 					HeapTupleHeaderSetXvac(tuple.t_data, myXID);
 
@@ -1901,12 +1901,16 @@ repair_frag(VRelStats *vacrelstats, Relation onerel,
 					 * Update the state of the copied tuple, and store it
 					 * on the destination page.
 					 */
-					newtup.t_data->t_infomask &=
-						~(HEAP_XMIN_COMMITTED | HEAP_XMIN_INVALID | HEAP_MOVED_OFF);
+					newtup.t_data->t_infomask &= ~(HEAP_XMIN_COMMITTED |
+												   HEAP_XMIN_INVALID |
+												   HEAP_MOVED_OFF);
 					newtup.t_data->t_infomask |= HEAP_MOVED_IN;
 					HeapTupleHeaderSetXvac(newtup.t_data, myXID);
-					newoff = PageAddItem(ToPage, (Item) newtup.t_data, tuple_len,
-										 InvalidOffsetNumber, LP_USED);
+					newoff = PageAddItem(ToPage,
+										 (Item) newtup.t_data,
+										 tuple_len,
+										 InvalidOffsetNumber,
+										 LP_USED);
 					if (newoff == InvalidOffsetNumber)
 					{
 						elog(PANIC, "moving chain: failed to add item with len = %lu to page %u",
@@ -2038,11 +2042,11 @@ repair_frag(VRelStats *vacrelstats, Relation onerel,
 			START_CRIT_SECTION();
 
 			/*
-			 * Mark new tuple as moved_in by vacuum and store vacuum XID
-			 * in t_cid !!!
+			 * Mark new tuple as MOVED_IN by me.
 			 */
-			newtup.t_data->t_infomask &=
-				~(HEAP_XMIN_COMMITTED | HEAP_XMIN_INVALID | HEAP_MOVED_OFF);
+			newtup.t_data->t_infomask &= ~(HEAP_XMIN_COMMITTED |
+										   HEAP_XMIN_INVALID |
+										   HEAP_MOVED_OFF);
 			newtup.t_data->t_infomask |= HEAP_MOVED_IN;
 			HeapTupleHeaderSetXvac(newtup.t_data, myXID);
 
@@ -2064,11 +2068,11 @@ repair_frag(VRelStats *vacrelstats, Relation onerel,
 			newtup.t_self = newtup.t_data->t_ctid;
 
 			/*
-			 * Mark old tuple as moved_off by vacuum and store vacuum XID
-			 * in t_cid !!!
+			 * Mark old tuple as MOVED_OFF by me.
 			 */
-			tuple.t_data->t_infomask &=
-				~(HEAP_XMIN_COMMITTED | HEAP_XMIN_INVALID | HEAP_MOVED_IN);
+			tuple.t_data->t_infomask &= ~(HEAP_XMIN_COMMITTED |
+										  HEAP_XMIN_INVALID |
+										  HEAP_MOVED_IN);
 			tuple.t_data->t_infomask |= HEAP_MOVED_OFF;
 			HeapTupleHeaderSetXvac(tuple.t_data, myXID);
 
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index 6318d79d4eb916331d02bedec1dcd23b0b83a0fc..fd4431ce5f73475c23a8dd8990c9de046a32b78d 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -27,7 +27,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.176 2002/08/29 00:17:03 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.177 2002/09/02 01:05:05 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -712,22 +712,25 @@ InitPlan(CmdType operation, Query *parseTree, Plan *plan, EState *estate)
 					aclcheck_error(aclresult,
 								   get_namespace_name(namespaceId));
 
-				/*
-				 * new "INTO" table is created WITH OIDS
-				 */
-				tupType->tdhasoid = WITHOID;
 				/*
 				 * have to copy tupType to get rid of constraints
 				 */
 				tupdesc = CreateTupleDescCopy(tupType);
 
+				/*
+				 * Formerly we forced the output table to have OIDs, but
+				 * as of 7.3 it will not have OIDs, because it's too late
+				 * here to change the tupdescs of the already-initialized
+				 * plan tree.  (Perhaps we could recurse and change them
+				 * all, but it's not really worth the trouble IMHO...)
+				 */
+
 				intoRelationId =
 					heap_create_with_catalog(intoName,
 											 namespaceId,
 											 tupdesc,
 											 RELKIND_RELATION,
 											 false,
-											 true,
 											 allowSystemTableMods);
 
 				FreeTupleDesc(tupdesc);
diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c
index 30f5b0e378f3406ab985c605180ede27d0c009e5..12b2089fd7363d85d416f761bed752d65ae36e34 100644
--- a/src/backend/executor/execQual.c
+++ b/src/backend/executor/execQual.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.106 2002/08/31 22:10:43 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.107 2002/09/02 01:05:05 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -997,7 +997,7 @@ ExecMakeTableFunctionResult(Expr *funcexpr,
 					/*
 					 * Scalar type, so make a single-column descriptor
 					 */
-					tupdesc = CreateTemplateTupleDesc(1, WITHOUTOID);
+					tupdesc = CreateTemplateTupleDesc(1, false);
 					TupleDescInitEntry(tupdesc,
 									   (AttrNumber) 1,
 									   "column",
@@ -2006,10 +2006,7 @@ ExecTargetList(List *targetlist,
 	 * natts = 0 to deal with it.
 	 */
 	if (targettype == NULL)
-	{
 		targettype = &NullTupleDesc;
-		targettype->tdhasoid = WITHOUTOID;
-	}
 
 	/*
 	 * allocate an array of char's to hold the "null" information only if
diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c
index afadcd3137b028ef74fb2ea4090b3c8ab6d083d6..e07fd8719abeb21330f2a4a52b6b403df37e1a57 100644
--- a/src/backend/executor/execTuples.c
+++ b/src/backend/executor/execTuples.c
@@ -15,7 +15,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/execTuples.c,v 1.57 2002/08/29 00:17:03 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/execTuples.c,v 1.58 2002/09/02 01:05:05 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -541,7 +541,6 @@ ExecInitNullTupleSlot(EState *estate, TupleDesc tupType)
 
 	ExecSetSlotDescriptor(slot, tupType, false);
 
-	NullTupleDesc.tdhasoid = WITHOUTOID;
 	nullTuple = heap_formtuple(&NullTupleDesc, values, nulls);
 
 	return ExecStoreTuple(nullTuple, slot, InvalidBuffer, true);
@@ -559,7 +558,7 @@ ExecInitNullTupleSlot(EState *estate, TupleDesc tupType)
  * ----------------------------------------------------------------
  */
 TupleDesc
-ExecTypeFromTL(List *targetList, hasoid_t withoid)
+ExecTypeFromTL(List *targetList, bool hasoid)
 {
 	List	   *tlitem;
 	TupleDesc	typeInfo;
@@ -578,7 +577,7 @@ ExecTypeFromTL(List *targetList, hasoid_t withoid)
 	/*
 	 * allocate a new typeInfo
 	 */
-	typeInfo = CreateTemplateTupleDesc(len, withoid);
+	typeInfo = CreateTemplateTupleDesc(len, hasoid);
 
 	/*
 	 * scan list, generate type info for each entry
diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c
index 24f232469b0ac281ca05986ecb86a1d4a59f3518..3e8cf203b11f8441a311f22fb1f8897641ca1113 100644
--- a/src/backend/executor/execUtils.c
+++ b/src/backend/executor/execUtils.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/execUtils.c,v 1.88 2002/08/06 02:36:34 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/execUtils.c,v 1.89 2002/09/02 01:05:05 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -291,22 +291,36 @@ void
 ExecAssignResultTypeFromTL(Plan *node, CommonState *commonstate)
 {
 	ResultRelInfo *ri;
-	Relation	rel;
-	hasoid_t	withoid;
+	bool		hasoid = false;
 	TupleDesc	tupDesc;
 
+	/*
+	 * This is pretty grotty: we need to ensure that result tuples have
+	 * space for an OID iff they are going to be stored into a relation
+	 * that has OIDs.  We assume that estate->es_result_relation_info
+	 * is already set up to describe the target relation.  One reason
+	 * this is ugly is that all plan nodes in the plan tree will emit
+	 * tuples with space for an OID, though we really only need the topmost
+	 * plan to do so.
+	 *
+	 * It would be better to have InitPlan adjust the topmost plan node's
+	 * output descriptor after plan tree initialization.  However, that
+	 * doesn't quite work because in an UPDATE that spans an inheritance
+	 * tree, some of the target relations may have OIDs and some not.
+	 * We have to make the decision on a per-relation basis as we initialize
+	 * each of the child plans of the topmost Append plan.  So, this is ugly
+	 * but it works, for now ...
+	 */
 	ri = node->state->es_result_relation_info;
 	if (ri != NULL)
-		rel = ri->ri_RelationDesc;
-	else
-		rel = node->state->es_into_relation_descriptor;
+	{
+		Relation	rel = ri->ri_RelationDesc;
 
-	if (rel != NULL)
-		withoid = BoolToHasOid(rel->rd_rel->relhasoids);
-	else
-		withoid = WITHOUTOID;
+		if (rel != NULL)
+			hasoid = rel->rd_rel->relhasoids;
+	}
 		
-	tupDesc = ExecTypeFromTL(node->targetlist, withoid);
+	tupDesc = ExecTypeFromTL(node->targetlist, hasoid);
 	ExecAssignResultType(commonstate, tupDesc, true);
 }
 
diff --git a/src/backend/executor/nodeAppend.c b/src/backend/executor/nodeAppend.c
index 724f0c8ce8950afc4bb4a8613cfed3158dc0e30f..8bc7136cb442b08991c84a89ede66e989ce970e8 100644
--- a/src/backend/executor/nodeAppend.c
+++ b/src/backend/executor/nodeAppend.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeAppend.c,v 1.45 2002/06/20 20:29:28 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeAppend.c,v 1.46 2002/09/02 01:05:05 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -217,7 +217,9 @@ ExecInitAppend(Append *node, EState *estate, Plan *parent)
 
 	/*
 	 * call ExecInitNode on each of the plans to be executed and save the
-	 * results into the array "initialized"
+	 * results into the array "initialized".  Note we *must* set
+	 * estate->es_result_relation_info correctly while we initialize each
+	 * sub-plan; ExecAssignResultTypeFromTL depends on that!
 	 */
 	for (i = appendstate->as_firstplan; i <= appendstate->as_lastplan; i++)
 	{
diff --git a/src/backend/executor/nodeFunctionscan.c b/src/backend/executor/nodeFunctionscan.c
index 89b5a544e5a42855e5b98a3b322a0881df27f831..8a9ac848e7bd45ba85924a3b469040c920c9abd4 100644
--- a/src/backend/executor/nodeFunctionscan.c
+++ b/src/backend/executor/nodeFunctionscan.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeFunctionscan.c,v 1.10 2002/08/31 19:09:27 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeFunctionscan.c,v 1.11 2002/09/02 01:05:05 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -209,7 +209,7 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate, Plan *parent)
 		 */
 		char	   *attname = strVal(lfirst(rte->eref->colnames));
 
-		tupdesc = CreateTemplateTupleDesc(1, WITHOUTOID);
+		tupdesc = CreateTemplateTupleDesc(1, false);
 		TupleDescInitEntry(tupdesc,
 						   (AttrNumber) 1,
 						   attname,
@@ -223,10 +223,7 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate, Plan *parent)
 		/*
 		 * Must be a pseudo type, i.e. record
 		 */
-		List *coldeflist = rte->coldeflist;
-
-		tupdesc = BuildDescForRelation(coldeflist);
-		tupdesc->tdhasoid = WITHOUTOID;
+		tupdesc = BuildDescForRelation(rte->coldeflist);
 	}
 	else
 		elog(ERROR, "Unknown kind of return type specified for function");
diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c
index 8535d875328ff0ff02ce4c27bce0092efc32b54c..f0cc3fe17ae500e2a9f882fe526adf96565b2c45 100644
--- a/src/backend/executor/spi.c
+++ b/src/backend/executor/spi.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/spi.c,v 1.72 2002/07/20 05:16:58 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/spi.c,v 1.73 2002/09/02 01:05:05 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -392,7 +392,6 @@ SPI_modifytuple(Relation rel, HeapTuple tuple, int natts, int *attnum,
 	MemoryContext oldcxt = NULL;
 	HeapTuple	mtuple;
 	int			numberOfAttributes;
-	uint8		infomask;
 	Datum	   *v;
 	char	   *n;
 	bool		isnull;
@@ -434,14 +433,13 @@ SPI_modifytuple(Relation rel, HeapTuple tuple, int natts, int *attnum,
 	if (i == natts)				/* no errors in *attnum */
 	{
 		mtuple = heap_formtuple(rel->rd_att, v, n);
-		infomask = mtuple->t_data->t_infomask;
 		/*
-		 * copy t_xmin, t_cid, t_xmax, t_ctid, t_natts, t_infomask
+		 * copy the identification info of the old tuple: t_ctid, t_self,
+		 * and OID (if any)
 		 */
-		memmove((char *)mtuple->t_data, (char *)tuple->t_data,
-				offsetof(HeapTupleHeaderData, t_hoff));
-		mtuple->t_data->t_infomask = infomask;
-		mtuple->t_data->t_natts = numberOfAttributes;
+		mtuple->t_data->t_ctid = tuple->t_data->t_ctid;
+		mtuple->t_self = tuple->t_self;
+		mtuple->t_tableOid = tuple->t_tableOid;
 		if (rel->rd_rel->relhasoids)
 			HeapTupleSetOid(mtuple, HeapTupleGetOid(tuple));
 	}
diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c
index c5aacab95b1686a1e9134952fa2b5952735a265c..3dd3b4e2625d8d33c79cde07ba710594dd9699d4 100644
--- a/src/backend/postmaster/pgstat.c
+++ b/src/backend/postmaster/pgstat.c
@@ -16,7 +16,7 @@
  *
  *	Copyright (c) 2001, PostgreSQL Global Development Group
  *
- *	$Header: /cvsroot/pgsql/src/backend/postmaster/pgstat.c,v 1.24 2002/08/29 07:22:23 ishii Exp $
+ *	$Header: /cvsroot/pgsql/src/backend/postmaster/pgstat.c,v 1.25 2002/09/02 01:05:05 tgl Exp $
  * ----------
  */
 #include "postgres.h"
@@ -620,7 +620,6 @@ pgstat_vacuum_tabstat(void)
 			dbidlist = (Oid *) repalloc((char *) dbidlist,
 										sizeof(Oid) * dbidalloc);
 		}
-		AssertTupleDescHasOid(dbrel->rd_att);
 		dbidlist[dbidused++] = HeapTupleGetOid(dbtup);
 	}
 	heap_endscan(dbscan);
diff --git a/src/backend/storage/page/bufpage.c b/src/backend/storage/page/bufpage.c
index ca8186ccdace1030a045eb62396c9f053543c7d0..a44e405d0fde921fb9d1f730eb34fa3c34760268 100644
--- a/src/backend/storage/page/bufpage.c
+++ b/src/backend/storage/page/bufpage.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/storage/page/bufpage.c,v 1.47 2002/08/06 19:41:23 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/storage/page/bufpage.c,v 1.48 2002/09/02 01:05:05 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -45,7 +45,7 @@ PageInit(Page page, Size pageSize, Size specialSize)
 	p->pd_lower = SizeOfPageHeaderData;
 	p->pd_upper = pageSize - specialSize;
 	p->pd_special = pageSize - specialSize;
-	PageSetPageSize(page, pageSize);
+	PageSetPageSizeAndVersion(page, pageSize, PG_PAGE_LAYOUT_VERSION);
 }
 
 
diff --git a/src/backend/utils/adt/lockfuncs.c b/src/backend/utils/adt/lockfuncs.c
index b1ccceebcb9aa53b380b4ab7cd3af1fee276e3c0..a5dda27f640bd1704e4d3c29fdb5ec2d16895222 100644
--- a/src/backend/utils/adt/lockfuncs.c
+++ b/src/backend/utils/adt/lockfuncs.c
@@ -6,7 +6,7 @@
  * Copyright (c) 2002, PostgreSQL Global Development Group
  *
  * IDENTIFICATION
- *		$Header: /cvsroot/pgsql/src/backend/utils/adt/lockfuncs.c,v 1.5 2002/08/31 17:14:28 tgl Exp $
+ *		$Header: /cvsroot/pgsql/src/backend/utils/adt/lockfuncs.c,v 1.6 2002/09/02 01:05:06 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -50,7 +50,7 @@ pg_lock_status(PG_FUNCTION_ARGS)
 
 		/* build tupdesc for result tuples */
 		/* this had better match pg_locks view in initdb.sh */
-		tupdesc = CreateTemplateTupleDesc(6, WITHOUTOID);
+		tupdesc = CreateTemplateTupleDesc(6, false);
 		TupleDescInitEntry(tupdesc, (AttrNumber) 1, "relation",
 						   OIDOID, -1, 0, false);
 		TupleDescInitEntry(tupdesc, (AttrNumber) 2, "database",
diff --git a/src/backend/utils/adt/regproc.c b/src/backend/utils/adt/regproc.c
index c99685d675f3051d93237bf77d636cd715b3cd10..c39d176ece8e6c1c85969716be731f3c995fa1ec 100644
--- a/src/backend/utils/adt/regproc.c
+++ b/src/backend/utils/adt/regproc.c
@@ -13,7 +13,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/utils/adt/regproc.c,v 1.73 2002/08/22 00:01:43 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/adt/regproc.c,v 1.74 2002/09/02 01:05:06 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -110,7 +110,6 @@ regprocin(PG_FUNCTION_ARGS)
 
 		while (HeapTupleIsValid(tuple = systable_getnext(sysscan)))
 		{
-			AssertTupleDescHasOid(hdesc->rd_att);
 			result = (RegProcedure) HeapTupleGetOid(tuple);
 			if (++matches > 1)
 				break;
@@ -425,7 +424,6 @@ regoperin(PG_FUNCTION_ARGS)
 
 		while (HeapTupleIsValid(tuple = systable_getnext(sysscan)))
 		{
-			AssertTupleDescHasOid(hdesc->rd_att);
 			result = HeapTupleGetOid(tuple);
 			if (++matches > 1)
 				break;
@@ -756,10 +754,7 @@ regclassin(PG_FUNCTION_ARGS)
 									 SnapshotNow, 1, skey);
 
 		if (HeapTupleIsValid(tuple = systable_getnext(sysscan)))
-		{
-			AssertTupleDescHasOid(hdesc->rd_att);
 			result = HeapTupleGetOid(tuple);
-		}
 		else
 			elog(ERROR, "No class with name %s", class_name_or_oid);
 
@@ -912,10 +907,7 @@ regtypein(PG_FUNCTION_ARGS)
 									 SnapshotNow, 1, skey);
 
 		if (HeapTupleIsValid(tuple = systable_getnext(sysscan)))
-		{
-			AssertTupleDescHasOid(hdesc->rd_att);
 			result = HeapTupleGetOid(tuple);
-		}
 		else
 			elog(ERROR, "No type with name %s", typ_name_or_oid);
 
diff --git a/src/backend/utils/adt/sets.c b/src/backend/utils/adt/sets.c
index 2c0beb25e0d133df52b1941f06c840c0acb2fb20..321b9c6855e0b4577594ff5869e99a991dd5386d 100644
--- a/src/backend/utils/adt/sets.c
+++ b/src/backend/utils/adt/sets.c
@@ -10,7 +10,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/utils/adt/Attic/sets.c,v 1.52 2002/08/27 03:56:35 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/adt/Attic/sets.c,v 1.53 2002/09/02 01:05:06 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -116,7 +116,6 @@ SetDefine(char *querystr, Oid elemType)
 
 		simple_heap_update(procrel, &newtup->t_self, newtup);
 
-		AssertTupleDescHasOid(procrel->rd_att);
 		setoid = HeapTupleGetOid(newtup);
 
 		CatalogUpdateIndexes(procrel, newtup);
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index 21f1e01dabb9829aadbaa94afa3af2464c410c3f..5703bf16530bb07e6f7f87d78ac74683fad82abb 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/utils/cache/catcache.c,v 1.97 2002/07/20 05:16:58 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/cache/catcache.c,v 1.98 2002/09/02 01:05:06 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -907,7 +907,6 @@ CatalogCacheInitializeCache(CatCache *cache)
 	 * copy the relcache's tuple descriptor to permanent cache storage
 	 */
 	tupdesc = CreateTupleDescCopyConstr(RelationGetDescr(relation));
-	AssertTupleDescHasOidIsValid(tupdesc);
 
 	/*
 	 * get the relation's OID and relisshared flag, too
@@ -1687,10 +1686,7 @@ build_dummy_tuple(CatCache *cache, int nkeys, ScanKey skeys)
 
 	ntp = heap_formtuple(tupDesc, values, nulls);
 	if (tupOid != InvalidOid)
-	{
-		AssertTupleDescHasOid(tupDesc);
 		HeapTupleSetOid(ntp, tupOid);
-	}
 
 	pfree(values);
 	pfree(nulls);
diff --git a/src/backend/utils/cache/inval.c b/src/backend/utils/cache/inval.c
index c292926238171082c7481e6400a047b3bba228c1..d64f5bf0b57be73dd0aef00b8b30a881d5926234 100644
--- a/src/backend/utils/cache/inval.c
+++ b/src/backend/utils/cache/inval.c
@@ -74,7 +74,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/utils/cache/inval.c,v 1.53 2002/07/20 05:16:58 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/cache/inval.c,v 1.54 2002/09/02 01:05:06 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -525,10 +525,7 @@ PrepareForTupleInvalidation(Relation relation, HeapTuple tuple,
 	tupleRelId = RelationGetRelid(relation);
 
 	if (tupleRelId == RelOid_pg_class)
-	{
-		AssertTupleDescHasOid(relation->rd_att);
 		relationId = HeapTupleGetOid(tuple);
-	}
 	else if (tupleRelId == RelOid_pg_attribute)
 		relationId = ((Form_pg_attribute) GETSTRUCT(tuple))->attrelid;
 	else
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index 1b5717029aa65f4424d769f700d284a7680f9c0d..036d11022bb633b22295d828e0d8efd8cc984a0f 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/utils/cache/relcache.c,v 1.172 2002/08/11 21:17:35 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/cache/relcache.c,v 1.173 2002/09/02 01:05:06 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -434,7 +434,8 @@ AllocateRelationDesc(Relation relation, Form_pg_class relp)
 	relation->rd_rel = relationForm;
 
 	/* and allocate attribute tuple form storage */
-	relation->rd_att = CreateTemplateTupleDesc(relationForm->relnatts, BoolToHasOid(relationForm->relhasoids));
+	relation->rd_att = CreateTemplateTupleDesc(relationForm->relnatts,
+											   relationForm->relhasoids);
 
 	MemoryContextSwitchTo(oldcxt);
 
@@ -460,6 +461,8 @@ RelationBuildTupleDesc(RelationBuildDescInfo buildinfo,
 	AttrDefault *attrdef = NULL;
 	int			ndef = 0;
 
+	relation->rd_att->tdhasoid = RelationGetForm(relation)->relhasoids;
+
 	constr = (TupleConstr *) MemoryContextAlloc(CacheMemoryContext,
 												sizeof(TupleConstr));
 	constr->has_not_null = false;
@@ -695,7 +698,6 @@ RelationBuildRuleLock(Relation relation)
 		rule = (RewriteRule *) MemoryContextAlloc(rulescxt,
 												  sizeof(RewriteRule));
 
-		AssertTupleDescHasOid(rewrite_tupdesc);
 		rule->ruleId = HeapTupleGetOid(rewrite_tuple);
 
 		rule->event = rewrite_form->ev_type - '0';
@@ -871,7 +873,6 @@ RelationBuildDesc(RelationBuildDescInfo buildinfo,
 	 * initialize the tuple descriptor (relation->rd_att).
 	 */
 	RelationBuildTupleDesc(buildinfo, relation);
-	RelationGetDescr(relation)->tdhasoid = BoolToHasOid(RelationGetForm(relation)->relhasoids);
 
 	/*
 	 * Fetch rules and triggers that affect this relation
@@ -1405,7 +1406,8 @@ formrdesc(const char *relationName,
 	 * right because it will never be replaced.  The input values must be
 	 * correctly defined by macros in src/include/catalog/ headers.
 	 */
-	relation->rd_att = CreateTemplateTupleDesc(natts, BoolToHasOid(relation->rd_rel->relhasoids));
+	relation->rd_att = CreateTemplateTupleDesc(natts,
+											   relation->rd_rel->relhasoids);
 
 	/*
 	 * initialize tuple desc info
@@ -2101,7 +2103,7 @@ RelationBuildLocalRelation(const char *relname,
 	rel->rd_rel->relnamespace = relnamespace;
 
 	rel->rd_rel->relkind = RELKIND_UNCATALOGED;
-	rel->rd_rel->relhasoids = (rel->rd_att->tdhasoid == WITHOID);
+	rel->rd_rel->relhasoids = rel->rd_att->tdhasoid;
 	rel->rd_rel->relnatts = natts;
 	rel->rd_rel->reltype = InvalidOid;
 
@@ -2304,7 +2306,7 @@ RelationCacheInitializePhase2(void)
 			 */
 			Assert(relation->rd_rel != NULL);
 			memcpy((char *) relation->rd_rel, (char *) relp, CLASS_TUPLE_SIZE);
-			relation->rd_att->tdhasoid = BoolToHasOid(relp->relhasoids);
+			relation->rd_att->tdhasoid = relp->relhasoids;
 
 			ReleaseSysCache(htup);
 		}
@@ -2766,7 +2768,8 @@ load_relcache_init_file(void)
 		rel->rd_rel = relform;
 
 		/* initialize attribute tuple forms */
-		rel->rd_att = CreateTemplateTupleDesc(relform->relnatts, BoolToHasOid(relform->relhasoids));
+		rel->rd_att = CreateTemplateTupleDesc(relform->relnatts,
+											  relform->relhasoids);
 
 		/* next read all the attribute tuple form data entries */
 		for (i = 0; i < relform->relnatts; i++)
diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c
index 82f4f632e58a4e23c56fb18c5f526c2e4d287a34..5e4100384a275d30289c244a67cf9cdfba7172a0 100644
--- a/src/backend/utils/init/postinit.c
+++ b/src/backend/utils/init/postinit.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/utils/init/postinit.c,v 1.112 2002/08/30 22:18:06 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/init/postinit.c,v 1.113 2002/09/02 01:05:06 tgl Exp $
  *
  *
  *-------------------------------------------------------------------------
@@ -98,7 +98,6 @@ ReverifyMyDatabase(const char *name)
 	pgdbscan = heap_beginscan(pgdbrel, SnapshotNow, 1, &key);
 
 	tup = heap_getnext(pgdbscan, ForwardScanDirection);
-	AssertTupleDescHasOid(pgdbrel->rd_att);
 	if (!HeapTupleIsValid(tup) ||
 		HeapTupleGetOid(tup) != MyDatabaseId)
 	{
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index e07421a87cfa97b844c4b80b6b1ae228bc033ae7..0c24034b5f24fd4fa0508d449db556fea9d8b8cd 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -5,7 +5,7 @@
  * command, configuration file, and command line options.
  * See src/backend/utils/misc/README for more information.
  *
- * $Header: /cvsroot/pgsql/src/backend/utils/misc/guc.c,v 1.90 2002/09/01 23:26:06 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/utils/misc/guc.c,v 1.91 2002/09/02 01:05:06 tgl Exp $
  *
  * Copyright 2000 by PostgreSQL Global Development Group
  * Written by Peter Eisentraut <peter_e@gmx.net>.
@@ -2288,7 +2288,7 @@ ShowGUCConfigOption(const char *name)
 	value = GetConfigOptionByName(name, &varname);
 
 	/* need a tuple descriptor representing a single TEXT column */
-	tupdesc = CreateTemplateTupleDesc(1, WITHOUTOID);
+	tupdesc = CreateTemplateTupleDesc(1, false);
 	TupleDescInitEntry(tupdesc, (AttrNumber) 1, (char *) varname,
 					   TEXTOID, -1, 0, false);
 
@@ -2314,7 +2314,7 @@ ShowAllGUCConfig(void)
 	char		  *values[2];
 
 	/* need a tuple descriptor representing two TEXT columns */
-	tupdesc = CreateTemplateTupleDesc(2, WITHOUTOID);
+	tupdesc = CreateTemplateTupleDesc(2, false);
 	TupleDescInitEntry(tupdesc, (AttrNumber) 1, "name",
 					   TEXTOID, -1, 0, false);
 	TupleDescInitEntry(tupdesc, (AttrNumber) 2, "setting",
@@ -2445,7 +2445,7 @@ show_all_settings(PG_FUNCTION_ARGS)
 		oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
 
 		/* need a tuple descriptor representing two TEXT columns */
-		tupdesc = CreateTemplateTupleDesc(2, WITHOUTOID);
+		tupdesc = CreateTemplateTupleDesc(2, false);
 		TupleDescInitEntry(tupdesc, (AttrNumber) 1, "name",
 						   TEXTOID, -1, 0, false);
 		TupleDescInitEntry(tupdesc, (AttrNumber) 2, "setting",
diff --git a/src/backend/utils/time/tqual.c b/src/backend/utils/time/tqual.c
index bd7693ca37cef3dc6deaedef4fc7906193fbba55..0cc88167dac69ec8b4db42fdad82385856dbe9cd 100644
--- a/src/backend/utils/time/tqual.c
+++ b/src/backend/utils/time/tqual.c
@@ -16,7 +16,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/utils/time/tqual.c,v 1.58 2002/07/30 16:08:33 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/time/tqual.c,v 1.59 2002/09/02 01:05:06 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -83,7 +83,6 @@ HeapTupleSatisfiesItself(HeapTupleHeader tuple)
 					return false;
 				}
 				tuple->t_infomask |= HEAP_XMIN_COMMITTED;
-				tuple->t_infomask &= ~HEAP_MOVED;
 			}
 		}
 		else if (tuple->t_infomask & HEAP_MOVED_IN)
@@ -93,10 +92,7 @@ HeapTupleSatisfiesItself(HeapTupleHeader tuple)
 				if (TransactionIdIsInProgress(HeapTupleHeaderGetXvac(tuple)))
 					return false;
 				if (TransactionIdDidCommit(HeapTupleHeaderGetXvac(tuple)))
-				{
 					tuple->t_infomask |= HEAP_XMIN_COMMITTED;
-					tuple->t_infomask &= ~HEAP_MOVED;
-				}
 				else
 				{
 					tuple->t_infomask |= HEAP_XMIN_INVALID;
@@ -223,7 +219,6 @@ HeapTupleSatisfiesNow(HeapTupleHeader tuple)
 					return false;
 				}
 				tuple->t_infomask |= HEAP_XMIN_COMMITTED;
-				tuple->t_infomask &= ~HEAP_MOVED;
 			}
 		}
 		else if (tuple->t_infomask & HEAP_MOVED_IN)
@@ -233,10 +228,7 @@ HeapTupleSatisfiesNow(HeapTupleHeader tuple)
 				if (TransactionIdIsInProgress(HeapTupleHeaderGetXvac(tuple)))
 					return false;
 				if (TransactionIdDidCommit(HeapTupleHeaderGetXvac(tuple)))
-				{
 					tuple->t_infomask |= HEAP_XMIN_COMMITTED;
-					tuple->t_infomask &= ~HEAP_MOVED;
-				}
 				else
 				{
 					tuple->t_infomask |= HEAP_XMIN_INVALID;
@@ -344,7 +336,6 @@ HeapTupleSatisfiesToast(HeapTupleHeader tuple)
 					return false;
 				}
 				tuple->t_infomask |= HEAP_XMIN_COMMITTED;
-				tuple->t_infomask &= ~HEAP_MOVED;
 			}
 		}
 		else if (tuple->t_infomask & HEAP_MOVED_IN)
@@ -354,10 +345,7 @@ HeapTupleSatisfiesToast(HeapTupleHeader tuple)
 				if (TransactionIdIsInProgress(HeapTupleHeaderGetXvac(tuple)))
 					return false;
 				if (TransactionIdDidCommit(HeapTupleHeaderGetXvac(tuple)))
-				{
 					tuple->t_infomask |= HEAP_XMIN_COMMITTED;
-					tuple->t_infomask &= ~HEAP_MOVED;
-				}
 				else
 				{
 					tuple->t_infomask |= HEAP_XMIN_INVALID;
@@ -401,7 +389,6 @@ HeapTupleSatisfiesUpdate(HeapTuple htuple, CommandId curcid)
 					return HeapTupleInvisible;
 				}
 				tuple->t_infomask |= HEAP_XMIN_COMMITTED;
-				tuple->t_infomask &= ~HEAP_MOVED;
 			}
 		}
 		else if (tuple->t_infomask & HEAP_MOVED_IN)
@@ -411,10 +398,7 @@ HeapTupleSatisfiesUpdate(HeapTuple htuple, CommandId curcid)
 				if (TransactionIdIsInProgress(HeapTupleHeaderGetXvac(tuple)))
 					return HeapTupleInvisible;
 				if (TransactionIdDidCommit(HeapTupleHeaderGetXvac(tuple)))
-				{
 					tuple->t_infomask |= HEAP_XMIN_COMMITTED;
-					tuple->t_infomask &= ~HEAP_MOVED;
-				}
 				else
 				{
 					tuple->t_infomask |= HEAP_XMIN_INVALID;
@@ -536,7 +520,6 @@ HeapTupleSatisfiesDirty(HeapTupleHeader tuple)
 					return false;
 				}
 				tuple->t_infomask |= HEAP_XMIN_COMMITTED;
-				tuple->t_infomask &= ~HEAP_MOVED;
 			}
 		}
 		else if (tuple->t_infomask & HEAP_MOVED_IN)
@@ -546,10 +529,7 @@ HeapTupleSatisfiesDirty(HeapTupleHeader tuple)
 				if (TransactionIdIsInProgress(HeapTupleHeaderGetXvac(tuple)))
 					return false;
 				if (TransactionIdDidCommit(HeapTupleHeaderGetXvac(tuple)))
-				{
 					tuple->t_infomask |= HEAP_XMIN_COMMITTED;
-					tuple->t_infomask &= ~HEAP_MOVED;
-				}
 				else
 				{
 					tuple->t_infomask |= HEAP_XMIN_INVALID;
@@ -671,7 +651,6 @@ HeapTupleSatisfiesSnapshot(HeapTupleHeader tuple, Snapshot snapshot)
 					return false;
 				}
 				tuple->t_infomask |= HEAP_XMIN_COMMITTED;
-				tuple->t_infomask &= ~HEAP_MOVED;
 			}
 		}
 		else if (tuple->t_infomask & HEAP_MOVED_IN)
@@ -681,10 +660,7 @@ HeapTupleSatisfiesSnapshot(HeapTupleHeader tuple, Snapshot snapshot)
 				if (TransactionIdIsInProgress(HeapTupleHeaderGetXvac(tuple)))
 					return false;
 				if (TransactionIdDidCommit(HeapTupleHeaderGetXvac(tuple)))
-				{
 					tuple->t_infomask |= HEAP_XMIN_COMMITTED;
-					tuple->t_infomask &= ~HEAP_MOVED;
-				}
 				else
 				{
 					tuple->t_infomask |= HEAP_XMIN_INVALID;
@@ -833,7 +809,6 @@ HeapTupleSatisfiesVacuum(HeapTupleHeader tuple, TransactionId OldestXmin)
 				return HEAPTUPLE_DEAD;
 			}
 			tuple->t_infomask |= HEAP_XMIN_COMMITTED;
-			tuple->t_infomask &= ~HEAP_MOVED;
 		}
 		else if (tuple->t_infomask & HEAP_MOVED_IN)
 		{
@@ -842,10 +817,7 @@ HeapTupleSatisfiesVacuum(HeapTupleHeader tuple, TransactionId OldestXmin)
 			if (TransactionIdIsInProgress(HeapTupleHeaderGetXvac(tuple)))
 				return HEAPTUPLE_INSERT_IN_PROGRESS;
 			if (TransactionIdDidCommit(HeapTupleHeaderGetXvac(tuple)))
-			{
 				tuple->t_infomask |= HEAP_XMIN_COMMITTED;
-				tuple->t_infomask &= ~HEAP_MOVED;
-			}
 			else
 			{
 				tuple->t_infomask |= HEAP_XMIN_INVALID;
diff --git a/src/include/access/htup.h b/src/include/access/htup.h
index f1d9748f5c526950e5eb106bf544e12a90649010..3bede1bfa3af99f7fc156f8dec3f94aa4bcbd666 100644
--- a/src/include/access/htup.h
+++ b/src/include/access/htup.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: htup.h,v 1.58 2002/08/25 17:20:01 tgl Exp $
+ * $Id: htup.h,v 1.59 2002/09/02 01:05:06 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -46,33 +46,69 @@
  */
 #define MaxHeapAttributeNumber	1600	/* 8 * 200 */
 
-/*
+/*----------
  * On-disk heap tuple header.  Currently this is also used as the header
  * format for tuples formed in memory, although in principle they could
- * be different.
+ * be different.  To avoid wasting space, the fields should be layed out
+ * in such a way to avoid structure padding.
+ *
+ * The overall structure of a heap tuple looks like:
+ *			fixed fields (HeapTupleHeaderData struct)
+ *			nulls bitmap (if HEAP_HASNULL is set in t_infomask)
+ *			alignment padding (as needed to make user data MAXALIGN'd)
+ *			object ID (if HEAP_HASOID is set in t_infomask)
+ *			user data fields
+ *
+ * We store five "virtual" fields Xmin, Cmin, Xmax, Cmax, and Xvac
+ * in just three physical fields.  Xmin is always really stored, but
+ * Cmin and Xmax share a field, as do Cmax and Xvac.  This works because
+ * we know that there are only a limited number of states that a tuple can
+ * be in, and that Cmin and Cmax are only interesting for the lifetime of
+ * the inserting and deleting transactions respectively.  We have the
+ * following possible states of a tuple:
+ *
+ *		XMIN		CMIN		XMAX		CMAX		XVAC
+ *
+ * NEW (never deleted, not moved by vacuum):
+ *		valid		valid		invalid		invalid		invalid
  *
- * To avoid wasting space, the attributes should be layed out in such a
- * way to reduce structure padding.  Note that t_hoff is the offset to
- * the start of the user data, and so must be a multiple of MAXALIGN.
- * Also note that we omit the nulls bitmap if t_infomask shows that there
- * are no nulls in the tuple.
+ * DELETED BY CREATING XACT:
+ *		valid		valid		= XMIN		valid		invalid
+ *
+ * DELETED BY OTHER XACT:
+ *		valid		unneeded	valid		valid		invalid
+ *
+ * MOVED BY VACUUM FULL:
+ *		valid		unneeded	maybe-valid	unneeded	valid
+ *
+ * This assumes that VACUUM FULL never tries to move a tuple whose Cmin or
+ * Cmax is still interesting (ie, insert-in-progress or delete-in-progress).
+ *
+ * This table shows that if we use an infomask bit to handle the case
+ * XMAX=XMIN specially, we never need to store Cmin and Xmax at the same
+ * time.  Nor do we need to store Cmax and Xvac at the same time.
+ *
+ * Following the fixed header fields, the nulls bitmap is stored (beginning
+ * at t_bits).  The bitmap is *not* stored if t_infomask shows that there
+ * are no nulls in the tuple.  If an OID field is present (as indicated by
+ * t_infomask), then it is stored just before the user data, which begins at
+ * the offset shown by t_hoff.  Note that t_hoff must be a multiple of
+ * MAXALIGN.
+ *----------
  */
-/*
-** We store five "virtual" fields Xmin, Cmin, Xmax, Cmax, and Xvac
-** in three physical fields t_xmin, t_cid, t_xmax:
-** CommandId     Cmin;		insert CID stamp
-** CommandId     Cmax;		delete CommandId stamp
-** TransactionId Xmin;		insert XID stamp
-** TransactionId Xmax;		delete XID stamp
-** TransactionId Xvac;		used by VACCUUM
-**
-** This assumes, that a CommandId can be stored in a TransactionId.
-*/
 typedef struct HeapTupleHeaderData
 {
-	TransactionId t_xmin;		/* Xmin -- 4 bytes each */
-	TransactionId t_cid;		/* Cmin, Cmax, Xvac */
-	TransactionId t_xmax;		/* Xmax, Cmax */
+	TransactionId	t_xmin;		/* inserting xact ID */
+
+	union {
+		CommandId	t_cmin;		/* inserting command ID */
+		TransactionId t_xmax;	/* deleting xact ID */
+	} t_field2;
+
+	union {
+		CommandId	t_cmax;		/* deleting command ID */
+		TransactionId t_xvac;	/* VACUUM FULL xact ID */
+	} t_field3;
 
 	ItemPointerData t_ctid;		/* current TID of this or newer tuple */
 
@@ -101,11 +137,12 @@ typedef HeapTupleHeaderData *HeapTupleHeader;
 #define HEAP_HASCOMPRESSED		0x0008	/* has compressed stored
 										 * attribute(s) */
 #define HEAP_HASEXTENDED		0x000C	/* the two above combined */
-
-#define HEAP_XMIN_IS_XMAX		0x0040	/* created and deleted in the */
-										/* same transaction */
-#define HEAP_XMAX_UNLOGGED		0x0080	/* to lock tuple for update */
-										/* without logging */
+#define HEAP_HASOID				0x0010	/* has an object-id field */
+/* bit 0x0020 is presently unused */
+#define HEAP_XMAX_IS_XMIN		0x0040	/* created and deleted in the
+										 * same transaction */
+#define HEAP_XMAX_UNLOGGED		0x0080	/* to lock tuple for update
+										 * without logging */
 #define HEAP_XMIN_COMMITTED		0x0100	/* t_xmin committed */
 #define HEAP_XMIN_INVALID		0x0200	/* t_xmin invalid/aborted */
 #define HEAP_XMAX_COMMITTED		0x0400	/* t_xmax committed */
@@ -113,143 +150,111 @@ typedef HeapTupleHeaderData *HeapTupleHeader;
 #define HEAP_MARKED_FOR_UPDATE	0x1000	/* marked for UPDATE */
 #define HEAP_UPDATED			0x2000	/* this is UPDATEd version of row */
 #define HEAP_MOVED_OFF			0x4000	/* moved to another place by
-										 * vacuum */
+										 * VACUUM FULL */
 #define HEAP_MOVED_IN			0x8000	/* moved from another place by
-										 * vacuum */
+										 * VACUUM FULL */
 #define HEAP_MOVED (HEAP_MOVED_OFF | HEAP_MOVED_IN)
 
-#define HEAP_XACT_MASK			0xFFF0	/* visibility-related bits */
-
-/* paranoid checking */
-
-#ifdef DEBUG_TUPLE_ACCESS
-
-#define HeapTupleHeaderExpectedLen(tup, withoid) \
-	MAXALIGN(offsetof(HeapTupleHeaderData, t_bits) + \
-	         (((tup)->t_infomask & HEAP_HASNULL) \
-	          ? BITMAPLEN((tup)->t_natts) : 0) + \
-		     ((withoid) ? sizeof(Oid) : 0) \
-	        ) 
-
-#define AssertHeapTupleHeaderHoffIsValid(tup, withoid) \
-	AssertMacro((tup)->t_hoff == HeapTupleHeaderExpectedLen(tup, withoid))
-
-#else
-
-#define AssertHeapTupleHeaderHoffIsValid(tup, withoid) ((void)true)
-
-#endif  /* DEBUG_TUPLE_ACCESS */
-
+#define HEAP_XACT_MASK			0xFFC0	/* visibility-related bits */
 
-/* HeapTupleHeader accessor macros */
 
-#define HeapTupleHeaderGetOid(tup) \
-( \
-	AssertHeapTupleHeaderHoffIsValid(tup, true), \
-	*((Oid *)((char *)(tup) + (tup)->t_hoff - sizeof(Oid))) \
-)
-
-#define HeapTupleHeaderSetOid(tup, oid) \
-( \
-	AssertHeapTupleHeaderHoffIsValid(tup, true), \
-	*((Oid *)((char *)(tup) + (tup)->t_hoff - sizeof(Oid))) = (oid) \
-)
-		
+/*
+ * HeapTupleHeader accessor macros
+ *
+ * Note: beware of multiple evaluations of "tup" argument.  But the Set
+ * macros evaluate their other argument only once.
+ */
 
 #define HeapTupleHeaderGetXmin(tup) \
 ( \
 	(tup)->t_xmin \
 )
 
-#define HeapTupleHeaderGetXmax(tup) \
-( \
-	((tup)->t_infomask & HEAP_XMIN_IS_XMAX) ? \
-		(tup)->t_xmin \
-	: \
-		(tup)->t_xmax \
-)
-
-/* no AssertMacro, because this is read as a system-defined attribute */
-#define HeapTupleHeaderGetCmin(tup) \
+#define HeapTupleHeaderSetXmin(tup, xid) \
 ( \
-	((tup)->t_infomask & HEAP_MOVED) ? \
-		FirstCommandId \
-	: \
-	( \
-		((tup)->t_infomask & (HEAP_XMIN_IS_XMAX | HEAP_XMAX_INVALID)) ? \
-			(CommandId) (tup)->t_cid \
-		: \
-			FirstCommandId \
-	) \
+	TransactionIdStore((xid), &(tup)->t_xmin) \
 )
 
-#define HeapTupleHeaderGetCmax(tup) \
+#define HeapTupleHeaderGetXmax(tup) \
 ( \
-	((tup)->t_infomask & HEAP_MOVED) ? \
-		FirstCommandId \
+	((tup)->t_infomask & HEAP_XMAX_IS_XMIN) ? \
+		(tup)->t_xmin \
 	: \
-	( \
-		((tup)->t_infomask & (HEAP_XMIN_IS_XMAX | HEAP_XMAX_INVALID)) ? \
-			(CommandId) (tup)->t_xmax \
-		: \
-			(CommandId) (tup)->t_cid \
-	) \
-)
-
-#define HeapTupleHeaderGetXvac(tup) \
-( \
-	AssertMacro((tup)->t_infomask & HEAP_MOVED), \
-	(tup)->t_cid \
-)
-
-
-#define HeapTupleHeaderSetXmin(tup, xid) \
-( \
-	TransactionIdStore((xid), &(tup)->t_xmin) \
+		(tup)->t_field2.t_xmax \
 )
 
-#define HeapTupleHeaderSetXminInvalid(tup) \
-do { \
-	(tup)->t_infomask &= ~HEAP_XMIN_IS_XMAX; \
-	StoreInvalidTransactionId(&(tup)->t_xmin); \
-} while (0)
-
 #define HeapTupleHeaderSetXmax(tup, xid) \
 do { \
-	if (TransactionIdEquals((tup)->t_xmin, (xid))) \
-		(tup)->t_infomask |= HEAP_XMIN_IS_XMAX; \
+	TransactionId	_newxid = (xid); \
+	if (TransactionIdEquals((tup)->t_xmin, _newxid)) \
+		(tup)->t_infomask |= HEAP_XMAX_IS_XMIN; \
 	else \
 	{ \
-		(tup)->t_infomask &= ~HEAP_XMIN_IS_XMAX; \
-		TransactionIdStore((xid), &(tup)->t_xmax); \
+		(tup)->t_infomask &= ~HEAP_XMAX_IS_XMIN; \
+		TransactionIdStore(_newxid, &(tup)->t_field2.t_xmax); \
 	} \
 } while (0)
 
-#define HeapTupleHeaderSetXmaxInvalid(tup) \
-do { \
-	(tup)->t_infomask &= ~HEAP_XMIN_IS_XMAX; \
-	StoreInvalidTransactionId(&(tup)->t_xmax); \
-} while (0)
+/*
+ * Note: GetCmin will produce wrong answers after SetXmax has been executed
+ * by a transaction other than the inserting one.  We could check
+ * HEAP_XMAX_INVALID and return FirstCommandId if it's clear, but since that
+ * bit will be set again if the deleting transaction aborts, there'd be no
+ * real gain in safety from the extra test.  So, just rely on the caller not
+ * to trust the value unless it's meaningful.
+ */
+#define HeapTupleHeaderGetCmin(tup) \
+( \
+	(tup)->t_field2.t_cmin \
+)
 
 #define HeapTupleHeaderSetCmin(tup, cid) \
 do { \
-	Assert(!((tup)->t_infomask & HEAP_MOVED)); \
-	TransactionIdStore((TransactionId) (cid), &(tup)->t_cid); \
+	Assert((tup)->t_infomask & HEAP_XMAX_INVALID); \
+	(tup)->t_field2.t_cmin = (cid); \
 } while (0)
 
+/*
+ * As with GetCmin, we can't completely ensure that GetCmax can detect whether
+ * a valid command ID is available, and there's little point in a partial test.
+ */
+#define HeapTupleHeaderGetCmax(tup) \
+( \
+	(tup)->t_field3.t_cmax \
+)
+
 #define HeapTupleHeaderSetCmax(tup, cid) \
 do { \
 	Assert(!((tup)->t_infomask & HEAP_MOVED)); \
-	if ((tup)->t_infomask & HEAP_XMIN_IS_XMAX) \
-		TransactionIdStore((TransactionId) (cid), &(tup)->t_xmax); \
-	else \
-		TransactionIdStore((TransactionId) (cid), &(tup)->t_cid); \
+	(tup)->t_field3.t_cmax = (cid); \
 } while (0)
 
+#define HeapTupleHeaderGetXvac(tup) \
+( \
+	((tup)->t_infomask & HEAP_MOVED) ? \
+		(tup)->t_field3.t_xvac \
+	: \
+		InvalidTransactionId \
+)
+
 #define HeapTupleHeaderSetXvac(tup, xid) \
 do { \
 	Assert((tup)->t_infomask & HEAP_MOVED); \
-	TransactionIdStore((xid), &(tup)->t_cid); \
+	TransactionIdStore((xid), &(tup)->t_field3.t_xvac); \
+} while (0)
+
+#define HeapTupleHeaderGetOid(tup) \
+( \
+	((tup)->t_infomask & HEAP_HASOID) ? \
+		*((Oid *) ((char *)(tup) + (tup)->t_hoff - sizeof(Oid))) \
+	: \
+		InvalidOid \
+)
+
+#define HeapTupleHeaderSetOid(tup, oid) \
+do { \
+	Assert((tup)->t_infomask & HEAP_HASOID); \
+	*((Oid *) ((char *)(tup) + (tup)->t_hoff - sizeof(Oid))) = (oid); \
 } while (0)
 
 
@@ -400,12 +405,10 @@ typedef HeapTupleData *HeapTuple;
 #define HEAPTUPLESIZE	MAXALIGN(sizeof(HeapTupleData))
 
 
-/* ----------------
- *		support macros
- * ----------------
+/*
+ * GETSTRUCT - given a HeapTuple pointer, return address of the user data
  */
-#define GETSTRUCT(TUP) (((char *)((HeapTuple)(TUP))->t_data) + \
-						((HeapTuple)(TUP))->t_data->t_hoff)
+#define GETSTRUCT(TUP) ((char *) ((TUP)->t_data) + (TUP)->t_data->t_hoff)
 
 
 /*
@@ -421,24 +424,24 @@ typedef HeapTupleData *HeapTuple;
 #define HeapTupleIsValid(tuple) PointerIsValid(tuple)
 
 #define HeapTupleNoNulls(tuple) \
-		(!(((HeapTuple) (tuple))->t_data->t_infomask & HEAP_HASNULL))
+		(!((tuple)->t_data->t_infomask & HEAP_HASNULL))
 
 #define HeapTupleAllFixed(tuple) \
-		(!(((HeapTuple) (tuple))->t_data->t_infomask & HEAP_HASVARWIDTH))
+		(!((tuple)->t_data->t_infomask & HEAP_HASVARWIDTH))
 
 #define HeapTupleHasExternal(tuple) \
-		((((HeapTuple)(tuple))->t_data->t_infomask & HEAP_HASEXTERNAL) != 0)
+		(((tuple)->t_data->t_infomask & HEAP_HASEXTERNAL) != 0)
 
 #define HeapTupleHasCompressed(tuple) \
-		((((HeapTuple)(tuple))->t_data->t_infomask & HEAP_HASCOMPRESSED) != 0)
+		(((tuple)->t_data->t_infomask & HEAP_HASCOMPRESSED) != 0)
 
 #define HeapTupleHasExtended(tuple) \
-		((((HeapTuple)(tuple))->t_data->t_infomask & HEAP_HASEXTENDED) != 0)
+		(((tuple)->t_data->t_infomask & HEAP_HASEXTENDED) != 0)
 
 #define HeapTupleGetOid(tuple) \
-		HeapTupleHeaderGetOid(((HeapTuple)(tuple))->t_data)
+		HeapTupleHeaderGetOid((tuple)->t_data)
 
 #define HeapTupleSetOid(tuple, oid) \
-		HeapTupleHeaderSetOid(((HeapTuple)(tuple))->t_data, (oid))
+		HeapTupleHeaderSetOid((tuple)->t_data, (oid))
 
 #endif   /* HTUP_H */
diff --git a/src/include/access/tupdesc.h b/src/include/access/tupdesc.h
index c8c9839985e454f3e47d19acf2dcf8ed9ed56835..09512a322e179d0c517e5e98771cd0482c4e1848 100644
--- a/src/include/access/tupdesc.h
+++ b/src/include/access/tupdesc.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: tupdesc.h,v 1.37 2002/07/20 05:16:59 momjian Exp $
+ * $Id: tupdesc.h,v 1.38 2002/09/02 01:05:06 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -41,14 +41,9 @@ typedef struct tupleConstr
 	bool		has_not_null;
 } TupleConstr;
 
-typedef char hasoid_t;
-#define WITHOID 'C'
-#define WITHOUTOID 'S'
-#define UNDEFOID '?'
-#define BoolToHasOid(b) ((b) ? WITHOID : WITHOUTOID)
 /*
  * This structure contains all information (i.e. from Classes
- * pg_attribute, pg_attrdef, pg_constraint) for a tuple.
+ * pg_attribute, pg_attrdef, pg_constraint) for the structure of a tuple.
  */
 typedef struct tupleDesc
 {
@@ -56,29 +51,14 @@ typedef struct tupleDesc
 	Form_pg_attribute *attrs;
 	/* attrs[N] is a pointer to the description of Attribute Number N+1.  */
 	TupleConstr *constr;
-	hasoid_t	tdhasoid;		/* Tuple has an oid attribute in its header */
+	bool		tdhasoid;		/* Tuple has oid attribute in its header */
 }	*TupleDesc;
 
-#ifdef DEBUG_TUPLE_ACCESS
 
-#define AssertTupleDescHasOidIsValid(td) \
-	Assert(((td)->tdhasoid == WITHOID) || ((td)->tdhasoid == WITHOUTOID))
-#define AssertTupleDescHasOid(td) \
-	Assert((td)->tdhasoid == WITHOID)
-#define AssertTupleDescHasNoOid(td) \
-	Assert((td)->tdhasoid == WITHOUTOID)
+extern TupleDesc CreateTemplateTupleDesc(int natts, bool hasoid);
 
-#else
-
-#define AssertTupleDescHasOidIsValid(td)
-#define AssertTupleDescHasOid(td)
-#define AssertTupleDescHasNoOid(td)
-
-#endif
-
-extern TupleDesc CreateTemplateTupleDesc(int natts, hasoid_t withoid);
-
-extern TupleDesc CreateTupleDesc(int natts, Form_pg_attribute *attrs);
+extern TupleDesc CreateTupleDesc(int natts, bool hasoid,
+								 Form_pg_attribute *attrs);
 
 extern TupleDesc CreateTupleDescCopy(TupleDesc tupdesc);
 
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index f561dee66b1c58db453be10da83cd8c9e5d03943..652009104fe30c71c40507e1d7198012d3362914 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -37,7 +37,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: catversion.h,v 1.156 2002/08/31 17:14:28 tgl Exp $
+ * $Id: catversion.h,v 1.157 2002/09/02 01:05:06 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -53,6 +53,6 @@
  */
 
 /*							yyyymmddN */
-#define CATALOG_VERSION_NO	200208311
+#define CATALOG_VERSION_NO	200209011
 
 #endif
diff --git a/src/include/catalog/heap.h b/src/include/catalog/heap.h
index f45c61515adb6dac3a1987967d3f7ba5a6ae33b3..b1f5dc8600c5645187ad3117ad2fa3428ffedd96 100644
--- a/src/include/catalog/heap.h
+++ b/src/include/catalog/heap.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: heap.h,v 1.55 2002/08/02 18:15:09 tgl Exp $
+ * $Id: heap.h,v 1.56 2002/09/02 01:05:06 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -41,7 +41,6 @@ extern Oid heap_create_with_catalog(const char *relname,
 						 TupleDesc tupdesc,
 						 char relkind,
 						 bool shared_relation,
-						 bool relhasoids,
 						 bool allow_system_table_mods);
 
 extern void heap_drop_with_catalog(Oid rid);
diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h
index 3fbf63567acc0c09f4a93268c6f5ecb65090add4..f2fa81857cdbd0a34ecb5f8c1fe36a37ca5622db 100644
--- a/src/include/executor/executor.h
+++ b/src/include/executor/executor.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: executor.h,v 1.76 2002/08/30 23:59:46 tgl Exp $
+ * $Id: executor.h,v 1.77 2002/09/02 01:05:06 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -122,7 +122,7 @@ extern void ExecInitScanTupleSlot(EState *estate,
 extern TupleTableSlot *ExecInitExtraTupleSlot(EState *estate);
 extern TupleTableSlot *ExecInitNullTupleSlot(EState *estate,
 					  TupleDesc tupType);
-extern TupleDesc ExecTypeFromTL(List *targetList, hasoid_t withoid);
+extern TupleDesc ExecTypeFromTL(List *targetList, bool hasoid);
 extern void SetChangedParamList(Plan *node, List *newchg);
 
 typedef struct TupOutputState
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
index a64be7fcf2ae8489dabbbc1f5efee6f6cf9927f9..670c80a4030624d61245598864a77657d75489a7 100644
--- a/src/include/pg_config.h.in
+++ b/src/include/pg_config.h.in
@@ -8,7 +8,7 @@
  * or in pg_config.h afterwards.  Of course, if you edit pg_config.h, then your
  * changes will be overwritten the next time you run configure.
  *
- * $Id: pg_config.h.in,v 1.29 2002/08/29 08:03:22 ishii Exp $
+ * $Id: pg_config.h.in,v 1.30 2002/09/02 01:05:06 tgl Exp $
  */
 
 #ifndef PG_CONFIG_H
@@ -298,12 +298,6 @@
 /* #define ACLDEBUG */
 /* #define RTDEBUG */
 /* #define GISTDEBUG */
-/*
- * DEBUG_TUPLE_ACCESS enables paranoid assertions during
- * elimination of oids from the fixed sized part of HeapTupleHeader.
- * This is expected to be undef'd after v7.3 release at the latest.
- */
-#define DEBUG_TUPLE_ACCESS
 
 /*
  * defining unsafe floats will make float4 and float8 ops faster
diff --git a/src/include/storage/bufpage.h b/src/include/storage/bufpage.h
index a9fc0f7a2a243fdc7eb6f5dd1b3ffa848b6c5371..7a6080a1bcd0071ba39cddfa1cd0f7717b581a21 100644
--- a/src/include/storage/bufpage.h
+++ b/src/include/storage/bufpage.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: bufpage.h,v 1.51 2002/08/06 19:37:10 tgl Exp $
+ * $Id: bufpage.h,v 1.52 2002/09/02 01:05:06 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -97,12 +97,20 @@ typedef uint16 LocationIndex;
  *		pd_lower   	- offset to start of free space.
  *		pd_upper   	- offset to end of free space.
  *		pd_special 	- offset to start of special space.
- *		pd_pagesize	- size in bytes.
- *					  Minimum possible page size is perhaps 64B to fit
- *					  page header, opaque space and a minimal tuple;
- *					  of course, in reality you want it much bigger.
- *					  On the high end, we can only support pages up
- *					  to 32KB because lp_off/lp_len are 15 bits.
+ *		pd_pagesize_version	- size in bytes and page layout version number.
+ *
+ * The page version number and page size are packed together into a single
+ * uint16 field.  This is for historical reasons: before PostgreSQL 7.3,
+ * there was no concept of a page version number, and doing it this way
+ * lets us pretend that pre-7.3 databases have page version number zero.
+ * We constrain page sizes to be multiples of 256, leaving the low eight
+ * bytes available for a version number.
+ *
+ * Minimum possible page size is perhaps 64B to fit page header, opaque space
+ * and a minimal tuple; of course, in reality you want it much bigger, so
+ * the constraint on pagesize mod 256 is not an important restriction.
+ * On the high end, we can only support pages up to 32KB because lp_off/lp_len
+ * are 15 bits.
  */
 typedef struct PageHeaderData
 {
@@ -116,12 +124,18 @@ typedef struct PageHeaderData
 	LocationIndex pd_lower;		/* offset to start of free space */
 	LocationIndex pd_upper;		/* offset to end of free space */
 	LocationIndex pd_special;	/* offset to start of special space */
-	uint16		pd_pagesize;
+	uint16		pd_pagesize_version;
 	ItemIdData	pd_linp[1];		/* beginning of line pointer array */
 } PageHeaderData;
 
 typedef PageHeaderData *PageHeader;
 
+/*
+ * Page layout version number 0 is for pre-7.3 Postgres releases.  The
+ * current version number is 1, denoting a new HeapTupleHeader layout.
+ */
+#define PG_PAGE_LAYOUT_VERSION		1
+
 
 /* ----------------------------------------------------------------
  *						page support macros
@@ -178,7 +192,7 @@ typedef PageHeaderData *PageHeader;
 	((char *) (&((PageHeader) (page))->pd_linp[0]))
 
 /* ----------------
- *		macros to access opaque space
+ *		macros to access page size info
  * ----------------
  */
 
@@ -203,14 +217,32 @@ typedef PageHeaderData *PageHeader;
  * however, it can be called on a page for which there is no buffer.
  */
 #define PageGetPageSize(page) \
-	((Size) ((PageHeader) (page))->pd_pagesize)
+	((Size) (((PageHeader) (page))->pd_pagesize_version & (uint16) 0xFF00))
 
 /*
- * PageSetPageSize
- *		Sets the page size of a page.
+ * PageGetPageLayoutVersion
+ *		Returns the page layout version of a page.
+ *
+ * this can only be called on a formatted page (unlike
+ * BufferGetPageSize, which can be called on an unformatted page).
+ * however, it can be called on a page for which there is no buffer.
  */
-#define PageSetPageSize(page, size) \
-	(((PageHeader) (page))->pd_pagesize = (size))
+#define PageGetPageLayoutVersion(page) \
+	(((PageHeader) (page))->pd_pagesize_version & 0x00FF)
+
+/*
+ * PageSetPageSizeAndVersion
+ *		Sets the page size and page layout version number of a page.
+ *
+ * We could support setting these two values separately, but there's
+ * no real need for it at the moment.
+ */
+#define PageSetPageSizeAndVersion(page, size, version) \
+( \
+	AssertMacro(((size) & 0xFF00) == (size)), \
+	AssertMacro(((version) & 0x00FF) == (version)), \
+	((PageHeader) (page))->pd_pagesize_version = (size) | (version) \
+)
 
 /* ----------------
  *		page special data macros