v27-0014-Design-to-extend-the-varattrib_4b-and-toast-poin.patch
application/octet-stream
Filename: v27-0014-Design-to-extend-the-varattrib_4b-and-toast-poin.patch
Type: application/octet-stream
Part: 1
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: format-patch
Series: patch v27-0014
Subject: Design to extend the varattrib_4b and toast pointer to support of multiple TOAST compression algorithms.
| File | + | − |
|---|---|---|
| contrib/amcheck/verify_heapam.c | 4 | 1 |
| src/backend/access/common/detoast.c | 3 | 3 |
| src/backend/access/common/toast_external.c | 162 | 14 |
| src/backend/access/common/toast_internals.c | 13 | 14 |
| src/backend/access/heap/heaptoast.c | 17 | 32 |
| src/backend/access/table/toast_helper.c | 32 | 9 |
| src/include/access/toast_compression.h | 6 | 0 |
| src/include/access/toast_helper.h | 3 | 1 |
| src/include/access/toast_internals.h | 2 | 17 |
| src/include/varatt.h | 97 | 18 |
From 43f9fc8c7b1ea13756d089bae8dadf0786ed5494 Mon Sep 17 00:00:00 2001
From: Nikhil Kumar Veldanda <veldanda.nikhilkumar17@gmail.com>
Date: Sat, 19 Jul 2025 23:07:41 +0000
Subject: [PATCH v27 14/15] Design to extend the varattrib_4b and toast pointer
to support of multiple TOAST compression algorithms.
---
contrib/amcheck/verify_heapam.c | 5 +-
src/backend/access/common/detoast.c | 6 +-
src/backend/access/common/toast_external.c | 176 ++++++++++++++++++--
src/backend/access/common/toast_internals.c | 27 ++-
src/backend/access/heap/heaptoast.c | 49 ++----
src/backend/access/table/toast_helper.c | 41 ++++-
src/include/access/toast_compression.h | 6 +
src/include/access/toast_helper.h | 4 +-
src/include/access/toast_internals.h | 19 +--
src/include/varatt.h | 115 +++++++++++--
10 files changed, 339 insertions(+), 109 deletions(-)
diff --git a/contrib/amcheck/verify_heapam.c b/contrib/amcheck/verify_heapam.c
index 958b1451b4f..3a23dddcff4 100644
--- a/contrib/amcheck/verify_heapam.c
+++ b/contrib/amcheck/verify_heapam.c
@@ -1733,7 +1733,10 @@ check_tuple_attribute(HeapCheckContext *ctx)
{
uint8 va_tag = VARTAG_EXTERNAL(tp + ctx->offset);
- if (va_tag != VARTAG_ONDISK_OID && va_tag != VARTAG_ONDISK_INT8)
+ if (va_tag != VARTAG_ONDISK_OID &&
+ va_tag != VARTAG_ONDISK_INT8 &&
+ va_tag != VARTAG_ONDISK_CE_INT8 &&
+ va_tag != VARTAG_ONDISK_CE_OID)
{
report_corruption(ctx,
psprintf("toasted attribute has unexpected TOAST tag %u",
diff --git a/src/backend/access/common/detoast.c b/src/backend/access/common/detoast.c
index 684e1b0b7d3..34a3f7c6694 100644
--- a/src/backend/access/common/detoast.c
+++ b/src/backend/access/common/detoast.c
@@ -484,7 +484,7 @@ toast_decompress_datum(struct varlena *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);
+ cmid = VARDATA_COMPRESSED_GET_COMPRESS_METHOD(attr);
switch (cmid)
{
case TOAST_PGLZ_COMPRESSION_ID:
@@ -520,14 +520,14 @@ toast_decompress_datum_slice(struct varlena *attr, int32 slicelength)
* 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))
+ if ((uint32) slicelength >= VARDATA_COMPRESSED_GET_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);
+ cmid = VARDATA_COMPRESSED_GET_COMPRESS_METHOD(attr);
switch (cmid)
{
case TOAST_PGLZ_COMPRESSION_ID:
diff --git a/src/backend/access/common/toast_external.c b/src/backend/access/common/toast_external.c
index 0e79ac8acae..5bf17ed7182 100644
--- a/src/backend/access/common/toast_external.c
+++ b/src/backend/access/common/toast_external.c
@@ -38,6 +38,15 @@ static struct varlena *ondisk_oid_create_external_data(toast_external_data data)
static uint64 ondisk_oid_get_new_value(Relation toastrel, Oid indexid,
AttrNumber attnum);
+/* Callbacks for VARTAG_ONDISK_CE_OID */
+static void ondisk_ce_oid_to_external_data(struct varlena *attr,
+ toast_external_data *data);
+static struct varlena *ondisk_ce_oid_create_external_data(toast_external_data data);
+
+/* Callbacks for VARTAG_ONDISK_CE_INT8 */
+static void ondisk_ce_int8_to_external_data(struct varlena *attr,
+ toast_external_data *data);
+static struct varlena *ondisk_ce_int8_create_external_data(toast_external_data data);
/*
* Size of an EXTERNAL datum that contains a standard TOAST pointer
@@ -51,6 +60,18 @@ static uint64 ondisk_oid_get_new_value(Relation toastrel, Oid indexid,
*/
#define TOAST_POINTER_OID_SIZE (VARHDRSZ_EXTERNAL + sizeof(varatt_external_oid))
+/*
+ * Size of an EXTERNAL datum that contains a TOAST pointer which supports extended compression methods
+ * (OID value).
+ */
+#define TOAST_POINTER_CE_OID_SIZE (VARHDRSZ_EXTERNAL + sizeof(varatt_external_ce_oid))
+
+/*
+ * Size of an EXTERNAL datum that contains a TOAST pointer which supports extended compression methods
+ * (int8 value).
+ */
+#define TOAST_POINTER_CE_INT8_SIZE (VARHDRSZ_EXTERNAL + sizeof(varatt_external_ce_int8))
+
/*
* For now there are only two types, all defined in this file. For now this
* is the maximum value of vartag_external, which is a historical choice.
@@ -72,6 +93,13 @@ static const toast_external_info toast_external_infos[TOAST_EXTERNAL_INFO_SIZE]
.create_external_data = ondisk_int8_create_external_data,
.get_new_value = ondisk_int8_get_new_value,
},
+ [VARTAG_ONDISK_CE_INT8] = {
+ .toast_pointer_size = TOAST_POINTER_CE_INT8_SIZE,
+ .maximum_chunk_size = TOAST_MAX_CHUNK_SIZE_INT8,
+ .to_external_data = ondisk_ce_int8_to_external_data,
+ .create_external_data = ondisk_ce_int8_create_external_data,
+ .get_new_value = ondisk_int8_get_new_value,
+ },
[VARTAG_ONDISK_OID] = {
.toast_pointer_size = TOAST_POINTER_OID_SIZE,
.maximum_chunk_size = TOAST_MAX_CHUNK_SIZE_OID,
@@ -79,6 +107,13 @@ static const toast_external_info toast_external_infos[TOAST_EXTERNAL_INFO_SIZE]
.create_external_data = ondisk_oid_create_external_data,
.get_new_value = ondisk_oid_get_new_value,
},
+ [VARTAG_ONDISK_CE_OID] = {
+ .toast_pointer_size = TOAST_POINTER_CE_OID_SIZE,
+ .maximum_chunk_size = TOAST_MAX_CHUNK_SIZE_OID,
+ .to_external_data = ondisk_ce_oid_to_external_data,
+ .create_external_data = ondisk_ce_oid_create_external_data,
+ .get_new_value = ondisk_oid_get_new_value,
+ },
};
@@ -108,7 +143,7 @@ toast_external_info_get_pointer_size(uint8 tag)
static void
ondisk_int8_to_external_data(struct varlena *attr, toast_external_data *data)
{
- varatt_external_int8 external;
+ varatt_external_int8 external;
VARATT_EXTERNAL_GET_POINTER(external, attr);
data->rawsize = external.va_rawsize;
@@ -117,7 +152,7 @@ ondisk_int8_to_external_data(struct varlena *attr, toast_external_data *data)
if (VARATT_EXTERNAL_IS_COMPRESSED(external))
{
data->extsize = VARATT_EXTERNAL_GET_EXTSIZE(external);
- data->compression_method = VARATT_EXTERNAL_GET_COMPRESS_METHOD(external);
+ data->compression_method = external.va_extinfo >> VARLENA_EXTSIZE_BITS;
}
else
{
@@ -141,10 +176,10 @@ ondisk_int8_create_external_data(toast_external_data data)
if (data.compression_method != TOAST_INVALID_COMPRESSION_ID)
{
+ /* Regular variants only support basic compression methods */
+ Assert(!CompressionMethodIdIsExtended(data.compression_method));
/* Set size and compression method, in a single field. */
- VARATT_EXTERNAL_SET_SIZE_AND_COMPRESS_METHOD(external,
- data.extsize,
- data.compression_method);
+ external.va_extinfo = (uint32) data.extsize | ((uint32) data.compression_method << VARLENA_EXTSIZE_BITS);
}
else
external.va_extinfo = data.extsize;
@@ -165,8 +200,8 @@ ondisk_int8_get_new_value(Relation toastrel, Oid indexid,
AttrNumber attnum)
{
uint64 new_value;
- SysScanDesc scan;
- ScanKeyData key;
+ SysScanDesc scan;
+ ScanKeyData key;
bool collides = false;
retry:
@@ -181,8 +216,8 @@ retry:
CHECK_FOR_INTERRUPTS();
/*
- * Check if the new value picked already exists in the toast relation.
- * If there is a conflict, retry.
+ * Check if the new value picked already exists in the toast relation. If
+ * there is a conflict, retry.
*/
ScanKeyInit(&key,
attnum,
@@ -206,7 +241,7 @@ retry:
static void
ondisk_oid_to_external_data(struct varlena *attr, toast_external_data *data)
{
- varatt_external_oid external;
+ varatt_external_oid external;
VARATT_EXTERNAL_GET_POINTER(external, attr);
data->rawsize = external.va_rawsize;
@@ -218,7 +253,7 @@ ondisk_oid_to_external_data(struct varlena *attr, toast_external_data *data)
if (VARATT_EXTERNAL_IS_COMPRESSED(external))
{
data->extsize = VARATT_EXTERNAL_GET_EXTSIZE(external);
- data->compression_method = VARATT_EXTERNAL_GET_COMPRESS_METHOD(external);
+ data->compression_method = external.va_extinfo >> VARLENA_EXTSIZE_BITS;
}
else
{
@@ -240,10 +275,10 @@ ondisk_oid_create_external_data(toast_external_data data)
if (data.compression_method != TOAST_INVALID_COMPRESSION_ID)
{
+ /* Regular variants only support basic compression methods */
+ Assert(!CompressionMethodIdIsExtended(data.compression_method));
/* Set size and compression method, in a single field. */
- VARATT_EXTERNAL_SET_SIZE_AND_COMPRESS_METHOD(external,
- data.extsize,
- data.compression_method);
+ external.va_extinfo = (uint32) data.extsize | ((uint32) data.compression_method << VARLENA_EXTSIZE_BITS);
}
else
external.va_extinfo = data.extsize;
@@ -264,3 +299,116 @@ ondisk_oid_get_new_value(Relation toastrel, Oid indexid,
{
return GetNewOidWithIndex(toastrel, indexid, attnum);
}
+
+/* Callbacks for VARTAG_ONDISK_CE_OID */
+static void
+ondisk_ce_oid_to_external_data(struct varlena *attr, toast_external_data *data)
+{
+ varatt_external_ce_oid external;
+
+ VARATT_EXTERNAL_GET_POINTER(external, attr);
+ data->rawsize = external.va_rawsize;
+
+ /*
+ * External size and compression methods are stored in the different
+ * fields, extract.
+ */
+ if (VARATT_EXTERNAL_IS_COMPRESSED(external))
+ {
+ data->extsize = VARATT_EXTERNAL_GET_EXTSIZE(external);
+ data->compression_method = VARATT_CE_GET_COMPRESS_METHOD(external.va_ecinfo);
+ }
+ else
+ {
+ data->extsize = external.va_extinfo;
+ data->compression_method = TOAST_INVALID_COMPRESSION_ID;
+ }
+
+ data->value = (uint64) external.va_valueid;
+ data->toastrelid = external.va_toastrelid;
+}
+
+static struct varlena *
+ondisk_ce_oid_create_external_data(toast_external_data data)
+{
+ struct varlena *result = NULL;
+ varatt_external_ce_oid external;
+
+ external.va_rawsize = data.rawsize;
+
+ if (data.compression_method != TOAST_INVALID_COMPRESSION_ID)
+ {
+ Assert(CompressionMethodIdIsExtended(data.compression_method));
+ /* Set size and compression method. */
+ external.va_extinfo = (uint32) data.extsize | (VARATT_CE_FLAG << VARLENA_EXTSIZE_BITS);
+ VARATT_CE_SET_COMPRESS_METHOD(external.va_ecinfo, data.compression_method);
+ }
+ else
+ external.va_extinfo = data.extsize;
+
+ external.va_toastrelid = data.toastrelid;
+ external.va_valueid = (Oid) data.value;
+
+ result = (struct varlena *) palloc(TOAST_POINTER_CE_OID_SIZE);
+ SET_VARTAG_EXTERNAL(result, VARTAG_ONDISK_CE_OID);
+ memcpy(VARDATA_EXTERNAL(result), &external, sizeof(external));
+
+ return result;
+}
+
+/* Callbacks for VARTAG_ONDISK_CE_INT8 */
+static void
+ondisk_ce_int8_to_external_data(struct varlena *attr, toast_external_data *data)
+{
+ varatt_external_ce_int8 external;
+
+ VARATT_EXTERNAL_GET_POINTER(external, attr);
+ data->rawsize = external.va_rawsize;
+
+ /*
+ * External size and compression methods are stored in the different
+ * fields
+ */
+ if (VARATT_EXTERNAL_IS_COMPRESSED(external))
+ {
+ data->extsize = VARATT_EXTERNAL_GET_EXTSIZE(external);
+ data->compression_method = VARATT_CE_GET_COMPRESS_METHOD(external.va_ecinfo);
+ }
+ else
+ {
+ data->extsize = external.va_extinfo;
+ data->compression_method = TOAST_INVALID_COMPRESSION_ID;
+ }
+
+ data->value = (((uint64) external.va_valueid_hi) << 32) |
+ external.va_valueid_lo;
+ data->toastrelid = external.va_toastrelid;
+}
+
+static struct varlena *
+ondisk_ce_int8_create_external_data(toast_external_data data)
+{
+ struct varlena *result = NULL;
+ varatt_external_ce_int8 external;
+
+ external.va_rawsize = data.rawsize;
+
+ if (data.compression_method != TOAST_INVALID_COMPRESSION_ID)
+ {
+ /* Set size and compression method. */
+ external.va_extinfo = (uint32) data.extsize | (VARATT_CE_FLAG << VARLENA_EXTSIZE_BITS);
+ VARATT_CE_SET_COMPRESS_METHOD(external.va_ecinfo, data.compression_method);
+ }
+ else
+ external.va_extinfo = data.extsize;
+
+ external.va_toastrelid = data.toastrelid;
+ external.va_valueid_hi = (((uint64) data.value) >> 32);
+ external.va_valueid_lo = (uint32) data.value;
+
+ result = (struct varlena *) palloc(TOAST_POINTER_CE_INT8_SIZE);
+ SET_VARTAG_EXTERNAL(result, VARTAG_ONDISK_CE_INT8);
+ memcpy(VARDATA_EXTERNAL(result), &external, sizeof(external));
+
+ return result;
+}
diff --git a/src/backend/access/common/toast_internals.c b/src/backend/access/common/toast_internals.c
index c6b2d1522ce..468aae64676 100644
--- a/src/backend/access/common/toast_internals.c
+++ b/src/backend/access/common/toast_internals.c
@@ -160,18 +160,6 @@ toast_save_datum(Relation rel, Datum value,
toast_typid = TupleDescAttr(toasttupDesc, 0)->atttypid;
Assert(toast_typid == OIDOID || toast_typid == INT8OID);
- /*
- * Grab the information for toast_external_data.
- *
- * Note: if we support multiple external vartags for a single value
- * type, we would need to be smarter in the vartag selection.
- */
- if (toast_typid == OIDOID)
- tag = VARTAG_ONDISK_OID;
- else if (toast_typid == INT8OID)
- tag = VARTAG_ONDISK_INT8;
- info = toast_external_get_info(tag);
-
/* Open all the toast indexes and look for the valid one */
validIndex = toast_open_indexes(toastrel,
RowExclusiveLock,
@@ -242,6 +230,18 @@ toast_save_datum(Relation rel, Datum value,
else
toast_pointer.toastrelid = RelationGetRelid(toastrel);
+ /*
+ * Grab the information for toast_external_data.
+ *
+ * Note: if we support multiple external vartags for a single value type,
+ * we would need to be smarter in the vartag selection.
+ */
+ if (toast_typid == OIDOID)
+ tag = CompressionMethodIdIsExtended(toast_pointer.compression_method) ? VARTAG_ONDISK_CE_OID : VARTAG_ONDISK_OID;
+ else if (toast_typid == INT8OID)
+ tag = CompressionMethodIdIsExtended(toast_pointer.compression_method) ? VARTAG_ONDISK_CE_INT8 : VARTAG_ONDISK_INT8;
+ info = toast_external_get_info(tag);
+
/*
* Choose a new value to use as the value ID for this toast value, be it
* for OID or int8-based TOAST relations.
@@ -254,8 +254,7 @@ toast_save_datum(Relation rel, Datum value,
* value (which is a corner case, but possible if the table's attstorage
* options have been changed), we have to pick a value ID that doesn't
* conflict with either new or existing toast value IDs. If the TOAST
- * table uses 8-byte value IDs, we should not really care much about
- * that.
+ * table uses 8-byte value IDs, we should not really care much about that.
*/
if (!OidIsValid(rel->rd_toastoid))
{
diff --git a/src/backend/access/heap/heaptoast.c b/src/backend/access/heap/heaptoast.c
index 87f1630d85f..7b03b27341f 100644
--- a/src/backend/access/heap/heaptoast.c
+++ b/src/backend/access/heap/heaptoast.c
@@ -152,7 +152,7 @@ heap_toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup,
{
HeapTuple atttuple;
Form_pg_attribute atttoast;
- uint8 vartag = VARTAG_ONDISK_OID;
+ ToastTypeId toast_type = TOAST_TYPE_INVALID;
/*
* XXX: This is very unlikely efficient, but it is not possible to
@@ -166,32 +166,22 @@ heap_toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup,
atttoast = (Form_pg_attribute) GETSTRUCT(atttuple);
if (atttoast->atttypid == OIDOID)
- vartag = VARTAG_ONDISK_OID;
+ toast_type = TOAST_TYPE_OID;
else if (atttoast->atttypid == INT8OID)
- vartag = VARTAG_ONDISK_INT8;
+ toast_type = TOAST_TYPE_INT8;
else
Assert(false);
- ttc.ttc_toast_pointer_size =
- toast_external_info_get_pointer_size(vartag);
+ ttc.toast_type = toast_type;
ReleaseSysCache(atttuple);
}
else
{
/*
- * No TOAST relation to rely on, which is a case possible when
- * dealing with partitioned tables, for example. Hence, do a best
- * guess based on the GUC default_toast_type.
+ * No TOAST relation to rely on, which is a case possible when dealing
+ * with partitioned tables, for example. Hence, do a best guess based
+ * on the GUC default_toast_type.
*/
- uint8 vartag = VARTAG_ONDISK_OID;
-
- if (default_toast_type == TOAST_TYPE_INT8)
- vartag = VARTAG_ONDISK_INT8;
- else if (default_toast_type == TOAST_TYPE_OID)
- vartag = VARTAG_ONDISK_OID;
- else
- Assert(false);
- ttc.ttc_toast_pointer_size =
- toast_external_info_get_pointer_size(vartag);
+ ttc.toast_type = default_toast_type;
}
ttc.ttc_rel = rel;
@@ -693,9 +683,7 @@ heap_fetch_toast_slice(Relation toastrel, uint64 valueid, int32 attrsize,
int endchunk;
int num_indexes;
int validIndex;
- int32 max_chunk_size;
- const toast_external_info *info;
- uint8 tag = VARTAG_INDIRECT; /* init value does not matter */
+ int32 max_chunk_size = TOAST_MAX_CHUNK_SIZE_OID;
Oid toast_typid = InvalidOid;
/* Look for the valid index of toast relation */
@@ -708,26 +696,23 @@ heap_fetch_toast_slice(Relation toastrel, uint64 valueid, int32 attrsize,
* Grab the information for toast_external_data.
*
* Note: there is no access to the vartag of the original varlena from
- * which we are trying to retrieve the chunks from the TOAST relation,
- * so guess the external TOAST pointer information to use depending
- * on the attribute of the TOAST value. If we begin to support multiple
- * external TOAST pointers for a single attribute type, we would need
- * to pass down this information from the upper callers. This is
- * currently on required for the maximum chunk_size.
+ * which we are trying to retrieve the chunks from the TOAST relation, so
+ * guess the external TOAST pointer information to use depending on the
+ * attribute of the TOAST value. If we begin to support multiple external
+ * TOAST pointers for a single attribute type, we would need to pass down
+ * this information from the upper callers. This is currently on required
+ * for the maximum chunk_size.
*/
toast_typid = TupleDescAttr(toastrel->rd_att, 0)->atttypid;
Assert(toast_typid == OIDOID || toast_typid == INT8OID);
if (toast_typid == OIDOID)
- tag = VARTAG_ONDISK_OID;
+ max_chunk_size = TOAST_MAX_CHUNK_SIZE_OID;
else if (toast_typid == INT8OID)
- tag = VARTAG_ONDISK_INT8;
+ max_chunk_size = TOAST_MAX_CHUNK_SIZE_INT8;
else
Assert(false);
- info = toast_external_get_info(tag);
- max_chunk_size = info->maximum_chunk_size;
-
totalchunks = ((attrsize - 1) / max_chunk_size) + 1;
startchunk = sliceoffset / max_chunk_size;
endchunk = (sliceoffset + slicelength - 1) / max_chunk_size;
diff --git a/src/backend/access/table/toast_helper.c b/src/backend/access/table/toast_helper.c
index a2b44e093d7..d2a0517a36d 100644
--- a/src/backend/access/table/toast_helper.c
+++ b/src/backend/access/table/toast_helper.c
@@ -15,6 +15,7 @@
#include "postgres.h"
#include "access/detoast.h"
+#include "access/toast_external.h"
#include "access/toast_helper.h"
#include "access/toast_internals.h"
#include "catalog/pg_type_d.h"
@@ -51,10 +52,23 @@ toast_tuple_init(ToastTupleContext *ttc)
Form_pg_attribute att = TupleDescAttr(tupleDesc, i);
struct varlena *old_value;
struct varlena *new_value;
+ uint8 vartag = VARTAG_ONDISK_OID;
+ char cmethod = att->attcompression;
+
+ if (!CompressionMethodIsValid(cmethod))
+ cmethod = default_toast_compression;
+
+ if (ttc->toast_type == TOAST_TYPE_OID)
+ vartag = CompressionMethodIsExtended(cmethod) ? VARTAG_ONDISK_CE_OID : VARTAG_ONDISK_OID;
+ else if (ttc->toast_type == TOAST_TYPE_INT8)
+ vartag = CompressionMethodIsExtended(cmethod) ? VARTAG_ONDISK_CE_INT8 : VARTAG_ONDISK_INT8;
+ else
+ Assert(false);
ttc->ttc_attr[i].tai_colflags = 0;
ttc->ttc_attr[i].tai_oldexternal = NULL;
ttc->ttc_attr[i].tai_compression = att->attcompression;
+ ttc->ttc_attr[i].toast_pointer_size = toast_external_info_get_pointer_size(vartag);
if (ttc->ttc_oldvalues != NULL)
{
@@ -171,10 +185,10 @@ toast_tuple_init(ToastTupleContext *ttc)
* The column must have attstorage EXTERNAL or EXTENDED if check_main is
* false, and must have attstorage MAIN if check_main is true.
*
- * The column must have a minimum size of MAXALIGN(tcc_toast_pointer_size);
- * if not, no benefit is to be expected by compressing it. The TOAST
- * pointer size is given by the caller, depending on the type of TOAST
- * table we are dealing with.
+ * Each column must have a minimum size of MAXALIGN(toast_pointer_size) for
+ * that specific column; if not, no benefit is to be expected by compressing it.
+ * The TOAST pointer size varies per column based on the TOAST table type
+ * (OID vs INT8) and different variants used for that specific attribute.
*
* The return value is the index of the biggest suitable column, or
* -1 if there is none.
@@ -190,16 +204,13 @@ toast_tuple_find_biggest_attribute(ToastTupleContext *ttc,
int32 skip_colflags = TOASTCOL_IGNORE;
int i;
- /* Define the lower-bound */
- biggest_size = MAXALIGN(ttc->ttc_toast_pointer_size);
- Assert(biggest_size != 0);
-
if (for_compression)
skip_colflags |= TOASTCOL_INCOMPRESSIBLE;
for (i = 0; i < numAttrs; i++)
{
Form_pg_attribute att = TupleDescAttr(tupleDesc, i);
+ int32 min_size_for_column;
if ((ttc->ttc_attr[i].tai_colflags & skip_colflags) != 0)
continue;
@@ -214,7 +225,19 @@ toast_tuple_find_biggest_attribute(ToastTupleContext *ttc,
att->attstorage != TYPSTORAGE_EXTERNAL)
continue;
- if (ttc->ttc_attr[i].tai_size > biggest_size)
+ /*
+ * Each column has its own minimum size threshold based on its TOAST
+ * pointer size
+ */
+ min_size_for_column = MAXALIGN(ttc->ttc_attr[i].toast_pointer_size);
+ Assert(min_size_for_column > 0);
+
+ /*
+ * Only consider this column if it's bigger than its specific
+ * threshold AND bigger than current biggest
+ */
+ if (ttc->ttc_attr[i].tai_size > min_size_for_column &&
+ ttc->ttc_attr[i].tai_size > biggest_size)
{
biggest_attno = i;
biggest_size = ttc->ttc_attr[i].tai_size;
diff --git a/src/include/access/toast_compression.h b/src/include/access/toast_compression.h
index 13c4612ceed..494e1b0dce6 100644
--- a/src/include/access/toast_compression.h
+++ b/src/include/access/toast_compression.h
@@ -51,6 +51,12 @@ typedef enum ToastCompressionId
#define InvalidCompressionMethod '\0'
#define CompressionMethodIsValid(cm) ((cm) != InvalidCompressionMethod)
+#define CompressionMethodIsExtended(cm) (!(cm == TOAST_PGLZ_COMPRESSION || \
+ cm == TOAST_LZ4_COMPRESSION || \
+ cm == InvalidCompressionMethod))
+#define CompressionMethodIdIsExtended(cmpid) (!(cmpid == TOAST_PGLZ_COMPRESSION_ID || \
+ cmpid == TOAST_LZ4_COMPRESSION_ID || \
+ cmpid == TOAST_INVALID_COMPRESSION_ID))
/* pglz compression/decompression routines */
diff --git a/src/include/access/toast_helper.h b/src/include/access/toast_helper.h
index 729c593afeb..e0ae11a581c 100644
--- a/src/include/access/toast_helper.h
+++ b/src/include/access/toast_helper.h
@@ -14,6 +14,7 @@
#ifndef TOAST_HELPER_H
#define TOAST_HELPER_H
+#include "access/toast_type.h"
#include "utils/rel.h"
/*
@@ -33,6 +34,7 @@ typedef struct
int32 tai_size;
uint8 tai_colflags;
char tai_compression;
+ int32 toast_pointer_size;
} ToastAttrInfo;
/*
@@ -47,11 +49,11 @@ typedef struct
* should be NULL in the case of an insert.
*/
Relation ttc_rel; /* the relation that contains the tuple */
- int32 ttc_toast_pointer_size; /* size of external TOAST pointer */
Datum *ttc_values; /* values from the tuple columns */
bool *ttc_isnull; /* null flags for the tuple columns */
Datum *ttc_oldvalues; /* values from previous tuple */
bool *ttc_oldisnull; /* null flags from previous tuple */
+ ToastTypeId toast_type; /* toast table type */
/*
* Before calling toast_tuple_init, the caller should set ttc_attr to
diff --git a/src/include/access/toast_internals.h b/src/include/access/toast_internals.h
index 06ae8583c1e..27bc8a0b816 100644
--- a/src/include/access/toast_internals.h
+++ b/src/include/access/toast_internals.h
@@ -17,32 +17,17 @@
#include "utils/relcache.h"
#include "utils/snapshot.h"
-/*
- * The information at the start of the compressed toast data.
- */
-typedef struct toast_compress_header
-{
- int32 vl_len_; /* varlena header (do not touch directly!) */
- uint32 tcinfo; /* 2 bits for compression method and 30 bits
- * external size; see va_extinfo */
-} toast_compress_header;
-
/*
* Utilities for manipulation of header information for compressed
* toast entries.
*/
-#define TOAST_COMPRESS_EXTSIZE(ptr) \
- (((toast_compress_header *) (ptr))->tcinfo & VARLENA_EXTSIZE_MASK)
-#define TOAST_COMPRESS_METHOD(ptr) \
- (((toast_compress_header *) (ptr))->tcinfo >> VARLENA_EXTSIZE_BITS)
-
#define TOAST_COMPRESS_SET_SIZE_AND_COMPRESS_METHOD(ptr, len, cm_method) \
do { \
Assert((len) > 0 && (len) <= VARLENA_EXTSIZE_MASK); \
Assert((cm_method) == TOAST_PGLZ_COMPRESSION_ID || \
(cm_method) == TOAST_LZ4_COMPRESSION_ID); \
- ((toast_compress_header *) (ptr))->tcinfo = \
- (len) | ((uint32) (cm_method) << VARLENA_EXTSIZE_BITS); \
+ ((varattrib_4b *)(ptr))->va_compressed.va_tcinfo = \
+ ((uint32)(len)) | ((uint32)(cm_method) << VARLENA_EXTSIZE_BITS); \
} while (0)
extern Datum toast_compress_datum(Datum value, char cmethod);
diff --git a/src/include/varatt.h b/src/include/varatt.h
index aa36e8e1f56..52b17c349c3 100644
--- a/src/include/varatt.h
+++ b/src/include/varatt.h
@@ -53,17 +53,41 @@ typedef struct varatt_external_int8
int32 va_rawsize; /* Original data size (includes header) */
uint32 va_extinfo; /* External saved size (without header) and
* compression method */
+
/*
- * Unique ID of value within TOAST table, as two uint32 for alignment
- * and padding.
- * XXX: think for example about the addition of an extra field for
- * meta-data and/or more compression data, even if it's OK here).
+ * Unique ID of value within TOAST table, as two uint32 for alignment and
+ * padding.
*/
uint32 va_valueid_lo;
uint32 va_valueid_hi;
Oid va_toastrelid; /* RelID of TOAST table containing it */
} varatt_external_int8;
+typedef struct varatt_external_ce_oid
+{
+ int32 va_rawsize; /* Original data size (includes header) */
+ uint32 va_extinfo; /* External saved size (without header) and
+ * VARATT_CE_FLAG in top 2 bits */
+ uint32 va_ecinfo; /* Extended compression info */
+ Oid va_valueid; /* Unique ID of value within TOAST table */
+ Oid va_toastrelid; /* RelID of TOAST table containing it */
+} varatt_external_ce_oid;
+
+typedef struct varatt_external_ce_int8
+{
+ int32 va_rawsize; /* Original data size (includes header) */
+ uint32 va_extinfo; /* External saved size (without header) and
+ * VARATT_CE_FLAG in top 2 bits */
+ uint32 va_ecinfo; /* Extended compression info */
+
+ /*
+ * Unique ID of value within TOAST table, as two uint32 for alignment and
+ * padding.
+ */
+ uint32 va_valueid_lo;
+ uint32 va_valueid_hi;
+ Oid va_toastrelid; /* RelID of TOAST table containing it */
+} varatt_external_ce_int8;
/*
* These macros define the "saved size" portion of va_extinfo. Its remaining
@@ -115,6 +139,8 @@ typedef enum vartag_external
VARTAG_EXPANDED_RO = 2,
VARTAG_EXPANDED_RW = 3,
VARTAG_ONDISK_INT8 = 4,
+ VARTAG_ONDISK_CE_OID = 5,
+ VARTAG_ONDISK_CE_INT8 = 6,
VARTAG_ONDISK_OID = 18
} vartag_external;
@@ -127,6 +153,8 @@ typedef enum vartag_external
VARTAG_IS_EXPANDED(tag) ? sizeof(varatt_expanded) : \
(tag) == VARTAG_ONDISK_OID ? sizeof(varatt_external_oid) : \
(tag) == VARTAG_ONDISK_INT8 ? sizeof(varatt_external_int8) : \
+ (tag) == VARTAG_ONDISK_CE_OID ? sizeof(varatt_external_ce_oid): \
+ (tag) == VARTAG_ONDISK_CE_INT8 ? sizeof(varatt_external_ce_int8): \
(AssertMacro(false), 0))
/*
@@ -152,6 +180,21 @@ typedef union
* compression method; see va_extinfo */
char va_data[FLEXIBLE_ARRAY_MEMBER]; /* Compressed data */
} va_compressed;
+ struct
+ {
+ uint32 va_header;
+ uint32 va_tcinfo; /* Original data size (excludes header) and
+ * compression method or VARATT_CE_FLAG flag;
+ * see va_extinfo */
+ uint32 va_ecinfo; /* Extended compression info: 32-bit field
+ * where only the lower 8 bits are used for
+ * compression method. Upper 24 bits are
+ * reserved/unused. Lower 8 bits layout: Bits
+ * 7–1: encode (cmid − 2), so cmid is
+ * [2…129] Bit 0: flag for extra metadata
+ */
+ char va_data[FLEXIBLE_ARRAY_MEMBER];
+ } va_compressed_ext;
} varattrib_4b;
typedef struct
@@ -321,8 +364,13 @@ typedef struct
(VARATT_IS_EXTERNAL(PTR) && VARTAG_EXTERNAL(PTR) == VARTAG_ONDISK_OID)
#define VARATT_IS_EXTERNAL_ONDISK_INT8(PTR) \
(VARATT_IS_EXTERNAL(PTR) && VARTAG_EXTERNAL(PTR) == VARTAG_ONDISK_INT8)
+#define VARATT_IS_EXTERNAL_ONDISK_CE_OID(PTR) \
+ (VARATT_IS_EXTERNAL(PTR) && VARTAG_EXTERNAL(PTR) == VARTAG_ONDISK_CE_OID)
+#define VARATT_IS_EXTERNAL_ONDISK_CE_INT8(PTR) \
+ (VARATT_IS_EXTERNAL(PTR) && VARTAG_EXTERNAL(PTR) == VARTAG_ONDISK_CE_INT8)
#define VARATT_IS_EXTERNAL_ONDISK(PTR) \
- (VARATT_IS_EXTERNAL_ONDISK_OID(PTR) || VARATT_IS_EXTERNAL_ONDISK_INT8(PTR))
+ (VARATT_IS_EXTERNAL_ONDISK_OID(PTR) || VARATT_IS_EXTERNAL_ONDISK_INT8(PTR) \
+ || VARATT_IS_EXTERNAL_ONDISK_CE_OID(PTR) || VARATT_IS_EXTERNAL_ONDISK_CE_INT8(PTR))
#define VARATT_IS_EXTERNAL_INDIRECT(PTR) \
(VARATT_IS_EXTERNAL(PTR) && VARTAG_EXTERNAL(PTR) == VARTAG_INDIRECT)
#define VARATT_IS_EXTERNAL_EXPANDED_RO(PTR) \
@@ -359,10 +407,15 @@ typedef struct
(VARATT_IS_1B(PTR) ? VARDATA_1B(PTR) : VARDATA_4B(PTR))
/* Decompressed size and compression method of a compressed-in-line Datum */
-#define VARDATA_COMPRESSED_GET_EXTSIZE(PTR) \
- (((varattrib_4b *) (PTR))->va_compressed.va_tcinfo & VARLENA_EXTSIZE_MASK)
+#define VARDATA_COMPRESSED_GET_EXTSIZE(PTR) \
+ ( \
+ (VARATT_IS_EXTENDED_COMPRESSED(PTR)) \
+ ? ( ((varattrib_4b *)(PTR))->va_compressed_ext.va_tcinfo & VARLENA_EXTSIZE_MASK ) \
+ : ( ((varattrib_4b *)(PTR))->va_compressed.va_tcinfo & VARLENA_EXTSIZE_MASK ) \
+ )
#define VARDATA_COMPRESSED_GET_COMPRESS_METHOD(PTR) \
- (((varattrib_4b *) (PTR))->va_compressed.va_tcinfo >> VARLENA_EXTSIZE_BITS)
+ ( (VARATT_IS_EXTENDED_COMPRESSED(PTR)) ? VARATT_CE_GET_COMPRESS_METHOD(((varattrib_4b *) (PTR))->va_compressed_ext.va_ecinfo) \
+ : (((varattrib_4b *) (PTR))->va_compressed.va_tcinfo >> VARLENA_EXTSIZE_BITS))
/*
* Same for external Datums; but note argument is a struct
@@ -370,16 +423,6 @@ typedef struct
*/
#define VARATT_EXTERNAL_GET_EXTSIZE(toast_pointer) \
((toast_pointer).va_extinfo & VARLENA_EXTSIZE_MASK)
-#define VARATT_EXTERNAL_GET_COMPRESS_METHOD(toast_pointer) \
- ((toast_pointer).va_extinfo >> VARLENA_EXTSIZE_BITS)
-
-#define VARATT_EXTERNAL_SET_SIZE_AND_COMPRESS_METHOD(toast_pointer, len, cm) \
- do { \
- Assert((cm) == TOAST_PGLZ_COMPRESSION_ID || \
- (cm) == TOAST_LZ4_COMPRESSION_ID); \
- ((toast_pointer).va_extinfo = \
- (len) | ((uint32) (cm) << VARLENA_EXTSIZE_BITS)); \
- } while (0)
/*
* Testing whether an externally-stored value is compressed now requires
@@ -393,5 +436,41 @@ typedef struct
(VARATT_EXTERNAL_GET_EXTSIZE(toast_pointer) < \
(toast_pointer).va_rawsize - VARHDRSZ)
+/* Extended compression flag (0b11) marks use of extended compression methods */
+#define VARATT_CE_FLAG 0x3
+
+/*
+ * Extended compression info encoding (8-bit layout):
+ *
+ * bit 7 6 5 4 3 2 1 0
+ * +---+---+---+---+---+---+---+---+
+ * | compression_id | M |
+ * +---+---+---+---+---+---+---+---+
+ *
+ * • Bits 7–1: Compression method ID offset (cmid − 2)
+ * Range: [0…127] maps to compression ID [2…129]
+ *
+ * • Bit 0 (M): Metadata flag (currently unused, always 0)
+ * Reserved for future use to indicate extra compression metadata
+ */
+#define VARATT_CE_SET_COMPRESS_METHOD(va_ecinfo, cmid) \
+ do { \
+ uint8 _cmid = (uint8)(cmid); \
+ Assert(_cmid >= 2 && _cmid <= 129); \
+ (va_ecinfo) = (uint32)((_cmid - 2) << 1); \
+ } while (0)
+
+#define VARATT_CE_GET_COMPRESS_METHOD(ecinfo) ((((uint8)(ecinfo) >> 1) & 0x7F) + 2)
+
+/* Test if varattrib_4b uses extended compression format */
+#define VARATT_IS_EXTENDED_COMPRESSED(ptr) \
+ ((((varattrib_4b *)(ptr))->va_compressed_ext.va_tcinfo >> VARLENA_EXTSIZE_BITS) \
+ == VARATT_CE_FLAG)
+
+/* Access compressed data payload in extended format */
+#define VARDATA_EXTENDED_COMPRESSED(ptr) \
+ (((varattrib_4b *)(ptr))->va_compressed_ext.va_data)
+
+#define VARHDRSZ_EXTENDED_COMPRESSED (offsetof(varattrib_4b, va_compressed_ext.va_data))
#endif
--
2.47.1