Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Postgres/src/backend/access/common/   (Postgres Database Version 18.4©)  Datei vom 11.4.2026 mit Größe 16 kB image not shown  

Quelle  detoast.c

  Sprache: C
 

/*-------------------------------------------------------------------------
 *
 * detoast.c
 *   Retrieve compressed or external variable size attributes.
 *
 * Copyright (c) 2000-2025, PostgreSQL Global Development Group
 *
 * IDENTIFICATION
 *   src/backend/access/common/detoast.c
 *
 *-------------------------------------------------------------------------
 */


#include "postgres.h"

#include "access/detoast.h"
#include "access/table.h"
#include "access/tableam.h"
#include "access/toast_internals.h"
#include "common/int.h"
#include "common/pg_lzcompress.h"
#include "utils/expandeddatum.h"
#include "utils/rel.h"

static struct varlena *toast_fetch_datum(struct varlena *attr);
static struct varlena *toast_fetch_datum_slice(struct varlena *attr,
              int32 sliceoffset,
              int32 slicelength);
static struct varlena *toast_decompress_datum(struct varlena *attr);
static struct varlena *toast_decompress_datum_slice(struct varlena *attr, int32 slicelength);

/* ----------
 * detoast_external_attr -
 *
 * Public entry point to get back a toasted value from
 * external source (possibly still in compressed format).
 *
 * This will return a datum that contains all the data internally, ie, not
 * relying on external storage or memory, but it can still be compressed or
 * have a short header.  Note some callers assume that if the input is an
 * EXTERNAL datum, the result will be a pfree'able chunk.
 * ----------
 */

struct varlena *
detoast_external_attr(struct varlena *attr)
{
 struct varlena *result;

 if (VARATT_IS_EXTERNAL_ONDISK(attr))
 {
  /*
   * This is an external stored plain value
 */

  result = toast_fetch_datum(attr);
 }
 else if (VARATT_IS_EXTERNAL_INDIRECT(attr))
 {
  /*
   * This is an indirect pointer --- dereference it
 */

  struct varatt_indirect redirect;

  VARATT_EXTERNAL_GET_POINTER(redirect, attr);
  attr = (struct varlena *) redirect.pointer;

  /* nested indirect Datums aren't allowed */
  Assert(!VARATT_IS_EXTERNAL_INDIRECT(attr));

  /* recurse if value is still external in some other way */
  if (VARATT_IS_EXTERNAL(attr))
   return detoast_external_attr(attr);

  /*
   * Copy into the caller's memory context, in case caller tries to
   * pfree the result.
 */

  result = (struct varlena *) palloc(VARSIZE_ANY(attr));
  memcpy(result, attr, VARSIZE_ANY(attr));
 }
 else if (VARATT_IS_EXTERNAL_EXPANDED(attr))
 {
  /*
   * This is an expanded-object pointer --- get flat format
 */

  ExpandedObjectHeader *eoh;
  Size  resultsize;

  eoh = DatumGetEOHP(PointerGetDatum(attr));
  resultsize = EOH_get_flat_size(eoh);
  result = (struct varlena *) palloc(resultsize);
  EOH_flatten_into(eoh, result, resultsize);
 }
 else
 {
  /*
   * This is a plain value inside of the main tuple - why am I called?
 */

  result = attr;
 }

 return result;
}


/* ----------
 * detoast_attr -
 *
 * Public entry point to get back a toasted value from compression
 * or external storage.  The result is always non-extended varlena form.
 *
 * Note some callers assume that if the input is an EXTERNAL or COMPRESSED
 * datum, the result will be a pfree'able chunk.
 * ----------
 */

