From 3212cf94176ff439095104693f45108f3c6d3a6d Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Mon, 27 May 2002 19:53:33 +0000
Subject: [PATCH] Distinguish between MaxHeapAttributeNumber and
 MaxTupleAttributeNumber, where the latter is made slightly larger to allow
 for in-memory tuples containing resjunk attributes.  Responds to today's
 complaint that one cannot UPDATE a table containing the allegedly-legal
 maximum number of columns.

Also, apply Manfred Koizar's recent patch to avoid extra alignment padding
when there is a null bitmap.  This saves bytes in some cases while not
creating any backward-compatibility problem AFAICS.
---
 src/backend/access/common/heaptuple.c | 22 ++++-----
 src/backend/access/heap/tuptoaster.c  | 32 +++++-------
 src/include/access/htup.h             | 70 ++++++++++++++++-----------
 3 files changed, 63 insertions(+), 61 deletions(-)

diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c
index 5b005b3163c..daad2bd5370 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.74 2001/10/25 05:49:20 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/access/common/heaptuple.c,v 1.75 2002/05/27 19:53:33 tgl Exp $
  *
  * NOTES
  *	  The old interface functions have been converted to macros
@@ -571,18 +571,15 @@ heap_formtuple(TupleDesc tupleDescriptor,
 {
 	HeapTuple	tuple;			/* return tuple */
 	HeapTupleHeader td;			/* tuple data */
-	int			bitmaplen;
 	unsigned long len;
 	int			hoff;
 	bool		hasnull = false;
 	int			i;
 	int			numberOfAttributes = tupleDescriptor->natts;
 
-	if (numberOfAttributes > MaxHeapAttributeNumber)
-		elog(ERROR, "heap_formtuple: numberOfAttributes of %d > %d",
-			 numberOfAttributes, MaxHeapAttributeNumber);
-
-	len = offsetof(HeapTupleHeaderData, t_bits);
+	if (numberOfAttributes > MaxTupleAttributeNumber)
+		elog(ERROR, "heap_formtuple: numberOfAttributes %d exceeds limit %d",
+			 numberOfAttributes, MaxTupleAttributeNumber);
 
 	for (i = 0; i < numberOfAttributes; i++)
 	{
@@ -593,13 +590,12 @@ heap_formtuple(TupleDesc tupleDescriptor,
 		}
 	}
 
+	len = offsetof(HeapTupleHeaderData, t_bits);
+
 	if (hasnull)
-	{
-		bitmaplen = BITMAPLEN(numberOfAttributes);
-		len += bitmaplen;
-	}
+		len += BITMAPLEN(numberOfAttributes);
 
-	hoff = len = MAXALIGN(len); /* be conservative here */
+	hoff = len = MAXALIGN(len); /* align user data safely */
 
 	len += ComputeDataSize(tupleDescriptor, value, nulls);
 
@@ -615,7 +611,7 @@ heap_formtuple(TupleDesc tupleDescriptor,
 	td->t_natts = numberOfAttributes;
 	td->t_hoff = hoff;
 
-	DataFill((char *) td + td->t_hoff,
+	DataFill((char *) td + hoff,
 			 tupleDescriptor,
 			 value,
 			 nulls,
diff --git a/src/backend/access/heap/tuptoaster.c b/src/backend/access/heap/tuptoaster.c
index ab5e96f8cc9..3c66aed1db1 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.31 2002/05/24 18:57:55 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/access/heap/tuptoaster.c,v 1.32 2002/05/27 19:53:33 tgl Exp $
  *
  *
  * INTERFACE ROUTINES
@@ -715,39 +715,34 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup)
 	 */
 	if (need_change)
 	{
+		HeapTupleHeader olddata = newtup->t_data;
 		char	   *new_data;
 		int32		new_len;
-		MemoryContext oldcxt;
-		HeapTupleHeader olddata;
 
 		/*
-		 * Calculate the new size of the tuple
+		 * Calculate the new size of the tuple.  Header size should not
+		 * change, but data size might.
 		 */
 		new_len = offsetof(HeapTupleHeaderData, t_bits);
 		if (has_nulls)
 			new_len += BITMAPLEN(numAttrs);
 		new_len = MAXALIGN(new_len);
+		Assert(new_len == olddata->t_hoff);
 		new_len += ComputeDataSize(tupleDesc, toast_values, toast_nulls);
 
 		/*
-		 * Remember the old memory location of the tuple (for below),
-		 * switch to the memory context of the HeapTuple structure and
-		 * allocate the new tuple.
+		 * Allocate new tuple in same context as old one.
 		 */
-		olddata = newtup->t_data;
-		oldcxt = MemoryContextSwitchTo(newtup->t_datamcxt);
-		new_data = palloc(new_len);
+		new_data = (char *) MemoryContextAlloc(newtup->t_datamcxt, new_len);
+		newtup->t_data = (HeapTupleHeader) new_data;
+		newtup->t_len = new_len;
 
 		/*
 		 * Put the tuple header and the changed values into place
 		 */
-		memcpy(new_data, newtup->t_data, newtup->t_data->t_hoff);
-		newtup->t_data = (HeapTupleHeader) new_data;
-		newtup->t_len = new_len;
+		memcpy(new_data, olddata, olddata->t_hoff);
 
-		DataFill((char *) (MAXALIGN((long) new_data +
-								  offsetof(HeapTupleHeaderData, t_bits) +
-							   ((has_nulls) ? BITMAPLEN(numAttrs) : 0))),
+		DataFill((char *) new_data + olddata->t_hoff,
 				 tupleDesc,
 				 toast_values,
 				 toast_nulls,
@@ -760,11 +755,6 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup)
 		 */
 		if ((char *) olddata != ((char *) newtup + HEAPTUPLESIZE))
 			pfree(olddata);
-
-		/*
-		 * Switch back to the old memory context
-		 */
-		MemoryContextSwitchTo(oldcxt);
 	}
 
 	/*
diff --git a/src/include/access/htup.h b/src/include/access/htup.h
index 08506bbf8fe..4860c4ec577 100644
--- a/src/include/access/htup.h
+++ b/src/include/access/htup.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: htup.h,v 1.51 2001/11/05 17:46:31 momjian Exp $
+ * $Id: htup.h,v 1.52 2002/05/27 19:53:33 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -17,25 +17,44 @@
 #include "storage/bufpage.h"
 #include "storage/relfilenode.h"
 
-#define MinHeapTupleBitmapSize	32		/* 8 * 4 */
 
 /*
- * MaxHeapAttributeNumber limits the number of (user) columns in a table.
+ * MaxTupleAttributeNumber limits the number of (user) columns in a tuple.
  * The key limit on this value is that the size of the fixed overhead for
  * a tuple, plus the size of the null-values bitmap (at 1 bit per column),
  * plus MAXALIGN alignment, must fit into t_hoff which is uint8.  On most
- * machines the absolute upper limit without making t_hoff wider would be
- * about 1700.	Note, however, that depending on column data types you will
- * likely also be running into the disk-block-based limit on overall tuple
- * size if you have more than a thousand or so columns.  TOAST won't help.
+ * machines the upper limit without making t_hoff wider would be a little
+ * over 1700.  We use round numbers here and for MaxHeapAttributeNumber
+ * so that alterations in HeapTupleHeaderData layout won't change the
+ * supported max number of columns.
+ */
+#define MaxTupleAttributeNumber	1664	/* 8 * 208 */
+
+/*----------
+ * MaxHeapAttributeNumber limits the number of (user) columns in a table.
+ * This should be somewhat less than MaxTupleAttributeNumber.  It must be
+ * at least one less, else we will fail to do UPDATEs on a maximal-width
+ * table (because UPDATE has to form working tuples that include CTID).
+ * In practice we want some additional daylight so that we can gracefully
+ * support operations that add hidden "resjunk" columns, for example
+ * SELECT * FROM wide_table ORDER BY foo, bar, baz.
+ * In any case, depending on column data types you will likely be running
+ * into the disk-block-based limit on overall tuple size if you have more
+ * than a thousand or so columns.  TOAST won't help.
+ *----------
  */
 #define MaxHeapAttributeNumber	1600	/* 8 * 200 */
 
 /*
- * This is the on-disk copy of the tuple.
+ * 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.
  *
  * To avoid wasting space, the attributes should be layed out in such a
- * way to reduce structure padding.
+ * 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.
  */
 typedef struct HeapTupleHeaderData
 {
@@ -51,14 +70,13 @@ typedef struct HeapTupleHeaderData
 
 	int16		t_natts;		/* number of attributes */
 
-	uint16		t_infomask;		/* various infos */
+	uint16		t_infomask;		/* various flag bits, see below */
 
-	uint8		t_hoff;			/* sizeof() tuple header */
+	uint8		t_hoff;			/* sizeof header incl. bitmap, padding */
 
 	/* ^ - 31 bytes - ^ */
 
-	bits8		t_bits[MinHeapTupleBitmapSize / 8];
-	/* bit map of NULLs */
+	bits8		t_bits[1];		/* bitmap of NULLs -- VARIABLE LENGTH */
 
 	/* MORE DATA FOLLOWS AT END OF STRUCT */
 } HeapTupleHeaderData;
@@ -183,7 +201,7 @@ typedef struct xl_heap_clean
 #define FirstLowInvalidHeapAttributeNumber		(-8)
 
 /*
- * This is the in-memory copy of the tuple.
+ * HeapTupleData is an in-memory data structure that points to a tuple.
  *
  * This new HeapTuple for version >= 6.5 and this is why it was changed:
  *
@@ -222,11 +240,9 @@ typedef HeapTupleData *HeapTuple;
 
 /*
  * BITMAPLEN(NATTS) -
- *		Computes minimum size of bitmap given number of domains.
+ *		Computes size of null bitmap given number of data columns.
  */
-#define BITMAPLEN(NATTS) \
-		((((((int)(NATTS) - 1) >> 3) + 4 - (MinHeapTupleBitmapSize >> 3)) \
-		  & ~03) + (MinHeapTupleBitmapSize >> 3))
+#define BITMAPLEN(NATTS)	(((int)(NATTS) + 7) / 8)
 
 /*
  * HeapTupleIsValid
@@ -240,26 +256,26 @@ typedef HeapTupleData *HeapTuple;
 #define HEAP_HASNULL			0x0001	/* has null attribute(s) */
 #define HEAP_HASVARLENA			0x0002	/* has variable length
 										 * attribute(s) */
-#define HEAP_HASEXTERNAL		0x0004	/* has external stored */
- /* attribute(s) */
-#define HEAP_HASCOMPRESSED		0x0008	/* has compressed stored */
- /* attribute(s) */
+#define HEAP_HASEXTERNAL		0x0004	/* has external stored
+										 * attribute(s) */
+#define HEAP_HASCOMPRESSED		0x0008	/* has compressed stored
+										 * attribute(s) */
 #define HEAP_HASEXTENDED		0x000C	/* the two above combined */
 
-#define HEAP_XMAX_UNLOGGED		0x0080	/* to lock tuple for update */
- /* without logging */
+#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 */
 #define HEAP_XMAX_INVALID		0x0800	/* t_xmax invalid/aborted */
 #define HEAP_MARKED_FOR_UPDATE	0x1000	/* marked for UPDATE */
 #define HEAP_UPDATED			0x2000	/* this is UPDATEd version of row */
-#define HEAP_MOVED_OFF			0x4000	/* removed or moved to another
-										 * place by vacuum */
+#define HEAP_MOVED_OFF			0x4000	/* moved to another place by
+										 * vacuum */
 #define HEAP_MOVED_IN			0x8000	/* moved from another place by
 										 * vacuum */
 
-#define HEAP_XACT_MASK			0xFFF0	/* */
+#define HEAP_XACT_MASK			0xFFF0	/* visibility-related bits */
 
 #define HeapTupleNoNulls(tuple) \
 		(!(((HeapTuple) (tuple))->t_data->t_infomask & HEAP_HASNULL))
-- 
GitLab