## 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
+#include
+
+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
+#include
+
+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
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
##
m4_defun([AC_PROG_CC_STDC], []) dnl We don't want that.
+AC_C_BIGENDIAN
AC_C_CONST
AC_C_INLINE
AC_C_STRINGIZE
* Darko Prenosil
* Shridhar Daithankar
*
- * $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;
*
{
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 */
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
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);
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));
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
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);
-
+
-
TOAST> usurps the high-order two bits of the varlena length word,
+
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
TOAST>-able
data type to 1 GB (230> - 1 bytes). When both bits are zero,
-the value is an ordinary un-
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-
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.
TOAST> table in which to look and the OID of the specific value
(its 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
TOAST> pointer datum is therefore 20 bytes
+(different if compression was applied). Allowing for the varlena header byte,
+the total size of a
TOAST> pointer datum is therefore 17 bytes
regardless of the actual size of the represented value.
PLAIN 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-
TOAST>-able data types.
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 . The actual user data
4 bytes
insert XID stamp
- |
- t_cmin
- CommandId
- 4 bytes
- insert CID stamp
-
|
t_xmax
TransactionId
delete XID stamp
|
- t_cmax
+ t_cid
CommandId
4 bytes
- delete CID stamp (overlays with t_xvac)
+ insert and/or delete CID stamp (overlays with t_xvac)
|
t_xvac
current TID of this or newer row version
|
- t_natts
+ t_infomask2
int16
2 bytes
- number of attributes
+ number of attributes, plus various flag bits
|
t_infomask
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
- varattrib, which includes the total length of the stored
+ struct varlena, 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
TOAST> table;
it might be compressed, too (see ).
* 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 $
*
*-------------------------------------------------------------------------
*/
#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
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;
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;
*
* 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)
{
bitmask = 0;
}
- *infomask &= ~(HEAP_HASNULL | HEAP_HASVARWIDTH | HEAP_HASEXTENDED);
+ *infomask &= ~(HEAP_HASNULL | HEAP_HASVARWIDTH | HEAP_HASEXTERNAL);
for (i = 0; i < numberOfAttributes; i++)
{
*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);
data += data_length;
}
+
+ Assert((data - start) == data_size);
}
/* ----------------
* ----------------
*/
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)
{
bitmask = 0;
}
- *infomask &= ~(HEAP_HASNULL | HEAP_HASVARWIDTH | HEAP_HASEXTENDED);
+ *infomask &= ~(HEAP_HASNULL | HEAP_HASVARWIDTH | HEAP_HASEXTERNAL);
for (i = 0; i < numberOfAttributes; i++)
{
*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);
data += data_length;
}
+
+ Assert((data - start) == data_size);
}
/* ----------------------------------------------------------------
* 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
{
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:
* ----------------
*/
+#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 +
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)
}
}
- /*
- * 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);
}
/* ----------------
{
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;
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
values,
isnull,
(char *) td + hoff,
+ data_len,
&td->t_infomask,
(hasnull ? td->t_bits : NULL));
{
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;
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
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.
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;
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 */
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;
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 */
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;
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 */
bool *isnull)
{
MinimalTuple tuple; /* return tuple */
- unsigned long len;
+ Size len, data_len;
int hoff;
bool hasnull = false;
Form_pg_attribute *att = tupleDescriptor->attrs;
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.
values,
isnull,
(char *) tuple + hoff,
+ data_len,
&tuple->t_infomask,
(hasnull ? tuple->t_bits : NULL));
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
char *tp; /* tuple pointer */
IndexTuple tuple; /* return tuple */
Size size,
+ data_size,
hoff;
int i;
unsigned short infomask = 0;
*/
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;
}
* 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]);
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);
#endif
isnull,
(char *) tp + hoff,
+ data_size,
&tupmask,
(hasnull ? (bits8 *) tp + sizeof(IndexTupleData) : NULL));
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:
*
{
#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 +
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)
{
}
}
- /*
- * 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);
}
/*
*
*
* 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
#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))
{
* 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))
{
*/
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;
}
* 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;
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);
* 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
{
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))
* 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
{
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
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
* tuple.
*/
toast_action[i] = 'p';
- toast_sizes[i] = VARSIZE(toast_values[i]);
continue;
}
}
/*
* For INSERT simply get the new value
*/
- new_value = (varattrib *) DatumGetPointer(toast_values[i]);
+ new_value = (struct varlena *) DatumGetPointer(toast_values[i]);
}
/*
/*
* Remember the size of this attribute
*/
- toast_sizes[i] = VARSIZE(new_value);
+ toast_sizes[i] = VARSIZE_ANY(new_value);
}
else
{
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;
{
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;
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;
/*------
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;
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;
{
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;
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;
/*--------
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
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.
toast_values,
toast_isnull,
(char *) new_data + olddata->t_hoff,
+ new_data_len,
&(new_data->t_infomask),
has_nulls ? new_data->t_bits : NULL);
}
* 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
HeapTupleHeader olddata;
HeapTupleHeader new_data;
int32 new_len;
+ int32 new_data_len;
HeapTupleData tmptup;
Form_pg_attribute *att;
int numAttrs;
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);
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);
toast_values,
toast_isnull,
(char *) new_data + olddata->t_hoff,
+ new_data_len,
&(new_data->t_infomask),
has_nulls ? new_data->t_bits : NULL);
* 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
* 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
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;
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
*/
}
/*
- * 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);
}
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;
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);
/*
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
/* ----------
* 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;
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);
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
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
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++;
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
/* ----------
* 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;
IndexScanDesc toastscan;
HeapTuple ttup;
TupleDesc toasttupDesc;
- varattrib *result;
+ struct varlena *result;
+ struct varatt_external toast_pointer;
int32 attrsize;
int32 residx;
int32 nextidx;
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)
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! */
/*
* 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);
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:
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
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
memcpy(VARDATA(result) +
(residx * TOAST_MAX_CHUNK_SIZE - sliceoffset) + chcpystrt,
- VARDATA(chunk) + chcpystrt,
+ chunkdata + chcpystrt,
(chcpyend - chcpystrt) + 1);
nextidx++;
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
* 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 $
*
*-------------------------------------------------------------------------
*/
{
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 */
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
*/
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
*/
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
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
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;
}
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
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);
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);
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);
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);
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
/* 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,
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')
/* 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,
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 */
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);
}
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;
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;
}
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,
/* 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,
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 */
{
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)
{
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 */
{
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)
{
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 */
{
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 */
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;
/* 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)
{
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)
{
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;
/* 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);
}
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;
}
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
* 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.
*-------------------------------------------------------------------------
*/
(errcode(ERRCODE_DATA_EXCEPTION),
errmsg("invalid Datum pointer")));
- size = (Size) VARSIZE(s);
+ size = (Size) VARSIZE_ANY(s);
}
else if (typLen == -2)
{
/*
* 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
*/
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)
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
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;
}
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 &&
/* 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++)
errdetail("Value has bits set to right of mask.")));
}
+ SET_INET_VARSIZE(addr);
+
return addr;
}
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;
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;
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;
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);
}
/*
*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;
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);
}
*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);
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);
}
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);
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);
}
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;
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);
}
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);
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);
}
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,
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);
}
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,
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);
}
{
inet *dst;
- dst = (inet *) palloc0(VARHDRSZ + sizeof(inet_struct));
+ dst = (inet *) palloc0(sizeof(inet));
{
int nb = ip_addrsize(ip);
(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;
}
*
* 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"
/*
* 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;
}
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
#include "access/hash.h"
+#include "access/tuptoaster.h"
#include "libpq/pqformat.h"
#include "utils/array.h"
#include "utils/builtins.h"
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);
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;
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)
/* 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;
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);
}
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)
/* 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)
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);
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);
}
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] != ' ')
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 */
/* 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);
}
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);
}
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;
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);
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;
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);
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;
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);
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;
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);
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;
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);
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;
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);
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;
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);
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;
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);
}
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;
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);
}
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);
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
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;
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;
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 == '\\')
{
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);
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));
}
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)));
}
}
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;
/* 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);
}
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);
}
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);
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);
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);
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);
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);
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);
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);
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);
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);
{
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;
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);
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);
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);
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);
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);
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);
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);
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;
/* 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);
}
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;
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);
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,
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);
}
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,
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);
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)
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);
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);
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);
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);
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);
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);
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);
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;
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 */
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")));
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")));
* 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 $
*
*-------------------------------------------------------------------------
*/
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 */
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
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;
}
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 */
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;
}
/*-------------------------------------------------------------------------
* 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 $
*
*-------------------------------------------------------------------------
*/
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) &&
* 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));
}
* 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 $
*
*-------------------------------------------------------------------------
*/
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);
* 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 $
*
*-------------------------------------------------------------------------
*/
#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 */
* 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)
#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)
* 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 $
*
*-------------------------------------------------------------------------
*/
#endif /* SIZEOF_DATUM == 8 */
/*
- * att_align aligns the given offset as needed for a datum of alignment
- * requirement attalign. The cases are tested in what is hopefully something
- * like their frequency of occurrence.
+ * att_align_datum aligns the given offset as needed for a datum of alignment
+ * requirement attalign and typlen attlen. attdatum is the Datum variable
+ * we intend to pack into a tuple (it's only accessed if we are dealing with
+ * a varlena type). Note that this assumes the Datum will be stored as-is;
+ * callers that are intending to convert non-short varlena datums to short
+ * format have to account for that themselves.
*/
-#define att_align(cur_offset, attalign) \
+#define att_align_datum(cur_offset, attalign, attlen, attdatum) \
+( \
+ ((attlen) == -1 && VARATT_IS_SHORT(DatumGetPointer(attdatum))) ? (long) (cur_offset) : \
+ att_align_nominal(cur_offset, attalign) \
+)
+
+/*
+ * att_align_pointer performs the same calculation as att_align_datum,
+ * but is used when walking a tuple. attptr is the current actual data
+ * pointer; when accessing a varlena field we have to "peek" to see if we
+ * are looking at a pad byte or the first byte of a 1-byte-header datum.
+ * (A zero byte must be either a pad byte, or the first byte of a correctly
+ * aligned 4-byte length word; in either case we can align safely. A non-zero
+ * byte must be either a 1-byte length word, or the first byte of a correctly
+ * aligned 4-byte length word; in either case we need not align.)
+ *
+ * Note: some callers pass a "char *" pointer for cur_offset. This is
+ * a bit of a hack but works OK on all known platforms. It ought to be
+ * cleaned up someday, though.
+ */
+#define att_align_pointer(cur_offset, attalign, attlen, attptr) \
+( \
+ ((attlen) == -1 && VARATT_NOT_PAD_BYTE(attptr)) ? (long) (cur_offset) : \
+ att_align_nominal(cur_offset, attalign) \
+)
+
+/*
+ * att_align_nominal aligns the given offset as needed for a datum of alignment
+ * requirement attalign, ignoring any consideration of packed varlena datums.
+ * There are three main use cases for using this macro directly:
+ * * we know that the att in question is not varlena (attlen != -1);
+ * in this case it is cheaper than the above macros and just as good.
+ * * we need to estimate alignment padding cost abstractly, ie without
+ * reference to a real tuple. We must assume the worst case that
+ * all varlenas are aligned.
+ * * within arrays, we unconditionally align varlenas (XXX this should be
+ * revisited, probably).
+ *
+ * The attalign cases are tested in what is hopefully something like their
+ * frequency of occurrence.
+ */
+#define att_align_nominal(cur_offset, attalign) \
( \
((attalign) == 'i') ? INTALIGN(cur_offset) : \
- (((attalign) == 'c') ? ((long)(cur_offset)) : \
+ (((attalign) == 'c') ? (long) (cur_offset) : \
(((attalign) == 'd') ? DOUBLEALIGN(cur_offset) : \
- ( \
+ ( \
AssertMacro((attalign) == 's'), \
SHORTALIGN(cur_offset) \
- ))) \
+ ))) \
)
/*
- * att_addlength increments the given offset by the length of the attribute.
- * attval is only accessed if we are dealing with a variable-length attribute.
+ * att_addlength_datum increments the given offset by the space needed for
+ * the given Datum variable. attdatum is only accessed if we are dealing
+ * with a variable-length attribute.
+ */
+#define att_addlength_datum(cur_offset, attlen, attdatum) \
+ att_addlength_pointer(cur_offset, attlen, DatumGetPointer(attdatum))
+
+/*
+ * att_addlength_pointer performs the same calculation as att_addlength_datum,
+ * but is used when walking a tuple --- attptr is the pointer to the field
+ * within the tuple.
+ *
+ * Note: some callers pass a "char *" pointer for cur_offset. This is
+ * actually perfectly OK, but probably should be cleaned up along with
+ * the same practice for att_align_pointer.
*/
-#define att_addlength(cur_offset, attlen, attval) \
+#define att_addlength_pointer(cur_offset, attlen, attptr) \
( \
((attlen) > 0) ? \
( \
) \
: (((attlen) == -1) ? \
( \
- (cur_offset) + VARSIZE(DatumGetPointer(attval)) \
+ (cur_offset) + VARSIZE_ANY(attptr) \
) \
: \
( \
AssertMacro((attlen) == -2), \
- (cur_offset) + (strlen(DatumGetCString(attval)) + 1) \
+ (cur_offset) + (strlen((char *) (attptr)) + 1) \
)) \
)
*
* 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 $
*
*-------------------------------------------------------------------------
*/
* 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() -
* 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() -
* (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);
* 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 $
*
*-------------------------------------------------------------------------
*/
*/
/* yyyymmddN */
-#define CATALOG_VERSION_NO 200704012
+#define CATALOG_VERSION_NO 200704051
#endif
* 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
* '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
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
* 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 $
*
*-------------------------------------------------------------------------
*/
* 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.
*/
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))
#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
#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))
#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))
/* 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
* 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 $
*
*-------------------------------------------------------------------------
*/
* ----------------------------------------------------------------
*/
-/* ----------------
- * 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))
/* ----------------------------------------------------------------
* 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 $
*
*-------------------------------------------------------------------------
*/
/*
* 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;
/*
/*
* 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)
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();
+ ^
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
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();
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
--