struct varlena *
detoast_attr(struct varlena *attr)
{
 if (VARATT_IS_EXTERNAL_ONDISK(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))
  {
   struct varlena *tmp = attr;

   attr = toast_decompress_datum(tmp);
   pfree(tmp);
  }
 }
 else if (VARATT_IS_EXTERNAL_INDIRECT(attr))
 {
  /*
   * This is an indirect pointer --- dereference it
 */

  struct varatt_indirect redirect;

  VARATT_EXTERNAL_GET_POINTER(redirect, attr);
  attr = (struct varlena *) redirect.pointer;

  /* nested indirect Datums aren't allowed */
  Assert(!VARATT_IS_EXTERNAL_INDIRECT(attr));

  /* recurse in case value is still extended in some other way */
  attr = detoast_attr(attr);

  /* if it isn't, we'd better copy it */
  if (attr == (struct varlena *) redirect.pointer)
  {
   struct varlena *result;

   result = (struct varlena *) palloc(VARSIZE_ANY(attr));
   memcpy(result, attr, VARSIZE_ANY(attr));
   attr = result;
  }
 }
 else if (VARATT_IS_EXTERNAL_EXPANDED(attr))
 {
  /*
   * This is an expanded-object pointer --- get flat format
 */

  attr = detoast_external_attr(attr);
  /* flatteners are not allowed to produce compressed/short output */
  Assert(!VARATT_IS_EXTENDED(attr));
 }
 else if (VARATT_IS_COMPRESSED(attr))
 {
  /*
   * This is a compressed value inside of the main tuple
 */

  attr = toast_decompress_datum(attr);
 }
 else if (VARATT_IS_SHORT(attr))
 {
  /*
   * This is a short-header varlena --- convert to 4-byte header format
 */

  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 attr;
}


/* ----------
 * detoast_attr_slice -
 *
 *  Public entry point to get back part of a toasted value
 *  from compression or external storage.
 *
 * sliceoffset is where to start (zero or more)
 * If slicelength < 0, return everything beyond sliceoffset
 * ----------
 */

struct varlena *
detoast_attr_slice(struct varlena *attr,
       int32 sliceoffset, int32 slicelength)
{
 struct varlena *preslice;
 struct varlena *result;
 char    *attrdata;
 int32  slicelimit;
 int32  attrsize;

 if (sliceoffset < 0)
  elog(ERROR, "invalid sliceoffset: %d", sliceoffset);

 /*
  * Compute slicelimit = offset + length, or -1 if we must fetch all of the
  * value.  In case of integer overflow, we must fetch all.
 */

 if (slicelength < 0)
  slicelimit = -1;
 else if (pg_add_s32_overflow(sliceoffset, slicelength, &slicelimit))
  slicelength = slicelimit = -1;

 if (VARATT_IS_EXTERNAL_ONDISK(attr))
 {
  struct varatt_external toast_pointer;

  VARATT_EXTERNAL_GET_POINTER(toast_pointer, attr);

  /* fast path for non-compressed external datums */
  if (!VARATT_EXTERNAL_IS_COMPRESSED(toast_pointer))
   return toast_fetch_datum_slice(attr, sliceoffset, slicelength);

  /*
   * For compressed values, we need to fetch enough slices to decompress
   * at least the requested part (when a prefix is requested).
   * Otherwise, just fetch all slices.
 */

  if (slicelimit >= 0)
  {
   int32  max_size = VARATT_EXTERNAL_GET_EXTSIZE(toast_pointer);

   /*
    * Determine maximum amount of compressed data needed for a prefix
    * of a given length (after decompression).
    *
    * At least for now, if it's LZ4 data, we'll have to fetch the
    * whole thing, because there doesn't seem to be an API call to
    * determine how much compressed data we need to be sure of being
    * able to decompress the required slice.
 */

   if (VARATT_EXTERNAL_GET_COMPRESS_METHOD(toast_pointer) ==
    TOAST_PGLZ_COMPRESSION_ID)
    max_size = pglz_maximum_compressed_size(slicelimit, max_size);

   /*
    * Fetch enough compressed slices (compressed marker will get set
    * automatically).
 */

   preslice = toast_fetch_datum_slice(attr, 0, max_size);
  }
  else
   preslice = toast_fetch_datum(attr);
 }
 else if (VARATT_IS_EXTERNAL_INDIRECT(attr))
 {
  struct varatt_indirect redirect;

  VARATT_EXTERNAL_GET_POINTER(redirect, attr);

  /* nested indirect Datums aren't allowed */
  Assert(!VARATT_IS_EXTERNAL_INDIRECT(redirect.pointer));

  return detoast_attr_slice(redirect.pointer,
          sliceoffset, slicelength);
 }
 else if (VARATT_IS_EXTERNAL_EXPANDED(attr))
 {
  /* pass it off to detoast_external_attr to flatten */
  preslice = detoast_external_attr(attr);
 }
 else
  preslice = attr;

 Assert(!VARATT_IS_EXTERNAL(preslice));

 if (VARATT_IS_COMPRESSED(preslice))
 {
  struct varlena *tmp = preslice;

  /* Decompress enough to encompass the slice and the offset */
  if (slicelimit >= 0)
   preslice = toast_decompress_datum_slice(tmp, slicelimit);
  else
   preslice = toast_decompress_datum(tmp);

  if (tmp != attr)
   pfree(tmp);
 }

 if (VARATT_IS_SHORT(preslice))
 {
  attrdata = VARDATA_SHORT(preslice);
  attrsize = VARSIZE_SHORT(preslice) - VARHDRSZ_SHORT;
 }
 else
 {
  attrdata = VARDATA(preslice);
  attrsize = VARSIZE(preslice) - VARHDRSZ;
 }

 /* slicing of datum for compressed cases and plain value */

 if (sliceoffset >= attrsize)
 {
  sliceoffset = 0;
  slicelength = 0;
 }
 else if (slicelength < 0 || slicelimit > attrsize)
  slicelength = attrsize - sliceoffset;

 result = (struct varlena *) palloc(slicelength + VARHDRSZ);
 SET_VARSIZE(result, slicelength + VARHDRSZ);

 memcpy(VARDATA(result), attrdata + sliceoffset, slicelength);

 if (preslice != attr)
  pfree(preslice);

 return result;
}

