Support varlena fields with single-byte headers and unaligned storage.
authorTom Lane <tgl@sss.pgh.pa.us>
Fri, 6 Apr 2007 04:21:44 +0000 (04:21 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Fri, 6 Apr 2007 04:21:44 +0000 (04:21 +0000)
This commit breaks any code that assumes that the mere act of forming a tuple
(without writing it to disk) does not "toast" any fields.  While all available
regression tests pass, I'm not totally sure that we've fixed every nook and
cranny, especially in contrib.

Greg Stark with some help from Tom Lane

38 files changed:
configure
configure.in
contrib/dblink/dblink.c
contrib/hstore/hstore_gist.c
contrib/intarray/_int_gist.c
contrib/pg_trgm/trgm_gist.c
contrib/tsearch2/ts_cfg.c
doc/src/sgml/storage.sgml
src/backend/access/common/heaptuple.c
src/backend/access/common/indextuple.c
src/backend/access/heap/tuptoaster.c
src/backend/catalog/toasting.c
src/backend/commands/analyze.c
src/backend/executor/execQual.c
src/backend/storage/large_object/inv_api.c
src/backend/utils/adt/arrayfuncs.c
src/backend/utils/adt/datum.c
src/backend/utils/adt/network.c
src/backend/utils/adt/pg_lzcompress.c
src/backend/utils/adt/varchar.c
src/backend/utils/adt/varlena.c
src/backend/utils/adt/xml.c
src/backend/utils/fmgr/fmgr.c
src/backend/utils/init/flatfiles.c
src/include/access/heapam.h
src/include/access/htup.h
src/include/access/tupmacs.h
src/include/access/tuptoaster.h
src/include/catalog/catversion.h
src/include/catalog/pg_type.h
src/include/fmgr.h
src/include/pg_config.h.in
src/include/postgres.h
src/include/utils/inet.h
src/test/regress/expected/rowtypes.out
src/test/regress/expected/strings.out
src/test/regress/sql/rowtypes.sql
src/test/regress/sql/strings.sql

index c5a20b1e122d789b2d7a482ccaa513fce8266ab0..e53c705d7141059aa048cf55b5e78a345e76c402 100755 (executable)
--- a/configure
+++ b/configure
 ## 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
index 17487e4a799d79d20f3ffa8adf2559c3b1daf701..267fc90dc7d14852fb75848e381c675281556f4c 100644 (file)
@@ -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
index e5e5663f210e6458fe703da48ea478bb0e491d5d..35df25a2b8448944fd86b0c05648926ceb0e9b60 100644 (file)
@@ -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 */
index fbee64be7da71b207608f0d736cba2265e994e39..19ed74933f8f5d2f8113470f0ae043f4799b2479 100644 (file)
@@ -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
index 56eb0c08c2e53835755011fba0ce2574ffbc3a83..3c34cb67a7aa7a6cef4c7a9ec9d9240ea39272d3 100644 (file)
@@ -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);
 
index 476cb1b9763f72462370449eba9a45003054107b..260fe01da42e2b5ea6d6dc0f87156aae2fe9b3be 100644 (file)
@@ -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
index 0dc17703c3832fa8a691423ea39957f6831a34e9..646ffc148117df10f20c60bcfbac8b16b6562335 100644 (file)
@@ -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);
index f3881546e75d7fffbce625ccfe6e8f7fc3aed089..9831ec14ff2c47993025eacc5ddd55a9aa7101b7 100644 (file)
@@ -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">).
index 84f447ef875ec4718a4c193c141bebbd5c427720..071b9867ebbd05bd4470b57d85747df967f1e7db 100644 (file)
  * 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
  *
 #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));
 
index bbe3e944e3e89eb641271bce13d6268ba028d4ca..8a4660c946b6c425afa8edea29e1dbcea3ebc81d 100644 (file)
@@ -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);
 }
 
 /*
index 6b8b57f98bf83f1e2b00f09824cf35b749c2796f..5aaa660a8b52025252406fd6c377935f37438792 100644 (file)
 
 #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
index b46c8740e8dff1a5492e36c5251b7845103e391c..e6a42c712a73aa440a16406374147365f8fff738 100644 (file)
@@ -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 */
index f4d1cb72a1038ef93b0da5eb48cbb1f172661ae5..c0d7dd57c23400449b142dc9172912c058f10d86 100644 (file)
@@ -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
index 68908f992df44c837f5ffd4f51d1bcf8665e12f6..0f3979a9e985ea3fbc45bf2da9eb7f8ed296ad1f 100644 (file)
@@ -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;
                }
