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