/* ----------
 * toast_fetch_datum -
 *
 * Reconstruct an in memory Datum from the chunks saved
 * in the toast relation
 * ----------
 */

static struct varlena *
toast_fetch_datum(struct varlena *attr)
{
 Relation toastrel;
 struct varlena *result;
 struct varatt_external toast_pointer;
 int32  attrsize;

 if (!VARATT_IS_EXTERNAL_ONDISK(attr))
  elog(ERROR, "toast_fetch_datum shouldn't be called for non-ondisk datums");

 /* Must copy to access aligned fields */
 VARATT_EXTERNAL_GET_POINTER(toast_pointer, attr);

 attrsize = VARATT_EXTERNAL_GET_EXTSIZE(toast_pointer);

 result = (struct varlena *) palloc(attrsize + VARHDRSZ);

 if (VARATT_EXTERNAL_IS_COMPRESSED(toast_pointer))
  SET_VARSIZE_COMPRESSED(result, attrsize + VARHDRSZ);
 else
  SET_VARSIZE(result, attrsize + VARHDRSZ);

 if (attrsize == 0)
  return result;   /* Probably shouldn't happen, but just in
 * case. */


 /*
  * Open the toast relation and its indexes
 */

 toastrel = table_open(toast_pointer.va_toastrelid, AccessShareLock);

 /* Fetch all chunks */
 table_relation_fetch_toast_slice(toastrel, toast_pointer.va_valueid,
          attrsize, 0, attrsize, result);

 /* Close toast table */
 table_close(toastrel, AccessShareLock);

 return result;
}

/* ----------
 * toast_fetch_datum_slice -
 *
 * Reconstruct a segment of a Datum from the chunks saved
 * in the toast relation
 *
 * Note that this function supports non-compressed external datums
 * and compressed external datums (in which case the requested slice
 * has to be a prefix, i.e. sliceoffset has to be 0).
 * ----------
 */

