diff --git a/configure b/configure
index c5a20b1e122d789b2d7a482ccaa513fce8266ab0..e53c705d7141059aa048cf55b5e78a345e76c402 100755
--- a/configure
+++ b/configure
@@ -11143,7 +11143,237 @@ fi
 ## Types, structures, compiler characteristics
 ##
 
- echo "$as_me:$LINENO: checking for an ANSI C-conforming const" >&5
+ echo "$as_me:$LINENO: checking whether byte ordering is bigendian" >&5
+echo $ECHO_N "checking whether byte ordering is bigendian... $ECHO_C" >&6
+if test "${ac_cv_c_bigendian+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  # See if sys/param.h defines the BYTE_ORDER macro.
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <sys/types.h>
+#include <sys/param.h>
+
+int
+main ()
+{
+#if !BYTE_ORDER || !BIG_ENDIAN || !LITTLE_ENDIAN
+ bogus endian macros
+#endif
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  # It does; now see whether it defined to BIG_ENDIAN or not.
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <sys/types.h>
+#include <sys/param.h>
+
+int
+main ()
+{
+#if BYTE_ORDER != BIG_ENDIAN
+ not big endian
+#endif
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_c_bigendian=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_c_bigendian=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+# It does not; compile a test program.
+if test "$cross_compiling" = yes; then
+  # try to guess the endianness by grepping values into an object file
+  ac_cv_c_bigendian=unknown
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+short ascii_mm[] = { 0x4249, 0x4765, 0x6E44, 0x6961, 0x6E53, 0x7953, 0 };
+short ascii_ii[] = { 0x694C, 0x5454, 0x656C, 0x6E45, 0x6944, 0x6E61, 0 };
+void _ascii () { char *s = (char *) ascii_mm; s = (char *) ascii_ii; }
+short ebcdic_ii[] = { 0x89D3, 0xE3E3, 0x8593, 0x95C5, 0x89C4, 0x9581, 0 };
+short ebcdic_mm[] = { 0xC2C9, 0xC785, 0x95C4, 0x8981, 0x95E2, 0xA8E2, 0 };
+void _ebcdic () { char *s = (char *) ebcdic_mm; s = (char *) ebcdic_ii; }
+int
+main ()
+{
+ _ascii (); _ebcdic ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  if grep BIGenDianSyS conftest.$ac_objext >/dev/null ; then
+  ac_cv_c_bigendian=yes
+fi
+if grep LiTTleEnDian conftest.$ac_objext >/dev/null ; then
+  if test "$ac_cv_c_bigendian" = unknown; then
+    ac_cv_c_bigendian=no
+  else
+    # finding both strings is unlikely to happen, but who knows?
+    ac_cv_c_bigendian=unknown
+  fi
+fi
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+int
+main ()
+{
+  /* Are we little or big endian?  From Harbison&Steele.  */
+  union
+  {
+    long l;
+    char c[sizeof (long)];
+  } u;
+  u.l = 1;
+  exit (u.c[sizeof (long) - 1] == 1);
+}
+_ACEOF
+rm -f conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && { ac_try='./conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_c_bigendian=no
+else
+  echo "$as_me: program exited with status $ac_status" >&5
+echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+( exit $ac_status )
+ac_cv_c_bigendian=yes
+fi
+rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext
+fi
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: $ac_cv_c_bigendian" >&5
+echo "${ECHO_T}$ac_cv_c_bigendian" >&6
+case $ac_cv_c_bigendian in
+  yes)
+
+cat >>confdefs.h <<\_ACEOF
+#define WORDS_BIGENDIAN 1
+_ACEOF
+ ;;
+  no)
+     ;;
+  *)
+    { { echo "$as_me:$LINENO: error: unknown endianness
+presetting ac_cv_c_bigendian=no (or yes) will help" >&5
+echo "$as_me: error: unknown endianness
+presetting ac_cv_c_bigendian=no (or yes) will help" >&2;}
+   { (exit 1); exit 1; }; } ;;
+esac
+
+echo "$as_me:$LINENO: checking for an ANSI C-conforming const" >&5
 echo $ECHO_N "checking for an ANSI C-conforming const... $ECHO_C" >&6
 if test "${ac_cv_c_const+set}" = set; then
   echo $ECHO_N "(cached) $ECHO_C" >&6
diff --git a/configure.in b/configure.in
index 03511fbd464e5349d67948dc162326e74abba467..9f7bf314d5bf89083f53a1a0f3d8cdadd97ee4ad 100644
--- a/configure.in
+++ b/configure.in
@@ -1,5 +1,5 @@
 dnl Process this file with autoconf to produce a configure script.
-dnl $PostgreSQL: pgsql/configure.in,v 1.507 2007/03/29 15:30:51 mha Exp $
+dnl $PostgreSQL: pgsql/configure.in,v 1.508 2007/04/06 04:21:41 tgl Exp $
 dnl
 dnl Developers, please strive to achieve this order:
 dnl
@@ -869,6 +869,7 @@ fi
 ##
 
 m4_defun([AC_PROG_CC_STDC], []) dnl We don't want that.
+AC_C_BIGENDIAN
 AC_C_CONST
 AC_C_INLINE
 AC_C_STRINGIZE
diff --git a/contrib/dblink/dblink.c b/contrib/dblink/dblink.c
index 568ac1e5f73d05a33bc7a9ccf11b48e7107cc323..b42dd026724055763ff6a8a0847edbdf2a0baa3d 100644
--- a/contrib/dblink/dblink.c
+++ b/contrib/dblink/dblink.c
@@ -8,7 +8,7 @@
  * Darko Prenosil <Darko.Prenosil@finteh.hr>
  * Shridhar Daithankar <shridhar_daithankar@persistent.co.in>
  *
- * $PostgreSQL: pgsql/contrib/dblink/dblink.c,v 1.62 2007/02/07 00:52:35 petere Exp $
+ * $PostgreSQL: pgsql/contrib/dblink/dblink.c,v 1.63 2007/04/06 04:21:41 tgl Exp $
  * Copyright (c) 2001-2007, PostgreSQL Global Development Group
  * ALL RIGHTS RESERVED;
  *
@@ -1752,8 +1752,8 @@ get_text_array_contents(ArrayType *array, int *numitems)
 		{
 			values[i] = DatumGetCString(DirectFunctionCall1(textout,
 													  PointerGetDatum(ptr)));
-			ptr = att_addlength(ptr, typlen, PointerGetDatum(ptr));
-			ptr = (char *) att_align(ptr, typalign);
+			ptr = att_addlength_pointer(ptr, typlen, ptr);
+			ptr = (char *) att_align_nominal(ptr, typalign);
 		}
 
 		/* advance bitmap pointer if any */
diff --git a/contrib/hstore/hstore_gist.c b/contrib/hstore/hstore_gist.c
index fbee64be7da71b207608f0d736cba2265e994e39..19ed74933f8f5d2f8113470f0ae043f4799b2479 100644
--- a/contrib/hstore/hstore_gist.c
+++ b/contrib/hstore/hstore_gist.c
@@ -170,7 +170,25 @@ ghstore_compress(PG_FUNCTION_ARGS)
 Datum
 ghstore_decompress(PG_FUNCTION_ARGS)
 {
-	PG_RETURN_DATUM(PG_GETARG_DATUM(0));
+	GISTENTRY  *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
+	GISTENTRY  *retval;
+	HStore *key;
+
+	key = (HStore *) PG_DETOAST_DATUM(entry->key);
+
+	if (key != (HStore *) DatumGetPointer(entry->key))
+	{
+		/* need to pass back the decompressed item */
+		retval = palloc(sizeof(GISTENTRY));
+		gistentryinit(*retval, PointerGetDatum(key),
+					  entry->rel, entry->page, entry->offset, entry->leafkey);
+		PG_RETURN_POINTER(retval);
+	}
+	else
+	{
+		/* we can return the entry as-is */
+		PG_RETURN_POINTER(entry);
+	}
 }
 
 Datum
diff --git a/contrib/intarray/_int_gist.c b/contrib/intarray/_int_gist.c
index 56eb0c08c2e53835755011fba0ce2574ffbc3a83..3c34cb67a7aa7a6cef4c7a9ec9d9240ea39272d3 100644
--- a/contrib/intarray/_int_gist.c
+++ b/contrib/intarray/_int_gist.c
@@ -232,7 +232,16 @@ g_int_decompress(PG_FUNCTION_ARGS)
 
 	CHECKARRVALID(in);
 	if (ARRISVOID(in))
+	{
+		if (in != (ArrayType *) DatumGetPointer(entry->key)) {
+			retval = palloc(sizeof(GISTENTRY));
+			gistentryinit(*retval, PointerGetDatum(in),
+				entry->rel, entry->page, entry->offset, FALSE);
+			PG_RETURN_POINTER(retval);
+		}
+
 		PG_RETURN_POINTER(entry);
+	}
 
 	lenin = ARRNELEMS(in);
 
diff --git a/contrib/pg_trgm/trgm_gist.c b/contrib/pg_trgm/trgm_gist.c
index 476cb1b9763f72462370449eba9a45003054107b..260fe01da42e2b5ea6d6dc0f87156aae2fe9b3be 100644
--- a/contrib/pg_trgm/trgm_gist.c
+++ b/contrib/pg_trgm/trgm_gist.c
@@ -97,7 +97,7 @@ gtrgm_compress(PG_FUNCTION_ARGS)
 	if (entry->leafkey)
 	{							/* trgm */
 		TRGM	   *res;
-		text	   *val = (text *) DatumGetPointer(PG_DETOAST_DATUM(entry->key));
+		text	   *val = DatumGetTextP(entry->key);
 
 		res = generate_trgm(VARDATA(val), VARSIZE(val) - VARHDRSZ);
 		retval = (GISTENTRY *) palloc(sizeof(GISTENTRY));
@@ -134,7 +134,25 @@ gtrgm_compress(PG_FUNCTION_ARGS)
 Datum
 gtrgm_decompress(PG_FUNCTION_ARGS)
 {
-	PG_RETURN_DATUM(PG_GETARG_DATUM(0));
+	GISTENTRY  *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
+	GISTENTRY  *retval;
+	text *key;
+
+	key = DatumGetTextP(entry->key);
+
+	if (key != (text *) DatumGetPointer(entry->key))
+	{
+		/* need to pass back the decompressed item */
+		retval = palloc(sizeof(GISTENTRY));
+		gistentryinit(*retval, PointerGetDatum(key),
+					  entry->rel, entry->page, entry->offset, entry->leafkey);
+		PG_RETURN_POINTER(retval);
+	}
+	else
+	{
+		/* we can return the entry as-is */
+		PG_RETURN_POINTER(entry);
+	}
 }
 
 Datum
diff --git a/contrib/tsearch2/ts_cfg.c b/contrib/tsearch2/ts_cfg.c
index 0dc17703c3832fa8a691423ea39957f6831a34e9..646ffc148117df10f20c60bcfbac8b16b6562335 100644
--- a/contrib/tsearch2/ts_cfg.c
+++ b/contrib/tsearch2/ts_cfg.c
@@ -62,9 +62,7 @@ init_cfg(Oid id, TSCfgInfo * cfg)
 		ts_error(ERROR, "SPI_execp return %d", stat);
 	if (SPI_processed > 0)
 	{
-		prsname = (text *) DatumGetPointer(
-										   SPI_getbinval(SPI_tuptable->vals[0], SPI_tuptable->tupdesc, 1, &isnull)
-			);
+		prsname = DatumGetTextP(SPI_getbinval(SPI_tuptable->vals[0], SPI_tuptable->tupdesc, 1, &isnull));
 		oldcontext = MemoryContextSwitchTo(TopMemoryContext);
 		prsname = ptextdup(prsname);
 		MemoryContextSwitchTo(oldcontext);
diff --git a/doc/src/sgml/storage.sgml b/doc/src/sgml/storage.sgml
index 1973a5b90c31d273051c282da2ec1cebe5fee138..9c3cf7589da4b3dad47d61ecd62d5af902a7dc95 100644
--- a/doc/src/sgml/storage.sgml
+++ b/doc/src/sgml/storage.sgml
@@ -1,4 +1,4 @@
-<!-- $PostgreSQL: pgsql/doc/src/sgml/storage.sgml,v 1.16 2007/04/03 04:14:26 tgl Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/storage.sgml,v 1.17 2007/04/06 04:21:41 tgl Exp $ -->
 
 <chapter id="storage">
 
@@ -210,18 +210,27 @@ value, but in some cases more efficient approaches are possible.)
 </para>
 
 <para>
-<acronym>TOAST</> usurps the high-order two bits of the varlena length word,
+<acronym>TOAST</> usurps two bits of the varlena length word (the high-order
+bits on big-endian machines, the low-order bits on little-endian machines),
 thereby limiting the logical size of any value of a <acronym>TOAST</>-able
 data type to 1 GB (2<superscript>30</> - 1 bytes).  When both bits are zero,
-the value is an ordinary un-<acronym>TOAST</>ed value of the data type.  One
-of these bits, if set, indicates that the value has been compressed and must
-be decompressed before use.  The other bit, if set, indicates that the value
-has been stored out-of-line.  In this case the remainder of the value is
-actually just a pointer, and the correct data has to be found elsewhere.  When
-both bits are set, the out-of-line data has been compressed too.  In each case
-the length in the low-order bits of the varlena word indicates the actual size
-of the datum, not the size of the logical value that would be extracted by
-decompression or fetching of the out-of-line data.
+the value is an ordinary un-<acronym>TOAST</>ed value of the data type, and
+the remaining bits of the length word give the total datum size (including
+length word) in bytes.  When the highest-order or lowest-order bit is set,
+the value has only a single-byte header instead of the normal four-byte
+header, and the remaining bits give the total datum size (including length
+byte) in bytes.  As a special case, if the remaining bits are all zero
+(which would be impossible for a self-inclusive length), the value is a
+pointer to out-of-line data stored in a separate TOAST table.  (The size of
+a TOAST pointer is known a priori, so it doesn't need to be represented in
+the header.)  Values with single-byte headers aren't aligned on any particular
+boundary, either.  Lastly, when the highest-order or lowest-order bit is
+clear but the adjacent bit is set, the content of the datum has been
+compressed and must be decompressed before use.  In this case the remaining
+bits of the length word give the total size of the compressed datum, not the
+original data.  Note that compression is also possible for out-of-line data
+but the varlena header does not tell whether it has occurred &mdash;
+the content of the TOAST pointer tells that, instead.
 </para>
 
 <para>
@@ -254,8 +263,8 @@ retrieval of the values.  A pointer datum representing an out-of-line
 <acronym>TOAST</> table in which to look and the OID of the specific value
 (its <structfield>chunk_id</>).  For convenience, pointer datums also store the
 logical datum size (original uncompressed data length) and actual stored size
-(different if compression was applied).  Allowing for the varlena header word,
-the total size of a <acronym>TOAST</> pointer datum is therefore 20 bytes
+(different if compression was applied).  Allowing for the varlena header byte,
+the total size of a <acronym>TOAST</> pointer datum is therefore 17 bytes
 regardless of the actual size of the represented value.
 </para>
 
@@ -280,7 +289,9 @@ The <acronym>TOAST</> code recognizes four different strategies for storing
     <listitem>
      <para>
       <literal>PLAIN</literal> prevents either compression or
-      out-of-line storage.  This is the only possible strategy for
+      out-of-line storage; furthermore it disables use of single-byte headers
+      for varlena types.
+      This is the only possible strategy for
       columns of non-<acronym>TOAST</>-able data types.
      </para>
     </listitem>
@@ -562,7 +573,7 @@ data. Empty in ordinary tables.</entry>
  <para>
 
   All table rows are structured in the same way. There is a fixed-size
-  header (occupying 27 bytes on most machines), followed by an optional null
+  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
@@ -604,12 +615,6 @@ data. Empty in ordinary tables.</entry>
    <entry>4 bytes</entry>
    <entry>insert XID stamp</entry>
   </row>
-  <row>
-   <entry>t_cmin</entry>
-   <entry>CommandId</entry>
-   <entry>4 bytes</entry>
-   <entry>insert CID stamp</entry>
-  </row>
   <row>
    <entry>t_xmax</entry>
    <entry>TransactionId</entry>
@@ -617,10 +622,10 @@ data. Empty in ordinary tables.</entry>
    <entry>delete XID stamp</entry>
   </row>
   <row>
-   <entry>t_cmax</entry>
+   <entry>t_cid</entry>
    <entry>CommandId</entry>
    <entry>4 bytes</entry>
-   <entry>delete CID stamp (overlays with t_xvac)</entry>
+   <entry>insert and/or delete CID stamp (overlays with t_xvac)</entry>
   </row>
   <row>
    <entry>t_xvac</entry>
@@ -635,10 +640,10 @@ data. Empty in ordinary tables.</entry>
    <entry>current TID of this or newer row version</entry>
   </row>
   <row>
-   <entry>t_natts</entry>
+   <entry>t_infomask2</entry>
    <entry>int16</entry>
    <entry>2 bytes</entry>
-   <entry>number of attributes</entry>
+   <entry>number of attributes, plus various flag bits</entry>
   </row>
   <row>
    <entry>t_infomask</entry>
@@ -682,7 +687,7 @@ data. Empty in ordinary tables.</entry>
   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.
   All variable-length datatypes share the common header structure
-  <type>varattrib</type>, which includes the total length of the stored
+  <type>struct varlena</type>, which includes the total length of the stored
   value and some flag bits.  Depending on the flags, the data can be either
   inline or in a <acronym>TOAST</> table;
   it might be compressed, too (see <xref linkend="storage-toast">).
diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c
index 0c83262c3b8f4321dba82d2f1202ffe889840c2a..f1a80d6feeecd4a8288a12aac171a3f25e498997 100644
--- a/src/backend/access/common/heaptuple.c
+++ b/src/backend/access/common/heaptuple.c
@@ -11,12 +11,53 @@
  * we can get rid of it entirely.
  *
  *
+ * Some notes about varlenas and this code:
+ *
+ * Before Postgres 8.3 varlenas always had a 4-byte length header, and
+ * therefore always needed 4-byte alignment (at least).  This wasted space
+ * for short varlenas, for example CHAR(1) took 5 bytes and could need up to
+ * 3 additional padding bytes for alignment.
+ *
+ * Now, a short varlena (up to 126 data bytes) is reduced to a 1-byte header
+ * and we don't align it.  To hide this from datatype-specific functions that
+ * don't want to deal with it, such a datum is considered "toasted" and will
+ * be expanded back to the normal 4-byte-header format by pg_detoast_datum.
+ * (In performance-critical code paths we can use pg_detoast_datum_packed
+ * and the appropriate access macros to avoid that overhead.)  Note that this
+ * conversion is performed directly in heap_form_tuple (or heap_formtuple),
+ * without explicitly invoking the toaster.
+ *
+ * This change will break any code that assumes it needn't detoast values
+ * that have been put into a tuple but never sent to disk.  Hopefully there
+ * are few such places.
+ *
+ * Varlenas still have alignment 'i' (or 'd') in pg_type/pg_attribute, since
+ * that's the normal requirement for the untoasted format.  But we ignore that
+ * for the 1-byte-header format.  This means that the actual start position
+ * of a varlena datum may vary depending on which format it has.  To determine
+ * what is stored, we have to require that alignment padding bytes be zero.
+ * (Postgres actually has always zeroed them, but now it's required!)  Since
+ * the first byte of a 1-byte-header varlena can never be zero, we can examine
+ * the first byte after the previous datum to tell if it's a pad byte or the
+ * start of a 1-byte-header varlena.
+ *
+ * Note that while formerly we could rely on the first varlena column of a
+ * system catalog to be at the offset suggested by the C struct for the
+ * catalog, this is now risky: it's only safe if the preceding field is
+ * word-aligned, so that there will never be any padding.
+ *
+ * We don't pack varlenas whose attstorage is 'p', since the data type
+ * isn't expecting to have to detoast values.  This is used in particular
+ * by oidvector and int2vector, which are used in the system catalogs
+ * and we'd like to still refer to them via C struct offsets.
+ *
+ *
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/access/common/heaptuple.c,v 1.116 2007/02/27 23:48:06 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/access/common/heaptuple.c,v 1.117 2007/04/06 04:21:41 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -28,11 +69,20 @@
 #include "executor/tuptable.h"
 
 
+/* Does att's datatype allow packing into the 1-byte-header varlena format? */
+#define ATT_IS_PACKABLE(att) \
+	((att)->attlen == -1 && (att)->attstorage != 'p')
+/* Use this if it's already known varlena */
+#define VARLENA_ATT_IS_PACKABLE(att) \
+	((att)->attstorage != 'p')
+
+
 /* ----------------------------------------------------------------
  *						misc support routines
  * ----------------------------------------------------------------
  */
 
+
 /*
  * heap_compute_data_size
  *		Determine size of the data area of a tuple to be constructed
@@ -49,11 +99,29 @@ heap_compute_data_size(TupleDesc tupleDesc,
 
 	for (i = 0; i < numberOfAttributes; i++)
 	{
+		Datum	val;
+
 		if (isnull[i])
 			continue;
 
-		data_length = att_align(data_length, att[i]->attalign);
-		data_length = att_addlength(data_length, att[i]->attlen, values[i]);
+		val = values[i];
+
+ 		if (ATT_IS_PACKABLE(att[i]) &&
+			VARATT_CAN_MAKE_SHORT(DatumGetPointer(val)))
+		{
+			/*
+			 * we're anticipating converting to a short varlena header,
+			 * so adjust length and don't count any alignment
+			 */
+			data_length += VARATT_CONVERTED_SHORT_SIZE(DatumGetPointer(val));
+		}
+		else
+		{
+			data_length = att_align_datum(data_length, att[i]->attalign,
+										  att[i]->attlen, val);
+			data_length = att_addlength_datum(data_length, att[i]->attlen,
+											  val);
+		}
 	}
 
 	return data_length;
@@ -79,11 +147,29 @@ ComputeDataSize(TupleDesc tupleDesc,
 
 	for (i = 0; i < numberOfAttributes; i++)
 	{
+		Datum	val;
+
 		if (nulls[i] != ' ')
 			continue;
 
-		data_length = att_align(data_length, att[i]->attalign);
-		data_length = att_addlength(data_length, att[i]->attlen, values[i]);
+		val = values[i];
+
+ 		if (ATT_IS_PACKABLE(att[i]) &&
+			VARATT_CAN_MAKE_SHORT(DatumGetPointer(val)))
+		{
+			/*
+			 * we're anticipating converting to a short varlena header,
+			 * so adjust length and don't count any alignment
+			 */
+			data_length += VARATT_CONVERTED_SHORT_SIZE(DatumGetPointer(val));
+		}
+		else
+		{
+			data_length = att_align_datum(data_length, att[i]->attalign,
+										  att[i]->attlen, val);
+			data_length = att_addlength_datum(data_length, att[i]->attlen,
+											  val);
+		}
 	}
 
 	return data_length;
@@ -95,17 +181,23 @@ ComputeDataSize(TupleDesc tupleDesc,
  *
  * We also fill the null bitmap (if any) and set the infomask bits
  * that reflect the tuple's data contents.
+ *
+ * NOTE: it is now REQUIRED that the caller have pre-zeroed the data area.
  */
 void
 heap_fill_tuple(TupleDesc tupleDesc,
 				Datum *values, bool *isnull,
-				char *data, uint16 *infomask, bits8 *bit)
+				char *data, Size data_size,
+				uint16 *infomask, bits8 *bit)
 {
 	bits8	   *bitP;
 	int			bitmask;
 	int			i;
 	int			numberOfAttributes = tupleDesc->natts;
 	Form_pg_attribute *att = tupleDesc->attrs;
+#ifdef USE_ASSERT_CHECKING
+	char	   *start = data;
+#endif
 
 	if (bit != NULL)
 	{
@@ -119,7 +211,7 @@ heap_fill_tuple(TupleDesc tupleDesc,
 		bitmask = 0;
 	}
 
-	*infomask &= ~(HEAP_HASNULL | HEAP_HASVARWIDTH | HEAP_HASEXTENDED);
+	*infomask &= ~(HEAP_HASNULL | HEAP_HASVARWIDTH | HEAP_HASEXTERNAL);
 
 	for (i = 0; i < numberOfAttributes; i++)
 	{
@@ -145,36 +237,66 @@ heap_fill_tuple(TupleDesc tupleDesc,
 			*bitP |= bitmask;
 		}
 
-		/* XXX we are aligning the pointer itself, not the offset */
-		data = (char *) att_align((long) data, att[i]->attalign);
+		/*
+		 * XXX we use the att_align macros on the pointer value itself,
+		 * not on an offset.  This is a bit of a hack.
+		 */
 
 		if (att[i]->attbyval)
 		{
 			/* pass-by-value */
+			data = (char *) att_align_nominal((long) data, att[i]->attalign);
 			store_att_byval(data, values[i], att[i]->attlen);
 			data_length = att[i]->attlen;
 		}
 		else if (att[i]->attlen == -1)
 		{
 			/* varlena */
+			Pointer		val = DatumGetPointer(values[i]);
+
 			*infomask |= HEAP_HASVARWIDTH;
-			if (VARATT_IS_EXTERNAL(values[i]))
+			if (VARATT_IS_EXTERNAL(val))
+			{
 				*infomask |= HEAP_HASEXTERNAL;
-			if (VARATT_IS_COMPRESSED(values[i]))
-				*infomask |= HEAP_HASCOMPRESSED;
-			data_length = VARSIZE(DatumGetPointer(values[i]));
-			memcpy(data, DatumGetPointer(values[i]), data_length);
+				/* no alignment, since it's short by definition */
+				data_length = VARSIZE_EXTERNAL(val);
+				memcpy(data, val, data_length);
+			}
+			else if (VARATT_IS_SHORT(val))
+			{
+				/* no alignment for short varlenas */
+				data_length = VARSIZE_SHORT(val);
+				memcpy(data, val, data_length);
+			}
+			else if (VARLENA_ATT_IS_PACKABLE(att[i]) &&
+					 VARATT_CAN_MAKE_SHORT(val))
+			{
+				/* convert to short varlena -- no alignment */
+				data_length = VARATT_CONVERTED_SHORT_SIZE(val);
+				SET_VARSIZE_SHORT(data, data_length);
+				memcpy(data + 1, VARDATA(val), data_length - 1);
+			}
+			else
+			{
+				/* full 4-byte header varlena */
+				data = (char *) att_align_nominal((long) data,
+												  att[i]->attalign);
+				data_length = VARSIZE(val);
+				memcpy(data, val, data_length);
+			}
 		}
 		else if (att[i]->attlen == -2)
 		{
-			/* cstring */
+			/* cstring ... never needs alignment */
 			*infomask |= HEAP_HASVARWIDTH;
+			Assert(att[i]->attalign == 'c');
 			data_length = strlen(DatumGetCString(values[i])) + 1;
 			memcpy(data, DatumGetPointer(values[i]), data_length);
 		}
 		else
 		{
 			/* fixed-length pass-by-reference */
+			data = (char *) att_align_nominal((long) data, att[i]->attalign);
 			Assert(att[i]->attlen > 0);
 			data_length = att[i]->attlen;
 			memcpy(data, DatumGetPointer(values[i]), data_length);
@@ -182,6 +304,8 @@ heap_fill_tuple(TupleDesc tupleDesc,
 
 		data += data_length;
 	}
+
+	Assert((data - start) == data_size);
 }
 
 /* ----------------
@@ -193,18 +317,19 @@ heap_fill_tuple(TupleDesc tupleDesc,
  * ----------------
  */
 static void
-DataFill(char *data,
-		 TupleDesc tupleDesc,
-		 Datum *values,
-		 char *nulls,
-		 uint16 *infomask,
-		 bits8 *bit)
+DataFill(TupleDesc tupleDesc,
+		 Datum *values, char *nulls,
+		 char *data, Size data_size,
+		 uint16 *infomask, bits8 *bit)
 {
 	bits8	   *bitP;
 	int			bitmask;
 	int			i;
 	int			numberOfAttributes = tupleDesc->natts;
 	Form_pg_attribute *att = tupleDesc->attrs;
+#ifdef USE_ASSERT_CHECKING
+	char	   *start = data;
+#endif
 
 	if (bit != NULL)
 	{
@@ -218,7 +343,7 @@ DataFill(char *data,
 		bitmask = 0;
 	}
 
-	*infomask &= ~(HEAP_HASNULL | HEAP_HASVARWIDTH | HEAP_HASEXTENDED);
+	*infomask &= ~(HEAP_HASNULL | HEAP_HASVARWIDTH | HEAP_HASEXTERNAL);
 
 	for (i = 0; i < numberOfAttributes; i++)
 	{
@@ -244,36 +369,66 @@ DataFill(char *data,
 			*bitP |= bitmask;
 		}
 
-		/* XXX we are aligning the pointer itself, not the offset */
-		data = (char *) att_align((long) data, att[i]->attalign);
+		/*
+		 * XXX we use the att_align macros on the pointer value itself,
+		 * not on an offset.  This is a bit of a hack.
+		 */
 
 		if (att[i]->attbyval)
 		{
 			/* pass-by-value */
+			data = (char *) att_align_nominal((long) data, att[i]->attalign);
 			store_att_byval(data, values[i], att[i]->attlen);
 			data_length = att[i]->attlen;
 		}
 		else if (att[i]->attlen == -1)
 		{
 			/* varlena */
+			Pointer		val = DatumGetPointer(values[i]);
+
 			*infomask |= HEAP_HASVARWIDTH;
-			if (VARATT_IS_EXTERNAL(values[i]))
+			if (VARATT_IS_EXTERNAL(val))
+			{
 				*infomask |= HEAP_HASEXTERNAL;
-			if (VARATT_IS_COMPRESSED(values[i]))
-				*infomask |= HEAP_HASCOMPRESSED;
-			data_length = VARSIZE(DatumGetPointer(values[i]));
-			memcpy(data, DatumGetPointer(values[i]), data_length);
+				/* no alignment, since it's short by definition */
+				data_length = VARSIZE_EXTERNAL(val);
+				memcpy(data, val, data_length);
+			}
+			else if (VARATT_IS_SHORT(val))
+			{
+				/* no alignment for short varlenas */
+				data_length = VARSIZE_SHORT(val);
+				memcpy(data, val, data_length);
+			}
+			else if (VARLENA_ATT_IS_PACKABLE(att[i]) &&
+					 VARATT_CAN_MAKE_SHORT(val))
+			{
+				/* convert to short varlena -- no alignment */
+				data_length = VARATT_CONVERTED_SHORT_SIZE(val);
+				SET_VARSIZE_SHORT(data, data_length);
+				memcpy(data + 1, VARDATA(val), data_length - 1);
+			}
+			else
+			{
+				/* full 4-byte header varlena */
+				data = (char *) att_align_nominal((long) data,
+												  att[i]->attalign);
+				data_length = VARSIZE(val);
+				memcpy(data, val, data_length);
+			}
 		}
 		else if (att[i]->attlen == -2)
 		{
-			/* cstring */
+			/* cstring ... never needs alignment */
 			*infomask |= HEAP_HASVARWIDTH;
+			Assert(att[i]->attalign == 'c');
 			data_length = strlen(DatumGetCString(values[i])) + 1;
 			memcpy(data, DatumGetPointer(values[i]), data_length);
 		}
 		else
 		{
 			/* fixed-length pass-by-reference */
+			data = (char *) att_align_nominal((long) data, att[i]->attalign);
 			Assert(att[i]->attlen > 0);
 			data_length = att[i]->attlen;
 			memcpy(data, DatumGetPointer(values[i]), data_length);
@@ -281,6 +436,8 @@ DataFill(char *data,
 
 		data += data_length;
 	}
+
+	Assert((data - start) == data_size);
 }
 
 /* ----------------------------------------------------------------
@@ -343,6 +500,8 @@ heap_attisnull(HeapTuple tup, int attnum)
  *		the same attribute descriptor will go much quicker. -cim 5/4/91
  *
  *		NOTE: if you need to change this code, see also heap_deform_tuple.
+ *		Also see nocache_index_getattr, which is the same code for index
+ *		tuples.
  * ----------------
  */
 Datum
@@ -353,20 +512,12 @@ nocachegetattr(HeapTuple tuple,
 {
 	HeapTupleHeader tup = tuple->t_data;
 	Form_pg_attribute *att = tupleDesc->attrs;
-	char	   *tp;				/* ptr to att in tuple */
+	char	   *tp;				/* ptr to data part of tuple */
 	bits8	   *bp = tup->t_bits;		/* ptr to null bitmap in tuple */
-	bool		slow = false;	/* do we have to walk nulls? */
+	bool		slow = false;	/* do we have to walk attrs? */
+	int			off;			/* current offset within data */
 
 	(void) isnull;				/* not used */
-#ifdef IN_MACRO
-/* This is handled in the macro */
-	Assert(attnum > 0);
-
-	if (isnull)
-		*isnull = false;
-#endif
-
-	attnum--;
 
 	/* ----------------
 	 *	 Three cases:
@@ -377,11 +528,21 @@ nocachegetattr(HeapTuple tuple,
 	 * ----------------
 	 */
 
+#ifdef IN_MACRO
+/* This is handled in the macro */
+	Assert(attnum > 0);
+
+	if (isnull)
+		*isnull = false;
+#endif
+
+	attnum--;
+
 	if (HeapTupleNoNulls(tuple))
 	{
 #ifdef IN_MACRO
 /* This is handled in the macro */
-		if (att[attnum]->attcacheoff != -1)
+		if (att[attnum]->attcacheoff >= 0)
 		{
 			return fetchatt(att[attnum],
 							(char *) tup + tup->t_hoff +
@@ -436,24 +597,27 @@ nocachegetattr(HeapTuple tuple,
 
 	tp = (char *) tup + tup->t_hoff;
 
-	/*
-	 * now check for any non-fixed length attrs before our attribute
-	 */
 	if (!slow)
 	{
-		if (att[attnum]->attcacheoff != -1)
+		/*
+		 * If we get here, there are no nulls up to and including the target
+		 * attribute.  If we have a cached offset, we can use it.
+		 */
+		if (att[attnum]->attcacheoff >= 0)
 		{
 			return fetchatt(att[attnum],
 							tp + att[attnum]->attcacheoff);
 		}
-		else if (HeapTupleHasVarWidth(tuple))
+
+		/*
+		 * Otherwise, check for non-fixed-length attrs up to and including
+		 * target.  If there aren't any, it's safe to cheaply initialize
+		 * the cached offsets for these attrs.
+		 */
+		if (HeapTupleHasVarWidth(tuple))
 		{
 			int			j;
 
-			/*
-			 * In for(), we test <= and not < because we want to see if we can
-			 * go past it in initializing offsets.
-			 */
 			for (j = 0; j <= attnum; j++)
 			{
 				if (att[j]->attlen <= 0)
@@ -465,89 +629,109 @@ nocachegetattr(HeapTuple tuple,
 		}
 	}
 
-	/*
-	 * If slow is false, and we got here, we know that we have a tuple with no
-	 * nulls or var-widths before the target attribute. If possible, we also
-	 * want to initialize the remainder of the attribute cached offset values.
-	 */
 	if (!slow)
 	{
+		int			natts = tupleDesc->natts;
 		int			j = 1;
-		long		off;
-		int			natts = HeapTupleHeaderGetNatts(tup);
 
 		/*
-		 * need to set cache for some atts
+		 * If we get here, we have a tuple with no nulls or var-widths up to
+		 * and including the target attribute, so we can use the cached offset
+		 * ... only we don't have it yet, or we'd not have got here.  Since
+		 * it's cheap to compute offsets for fixed-width columns, we take the
+		 * opportunity to initialize the cached offsets for *all* the leading
+		 * fixed-width columns, in hope of avoiding future visits to this
+		 * routine.
 		 */
-
 		att[0]->attcacheoff = 0;
 
-		while (j < attnum && att[j]->attcacheoff > 0)
+		/* we might have set some offsets in the slow path previously */
+		while (j < natts && att[j]->attcacheoff > 0)
 			j++;
 
 		off = att[j - 1]->attcacheoff + att[j - 1]->attlen;
 
-		for (; j <= attnum ||
-		/* Can we compute more?  We will probably need them */
-			 (j < natts &&
-			  att[j]->attcacheoff == -1 &&
-			  (HeapTupleNoNulls(tuple) || !att_isnull(j, bp)) &&
-			  (HeapTupleAllFixed(tuple) || att[j]->attlen > 0)); j++)
+		for (; j < natts; j++)
 		{
-			off = att_align(off, att[j]->attalign);
+			if (att[j]->attlen <= 0)
+				break;
+
+			off = att_align_nominal(off, att[j]->attalign);
 
 			att[j]->attcacheoff = off;
 
-			off = att_addlength(off, att[j]->attlen, tp + off);
+			off += att[j]->attlen;
 		}
 
-		return fetchatt(att[attnum], tp + att[attnum]->attcacheoff);
+		Assert(j > attnum);
+
+		off = att[attnum]->attcacheoff;
 	}
 	else
 	{
 		bool		usecache = true;
-		int			off = 0;
 		int			i;
 
 		/*
-		 * Now we know that we have to walk the tuple CAREFULLY.
+		 * Now we know that we have to walk the tuple CAREFULLY.  But we
+		 * still might be able to cache some offsets for next time.
 		 *
 		 * Note - This loop is a little tricky.  For each non-null attribute,
 		 * we have to first account for alignment padding before the attr,
 		 * then advance over the attr based on its length.	Nulls have no
 		 * storage and no alignment padding either.  We can use/set
-		 * attcacheoff until we pass either a null or a var-width attribute.
+		 * attcacheoff until we reach either a null or a var-width attribute.
 		 */
-
-		for (i = 0; i < attnum; i++)
+		off = 0;
+		for (i = 0; ; i++)			/* loop exit is at "break" */
 		{
 			if (HeapTupleHasNulls(tuple) && att_isnull(i, bp))
 			{
 				usecache = false;
-				continue;
+				continue;			/* this cannot be the target att */
 			}
 
-			/* If we know the next offset, we can skip the alignment calc */
-			if (usecache && att[i]->attcacheoff != -1)
+			/* If we know the next offset, we can skip the rest */
+			if (usecache && att[i]->attcacheoff >= 0)
 				off = att[i]->attcacheoff;
+			else if (att[i]->attlen == -1)
+			{
+				/*
+				 * We can only cache the offset for a varlena attribute
+				 * if the offset is already suitably aligned, so that there
+				 * would be no pad bytes in any case: then the offset will
+				 * be valid for either an aligned or unaligned value.
+				 */
+				if (usecache &&
+					off == att_align_nominal(off, att[i]->attalign))
+					att[i]->attcacheoff = off;
+				else
+				{
+					off = att_align_pointer(off, att[i]->attalign, -1,
+											tp + off);
+					usecache = false;
+				}
+			}
 			else
 			{
-				off = att_align(off, att[i]->attalign);
+				/* not varlena, so safe to use att_align_nominal */
+				off = att_align_nominal(off, att[i]->attalign);
 
 				if (usecache)
 					att[i]->attcacheoff = off;
 			}
 
-			off = att_addlength(off, att[i]->attlen, tp + off);
+			if (i == attnum)
+				break;
+
+			off = att_addlength_pointer(off, att[i]->attlen, tp + off);
 
 			if (usecache && att[i]->attlen <= 0)
 				usecache = false;
 		}
-
-		off = att_align(off, att[attnum]->attalign);
-
-		return fetchatt(att[attnum], tp + off);
 	}
+
+	return fetchatt(att[attnum], tp + off);
 }
 
 /* ----------------
@@ -671,7 +855,7 @@ heap_form_tuple(TupleDesc tupleDescriptor,
 {
 	HeapTuple	tuple;			/* return tuple */
 	HeapTupleHeader td;			/* tuple data */
-	unsigned long len;
+	Size		len, data_len;
 	int			hoff;
 	bool		hasnull = false;
 	Form_pg_attribute *att = tupleDescriptor->attrs;
@@ -723,7 +907,9 @@ heap_form_tuple(TupleDesc tupleDescriptor,
 
 	hoff = len = MAXALIGN(len); /* align user data safely */
 
-	len += heap_compute_data_size(tupleDescriptor, values, isnull);
+	data_len = heap_compute_data_size(tupleDescriptor, values, isnull);
+
+	len += data_len;
 
 	/*
 	 * Allocate and zero the space needed.	Note that the tuple body and
@@ -754,6 +940,7 @@ heap_form_tuple(TupleDesc tupleDescriptor,
 					values,
 					isnull,
 					(char *) td + hoff,
+					data_len,
 					&td->t_infomask,
 					(hasnull ? td->t_bits : NULL));
 
@@ -778,7 +965,7 @@ heap_formtuple(TupleDesc tupleDescriptor,
 {
 	HeapTuple	tuple;			/* return tuple */
 	HeapTupleHeader td;			/* tuple data */
-	unsigned long len;
+	Size		len, data_len;
 	int			hoff;
 	bool		hasnull = false;
 	Form_pg_attribute *att = tupleDescriptor->attrs;
@@ -830,7 +1017,9 @@ heap_formtuple(TupleDesc tupleDescriptor,
 
 	hoff = len = MAXALIGN(len); /* align user data safely */
 
-	len += ComputeDataSize(tupleDescriptor, values, nulls);
+	data_len = ComputeDataSize(tupleDescriptor, values, nulls);
+
+	len += data_len;
 
 	/*
 	 * Allocate and zero the space needed.	Note that the tuple body and
@@ -857,16 +1046,18 @@ heap_formtuple(TupleDesc tupleDescriptor,
 	if (tupleDescriptor->tdhasoid)		/* else leave infomask = 0 */
 		td->t_infomask = HEAP_HASOID;
 
-	DataFill((char *) td + hoff,
-			 tupleDescriptor,
+	DataFill(tupleDescriptor,
 			 values,
 			 nulls,
+			 (char *) td + hoff,
+			 data_len,
 			 &td->t_infomask,
 			 (hasnull ? td->t_bits : NULL));
 
 	return tuple;
 }
 
+
 /*
  * heap_modify_tuple
  *		form a new tuple from an old tuple and a set of replacement values.
@@ -1069,9 +1260,28 @@ heap_deform_tuple(HeapTuple tuple, TupleDesc tupleDesc,
 
 		if (!slow && thisatt->attcacheoff >= 0)
 			off = thisatt->attcacheoff;
+		else if (thisatt->attlen == -1)
+		{
+			/*
+			 * We can only cache the offset for a varlena attribute
+			 * if the offset is already suitably aligned, so that there
+			 * would be no pad bytes in any case: then the offset will
+			 * be valid for either an aligned or unaligned value.
+			 */
+			if (!slow &&
+				off == att_align_nominal(off, thisatt->attalign))
+				thisatt->attcacheoff = off;
+			else
+			{
+				off = att_align_pointer(off, thisatt->attalign, -1,
+										tp + off);
+				slow = true;
+			}
+		}
 		else
 		{
-			off = att_align(off, thisatt->attalign);
+			/* not varlena, so safe to use att_align_nominal */
+			off = att_align_nominal(off, thisatt->attalign);
 
 			if (!slow)
 				thisatt->attcacheoff = off;
@@ -1079,7 +1289,7 @@ heap_deform_tuple(HeapTuple tuple, TupleDesc tupleDesc,
 
 		values[attnum] = fetchatt(thisatt, tp + off);
 
-		off = att_addlength(off, thisatt->attlen, tp + off);
+		off = att_addlength_pointer(off, thisatt->attlen, tp + off);
 
 		if (thisatt->attlen <= 0)
 			slow = true;		/* can't use attcacheoff anymore */
@@ -1162,9 +1372,28 @@ heap_deformtuple(HeapTuple tuple,
 
 		if (!slow && thisatt->attcacheoff >= 0)
 			off = thisatt->attcacheoff;
+		else if (thisatt->attlen == -1)
+		{
+			/*
+			 * We can only cache the offset for a varlena attribute
+			 * if the offset is already suitably aligned, so that there
+			 * would be no pad bytes in any case: then the offset will
+			 * be valid for either an aligned or unaligned value.
+			 */
+			if (!slow &&
+				off == att_align_nominal(off, thisatt->attalign))
+				thisatt->attcacheoff = off;
+			else
+			{
+				off = att_align_pointer(off, thisatt->attalign, -1,
+										tp + off);
+				slow = true;
+			}
+		}
 		else
 		{
-			off = att_align(off, thisatt->attalign);
+			/* not varlena, so safe to use att_align_nominal */
+			off = att_align_nominal(off, thisatt->attalign);
 
 			if (!slow)
 				thisatt->attcacheoff = off;
@@ -1172,7 +1401,7 @@ heap_deformtuple(HeapTuple tuple,
 
 		values[attnum] = fetchatt(thisatt, tp + off);
 
-		off = att_addlength(off, thisatt->attlen, tp + off);
+		off = att_addlength_pointer(off, thisatt->attlen, tp + off);
 
 		if (thisatt->attlen <= 0)
 			slow = true;		/* can't use attcacheoff anymore */
@@ -1252,9 +1481,28 @@ slot_deform_tuple(TupleTableSlot *slot, int natts)
 
 		if (!slow && thisatt->attcacheoff >= 0)
 			off = thisatt->attcacheoff;
+		else if (thisatt->attlen == -1)
+		{
+			/*
+			 * We can only cache the offset for a varlena attribute
+			 * if the offset is already suitably aligned, so that there
+			 * would be no pad bytes in any case: then the offset will
+			 * be valid for either an aligned or unaligned value.
+			 */
+			if (!slow &&
+				off == att_align_nominal(off, thisatt->attalign))
+				thisatt->attcacheoff = off;
+			else
+			{
+				off = att_align_pointer(off, thisatt->attalign, -1,
+										tp + off);
+				slow = true;
+			}
+		}
 		else
 		{
-			off = att_align(off, thisatt->attalign);
+			/* not varlena, so safe to use att_align_nominal */
+			off = att_align_nominal(off, thisatt->attalign);
 
 			if (!slow)
 				thisatt->attcacheoff = off;
@@ -1262,7 +1510,7 @@ slot_deform_tuple(TupleTableSlot *slot, int natts)
 
 		values[attnum] = fetchatt(thisatt, tp + off);
 
-		off = att_addlength(off, thisatt->attlen, tp + off);
+		off = att_addlength_pointer(off, thisatt->attlen, tp + off);
 
 		if (thisatt->attlen <= 0)
 			slow = true;		/* can't use attcacheoff anymore */
@@ -1543,7 +1791,7 @@ heap_form_minimal_tuple(TupleDesc tupleDescriptor,
 						bool *isnull)
 {
 	MinimalTuple tuple;			/* return tuple */
-	unsigned long len;
+	Size		len, data_len;
 	int			hoff;
 	bool		hasnull = false;
 	Form_pg_attribute *att = tupleDescriptor->attrs;
@@ -1595,7 +1843,9 @@ heap_form_minimal_tuple(TupleDesc tupleDescriptor,
 
 	hoff = len = MAXALIGN(len); /* align user data safely */
 
-	len += heap_compute_data_size(tupleDescriptor, values, isnull);
+	data_len = heap_compute_data_size(tupleDescriptor, values, isnull);
+
+	len += data_len;
 
 	/*
 	 * Allocate and zero the space needed.
@@ -1616,6 +1866,7 @@ heap_form_minimal_tuple(TupleDesc tupleDescriptor,
 					values,
 					isnull,
 					(char *) tuple + hoff,
+					data_len,
 					&tuple->t_infomask,
 					(hasnull ? tuple->t_bits : NULL));
 
diff --git a/src/backend/access/common/indextuple.c b/src/backend/access/common/indextuple.c
index c83e34834ca80f113c62625f99524e95b1735f1f..471d28c28c420b400c684a587e730e6e49912219 100644
--- a/src/backend/access/common/indextuple.c
+++ b/src/backend/access/common/indextuple.c
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/access/common/indextuple.c,v 1.81 2007/02/27 23:48:06 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/access/common/indextuple.c,v 1.82 2007/04/06 04:21:41 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -38,6 +38,7 @@ index_form_tuple(TupleDesc tupleDescriptor,
 	char	   *tp;				/* tuple pointer */
 	IndexTuple	tuple;			/* return tuple */
 	Size		size,
+				data_size,
 				hoff;
 	int			i;
 	unsigned short infomask = 0;
@@ -74,9 +75,9 @@ index_form_tuple(TupleDesc tupleDescriptor,
 		 */
 		if (VARATT_IS_EXTERNAL(values[i]))
 		{
-			untoasted_values[i] = PointerGetDatum(
-												  heap_tuple_fetch_attr(
-								  (varattrib *) DatumGetPointer(values[i])));
+			untoasted_values[i] =
+				PointerGetDatum(heap_tuple_fetch_attr((struct varlena *)
+													  DatumGetPointer(values[i])));
 			untoasted_free[i] = true;
 		}
 
@@ -84,8 +85,8 @@ index_form_tuple(TupleDesc tupleDescriptor,
 		 * If value is above size target, and is of a compressible datatype,
 		 * try to compress it in-line.
 		 */
-		if (VARSIZE(untoasted_values[i]) > TOAST_INDEX_TARGET &&
-			!VARATT_IS_EXTENDED(untoasted_values[i]) &&
+		if (!VARATT_IS_EXTENDED(untoasted_values[i]) &&
+			VARSIZE(untoasted_values[i]) > TOAST_INDEX_TARGET &&
 			(att->attstorage == 'x' || att->attstorage == 'm'))
 		{
 			Datum		cvalue = toast_compress_datum(untoasted_values[i]);
@@ -116,12 +117,13 @@ index_form_tuple(TupleDesc tupleDescriptor,
 
 	hoff = IndexInfoFindDataOffset(infomask);
 #ifdef TOAST_INDEX_HACK
-	size = hoff + heap_compute_data_size(tupleDescriptor,
-										 untoasted_values, isnull);
+	data_size = heap_compute_data_size(tupleDescriptor,
+									   untoasted_values, isnull);
 #else
-	size = hoff + heap_compute_data_size(tupleDescriptor,
-										 values, isnull);
+	data_size = heap_compute_data_size(tupleDescriptor,
+									   values, isnull);
 #endif
+	size = hoff + data_size;
 	size = MAXALIGN(size);		/* be conservative */
 
 	tp = (char *) palloc0(size);
@@ -135,6 +137,7 @@ index_form_tuple(TupleDesc tupleDescriptor,
 #endif
 					isnull,
 					(char *) tp + hoff,
+					data_size,
 					&tupmask,
 					(hasnull ? (bits8 *) tp + sizeof(IndexTupleData) : NULL));
 
@@ -201,17 +204,14 @@ nocache_index_getattr(IndexTuple tup,
 					  bool *isnull)
 {
 	Form_pg_attribute *att = tupleDesc->attrs;
-	char	   *tp;				/* ptr to att in tuple */
-	bits8	   *bp = NULL;		/* ptr to null bitmask in tuple */
-	bool		slow = false;	/* do we have to walk nulls? */
+	char	   *tp;				/* ptr to data part of tuple */
+	bits8	   *bp = NULL;		/* ptr to null bitmap in tuple */
+	bool		slow = false;	/* do we have to walk attrs? */
 	int			data_off;		/* tuple data offset */
+	int			off;			/* current offset within data */
 
 	(void) isnull;				/* not used */
 
-	/*
-	 * sanity checks
-	 */
-
 	/* ----------------
 	 *	 Three cases:
 	 *
@@ -237,7 +237,7 @@ nocache_index_getattr(IndexTuple tup,
 	{
 #ifdef IN_MACRO
 /* This is handled in the macro */
-		if (att[attnum]->attcacheoff != -1)
+		if (att[attnum]->attcacheoff >= 0)
 		{
 			return fetchatt(att[attnum],
 							(char *) tup + data_off +
@@ -295,21 +295,28 @@ nocache_index_getattr(IndexTuple tup,
 
 	tp = (char *) tup + data_off;
 
-	/*
-	 * now check for any non-fixed length attrs before our attribute
-	 */
 	if (!slow)
 	{
-		if (att[attnum]->attcacheoff != -1)
+		/*
+		 * If we get here, there are no nulls up to and including the target
+		 * attribute.  If we have a cached offset, we can use it.
+		 */
+		if (att[attnum]->attcacheoff >= 0)
 		{
 			return fetchatt(att[attnum],
 							tp + att[attnum]->attcacheoff);
 		}
-		else if (IndexTupleHasVarwidths(tup))
+
+		/*
+		 * Otherwise, check for non-fixed-length attrs up to and including
+		 * target.  If there aren't any, it's safe to cheaply initialize
+		 * the cached offsets for these attrs.
+		 */
+		if (IndexTupleHasVarwidths(tup))
 		{
 			int			j;
 
-			for (j = 0; j < attnum; j++)
+			for (j = 0; j <= attnum; j++)
 			{
 				if (att[j]->attlen <= 0)
 				{
@@ -320,80 +327,109 @@ nocache_index_getattr(IndexTuple tup,
 		}
 	}
 
-	/*
-	 * If slow is false, and we got here, we know that we have a tuple with no
-	 * nulls or var-widths before the target attribute. If possible, we also
-	 * want to initialize the remainder of the attribute cached offset values.
-	 */
 	if (!slow)
 	{
+		int			natts = tupleDesc->natts;
 		int			j = 1;
-		long		off;
 
 		/*
-		 * need to set cache for some atts
+		 * If we get here, we have a tuple with no nulls or var-widths up to
+		 * and including the target attribute, so we can use the cached offset
+		 * ... only we don't have it yet, or we'd not have got here.  Since
+		 * it's cheap to compute offsets for fixed-width columns, we take the
+		 * opportunity to initialize the cached offsets for *all* the leading
+		 * fixed-width columns, in hope of avoiding future visits to this
+		 * routine.
 		 */
-
 		att[0]->attcacheoff = 0;
 
-		while (j < attnum && att[j]->attcacheoff > 0)
+		/* we might have set some offsets in the slow path previously */
+		while (j < natts && att[j]->attcacheoff > 0)
 			j++;
 
 		off = att[j - 1]->attcacheoff + att[j - 1]->attlen;
 
-		for (; j <= attnum; j++)
+		for (; j < natts; j++)
 		{
-			off = att_align(off, att[j]->attalign);
+			if (att[j]->attlen <= 0)
+				break;
+
+			off = att_align_nominal(off, att[j]->attalign);
 
 			att[j]->attcacheoff = off;
 
 			off += att[j]->attlen;
 		}
 
-		return fetchatt(att[attnum], tp + att[attnum]->attcacheoff);
+		Assert(j > attnum);
+
+		off = att[attnum]->attcacheoff;
 	}
 	else
 	{
 		bool		usecache = true;
-		int			off = 0;
 		int			i;
 
 		/*
-		 * Now we know that we have to walk the tuple CAREFULLY.
+		 * Now we know that we have to walk the tuple CAREFULLY.  But we
+		 * still might be able to cache some offsets for next time.
+		 *
+		 * Note - This loop is a little tricky.  For each non-null attribute,
+		 * we have to first account for alignment padding before the attr,
+		 * then advance over the attr based on its length.	Nulls have no
+		 * storage and no alignment padding either.  We can use/set
+		 * attcacheoff until we reach either a null or a var-width attribute.
 		 */
-
-		for (i = 0; i < attnum; i++)
+		off = 0;
+		for (i = 0; ; i++)			/* loop exit is at "break" */
 		{
-			if (IndexTupleHasNulls(tup))
+			if (IndexTupleHasNulls(tup) && att_isnull(i, bp))
 			{
-				if (att_isnull(i, bp))
-				{
-					usecache = false;
-					continue;
-				}
+				usecache = false;
+				continue;			/* this cannot be the target att */
 			}
 
 			/* If we know the next offset, we can skip the rest */
-			if (usecache && att[i]->attcacheoff != -1)
+			if (usecache && att[i]->attcacheoff >= 0)
 				off = att[i]->attcacheoff;
+			else if (att[i]->attlen == -1)
+			{
+				/*
+				 * We can only cache the offset for a varlena attribute
+				 * if the offset is already suitably aligned, so that there
+				 * would be no pad bytes in any case: then the offset will
+				 * be valid for either an aligned or unaligned value.
+				 */
+				if (usecache &&
+					off == att_align_nominal(off, att[i]->attalign))
+					att[i]->attcacheoff = off;
+				else
+				{
+					off = att_align_pointer(off, att[i]->attalign, -1,
+											tp + off);
+					usecache = false;
+				}
+			}
 			else
 			{
-				off = att_align(off, att[i]->attalign);
+				/* not varlena, so safe to use att_align_nominal */
+				off = att_align_nominal(off, att[i]->attalign);
 
 				if (usecache)
 					att[i]->attcacheoff = off;
 			}
 
-			off = att_addlength(off, att[i]->attlen, tp + off);
+			if (i == attnum)
+				break;
+
+			off = att_addlength_pointer(off, att[i]->attlen, tp + off);
 
 			if (usecache && att[i]->attlen <= 0)
 				usecache = false;
 		}
-
-		off = att_align(off, att[attnum]->attalign);
-
-		return fetchatt(att[attnum], tp + off);
 	}
+
+	return fetchatt(att[attnum], tp + off);
 }
 
 /*
diff --git a/src/backend/access/heap/tuptoaster.c b/src/backend/access/heap/tuptoaster.c
index 334d670042382e1810ed872ab48104217382d4cc..1a3c01bcac9c77848403dca0767f76626346f844 100644
--- a/src/backend/access/heap/tuptoaster.c
+++ b/src/backend/access/heap/tuptoaster.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/access/heap/tuptoaster.c,v 1.73 2007/04/03 04:14:26 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/access/heap/tuptoaster.c,v 1.74 2007/04/06 04:21:41 tgl Exp $
  *
  *
  * INTERFACE ROUTINES
@@ -42,25 +42,39 @@
 
 #undef TOAST_DEBUG
 
+/*
+ * Testing whether an externally-stored value is compressed now requires
+ * comparing extsize (the actual length of the external data) to rawsize
+ * (the original uncompressed datum's size).  The latter includes VARHDRSZ
+ * overhead, the former doesn't.  We never use compression unless it actually
+ * saves space, so we expect either equality or less-than.
+ */
+#define VARATT_EXTERNAL_IS_COMPRESSED(toast_pointer) \
+	((toast_pointer).va_extsize < (toast_pointer).va_rawsize - VARHDRSZ)
+
 static void toast_delete_datum(Relation rel, Datum value);
 static Datum toast_save_datum(Relation rel, Datum value,
 							  bool use_wal, bool use_fsm);
-static varattrib *toast_fetch_datum(varattrib *attr);
-static varattrib *toast_fetch_datum_slice(varattrib *attr,
+static struct varlena *toast_fetch_datum(struct varlena *attr);
+static struct varlena *toast_fetch_datum_slice(struct varlena *attr,
 						int32 sliceoffset, int32 length);
 
 
 /* ----------
  * heap_tuple_fetch_attr -
  *
- *	Public entry point to get back a toasted value
+ *	Public entry point to get back a toasted value from
  *	external storage (possibly still in compressed format).
- * ----------
+ *
+ * This will return a datum that contains all the data internally, ie, not
+ * relying on external storage, but it can still be compressed or have a short
+ * header.
+ ----------
  */
-varattrib *
-heap_tuple_fetch_attr(varattrib *attr)
+struct varlena *
+heap_tuple_fetch_attr(struct varlena *attr)
 {
-	varattrib  *result;
+	struct varlena  *result;
 
 	if (VARATT_IS_EXTERNAL(attr))
 	{
@@ -88,35 +102,25 @@ heap_tuple_fetch_attr(varattrib *attr)
  *	or external storage.
  * ----------
  */
-varattrib *
-heap_tuple_untoast_attr(varattrib *attr)
+struct varlena *
+heap_tuple_untoast_attr(struct varlena *attr)
 {
-	varattrib  *result;
-
 	if (VARATT_IS_EXTERNAL(attr))
 	{
+		/*
+		 * This is an externally stored datum --- fetch it back from there
+		 */
+		attr = toast_fetch_datum(attr);
+		/* If it's compressed, decompress it */
 		if (VARATT_IS_COMPRESSED(attr))
 		{
-			/* ----------
-			 * This is an external stored compressed value
-			 * Fetch it from the toast heap and decompress.
-			 * ----------
-			 */
-			PGLZ_Header *tmp;
+			PGLZ_Header *tmp = (PGLZ_Header *) attr;
 
-			tmp = (PGLZ_Header *) toast_fetch_datum(attr);
-			result = (varattrib *) palloc(PGLZ_RAW_SIZE(tmp) + VARHDRSZ);
-			SET_VARSIZE(result, PGLZ_RAW_SIZE(tmp) + VARHDRSZ);
-			pglz_decompress(tmp, VARDATA(result));
+			attr = (struct varlena *) palloc(PGLZ_RAW_SIZE(tmp) + VARHDRSZ);
+			SET_VARSIZE(attr, PGLZ_RAW_SIZE(tmp) + VARHDRSZ);
+			pglz_decompress(tmp, VARDATA(attr));
 			pfree(tmp);
 		}
-		else
-		{
-			/*
-			 * This is an external stored plain value
-			 */
-			result = toast_fetch_datum(attr);
-		}
 	}
 	else if (VARATT_IS_COMPRESSED(attr))
 	{
@@ -125,18 +129,26 @@ heap_tuple_untoast_attr(varattrib *attr)
 		 */
 		PGLZ_Header *tmp = (PGLZ_Header *) attr;
 
-		result = (varattrib *) palloc(PGLZ_RAW_SIZE(tmp) + VARHDRSZ);
-		SET_VARSIZE(result, PGLZ_RAW_SIZE(tmp) + VARHDRSZ);
-		pglz_decompress(tmp, VARDATA(result));
+		attr = (struct varlena *) palloc(PGLZ_RAW_SIZE(tmp) + VARHDRSZ);
+		SET_VARSIZE(attr, PGLZ_RAW_SIZE(tmp) + VARHDRSZ);
+		pglz_decompress(tmp, VARDATA(attr));
 	}
-	else
-
+	else if (VARATT_IS_SHORT(attr))
+	{
 		/*
-		 * This is a plain value inside of the main tuple - why am I called?
+		 * This is a short-header varlena --- convert to 4-byte header format
 		 */
-		return attr;
+		Size	data_size = VARSIZE_SHORT(attr) - VARHDRSZ_SHORT;
+		Size	new_size = data_size + VARHDRSZ;
+		struct varlena *new_attr;
+
+		new_attr = (struct varlena *) palloc(new_size);
+		SET_VARSIZE(new_attr, new_size);
+		memcpy(VARDATA(new_attr), VARDATA_SHORT(attr), data_size);
+		attr = new_attr;
+	}
 
-	return result;
+	return attr;
 }
 
 
@@ -147,44 +159,57 @@ heap_tuple_untoast_attr(varattrib *attr)
  *		from compression or external storage.
  * ----------
  */
-varattrib *
-heap_tuple_untoast_attr_slice(varattrib *attr, int32 sliceoffset, int32 slicelength)
+struct varlena *
+heap_tuple_untoast_attr_slice(struct varlena *attr,
+							  int32 sliceoffset, int32 slicelength)
 {
-	varattrib  *preslice;
-	varattrib  *result;
+	struct varlena *preslice;
+	struct varlena *result;
+	char       *attrdata;
 	int32		attrsize;
 
-	if (VARATT_IS_COMPRESSED(attr))
+	if (VARATT_IS_EXTERNAL(attr))
 	{
-		PGLZ_Header *tmp;
+		struct varatt_external toast_pointer;
 
-		if (VARATT_IS_EXTERNAL(attr))
-			tmp = (PGLZ_Header *) toast_fetch_datum(attr);
-		else
-			tmp = (PGLZ_Header *) attr;		/* compressed in main tuple */
+		memcpy(&toast_pointer, VARDATA_SHORT(attr), sizeof(toast_pointer));
+
+		/* fast path for non-compressed external datums */
+		if (!VARATT_EXTERNAL_IS_COMPRESSED(toast_pointer))
+			return toast_fetch_datum_slice(attr, sliceoffset, slicelength);
 
-		preslice = (varattrib *) palloc(PGLZ_RAW_SIZE(tmp) + VARHDRSZ);
-		SET_VARSIZE(preslice, PGLZ_RAW_SIZE(tmp) + VARHDRSZ);
+		/* fetch it back (compressed marker will get set automatically) */
+		preslice = toast_fetch_datum(attr);
+	}
+	else
+		preslice = attr;
+
+	if (VARATT_IS_COMPRESSED(preslice))
+	{
+		PGLZ_Header *tmp = (PGLZ_Header *) preslice;
+		Size size = PGLZ_RAW_SIZE(tmp) + VARHDRSZ;
+
+		preslice = (struct varlena *) palloc(size);
+		SET_VARSIZE(preslice, size);
 		pglz_decompress(tmp, VARDATA(preslice));
 
 		if (tmp != (PGLZ_Header *) attr)
 			pfree(tmp);
 	}
+
+	if (VARATT_IS_SHORT(preslice))
+	{
+		attrdata = VARDATA_SHORT(preslice);
+		attrsize = VARSIZE_SHORT(preslice) - VARHDRSZ_SHORT;
+	}
 	else
 	{
-		/* Plain value */
-		if (VARATT_IS_EXTERNAL(attr))
-		{
-			/* fast path */
-			return toast_fetch_datum_slice(attr, sliceoffset, slicelength);
-		}
-		else
-			preslice = attr;
+		attrdata = VARDATA(preslice);
+		attrsize = VARSIZE(preslice) - VARHDRSZ;
 	}
 
 	/* slicing of datum for compressed cases and plain value */
 
-	attrsize = VARSIZE(preslice) - VARHDRSZ;
 	if (sliceoffset >= attrsize)
 	{
 		sliceoffset = 0;
@@ -194,10 +219,10 @@ heap_tuple_untoast_attr_slice(varattrib *attr, int32 sliceoffset, int32 slicelen
 	if (((sliceoffset + slicelength) > attrsize) || slicelength < 0)
 		slicelength = attrsize - sliceoffset;
 
-	result = (varattrib *) palloc(slicelength + VARHDRSZ);
+	result = (struct varlena *) palloc(slicelength + VARHDRSZ);
 	SET_VARSIZE(result, slicelength + VARHDRSZ);
 
-	memcpy(VARDATA(result), VARDATA(preslice) + sliceoffset, slicelength);
+	memcpy(VARDATA(result), attrdata + sliceoffset, slicelength);
 
 	if (preslice != attr)
 		pfree(preslice);
@@ -210,29 +235,35 @@ heap_tuple_untoast_attr_slice(varattrib *attr, int32 sliceoffset, int32 slicelen
  * toast_raw_datum_size -
  *
  *	Return the raw (detoasted) size of a varlena datum
+ *	(including the VARHDRSZ header)
  * ----------
  */
 Size
 toast_raw_datum_size(Datum value)
 {
-	varattrib  *attr = (varattrib *) DatumGetPointer(value);
+	struct varlena *attr = (struct varlena *) DatumGetPointer(value);
 	Size		result;
 
-	if (VARATT_IS_COMPRESSED(attr))
+	if (VARATT_IS_EXTERNAL(attr))
 	{
-		/*
-		 * va_rawsize shows the original data size, whether the datum is
-		 * external or not.
-		 */
-		result = attr->va_content.va_compressed.va_rawsize + VARHDRSZ;
+		/* va_rawsize is the size of the original datum -- including header */
+		struct varatt_external toast_pointer;
+
+		memcpy(&toast_pointer, VARDATA_SHORT(attr), sizeof(toast_pointer));
+		result = toast_pointer.va_rawsize;
 	}
-	else if (VARATT_IS_EXTERNAL(attr))
+	else if (VARATT_IS_COMPRESSED(attr))
+	{
+		/* here, va_rawsize is just the payload size */
+		result = VARRAWSIZE_4B_C(attr) + VARHDRSZ;
+	}
+	else if (VARATT_IS_SHORT(attr))
 	{
 		/*
-		 * an uncompressed external attribute has rawsize including the header
-		 * (not too consistent!)
+		 * we have to normalize the header length to VARHDRSZ or else the
+		 * callers of this function will be confused.
 		 */
-		result = attr->va_content.va_external.va_rawsize;
+		result = VARSIZE_SHORT(attr) - VARHDRSZ_SHORT + VARHDRSZ;
 	}
 	else
 	{
@@ -251,7 +282,7 @@ toast_raw_datum_size(Datum value)
 Size
 toast_datum_size(Datum value)
 {
-	varattrib  *attr = (varattrib *) DatumGetPointer(value);
+	struct varlena  *attr = (struct varlena *) DatumGetPointer(value);
 	Size		result;
 
 	if (VARATT_IS_EXTERNAL(attr))
@@ -261,7 +292,14 @@ toast_datum_size(Datum value)
 		 * compressed or not.  We do not count the size of the toast pointer
 		 * ... should we?
 		 */
-		result = attr->va_content.va_external.va_extsize;
+		struct varatt_external toast_pointer;
+
+		memcpy(&toast_pointer, VARDATA_SHORT(attr), sizeof(toast_pointer));
+		result = toast_pointer.va_extsize;
+	}
+	else if (VARATT_IS_SHORT(attr))
+	{
+		result = VARSIZE_SHORT(attr);
 	}
 	else
 	{
@@ -413,16 +451,16 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup,
 
 	for (i = 0; i < numAttrs; i++)
 	{
-		varattrib  *old_value;
-		varattrib  *new_value;
+		struct varlena  *old_value;
+		struct varlena  *new_value;
 
 		if (oldtup != NULL)
 		{
 			/*
 			 * For UPDATE get the old and new values of this attribute
 			 */
-			old_value = (varattrib *) DatumGetPointer(toast_oldvalues[i]);
-			new_value = (varattrib *) DatumGetPointer(toast_values[i]);
+			old_value = (struct varlena *) DatumGetPointer(toast_oldvalues[i]);
+			new_value = (struct varlena *) DatumGetPointer(toast_values[i]);
 
 			/*
 			 * If the old value is an external stored one, check if it has
@@ -432,10 +470,9 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup,
 				VARATT_IS_EXTERNAL(old_value))
 			{
 				if (toast_isnull[i] || !VARATT_IS_EXTERNAL(new_value) ||
-					old_value->va_content.va_external.va_valueid !=
-					new_value->va_content.va_external.va_valueid ||
-					old_value->va_content.va_external.va_toastrelid !=
-					new_value->va_content.va_external.va_toastrelid)
+					memcmp(VARDATA_SHORT(old_value),
+						   VARDATA_SHORT(new_value),
+						   sizeof(struct varatt_external)) != 0)
 				{
 					/*
 					 * The old external stored value isn't needed any more
@@ -452,7 +489,6 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup,
 					 * tuple.
 					 */
 					toast_action[i] = 'p';
-					toast_sizes[i] = VARSIZE(toast_values[i]);
 					continue;
 				}
 			}
@@ -462,7 +498,7 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup,
 			/*
 			 * For INSERT simply get the new value
 			 */
-			new_value = (varattrib *) DatumGetPointer(toast_values[i]);
+			new_value = (struct varlena *) DatumGetPointer(toast_values[i]);
 		}
 
 		/*
@@ -503,7 +539,7 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup,
 			/*
 			 * Remember the size of this attribute
 			 */
-			toast_sizes[i] = VARSIZE(new_value);
+			toast_sizes[i] = VARSIZE_ANY(new_value);
 		}
 		else
 		{
@@ -542,7 +578,7 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup,
 								  toast_values, toast_isnull) > maxDataLen)
 	{
 		int			biggest_attno = -1;
-		int32		biggest_size = MAXALIGN(sizeof(varattrib));
+		int32		biggest_size = MAXALIGN(sizeof(varattrib_pointer));
 		Datum		old_value;
 		Datum		new_value;
 
@@ -553,7 +589,9 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup,
 		{
 			if (toast_action[i] != ' ')
 				continue;
-			if (VARATT_IS_EXTENDED(toast_values[i]))
+			if (VARATT_IS_EXTERNAL(toast_values[i]))
+				continue;
+			if (VARATT_IS_COMPRESSED(toast_values[i]))
 				continue;
 			if (att[i]->attstorage != 'x')
 				continue;
@@ -603,7 +641,7 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup,
 		   rel->rd_rel->reltoastrelid != InvalidOid)
 	{
 		int			biggest_attno = -1;
-		int32		biggest_size = MAXALIGN(sizeof(varattrib));
+		int32		biggest_size = MAXALIGN(sizeof(varattrib_pointer));
 		Datum		old_value;
 
 		/*------
@@ -639,9 +677,7 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup,
 										   use_wal, use_fsm);
 		if (toast_free[i])
 			pfree(DatumGetPointer(old_value));
-
 		toast_free[i] = true;
-		toast_sizes[i] = VARSIZE(toast_values[i]);
 
 		need_change = true;
 		need_free = true;
@@ -655,7 +691,7 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup,
 								  toast_values, toast_isnull) > maxDataLen)
 	{
 		int			biggest_attno = -1;
-		int32		biggest_size = MAXALIGN(sizeof(varattrib));
+		int32		biggest_size = MAXALIGN(sizeof(varattrib_pointer));
 		Datum		old_value;
 		Datum		new_value;
 
@@ -666,7 +702,9 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup,
 		{
 			if (toast_action[i] != ' ')
 				continue;
-			if (VARATT_IS_EXTENDED(toast_values[i]))
+			if (VARATT_IS_EXTERNAL(toast_values[i]))
+				continue;
+			if (VARATT_IS_COMPRESSED(toast_values[i]))
 				continue;
 			if (att[i]->attstorage != 'm')
 				continue;
@@ -715,7 +753,7 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup,
 		   rel->rd_rel->reltoastrelid != InvalidOid)
 	{
 		int			biggest_attno = -1;
-		int32		biggest_size = MAXALIGN(sizeof(varattrib));
+		int32		biggest_size = MAXALIGN(sizeof(varattrib_pointer));
 		Datum		old_value;
 
 		/*--------
@@ -768,6 +806,7 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup,
 		HeapTupleHeader olddata = newtup->t_data;
 		HeapTupleHeader new_data;
 		int32		new_len;
+		int32		new_data_len;
 
 		/*
 		 * Calculate the new size of the tuple.  Header size should not
@@ -780,8 +819,9 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup,
 			new_len += sizeof(Oid);
 		new_len = MAXALIGN(new_len);
 		Assert(new_len == olddata->t_hoff);
-		new_len += heap_compute_data_size(tupleDesc,
-										  toast_values, toast_isnull);
+		new_data_len = heap_compute_data_size(tupleDesc,
+											  toast_values, toast_isnull);
+		new_len += new_data_len;
 
 		/*
 		 * Allocate and zero the space needed, and fill HeapTupleData fields.
@@ -802,6 +842,7 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup,
 						toast_values,
 						toast_isnull,
 						(char *) new_data + olddata->t_hoff,
+						new_data_len,
 						&(new_data->t_infomask),
 						has_nulls ? new_data->t_bits : NULL);
 	}
@@ -835,6 +876,9 @@ toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup,
  *	This must be invoked on any potentially-composite field that is to be
  *	inserted into a tuple.	Doing this preserves the invariant that toasting
  *	goes only one level deep in a tuple.
+ *
+ *	Note that flattening does not mean expansion of short-header varlenas,
+ *	so in one sense toasting is allowed within composite datums.
  * ----------
  */
 Datum
@@ -845,6 +889,7 @@ toast_flatten_tuple_attribute(Datum value,
 	HeapTupleHeader olddata;
 	HeapTupleHeader new_data;
 	int32		new_len;
+	int32		new_data_len;
 	HeapTupleData tmptup;
 	Form_pg_attribute *att;
 	int			numAttrs;
@@ -891,10 +936,11 @@ toast_flatten_tuple_attribute(Datum value,
 			has_nulls = true;
 		else if (att[i]->attlen == -1)
 		{
-			varattrib  *new_value;
+			struct varlena  *new_value;
 
-			new_value = (varattrib *) DatumGetPointer(toast_values[i]);
-			if (VARATT_IS_EXTENDED(new_value))
+			new_value = (struct varlena *) DatumGetPointer(toast_values[i]);
+			if (VARATT_IS_EXTERNAL(new_value) ||
+				VARATT_IS_COMPRESSED(new_value))
 			{
 				new_value = heap_tuple_untoast_attr(new_value);
 				toast_values[i] = PointerGetDatum(new_value);
@@ -924,7 +970,9 @@ toast_flatten_tuple_attribute(Datum value,
 		new_len += sizeof(Oid);
 	new_len = MAXALIGN(new_len);
 	Assert(new_len == olddata->t_hoff);
-	new_len += heap_compute_data_size(tupleDesc, toast_values, toast_isnull);
+	new_data_len = heap_compute_data_size(tupleDesc,
+										  toast_values, toast_isnull);
+	new_len += new_data_len;
 
 	new_data = (HeapTupleHeader) palloc0(new_len);
 
@@ -939,6 +987,7 @@ toast_flatten_tuple_attribute(Datum value,
 					toast_values,
 					toast_isnull,
 					(char *) new_data + olddata->t_hoff,
+					new_data_len,
 					&(new_data->t_infomask),
 					has_nulls ? new_data->t_bits : NULL);
 
@@ -962,21 +1011,26 @@ toast_flatten_tuple_attribute(Datum value,
  *	If we fail (ie, compressed result is actually bigger than original)
  *	then return NULL.  We must not use compressed data if it'd expand
  *	the tuple!
+ *
+ *	We use VAR{SIZE,DATA}_ANY so we can handle short varlenas here without
+ *	copying them.  But we can't handle external or compressed datums.
  * ----------
  */
 Datum
 toast_compress_datum(Datum value)
 {
-	varattrib  *tmp;
-	int32		valsize = VARSIZE(value) - VARHDRSZ;
+	struct varlena *tmp;
+	int32		valsize = VARSIZE_ANY_EXHDR(value);
 
-	tmp = (varattrib *) palloc(PGLZ_MAX_OUTPUT(valsize));
-	if (pglz_compress(VARDATA(value), valsize,
+	Assert(!VARATT_IS_EXTERNAL(value));
+	Assert(!VARATT_IS_COMPRESSED(value));
+
+	tmp = (struct varlena *) palloc(PGLZ_MAX_OUTPUT(valsize));
+	if (pglz_compress(VARDATA_ANY(value), valsize,
 					  (PGLZ_Header *) tmp, PGLZ_strategy_default) &&
-		VARSIZE(tmp) < VARSIZE(value))
+		VARSIZE(tmp) < VARSIZE_ANY(value))
 	{
 		/* successful compression */
-		VARATT_SIZEP_DEPRECATED(tmp) |= VARATT_FLAG_COMPRESSED;
 		return PointerGetDatum(tmp);
 	}
 	else
@@ -992,7 +1046,7 @@ toast_compress_datum(Datum value)
  * toast_save_datum -
  *
  *	Save one single datum into the secondary relation and return
- *	a varattrib reference for it.
+ *	a Datum reference for it.
  * ----------
  */
 static Datum
@@ -1006,7 +1060,8 @@ toast_save_datum(Relation rel, Datum value,
 	Datum		t_values[3];
 	bool		t_isnull[3];
 	CommandId	mycid = GetCurrentCommandId();
-	varattrib  *result;
+	struct varlena *result;
+	struct varatt_external toast_pointer;
 	struct
 	{
 		struct varlena hdr;
@@ -1027,43 +1082,50 @@ toast_save_datum(Relation rel, Datum value,
 	toastidx = index_open(toastrel->rd_rel->reltoastidxid, RowExclusiveLock);
 
 	/*
-	 * Create the varattrib reference
+	 * Get the data pointer and length, and compute va_rawsize and va_extsize.
+	 *
+	 * va_rawsize is the size of the equivalent fully uncompressed datum,
+	 * so we have to adjust for short headers.
+	 *
+	 * va_extsize is the actual size of the data payload in the toast records.
 	 */
-	result = (varattrib *) palloc(sizeof(varattrib));
-
-	SET_VARSIZE(result, sizeof(varattrib));
-	VARATT_SIZEP_DEPRECATED(result) |= VARATT_FLAG_EXTERNAL;
-	if (VARATT_IS_COMPRESSED(value))
+	if (VARATT_IS_SHORT(value))
+	{
+		data_p = VARDATA_SHORT(value);
+		data_todo = VARSIZE_SHORT(value) - VARHDRSZ_SHORT;
+		toast_pointer.va_rawsize = data_todo + VARHDRSZ; /* as if not short */
+		toast_pointer.va_extsize = data_todo;
+	}
+	else if (VARATT_IS_COMPRESSED(value))
 	{
-		VARATT_SIZEP_DEPRECATED(result) |= VARATT_FLAG_COMPRESSED;
-		result->va_content.va_external.va_rawsize =
-			((varattrib *) value)->va_content.va_compressed.va_rawsize;
+		data_p = VARDATA(value);
+		data_todo = VARSIZE(value) - VARHDRSZ;
+		/* rawsize in a compressed datum is just the size of the payload */
+		toast_pointer.va_rawsize = VARRAWSIZE_4B_C(value) + VARHDRSZ;
+		toast_pointer.va_extsize = data_todo;
+		/* Assert that the numbers look like it's compressed */
+		Assert(VARATT_EXTERNAL_IS_COMPRESSED(toast_pointer));
 	}
 	else
-		result->va_content.va_external.va_rawsize = VARSIZE(value);
+	{
+		data_p = VARDATA(value);
+		data_todo = VARSIZE(value) - VARHDRSZ;
+		toast_pointer.va_rawsize = VARSIZE(value);
+		toast_pointer.va_extsize = data_todo;
+	}
 
-	result->va_content.va_external.va_extsize =
-		VARSIZE(value) - VARHDRSZ;
-	result->va_content.va_external.va_valueid =
-		GetNewOidWithIndex(toastrel, toastidx);
-	result->va_content.va_external.va_toastrelid =
-		rel->rd_rel->reltoastrelid;
+	toast_pointer.va_valueid = GetNewOidWithIndex(toastrel, toastidx);
+	toast_pointer.va_toastrelid = rel->rd_rel->reltoastrelid;
 
 	/*
 	 * Initialize constant parts of the tuple data
 	 */
-	t_values[0] = ObjectIdGetDatum(result->va_content.va_external.va_valueid);
+	t_values[0] = ObjectIdGetDatum(toast_pointer.va_valueid);
 	t_values[2] = PointerGetDatum(&chunk_data);
 	t_isnull[0] = false;
 	t_isnull[1] = false;
 	t_isnull[2] = false;
 
-	/*
-	 * Get the data to process
-	 */
-	data_p = VARDATA(value);
-	data_todo = VARSIZE(value) - VARHDRSZ;
-
 	/*
 	 * Split up the item into chunks
 	 */
@@ -1111,11 +1173,18 @@ toast_save_datum(Relation rel, Datum value,
 	}
 
 	/*
-	 * Done - close toast relation and return the reference
+	 * Done - close toast relation
 	 */
 	index_close(toastidx, RowExclusiveLock);
 	heap_close(toastrel, RowExclusiveLock);
 
+	/*
+	 * Create the TOAST pointer value that we'll return
+	 */
+	result = (struct varlena *) palloc(sizeof(varattrib_pointer));
+	SET_VARSIZE_EXTERNAL(result);
+	memcpy(VARDATA_SHORT(result), &toast_pointer, sizeof(toast_pointer));
+
 	return PointerGetDatum(result);
 }
 
@@ -1129,7 +1198,8 @@ toast_save_datum(Relation rel, Datum value,
 static void
 toast_delete_datum(Relation rel, Datum value)
 {
-	varattrib  *attr = (varattrib *) DatumGetPointer(value);
+	struct varlena *attr = (struct varlena *) DatumGetPointer(value);
+	struct varatt_external toast_pointer;
 	Relation	toastrel;
 	Relation	toastidx;
 	ScanKeyData toastkey;
@@ -1139,11 +1209,14 @@ toast_delete_datum(Relation rel, Datum value)
 	if (!VARATT_IS_EXTERNAL(attr))
 		return;
 
+	/* Must copy to access aligned fields */
+	memcpy(&toast_pointer, VARDATA_SHORT(attr),
+		   sizeof(struct varatt_external));
+
 	/*
 	 * Open the toast relation and its index
 	 */
-	toastrel = heap_open(attr->va_content.va_external.va_toastrelid,
-						 RowExclusiveLock);
+	toastrel = heap_open(toast_pointer.va_toastrelid, RowExclusiveLock);
 	toastidx = index_open(toastrel->rd_rel->reltoastidxid, RowExclusiveLock);
 
 	/*
@@ -1153,7 +1226,7 @@ toast_delete_datum(Relation rel, Datum value)
 	ScanKeyInit(&toastkey,
 				(AttrNumber) 1,
 				BTEqualStrategyNumber, F_OIDEQ,
-				ObjectIdGetDatum(attr->va_content.va_external.va_valueid));
+				ObjectIdGetDatum(toast_pointer.va_valueid));
 
 	/*
 	 * Find the chunks by index
@@ -1180,12 +1253,12 @@ toast_delete_datum(Relation rel, Datum value)
 /* ----------
  * toast_fetch_datum -
  *
- *	Reconstruct an in memory varattrib from the chunks saved
+ *	Reconstruct an in memory Datum from the chunks saved
  *	in the toast relation
  * ----------
  */
-static varattrib *
-toast_fetch_datum(varattrib *attr)
+static struct varlena *
+toast_fetch_datum(struct varlena *attr)
 {
 	Relation	toastrel;
 	Relation	toastidx;
@@ -1193,28 +1266,35 @@ toast_fetch_datum(varattrib *attr)
 	IndexScanDesc toastscan;
 	HeapTuple	ttup;
 	TupleDesc	toasttupDesc;
-	varattrib  *result;
+	struct varlena *result;
+	struct varatt_external toast_pointer;
 	int32		ressize;
 	int32		residx,
 				nextidx;
 	int32		numchunks;
 	Pointer		chunk;
 	bool		isnull;
+	char 	   *chunkdata;
 	int32		chunksize;
 
-	ressize = attr->va_content.va_external.va_extsize;
+	/* Must copy to access aligned fields */
+	memcpy(&toast_pointer, VARDATA_SHORT(attr),
+		   sizeof(struct varatt_external));
+
+	ressize = toast_pointer.va_extsize;
 	numchunks = ((ressize - 1) / TOAST_MAX_CHUNK_SIZE) + 1;
 
-	result = (varattrib *) palloc(ressize + VARHDRSZ);
-	SET_VARSIZE(result, ressize + VARHDRSZ);
-	if (VARATT_IS_COMPRESSED(attr))
-		VARATT_SIZEP_DEPRECATED(result) |= VARATT_FLAG_COMPRESSED;
+	result = (struct varlena *) palloc(ressize + VARHDRSZ);
+
+	if (VARATT_EXTERNAL_IS_COMPRESSED(toast_pointer))
+		SET_VARSIZE_COMPRESSED(result, ressize + VARHDRSZ);
+	else
+		SET_VARSIZE(result, ressize + VARHDRSZ);
 
 	/*
 	 * Open the toast relation and its index
 	 */
-	toastrel = heap_open(attr->va_content.va_external.va_toastrelid,
-						 AccessShareLock);
+	toastrel = heap_open(toast_pointer.va_toastrelid, AccessShareLock);
 	toasttupDesc = toastrel->rd_att;
 	toastidx = index_open(toastrel->rd_rel->reltoastidxid, AccessShareLock);
 
@@ -1224,7 +1304,7 @@ toast_fetch_datum(varattrib *attr)
 	ScanKeyInit(&toastkey,
 				(AttrNumber) 1,
 				BTEqualStrategyNumber, F_OIDEQ,
-				ObjectIdGetDatum(attr->va_content.va_external.va_valueid));
+				ObjectIdGetDatum(toast_pointer.va_valueid));
 
 	/*
 	 * Read the chunks by index
@@ -1246,7 +1326,24 @@ toast_fetch_datum(varattrib *attr)
 		Assert(!isnull);
 		chunk = DatumGetPointer(fastgetattr(ttup, 3, toasttupDesc, &isnull));
 		Assert(!isnull);
-		chunksize = VARSIZE(chunk) - VARHDRSZ;
+		if (!VARATT_IS_EXTENDED(chunk))
+		{
+			chunksize = VARSIZE(chunk) - VARHDRSZ;
+			chunkdata = VARDATA(chunk);
+		}
+		else if (VARATT_IS_SHORT(chunk))
+		{
+			/* could happen due to heap_form_tuple doing its thing */
+			chunksize = VARSIZE_SHORT(chunk) - VARHDRSZ_SHORT;
+			chunkdata = VARDATA_SHORT(chunk);
+		}
+		else
+		{
+			/* should never happen */
+			elog(ERROR, "found toasted toast chunk");
+			chunksize = 0;				/* keep compiler quiet */
+			chunkdata = NULL;
+		}
 
 		/*
 		 * Some checks on the data we've found
@@ -1254,31 +1351,35 @@ toast_fetch_datum(varattrib *attr)
 		if (residx != nextidx)
 			elog(ERROR, "unexpected chunk number %d (expected %d) for toast value %u",
 				 residx, nextidx,
-				 attr->va_content.va_external.va_valueid);
+				 toast_pointer.va_valueid);
 		if (residx < numchunks - 1)
 		{
 			if (chunksize != TOAST_MAX_CHUNK_SIZE)
-				elog(ERROR, "unexpected chunk size %d in chunk %d for toast value %u",
-					 chunksize, residx,
-					 attr->va_content.va_external.va_valueid);
+				elog(ERROR, "unexpected chunk size %d (expected %d) in chunk %d of %d for toast value %u",
+					 chunksize, (int) TOAST_MAX_CHUNK_SIZE,
+					 residx, numchunks,
+					 toast_pointer.va_valueid);
 		}
-		else if (residx < numchunks)
+		else if (residx == numchunks-1)
 		{
 			if ((residx * TOAST_MAX_CHUNK_SIZE + chunksize) != ressize)
-				elog(ERROR, "unexpected chunk size %d in chunk %d for toast value %u",
-					 chunksize, residx,
-					 attr->va_content.va_external.va_valueid);
+				elog(ERROR, "unexpected chunk size %d (expected %d) in final chunk %d for toast value %u",
+					 chunksize,
+					 (int) (ressize - residx*TOAST_MAX_CHUNK_SIZE),
+					 residx,
+					 toast_pointer.va_valueid);
 		}
 		else
-			elog(ERROR, "unexpected chunk number %d for toast value %u",
+			elog(ERROR, "unexpected chunk number %d for toast value %u (out of range %d..%d)",
 				 residx,
-				 attr->va_content.va_external.va_valueid);
+				 toast_pointer.va_valueid,
+				 0, numchunks-1);
 
 		/*
 		 * Copy the data into proper place in our result
 		 */
 		memcpy(VARDATA(result) + residx * TOAST_MAX_CHUNK_SIZE,
-			   VARDATA(chunk),
+			   chunkdata,
 			   chunksize);
 
 		nextidx++;
@@ -1290,7 +1391,7 @@ toast_fetch_datum(varattrib *attr)
 	if (nextidx != numchunks)
 		elog(ERROR, "missing chunk number %d for toast value %u",
 			 nextidx,
-			 attr->va_content.va_external.va_valueid);
+			 toast_pointer.va_valueid);
 
 	/*
 	 * End scan and close relations
@@ -1305,12 +1406,12 @@ toast_fetch_datum(varattrib *attr)
 /* ----------
  * toast_fetch_datum_slice -
  *
- *	Reconstruct a segment of a varattrib from the chunks saved
+ *	Reconstruct a segment of a Datum from the chunks saved
  *	in the toast relation
  * ----------
  */
-static varattrib *
-toast_fetch_datum_slice(varattrib *attr, int32 sliceoffset, int32 length)
+static struct varlena *
+toast_fetch_datum_slice(struct varlena *attr, int32 sliceoffset, int32 length)
 {
 	Relation	toastrel;
 	Relation	toastidx;
@@ -1319,7 +1420,8 @@ toast_fetch_datum_slice(varattrib *attr, int32 sliceoffset, int32 length)
 	IndexScanDesc toastscan;
 	HeapTuple	ttup;
 	TupleDesc	toasttupDesc;
-	varattrib  *result;
+	struct varlena *result;
+	struct varatt_external toast_pointer;
 	int32		attrsize;
 	int32		residx;
 	int32		nextidx;
@@ -1331,11 +1433,16 @@ toast_fetch_datum_slice(varattrib *attr, int32 sliceoffset, int32 length)
 	int			totalchunks;
 	Pointer		chunk;
 	bool		isnull;
+	char 	   *chunkdata;
 	int32		chunksize;
 	int32		chcpystrt;
 	int32		chcpyend;
 
-	attrsize = attr->va_content.va_external.va_extsize;
+	/* Must copy to access aligned fields */
+	memcpy(&toast_pointer, VARDATA_SHORT(attr),
+		   sizeof(struct varatt_external));
+
+	attrsize = toast_pointer.va_extsize;
 	totalchunks = ((attrsize - 1) / TOAST_MAX_CHUNK_SIZE) + 1;
 
 	if (sliceoffset >= attrsize)
@@ -1347,11 +1454,12 @@ toast_fetch_datum_slice(varattrib *attr, int32 sliceoffset, int32 length)
 	if (((sliceoffset + length) > attrsize) || length < 0)
 		length = attrsize - sliceoffset;
 
-	result = (varattrib *) palloc(length + VARHDRSZ);
-	SET_VARSIZE(result, length + VARHDRSZ);
+	result = (struct varlena *) palloc(length + VARHDRSZ);
 
-	if (VARATT_IS_COMPRESSED(attr))
-		VARATT_SIZEP_DEPRECATED(result) |= VARATT_FLAG_COMPRESSED;
+	if (VARATT_EXTERNAL_IS_COMPRESSED(toast_pointer))
+		SET_VARSIZE_COMPRESSED(result, length + VARHDRSZ);
+	else
+		SET_VARSIZE(result, length + VARHDRSZ);
 
 	if (length == 0)
 		return result;			/* Can save a lot of work at this point! */
@@ -1366,8 +1474,7 @@ toast_fetch_datum_slice(varattrib *attr, int32 sliceoffset, int32 length)
 	/*
 	 * Open the toast relation and its index
 	 */
-	toastrel = heap_open(attr->va_content.va_external.va_toastrelid,
-						 AccessShareLock);
+	toastrel = heap_open(toast_pointer.va_toastrelid, AccessShareLock);
 	toasttupDesc = toastrel->rd_att;
 	toastidx = index_open(toastrel->rd_rel->reltoastidxid, AccessShareLock);
 
@@ -1378,7 +1485,7 @@ toast_fetch_datum_slice(varattrib *attr, int32 sliceoffset, int32 length)
 	ScanKeyInit(&toastkey[0],
 				(AttrNumber) 1,
 				BTEqualStrategyNumber, F_OIDEQ,
-				ObjectIdGetDatum(attr->va_content.va_external.va_valueid));
+				ObjectIdGetDatum(toast_pointer.va_valueid));
 
 	/*
 	 * Use equality condition for one chunk, a range condition otherwise:
@@ -1421,7 +1528,24 @@ toast_fetch_datum_slice(varattrib *attr, int32 sliceoffset, int32 length)
 		Assert(!isnull);
 		chunk = DatumGetPointer(fastgetattr(ttup, 3, toasttupDesc, &isnull));
 		Assert(!isnull);
-		chunksize = VARSIZE(chunk) - VARHDRSZ;
+		if (!VARATT_IS_EXTENDED(chunk))
+		{
+			chunksize = VARSIZE(chunk) - VARHDRSZ;
+			chunkdata = VARDATA(chunk);
+		}
+		else if (VARATT_IS_SHORT(chunk))
+		{
+			/* could happen due to heap_form_tuple doing its thing */
+			chunksize = VARSIZE_SHORT(chunk) - VARHDRSZ_SHORT;
+			chunkdata = VARDATA_SHORT(chunk);
+		}
+		else
+		{
+			/* should never happen */
+			elog(ERROR, "found toasted toast chunk");
+			chunksize = 0;				/* keep compiler quiet */
+			chunkdata = NULL;
+		}
 
 		/*
 		 * Some checks on the data we've found
@@ -1429,21 +1553,29 @@ toast_fetch_datum_slice(varattrib *attr, int32 sliceoffset, int32 length)
 		if ((residx != nextidx) || (residx > endchunk) || (residx < startchunk))
 			elog(ERROR, "unexpected chunk number %d (expected %d) for toast value %u",
 				 residx, nextidx,
-				 attr->va_content.va_external.va_valueid);
+				 toast_pointer.va_valueid);
 		if (residx < totalchunks - 1)
 		{
 			if (chunksize != TOAST_MAX_CHUNK_SIZE)
-				elog(ERROR, "unexpected chunk size %d in chunk %d for toast value %u",
-					 chunksize, residx,
-					 attr->va_content.va_external.va_valueid);
+				elog(ERROR, "unexpected chunk size %d (expected %d) in chunk %d of %d for toast value %u when fetching slice",
+					 chunksize, (int) TOAST_MAX_CHUNK_SIZE,
+					 residx, totalchunks,
+					 toast_pointer.va_valueid);
 		}
-		else
+		else if (residx == totalchunks-1)
 		{
 			if ((residx * TOAST_MAX_CHUNK_SIZE + chunksize) != attrsize)
-				elog(ERROR, "unexpected chunk size %d in chunk %d for toast value %u",
-					 chunksize, residx,
-					 attr->va_content.va_external.va_valueid);
+				elog(ERROR, "unexpected chunk size %d (expected %d) in final chunk %d for toast value %u when fetching slice",
+					 chunksize,
+					 (int) (attrsize - residx * TOAST_MAX_CHUNK_SIZE),
+					 residx,
+					 toast_pointer.va_valueid);
 		}
+		else
+			elog(ERROR, "unexpected chunk number %d for toast value %u (out of range %d..%d)",
+				 residx,
+				 toast_pointer.va_valueid,
+				 0, totalchunks-1);
 
 		/*
 		 * Copy the data into proper place in our result
@@ -1457,7 +1589,7 @@ toast_fetch_datum_slice(varattrib *attr, int32 sliceoffset, int32 length)
 
 		memcpy(VARDATA(result) +
 			   (residx * TOAST_MAX_CHUNK_SIZE - sliceoffset) + chcpystrt,
-			   VARDATA(chunk) + chcpystrt,
+			   chunkdata + chcpystrt,
 			   (chcpyend - chcpystrt) + 1);
 
 		nextidx++;
@@ -1469,7 +1601,7 @@ toast_fetch_datum_slice(varattrib *attr, int32 sliceoffset, int32 length)
 	if (nextidx != (endchunk + 1))
 		elog(ERROR, "missing chunk number %d for toast value %u",
 			 nextidx,
-			 attr->va_content.va_external.va_valueid);
+			 toast_pointer.va_valueid);
 
 	/*
 	 * End scan and close relations
diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c
index 47f5e25fd204d92fa226900e32b93e81eeded472..463f038c0f73720e8ecb8c4f1e00ad4f0b004749 100644
--- a/src/backend/catalog/toasting.c
+++ b/src/backend/catalog/toasting.c
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/catalog/toasting.c,v 1.5 2007/01/09 02:14:11 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/catalog/toasting.c,v 1.6 2007/04/06 04:21:42 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -314,7 +314,7 @@ needs_toast_table(Relation rel)
 	{
 		if (att[i]->attisdropped)
 			continue;
-		data_length = att_align(data_length, att[i]->attalign);
+		data_length = att_align_nominal(data_length, att[i]->attalign);
 		if (att[i]->attlen > 0)
 		{
 			/* Fixed-length types are never toastable */
diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c
index c568f04284c65d2ee2c504314a787847b3f362e4..44e743363ad135d57eccf964162c41c5bd2535bd 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/commands/analyze.c,v 1.103 2007/01/09 02:14:11 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/commands/analyze.c,v 1.104 2007/04/06 04:21:42 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1478,7 +1478,7 @@ compute_minimal_stats(VacAttrStatsP stats,
 		 */
 		if (is_varlena)
 		{
-			total_width += VARSIZE(DatumGetPointer(value));
+			total_width += VARSIZE_ANY(DatumGetPointer(value));
 
 			/*
 			 * If the value is toasted, we want to detoast it just once to
@@ -1792,7 +1792,7 @@ compute_scalar_stats(VacAttrStatsP stats,
 		 */
 		if (is_varlena)
 		{
-			total_width += VARSIZE(DatumGetPointer(value));
+			total_width += VARSIZE_ANY(DatumGetPointer(value));
 
 			/*
 			 * If the value is toasted, we want to detoast it just once to
diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c
index 94e6829f544cf7827110efa484de127b77f2ae6b..50ab3ada237bf39e93480d14c1293637d8625e06 100644
--- a/src/backend/executor/execQual.c
+++ b/src/backend/executor/execQual.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.216 2007/03/27 23:21:08 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.217 2007/04/06 04:21:42 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1896,8 +1896,8 @@ ExecEvalScalarArrayOp(ScalarArrayOpExprState *sstate,
 		else
 		{
 			elt = fetch_att(s, typbyval, typlen);
-			s = att_addlength(s, typlen, PointerGetDatum(s));
-			s = (char *) att_align(s, typalign);
+			s = att_addlength_pointer(s, typlen, s);
+			s = (char *) att_align_nominal(s, typalign);
 			fcinfo.arg[1] = elt;
 			fcinfo.argnull[1] = false;
 		}
diff --git a/src/backend/storage/large_object/inv_api.c b/src/backend/storage/large_object/inv_api.c
index 7becf160b499caa999fbc940b7ad6890f09a9d6d..ea98ab2952069a6224e375a15e5e0e596ae30386 100644
--- a/src/backend/storage/large_object/inv_api.c
+++ b/src/backend/storage/large_object/inv_api.c
@@ -17,7 +17,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/storage/large_object/inv_api.c,v 1.123 2007/03/03 19:52:46 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/storage/large_object/inv_api.c,v 1.124 2007/04/06 04:21:42 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -336,7 +336,7 @@ inv_getsize(LargeObjectDesc *obj_desc)
 		if (VARATT_IS_EXTENDED(datafield))
 		{
 			datafield = (bytea *)
-				heap_tuple_untoast_attr((varattrib *) datafield);
+				heap_tuple_untoast_attr((struct varlena *) datafield);
 			pfreeit = true;
 		}
 		lastbyte = data->pageno * LOBLKSIZE + getbytealen(datafield);
@@ -462,7 +462,7 @@ inv_read(LargeObjectDesc *obj_desc, char *buf, int nbytes)
 			if (VARATT_IS_EXTENDED(datafield))
 			{
 				datafield = (bytea *)
-					heap_tuple_untoast_attr((varattrib *) datafield);
+					heap_tuple_untoast_attr((struct varlena *) datafield);
 				pfreeit = true;
 			}
 			len = getbytealen(datafield);
@@ -580,7 +580,7 @@ inv_write(LargeObjectDesc *obj_desc, const char *buf, int nbytes)
 			if (VARATT_IS_EXTENDED(datafield))
 			{
 				datafield = (bytea *)
-					heap_tuple_untoast_attr((varattrib *) datafield);
+					heap_tuple_untoast_attr((struct varlena *) datafield);
 				pfreeit = true;
 			}
 			len = getbytealen(datafield);
@@ -756,7 +756,7 @@ inv_truncate(LargeObjectDesc *obj_desc, int len)
 		if (VARATT_IS_EXTENDED(datafield))
 		{
 			datafield = (bytea *)
-				heap_tuple_untoast_attr((varattrib *) datafield);
+				heap_tuple_untoast_attr((struct varlena *) datafield);
 			pfreeit = true;
 		}
 		pagelen = getbytealen(datafield);
diff --git a/src/backend/utils/adt/arrayfuncs.c b/src/backend/utils/adt/arrayfuncs.c
index 38a86452e3f4dd167559afeef707dcaccb7c64ec..991c7a46f7cf7fed2edbdc40459858e035bf444d 100644
--- a/src/backend/utils/adt/arrayfuncs.c
+++ b/src/backend/utils/adt/arrayfuncs.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/utils/adt/arrayfuncs.c,v 1.138 2007/03/27 23:21:10 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/utils/adt/arrayfuncs.c,v 1.139 2007/04/06 04:21:42 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -805,8 +805,8 @@ ReadArrayStr(char *arrayStr,
 			/* let's just make sure data is not toasted */
 			if (typlen == -1)
 				values[i] = PointerGetDatum(PG_DETOAST_DATUM(values[i]));
-			totbytes = att_addlength(totbytes, typlen, values[i]);
-			totbytes = att_align(totbytes, typalign);
+			totbytes = att_addlength_datum(totbytes, typlen, values[i]);
+			totbytes = att_align_nominal(totbytes, typalign);
 			/* check for overflow of total request */
 			if (!AllocSizeIsValid(totbytes))
 				ereport(ERROR,
@@ -1011,8 +1011,8 @@ array_out(PG_FUNCTION_ARGS)
 
 			itemvalue = fetch_att(p, typbyval, typlen);
 			values[i] = OutputFunctionCall(&my_extra->proc, itemvalue);
-			p = att_addlength(p, typlen, PointerGetDatum(p));
-			p = (char *) att_align(p, typalign);
+			p = att_addlength_pointer(p, typlen, p);
+			p = (char *) att_align_nominal(p, typalign);
 
 			/* count data plus backslashes; detect chars needing quotes */
 			if (values[i][0] == '\0')
@@ -1399,8 +1399,8 @@ ReadArrayBinary(StringInfo buf,
 			/* let's just make sure data is not toasted */
 			if (typlen == -1)
 				values[i] = PointerGetDatum(PG_DETOAST_DATUM(values[i]));
-			totbytes = att_addlength(totbytes, typlen, values[i]);
-			totbytes = att_align(totbytes, typalign);
+			totbytes = att_addlength_datum(totbytes, typlen, values[i]);
+			totbytes = att_align_nominal(totbytes, typalign);
 			/* check for overflow of total request */
 			if (!AllocSizeIsValid(totbytes))
 				ereport(ERROR,
@@ -1512,8 +1512,8 @@ array_send(PG_FUNCTION_ARGS)
 						 VARSIZE(outputbytes) - VARHDRSZ);
 			pfree(outputbytes);
 
-			p = att_addlength(p, typlen, PointerGetDatum(p));
-			p = (char *) att_align(p, typalign);
+			p = att_addlength_pointer(p, typlen, p);
+			p = (char *) att_align_nominal(p, typalign);
 		}
 
 		/* advance bitmap pointer if any */
@@ -2108,8 +2108,8 @@ array_set(ArrayType *array,
 			olditemlen = 0;
 		else
 		{
-			olditemlen = att_addlength(0, elmlen, PointerGetDatum(elt_ptr));
-			olditemlen = att_align(olditemlen, elmalign);
+			olditemlen = att_addlength_pointer(0, elmlen, elt_ptr);
+			olditemlen = att_align_nominal(olditemlen, elmalign);
 		}
 		lenafter = (int) (olddatasize - lenbefore - olditemlen);
 	}
@@ -2118,8 +2118,8 @@ array_set(ArrayType *array,
 		newitemlen = 0;
 	else
 	{
-		newitemlen = att_addlength(0, elmlen, dataValue);
-		newitemlen = att_align(newitemlen, elmalign);
+		newitemlen = att_addlength_datum(0, elmlen, dataValue);
+		newitemlen = att_align_nominal(newitemlen, elmalign);
 	}
 
 	newsize = overheadlen + lenbefore + newitemlen + lenafter;
@@ -2639,8 +2639,8 @@ array_map(FunctionCallInfo fcinfo, Oid inpType, Oid retType,
 		else
 		{
 			elt = fetch_att(s, inp_typbyval, inp_typlen);
-			s = att_addlength(s, inp_typlen, elt);
-			s = (char *) att_align(s, inp_typalign);
+			s = att_addlength_datum(s, inp_typlen, elt);
+			s = (char *) att_align_nominal(s, inp_typalign);
 			fcinfo->arg[0] = elt;
 			fcinfo->argnull[0] = false;
 		}
@@ -2679,8 +2679,8 @@ array_map(FunctionCallInfo fcinfo, Oid inpType, Oid retType,
 			if (typlen == -1)
 				values[i] = PointerGetDatum(PG_DETOAST_DATUM(values[i]));
 			/* Update total result size */
-			nbytes = att_addlength(nbytes, typlen, values[i]);
-			nbytes = att_align(nbytes, typalign);
+			nbytes = att_addlength_datum(nbytes, typlen, values[i]);
+			nbytes = att_align_nominal(nbytes, typalign);
 			/* check for overflow of total request */
 			if (!AllocSizeIsValid(nbytes))
 				ereport(ERROR,
@@ -2827,8 +2827,8 @@ construct_md_array(Datum *elems,
 		/* make sure data is not toasted */
 		if (elmlen == -1)
 			elems[i] = PointerGetDatum(PG_DETOAST_DATUM(elems[i]));
-		nbytes = att_addlength(nbytes, elmlen, elems[i]);
-		nbytes = att_align(nbytes, elmalign);
+		nbytes = att_addlength_datum(nbytes, elmlen, elems[i]);
+		nbytes = att_align_nominal(nbytes, elmalign);
 		/* check for overflow of total request */
 		if (!AllocSizeIsValid(nbytes))
 			ereport(ERROR,
@@ -2947,8 +2947,8 @@ deconstruct_array(ArrayType *array,
 			elems[i] = fetch_att(p, elmbyval, elmlen);
 			if (nulls)
 				nulls[i] = false;
-			p = att_addlength(p, elmlen, PointerGetDatum(p));
-			p = (char *) att_align(p, elmalign);
+			p = att_addlength_pointer(p, elmlen, p);
+			p = (char *) att_align_nominal(p, elmalign);
 		}
 
 		/* advance bitmap pointer if any */
@@ -3064,8 +3064,8 @@ array_eq(PG_FUNCTION_ARGS)
 			{
 				isnull1 = false;
 				elt1 = fetch_att(ptr1, typbyval, typlen);
-				ptr1 = att_addlength(ptr1, typlen, PointerGetDatum(ptr1));
-				ptr1 = (char *) att_align(ptr1, typalign);
+				ptr1 = att_addlength_pointer(ptr1, typlen, ptr1);
+				ptr1 = (char *) att_align_nominal(ptr1, typalign);
 			}
 
 			if (bitmap2 && (*bitmap2 & bitmask) == 0)
@@ -3077,8 +3077,8 @@ array_eq(PG_FUNCTION_ARGS)
 			{
 				isnull2 = false;
 				elt2 = fetch_att(ptr2, typbyval, typlen);
-				ptr2 = att_addlength(ptr2, typlen, PointerGetDatum(ptr2));
-				ptr2 = (char *) att_align(ptr2, typalign);
+				ptr2 = att_addlength_pointer(ptr2, typlen, ptr2);
+				ptr2 = (char *) att_align_nominal(ptr2, typalign);
 			}
 
 			/* advance bitmap pointers if any */
@@ -3265,8 +3265,8 @@ array_cmp(FunctionCallInfo fcinfo)
 		{
 			isnull1 = false;
 			elt1 = fetch_att(ptr1, typbyval, typlen);
-			ptr1 = att_addlength(ptr1, typlen, PointerGetDatum(ptr1));
-			ptr1 = (char *) att_align(ptr1, typalign);
+			ptr1 = att_addlength_pointer(ptr1, typlen, ptr1);
+			ptr1 = (char *) att_align_nominal(ptr1, typalign);
 		}
 
 		if (bitmap2 && (*bitmap2 & bitmask) == 0)
@@ -3278,8 +3278,8 @@ array_cmp(FunctionCallInfo fcinfo)
 		{
 			isnull2 = false;
 			elt2 = fetch_att(ptr2, typbyval, typlen);
-			ptr2 = att_addlength(ptr2, typlen, PointerGetDatum(ptr2));
-			ptr2 = (char *) att_align(ptr2, typalign);
+			ptr2 = att_addlength_pointer(ptr2, typlen, ptr2);
+			ptr2 = (char *) att_align_nominal(ptr2, typalign);
 		}
 
 		/* advance bitmap pointers if any */
@@ -3468,8 +3468,8 @@ array_contain_compare(ArrayType *array1, ArrayType *array2, bool matchall,
 		{
 			isnull1 = false;
 			elt1 = fetch_att(ptr1, typbyval, typlen);
-			ptr1 = att_addlength(ptr1, typlen, PointerGetDatum(ptr1));
-			ptr1 = (char *) att_align(ptr1, typalign);
+			ptr1 = att_addlength_pointer(ptr1, typlen, ptr1);
+			ptr1 = (char *) att_align_nominal(ptr1, typalign);
 		}
 
 		/* advance bitmap pointer if any */
@@ -3667,14 +3667,14 @@ ArrayCastAndSet(Datum src,
 			store_att_byval(dest, src, typlen);
 		else
 			memmove(dest, DatumGetPointer(src), typlen);
-		inc = att_align(typlen, typalign);
+		inc = att_align_nominal(typlen, typalign);
 	}
 	else
 	{
 		Assert(!typbyval);
-		inc = att_addlength(0, typlen, src);
+		inc = att_addlength_datum(0, typlen, src);
 		memmove(dest, DatumGetPointer(src), inc);
-		inc = att_align(inc, typalign);
+		inc = att_align_nominal(inc, typalign);
 	}
 
 	return inc;
@@ -3700,7 +3700,7 @@ array_seek(char *ptr, int offset, bits8 *nullbitmap, int nitems,
 
 	/* easy if fixed-size elements and no NULLs */
 	if (typlen > 0 && !nullbitmap)
-		return ptr + nitems * ((Size) att_align(typlen, typalign));
+		return ptr + nitems * ((Size) att_align_nominal(typlen, typalign));
 
 	/* seems worth having separate loops for NULL and no-NULLs cases */
 	if (nullbitmap)
@@ -3712,8 +3712,8 @@ array_seek(char *ptr, int offset, bits8 *nullbitmap, int nitems,
 		{
 			if (*nullbitmap & bitmask)
 			{
-				ptr = att_addlength(ptr, typlen, PointerGetDatum(ptr));
-				ptr = (char *) att_align(ptr, typalign);
+				ptr = att_addlength_pointer(ptr, typlen, ptr);
+				ptr = (char *) att_align_nominal(ptr, typalign);
 			}
 			bitmask <<= 1;
 			if (bitmask == 0x100)
@@ -3727,8 +3727,8 @@ array_seek(char *ptr, int offset, bits8 *nullbitmap, int nitems,
 	{
 		for (i = 0; i < nitems; i++)
 		{
-			ptr = att_addlength(ptr, typlen, PointerGetDatum(ptr));
-			ptr = (char *) att_align(ptr, typalign);
+			ptr = att_addlength_pointer(ptr, typlen, ptr);
+			ptr = (char *) att_align_nominal(ptr, typalign);
 		}
 	}
 	return ptr;
@@ -3883,7 +3883,7 @@ array_slice_size(char *arraydataptr, bits8 *arraynullsptr,
 
 	/* Pretty easy for fixed element length without nulls ... */
 	if (typlen > 0 && !arraynullsptr)
-		return ArrayGetNItems(ndim, span) * att_align(typlen, typalign);
+		return ArrayGetNItems(ndim, span) * att_align_nominal(typlen, typalign);
 
 	/* Else gotta do it the hard way */
 	src_offset = ArrayGetOffset(ndim, dim, lb, st);
@@ -3904,8 +3904,8 @@ array_slice_size(char *arraydataptr, bits8 *arraynullsptr,
 		}
 		if (!array_get_isnull(arraynullsptr, src_offset))
 		{
-			inc = att_addlength(0, typlen, PointerGetDatum(ptr));
-			inc = att_align(inc, typalign);
+			inc = att_addlength_pointer(0, typlen, ptr);
+			inc = att_align_nominal(inc, typalign);
 			ptr += inc;
 			count += inc;
 		}
diff --git a/src/backend/utils/adt/datum.c b/src/backend/utils/adt/datum.c
index 21e7cc55412fb4ca64890fc1b1f2f8bc77752fc3..c025de61f74bf76731b31269d25d09fefa5e7937 100644
--- a/src/backend/utils/adt/datum.c
+++ b/src/backend/utils/adt/datum.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/utils/adt/datum.c,v 1.34 2007/02/27 23:48:07 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/utils/adt/datum.c,v 1.35 2007/04/06 04:21:42 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -47,7 +47,7 @@
  * Find the "real" size of a datum, given the datum value,
  * whether it is a "by value", and the declared type length.
  *
- * This is essentially an out-of-line version of the att_addlength()
+ * This is essentially an out-of-line version of the att_addlength_datum()
  * macro in access/tupmacs.h.  We do a tad more error checking though.
  *-------------------------------------------------------------------------
  */
@@ -79,7 +79,7 @@ datumGetSize(Datum value, bool typByVal, int typLen)
 						(errcode(ERRCODE_DATA_EXCEPTION),
 						 errmsg("invalid Datum pointer")));
 
-			size = (Size) VARSIZE(s);
+			size = (Size) VARSIZE_ANY(s);
 		}
 		else if (typLen == -2)
 		{
diff --git a/src/backend/utils/adt/network.c b/src/backend/utils/adt/network.c
index 88fa427450f2da6282093406505b9252472d2011..c1dadfbcc1b8484ae820c949e5677b17124a1954 100644
--- a/src/backend/utils/adt/network.c
+++ b/src/backend/utils/adt/network.c
@@ -1,7 +1,7 @@
 /*
  *	PostgreSQL type definitions for the INET and CIDR types.
  *
- *	$PostgreSQL: pgsql/src/backend/utils/adt/network.c,v 1.68 2007/02/27 23:48:08 tgl Exp $
+ *	$PostgreSQL: pgsql/src/backend/utils/adt/network.c,v 1.69 2007/04/06 04:21:43 tgl Exp $
  *
  *	Jon Postel RIP 16 Oct 1998
  */
@@ -30,23 +30,38 @@ static int	ip_addrsize(inet *inetptr);
 static inet *internal_inetpl(inet *ip, int64 addend);
 
 /*
- *	Access macros.
+ *	Access macros.  We use VARDATA_ANY so that we can process short-header
+ *	varlena values without detoasting them.  This requires a trick:
+ *	VARDATA_ANY assumes the varlena header is already filled in, which is
+ *	not the case when constructing a new value (until SET_INET_VARSIZE is
+ *	called, which we typically can't do till the end).  Therefore, we
+ *	always initialize the newly-allocated value to zeroes (using palloc0).
+ *	A zero length word will look like the not-1-byte case to VARDATA_ANY,
+ *	and so we correctly construct an uncompressed value.
+ *
+ *	Note that ip_maxbits() and SET_INET_VARSIZE() require
+ *	the family field to be set correctly.
  */
 
 #define ip_family(inetptr) \
-	(((inet_struct *)VARDATA(inetptr))->family)
+	(((inet_struct *) VARDATA_ANY(inetptr))->family)
 
 #define ip_bits(inetptr) \
-	(((inet_struct *)VARDATA(inetptr))->bits)
+	(((inet_struct *) VARDATA_ANY(inetptr))->bits)
 
 #define ip_addr(inetptr) \
-	(((inet_struct *)VARDATA(inetptr))->ipaddr)
+	(((inet_struct *) VARDATA_ANY(inetptr))->ipaddr)
 
 #define ip_maxbits(inetptr) \
 	(ip_family(inetptr) == PGSQL_AF_INET ? 32 : 128)
 
+#define SET_INET_VARSIZE(dst) \
+	SET_VARSIZE(dst, VARHDRSZ + offsetof(inet_struct, ipaddr) + \
+				ip_addrsize(dst))
+
+
 /*
- * Return the number of bytes of storage needed for this data type.
+ * Return the number of bytes of address storage needed for this data type.
  */
 static int
 ip_addrsize(inet *inetptr)
@@ -71,7 +86,7 @@ network_in(char *src, bool is_cidr)
 	int			bits;
 	inet	   *dst;
 
-	dst = (inet *) palloc0(VARHDRSZ + sizeof(inet_struct));
+	dst = (inet *) palloc0(sizeof(inet));
 
 	/*
 	 * First, check to see if this is an IPv6 or IPv4 address.	IPv6 addresses
@@ -105,10 +120,8 @@ network_in(char *src, bool is_cidr)
 					 errdetail("Value has bits set to right of mask.")));
 	}
 
-	SET_VARSIZE(dst, VARHDRSZ +
-		((char *) ip_addr(dst) - (char *) VARDATA(dst)) +
-		ip_addrsize(dst));
 	ip_bits(dst) = bits;
+	SET_INET_VARSIZE(dst);
 
 	return dst;
 }
@@ -194,7 +207,7 @@ network_recv(StringInfo buf, bool is_cidr)
 				i;
 
 	/* make sure any unused bits in a CIDR value are zeroed */
-	addr = (inet *) palloc0(VARHDRSZ + sizeof(inet_struct));
+	addr = (inet *) palloc0(sizeof(inet));
 
 	ip_family(addr) = pq_getmsgbyte(buf);
 	if (ip_family(addr) != PGSQL_AF_INET &&
@@ -220,9 +233,6 @@ network_recv(StringInfo buf, bool is_cidr)
 		/* translator: %s is inet or cidr */
 				 errmsg("invalid length in external \"%s\" value",
 						is_cidr ? "cidr" : "inet")));
-	SET_VARSIZE(addr, VARHDRSZ +
-		((char *) ip_addr(addr) - (char *) VARDATA(addr)) +
-		ip_addrsize(addr));
 
 	addrptr = (char *) ip_addr(addr);
 	for (i = 0; i < nb; i++)
@@ -240,6 +250,8 @@ network_recv(StringInfo buf, bool is_cidr)
 					 errdetail("Value has bits set to right of mask.")));
 	}
 
+	SET_INET_VARSIZE(addr);
+
 	return addr;
 }
 
@@ -348,8 +360,8 @@ inet_to_cidr(PG_FUNCTION_ARGS)
 		elog(ERROR, "invalid inet bit length: %d", bits);
 
 	/* clone the original data */
-	dst = (inet *) palloc(VARSIZE(src));
-	memcpy(dst, src, VARSIZE(src));
+	dst = (inet *) palloc(VARSIZE_ANY(src));
+	memcpy(dst, src, VARSIZE_ANY(src));
 
 	/* zero out any bits to the right of the netmask */
 	byte = bits / 8;
@@ -387,8 +399,8 @@ inet_set_masklen(PG_FUNCTION_ARGS)
 				 errmsg("invalid mask length: %d", bits)));
 
 	/* clone the original data */
-	dst = (inet *) palloc(VARSIZE(src));
-	memcpy(dst, src, VARSIZE(src));
+	dst = (inet *) palloc(VARSIZE_ANY(src));
+	memcpy(dst, src, VARSIZE_ANY(src));
 
 	ip_bits(dst) = bits;
 
@@ -414,8 +426,8 @@ cidr_set_masklen(PG_FUNCTION_ARGS)
 				 errmsg("invalid mask length: %d", bits)));
 
 	/* clone the original data */
-	dst = (inet *) palloc(VARSIZE(src));
-	memcpy(dst, src, VARSIZE(src));
+	dst = (inet *) palloc(VARSIZE_ANY(src));
+	memcpy(dst, src, VARSIZE_ANY(src));
 
 	ip_bits(dst) = bits;
 
@@ -546,7 +558,7 @@ hashinet(PG_FUNCTION_ARGS)
 	int			addrsize = ip_addrsize(addr);
 
 	/* XXX this assumes there are no pad bytes in the data structure */
-	return hash_any((unsigned char *) VARDATA(addr), addrsize + 2);
+	return hash_any((unsigned char *) VARDATA_ANY(addr), addrsize + 2);
 }
 
 /*
@@ -762,7 +774,7 @@ network_broadcast(PG_FUNCTION_ARGS)
 			   *b;
 
 	/* make sure any unused bits are zeroed */
-	dst = (inet *) palloc0(VARHDRSZ + sizeof(inet_struct));
+	dst = (inet *) palloc0(sizeof(inet));
 
 	if (ip_family(ip) == PGSQL_AF_INET)
 		maxbytes = 4;
@@ -793,9 +805,7 @@ network_broadcast(PG_FUNCTION_ARGS)
 
 	ip_family(dst) = ip_family(ip);
 	ip_bits(dst) = ip_bits(ip);
-	SET_VARSIZE(dst, VARHDRSZ +
-		((char *) ip_addr(dst) - (char *) VARDATA(dst)) +
-		ip_addrsize(dst));
+	SET_INET_VARSIZE(dst);
 
 	PG_RETURN_INET_P(dst);
 }
@@ -812,7 +822,7 @@ network_network(PG_FUNCTION_ARGS)
 			   *b;
 
 	/* make sure any unused bits are zeroed */
-	dst = (inet *) palloc0(VARHDRSZ + sizeof(inet_struct));
+	dst = (inet *) palloc0(sizeof(inet));
 
 	bits = ip_bits(ip);
 	a = ip_addr(ip);
@@ -838,9 +848,7 @@ network_network(PG_FUNCTION_ARGS)
 
 	ip_family(dst) = ip_family(ip);
 	ip_bits(dst) = ip_bits(ip);
-	SET_VARSIZE(dst, VARHDRSZ +
-		((char *) ip_addr(dst) - (char *) VARDATA(dst)) +
-		ip_addrsize(dst));
+	SET_INET_VARSIZE(dst);
 
 	PG_RETURN_INET_P(dst);
 }
@@ -856,7 +864,7 @@ network_netmask(PG_FUNCTION_ARGS)
 	unsigned char *b;
 
 	/* make sure any unused bits are zeroed */
-	dst = (inet *) palloc0(VARHDRSZ + sizeof(inet_struct));
+	dst = (inet *) palloc0(sizeof(inet));
 
 	bits = ip_bits(ip);
 	b = ip_addr(dst);
@@ -881,9 +889,7 @@ network_netmask(PG_FUNCTION_ARGS)
 
 	ip_family(dst) = ip_family(ip);
 	ip_bits(dst) = ip_maxbits(ip);
-	SET_VARSIZE(dst, VARHDRSZ +
-		((char *) ip_addr(dst) - (char *) VARDATA(dst)) +
-		ip_addrsize(dst));
+	SET_INET_VARSIZE(dst);
 
 	PG_RETURN_INET_P(dst);
 }
@@ -900,7 +906,7 @@ network_hostmask(PG_FUNCTION_ARGS)
 	unsigned char *b;
 
 	/* make sure any unused bits are zeroed */
-	dst = (inet *) palloc0(VARHDRSZ + sizeof(inet_struct));
+	dst = (inet *) palloc0(sizeof(inet));
 
 	if (ip_family(ip) == PGSQL_AF_INET)
 		maxbytes = 4;
@@ -930,9 +936,7 @@ network_hostmask(PG_FUNCTION_ARGS)
 
 	ip_family(dst) = ip_family(ip);
 	ip_bits(dst) = ip_maxbits(ip);
-	SET_VARSIZE(dst, VARHDRSZ +
-		((char *) ip_addr(dst) - (char *) VARDATA(dst)) +
-		ip_addrsize(dst));
+	SET_INET_VARSIZE(dst);
 
 	PG_RETURN_INET_P(dst);
 }
@@ -1259,7 +1263,7 @@ inetnot(PG_FUNCTION_ARGS)
 	inet	   *ip = PG_GETARG_INET_P(0);
 	inet	   *dst;
 
-	dst = (inet *) palloc0(VARHDRSZ + sizeof(inet_struct));
+	dst = (inet *) palloc0(sizeof(inet));
 
 	{
 		int			nb = ip_addrsize(ip);
@@ -1272,9 +1276,7 @@ inetnot(PG_FUNCTION_ARGS)
 	ip_bits(dst) = ip_bits(ip);
 
 	ip_family(dst) = ip_family(ip);
-	SET_VARSIZE(dst, VARHDRSZ +
-		((char *) ip_addr(dst) - (char *) VARDATA(dst)) +
-		ip_addrsize(dst));
+	SET_INET_VARSIZE(dst);
 
 	PG_RETURN_INET_P(dst);
 }
@@ -1287,7 +1289,7 @@ inetand(PG_FUNCTION_ARGS)
 	inet	   *ip2 = PG_GETARG_INET_P(1);
 	inet	   *dst;
 
-	dst = (inet *) palloc0(VARHDRSZ + sizeof(inet_struct));
+	dst = (inet *) palloc0(sizeof(inet));
 
 	if (ip_family(ip) != ip_family(ip2))
 		ereport(ERROR,
@@ -1306,9 +1308,7 @@ inetand(PG_FUNCTION_ARGS)
 	ip_bits(dst) = Max(ip_bits(ip), ip_bits(ip2));
 
 	ip_family(dst) = ip_family(ip);
-	SET_VARSIZE(dst, VARHDRSZ +
-		((char *) ip_addr(dst) - (char *) VARDATA(dst)) +
-		ip_addrsize(dst));
+	SET_INET_VARSIZE(dst);
 
 	PG_RETURN_INET_P(dst);
 }
@@ -1321,7 +1321,7 @@ inetor(PG_FUNCTION_ARGS)
 	inet	   *ip2 = PG_GETARG_INET_P(1);
 	inet	   *dst;
 
-	dst = (inet *) palloc0(VARHDRSZ + sizeof(inet_struct));
+	dst = (inet *) palloc0(sizeof(inet));
 
 	if (ip_family(ip) != ip_family(ip2))
 		ereport(ERROR,
@@ -1340,9 +1340,7 @@ inetor(PG_FUNCTION_ARGS)
 	ip_bits(dst) = Max(ip_bits(ip), ip_bits(ip2));
 
 	ip_family(dst) = ip_family(ip);
-	SET_VARSIZE(dst, VARHDRSZ +
-		((char *) ip_addr(dst) - (char *) VARDATA(dst)) +
-		ip_addrsize(dst));
+	SET_INET_VARSIZE(dst);
 
 	PG_RETURN_INET_P(dst);
 }
@@ -1353,7 +1351,7 @@ internal_inetpl(inet *ip, int64 addend)
 {
 	inet	   *dst;
 
-	dst = (inet *) palloc0(VARHDRSZ + sizeof(inet_struct));
+	dst = (inet *) palloc0(sizeof(inet));
 
 	{
 		int			nb = ip_addrsize(ip);
@@ -1391,12 +1389,10 @@ internal_inetpl(inet *ip, int64 addend)
 					(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
 					 errmsg("result is out of range")));
 	}
-	ip_bits(dst) = ip_bits(ip);
 
+	ip_bits(dst) = ip_bits(ip);
 	ip_family(dst) = ip_family(ip);
-	SET_VARSIZE(dst, VARHDRSZ +
-		((char *) ip_addr(dst) - (char *) VARDATA(dst)) +
-		ip_addrsize(dst));
+	SET_INET_VARSIZE(dst);
 
 	return dst;
 }
diff --git a/src/backend/utils/adt/pg_lzcompress.c b/src/backend/utils/adt/pg_lzcompress.c
index c756e5707a5ae20368dffe7a179274ecebb03a24..085bc63e0dd0e6d1326296c1202aeb6b07fb2065 100644
--- a/src/backend/utils/adt/pg_lzcompress.c
+++ b/src/backend/utils/adt/pg_lzcompress.c
@@ -166,7 +166,7 @@
  *
  * Copyright (c) 1999-2007, PostgreSQL Global Development Group
  *
- * $PostgreSQL: pgsql/src/backend/utils/adt/pg_lzcompress.c,v 1.25 2007/02/27 23:48:08 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/adt/pg_lzcompress.c,v 1.26 2007/04/06 04:21:43 tgl Exp $
  * ----------
  */
 #include "postgres.h"
@@ -618,7 +618,7 @@ pglz_compress(const char *source, int32 slen, PGLZ_Header *dest,
 	/*
 	 * Success - need only fill in the actual length of the compressed datum.
 	 */
-	SET_VARSIZE(dest, result_size + sizeof(PGLZ_Header));
+	SET_VARSIZE_COMPRESSED(dest, result_size + sizeof(PGLZ_Header));
 
 	return true;
 }
diff --git a/src/backend/utils/adt/varchar.c b/src/backend/utils/adt/varchar.c
index 371a8a605d677cf5d363e0141217fafd6afc20bc..77a6ab9c4fc51bb7b452b3655de54827b087326d 100644
--- a/src/backend/utils/adt/varchar.c
+++ b/src/backend/utils/adt/varchar.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/utils/adt/varchar.c,v 1.122 2007/02/27 23:48:09 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/utils/adt/varchar.c,v 1.123 2007/04/06 04:21:43 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -16,6 +16,7 @@
 
 
 #include "access/hash.h"
+#include "access/tuptoaster.h"
 #include "libpq/pqformat.h"
 #include "utils/array.h"
 #include "utils/builtins.h"
@@ -206,14 +207,14 @@ bpcharin(PG_FUNCTION_ARGS)
 Datum
 bpcharout(PG_FUNCTION_ARGS)
 {
-	BpChar	   *s = PG_GETARG_BPCHAR_P(0);
+	BpChar	   *s = PG_GETARG_BPCHAR_PP(0);
 	char	   *result;
 	int			len;
 
 	/* copy and add null term */
-	len = VARSIZE(s) - VARHDRSZ;
+	len = VARSIZE_ANY_EXHDR(s);
 	result = (char *) palloc(len + 1);
-	memcpy(result, VARDATA(s), len);
+	memcpy(result, VARDATA_ANY(s), len);
 	result[len] = '\0';
 
 	PG_RETURN_CSTRING(result);
@@ -267,7 +268,7 @@ bpcharsend(PG_FUNCTION_ARGS)
 Datum
 bpchar(PG_FUNCTION_ARGS)
 {
-	BpChar	   *source = PG_GETARG_BPCHAR_P(0);
+	BpChar	   *source = PG_GETARG_BPCHAR_PP(0);
 	int32		maxlen = PG_GETARG_INT32(1);
 	bool		isExplicit = PG_GETARG_BOOL(2);
 	BpChar	   *result;
@@ -282,9 +283,12 @@ bpchar(PG_FUNCTION_ARGS)
 	if (maxlen < (int32) VARHDRSZ)
 		PG_RETURN_BPCHAR_P(source);
 
-	len = VARSIZE(source);
+	maxlen -= VARHDRSZ;
 
-	charlen = pg_mbstrlen_with_len(VARDATA(source), len - VARHDRSZ) + VARHDRSZ;
+	len = VARSIZE_ANY_EXHDR(source);
+	s = VARDATA_ANY(source);
+
+	charlen = pg_mbstrlen_with_len(s, len);
 
 	/* No work if supplied data matches typmod already */
 	if (charlen == maxlen)
@@ -295,23 +299,22 @@ bpchar(PG_FUNCTION_ARGS)
 		/* Verify that extra characters are spaces, and clip them off */
 		size_t		maxmblen;
 
-		maxmblen = pg_mbcharcliplen(VARDATA(source), len - VARHDRSZ,
-									maxlen - VARHDRSZ) + VARHDRSZ;
+		maxmblen = pg_mbcharcliplen(s, len, maxlen);
 
 		if (!isExplicit)
 		{
-			for (i = maxmblen - VARHDRSZ; i < len - VARHDRSZ; i++)
-				if (*(VARDATA(source) + i) != ' ')
+			for (i = maxmblen; i < len; i++)
+				if (s[i] != ' ')
 					ereport(ERROR,
 							(errcode(ERRCODE_STRING_DATA_RIGHT_TRUNCATION),
 							 errmsg("value too long for type character(%d)",
-									maxlen - VARHDRSZ)));
+									maxlen)));
 		}
 
 		len = maxmblen;
 
 		/*
-		 * XXX: at this point, maxlen is the necessary byte length+VARHDRSZ,
+		 * At this point, maxlen is the necessary byte length,
 		 * not the number of CHARACTERS!
 		 */
 		maxlen = len;
@@ -319,23 +322,23 @@ bpchar(PG_FUNCTION_ARGS)
 	else
 	{
 		/*
-		 * XXX: at this point, maxlen is the necessary byte length+VARHDRSZ,
+		 * At this point, maxlen is the necessary byte length,
 		 * not the number of CHARACTERS!
 		 */
 		maxlen = len + (maxlen - charlen);
 	}
 
-	s = VARDATA(source);
+	Assert(maxlen >= len);
 
-	result = palloc(maxlen);
-	SET_VARSIZE(result, maxlen);
+	result = palloc(maxlen+VARHDRSZ);
+	SET_VARSIZE(result, maxlen+VARHDRSZ);
 	r = VARDATA(result);
 
-	memcpy(r, s, len - VARHDRSZ);
+	memcpy(r, s, len);
 
 	/* blank pad the string if necessary */
 	if (maxlen > len)
-		memset(r + len - VARHDRSZ, ' ', maxlen - len);
+		memset(r + len, ' ', maxlen - len);
 
 	PG_RETURN_BPCHAR_P(result);
 }
@@ -365,11 +368,13 @@ char_bpchar(PG_FUNCTION_ARGS)
 Datum
 bpchar_name(PG_FUNCTION_ARGS)
 {
-	BpChar	   *s = PG_GETARG_BPCHAR_P(0);
+	BpChar	   *s = PG_GETARG_BPCHAR_PP(0);
+	char       *s_data;
 	Name		result;
 	int			len;
 
-	len = VARSIZE(s) - VARHDRSZ;
+	len = VARSIZE_ANY_EXHDR(s);
+	s_data = VARDATA_ANY(s);
 
 	/* Truncate to max length for a Name */
 	if (len >= NAMEDATALEN)
@@ -378,13 +383,13 @@ bpchar_name(PG_FUNCTION_ARGS)
 	/* Remove trailing blanks */
 	while (len > 0)
 	{
-		if (*(VARDATA(s) + len - 1) != ' ')
+		if (s_data[len - 1] != ' ')
 			break;
 		len--;
 	}
 
 	result = (NameData *) palloc(NAMEDATALEN);
-	memcpy(NameStr(*result), VARDATA(s), len);
+	memcpy(NameStr(*result), s_data, len);
 
 	/* Now null pad to full length... */
 	while (len < NAMEDATALEN)
@@ -509,14 +514,14 @@ varcharin(PG_FUNCTION_ARGS)
 Datum
 varcharout(PG_FUNCTION_ARGS)
 {
-	VarChar    *s = PG_GETARG_VARCHAR_P(0);
+	VarChar    *s = PG_GETARG_VARCHAR_PP(0);
 	char	   *result;
 	int32		len;
 
 	/* copy and add null term */
-	len = VARSIZE(s) - VARHDRSZ;
+	len = VARSIZE_ANY_EXHDR(s);
 	result = palloc(len + 1);
-	memcpy(result, VARDATA(s), len);
+	memcpy(result, VARDATA_ANY(s), len);
 	result[len] = '\0';
 
 	PG_RETURN_CSTRING(result);
@@ -570,39 +575,41 @@ varcharsend(PG_FUNCTION_ARGS)
 Datum
 varchar(PG_FUNCTION_ARGS)
 {
-	VarChar    *source = PG_GETARG_VARCHAR_P(0);
-	int32		maxlen = PG_GETARG_INT32(1);
+	VarChar    *source = PG_GETARG_VARCHAR_PP(0);
+	int32		typmod = PG_GETARG_INT32(1);
 	bool		isExplicit = PG_GETARG_BOOL(2);
 	VarChar    *result;
-	int32		len;
+	int32		len, maxlen;
 	size_t		maxmblen;
 	int			i;
+	char 	   *s_data;
+
+	len = VARSIZE_ANY_EXHDR(source);
+	s_data = VARDATA_ANY(source);
+	maxlen = typmod - VARHDRSZ;
 
-	len = VARSIZE(source);
 	/* No work if typmod is invalid or supplied data fits it already */
-	if (maxlen < (int32) VARHDRSZ || len <= maxlen)
+	if (maxlen < 0 || len <= maxlen)
 		PG_RETURN_VARCHAR_P(source);
 
 	/* only reach here if string is too long... */
 
 	/* truncate multibyte string preserving multibyte boundary */
-	maxmblen = pg_mbcharcliplen(VARDATA(source), len - VARHDRSZ,
-								maxlen - VARHDRSZ);
+	maxmblen = pg_mbcharcliplen(s_data, len, maxlen);
 
 	if (!isExplicit)
 	{
-		for (i = maxmblen; i < len - VARHDRSZ; i++)
-			if (*(VARDATA(source) + i) != ' ')
+		for (i = maxmblen; i < len; i++)
+			if (s_data[i] != ' ')
 				ereport(ERROR,
 						(errcode(ERRCODE_STRING_DATA_RIGHT_TRUNCATION),
-					  errmsg("value too long for type character varying(%d)",
-							 maxlen - VARHDRSZ)));
+						 errmsg("value too long for type character varying(%d)",
+								maxlen)));
 	}
 
-	len = maxmblen + VARHDRSZ;
-	result = palloc(len);
-	SET_VARSIZE(result, len);
-	memcpy(VARDATA(result), VARDATA(source), len - VARHDRSZ);
+	result = palloc(maxmblen + VARHDRSZ);
+	SET_VARSIZE(result, maxmblen + VARHDRSZ);
+	memcpy(VARDATA(result), s_data, maxmblen);
 
 	PG_RETURN_VARCHAR_P(result);
 }
@@ -632,11 +639,11 @@ varchartypmodout(PG_FUNCTION_ARGS)
 static int
 bcTruelen(BpChar *arg)
 {
-	char	   *s = VARDATA(arg);
+	char	   *s = VARDATA_ANY(arg);
 	int			i;
 	int			len;
 
-	len = VARSIZE(arg) - VARHDRSZ;
+	len = VARSIZE_ANY_EXHDR(arg);
 	for (i = len - 1; i >= 0; i--)
 	{
 		if (s[i] != ' ')
@@ -648,7 +655,7 @@ bcTruelen(BpChar *arg)
 Datum
 bpcharlen(PG_FUNCTION_ARGS)
 {
-	BpChar	   *arg = PG_GETARG_BPCHAR_P(0);
+	BpChar	   *arg = PG_GETARG_BPCHAR_PP(0);
 	int			len;
 
 	/* get number of bytes, ignoring trailing spaces */
@@ -656,7 +663,7 @@ bpcharlen(PG_FUNCTION_ARGS)
 
 	/* in multibyte encoding, convert to number of characters */
 	if (pg_database_encoding_max_length() != 1)
-		len = pg_mbstrlen_with_len(VARDATA(arg), len);
+		len = pg_mbstrlen_with_len(VARDATA_ANY(arg), len);
 
 	PG_RETURN_INT32(len);
 }
@@ -664,9 +671,10 @@ bpcharlen(PG_FUNCTION_ARGS)
 Datum
 bpcharoctetlen(PG_FUNCTION_ARGS)
 {
-	BpChar	   *arg = PG_GETARG_BPCHAR_P(0);
+	Datum	   arg = PG_GETARG_DATUM(0);
 
-	PG_RETURN_INT32(VARSIZE(arg) - VARHDRSZ);
+	/* We need not detoast the input at all */
+	PG_RETURN_INT32(toast_raw_datum_size(arg) - VARHDRSZ);
 }
 
 
@@ -681,8 +689,8 @@ bpcharoctetlen(PG_FUNCTION_ARGS)
 Datum
 bpchareq(PG_FUNCTION_ARGS)
 {
-	BpChar	   *arg1 = PG_GETARG_BPCHAR_P(0);
-	BpChar	   *arg2 = PG_GETARG_BPCHAR_P(1);
+	BpChar	   *arg1 = PG_GETARG_BPCHAR_PP(0);
+	BpChar	   *arg2 = PG_GETARG_BPCHAR_PP(1);
 	int			len1,
 				len2;
 	bool		result;
@@ -697,7 +705,7 @@ bpchareq(PG_FUNCTION_ARGS)
 	if (len1 != len2)
 		result = false;
 	else
-		result = (strncmp(VARDATA(arg1), VARDATA(arg2), len1) == 0);
+		result = (strncmp(VARDATA_ANY(arg1), VARDATA_ANY(arg2), len1) == 0);
 
 	PG_FREE_IF_COPY(arg1, 0);
 	PG_FREE_IF_COPY(arg2, 1);
@@ -708,8 +716,8 @@ bpchareq(PG_FUNCTION_ARGS)
 Datum
 bpcharne(PG_FUNCTION_ARGS)
 {
-	BpChar	   *arg1 = PG_GETARG_BPCHAR_P(0);
-	BpChar	   *arg2 = PG_GETARG_BPCHAR_P(1);
+	BpChar	   *arg1 = PG_GETARG_BPCHAR_PP(0);
+	BpChar	   *arg2 = PG_GETARG_BPCHAR_PP(1);
 	int			len1,
 				len2;
 	bool		result;
@@ -724,7 +732,7 @@ bpcharne(PG_FUNCTION_ARGS)
 	if (len1 != len2)
 		result = true;
 	else
-		result = (strncmp(VARDATA(arg1), VARDATA(arg2), len1) != 0);
+		result = (strncmp(VARDATA_ANY(arg1), VARDATA_ANY(arg2), len1) != 0);
 
 	PG_FREE_IF_COPY(arg1, 0);
 	PG_FREE_IF_COPY(arg2, 1);
@@ -735,8 +743,8 @@ bpcharne(PG_FUNCTION_ARGS)
 Datum
 bpcharlt(PG_FUNCTION_ARGS)
 {
-	BpChar	   *arg1 = PG_GETARG_BPCHAR_P(0);
-	BpChar	   *arg2 = PG_GETARG_BPCHAR_P(1);
+	BpChar	   *arg1 = PG_GETARG_BPCHAR_PP(0);
+	BpChar	   *arg2 = PG_GETARG_BPCHAR_PP(1);
 	int			len1,
 				len2;
 	int			cmp;
@@ -744,7 +752,7 @@ bpcharlt(PG_FUNCTION_ARGS)
 	len1 = bcTruelen(arg1);
 	len2 = bcTruelen(arg2);
 
-	cmp = varstr_cmp(VARDATA(arg1), len1, VARDATA(arg2), len2);
+	cmp = varstr_cmp(VARDATA_ANY(arg1), len1, VARDATA_ANY(arg2), len2);
 
 	PG_FREE_IF_COPY(arg1, 0);
 	PG_FREE_IF_COPY(arg2, 1);
@@ -755,8 +763,8 @@ bpcharlt(PG_FUNCTION_ARGS)
 Datum
 bpcharle(PG_FUNCTION_ARGS)
 {
-	BpChar	   *arg1 = PG_GETARG_BPCHAR_P(0);
-	BpChar	   *arg2 = PG_GETARG_BPCHAR_P(1);
+	BpChar	   *arg1 = PG_GETARG_BPCHAR_PP(0);
+	BpChar	   *arg2 = PG_GETARG_BPCHAR_PP(1);
 	int			len1,
 				len2;
 	int			cmp;
@@ -764,7 +772,7 @@ bpcharle(PG_FUNCTION_ARGS)
 	len1 = bcTruelen(arg1);
 	len2 = bcTruelen(arg2);
 
-	cmp = varstr_cmp(VARDATA(arg1), len1, VARDATA(arg2), len2);
+	cmp = varstr_cmp(VARDATA_ANY(arg1), len1, VARDATA_ANY(arg2), len2);
 
 	PG_FREE_IF_COPY(arg1, 0);
 	PG_FREE_IF_COPY(arg2, 1);
@@ -775,8 +783,8 @@ bpcharle(PG_FUNCTION_ARGS)
 Datum
 bpchargt(PG_FUNCTION_ARGS)
 {
-	BpChar	   *arg1 = PG_GETARG_BPCHAR_P(0);
-	BpChar	   *arg2 = PG_GETARG_BPCHAR_P(1);
+	BpChar	   *arg1 = PG_GETARG_BPCHAR_PP(0);
+	BpChar	   *arg2 = PG_GETARG_BPCHAR_PP(1);
 	int			len1,
 				len2;
 	int			cmp;
@@ -784,7 +792,7 @@ bpchargt(PG_FUNCTION_ARGS)
 	len1 = bcTruelen(arg1);
 	len2 = bcTruelen(arg2);
 
-	cmp = varstr_cmp(VARDATA(arg1), len1, VARDATA(arg2), len2);
+	cmp = varstr_cmp(VARDATA_ANY(arg1), len1, VARDATA_ANY(arg2), len2);
 
 	PG_FREE_IF_COPY(arg1, 0);
 	PG_FREE_IF_COPY(arg2, 1);
@@ -795,8 +803,8 @@ bpchargt(PG_FUNCTION_ARGS)
 Datum
 bpcharge(PG_FUNCTION_ARGS)
 {
-	BpChar	   *arg1 = PG_GETARG_BPCHAR_P(0);
-	BpChar	   *arg2 = PG_GETARG_BPCHAR_P(1);
+	BpChar	   *arg1 = PG_GETARG_BPCHAR_PP(0);
+	BpChar	   *arg2 = PG_GETARG_BPCHAR_PP(1);
 	int			len1,
 				len2;
 	int			cmp;
@@ -804,7 +812,7 @@ bpcharge(PG_FUNCTION_ARGS)
 	len1 = bcTruelen(arg1);
 	len2 = bcTruelen(arg2);
 
-	cmp = varstr_cmp(VARDATA(arg1), len1, VARDATA(arg2), len2);
+	cmp = varstr_cmp(VARDATA_ANY(arg1), len1, VARDATA_ANY(arg2), len2);
 
 	PG_FREE_IF_COPY(arg1, 0);
 	PG_FREE_IF_COPY(arg2, 1);
@@ -815,8 +823,8 @@ bpcharge(PG_FUNCTION_ARGS)
 Datum
 bpcharcmp(PG_FUNCTION_ARGS)
 {
-	BpChar	   *arg1 = PG_GETARG_BPCHAR_P(0);
-	BpChar	   *arg2 = PG_GETARG_BPCHAR_P(1);
+	BpChar	   *arg1 = PG_GETARG_BPCHAR_PP(0);
+	BpChar	   *arg2 = PG_GETARG_BPCHAR_PP(1);
 	int			len1,
 				len2;
 	int			cmp;
@@ -824,7 +832,7 @@ bpcharcmp(PG_FUNCTION_ARGS)
 	len1 = bcTruelen(arg1);
 	len2 = bcTruelen(arg2);
 
-	cmp = varstr_cmp(VARDATA(arg1), len1, VARDATA(arg2), len2);
+	cmp = varstr_cmp(VARDATA_ANY(arg1), len1, VARDATA_ANY(arg2), len2);
 
 	PG_FREE_IF_COPY(arg1, 0);
 	PG_FREE_IF_COPY(arg2, 1);
@@ -835,8 +843,8 @@ bpcharcmp(PG_FUNCTION_ARGS)
 Datum
 bpchar_larger(PG_FUNCTION_ARGS)
 {
-	BpChar	   *arg1 = PG_GETARG_BPCHAR_P(0);
-	BpChar	   *arg2 = PG_GETARG_BPCHAR_P(1);
+	BpChar	   *arg1 = PG_GETARG_BPCHAR_PP(0);
+	BpChar	   *arg2 = PG_GETARG_BPCHAR_PP(1);
 	int			len1,
 				len2;
 	int			cmp;
@@ -844,7 +852,7 @@ bpchar_larger(PG_FUNCTION_ARGS)
 	len1 = bcTruelen(arg1);
 	len2 = bcTruelen(arg2);
 
-	cmp = varstr_cmp(VARDATA(arg1), len1, VARDATA(arg2), len2);
+	cmp = varstr_cmp(VARDATA_ANY(arg1), len1, VARDATA_ANY(arg2), len2);
 
 	PG_RETURN_BPCHAR_P((cmp >= 0) ? arg1 : arg2);
 }
@@ -852,8 +860,8 @@ bpchar_larger(PG_FUNCTION_ARGS)
 Datum
 bpchar_smaller(PG_FUNCTION_ARGS)
 {
-	BpChar	   *arg1 = PG_GETARG_BPCHAR_P(0);
-	BpChar	   *arg2 = PG_GETARG_BPCHAR_P(1);
+	BpChar	   *arg1 = PG_GETARG_BPCHAR_PP(0);
+	BpChar	   *arg2 = PG_GETARG_BPCHAR_PP(1);
 	int			len1,
 				len2;
 	int			cmp;
@@ -861,7 +869,7 @@ bpchar_smaller(PG_FUNCTION_ARGS)
 	len1 = bcTruelen(arg1);
 	len2 = bcTruelen(arg2);
 
-	cmp = varstr_cmp(VARDATA(arg1), len1, VARDATA(arg2), len2);
+	cmp = varstr_cmp(VARDATA_ANY(arg1), len1, VARDATA_ANY(arg2), len2);
 
 	PG_RETURN_BPCHAR_P((cmp <= 0) ? arg1 : arg2);
 }
@@ -878,12 +886,12 @@ bpchar_smaller(PG_FUNCTION_ARGS)
 Datum
 hashbpchar(PG_FUNCTION_ARGS)
 {
-	BpChar	   *key = PG_GETARG_BPCHAR_P(0);
+	BpChar	   *key = PG_GETARG_BPCHAR_PP(0);
 	char	   *keydata;
 	int			keylen;
 	Datum		result;
 
-	keydata = VARDATA(key);
+	keydata = VARDATA_ANY(key);
 	keylen = bcTruelen(key);
 
 	result = hash_any((unsigned char *) keydata, keylen);
diff --git a/src/backend/utils/adt/varlena.c b/src/backend/utils/adt/varlena.c
index 51130efd79b2bc9a935a75d901ba06fe3a7a25d1..0b5b4fd16a402e92ccf6fd6b814327186884d7cf 100644
--- a/src/backend/utils/adt/varlena.c
+++ b/src/backend/utils/adt/varlena.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/utils/adt/varlena.c,v 1.155 2007/02/27 23:48:09 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/utils/adt/varlena.c,v 1.156 2007/04/06 04:21:43 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -175,7 +175,7 @@ byteain(PG_FUNCTION_ARGS)
 Datum
 byteaout(PG_FUNCTION_ARGS)
 {
-	bytea	   *vlena = PG_GETARG_BYTEA_P(0);
+	bytea	   *vlena = PG_GETARG_BYTEA_PP(0);
 	char	   *result;
 	char	   *vp;
 	char	   *rp;
@@ -184,8 +184,8 @@ byteaout(PG_FUNCTION_ARGS)
 	int			len;
 
 	len = 1;					/* empty string has 1 char */
-	vp = VARDATA(vlena);
-	for (i = VARSIZE(vlena) - VARHDRSZ; i != 0; i--, vp++)
+	vp = VARDATA_ANY(vlena);
+	for (i = VARSIZE_ANY_EXHDR(vlena); i != 0; i--, vp++)
 	{
 		if (*vp == '\\')
 			len += 2;
@@ -195,8 +195,8 @@ byteaout(PG_FUNCTION_ARGS)
 			len++;
 	}
 	rp = result = (char *) palloc(len);
-	vp = VARDATA(vlena);
-	for (i = VARSIZE(vlena) - VARHDRSZ; i != 0; i--, vp++)
+	vp = VARDATA_ANY(vlena);
+	for (i = VARSIZE_ANY_EXHDR(vlena); i != 0; i--, vp++)
 	{
 		if (*vp == '\\')
 		{
@@ -277,13 +277,13 @@ textin(PG_FUNCTION_ARGS)
 Datum
 textout(PG_FUNCTION_ARGS)
 {
-	text	   *t = PG_GETARG_TEXT_P(0);
+	text	   *t = PG_GETARG_TEXT_PP(0);
 	int			len;
 	char	   *result;
 
-	len = VARSIZE(t) - VARHDRSZ;
+	len = VARSIZE_ANY_EXHDR(t);
 	result = (char *) palloc(len + 1);
-	memcpy(result, VARDATA(t), len);
+	memcpy(result, VARDATA_ANY(t), len);
 	result[len] = '\0';
 
 	PG_RETURN_CSTRING(result);
@@ -315,11 +315,11 @@ textrecv(PG_FUNCTION_ARGS)
 Datum
 textsend(PG_FUNCTION_ARGS)
 {
-	text	   *t = PG_GETARG_TEXT_P(0);
+	text	   *t = PG_GETARG_TEXT_PP(0);
 	StringInfoData buf;
 
 	pq_begintypsend(&buf);
-	pq_sendtext(&buf, VARDATA(t), VARSIZE(t) - VARHDRSZ);
+	pq_sendtext(&buf, VARDATA_ANY(t), VARSIZE_ANY_EXHDR(t));
 	PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
 }
 
@@ -412,10 +412,10 @@ text_length(Datum str)
 		PG_RETURN_INT32(toast_raw_datum_size(str) - VARHDRSZ);
 	else
 	{
-		text	   *t = DatumGetTextP(str);
+		text	   *t = DatumGetTextPP(str);
 
-		PG_RETURN_INT32(pg_mbstrlen_with_len(VARDATA(t),
-											 VARSIZE(t) - VARHDRSZ));
+		PG_RETURN_INT32(pg_mbstrlen_with_len(VARDATA_ANY(t), 
+											 VARSIZE_ANY_EXHDR(t)));
 	}
 }
 
@@ -446,19 +446,19 @@ textoctetlen(PG_FUNCTION_ARGS)
 Datum
 textcat(PG_FUNCTION_ARGS)
 {
-	text	   *t1 = PG_GETARG_TEXT_P(0);
-	text	   *t2 = PG_GETARG_TEXT_P(1);
+	text	   *t1 = PG_GETARG_TEXT_PP(0);
+	text	   *t2 = PG_GETARG_TEXT_PP(1);
 	int			len1,
 				len2,
 				len;
 	text	   *result;
 	char	   *ptr;
 
-	len1 = VARSIZE(t1) - VARHDRSZ;
+	len1 = VARSIZE_ANY_EXHDR(t1);
 	if (len1 < 0)
 		len1 = 0;
 
-	len2 = VARSIZE(t2) - VARHDRSZ;
+	len2 = VARSIZE_ANY_EXHDR(t2);
 	if (len2 < 0)
 		len2 = 0;
 
@@ -471,9 +471,9 @@ textcat(PG_FUNCTION_ARGS)
 	/* Fill data field of result string... */
 	ptr = VARDATA(result);
 	if (len1 > 0)
-		memcpy(ptr, VARDATA(t1), len1);
+		memcpy(ptr, VARDATA_ANY(t1), len1);
 	if (len2 > 0)
-		memcpy(ptr + len1, VARDATA(t2), len2);
+		memcpy(ptr + len1, VARDATA_ANY(t2), len2);
 
 	PG_RETURN_TEXT_P(result);
 }
@@ -1058,12 +1058,12 @@ text_cmp(text *arg1, text *arg2)
 	int			len1,
 				len2;
 
-	a1p = VARDATA(arg1);
-	a2p = VARDATA(arg2);
-
-	len1 = VARSIZE(arg1) - VARHDRSZ;
-	len2 = VARSIZE(arg2) - VARHDRSZ;
+	a1p = VARDATA_ANY(arg1);
+	a2p = VARDATA_ANY(arg2);
 
+	len1 = VARSIZE_ANY_EXHDR(arg1);
+	len2 = VARSIZE_ANY_EXHDR(arg2);
+	
 	return varstr_cmp(a1p, len1, a2p, len2);
 }
 
@@ -1078,19 +1078,19 @@ text_cmp(text *arg1, text *arg2)
 Datum
 texteq(PG_FUNCTION_ARGS)
 {
-	text	   *arg1 = PG_GETARG_TEXT_P(0);
-	text	   *arg2 = PG_GETARG_TEXT_P(1);
+	text	   *arg1 = PG_GETARG_TEXT_PP(0);
+	text	   *arg2 = PG_GETARG_TEXT_PP(1);
 	bool		result;
 
 	/*
 	 * Since we only care about equality or not-equality, we can avoid all the
 	 * expense of strcoll() here, and just do bitwise comparison.
 	 */
-	if (VARSIZE(arg1) != VARSIZE(arg2))
+	if (VARSIZE_ANY_EXHDR(arg1) != VARSIZE_ANY_EXHDR(arg2))
 		result = false;
 	else
-		result = (strncmp(VARDATA(arg1), VARDATA(arg2),
-						  VARSIZE(arg1) - VARHDRSZ) == 0);
+		result = (strncmp(VARDATA_ANY(arg1), VARDATA_ANY(arg2),
+						  VARSIZE_ANY_EXHDR(arg1)) == 0);
 
 	PG_FREE_IF_COPY(arg1, 0);
 	PG_FREE_IF_COPY(arg2, 1);
@@ -1101,19 +1101,19 @@ texteq(PG_FUNCTION_ARGS)
 Datum
 textne(PG_FUNCTION_ARGS)
 {
-	text	   *arg1 = PG_GETARG_TEXT_P(0);
-	text	   *arg2 = PG_GETARG_TEXT_P(1);
+	text	   *arg1 = PG_GETARG_TEXT_PP(0);
+	text	   *arg2 = PG_GETARG_TEXT_PP(1);
 	bool		result;
 
 	/*
 	 * Since we only care about equality or not-equality, we can avoid all the
 	 * expense of strcoll() here, and just do bitwise comparison.
 	 */
-	if (VARSIZE(arg1) != VARSIZE(arg2))
+	if (VARSIZE_ANY_EXHDR(arg1) != VARSIZE_ANY_EXHDR(arg2))
 		result = true;
 	else
-		result = (strncmp(VARDATA(arg1), VARDATA(arg2),
-						  VARSIZE(arg1) - VARHDRSZ) != 0);
+		result = (strncmp(VARDATA_ANY(arg1), VARDATA_ANY(arg2),
+						  VARSIZE_ANY_EXHDR(arg1)) != 0);
 
 	PG_FREE_IF_COPY(arg1, 0);
 	PG_FREE_IF_COPY(arg2, 1);
@@ -1124,8 +1124,8 @@ textne(PG_FUNCTION_ARGS)
 Datum
 text_lt(PG_FUNCTION_ARGS)
 {
-	text	   *arg1 = PG_GETARG_TEXT_P(0);
-	text	   *arg2 = PG_GETARG_TEXT_P(1);
+	text	   *arg1 = PG_GETARG_TEXT_PP(0);
+	text	   *arg2 = PG_GETARG_TEXT_PP(1);
 	bool		result;
 
 	result = (text_cmp(arg1, arg2) < 0);
@@ -1139,8 +1139,8 @@ text_lt(PG_FUNCTION_ARGS)
 Datum
 text_le(PG_FUNCTION_ARGS)
 {
-	text	   *arg1 = PG_GETARG_TEXT_P(0);
-	text	   *arg2 = PG_GETARG_TEXT_P(1);
+	text	   *arg1 = PG_GETARG_TEXT_PP(0);
+	text	   *arg2 = PG_GETARG_TEXT_PP(1);
 	bool		result;
 
 	result = (text_cmp(arg1, arg2) <= 0);
@@ -1154,8 +1154,8 @@ text_le(PG_FUNCTION_ARGS)
 Datum
 text_gt(PG_FUNCTION_ARGS)
 {
-	text	   *arg1 = PG_GETARG_TEXT_P(0);
-	text	   *arg2 = PG_GETARG_TEXT_P(1);
+	text	   *arg1 = PG_GETARG_TEXT_PP(0);
+	text	   *arg2 = PG_GETARG_TEXT_PP(1);
 	bool		result;
 
 	result = (text_cmp(arg1, arg2) > 0);
@@ -1169,8 +1169,8 @@ text_gt(PG_FUNCTION_ARGS)
 Datum
 text_ge(PG_FUNCTION_ARGS)
 {
-	text	   *arg1 = PG_GETARG_TEXT_P(0);
-	text	   *arg2 = PG_GETARG_TEXT_P(1);
+	text	   *arg1 = PG_GETARG_TEXT_PP(0);
+	text	   *arg2 = PG_GETARG_TEXT_PP(1);
 	bool		result;
 
 	result = (text_cmp(arg1, arg2) >= 0);
@@ -1184,8 +1184,8 @@ text_ge(PG_FUNCTION_ARGS)
 Datum
 bttextcmp(PG_FUNCTION_ARGS)
 {
-	text	   *arg1 = PG_GETARG_TEXT_P(0);
-	text	   *arg2 = PG_GETARG_TEXT_P(1);
+	text	   *arg1 = PG_GETARG_TEXT_PP(0);
+	text	   *arg2 = PG_GETARG_TEXT_PP(1);
 	int32		result;
 
 	result = text_cmp(arg1, arg2);
@@ -1200,8 +1200,8 @@ bttextcmp(PG_FUNCTION_ARGS)
 Datum
 text_larger(PG_FUNCTION_ARGS)
 {
-	text	   *arg1 = PG_GETARG_TEXT_P(0);
-	text	   *arg2 = PG_GETARG_TEXT_P(1);
+	text	   *arg1 = PG_GETARG_TEXT_PP(0);
+	text	   *arg2 = PG_GETARG_TEXT_PP(1);
 	text	   *result;
 
 	result = ((text_cmp(arg1, arg2) > 0) ? arg1 : arg2);
@@ -1212,8 +1212,8 @@ text_larger(PG_FUNCTION_ARGS)
 Datum
 text_smaller(PG_FUNCTION_ARGS)
 {
-	text	   *arg1 = PG_GETARG_TEXT_P(0);
-	text	   *arg2 = PG_GETARG_TEXT_P(1);
+	text	   *arg1 = PG_GETARG_TEXT_PP(0);
+	text	   *arg2 = PG_GETARG_TEXT_PP(1);
 	text	   *result;
 
 	result = ((text_cmp(arg1, arg2) < 0) ? arg1 : arg2);
@@ -1233,13 +1233,13 @@ internal_text_pattern_compare(text *arg1, text *arg2)
 {
 	int			result;
 
-	result = memcmp(VARDATA(arg1), VARDATA(arg2),
-					Min(VARSIZE(arg1), VARSIZE(arg2)) - VARHDRSZ);
+	result = memcmp(VARDATA_ANY(arg1), VARDATA_ANY(arg2),
+					Min(VARSIZE_ANY_EXHDR(arg1), VARSIZE_ANY_EXHDR(arg2)));
 	if (result != 0)
 		return result;
-	else if (VARSIZE(arg1) < VARSIZE(arg2))
+	else if (VARSIZE_ANY_EXHDR(arg1) < VARSIZE_ANY_EXHDR(arg2))
 		return -1;
-	else if (VARSIZE(arg1) > VARSIZE(arg2))
+	else if (VARSIZE_ANY_EXHDR(arg1) > VARSIZE_ANY_EXHDR(arg2))
 		return 1;
 	else
 		return 0;
@@ -1249,8 +1249,8 @@ internal_text_pattern_compare(text *arg1, text *arg2)
 Datum
 text_pattern_lt(PG_FUNCTION_ARGS)
 {
-	text	   *arg1 = PG_GETARG_TEXT_P(0);
-	text	   *arg2 = PG_GETARG_TEXT_P(1);
+	text	   *arg1 = PG_GETARG_TEXT_PP(0);
+	text	   *arg2 = PG_GETARG_TEXT_PP(1);
 	int			result;
 
 	result = internal_text_pattern_compare(arg1, arg2);
@@ -1265,8 +1265,8 @@ text_pattern_lt(PG_FUNCTION_ARGS)
 Datum
 text_pattern_le(PG_FUNCTION_ARGS)
 {
-	text	   *arg1 = PG_GETARG_TEXT_P(0);
-	text	   *arg2 = PG_GETARG_TEXT_P(1);
+	text	   *arg1 = PG_GETARG_TEXT_PP(0);
+	text	   *arg2 = PG_GETARG_TEXT_PP(1);
 	int			result;
 
 	result = internal_text_pattern_compare(arg1, arg2);
@@ -1281,11 +1281,11 @@ text_pattern_le(PG_FUNCTION_ARGS)
 Datum
 text_pattern_eq(PG_FUNCTION_ARGS)
 {
-	text	   *arg1 = PG_GETARG_TEXT_P(0);
-	text	   *arg2 = PG_GETARG_TEXT_P(1);
+	text	   *arg1 = PG_GETARG_TEXT_PP(0);
+	text	   *arg2 = PG_GETARG_TEXT_PP(1);
 	int			result;
 
-	if (VARSIZE(arg1) != VARSIZE(arg2))
+	if (VARSIZE_ANY_EXHDR(arg1) != VARSIZE_ANY_EXHDR(arg2))
 		result = 1;
 	else
 		result = internal_text_pattern_compare(arg1, arg2);
@@ -1300,8 +1300,8 @@ text_pattern_eq(PG_FUNCTION_ARGS)
 Datum
 text_pattern_ge(PG_FUNCTION_ARGS)
 {
-	text	   *arg1 = PG_GETARG_TEXT_P(0);
-	text	   *arg2 = PG_GETARG_TEXT_P(1);
+	text	   *arg1 = PG_GETARG_TEXT_PP(0);
+	text	   *arg2 = PG_GETARG_TEXT_PP(1);
 	int			result;
 
 	result = internal_text_pattern_compare(arg1, arg2);
@@ -1316,8 +1316,8 @@ text_pattern_ge(PG_FUNCTION_ARGS)
 Datum
 text_pattern_gt(PG_FUNCTION_ARGS)
 {
-	text	   *arg1 = PG_GETARG_TEXT_P(0);
-	text	   *arg2 = PG_GETARG_TEXT_P(1);
+	text	   *arg1 = PG_GETARG_TEXT_PP(0);
+	text	   *arg2 = PG_GETARG_TEXT_PP(1);
 	int			result;
 
 	result = internal_text_pattern_compare(arg1, arg2);
@@ -1332,11 +1332,11 @@ text_pattern_gt(PG_FUNCTION_ARGS)
 Datum
 text_pattern_ne(PG_FUNCTION_ARGS)
 {
-	text	   *arg1 = PG_GETARG_TEXT_P(0);
-	text	   *arg2 = PG_GETARG_TEXT_P(1);
+	text	   *arg1 = PG_GETARG_TEXT_PP(0);
+	text	   *arg2 = PG_GETARG_TEXT_PP(1);
 	int			result;
 
-	if (VARSIZE(arg1) != VARSIZE(arg2))
+	if (VARSIZE_ANY_EXHDR(arg1) != VARSIZE_ANY_EXHDR(arg2))
 		result = 1;
 	else
 		result = internal_text_pattern_compare(arg1, arg2);
@@ -1351,8 +1351,8 @@ text_pattern_ne(PG_FUNCTION_ARGS)
 Datum
 bttext_pattern_cmp(PG_FUNCTION_ARGS)
 {
-	text	   *arg1 = PG_GETARG_TEXT_P(0);
-	text	   *arg2 = PG_GETARG_TEXT_P(1);
+	text	   *arg1 = PG_GETARG_TEXT_PP(0);
+	text	   *arg2 = PG_GETARG_TEXT_PP(1);
 	int			result;
 
 	result = internal_text_pattern_compare(arg1, arg2);
@@ -1389,19 +1389,19 @@ byteaoctetlen(PG_FUNCTION_ARGS)
 Datum
 byteacat(PG_FUNCTION_ARGS)
 {
-	bytea	   *t1 = PG_GETARG_BYTEA_P(0);
-	bytea	   *t2 = PG_GETARG_BYTEA_P(1);
+	bytea	   *t1 = PG_GETARG_BYTEA_PP(0);
+	bytea	   *t2 = PG_GETARG_BYTEA_PP(1);
 	int			len1,
 				len2,
 				len;
 	bytea	   *result;
 	char	   *ptr;
 
-	len1 = VARSIZE(t1) - VARHDRSZ;
+	len1 = VARSIZE_ANY_EXHDR(t1);
 	if (len1 < 0)
 		len1 = 0;
 
-	len2 = VARSIZE(t2) - VARHDRSZ;
+	len2 = VARSIZE_ANY_EXHDR(t2);
 	if (len2 < 0)
 		len2 = 0;
 
@@ -1414,9 +1414,9 @@ byteacat(PG_FUNCTION_ARGS)
 	/* Fill data field of result string... */
 	ptr = VARDATA(result);
 	if (len1 > 0)
-		memcpy(ptr, VARDATA(t1), len1);
+		memcpy(ptr, VARDATA_ANY(t1), len1);
 	if (len2 > 0)
-		memcpy(ptr + len1, VARDATA(t2), len2);
+		memcpy(ptr + len1, VARDATA_ANY(t2), len2);
 
 	PG_RETURN_BYTEA_P(result);
 }
@@ -1509,8 +1509,8 @@ bytea_substr_no_len(PG_FUNCTION_ARGS)
 Datum
 byteapos(PG_FUNCTION_ARGS)
 {
-	bytea	   *t1 = PG_GETARG_BYTEA_P(0);
-	bytea	   *t2 = PG_GETARG_BYTEA_P(1);
+	bytea	   *t1 = PG_GETARG_BYTEA_PP(0);
+	bytea	   *t2 = PG_GETARG_BYTEA_PP(1);
 	int			pos;
 	int			px,
 				p;
@@ -1519,14 +1519,14 @@ byteapos(PG_FUNCTION_ARGS)
 	char	   *p1,
 			   *p2;
 
-	if (VARSIZE(t2) <= VARHDRSZ)
-		PG_RETURN_INT32(1);		/* result for empty pattern */
+	len1 = VARSIZE_ANY_EXHDR(t1);
+	len2 = VARSIZE_ANY_EXHDR(t2);
 
-	len1 = VARSIZE(t1) - VARHDRSZ;
-	len2 = VARSIZE(t2) - VARHDRSZ;
+	if (len2 <= 0)
+		PG_RETURN_INT32(1);		/* result for empty pattern */
 
-	p1 = VARDATA(t1);
-	p2 = VARDATA(t2);
+	p1 = VARDATA_ANY(t1);
+	p2 = VARDATA_ANY(t2);
 
 	pos = 0;
 	px = (len1 - len2);
@@ -1553,12 +1553,12 @@ byteapos(PG_FUNCTION_ARGS)
 Datum
 byteaGetByte(PG_FUNCTION_ARGS)
 {
-	bytea	   *v = PG_GETARG_BYTEA_P(0);
+	bytea	   *v = PG_GETARG_BYTEA_PP(0);
 	int32		n = PG_GETARG_INT32(1);
 	int			len;
 	int			byte;
 
-	len = VARSIZE(v) - VARHDRSZ;
+	len = VARSIZE_ANY_EXHDR(v);
 
 	if (n < 0 || n >= len)
 		ereport(ERROR,
@@ -1566,7 +1566,7 @@ byteaGetByte(PG_FUNCTION_ARGS)
 				 errmsg("index %d out of valid range, 0..%d",
 						n, len - 1)));
 
-	byte = ((unsigned char *) VARDATA(v))[n];
+	byte = ((unsigned char *) VARDATA_ANY(v))[n];
 
 	PG_RETURN_INT32(byte);
 }
@@ -1582,14 +1582,14 @@ byteaGetByte(PG_FUNCTION_ARGS)
 Datum
 byteaGetBit(PG_FUNCTION_ARGS)
 {
-	bytea	   *v = PG_GETARG_BYTEA_P(0);
+	bytea	   *v = PG_GETARG_BYTEA_PP(0);
 	int32		n = PG_GETARG_INT32(1);
 	int			byteNo,
 				bitNo;
 	int			len;
 	int			byte;
 
-	len = VARSIZE(v) - VARHDRSZ;
+	len = VARSIZE_ANY_EXHDR(v);
 
 	if (n < 0 || n >= len * 8)
 		ereport(ERROR,
@@ -1600,7 +1600,7 @@ byteaGetBit(PG_FUNCTION_ARGS)
 	byteNo = n / 8;
 	bitNo = n % 8;
 
-	byte = ((unsigned char *) VARDATA(v))[byteNo];
+	byte = ((unsigned char *) VARDATA_ANY(v))[byteNo];
 
 	if (byte & (1 << bitNo))
 		PG_RETURN_INT32(1);
@@ -1715,23 +1715,18 @@ byteaSetBit(PG_FUNCTION_ARGS)
 Datum
 text_name(PG_FUNCTION_ARGS)
 {
-	text	   *s = PG_GETARG_TEXT_P(0);
+	text	   *s = PG_GETARG_TEXT_PP(0);
 	Name		result;
 	int			len;
 
-	len = VARSIZE(s) - VARHDRSZ;
+	len = VARSIZE_ANY_EXHDR(s);
 
 	/* Truncate oversize input */
 	if (len >= NAMEDATALEN)
 		len = NAMEDATALEN - 1;
 
-#ifdef STRINGDEBUG
-	printf("text- convert string length %d (%d) ->%d\n",
-		   VARSIZE(s) - VARHDRSZ, VARSIZE(s), len);
-#endif
-
 	result = (Name) palloc(NAMEDATALEN);
-	memcpy(NameStr(*result), VARDATA(s), len);
+	memcpy(NameStr(*result), VARDATA_ANY(s), len);
 
 	/* now null pad to full length... */
 	while (len < NAMEDATALEN)
@@ -1755,11 +1750,6 @@ name_text(PG_FUNCTION_ARGS)
 
 	len = strlen(NameStr(*s));
 
-#ifdef STRINGDEBUG
-	printf("text- convert string length %d (%d) ->%d\n",
-		   VARSIZE(s) - VARHDRSZ, VARSIZE(s), len);
-#endif
-
 	result = palloc(VARHDRSZ + len);
 	SET_VARSIZE(result, VARHDRSZ + len);
 	memcpy(VARDATA(result), NameStr(*s), len);
@@ -1948,20 +1938,20 @@ SplitIdentifierString(char *rawstring, char separator,
 Datum
 byteaeq(PG_FUNCTION_ARGS)
 {
-	bytea	   *arg1 = PG_GETARG_BYTEA_P(0);
-	bytea	   *arg2 = PG_GETARG_BYTEA_P(1);
+	bytea	   *arg1 = PG_GETARG_BYTEA_PP(0);
+	bytea	   *arg2 = PG_GETARG_BYTEA_PP(1);
 	int			len1,
 				len2;
 	bool		result;
 
-	len1 = VARSIZE(arg1) - VARHDRSZ;
-	len2 = VARSIZE(arg2) - VARHDRSZ;
+	len1 = VARSIZE_ANY_EXHDR(arg1);
+	len2 = VARSIZE_ANY_EXHDR(arg2);
 
 	/* fast path for different-length inputs */
 	if (len1 != len2)
 		result = false;
 	else
-		result = (memcmp(VARDATA(arg1), VARDATA(arg2), len1) == 0);
+		result = (memcmp(VARDATA_ANY(arg1), VARDATA_ANY(arg2), len1) == 0);
 
 	PG_FREE_IF_COPY(arg1, 0);
 	PG_FREE_IF_COPY(arg2, 1);
@@ -1972,20 +1962,20 @@ byteaeq(PG_FUNCTION_ARGS)
 Datum
 byteane(PG_FUNCTION_ARGS)
 {
-	bytea	   *arg1 = PG_GETARG_BYTEA_P(0);
-	bytea	   *arg2 = PG_GETARG_BYTEA_P(1);
+	bytea	   *arg1 = PG_GETARG_BYTEA_PP(0);
+	bytea	   *arg2 = PG_GETARG_BYTEA_PP(1);
 	int			len1,
 				len2;
 	bool		result;
 
-	len1 = VARSIZE(arg1) - VARHDRSZ;
-	len2 = VARSIZE(arg2) - VARHDRSZ;
+	len1 = VARSIZE_ANY_EXHDR(arg1);
+	len2 = VARSIZE_ANY_EXHDR(arg2);
 
 	/* fast path for different-length inputs */
 	if (len1 != len2)
 		result = true;
 	else
-		result = (memcmp(VARDATA(arg1), VARDATA(arg2), len1) != 0);
+		result = (memcmp(VARDATA_ANY(arg1), VARDATA_ANY(arg2), len1) != 0);
 
 	PG_FREE_IF_COPY(arg1, 0);
 	PG_FREE_IF_COPY(arg2, 1);
@@ -1996,16 +1986,16 @@ byteane(PG_FUNCTION_ARGS)
 Datum
 bytealt(PG_FUNCTION_ARGS)
 {
-	bytea	   *arg1 = PG_GETARG_BYTEA_P(0);
-	bytea	   *arg2 = PG_GETARG_BYTEA_P(1);
+	bytea	   *arg1 = PG_GETARG_BYTEA_PP(0);
+	bytea	   *arg2 = PG_GETARG_BYTEA_PP(1);
 	int			len1,
 				len2;
 	int			cmp;
 
-	len1 = VARSIZE(arg1) - VARHDRSZ;
-	len2 = VARSIZE(arg2) - VARHDRSZ;
+	len1 = VARSIZE_ANY_EXHDR(arg1);
+	len2 = VARSIZE_ANY_EXHDR(arg2);
 
-	cmp = memcmp(VARDATA(arg1), VARDATA(arg2), Min(len1, len2));
+	cmp = memcmp(VARDATA_ANY(arg1), VARDATA_ANY(arg2), Min(len1, len2));
 
 	PG_FREE_IF_COPY(arg1, 0);
 	PG_FREE_IF_COPY(arg2, 1);
@@ -2016,16 +2006,16 @@ bytealt(PG_FUNCTION_ARGS)
 Datum
 byteale(PG_FUNCTION_ARGS)
 {
-	bytea	   *arg1 = PG_GETARG_BYTEA_P(0);
-	bytea	   *arg2 = PG_GETARG_BYTEA_P(1);
+	bytea	   *arg1 = PG_GETARG_BYTEA_PP(0);
+	bytea	   *arg2 = PG_GETARG_BYTEA_PP(1);
 	int			len1,
 				len2;
 	int			cmp;
 
-	len1 = VARSIZE(arg1) - VARHDRSZ;
-	len2 = VARSIZE(arg2) - VARHDRSZ;
+	len1 = VARSIZE_ANY_EXHDR(arg1);
+	len2 = VARSIZE_ANY_EXHDR(arg2);
 
-	cmp = memcmp(VARDATA(arg1), VARDATA(arg2), Min(len1, len2));
+	cmp = memcmp(VARDATA_ANY(arg1), VARDATA_ANY(arg2), Min(len1, len2));
 
 	PG_FREE_IF_COPY(arg1, 0);
 	PG_FREE_IF_COPY(arg2, 1);
@@ -2036,16 +2026,16 @@ byteale(PG_FUNCTION_ARGS)
 Datum
 byteagt(PG_FUNCTION_ARGS)
 {
-	bytea	   *arg1 = PG_GETARG_BYTEA_P(0);
-	bytea	   *arg2 = PG_GETARG_BYTEA_P(1);
+	bytea	   *arg1 = PG_GETARG_BYTEA_PP(0);
+	bytea	   *arg2 = PG_GETARG_BYTEA_PP(1);
 	int			len1,
 				len2;
 	int			cmp;
 
-	len1 = VARSIZE(arg1) - VARHDRSZ;
-	len2 = VARSIZE(arg2) - VARHDRSZ;
+	len1 = VARSIZE_ANY_EXHDR(arg1);
+	len2 = VARSIZE_ANY_EXHDR(arg2);
 
-	cmp = memcmp(VARDATA(arg1), VARDATA(arg2), Min(len1, len2));
+	cmp = memcmp(VARDATA_ANY(arg1), VARDATA_ANY(arg2), Min(len1, len2));
 
 	PG_FREE_IF_COPY(arg1, 0);
 	PG_FREE_IF_COPY(arg2, 1);
@@ -2056,16 +2046,16 @@ byteagt(PG_FUNCTION_ARGS)
 Datum
 byteage(PG_FUNCTION_ARGS)
 {
-	bytea	   *arg1 = PG_GETARG_BYTEA_P(0);
-	bytea	   *arg2 = PG_GETARG_BYTEA_P(1);
+	bytea	   *arg1 = PG_GETARG_BYTEA_PP(0);
+	bytea	   *arg2 = PG_GETARG_BYTEA_PP(1);
 	int			len1,
 				len2;
 	int			cmp;
 
-	len1 = VARSIZE(arg1) - VARHDRSZ;
-	len2 = VARSIZE(arg2) - VARHDRSZ;
+	len1 = VARSIZE_ANY_EXHDR(arg1);
+	len2 = VARSIZE_ANY_EXHDR(arg2);
 
-	cmp = memcmp(VARDATA(arg1), VARDATA(arg2), Min(len1, len2));
+	cmp = memcmp(VARDATA_ANY(arg1), VARDATA_ANY(arg2), Min(len1, len2));
 
 	PG_FREE_IF_COPY(arg1, 0);
 	PG_FREE_IF_COPY(arg2, 1);
@@ -2076,16 +2066,16 @@ byteage(PG_FUNCTION_ARGS)
 Datum
 byteacmp(PG_FUNCTION_ARGS)
 {
-	bytea	   *arg1 = PG_GETARG_BYTEA_P(0);
-	bytea	   *arg2 = PG_GETARG_BYTEA_P(1);
+	bytea	   *arg1 = PG_GETARG_BYTEA_PP(0);
+	bytea	   *arg2 = PG_GETARG_BYTEA_PP(1);
 	int			len1,
 				len2;
 	int			cmp;
 
-	len1 = VARSIZE(arg1) - VARHDRSZ;
-	len2 = VARSIZE(arg2) - VARHDRSZ;
+	len1 = VARSIZE_ANY_EXHDR(arg1);
+	len2 = VARSIZE_ANY_EXHDR(arg2);
 
-	cmp = memcmp(VARDATA(arg1), VARDATA(arg2), Min(len1, len2));
+	cmp = memcmp(VARDATA_ANY(arg1), VARDATA_ANY(arg2), Min(len1, len2));
 	if ((cmp == 0) && (len1 != len2))
 		cmp = (len1 < len2) ? -1 : 1;
 
@@ -2713,8 +2703,8 @@ array_to_text(PG_FUNCTION_ARGS)
 				appendStringInfoString(&buf, value);
 			printed = true;
 
-			p = att_addlength(p, typlen, PointerGetDatum(p));
-			p = (char *) att_align(p, typalign);
+			p = att_addlength_pointer(p, typlen, p);
+			p = (char *) att_align_nominal(p, typalign);
 		}
 
 		/* advance bitmap pointer if any */
@@ -2795,16 +2785,16 @@ to_hex64(PG_FUNCTION_ARGS)
 Datum
 md5_text(PG_FUNCTION_ARGS)
 {
-	text	   *in_text = PG_GETARG_TEXT_P(0);
+	text	   *in_text = PG_GETARG_TEXT_PP(0);
 	size_t		len;
 	char		hexsum[MD5_HASH_LEN + 1];
 	text	   *result_text;
 
 	/* Calculate the length of the buffer using varlena metadata */
-	len = VARSIZE(in_text) - VARHDRSZ;
+	len = VARSIZE_ANY_EXHDR(in_text);
 
 	/* get the hash result */
-	if (pg_md5_hash(VARDATA(in_text), len, hexsum) == false)
+	if (pg_md5_hash(VARDATA_ANY(in_text), len, hexsum) == false)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of memory")));
@@ -2821,13 +2811,13 @@ md5_text(PG_FUNCTION_ARGS)
 Datum
 md5_bytea(PG_FUNCTION_ARGS)
 {
-	bytea	   *in = PG_GETARG_BYTEA_P(0);
+	bytea	   *in = PG_GETARG_BYTEA_PP(0);
 	size_t		len;
 	char		hexsum[MD5_HASH_LEN + 1];
 	text	   *result_text;
 
-	len = VARSIZE(in) - VARHDRSZ;
-	if (pg_md5_hash(VARDATA(in), len, hexsum) == false)
+	len = VARSIZE_ANY_EXHDR(in);
+	if (pg_md5_hash(VARDATA_ANY(in), len, hexsum) == false)
 		ereport(ERROR,
 				(errcode(ERRCODE_OUT_OF_MEMORY),
 				 errmsg("out of memory")));
diff --git a/src/backend/utils/adt/xml.c b/src/backend/utils/adt/xml.c
index f039bba21e06ca1146056e1ba53d9cd2394b32fb..e873264e3a1fa896b539820cb6f3f459585dd040 100644
--- a/src/backend/utils/adt/xml.c
+++ b/src/backend/utils/adt/xml.c
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/backend/utils/adt/xml.c,v 1.41 2007/04/05 13:53:23 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/adt/xml.c,v 1.42 2007/04/06 04:21:43 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -3071,8 +3071,8 @@ xmlpath(PG_FUNCTION_ARGS)
 				else
 					ns_uris[i - ns_count] = DatumGetCString(DirectFunctionCall1(textout,
 														  PointerGetDatum(ptr)));
-				ptr = att_addlength(ptr, typlen, PointerGetDatum(ptr));
-				ptr = (char *) att_align(ptr, typalign);
+				ptr = att_addlength_pointer(ptr, typlen, ptr);
+				ptr = (char *) att_align_nominal(ptr, typalign);
 			}
 	
 			/* advance bitmap pointer if any */
diff --git a/src/backend/utils/fmgr/fmgr.c b/src/backend/utils/fmgr/fmgr.c
index a28acdc2ad8e71d9727b7fe404117831b7320905..5a67a84cf880f32ff6c027add7a3a75ff65abad0 100644
--- a/src/backend/utils/fmgr/fmgr.c
+++ b/src/backend/utils/fmgr/fmgr.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/utils/fmgr/fmgr.c,v 1.105 2007/03/27 23:21:10 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/utils/fmgr/fmgr.c,v 1.106 2007/04/06 04:21:43 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1962,7 +1962,7 @@ struct varlena *
 pg_detoast_datum(struct varlena * datum)
 {
 	if (VARATT_IS_EXTENDED(datum))
-		return (struct varlena *) heap_tuple_untoast_attr((varattrib *) datum);
+		return heap_tuple_untoast_attr(datum);
 	else
 		return datum;
 }
@@ -1971,7 +1971,7 @@ struct varlena *
 pg_detoast_datum_copy(struct varlena * datum)
 {
 	if (VARATT_IS_EXTENDED(datum))
-		return (struct varlena *) heap_tuple_untoast_attr((varattrib *) datum);
+		return heap_tuple_untoast_attr(datum);
 	else
 	{
 		/* Make a modifiable copy of the varlena object */
@@ -1987,7 +1987,16 @@ struct varlena *
 pg_detoast_datum_slice(struct varlena * datum, int32 first, int32 count)
 {
 	/* Only get the specified portion from the toast rel */
-	return (struct varlena *) heap_tuple_untoast_attr_slice((varattrib *) datum, first, count);
+	return heap_tuple_untoast_attr_slice(datum, first, count);
+}
+
+struct varlena *
+pg_detoast_datum_packed(struct varlena * datum)
+{
+	if (VARATT_IS_COMPRESSED(datum) || VARATT_IS_EXTERNAL(datum))
+		return heap_tuple_untoast_attr(datum);
+	else
+		return datum;
 }
 
 /*-------------------------------------------------------------------------
diff --git a/src/backend/utils/init/flatfiles.c b/src/backend/utils/init/flatfiles.c
index 1699979c3d09b7d42093492d27fb3ab8d41bcbf6..e388d1edfdc0e6a532775a2a86c7f4a5f8b6f861 100644
--- a/src/backend/utils/init/flatfiles.c
+++ b/src/backend/utils/init/flatfiles.c
@@ -23,7 +23,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/backend/utils/init/flatfiles.c,v 1.24 2007/01/05 22:19:43 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/init/flatfiles.c,v 1.25 2007/04/06 04:21:43 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -467,7 +467,7 @@ write_auth_file(Relation rel_authid, Relation rel_authmem)
 				auth_info[curr_role].rolpassword = DatumGetCString(DirectFunctionCall1(textout, datum));
 
 			/* assume passwd has attlen -1 */
-			off = att_addlength(off, -1, tp + off);
+			off = att_addlength_pointer(off, -1, tp + off);
 		}
 
 		if (HeapTupleHasNulls(tuple) &&
@@ -482,7 +482,7 @@ write_auth_file(Relation rel_authid, Relation rel_authmem)
 			 * rolvaliduntil is timestamptz, which we assume is double
 			 * alignment and pass-by-reference.
 			 */
-			off = att_align(off, 'd');
+			off = att_align_nominal(off, 'd');
 			datum = PointerGetDatum(tp + off);
 			auth_info[curr_role].rolvaliduntil = DatumGetCString(DirectFunctionCall1(timestamptz_out, datum));
 		}
diff --git a/src/include/access/heapam.h b/src/include/access/heapam.h
index 6c7c98b3f286719f1a024d709b56432354b85741..1fbb713b55d064972cfb5b68312d57be6d0b63f5 100644
--- a/src/include/access/heapam.h
+++ b/src/include/access/heapam.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/access/heapam.h,v 1.121 2007/03/29 00:15:39 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/access/heapam.h,v 1.122 2007/04/06 04:21:43 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -200,7 +200,8 @@ extern Size heap_compute_data_size(TupleDesc tupleDesc,
 					   Datum *values, bool *isnull);
 extern void heap_fill_tuple(TupleDesc tupleDesc,
 				Datum *values, bool *isnull,
-				char *data, uint16 *infomask, bits8 *bit);
+				char *data, Size data_size,
+				uint16 *infomask, bits8 *bit);
 extern bool heap_attisnull(HeapTuple tup, int attnum);
 extern Datum nocachegetattr(HeapTuple tup, int attnum,
 			   TupleDesc att, bool *isnull);
diff --git a/src/include/access/htup.h b/src/include/access/htup.h
index c37501d9109f0b59a89c13748dc8ee6d66cf0ff0..ee816c568a8aee6ae6437671a947b7f3b3b006da 100644
--- a/src/include/access/htup.h
+++ b/src/include/access/htup.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/access/htup.h,v 1.92 2007/02/27 23:48:09 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/access/htup.h,v 1.93 2007/04/06 04:21:43 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -160,9 +160,8 @@ typedef HeapTupleHeaderData *HeapTupleHeader;
 #define HEAP_HASNULL			0x0001	/* has null attribute(s) */
 #define HEAP_HASVARWIDTH		0x0002	/* has variable-width 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_HASOID				0x0010	/* has an object-id field */
+#define HEAP_HASOID				0x0008	/* has an object-id field */
+/* bit 0x0010 is available */
 #define HEAP_COMBOCID			0x0020	/* t_cid is a combo cid */
 #define HEAP_XMAX_EXCL_LOCK		0x0040	/* xmax is exclusive locker */
 #define HEAP_XMAX_SHARED_LOCK	0x0080	/* xmax is shared locker */
@@ -341,7 +340,7 @@ do { \
  * MaxAttrSize is a somewhat arbitrary upper limit on the declared size of
  * data fields of char(n) and similar types.  It need not have anything
  * directly to do with the *actual* upper limit of varlena values, which
- * is currently 1Gb (see struct varattrib in postgres.h).  I've set it
+ * is currently 1Gb (see TOAST structures in postgres.h).  I've set it
  * at 10Mb which seems like a reasonable number --- tgl 8/6/00.
  */
 #define MaxAttrSize		(10 * 1024 * 1024)
@@ -485,12 +484,6 @@ typedef HeapTupleData *HeapTuple;
 #define HeapTupleHasExternal(tuple) \
 		(((tuple)->t_data->t_infomask & HEAP_HASEXTERNAL) != 0)
 
-#define HeapTupleHasCompressed(tuple) \
-		(((tuple)->t_data->t_infomask & HEAP_HASCOMPRESSED) != 0)
-
-#define HeapTupleHasExtended(tuple) \
-		(((tuple)->t_data->t_infomask & HEAP_HASEXTENDED) != 0)
-
 #define HeapTupleGetOid(tuple) \
 		HeapTupleHeaderGetOid((tuple)->t_data)
 
diff --git a/src/include/access/tupmacs.h b/src/include/access/tupmacs.h
index 80c2d436f047ce5f5935c957627a5f0a005c68fa..f4fb8c7b33d660a283d32935a801c95edb8b4a95 100644
--- a/src/include/access/tupmacs.h
+++ b/src/include/access/tupmacs.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/access/tupmacs.h,v 1.32 2007/02/27 23:48:09 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/access/tupmacs.h,v 1.33 2007/04/06 04:21:43 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -91,26 +91,83 @@
 #endif   /* SIZEOF_DATUM == 8 */
 
 /*
- * att_align aligns the given offset as needed for a datum of alignment
- * requirement attalign.  The cases are tested in what is hopefully something
- * like their frequency of occurrence.
+ * att_align_datum aligns the given offset as needed for a datum of alignment
+ * requirement attalign and typlen attlen.  attdatum is the Datum variable
+ * we intend to pack into a tuple (it's only accessed if we are dealing with
+ * a varlena type).  Note that this assumes the Datum will be stored as-is;
+ * callers that are intending to convert non-short varlena datums to short
+ * format have to account for that themselves.
  */
-#define att_align(cur_offset, attalign) \
+#define att_align_datum(cur_offset, attalign, attlen, attdatum) \
+( \
+	((attlen) == -1 && VARATT_IS_SHORT(DatumGetPointer(attdatum))) ? (long) (cur_offset) : \
+	att_align_nominal(cur_offset, attalign) \
+)
+
+/*
+ * att_align_pointer performs the same calculation as att_align_datum,
+ * but is used when walking a tuple.  attptr is the current actual data
+ * pointer; when accessing a varlena field we have to "peek" to see if we
+ * are looking at a pad byte or the first byte of a 1-byte-header datum.
+ * (A zero byte must be either a pad byte, or the first byte of a correctly
+ * aligned 4-byte length word; in either case we can align safely.  A non-zero
+ * byte must be either a 1-byte length word, or the first byte of a correctly
+ * aligned 4-byte length word; in either case we need not align.)
+ *
+ * Note: some callers pass a "char *" pointer for cur_offset.  This is
+ * a bit of a hack but works OK on all known platforms.  It ought to be
+ * cleaned up someday, though.
+ */
+#define att_align_pointer(cur_offset, attalign, attlen, attptr) \
+( \
+	((attlen) == -1 && VARATT_NOT_PAD_BYTE(attptr)) ? (long) (cur_offset) : \
+	att_align_nominal(cur_offset, attalign) \
+)
+
+/*
+ * att_align_nominal aligns the given offset as needed for a datum of alignment
+ * requirement attalign, ignoring any consideration of packed varlena datums.
+ * There are three main use cases for using this macro directly:
+ * 	* we know that the att in question is not varlena (attlen != -1);
+ *	  in this case it is cheaper than the above macros and just as good.
+ *	* we need to estimate alignment padding cost abstractly, ie without
+ *	  reference to a real tuple.  We must assume the worst case that
+ *	  all varlenas are aligned.
+ *	* within arrays, we unconditionally align varlenas (XXX this should be
+ *	  revisited, probably).
+ *
+ * The attalign cases are tested in what is hopefully something like their
+ * frequency of occurrence.
+ */
+#define att_align_nominal(cur_offset, attalign) \
 ( \
 	((attalign) == 'i') ? INTALIGN(cur_offset) : \
-	 (((attalign) == 'c') ? ((long)(cur_offset)) : \
+	 (((attalign) == 'c') ? (long) (cur_offset) : \
 	  (((attalign) == 'd') ? DOUBLEALIGN(cur_offset) : \
-		( \
+	   ( \
 			AssertMacro((attalign) == 's'), \
 			SHORTALIGN(cur_offset) \
-		))) \
+	   ))) \
 )
 
 /*
- * att_addlength increments the given offset by the length of the attribute.
- * attval is only accessed if we are dealing with a variable-length attribute.
+ * att_addlength_datum increments the given offset by the space needed for
+ * the given Datum variable.  attdatum is only accessed if we are dealing
+ * with a variable-length attribute.
+ */
+#define att_addlength_datum(cur_offset, attlen, attdatum) \
+	att_addlength_pointer(cur_offset, attlen, DatumGetPointer(attdatum))
+
+/*
+ * att_addlength_pointer performs the same calculation as att_addlength_datum,
+ * but is used when walking a tuple --- attptr is the pointer to the field
+ * within the tuple.
+ *
+ * Note: some callers pass a "char *" pointer for cur_offset.  This is
+ * actually perfectly OK, but probably should be cleaned up along with
+ * the same practice for att_align_pointer.
  */
-#define att_addlength(cur_offset, attlen, attval) \
+#define att_addlength_pointer(cur_offset, attlen, attptr) \
 ( \
 	((attlen) > 0) ? \
 	( \
@@ -118,12 +175,12 @@
 	) \
 	: (((attlen) == -1) ? \
 	( \
-		(cur_offset) + VARSIZE(DatumGetPointer(attval)) \
+		(cur_offset) + VARSIZE_ANY(attptr) \
 	) \
 	: \
 	( \
 		AssertMacro((attlen) == -2), \
-		(cur_offset) + (strlen(DatumGetCString(attval)) + 1) \
+		(cur_offset) + (strlen((char *) (attptr)) + 1) \
 	)) \
 )
 
diff --git a/src/include/access/tuptoaster.h b/src/include/access/tuptoaster.h
index 3ab29979468d788cbc0ef8921ac5912cde97fbb3..ea870aff1103479d105647c3fc3f67b603f3c3af 100644
--- a/src/include/access/tuptoaster.h
+++ b/src/include/access/tuptoaster.h
@@ -6,7 +6,7 @@
  *
  * Copyright (c) 2000-2007, PostgreSQL Global Development Group
  *
- * $PostgreSQL: pgsql/src/include/access/tuptoaster.h,v 1.34 2007/04/03 04:14:26 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/access/tuptoaster.h,v 1.35 2007/04/06 04:21:43 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -111,7 +111,7 @@ extern void toast_delete(Relation rel, HeapTuple oldtup);
  *		in compressed format.
  * ----------
  */
-extern varattrib *heap_tuple_fetch_attr(varattrib *attr);
+extern struct varlena *heap_tuple_fetch_attr(struct varlena *attr);
 
 /* ----------
  * heap_tuple_untoast_attr() -
@@ -120,7 +120,7 @@ extern varattrib *heap_tuple_fetch_attr(varattrib *attr);
  *		it as needed.
  * ----------
  */
-extern varattrib *heap_tuple_untoast_attr(varattrib *attr);
+extern struct varlena *heap_tuple_untoast_attr(struct varlena *attr);
 
 /* ----------
  * heap_tuple_untoast_attr_slice() -
@@ -129,7 +129,7 @@ extern varattrib *heap_tuple_untoast_attr(varattrib *attr);
  *		(Handles all cases for attribute storage)
  * ----------
  */
-extern varattrib *heap_tuple_untoast_attr_slice(varattrib *attr,
+extern struct varlena *heap_tuple_untoast_attr_slice(struct varlena *attr,
 							  int32 sliceoffset,
 							  int32 slicelength);
 
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index 9525a198feeb912f387ec268d7f4fa6db9d7dbbb..68486709cc47f29541d7ec5318bea13e3739379b 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -37,7 +37,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.399 2007/04/02 03:49:40 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.400 2007/04/06 04:21:43 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -53,6 +53,6 @@
  */
 
 /*							yyyymmddN */
-#define CATALOG_VERSION_NO	200704012
+#define CATALOG_VERSION_NO	200704051
 
 #endif
diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h
index 69713196000b78b9e437e235a1ab12dddfe42992..519be08afc3c8929b94268b7283dbd119781e26d 100644
--- a/src/include/catalog/pg_type.h
+++ b/src/include/catalog/pg_type.h
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/pg_type.h,v 1.181 2007/04/02 03:49:41 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/pg_type.h,v 1.182 2007/04/06 04:21:43 tgl Exp $
  *
  * NOTES
  *	  the genbki.sh script reads this file and generates .bki
@@ -130,8 +130,10 @@ CATALOG(pg_type,1247) BKI_BOOTSTRAP
 	 * 'i' = INT alignment (4 bytes on most machines).
 	 * 'd' = DOUBLE alignment (8 bytes on many machines, but by no means all).
 	 *
-	 * See include/utils/memutils.h for the macros that compute these
-	 * alignment requirements.
+	 * See include/access/tupmacs.h for the macros that compute these
+	 * alignment requirements.  Note also that we allow the nominal alignment
+	 * to be violated when storing "packed" varlenas; the TOAST mechanism
+	 * takes care of hiding that from most code.
 	 *
 	 * NOTE: for types used in system tables, it is critical that the
 	 * size and alignment defined in pg_type agree with the way that the
@@ -398,10 +400,10 @@ DATA(insert OID = 791 (  _money    PGNSP PGUID	-1 f b t \054 0  790 array_in arr
 DATA(insert OID = 829 ( macaddr    PGNSP PGUID	6 f b t \054 0 0 macaddr_in macaddr_out macaddr_recv macaddr_send - - - i p f 0 -1 0 _null_ _null_ ));
 DESCR("XX:XX:XX:XX:XX:XX, MAC address");
 #define MACADDROID 829
-DATA(insert OID = 869 ( inet	   PGNSP PGUID	-1 f b t \054 0 0 inet_in inet_out inet_recv inet_send - - - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 869 ( inet	   PGNSP PGUID	-1 f b t \054 0 0 inet_in inet_out inet_recv inet_send - - - i m f 0 -1 0 _null_ _null_ ));
 DESCR("IP address/netmask, host address, netmask optional");
 #define INETOID 869
-DATA(insert OID = 650 ( cidr	   PGNSP PGUID	-1 f b t \054 0 0 cidr_in cidr_out cidr_recv cidr_send - - - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 650 ( cidr	   PGNSP PGUID	-1 f b t \054 0 0 cidr_in cidr_out cidr_recv cidr_send - - - i m f 0 -1 0 _null_ _null_ ));
 DESCR("network IP address/netmask, network address");
 #define CIDROID 650
 
diff --git a/src/include/fmgr.h b/src/include/fmgr.h
index f6462d43a89080c66a2f6e386f9a2f1296e1177f..298cd12c775b27b37b154a3e8cd8c2cf6840dbf2 100644
--- a/src/include/fmgr.h
+++ b/src/include/fmgr.h
@@ -11,7 +11,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/fmgr.h,v 1.49 2007/01/05 22:19:50 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/fmgr.h,v 1.50 2007/04/06 04:21:44 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -153,6 +153,11 @@ extern void fmgr_info_copy(FmgrInfo *dstinfo, FmgrInfo *srcinfo,
  * if you need a modifiable copy of the input.	Caller is expected to have
  * checked for null inputs first, if necessary.
  *
+ * pg_detoast_datum_packed() will return packed (1-byte header) datums
+ * unmodified.  It will still expand an externally toasted or compressed datum.
+ * The resulting datum can be accessed using VARSIZE_ANY() and VARDATA_ANY()
+ * (beware of multiple evaluations in those macros!)
+ *
  * Note: it'd be nice if these could be macros, but I see no way to do that
  * without evaluating the arguments multiple times, which is NOT acceptable.
  */
@@ -160,6 +165,7 @@ extern struct varlena *pg_detoast_datum(struct varlena * datum);
 extern struct varlena *pg_detoast_datum_copy(struct varlena * datum);
 extern struct varlena *pg_detoast_datum_slice(struct varlena * datum,
 					   int32 first, int32 count);
+extern struct varlena *pg_detoast_datum_packed(struct varlena * datum);
 
 #define PG_DETOAST_DATUM(datum) \
 	pg_detoast_datum((struct varlena *) DatumGetPointer(datum))
@@ -168,6 +174,8 @@ extern struct varlena *pg_detoast_datum_slice(struct varlena * datum,
 #define PG_DETOAST_DATUM_SLICE(datum,f,c) \
 		pg_detoast_datum_slice((struct varlena *) DatumGetPointer(datum), \
 		(int32) f, (int32) c)
+#define PG_DETOAST_DATUM_PACKED(datum) \
+	pg_detoast_datum_packed((struct varlena *) DatumGetPointer(datum))
 
 /*
  * Support for cleaning up detoasted copies of inputs.	This must only
@@ -207,9 +215,13 @@ extern struct varlena *pg_detoast_datum_slice(struct varlena * datum,
 #define PG_GETARG_VARLENA_P(n) PG_DETOAST_DATUM(PG_GETARG_DATUM(n))
 /* DatumGetFoo macros for varlena types will typically look like this: */
 #define DatumGetByteaP(X)			((bytea *) PG_DETOAST_DATUM(X))
+#define DatumGetByteaPP(X)			((bytea *) PG_DETOAST_DATUM_PACKED(X))
 #define DatumGetTextP(X)			((text *) PG_DETOAST_DATUM(X))
+#define DatumGetTextPP(X)			((text *) PG_DETOAST_DATUM_PACKED(X))
 #define DatumGetBpCharP(X)			((BpChar *) PG_DETOAST_DATUM(X))
+#define DatumGetBpCharPP(X)			((BpChar *) PG_DETOAST_DATUM_PACKED(X))
 #define DatumGetVarCharP(X)			((VarChar *) PG_DETOAST_DATUM(X))
+#define DatumGetVarCharPP(X)		((VarChar *) PG_DETOAST_DATUM_PACKED(X))
 #define DatumGetHeapTupleHeader(X)	((HeapTupleHeader) PG_DETOAST_DATUM(X))
 /* And we also offer variants that return an OK-to-write copy */
 #define DatumGetByteaPCopy(X)		((bytea *) PG_DETOAST_DATUM_COPY(X))
@@ -224,9 +236,13 @@ extern struct varlena *pg_detoast_datum_slice(struct varlena * datum,
 #define DatumGetVarCharPSlice(X,m,n) ((VarChar *) PG_DETOAST_DATUM_SLICE(X,m,n))
 /* GETARG macros for varlena types will typically look like this: */
 #define PG_GETARG_BYTEA_P(n)		DatumGetByteaP(PG_GETARG_DATUM(n))
+#define PG_GETARG_BYTEA_PP(n)		DatumGetByteaPP(PG_GETARG_DATUM(n))
 #define PG_GETARG_TEXT_P(n)			DatumGetTextP(PG_GETARG_DATUM(n))
+#define PG_GETARG_TEXT_PP(n)		DatumGetTextPP(PG_GETARG_DATUM(n))
 #define PG_GETARG_BPCHAR_P(n)		DatumGetBpCharP(PG_GETARG_DATUM(n))
+#define PG_GETARG_BPCHAR_PP(n)		DatumGetBpCharPP(PG_GETARG_DATUM(n))
 #define PG_GETARG_VARCHAR_P(n)		DatumGetVarCharP(PG_GETARG_DATUM(n))
+#define PG_GETARG_VARCHAR_PP(n)		DatumGetVarCharPP(PG_GETARG_DATUM(n))
 #define PG_GETARG_HEAPTUPLEHEADER(n)	DatumGetHeapTupleHeader(PG_GETARG_DATUM(n))
 /* And we also offer variants that return an OK-to-write copy */
 #define PG_GETARG_BYTEA_P_COPY(n)	DatumGetByteaPCopy(PG_GETARG_DATUM(n))
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
index 9b107e27f72146b26633ab05f097e27d6ee47889..35c1be6f9c1af650d50b8fc25cb2b244f5824f3c 100644
--- a/src/include/pg_config.h.in
+++ b/src/include/pg_config.h.in
@@ -671,6 +671,10 @@
 /* Define to select Win32-style shared memory. */
 #undef USE_WIN32_SHARED_MEMORY
 
+/* Define to 1 if your processor stores words with the most significant byte
+   first (like Motorola and SPARC, unlike Intel and VAX). */
+#undef WORDS_BIGENDIAN
+
 /* Number of bits in a file offset, on hosts where this is settable. */
 #undef _FILE_OFFSET_BITS
 
diff --git a/src/include/postgres.h b/src/include/postgres.h
index 5234c34e291e971de985a999ea141526a230eb4d..1ff0e97e9b6343826096d48af14d21f27458a0c4 100644
--- a/src/include/postgres.h
+++ b/src/include/postgres.h
@@ -10,7 +10,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1995, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/postgres.h,v 1.78 2007/03/23 20:24:41 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/postgres.h,v 1.79 2007/04/06 04:21:44 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -54,56 +54,219 @@
  * ----------------------------------------------------------------
  */
 
-/* ----------------
- * struct varattrib is the header of a varlena object that may have been
- * TOASTed.  Generally, only the code closely associated with TOAST logic
- * should mess directly with struct varattrib or use the VARATT_FOO macros.
- * ----------------
+/*
+ * struct varatt_external is a "TOAST pointer", that is, the information
+ * needed to fetch a stored-out-of-line Datum.  The data is compressed
+ * if and only if va_extsize < va_rawsize - VARHDRSZ.  This struct must not
+ * contain any padding, because we sometimes compare pointers using memcmp.
+ *
+ * Note that this information is stored unaligned within actual tuples, so
+ * you need to memcpy from the tuple into a local struct variable before
+ * you can look at these fields!  (The reason we use memcmp is to avoid
+ * having to do that just to detect equality of two TOAST pointers...)
+ */
+struct varatt_external
+{
+	int32		va_rawsize;			/* Original data size (includes header) */
+	int32		va_extsize;			/* External saved size (doesn't) */
+	Oid			va_valueid;			/* Unique ID of value within TOAST table */
+	Oid			va_toastrelid;		/* RelID of TOAST table containing it */
+};
+
+/*
+ * These structs describe the header of a varlena object that may have been
+ * TOASTed.  Generally, don't reference these structs directly, but use the
+ * macros below.
+ *
+ * We use separate structs for the aligned and unaligned cases because the
+ * compiler might otherwise think it could generate code that assumes
+ * alignment while touching fields of a 1-byte-header varlena.
  */
-typedef struct varattrib
+typedef union
 {
-	int32		va_header_;		/* External/compressed storage */
-	/* flags and item size */
-	union
+	struct							/* Normal varlena (4-byte length) */
+	{
+		uint32	va_header;
+		char	va_data[1];
+	} va_4byte;
+	struct							/* Compressed-in-line format */
 	{
-		struct
-		{
-			int32		va_rawsize;		/* Plain data size */
-			char		va_data[1];		/* Compressed data */
-		}			va_compressed;		/* Compressed stored attribute */
-
-		struct
-		{
-			int32		va_rawsize;		/* Plain data size */
-			int32		va_extsize;		/* External saved size */
-			Oid			va_valueid;		/* Unique identifier of value */
-			Oid			va_toastrelid;	/* RelID where to find chunks */
-		}			va_external;	/* External stored attribute */
-
-		char		va_data[1]; /* Plain stored attribute */
-	}			va_content;
-} varattrib;
-
-#define VARATT_FLAG_EXTERNAL	0x80000000
-#define VARATT_FLAG_COMPRESSED	0x40000000
-#define VARATT_MASK_FLAGS		0xc0000000
-#define VARATT_MASK_SIZE		0x3fffffff
-
-#define VARATT_SIZEP_DEPRECATED(PTR)	(((varattrib *) (PTR))->va_header_)
-
-#define VARATT_IS_EXTENDED(PTR)		\
-				((VARATT_SIZEP_DEPRECATED(PTR) & VARATT_MASK_FLAGS) != 0)
-#define VARATT_IS_EXTERNAL(PTR)		\
-				((VARATT_SIZEP_DEPRECATED(PTR) & VARATT_FLAG_EXTERNAL) != 0)
-#define VARATT_IS_COMPRESSED(PTR)	\
-				((VARATT_SIZEP_DEPRECATED(PTR) & VARATT_FLAG_COMPRESSED) != 0)
-
-/* These macros are the ones for non-TOAST code to use */
-
-#define VARSIZE(PTR)	(VARATT_SIZEP_DEPRECATED(PTR) & VARATT_MASK_SIZE)
-#define VARDATA(PTR)	(((varattrib *) (PTR))->va_content.va_data)
-
-#define SET_VARSIZE(PTR,SIZE)	(VARATT_SIZEP_DEPRECATED(PTR) = (SIZE))
+		uint32	va_header;
+		uint32	va_rawsize;			/* Original data size (excludes header) */
+		char	va_data[1];			/* Compressed data */
+	} va_compressed;
+} varattrib_4b;
+
+typedef struct
+{
+	uint8		va_header;
+	char		va_data[1];			/* Data or TOAST pointer */
+} varattrib_1b;
+
+typedef struct
+{
+	uint8		va_header;
+	char		va_data[sizeof(struct varatt_external)];
+} varattrib_pointer;
+
+/*
+ * Bit layouts for varlena headers on big-endian machines:
+ *
+ * 00xxxxxx	4-byte length word, aligned, uncompressed data (up to 1G)
+ * 01xxxxxx	4-byte length word, aligned, *compressed* data (up to 1G)
+ * 10000000	1-byte length word, unaligned, TOAST pointer
+ * 1xxxxxxx	1-byte length word, unaligned, uncompressed data (up to 126b)
+ *
+ * Bit layouts for varlena headers on little-endian machines:
+ *
+ * xxxxxx00	4-byte length word, aligned, uncompressed data (up to 1G)
+ * xxxxxx10	4-byte length word, aligned, *compressed* data (up to 1G)
+ * 00000001	1-byte length word, unaligned, TOAST pointer
+ * xxxxxxx1	1-byte length word, unaligned, uncompressed data (up to 126b)
+ *
+ * The "xxx" bits are the length field (which includes itself in all cases).
+ * In the big-endian case we mask to extract the length, in the little-endian
+ * case we shift.  Note that in both cases the flag bits are in the physically
+ * first byte.  Also, it is not possible for a 1-byte length word to be zero;
+ * this lets us disambiguate alignment padding bytes from the start of an
+ * unaligned datum.  (We now *require* pad bytes to be filled with zero!)
+ */
+
+/*
+ * Endian-dependent macros.  These are considered internal --- use the
+ * external macros below instead of using these directly.
+ *
+ * Note: IS_1B is true for external toast records but VARSIZE_1B will return 0
+ * for such records. Hence you should usually check for IS_EXTERNAL before
+ * checking for IS_1B.
+ */
+
+#ifdef WORDS_BIGENDIAN
+
+#define VARATT_IS_4B(PTR) \
+	((((varattrib_1b *) (PTR))->va_header & 0x80) == 0x00)
+#define VARATT_IS_4B_U(PTR) \
+	((((varattrib_1b *) (PTR))->va_header & 0xC0) == 0x00)
+#define VARATT_IS_4B_C(PTR) \
+	((((varattrib_1b *) (PTR))->va_header & 0xC0) == 0x40)
+#define VARATT_IS_1B(PTR) \
+	((((varattrib_1b *) (PTR))->va_header & 0x80) == 0x80)
+#define VARATT_IS_1B_E(PTR) \
+	((((varattrib_1b *) (PTR))->va_header) == 0x80)
+#define VARATT_NOT_PAD_BYTE(PTR) \
+	(*((uint8 *) (PTR)) != 0)
+
+/* VARSIZE_4B() should only be used on known-aligned data */
+#define VARSIZE_4B(PTR) \
+	(((varattrib_4b *) (PTR))->va_4byte.va_header & 0x3FFFFFFF)
+#define VARSIZE_1B(PTR) \
+	(((varattrib_1b *) (PTR))->va_header & 0x7F)
+/* Currently there is only one size of toast pointer, but someday maybe not */
+#define VARSIZE_1B_E(PTR) \
+	(sizeof(varattrib_pointer))
+
+#define SET_VARSIZE_4B(PTR,len) \
+	(((varattrib_4b *) (PTR))->va_4byte.va_header = (len) & 0x3FFFFFFF)
+#define SET_VARSIZE_4B_C(PTR,len) \
+	(((varattrib_4b *) (PTR))->va_4byte.va_header = ((len) & 0x3FFFFFFF) | 0x40000000)
+#define SET_VARSIZE_1B(PTR,len) \
+	(((varattrib_1b *) (PTR))->va_header = (len) | 0x80)
+#define SET_VARSIZE_1B_E(PTR) \
+	(((varattrib_1b *) (PTR))->va_header = 0x80)
+
+#else  /* !WORDS_BIGENDIAN */
+
+#define VARATT_IS_4B(PTR) \
+	((((varattrib_1b *) (PTR))->va_header & 0x01) == 0x00)
+#define VARATT_IS_4B_U(PTR) \
+	((((varattrib_1b *) (PTR))->va_header & 0x03) == 0x00)
+#define VARATT_IS_4B_C(PTR) \
+	((((varattrib_1b *) (PTR))->va_header & 0x03) == 0x02)
+#define VARATT_IS_1B(PTR) \
+	((((varattrib_1b *) (PTR))->va_header & 0x01) == 0x01)
+#define VARATT_IS_1B_E(PTR) \
+	((((varattrib_1b *) (PTR))->va_header) == 0x01)
+#define VARATT_NOT_PAD_BYTE(PTR) \
+	(*((uint8 *) (PTR)) != 0)
+
+/* VARSIZE_4B() should only be used on known-aligned data */
+#define VARSIZE_4B(PTR) \
+	((((varattrib_4b *) (PTR))->va_4byte.va_header >> 2) & 0x3FFFFFFF)
+#define VARSIZE_1B(PTR) \
+	((((varattrib_1b *) (PTR))->va_header >> 1) & 0x7F)
+/* Currently there is only one size of toast pointer, but someday maybe not */
+#define VARSIZE_1B_E(PTR) \
+	(sizeof(varattrib_pointer))
+
+#define SET_VARSIZE_4B(PTR,len) \
+	(((varattrib_4b *) (PTR))->va_4byte.va_header = (((uint32) (len)) << 2))
+#define SET_VARSIZE_4B_C(PTR,len) \
+	(((varattrib_4b *) (PTR))->va_4byte.va_header = (((uint32) (len)) << 2) | 0x02)
+#define SET_VARSIZE_1B(PTR,len) \
+	(((varattrib_1b *) (PTR))->va_header = (((uint8) (len)) << 1) | 0x01)
+#define SET_VARSIZE_1B_E(PTR) \
+	(((varattrib_1b *) (PTR))->va_header = 0x01)
+
+#endif /* WORDS_BIGENDIAN */
+
+#define VARHDRSZ_SHORT			1
+#define VARATT_SHORT_MAX		0x7F
+#define VARATT_CAN_MAKE_SHORT(PTR) \
+	(VARATT_IS_4B_U(PTR) && \
+	 (VARSIZE(PTR) - VARHDRSZ + VARHDRSZ_SHORT) <= VARATT_SHORT_MAX)
+#define VARATT_CONVERTED_SHORT_SIZE(PTR) \
+	(VARSIZE(PTR) - VARHDRSZ + VARHDRSZ_SHORT)
+
+#define VARDATA_4B(PTR)		(((varattrib_4b *) (PTR))->va_4byte.va_data)
+#define VARDATA_4B_C(PTR)	(((varattrib_4b *) (PTR))->va_compressed.va_data)
+#define VARDATA_1B(PTR)		(((varattrib_1b *) (PTR))->va_data)
+
+#define VARRAWSIZE_4B_C(PTR) \
+	(((varattrib_4b *) (PTR))->va_compressed.va_rawsize)
+
+/* Externally visible macros */
+
+/*
+ * VARDATA, VARSIZE, and SET_VARSIZE are the recommended API for most code
+ * for varlena datatypes.  Note that they only work on untoasted,
+ * 4-byte-header Datums!
+ *
+ * Code that wants to use 1-byte-header values without detoasting should
+ * use VARSIZE_ANY/VARSIZE_ANY_EXHDR/VARDATA_ANY.  The other macros here
+ * should usually be used only by tuple assembly/disassembly code and
+ * code that specifically wants to work with still-toasted Datums.
+ */
+#define VARDATA(PTR)						VARDATA_4B(PTR)
+#define VARSIZE(PTR)						VARSIZE_4B(PTR)
+
+#define VARSIZE_SHORT(PTR)					VARSIZE_1B(PTR)
+#define VARDATA_SHORT(PTR)					VARDATA_1B(PTR)
+
+#define VARSIZE_EXTERNAL(PTR)				VARSIZE_1B_E(PTR)
+
+#define VARATT_IS_COMPRESSED(PTR)			VARATT_IS_4B_C(PTR)
+#define VARATT_IS_EXTERNAL(PTR)				VARATT_IS_1B_E(PTR)
+#define VARATT_IS_SHORT(PTR)				VARATT_IS_1B(PTR)
+#define VARATT_IS_EXTENDED(PTR)				(!VARATT_IS_4B_U(PTR))
+
+#define SET_VARSIZE(PTR, len)				SET_VARSIZE_4B(PTR, len)
+#define SET_VARSIZE_SHORT(PTR, len)			SET_VARSIZE_1B(PTR, len)
+#define SET_VARSIZE_COMPRESSED(PTR, len)	SET_VARSIZE_4B_C(PTR, len)
+#define SET_VARSIZE_EXTERNAL(PTR)			SET_VARSIZE_1B_E(PTR)
+
+#define VARSIZE_ANY(PTR) \
+	(VARATT_IS_1B_E(PTR) ? VARSIZE_1B_E(PTR) : \
+	 (VARATT_IS_1B(PTR) ? VARSIZE_1B(PTR) : \
+	  VARSIZE_4B(PTR)))
+
+#define VARSIZE_ANY_EXHDR(PTR) \
+	(VARATT_IS_1B_E(PTR) ? VARSIZE_1B_E(PTR)-1 : \
+	 (VARATT_IS_1B(PTR) ? VARSIZE_1B(PTR)-1 : \
+	  VARSIZE_4B(PTR)-4))
+
+/* caution: this will not work on an external or compressed-in-line Datum */
+#define VARDATA_ANY(PTR) \
+	 (VARATT_IS_1B(PTR) ? VARDATA_1B(PTR) : VARDATA_4B(PTR))
 
 
 /* ----------------------------------------------------------------
diff --git a/src/include/utils/inet.h b/src/include/utils/inet.h
index ca92f4ca9e127598ebdde8721d0c3ea5a9923085..d94855bb220b66a14bdb1c67459972f22ccd962e 100644
--- a/src/include/utils/inet.h
+++ b/src/include/utils/inet.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/utils/inet.h,v 1.25 2007/01/05 22:19:59 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/utils/inet.h,v 1.26 2007/04/06 04:21:44 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -39,13 +39,19 @@ typedef struct
 
 /*
  * Both INET and CIDR addresses are represented within Postgres as varlena
- * objects, ie, there is a varlena header (basically a length word) in front
- * of the struct type depicted above.
- *
- * Although these types are variable-length, the maximum length
- * is pretty short, so we make no provision for TOASTing them.
+ * objects, ie, there is a varlena header in front of the struct type
+ * depicted above.  This struct depicts what we actually have in memory
+ * in "uncompressed" cases.  Note that since the maximum data size is only
+ * 18 bytes, INET/CIDR will invariably be stored into tuples using the
+ * 1-byte-header varlena format.  However, we have to be prepared to cope
+ * with the 4-byte-header format too, because various code may helpfully
+ * try to "decompress" 1-byte-header datums.
  */
-typedef struct varlena inet;
+typedef struct
+{
+	int32		vl_len_;		/* Do not touch this field directly! */
+	inet_struct	inet_data;
+} inet;
 
 
 /*
@@ -64,7 +70,7 @@ typedef struct macaddr
 /*
  * fmgr interface macros
  */
-#define DatumGetInetP(X)	((inet *) DatumGetPointer(X))
+#define DatumGetInetP(X)	((inet *) PG_DETOAST_DATUM_PACKED(X))
 #define InetPGetDatum(X)	PointerGetDatum(X)
 #define PG_GETARG_INET_P(n) DatumGetInetP(PG_GETARG_DATUM(n))
 #define PG_RETURN_INET_P(x) return InetPGetDatum(x)
diff --git a/src/test/regress/expected/rowtypes.out b/src/test/regress/expected/rowtypes.out
index dbf163cd695b88c57088266ed9aa3fca5e44bdaf..6af1aa705fd21a55243c9c78442fad6f49f6610d 100644
--- a/src/test/regress/expected/rowtypes.out
+++ b/src/test/regress/expected/rowtypes.out
@@ -248,3 +248,20 @@ order by thousand, tenthous;
       999 |     9999
 (25 rows)
 
+-- Check some corner cases involving empty rowtypes
+select ROW();
+ row 
+-----
+ ()
+(1 row)
+
+select ROW() IS NULL;
+ ?column? 
+----------
+ t
+(1 row)
+
+select ROW() = ROW();
+ERROR:  cannot compare rows of zero length
+LINE 1: select ROW() = ROW();
+                     ^
diff --git a/src/test/regress/expected/strings.out b/src/test/regress/expected/strings.out
index e11dfc35d786231352f905a75ab7cb817d4f6eec..bd82846b1a41f8da60830fdf888839d4d178b8e7 100644
--- a/src/test/regress/expected/strings.out
+++ b/src/test/regress/expected/strings.out
@@ -940,6 +940,26 @@ SELECT substr(f1, 99995, 10) from toasttest;
  567890
 (4 rows)
 
+DROP TABLE toasttest;
+-- test internally compressing datums
+-- this tests compressing a datum to a very small size which exercises a
+-- corner case in packed-varlena handling: even though small, the compressed
+-- datum must be given a 4-byte header because there are no bits to indicate
+-- compression in a 1-byte header
+CREATE TABLE toasttest (c char(4096));
+INSERT INTO toasttest VALUES('x');
+SELECT length(c), c::text FROM toasttest;
+ length | c 
+--------+---
+      1 | x
+(1 row)
+
+SELECT c FROM toasttest;
+                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                c                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 
+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ x                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               
+(1 row)
+
 DROP TABLE toasttest;
 --
 -- test length
diff --git a/src/test/regress/sql/rowtypes.sql b/src/test/regress/sql/rowtypes.sql
index 779b20e8631e2f01cd57cda59a521d30a67afc8f..7e5c554d3b79b75cb7c3b890196766b478ea850a 100644
--- a/src/test/regress/sql/rowtypes.sql
+++ b/src/test/regress/sql/rowtypes.sql
@@ -109,3 +109,7 @@ select thousand, tenthous from tenk1
 where (thousand, tenthous) >= (997, 5000)
 order by thousand, tenthous;
 
+-- Check some corner cases involving empty rowtypes
+select ROW();
+select ROW() IS NULL;
+select ROW() = ROW();
diff --git a/src/test/regress/sql/strings.sql b/src/test/regress/sql/strings.sql
index 389ff63517f75bb1ad94c943abc782063ae2f506..800028e4fcbdc0bfed7db3abf4b735cde028e5f4 100644
--- a/src/test/regress/sql/strings.sql
+++ b/src/test/regress/sql/strings.sql
@@ -329,6 +329,19 @@ SELECT substr(f1, 99995, 10) from toasttest;
 
 DROP TABLE toasttest;
 
+-- test internally compressing datums
+
+-- this tests compressing a datum to a very small size which exercises a
+-- corner case in packed-varlena handling: even though small, the compressed
+-- datum must be given a 4-byte header because there are no bits to indicate
+-- compression in a 1-byte header
+
+CREATE TABLE toasttest (c char(4096));
+INSERT INTO toasttest VALUES('x');
+SELECT length(c), c::text FROM toasttest;
+SELECT c FROM toasttest;
+DROP TABLE toasttest;
+
 --
 -- test length
 --