index fc26e7e321d828dff7597839ff496bd855dc819b..31f8b53d57accf9c90582f137112d32e168ed0b4 100644 (file)
@@ -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);
index e0739fdb8520a0f07a54c4fe5ef3821883428b18..b46a5ffd914abc06850a4bc894e1319cc85cb54a 100644 (file)
@@ -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;
                }
index be8cfa18c6cb301a412bfce9ed647cf5be0e5a01..b2123dcf02f98d44efba6559afd3d549094c11ec 100644 (file)
@@ -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)
                {
index a6d73bad83efe0b0309dc4abafa023b27d93ec33..5dd78d3fd1b974719af8a673abebee5b889e6a39 100644 (file)
@@ -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;
 }
index c0c8464e9cd5049db32f9566272fd77478e4b04f..d0046e23f51f975a06bc4cca293c10b9b0f2337e 100644 (file)
@@ -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;
 }
index 50c3ac479b0b827276f3f6d63bfacd924e3ad9c7..e917d69a5f0ecf1e5813d8f4e78dcfd51fdfa63e 100644 (file)
@@ -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);
index 34fbedec3de06c929737345693bb4aeed4125f19..7c3f7adce692e51f88f3c4d40ac90ac799c33f3e 100644 (file)
@@ -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")));
index b252cc48dce6c7ef8fad0072c337058608cf18b7..f533fb98f16805c8cf010bb343cf404da0fbe752 100644 (file)
@@ -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 */
index c13e7f3c305faf6663dbf23076ef8eff4486dd46..32a24414c77bb51fe7ffa5452c357bff5677b5ad 100644 (file)
@@ -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;
 }
 
 /*-------------------------------------------------------------------------
index 150e8102a12ce56194bac621c3f6f3ed35210cce..2399408d3065b7557637c0cfe9f9371490ef0df8 100644 (file)
@@ -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));
                }
index c685db1e222dd0eb818bedcdf2d738277c0ffb10..c9aa3a75f3a4942883a267ca36517ef3a1482c5e 100644 (file)
@@ -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);
index b19c4164e4cbce4f641171cc71688086fa122157..bc529f1bfdfdbdb5cc36e4808cf4182983ac874c 100644 (file)
@@ -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)
 
index 44f832f52116835529bbda6c74226460f20d12c1..22f79c441f06e87ac33acb8b6d602709e77ddfb4 100644 (file)
 #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) ? \
        ( \
        ) \
        : (((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) \
        )) \
 )
 
index db7118e039153314769e71670b75adefe0d92106..bb3ca635de9cdc4ebd80e491d142fe2b9429a1ed 100644 (file)
@@ -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);
 
index bd9cca64286b5cd7e19720ecc735d232d09f54ce..7fc20b03532d807de8785bf7218d5ef0d3607d65 100644 (file)
@@ -53,6 +53,6 @@
  */
 
 /*                                                     yyyymmddN */
-#define CATALOG_VERSION_NO     200704012
+#define CATALOG_VERSION_NO     200704051
 
 #endif
index 13ef5f2c522adac11a8ca163e0ce5b50799e4066..7667e2a1ccb89de04b5c1eed6366d14c39c0a7b4 100644 (file)
@@ -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
 
index c414fcf4aefc45fff50ce4d69bf1ab9e3a1b6186..499888e8eeb0dfdfe9c5ca3c95071168a2160c16 100644 (file)
@@ -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))
index 9b107e27f72146b26633ab05f097e27d6ee47889..35c1be6f9c1af650d50b8fc25cb2b244f5824f3c 100644 (file)
 /* 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
 
index 719f64ea783eaf733d0e0a4e20862bf08cb875f1..f86f1bbeeea81f12380f27457bd15fb01d72c96b 100644 (file)
  * ----------------------------------------------------------------
  */
 
-/* ----------------
- * 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))
 
 
 /* ----------------------------------------------------------------
index e2909648cf3c8d7791a93f9bc33221da40680584..020f86d0c282a6cfde7f34e92dec440df3f10585 100644 (file)
@@ -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)
index dbf163cd695b88c57088266ed9aa3fca5e44bdaf..6af1aa705fd21a55243c9c78442fad6f49f6610d 100644 (file)
@@ -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();
+                     ^
index e11dfc35d786231352f905a75ab7cb817d4f6eec..bd82846b1a41f8da60830fdf888839d4d178b8e7 100644 (file)
@@ -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
index 779b20e8631e2f01cd57cda59a521d30a67afc8f..7e5c554d3b79b75cb7c3b890196766b478ea850a 100644 (file)
@@ -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();
index 389ff63517f75bb1ad94c943abc782063ae2f506..800028e4fcbdc0bfed7db3abf4b735cde028e5f4 100644 (file)
@@ -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
 --