static struct varlena *
toast_fetch_datum_slice(struct varlena *attr, int32 sliceoffset,
      int32 slicelength)
{
 Relation toastrel;
 struct varlena *result;
 struct varatt_external toast_pointer;
 int32  attrsize;

 if (!VARATT_IS_EXTERNAL_ONDISK(attr))
  elog(ERROR, "toast_fetch_datum_slice shouldn't be called for non-ondisk datums");

 /* Must copy to access aligned fields */
 VARATT_EXTERNAL_GET_POINTER(toast_pointer, attr);

 /*
  * It's nonsense to fetch slices of a compressed datum unless when it's a
  * prefix -- this isn't lo_* we can't return a compressed datum which is
  * meaningful to toast later.
 */

 Assert(!VARATT_EXTERNAL_IS_COMPRESSED(toast_pointer) || 0 == sliceoffset);

 attrsize = VARATT_EXTERNAL_GET_EXTSIZE(toast_pointer);

 if (sliceoffset >= attrsize)
 {
  sliceoffset = 0;
  slicelength = 0;
 }

 /*
  * When fetching a prefix of a compressed external datum, account for the
  * space required by va_tcinfo, which is stored at the beginning as an
  * int32 value.
 */

 if (VARATT_EXTERNAL_IS_COMPRESSED(toast_pointer) && slicelength > 0)
  slicelength = slicelength + sizeof(int32);

 /*
  * Adjust length request if needed.  (Note: our sole caller,
  * detoast_attr_slice, protects us against sliceoffset + slicelength
  * overflowing.)
 */

 if (((sliceoffset + slicelength) > attrsize) || slicelength < 0)
  slicelength = attrsize - sliceoffset;

 result = (struct varlena *) palloc(slicelength + VARHDRSZ);

 if (VARATT_EXTERNAL_IS_COMPRESSED(toast_pointer))
  SET_VARSIZE_COMPRESSED(result, slicelength + VARHDRSZ);
 else
  SET_VARSIZE(result, slicelength + VARHDRSZ);

 if (slicelength == 0)
  return result;   /* Can save a lot of work at this point! */

 /* Open the toast relation */
 toastrel = table_open(toast_pointer.va_toastrelid, AccessShareLock);

 /* Fetch all chunks */
 table_relation_fetch_toast_slice(toastrel, toast_pointer.va_valueid,
          attrsize, sliceoffset, slicelength,
          result);

 /* Close toast table */
 table_close(toastrel, AccessShareLock);

 return result;
}

/* ----------
 * toast_decompress_datum -
 *
 * Decompress a compressed version of a varlena datum
 */

static struct varlena *
toast_decompress_datum(struct varlena *attr)
{
 ToastCompressionId cmid;

 Assert(VARATT_IS_COMPRESSED(attr));

 /*
  * Fetch the compression method id stored in the compression header and
  * decompress the data using the appropriate decompression routine.
 */

 cmid = TOAST_COMPRESS_METHOD(attr);
 switch (cmid)
 {
  case TOAST_PGLZ_COMPRESSION_ID:
   return pglz_decompress_datum(attr);
  case TOAST_LZ4_COMPRESSION_ID:
   return lz4_decompress_datum(attr);
  default:
   elog(ERROR, "invalid compression method id %d", cmid);
   return NULL;  /* keep compiler quiet */
 }
}


/* ----------
 * toast_decompress_datum_slice -
 *
 * Decompress the front of a compressed version of a varlena datum.
 * offset handling happens in detoast_attr_slice.
 * Here we just decompress a slice from the front.
 */

static struct varlena *
toast_decompress_datum_slice(struct varlena *attr, int32 slicelength)
{
 ToastCompressionId cmid;

 Assert(VARATT_IS_COMPRESSED(attr));

 /*
  * Some callers may pass a slicelength that's more than the actual
  * decompressed size.  If so, just decompress normally.  This avoids
  * possibly allocating a larger-than-necessary result object, and may be
  * faster and/or more robust as well.  Notably, some versions of liblz4
  * have been seen to give wrong results if passed an output size that is
  * more than the data's true decompressed size.
 */

 if ((uint32) slicelength >= TOAST_COMPRESS_EXTSIZE(attr))
  return toast_decompress_datum(attr);

 /*
  * Fetch the compression method id stored in the compression header and
  * decompress the data slice using the appropriate decompression routine.
 */

 cmid = TOAST_COMPRESS_METHOD(attr);
 switch (cmid)
 {
  case TOAST_PGLZ_COMPRESSION_ID:
   return pglz_decompress_datum_slice(attr, slicelength);
  case TOAST_LZ4_COMPRESSION_ID:
   return lz4_decompress_datum_slice(attr, slicelength);
  default:
   elog(ERROR, "invalid compression method id %d", cmid);
   return NULL;  /* keep compiler quiet */
 }
}

/* ----------
 * toast_raw_datum_size -
 *
 * Return the raw (detoasted) size of a varlena datum
 * (including the VARHDRSZ header)
 * ----------
 */

Size
toast_raw_datum_size(Datum value)
{
 struct varlena *attr = (struct varlena *) DatumGetPointer(value);
 Size  result;

 if (VARATT_IS_EXTERNAL_ONDISK(attr))
 {
  /* va_rawsize is the size of the original datum -- including header */
  struct varatt_external toast_pointer;

  VARATT_EXTERNAL_GET_POINTER(toast_pointer, attr);
  result = toast_pointer.va_rawsize;
 }
 else if (VARATT_IS_EXTERNAL_INDIRECT(attr))
 {
  struct varatt_indirect toast_pointer;

  VARATT_EXTERNAL_GET_POINTER(toast_pointer, attr);

  /* nested indirect Datums aren't allowed */
  Assert(!VARATT_IS_EXTERNAL_INDIRECT(toast_pointer.pointer));

  return toast_raw_datum_size(PointerGetDatum(toast_pointer.pointer));
 }
 else if (VARATT_IS_EXTERNAL_EXPANDED(attr))
 {
  result = EOH_get_flat_size(DatumGetEOHP(value));
 }
 else if (VARATT_IS_COMPRESSED(attr))
 {
  /* here, va_rawsize is just the payload size */
  result = VARDATA_COMPRESSED_GET_EXTSIZE(attr) + VARHDRSZ;
 }
 else if (VARATT_IS_SHORT(attr))
 {
  /*
   * we have to normalize the header length to VARHDRSZ or else the
   * callers of this function will be confused.
 */

  result = VARSIZE_SHORT(attr) - VARHDRSZ_SHORT + VARHDRSZ;
 }
 else
 {
  /* plain untoasted datum */
  result = VARSIZE(attr);
 }
 return result;
}

/* ----------
 * toast_datum_size
 *
 * Return the physical storage size (possibly compressed) of a varlena datum
 * ----------
 */

Size
toast_datum_size(Datum value)
{
 struct varlena *attr = (struct varlena *) DatumGetPointer(value);
 Size  result;

 if (VARATT_IS_EXTERNAL_ONDISK(attr))
 {
  /*
   * Attribute is stored externally - return the extsize whether
   * compressed or not.  We do not count the size of the toast pointer
   * ... should we?
 */

  struct varatt_external toast_pointer;

  VARATT_EXTERNAL_GET_POINTER(toast_pointer, attr);
  result = VARATT_EXTERNAL_GET_EXTSIZE(toast_pointer);
 }
 else if (VARATT_IS_EXTERNAL_INDIRECT(attr))
 {
  struct varatt_indirect toast_pointer;

  VARATT_EXTERNAL_GET_POINTER(toast_pointer, attr);

  /* nested indirect Datums aren't allowed */
  Assert(!VARATT_IS_EXTERNAL_INDIRECT(attr));

  return toast_datum_size(PointerGetDatum(toast_pointer.pointer));
 }
 else if (VARATT_IS_EXTERNAL_EXPANDED(attr))
 {
  result = EOH_get_flat_size(DatumGetEOHP(value));
 }
 else if (VARATT_IS_SHORT(attr))
 {
  result = VARSIZE_SHORT(attr);
 }
 else
 {
  /*
   * Attribute is stored inline either compressed or not, just calculate
   * the size of the datum in either case.
 */

  result = VARSIZE(attr);
 }
 return result;
}

Messung V0.5 in Prozent
C=91 H=95 G=92

¤ Dauer der Verarbeitung: 0.20 Sekunden  (vorverarbeitet am  2026-08-08) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.